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 + -
显示快捷键?