[PATCH v4 24/43] usb: gadget: f_rndis: conversion to new API

From: Robert Baldyga
Date: Wed Feb 03 2016 - 07:41:32 EST


Generate descriptors in new format and attach them to USB function in
prep_descs(). Implement prep_vendor_descs() to supply class specific
descriptors. Change set_alt() implementation and implement clear_alt()
operation. Remove boilerplate code. Change USB request lifetime
management - now it's allocated in set_alt() and freed in clear_alt().

Signed-off-by: Robert Baldyga <r.baldyga@xxxxxxxxxxx>
---
drivers/usb/gadget/function/f_rndis.c | 321 ++++++++++++----------------------
1 file changed, 112 insertions(+), 209 deletions(-)

diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index e587767..abe11a7 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -212,24 +212,6 @@ static struct usb_endpoint_descriptor fs_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};

-static struct usb_descriptor_header *eth_fs_function[] = {
- (struct usb_descriptor_header *) &rndis_iad_descriptor,
-
- /* control interface matches ACM, not Ethernet */
- (struct usb_descriptor_header *) &rndis_control_intf,
- (struct usb_descriptor_header *) &header_desc,
- (struct usb_descriptor_header *) &call_mgmt_descriptor,
- (struct usb_descriptor_header *) &rndis_acm_descriptor,
- (struct usb_descriptor_header *) &rndis_union_desc,
- (struct usb_descriptor_header *) &fs_notify_desc,
-
- /* data interface has no altsetting */
- (struct usb_descriptor_header *) &rndis_data_intf,
- (struct usb_descriptor_header *) &fs_in_desc,
- (struct usb_descriptor_header *) &fs_out_desc,
- NULL,
-};
-
/* high speed support: */

static struct usb_endpoint_descriptor hs_notify_desc = {
@@ -260,24 +242,6 @@ static struct usb_endpoint_descriptor hs_out_desc = {
.wMaxPacketSize = cpu_to_le16(512),
};

-static struct usb_descriptor_header *eth_hs_function[] = {
- (struct usb_descriptor_header *) &rndis_iad_descriptor,
-
- /* control interface matches ACM, not Ethernet */
- (struct usb_descriptor_header *) &rndis_control_intf,
- (struct usb_descriptor_header *) &header_desc,
- (struct usb_descriptor_header *) &call_mgmt_descriptor,
- (struct usb_descriptor_header *) &rndis_acm_descriptor,
- (struct usb_descriptor_header *) &rndis_union_desc,
- (struct usb_descriptor_header *) &hs_notify_desc,
-
- /* data interface has no altsetting */
- (struct usb_descriptor_header *) &rndis_data_intf,
- (struct usb_descriptor_header *) &hs_in_desc,
- (struct usb_descriptor_header *) &hs_out_desc,
- NULL,
-};
-
/* super speed support: */

static struct usb_endpoint_descriptor ss_notify_desc = {
@@ -327,26 +291,20 @@ static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
/* .bmAttributes = 0, */
};

-static struct usb_descriptor_header *eth_ss_function[] = {
- (struct usb_descriptor_header *) &rndis_iad_descriptor,
-
- /* control interface matches ACM, not Ethernet */
- (struct usb_descriptor_header *) &rndis_control_intf,
- (struct usb_descriptor_header *) &header_desc,
- (struct usb_descriptor_header *) &call_mgmt_descriptor,
- (struct usb_descriptor_header *) &rndis_acm_descriptor,
- (struct usb_descriptor_header *) &rndis_union_desc,
- (struct usb_descriptor_header *) &ss_notify_desc,
- (struct usb_descriptor_header *) &ss_intr_comp_desc,
-
- /* data interface has no altsetting */
- (struct usb_descriptor_header *) &rndis_data_intf,
- (struct usb_descriptor_header *) &ss_in_desc,
- (struct usb_descriptor_header *) &ss_bulk_comp_desc,
- (struct usb_descriptor_header *) &ss_out_desc,
- (struct usb_descriptor_header *) &ss_bulk_comp_desc,
- NULL,
-};
+USB_COMPOSITE_ENDPOINT(ep_notify, &fs_notify_desc, &hs_notify_desc,
+ &ss_notify_desc, &ss_intr_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_in, &fs_in_desc, &hs_in_desc,
+ &ss_in_desc, &ss_bulk_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_out, &fs_out_desc, &hs_out_desc,
+ &ss_out_desc, &ss_bulk_comp_desc);
+
+USB_COMPOSITE_ALTSETTING(intf0alt0, &rndis_control_intf, &ep_notify);
+USB_COMPOSITE_ALTSETTING(intf1alt0, &rndis_data_intf, &ep_in, &ep_out);
+
+USB_COMPOSITE_INTERFACE(intf0, &intf0alt0);
+USB_COMPOSITE_INTERFACE(intf1, &intf1alt0);
+
+USB_COMPOSITE_DESCRIPTORS(rndis_descs, &intf0, &intf1);

/* string descriptors: */

@@ -542,36 +500,35 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)

/* we know alt == 0 */

- if (intf == rndis->ctrl_id) {
+ if (intf == 0) {
VDBG(cdev, "reset rndis control %d\n", intf);
- usb_ep_disable(rndis->notify);

- if (!rndis->notify->desc) {
- VDBG(cdev, "init rndis ctrl %d\n", intf);
- if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
- goto fail;
- }
- usb_ep_enable(rndis->notify);
-
- } else if (intf == rndis->data_id) {
- struct net_device *net;
+ rndis->notify = usb_function_get_ep(f, intf, 0);
+ if (!rndis->notify)
+ return -ENODEV;

- if (rndis->port.in_ep->enabled) {
- DBG(cdev, "reset rndis\n");
- gether_disconnect(&rndis->port);
+ /* allocate notification request and buffer */
+ rndis->notify_req = usb_ep_alloc_request(rndis->notify,
+ GFP_KERNEL);
+ if (!rndis->notify_req)
+ return -ENOMEM;
+ rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!rndis->notify_req->buf) {
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ return -ENOMEM;
}
+ rndis->notify_req->length = STATUS_BYTECOUNT;
+ rndis->notify_req->context = rndis;
+ rndis->notify_req->complete = rndis_response_complete;
+ } else if (intf == 1) {
+ struct net_device *net;

- if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
- DBG(cdev, "init rndis\n");
- if (config_ep_by_speed(cdev->gadget, f,
- rndis->port.in_ep) ||
- config_ep_by_speed(cdev->gadget, f,
- rndis->port.out_ep)) {
- rndis->port.in_ep->desc = NULL;
- rndis->port.out_ep->desc = NULL;
- goto fail;
- }
- }
+ rndis->port.in_ep = usb_function_get_ep(f, intf, 0);
+ if (!rndis->port.in_ep)
+ return -ENODEV;
+ rndis->port.out_ep = usb_function_get_ep(f, intf, 1);
+ if (!rndis->port.out_ep)
+ return -ENODEV;

/* Avoid ZLPs; they can be troublesome. */
rndis->port.is_zlp_ok = false;
@@ -597,28 +554,25 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)

rndis_set_param_dev(rndis->params, net,
&rndis->port.cdc_filter);
- } else
- goto fail;
+ }

return 0;
-fail:
- return -EINVAL;
}

-static void rndis_disable(struct usb_function *f)
+static void rndis_clear_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;

- if (!rndis->notify->enabled)
- return;
-
- DBG(cdev, "rndis deactivated\n");
-
- rndis_uninit(rndis->params);
- gether_disconnect(&rndis->port);
-
- usb_ep_disable(rndis->notify);
+ if (intf == 0) {
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ } else if (intf == 1) {
+ DBG(cdev, "rndis deactivated\n");
+ rndis_uninit(rndis->params);
+ gether_disconnect(&rndis->port);
+ }
}

/*-------------------------------------------------------------------------*/
@@ -663,22 +617,19 @@ static inline bool can_support_rndis(struct usb_configuration *c)

/* ethernet function driver setup/binding */

-static int
-rndis_bind(struct usb_configuration *c, struct usb_function *f)
+static int rndis_prep_descs(struct usb_function *f)
{
- struct usb_composite_dev *cdev = c->cdev;
struct f_rndis *rndis = func_to_rndis(f);
- struct usb_string *us;
+ struct f_rndis_opts *rndis_opts;
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_string *us;
int status;
- struct usb_ep *ep;

- struct f_rndis_opts *rndis_opts;
+ rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);

- if (!can_support_rndis(c))
+ if (!can_support_rndis(f->config))
return -EINVAL;

- rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
-
if (cdev->use_os_string) {
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
GFP_KERNEL);
@@ -688,21 +639,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
}

- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to rndis_opts->bound access
- */
- if (!rndis_opts->bound) {
- gether_set_gadget(rndis_opts->net, cdev->gadget);
- status = gether_register_netdev(rndis_opts->net);
- if (status)
- goto fail;
- rndis_opts->bound = true;
- }
-
us = usb_gstrings_attach(cdev, rndis_strings,
ARRAY_SIZE(rndis_string_defs));
if (IS_ERR(us)) {
@@ -713,79 +649,72 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_data_intf.iInterface = us[1].id;
rndis_iad_descriptor.iFunction = us[2].id;

- /* allocate instance-specific interface IDs */
- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
- rndis->ctrl_id = status;
- rndis_iad_descriptor.bFirstInterface = status;
-
- rndis_control_intf.bInterfaceNumber = status;
- rndis_union_desc.bMasterInterface0 = status;
+ return usb_function_set_descs(f, &rndis_descs);

- if (cdev->use_os_string)
- f->os_desc_table[0].if_id =
- rndis_iad_descriptor.bFirstInterface;
+fail:
+ kfree(f->os_desc_table);
+ f->os_desc_n = 0;

- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
- rndis->data_id = status;
+ if (rndis->notify_req) {
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ }

- rndis_data_intf.bInterfaceNumber = status;
- rndis_union_desc.bSlaveInterface0 = status;
+ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);

- status = -ENODEV;
+ return status;
+}

- /* allocate instance-specific endpoints */
- ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
- if (!ep)
- goto fail;
- rndis->port.in_ep = ep;
+static int rndis_prep_vendor_descs(struct usb_function *f)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct f_rndis_opts *rndis_opts;
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int status, intf0_id, intf1_id;

- ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
- if (!ep)
- goto fail;
- rndis->port.out_ep = ep;
+ rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);

- /* NOTE: a status/notification endpoint is, strictly speaking,
- * optional. We don't treat it that way though! It's simpler,
- * and some newer profiles don't treat it as optional.
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to rndis_opts->bound access
*/
- ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
- if (!ep)
- goto fail;
- rndis->notify = ep;
+ if (!rndis_opts->bound) {
+ gether_set_gadget(rndis_opts->net, cdev->gadget);
+ status = gether_register_netdev(rndis_opts->net);
+ if (status)
+ return status;
+ rndis_opts->bound = true;
+ }

- status = -ENOMEM;
+ intf0_id = usb_get_interface_id(f, 0);
+ intf1_id = usb_get_interface_id(f, 1);

- /* allocate notification request and buffer */
- rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!rndis->notify_req)
- goto fail;
- rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
- if (!rndis->notify_req->buf)
- goto fail;
- rndis->notify_req->length = STATUS_BYTECOUNT;
- rndis->notify_req->context = rndis;
- rndis->notify_req->complete = rndis_response_complete;
+ rndis->ctrl_id = intf0_id;
+ rndis->data_id = intf1_id;

- /* support all relevant hardware speeds... we expect that when
- * hardware is dual speed, all bulk-capable endpoints work at
- * both speeds
- */
- hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
- hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
- hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
+ rndis_iad_descriptor.bFirstInterface = intf0_id;

- ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
- ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
- ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
+ rndis_union_desc.bMasterInterface0 = intf0_id;
+ rndis_union_desc.bSlaveInterface0 = intf1_id;

- status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
- eth_ss_function);
- if (status)
- goto fail;
+ if (cdev->use_os_string)
+ f->os_desc_table[0].if_id =
+ rndis_iad_descriptor.bFirstInterface;
+
+ usb_function_add_vendor_desc(f,
+ (struct usb_descriptor_header *)&rndis_iad_descriptor);
+
+ usb_altset_add_vendor_desc(f, 0, 0,
+ (struct usb_descriptor_header *)&header_desc);
+ usb_altset_add_vendor_desc(f, 0, 0,
+ (struct usb_descriptor_header *)&call_mgmt_descriptor);
+ usb_altset_add_vendor_desc(f, 0, 0,
+ (struct usb_descriptor_header *)&rndis_acm_descriptor);
+ usb_altset_add_vendor_desc(f, 0, 0,
+ (struct usb_descriptor_header *)&rndis_union_desc);

rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
@@ -796,8 +725,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->params, rndis->vendorID,
rndis->manufacturer)) {
- status = -EINVAL;
- goto fail_free_descs;
+ return -EINVAL;
}

/* NOTE: all that is done without knowing or caring about
@@ -805,27 +733,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
* until we're activated via set_alt().
*/

- DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- rndis->port.in_ep->name, rndis->port.out_ep->name,
- rndis->notify->name);
return 0;
-
-fail_free_descs:
- usb_free_all_descriptors(f);
-fail:
- kfree(f->os_desc_table);
- f->os_desc_n = 0;
-
- if (rndis->notify_req) {
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}

void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
@@ -940,14 +848,8 @@ static void rndis_free(struct usb_function *f)

static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
{
- struct f_rndis *rndis = func_to_rndis(f);
-
kfree(f->os_desc_table);
f->os_desc_n = 0;
- usb_free_all_descriptors(f);
-
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
}

static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
@@ -981,11 +883,12 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)

rndis->port.func.name = "rndis";
/* descriptors are per-instance copies */
- rndis->port.func.bind = rndis_bind;
+ rndis->port.func.prep_descs = rndis_prep_descs;
+ rndis->port.func.prep_vendor_descs = rndis_prep_vendor_descs;
rndis->port.func.unbind = rndis_unbind;
rndis->port.func.set_alt = rndis_set_alt;
+ rndis->port.func.clear_alt = rndis_clear_alt;
rndis->port.func.setup = rndis_setup;
- rndis->port.func.disable = rndis_disable;
rndis->port.func.free_func = rndis_free;

params = rndis_register(rndis_response_available, rndis);
--
1.9.1