hcd.c

来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,743 行 · 第 1/4 页

C
1,743
字号
	case USB_SPEED_FULL:	/* ISOC or INTR */		if (isoc) {			tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;			return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);		} else {			tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;			return (9107L + BW_HOST_DELAY + tmp);		}	case USB_SPEED_HIGH:	/* ISOC or INTR */		// FIXME adjust for input vs output		if (isoc)			tmp = HS_NSECS_ISO (bytecount);		else			tmp = HS_NSECS (bytecount);		return tmp;	default:		pr_debug ("%s: bogus device speed!\n", usbcore_name);		return -1;	}}EXPORT_SYMBOL (usb_calc_bus_time);/*-------------------------------------------------------------------------*//* * Generic HC operations. *//*-------------------------------------------------------------------------*/static void urb_unlink (struct urb *urb){	unsigned long		flags;	/* clear all state linking urb to this dev (and hcd) */	spin_lock_irqsave (&hcd_data_lock, flags);	list_del_init (&urb->urb_list);	spin_unlock_irqrestore (&hcd_data_lock, flags);}/* may be called in any context with a valid urb->dev usecount * caller surrenders "ownership" of urb * expects usb_submit_urb() to have sanity checked and conditioned all * inputs in the urb */int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags){	int			status;	struct usb_hcd		*hcd = bus_to_hcd(urb->dev->bus);	struct usb_host_endpoint *ep;	unsigned long		flags;	if (!hcd)		return -ENODEV;	usbmon_urb_submit(&hcd->self, urb);	/*	 * Atomically queue the urb,  first to our records, then to the HCD.	 * Access to urb->status is controlled by urb->lock ... changes on	 * i/o completion (normal or fault) or unlinking.	 */	// FIXME:  verify that quiescing hc works right (RH cleans up)	spin_lock_irqsave (&hcd_data_lock, flags);	ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)			[usb_pipeendpoint(urb->pipe)];	if (unlikely (!ep))		status = -ENOENT;	else if (unlikely (urb->reject))		status = -EPERM;	else switch (hcd->state) {	case HC_STATE_RUNNING:	case HC_STATE_RESUMING:doit:		list_add_tail (&urb->urb_list, &ep->urb_list);		status = 0;		break;	case HC_STATE_SUSPENDED:		/* HC upstream links (register access, wakeup signaling) can work		 * even when the downstream links (and DMA etc) are quiesced; let		 * usbcore talk to the root hub.		 */		if (hcd->self.controller->power.power_state.event == PM_EVENT_ON				&& urb->dev->parent == NULL)			goto doit;		/* FALL THROUGH */	default:		status = -ESHUTDOWN;		break;	}	spin_unlock_irqrestore (&hcd_data_lock, flags);	if (status) {		INIT_LIST_HEAD (&urb->urb_list);		usbmon_urb_submit_error(&hcd->self, urb, status);		return status;	}	/* increment urb's reference count as part of giving it to the HCD	 * (which now controls it).  HCD guarantees that it either returns	 * an error or calls giveback(), but not both.	 */	urb = usb_get_urb (urb);	atomic_inc (&urb->use_count);	if (urb->dev == hcd->self.root_hub) {		/* NOTE:  requirement on hub callers (usbfs and the hub		 * driver, for now) that URBs' urb->transfer_buffer be		 * valid and usb_buffer_{sync,unmap}() not be needed, since		 * they could clobber root hub response data.		 */		status = rh_urb_enqueue (hcd, urb);		goto done;	}	/* lower level hcd code should use *_dma exclusively,	 * unless it uses pio or talks to another transport.	 */	if (hcd->self.uses_dma) {		if (usb_pipecontrol (urb->pipe)			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))			urb->setup_dma = dma_map_single (					hcd->self.controller,					urb->setup_packet,					sizeof (struct usb_ctrlrequest),					DMA_TO_DEVICE);		if (urb->transfer_buffer_length != 0			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))			urb->transfer_dma = dma_map_single (					hcd->self.controller,					urb->transfer_buffer,					urb->transfer_buffer_length,					usb_pipein (urb->pipe)					    ? DMA_FROM_DEVICE					    : DMA_TO_DEVICE);	}	status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);done:	if (unlikely (status)) {		urb_unlink (urb);		atomic_dec (&urb->use_count);		if (urb->reject)			wake_up (&usb_kill_urb_queue);		usbmon_urb_submit_error(&hcd->self, urb, status);		usb_put_urb (urb);	}	return status;}/*-------------------------------------------------------------------------*//* called in any context */int usb_hcd_get_frame_number (struct usb_device *udev){	struct usb_hcd	*hcd = bus_to_hcd(udev->bus);	if (!HC_IS_RUNNING (hcd->state))		return -ESHUTDOWN;	return hcd->driver->get_frame_number (hcd);}/*-------------------------------------------------------------------------*//* this makes the hcd giveback() the urb more quickly, by kicking it * off hardware queues (which may take a while) and returning it as * soon as practical.  we've already set up the urb's return status, * but we can't know if the callback completed already. */static intunlink1 (struct usb_hcd *hcd, struct urb *urb){	int		value;	if (urb->dev == hcd->self.root_hub)		value = usb_rh_urb_dequeue (hcd, urb);	else {		/* The only reason an HCD might fail this call is if		 * it has not yet fully queued the urb to begin with.		 * Such failures should be harmless. */		value = hcd->driver->urb_dequeue (hcd, urb);	}	if (value != 0)		dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",				urb, value);	return value;}/* * called in any context * * caller guarantees urb won't be recycled till both unlink() * and the urb's completion function return */int usb_hcd_unlink_urb (struct urb *urb, int status){	struct usb_host_endpoint	*ep;	struct usb_hcd			*hcd = NULL;	struct device			*sys = NULL;	unsigned long			flags;	struct list_head		*tmp;	int				retval;	if (!urb)		return -EINVAL;	if (!urb->dev || !urb->dev->bus)		return -ENODEV;	ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)			[usb_pipeendpoint(urb->pipe)];	if (!ep)		return -ENODEV;	/*	 * we contend for urb->status with the hcd core,	 * which changes it while returning the urb.	 *	 * Caller guaranteed that the urb pointer hasn't been freed, and	 * that it was submitted.  But as a rule it can't know whether or	 * not it's already been unlinked ... so we respect the reversed	 * lock sequence needed for the usb_hcd_giveback_urb() code paths	 * (urb lock, then hcd_data_lock) in case some other CPU is now	 * unlinking it.	 */	spin_lock_irqsave (&urb->lock, flags);	spin_lock (&hcd_data_lock);	sys = &urb->dev->dev;	hcd = bus_to_hcd(urb->dev->bus);	if (hcd == NULL) {		retval = -ENODEV;		goto done;	}	/* insist the urb is still queued */	list_for_each(tmp, &ep->urb_list) {		if (tmp == &urb->urb_list)			break;	}	if (tmp != &urb->urb_list) {		retval = -EIDRM;		goto done;	}	/* Any status except -EINPROGRESS means something already started to	 * unlink this URB from the hardware.  So there's no more work to do.	 */	if (urb->status != -EINPROGRESS) {		retval = -EBUSY;		goto done;	}	/* IRQ setup can easily be broken so that USB controllers	 * never get completion IRQs ... maybe even the ones we need to	 * finish unlinking the initial failed usb_set_address()	 * or device descriptor fetch.	 */	if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)	    && hcd->self.root_hub != urb->dev) {		dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "			"Controller is probably using the wrong IRQ."			"\n");		set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);	}	urb->status = status;	spin_unlock (&hcd_data_lock);	spin_unlock_irqrestore (&urb->lock, flags);	retval = unlink1 (hcd, urb);	if (retval == 0)		retval = -EINPROGRESS;	return retval;done:	spin_unlock (&hcd_data_lock);	spin_unlock_irqrestore (&urb->lock, flags);	if (retval != -EIDRM && sys && sys->driver)		dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);	return retval;}/*-------------------------------------------------------------------------*//* disables the endpoint: cancels any pending urbs, then synchronizes with * the hcd to make sure all endpoint state is gone from hardware, and then * waits until the endpoint's queue is completely drained. use for * set_configuration, set_interface, driver removal, physical disconnect. * * example:  a qh stored in ep->hcpriv, holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */void usb_hcd_endpoint_disable (struct usb_device *udev,		struct usb_host_endpoint *ep){	struct usb_hcd		*hcd;	struct urb		*urb;	hcd = bus_to_hcd(udev->bus);	local_irq_disable ();	/* ep is already gone from udev->ep_{in,out}[]; no more submits */rescan:	spin_lock (&hcd_data_lock);	list_for_each_entry (urb, &ep->urb_list, urb_list) {		int	tmp;		/* the urb may already have been unlinked */		if (urb->status != -EINPROGRESS)			continue;		usb_get_urb (urb);		spin_unlock (&hcd_data_lock);		spin_lock (&urb->lock);		tmp = urb->status;		if (tmp == -EINPROGRESS)			urb->status = -ESHUTDOWN;		spin_unlock (&urb->lock);		/* kick hcd unless it's already returning this */		if (tmp == -EINPROGRESS) {			tmp = urb->pipe;			unlink1 (hcd, urb);			dev_dbg (hcd->self.controller,				"shutdown urb %p pipe %08x ep%d%s%s\n",				urb, tmp, usb_pipeendpoint (tmp),				(tmp & USB_DIR_IN) ? "in" : "out",				({ char *s; \				 switch (usb_pipetype (tmp)) { \				 case PIPE_CONTROL:	s = ""; break; \				 case PIPE_BULK:	s = "-bulk"; break; \				 case PIPE_INTERRUPT:	s = "-intr"; break; \				 default: 		s = "-iso"; break; \				}; s;}));		}		usb_put_urb (urb);		/* list contents may have changed */		goto rescan;	}	spin_unlock (&hcd_data_lock);	local_irq_enable ();	/* synchronize with the hardware, so old configuration state	 * clears out immediately (and will be freed).	 */	might_sleep ();	if (hcd->driver->endpoint_disable)		hcd->driver->endpoint_disable (hcd, ep);	/* Wait until the endpoint queue is completely empty.  Most HCDs	 * will have done this already in their endpoint_disable method,	 * but some might not.  And there could be root-hub control URBs	 * still pending since they aren't affected by the HCDs'	 * endpoint_disable methods.	 */	while (!list_empty (&ep->urb_list)) {		spin_lock_irq (&hcd_data_lock);		/* The list may have changed while we acquired the spinlock */		urb = NULL;		if (!list_empty (&ep->urb_list)) {			urb = list_entry (ep->urb_list.prev, struct urb,					urb_list);			usb_get_urb (urb);		}		spin_unlock_irq (&hcd_data_lock);		if (urb) {			usb_kill_urb (urb);			usb_put_urb (urb);		}	}}/*-------------------------------------------------------------------------*/int hcd_bus_suspend (struct usb_bus *bus){	struct usb_hcd		*hcd;	int			status;	hcd = container_of (bus, struct usb_hcd, self);	if (!hcd->driver->bus_suspend)		return -ENOENT;	hcd->state = HC_STATE_QUIESCING;	status = hcd->driver->bus_suspend (hcd);	if (status == 0)		hcd->state = HC_STATE_SUSPENDED;	else		dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",				"suspend", status);	return status;}int hcd_bus_resume (struct usb_bus *bus){	struct usb_hcd		*hcd;	int			status;	hcd = container_of (bus, struct usb_hcd, self);	if (!hcd->driver->bus_resume)		return -ENOENT;	if (hcd->state == HC_STATE_RUNNING)		return 0;	hcd->state = HC_STATE_RESUMING;	status = hcd->driver->bus_resume (hcd);	if (status == 0)		hcd->state = HC_STATE_RUNNING;	else {		dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",				"resume", status);		usb_hc_died(hcd);	}	return status;}/* Workqueue routine for root-hub remote wakeup */static void hcd_resume_work(struct work_struct *work){	struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);	struct usb_device *udev = hcd->self.root_hub;	usb_lock_device(udev);	usb_mark_last_busy(udev);	usb_external_resume_device(udev);	usb_unlock_device(udev);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?