mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 12:14:10 +08:00
usb: gadget: f_acm: make bInterfaceProtocol configurable
The bInterfaceProtocol is hardcoded to USB_CDC_ACM_PROTO_AT_V25TER. This
will lead to problems with ModemManger which will gladly try to probe
that port as a modem if the gadget also has a network function.
ModemManager will try to send AT commands to the ACM port. Make the
bInterfaceProtocol configurable. For this, track the number of instances
and only allow write to the property if there are no intances (yet).
This will also set bFunctionProtocol to the same value, see commit
5c8db070b4
("USB: Change acm_iad_descriptor bFunctionProtocol to
USB_CDC_ACM_PROTO_AT_V25TER") for more details.
Signed-off-by: Michael Walle <mwalle@kernel.org>
Link: https://lore.kernel.org/r/20240825180446.3757073-1-mwalle@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
c146ede472
commit
a17f04d4f2
@ -6,3 +6,10 @@ Description:
|
||||
This item contains just one readonly attribute: port_num.
|
||||
It contains the port number of the /dev/ttyGS<n> device
|
||||
associated with acm function's instance "name".
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/acm.name/protocol
|
||||
Date: Aug 2024
|
||||
KernelVersion: 6.13
|
||||
Description:
|
||||
Reported bInterfaceProtocol for the ACM device. For legacy
|
||||
reasons, this defaults to 1 (USB_CDC_ACM_PROTO_AT_V25TER).
|
||||
|
@ -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);
|
||||
|
@ -17,6 +17,10 @@
|
||||
struct f_serial_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
u8 port_num;
|
||||
u8 protocol;
|
||||
|
||||
struct mutex lock; /* protect instances */
|
||||
int instances;
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user