mirror of https://github.com/OpenIPC/firmware.git
473 lines
12 KiB
Diff
473 lines
12 KiB
Diff
diff -drupN a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
|
|
--- a/drivers/usb/gadget/configfs.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/drivers/usb/gadget/configfs.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -1,3 +1,10 @@
|
|
+/*
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ */
|
|
+
|
|
#include <linux/configfs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
@@ -9,6 +16,115 @@
|
|
#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
|
|
+
|
|
+#if IS_ENABLED(CONFIG_USB_SUNXI_UDC0)
|
|
+#include <linux/of_device.h>
|
|
+#define KEY_USB_SERIAL_UNIQUE "usb_serial_unique"
|
|
+#define KEY_USB_SERIAL_NUMBER "usb_serial_number"
|
|
+#define KEY_CMDLINE_SERIAL "androidboot.serialno"
|
|
+
|
|
+u32 serial_unique;
|
|
+char g_usb_serial_number[64];
|
|
+
|
|
+static int get_para_from_cmdline(const char *cmdline,
|
|
+ const char *name, char *value, int maxsize)
|
|
+{
|
|
+ char *p = (char *)cmdline;
|
|
+ char *value_p = value;
|
|
+ int size = 0;
|
|
+
|
|
+ if (!cmdline || !name || !value)
|
|
+ return -1;
|
|
+
|
|
+ for (; *p != 0;) {
|
|
+ if (*p++ == ' ') {
|
|
+ if (strncmp(p, name, strlen(name)) == 0) {
|
|
+ p += strlen(name);
|
|
+ if (*p++ != '=')
|
|
+ continue;
|
|
+ while ((*p != 0) && (*p != ' ')
|
|
+ && (++size < maxsize))
|
|
+ *value_p++ = *p++;
|
|
+ *value_p = '\0';
|
|
+ return value_p - value;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int get_android_usb_config(void)
|
|
+{
|
|
+ struct device_node *usbc0_np = NULL;
|
|
+
|
|
+ const char *usb_serial_number;
|
|
+ int ret = -1;
|
|
+
|
|
+ usbc0_np = of_find_node_by_type(NULL, "usbc0");
|
|
+
|
|
+ /* get serial unique */
|
|
+ ret = of_property_read_u32(usbc0_np, KEY_USB_SERIAL_UNIQUE,
|
|
+ &serial_unique);
|
|
+ if (ret)
|
|
+ pr_info("get serial_unique failed\n");
|
|
+
|
|
+ /* get usb serial number from boot command line*/
|
|
+ ret = get_para_from_cmdline(saved_command_line,
|
|
+ KEY_CMDLINE_SERIAL,
|
|
+ g_usb_serial_number,
|
|
+ sizeof(g_usb_serial_number));
|
|
+
|
|
+ if (serial_unique == 1 && ret > 0) {
|
|
+ pr_info("get usb_serial_number success from boot command line");
|
|
+ } else {
|
|
+ /* get serial number */
|
|
+ ret = of_property_read_string(usbc0_np, KEY_USB_SERIAL_NUMBER,
|
|
+ &usb_serial_number);
|
|
+ if (ret) {
|
|
+ pr_info("get usb_serial_number failed\n");
|
|
+ strncpy(g_usb_serial_number,
|
|
+ "20080411",
|
|
+ ARRAY_SIZE(g_usb_serial_number));
|
|
+ } else {
|
|
+ if (usb_serial_number != NULL) {
|
|
+ strncpy(g_usb_serial_number,
|
|
+ usb_serial_number,
|
|
+ ARRAY_SIZE(g_usb_serial_number));
|
|
+ pr_info("usb_serial_number:%s\n",
|
|
+ usb_serial_number);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
int check_user_usb_string(const char *name,
|
|
struct usb_gadget_strings *stringtab_dev)
|
|
{
|
|
@@ -60,6 +176,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)
|
|
@@ -265,7 +387,7 @@ static ssize_t gadget_dev_desc_UDC_store
|
|
|
|
mutex_lock(&gi->lock);
|
|
|
|
- if (!strlen(name)) {
|
|
+ if (!strlen(name) || strcmp(name, "none") == 0) {
|
|
ret = unregister_gadget(gi);
|
|
if (ret)
|
|
goto err;
|
|
@@ -1280,6 +1402,17 @@ static int configfs_composite_bind(struc
|
|
gs->strings[USB_GADGET_MANUFACTURER_IDX].s =
|
|
gs->manufacturer;
|
|
gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product;
|
|
+#if IS_ENABLED(CONFIG_USB_SUNXI_UDC0)
|
|
+ if (!gs->serialnumber) {
|
|
+ char *p_serial = kzalloc(sizeof(g_usb_serial_number), GFP_KERNEL);
|
|
+
|
|
+ if (!p_serial)
|
|
+ return -ENOMEM;
|
|
+ strncpy(p_serial, g_usb_serial_number,
|
|
+ sizeof(g_usb_serial_number) - 1);
|
|
+ gs->serialnumber = p_serial;
|
|
+ }
|
|
+#endif
|
|
gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber;
|
|
i++;
|
|
}
|
|
@@ -1369,6 +1502,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;
|
|
@@ -1388,25 +1575,185 @@ static void configfs_composite_unbind(st
|
|
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);
|
|
+
|
|
+ /* FIXME: There's a race between usb_gadget_udc_stop() which is likely
|
|
+ * to set the gadget driver to NULL in the udc driver and this drivers
|
|
+ * gadget disconnect fn which likely checks for the gadget driver to
|
|
+ * be a null ptr. It happens that unbind (doing set_gadget_data(NULL))
|
|
+ * is called before the gadget driver is set to NULL and the udc driver
|
|
+ * calls disconnect fn which results in cdev being a null ptr.
|
|
+ */
|
|
+ if (cdev == NULL) {
|
|
+ WARN(1, "%s: gadget driver already disconnected\n", __func__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* 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,
|
|
|
|
.max_speed = USB_SPEED_SUPER,
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
- .name = "configfs-gadget",
|
|
+ .name = "configfs-gadget",
|
|
},
|
|
.match_existing_only = 1,
|
|
};
|
|
|
|
+#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)
|
|
@@ -1458,7 +1805,11 @@ static struct config_group *gadgets_make
|
|
if (!gi->composite.gadget_driver.function)
|
|
goto err;
|
|
|
|
+ if (android_device_create(gi) < 0)
|
|
+ goto err;
|
|
+
|
|
return &gi->group;
|
|
+
|
|
err:
|
|
kfree(gi);
|
|
return ERR_PTR(-ENOMEM);
|
|
@@ -1467,6 +1818,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 = {
|
|
@@ -1503,9 +1855,20 @@ static int __init gadget_cfs_init(void)
|
|
{
|
|
int ret;
|
|
|
|
+#if IS_ENABLED(CONFIG_USB_SUNXI_UDC0)
|
|
+ get_android_usb_config();
|
|
+#endif
|
|
+
|
|
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 +1876,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);
|