Now that we have support for arbitrary string descriptors we need to attach them during uvc_function_bind(). Set the iExtension field of any Extension Units after the strings have attached. Signed-off-by: Daniel Scally <dan.scally@xxxxxxxxxxxxxxxx> --- Changes in v2: - New patch drivers/usb/gadget/function/f_uvc.c | 71 ++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 1b8871a24be8..7821fe8f9120 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -622,12 +622,72 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) return hdr; } +static struct usb_string * +uvc_function_attach_usb_strings(struct usb_composite_dev *cdev, struct usb_function *f) +{ + struct f_uvc_opts *opts = fi_to_f_uvc_opts(f->fi); + struct usb_gadget_strings **gadget_strings; + struct uvcg_language *language; + struct uvcg_string *string; + struct usb_string *us; + unsigned int i = 0; + int nstrings = -1; + unsigned int j; + + gadget_strings = kcalloc(opts->nlangs + 1, /* including NULL terminator */ + sizeof(struct usb_gadget_strings *), GFP_KERNEL); + if (!gadget_strings) + return ERR_PTR(-ENOMEM); + + list_for_each_entry(language, &opts->languages, list) { + if (nstrings == -1) { + nstrings = language->nstrings; + } else if (nstrings != language->nstrings) { + uvcg_err(f, "languages must contain the same number of strings\n"); + us = ERR_PTR(-EINVAL); + goto cleanup; + } + + language->stringtab.strings = kcalloc(language->nstrings + 1, + sizeof(struct usb_string), + GFP_KERNEL); + if (!language->stringtab.strings) { + us = ERR_PTR(-ENOMEM); + goto cleanup; + } + + j = 0; + list_for_each_entry(string, &language->strings, list) { + memcpy(&language->stringtab.strings[j], &string->usb_string, + sizeof(struct usb_string)); + j++; + } + + gadget_strings[i] = &language->stringtab; + i++; + } + + us = usb_gstrings_attach(cdev, gadget_strings, nstrings); + +cleanup: + + list_for_each_entry(language, &opts->languages, list) { + kfree(language->stringtab.strings); + language->stringtab.strings = NULL; + } + + kfree(gadget_strings); + + return us; +} + static int uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); - struct usb_string *us; + struct uvcg_extension *xu; + struct usb_string *us, *cus; unsigned int max_packet_mult; unsigned int max_packet_size; struct usb_ep *ep; @@ -715,6 +775,15 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + cus = uvc_function_attach_usb_strings(cdev, f); + if (IS_ERR(cus)) { + ret = PTR_ERR(cus); + goto error; + } + + list_for_each_entry(xu, &opts->extension_units, list) + xu->desc.iExtension = cus[xu->string_descriptor_index].id; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].s = opts->function_name; us = usb_gstrings_attach(cdev, uvc_function_strings, ARRAY_SIZE(uvc_en_us_strings)); -- 2.34.1