// SPDX-License-Identifier: GPL-2.0 /* * MTK FASTMETA Gadget Driver for Android * * Copyright (c) 2008 Google, Inc. * Copyright (c) 2015 MediaTek Inc. * Author: Mike Lockwood * Benoit Goby */ #include #include #include #include #include #include #include #include #include #include /* Add for HW/SW connect */ #include "mtk_gadget.h" #include "function/u_fs.h" #include "function/f_mass_storage.h" MODULE_AUTHOR("Mike Lockwood"); MODULE_DESCRIPTION("Android Composite USB Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION("2.0"); static const char longname[] = "Gadget Android"; /* Default vendor and product IDs, overridden by userspace */ #define VENDOR_ID 0x0E8D #define PRODUCT_ID 0x0001 #ifdef CONFIG_MTPROF #include #endif static int quick_vcom_num; struct android_usb_function { char *name; void *config; struct device *dev; char *dev_name; struct device_attribute **attributes; /* for android_dev.enabled_functions */ struct list_head enabled_list; /* Optional: initialization during gadget bind */ int (*init)(struct android_usb_function *f, struct usb_composite_dev *cdev); /* Optional: cleanup during gadget unbind */ void (*cleanup)(struct android_usb_function *f); /* Optional: called when the function is added the list of * enabled functions */ void (*enable)(struct android_usb_function *f); /* Optional: called when it is removed */ void (*disable)(struct android_usb_function *f); int (*bind_config)(struct android_usb_function *f, struct usb_configuration *c); /* Optional: called when the configuration is removed */ void (*unbind_config)(struct android_usb_function *f, struct usb_configuration *c); /* Optional: handle ctrl requests before the device is configured */ int (*ctrlrequest)(struct android_usb_function *f, struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl_req); }; struct android_dev { struct android_usb_function **functions; struct list_head enabled_functions; struct usb_composite_dev *cdev; struct device *dev; void (*setup_complete)(struct usb_ep *ep, struct usb_request *req); bool enabled; int disable_depth; struct mutex mutex; bool connected; bool sw_connected; struct work_struct work; char ffs_aliases[256]; }; static struct class *android_class; static struct android_dev *_android_dev; static int android_bind_config(struct usb_configuration *c); static void android_unbind_config(struct usb_configuration *c); static int android_setup_config(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl); /* string IDs are assigned dynamically */ #define STRING_MANUFACTURER_IDX 0 #define STRING_PRODUCT_IDX 1 #define STRING_SERIAL_IDX 2 static char manufacturer_string[256]; static char product_string[256]; static char serial_str[256]; /* String Table */ static struct usb_string strings_dev[] = { [STRING_MANUFACTURER_IDX].s = manufacturer_string, [STRING_PRODUCT_IDX].s = product_string, [STRING_SERIAL_IDX].s = serial_str, { } /* end of list */ }; static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ .strings = strings_dev, }; static struct usb_gadget_strings *dev_strings[] = { &stringtab_dev, NULL, }; static struct usb_device_descriptor device_desc = { .bLength = sizeof(device_desc), .bDescriptorType = USB_DT_DEVICE, #ifdef CONFIG_USB_MU3D_DRV .bcdUSB = cpu_to_le16(0x0300), #else .bcdUSB = cpu_to_le16(0x0200), #endif .bDeviceClass = USB_CLASS_PER_INTERFACE, .idVendor = cpu_to_le16(VENDOR_ID), .idProduct = cpu_to_le16(PRODUCT_ID), .bcdDevice = cpu_to_le16(0xffff), .bNumConfigurations = 1, }; static struct usb_configuration android_config_driver = { .label = "android", .setup = android_setup_config, .unbind = android_unbind_config, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, #ifdef CONFIG_USB_MU3D_DRV /* for passing USB30CV Descriptor Test [USB3.0 devices]*/ .MaxPower = 192, #else .MaxPower = 500, /* 500ma */ #endif }; #ifdef CONFIG_MTPROF static void bootprof_log(char *str) { static int first_shot = 1; if (first_shot) { bootprof_log_boot(str); first_shot = 0; } } #else static void bootprof_log(char *str) {} #endif static void android_work(struct work_struct *data) { struct android_dev *dev = container_of(data, struct android_dev, work); struct usb_composite_dev *cdev = dev->cdev; char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; char *connected[2] = { "USB_STATE=CONNECTED", NULL }; char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; /* Add for HW/SW connect */ char **uevent_envp = NULL; unsigned long flags; if (!cdev) { pr_notice("%s, !cdev\n", __func__); return; } spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) uevent_envp = configured; else if (dev->connected != dev->sw_connected) uevent_envp = dev->connected ? connected : disconnected; dev->sw_connected = dev->connected; spin_unlock_irqrestore(&cdev->lock, flags); if (uevent_envp) { kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp); pr_notice("%s: sent uevent %s\n", __func__, uevent_envp[0]); if (IS_ENABLED(CONFIG_MTPROF)) if (uevent_envp == configured) bootprof_log("USB configured"); } else { pr_notice("%s: did not send uevent (%d %d %p)\n", __func__, dev->connected, dev->sw_connected, cdev->config); } } static void android_enable(struct android_dev *dev) { struct usb_composite_dev *cdev = dev->cdev; if (WARN_ON(!dev->disable_depth)) return; if (--dev->disable_depth == 0) { usb_add_config(cdev, &android_config_driver, android_bind_config); usb_gadget_connect(cdev->gadget); } } static void android_disable(struct android_dev *dev) { struct usb_composite_dev *cdev = dev->cdev; if (dev->disable_depth++ == 0) { usb_gadget_disconnect(cdev->gadget); /* Cancel pending control requests */ usb_ep_dequeue(cdev->gadget->ep0, cdev->req); usb_remove_config(cdev, &android_config_driver); } } /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ /* note all serial port number could not exceed MAX_U_SERIAL_PORTS */ #define MAX_ACM_INSTANCES 4 struct acm_function_config { int instances; int instances_on; struct usb_function *f_acm[MAX_ACM_INSTANCES]; struct usb_function_instance *f_acm_inst[MAX_ACM_INSTANCES]; int port_index[MAX_ACM_INSTANCES]; int port_index_on[MAX_ACM_INSTANCES]; }; static int acm_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { int i; int ret; struct acm_function_config *config; config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); if (!config) return -ENOMEM; f->config = config; for (i = 0; i < MAX_ACM_INSTANCES; i++) { config->f_acm_inst[i] = usb_get_function_instance("acm"); if (IS_ERR(config->f_acm_inst[i])) { ret = PTR_ERR(config->f_acm_inst[i]); goto err_usb_get_function_instance; } config->f_acm[i] = usb_get_function(config->f_acm_inst[i]); if (IS_ERR(config->f_acm[i])) { ret = PTR_ERR(config->f_acm[i]); goto err_usb_get_function; } } return 0; err_usb_get_function_instance: pr_info("Could not usb_get_function_instance() %d\n", i); while (i-- > 0) { usb_put_function(config->f_acm[i]); err_usb_get_function: pr_info("Could not usb_get_function() %d\n", i); usb_put_function_instance(config->f_acm_inst[i]); } return ret; } static void acm_function_cleanup(struct android_usb_function *f) { int i; struct acm_function_config *config = f->config; for (i = 0; i < MAX_ACM_INSTANCES; i++) { usb_put_function(config->f_acm[i]); usb_put_function_instance(config->f_acm_inst[i]); } kfree(f->config); f->config = NULL; } static int acm_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) { int i; int ret = 0; struct acm_function_config *config = f->config; /*1st:Modem, 2nd:Modem, 3rd:BT, 4th:MD logger*/ for (i = 0; i < MAX_ACM_INSTANCES; i++) { if (config->port_index[i] != 0 || (quick_vcom_num & (0x1 << i))) { ret = usb_add_function(c, config->f_acm[i]); if (ret) { pr_info("Could not bind acm%u config\n", i); goto err_usb_add_function; } pr_notice("%s Open /dev/ttyGS%d\n", __func__, i); config->port_index[i] = 0; config->port_index_on[i] = 1; config->instances = 0; } } quick_vcom_num = 0; config->instances_on = config->instances; for (i = 0; i < config->instances_on; i++) { ret = usb_add_function(c, config->f_acm[i]); if (ret) { pr_info("Could not bind acm%u config\n", i); goto err_usb_add_function; } } return 0; err_usb_add_function: while (i-- > 0) usb_remove_function(c, config->f_acm[i]); return ret; } static void acm_function_unbind_config(struct android_usb_function *f, struct usb_configuration *c) { struct acm_function_config *config = f->config; config->instances_on = 0; } static struct android_usb_function acm_function = { .name = "acm", .init = acm_function_init, .cleanup = acm_function_cleanup, .bind_config = acm_function_bind_config, .unbind_config = acm_function_unbind_config, }; struct mass_storage_function_config { struct usb_function *f_ms; struct usb_function_instance *f_ms_inst; }; #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS static struct fsg_module_parameters fsg_mod_data; FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); static int mass_storage_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { struct mass_storage_function_config *config; int ret, i; struct fsg_opts *fsg_opts; struct fsg_config m_config; pr_debug("%s(): Inside\n", __func__); config = kzalloc(sizeof(struct mass_storage_function_config), GFP_KERNEL); if (!config) return -ENOMEM; f->config = config; config->f_ms_inst = usb_get_function_instance("mass_storage"); if (IS_ERR(config->f_ms_inst)) { ret = PTR_ERR(config->f_ms_inst); goto err_usb_get_function_instance; } config->f_ms = usb_get_function(config->f_ms_inst); if (IS_ERR(config->f_ms)) { ret = PTR_ERR(config->f_ms); goto err_usb_get_function; } fsg_mod_data.file_count = 1; for (i = 0 ; i < fsg_mod_data.file_count; i++) { fsg_mod_data.file[i] = ""; fsg_mod_data.removable[i] = true; fsg_mod_data.nofua[i] = true; } fsg_config_from_params(&m_config, &fsg_mod_data, fsg_num_buffers); fsg_opts = fsg_opts_from_func_inst(config->f_ms_inst); ret = fsg_common_set_cdev(fsg_opts->common, cdev, m_config.can_stall); if (ret) { pr_info("%s(): error(%d) for fsg_common_set_cdev\n", __func__, ret); } /* this will affect lun create name */ fsg_common_set_sysfs(fsg_opts->common, true); ret = fsg_common_create_luns(fsg_opts->common, &m_config); if (ret) { pr_info("%s(): error(%d) for fsg_common_create_luns\n", __func__, ret); } /* use default one currently */ fsg_common_set_inquiry_string(fsg_opts->common, m_config.vendor_name, m_config.product_name); /* SYSFS create */ fsg_sysfs_update(fsg_opts->common, f->dev, true); /* invoke thread */ /* ret = fsg_common_run_thread(fsg_opts->common); */ /*if (ret) */ /* return ret; */ /* setup this to avoid create fsg thread in fsg_bind again */ fsg_opts->no_configfs = false; return 0; err_usb_get_function: usb_put_function_instance(config->f_ms_inst); err_usb_get_function_instance: return ret; } static void mass_storage_function_cleanup(struct android_usb_function *f) { struct mass_storage_function_config *config = f->config; /* release what we required */ struct fsg_opts *fsg_opts; fsg_opts = fsg_opts_from_func_inst(config->f_ms_inst); fsg_sysfs_update(fsg_opts->common, f->dev, false); usb_put_function(config->f_ms); usb_put_function_instance(config->f_ms_inst); kfree(f->config); f->config = NULL; } static int mass_storage_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) { struct mass_storage_function_config *config = f->config; int ret = 0; /* no_configfs :true, make fsg_bind skip for creating fsg thread */ ret = usb_add_function(c, config->f_ms); if (ret) pr_info("Could not bind config\n"); return 0; } /* mass_storage: storage_inquiry_show */ static ssize_t inquiry_string_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fsg_opts *fsg_opts; struct android_usb_function *f = dev_get_drvdata(dev); struct mass_storage_function_config *config = f->config; fsg_opts = fsg_opts_from_func_inst(config->f_ms_inst); return fsg_inquiry_show(fsg_opts->common, buf); } /* mass_storage: storage_inquiry_store */ static ssize_t inquiry_string_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct fsg_opts *fsg_opts; struct android_usb_function *f = dev_get_drvdata(dev); struct mass_storage_function_config *config = f->config; fsg_opts = fsg_opts_from_func_inst(config->f_ms_inst); return fsg_inquiry_store(fsg_opts->common, buf, size); } static DEVICE_ATTR_RW(inquiry_string); static struct device_attribute *mass_storage_function_attributes[] = { &dev_attr_inquiry_string, NULL }; static struct android_usb_function mass_storage_function = { .name = "mass_storage", .init = mass_storage_function_init, .cleanup = mass_storage_function_cleanup, .bind_config = mass_storage_function_bind_config, .attributes = mass_storage_function_attributes, }; static struct android_usb_function *supported_functions[] = { &acm_function, &mass_storage_function, NULL }; static int android_init_functions(struct android_usb_function **functions, struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct android_usb_function *f; struct device_attribute **attrs; struct device_attribute *attr; int err = 0; int index = 0; for (; (f = *functions++); index++) { f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name); pr_notice("[USB]%s: f->dev_name = %s, f->name = %s\n", __func__, f->dev_name, f->name); f->dev = device_create(android_class, dev->dev, MKDEV(0, index), f, f->dev_name); if (IS_ERR(f->dev)) { pr_info("%s: Failed to create dev %s", __func__, f->dev_name); err = PTR_ERR(f->dev); goto err_create; } if (f->init) { err = f->init(f, cdev); if (err) { pr_info("%s: Failed to init %s", __func__, f->name); goto err_out; } else pr_notice("[USB]%s: init %s success!!\n", __func__, f->name); } attrs = f->attributes; if (attrs) { while ((attr = *attrs++) && !err) err = device_create_file(f->dev, attr); } if (err) { pr_info("%s: Failed to create function %s attributes", __func__, f->name); goto err_out; } } return 0; err_out: device_destroy(android_class, f->dev->devt); err_create: kfree(f->dev_name); return err; } static void android_cleanup_functions(struct android_usb_function **functions) { struct android_usb_function *f; while (*functions) { f = *functions++; if (f->dev) { device_destroy(android_class, f->dev->devt); kfree(f->dev_name); } if (f->cleanup) f->cleanup(f); } } static int android_bind_enabled_functions(struct android_dev *dev, struct usb_configuration *c) { struct android_usb_function *f; int ret; list_for_each_entry(f, &dev->enabled_functions, enabled_list) { pr_notice("[USB]bind_config function '%s'/%p\n", f->name, f); ret = f->bind_config(f, c); if (ret) { pr_info("%s: %s failed", __func__, f->name); return ret; } } return 0; } static void android_unbind_enabled_functions(struct android_dev *dev, struct usb_configuration *c) { struct android_usb_function *f; list_for_each_entry(f, &dev->enabled_functions, enabled_list) { if (f->unbind_config) f->unbind_config(f, c); } } static int android_enable_function(struct android_dev *dev, const char *name) { struct android_usb_function **functions = dev->functions; struct android_usb_function *f; while ((f = *functions++)) { if (!strcmp(name, f->name)) { list_add_tail(&f->enabled_list, &dev->enabled_functions); return 0; } } return -EINVAL; } /*-------------------------------------------------------------------------*/ /* /sys/class/android_usb/android%d/ interface */ static ssize_t functions_show(struct device *pdev, struct device_attribute *attr, char *buf) { struct android_dev *dev = dev_get_drvdata(pdev); struct android_usb_function *f; char *buff = buf; pr_notice("[USB]%s: ", __func__); mutex_lock(&dev->mutex); list_for_each_entry(f, &dev->enabled_functions, enabled_list) buff += sprintf(buff, "%s,", f->name); mutex_unlock(&dev->mutex); if (buff != buf) *(buff-1) = '\n'; return buff - buf; } static ssize_t functions_store(struct device *pdev, struct device_attribute *attr, const char *buff, size_t size) { struct android_dev *dev = dev_get_drvdata(pdev); char *name; char buf[256], *b; char aliases[256], *a; int err; int is_ffs; int ffs_enabled = 0; mutex_lock(&dev->mutex); if (dev->enabled) { mutex_unlock(&dev->mutex); return -EBUSY; } INIT_LIST_HEAD(&dev->enabled_functions); strlcpy(buf, buff, sizeof(buf)); b = strim(buf); while (b) { name = strsep(&b, ","); pr_notice("[USB]%s: name = %s\n", __func__, name); if (!name) continue; is_ffs = 0; strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); a = aliases; while (a) { char *alias = strsep(&a, ","); if (alias && !strcmp(name, alias)) { is_ffs = 1; break; } } if (is_ffs) { if (ffs_enabled) continue; err = android_enable_function(dev, "ffs"); if (err) pr_info("android_usb: Cannot enable ffs (%d)", err); else ffs_enabled = 1; continue; } err = android_enable_function(dev, name); if (err) pr_info("android_usb: Cannot enable '%s' (%d)", name, err); } mutex_unlock(&dev->mutex); return size; } static ssize_t enable_show(struct device *pdev, struct device_attribute *attr, char *buf) { struct android_dev *dev = dev_get_drvdata(pdev); return sprintf(buf, "%d\n", dev->enabled); } static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, const char *buff, size_t size) { struct android_dev *dev = dev_get_drvdata(pdev); struct usb_composite_dev *cdev = dev->cdev; struct android_usb_function *f; int enabled = 0; int ret; if (!cdev) return -ENODEV; mutex_lock(&dev->mutex); pr_notice("[USB]%s: %d %d\n", __func__, enabled, dev->enabled); ret = kstrtoint(buff, 0, &enabled); if (enabled && !dev->enabled) { /* ALPS01770952 * Reset next_string_id to 0 before enabling the gadget driver. * Otherwise, after a large number of enable/disable cycles, * function bind will fail because * we cannot allocate new string ids. * String ids cannot be larger than 254 per USB spec. * 0~15 are reserved for usb device descriptor * 16~254 are for functions. */ cdev->next_string_id = 0x10; /* * Update values in composite driver's copy of * device descriptor. */ cdev->desc.idVendor = device_desc.idVendor; cdev->desc.idProduct = device_desc.idProduct; cdev->desc.bcdDevice = device_desc.bcdDevice; cdev->desc.bDeviceClass = device_desc.bDeviceClass; cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; /* special case for meta mode */ if (strlen(serial_str) == 0) cdev->desc.iSerialNumber = 0; else cdev->desc.iSerialNumber = device_desc.iSerialNumber; list_for_each_entry(f, &dev->enabled_functions, enabled_list) { if (f->enable) f->enable(f); } android_enable(dev); dev->enabled = true; pr_notice("[USB]%s: enable 0->1 case, idVendor=0x%x, idProduct=0x%x\n", __func__, device_desc.idVendor, device_desc.idProduct); if (IS_ENABLED(CONFIG_MTPROF)) bootprof_log("USB ready"); } else if (!enabled && dev->enabled) { pr_notice("[USB]%s: enable 1->0 case, idVendor=0x%x, idProduct=0x%x\n", __func__, device_desc.idVendor, device_desc.idProduct); android_disable(dev); list_for_each_entry(f, &dev->enabled_functions, enabled_list) { pr_notice("[USB]disable function '%s'/%p\n", f->name, f); if (f->disable) f->disable(f); } dev->enabled = false; } else { pr_info("android_usb: already %s\n", dev->enabled ? "enabled" : "disabled"); } mutex_unlock(&dev->mutex); return size; } static ssize_t state_show(struct device *pdev, struct device_attribute *attr, char *buf) { struct android_dev *dev = dev_get_drvdata(pdev); struct usb_composite_dev *cdev = dev->cdev; char *state = "DISCONNECTED"; unsigned long flags; if (!cdev) goto out; spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) state = "CONFIGURED"; else if (dev->connected) state = "CONNECTED"; pr_info("[USB]%s, state:%s\n", __func__, state); spin_unlock_irqrestore(&cdev->lock, flags); out: return sprintf(buf, "%s\n", state); } #define LOG_BUG_SZ 2048 static char log_buf[LOG_BUG_SZ]; static int log_buf_idx; static ssize_t log_show(struct device *pdev, struct device_attribute *attr, char *buf) { struct android_dev *dev = dev_get_drvdata(pdev); mutex_lock(&dev->mutex); memcpy(buf, log_buf, log_buf_idx); mutex_unlock(&dev->mutex); return log_buf_idx; } static ssize_t log_store(struct device *pdev, struct device_attribute *attr, const char *buff, size_t size) { struct android_dev *dev = dev_get_drvdata(pdev); char buf[256], n; mutex_lock(&dev->mutex); n = strlcpy(buf, buff, sizeof(buf)); if ((log_buf_idx + (n + 1)) > LOG_BUG_SZ) log_buf_idx = 0; memcpy(log_buf + log_buf_idx, buf, n); log_buf_idx += n; log_buf[log_buf_idx++] = ' '; pr_info("[USB]%s, <%s>, n:%d, log_buf_idx:%d\n", __func__, buf, n, log_buf_idx); mutex_unlock(&dev->mutex); return size; } #define DESCRIPTOR_ATTR(field, format_string) \ static ssize_t \ field ## _show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ return sprintf(buf, format_string, device_desc.field); \ } \ static ssize_t \ field ## _store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t size) \ { \ int value; \ if (sscanf(buf, format_string, &value) == 1) { \ device_desc.field = value; \ return size; \ } \ return -1; \ } \ static DEVICE_ATTR(field, 0444, field ## _show, field ## _store) #define DESCRIPTOR_STRING_ATTR(field, buffer) \ static ssize_t \ field ## _show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ return sprintf(buf, "%s", buffer); \ } \ static ssize_t \ field ## _store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t size) \ { \ if (size >= sizeof(buffer)) \ return -EINVAL; \ return strlcpy(buffer, buf, sizeof(buffer)); \ } \ static DEVICE_ATTR(field, 0444, field ## _show, field ## _store) DESCRIPTOR_ATTR(idVendor, "%04x\n"); DESCRIPTOR_ATTR(idProduct, "%04x\n"); DESCRIPTOR_ATTR(bcdDevice, "%04x\n"); DESCRIPTOR_ATTR(bDeviceClass, "%d\n"); DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n"); DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n"); DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string); DESCRIPTOR_STRING_ATTR(iProduct, product_string); DESCRIPTOR_STRING_ATTR(iSerial, serial_str); static DEVICE_ATTR_RW(functions); static DEVICE_ATTR_RW(enable); static DEVICE_ATTR_RO(state); static DEVICE_ATTR_RW(log); static struct device_attribute *android_usb_attributes[] = { &dev_attr_idVendor, &dev_attr_idProduct, &dev_attr_bcdDevice, &dev_attr_bDeviceClass, &dev_attr_bDeviceSubClass, &dev_attr_bDeviceProtocol, &dev_attr_iManufacturer, &dev_attr_iProduct, &dev_attr_iSerial, &dev_attr_functions, &dev_attr_enable, &dev_attr_state, &dev_attr_log, NULL }; /*-------------------------------------------------------------------------*/ /* Composite driver */ static int android_bind_config(struct usb_configuration *c) { struct android_dev *dev = _android_dev; int ret = 0; ret = android_bind_enabled_functions(dev, c); if (ret) return ret; return 0; } static void android_unbind_config(struct usb_configuration *c) { struct android_dev *dev = _android_dev; android_unbind_enabled_functions(dev, c); } static int android_setup_config(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) { int handled = -EINVAL; const u8 recip = ctrl->bRequestType & USB_RECIP_MASK; pr_notice("%s bRequestType=%x, bRequest=%x, recip=%x\n", __func__, ctrl->bRequestType, ctrl->bRequest, recip); if (!((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)) return handled; switch (ctrl->bRequest) { case USB_REQ_CLEAR_FEATURE: switch (recip) { case USB_RECIP_DEVICE: switch (ctrl->wValue) { case USB_DEVICE_U1_ENABLE: handled = 1; pr_notice("Clear Feature->U1 Enable\n"); break; case USB_DEVICE_U2_ENABLE: handled = 1; pr_notice("Clear Feature->U2 Enable\n"); break; default: handled = -EINVAL; break; } break; default: handled = -EINVAL; break; } break; case USB_REQ_SET_FEATURE: switch (recip) { case USB_RECIP_DEVICE: switch (ctrl->wValue) { case USB_DEVICE_U1_ENABLE: pr_notice("Set Feature->U1 Enable\n"); handled = 1; break; case USB_DEVICE_U2_ENABLE: pr_notice("Set Feature->U2 Enable\n"); handled = 1; break; default: handled = -EINVAL; break; } break; default: handled = -EINVAL; break; } break; default: handled = -EINVAL; break; } return handled; } static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct usb_gadget *gadget = cdev->gadget; int id, ret; /* Save the default handler */ dev->setup_complete = cdev->req->complete; /* * Start disconnected. Userspace will connect the gadget once * it is done configuring the functions. */ usb_gadget_disconnect(gadget); ret = android_init_functions(dev->functions, cdev); if (ret) return ret; /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ id = usb_string_id(cdev); if (id < 0) return id; strings_dev[STRING_MANUFACTURER_IDX].id = id; device_desc.iManufacturer = id; id = usb_string_id(cdev); if (id < 0) return id; strings_dev[STRING_PRODUCT_IDX].id = id; device_desc.iProduct = id; /* Default strings - should be updated by userspace */ strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1); strncpy(product_string, "Android", sizeof(product_string) - 1); strncpy(serial_str, "0123456789ABCDEF", sizeof(serial_str) - 1); id = usb_string_id(cdev); if (id < 0) return id; strings_dev[STRING_SERIAL_IDX].id = id; device_desc.iSerialNumber = id; usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; return 0; } static int android_usb_unbind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; cancel_work_sync(&dev->work); android_cleanup_functions(dev->functions); return 0; } /* HACK: android needs to override setup for accessory to work */ static int (*composite_setup_func)(struct usb_gadget *gadget, const struct usb_ctrlrequest *c); static int android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) { struct android_dev *dev = _android_dev; struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_request *req = cdev->req; struct android_usb_function *f; int value = -EOPNOTSUPP; unsigned long flags; req->zero = 0; req->length = 0; /* no need for FASTMETA */ /* req->complete = dev->setup_complete; */ /* req->complete = composite_setup_complete; */ gadget->ep0->driver_data = cdev; list_for_each_entry(f, &dev->enabled_functions, enabled_list) { if (f->ctrlrequest) { value = f->ctrlrequest(f, cdev, c); if (value >= 0) break; } } if (value < 0) value = composite_setup_func(gadget, c); spin_lock_irqsave(&cdev->lock, flags); if (!dev->connected) { dev->connected = 1; schedule_work(&dev->work); } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) { schedule_work(&dev->work); } spin_unlock_irqrestore(&cdev->lock, flags); return value; } static void android_disconnect(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; dev->connected = 0; schedule_work(&dev->work); pr_notice("[USB]%s: dev->connected = %d\n", __func__, dev->connected); } static struct usb_composite_driver android_usb_driver = { .name = "android_usb", .dev = &device_desc, .strings = dev_strings, .bind = android_bind, .unbind = android_usb_unbind, .disconnect = android_disconnect, #ifdef CONFIG_USB_MU3D_DRV .max_speed = USB_SPEED_SUPER #else .max_speed = USB_SPEED_HIGH #endif }; #define USB_STATE_MONITOR_DELAY 3000 static struct delayed_work android_usb_state_monitor_work; static void do_android_usb_state_monitor_work(struct work_struct *work) { struct android_dev *dev = _android_dev; char *usb_state = "NO-DEV"; if (dev && dev->cdev) usb_state = "DISCONNECTED"; if (dev && dev->cdev && dev->cdev->config) usb_state = "CONFIGURED"; pr_info("usb_state<%s>\n", usb_state); schedule_delayed_work(&android_usb_state_monitor_work, msecs_to_jiffies(USB_STATE_MONITOR_DELAY)); } void trigger_android_usb_state_monitor_work(void) { static int inited; #ifdef CONFIG_FPGA_EARLY_PORTING pr_info("SKIP %s\n", __func__); return; #endif if (!inited) { /* TIMER_DEFERRABLE for not interfering with deep idle */ INIT_DEFERRABLE_WORK(&android_usb_state_monitor_work, do_android_usb_state_monitor_work); inited = 1; } schedule_delayed_work(&android_usb_state_monitor_work, msecs_to_jiffies(USB_STATE_MONITOR_DELAY)); }; static int android_create_device(struct android_dev *dev) { struct device_attribute **attrs = android_usb_attributes; struct device_attribute *attr; int err; dev->dev = device_create(android_class, NULL, MKDEV(0, 0), NULL, "android0"); if (IS_ERR(dev->dev)) return PTR_ERR(dev->dev); dev_set_drvdata(dev->dev, dev); while ((attr = *attrs++)) { err = device_create_file(dev->dev, attr); if (err) { device_destroy(android_class, dev->dev->devt); return err; } } trigger_android_usb_state_monitor_work(); return 0; } void enable_meta_vcom(int mode) { struct android_dev *dev = _android_dev; struct device *pdev = dev->dev; enable_store(pdev, NULL, "0", 1); if (mode == 1) { strncpy(serial_str, "", sizeof(serial_str) - 1); device_desc.idVendor = 0x0e8d; device_desc.idProduct = 0x2007; device_desc.bDeviceClass = 0x02; /*ttyGS0*/ quick_vcom_num = (1 << 0); functions_store(pdev, NULL, "acm", 3); } else if (mode == 2) { strncpy(serial_str, "", sizeof(serial_str) - 1); device_desc.idVendor = 0x0e8d; device_desc.idProduct = 0x202d; /*ttyGS0 + ttyGS3*/ quick_vcom_num = (1 << 0) + (1 << 3); functions_store(pdev, NULL, "mass_storage,acm", 16); } enable_store(pdev, NULL, "1", 1); } static const char TAG_NAME[] = "androidboot.usbconfig="; int meta_dt_get_mboot_params(void) { struct device_node *np_chosen; struct tag_bootmode *tag = NULL; char *ptr; char mode; np_chosen = of_find_node_by_path("/chosen"); if (!np_chosen) np_chosen = of_find_node_by_path("/chosen@0"); tag = (struct tag_bootmode *)of_get_property(np_chosen, "atag,boot", NULL); if (!tag) { pr_notice("%s: fail to get atag,boot\n", __func__); } else { pr_notice("bootmode: 0x%x boottype: 0x%x\n", tag->bootmode, tag->boottype); /* check androidboot.usbconfig mode */ ptr = strstr(saved_command_line, TAG_NAME); if (ptr) { mode = *(ptr + strlen(TAG_NAME)); } else { pr_notice("cannot find \"androidboot.usbconfig=\"\n"); return 0; } if (mode == '1' || mode == '2') { pr_notice("Mediatek FASTMETA mode [%c]\n", mode); if (mode == '1') return 1; else if (mode == '2') return 2; } pr_notice("not FASTMETA mode [%c]\n", mode); } return 0; } static int __init meta_usb_init(void) { struct android_dev *dev; int err; int config = 0; config = meta_dt_get_mboot_params(); if (!config) return 0; pr_info("%s: config %d", __func__, config); android_class = class_create(THIS_MODULE, "android_usb"); if (IS_ERR(android_class)) return PTR_ERR(android_class); dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { err = -ENOMEM; goto err_dev; } dev->disable_depth = 1; dev->functions = supported_functions; INIT_LIST_HEAD(&dev->enabled_functions); INIT_WORK(&dev->work, android_work); mutex_init(&dev->mutex); err = android_create_device(dev); if (err) { pr_info("%s: failed to create android device %d\n", __func__, err); goto err_create; } _android_dev = dev; err = usb_composite_probe(&android_usb_driver); if (err) { pr_info("%s: failed to probe driver %d\n", __func__, err); _android_dev = NULL; goto err_probe; } /* HACK: exchange composite's setup with ours */ composite_setup_func = android_usb_driver.gadget_driver.setup; android_usb_driver.gadget_driver.setup = android_setup; enable_meta_vcom(config); return 0; err_probe: device_destroy(android_class, dev->dev->devt); err_create: kfree(dev); err_dev: class_destroy(android_class); return err; } late_initcall(meta_usb_init); static void __exit cleanup(void) { usb_composite_unregister(&android_usb_driver); class_destroy(android_class); kfree(_android_dev); _android_dev = NULL; } module_exit(cleanup);