usb: gadget: aspeed: support multiple language strings
authorTao Ren <rentao.bupt@gmail.com>
Sun, 15 Mar 2020 19:16:27 +0000 (12:16 -0700)
committerFelipe Balbi <balbi@kernel.org>
Tue, 5 May 2020 07:58:51 +0000 (10:58 +0300)
This patch introduces a link list to store string descriptors with
different languages, and "ast_vhub_rep_string" function is also improved
to support multiple language usb strings.

Signed-off-by: Tao Ren <rentao.bupt@gmail.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
drivers/usb/gadget/udc/aspeed-vhub/core.c
drivers/usb/gadget/udc/aspeed-vhub/hub.c
drivers/usb/gadget/udc/aspeed-vhub/vhub.h

index 555e864..cdf9691 100644 (file)
@@ -408,7 +408,9 @@ static int ast_vhub_probe(struct platform_device *pdev)
                goto err;
 
        /* Init hub emulation */
-       ast_vhub_init_hub(vhub);
+       rc = ast_vhub_init_hub(vhub);
+       if (rc)
+               goto err;
 
        /* Initialize HW */
        ast_vhub_init_hw(vhub);
index 6e565c3..35edf37 100644 (file)
@@ -50,6 +50,7 @@
 #define KERNEL_VER     bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
 
 enum {
+       AST_VHUB_STR_INDEX_MAX = 4,
        AST_VHUB_STR_MANUF = 3,
        AST_VHUB_STR_PRODUCT = 2,
        AST_VHUB_STR_SERIAL = 1,
@@ -310,23 +311,77 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
        return ast_vhub_reply(ep, NULL, len);
 }
 
+static struct usb_gadget_strings*
+ast_vhub_str_of_container(struct usb_gadget_string_container *container)
+{
+       return (struct usb_gadget_strings *)container->stash;
+}
+
+static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
+                                     size_t size)
+{
+       int rc, hdr_len, nlangs, max_langs;
+       struct usb_gadget_strings *lang_str;
+       struct usb_gadget_string_container *container;
+       struct usb_string_descriptor *sdesc = buf;
+
+       nlangs = 0;
+       hdr_len = sizeof(struct usb_descriptor_header);
+       max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
+       list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+               if (nlangs >= max_langs)
+                       break;
+
+               lang_str = ast_vhub_str_of_container(container);
+               sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
+       }
+
+       rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
+       sdesc->bLength = rc;
+       sdesc->bDescriptorType = USB_DT_STRING;
+
+       return rc;
+}
+
+static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
+                                                        u16 lang_id)
+{
+       struct usb_gadget_strings *lang_str;
+       struct usb_gadget_string_container *container;
+
+       list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+               lang_str = ast_vhub_str_of_container(container);
+               if (lang_str->language == lang_id)
+                       return lang_str;
+       }
+
+       return NULL;
+}
+
 static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
                               u8 string_id, u16 lang_id,
                               u16 len)
 {
-       int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
-                                       string_id, ep->buf);
+       int rc;
+       u8 buf[256];
+       struct ast_vhub *vhub = ep->vhub;
+       struct usb_gadget_strings *lang_str;
 
-       /*
-        * This should never happen unless we put too big strings in
-        * the array above
-        */
-       BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
+       if (string_id == 0) {
+               rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
+       } else {
+               lang_str = ast_vhub_lookup_string(vhub, lang_id);
+               if (!lang_str)
+                       return std_req_stall;
+
+               rc = usb_gadget_get_string(lang_str, string_id, buf);
+       }
 
-       if (rc < 0)
+       if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
                return std_req_stall;
 
        /* Shoot it from the EP buffer */
+       memcpy(ep->buf, buf, rc);
        return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
 }
 
@@ -832,8 +887,64 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
        writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
 }
 
-static void ast_vhub_init_desc(struct ast_vhub *vhub)
+static struct usb_gadget_string_container*
+ast_vhub_str_container_alloc(struct ast_vhub *vhub)
+{
+       unsigned int size;
+       struct usb_string *str_array;
+       struct usb_gadget_strings *lang_str;
+       struct usb_gadget_string_container *container;
+
+       size = sizeof(*container);
+       size += sizeof(struct usb_gadget_strings);
+       size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
+       container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
+       if (!container)
+               return ERR_PTR(-ENOMEM);
+
+       lang_str = ast_vhub_str_of_container(container);
+       str_array = (struct usb_string *)(lang_str + 1);
+       lang_str->strings = str_array;
+       return container;
+}
+
+static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
+                                  const struct usb_gadget_strings *src)
+{
+       struct usb_string *src_array = src->strings;
+       struct usb_string *dest_array = dest->strings;
+
+       dest->language = src->language;
+       if (src_array && dest_array) {
+               do {
+                       *dest_array = *src_array;
+                       dest_array++;
+                       src_array++;
+               } while (src_array->s);
+       }
+}
+
+static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
+                                 const struct usb_gadget_strings *src_str)
 {
+       struct usb_gadget_strings *dest_str;
+       struct usb_gadget_string_container *container;
+
+       container = ast_vhub_str_container_alloc(vhub);
+       if (IS_ERR(container))
+               return PTR_ERR(container);
+
+       dest_str = ast_vhub_str_of_container(container);
+       ast_vhub_str_deep_copy(dest_str, src_str);
+       list_add_tail(&container->list, &vhub->vhub_str_desc);
+
+       return 0;
+}
+
+static int ast_vhub_init_desc(struct ast_vhub *vhub)
+{
+       int ret;
+
        /* Initialize vhub Device Descriptor. */
        memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
                sizeof(vhub->vhub_dev_desc));
@@ -848,15 +959,16 @@ static void ast_vhub_init_desc(struct ast_vhub *vhub)
        vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
 
        /* Initialize vhub String Descriptors. */
-       memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
-               sizeof(vhub->vhub_str_desc));
+       INIT_LIST_HEAD(&vhub->vhub_str_desc);
+       ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
+
+       return ret;
 }
 
-void ast_vhub_init_hub(struct ast_vhub *vhub)
+int ast_vhub_init_hub(struct ast_vhub *vhub)
 {
        vhub->speed = USB_SPEED_UNKNOWN;
        INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
 
-       ast_vhub_init_desc(vhub);
+       return ast_vhub_init_desc(vhub);
 }
-
index 23a1ac9..2e5a1ef 100644 (file)
@@ -421,7 +421,7 @@ struct ast_vhub {
        struct usb_device_descriptor    vhub_dev_desc;
        struct ast_vhub_full_cdesc      vhub_conf_desc;
        struct usb_hub_descriptor       vhub_hub_desc;
-       struct usb_gadget_strings       vhub_str_desc;
+       struct list_head                vhub_str_desc;
 };
 
 /* Standard request handlers result codes */
@@ -531,7 +531,7 @@ int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
                               __VA_ARGS__)
 
 /* hub.c */
-void ast_vhub_init_hub(struct ast_vhub *vhub);
+int ast_vhub_init_hub(struct ast_vhub *vhub);
 enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
                                         struct usb_ctrlrequest *crq);
 enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,