# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.537 -> 1.538 # drivers/usb/core/hub.h 1.9 -> 1.10 # include/linux/usb.h 1.30 -> 1.31 # drivers/usb/core/hub.c 1.25 -> 1.26 # drivers/usb/core/usb.c 1.50 -> 1.51 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/05/11 david-b@pacbell.net 1.538 # [PATCH] -- hub/tt error recovery # # This patch adds missing functionality to the transaction translator # support for USB 2.0 hubs: # # - moves the 'struct usb_tt' definition to "hub.h" from # - adds state to it as neeed for some control/bulk error recovery # - teaches the hub driver how to use that state (via keventd) # - adds a call letting HCDs trigger that recovery # -------------------------------------------- # diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c Sat May 11 22:29:25 2002 +++ b/drivers/usb/core/hub.c Sat May 11 22:29:25 2002 @@ -149,6 +149,98 @@ spin_unlock_irqrestore(&hub_event_lock, flags); } +/* USB 2.0 spec Section 11.24.2.3 */ +static inline int +hub_clear_tt_buffer (struct usb_device *hub, u16 devinfo, u16 tt) +{ + return usb_control_msg (hub, usb_rcvctrlpipe (hub, 0), + HUB_CLEAR_TT_BUFFER, USB_DIR_IN | USB_RECIP_OTHER, + devinfo, tt, 0, 0, HZ); +} + +/* + * enumeration blocks khubd for a long time. we use keventd instead, since + * long blocking there is the exception, not the rule. accordingly, HCDs + * talking to TTs must queue control transfers (not just bulk and iso), so + * both can talk to the same hub concurrently. + */ +static void hub_tt_kevent (void *arg) +{ + struct usb_hub *hub = arg; + unsigned long flags; + + spin_lock_irqsave (&hub->tt.lock, flags); + while (!list_empty (&hub->tt.clear_list)) { + struct list_head *temp; + struct usb_tt_clear *clear; + int status; + + temp = hub->tt.clear_list.next; + clear = list_entry (temp, struct usb_tt_clear, clear_list); + list_del (&clear->clear_list); + + /* drop lock so HCD can concurrently report other TT errors */ + spin_unlock_irqrestore (&hub->tt.lock, flags); + status = hub_clear_tt_buffer (hub->dev, + clear->devinfo, clear->tt); + spin_lock_irqsave (&hub->tt.lock, flags); + + if (status) + err ("usb-%s-%s clear tt %d (%04x) error %d", + hub->dev->bus->bus_name, hub->dev->devpath, + clear->tt, clear->devinfo, status); + kfree (clear); + } + spin_unlock_irqrestore (&hub->tt.lock, flags); +} + +/** + * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub + * @dev: the device whose split transaction failed + * @pipe: identifies the endpoint of the failed transaction + * + * High speed HCDs use this to tell the hub driver that some split control or + * bulk transaction failed in a way that requires clearing internal state of + * a transaction translator. This is normally detected (and reported) from + * interrupt context. + * + * It may not be possible for that hub to handle additional full (or low) + * speed transactions until that state is fully cleared out. + */ +void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe) +{ + struct usb_tt *tt = dev->tt; + unsigned long flags; + struct usb_tt_clear *clear; + + /* we've got to cope with an arbitrary number of pending TT clears, + * since each TT has "at least two" buffers that can need it (and + * there can be many TTs per hub). even if they're uncommon. + */ + if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) { + err ("can't save CLEAR_TT_BUFFER state for hub at usb-%s-%s", + dev->bus->bus_name, tt->hub->devpath); + /* FIXME recover somehow ... RESET_TT? */ + return; + } + + /* info that CLEAR_TT_BUFFER needs */ + clear->tt = tt->multi ? dev->ttport : 1; + clear->devinfo = usb_pipeendpoint (pipe); + clear->devinfo |= dev->devnum << 4; + clear->devinfo |= usb_pipecontrol (pipe) + ? (USB_ENDPOINT_XFER_CONTROL << 11) + : (USB_ENDPOINT_XFER_BULK << 11); + if (usb_pipein (pipe)) + clear->devinfo |= 1 << 15; + + /* tell keventd to clear state for this TT */ + spin_lock_irqsave (&tt->lock, flags); + list_add_tail (&clear->clear_list, &tt->clear_list); + schedule_task (&tt->kevent); + spin_unlock_irqrestore (&tt->lock, flags); +} + static void usb_hub_power_on(struct usb_hub *hub) { int i; @@ -231,6 +323,9 @@ break; } + spin_lock_init (&hub->tt.lock); + INIT_LIST_HEAD (&hub->tt.clear_list); + INIT_TQUEUE (&hub->tt.kevent, hub_tt_kevent, hub); switch (dev->descriptor.bDeviceProtocol) { case 0: break; @@ -431,6 +526,10 @@ down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */ up(&hub->khubd_sem); + + /* assuming we used keventd, it must quiesce too */ + if (hub->tt.hub) + flush_scheduled_tasks (); if (hub->urb) { usb_unlink_urb(hub->urb); diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h Sat May 11 22:29:25 2002 +++ b/drivers/usb/core/hub.h Sat May 11 22:29:25 2002 @@ -136,6 +136,34 @@ struct usb_device; +/* + * As of USB 2.0, full/low speed devices are segregated into trees. + * One type grows from USB 1.1 host controllers (OHCI, UHCI etc). + * The other type grows from high speed hubs when they connect to + * full/low speed devices using "Transaction Translators" (TTs). + * + * TTs should only be known to the hub driver, and high speed bus + * drivers (only EHCI for now). They affect periodic scheduling and + * sometimes control/bulk error recovery. + */ +struct usb_tt { + struct usb_device *hub; /* upstream highspeed hub */ + int multi; /* true means one TT per port */ + + /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ + spinlock_t lock; + struct list_head clear_list; /* of usb_tt_clear */ + struct tq_struct kevent; +}; + +struct usb_tt_clear { + struct list_head clear_list; + unsigned tt; + u16 devinfo; +}; + +extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe); + struct usb_hub { struct usb_device *dev; /* the "real" device */ struct urb *urb; /* for interrupt polling pipe */ diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Sat May 11 22:29:25 2002 +++ b/drivers/usb/core/usb.c Sat May 11 22:29:25 2002 @@ -2743,9 +2743,8 @@ /* * USB may be built into the kernel or be built as modules. - * If the USB core [and maybe a host controller driver] is built - * into the kernel, and other device drivers are built as modules, - * then these symbols need to be exported for the modules to use. + * These symbols are exported for device (or host controller) + * driver modules to use. */ EXPORT_SYMBOL(usb_ifnum_to_ifpos); EXPORT_SYMBOL(usb_ifnum_to_if); @@ -2762,6 +2761,7 @@ EXPORT_SYMBOL(usb_alloc_dev); EXPORT_SYMBOL(usb_free_dev); +EXPORT_SYMBOL(usb_hub_tt_clear_buffer); EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum); EXPORT_SYMBOL(usb_driver_claim_interface); @@ -2799,6 +2799,5 @@ EXPORT_SYMBOL(usb_set_configuration); EXPORT_SYMBOL(usb_set_interface); -EXPORT_SYMBOL(usb_make_path); EXPORT_SYMBOL(usb_devfs_handle); MODULE_LICENSE("GPL"); diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Sat May 11 22:29:25 2002 +++ b/include/linux/usb.h Sat May 11 22:29:25 2002 @@ -363,22 +363,6 @@ extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len); -/* - * As of USB 2.0, full/low speed devices are segregated into trees. - * One type grows from USB 1.1 host controllers (OHCI, UHCI etc). - * The other type grows from high speed hubs when they connect to - * full/low speed devices using "Transaction Translators" (TTs). - * - * TTs should only be known to the hub driver, and high speed bus - * drivers (only EHCI for now). They affect periodic scheduling and - * sometimes control/bulk error recovery. - */ -struct usb_tt { - struct usb_device *hub; /* upstream highspeed hub */ - int multi; /* true means one TT per port */ -}; - - /* -------------------------------------------------------------------------- */ /* This is arbitrary. @@ -387,6 +371,8 @@ */ #define USB_MAXCHILDREN (16) +struct usb_tt; + struct usb_device { int devnum; /* Address on USB bus */ char devpath [16]; /* Use in messages: /port/port/... */ @@ -1176,6 +1162,7 @@ * appropriately. */ +/* NOTE: these are not the standard USB_ENDPOINT_XFER_* values!! */ #define PIPE_ISOCHRONOUS 0 #define PIPE_INTERRUPT 1 #define PIPE_CONTROL 2