summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/function/f_ncm.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/usb/gadget/function/f_ncm.c57
1 files changed, 47 insertions, 10 deletions
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index d01fd2115..5558ea5ac 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -23,6 +23,7 @@
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_ncm.h"
+#include "configfs.h"
/*
* This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
@@ -35,9 +36,7 @@
/* to trigger crc/non-crc ndp signature */
-#define NCM_NDP_HDR_CRC_MASK 0x01000000
#define NCM_NDP_HDR_CRC 0x01000000
-#define NCM_NDP_HDR_NOCRC 0x00000000
enum ncm_notify_state {
NCM_NOTIFY_NONE, /* don't notify */
@@ -531,6 +530,7 @@ static inline void ncm_reset_values(struct f_ncm *ncm)
{
ncm->parser_opts = &ndp16_opts;
ncm->is_crc = false;
+ ncm->ndp_sign = ncm->parser_opts->ndp_sign;
ncm->port.cdc_filter = DEFAULT_FILTER;
/* doesn't make sense for ncm, fixed size used */
@@ -813,25 +813,20 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SET_CRC_MODE:
{
- int ndp_hdr_crc = 0;
-
if (w_length != 0 || w_index != ncm->ctrl_id)
goto invalid;
switch (w_value) {
case 0x0000:
ncm->is_crc = false;
- ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
DBG(cdev, "non-CRC mode selected\n");
break;
case 0x0001:
ncm->is_crc = true;
- ndp_hdr_crc = NCM_NDP_HDR_CRC;
DBG(cdev, "CRC mode selected\n");
break;
default:
goto invalid;
}
- ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
value = 0;
break;
}
@@ -848,6 +843,8 @@ invalid:
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
+ ncm->ndp_sign = ncm->parser_opts->ndp_sign |
+ (ncm->is_crc ? NCM_NDP_HDR_CRC : 0);
/* respond with data transfer or status phase? */
if (value >= 0) {
@@ -1434,6 +1431,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
return -EINVAL;
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()
* configurations are bound in sequence with list_for_each_entry,
@@ -1447,13 +1454,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
status = gether_register_netdev(ncm_opts->net);
mutex_unlock(&ncm_opts->lock);
if (status)
- return status;
+ goto fail;
ncm_opts->bound = true;
}
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
- if (IS_ERR(us))
- return PTR_ERR(us);
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto fail;
+ }
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
@@ -1470,6 +1479,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm_control_intf.bInterfaceNumber = 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);
if (status < 0)
goto fail;
@@ -1549,6 +1562,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
+ kfree(f->os_desc_table);
+ f->os_desc_n = 0;
+
if (ncm->notify_req) {
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
@@ -1603,16 +1619,22 @@ static void ncm_free_inst(struct usb_function_instance *f)
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
+ kfree(opts->ncm_interf_group);
kfree(opts);
}
static struct usb_function_instance *ncm_alloc_inst(void)
{
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);
if (!opts)
return ERR_PTR(-ENOMEM);
+ opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
+
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
opts->net = gether_setup_default();
@@ -1621,8 +1643,20 @@ static struct usb_function_instance *ncm_alloc_inst(void)
kfree(opts);
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);
+ 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;
}
@@ -1648,6 +1682,9 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
hrtimer_cancel(&ncm->task_timer);
+ kfree(f->os_desc_table);
+ f->os_desc_n = 0;
+
ncm_string_defs[0].id = 0;
usb_free_all_descriptors(f);