@@ -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));
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@ideasonboard.com> --- Changes in v2: - New patch drivers/usb/gadget/function/f_uvc.c | 71 ++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-)