Commit 79340929 authored by Romain Izard's avatar Romain Izard Committed by Felipe Balbi

usb: gadget: f_ncm: Add OS descriptor support

To be able to use the default USB class drivers available in Microsoft
Windows, we need to add OS descriptors to the exported USB gadget to
tell the OS that we are compatible with the built-in drivers.

Copy the OS descriptor support from f_rndis into f_ncm. As a result,
using the WINNCM compatible ID, the UsbNcm driver is loaded on
enumeration without the need for a custom driver or inf file.
Signed-off-by: default avatarRomain Izard <romain.izard.pro@gmail.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 550eef0c
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "u_ether.h" #include "u_ether.h"
#include "u_ether_configfs.h" #include "u_ether_configfs.h"
#include "u_ncm.h" #include "u_ncm.h"
#include "configfs.h"
/* /*
* This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
...@@ -1391,6 +1392,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -1391,6 +1392,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
return -EINVAL; return -EINVAL;
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
if (cdev->use_os_string) {
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
GFP_KERNEL);
if (!f->os_desc_table)
return -ENOMEM;
f->os_desc_n = 1;
f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
}
/* /*
* in drivers/usb/gadget/configfs.c:configfs_composite_bind() * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry, * configurations are bound in sequence with list_for_each_entry,
...@@ -1404,13 +1415,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -1404,13 +1415,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
status = gether_register_netdev(ncm_opts->net); status = gether_register_netdev(ncm_opts->net);
mutex_unlock(&ncm_opts->lock); mutex_unlock(&ncm_opts->lock);
if (status) if (status)
return status; goto fail;
ncm_opts->bound = true; ncm_opts->bound = true;
} }
us = usb_gstrings_attach(cdev, ncm_strings, us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs)); ARRAY_SIZE(ncm_string_defs));
if (IS_ERR(us)) if (IS_ERR(us)) {
return PTR_ERR(us); status = PTR_ERR(us);
goto fail;
}
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
...@@ -1427,6 +1440,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -1427,6 +1440,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm_control_intf.bInterfaceNumber = status; ncm_control_intf.bInterfaceNumber = status;
ncm_union_desc.bMasterInterface0 = status; ncm_union_desc.bMasterInterface0 = status;
if (cdev->use_os_string)
f->os_desc_table[0].if_id =
ncm_iad_desc.bFirstInterface;
status = usb_interface_id(c, f); status = usb_interface_id(c, f);
if (status < 0) if (status < 0)
goto fail; goto fail;
...@@ -1506,6 +1523,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -1506,6 +1523,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
return 0; return 0;
fail: fail:
kfree(f->os_desc_table);
f->os_desc_n = 0;
if (ncm->notify_req) { if (ncm->notify_req) {
kfree(ncm->notify_req->buf); kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req); usb_ep_free_request(ncm->notify, ncm->notify_req);
...@@ -1560,16 +1580,22 @@ static void ncm_free_inst(struct usb_function_instance *f) ...@@ -1560,16 +1580,22 @@ static void ncm_free_inst(struct usb_function_instance *f)
gether_cleanup(netdev_priv(opts->net)); gether_cleanup(netdev_priv(opts->net));
else else
free_netdev(opts->net); free_netdev(opts->net);
kfree(opts->ncm_interf_group);
kfree(opts); kfree(opts);
} }
static struct usb_function_instance *ncm_alloc_inst(void) static struct usb_function_instance *ncm_alloc_inst(void)
{ {
struct f_ncm_opts *opts; struct f_ncm_opts *opts;
struct usb_os_desc *descs[1];
char *names[1];
struct config_group *ncm_interf_group;
opts = kzalloc(sizeof(*opts), GFP_KERNEL); opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts) if (!opts)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
mutex_init(&opts->lock); mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst; opts->func_inst.free_func_inst = ncm_free_inst;
opts->net = gether_setup_default(); opts->net = gether_setup_default();
...@@ -1578,8 +1604,20 @@ static struct usb_function_instance *ncm_alloc_inst(void) ...@@ -1578,8 +1604,20 @@ static struct usb_function_instance *ncm_alloc_inst(void)
kfree(opts); kfree(opts);
return ERR_CAST(net); return ERR_CAST(net);
} }
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
descs[0] = &opts->ncm_os_desc;
names[0] = "ncm";
config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE);
if (IS_ERR(ncm_interf_group)) {
ncm_free_inst(&opts->func_inst);
return ERR_CAST(ncm_interf_group);
}
opts->ncm_interf_group = ncm_interf_group;
return &opts->func_inst; return &opts->func_inst;
} }
...@@ -1605,6 +1643,9 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) ...@@ -1605,6 +1643,9 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
hrtimer_cancel(&ncm->task_timer); hrtimer_cancel(&ncm->task_timer);
kfree(f->os_desc_table);
f->os_desc_n = 0;
ncm_string_defs[0].id = 0; ncm_string_defs[0].id = 0;
usb_free_all_descriptors(f); usb_free_all_descriptors(f);
......
...@@ -20,6 +20,9 @@ struct f_ncm_opts { ...@@ -20,6 +20,9 @@ struct f_ncm_opts {
struct net_device *net; struct net_device *net;
bool bound; bool bound;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];
/* /*
* Read/write access to configfs attributes is handled by configfs. * Read/write access to configfs attributes is handled by configfs.
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment