Exemple #1
0
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
	unsigned long		flags;
	struct sk_buff		*skb;
	int			count = 0;

	spin_lock_irqsave (&q->lock, flags);
	while (!skb_queue_empty(q)) {
		struct skb_data		*entry;
		struct urb		*urb;
		int			retval;

		skb_queue_walk(q, skb) {
			entry = (struct skb_data *) skb->cb;
			if (entry->state != unlink_start)
				goto found;
		}
		break;
found:
		entry->state = unlink_start;
		urb = entry->urb;

		usb_get_urb(urb);
		spin_unlock_irqrestore(&q->lock, flags);
		
		
		retval = usb_unlink_urb (urb);
		if (retval != -EINPROGRESS && retval != 0)
			netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
		else
			count++;
		usb_put_urb(urb);
		spin_lock_irqsave(&q->lock, flags);
	}
Exemple #2
0
/*
 * Can not be called in atomic context.
 */
static void message_cancel(struct gb_message *message)
{
	struct gb_host_device *hd = message->operation->connection->hd;
	struct es2_ap_dev *es2 = hd_to_es2(hd);
	struct urb *urb;
	int i;

	might_sleep();

	spin_lock_irq(&es2->cport_out_urb_lock);
	urb = message->hcpriv;

	/* Prevent dynamically allocated urb from being deallocated. */
	usb_get_urb(urb);

	/* Prevent pre-allocated urb from being reused. */
	for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
		if (urb == es2->cport_out_urb[i]) {
			es2->cport_out_urb_cancelled[i] = true;
			break;
		}
	}
	spin_unlock_irq(&es2->cport_out_urb_lock);

	usb_kill_urb(urb);

	if (i < NUM_CPORT_OUT_URB) {
		spin_lock_irq(&es2->cport_out_urb_lock);
		es2->cport_out_urb_cancelled[i] = false;
		spin_unlock_irq(&es2->cport_out_urb_lock);
	}

	usb_free_urb(urb);
}
Exemple #3
0
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
	unsigned long		flags;
	struct sk_buff		*skb, *skbnext;
	int			count = 0;

	spin_lock_irqsave (&q->lock, flags);
	skb_queue_walk_safe(q, skb, skbnext) {
		struct skb_data		*entry;
		struct urb		*urb;
		int			retval;

		entry = (struct skb_data *) skb->cb;
		urb = entry->urb;

		/*
		 * Get reference count of the URB to avoid it to be
		 * freed during usb_unlink_urb, which may trigger
		 * use-after-free problem inside usb_unlink_urb since
		 * usb_unlink_urb is always racing with .complete
		 * handler(include defer_bh).
		 */
		usb_get_urb(urb);
		// during some PM-driven resume scenarios,
		// these (async) unlinks complete immediately
		retval = usb_unlink_urb (urb);
		if (retval != -EINPROGRESS && retval != 0)
			netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
		else
			count++;
		usb_put_urb(urb);
	}
	spin_unlock_irqrestore (&q->lock, flags);
	return count;
}
/**
 * usb_anchor_urb - anchors an URB while it is processed
 * @urb: pointer to the urb to anchor
 * @anchor: pointer to the anchor
 *
 * This can be called to have access to URBs which are to be executed
 * without bothering to track them
 */
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
	unsigned long flags;

	spin_lock_irqsave(&anchor->lock, flags);
	usb_get_urb(urb);
	list_add_tail(&urb->anchor_list, &anchor->urb_list);
	urb->anchor = anchor;
	spin_unlock_irqrestore(&anchor->lock, flags);
}
Exemple #5
0
/**
 * usb_anchor_urb - anchors an URB while it is processed
 * @urb: pointer to the urb to anchor
 * @anchor: pointer to the anchor
 *
 * This can be called to have access to URBs which are to be executed
 * without bothering to track them
 */
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
	unsigned long flags;

	spin_lock_irqsave(&anchor->lock, flags);
	usb_get_urb(urb);
	list_add_tail(&urb->anchor_list, &anchor->urb_list);
	urb->anchor = anchor;

	if (unlikely(anchor->poisoned))
		atomic_inc(&urb->reject);

	spin_unlock_irqrestore(&anchor->lock, flags);
}
Exemple #6
0
/**
 * usb_poison_anchored_urbs - cease all traffic from an anchor
 * @anchor: anchor the requests are bound to
 *
 * this allows all outstanding URBs to be poisoned starting
 * from the back of the queue. Newly added URBs will also be
 * poisoned
 *
 * This routine should not be called by a driver after its disconnect
 * method has returned.
 */
void usb_poison_anchored_urbs(struct usb_anchor *anchor)
{
	struct urb *victim;

	spin_lock_irq(&anchor->lock);
	// anchor->poisoned = 1; /* XXX: Cannot backport */
	while (!list_empty(&anchor->urb_list)) {
		victim = list_entry(anchor->urb_list.prev, struct urb,
				    anchor_list);
		/* we must make sure the URB isn't freed before we kill it*/
		usb_get_urb(victim);
		spin_unlock_irq(&anchor->lock);
		/* this will unanchor the URB */
		usb_poison_urb(victim);
		usb_put_urb(victim);
		spin_lock_irq(&anchor->lock);
	}
	spin_unlock_irq(&anchor->lock);
}
Exemple #7
0
/*
 * Allocate a URB and initialize the various fields of it.
 * This API is used by the single_step_set_feature test of
 * EHSET where IN packet of the GetDescriptor request is
 * sent 15secs after the SETUP packet.
 * Return NULL if failed.
 */
static struct urb *xhci_request_single_step_set_feature_urb(
		struct usb_device *udev,
		void *dr,
		void *buf,
		struct completion *done)
{
	struct urb *urb;
	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
	struct usb_host_endpoint *ep;

	urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!urb)
		return NULL;

	urb->pipe = usb_rcvctrlpipe(udev, 0);
	ep = udev->ep_in[usb_pipeendpoint(urb->pipe)];
	if (!ep) {
		usb_free_urb(urb);
		return NULL;
	}

	/*
	 * Initialize the various URB fields as these are used by the HCD
	 * driver to queue it and as well as when completion happens.
	 */
	urb->ep = ep;
	urb->dev = udev;
	urb->setup_packet = dr;
	urb->transfer_buffer = buf;
	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
	urb->complete = xhci_single_step_completion;
	urb->status = -EINPROGRESS;
	urb->actual_length = 0;
	urb->transfer_flags = URB_DIR_IN;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);
	usb_hcd_map_urb_for_dma(hcd, urb, GFP_KERNEL);
	urb->context = done;
	return urb;
}
Exemple #8
0
static int vhcd_urb_enqueue(struct usb_hcd *hcd,
                            struct usb_host_endpoint *ep,
                            struct urb *urb,
                            gfp_t mem_flags)
{
	int ret = 0;
	unsigned int transfer_flags = 0 ;

	struct usb_device * udev = urb->dev;

	/* FIXME Check for non existent device */

	if (!HC_IS_RUNNING(hcd->state)) {
		LOG("HC is not running\n");
		return -ENODEV;
	}

	/* we have to trap some control messages, i.e. USB_REQ_SET_ADDRESS... */
	/* TODO we don't have to do it here, but in the server */

	if (usb_pipedevice(urb->pipe) == 0) {
		__u8 type = usb_pipetype(urb->pipe);
		struct usb_ctrlrequest *ctrlreq = (struct usb_ctrlrequest *) urb->setup_packet;

		if (type != PIPE_CONTROL || !ctrlreq ) {
			LOG("invalid request to devnum 0\n");
			ret = -EINVAL;
			goto no_need_xmit;
		}

		switch (ctrlreq->bRequest) {

		case USB_REQ_SET_ADDRESS:
			LOG("SetAddress Request (%d) to port %d\n",
			          ctrlreq->wValue, urb->dev->portnum);

			spin_lock (&urb->lock);
			if (urb->status == -EINPROGRESS) {
				/* This request is successfully completed. */
				/* If not -EINPROGRESS, possibly unlinked. */
				urb->status = 0;
			}
			spin_unlock (&urb->lock);

			goto no_need_xmit;

		case USB_REQ_GET_DESCRIPTOR:
			if (ctrlreq->wValue == (USB_DT_DEVICE << 8))
				LOG("Get_Descriptor to device 0 (get max pipe size)\n");
			goto out;

		default:
			/* NOT REACHED */
			LOG("invalid request to devnum 0 bRequest %u, wValue %u\n",
			          ctrlreq->bRequest, ctrlreq->wValue);
			ret = -EINVAL;
			goto no_need_xmit;
		}
	}

out:
	if (urb->status != -EINPROGRESS) {
		LOG("URB already unlinked!, status %d\n", urb->status);
		return urb->status;
	}

	if (usb_pipeisoc(urb->pipe)) {
		LOG("ISO URBs not supported");
		ret = -EINVAL;
		goto no_need_xmit;
	}

	urb->hcpriv = (void *) hcd_to_vhcd(hcd);
	LOG("hcpriv %p", urb->hcpriv);

	transfer_flags = urb->transfer_flags;
	usb_get_urb(urb);

#if 0
	d_urb->type              = usb_pipetype(urb->pipe);
	d_urb->dev_id            = data->gadget[urb->dev->portnum-1].id;
	d_urb->endpoint          = usb_pipeendpoint(urb->pipe);
	d_urb->direction         = 0 || usb_pipein(urb->pipe);
	d_urb->interval          = urb->interval;
	d_urb->transfer_flags    = urb->transfer_flags;
	d_urb->number_of_packets = urb->number_of_packets;
	d_urb->priv              = priv;
	d_urb->size              = urb->transfer_buffer_length; 
	d_urb->data			     = urb->transfer_buffer;
	d_urb->phys_addr	     = d_urb->data?virt_to_phys(d_urb->data):0;


	if (urb->setup_packet) {
		memcpy(d_urb->setup_packet, urb->setup_packet, 8);
	}

	/* XXX ISO ? */
//	if (urb->number_of_packets)
//		memcpy(d_urb->iso_desc, urb->iso_frame_desc, urb->number_of_packets*sizeof(struct usb_iso_packet_descriptor));

	ret = libddeusb_submit_d_urb(d_urb);
#else
	unsigned port_num = urb->dev->portnum;

	switch (usb_pipetype(urb->pipe)) {

	case PIPE_CONTROL:
		{
			struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)
			                               urb->setup_packet;
			dde_linux26_usb_vhcd_submit_control_urb_cb(port_num,
			                                           usb_pipeendpoint(urb->pipe),
			                                           usb_pipein(urb->pipe),
			                                           urb, /* handle */
			                                           sizeof(*req), req);
		}
		break;

	case PIPE_INTERRUPT:
		printk(" int\n");
//	dde_linux26_usb_vhcd_submit_urb(urb->transfer_buffer,
//	                                urb->transfer_buffer_length);
		return -EINVAL;
		break;

	/* unsupported transfer types */
	case PIPE_BULK:
		printk(" bulk\n");
		return -EINVAL;
	case PIPE_ISOCHRONOUS:
		printk(" isoc\n");
		return -EINVAL;
	}
#endif

//	if (ret) {
//		LOG("URB SUBMIT FAILED (%d).",ret);
//		/* s.t. went wrong. */	
//		spin_lock_irqsave(&data->lock, flags);  
//		data->rcv_buf[i]=NULL;
//		spin_unlock_irqrestore(&data->lock, flags);
//		down(&data->rcv_buf_free);
//		kmem_cache_free(priv_cache, urb->hcpriv);
//		usb_put_urb(urb);
//		urb->status = ret;
//		urb->hcpriv = NULL;
//		libddeusb_free_d_urb(d_urb);
//		return ret;
//	}

	LOG("URB %p submitted", urb);

	return 0;

no_need_xmit:
	usb_hcd_giveback_urb(hcd, urb);
	return 0;
}
Exemple #9
0
/*
 * This function implements the USB_PORT_FEAT_TEST handling of the
 * SINGLE_STEP_SET_FEATURE test mode as defined in the Embedded
 * High-Speed Electrical Test (EHSET) specification. This simply
 * issues a GetDescriptor control transfer, with an inserted 15-second
 * delay after the end of the SETUP stage and before the IN token of
 * the DATA stage is set. The idea is that this gives the test operator
 * enough time to configure the oscilloscope to perform a measurement
 * of the response time between the DATA and ACK packets that follow.
 */
static int xhci_ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
	int retval = -ENOMEM;
	struct usb_ctrlrequest *dr;
	struct urb *urb;
	struct usb_device *udev;
	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
	struct usb_device_descriptor *buf;
	unsigned long flags;
	DECLARE_COMPLETION_ONSTACK(done);

	/* Obtain udev of the rhub's child port */
	udev = usb_hub_find_child(hcd->self.root_hub, port);
	if (!udev) {
		xhci_err(xhci, "No device attached to the RootHub\n");
		return -ENODEV;
	}
	buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
	if (!dr) {
		kfree(buf);
		return -ENOMEM;
	}

	/* Fill Setup packet for GetDescriptor */
	dr->bRequestType = USB_DIR_IN;
	dr->bRequest = USB_REQ_GET_DESCRIPTOR;
	dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
	dr->wIndex = 0;
	dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
	urb = xhci_request_single_step_set_feature_urb(udev, dr, buf, &done);
	if (!urb)
		goto cleanup;

	/* Now complete just the SETUP stage */
	spin_lock_irqsave(&xhci->lock, flags);
	retval = xhci_submit_single_step_set_feature(hcd, urb, 1);
	spin_unlock_irqrestore(&xhci->lock, flags);
	if (retval)
		goto out1;

	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		xhci_err(xhci, "%s SETUP stage timed out on ep0\n", __func__);
		goto out1;
	}

	/* Sleep for 15 seconds; HC will send SOFs during this period */
	msleep(15 * 1000);

	/* Complete remaining DATA and status stages. Re-use same URB */
	urb->status = -EINPROGRESS;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);

	spin_lock_irqsave(&xhci->lock, flags);
	retval = xhci_submit_single_step_set_feature(hcd, urb, 0);
	spin_unlock_irqrestore(&xhci->lock, flags);
	if (!retval && !wait_for_completion_timeout(&done,
						msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		xhci_err(xhci, "%s IN stage timed out on ep0\n", __func__);
	}
out1:
	usb_free_urb(urb);
cleanup:
	kfree(dr);
	kfree(buf);
	return retval;
}
Exemple #10
0
/*
 * Read an ack from  the notification endpoint
 *
 * @i2400m:
 * @_ack: pointer to where to store the read data
 * @ack_size: how many bytes we should read
 *
 * Returns: < 0 errno code on error; otherwise, amount of received bytes.
 *
 * Submits a notification read, appends the read data to the given ack
 * buffer and then repeats (until @ack_size bytes have been
 * received).
 */
ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m,
				    struct i2400m_bootrom_header *_ack,
				    size_t ack_size)
{
	ssize_t result = -ENOMEM;
	struct device *dev = i2400m_dev(i2400m);
	struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
	struct urb notif_urb;
	void *ack = _ack;
	size_t offset, len;
	long val;
	int do_autopm = 1;
	DECLARE_COMPLETION_ONSTACK(notif_completion);

	d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n",
		  i2400m, ack, ack_size);
	BUG_ON(_ack == i2400m->bm_ack_buf);
	result = usb_autopm_get_interface(i2400mu->usb_iface);
	if (result < 0) {
		dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result);
		do_autopm = 0;
	}
	usb_init_urb(&notif_urb);	/* ready notifications */
	usb_get_urb(&notif_urb);
	offset = 0;
	while (offset < ack_size) {
		init_completion(&notif_completion);
		result = i2400mu_notif_submit(i2400mu, &notif_urb,
					      &notif_completion);
		if (result < 0)
			goto error_notif_urb_submit;
		val = wait_for_completion_interruptible_timeout(
			&notif_completion, HZ);
		if (val == 0) {
			result = -ETIMEDOUT;
			usb_kill_urb(&notif_urb);	/* Timedout */
			goto error_notif_wait;
		}
		if (val == -ERESTARTSYS) {
			result = -EINTR;		/* Interrupted */
			usb_kill_urb(&notif_urb);
			goto error_notif_wait;
		}
		result = notif_urb.status;		/* How was the ack? */
		switch (result) {
		case 0:
			break;
		case -EINVAL:			/* while removing driver */
		case -ENODEV:			/* dev disconnect ... */
		case -ENOENT:			/* just ignore it */
		case -ESHUTDOWN:		/* and exit */
		case -ECONNRESET:
			result = -ESHUTDOWN;
			goto error_dev_gone;
		default:				/* any other? */
			usb_kill_urb(&notif_urb);	/* Timedout */
			if (edc_inc(&i2400mu->urb_edc,
				    EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
				goto error_exceeded;
			dev_err(dev, "BM-ACK: URB error %d, "
				"retrying\n", notif_urb.status);
			continue;	/* retry */
		}
		if (notif_urb.actual_length == 0) {
			d_printf(6, dev, "ZLP received, retrying\n");
			continue;
		}
		/* Got data, append it to the buffer */
		len = min(ack_size - offset, (size_t) notif_urb.actual_length);
		memcpy(ack + offset, i2400m->bm_ack_buf, len);
		offset += len;
	}
	result = offset;
error_notif_urb_submit:
error_notif_wait:
error_dev_gone:
out:
	if (do_autopm)
		usb_autopm_put_interface(i2400mu->usb_iface);
	d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n",
		i2400m, ack, ack_size, (long) result);
	return result;

error_exceeded:
	dev_err(dev, "bm: maximum errors in notification URB exceeded; "
		"resetting device\n");
	usb_queue_reset_device(i2400mu->usb_iface);
	goto out;
}
static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
	struct sk_buff		*skb;
	struct skb_data		*entry;
	int			retval = 0;
	unsigned long		lockflags;
	size_t			size = dev->rx_urb_size;

#if defined(CONFIG_RA_HW_NAT_PCI) && (defined(CONFIG_RA_HW_NAT) || defined(CONFIG_RA_HW_NAT_MODULE))
	if ((skb = alloc_skb (size + NET_IP_ALIGN + FOE_INFO_LEN, flags)) == NULL) {
#else
	if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
#endif
		if (netif_msg_rx_err (dev))
			devdbg (dev, "no rx skb");
		usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
		usb_free_urb (urb);
		return;
	}
#if defined(CONFIG_RA_HW_NAT_PCI) && (defined(CONFIG_RA_HW_NAT) || defined(CONFIG_RA_HW_NAT_MODULE))
	skb_reserve (skb, NET_IP_ALIGN + FOE_INFO_LEN);
#else
	skb_reserve (skb, NET_IP_ALIGN);
#endif
	entry = (struct skb_data *) skb->cb;
	entry->urb = urb;
	entry->dev = dev;
	entry->length = 0;

	usb_fill_bulk_urb (urb, dev->udev, dev->in,
		skb->data, size, rx_complete, skb);

	spin_lock_irqsave (&dev->rxq.lock, lockflags);

	if (netif_running (dev->net)
			&& netif_device_present (dev->net)
			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
		switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){
		case -EPIPE:
			usbnet_defer_kevent (dev, EVENT_RX_HALT);
			break;
		case -ENOMEM:
			usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
			break;
		case -ENODEV:
			if (netif_msg_ifdown (dev))
				devdbg (dev, "device gone");
			netif_device_detach (dev->net);
			break;
		default:
			if (netif_msg_rx_err (dev))
				devdbg (dev, "rx submit, %d", retval);
			tasklet_schedule (&dev->bh);
			break;
		case 0:
			__usbnet_queue_skb(&dev->rxq, skb, rx_start);
		}
	} else {
		if (netif_msg_ifdown (dev))
			devdbg (dev, "rx: stopped");
		retval = -ENOLINK;
	}
	spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
	if (retval) {
		dev_kfree_skb_any (skb);
		usb_free_urb (urb);
	}
}


/*-------------------------------------------------------------------------*/

static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
{
	if (dev->driver_info->rx_fixup
			&& !dev->driver_info->rx_fixup (dev, skb))
		goto error;
	// else network stack removes extra byte if we forced a short packet

	if (skb->len)
		usbnet_skb_return (dev, skb);
	else {
		if (netif_msg_rx_err (dev))
			devdbg (dev, "drop");
error:
		dev->stats.rx_errors++;
		skb_queue_tail (&dev->done, skb);
	}
}

/*-------------------------------------------------------------------------*/

static void rx_complete (struct urb *urb)
{
	struct sk_buff		*skb = (struct sk_buff *) urb->context;
	struct skb_data		*entry = (struct skb_data *) skb->cb;
	struct usbnet		*dev = entry->dev;
	int			urb_status = urb->status;
	enum skb_state		state;

	skb_put (skb, urb->actual_length);
	state = rx_done;
	entry->urb = NULL;

	switch (urb_status) {
	    // success
	    case 0:
		if (skb->len < dev->net->hard_header_len) {
			state = rx_cleanup;
			dev->stats.rx_errors++;
			dev->stats.rx_length_errors++;
			if (netif_msg_rx_err (dev))
				devdbg (dev, "rx length %d", skb->len);
		}
		break;

	    // stalls need manual reset. this is rare ... except that
	    // when going through USB 2.0 TTs, unplug appears this way.
	    // we avoid the highspeed version of the ETIMEOUT/EILSEQ
	    // storm, recovering as needed.
	    case -EPIPE:
		dev->stats.rx_errors++;
		usbnet_defer_kevent (dev, EVENT_RX_HALT);
		// FALLTHROUGH

	    // software-driven interface shutdown
	    case -ECONNRESET:		// async unlink
	    case -ESHUTDOWN:		// hardware gone
		if (netif_msg_ifdown (dev))
			devdbg (dev, "rx shutdown, code %d", urb_status);
		goto block;

	    // we get controller i/o faults during khubd disconnect() delays.
	    // throttle down resubmits, to avoid log floods; just temporarily,
	    // so we still recover when the fault isn't a khubd delay.
	    case -EPROTO:
	    case -ETIME:
	    case -EILSEQ:
		dev->stats.rx_errors++;
		if (!timer_pending (&dev->delay)) {
			mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
			if (netif_msg_link (dev))
				devdbg (dev, "rx throttle %d", urb_status);
		}
block:
		state = rx_cleanup;
		entry->urb = urb;
		urb = NULL;
		break;

	    // data overrun ... flush fifo?
	    case -EOVERFLOW:
		dev->stats.rx_over_errors++;
		// FALLTHROUGH

	    default:
		state = rx_cleanup;
		dev->stats.rx_errors++;
		if (netif_msg_rx_err (dev))
			devdbg (dev, "rx status %d", urb_status);
		break;
	}

	state = defer_bh(dev, skb, &dev->rxq, state);

	if (urb) {
		if (netif_running (dev->net)
		    && !test_bit (EVENT_RX_HALT, &dev->flags) &&
		    state != unlink_start) {
			rx_submit (dev, urb, GFP_ATOMIC);
			return;
		}
		usb_free_urb (urb);
	}
	if (netif_msg_rx_err (dev))
		devdbg (dev, "no read resubmitted");
}

static void intr_complete (struct urb *urb)
{
	struct usbnet	*dev = urb->context;
	int		status = urb->status;

	switch (status) {
	    /* success */
	    case 0:
		dev->driver_info->status(dev, urb);
		break;

	    /* software-driven interface shutdown */
	    case -ENOENT:		// urb killed
	    case -ESHUTDOWN:		// hardware gone
		if (netif_msg_ifdown (dev))
			devdbg (dev, "intr shutdown, code %d", status);
		return;

	    /* NOTE:  not throttling like RX/TX, since this endpoint
	     * already polls infrequently
	     */
	    default:
		devdbg (dev, "intr status %d", status);
		break;
	}

	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
	status = usb_submit_urb (urb, GFP_ATOMIC);
	if (status != 0 && netif_msg_timer (dev))
		deverr(dev, "intr resubmit --> %d", status);
}

/*-------------------------------------------------------------------------*/

// unlink pending rx/tx; completion handlers do all other cleanup

static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
	unsigned long		flags;
	struct sk_buff		*skb;
	int			count = 0;

	spin_lock_irqsave (&q->lock, flags);
	while (!skb_queue_empty(q)) {
		struct skb_data		*entry;
		struct urb		*urb;
		int			retval;

		skb_queue_walk(q, skb) {
		entry = (struct skb_data *) skb->cb;
			if (entry->state != unlink_start)
				goto found;
		}
		break;
found:
		entry->state = unlink_start;
		urb = entry->urb;

		/*
		 * Get reference count of the URB to avoid it to be
		 * freed during usb_unlink_urb, which may trigger
		 * use-after-free problem inside usb_unlink_urb since
		 * usb_unlink_urb is always racing with .complete
		 * handler(include defer_bh).
		 */
		usb_get_urb(urb);
		spin_unlock_irqrestore(&q->lock, flags);
		// during some PM-driven resume scenarios,
		// these (async) unlinks complete immediately
		retval = usb_unlink_urb (urb);
		if (retval != -EINPROGRESS && retval != 0)
			devdbg (dev, "unlink urb err, %d", retval);
		else
			count++;
		usb_put_urb(urb);
		spin_lock_irqsave(&q->lock, flags);
	}
pUrb SysUsbGetUrb(pUrb urb) { return (pUrb) usb_get_urb((struct urb *)urb); }