/* Some devices combine the "control" and "data" functions into a * single interface with all three endpoints: interrupt + bulk in and * out */ static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) { struct qmi_wwan_state *info = (void *)&dev->data; /* control and data is shared */ info->control = intf; info->data = intf; return qmi_wwan_register_subdriver(dev); }
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; struct usb_cdc_union_desc *cdc_union = NULL; struct usb_cdc_ether_desc *cdc_ether = NULL; u32 found = 0; struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); /* set up initial state */ info->control = intf; info->data = intf; /* and a number of CDC descriptors */ while (len > 3) { struct usb_descriptor_header *h = (void *)buf; /* ignore any misplaced descriptors */ if (h->bDescriptorType != USB_DT_CS_INTERFACE) goto next_desc; /* buf[2] is CDC descriptor subtype */ switch (buf[2]) { case USB_CDC_HEADER_TYPE: if (found & 1 << USB_CDC_HEADER_TYPE) { dev_dbg(&intf->dev, "extra CDC header\n"); goto err; } if (h->bLength != sizeof(struct usb_cdc_header_desc)) { dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength); goto err; } break; case USB_CDC_UNION_TYPE: if (found & 1 << USB_CDC_UNION_TYPE) { dev_dbg(&intf->dev, "extra CDC union\n"); goto err; } if (h->bLength != sizeof(struct usb_cdc_union_desc)) { dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength); goto err; } cdc_union = (struct usb_cdc_union_desc *)buf; break; case USB_CDC_ETHERNET_TYPE: if (found & 1 << USB_CDC_ETHERNET_TYPE) { dev_dbg(&intf->dev, "extra CDC ether\n"); goto err; } if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength); goto err; } cdc_ether = (struct usb_cdc_ether_desc *)buf; break; } /* Remember which CDC functional descriptors we've seen. Works * for all types we care about, of which USB_CDC_ETHERNET_TYPE * (0x0f) is the highest numbered */ if (buf[2] < 32) found |= 1 << buf[2]; next_desc: len -= h->bLength; buf += h->bLength; } /* Use separate control and data interfaces if we found a CDC Union */ if (cdc_union) { info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) { dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n", cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); goto err; } } /* errors aren't fatal - we can live with the dynamic address */ if (cdc_ether) { dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); } /* claim data interface and set it up */ if (info->control != info->data) { status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) goto err; } status = qmi_wwan_register_subdriver(dev); if (status < 0 && info->control != info->data) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); } /* Never use the same address on both ends of the link, even if the * buggy firmware told us to. Or, if device is assigned the well-known * buggy firmware MAC address, replace it with a random address, */ if (ether_addr_equal(dev->net->dev_addr, default_modem_addr) || ether_addr_equal(dev->net->dev_addr, buggy_fw_addr)) eth_hw_addr_random(dev->net); /* make MAC addr easily distinguishable from an IP header */ if (possibly_iphdr(dev->net->dev_addr)) { dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ } dev->net->netdev_ops = &qmi_wwan_netdev_ops; err: return status; }
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; struct usb_cdc_union_desc *cdc_union; struct usb_cdc_ether_desc *cdc_ether; struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; struct usb_cdc_parsed_header hdr; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); /* set up initial state */ info->control = intf; info->data = intf; /* and a number of CDC descriptors */ cdc_parse_cdc_header(&hdr, intf, buf, len); cdc_union = hdr.usb_cdc_union_desc; cdc_ether = hdr.usb_cdc_ether_desc; /* Use separate control and data interfaces if we found a CDC Union */ if (cdc_union) { info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) { dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n", cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); goto err; } } /* errors aren't fatal - we can live with the dynamic address */ if (cdc_ether) { dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); } /* claim data interface and set it up */ if (info->control != info->data) { status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) goto err; } status = qmi_wwan_register_subdriver(dev); if (status < 0 && info->control != info->data) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); } /* Never use the same address on both ends of the link, even if the * buggy firmware told us to. Or, if device is assigned the well-known * buggy firmware MAC address, replace it with a random address, */ if (ether_addr_equal(dev->net->dev_addr, default_modem_addr) || ether_addr_equal(dev->net->dev_addr, buggy_fw_addr)) eth_hw_addr_random(dev->net); /* make MAC addr easily distinguishable from an IP header */ if (possibly_iphdr(dev->net->dev_addr)) { dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ } dev->net->netdev_ops = &qmi_wwan_netdev_ops; err: return status; }
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; struct usb_cdc_union_desc *cdc_union = NULL; struct usb_cdc_ether_desc *cdc_ether = NULL; u32 found = 0; struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); /* control and data is shared? */ if (intf->cur_altsetting->desc.bNumEndpoints == 3) { info->control = intf; info->data = intf; goto shared; } /* else require a single interrupt status endpoint on control intf */ if (intf->cur_altsetting->desc.bNumEndpoints != 1) goto err; /* and a number of CDC descriptors */ while (len > 3) { struct usb_descriptor_header *h = (void *)buf; /* ignore any misplaced descriptors */ if (h->bDescriptorType != USB_DT_CS_INTERFACE) goto next_desc; /* buf[2] is CDC descriptor subtype */ switch (buf[2]) { case USB_CDC_HEADER_TYPE: if (found & 1 << USB_CDC_HEADER_TYPE) { dev_dbg(&intf->dev, "extra CDC header\n"); goto err; } if (h->bLength != sizeof(struct usb_cdc_header_desc)) { dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength); goto err; } break; case USB_CDC_UNION_TYPE: if (found & 1 << USB_CDC_UNION_TYPE) { dev_dbg(&intf->dev, "extra CDC union\n"); goto err; } if (h->bLength != sizeof(struct usb_cdc_union_desc)) { dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength); goto err; } cdc_union = (struct usb_cdc_union_desc *)buf; break; case USB_CDC_ETHERNET_TYPE: if (found & 1 << USB_CDC_ETHERNET_TYPE) { dev_dbg(&intf->dev, "extra CDC ether\n"); goto err; } if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength); goto err; } cdc_ether = (struct usb_cdc_ether_desc *)buf; break; } /* * Remember which CDC functional descriptors we've seen. Works * for all types we care about, of which USB_CDC_ETHERNET_TYPE * (0x0f) is the highest numbered */ if (buf[2] < 32) found |= 1 << buf[2]; next_desc: len -= h->bLength; buf += h->bLength; } /* did we find all the required ones? */ if (!(found & (1 << USB_CDC_HEADER_TYPE)) || !(found & (1 << USB_CDC_UNION_TYPE))) { dev_err(&intf->dev, "CDC functional descriptors missing\n"); goto err; } /* verify CDC Union */ if (desc->bInterfaceNumber != cdc_union->bMasterInterface0) { dev_err(&intf->dev, "bogus CDC Union: master=%u\n", cdc_union->bMasterInterface0); goto err; } /* need to save these for unbind */ info->control = intf; info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); if (!info->data) { dev_err(&intf->dev, "bogus CDC Union: slave=%u\n", cdc_union->bSlaveInterface0); goto err; } /* errors aren't fatal - we can live with the dynamic address */ if (cdc_ether) { dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); } /* claim data interface and set it up */ status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) goto err; shared: status = qmi_wwan_register_subdriver(dev); if (status < 0 && info->control != info->data) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); } err: return status; }
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; struct usb_cdc_union_desc *cdc_union; struct usb_cdc_ether_desc *cdc_ether; struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; struct usb_cdc_parsed_header hdr; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); /* set up initial state */ info->control = intf; info->data = intf; /* and a number of CDC descriptors */ cdc_parse_cdc_header(&hdr, intf, buf, len); cdc_union = hdr.usb_cdc_union_desc; cdc_ether = hdr.usb_cdc_ether_desc; /* Use separate control and data interfaces if we found a CDC Union */ if (cdc_union) { info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) { dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n", cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); /* ignore and continue... */ cdc_union = NULL; info->data = intf; } } /* errors aren't fatal - we can live with the dynamic address */ if (cdc_ether) { dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); } /* claim data interface and set it up */ if (info->control != info->data) { status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) goto err; } status = qmi_wwan_register_subdriver(dev); if (status < 0 && info->control != info->data) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); } /* disabling remote wakeup on MDM9x30 devices has the same * effect as clearing DTR. The device will not respond to QMI * requests until we set DTR again. This is similar to a * QMI_CTL SYNC request, clearing a lot of firmware state * including the client ID allocations. * * Our usage model allows a session to span multiple * open/close events, so we must prevent the firmware from * clearing out state the clients might need. * * MDM9x30 is the first QMI chipset with USB3 support. Abuse * this fact to enable the quirk. */ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { qmi_wwan_manage_power(dev, 1); qmi_wwan_change_dtr(dev, true); } /* Never use the same address on both ends of the link, even if the * buggy firmware told us to. Or, if device is assigned the well-known * buggy firmware MAC address, replace it with a random address, */ if (ether_addr_equal(dev->net->dev_addr, default_modem_addr) || ether_addr_equal(dev->net->dev_addr, buggy_fw_addr)) eth_hw_addr_random(dev->net); /* make MAC addr easily distinguishable from an IP header */ if (possibly_iphdr(dev->net->dev_addr)) { dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ } dev->net->netdev_ops = &qmi_wwan_netdev_ops; dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group; err: return status; }