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;
}
Пример #2
0
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);
}
Пример #3
0
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);
	}
}
Пример #4
0
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);
}
Пример #5
0
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);
		}
	}
}
Пример #6
0
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]);
        }
}
Пример #7
0
/*
 * 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.");
}
Пример #8
0
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);
}
Пример #9
0
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);
		}
	}
}
Пример #10
0
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);
		}
	}
}
Пример #12
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;
	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;
	}
}
Пример #13
0
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;
}
Пример #14
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;
}
Пример #15
0
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;
}
Пример #16
0
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;
}
Пример #17
0
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;
}
Пример #18
0
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;
}
Пример #19
0
/*
 * 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;
}
Пример #20
0
/* 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;
}
Пример #21
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;
	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;
}
Пример #22
0
/*
 * 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;
}
Пример #23
0
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;
}
Пример #24
0
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
}
Пример #25
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)));

	/* 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;
}
Пример #26
0
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;
}
Пример #28
0
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;
}
Пример #29
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;
}
Пример #30
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;
	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;
}