/* disallow addresses which may be confused with IP headers */ static int qmi_wwan_mac_addr(struct net_device *dev, void *p) { struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data) || possibly_iphdr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); return 0; }
/* disallow addresses which may be confused with IP headers */ static int qmi_wwan_mac_addr(struct net_device *dev, void *p) { int ret; struct sockaddr *addr = p; ret = eth_prepare_mac_addr_change(dev, p); if (ret < 0) return ret; if (possibly_iphdr(addr->sa_data)) return -EADDRNOTAVAIL; eth_commit_mac_addr_change(dev, p); return 0; }
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; 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; }
/*=========================================================================== METHOD: GobiNetDriverBind (Public Method) DESCRIPTION: Setup in and out pipes PARAMETERS pDev [ I ] - Pointer to usbnet device pIntf [ I ] - Pointer to interface RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int GobiNetDriverBind( struct usbnet * pDev, struct usb_interface * pIntf ) { int numEndpoints; int endpointIndex; struct usb_host_endpoint * pEndpoint = NULL; struct usb_host_endpoint * pIn = NULL; struct usb_host_endpoint * pOut = NULL; // Verify one altsetting if (pIntf->num_altsetting != 1) { DBG( "invalid num_altsetting %u\n", pIntf->num_altsetting ); return -ENODEV; } /* We only accept certain interfaces */ if (pIntf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC ) { DBG( "Ignoring non vendor class interface #%d\n", pIntf->cur_altsetting->desc.bInterfaceNumber ); return -ENODEV; } else if (pDev->driver_info->data && !test_bit(pIntf->cur_altsetting->desc.bInterfaceNumber, &pDev->driver_info->data)) { DBG( "invalid interface %d\n", pIntf->cur_altsetting->desc.bInterfaceNumber ); return -ENODEV; } // Collect In and Out endpoints numEndpoints = pIntf->cur_altsetting->desc.bNumEndpoints; for (endpointIndex = 0; endpointIndex < numEndpoints; endpointIndex++) { pEndpoint = pIntf->cur_altsetting->endpoint + endpointIndex; if (pEndpoint == NULL) { DBG( "invalid endpoint %u\n", endpointIndex ); return -ENODEV; } if (usb_endpoint_dir_in( &pEndpoint->desc ) == true && usb_endpoint_xfer_int( &pEndpoint->desc ) == false) { pIn = pEndpoint; } else if (usb_endpoint_dir_out( &pEndpoint->desc ) == true) { pOut = pEndpoint; } } if (pIn == NULL || pOut == NULL) { DBG( "invalid endpoints\n" ); return -ENODEV; } if (usb_set_interface( pDev->udev, pIntf->cur_altsetting->desc.bInterfaceNumber, 0 ) != 0) { DBG( "unable to set interface\n" ); return -ENODEV; } pDev->in = usb_rcvbulkpipe( pDev->udev, pIn->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ); pDev->out = usb_sndbulkpipe( pDev->udev, pOut->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ); DBG( "in %x, out %x\n", pIn->desc.bEndpointAddress, pOut->desc.bEndpointAddress ); // In later versions of the kernel, usbnet helps with this #if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,23 )) pIntf->dev.platform_data = (void *)pDev; #endif /* make MAC addr easily distinguishable from an IP header */ if (possibly_iphdr(pDev->net->dev_addr)) { pDev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ pDev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ } return 0; }