OSDN Git Service

USB: gadget: composite: Add userspace notifications for USB state changes
authorMike Lockwood <lockwood@android.com>
Wed, 23 Jun 2010 12:20:59 +0000 (08:20 -0400)
committerBrian Swetland <swetland@google.com>
Tue, 29 Mar 2011 20:57:44 +0000 (13:57 -0700)
Add switch to notify current USB configuration.  This can be used to detect
USB connect and disconnect events.

Broadcast a change via the usb_composite class when a USB function is
enabled or disabled.

Rename usb_function.hidden to usb_function.disabled.

Signed-off-by: Mike Lockwood <lockwood@android.com>
drivers/usb/gadget/android.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/f_adb.c
drivers/usb/gadget/f_rndis.c
include/linux/usb/composite.h

index 3c4d44d..6e3c32e 100644 (file)
@@ -208,7 +208,7 @@ static int product_matches_functions(struct android_usb_product *p)
 {
        struct usb_function             *f;
        list_for_each_entry(f, &android_config_driver.functions, list) {
-               if (product_has_function(p, f) == !!f->hidden)
+               if (product_has_function(p, f) == !!f->disabled)
                        return 0;
        }
        return 1;
@@ -323,8 +323,8 @@ void android_enable_function(struct usb_function *f, int enable)
        int disable = !enable;
        int product_id;
 
-       if (!!f->hidden != disable) {
-               f->hidden = disable;
+       if (!!f->disabled != disable) {
+               usb_function_set_enabled(f, !disable);
 
 #ifdef CONFIG_USB_ANDROID_RNDIS
                if (!strcmp(f->name, "rndis")) {
@@ -347,7 +347,7 @@ void android_enable_function(struct usb_function *f, int enable)
                         */
                        list_for_each_entry(func, &android_config_driver.functions, list) {
                                if (!strcmp(func->name, "usb_mass_storage")) {
-                                       func->hidden = enable;
+                                       usb_function_set_enabled(f, !enable);
                                        break;
                                }
                        }
index b1f4016..156b3b9 100644 (file)
@@ -79,7 +79,7 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
                char *buf)
 {
        struct usb_function *f = dev_get_drvdata(dev);
-       return sprintf(buf, "%d\n", !f->hidden);
+       return sprintf(buf, "%d\n", !f->disabled);
 }
 
 static ssize_t enable_store(
@@ -94,13 +94,18 @@ static ssize_t enable_store(
        if (driver->enable_function)
                driver->enable_function(f, value);
        else
-               f->hidden = !value;
+               usb_function_set_enabled(f, value);
 
        return size;
 }
 
 static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
 
+void usb_function_set_enabled(struct usb_function *f, int enabled)
+{
+       f->disabled = !enabled;
+       kobject_uevent(&f->dev->kobj, KOBJ_CHANGE);
+}
 
 /**
  * usb_add_function() - add a function to a configuration
@@ -315,7 +320,7 @@ static int config_buf(struct usb_configuration *config,
                        descriptors = f->hs_descriptors;
                else
                        descriptors = f->descriptors;
-               if (f->hidden || !descriptors || descriptors[0] == NULL)
+               if (f->disabled || !descriptors || descriptors[0] == NULL)
                        continue;
                status = usb_descriptor_fillbuf(next, len,
                        (const struct usb_descriptor_header **) descriptors);
@@ -489,7 +494,7 @@ static int set_config(struct usb_composite_dev *cdev,
 
                if (!f)
                        break;
-               if (f->hidden)
+               if (f->disabled)
                        continue;
 
                /*
@@ -530,6 +535,8 @@ static int set_config(struct usb_composite_dev *cdev,
        power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
 done:
        usb_gadget_vbus_draw(gadget, power);
+
+       switch_set_state(&cdev->sdev, number);
        return result;
 }
 
@@ -1071,6 +1078,8 @@ static void composite_disconnect(struct usb_gadget *gadget)
        if (composite->disconnect)
                composite->disconnect(cdev);
        spin_unlock_irqrestore(&cdev->lock, flags);
+
+       switch_set_state(&cdev->sdev, 0);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1131,6 +1140,7 @@ composite_unbind(struct usb_gadget *gadget)
                kfree(cdev->req->buf);
                usb_ep_free_request(gadget->ep0, cdev->req);
        }
+       switch_dev_unregister(&cdev->sdev);
        device_remove_file(&gadget->dev, &dev_attr_suspended);
        kfree(cdev);
        set_gadget_data(gadget, NULL);
@@ -1199,6 +1209,11 @@ static int composite_bind(struct usb_gadget *gadget)
        if (status < 0)
                goto fail;
 
+       cdev->sdev.name = "usb_configuration";
+       status = switch_dev_register(&cdev->sdev);
+       if (status < 0)
+               goto fail;
+
        cdev->desc = *composite->dev;
        cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
 
@@ -1304,6 +1319,23 @@ composite_resume(struct usb_gadget *gadget)
        cdev->suspended = 0;
 }
 
+static int
+composite_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct usb_function *f = dev_get_drvdata(dev);
+
+       if (!f) {
+               /* this happens when the device is first created */
+               return 0;
+       }
+
+       if (add_uevent_var(env, "FUNCTION=%s", f->name))
+               return -ENOMEM;
+       if (add_uevent_var(env, "ENABLED=%d", !f->disabled))
+               return -ENOMEM;
+       return 0;
+}
+
 /*-------------------------------------------------------------------------*/
 
 static struct usb_gadget_driver composite_driver = {
@@ -1359,6 +1391,7 @@ extern int usb_composite_probe(struct usb_composite_driver *driver,
        driver->class = class_create(THIS_MODULE, "usb_composite");
        if (IS_ERR(driver->class))
                return PTR_ERR(driver->class);
+       driver->class->dev_uevent = composite_uevent;
 
        return usb_gadget_probe_driver(&composite_driver, composite_bind);
 }
index 301300e..a0b0774 100644 (file)
@@ -613,7 +613,7 @@ static int adb_bind_config(struct usb_configuration *c)
        dev->function.disable = adb_function_disable;
 
        /* start disabled */
-       dev->function.hidden = 1;
+       dev->function.disabled = 1;
 
        /* _adb_dev must be set before calling usb_gadget_register_driver */
        _adb_dev = dev;
index d83f79e..8e40e11 100644 (file)
@@ -865,7 +865,7 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
 
 #ifdef CONFIG_USB_ANDROID_RNDIS
        /* start disabled */
-       rndis->port.func.hidden = 1;
+       rndis->port.func.disabled = 1;
 #endif
 
        status = usb_add_function(c, &rndis->port.func);
index 0170356..183f7f8 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/switch.h>
 
 
 struct usb_configuration;
@@ -100,7 +101,9 @@ struct usb_function {
        struct usb_descriptor_header    **hs_descriptors;
 
        struct usb_configuration        *config;
-       int                             hidden;
+
+       /* disabled is zero if the function is enabled */
+       int                             disabled;
 
        /* REVISIT:  bind() functions can be marked __init, which
         * makes trouble for section mismatch analysis.  See if
@@ -139,6 +142,8 @@ int usb_function_activate(struct usb_function *);
 
 int usb_interface_id(struct usb_configuration *, struct usb_function *);
 
+void usb_function_set_enabled(struct usb_function *, int);
+
 /**
  * ep_choose - select descriptor endpoint at current device speed
  * @g: gadget, connected and running at some speed
@@ -351,6 +356,8 @@ struct usb_composite_dev {
 
        /* protects at least deactivation count */
        spinlock_t                      lock;
+
+       struct switch_dev sdev;
 };
 
 extern int usb_string_id(struct usb_composite_dev *c);