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;
> +}

Reply via email to