* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#endif
-
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/completion.h>
-#include <linux/uts.h> /* for UTS_SYSNAME */
+#include <linux/utsname.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <asm/irq.h>
#include <asm/byteorder.h>
+#include <linux/platform_device.h>
#include <linux/usb.h>
#include "usb.h"
#include "hcd.h"
+#include "hub.h"
// #define USB_BANDWIDTH_MESSAGES
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 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);
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 */
+ 0x40, /* __u8 bMaxPacketSize0; 64 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; */
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 */
+ 0x40, /* __u8 bMaxPacketSize0; 64 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; */
/* 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; */
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) */
};
/* 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; */
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) */
+ /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
+ * see hub.c:hub_configure() for details. */
+ (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
};
*utf++ = *s++;
*utf++ = 0;
}
+ if (utfmax > 0) {
+ *utf = *s;
+ ++retval;
+ }
return retval;
}
// 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", UTS_SYSNAME, UTS_RELEASE,
- hcd->description);
+ snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname,
+ init_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;
}
{
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)]
+ __attribute__((aligned(4)));
+ 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;
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;
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;
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:
default:
/* 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,
- 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;
- dev_dbg (hcd->self.controller, "unsupported hub control message (maxchild %d)\n",
- urb->dev->maxchild);
+ status = -EPIPE;
}
- 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 (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, status);
+ }
}
- if (bufp) {
+ if (len) {
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
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);
- usb_hcd_giveback_urb (hcd, urb, NULL);
+ spin_lock (&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = status;
+ spin_unlock (&urb->lock);
+ usb_hcd_giveback_urb (hcd, urb);
local_irq_restore (flags);
return 0;
}
/*-------------------------------------------------------------------------*/
/*
- * 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.
+ *
+ * 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);
-
- /* 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)) {
- dev_dbg (hcd->self.controller,
- "not queuing rh status urb, stat %d\n",
- urb->status);
- return -EINVAL;
+ length = hcd->driver->hub_status_data(hcd, buffer);
+ if (length > 0) {
+
+ /* 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);
+ 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) == 0) {
- 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;
}
-
- 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);
- 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;
}
/*-------------------------------------------------------------------------*/
-int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
+/* Unlinks of root-hub control URBs are legal, but they don't do anything
+ * since these URBs always execute synchronously.
+ */
+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 (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+ ; /* Do nothing */
+
+ } else { /* Status URB */
+ if (!hcd->uses_new_polling)
+ del_timer (&hcd->rh_timer);
+ local_irq_save (flags);
+ 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);
+ local_irq_restore (flags);
+ }
- local_irq_save (flags);
- urb->hcpriv = NULL;
- usb_hcd_giveback_urb (hcd, urb, NULL);
- local_irq_restore (flags);
return 0;
}
/*-------------------------------------------------------------------------*/
-/* exported only within usbcore */
-struct usb_bus *usb_bus_get (struct usb_bus *bus)
-{
- struct class_device *tmp;
-
- if (!bus)
- return NULL;
-
- tmp = class_device_get(&bus->class_dev);
- if (tmp)
- return to_usb_bus(tmp);
- else
- return NULL;
-}
-
-/* exported only within usbcore */
-void usb_bus_put (struct usb_bus *bus)
-{
- if (bus)
- class_device_put(&bus->class_dev);
-}
-
-/*-------------------------------------------------------------------------*/
-
-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);
}
/**
* 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));
bus->devnum_next = 1;
bus->root_hub = NULL;
- bus->hcpriv = NULL;
bus->busnum = -1;
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
INIT_LIST_HEAD (&bus->bus_list);
}
-EXPORT_SYMBOL (usb_bus_init);
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure
- * @op: pointer to a struct usb_operations that this bus structure should use
- * Context: !in_interrupt()
- *
- * Creates a USB host controller bus structure with the specified
- * usb_operations and initializes all the necessary internal objects.
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_put_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus (struct usb_operations *op)
-{
- struct usb_bus *bus;
-
- bus = kmalloc (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);
/*-------------------------------------------------------------------------*/
* 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);
+ mutex_unlock(&usb_bus_list_lock);
return -E2BIG;
}
- 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);
- 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
* 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);
* 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;
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->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) {
+ 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;
}
- down (&usb_dev->serialize);
retval = usb_new_device (usb_dev);
- up (&usb_dev->serialize);
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->state != HC_STATE_HALT)
+ hcd->driver->hub_irq_enable (hcd);
+}
/*-------------------------------------------------------------------------*/
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);
/*-------------------------------------------------------------------------*/
-/* 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;
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);
}
* 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)
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
- struct usb_hcd *hcd = urb->dev->bus->hcpriv;
- struct hcd_dev *dev = urb->dev->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
+ struct usb_host_endpoint *ep;
unsigned long flags;
- if (!hcd || !dev)
+ 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.
// 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 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);
+ else switch (hcd->state) {
+ case HC_STATE_RUNNING:
+ case HC_STATE_RESUMING:
+doit:
+ list_add_tail (&urb->urb_list, &ep->urb_list);
status = 0;
- } else
+ 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;
+ }
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;
}
* 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 (hcd->self.uses_dma) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
urb->setup_dma = dma_map_single (
: 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);
if (urb->reject)
wake_up (&usb_kill_urb_queue);
usb_put_urb (urb);
+ usbmon_urb_submit_error(&hcd->self, urb, status);
}
return status;
}
/*-------------------------------------------------------------------------*/
/* called in any context */
-static int hcd_get_frame_number (struct usb_device *udev)
+int usb_hcd_get_frame_number (struct usb_device *udev)
{
- struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
- if (!HCD_IS_RUNNING (hcd->state))
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ if (!HC_IS_RUNNING (hcd->state))
return -ESHUTDOWN;
return hcd->driver->get_frame_number (hcd);
}
{
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
* 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)
+int usb_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;
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,
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) {
+ hcd = bus_to_hcd(urb->dev->bus);
+ 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, &dev->urb_list) {
+ list_for_each(tmp, &ep->urb_list) {
if (tmp == &urb->urb_list)
break;
}
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 (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)
+ && 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;
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
}
urb->status = status;
/*-------------------------------------------------------------------------*/
/* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware. use for
+ * the hcd to make sure all endpoint state is gone from hardware, and then
+ * waits until the endpoint's queue is completely drained. 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)
+void usb_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;
+ hcd = bus_to_hcd(udev->bus);
- 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 ();
+ /* ep is already gone from udev->ep_{in,out}[]; no more submits */
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;
+ list_for_each_entry (urb, &ep->urb_list, urb_list) {
+ int tmp;
- /* 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.
- */
+ /* the urb may already have been unlinked */
if (urb->status != -EINPROGRESS)
continue;
usb_get_urb (urb);
*/
might_sleep ();
if (hcd->driver->endpoint_disable)
- hcd->driver->endpoint_disable (hcd, dev, endpoint);
+ hcd->driver->endpoint_disable (hcd, ep);
+
+ /* Wait until the endpoint queue is completely empty. Most HCDs
+ * will have done this already in their endpoint_disable method,
+ * but some might not. And there could be root-hub control URBs
+ * still pending since they aren't affected by the HCDs'
+ * endpoint_disable methods.
+ */
+ while (!list_empty (&ep->urb_list)) {
+ spin_lock_irq (&hcd_data_lock);
+
+ /* The list may have changed while we acquired the spinlock */
+ urb = NULL;
+ if (!list_empty (&ep->urb_list)) {
+ urb = list_entry (ep->urb_list.prev, struct urb,
+ urb_list);
+ usb_get_urb (urb);
+ }
+ spin_unlock_irq (&hcd_data_lock);
+
+ if (urb) {
+ usb_kill_urb (urb);
+ usb_put_urb (urb);
+ }
+ }
}
/*-------------------------------------------------------------------------*/
-#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;
}
-#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.
+/**
+ * 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).
*/
-
-static int hcd_free_dev (struct usb_device *udev)
+void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
{
- struct hcd_dev *dev;
- struct usb_hcd *hcd;
- unsigned long flags;
+ unsigned long flags;
- if (!udev || !udev->hcpriv)
- return -EINVAL;
+ 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);
- if (!udev->bus || !udev->bus->hcpriv)
- return -ENODEV;
+#endif
- // should udev->devnum == -1 ??
+/*-------------------------------------------------------------------------*/
- dev = udev->hcpriv;
- hcd = udev->bus->hcpriv;
+#ifdef CONFIG_USB_OTG
- /* 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;
- }
+/**
+ * usb_bus_start_enum - start immediate enumeration (for OTG)
+ * @bus: the bus (must use hcd framework)
+ * @port_num: 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;
- spin_lock_irqsave (&hcd_data_lock, flags);
- list_del (&dev->dev_list);
- udev->hcpriv = NULL;
- spin_unlock_irqrestore (&hcd_data_lock, flags);
+ /* 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);
- kfree (dev);
- return 0;
+ /* 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);
-/*
- * 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
* 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)
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
{
- 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) {
+ /* lower level hcd code should use *_dma exclusively if the
+ * host controller does DMA */
+ if (hcd->self.uses_dma && !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,
: DMA_TO_DEVICE);
}
+ usbmon_urb_complete (&hcd->self, urb);
/* pass ownership to the completion handler */
- urb->complete (urb, regs);
+ urb->complete (urb);
atomic_dec (&urb->use_count);
if (unlikely (urb->reject))
wake_up (&usb_kill_urb_queue);
/**
* 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)
+irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
- if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
+ if (unlikely(start == HC_STATE_HALT ||
+ !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
return IRQ_NONE;
-
- hcd->saw_irq = 1;
- if (hcd->driver->irq (hcd, r) == IRQ_NONE)
+ if (hcd->driver->irq (hcd) == IRQ_NONE)
return IRQ_NONE;
- 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);
/*-------------------------------------------------------------------------*/
-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
*/
void usb_hc_died (struct usb_hcd *hcd)
{
+ unsigned long flags;
+
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);
+ 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);
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * 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
+ * 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 device *dev, char *bus_name)
+{
+ struct usb_hcd *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);
+ kref_init(&hcd->kref);
+
+ usb_bus_init(&hcd->self);
+ hcd->self.controller = dev;
+ hcd->self.bus_name = bus_name;
+ hcd->self.uses_dma = (dev->dma_mask != NULL);
+
+ 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";
+
+ return hcd;
+}
+EXPORT_SYMBOL (usb_create_hcd);
+
+static void hcd_release (struct kref *kref)
+{
+ struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+
+ kfree(hcd);
+}
+
+struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+{
+ if (hcd)
+ kref_get (&hcd->kref);
+ return hcd;
+}
+EXPORT_SYMBOL (usb_get_hcd);
+
+void usb_put_hcd (struct usb_hcd *hcd)
+{
+ if (hcd)
+ kref_put (&hcd->kref, hcd_release);
+}
+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) {
+ 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 %d failed\n", irqnum);
+ goto err_request_irq;
+ }
+ hcd->irq = irqnum;
+ dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
+ (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);
+
+void
+usb_hcd_platform_shutdown(struct platform_device* dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_platform_shutdown);
+
+/*-------------------------------------------------------------------------*/
+
+#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 (usb_hc_died);
+EXPORT_SYMBOL_GPL (usb_mon_deregister);
+#endif /* CONFIG_USB_MON */