X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Fhcd.c;h=e2e00ba4e1e6dd5b20aff7dda22316cd02a39e97;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=b87aa7c06e0c6ea14304c316e3c9e4a29d63025f;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index b87aa7c06..e2e00ba4e 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -23,11 +23,6 @@ */ #include - -#ifdef CONFIG_USB_DEBUG -#define DEBUG -#endif - #include #include #include @@ -39,6 +34,7 @@ #include #include #include +#include #include #include @@ -46,6 +42,7 @@ #include "usb.h" #include "hcd.h" +#include "hub.h" // #define USB_BANDWIDTH_MESSAGES @@ -87,6 +84,7 @@ /* host controllers we manage */ LIST_HEAD (usb_bus_list); +EXPORT_SYMBOL_GPL (usb_bus_list); /* used when allocating bus numbers */ #define USB_MAXBUS 64 @@ -96,7 +94,11 @@ struct usb_busmap { static struct usb_busmap busmap; /* used when updating list of hcds */ -DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ +DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */ +EXPORT_SYMBOL_GPL (usb_bus_list_lock); + +/* used for controlling access to virtual root hubs */ +static DEFINE_SPINLOCK(hcd_root_hub_lock); /* used when updating hcd data */ static DEFINE_SPINLOCK(hcd_data_lock); @@ -124,7 +126,7 @@ static const u8 usb2_rh_dev_descriptor [18] = { 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ 0x00, 0x00, /* __le16 idVendor; */ 0x00, 0x00, /* __le16 idProduct; */ @@ -147,7 +149,7 @@ static const u8 usb11_rh_dev_descriptor [18] = { 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ 0x00, 0x00, /* __le16 idVendor; */ 0x00, 0x00, /* __le16 idProduct; */ @@ -272,6 +274,10 @@ static int ascii2utf (char *s, u8 *utf, int utfmax) *utf++ = *s++; *utf++ = 0; } + if (utfmax > 0) { + *utf = *s; + ++retval; + } return retval; } @@ -296,30 +302,40 @@ static int rh_string ( // language ids if (id == 0) { - *data++ = 4; *data++ = 3; /* 4 bytes string data */ - *data++ = 0x09; *data++ = 0x04; /* MSFT-speak for "en-us" */ - return 4; + buf[0] = 4; buf[1] = 3; /* 4 bytes string data */ + buf[2] = 0x09; buf[3] = 0x04; /* MSFT-speak for "en-us" */ + len = min (len, 4); + memcpy (data, buf, len); + return len; // serial number } else if (id == 1) { - strcpy (buf, hcd->self.bus_name); + strlcpy (buf, hcd->self.bus_name, sizeof buf); // product description } else if (id == 2) { - strcpy (buf, hcd->product_desc); + strlcpy (buf, hcd->product_desc, sizeof buf); // id 3 == vendor description } else if (id == 3) { - sprintf (buf, "%s %s %s", system_utsname.sysname, + snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname, system_utsname.release, hcd->driver->description); // unsupported IDs --> "protocol stall" } else - return 0; + return -EPIPE; - data [0] = 2 * (strlen (buf) + 1); - data [1] = 3; /* type == string */ - return 2 + ascii2utf (buf, data + 2, len - 2); + switch (len) { /* All cases fall through */ + default: + len = 2 + ascii2utf (buf, data + 2, len - 2); + case 2: + data [1] = 3; /* type == string */ + case 1: + data [0] = 2 * (strlen (buf) + 1); + case 0: + ; /* Compiler wants a statement here */ + } + return len; } @@ -328,11 +344,14 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) { struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; - const u8 *bufp = NULL; u8 *ubuf = urb->transfer_buffer; + u8 tbuf [sizeof (struct usb_hub_descriptor)]; + const u8 *bufp = tbuf; int len = 0; int patch_wakeup = 0; unsigned long flags; + int status = 0; + int n; cmd = (struct usb_ctrlrequest *) urb->setup_packet; typeReq = (cmd->bRequestType << 8) | cmd->bRequest; @@ -343,32 +362,50 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) if (wLength > urb->transfer_buffer_length) goto error; - /* set up for success */ - urb->status = 0; - urb->actual_length = wLength; + urb->actual_length = 0; switch (typeReq) { /* DEVICE REQUESTS */ + /* The root hub's remote wakeup enable bit is implemented using + * driver model wakeup flags. If this system supports wakeup + * through USB, userspace may change the default "allow wakeup" + * policy through sysfs or these calls. + * + * Most root hubs support wakeup from downstream devices, for + * runtime power management (disabling USB clocks and reducing + * VBUS power usage). However, not all of them do so; silicon, + * board, and BIOS bugs here are not uncommon, so these can't + * be treated quite like external hubs. + * + * Likewise, not all root hubs will pass wakeup events upstream, + * to wake up the whole system. So don't assume root hub and + * controller capabilities are identical. + */ + case DeviceRequest | USB_REQ_GET_STATUS: - ubuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP) + tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev) + << USB_DEVICE_REMOTE_WAKEUP) | (1 << USB_DEVICE_SELF_POWERED); - ubuf [1] = 0; + tbuf [1] = 0; + len = 2; break; case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (wValue == USB_DEVICE_REMOTE_WAKEUP) - hcd->remote_wakeup = 0; + device_set_wakeup_enable(&hcd->self.root_hub->dev, 0); else goto error; break; case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP) - hcd->remote_wakeup = 1; + if (device_can_wakeup(&hcd->self.root_hub->dev) + && wValue == USB_DEVICE_REMOTE_WAKEUP) + device_set_wakeup_enable(&hcd->self.root_hub->dev, 1); else goto error; break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: - ubuf [0] = 1; + tbuf [0] = 1; + len = 1; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: break; @@ -391,20 +428,22 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) bufp = fs_rh_config_descriptor; len = sizeof fs_rh_config_descriptor; } - if (hcd->can_wakeup) + if (device_can_wakeup(&hcd->self.root_hub->dev)) patch_wakeup = 1; break; case USB_DT_STRING << 8: - urb->actual_length = rh_string ( - wValue & 0xff, hcd, - ubuf, wLength); + n = rh_string (wValue & 0xff, hcd, ubuf, wLength); + if (n < 0) + goto error; + urb->actual_length = n; break; default: goto error; } break; case DeviceRequest | USB_REQ_GET_INTERFACE: - ubuf [0] = 0; + tbuf [0] = 0; + len = 1; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_SET_INTERFACE: break; @@ -420,8 +459,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case EndpointRequest | USB_REQ_GET_STATUS: // ENDPOINT_HALT flag - ubuf [0] = 0; - ubuf [1] = 0; + tbuf [0] = 0; + tbuf [1] = 0; + len = 2; /* FALLTHROUGH */ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_SET_FEATURE: @@ -432,28 +472,35 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) default: /* non-generic request */ - if (HCD_IS_SUSPENDED (hcd->state)) - urb->status = -EAGAIN; - else - urb->status = hcd->driver->hub_control (hcd, - typeReq, wValue, wIndex, - ubuf, wLength); + switch (typeReq) { + case GetHubStatus: + case GetPortStatus: + len = 4; + break; + case GetHubDescriptor: + len = sizeof (struct usb_hub_descriptor); + break; + } + status = hcd->driver->hub_control (hcd, + typeReq, wValue, wIndex, + tbuf, wLength); break; error: /* "protocol stall" on error */ - urb->status = -EPIPE; + status = -EPIPE; } - if (urb->status) { - urb->actual_length = 0; - if (urb->status != -EPIPE) { + + if (status) { + len = 0; + if (status != -EPIPE) { dev_dbg (hcd->self.controller, "CTRL: TypeReq=0x%x val=0x%x " "idx=0x%x len=%d ==> %d\n", typeReq, wValue, wIndex, - wLength, urb->status); + wLength, status); } } - if (bufp) { + if (len) { if (urb->transfer_buffer_length < len) len = urb->transfer_buffer_length; urb->actual_length = len; @@ -461,13 +508,19 @@ error: memcpy (ubuf, bufp, len); /* report whether RH hardware supports remote wakeup */ - if (patch_wakeup) + if (patch_wakeup && + len > offsetof (struct usb_config_descriptor, + bmAttributes)) ((struct usb_config_descriptor *)ubuf)->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } /* any errors get returned through the urb completion */ local_irq_save (flags); + spin_lock (&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = status; + spin_unlock (&urb->lock); usb_hcd_giveback_urb (hcd, urb, NULL); local_irq_restore (flags); return 0; @@ -476,119 +529,120 @@ error: /*-------------------------------------------------------------------------*/ /* - * Root Hub interrupt transfers are synthesized with a timer. - * Completions are called in_interrupt() but not in_irq(). + * Root Hub interrupt transfers are polled using a timer if the + * driver requests it; otherwise the driver is responsible for + * calling usb_hcd_poll_rh_status() when an event occurs. * - * Note: some root hubs (including common UHCI based designs) can't - * correctly issue port change IRQs. They're the ones that _need_ a - * timer; most other root hubs don't. Some systems could save a - * lot of battery power by eliminating these root hub timer IRQs. + * Completions are called in_interrupt(), but they may or may not + * be in_irq(). */ +void usb_hcd_poll_rh_status(struct usb_hcd *hcd) +{ + struct urb *urb; + int length; + unsigned long flags; + char buffer[4]; /* Any root hubs with > 31 ports? */ -static void rh_report_status (unsigned long ptr); + if (!hcd->uses_new_polling && !hcd->status_urb) + return; -static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) -{ - int len = 1 + (urb->dev->maxchild / 8); + length = hcd->driver->hub_status_data(hcd, buffer); + if (length > 0) { - /* rh_timer protected by hcd_data_lock */ - if (hcd->rh_timer.data || urb->transfer_buffer_length < len) { - dev_dbg (hcd->self.controller, - "not queuing rh status urb, stat %d\n", - urb->status); - return -EINVAL; + /* try to complete the status urb */ + local_irq_save (flags); + spin_lock(&hcd_root_hub_lock); + urb = hcd->status_urb; + if (urb) { + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) { + hcd->poll_pending = 0; + hcd->status_urb = NULL; + urb->status = 0; + urb->hcpriv = NULL; + urb->actual_length = length; + memcpy(urb->transfer_buffer, buffer, length); + } else /* urb has been unlinked */ + length = 0; + spin_unlock(&urb->lock); + } else + length = 0; + spin_unlock(&hcd_root_hub_lock); + + /* local irqs are always blocked in completions */ + if (length > 0) + usb_hcd_giveback_urb (hcd, urb, NULL); + else + hcd->poll_pending = 1; + local_irq_restore (flags); } - init_timer (&hcd->rh_timer); - hcd->rh_timer.function = rh_report_status; - hcd->rh_timer.data = (unsigned long) urb; - /* USB 2.0 spec says 256msec; this is close enough */ - hcd->rh_timer.expires = jiffies + HZ/4; - add_timer (&hcd->rh_timer); - urb->hcpriv = hcd; /* nonzero to indicate it's queued */ - return 0; + /* The USB 2.0 spec says 256 ms. This is close enough and won't + * exceed that limit if HZ is 100. */ + if (hcd->uses_new_polling ? hcd->poll_rh : + (length == 0 && hcd->status_urb != NULL)) + mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250)); } +EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); /* timer callback */ +static void rh_timer_func (unsigned long _hcd) +{ + usb_hcd_poll_rh_status((struct usb_hcd *) _hcd); +} + +/*-------------------------------------------------------------------------*/ -static void rh_report_status (unsigned long ptr) +static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) { - struct urb *urb; - struct usb_hcd *hcd; - int length = 0; + int retval; unsigned long flags; + int len = 1 + (urb->dev->maxchild / 8); + + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (urb->status != -EINPROGRESS) /* already unlinked */ + retval = urb->status; + else if (hcd->status_urb || urb->transfer_buffer_length < len) { + dev_dbg (hcd->self.controller, "not queuing rh status urb\n"); + retval = -EINVAL; + } else { + hcd->status_urb = urb; + urb->hcpriv = hcd; /* indicate it's queued */ - urb = (struct urb *) ptr; - local_irq_save (flags); - spin_lock (&urb->lock); + if (!hcd->uses_new_polling) + mod_timer (&hcd->rh_timer, jiffies + + msecs_to_jiffies(250)); - /* do nothing if the urb's been unlinked */ - if (!urb->dev - || urb->status != -EINPROGRESS - || (hcd = urb->dev->bus->hcpriv) == NULL) { - spin_unlock (&urb->lock); - local_irq_restore (flags); - return; + /* If a status change has already occurred, report it ASAP */ + else if (hcd->poll_pending) + mod_timer (&hcd->rh_timer, jiffies); + retval = 0; } - - /* complete the status urb, or retrigger the timer */ - spin_lock (&hcd_data_lock); - if (urb->dev->state == USB_STATE_CONFIGURED) { - length = hcd->driver->hub_status_data ( - hcd, urb->transfer_buffer); - if (length > 0) { - hcd->rh_timer.data = 0; - urb->actual_length = length; - urb->status = 0; - urb->hcpriv = NULL; - } else - mod_timer (&hcd->rh_timer, jiffies + HZ/4); - } - spin_unlock (&hcd_data_lock); - spin_unlock (&urb->lock); - - /* local irqs are always blocked in completions */ - if (length > 0) - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); + return retval; } -/*-------------------------------------------------------------------------*/ - static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) { - if (usb_pipeint (urb->pipe)) { - int retval; - unsigned long flags; - - spin_lock_irqsave (&hcd_data_lock, flags); - retval = rh_status_urb (hcd, urb); - spin_unlock_irqrestore (&hcd_data_lock, flags); - return retval; - } + if (usb_pipeint (urb->pipe)) + return rh_queue_status (hcd, urb); if (usb_pipecontrol (urb->pipe)) return rh_call_control (hcd, urb); - else - return -EINVAL; + return -EINVAL; } /*-------------------------------------------------------------------------*/ +/* Asynchronous unlinks of root-hub control URBs are legal, but they + * don't do anything. Status URB unlinks must be made in process context + * with interrupts enabled. + */ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { - unsigned long flags; + if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */ + if (in_interrupt()) + return 0; /* nothing to do */ - /* note: always a synchronous unlink */ - if ((unsigned long) urb == hcd->rh_timer.data) { - del_timer_sync (&hcd->rh_timer); - hcd->rh_timer.data = 0; - - local_irq_save (flags); - urb->hcpriv = NULL; - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); - - } else if (usb_pipeendpoint(urb->pipe) == 0) { spin_lock_irq(&urb->lock); /* from usb_kill_urb */ ++urb->reject; spin_unlock_irq(&urb->lock); @@ -599,8 +653,22 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) spin_lock_irq(&urb->lock); --urb->reject; spin_unlock_irq(&urb->lock); - } else - return -EINVAL; + + } else { /* Status URB */ + if (!hcd->uses_new_polling) + del_timer_sync (&hcd->rh_timer); + local_irq_disable (); + spin_lock (&hcd_root_hub_lock); + if (urb == hcd->status_urb) { + hcd->status_urb = NULL; + urb->hcpriv = NULL; + } else + urb = NULL; /* wasn't fully queued */ + spin_unlock (&hcd_root_hub_lock); + if (urb) + usb_hcd_giveback_urb (hcd, urb, NULL); + local_irq_enable (); + } return 0; } @@ -608,50 +676,45 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) /*-------------------------------------------------------------------------*/ /* exported only within usbcore */ -struct usb_bus *usb_bus_get (struct usb_bus *bus) +struct usb_bus *usb_bus_get(struct usb_bus *bus) { - struct class_device *tmp; + if (bus) + kref_get(&bus->kref); + return bus; +} - if (!bus) - return NULL; +static void usb_host_release(struct kref *kref) +{ + struct usb_bus *bus = container_of(kref, struct usb_bus, kref); - tmp = class_device_get(&bus->class_dev); - if (tmp) - return to_usb_bus(tmp); - else - return NULL; + if (bus->release) + bus->release(bus); } /* exported only within usbcore */ -void usb_bus_put (struct usb_bus *bus) +void usb_bus_put(struct usb_bus *bus) { if (bus) - class_device_put(&bus->class_dev); + kref_put(&bus->kref, usb_host_release); } /*-------------------------------------------------------------------------*/ -static void usb_host_release(struct class_device *class_dev) -{ - struct usb_bus *bus = to_usb_bus(class_dev); - - if (bus->release) - bus->release(bus); -} - -static struct class usb_host_class = { - .name = "usb_host", - .release = &usb_host_release, -}; +static struct class *usb_host_class; int usb_host_init(void) { - return class_register(&usb_host_class); + int retval = 0; + + usb_host_class = class_create(THIS_MODULE, "usb_host"); + if (IS_ERR(usb_host_class)) + retval = PTR_ERR(usb_host_class); + return retval; } void usb_host_cleanup(void) { - class_unregister(&usb_host_class); + class_destroy(usb_host_class); } /** @@ -661,7 +724,7 @@ void usb_host_cleanup(void) * This code is used to initialize a usb_bus structure, memory for which is * separately managed. */ -void usb_bus_init (struct usb_bus *bus) +static void usb_bus_init (struct usb_bus *bus) { memset (&bus->devmap, 0, sizeof(struct usb_devmap)); @@ -676,10 +739,8 @@ void usb_bus_init (struct usb_bus *bus) INIT_LIST_HEAD (&bus->bus_list); - class_device_initialize(&bus->class_dev); - bus->class_dev.class = &usb_host_class; + kref_init(&bus->kref); } -EXPORT_SYMBOL (usb_bus_init); /** * usb_alloc_bus - creates a new USB host controller structure @@ -697,15 +758,13 @@ struct usb_bus *usb_alloc_bus (struct usb_operations *op) { struct usb_bus *bus; - bus = kmalloc (sizeof *bus, GFP_KERNEL); + bus = kzalloc (sizeof *bus, GFP_KERNEL); if (!bus) return NULL; - memset(bus, 0, sizeof(struct usb_bus)); usb_bus_init (bus); bus->op = op; return bus; } -EXPORT_SYMBOL (usb_alloc_bus); /*-------------------------------------------------------------------------*/ @@ -717,41 +776,40 @@ EXPORT_SYMBOL (usb_alloc_bus); * Assigns a bus number, and links the controller into usbcore data * structures so that it can be seen by scanning the bus list. */ -int usb_register_bus(struct usb_bus *bus) +static int usb_register_bus(struct usb_bus *bus) { int busnum; - int retval; - down (&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1); if (busnum < USB_MAXBUS) { set_bit (busnum, busmap.busmap); bus->busnum = busnum; } else { printk (KERN_ERR "%s: too many buses\n", usbcore_name); - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return -E2BIG; } - snprintf(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum); - bus->class_dev.dev = bus->controller; - retval = class_device_add(&bus->class_dev); - if (retval) { + bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0), + bus->controller, "usb_host%d", busnum); + if (IS_ERR(bus->class_dev)) { clear_bit(busnum, busmap.busmap); - up(&usb_bus_list_lock); - return retval; + mutex_unlock(&usb_bus_list_lock); + return PTR_ERR(bus->class_dev); } + class_set_devdata(bus->class_dev, bus); + /* Add it to the local list of buses */ list_add (&bus->bus_list, &usb_bus_list); - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); - usbfs_add_bus (bus); + usb_notify_add_bus(bus); dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum); return 0; } -EXPORT_SYMBOL (usb_register_bus); /** * usb_deregister_bus - deregisters the USB host controller @@ -761,7 +819,7 @@ EXPORT_SYMBOL (usb_register_bus); * Recycles the bus number, and unlinks the controller from usbcore data * structures so that it won't be seen by scanning the bus list. */ -void usb_deregister_bus (struct usb_bus *bus) +static void usb_deregister_bus (struct usb_bus *bus) { dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum); @@ -770,31 +828,30 @@ void usb_deregister_bus (struct usb_bus *bus) * controller code, as well as having it call this when cleaning * itself up */ - down (&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); list_del (&bus->bus_list); - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); - usbfs_remove_bus (bus); + usb_notify_remove_bus(bus); clear_bit (bus->busnum, busmap.busmap); - class_device_unregister(&bus->class_dev); + class_device_unregister(bus->class_dev); } -EXPORT_SYMBOL (usb_deregister_bus); /** - * usb_register_root_hub - called by HCD to register its root hub - * @usb_dev: the usb root hub device to be registered. - * @parent_dev: the parent device of this root hub. + * register_root_hub - called by usb_add_hcd() to register a root hub + * @hcd: host controller for this root hub * - * The USB host controller calls this function to register the root hub - * properly with the USB subsystem. It sets up the device properly in - * the device tree and stores the root_hub pointer in the bus structure, - * then calls usb_new_device() to register the usb device. It also - * assigns the root hub's USB address (always 1). + * This function registers the root hub with the USB subsystem. It sets up + * the device properly in the device tree and then calls usb_new_device() + * to register the usb device. It also assigns the root hub's USB address + * (always 1). */ -int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev) +static int register_root_hub(struct usb_hcd *hcd) { + struct device *parent_dev = hcd->self.controller; + struct usb_device *usb_dev = hcd->self.root_hub; const int devnum = 1; int retval; @@ -805,31 +862,46 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev set_bit (devnum, usb_dev->bus->devmap.devicemap); usb_set_device_state(usb_dev, USB_STATE_ADDRESS); - down (&usb_bus_list_lock); - usb_dev->bus->root_hub = usb_dev; + mutex_lock(&usb_bus_list_lock); usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); if (retval != sizeof usb_dev->descriptor) { - usb_dev->bus->root_hub = NULL; - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", usb_dev->dev.bus_id, retval); return (retval < 0) ? retval : -EMSGSIZE; } - usb_lock_device (usb_dev); retval = usb_new_device (usb_dev); - usb_unlock_device (usb_dev); if (retval) { - usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", usb_dev->dev.bus_id, retval); } - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); + + if (retval == 0) { + spin_lock_irq (&hcd_root_hub_lock); + hcd->rh_registered = 1; + spin_unlock_irq (&hcd_root_hub_lock); + + /* Did the HC die before the root hub was registered? */ + if (hcd->state == HC_STATE_HALT) + usb_hc_died (hcd); /* This time clean up */ + } + return retval; } -EXPORT_SYMBOL (usb_register_root_hub); + +void usb_enable_root_hub_irq (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of (bus, struct usb_hcd, self); + if (hcd->driver->hub_irq_enable && !hcd->poll_rh && + hcd->state != HC_STATE_HALT) + hcd->driver->hub_irq_enable (hcd); +} /*-------------------------------------------------------------------------*/ @@ -869,9 +941,9 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) case USB_SPEED_HIGH: /* ISOC or INTR */ // FIXME adjust for input vs output if (isoc) - tmp = HS_USECS (bytecount); + tmp = HS_NSECS_ISO (bytecount); else - tmp = HS_USECS_ISO (bytecount); + tmp = HS_NSECS (bytecount); return tmp; default: pr_debug ("%s: bogus device speed!\n", usbcore_name); @@ -1033,7 +1105,6 @@ static void urb_unlink (struct urb *urb) spin_lock_irqsave (&hcd_data_lock, flags); list_del_init (&urb->urb_list); spin_unlock_irqrestore (&hcd_data_lock, flags); - usb_put_dev (urb->dev); } @@ -1042,26 +1113,17 @@ static void urb_unlink (struct urb *urb) * expects usb_submit_urb() to have sanity checked and conditioned all * inputs in the urb */ -static int hcd_submit_urb (struct urb *urb, int mem_flags) +static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags) { int status; struct usb_hcd *hcd = urb->dev->bus->hcpriv; struct usb_host_endpoint *ep; unsigned long flags; - ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out) - [usb_pipeendpoint(urb->pipe)]; - if (!hcd || !ep) + if (!hcd) 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. + usbmon_urb_submit(&hcd->self, urb); /* * Atomically queue the urb, first to our records, then to the HCD. @@ -1072,15 +1134,28 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) // FIXME: verify that quiescing hc works right (RH cleans up) spin_lock_irqsave (&hcd_data_lock, flags); - if (unlikely (urb->reject)) + 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 USB_STATE_RUNNING: - case USB_STATE_RESUMING: - usb_get_dev (urb->dev); + 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; @@ -1088,6 +1163,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) 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; } @@ -1104,8 +1180,6 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) * 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; } @@ -1140,6 +1214,7 @@ done: if (urb->reject) wake_up (&usb_kill_urb_queue); usb_put_urb (urb); + usbmon_urb_submit_error(&hcd->self, urb, status); } return status; } @@ -1150,7 +1225,7 @@ done: 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)) + if (!HC_IS_RUNNING (hcd->state)) return -ESHUTDOWN; return hcd->driver->get_frame_number (hcd); } @@ -1228,12 +1303,6 @@ static int hcd_unlink_urb (struct urb *urb, int status) 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) @@ -1257,11 +1326,12 @@ static int hcd_unlink_urb (struct urb *urb, int status) * finish unlinking the initial failed usb_set_address() * or device descriptor fetch. */ - if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) { + 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"); - hcd->saw_irq = 1; + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); } urb->status = status; @@ -1299,7 +1369,8 @@ hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) hcd = udev->bus->hcpriv; - WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT); + WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && + udev->state != USB_STATE_NOTATTACHED); local_irq_disable (); @@ -1364,28 +1435,112 @@ rescan: /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM -static int hcd_hub_suspend (struct usb_bus *bus) +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->hub_suspend) - return hcd->driver->hub_suspend (hcd); - return 0; + 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; } -static int hcd_hub_resume (struct usb_bus *bus) +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->hub_resume) - return hcd->driver->hub_resume (hcd); - return 0; + 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; } +/* + * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports + * @hcd: host controller for this root hub + * + * This call arranges that usb_hcd_resume_root_hub() is safe to call later; + * that the HCD's root hub polling is deactivated; and that the root's hub + * driver is suspended. HCDs may call this to autosuspend when their root + * hub's downstream ports are all inactive: unpowered, disconnected, + * disabled, or suspended. + * + * The HCD will autoresume on device connect change detection (using SRP + * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling + * from any ports that are suspended (if that is enabled). In most cases, + * overcurrent signaling (on powered ports) will also start autoresume. + * + * Always called with IRQs blocked. + */ +void usb_hcd_suspend_root_hub (struct usb_hcd *hcd) +{ + struct urb *urb; + + spin_lock (&hcd_root_hub_lock); + usb_suspend_root_hub (hcd->self.root_hub); + + /* force status urb to complete/unlink while suspended */ + if (hcd->status_urb) { + urb = hcd->status_urb; + urb->status = -ECONNRESET; + urb->hcpriv = NULL; + urb->actual_length = 0; + + del_timer (&hcd->rh_timer); + hcd->poll_pending = 0; + hcd->status_urb = NULL; + } else + urb = NULL; + spin_unlock (&hcd_root_hub_lock); + hcd->state = HC_STATE_SUSPENDED; + + if (urb) + usb_hcd_giveback_urb (hcd, urb, NULL); +} +EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub); + +/** + * usb_hcd_resume_root_hub - called by HCD to resume its root hub + * @hcd: host controller for this root hub + * + * The USB host controller calls this function when its root hub is + * suspended (with the remote wakeup feature enabled) and a remote + * wakeup request is received. It queues a request for khubd to + * resume the root hub (that is, manage its downstream ports again). + */ +void usb_hcd_resume_root_hub (struct usb_hcd *hcd) +{ + unsigned long flags; + + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (hcd->rh_registered) + usb_resume_root_hub (hcd->self.root_hub); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); +} +EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); + #endif /*-------------------------------------------------------------------------*/ @@ -1395,7 +1550,7 @@ static int hcd_hub_resume (struct usb_bus *bus) /** * 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 + * @port_num: 1-based number of port; usually bus->otg_port * Context: in_interrupt() * * Starts enumeration, with an immediate reset followed later by @@ -1439,10 +1594,6 @@ static struct usb_operations usb_hcd_operations = { .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 }; /*-------------------------------------------------------------------------*/ @@ -1462,14 +1613,13 @@ static struct usb_operations usb_hcd_operations = { */ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) { - urb_unlink (urb); + int at_root_hub; - // 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. + at_root_hub = (urb->dev == hcd->self.root_hub); + urb_unlink (urb); /* lower level hcd code should use *_dma exclusively */ - if (hcd->self.controller->dma_mask) { + if (hcd->self.controller->dma_mask && !at_root_hub) { if (usb_pipecontrol (urb->pipe) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) dma_unmap_single (hcd->self.controller, urb->setup_dma, @@ -1485,6 +1635,7 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs : DMA_TO_DEVICE); } + usbmon_urb_complete (&hcd->self, urb); /* pass ownership to the completion handler */ urb->complete (urb, regs); atomic_dec (&urb->use_count); @@ -1499,29 +1650,29 @@ 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 + * @__hcd: pointer to the HCD whose IRQ is being 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. + * If the controller isn't HALTed, calls the driver's irq handler. + * Checks whether the controller is now dead. */ 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) + if (unlikely(start == HC_STATE_HALT || + !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) 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) + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + if (unlikely(hcd->state == HC_STATE_HALT)) usb_hc_died (hcd); return IRQ_HANDLED; } -EXPORT_SYMBOL (usb_hcd_irq); /*-------------------------------------------------------------------------*/ @@ -1535,12 +1686,22 @@ EXPORT_SYMBOL (usb_hcd_irq); */ void usb_hc_died (struct usb_hcd *hcd) { + unsigned long flags; + 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); + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (hcd->rh_registered) { + hcd->poll_rh = 0; + + /* make khubd clean up old urbs and devices */ + usb_set_device_state (hcd->self.root_hub, + USB_STATE_NOTATTACHED); + usb_kick_khubd (hcd->self.root_hub); + } + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } +EXPORT_SYMBOL_GPL (usb_hc_died); /*-------------------------------------------------------------------------*/ @@ -1555,6 +1716,8 @@ static void hcd_release (struct usb_bus *bus) /** * usb_create_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd + * @dev: device for this HC, stored in hcd->self.controller + * @bus_name: value to store in hcd->self.bus_name * Context: !in_interrupt() * * Allocate a struct usb_hcd, with extra space at the end for the @@ -1563,25 +1726,32 @@ static void hcd_release (struct usb_bus *bus) * * If memory is unavailable, returns NULL. */ -struct usb_hcd *usb_create_hcd (const struct hc_driver *driver) +struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, + struct device *dev, char *bus_name) { struct usb_hcd *hcd; - hcd = kcalloc(1, sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); - if (!hcd) + hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); + if (!hcd) { + dev_dbg (dev, "hcd alloc failed\n"); return NULL; + } + dev_set_drvdata(dev, hcd); usb_bus_init(&hcd->self); hcd->self.op = &usb_hcd_operations; hcd->self.hcpriv = hcd; hcd->self.release = &hcd_release; + hcd->self.controller = dev; + hcd->self.bus_name = bus_name; init_timer(&hcd->rh_timer); + hcd->rh_timer.function = rh_timer_func; + hcd->rh_timer.data = (unsigned long) hcd; hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller"; - hcd->state = USB_STATE_HALT; return hcd; } @@ -1589,6 +1759,207 @@ EXPORT_SYMBOL (usb_create_hcd); void usb_put_hcd (struct usb_hcd *hcd) { + dev_set_drvdata(hcd->self.controller, NULL); usb_bus_put(&hcd->self); } EXPORT_SYMBOL (usb_put_hcd); + +/** + * usb_add_hcd - finish generic HCD structure initialization and register + * @hcd: the usb_hcd structure to initialize + * @irqnum: Interrupt line to allocate + * @irqflags: Interrupt type flags + * + * Finish the remaining parts of generic HCD initialization: allocate the + * buffers of consistent memory, register the bus, request the IRQ line, + * and call the driver's reset() and start() routines. + */ +int usb_add_hcd(struct usb_hcd *hcd, + unsigned int irqnum, unsigned long irqflags) +{ + int retval; + struct usb_device *rhdev; + + dev_info(hcd->self.controller, "%s\n", hcd->product_desc); + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* HC is in reset state, but accessible. Now do the one-time init, + * bottom up so that hcds can customize the root hubs before khubd + * starts talking to them. (Note, bus id is assigned early too.) + */ + if ((retval = hcd_buffer_create(hcd)) != 0) { + dev_dbg(hcd->self.controller, "pool alloc failed\n"); + return retval; + } + + if ((retval = usb_register_bus(&hcd->self)) < 0) + goto err_register_bus; + + if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { + dev_err(hcd->self.controller, "unable to allocate root hub\n"); + retval = -ENOMEM; + goto err_allocate_root_hub; + } + rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : + USB_SPEED_FULL; + hcd->self.root_hub = rhdev; + + /* wakeup flag init defaults to "everything works" for root hubs, + * but drivers can override it in reset() if needed, along with + * recording the overall controller's system wakeup capability. + */ + device_init_wakeup(&rhdev->dev, 1); + + /* "reset" is misnamed; its role is now one-time init. the controller + * should already have been reset (and boot firmware kicked off etc). + */ + if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { + dev_err(hcd->self.controller, "can't setup\n"); + goto err_hcd_driver_setup; + } + + /* NOTE: root hub and controller capabilities may not be the same */ + if (device_can_wakeup(hcd->self.controller) + && device_can_wakeup(&hcd->self.root_hub->dev)) + dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); + + /* enable irqs just before we start the controller */ + if (hcd->driver->irq) { + char buf[8], *bufp = buf; + +#ifdef __sparc__ + bufp = __irq_itoa(irqnum); +#else + sprintf(buf, "%d", irqnum); +#endif + + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", + hcd->driver->description, hcd->self.busnum); + if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, + hcd->irq_descr, hcd)) != 0) { + dev_err(hcd->self.controller, + "request interrupt %s failed\n", bufp); + goto err_request_irq; + } + hcd->irq = irqnum; + dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp, + (hcd->driver->flags & HCD_MEMORY) ? + "io mem" : "io base", + (unsigned long long)hcd->rsrc_start); + } else { + hcd->irq = -1; + if (hcd->rsrc_start) + dev_info(hcd->self.controller, "%s 0x%08llx\n", + (hcd->driver->flags & HCD_MEMORY) ? + "io mem" : "io base", + (unsigned long long)hcd->rsrc_start); + } + + if ((retval = hcd->driver->start(hcd)) < 0) { + dev_err(hcd->self.controller, "startup error %d\n", retval); + goto err_hcd_driver_start; + } + + /* starting here, usbcore will pay attention to this root hub */ + rhdev->bus_mA = min(500u, hcd->power_budget); + if ((retval = register_root_hub(hcd)) != 0) + goto err_register_root_hub; + + if (hcd->uses_new_polling && hcd->poll_rh) + usb_hcd_poll_rh_status(hcd); + return retval; + +err_register_root_hub: + hcd->driver->stop(hcd); +err_hcd_driver_start: + if (hcd->irq >= 0) + free_irq(irqnum, hcd); +err_request_irq: +err_hcd_driver_setup: + hcd->self.root_hub = NULL; + usb_put_dev(rhdev); +err_allocate_root_hub: + usb_deregister_bus(&hcd->self); +err_register_bus: + hcd_buffer_destroy(hcd); + return retval; +} +EXPORT_SYMBOL (usb_add_hcd); + +/** + * usb_remove_hcd - shutdown processing for generic HCDs + * @hcd: the usb_hcd structure to remove + * Context: !in_interrupt() + * + * Disconnects the root hub, then reverses the effects of usb_add_hcd(), + * invoking the HCD's stop() method. + */ +void usb_remove_hcd(struct usb_hcd *hcd) +{ + dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); + + if (HC_IS_RUNNING (hcd->state)) + hcd->state = HC_STATE_QUIESCING; + + dev_dbg(hcd->self.controller, "roothub graceful disconnect\n"); + spin_lock_irq (&hcd_root_hub_lock); + hcd->rh_registered = 0; + spin_unlock_irq (&hcd_root_hub_lock); + + mutex_lock(&usb_bus_list_lock); + usb_disconnect(&hcd->self.root_hub); + mutex_unlock(&usb_bus_list_lock); + + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + + hcd->driver->stop(hcd); + hcd->state = HC_STATE_HALT; + + if (hcd->irq >= 0) + free_irq(hcd->irq, hcd); + usb_deregister_bus(&hcd->self); + hcd_buffer_destroy(hcd); +} +EXPORT_SYMBOL (usb_remove_hcd); + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_MON) + +struct usb_mon_operations *mon_ops; + +/* + * The registration is unlocked. + * We do it this way because we do not want to lock in hot paths. + * + * Notice that the code is minimally error-proof. Because usbmon needs + * symbols from usbcore, usbcore gets referenced and cannot be unloaded first. + */ + +int usb_mon_register (struct usb_mon_operations *ops) +{ + + if (mon_ops) + return -EBUSY; + + mon_ops = ops; + mb(); + return 0; +} +EXPORT_SYMBOL_GPL (usb_mon_register); + +void usb_mon_deregister (void) +{ + + if (mon_ops == NULL) { + printk(KERN_ERR "USB: monitor was not registered\n"); + return; + } + mon_ops = NULL; + mb(); +} +EXPORT_SYMBOL_GPL (usb_mon_deregister); + +#endif /* CONFIG_USB_MON */