hcd.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,586 行 · 第 1/3 页

C
1,586
字号
	struct usb_hcd		*hcd = urb->dev->bus->hcpriv;	struct hcd_dev		*dev = urb->dev->hcpriv;	unsigned long		flags;	if (!hcd || !dev)		return -ENODEV;	/*	 * FIXME:  make urb timeouts be generic, keeping the HCD cores	 * as simple as possible.	 */	// NOTE:  a generic device/urb monitoring hook would go here.	// hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)	// It would catch submission paths for all urbs.	/*	 * 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);	if (unlikely (urb->reject))		status = -EPERM;	else if (HCD_IS_RUNNING (hcd->state) &&			hcd->state != USB_STATE_QUIESCING) {		usb_get_dev (urb->dev);		list_add_tail (&urb->urb_list, &dev->urb_list);		status = 0;	} else		status = -ESHUTDOWN;	spin_unlock_irqrestore (&hcd_data_lock, flags);	if (status) {		INIT_LIST_HEAD (&urb->urb_list);		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.		 */		urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP					| URB_NO_SETUP_DMA_MAP);		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.controller->dma_mask) {		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, urb, mem_flags);done:	if (unlikely (status)) {		urb_unlink (urb);		atomic_dec (&urb->use_count);		if (urb->reject)			wake_up (&usb_kill_urb_queue);		usb_put_urb (urb);	}	return status;}/*-------------------------------------------------------------------------*//* called in any context */static int hcd_get_frame_number (struct usb_device *udev){	struct usb_hcd	*hcd = (struct usb_hcd *)udev->bus->hcpriv;	if (!HCD_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 == (struct urb *) hcd->rh_timer.data)		value = usb_rh_status_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 */static int hcd_unlink_urb (struct urb *urb, int status){	struct hcd_dev			*dev;	struct usb_hcd			*hcd = NULL;	struct device			*sys = NULL;	unsigned long			flags;	struct list_head		*tmp;	int				retval;	if (!urb)		return -EINVAL;	/*	 * 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);	if (!urb->dev || !urb->dev->bus) {		retval = -ENODEV;		goto done;	}	dev = urb->dev->hcpriv;	sys = &urb->dev->dev;	hcd = urb->dev->bus->hcpriv;	if (!dev || !hcd) {		retval = -ENODEV;		goto done;	}	/* running ~= hc unlink handshake works (irq, timer, etc)	 * halted ~= no unlink handshake is needed	 * suspended, resuming == should never happen	 */	WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);	/* insist the urb is still queued */	list_for_each(tmp, &dev->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;	}	/* PCI 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().	 */	if (!hcd->saw_irq) {		dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "			"Different ACPI or APIC settings may help."			"\n");		hcd->saw_irq = 1;	}	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. use for * set_configuration, set_interface, driver removal, physical disconnect. * * example:  a qh stored in hcd_dev.ep[], holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */static void hcd_endpoint_disable (struct usb_device *udev, int endpoint){	struct hcd_dev	*dev;	struct usb_hcd	*hcd;	struct urb	*urb;	unsigned	epnum = endpoint & USB_ENDPOINT_NUMBER_MASK;	dev = udev->hcpriv;	hcd = udev->bus->hcpriv;	WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);	local_irq_disable ();rescan:	/* (re)block new requests, as best we can */	if (endpoint & USB_DIR_IN) {		usb_endpoint_halt (udev, epnum, 0);		udev->epmaxpacketin [epnum] = 0;	} else {		usb_endpoint_halt (udev, epnum, 1);		udev->epmaxpacketout [epnum] = 0;	}	/* then kill any current requests */	spin_lock (&hcd_data_lock);	list_for_each_entry (urb, &dev->urb_list, urb_list) {		int	tmp = urb->pipe;		/* ignore urbs for other endpoints */		if (usb_pipeendpoint (tmp) != epnum)			continue;		/* NOTE assumption that only ep0 is a control endpoint */		if (epnum != 0 && ((tmp ^ endpoint) & USB_DIR_IN))			continue;		/* another cpu may be in hcd, spinning on hcd_data_lock		 * to giveback() this urb.  the races here should be		 * small, but a full fix needs a new "can't submit"		 * urb state.		 */		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, dev, endpoint);}/*-------------------------------------------------------------------------*/#ifdef	CONFIG_USB_SUSPENDstatic int hcd_hub_suspend (struct usb_bus *bus){	struct usb_hcd		*hcd;	hcd = container_of (bus, struct usb_hcd, self);	if (hcd->driver->hub_suspend)		return hcd->driver->hub_suspend (hcd);	return 0;}static int hcd_hub_resume (struct usb_bus *bus){	struct usb_hcd		*hcd;	hcd = container_of (bus, struct usb_hcd, self);	if (hcd->driver->hub_resume)		return hcd->driver->hub_resume (hcd);	return 0;}#endif/*-------------------------------------------------------------------------*//* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup. * we're guaranteed that the device is fully quiesced.  also, that each * endpoint has been hcd_endpoint_disabled. */static int hcd_free_dev (struct usb_device *udev){	struct hcd_dev		*dev;	struct usb_hcd		*hcd;	unsigned long		flags;	if (!udev || !udev->hcpriv)		return -EINVAL;	if (!udev->bus || !udev->bus->hcpriv)		return -ENODEV;	// should udev->devnum == -1 ??	dev = udev->hcpriv;	hcd = udev->bus->hcpriv;	/* device driver problem with refcounts? */	if (!list_empty (&dev->urb_list)) {		dev_dbg (hcd->self.controller, "free busy dev, %s devnum %d (bug!)\n",			hcd->self.bus_name, udev->devnum);		return -EINVAL;	}	spin_lock_irqsave (&hcd_data_lock, flags);	list_del (&dev->dev_list);	udev->hcpriv = NULL;	spin_unlock_irqrestore (&hcd_data_lock, flags);	kfree (dev);	return 0;}/* * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue) * * When registering a USB bus through the HCD framework code, use this * usb_operations vector.  The PCI glue layer does so automatically; only * bus glue for non-PCI system busses will need to use this. */struct usb_operations usb_hcd_operations = {	.allocate =		hcd_alloc_dev,	.get_frame_number =	hcd_get_frame_number,	.submit_urb =		hcd_submit_urb,	.unlink_urb =		hcd_unlink_urb,	.deallocate =		hcd_free_dev,	.buffer_alloc =		hcd_buffer_alloc,	.buffer_free =		hcd_buffer_free,	.disable =		hcd_endpoint_disable,#ifdef	CONFIG_USB_SUSPEND	.hub_suspend =		hcd_hub_suspend,	.hub_resume =		hcd_hub_resume,#endif};EXPORT_SYMBOL (usb_hcd_operations);/*-------------------------------------------------------------------------*//** * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB * @urb: urb being returned to the USB device driver. * @regs: pt_regs, passed down to the URB completion handler * Context: in_interrupt() * * This hands the URB from HCD to its USB device driver, using its * completion function.  The HCD has freed all per-urb resources * (and is done using urb->hcpriv).  It also released all HCD locks; * the device driver won't cause problems if it frees, modifies, * or resubmits this URB. */void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs){	urb_unlink (urb);	// NOTE:  a generic device/urb monitoring hook would go here.	// hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)	// It would catch exit/unlink paths for all urbs.	/* lower level hcd code should use *_dma exclusively */	if (hcd->self.controller->dma_mask) {		if (usb_pipecontrol (urb->pipe)			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))			dma_unmap_single (hcd->self.controller, urb->setup_dma,					sizeof (struct usb_ctrlrequest),					DMA_TO_DEVICE);		if (urb->transfer_buffer_length != 0			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))			dma_unmap_single (hcd->self.controller, 					urb->transfer_dma,					urb->transfer_buffer_length,					usb_pipein (urb->pipe)					    ? DMA_FROM_DEVICE					    : DMA_TO_DEVICE);	}	/* pass ownership to the completion handler */	urb->complete (urb, regs);	atomic_dec (&urb->use_count);	if (unlikely (urb->reject))		wake_up (&usb_kill_urb_queue);	usb_put_urb (urb);}EXPORT_SYMBOL (usb_hcd_giveback_urb);/*-------------------------------------------------------------------------*//** * usb_hcd_irq - hook IRQs to HCD framework (bus glue) * @irq: the IRQ being raised * @__hcd: pointer to the HCD whose IRQ is beinng signaled * @r: saved hardware registers * * When registering a USB bus through the HCD framework code, use this * to handle interrupts.  The PCI glue layer does so automatically; only * bus glue for non-PCI system busses will need to use this. */irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r){	struct usb_hcd		*hcd = __hcd;	int			start = hcd->state;	if (unlikely (hcd->state == USB_STATE_HALT))	/* irq sharing? */		return IRQ_NONE;	hcd->saw_irq = 1;	if (hcd->driver->irq (hcd, r) == IRQ_NONE)		return IRQ_NONE;	if (hcd->state != start && hcd->state == USB_STATE_HALT)		usb_hc_died (hcd);	return IRQ_HANDLED;}EXPORT_SYMBOL (usb_hcd_irq);/*-------------------------------------------------------------------------*/static void hcd_panic (void *_hcd){	struct usb_hcd		*hcd = _hcd;	struct usb_device	*hub = hcd->self.root_hub;	unsigned		i;	/* hc's root hub is removed later removed in hcd->stop() */	down (&hub->serialize);	usb_set_device_state(hub, USB_STATE_NOTATTACHED);	for (i = 0; i < hub->maxchild; i++) {		if (hub->children [i])			usb_disconnect (&hub->children [i]);	}	up (&hub->serialize);}/** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller * * This is called by bus glue to report a USB host controller that died * while operations may still have been pending.  It's called automatically * by the PCI glue, so only glue for non-PCI busses should need to call it.  */void usb_hc_died (struct usb_hcd *hcd){	dev_err (hcd->self.controller, "HC died; cleaning up\n");	/* clean up old urbs and devices; needs a task context */	INIT_WORK (&hcd->work, hcd_panic, hcd);	(void) schedule_work (&hcd->work);}EXPORT_SYMBOL (usb_hc_died);

⌨️ 快捷键说明

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