void cdc_ncm_unbind(struct if_usb_devdata *pipe_data, struct usb_interface *intf) { struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)pipe_data->sedata; struct usb_driver *usbdrv = driver_of(intf); if (ctx == NULL) return; /* no setup */ netif_carrier_off(pipe_data->iod->ndev); atomic_set(&ctx->stop, 1); if (hrtimer_active(&ctx->tx_timer)) hrtimer_cancel(&ctx->tx_timer); tasklet_kill(&ctx->bh); /* disconnect master --> disconnect slave */ if (intf == ctx->control && ctx->data) { usb_set_intfdata(ctx->data, NULL); usb_driver_release_interface(usbdrv, ctx->data); ctx->data = NULL; } else if (intf == ctx->data && ctx->control) { usb_set_intfdata(ctx->control, NULL); usb_driver_release_interface(usbdrv, ctx->control); ctx->control = NULL; } pipe_data->usbdev = NULL; pipe_data->disconnected = 1; pipe_data->state = STATE_SUSPENDED; usb_set_intfdata(ctx->intf, NULL); cdc_ncm_free(ctx); pipe_data->sedata = NULL; }
static void __devexit cnxthwusb_disconnect(struct usb_interface *intf) #endif { #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) POS_DEVNODE pDevNode = ptr; #else POS_DEVNODE pDevNode = usb_get_intfdata(intf); #endif PUSBOSHAL pUsbOsHal; dbg("%s: %p", __FUNCTION__, pDevNode); if(!pDevNode) return; pUsbOsHal = pDevNode->hwDev; pDevNode->hwDev = NULL; #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) usb_set_intfdata (intf, NULL); #endif if(!pUsbOsHal) return; #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) usb_set_intfdata ((intf == pUsbOsHal->pUsbCommInterface) ? pUsbOsHal->pUsbDataInterface : pUsbOsHal->pUsbCommInterface, NULL); #endif pUsbOsHal->bActive = FALSE; cnxt_serial_remove(pDevNode); OsUsbFreeUrbs(pUsbOsHal); #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) usb_driver_release_interface(&cnxthwusb_driver, pUsbOsHal->pUsbCommInterface); #else // XXX needed on older 2.6 kernels ? usb_driver_release_interface(&cnxthwusb_driver, (intf == pUsbOsHal->pUsbCommInterface) ? pUsbOsHal->pUsbDataInterface : pUsbOsHal->pUsbCommInterface); #endif OsEventDestroy(pUsbOsHal->ControlRequestEvent); kfree(pUsbOsHal); pUsbOsHal = NULL; kfree(pDevNode); }
static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); struct tty_struct *tty; /* sibling interface is already cleaning up */ if (!acm) return; mutex_lock(&open_mutex); if (acm->country_codes) { device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); } device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); acm->dev = NULL; usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); stop_data_traffic(acm); usb_kill_anchored_urbs(&acm->deferred); acm_write_buffers_free(acm); usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); acm_read_buffers_free(acm); if (!acm->combined_interfaces) usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : acm->control); if (acm->port.count == 0) { acm_tty_unregister(acm); mutex_unlock(&open_mutex); return; } mutex_unlock(&open_mutex); tty = tty_port_tty_get(&acm->port); if (tty) { tty_hangup(tty); tty_kref_put(tty); } }
static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); int i; if (!acm || !acm->dev) { dbg("disconnect on nonexisting interface"); return; } mutex_lock(&open_mutex); if (!usb_get_intfdata(intf)) { mutex_unlock(&open_mutex); return; } if (acm->country_codes){ device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); } device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); acm->dev = NULL; usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); stop_data_traffic(acm); acm_write_buffers_free(acm); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); for (i = 0; i < acm->rx_buflimit; i++) usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf); if (!acm->used) { acm_tty_unregister(acm); mutex_unlock(&open_mutex); return; } mutex_unlock(&open_mutex); if (acm->tty) tty_hangup(acm->tty); }
static void xusbatm_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) { struct usb_device *usb_dev = interface_to_usbdev(intf); int i; usb_dbg(usbatm, "%s entered\n", __func__); for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *cur_intf = usb_dev->actconfig->interface[i]; if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) { usb_set_intfdata(cur_intf, NULL); usb_driver_release_interface(&xusbatm_usb_driver, cur_intf); } } }
static void free_usb_related_resources(struct ua101 *ua, struct usb_interface *interface) { unsigned int i; free_stream_urbs(&ua->capture); free_stream_urbs(&ua->playback); free_stream_buffers(ua, &ua->capture); free_stream_buffers(ua, &ua->playback); for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) if (ua->intf[i]) { usb_set_intfdata(ua->intf[i], NULL); if (ua->intf[i] != interface) usb_driver_release_interface(&ua101_driver, ua->intf[i]); } }
/* * Disconnect USB device (maybe the MDC800) */ static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr) { struct mdc800_data* mdc800=(struct mdc800_data*) ptr; dbg ("(mdc800_usb_disconnect) called"); if (mdc800->state == NOT_CONNECTED) return; mdc800->state=NOT_CONNECTED; usb_unlink_urb (mdc800->irq_urb); usb_unlink_urb (mdc800->write_urb); usb_unlink_urb (mdc800->download_urb); usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]); mdc800->dev=0; info ("Mustek MDC800 disconnected from USB."); }
static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); /* sibling interface is already cleaning up */ if (!acm) return; mutex_lock(&open_mutex); if (acm->country_codes){ device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); } device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); acm->dev = NULL; usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); stop_data_traffic(acm); acm_write_buffers_free(acm); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); acm_read_buffers_free(acm); usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : acm->control); if (!acm->used) { acm_tty_unregister(acm); mutex_unlock(&open_mutex); return; } mutex_unlock(&open_mutex); if (acm->tty) tty_hangup(acm->tty); }
static void usbsvn_disconnect(struct usb_interface *intf) { struct usbsvn *svn = usb_get_intfdata(intf); struct usb_device *usbdev = svn->usbdev; int dev_id = intf->altsetting->desc.bInterfaceNumber / 2; struct device *ppdev; if (svn->devdata[dev_id].disconnected) return; svn->usbsvn_connected = 0; svn->flow_suspend = 1; dev_err(&usbdev->dev, "%s\n", __func__); svn->dev_count--; svn->devdata[dev_id].disconnected = 1; usb_driver_release_interface(&usbsvn_driver, svn->devdata[dev_id].data_intf); ppdev = usbdev->dev.parent->parent; pm_runtime_forbid(ppdev); /*ehci*/ usb_put_dev(usbdev); if (svn->dev_count == 0) { svn->usbdev = NULL; cancel_delayed_work_sync(&svn->pm_runtime_work); cancel_work_sync(&svn->post_resume_work); if (!svn->driver_info) { /*TODO:check the Phone ACTIVE pin*/ #ifdef CONFIG_SAMSUNG_PHONE_SVNET if (mc_is_modem_active()) { svn->reconnect_cnt = 2; schedule_delayed_work(&svn->try_reconnect_work, 10); } #endif wake_unlock_pm(svn); } } }
int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf) { int status; struct cdc_state *info = (void *) &dev->data; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct cdc_state))); status = usbnet_generic_cdc_bind(dev, intf); if (status < 0) return status; status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress); if (status < 0) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); return status; } return 0; }
static void if_usb_disconnect(struct usb_interface *intf) { struct usb_link_device *usb_ld = usb_get_intfdata(intf); struct usb_device *usbdev = usb_ld->usbdev; struct link_pm_data *pm_data = usb_ld->link_pm_data; int dev_id = intf->altsetting->desc.bInterfaceNumber; struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id]; usb_set_intfdata(intf, NULL); pipe_data->disconnected = 1; smp_wmb(); wake_up(&usb_ld->l2_wait); usb_ld->if_usb_connected = 0; usb_ld->flow_suspend = 1; dev_dbg(&usbdev->dev, "%s\n", __func__); usb_ld->dev_count--; usb_driver_release_interface(&if_usb_driver, pipe_data->data_intf); usb_kill_anchored_urbs(&pipe_data->reading); usb_free_urbs(usb_ld, pipe_data); if (usb_ld->dev_count == 0) { cancel_delayed_work_sync(&usb_ld->runtime_pm_work); cancel_delayed_work_sync(&usb_ld->post_resume_work); cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work); usb_put_dev(usbdev); usb_ld->usbdev = NULL; if (!has_hub(usb_ld)) { if (pm_data->root_hub) pm_runtime_forbid(pm_data->root_hub); schedule_delayed_work(&usb_ld->wait_enumeration, WAIT_ENUMURATION_TIMEOUT_JIFFIES); } } }
static void if_usb_disconnect(struct usb_interface *intf) { struct usb_link_device *usb_ld = usb_get_intfdata(intf); struct usb_device *usbdev = usb_ld->usbdev; int dev_id = intf->altsetting->desc.bInterfaceNumber; struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id]; usb_set_intfdata(intf, NULL); pipe_data->disconnected = 1; smp_wmb(); wake_up(&usb_ld->l2_wait); if (wakelock_held) { wakelock_held = 0; wake_unlock(&usb_ld->susplock); } usb_ld->if_usb_connected = 0; usb_ld->flow_suspend = 1; dev_dbg(&usbdev->dev, "%s\n", __func__); usb_ld->dev_count--; usb_driver_release_interface(&if_usb_driver, pipe_data->data_intf); usb_kill_anchored_urbs(&pipe_data->reading); usb_free_urbs(usb_ld, pipe_data); if (usb_ld->dev_count == 0) { cancel_delayed_work_sync(&usb_ld->runtime_pm_work); cancel_delayed_work_sync(&usb_ld->post_resume_work); cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work); cancel_work_sync(&usb_ld->disconnect_work); usb_free_deferred_urbs(usb_ld); /*wake_unlock(&usb_ld->susplock);*/ usb_put_dev(usbdev); usb_ld->usbdev = NULL; } }
static int cdc_bind(struct usbnet *dev, struct usb_interface *intf) { int status; struct cdc_state *info = (void *) &dev->data; status = usbnet_generic_cdc_bind(dev, intf); if (status < 0) return status; status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress); if (status < 0) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); return status; } /* FIXME cdc-ether has some multicast code too, though it complains * in routine cases. info->ether describes the multicast support. * Implement that here, manipulating the cdc filter as needed. */ return 0; }
int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) { u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *d; struct cdc_state *info = (void *) &dev->data; int status; int rndis; bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); struct usb_cdc_mdlm_desc *desc = NULL; struct usb_cdc_mdlm_detail_desc *detail = NULL; if (sizeof dev->data < sizeof *info) return -EDOM; if (len == 0 && dev->udev->actconfig->extralen) { buf = dev->udev->actconfig->extra; len = dev->udev->actconfig->extralen; dev_dbg(&intf->dev, "CDC descriptors on config\n"); } if (len == 0) { struct usb_host_endpoint *hep; hep = intf->cur_altsetting->endpoint; if (hep) { buf = hep->extra; len = hep->extralen; } if (len) dev_dbg(&intf->dev, "CDC descriptors on endpoint\n"); } rndis = (is_rndis(&intf->cur_altsetting->desc) || is_activesync(&intf->cur_altsetting->desc) || is_wireless_rndis(&intf->cur_altsetting->desc)); memset(info, 0, sizeof *info); info->control = intf; while (len > 3) { if (buf [1] != USB_DT_CS_INTERFACE) goto next_desc; switch (buf [2]) { case USB_CDC_HEADER_TYPE: if (info->header) { dev_dbg(&intf->dev, "extra CDC header\n"); goto bad_desc; } info->header = (void *) buf; if (info->header->bLength != sizeof *info->header) { dev_dbg(&intf->dev, "CDC header len %u\n", info->header->bLength); goto bad_desc; } break; case USB_CDC_ACM_TYPE: if (rndis) { struct usb_cdc_acm_descriptor *acm; acm = (void *) buf; if (acm->bmCapabilities) { dev_dbg(&intf->dev, "ACM capabilities %02x, " "not really RNDIS?\n", acm->bmCapabilities); goto bad_desc; } } break; case USB_CDC_UNION_TYPE: if (info->u) { dev_dbg(&intf->dev, "extra CDC union\n"); goto bad_desc; } info->u = (void *) buf; if (info->u->bLength != sizeof *info->u) { dev_dbg(&intf->dev, "CDC union len %u\n", info->u->bLength); goto bad_desc; } info->control = usb_ifnum_to_if(dev->udev, info->u->bMasterInterface0); info->data = usb_ifnum_to_if(dev->udev, info->u->bSlaveInterface0); if (!info->control || !info->data) { dev_dbg(&intf->dev, "master #%u/%p slave #%u/%p\n", info->u->bMasterInterface0, info->control, info->u->bSlaveInterface0, info->data); if (rndis) { android_rndis_quirk = true; goto next_desc; } goto bad_desc; } if (info->control != intf) { dev_dbg(&intf->dev, "bogus CDC Union\n"); if (info->data == intf) { info->data = info->control; info->control = intf; } else goto bad_desc; } d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { dev_dbg(&intf->dev, "slave class %u\n", d->bInterfaceClass); goto bad_desc; } break; case USB_CDC_ETHERNET_TYPE: if (info->ether) { dev_dbg(&intf->dev, "extra CDC ether\n"); goto bad_desc; } info->ether = (void *) buf; if (info->ether->bLength != sizeof *info->ether) { dev_dbg(&intf->dev, "CDC ether len %u\n", info->ether->bLength); goto bad_desc; } dev->hard_mtu = le16_to_cpu( info->ether->wMaxSegmentSize); break; case USB_CDC_MDLM_TYPE: if (desc) { dev_dbg(&intf->dev, "extra MDLM descriptor\n"); goto bad_desc; } desc = (void *)buf; if (desc->bLength != sizeof(*desc)) goto bad_desc; if (memcmp(&desc->bGUID, mbm_guid, 16)) goto bad_desc; break; case USB_CDC_MDLM_DETAIL_TYPE: if (detail) { dev_dbg(&intf->dev, "extra MDLM detail descriptor\n"); goto bad_desc; } detail = (void *)buf; if (detail->bGuidDescriptorType == 0) { if (detail->bLength < (sizeof(*detail) + 1)) goto bad_desc; } else goto bad_desc; break; } next_desc: len -= buf [0]; buf += buf [0]; } if (rndis && (!info->u || android_rndis_quirk)) { info->control = usb_ifnum_to_if(dev->udev, 0); info->data = usb_ifnum_to_if(dev->udev, 1); if (!info->control || !info->data || info->control != intf) { dev_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n", info->control, info->data); goto bad_desc; } } else if (!info->header || !info->u || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", info->ether ? "" : "ether "); goto bad_desc; } status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) return status; status = usbnet_get_endpoints(dev, info->data); if (status < 0) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return status; } dev->status = NULL; if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { struct usb_endpoint_descriptor *desc; dev->status = &info->control->cur_altsetting->endpoint [0]; desc = &dev->status->desc; if (!usb_endpoint_is_int_in(desc) || (le16_to_cpu(desc->wMaxPacketSize) < sizeof(struct usb_cdc_notification)) || !desc->bInterval) { dev_dbg(&intf->dev, "bad notification endpoint\n"); dev->status = NULL; } } if (rndis && !dev->status) { dev_dbg(&intf->dev, "missing RNDIS status endpoint\n"); usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return -ENODEV; } return 0; bad_desc: dev_info(&dev->udev->dev, "bad CDC descriptors\n"); return -ENODEV; }
int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) { int retval; struct net_device *net = dev->net; struct cdc_state *info = (void *) &dev->data; union { void *buf; struct rndis_msg_hdr *header; struct rndis_init *init; struct rndis_init_c *init_c; struct rndis_query *get; struct rndis_query_c *get_c; struct rndis_set *set; struct rndis_set_c *set_c; struct rndis_halt *halt; } u; u32 tmp; __le32 phym_unspec, *phym; int reply_len; unsigned char *bp; /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (!u.buf) return -ENOMEM; retval = usbnet_generic_cdc_bind(dev, intf); if (retval < 0) goto fail; u.init->msg_type = cpu_to_le32(RNDIS_MSG_INIT); u.init->msg_len = cpu_to_le32(sizeof *u.init); u.init->major_version = cpu_to_le32(1); u.init->minor_version = cpu_to_le32(0); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) /* can't we remove this? */ net->change_mtu = NULL; #endif /* max transfer (in spec) is 0x4000 at full speed, but for * TX we'll stick to one Ethernet packet plus RNDIS framing. * For RX we handle drivers that zero-pad to end-of-packet. * Don't let userspace change these settings. * * NOTE: there still seems to be wierdness here, as if we need * to do some more things to make sure WinCE targets accept this. * They default to jumbograms of 8KB or 16KB, which is absurd * for such low data rates and which is also more than Linux * can usually expect to allocate for SKB data... */ net->hard_header_len += sizeof (struct rndis_data_hdr); dev->hard_mtu = net->mtu + net->hard_header_len; dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1); if (dev->maxpacket == 0) { netif_dbg(dev, probe, dev->net, "dev->maxpacket can't be 0\n"); retval = -EINVAL; goto fail_and_release; } dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); dev->rx_urb_size &= ~(dev->maxpacket - 1); u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); netdev_attach_ops(net, &rndis_netdev_ops); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); goto fail_and_release; } tmp = le32_to_cpu(u.init_c->max_transfer_size); if (tmp < dev->hard_mtu) { if (tmp <= net->hard_header_len) { dev_err(&intf->dev, "dev can't take %u byte packets (max %u)\n", dev->hard_mtu, tmp); retval = -EINVAL; goto halt_fail_and_release; } dev_warn(&intf->dev, "dev can't take %u byte packets (max %u), " "adjusting MTU to %u\n", dev->hard_mtu, tmp, tmp - net->hard_header_len); dev->hard_mtu = tmp; net->mtu = dev->hard_mtu - net->hard_header_len; } /* REVISIT: peripheral "alignment" request is ignored ... */ dev_dbg(&intf->dev, "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); /* module has some device initialization code needs to be done right * after RNDIS_INIT */ if (dev->driver_info->early_init && dev->driver_info->early_init(dev) != 0) goto halt_fail_and_release; /* Check physical medium */ phym = NULL; reply_len = sizeof *phym; retval = rndis_query(dev, intf, u.buf, RNDIS_OID_GEN_PHYSICAL_MEDIUM, 0, (void **) &phym, &reply_len); if (retval != 0 || !phym) { /* OID is optional so don't fail here. */ phym_unspec = cpu_to_le32(RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED); phym = &phym_unspec; } if ((flags & FLAG_RNDIS_PHYM_WIRELESS) && le32_to_cpup(phym) != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { netif_dbg(dev, probe, dev->net, "driver requires wireless physical medium, but device is not\n"); retval = -ENODEV; goto halt_fail_and_release; } if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) && le32_to_cpup(phym) == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { netif_dbg(dev, probe, dev->net, "driver requires non-wireless physical medium, but device is wireless.\n"); retval = -ENODEV; goto halt_fail_and_release; } /* Get designated host ethernet address */ reply_len = ETH_ALEN; retval = rndis_query(dev, intf, u.buf, RNDIS_OID_802_3_PERMANENT_ADDRESS, 48, (void **) &bp, &reply_len); if (unlikely(retval< 0)) { dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); goto halt_fail_and_release; } memcpy(net->dev_addr, bp, ETH_ALEN); memcpy(net->perm_addr, bp, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); u.set->oid = cpu_to_le32(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); u.set->len = cpu_to_le32(4); u.set->offset = cpu_to_le32((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = cpu_to_le32(RNDIS_DEFAULT_FILTER); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto halt_fail_and_release; } retval = 0; kfree(u.buf); return retval; halt_fail_and_release: memset(u.halt, 0, sizeof *u.halt); u.halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); u.halt->msg_len = cpu_to_le32(sizeof *u.halt); (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); info->data = NULL; fail: kfree(u.buf); return retval; }
static void smdhsic_disconnect(struct usb_interface *intf) { int devid; struct usb_interface *smd_intf; struct str_intf_priv *intfpriv; struct usb_device *device = NULL; pr_info("%s: Called\n", __func__); intfpriv = usb_get_intfdata(intf); if (!intfpriv) { pr_err("%s: intfpriv is NULL\n", __func__); goto err_get_intfdata; } device = get_usb_device(intfpriv); devid = GET_DEVID(intfpriv->devid); pr_debug("%s : devid : %d\n", __func__, devid); smd_intf = get_usb_intf(intfpriv); if (!smd_intf) { pr_err("smd_intf is NULL\n"); goto err_get_usb_intf; } if (smd_intf != intf) { pr_err("smd_intf is not same intf\n"); goto err_mismatched_intf; } usb_driver_release_interface(get_usb_driver(intf), smd_intf); if (!device) usb_put_dev(device); switch (devid) { case FMT_DEV_ID: pm_runtime_disable(&device->dev); if (g_usbdev.hsic) cancel_delayed_work(&g_usbdev.hsic->pm_runtime_work); smdctl_request_connection_recover(true); case RAW_DEV_ID: case RFS_DEV_ID: case CMD_DEV_ID: case DOWN_DEV_ID: if (emu_discon_func[devid]) emu_discon_func[devid](g_usbdev.smd_device[devid]); else kfree(intfpriv->data); break; default: pr_warn("%s:Undefined Callback Function\n", __func__); } /* Power on/off kernel-panic workaround, * if USB suspend cmd was queued in power.work before disconnect, * reset the runtime PM request value to PM_REQ_NONE */ device->dev.power.request = RPM_REQ_NONE; kfree(intfpriv); usb_set_intfdata(intf, NULL); g_usbdev.usbdev = NULL; g_usbdev.suspended = 0; g_usbdev.hsic = NULL; return; err_mismatched_intf: err_get_usb_intf: if (device) usb_put_dev(device); err_get_intfdata: pr_err("release(2) : %p\n", intf); usb_driver_release_interface(get_usb_driver(intf), intf); return; }
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; }
static void smdhsic_disconnect(struct usb_interface *intf) { int devid; struct usb_interface *smd_intf; struct str_intf_priv *intfpriv; struct usb_device *device = NULL; pr_info("%s: Called\n", __func__); intfpriv = usb_get_intfdata(intf); if (!intfpriv) { pr_err("%s: intfpriv is NULL\n", __func__); goto err_get_intfdata; } device = get_usb_device(intfpriv); devid = GET_DEVID(intfpriv->devid); pr_debug("%s : devid : %d\n", __func__, devid); smd_intf = get_usb_intf(intfpriv); if (!smd_intf) { pr_err("smd_intf is NULL\n"); goto err_get_usb_intf; } if (smd_intf != intf) { pr_err("smd_intf is not same intf\n"); goto err_mismatched_intf; } usb_driver_release_interface(get_usb_driver(intf), smd_intf); if (!device) usb_put_dev(device); pm_runtime_disable(&device->dev); if (g_usbdev.hsic) cancel_delayed_work(&g_usbdev.hsic->pm_runtime_work); switch (devid) { case FMT_DEV_ID: flush_txurb(&g_usbdev.ipc_urbq); flush_txurb(&g_usbdev.data_urbq); smdctl_request_connection_recover(true); case RAW_DEV_ID: case RFS_DEV_ID: case CMD_DEV_ID: case DOWN_DEV_ID: if (emu_discon_func[devid]) emu_discon_func[devid](g_usbdev.smd_device[devid]); else kfree(intfpriv->data); break; default: pr_warn("%s:Undefined Callback Function\n", __func__); } /* to prevent sleep at connection recover * when, usb suspend and recover routine overlap * it makes huge delay on modem reset */ wake_lock_timeout(&g_usbdev.txwake, 20*HZ); kfree(intfpriv); usb_set_intfdata(intf, NULL); g_usbdev.usbdev = NULL; g_usbdev.suspended = 0; g_usbdev.hsic = NULL; return; err_mismatched_intf: err_get_usb_intf: if (!device) usb_put_dev(device); err_get_intfdata: pr_err("release(2) : %p\n", intf); usb_driver_release_interface(get_usb_driver(intf), intf); return; }
/* * probes control interface, claims data interface, collects the bulk * endpoints, activates data interface (if needed), maybe sets MTU. * all pure cdc, except for certain firmware workarounds, and knowing * that rndis uses one different rule. */ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) { u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *d; struct cdc_state *info = (void *) &dev->data; int status; int rndis; bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); struct usb_cdc_mdlm_desc *desc = NULL; struct usb_cdc_mdlm_detail_desc *detail = NULL; if (sizeof dev->data < sizeof *info) return -EDOM; /* expect strict spec conformance for the descriptors, but * cope with firmware which stores them in the wrong place */ if (len == 0 && dev->udev->actconfig->extralen) { /* Motorola SB4100 (and others: Brad Hards says it's * from a Broadcom design) put CDC descriptors here */ buf = dev->udev->actconfig->extra; len = dev->udev->actconfig->extralen; dev_dbg(&intf->dev, "CDC descriptors on config\n"); } /* Maybe CDC descriptors are after the endpoint? This bug has * been seen on some 2Wire Inc RNDIS-ish products. */ if (len == 0) { struct usb_host_endpoint *hep; hep = intf->cur_altsetting->endpoint; if (hep) { buf = hep->extra; len = hep->extralen; } if (len) dev_dbg(&intf->dev, "CDC descriptors on endpoint\n"); } /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ rndis = (is_rndis(&intf->cur_altsetting->desc) || is_activesync(&intf->cur_altsetting->desc) || is_wireless_rndis(&intf->cur_altsetting->desc)); memset(info, 0, sizeof *info); info->control = intf; while (len > 3) { if (buf [1] != USB_DT_CS_INTERFACE) goto next_desc; /* use bDescriptorSubType to identify the CDC descriptors. * We expect devices with CDC header and union descriptors. * For CDC Ethernet we need the ethernet descriptor. * For RNDIS, ignore two (pointless) CDC modem descriptors * in favor of a complicated OID-based RPC scheme doing what * CDC Ethernet achieves with a simple descriptor. */ switch (buf [2]) { case USB_CDC_HEADER_TYPE: if (info->header) { dev_dbg(&intf->dev, "extra CDC header\n"); goto bad_desc; } info->header = (void *) buf; if (info->header->bLength != sizeof *info->header) { dev_dbg(&intf->dev, "CDC header len %u\n", info->header->bLength); goto bad_desc; } break; case USB_CDC_ACM_TYPE: /* paranoia: disambiguate a "real" vendor-specific * modem interface from an RNDIS non-modem. */ if (rndis) { struct usb_cdc_acm_descriptor *acm; acm = (void *) buf; if (acm->bmCapabilities) { dev_dbg(&intf->dev, "ACM capabilities %02x, " "not really RNDIS?\n", acm->bmCapabilities); goto bad_desc; } } break; case USB_CDC_UNION_TYPE: if (info->u) { dev_dbg(&intf->dev, "extra CDC union\n"); goto bad_desc; } info->u = (void *) buf; if (info->u->bLength != sizeof *info->u) { dev_dbg(&intf->dev, "CDC union len %u\n", info->u->bLength); goto bad_desc; } /* we need a master/control interface (what we're * probed with) and a slave/data interface; union * descriptors sort this all out. */ info->control = usb_ifnum_to_if(dev->udev, info->u->bMasterInterface0); info->data = usb_ifnum_to_if(dev->udev, info->u->bSlaveInterface0); if (!info->control || !info->data) { dev_dbg(&intf->dev, "master #%u/%p slave #%u/%p\n", info->u->bMasterInterface0, info->control, info->u->bSlaveInterface0, info->data); /* fall back to hard-wiring for RNDIS */ if (rndis) { android_rndis_quirk = true; goto next_desc; } goto bad_desc; } if (info->control != intf) { dev_dbg(&intf->dev, "bogus CDC Union\n"); /* Ambit USB Cable Modem (and maybe others) * interchanges master and slave interface. */ if (info->data == intf) { info->data = info->control; info->control = intf; } else goto bad_desc; } /* a data interface altsetting does the real i/o */ d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { dev_dbg(&intf->dev, "slave class %u\n", d->bInterfaceClass); goto bad_desc; } break; case USB_CDC_ETHERNET_TYPE: if (info->ether) { dev_dbg(&intf->dev, "extra CDC ether\n"); goto bad_desc; } info->ether = (void *) buf; if (info->ether->bLength != sizeof *info->ether) { dev_dbg(&intf->dev, "CDC ether len %u\n", info->ether->bLength); goto bad_desc; } dev->hard_mtu = le16_to_cpu( info->ether->wMaxSegmentSize); /* because of Zaurus, we may be ignoring the host * side link address we were given. */ break; case USB_CDC_MDLM_TYPE: if (desc) { dev_dbg(&intf->dev, "extra MDLM descriptor\n"); goto bad_desc; } desc = (void *)buf; if (desc->bLength != sizeof(*desc)) goto bad_desc; if (memcmp(&desc->bGUID, mbm_guid, 16)) goto bad_desc; break; case USB_CDC_MDLM_DETAIL_TYPE: if (detail) { dev_dbg(&intf->dev, "extra MDLM detail descriptor\n"); goto bad_desc; } detail = (void *)buf; if (detail->bGuidDescriptorType == 0) { if (detail->bLength < (sizeof(*detail) + 1)) goto bad_desc; } else goto bad_desc; break; } next_desc: len -= buf [0]; /* bLength */ buf += buf [0]; } /* Microsoft ActiveSync based and some regular RNDIS devices lack the * CDC descriptors, so we'll hard-wire the interfaces and not check * for descriptors. * * Some Android RNDIS devices have a CDC Union descriptor pointing * to non-existing interfaces. Ignore that and attempt the same * hard-wired 0 and 1 interfaces. */ if (rndis && (!info->u || android_rndis_quirk)) { info->control = usb_ifnum_to_if(dev->udev, 0); info->data = usb_ifnum_to_if(dev->udev, 1); if (!info->control || !info->data || info->control != intf) { dev_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n", info->control, info->data); goto bad_desc; } } else if (!info->header || !info->u || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", info->ether ? "" : "ether "); goto bad_desc; } /* claim data interface and set it up ... with side effects. * network traffic can't flow until an altsetting is enabled. */ status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) return status; status = usbnet_get_endpoints(dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return status; } /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ dev->status = NULL; if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { struct usb_endpoint_descriptor *desc; dev->status = &info->control->cur_altsetting->endpoint [0]; desc = &dev->status->desc; if (!usb_endpoint_is_int_in(desc) || (le16_to_cpu(desc->wMaxPacketSize) < sizeof(struct usb_cdc_notification)) || !desc->bInterval) { dev_dbg(&intf->dev, "bad notification endpoint\n"); dev->status = NULL; } } if (rndis && !dev->status) { dev_dbg(&intf->dev, "missing RNDIS status endpoint\n"); usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return -ENODEV; } return 0; bad_desc: dev_info(&dev->udev->dev, "bad CDC descriptors\n"); return -ENODEV; }
/* probes control interface, claims data interface, collects the bulk * endpoints, activates data interface (if needed), maybe sets MTU. * all pure cdc, except for certain firmware workarounds, and knowing * that rndis uses one different rule. */ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) { u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *d; struct cdc_state *info = (void *) &dev->data; int status; int rndis; bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); struct usb_cdc_parsed_header header; if (sizeof(dev->data) < sizeof(*info)) return -EDOM; /* expect strict spec conformance for the descriptors, but * cope with firmware which stores them in the wrong place */ if (len == 0 && dev->udev->actconfig->extralen) { /* Motorola SB4100 (and others: Brad Hards says it's * from a Broadcom design) put CDC descriptors here */ buf = dev->udev->actconfig->extra; len = dev->udev->actconfig->extralen; dev_dbg(&intf->dev, "CDC descriptors on config\n"); } /* Maybe CDC descriptors are after the endpoint? This bug has * been seen on some 2Wire Inc RNDIS-ish products. */ if (len == 0) { struct usb_host_endpoint *hep; hep = intf->cur_altsetting->endpoint; if (hep) { buf = hep->extra; len = hep->extralen; } if (len) dev_dbg(&intf->dev, "CDC descriptors on endpoint\n"); } /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ rndis = (is_rndis(&intf->cur_altsetting->desc) || is_activesync(&intf->cur_altsetting->desc) || is_wireless_rndis(&intf->cur_altsetting->desc)); memset(info, 0, sizeof(*info)); info->control = intf; cdc_parse_cdc_header(&header, intf, buf, len); info->u = header.usb_cdc_union_desc; info->header = header.usb_cdc_header_desc; info->ether = header.usb_cdc_ether_desc; /* we need a master/control interface (what we're * probed with) and a slave/data interface; union * descriptors sort this all out. */ info->control = usb_ifnum_to_if(dev->udev, info->u->bMasterInterface0); info->data = usb_ifnum_to_if(dev->udev, info->u->bSlaveInterface0); if (!info->control || !info->data) { dev_dbg(&intf->dev, "master #%u/%p slave #%u/%p\n", info->u->bMasterInterface0, info->control, info->u->bSlaveInterface0, info->data); /* fall back to hard-wiring for RNDIS */ if (rndis) { android_rndis_quirk = true; goto skip; } goto bad_desc; } if (info->control != intf) { dev_dbg(&intf->dev, "bogus CDC Union\n"); /* Ambit USB Cable Modem (and maybe others) * interchanges master and slave interface. */ if (info->data == intf) { info->data = info->control; info->control = intf; } else goto bad_desc; } /* some devices merge these - skip class check */ if (info->control == info->data) goto skip; /* a data interface altsetting does the real i/o */ d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { dev_dbg(&intf->dev, "slave class %u\n", d->bInterfaceClass); goto bad_desc; } skip: if ( rndis && header.usb_cdc_acm_descriptor && header.usb_cdc_acm_descriptor->bmCapabilities) { dev_dbg(&intf->dev, "ACM capabilities %02x, not really RNDIS?\n", header.usb_cdc_acm_descriptor->bmCapabilities); goto bad_desc; } if (header.usb_cdc_ether_desc) { dev->hard_mtu = le16_to_cpu(info->ether->wMaxSegmentSize); /* because of Zaurus, we may be ignoring the host * side link address we were given. */ } if (header.usb_cdc_mdlm_desc && memcmp(header.usb_cdc_mdlm_desc->bGUID, mbm_guid, 16)) { dev_dbg(&intf->dev, "GUID doesn't match\n"); goto bad_desc; } if (header.usb_cdc_mdlm_detail_desc && header.usb_cdc_mdlm_detail_desc->bLength < (sizeof(struct usb_cdc_mdlm_detail_desc) + 1)) { dev_dbg(&intf->dev, "Descriptor too short\n"); goto bad_desc; } /* Microsoft ActiveSync based and some regular RNDIS devices lack the * CDC descriptors, so we'll hard-wire the interfaces and not check * for descriptors. * * Some Android RNDIS devices have a CDC Union descriptor pointing * to non-existing interfaces. Ignore that and attempt the same * hard-wired 0 and 1 interfaces. */ if (rndis && (!info->u || android_rndis_quirk)) { info->control = usb_ifnum_to_if(dev->udev, 0); info->data = usb_ifnum_to_if(dev->udev, 1); if (!info->control || !info->data || info->control != intf) { dev_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n", info->control, info->data); goto bad_desc; } } else if (!info->header || !info->u || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", info->ether ? "" : "ether "); goto bad_desc; } /* claim data interface and set it up ... with side effects. * network traffic can't flow until an altsetting is enabled. */ if (info->data != info->control) { status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) return status; } status = usbnet_get_endpoints(dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); if (info->data != info->control) usb_driver_release_interface(driver, info->data); return status; } /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ if (info->data != info->control) dev->status = NULL; if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { struct usb_endpoint_descriptor *desc; dev->status = &info->control->cur_altsetting->endpoint [0]; desc = &dev->status->desc; if (!usb_endpoint_is_int_in(desc) || (le16_to_cpu(desc->wMaxPacketSize) < sizeof(struct usb_cdc_notification)) || !desc->bInterval) { dev_dbg(&intf->dev, "bad notification endpoint\n"); dev->status = NULL; } } if (rndis && !dev->status) { dev_dbg(&intf->dev, "missing RNDIS status endpoint\n"); usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return -ENODEV; } /* Some devices don't initialise properly. In particular * the packet filter is not reset. There are devices that * don't do reset all the way. So the packet filter should * be set to a sane initial value. */ usbnet_cdc_update_filter(dev); return 0; bad_desc: dev_info(&dev->udev->dev, "bad CDC descriptors\n"); return -ENODEV; }
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; }
/* * udev, which is used for the firmware downloading, requires we cannot * block during module_init(). module_init() calls USB probe() which * is this routine. Due to that we delay actual operation using workqueue * and return always success here. */ static void dvb_usbv2_init_work(struct work_struct *work) { int ret; struct dvb_usb_device *d = container_of(work, struct dvb_usb_device, probe_work); d->work_pid = current->pid; dev_dbg(&d->udev->dev, "%s: work_pid=%d\n", __func__, d->work_pid); if (d->props->size_of_priv) { d->priv = kzalloc(d->props->size_of_priv, GFP_KERNEL); if (!d->priv) { dev_err(&d->udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); ret = -ENOMEM; goto err_usb_driver_release_interface; } } if (d->props->identify_state) { const char *name = NULL; ret = d->props->identify_state(d, &name); if (ret == 0) { ; } else if (ret == COLD) { dev_info(&d->udev->dev, "%s: found a '%s' in cold state\n", KBUILD_MODNAME, d->name); if (!name) name = d->props->firmware; ret = dvb_usbv2_download_firmware(d, name); if (ret == 0) { /* device is warm, continue initialization */ ; } else if (ret == RECONNECTS_USB) { /* * USB core will call disconnect() and then * probe() as device reconnects itself from the * USB bus. disconnect() will release all driver * resources and probe() is called for 'new' * device. As 'new' device is warm we should * never go here again. */ return; } else { /* * Unexpected error. We must unregister driver * manually from the device, because device is * already register by returning from probe() * with success. usb_driver_release_interface() * finally calls disconnect() in order to free * resources. */ goto err_usb_driver_release_interface; } } else { goto err_usb_driver_release_interface; } } dev_info(&d->udev->dev, "%s: found a '%s' in warm state\n", KBUILD_MODNAME, d->name); ret = dvb_usbv2_init(d); if (ret < 0) goto err_usb_driver_release_interface; dev_info(&d->udev->dev, "%s: '%s' successfully initialized and connected\n", KBUILD_MODNAME, d->name); return; err_usb_driver_release_interface: dev_info(&d->udev->dev, "%s: '%s' error while loading driver (%d)\n", KBUILD_MODNAME, d->name, ret); usb_driver_release_interface(to_usb_driver(d->intf->dev.driver), d->intf); dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); return; }
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) { int retval; struct net_device *net = dev->net; union { void *buf; struct rndis_msg_hdr *header; struct rndis_init *init; struct rndis_init_c *init_c; struct rndis_query *get; struct rndis_query_c *get_c; struct rndis_set *set; struct rndis_set_c *set_c; } u; u32 tmp; /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(1024, GFP_KERNEL); if (!u.buf) return -ENOMEM; retval = usbnet_generic_cdc_bind(dev, intf); if (retval < 0) goto done; net->hard_header_len += sizeof (struct rndis_data_hdr); /* initialize; max transfer is 16KB at full speed */ u.init->msg_type = RNDIS_MSG_INIT; u.init->msg_len = ccpu2(sizeof *u.init); u.init->major_version = ccpu2(1); u.init->minor_version = ccpu2(0); u.init->max_transfer_size = ccpu2(net->mtu + net->hard_header_len); retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); fail: usb_driver_release_interface(driver_of(intf), ((struct cdc_state *)&(dev->data))->data); goto done; } dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size); /* REVISIT: peripheral "alignment" request is ignored ... */ dev_dbg(&intf->dev, "hard mtu %u, align %d\n", dev->hard_mtu, 1 << le32_to_cpu(u.init_c->packet_alignment)); /* get designated host ethernet address */ memset(u.get, 0, sizeof *u.get); u.get->msg_type = RNDIS_MSG_QUERY; u.get->msg_len = ccpu2(sizeof *u.get); u.get->oid = OID_802_3_PERMANENT_ADDRESS; retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); goto fail; } tmp = le32_to_cpu(u.get_c->offset); if (unlikely((tmp + 8) > (1024 - ETH_ALEN) || u.get_c->len != ccpu2(ETH_ALEN))) { dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", tmp, le32_to_cpu(u.get_c->len)); retval = -EDOM; goto fail; } memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); u.set->msg_type = RNDIS_MSG_SET; u.set->msg_len = ccpu2(4 + sizeof *u.set); u.set->oid = OID_GEN_CURRENT_PACKET_FILTER; u.set->len = ccpu2(4); u.set->offset = ccpu2((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = ccpu2(DEFAULT_FILTER); retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto fail; } retval = 0; done: kfree(u.buf); return retval; }
static int __devinit cnxthwusb_probe(struct usb_interface *intf, const struct usb_device_id *id) #endif { int i; struct usb_interface *pUsbDataInterface, *pUsbCommInterface; POS_DEVNODE pDevNode = NULL; PUSBOSHAL pUsbOsHal = NULL; #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) struct usb_device *pUsbDevice = interface_to_usbdev(intf); __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; #endif #if TARGET_HCF_FAMILY dbg("%s: pUsbDevice=%p ifnum=%d id=%p bConfigurationValue=%d", __FUNCTION__, pUsbDevice, ifnum, id, pUsbDevice->config[0].CFGDESC(bConfigurationValue)); if (((USB_BYTEORDER16(pUsbDevice->descriptor.idVendor) != USB_AAPL_VENDOR_ID) && (USB_BYTEORDER16(pUsbDevice->descriptor.idVendor) != USB_CNXT_VENDOR_ID)) || ((USB_BYTEORDER16(pUsbDevice->descriptor.idProduct) != USB_AAPL_PRODUCT_ID) && (USB_BYTEORDER16(pUsbDevice->descriptor.idProduct) != USB_CNXT_PRODUCT_ID))) { err("Not the one we are interested about"); goto exit; } #else dbg("%s: pUsbDevice=%p ifnum=%d id=%p", __FUNCTION__, pUsbDevice, ifnum, id); #endif if(pUsbDevice->descriptor.bNumConfigurations != 1) { err("Wrong number of device configurations (%d)", pUsbDevice->descriptor.bNumConfigurations); goto exit; } if(pUsbDevice->actconfig->CFGDESC(bNumInterfaces) != 2) { err("Wrong number of device interfaces (%d)", pUsbDevice->actconfig->CFGDESC(bNumInterfaces)); goto exit; } #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) pUsbDataInterface = pUsbDevice->actconfig->interface + 0; #else pUsbDataInterface = pUsbDevice->actconfig->interface[0]; #endif if(pUsbDataInterface->num_altsetting != 1) { warn("DataInterface has more than one alternate setting (%d)", pUsbDataInterface->num_altsetting); } if(pUsbDataInterface->altsetting->CFGDESC(bNumEndpoints) != DATA_PIPE_NUM) { err("Wrong number of endpoints (%d) for DataInterface", pUsbDataInterface->altsetting->CFGDESC(bNumEndpoints)); goto exit; } #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) pUsbCommInterface = pUsbDevice->actconfig->interface + 1; #else pUsbCommInterface = pUsbDevice->actconfig->interface[1]; #endif if(pUsbCommInterface->num_altsetting != 1) { warn("CommInterface has more than one alternate setting (%d)", pUsbCommInterface->num_altsetting); } if(pUsbCommInterface->altsetting->CFGDESC(bNumEndpoints) != INT_PIPE_NUM) { err("Wrong number of endpoints (%d) for CommInterface", pUsbCommInterface->altsetting->CFGDESC(bNumEndpoints)); goto exit; } if(usb_interface_claimed(pUsbCommInterface)) { err("CommInterface already claimed"); goto exit; } pUsbOsHal = kmalloc(sizeof(USBOSHAL), GFP_KERNEL); if (!pUsbOsHal) { err ("Out of memory"); goto exit; } memset(pUsbOsHal, 0, sizeof(USBOSHAL)); for(i=0; i < pUsbDataInterface->altsetting->CFGDESC(bNumEndpoints); i++) { #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) dbg("%s: pUsbDataInterface endpoint=%d len=%d type=0x%02x addr=0x%02x attr=0x%02x maxsize=%d interval=%d", __FUNCTION__, i, pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bLength), pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bDescriptorType), pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress), pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bmAttributes), USB_BYTEORDER16(pUsbDataInterface->altsetting->endpoint[i].CFGDESC(wMaxPacketSize))&0x7ff, pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bInterval) ); #endif if (pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) == 0x81) { pUsbOsHal->DataInPipe = usb_rcvbulkpipe (pUsbDevice, pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) & USB_ENDPOINT_NUMBER_MASK); } else if (pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) == 0x01) { pUsbOsHal->DataOutPipe = usb_sndbulkpipe (pUsbDevice, pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) & USB_ENDPOINT_NUMBER_MASK); } else if (pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) == 0x83) { pUsbOsHal->DcpInPipe = usb_rcvbulkpipe (pUsbDevice, pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) & USB_ENDPOINT_NUMBER_MASK); #if TARGET_HCF_FAMILY } else if (pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) == 0x03) { pUsbOsHal->DownloadPipe = usb_sndbulkpipe (pUsbDevice, pUsbDataInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) & USB_ENDPOINT_NUMBER_MASK); if((USB_BYTEORDER16(pUsbDataInterface->altsetting->endpoint[i].CFGDESC(wMaxPacketSize))&0x7ff) == 64) pUsbOsHal->UpdateEEPROM = TRUE; #endif } } for(i=0; i < pUsbCommInterface->altsetting->CFGDESC(bNumEndpoints); i++) { #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) dbg("%s: pUsbCommInterface endpoint=%d len=%d type=0x%02x addr=0x%02x attr=0x%02x maxsize=%d interval=%d", __FUNCTION__, i, pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bLength), pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bDescriptorType), pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress), pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bmAttributes), USB_BYTEORDER16(pUsbCommInterface->altsetting->endpoint[i].CFGDESC(wMaxPacketSize))&0x7ff, pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bInterval) ); #endif if (pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) == 0x82) { pUsbOsHal->NotifyPipe = usb_rcvintpipe (pUsbDevice, pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bEndpointAddress) & USB_ENDPOINT_NUMBER_MASK); pUsbOsHal->NotifyInterval = pUsbCommInterface->altsetting->endpoint[i].CFGDESC(bInterval); pUsbOsHal->MaxNotifySize = USB_BYTEORDER16(pUsbCommInterface->altsetting->endpoint[i].CFGDESC(wMaxPacketSize))&0x7ff; } } if(!pUsbOsHal->DataInPipe || !pUsbOsHal->DataOutPipe || #if TARGET_HCF_FAMILY !pUsbOsHal->DcpInPipe || !pUsbOsHal->DownloadPipe || #endif !pUsbOsHal->NotifyPipe) { err("Missing endpoint(s)"); goto exit; } if(!pUsbOsHal->NotifyInterval) pUsbOsHal->NotifyInterval = 1; // ms pUsbOsHal->pUsbDevice = pUsbDevice; pUsbOsHal->pUsbCommInterface = pUsbCommInterface; pUsbOsHal->pUsbDataInterface = pUsbDataInterface; pUsbOsHal->ControlRequestEvent = OsEventCreate("USB ControlRequestEvent"); if(!pUsbOsHal->ControlRequestEvent) { goto exit; } pDevNode = kmalloc(sizeof(*pDevNode), GFP_KERNEL); if(!pDevNode) { err ("Out of memory"); goto exit; } memset(pDevNode, 0, sizeof(*pDevNode)); pDevNode->hwDev = pUsbOsHal; #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) pDevNode->hwDevLink = &pUsbDevice->dev; #endif pDevNode->hwModule = THIS_MODULE; strncpy(pDevNode->hwProfile, (char*)CNXTHWCFG("cadmus2"), sizeof(pDevNode->hwProfile)); pDevNode->hwProfile[sizeof(pDevNode->hwProfile)-1] = '\0'; snprintf(pDevNode->hwInstName, sizeof(pDevNode->hwInstName), "USB-%04x:%04x", USB_BYTEORDER16(pUsbDevice->descriptor.idVendor), USB_BYTEORDER16(pUsbDevice->descriptor.idProduct)); #ifdef CNXTHWUSB_TYPE pDevNode->hwType = CNXTHWUSB_TYPE; pDevNode->hwIf = GetHwFuncs(); #endif pDevNode->pmControl = cnxthw_DevMgrPMControl; pDevNode->osPageOffset = PAGE_OFFSET; usb_driver_claim_interface(&cnxthwusb_driver, pUsbCommInterface, pDevNode); if(!OsUsbAllocateUrbs(pUsbOsHal)) { err("Cannot allocate URBs"); #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) usb_set_intfdata (pUsbCommInterface, NULL); #endif usb_driver_release_interface(&cnxthwusb_driver, pUsbCommInterface); goto exit; } pUsbOsHal->bActive = TRUE; if(OsUsbFWDownload (pUsbOsHal)) { err("Firmware download failed"); pUsbOsHal->bActive = FALSE; OsUsbFreeUrbs(pUsbOsHal); #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) usb_set_intfdata (pUsbCommInterface, NULL); #endif usb_driver_release_interface(&cnxthwusb_driver, pUsbCommInterface); goto exit; } if (cnxt_serial_add(pDevNode, 0, pUsbDevice, ifnum, THIS_MODULE) < 0) { pUsbOsHal->bActive = FALSE; OsUsbFreeUrbs(pUsbOsHal); #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) usb_set_intfdata (pUsbCommInterface, NULL); #endif usb_driver_release_interface(&cnxthwusb_driver, pUsbCommInterface); goto exit; } #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) return pDevNode; #else usb_set_intfdata (intf, pDevNode); return 0; #endif exit: if(pDevNode) { kfree(pDevNode); } if(pUsbOsHal) { if(pUsbOsHal->ControlRequestEvent) OsEventDestroy(pUsbOsHal->ControlRequestEvent); kfree(pUsbOsHal); } #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) return NULL; #else return -ENODEV; #endif }
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; }
int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) { static const char ifname[] = "usbpn%d"; const struct usb_cdc_union_desc *union_header = NULL; const struct usb_host_interface *data_desc; struct usb_interface *data_intf; struct usb_device *usbdev = interface_to_usbdev(intf); struct net_device *dev; struct usbpn_dev *pnd; u8 *data; int phonet = 0; int len, err; data = intf->altsetting->extra; len = intf->altsetting->extralen; while (len >= 3) { u8 dlen = data[0]; if (dlen < 3) return -EINVAL; /* bDescriptorType */ if (data[1] == USB_DT_CS_INTERFACE) { /* bDescriptorSubType */ switch (data[2]) { case USB_CDC_UNION_TYPE: if (union_header || dlen < 5) break; union_header = (struct usb_cdc_union_desc *)data; break; case 0xAB: phonet = 1; break; } } data += dlen; len -= dlen; } if (!union_header || !phonet) return -EINVAL; data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); if (data_intf == NULL) return -ENODEV; /* Data interface has one inactive and one active setting */ if (data_intf->num_altsetting != 2) return -EINVAL; if (data_intf->altsetting[0].desc.bNumEndpoints == 0 && data_intf->altsetting[1].desc.bNumEndpoints == 2) data_desc = data_intf->altsetting + 1; else if (data_intf->altsetting[0].desc.bNumEndpoints == 2 && data_intf->altsetting[1].desc.bNumEndpoints == 0) data_desc = data_intf->altsetting; else return -EINVAL; dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size, ifname, usbpn_setup); if (!dev) return -ENOMEM; pnd = netdev_priv(dev); SET_NETDEV_DEV(dev, &intf->dev); netif_stop_queue(dev); pnd->dev = dev; pnd->usb = usb_get_dev(usbdev); pnd->intf = intf; pnd->data_intf = data_intf; spin_lock_init(&pnd->tx_lock); spin_lock_init(&pnd->rx_lock); /* Endpoints */ if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { pnd->rx_pipe = usb_rcvbulkpipe(usbdev, data_desc->endpoint[0].desc.bEndpointAddress); pnd->tx_pipe = usb_sndbulkpipe(usbdev, data_desc->endpoint[1].desc.bEndpointAddress); } else { pnd->rx_pipe = usb_rcvbulkpipe(usbdev, data_desc->endpoint[1].desc.bEndpointAddress); pnd->tx_pipe = usb_sndbulkpipe(usbdev, data_desc->endpoint[0].desc.bEndpointAddress); } pnd->active_setting = data_desc - data_intf->altsetting; err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); if (err) goto out; /* Force inactive mode until the network device is brought UP */ usb_set_interface(usbdev, union_header->bSlaveInterface0, !pnd->active_setting); usb_set_intfdata(intf, pnd); err = register_netdev(dev); if (err) { usb_driver_release_interface(&usbpn_driver, data_intf); goto out; } dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); return 0; out: usb_set_intfdata(intf, NULL); free_netdev(dev); return err; }
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) { int retval; struct net_device *net = dev->net; struct cdc_state *info = (void *) &dev->data; union { void *buf; struct rndis_msg_hdr *header; struct rndis_init *init; struct rndis_init_c *init_c; struct rndis_query *get; struct rndis_query_c *get_c; struct rndis_set *set; struct rndis_set_c *set_c; } u; u32 tmp; int reply_len; unsigned char *bp; /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (!u.buf) return -ENOMEM; retval = usbnet_generic_cdc_bind(dev, intf); if (retval < 0) goto fail; u.init->msg_type = RNDIS_MSG_INIT; u.init->msg_len = ccpu2(sizeof *u.init); u.init->major_version = ccpu2(1); u.init->minor_version = ccpu2(0); /* max transfer (in spec) is 0x4000 at full speed, but for * TX we'll stick to one Ethernet packet plus RNDIS framing. * For RX we handle drivers that zero-pad to end-of-packet. * Don't let userspace change these settings. * * NOTE: there still seems to be wierdness here, as if we need * to do some more things to make sure WinCE targets accept this. * They default to jumbograms of 8KB or 16KB, which is absurd * for such low data rates and which is also more than Linux * can usually expect to allocate for SKB data... */ net->hard_header_len += sizeof (struct rndis_data_hdr); dev->hard_mtu = net->mtu + net->hard_header_len; dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1); if (dev->maxpacket == 0) { if (netif_msg_probe(dev)) dev_dbg(&intf->dev, "dev->maxpacket can't be 0\n"); retval = -EINVAL; goto fail_and_release; } dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); dev->rx_urb_size &= ~(dev->maxpacket - 1); u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); net->change_mtu = NULL; retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); goto fail_and_release; } tmp = le32_to_cpu(u.init_c->max_transfer_size); if (tmp < dev->hard_mtu) { dev_err(&intf->dev, "dev can't take %u byte packets (max %u)\n", dev->hard_mtu, tmp); retval = -EINVAL; goto fail_and_release; } /* REVISIT: peripheral "alignment" request is ignored ... */ dev_dbg(&intf->dev, "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); /* Get designated host ethernet address */ reply_len = ETH_ALEN; retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS, 48, (void **) &bp, &reply_len); if (unlikely(retval< 0)) { dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); goto fail_and_release; } memcpy(net->dev_addr, bp, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); u.set->msg_type = RNDIS_MSG_SET; u.set->msg_len = ccpu2(4 + sizeof *u.set); u.set->oid = OID_GEN_CURRENT_PACKET_FILTER; u.set->len = ccpu2(4); u.set->offset = ccpu2((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = ccpu2(DEFAULT_FILTER); retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto fail_and_release; } retval = 0; kfree(u.buf); return retval; fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); info->data = NULL; fail: kfree(u.buf); return retval; }
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; }
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); /* 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; }