X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Fhcd.c;h=b87aa7c06e0c6ea14304c316e3c9e4a29d63025f;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=d970e2c0c066d7edcb66d900202216a60fed92f0;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d970e2c0c..b87aa7c06 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -33,12 +33,13 @@ #include #include #include -#include /* for UTS_SYSNAME */ +#include #include #include #include #include #include +#include #include #include @@ -86,7 +87,6 @@ /* host controllers we manage */ LIST_HEAD (usb_bus_list); -EXPORT_SYMBOL_GPL (usb_bus_list); /* used when allocating bus numbers */ #define USB_MAXBUS 64 @@ -97,10 +97,9 @@ static struct usb_busmap busmap; /* used when updating list of hcds */ DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ -EXPORT_SYMBOL_GPL (usb_bus_list_lock); /* used when updating hcd data */ -static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(hcd_data_lock); /* wait queue for synchronous unlinks */ DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); @@ -120,16 +119,16 @@ DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); static const u8 usb2_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ - 0x00, 0x02, /* __u16 bcdUSB; v2.0 */ + 0x00, 0x02, /* __le16 bcdUSB; v2.0 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, 0x00, /* __u16 idVendor; */ - 0x00, 0x00, /* __u16 idProduct; */ - KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */ + 0x00, 0x00, /* __le16 idVendor; */ + 0x00, 0x00, /* __le16 idProduct; */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ @@ -143,16 +142,16 @@ static const u8 usb2_rh_dev_descriptor [18] = { static const u8 usb11_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ - 0x10, 0x01, /* __u16 bcdUSB; v1.1 */ + 0x10, 0x01, /* __le16 bcdUSB; v1.1 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, 0x00, /* __u16 idVendor; */ - 0x00, 0x00, /* __u16 idProduct; */ - KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */ + 0x00, 0x00, /* __le16 idVendor; */ + 0x00, 0x00, /* __le16 idProduct; */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ @@ -170,7 +169,7 @@ static const u8 fs_rh_config_descriptor [] = { /* one configuration */ 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* __u16 wTotalLength; */ + 0x19, 0x00, /* __le16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ @@ -208,7 +207,7 @@ static const u8 fs_rh_config_descriptor [] = { 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ 0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */ }; @@ -217,7 +216,7 @@ static const u8 hs_rh_config_descriptor [] = { /* one configuration */ 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* __u16 wTotalLength; */ + 0x19, 0x00, /* __le16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ @@ -255,7 +254,7 @@ static const u8 hs_rh_config_descriptor [] = { 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ }; @@ -311,8 +310,8 @@ static int rh_string ( // id 3 == vendor description } else if (id == 3) { - sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, - hcd->description); + sprintf (buf, "%s %s %s", system_utsname.sysname, + system_utsname.release, hcd->driver->description); // unsupported IDs --> "protocol stall" } else @@ -435,8 +434,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) /* non-generic request */ if (HCD_IS_SUSPENDED (hcd->state)) urb->status = -EAGAIN; - else if (!HCD_IS_RUNNING (hcd->state)) - urb->status = -ENODEV; else urb->status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, @@ -445,13 +442,16 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) error: /* "protocol stall" on error */ urb->status = -EPIPE; - dev_dbg (hcd->self.controller, "unsupported hub control message (maxchild %d)\n", - urb->dev->maxchild); } if (urb->status) { urb->actual_length = 0; - 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); + if (urb->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); + } } if (bufp) { if (urb->transfer_buffer_length < len) @@ -478,6 +478,11 @@ error: /* * Root Hub interrupt transfers are synthesized with a timer. * Completions are called in_interrupt() but not in_irq(). + * + * 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. */ static void rh_report_status (unsigned long ptr); @@ -487,10 +492,7 @@ static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) int len = 1 + (urb->dev->maxchild / 8); /* rh_timer protected by hcd_data_lock */ - if (hcd->rh_timer.data - || urb->status != -EINPROGRESS - || urb->transfer_buffer_length < len - || !HCD_IS_RUNNING (hcd->state)) { + 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); @@ -523,25 +525,25 @@ static void rh_report_status (unsigned long ptr) /* do nothing if the urb's been unlinked */ if (!urb->dev || urb->status != -EINPROGRESS - || (hcd = urb->dev->bus->hcpriv) == 0) { + || (hcd = urb->dev->bus->hcpriv) == NULL) { spin_unlock (&urb->lock); local_irq_restore (flags); return; } - if (!HCD_IS_SUSPENDED (hcd->state)) - length = hcd->driver->hub_status_data ( - hcd, urb->transfer_buffer); - /* complete the status urb, or retrigger the timer */ spin_lock (&hcd_data_lock); - 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); + 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); @@ -572,18 +574,34 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) /*-------------------------------------------------------------------------*/ -int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb) +static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { unsigned long flags; /* note: always a synchronous unlink */ - del_timer_sync (&hcd->rh_timer); - hcd->rh_timer.data = 0; + 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); + + wait_event(usb_kill_urb_queue, + atomic_read(&urb->use_count) == 0); + + spin_lock_irq(&urb->lock); + --urb->reject; + spin_unlock_irq(&urb->lock); + } else + return -EINVAL; - local_irq_save (flags); - urb->hcpriv = NULL; - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); return 0; } @@ -657,6 +675,9 @@ void usb_bus_init (struct usb_bus *bus) bus->bandwidth_isoc_reqs = 0; INIT_LIST_HEAD (&bus->bus_list); + + class_device_initialize(&bus->class_dev); + bus->class_dev.class = &usb_host_class; } EXPORT_SYMBOL (usb_bus_init); @@ -713,9 +734,8 @@ int usb_register_bus(struct usb_bus *bus) } snprintf(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum); - bus->class_dev.class = &usb_host_class; bus->class_dev.dev = bus->controller; - retval = class_device_register(&bus->class_dev); + retval = class_device_add(&bus->class_dev); if (retval) { clear_bit(busnum, busmap.busmap); up(&usb_bus_list_lock); @@ -788,7 +808,7 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev down (&usb_bus_list_lock); usb_dev->bus->root_hub = usb_dev; - usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64; + 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; @@ -798,9 +818,9 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev return (retval < 0) ? retval : -EMSGSIZE; } - down (&usb_dev->serialize); + usb_lock_device (usb_dev); retval = usb_new_device (usb_dev); - up (&usb_dev->serialize); + 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", @@ -999,40 +1019,6 @@ EXPORT_SYMBOL (usb_release_bandwidth); /*-------------------------------------------------------------------------*/ -/* called from khubd, or root hub init threads for hcd-private init */ -static int hcd_alloc_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; - hcd = udev->bus->hcpriv; - if (hcd->state == USB_STATE_QUIESCING) - return -ENOLINK; - - dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; - memset (dev, 0, sizeof *dev); - - INIT_LIST_HEAD (&dev->dev_list); - INIT_LIST_HEAD (&dev->urb_list); - - spin_lock_irqsave (&hcd_data_lock, flags); - list_add (&dev->dev_list, &hcd->dev_list); - // refcount is implicit - udev->hcpriv = dev; - spin_unlock_irqrestore (&hcd_data_lock, flags); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - static void urb_unlink (struct urb *urb) { unsigned long flags; @@ -1060,10 +1046,12 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) { int status; struct usb_hcd *hcd = urb->dev->bus->hcpriv; - struct hcd_dev *dev = urb->dev->hcpriv; + struct usb_host_endpoint *ep; unsigned long flags; - if (!hcd || !dev) + ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!hcd || !ep) return -ENODEV; /* @@ -1086,13 +1074,17 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) 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) { + else switch (hcd->state) { + case USB_STATE_RUNNING: + case USB_STATE_RESUMING: usb_get_dev (urb->dev); - list_add_tail (&urb->urb_list, &dev->urb_list); + list_add_tail (&urb->urb_list, &ep->urb_list); status = 0; - } else + break; + default: status = -ESHUTDOWN; + break; + } spin_unlock_irqrestore (&hcd_data_lock, flags); if (status) { INIT_LIST_HEAD (&urb->urb_list); @@ -1140,7 +1132,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) : DMA_TO_DEVICE); } - status = hcd->driver->urb_enqueue (hcd, urb, mem_flags); + status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags); done: if (unlikely (status)) { urb_unlink (urb); @@ -1175,8 +1167,8 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) { int value; - if (urb == (struct urb *) hcd->rh_timer.data) - value = usb_rh_status_dequeue (hcd, urb); + if (urb->dev == hcd->self.root_hub) + value = usb_rh_urb_dequeue (hcd, urb); else { /* The only reason an HCD might fail this call is if @@ -1199,7 +1191,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) */ static int hcd_unlink_urb (struct urb *urb, int status) { - struct hcd_dev *dev; + struct usb_host_endpoint *ep; struct usb_hcd *hcd = NULL; struct device *sys = NULL; unsigned long flags; @@ -1208,6 +1200,12 @@ static int hcd_unlink_urb (struct urb *urb, int status) 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, @@ -1223,15 +1221,9 @@ static int hcd_unlink_urb (struct urb *urb, int status) 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) { + if (hcd == NULL) { retval = -ENODEV; goto done; } @@ -1243,7 +1235,7 @@ static int hcd_unlink_urb (struct urb *urb, int status) 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) { + list_for_each(tmp, &ep->urb_list) { if (tmp == &urb->urb_list) break; } @@ -1260,13 +1252,14 @@ static int hcd_unlink_urb (struct urb *urb, int status) goto done; } - /* PCI IRQ setup can easily be broken so that USB controllers + /* 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(). + * finish unlinking the initial failed usb_set_address() + * or device descriptor fetch. */ - if (!hcd->saw_irq) { + if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " - "Different ACPI or APIC settings may help." + "Controller is probably using the wrong IRQ." "\n"); hcd->saw_irq = 1; } @@ -1295,46 +1288,36 @@ done: * 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 + * example: a qh stored in ep->hcpriv, holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */ -static void hcd_endpoint_disable (struct usb_device *udev, int endpoint) +static void +hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) { - struct hcd_dev *dev; - struct usb_hcd *hcd; - struct urb *urb; - unsigned epnum = endpoint & USB_ENDPOINT_NUMBER_MASK; + struct usb_hcd *hcd; + struct urb *urb; - 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; + /* FIXME move most of this into message.c as part of its + * endpoint disable logic + */ - /* then kill any current requests */ + /* ep is already gone from udev->ep_{in,out}[]; no more submits */ +rescan: 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; + 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; @@ -1376,7 +1359,7 @@ rescan: */ might_sleep (); if (hcd->driver->endpoint_disable) - hcd->driver->endpoint_disable (hcd, dev, endpoint); + hcd->driver->endpoint_disable (hcd, ep); } /*-------------------------------------------------------------------------*/ @@ -1446,57 +1429,13 @@ EXPORT_SYMBOL (usb_bus_start_enum); /*-------------------------------------------------------------------------*/ -/* 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, +static struct usb_operations usb_hcd_operations = { .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, @@ -1505,7 +1444,6 @@ struct usb_operations usb_hcd_operations = { .hub_resume = hcd_hub_resume, #endif }; -EXPORT_SYMBOL (usb_hcd_operations); /*-------------------------------------------------------------------------*/ @@ -1573,13 +1511,12 @@ 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? */ + if (start == USB_STATE_HALT) return IRQ_NONE; - - hcd->saw_irq = 1; 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; @@ -1588,22 +1525,6 @@ 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 @@ -1616,9 +1537,58 @@ 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); + /* 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_hc_died); +EXPORT_SYMBOL (usb_create_hcd); +void usb_put_hcd (struct usb_hcd *hcd) +{ + usb_bus_put(&hcd->self); +} +EXPORT_SYMBOL (usb_put_hcd);