On 23/05/16(Mon) 15:38, Gerhard Roth wrote:
> This is part 2 of the MBIM patch. It adds the mbim driver to i386
Comments inline.
> Index: sys/dev/usb/if_mbim.c
> ===================================================================
> RCS file: sys/dev/usb/if_mbim.c
> diff -N sys/dev/usb/if_mbim.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ sys/dev/usb/if_mbim.c 23 May 2016 09:50:08 -0000
> +
> +struct cfdriver mbim_cd = {
> + NULL, "mbim", DV_DULL
> +};
> +
> +struct cfattach mbim_ca = {
> + sizeof (struct mbim_softc),
> + mbim_match,
> + mbim_attach,
> + mbim_detach,
> + mbim_activate,
> +};
This struct can be const.
> +/*
> + * Some devices are picky about too frequent control messages.
> + * Query device state not more often than every 0.5 secs.
> + */
> +struct timeval mbim_update_rate = { 0, 500000 };
> +int mbim_delay = 4000;
> +
> +/*
> + * Normally, MBIM devices are detected by their interface class and subclass.
> + * But for some models that have multiple configurations, it is better to
> + * match by vendor and product id so that we can select the desired
> + * configuration ourselves.
> + *
> + * OTOH, some devices identifiy themself als an MBIM device but fail to speak
> + * the MBIM protocol.
> + */
Why is it better? This is just working around usb_probe_and_attach()
and require developer to add an entry for every device we need to
support.
> +int
> +mbim_match(struct device *parent, void *match, void *aux)
> +{
> + struct usb_attach_arg *uaa = aux;
> + usb_interface_descriptor_t *id;
> +
> + if (usb_lookup(mbim_blacklist, uaa->vendor, uaa->product) != NULL)
> + return UMATCH_NONE;
> + if (mbim_lookup(uaa->vendor, uaa->product) != NULL)
> + return UMATCH_VENDOR_PRODUCT;
> + if (!uaa->iface)
> + return UMATCH_NONE;
> + if (!uaa->iface ||
> + (id = usbd_get_interface_descriptor(uaa->iface)) == NULL)
> + return UMATCH_NONE;
> + if (id->bInterfaceClass != UICLASS_CDC ||
> + id->bInterfaceSubClass !=
> + UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL ||
> + id->bNumEndpoints != 1)
> + return UMATCH_NONE;
> +
> + return UMATCH_DEVCLASS_DEVSUBCLASS;
> +}
> +
> +void
> +mbim_attach(struct device *parent, struct device *self, void *aux)
> +{
> + struct mbim_softc *sc = (struct mbim_softc *)self;
> + struct usb_attach_arg *uaa = aux;
> + usbd_status status;
> + struct usbd_desc_iter iter;
> + const usb_descriptor_t *desc;
> + int v;
> + struct mbim_descriptor *md;
> + int i;
> + struct usbd_interface *ctrl_iface = NULL;
> + int ctrl_ep;
> + uint8_t data_ifaceno;
> + usb_interface_descriptor_t *id;
> + usb_config_descriptor_t *cd;
> + usb_endpoint_descriptor_t *ed;
> + int s;
> + struct ifnet *ifp;
> + int hard_mtu;
> +
> + sc->sc_udev = uaa->device;
> +
> + if (uaa->configno < 0) {
> + uaa->configno = mbim_lookup(uaa->vendor, uaa->product)->confno;
> + DPRINTF("%s: switching to config #%d\n", DEVNAM(sc),
> + uaa->configno);
> + status = usbd_set_config_no(sc->sc_udev, uaa->configno, 1);
> + if (status) {
> + printf("%s: failed to switch to config #%d: %s\n",
> + DEVNAM(sc), uaa->configno, usbd_errstr(status));
> + goto fail;
> + }
> + }
> +
> + sc->sc_ver_maj = sc->sc_ver_min = -1;
> + usbd_desc_iter_init(sc->sc_udev, &iter);
> + hard_mtu = MBIM_MAXSEGSZ_MINVAL;
> + while ((desc = usbd_desc_iter_next(&iter))) {
> + if (desc->bDescriptorType != UDESC_CS_INTERFACE)
> + continue;
> + switch (desc->bDescriptorSubtype) {
> + case UDESCSUB_MBIM:
> + md = (struct mbim_descriptor *)desc;
> + v = UGETW(md->bcdMBIMVersion);
> + sc->sc_ver_maj = MBIM_VER_MAJOR(v);
> + sc->sc_ver_min = MBIM_VER_MINOR(v);
> + sc->sc_ctrl_len = UGETW(md->wMaxControlMessage);
> + /* Never trust a USB device! Could try to exploit us */
> + if (sc->sc_ctrl_len < MBIM_CTRLMSG_MINLEN ||
> + sc->sc_ctrl_len > MBIM_CTRLMSG_MAXLEN) {
> + printf("%s: control message len %d out of "
> + "bounds [%d .. %d]\n", DEVNAM(sc),
> + sc->sc_ctrl_len, MBIM_CTRLMSG_MINLEN,
> + MBIM_CTRLMSG_MAXLEN);
> + /* cont. anyway */
> + }
> + sc->sc_maxpktlen = UGETW(md->wMaxSegmentSize);
> + if (sc->sc_maxpktlen < MBIM_MAXSEGSZ_MINVAL) {
> + printf("%s: ignoring invalid segment size %d\n",
> + DEVNAM(sc), sc->sc_maxpktlen);
> + /* cont. anyway */
> + sc->sc_maxpktlen = 8 * 1024;
> + }
> + hard_mtu = sc->sc_maxpktlen;
> + DPRINTFN(2, "%s: ctrl_len=%d, maxpktlen=%d, cap=0x%x\n",
> + DEVNAM(sc), sc->sc_ctrl_len, sc->sc_maxpktlen,
> + md->bmNetworkCapabilities);
> + break;
> + default:
> + break;
> + }
> + }
> + cd = usbd_get_config_descriptor(sc->sc_udev);
> + if (sc->sc_ver_maj < 0) {
> + printf("%s: missing MBIM descriptor\n", DEVNAM(sc));
> + goto fail;
> + }
> +
> + for (i = 0; i < sc->sc_udev->cdesc->bNumInterface; i++) {
> + if (usbd_iface_claimed(sc->sc_udev, i))
> + continue;
> + id = usbd_get_interface_descriptor(&sc->sc_udev->ifaces[i]);
> + if (id == NULL)
> + continue;
> + if (id->bInterfaceClass == UICLASS_CDC &&
> + id->bInterfaceSubClass ==
> + UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL) {
> + ctrl_iface = &sc->sc_udev->ifaces[i];
> + sc->sc_ctrl_ifaceno = id->bInterfaceNumber;
> + usbd_claim_iface(sc->sc_udev, i);
> + } else if (id->bInterfaceClass == UICLASS_CDC_DATA &&
> + id->bInterfaceSubClass == UISUBCLASS_DATA &&
> + id->bInterfaceProtocol == UIPROTO_DATA_MBIM) {
> + sc->sc_data_iface = &sc->sc_udev->ifaces[i];
> + data_ifaceno = id->bInterfaceNumber;
> + usbd_claim_iface(sc->sc_udev, i);
> + }
> + }
> + if (ctrl_iface == NULL) {
> + printf("%s: no control interface found\n", DEVNAM(sc));
> + return;
> + }
> + if (sc->sc_data_iface == NULL) {
> + printf("%s: no data interface found\n", DEVNAM(sc));
> + goto fail;
> + }
> +
> + id = usbd_get_interface_descriptor(ctrl_iface);
> + ctrl_ep = -1;
> + for (i = 0; i < id->bNumEndpoints && ctrl_ep == -1; i++) {
> + ed = usbd_interface2endpoint_descriptor(ctrl_iface, i);
> + if (ed == NULL)
> + break;
> + if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT &&
> + UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
> + ctrl_ep = ed->bEndpointAddress;
> + }
> + if (ctrl_ep == -1) {
> + printf("%s: missing interrupt endpoint\n", DEVNAM(sc));
> + goto fail;
> + }
> +
> + id = usbd_get_interface_descriptor(sc->sc_data_iface);
> + sc->sc_rx_ep = sc->sc_tx_ep = -1;
> + if ((status = usbd_set_interface(sc->sc_data_iface,
> + MBIM_INTERFACE_ALTSETTING))) {
> + printf("%s: select alt interface %d failed: %s\n",
> + DEVNAM(sc), MBIM_INTERFACE_ALTSETTING, usbd_errstr(status));
> + goto fail;
> + }
> + id = usbd_get_interface_descriptor(sc->sc_data_iface);
> + for (i = 0; i < id->bNumEndpoints; i++) {
> + if ((ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface,
> + i)) == NULL)
> + break;
> + if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
> + UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
> + sc->sc_rx_ep = ed->bEndpointAddress;
> + else if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
> + UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
> + sc->sc_tx_ep = ed->bEndpointAddress;
> + }
> + if (sc->sc_rx_ep == -1 || sc->sc_tx_ep == -1) {
> + printf("%s: missing bulk endpoints\n", DEVNAM(sc));
> + goto fail;
> + }
> +
> + DPRINTFN(2, "%s: ctrl-ifno#%d: ep-ctrl=%d, data-ifno#%d: ep-rx=%d, "
> + "ep-tx=%d\n", DEVNAM(sc), sc->sc_ctrl_ifaceno,
> + UE_GET_ADDR(ctrl_ep), data_ifaceno,
> + UE_GET_ADDR(sc->sc_rx_ep), UE_GET_ADDR(sc->sc_tx_ep));
> +
> + usb_init_task(&sc->sc_mbim_task, mbim_state_task, sc,
> + USB_TASK_TYPE_GENERIC);
> + usb_init_task(&sc->sc_get_response_task, mbim_get_response_task, sc,
> + USB_TASK_TYPE_GENERIC);
> + timeout_set(&sc->sc_statechg_timer, mbim_statechg_timeout, sc);
> +
> + if (usbd_open_pipe_intr(ctrl_iface, ctrl_ep, USBD_SHORT_XFER_OK,
> + &sc->sc_ctrl_pipe, sc, &sc->sc_intr_msg, sizeof (sc->sc_intr_msg),
> + mbim_intr, USBD_DEFAULT_INTERVAL)) {
> + printf("%s: failed to open control pipe\n", DEVNAM(sc));
> + goto fail;
> + }
> + sc->sc_resp_buf = malloc(sc->sc_ctrl_len, M_USBDEV, M_NOWAIT);
> + if (sc->sc_resp_buf == NULL) {
> + printf("%s: allocation of resp buffer failed\n", DEVNAM(sc));
> + goto fail;
> + }
> + sc->sc_ctrl_msg = malloc(sc->sc_ctrl_len, M_USBDEV, M_NOWAIT);
> + if (sc->sc_ctrl_msg == NULL) {
> + printf("%s: allocation of ctrl msg buffer failed\n",
> + DEVNAM(sc));
> + goto fail;
> + }
> +
> + sc->sc_info.regstate = MBIM_REGSTATE_UNKNOWN;
> + sc->sc_info.pin_attempts_left = MBIM_VALUE_UNKNOWN;
> + sc->sc_info.rssi = MBIM_VALUE_UNKNOWN;
> + sc->sc_info.ber = MBIM_VALUE_UNKNOWN;
> +
> + s = splnet();
> + ifp = GET_IFP(sc);
> + ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_POINTOPOINT;
> + ifp->if_ioctl = mbim_ioctl;
> + ifp->if_start = mbim_start;
> +
> + ifp->if_watchdog = mbim_watchdog;
> + strlcpy(ifp->if_xname, DEVNAM(sc), IFNAMSIZ);
> + ifp->if_link_state = LINK_STATE_DOWN;
> +
> + ifp->if_type = IFT_MBIM;
> + ifp->if_addrlen = 0;
> + ifp->if_hdrlen = sizeof (struct ncm_header16) +
> + sizeof (struct ncm_pointer16);
> + ifp->if_mtu = 1500; /* use a common default */
> + ifp->if_hardmtu = hard_mtu;
> + ifp->if_output = mbim_output;
> + if_attach(ifp);
> + if_ih_insert(ifp, mbim_input, NULL);
> + if_alloc_sadl(ifp);
> + ifp->if_softc = sc;
> +#if NBPFILTER > 0
> + bpfattach(&ifp->if_bpf, ifp, DLT_RAW, 0);
> +#endif
> + /*
> + * Open the device now so that we are able to query device information.
> + * XXX maybe close when done?
> + */
> + mbim_open(sc);
> + splx(s);
> +
> + printf("%s: vers %d.%d\n", DEVNAM(sc), sc->sc_ver_maj, sc->sc_ver_min);
> + return;
> +
> +fail:
> + usbd_deactivate(sc->sc_udev);
> + return;
> +}
> +
> +int
> +mbim_detach(struct device *self, int flags)
> +{
> + struct mbim_softc *sc = (struct mbim_softc *)self;
> + struct ifnet *ifp = GET_IFP(sc);
> + int s;
> +
> + s = splnet();
> + if (ifp->if_flags & IFF_RUNNING)
> + mbim_down(sc, 1);
> + mbim_close(sc);
> +
> + usb_rem_wait_task(sc->sc_udev, &sc->sc_get_response_task);
> + if (timeout_initialized(&sc->sc_statechg_timer))
> + timeout_del(&sc->sc_statechg_timer);
> + sc->sc_nresp = 0;
> + usb_rem_wait_task(sc->sc_udev, &sc->sc_mbim_task);
> + if (sc->sc_ctrl_pipe) {
> + usbd_close_pipe(sc->sc_ctrl_pipe);
> + sc->sc_ctrl_pipe = NULL;
> + }
> + if (sc->sc_ctrl_msg) {
> + free(sc->sc_ctrl_msg, M_USBDEV, sc->sc_ctrl_len);
> + sc->sc_ctrl_msg = NULL;
> + }
> + if (sc->sc_resp_buf) {
> + free(sc->sc_resp_buf, M_USBDEV, sc->sc_ctrl_len);
> + sc->sc_resp_buf = NULL;
> + }
> + if (sc->sc_saved_ifaddr != NULL &&
> + sc->sc_saved_ifaddr != MBIM_IFADDR_NONE)
> + free(sc->sc_saved_ifaddr, M_USBDEV,
> + sizeof (struct saved_ifaddr));
> + sc->sc_saved_ifaddr = NULL;
> +
> + if (ifp->if_softc != NULL) {
> + if_ih_remove(ifp, mbim_input, NULL);
> + if_detach(ifp);
> + }
> +
> + splx(s);
> + return 0;
> +}
> +
> +int
> +mbim_activate(struct device *self, int act)
> +{
> + struct mbim_softc *sc = (struct mbim_softc *)self;
> +
> + switch (act) {
> + case DVACT_DEACTIVATE:
> + usbd_deactivate(sc->sc_udev);
> + break;
> + }
> + return 0;
> +}
This is no longer needed.
> +
> +int
> +mbim_alloc_xfers(struct mbim_softc *sc)
> +{
> + if (!sc->sc_rx_xfer) {
> + if ((sc->sc_rx_xfer = usbd_alloc_xfer(sc->sc_udev)) != NULL)
> + sc->sc_rx_buf = usbd_alloc_buffer(sc->sc_rx_xfer,
> + sc->sc_maxpktlen + MBIM_HDR32_LEN);
> + }
> + if (!sc->sc_tx_xfer) {
> + if ((sc->sc_tx_xfer = usbd_alloc_xfer(sc->sc_udev)) != NULL)
> + sc->sc_tx_buf = usbd_alloc_buffer(sc->sc_tx_xfer,
> + sc->sc_maxpktlen + MBIM_HDR16_LEN);
> + }
> + return (sc->sc_rx_buf && sc->sc_tx_buf) ? 1 : 0;
> +}
> +
> +void
> +mbim_free_xfers(struct mbim_softc *sc)
> +{
> + if (sc->sc_rx_xfer) {
> + /* implicit usbd_free_buffer() */
> + usbd_free_xfer(sc->sc_rx_xfer);
> + sc->sc_rx_xfer = NULL;
> + sc->sc_rx_buf = NULL;
> + }
> + if (sc->sc_tx_xfer) {
> + usbd_free_xfer(sc->sc_tx_xfer);
> + sc->sc_tx_xfer = NULL;
> + sc->sc_tx_buf = NULL;
> + }
> + if (sc->sc_tx_m) {
> + m_freem(sc->sc_tx_m);
> + sc->sc_tx_m = NULL;
> + }
> +}
> +
> +int
> +mbim_alloc_bulkpipes(struct mbim_softc *sc)
> +{
> + struct ifnet *ifp = GET_IFP(sc);
> +
> + if (!(ifp->if_flags & IFF_RUNNING)) {
> + if (usbd_open_pipe(sc->sc_data_iface, sc->sc_rx_ep,
> + USBD_EXCLUSIVE_USE, &sc->sc_rx_pipe))
> + return 0;
> + if (usbd_open_pipe(sc->sc_data_iface, sc->sc_tx_ep,
> + USBD_EXCLUSIVE_USE, &sc->sc_tx_pipe))
> + return 0;
> +
> + ifp->if_flags |= IFF_RUNNING;
> + ifp->if_flags &= ~IFF_OACTIVE;
> + mbim_rx(sc);
> + }
> + return 1;
> +}
> +
> +void
> +mbim_close_bulkpipes(struct mbim_softc *sc)
> +{
> + struct ifnet *ifp = GET_IFP(sc);
> +
> + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
> + ifp->if_timer = 0;
> + if (sc->sc_rx_pipe) {
> + usbd_close_pipe(sc->sc_rx_pipe);
> + sc->sc_rx_pipe = NULL;
> + }
> + if (sc->sc_tx_pipe) {
> + usbd_close_pipe(sc->sc_tx_pipe);
> + sc->sc_tx_pipe = NULL;
> + }
> +}
> +
> +int
> +mbim_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
> +{
> + struct proc *p = curproc;
> + struct mbim_softc *sc = ifp->if_softc;
> + struct ifreq *ifr = (struct ifreq *)data;
> + int s, error = 0;
> + struct mbim_parameter mp;
> +
> + if (usbd_is_dying(sc->sc_udev))
> + return EIO;
> +
> + s = splnet();
> + switch (cmd) {
> + case SIOCSIFADDR:
> + sc->sc_if.if_rtrequest = p2p_rtrequest;
> + break;
> + case SIOCSIFFLAGS:
> + usb_add_task(sc->sc_udev, &sc->sc_mbim_task);
> + break;
> + case SIOCGMBIMINFO:
> + error = copyout(&sc->sc_info, ifr->ifr_data,
> + sizeof (sc->sc_info));
> + break;
> + case SIOCSMBIMPARAM:
> + if ((error = suser(p, 0)) != 0)
> + break;
> + if ((error = copyin(ifr->ifr_data, &mp, sizeof (mp))) != 0)
> + break;
> +
> + if ((error = mbim_setpin(sc, mp.op, mp.is_puk,
> + mp.pin, mp.pinlen, mp.newpin, mp.newpinlen)) != 0)
> + break;
> +
> + if (mp.apnlen < 0 || mp.apnlen > sizeof (sc->sc_info.apn)) {
> + error = EINVAL;
> + break;
> + }
> + sc->sc_roaming = mp.roaming ? 1 : 0;
> + memset(sc->sc_info.apn, 0, sizeof (sc->sc_info.apn));
> + memcpy(sc->sc_info.apn, mp.apn, mp.apnlen);
> + sc->sc_info.apnlen = mp.apnlen;
> + sc->sc_info.preferredclasses = mp.preferredclasses;
> + mbim_setdataclass(sc);
> + break;
> + case SIOCGMBIMPARAM:
> + memset(&mp, 0, sizeof (mp));
> + memcpy(mp.apn, sc->sc_info.apn, sc->sc_info.apnlen);
> + mp.apnlen = sc->sc_info.apnlen;
> + mp.roaming = sc->sc_roaming;
> + mp.preferredclasses = sc->sc_info.preferredclasses;
> + error = copyout(&mp, ifr->ifr_data, sizeof (mp));
> + break;
> + case SIOCSIFMTU:
> + /* Does this include the NCM headers and tail? */
> + if (ifr->ifr_mtu > ifp->if_hardmtu) {
> + error = EINVAL;
> + break;
> + }
> + ifp->if_mtu = ifr->ifr_mtu;
> + break;
> + case SIOCGIFMTU:
> + ifr->ifr_mtu = ifp->if_mtu;
> + break;
> + case SIOCGIFHARDMTU:
> + ifr->ifr_hardmtu = ifp->if_hardmtu;
> + break;
> + case SIOCAIFADDR:
> + case SIOCSIFDSTADDR:
> + case SIOCADDMULTI:
> + case SIOCDELMULTI:
> + break;
> + default:
> + error = ENOTTY;
> + break;
> + }
> + splx(s);
> + return error;
> +}
> +
> +int
> +mbim_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
> + struct rtentry *rtp)
> +{
> + struct mbim_softc *sc = ifp->if_softc;
> +
> + if (usbd_is_dying(sc->sc_udev) || !(ifp->if_flags & IFF_RUNNING)) {
> + m_freem(m);
> + return ENETDOWN;
> + }
> + return if_enqueue(ifp, m);
> +}
> +
> +int
> +mbim_input(struct ifnet *ifp, struct mbuf *m, void *cookie)
> +{
> + struct niqueue *inq;
> + uint8_t ipv;
> +
> + if (((ifp->if_flags & IFF_UP) == 0) || (m->m_flags & M_FILDROP) != 0) {
> + m_freem(m);
> + return 1;
> + }
> + if (m->m_pkthdr.len < sizeof (struct ip)) {
> + ifp->if_ierrors++;
> + DPRINTFN(4, "%s: dropping short packet (len %d)\n", __func__,
> + m->m_pkthdr.len);
> + m_freem(m);
> + return 1;
> + }
> + m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
> + m_copydata(m, 0, sizeof (ipv), &ipv);
> + ipv >>= 4;
> +
> + ifp->if_ibytes += m->m_pkthdr.len;
> + switch (ipv) {
> + case 4:
> + inq = &ipintrq;
> + break;
> + case 6:
> + inq = &ip6intrq;
> + break;
> + default:
> + ifp->if_ierrors++;
> + DPRINTFN(4, "%s: dropping packet with bad IP version (%d)\n",
> + __func__, ipv);
> + m_freem(m);
> + return 1;
> + }
> + niq_enqueue(inq, m);
> + return 1;
> +}
> +
> +void
> +mbim_start(struct ifnet *ifp)
> +{
> + struct mbim_softc *sc = ifp->if_softc;
> + struct mbuf *m_head = NULL;
> +
> + if (usbd_is_dying(sc->sc_udev) ||
> + !(ifp->if_flags & IFF_RUNNING) ||
> + (ifp->if_flags & IFF_OACTIVE))
> + return;
> +
> + m_head = ifq_deq_begin(&ifp->if_snd);
> + if (m_head == NULL)
> + return;
> +
> + if (!mbim_encap(sc, m_head)) {
> + ifq_deq_rollback(&ifp->if_snd, m_head);
> + ifp->if_flags |= IFF_OACTIVE;
> + return;
> + }
> + ifq_deq_commit(&ifp->if_snd, m_head);
> +
> +#if NBPFILTER > 0
> + if (ifp->if_bpf)
> + bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
> +#endif
> +
> + ifp->if_flags |= IFF_OACTIVE;
> + ifp->if_timer = (2 * mbim_xfer_tout) / 1000;
> +}
> +
> +void
> +mbim_watchdog(struct ifnet *ifp)
> +{
> + struct mbim_softc *sc = ifp->if_softc;
> +
> + if (usbd_is_dying(sc->sc_udev))
> + return;
> +
> + ifp->if_oerrors++;
> + log(LOG_WARNING, "%s: watchdog timeout\n", DEVNAM(sc));
Watchdog are run in a task now, so it should be possible to stop/start
devices even if you need to sleep.
> + return;
> +}
> +
> [...]
> +int
> +mbim_decode_ip_configuration(struct mbim_softc *sc, void *data, int len)
> +{
> + struct mbim_cid_ip_configuration_info *ic = data;
> + struct ifnet *ifp = GET_IFP(sc);
> + int s;
> + uint32_t avail;
> + uint32_t val;
> + int n, i;
> + int off;
> + struct mbim_cid_ipv4_element ipv4elem;
> + struct in_ifaddr *ia = NULL;
> + struct ifaddr *ifa;
> + int newifaddr = 0;
> + int ifaddr_changed = 0;
> + int state = -1;
> +
> + if (len < sizeof (*ic))
> + return 0;
> + if (letoh32(ic->sessionid) != mbim_session_id) {
> + DPRINTF("%s: ignore IP configration for session id %d\n",
> + DEVNAM(sc), letoh32(ic->sessionid));
> + return 0;
> + }
> + s = splnet();
> +
> + /*
> + * IPv4 configuation
> + */
> + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
> + if (ifa->ifa_addr->sa_family == AF_INET) {
> + if ((ia = ifatoia(ifa)) != NULL)
> + break;
> + }
> + }
> +
> + avail = letoh32(ic->ipv4_available);
> + if (avail & MBIM_IPCONF_HAS_ADDRINFO) {
> + n = letoh32(ic->ipv4_naddr);
> + off = letoh32(ic->ipv4_addroffs);
> +
> + if (n == 0 || off + sizeof (ipv4elem) > len)
> + goto done;
> +
> + /* Only pick the first one */
> + memcpy(&ipv4elem, data + off, sizeof (ipv4elem));
> + ipv4elem.addr = letoh32(ipv4elem.addr);
> + ipv4elem.prefixlen = letoh32(ipv4elem.prefixlen);
> + if (ia == NULL) {
> + ia = malloc(sizeof (*ia), M_IFADDR, M_WAITOK | M_ZERO);
> + ia->ia_ifa.ifa_ifp = ifp;
> + newifaddr = 1;
> + /* No previous addr */
> + sc->sc_saved_ifaddr = MBIM_IFADDR_NONE;
> + } else if (memcmp(&ipv4elem.addr, &ia->ia_addr.sin_addr.s_addr,
> + sizeof (&ipv4elem.addr))) {
> + ifaddr_changed = 1;
> +
> + /*
> + * First time here, save original settings so we can
> + * restore them later, when link goes down.
> + */
> + if (sc->sc_saved_ifaddr == NULL) {
> + struct saved_ifaddr *sif;
> +
> + sif = malloc(sizeof (*sif), M_USBDEV,
> + M_WAITOK | M_ZERO);
> + memcpy(&sif->netmask, &ia->ia_netmask,
> + sizeof (sif->netmask));
> + memcpy(&sif->addr, &ia->ia_addr,
> + sizeof (sif->addr));
> + memcpy(&sif->dst, &ia->ia_dstaddr,
> + sizeof (sif->dst));
> + memcpy(&sif->sockmask, &ia->ia_sockmask,
> + sizeof (sif->sockmask));
> + sc->sc_saved_ifaddr = sif;
> + }
> + }
> + state = MBIM_S_UP;
> + } else if (ia != NULL) {
> + /* IP config has no address, but our interface has one. */
> + state = MBIM_S_CONNECTED;
> + }
> + if (newifaddr || ifaddr_changed) {
> + ia->ia_addr.sin_family = AF_INET;
> + ia->ia_addr.sin_len = sizeof(ia->ia_addr);
> + ia->ia_addr.sin_addr.s_addr = ipv4elem.addr;
> + ia->ia_dstaddr.sin_family = AF_INET;
> + ia->ia_dstaddr.sin_len = sizeof(ia->ia_addr);
> + if (avail & MBIM_IPCONF_HAS_GWINFO) {
> + off = letoh32(ic->ipv4_gwoffs);
> + ia->ia_dstaddr.sin_addr.s_addr =
> + letoh32(*((uint32_t *)(data + off)));
> + } else if (newifaddr)
> + ia->ia_dstaddr.sin_addr.s_addr = INADDR_BROADCAST;
> + ia->ia_sockmask.sin_family = AF_INET;
> + ia->ia_sockmask.sin_len = sizeof(ia->ia_addr);
> + in_len2mask(&ia->ia_sockmask.sin_addr, ipv4elem.prefixlen);
> + ia->ia_netmask = ia->ia_sockmask.sin_addr.s_addr;
> + ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
> + ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
> + ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
> + mbim_set_ipv4addr(ifp, ia, newifaddr);
This is crazy :) No driver should ever modify `ia' directly. This
code should call in_control() via the ioctl path.
> +}
> +
> +/* Code copied from if_spppsubr.c */
> +void
> +mbim_update_gw(struct ifnet *ifp)
> +{
> + unsigned int tid;
> +
> + /* update routing table */
> + for (tid = 0; tid <= RT_TABLEID_MAX; tid++) {
> + while (rtable_walk(tid, AF_INET, mbim_update_gw_walker, ifp) ==
> + EAGAIN)
> + ; /* nothing */
> + }
> +}
> +
> +int
> +mbim_update_gw_walker(struct rtentry *rt, void *arg, unsigned int id)
> +{
> + struct ifnet *ifp = arg;
> +
> + if (rt->rt_ifidx == ifp->if_index) {
> + if (rt->rt_ifa->ifa_dstaddr->sa_family !=
> + rt->rt_gateway->sa_family ||
> + !ISSET(rt->rt_flags, RTF_GATEWAY))
> + return 0; /* do not modify non-gateway routes */
> + log(LOG_INFO, "%s: update gw %s -> %s\n", DEVNAM(ifp->if_softc),
> + mbim_ntop(rt->rt_gateway),
> + mbim_ntop(rt->rt_ifa->ifa_dstaddr));
> + rt_setgate(rt, rt->rt_ifa->ifa_dstaddr);
> + }
This is the kind of horrors I have been removing during the past years.
Why do you need to set a default route in the first place?
> +void
> +mbim_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
> +{
> + struct mbim_softc *sc = priv;
> + struct ifnet *ifp = GET_IFP(sc);
> +
> + if (usbd_is_dying(sc->sc_udev) || !(ifp->if_flags & IFF_RUNNING))
> + return;
> +
> + if (status != USBD_NORMAL_COMPLETION) {
> + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
> + return;
> + if (usbd_ratecheck(&sc->sc_rx_ratechk))
> + DPRINTF("%s: rx error: %s\n", DEVNAM(sc),
> + usbd_errstr(status));
Why do you need a ratecheck, do you see that many errors?
> + if (status == USBD_STALLED)
> + usbd_clear_endpoint_stall_async(sc->sc_rx_pipe);
> + if (++sc->sc_rx_nerr > 100) {
> + log(LOG_ERR, "%s: too many rx errors, disabling\n",
> + DEVNAM(sc));
> + usbd_deactivate(sc->sc_udev);
> + }
> + } else {
> + sc->sc_rx_nerr = 0;
> + mbim_decap(sc, xfer);
> + }
> +
> + mbim_rx(sc);
> + return;
> +}