* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.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>
static struct usb_busmap busmap;
/* used when updating list of hcds */
-DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
+DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
EXPORT_SYMBOL_GPL (usb_bus_list_lock);
/* used for controlling access to virtual root hubs */
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- u8 tbuf [sizeof (struct usb_hub_descriptor)];
+ u8 tbuf [sizeof (struct usb_hub_descriptor)]
+ __attribute__((aligned(4)));
const u8 *bufp = tbuf;
int len = 0;
int patch_wakeup = 0;
/* 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:
- tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
- << USB_DEVICE_REMOTE_WAKEUP)
+ tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
| (1 << USB_DEVICE_SELF_POWERED);
tbuf [1] = 0;
len = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (wValue == USB_DEVICE_REMOTE_WAKEUP)
- device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
+ hcd->remote_wakeup = 0;
else
goto error;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
- if (device_can_wakeup(&hcd->self.root_hub->dev)
- && wValue == USB_DEVICE_REMOTE_WAKEUP)
- device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
+ if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP)
+ hcd->remote_wakeup = 1;
else
goto error;
break;
bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor;
}
- if (device_can_wakeup(&hcd->self.root_hub->dev))
+ if (hcd->can_wakeup)
patch_wakeup = 1;
break;
case USB_DT_STRING << 8:
{
int busnum;
- mutex_lock(&usb_bus_list_lock);
+ down (&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);
+ up(&usb_bus_list_lock);
return -E2BIG;
}
bus->controller, "usb_host%d", busnum);
if (IS_ERR(bus->class_dev)) {
clear_bit(busnum, busmap.busmap);
- mutex_unlock(&usb_bus_list_lock);
+ up(&usb_bus_list_lock);
return PTR_ERR(bus->class_dev);
}
/* Add it to the local list of buses */
list_add (&bus->bus_list, &usb_bus_list);
- mutex_unlock(&usb_bus_list_lock);
+ up (&usb_bus_list_lock);
usb_notify_add_bus(bus);
* controller code, as well as having it call this when cleaning
* itself up
*/
- mutex_lock(&usb_bus_list_lock);
+ down (&usb_bus_list_lock);
list_del (&bus->bus_list);
- mutex_unlock(&usb_bus_list_lock);
+ up (&usb_bus_list_lock);
usb_notify_remove_bus(bus);
/**
* register_root_hub - called by usb_add_hcd() to register a root hub
+ * @usb_dev: the usb root hub device to be registered.
* @hcd: host controller for this root hub
*
* 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).
+ * 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).
*/
-static int register_root_hub(struct usb_hcd *hcd)
+static int register_root_hub (struct usb_device *usb_dev,
+ 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);
- mutex_lock(&usb_bus_list_lock);
+ down (&usb_bus_list_lock);
+ usb_dev->bus->root_hub = usb_dev;
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);
+ usb_dev->bus->root_hub = NULL;
+ up (&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;
retval = usb_new_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);
}
- mutex_unlock(&usb_bus_list_lock);
+ up (&usb_bus_list_lock);
if (retval == 0) {
spin_lock_irq (&hcd_root_hub_lock);
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);
}
case HC_STATE_RUNNING:
case HC_STATE_RESUMING:
doit:
+ usb_get_dev (urb->dev);
list_add_tail (&urb->urb_list, &ep->urb_list);
status = 0;
break;
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.)
- */
+ /* till now HC has been in an indeterminate state ... */
+ if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
+ dev_err(hcd->self.controller, "can't reset\n");
+ return retval;
+ }
+
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;
- }
+ if (hcd->driver->irq) {
+ char buf[8], *bufp = buf;
- /* 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");
+#ifdef __sparc__
+ bufp = __irq_itoa(irqnum);
+#else
+ sprintf(buf, "%d", irqnum);
+#endif
- /* 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);
+ "request interrupt %s failed\n", bufp);
goto err_request_irq;
}
hcd->irq = irqnum;
- dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", 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);
(unsigned long long)hcd->rsrc_start);
}
+ /* Allocate the root hub before calling hcd->driver->start(),
+ * but don't register it until afterward so that the hardware
+ * is running.
+ */
+ 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;
+ }
+
+ /* Although in principle hcd->driver->start() might need to use rhdev,
+ * none of the current drivers do.
+ */
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 */
+ /* hcd->driver->start() reported can_wakeup, probably with
+ * assistance from board's boot firmware.
+ * NOTE: normal devices won't enable wakeup by default.
+ */
+ if (hcd->can_wakeup)
+ dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
+ hcd->remote_wakeup = hcd->can_wakeup;
+
+ rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
+ USB_SPEED_FULL;
rhdev->bus_mA = min(500u, hcd->power_budget);
- if ((retval = register_root_hub(hcd)) != 0)
+ if ((retval = register_root_hub(rhdev, 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:
+ err_register_root_hub:
hcd->driver->stop(hcd);
-err_hcd_driver_start:
+
+ err_hcd_driver_start:
+ usb_put_dev(rhdev);
+
+ err_allocate_root_hub:
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:
+
+ err_request_irq:
usb_deregister_bus(&hcd->self);
-err_register_bus:
+
+ err_register_bus:
hcd_buffer_destroy(hcd);
return retval;
}
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
- mutex_lock(&usb_bus_list_lock);
+ down(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
- mutex_unlock(&usb_bus_list_lock);
+ up(&usb_bus_list_lock);
hcd->poll_rh = 0;
del_timer_sync(&hcd->rh_timer);