diff mbox series

[v2,7/9] usb: gadget: uvc: Attach custom string descriptors

Message ID 20221121092517.225242-8-dan.scally@ideasonboard.com (mailing list archive)
State New, archived
Headers show
Series Add XU support to UVC Gadget | expand

Commit Message

Dan Scally Nov. 21, 2022, 9:25 a.m. UTC
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(-)
diff mbox series

Patch

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));