int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) { const struct usb_cdc_union_desc *union_desc = NULL; struct cdc_ncm_ctx *ctx; struct usb_driver *driver; u8 *buf; int len; int temp; u8 iface_no; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; ctx->bh.data = (unsigned long)dev; ctx->bh.func = cdc_ncm_txpath_bh; atomic_set(&ctx->stop, 0); spin_lock_init(&ctx->mtx); /* store ctx pointer in device data field */ dev->data[0] = (unsigned long)ctx; /* only the control interface can be successfully probed */ ctx->control = intf; /* get some pointers */ driver = driver_of(intf); buf = intf->cur_altsetting->extra; len = intf->cur_altsetting->extralen; /* parse through descriptors associated with control interface */ while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) { if (buf[1] != USB_DT_CS_INTERFACE) goto advance; switch (buf[2]) { case USB_CDC_UNION_TYPE: if (buf[0] < sizeof(*union_desc)) break; union_desc = (const struct usb_cdc_union_desc *)buf; /* the master must be the interface we are probing */ if (intf->cur_altsetting->desc.bInterfaceNumber != union_desc->bMasterInterface0) { dev_dbg(&intf->dev, "bogus CDC Union\n"); goto error; } ctx->data = usb_ifnum_to_if(dev->udev, union_desc->bSlaveInterface0); break; case USB_CDC_ETHERNET_TYPE: if (buf[0] < sizeof(*(ctx->ether_desc))) break; ctx->ether_desc = (const struct usb_cdc_ether_desc *)buf; break; case USB_CDC_NCM_TYPE: if (buf[0] < sizeof(*(ctx->func_desc))) break; ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf; break; case USB_CDC_MBIM_TYPE: if (buf[0] < sizeof(*(ctx->mbim_desc))) break; ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf; break; case USB_CDC_MBIM_EXTENDED_TYPE: if (buf[0] < sizeof(*(ctx->mbim_extended_desc))) break; ctx->mbim_extended_desc = (const struct usb_cdc_mbim_extended_desc *)buf; break; default: break; } advance: /* advance to next descriptor */ temp = buf[0]; buf += temp; len -= temp; } /* some buggy devices have an IAD but no CDC Union */ if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1); dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n"); } /* check if we got everything */ if (!ctx->data || (!ctx->mbim_desc && !ctx->ether_desc)) { dev_dbg(&intf->dev, "CDC descriptors missing\n"); goto error; } /* claim data interface, if different from control */ if (ctx->data != ctx->control) { temp = usb_driver_claim_interface(driver, ctx->data, dev); if (temp) { dev_dbg(&intf->dev, "failed to claim data intf\n"); goto error; } } iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber; /* reset data interface */ temp = usb_set_interface(dev->udev, iface_no, 0); if (temp) { dev_dbg(&intf->dev, "set interface failed\n"); goto error2; } /* configure data interface */ temp = usb_set_interface(dev->udev, iface_no, data_altsetting); if (temp) { dev_dbg(&intf->dev, "set interface failed\n"); goto error2; } cdc_ncm_find_endpoints(dev, ctx->data); cdc_ncm_find_endpoints(dev, ctx->control); if (!dev->in || !dev->out || !dev->status) { dev_dbg(&intf->dev, "failed to collect endpoints\n"); goto error2; } /* initialize data interface */ if (cdc_ncm_setup(dev)) { dev_dbg(&intf->dev, "cdc_ncm_setup() failed\n"); goto error2; } usb_set_intfdata(ctx->data, dev); usb_set_intfdata(ctx->control, dev); if (ctx->ether_desc) { temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); if (temp) { dev_dbg(&intf->dev, "failed to get mac address\n"); goto error2; } dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr); } /* usbnet use these values for sizing tx/rx queues */ dev->hard_mtu = ctx->tx_max; dev->rx_urb_size = ctx->rx_max; return 0; error2: usb_set_intfdata(ctx->control, NULL); usb_set_intfdata(ctx->data, NULL); if (ctx->data != ctx->control) usb_driver_release_interface(driver, ctx->data); error: cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]); dev->data[0] = 0; dev_info(&intf->dev, "bind() failure\n"); return -ENODEV; }
int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) { struct cdc_ncm_ctx *ctx; struct usb_driver *driver; u8 *buf; int len; int temp; u8 iface_no; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; ctx->bh.data = (unsigned long)ctx; ctx->bh.func = cdc_ncm_txpath_bh; atomic_set(&ctx->stop, 0); spin_lock_init(&ctx->mtx); ctx->netdev = dev->net; /* store ctx pointer in device data field */ dev->data[0] = (unsigned long)ctx; /* get some pointers */ driver = driver_of(intf); buf = intf->cur_altsetting->extra; len = intf->cur_altsetting->extralen; ctx->udev = dev->udev; ctx->intf = intf; /* parse through descriptors associated with control interface */ while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) { if (buf[1] != USB_DT_CS_INTERFACE) goto advance; switch (buf[2]) { case USB_CDC_UNION_TYPE: if (buf[0] < sizeof(*(ctx->union_desc))) break; ctx->union_desc = (const struct usb_cdc_union_desc *)buf; ctx->control = usb_ifnum_to_if(dev->udev, ctx->union_desc->bMasterInterface0); ctx->data = usb_ifnum_to_if(dev->udev, ctx->union_desc->bSlaveInterface0); break; case USB_CDC_ETHERNET_TYPE: if (buf[0] < sizeof(*(ctx->ether_desc))) break; ctx->ether_desc = (const struct usb_cdc_ether_desc *)buf; dev->hard_mtu = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); if (dev->hard_mtu < CDC_NCM_MIN_DATAGRAM_SIZE) dev->hard_mtu = CDC_NCM_MIN_DATAGRAM_SIZE; else if (dev->hard_mtu > CDC_NCM_MAX_DATAGRAM_SIZE) dev->hard_mtu = CDC_NCM_MAX_DATAGRAM_SIZE; break; case USB_CDC_NCM_TYPE: if (buf[0] < sizeof(*(ctx->func_desc))) break; ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf; break; case USB_CDC_MBIM_TYPE: if (buf[0] < sizeof(*(ctx->mbim_desc))) break; ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf; break; default: break; } advance: /* advance to next descriptor */ temp = buf[0]; buf += temp; len -= temp; } /* some buggy devices have an IAD but no CDC Union */ if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { ctx->control = intf; ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1); dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n"); } /* check if we got everything */ if ((ctx->control == NULL) || (ctx->data == NULL) || ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf)))) goto error; /* claim data interface, if different from control */ if (ctx->data != ctx->control) { temp = usb_driver_claim_interface(driver, ctx->data, dev); if (temp) goto error; } iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber; /* reset data interface */ temp = usb_set_interface(dev->udev, iface_no, 0); if (temp) goto error2; /* initialize data interface */ if (cdc_ncm_setup(ctx)) goto error2; /* configure data interface */ temp = usb_set_interface(dev->udev, iface_no, data_altsetting); if (temp) goto error2; cdc_ncm_find_endpoints(ctx, ctx->data); cdc_ncm_find_endpoints(ctx, ctx->control); if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) || (ctx->status_ep == NULL)) goto error2; dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; usb_set_intfdata(ctx->data, dev); usb_set_intfdata(ctx->control, dev); usb_set_intfdata(ctx->intf, dev); if (ctx->ether_desc) { temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); if (temp) goto error2; dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr); } dev->in = usb_rcvbulkpipe(dev->udev, ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->out = usb_sndbulkpipe(dev->udev, ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->status = ctx->status_ep; dev->rx_urb_size = ctx->rx_max; ctx->tx_speed = ctx->rx_speed = 0; return 0; error2: usb_set_intfdata(ctx->control, NULL); usb_set_intfdata(ctx->data, NULL); if (ctx->data != ctx->control) usb_driver_release_interface(driver, ctx->data); error: cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]); dev->data[0] = 0; dev_info(&dev->udev->dev, "bind() failure\n"); return -ENODEV; }
int cdc_ncm_bind(struct if_usb_devdata *pipe_data, struct usb_interface *intf, struct usb_link_device *usb_ld) { struct cdc_ncm_ctx *ctx; struct usb_driver *usbdrv = to_usb_driver(intf->dev.driver); struct usb_device *usbdev = interface_to_usbdev(intf); unsigned char *buf = intf->cur_altsetting->extra; int buflen = intf->cur_altsetting->extralen; const struct usb_cdc_union_desc *union_desc; int temp; u8 iface_no; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (ctx == NULL) return -ENODEV; hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; ctx->bh.data = (unsigned long)pipe_data; ctx->bh.func = cdc_ncm_txpath_bh; atomic_set(&ctx->stop, 0); spin_lock_init(&ctx->mtx); /* store ctx pointer in device data field */ pipe_data->sedata = (void *)ctx; ctx->intf = intf; /* parse through descriptors associated with control interface */ while ((buflen > 0) && (buf[0] > 2) && (buf[0] <= buflen)) { if (buf[1] == USB_DT_CS_INTERFACE) { switch (buf[2]) { case USB_CDC_UNION_TYPE: if (buf[0] < sizeof(*union_desc)) break; union_desc = (const struct usb_cdc_union_desc *)buf; ctx->control = usb_ifnum_to_if(usbdev, union_desc->bMasterInterface0); ctx->data = usb_ifnum_to_if(usbdev, union_desc->bSlaveInterface0); break; case USB_CDC_ETHERNET_TYPE: if (buf[0] < sizeof(*(ctx->ether_desc))) break; ctx->ether_desc = (const struct usb_cdc_ether_desc *)buf; break; case USB_CDC_NCM_TYPE: if (buf[0] < sizeof(*(ctx->func_desc))) break; ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf; break; default: break; } } temp = buf[0]; buf += temp; buflen -= temp; } /* check if we got everything */ if ((ctx->control == NULL) || (ctx->data == NULL) || (ctx->ether_desc == NULL) || (ctx->control != intf)) goto error; pipe_data->usbdev = usb_get_dev(usbdev); pipe_data->usb_ld = usb_ld; pipe_data->disconnected = 0; pipe_data->state = STATE_RESUMED; /* claim interfaces, if any */ temp = usb_driver_claim_interface(usbdrv, ctx->data, pipe_data); if (temp) goto error; iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber; /* reset data interface */ temp = usb_set_interface(usbdev, iface_no, 0); if (temp) goto error2; /* initialize data interface */ if (cdc_ncm_setup(pipe_data)) goto error2; /* configure data interface */ temp = usb_set_interface(usbdev, iface_no, 1); if (temp) goto error2; cdc_ncm_find_endpoints(ctx, ctx->data); cdc_ncm_find_endpoints(ctx, ctx->control); if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) || (ctx->status_ep == NULL)) goto error2; usb_set_intfdata(ctx->data, pipe_data); usb_set_intfdata(ctx->control, pipe_data); usb_set_intfdata(ctx->intf, pipe_data); pipe_data->rx_pipe = usb_rcvbulkpipe(usbdev, ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); pipe_data->tx_pipe = usb_sndbulkpipe(usbdev, ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); pipe_data->status = ctx->status_ep; pipe_data->rx_buf_size = ctx->rx_max; mif_debug("EP status: %x, tx:%x, rx:%x\n", ctx->status_ep->desc.bEndpointAddress, ctx->in_ep->desc.bEndpointAddress, ctx->out_ep->desc.bEndpointAddress); temp = cdc_ncm_setup_ethernet_address(pipe_data); if (temp) goto error2; /* * We should get an event when network connection is "connected" or * "disconnected". Set network connection in "disconnected" state * (carrier is OFF) during attach, so the IP network stack does not * start IPv6 negotiation and more. */ netif_carrier_off(pipe_data->iod->ndev); ctx->tx_speed = ctx->rx_speed = 0; if (pipe_data->iod->ndev->mtu != (ctx->max_datagram_size - ETH_HLEN)) pipe_data->iod->ndev->mtu = ctx->max_datagram_size - ETH_HLEN; return 0; error2: usb_set_intfdata(ctx->control, NULL); usb_set_intfdata(ctx->data, NULL); usb_driver_release_interface(usbdrv, ctx->data); error: cdc_ncm_free((struct cdc_ncm_ctx *)pipe_data->sedata); pipe_data->sedata = NULL; mif_err("bind() failure\n"); return -ENODEV; }