⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hcd.c

📁 h内核
💻 C
📖 第 1 页 / 共 4 页
字号:
	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 = urb->dev->bus->hcpriv;	if (hcd == NULL) {		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, &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 (!hcd->saw_irq && hcd->self.root_hub != urb->dev) {		dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "			"Controller is probably using the wrong IRQ."			"\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 ep->hcpriv, holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */static voidhcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep){	struct usb_hcd		*hcd;	struct urb		*urb;	hcd = udev->bus->hcpriv;	WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);	local_irq_disable ();	/* FIXME move most of this into message.c as part of its	 * endpoint disable logic	 */	/* 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;		/* 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.		 * FIXME urb->reject should allow that...		 */		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);}/*-------------------------------------------------------------------------*/#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/*-------------------------------------------------------------------------*//* * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue) */static struct usb_operations usb_hcd_operations = {	.get_frame_number =	hcd_get_frame_number,	.submit_urb =		hcd_submit_urb,	.unlink_urb =		hcd_unlink_urb,	.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};/*-------------------------------------------------------------------------*//** * 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 (start == USB_STATE_HALT)		return IRQ_NONE;	if (hcd->driver->irq (hcd, r) == IRQ_NONE)		return IRQ_NONE;	hcd->saw_irq = 1;	if (hcd->state != start && hcd->state == USB_STATE_HALT)		usb_hc_died (hcd);	return IRQ_HANDLED;}EXPORT_SYMBOL (usb_hcd_irq);/*-------------------------------------------------------------------------*//** * 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");	/* make khubd clean up old urbs and devices */	usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED);	mod_timer(&hcd->rh_timer, jiffies);}/*-------------------------------------------------------------------------*/static void hcd_release (struct usb_bus *bus){	struct usb_hcd *hcd;	hcd = container_of(bus, struct usb_hcd, self);	kfree(hcd);}/** * usb_create_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd * Context: !in_interrupt() * * Allocate a struct usb_hcd, with extra space at the end for the * HC driver's private data.  Initialize the generic members of the * hcd structure. * * If memory is unavailable, returns NULL. */struct usb_hcd *usb_create_hcd (const struct hc_driver *driver){	struct usb_hcd *hcd;	hcd = kcalloc(1, sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);	if (!hcd)		return NULL;	usb_bus_init(&hcd->self);	hcd->self.op = &usb_hcd_operations;	hcd->self.hcpriv = hcd;	hcd->self.release = &hcd_release;	init_timer(&hcd->rh_timer);	hcd->driver = driver;	hcd->product_desc = (driver->product_desc) ? driver->product_desc :			"USB Host Controller";	hcd->state = USB_STATE_HALT;	return hcd;}EXPORT_SYMBOL (usb_create_hcd);void usb_put_hcd (struct usb_hcd *hcd){	usb_bus_put(&hcd->self);}EXPORT_SYMBOL (usb_put_hcd);

⌨️ 快捷键说明

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