clk: rockchip: rk3288: fix up the clk register for hclk_vio
[firefly-linux-kernel-4.4.55.git] / drivers / usb / gadget / configfs.c
index 163d305e1200bb07a73357d7b5824d97b362b924..54849fe9cb01ed8c6b64ebfc3c3e08edd243af89 100644 (file)
@@ -9,6 +9,31 @@
 #include "u_f.h"
 #include "u_os_desc.h"
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+#include <linux/platform_device.h>
+#include <linux/kdev_t.h>
+#include <linux/usb/ch9.h>
+
+#ifdef CONFIG_USB_CONFIGFS_F_ACC
+extern int acc_ctrlrequest(struct usb_composite_dev *cdev,
+                               const struct usb_ctrlrequest *ctrl);
+void acc_disconnect(void);
+#endif
+static struct class *android_class;
+static struct device *android_device;
+static int index;
+
+struct device *create_function_device(char *name)
+{
+       if (android_device && !IS_ERR(android_device))
+               return device_create(android_class, android_device,
+                       MKDEV(0, index++), NULL, name);
+       else
+               return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(create_function_device);
+#endif
+
 int check_user_usb_string(const char *name,
                struct usb_gadget_strings *stringtab_dev)
 {
@@ -62,6 +87,12 @@ struct gadget_info {
        bool use_os_desc;
        char b_vendor_code;
        char qw_sign[OS_STRING_QW_SIGN_LEN];
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       bool connected;
+       bool sw_connected;
+       struct work_struct work;
+       struct device *dev;
+#endif
 };
 
 static inline struct gadget_info *to_gadget_info(struct config_item *item)
@@ -266,7 +297,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
 
        mutex_lock(&gi->lock);
 
-       if (!strlen(name)) {
+       if (!strlen(name) || strcmp(name, "none") == 0) {
                ret = unregister_gadget(gi);
                if (ret)
                        goto err;
@@ -1373,6 +1404,60 @@ err_comp_cleanup:
        return ret;
 }
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+static void android_work(struct work_struct *data)
+{
+       struct gadget_info *gi = container_of(data, struct gadget_info, work);
+       struct usb_composite_dev *cdev = &gi->cdev;
+       char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL };
+       char *connected[2]    = { "USB_STATE=CONNECTED", NULL };
+       char *configured[2]   = { "USB_STATE=CONFIGURED", NULL };
+       /* 0-connected 1-configured 2-disconnected*/
+       bool status[3] = { false, false, false };
+       unsigned long flags;
+       bool uevent_sent = false;
+
+       spin_lock_irqsave(&cdev->lock, flags);
+       if (cdev->config)
+               status[1] = true;
+
+       if (gi->connected != gi->sw_connected) {
+               if (gi->connected)
+                       status[0] = true;
+               else
+                       status[2] = true;
+               gi->sw_connected = gi->connected;
+       }
+       spin_unlock_irqrestore(&cdev->lock, flags);
+
+       if (status[0]) {
+               kobject_uevent_env(&android_device->kobj,
+                                       KOBJ_CHANGE, connected);
+               pr_info("%s: sent uevent %s\n", __func__, connected[0]);
+               uevent_sent = true;
+       }
+
+       if (status[1]) {
+               kobject_uevent_env(&android_device->kobj,
+                                       KOBJ_CHANGE, configured);
+               pr_info("%s: sent uevent %s\n", __func__, configured[0]);
+               uevent_sent = true;
+       }
+
+       if (status[2]) {
+               kobject_uevent_env(&android_device->kobj,
+                                       KOBJ_CHANGE, disconnected);
+               pr_info("%s: sent uevent %s\n", __func__, disconnected[0]);
+               uevent_sent = true;
+       }
+
+       if (!uevent_sent) {
+               pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
+                       gi->connected, gi->sw_connected, cdev->config);
+       }
+}
+#endif
+
 static void configfs_composite_unbind(struct usb_gadget *gadget)
 {
        struct usb_composite_dev        *cdev;
@@ -1392,14 +1477,79 @@ static void configfs_composite_unbind(struct usb_gadget *gadget)
        set_gadget_data(gadget, NULL);
 }
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+static int android_setup(struct usb_gadget *gadget,
+                       const struct usb_ctrlrequest *c)
+{
+       struct usb_composite_dev *cdev = get_gadget_data(gadget);
+       unsigned long flags;
+       struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
+       int value = -EOPNOTSUPP;
+       struct usb_function_instance *fi;
+
+       spin_lock_irqsave(&cdev->lock, flags);
+       if (!gi->connected) {
+               gi->connected = 1;
+               schedule_work(&gi->work);
+       }
+       spin_unlock_irqrestore(&cdev->lock, flags);
+       list_for_each_entry(fi, &gi->available_func, cfs_list) {
+               if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) {
+                       value = fi->f->setup(fi->f, c);
+                       if (value >= 0)
+                               break;
+               }
+       }
+
+#ifdef CONFIG_USB_CONFIGFS_F_ACC
+       if (value < 0)
+               value = acc_ctrlrequest(cdev, c);
+#endif
+
+       if (value < 0)
+               value = composite_setup(gadget, c);
+
+       spin_lock_irqsave(&cdev->lock, flags);
+       if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
+                                               cdev->config) {
+               schedule_work(&gi->work);
+       }
+       spin_unlock_irqrestore(&cdev->lock, flags);
+
+       return value;
+}
+
+static void android_disconnect(struct usb_gadget *gadget)
+{
+       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
+       struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
+
+       /* accessory HID support can be active while the
+               accessory function is not actually enabled,
+               so we need to inform it when we are disconnected.
+       */
+
+#ifdef CONFIG_USB_CONFIGFS_F_ACC
+       acc_disconnect();
+#endif
+       gi->connected = 0;
+       schedule_work(&gi->work);
+       composite_disconnect(gadget);
+}
+#endif
+
 static const struct usb_gadget_driver configfs_driver_template = {
        .bind           = configfs_composite_bind,
        .unbind         = configfs_composite_unbind,
-
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       .setup          = android_setup,
+       .reset          = android_disconnect,
+       .disconnect     = android_disconnect,
+#else
        .setup          = composite_setup,
        .reset          = composite_disconnect,
        .disconnect     = composite_disconnect,
-
+#endif
        .suspend        = composite_suspend,
        .resume         = composite_resume,
 
@@ -1410,6 +1560,89 @@ static const struct usb_gadget_driver configfs_driver_template = {
        },
 };
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+static ssize_t state_show(struct device *pdev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct gadget_info *dev = dev_get_drvdata(pdev);
+       struct usb_composite_dev *cdev;
+       char *state = "DISCONNECTED";
+       unsigned long flags;
+
+       if (!dev)
+               goto out;
+
+       cdev = &dev->cdev;
+
+       if (!cdev)
+               goto out;
+
+       spin_lock_irqsave(&cdev->lock, flags);
+       if (cdev->config)
+               state = "CONFIGURED";
+       else if (dev->connected)
+               state = "CONNECTED";
+       spin_unlock_irqrestore(&cdev->lock, flags);
+out:
+       return sprintf(buf, "%s\n", state);
+}
+
+static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+
+static struct device_attribute *android_usb_attributes[] = {
+       &dev_attr_state,
+       NULL
+};
+
+static int android_device_create(struct gadget_info *gi)
+{
+       struct device_attribute **attrs;
+       struct device_attribute *attr;
+
+       INIT_WORK(&gi->work, android_work);
+       android_device = device_create(android_class, NULL,
+                               MKDEV(0, 0), NULL, "android0");
+       if (IS_ERR(android_device))
+               return PTR_ERR(android_device);
+
+       dev_set_drvdata(android_device, gi);
+
+       attrs = android_usb_attributes;
+       while ((attr = *attrs++)) {
+               int err;
+
+               err = device_create_file(android_device, attr);
+               if (err) {
+                       device_destroy(android_device->class,
+                                      android_device->devt);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static void android_device_destroy(void)
+{
+       struct device_attribute **attrs;
+       struct device_attribute *attr;
+
+       attrs = android_usb_attributes;
+       while ((attr = *attrs++))
+               device_remove_file(android_device, attr);
+       device_destroy(android_device->class, android_device->devt);
+}
+#else
+static inline int android_device_create(struct gadget_info *gi)
+{
+       return 0;
+}
+
+static inline void android_device_destroy(void)
+{
+}
+#endif
+
 static struct config_group *gadgets_make(
                struct config_group *group,
                const char *name)
@@ -1419,7 +1652,6 @@ static struct config_group *gadgets_make(
        gi = kzalloc(sizeof(*gi), GFP_KERNEL);
        if (!gi)
                return ERR_PTR(-ENOMEM);
-
        gi->group.default_groups = gi->default_groups;
        gi->group.default_groups[0] = &gi->functions_group;
        gi->group.default_groups[1] = &gi->configs_group;
@@ -1458,9 +1690,13 @@ static struct config_group *gadgets_make(
        if (!gi->composite.gadget_driver.function)
                goto err;
 
+       if (android_device_create(gi) < 0)
+               goto err;
+
        config_group_init_type_name(&gi->group, name,
                                &gadget_root_type);
        return &gi->group;
+
 err:
        kfree(gi);
        return ERR_PTR(-ENOMEM);
@@ -1469,6 +1705,7 @@ err:
 static void gadgets_drop(struct config_group *group, struct config_item *item)
 {
        config_item_put(item);
+       android_device_destroy();
 }
 
 static struct configfs_group_operations gadgets_ops = {
@@ -1495,7 +1732,9 @@ void unregister_gadget_item(struct config_item *item)
 {
        struct gadget_info *gi = to_gadget_info(item);
 
+       mutex_lock(&gi->lock);
        unregister_gadget(gi);
+       mutex_unlock(&gi->lock);
 }
 EXPORT_SYMBOL_GPL(unregister_gadget_item);
 
@@ -1506,6 +1745,13 @@ static int __init gadget_cfs_init(void)
        config_group_init(&gadget_subsys.su_group);
 
        ret = configfs_register_subsystem(&gadget_subsys);
+
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       android_class = class_create(THIS_MODULE, "android_usb");
+       if (IS_ERR(android_class))
+               return PTR_ERR(android_class);
+#endif
+
        return ret;
 }
 module_init(gadget_cfs_init);
@@ -1513,5 +1759,10 @@ module_init(gadget_cfs_init);
 static void __exit gadget_cfs_exit(void)
 {
        configfs_unregister_subsystem(&gadget_subsys);
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       if (!IS_ERR(android_class))
+               class_destroy(android_class);
+#endif
+
 }
 module_exit(gadget_cfs_exit);