]> Git Repo - linux.git/blobdiff - drivers/usb/core/usb.c
Merge branch 'intelfb-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[linux.git] / drivers / usb / core / usb.c
index 0b8c67bcde6076f369452d37260d5349888a1e6e..e4df9edf1bc02bcfaa65f96f74730cb37f1e65bd 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/smp_lock.h>
 #include <linux/usb.h>
 #include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 #include <asm/io.h>
 #include <asm/scatterlist.h>
@@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore";
 
 static int nousb;      /* Disable USB when built into kernel image */
 
+struct workqueue_struct *ksuspend_usb_wq;      /* For autosuspend */
+
 
 /**
  * usb_ifnum_to_if - get the interface object with a given interface number
@@ -67,7 +70,8 @@ static int nousb;     /* Disable USB when built into kernel image */
  * Don't call this function unless you are bound to one of the interfaces
  * on this device or you have locked the device!
  */
-struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
+                                     unsigned ifnum)
 {
        struct usb_host_config *config = dev->actconfig;
        int i;
@@ -100,8 +104,8 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
  * Don't call this function unless you are bound to the intf interface
  * or you have locked the device!
  */
-struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
-               unsigned int altnum)
+struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf,
+                                                   unsigned int altnum)
 {
        int i;
 
@@ -123,7 +127,7 @@ static int __find_interface(struct device * dev, void * data)
        struct usb_interface *intf;
 
        /* can't look at usb devices, only interfaces */
-       if (dev->driver == &usb_generic_driver)
+       if (is_usb_device(dev))
                return 0;
 
        intf = to_usb_interface(dev);
@@ -146,10 +150,13 @@ static int __find_interface(struct device * dev, void * data)
 struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
 {
        struct find_interface_arg argb;
+       int retval;
 
        argb.minor = minor;
        argb.interface = NULL;
-       driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+       /* eat the error, it will be in argb.interface */
+       retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb,
+                                       __find_interface);
        return argb.interface;
 }
 
@@ -166,14 +173,60 @@ static void usb_release_dev(struct device *dev)
 
        udev = to_usb_device(dev);
 
+#ifdef CONFIG_USB_SUSPEND
+       cancel_delayed_work(&udev->autosuspend);
+       flush_workqueue(ksuspend_usb_wq);
+#endif
        usb_destroy_configuration(udev);
-       usb_bus_put(udev->bus);
+       usb_put_hcd(bus_to_hcd(udev->bus));
        kfree(udev->product);
        kfree(udev->manufacturer);
        kfree(udev->serial);
        kfree(udev);
 }
 
+#ifdef CONFIG_PM
+
+static int ksuspend_usb_init(void)
+{
+       ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd");
+       if (!ksuspend_usb_wq)
+               return -ENOMEM;
+       return 0;
+}
+
+static void ksuspend_usb_cleanup(void)
+{
+       destroy_workqueue(ksuspend_usb_wq);
+}
+
+#else
+
+#define ksuspend_usb_init()    0
+#define ksuspend_usb_cleanup() do {} while (0)
+
+#endif
+
+#ifdef CONFIG_USB_SUSPEND
+
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+static void usb_autosuspend_work(void *_udev)
+{
+       struct usb_device       *udev = _udev;
+
+       usb_pm_lock(udev);
+       udev->auto_pm = 1;
+       usb_suspend_both(udev, PMSG_SUSPEND);
+       usb_pm_unlock(udev);
+}
+
+#else
+
+static void usb_autosuspend_work(void *_udev)
+{}
+
+#endif
+
 /**
  * usb_alloc_dev - usb device constructor (usbcore-internal)
  * @parent: hub to which device is connected; null to allocate a root hub
@@ -195,8 +248,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        if (!dev)
                return NULL;
 
-       bus = usb_bus_get(bus);
-       if (!bus) {
+       if (!usb_get_hcd(bus_to_hcd(bus))) {
                kfree(dev);
                return NULL;
        }
@@ -204,11 +256,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        device_initialize(&dev->dev);
        dev->dev.bus = &usb_bus_type;
        dev->dev.dma_mask = bus->controller->dma_mask;
-       dev->dev.driver_data = &usb_generic_driver_data;
-       dev->dev.driver = &usb_generic_driver;
        dev->dev.release = usb_release_dev;
        dev->state = USB_STATE_ATTACHED;
 
+       /* This magic assignment distinguishes devices from interfaces */
+       dev->dev.platform_data = &usb_generic_driver;
+
        INIT_LIST_HEAD(&dev->ep0.urb_list);
        dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
        dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -249,6 +302,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        dev->parent = parent;
        INIT_LIST_HEAD(&dev->filelist);
 
+#ifdef CONFIG_PM
+       mutex_init(&dev->pm_mutex);
+       INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
        return dev;
 }
 
@@ -354,7 +411,7 @@ void usb_put_intf(struct usb_interface *intf)
  * case the driver already owns the device lock.)
  */
 int usb_lock_device_for_reset(struct usb_device *udev,
-               struct usb_interface *iface)
+                             const struct usb_interface *iface)
 {
        unsigned long jiffies_expire = jiffies + HZ;
 
@@ -477,7 +534,139 @@ exit:
  */
 int usb_get_current_frame_number(struct usb_device *dev)
 {
-       return dev->bus->op->get_frame_number (dev);
+       return usb_hcd_get_frame_number (dev);
+}
+
+/**
+ * usb_endpoint_dir_in - check if the endpoint has IN direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type IN, otherwise it returns false.
+ */
+int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+/**
+ * usb_endpoint_dir_out - check if the endpoint has OUT direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type OUT, otherwise it returns false.
+ */
+int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+}
+
+/**
+ * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type bulk, otherwise it returns false.
+ */
+int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_BULK);
+}
+
+/**
+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type interrupt, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_INT);
+}
+
+/**
+ * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type isochronous, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_ISOC);
+}
+
+/**
+ * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
 }
 
 /*-------------------------------------------------------------------*/
@@ -542,9 +731,9 @@ void *usb_buffer_alloc (
        dma_addr_t *dma
 )
 {
-       if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+       if (!dev || !dev->bus)
                return NULL;
-       return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+       return hcd_buffer_alloc (dev->bus, size, mem_flags, dma);
 }
 
 /**
@@ -565,11 +754,11 @@ void usb_buffer_free (
        dma_addr_t dma
 )
 {
-       if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
+       if (!dev || !dev->bus)
                return;
        if (!addr)
                return;
-       dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+       hcd_buffer_free (dev->bus, size, addr, dma);
 }
 
 /**
@@ -718,8 +907,8 @@ void usb_buffer_unmap (struct urb *urb)
  *
  * Reverse the effect of this call with usb_buffer_unmap_sg().
  */
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int nents)
+int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+                     struct scatterlist *sg, int nents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
@@ -753,8 +942,8 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
  * Use this when you are re-using a scatterlist's data buffers for
  * another USB request.
  */
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+                          struct scatterlist *sg, int n_hw_ents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
@@ -779,8 +968,8 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
  *
  * Reverses the effect of usb_buffer_map_sg().
  */
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+                        struct scatterlist *sg, int n_hw_ents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
@@ -817,9 +1006,12 @@ static int __init usb_init(void)
                return 0;
        }
 
+       retval = ksuspend_usb_init();
+       if (retval)
+               goto out;
        retval = bus_register(&usb_bus_type);
        if (retval) 
-               goto out;
+               goto bus_register_failed;
        retval = usb_host_init();
        if (retval)
                goto host_init_failed;
@@ -838,7 +1030,7 @@ static int __init usb_init(void)
        retval = usb_hub_init();
        if (retval)
                goto hub_init_failed;
-       retval = driver_register(&usb_generic_driver);
+       retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
        if (!retval)
                goto out;
 
@@ -855,6 +1047,8 @@ major_init_failed:
        usb_host_cleanup();
 host_init_failed:
        bus_unregister(&usb_bus_type);
+bus_register_failed:
+       ksuspend_usb_cleanup();
 out:
        return retval;
 }
@@ -868,7 +1062,7 @@ static void __exit usb_exit(void)
        if (nousb)
                return;
 
-       driver_unregister(&usb_generic_driver);
+       usb_deregister_device_driver(&usb_generic_driver);
        usb_major_cleanup();
        usbfs_cleanup();
        usb_deregister(&usbfs_driver);
@@ -876,6 +1070,7 @@ static void __exit usb_exit(void)
        usb_hub_cleanup();
        usb_host_cleanup();
        bus_unregister(&usb_bus_type);
+       ksuspend_usb_cleanup();
 }
 
 subsys_initcall(usb_init);
@@ -907,6 +1102,18 @@ EXPORT_SYMBOL(__usb_get_extra_descriptor);
 EXPORT_SYMBOL(usb_find_device);
 EXPORT_SYMBOL(usb_get_current_frame_number);
 
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_bulk);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_int);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_isoc);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_out);
+
 EXPORT_SYMBOL (usb_buffer_alloc);
 EXPORT_SYMBOL (usb_buffer_free);
 
This page took 0.047915 seconds and 4 git commands to generate.