mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 12:58:12 -04:00
Merge "defconfig: msm: Enable QTI composite gadget on sdxlemur"
This commit is contained in:
commit
77e147b951
7 changed files with 705 additions and 0 deletions
1
arch/arm/configs/vendor/sdxlemur.config
vendored
1
arch/arm/configs/vendor/sdxlemur.config
vendored
|
|
@ -226,6 +226,7 @@ CONFIG_USB_CONFIGFS_F_CDEV=y
|
|||
CONFIG_USB_CONFIGFS_F_GSI=y
|
||||
CONFIG_USB_CONFIGFS_F_DIAG=y
|
||||
CONFIG_USB_CONFIGFS_F_UAC1=y
|
||||
CONFIG_USB_G_QTI=y
|
||||
CONFIG_USB_BAM=y
|
||||
CONFIG_USB_RTL8152=y
|
||||
CONFIG_USB_NET_SMSC75XX=y
|
||||
|
|
|
|||
|
|
@ -3000,6 +3000,7 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
struct gsi_function_bind_info info = {0};
|
||||
struct f_gsi *gsi = func_to_gsi(f);
|
||||
struct rndis_params *params;
|
||||
struct usb_os_desc *descs[1];
|
||||
struct gsi_opts *opts;
|
||||
int status;
|
||||
__u8 class;
|
||||
|
|
@ -3202,6 +3203,14 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
f->os_desc_n = 1;
|
||||
f->os_desc_table[0].os_desc = &opts->os_desc;
|
||||
f->os_desc_table[0].if_id = gsi->data_id;
|
||||
opts->os_desc.ext_compat_id = opts->ext_compat_id;
|
||||
descs[0] = &opts->os_desc;
|
||||
snprintf(sub_compatible_id, sizeof(sub_compatible_id),
|
||||
"%u", c->bConfigurationValue);
|
||||
memcpy(descs[0]->ext_compat_id, compatible_id,
|
||||
strlen(compatible_id));
|
||||
memcpy(descs[0]->ext_compat_id + 8, sub_compatible_id,
|
||||
strlen(sub_compatible_id));
|
||||
}
|
||||
break;
|
||||
case IPA_USB_RMNET:
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@
|
|||
/* ID for Microsoft OS String */
|
||||
#define GSI_MBIM_OS_STRING_ID 0xEE
|
||||
|
||||
static char compatible_id[256] = "ALTRCFG";
|
||||
static char sub_compatible_id[8];
|
||||
|
||||
#define EVT_NONE 0
|
||||
#define EVT_UNINITIALIZED 1
|
||||
#define EVT_INITIALIZED 2
|
||||
|
|
|
|||
|
|
@ -350,6 +350,16 @@ config USB_G_PRINTER
|
|||
For more information, see Documentation/usb/gadget_printer.rst
|
||||
which includes sample code for accessing the device file.
|
||||
|
||||
config USB_G_QTI
|
||||
tristate "QTI composite gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This gadget provides support for multi-configuration
|
||||
composite device.
|
||||
Functions can be specified via devicetree properties,
|
||||
along with PID and PID details which can also also be
|
||||
updated using kernel commandline using module parameters.
|
||||
|
||||
if TTY
|
||||
|
||||
config USB_CDC_COMPOSITE
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ g_webcam-y := webcam.o
|
|||
g_ncm-y := ncm.o
|
||||
g_acm_ms-y := acm_ms.o
|
||||
g_tcm_usb_gadget-y := tcm_usb_gadget.o
|
||||
g_qti_gadget-y := qti_gadget.o
|
||||
|
||||
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||||
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
|
|
@ -44,3 +45,4 @@ obj-$(CONFIG_USB_G_NCM) += g_ncm.o
|
|||
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
|
||||
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
|
||||
obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o
|
||||
obj-$(CONFIG_USB_G_QTI) += g_qti_gadget.o
|
||||
|
|
|
|||
677
drivers/usb/gadget/legacy/qti_gadget.c
Normal file
677
drivers/usb/gadget/legacy/qti_gadget.c
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct qti_usb_function {
|
||||
struct usb_function_instance *fi;
|
||||
struct usb_function *f;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define MAX_FUNC_NAME_LEN 48
|
||||
#define MAX_CFG_NAME_LEN 128
|
||||
|
||||
#define QW_SIGN_LEN 14
|
||||
|
||||
char qw_sign[QW_SIGN_LEN] = "MSFT100";
|
||||
|
||||
struct qti_usb_config {
|
||||
struct usb_configuration c;
|
||||
|
||||
/* List of functions bound to this config */
|
||||
struct list_head func_list;
|
||||
/* List of qti_usb_functions bound to this config */
|
||||
struct list_head qti_funcs;
|
||||
};
|
||||
|
||||
struct qti_usb_gadget {
|
||||
struct usb_composite_dev cdev;
|
||||
struct usb_composite_driver composite;
|
||||
|
||||
const char *composition_funcs;
|
||||
bool enabled;
|
||||
struct device *dev;
|
||||
struct device_node *cfg_node;
|
||||
};
|
||||
|
||||
static char manufacturer_string[256] = "Qualcomm Technologies, Inc";
|
||||
module_param_string(manufacturer, manufacturer_string,
|
||||
sizeof(manufacturer_string), 0644);
|
||||
MODULE_PARM_DESC(quirks, "String representing name of manufacturer");
|
||||
|
||||
static char product_string[256] = "USB_device!_SN:12345";
|
||||
module_param_string(product, product_string,
|
||||
sizeof(product_string), 0644);
|
||||
MODULE_PARM_DESC(quirks, "String representing product string");
|
||||
|
||||
static char serialno_string[256] = "12345";
|
||||
|
||||
static char usb_pid_string[256];
|
||||
module_param_string(usb_pid, usb_pid_string, sizeof(usb_pid_string), 0644);
|
||||
MODULE_PARM_DESC(quirks, "String representing product id");
|
||||
|
||||
/* String Table */
|
||||
static struct usb_string strings_dev[] = {
|
||||
[USB_GADGET_MANUFACTURER_IDX].s = manufacturer_string,
|
||||
[USB_GADGET_PRODUCT_IDX].s = product_string,
|
||||
[USB_GADGET_SERIAL_IDX].s = serialno_string,
|
||||
{ } /* 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 void qti_configs_remove_funcs(struct qti_usb_gadget *qg)
|
||||
{
|
||||
struct usb_configuration *c;
|
||||
|
||||
list_for_each_entry(c, &qg->cdev.configs, list) {
|
||||
struct qti_usb_config *cfg;
|
||||
struct usb_function *f, *tmp;
|
||||
|
||||
cfg = container_of(c, struct qti_usb_config, c);
|
||||
|
||||
list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) {
|
||||
|
||||
list_move(&f->list, &cfg->func_list);
|
||||
if (f->unbind) {
|
||||
dev_dbg(&qg->cdev.gadget->dev,
|
||||
"unbind function '%s'/%pK\n",
|
||||
f->name, f);
|
||||
f->unbind(c, f);
|
||||
}
|
||||
}
|
||||
c->fullspeed = 0;
|
||||
c->highspeed = 0;
|
||||
c->superspeed = 0;
|
||||
c->superspeed_plus = 0;
|
||||
c->next_interface_id = 0;
|
||||
memset(c->interface, 0, sizeof(c->interface));
|
||||
}
|
||||
}
|
||||
|
||||
static int qti_composite_bind(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *gdriver)
|
||||
{
|
||||
struct usb_composite_driver *composite = to_cdriver(gdriver);
|
||||
struct qti_usb_gadget *qg = container_of(composite,
|
||||
struct qti_usb_gadget, composite);
|
||||
struct usb_composite_dev *cdev = &qg->cdev;
|
||||
struct usb_configuration *c;
|
||||
struct usb_string *s;
|
||||
int ret = -EINVAL;
|
||||
|
||||
cdev->gadget = gadget;
|
||||
set_gadget_data(gadget, cdev);
|
||||
spin_lock_init(&qg->cdev.lock);
|
||||
|
||||
ret = composite_dev_prepare(composite, cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (list_empty(&cdev->configs)) {
|
||||
pr_err("No configurations found in %s.\n", composite->name);
|
||||
ret = -EINVAL;
|
||||
goto composite_cleanup;
|
||||
}
|
||||
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
struct qti_usb_config *qcfg;
|
||||
|
||||
qcfg = container_of(c, struct qti_usb_config, c);
|
||||
if (list_empty(&qcfg->func_list)) {
|
||||
pr_err("Config %s/%d of %s doesn't have a function.\n",
|
||||
c->label, c->bConfigurationValue,
|
||||
qg->composite.name);
|
||||
goto composite_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
s = usb_gstrings_attach(cdev, dev_strings, USB_GADGET_FIRST_AVAIL_IDX);
|
||||
if (IS_ERR(s)) {
|
||||
ret = PTR_ERR(s);
|
||||
goto composite_cleanup;
|
||||
}
|
||||
|
||||
cdev->desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
cdev->desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id;
|
||||
cdev->desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id;
|
||||
|
||||
/* Go through all configs, attach all functions */
|
||||
list_for_each_entry(c, &qg->cdev.configs, list) {
|
||||
struct qti_usb_config *qcfg;
|
||||
struct usb_function *f, *tmp;
|
||||
|
||||
qcfg = container_of(c, struct qti_usb_config, c);
|
||||
|
||||
list_for_each_entry_safe(f, tmp, &qcfg->func_list, list) {
|
||||
list_del(&f->list);
|
||||
ret = usb_add_function(c, f);
|
||||
if (ret) {
|
||||
list_add(&f->list, &qcfg->func_list);
|
||||
goto remove_funcs;
|
||||
}
|
||||
}
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
}
|
||||
|
||||
if (cdev->use_os_string) {
|
||||
ret = composite_os_desc_req_prepare(cdev, gadget->ep0);
|
||||
if (ret)
|
||||
goto remove_funcs;
|
||||
}
|
||||
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
|
||||
return 0;
|
||||
|
||||
remove_funcs:
|
||||
qti_configs_remove_funcs(qg);
|
||||
composite_cleanup:
|
||||
composite_dev_cleanup(cdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qti_composite_unbind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
struct qti_usb_gadget *qg;
|
||||
|
||||
cdev = get_gadget_data(gadget);
|
||||
qg = container_of(cdev, struct qti_usb_gadget, cdev);
|
||||
|
||||
qti_configs_remove_funcs(qg);
|
||||
composite_dev_cleanup(cdev);
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
cdev->gadget = NULL;
|
||||
set_gadget_data(gadget, NULL);
|
||||
}
|
||||
|
||||
static const struct usb_gadget_driver qti_gadget_driver = {
|
||||
.bind = qti_composite_bind,
|
||||
.unbind = qti_composite_unbind,
|
||||
.setup = composite_setup,
|
||||
.reset = composite_disconnect,
|
||||
.disconnect = composite_disconnect,
|
||||
.suspend = composite_suspend,
|
||||
.resume = composite_resume,
|
||||
|
||||
.max_speed = USB_SPEED_SUPER_PLUS,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "qti-gadget",
|
||||
},
|
||||
};
|
||||
|
||||
static void qti_usb_funcs_free(struct qti_usb_config *qcfg)
|
||||
{
|
||||
struct usb_function *f, *tmp;
|
||||
struct qti_usb_function *qf, *qf_tmp;
|
||||
|
||||
list_for_each_entry_safe(f, tmp, &qcfg->func_list, list) {
|
||||
list_del(&f->list);
|
||||
usb_put_function(f);
|
||||
|
||||
/* find corresponding function_instance and free it */
|
||||
list_for_each_entry_safe(qf, qf_tmp, &qcfg->qti_funcs, list) {
|
||||
if (qf->f == f) {
|
||||
list_del(&qf->list);
|
||||
usb_put_function_instance(qf->fi);
|
||||
kfree(qf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void qti_cleanup_configs_funcs(struct qti_usb_gadget *qg)
|
||||
{
|
||||
struct usb_configuration *c, *c_tmp;
|
||||
|
||||
list_for_each_entry_safe(c, c_tmp, &qg->cdev.configs, list) {
|
||||
struct qti_usb_config *qcfg;
|
||||
|
||||
qcfg = container_of(c, struct qti_usb_config, c);
|
||||
WARN_ON(!list_empty(&qcfg->c.functions));
|
||||
|
||||
qti_usb_funcs_free(qcfg);
|
||||
|
||||
list_del(&qcfg->c.list);
|
||||
kfree(qcfg->c.label);
|
||||
kfree(qcfg);
|
||||
}
|
||||
}
|
||||
|
||||
static int qti_usb_func_alloc(struct qti_usb_config *qcfg,
|
||||
const char *name)
|
||||
{
|
||||
struct qti_usb_function *qf;
|
||||
struct usb_function_instance *fi;
|
||||
struct usb_composite_dev *cdev = qcfg->c.cdev;
|
||||
struct usb_function *f;
|
||||
char buf[MAX_FUNC_NAME_LEN];
|
||||
char *func_name;
|
||||
char *instance_name;
|
||||
int ret;
|
||||
|
||||
ret = scnprintf(buf, MAX_FUNC_NAME_LEN, "%s", name);
|
||||
if (ret >= MAX_FUNC_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
func_name = buf;
|
||||
instance_name = strnchr(func_name, MAX_FUNC_NAME_LEN, '.');
|
||||
if (!instance_name) {
|
||||
pr_err("Can't find . in <func>.<instance>:%s\n", buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
*instance_name = '\0';
|
||||
instance_name++;
|
||||
|
||||
qf = kzalloc(sizeof(*qf), GFP_KERNEL);
|
||||
if (!qf)
|
||||
return -ENOMEM;
|
||||
|
||||
fi = usb_get_function_instance(func_name);
|
||||
if (IS_ERR(fi)) {
|
||||
kfree(qf);
|
||||
return PTR_ERR(fi);
|
||||
}
|
||||
qf->fi = fi;
|
||||
|
||||
if (fi->set_inst_name) {
|
||||
ret = fi->set_inst_name(fi, instance_name);
|
||||
if (ret) {
|
||||
kfree(qf);
|
||||
usb_put_function_instance(fi);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
f = usb_get_function(fi);
|
||||
if (IS_ERR(f)) {
|
||||
kfree(qf);
|
||||
usb_put_function_instance(fi);
|
||||
return PTR_ERR(f);
|
||||
}
|
||||
qf->f = f;
|
||||
list_add_tail(&qf->list, &qcfg->qti_funcs);
|
||||
|
||||
/* stash the function until we bind it to the gadget */
|
||||
list_add_tail(&f->list, &qcfg->func_list);
|
||||
|
||||
if (!strcmp(instance_name, "mbim")) {
|
||||
cdev->os_desc_config = &qcfg->c;
|
||||
cdev->use_os_string = true;
|
||||
cdev->b_vendor_code = 0xA5;
|
||||
memcpy(cdev->qw_sign, qw_sign, QW_SIGN_LEN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qti_usb_funcs_alloc(struct qti_usb_config *qcfg,
|
||||
const char *funcs)
|
||||
{
|
||||
char buf[MAX_CFG_NAME_LEN];
|
||||
char *fn_name, *next_fn;
|
||||
int ret = 0;
|
||||
|
||||
ret = scnprintf(buf, MAX_CFG_NAME_LEN, "%s", funcs);
|
||||
if (ret >= MAX_CFG_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
fn_name = buf;
|
||||
while (fn_name) {
|
||||
next_fn = strnchr(fn_name, MAX_CFG_NAME_LEN, ',');
|
||||
if (next_fn)
|
||||
*next_fn++ = '\0';
|
||||
|
||||
ret = qti_usb_func_alloc(qcfg, fn_name);
|
||||
if (ret) {
|
||||
qti_usb_funcs_free(qcfg);
|
||||
break;
|
||||
}
|
||||
|
||||
fn_name = next_fn;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qti_usb_config_add(struct qti_usb_gadget *gadget,
|
||||
const char *name, u8 num)
|
||||
{
|
||||
struct qti_usb_config *qcfg;
|
||||
int ret = 0, val = 0;
|
||||
|
||||
qcfg = kzalloc(sizeof(*qcfg), GFP_KERNEL);
|
||||
if (!qcfg)
|
||||
return -ENOMEM;
|
||||
|
||||
qcfg->c.label = kstrdup(name, GFP_KERNEL);
|
||||
if (!qcfg->c.label) {
|
||||
ret = -ENOMEM;
|
||||
goto free_cfg;
|
||||
}
|
||||
qcfg->c.bConfigurationValue = num;
|
||||
qcfg->c.bmAttributes = USB_CONFIG_ATT_ONE;
|
||||
qcfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
|
||||
ret = of_property_read_u32(gadget->cfg_node, "qcom,bmAttributes", &val);
|
||||
if (!ret)
|
||||
qcfg->c.bmAttributes = (u8)val;
|
||||
|
||||
INIT_LIST_HEAD(&qcfg->func_list);
|
||||
INIT_LIST_HEAD(&qcfg->qti_funcs);
|
||||
|
||||
ret = usb_add_config_only(&gadget->cdev, &qcfg->c);
|
||||
if (ret)
|
||||
goto free_label;
|
||||
|
||||
ret = qti_usb_funcs_alloc(qcfg, name);
|
||||
if (ret)
|
||||
goto cfg_del;
|
||||
|
||||
return ret;
|
||||
|
||||
cfg_del:
|
||||
list_del(&qcfg->c.list);
|
||||
free_label:
|
||||
kfree(qcfg->c.label);
|
||||
free_cfg:
|
||||
kfree(qcfg);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int qti_usb_configs_make(struct qti_usb_gadget *gadget,
|
||||
const char *cfgs)
|
||||
{
|
||||
char buf[MAX_CFG_NAME_LEN];
|
||||
char *cfg_name, *next_cfg;
|
||||
int ret = 0;
|
||||
u8 num = 1;
|
||||
|
||||
ret = scnprintf(buf, MAX_CFG_NAME_LEN, "%s", cfgs);
|
||||
if (ret >= MAX_CFG_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
cfg_name = buf;
|
||||
while (cfg_name) {
|
||||
next_cfg = strnchr(cfg_name, MAX_CFG_NAME_LEN, '|');
|
||||
if (next_cfg)
|
||||
*next_cfg++ = '\0';
|
||||
|
||||
ret = qti_usb_config_add(gadget, cfg_name, num);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
cfg_name = next_cfg;
|
||||
num++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qti_gadget_register(struct qti_usb_gadget *qg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (qg->enabled)
|
||||
return -EINVAL;
|
||||
|
||||
ret = qti_usb_configs_make(qg, qg->composition_funcs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
qg->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
|
||||
qg->cdev.desc.bDescriptorType = USB_DT_DEVICE;
|
||||
qg->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
|
||||
|
||||
qg->composite.gadget_driver = qti_gadget_driver;
|
||||
qg->composite.max_speed = qti_gadget_driver.max_speed;
|
||||
|
||||
qg->composite.gadget_driver.function = kstrdup("qti-gadget",
|
||||
GFP_KERNEL);
|
||||
qg->composite.name = qg->composite.gadget_driver.function;
|
||||
|
||||
if (!qg->composite.gadget_driver.function) {
|
||||
ret = -ENOMEM;
|
||||
goto free_configs;
|
||||
}
|
||||
|
||||
ret = usb_gadget_probe_driver(&qg->composite.gadget_driver);
|
||||
if (ret)
|
||||
goto free_name;
|
||||
|
||||
qg->enabled = true;
|
||||
|
||||
return 0;
|
||||
|
||||
free_name:
|
||||
kfree(qg->composite.gadget_driver.function);
|
||||
free_configs:
|
||||
qti_cleanup_configs_funcs(qg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qti_gadget_unregister(struct qti_usb_gadget *qg)
|
||||
{
|
||||
if (!qg->enabled)
|
||||
return;
|
||||
|
||||
usb_gadget_unregister_driver(&qg->composite.gadget_driver);
|
||||
kfree(qg->composite.gadget_driver.function);
|
||||
qti_cleanup_configs_funcs(qg);
|
||||
|
||||
qg->enabled = false;
|
||||
}
|
||||
|
||||
static int qti_gadget_get_properties(struct qti_usb_gadget *gadget)
|
||||
{
|
||||
struct device *dev = gadget->dev;
|
||||
struct device_node *child = NULL;
|
||||
int ret = 0, val = 0, pid = 0;
|
||||
char *start = NULL, *end = NULL;
|
||||
|
||||
ret = device_property_read_u32(dev, "qcom,vid", &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "USB gadget idVendor not specified\n");
|
||||
return ret;
|
||||
}
|
||||
gadget->cdev.desc.idVendor = (u16)val;
|
||||
|
||||
ret = device_property_read_u32(dev, "qcom,class", &val);
|
||||
if (!ret)
|
||||
gadget->cdev.desc.bDeviceClass = (u8)val;
|
||||
|
||||
ret = device_property_read_u32(dev, "qcom,subclass", &val);
|
||||
if (!ret)
|
||||
gadget->cdev.desc.bDeviceSubClass = (u8)val;
|
||||
|
||||
ret = device_property_read_u32(dev, "qcom,protocol", &val);
|
||||
if (!ret)
|
||||
gadget->cdev.desc.bDeviceProtocol = (u8)val;
|
||||
|
||||
/* Check if pid passed via cmdline which takes precedence */
|
||||
if (usb_pid_string[0] != 0) {
|
||||
ret = kstrtoint(usb_pid_string, 16, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = device_property_read_u32(dev, "qcom,default-pid", &val);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "USB gadget default-pid not specified\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
pid = val;
|
||||
|
||||
/* Extract serial # from kernel cmdline */
|
||||
start = strnstr(saved_command_line, "androidboot.serialno=",
|
||||
strlen(saved_command_line));
|
||||
if (start) {
|
||||
/* If serial # is at the end of the kernel cmdline
|
||||
* it will be terminated by NULL character
|
||||
*/
|
||||
end = strchrnul(start, ' ');
|
||||
start += strlen("androidboot.serialno=");
|
||||
strlcpy(serialno_string, start, (end - start)+1);
|
||||
}
|
||||
|
||||
/* Product string contains a space but in kernel
|
||||
* cmdline it will be passed with a special character
|
||||
* '!' instead of space to maintain the design of
|
||||
* cmdline parameters
|
||||
*/
|
||||
strreplace(product_string, '!', ' ');
|
||||
|
||||
/* Go through all the child nodes and find matching pid */
|
||||
while ((child = of_get_next_child(dev->of_node, child)) != NULL) {
|
||||
of_property_read_u32(child, "qcom,pid", &val);
|
||||
if (val == pid) {
|
||||
of_property_read_string(child, "qcom,composition",
|
||||
&gadget->composition_funcs);
|
||||
|
||||
gadget->cfg_node = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if couldn't find a matching composition */
|
||||
if (gadget->composition_funcs == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* bail out if ffs is specified and let userspace handle it */
|
||||
if (strnstr(gadget->composition_funcs, "ffs.",
|
||||
strlen(gadget->composition_funcs))) {
|
||||
dev_err(dev, "user should enable ffs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gadget->cdev.desc.idProduct = (u16)pid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t enabled_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qti_usb_gadget *qg = dev_get_drvdata(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%c\n",
|
||||
qg->enabled ? 'Y' : 'N');
|
||||
}
|
||||
|
||||
static ssize_t enabled_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct qti_usb_gadget *qg = dev_get_drvdata(dev);
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
qti_gadget_register(qg);
|
||||
else
|
||||
qti_gadget_unregister(qg);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(enabled);
|
||||
|
||||
static int qti_gadget_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct qti_usb_gadget *gadget;
|
||||
|
||||
gadget = devm_kzalloc(dev, sizeof(*gadget), GFP_KERNEL);
|
||||
if (!gadget)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, gadget);
|
||||
gadget->dev = dev;
|
||||
INIT_LIST_HEAD(&gadget->cdev.configs);
|
||||
INIT_LIST_HEAD(&gadget->cdev.gstrings);
|
||||
|
||||
ret = qti_gadget_get_properties(gadget);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qti_gadget_register(gadget);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
device_create_file(&pdev->dev, &dev_attr_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qti_gadget_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qti_usb_gadget *qg = platform_get_drvdata(pdev);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_enabled);
|
||||
qti_gadget_unregister(qg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qti_gadget_dt_match[] = {
|
||||
{
|
||||
.compatible = "qcom,usb-gadget",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qti_gadget_dt_match);
|
||||
|
||||
static struct platform_driver qti_gadget_platform_driver = {
|
||||
.driver = {
|
||||
.name = "qti_usb_gadget",
|
||||
.of_match_table = qti_gadget_dt_match,
|
||||
},
|
||||
.probe = qti_gadget_probe,
|
||||
.remove = qti_gadget_remove,
|
||||
};
|
||||
|
||||
static int __init gadget_qti_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&qti_gadget_platform_driver);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to register qti gadget platform driver\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(gadget_qti_init);
|
||||
|
||||
static void __exit gadget_qti_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qti_gadget_platform_driver);
|
||||
}
|
||||
module_exit(gadget_qti_exit);
|
||||
|
|
@ -578,6 +578,9 @@ struct usb_composite_overwrite {
|
|||
void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
|
||||
struct usb_composite_overwrite *covr);
|
||||
|
||||
int composite_dev_prepare(struct usb_composite_driver *composite,
|
||||
struct usb_composite_dev *dev);
|
||||
|
||||
static inline u16 get_default_bcdDevice(void)
|
||||
{
|
||||
u16 bcdDevice;
|
||||
|
|
|
|||
Loading…
Reference in a new issue