aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function/f_acm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/function/f_acm.c')
-rw-r--r--drivers/usb/gadget/function/f_acm.c52
1 files changed, 50 insertions, 2 deletions
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 724b2631f249..7061720b9732 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -41,6 +41,7 @@ struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
u8 port_num;
+ u8 bInterfaceProtocol;
u8 pending;
@@ -89,7 +90,7 @@ acm_iad_descriptor = {
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
- .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .bFunctionProtocol = DYNAMIC */
/* .iFunction = DYNAMIC */
};
@@ -101,7 +102,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = {
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .bInterfaceProtocol = DYNAMIC */
/* .iInterface = DYNAMIC */
};
@@ -663,6 +664,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
acm->notify = ep;
+ acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol;
+ acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol;
+
/* allocate notification */
acm->notify_req = gs_alloc_req(ep,
sizeof(struct usb_cdc_notification) + 2,
@@ -719,8 +723,14 @@ static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
static void acm_free_func(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
+ struct f_serial_opts *opts;
+
+ opts = container_of(f->fi, struct f_serial_opts, func_inst);
kfree(acm);
+ mutex_lock(&opts->lock);
+ opts->instances--;
+ mutex_unlock(&opts->lock);
}
static void acm_resume(struct usb_function *f)
@@ -761,7 +771,11 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
acm->port.func.disable = acm_disable;
opts = container_of(fi, struct f_serial_opts, func_inst);
+ mutex_lock(&opts->lock);
acm->port_num = opts->port_num;
+ acm->bInterfaceProtocol = opts->protocol;
+ opts->instances++;
+ mutex_unlock(&opts->lock);
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
acm->port.func.resume = acm_resume;
@@ -812,11 +826,42 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
CONFIGFS_ATTR_RO(f_acm_, port_num);
+static ssize_t f_acm_protocol_show(struct config_item *item, char *page)
+{
+ return sprintf(page, "%u\n", to_f_serial_opts(item)->protocol);
+}
+
+static ssize_t f_acm_protocol_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct f_serial_opts *opts = to_f_serial_opts(item);
+ int ret;
+
+ mutex_lock(&opts->lock);
+
+ if (opts->instances) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = kstrtou8(page, 0, &opts->protocol);
+ if (ret)
+ goto out;
+ ret = count;
+
+out:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_acm_, protocol);
+
static struct configfs_attribute *acm_attrs[] = {
#ifdef CONFIG_U_SERIAL_CONSOLE
&f_acm_attr_console,
#endif
&f_acm_attr_port_num,
+ &f_acm_attr_protocol,
NULL,
};
@@ -832,6 +877,7 @@ static void acm_free_instance(struct usb_function_instance *fi)
opts = container_of(fi, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
+ mutex_destroy(&opts->lock);
kfree(opts);
}
@@ -843,7 +889,9 @@ static struct usb_function_instance *acm_alloc_instance(void)
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
+ opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER;
opts->func_inst.free_func_inst = acm_free_instance;
+ mutex_init(&opts->lock);
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);