hcd.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,625 行 · 第 1/4 页

C
1,625
字号
	 */	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)		udev->epmaxpacketin [epnum] = 0;	else		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/*-------------------------------------------------------------------------*/#ifdef	CONFIG_USB_OTG/** * usb_bus_start_enum - start immediate enumeration (for OTG) * @bus: the bus (must use hcd framework) * @port: 1-based number of port; usually bus->otg_port * Context: in_interrupt() * * Starts enumeration, with an immediate reset followed later by * khubd identifying and possibly configuring the device. * This is needed by OTG controller drivers, where it helps meet * HNP protocol timing requirements for starting a port reset. */int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num){	struct usb_hcd		*hcd;	int			status = -EOPNOTSUPP;	/* NOTE: since HNP can't start by grabbing the bus's address0_sem,	 * boards with root hubs hooked up to internal devices (instead of	 * just the OTG port) may need more attention to resetting...	 */	hcd = container_of (bus, struct usb_hcd, self);	if (port_num && hcd->driver->start_port_reset)		status = hcd->driver->start_port_reset(hcd, port_num);	/* run khubd shortly after (first) root port reset finishes;	 * it may issue others, until at least 50 msecs have passed.	 */	if (status == 0)		mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10));	return status;}EXPORT_SYMBOL (usb_bus_start_enum);#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 + -
显示快捷键?