OSDN Git Service

Merge tag 'v4.4.214' into 10
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / usb / gadget / function / f_ncm.c
index 1690873..a32a6a7 100644 (file)
@@ -334,6 +334,77 @@ static struct usb_descriptor_header *ncm_hs_function[] = {
        NULL,
 };
 
+/* Super Speed Support */
+static struct usb_endpoint_descriptor ncm_ss_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(NCM_STATUS_BYTECOUNT),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
+};
+
+static struct usb_ss_ep_comp_descriptor ncm_ss_notify_comp_desc = {
+       .bLength =              sizeof(ncm_ss_notify_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+       /* the following 3 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+       .wBytesPerInterval =    cpu_to_le16(NCM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ncm_ss_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ncm_ss_in_comp_desc = {
+       .bLength =              sizeof(ncm_ss_in_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_endpoint_descriptor ncm_ss_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ncm_ss_out_comp_desc = {
+       .bLength =              sizeof(ncm_ss_out_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *ncm_ss_function[] = {
+       (struct usb_descriptor_header *) &ncm_iad_desc,
+       /* CDC NCM control descriptors */
+       (struct usb_descriptor_header *) &ncm_control_intf,
+       (struct usb_descriptor_header *) &ncm_header_desc,
+       (struct usb_descriptor_header *) &ncm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+       (struct usb_descriptor_header *) &ncm_desc,
+       (struct usb_descriptor_header *) &ncm_ss_notify_desc,
+       (struct usb_descriptor_header *) &ncm_ss_notify_comp_desc,
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ncm_data_nop_intf,
+       (struct usb_descriptor_header *) &ncm_data_intf,
+       (struct usb_descriptor_header *) &ncm_ss_in_desc,
+       (struct usb_descriptor_header *) &ncm_ss_in_comp_desc,
+       (struct usb_descriptor_header *) &ncm_ss_out_desc,
+       (struct usb_descriptor_header *) &ncm_ss_out_comp_desc,
+       NULL,
+};
+
 /* string descriptors: */
 
 #define STRING_CTRL_IDX        0
@@ -1359,17 +1430,39 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
         */
        if (!ncm_opts->bound) {
                mutex_lock(&ncm_opts->lock);
+               ncm_opts->net = gether_setup_default();
+               if (IS_ERR(ncm_opts->net)) {
+                       status = PTR_ERR(ncm_opts->net);
+                       mutex_unlock(&ncm_opts->lock);
+                       goto error;
+               }
                gether_set_gadget(ncm_opts->net, cdev->gadget);
                status = gether_register_netdev(ncm_opts->net);
                mutex_unlock(&ncm_opts->lock);
-               if (status)
-                       return status;
+               if (status) {
+                       free_netdev(ncm_opts->net);
+                       goto error;
+               }
                ncm_opts->bound = true;
        }
+
+       /* export host's Ethernet address in CDC format */
+       status = gether_get_host_addr_cdc(ncm_opts->net, ncm->ethaddr,
+                                     sizeof(ncm->ethaddr));
+       if (status < 12) { /* strlen("01234567890a") */
+               ERROR(cdev, "%s: failed to get host eth addr, err %d\n",
+               __func__, status);
+               status = -EINVAL;
+               goto netdev_cleanup;
+       }
+       ncm->port.ioport = netdev_priv(ncm_opts->net);
+
        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 netdev_cleanup;
+       }
        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;
@@ -1435,8 +1528,17 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
        hs_ncm_notify_desc.bEndpointAddress =
                fs_ncm_notify_desc.bEndpointAddress;
 
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               ncm_ss_in_desc.bEndpointAddress =
+                                       fs_ncm_in_desc.bEndpointAddress;
+               ncm_ss_out_desc.bEndpointAddress =
+                                       fs_ncm_out_desc.bEndpointAddress;
+               ncm_ss_notify_desc.bEndpointAddress =
+                                       fs_ncm_notify_desc.bEndpointAddress;
+       }
+
        status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
-                       NULL);
+                       ncm_ss_function);
        if (status)
                goto fail;
 
@@ -1464,7 +1566,10 @@ fail:
                kfree(ncm->notify_req->buf);
                usb_ep_free_request(ncm->notify, ncm->notify_req);
        }
+netdev_cleanup:
+       gether_cleanup(netdev_priv(ncm_opts->net));
 
+error:
        ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
 
        return status;
@@ -1512,8 +1617,6 @@ static void ncm_free_inst(struct usb_function_instance *f)
        opts = container_of(f, struct f_ncm_opts, func_inst);
        if (opts->bound)
                gether_cleanup(netdev_priv(opts->net));
-       else
-               free_netdev(opts->net);
        kfree(opts);
 }
 
@@ -1526,12 +1629,6 @@ static struct usb_function_instance *ncm_alloc_inst(void)
                return ERR_PTR(-ENOMEM);
        mutex_init(&opts->lock);
        opts->func_inst.free_func_inst = ncm_free_inst;
-       opts->net = gether_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
 
        config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
 
@@ -1554,9 +1651,13 @@ static void ncm_free(struct usb_function *f)
 static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
 {
        struct f_ncm *ncm = func_to_ncm(f);
+       struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts,
+                                       func_inst);
 
        DBG(c->cdev, "ncm unbind\n");
 
+       opts->bound = false;
+
        hrtimer_cancel(&ncm->task_timer);
        tasklet_kill(&ncm->tx_tasklet);
 
@@ -1570,13 +1671,14 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
 
        kfree(ncm->notify_req->buf);
        usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+       gether_cleanup(netdev_priv(opts->net));
 }
 
 static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
 {
        struct f_ncm            *ncm;
        struct f_ncm_opts       *opts;
-       int status;
 
        /* allocate and initialize one new instance */
        ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1586,20 +1688,9 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
        opts = container_of(fi, struct f_ncm_opts, func_inst);
        mutex_lock(&opts->lock);
        opts->refcnt++;
-
-       /* export host's Ethernet address in CDC format */
-       status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
-                                     sizeof(ncm->ethaddr));
-       if (status < 12) { /* strlen("01234567890a") */
-               kfree(ncm);
-               mutex_unlock(&opts->lock);
-               return ERR_PTR(-EINVAL);
-       }
        ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
-
        spin_lock_init(&ncm->lock);
        ncm_reset_values(ncm);
-       ncm->port.ioport = netdev_priv(opts->net);
        mutex_unlock(&opts->lock);
        ncm->port.is_fixed = true;
        ncm->port.supports_multi_frame = true;