From patchwork Mon Nov 21 09:25:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050525 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5638AC4332F for ; Mon, 21 Nov 2022 09:25:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230115AbiKUJZl (ORCPT ); Mon, 21 Nov 2022 04:25:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40514 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229998AbiKUJZj (ORCPT ); Mon, 21 Nov 2022 04:25:39 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B9E8127DE4 for ; Mon, 21 Nov 2022 01:25:37 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 80C07987; Mon, 21 Nov 2022 10:25:35 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022736; bh=rYX2BsTP1C9FVK95o7ogWkuh4e8MasArZJA9fce+88U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KMkMoBjLJnhnv/oI45VYi3EEI216MFqTRj7r9z28IVnjpcLd3NS9Ua23kFBI+OYll lV/CrxPxTGf2ICYrelClstYenfCXZXW+wRbsKwQGgCW1ZUdQIdOskF5zxq1Yh0VEBO YzhdDOi/v8i3Tjt2+nlIQ8/962dUWjmdKpy/oZ8M= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 1/9] usb: gadget: uvc: Make bSourceID read/write Date: Mon, 21 Nov 2022 09:25:09 +0000 Message-Id: <20221121092517.225242-2-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org At the moment, the UVC function graph is hardcoded IT -> PU -> OT. To add XU support we need the ability to insert the XU descriptors into the chain. To facilitate that, make the output terminal's bSourceID attribute writeable so that we can configure its source. Signed-off-by: Daniel Scally --- Changes in v2: - Updated the ABI Documentation to reflect the change. .../ABI/testing/configfs-usb-gadget-uvc | 2 +- drivers/usb/gadget/function/uvc_configfs.c | 57 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 611b23e6488d..feb3f2cc0c16 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -52,7 +52,7 @@ Date: Dec 2014 KernelVersion: 4.0 Description: Default output terminal descriptors - All attributes read only: + All attributes read only except bSourceID: ============== ============================================= iTerminal index of string descriptor diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 4303a3283ba0..af4258120f9a 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -483,11 +483,66 @@ UVC_ATTR_RO(uvcg_default_output_, cname, aname) UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, 8); UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, 16); UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, 8); -UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, 8); UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, 8); #undef UVCG_DEFAULT_OUTPUT_ATTR +static ssize_t uvcg_default_output_b_source_id_show(struct config_item *item, + char *page) +{ + struct config_group *group = to_config_group(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvc_output_terminal_descriptor *cd; + int result; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + cd = &opts->uvc_output_terminal; + + mutex_lock(&opts->lock); + result = sprintf(page, "%u\n", le8_to_cpu(cd->bSourceID)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return result; +} + +static ssize_t uvcg_default_output_b_source_id_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvc_output_terminal_descriptor *cd; + int result; + u8 num; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + cd = &opts->uvc_output_terminal; + + result = kstrtou8(page, 0, &num); + if (result) + return result; + + mutex_lock(&opts->lock); + cd->bSourceID = num; + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return len; +} +UVC_ATTR(uvcg_default_output_, b_source_id, bSourceID); + static struct configfs_attribute *uvcg_default_output_attrs[] = { &uvcg_default_output_attr_b_terminal_id, &uvcg_default_output_attr_w_terminal_type, From patchwork Mon Nov 21 09:25:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050527 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EAFC6C433FE for ; Mon, 21 Nov 2022 09:25:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230179AbiKUJZm (ORCPT ); Mon, 21 Nov 2022 04:25:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40524 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230124AbiKUJZl (ORCPT ); Mon, 21 Nov 2022 04:25:41 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0D10527DCF for ; Mon, 21 Nov 2022 01:25:41 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 24702E61; Mon, 21 Nov 2022 10:25:36 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022736; bh=kP37ODIPxKzC6npoFrbinG945sO2Q0cQ6Wmkj6XcDxA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RR9737Zb+hecxVDjAEPB0zS/2YWNDAqs9rwI97UTApU/tiZeOXDx+LtmxQwxntocX f9rgiDK+uP6AgGt0s1MSmmudNg+9r2uogAwbWAqaADe4edKseR/yUheCPkETHRVuPa VL0tn1SNkXrzGdo+RVrzOgiSNhoT9fbE9fIfxPjU= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 2/9] usb: gadget: uvc: Generalise helper functions for reuse Date: Mon, 21 Nov 2022 09:25:10 +0000 Message-Id: <20221121092517.225242-3-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org the __uvcg_*frm_intrv() helper functions can be helpful when adding support for similar attributes. Generalise the function names and move them higher in the file for better coverage. We also need copies of the functions for different sized targets, so refactor them to a macro with configurable bit size. Signed-off-by: Daniel Scally --- Changes in v2: - none drivers/usb/gadget/function/uvc_configfs.c | 109 +++++++++++---------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index af4258120f9a..d542dd251633 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -594,6 +594,60 @@ static const struct uvcg_config_group_type uvcg_terminal_grp_type = { }, }; +static inline int __uvcg_count_item_entries(char *buf, void *priv) +{ + ++*((int *)priv); + return 0; +} + +#define UVCG_ITEM_ENTRY_FUNCS(bits) \ +static inline int __uvcg_fill_item_entries_u##bits(char *buf, void *priv)\ +{ \ + u##bits num, **interv; \ + int ret; \ + \ + ret = kstrtou##bits(buf, 0, &num); \ + if (ret) \ + return ret; \ + \ + interv = priv; \ + **interv = num; \ + ++*interv; \ + \ + return 0; \ +} \ + \ +static int __uvcg_iter_item_entries_u##bits(const char *page, size_t len,\ + int (*fun)(char *, void *), void *priv)\ +{ \ + /* sign, base 2 representation, newline, terminator */ \ + char buf[1 + sizeof(u##bits) * 8 + 1 + 1]; \ + const char *pg = page; \ + int i, ret; \ + \ + if (!fun) \ + return -EINVAL; \ + \ + while (pg - page < len) { \ + i = 0; \ + while (i < sizeof(buf) && (pg - page < len) && \ + *pg != '\0' && *pg != '\n') \ + buf[i++] = *pg++; \ + if (i == sizeof(buf)) \ + return -EINVAL; \ + while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))\ + ++pg; \ + buf[i] = '\0'; \ + ret = fun(buf, priv); \ + if (ret) \ + return ret; \ + } \ + \ + return 0; \ +} + +UVCG_ITEM_ENTRY_FUNCS(32) + /* ----------------------------------------------------------------------------- * control/class/{fs|ss} */ @@ -1186,57 +1240,6 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, return result; } -static inline int __uvcg_count_frm_intrv(char *buf, void *priv) -{ - ++*((int *)priv); - return 0; -} - -static inline int __uvcg_fill_frm_intrv(char *buf, void *priv) -{ - u32 num, **interv; - int ret; - - ret = kstrtou32(buf, 0, &num); - if (ret) - return ret; - - interv = priv; - **interv = num; - ++*interv; - - return 0; -} - -static int __uvcg_iter_frm_intrv(const char *page, size_t len, - int (*fun)(char *, void *), void *priv) -{ - /* sign, base 2 representation, newline, terminator */ - char buf[1 + sizeof(u32) * 8 + 1 + 1]; - const char *pg = page; - int i, ret; - - if (!fun) - return -EINVAL; - - while (pg - page < len) { - i = 0; - while (i < sizeof(buf) && (pg - page < len) && - *pg != '\0' && *pg != '\n') - buf[i++] = *pg++; - if (i == sizeof(buf)) - return -EINVAL; - while ((pg - page < len) && (*pg == '\0' || *pg == '\n')) - ++pg; - buf[i] = '\0'; - ret = fun(buf, priv); - if (ret) - return ret; - } - - return 0; -} - static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, const char *page, size_t len) { @@ -1260,7 +1263,7 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, goto end; } - ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n); + ret = __uvcg_iter_item_entries_u32(page, len, __uvcg_count_item_entries, &n); if (ret) goto end; @@ -1270,7 +1273,7 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, goto end; } - ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp); + ret = __uvcg_iter_item_entries_u32(page, len, __uvcg_fill_item_entries_u32, &tmp); if (ret) { kfree(frm_intrv); goto end; From patchwork Mon Nov 21 09:25:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050528 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F0D5AC4332F for ; Mon, 21 Nov 2022 09:25:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230192AbiKUJZo (ORCPT ); Mon, 21 Nov 2022 04:25:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40526 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229662AbiKUJZm (ORCPT ); Mon, 21 Nov 2022 04:25:42 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F15A127DC0 for ; Mon, 21 Nov 2022 01:25:40 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C22F0118E; Mon, 21 Nov 2022 10:25:36 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022737; bh=3QDfXP5S7MajJnNfC31p6Q72nTKAvRKcbw9q+510XCA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=h+LEHi58aHTeIsuPB2ZKB4ZrktG7Z2pawn6pWQ99OX5aTrGFW04+zzLKnEocZyT3P braFmv27Iew0+ZiozLZeBEOgqOEiJoP7I03XjAY9ydJA885R8RWDg81EBhA4yiYk0e QX7+YxrzZw3ZKy5r38rNHdSdOZZmM8myOl7O27EE= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 3/9] usb: gadget: uvc: Allow definition of XUs in configfs Date: Mon, 21 Nov 2022 09:25:11 +0000 Message-Id: <20221121092517.225242-4-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org The UVC gadget at present has no support for extension units. Add the infrastructure to uvc_configfs.c that allows users to create XUs via configfs. These will be stored in a new child of uvcg_control_grp_type with the name "extensions". Signed-off-by: Daniel Scally --- Changes in v2: - Updated the ABI documentation with the new elements. - Locked the su_mutex when appropriate. .../ABI/testing/configfs-usb-gadget-uvc | 28 + drivers/usb/gadget/function/f_uvc.c | 9 + drivers/usb/gadget/function/u_uvc.h | 7 + drivers/usb/gadget/function/uvc_configfs.c | 480 ++++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 29 ++ 5 files changed, 553 insertions(+) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index feb3f2cc0c16..045c57e7e245 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -111,6 +111,34 @@ Description: Default processing unit descriptors bUnitID a non-zero id of this unit =============== ======================================== +What: /config/usb-gadget/gadget/functions/uvc.name/control/extensions +Date: Nov 2022 +KernelVersion: 6.1 +Description: Extension unit descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/extensions/name +Date: Nov 2022 +KernelVersion: 6.1 +Description: Extension Unit (XU) Descriptor + + bLength, bUnitID and iExtension are read-only. All others are + read-write. + + =============== ======================================== + bLength size of the descriptor in bytes + bUnitID non-zero ID of this unit + guidExtensionCode vendor specific code identifying the XU + bNumControls number of controls in this XU + bNrInPins number of input pins for this unit + baSourceID list of the IDs of the units or terminals + to which this XU is connected + bControlSize size of the bmControls field in bytes + bmControls list of bitmaps detailing which vendor + specific controls are supported + iExtension index of a string descriptor that describes + this extension unit + =============== ======================================== + What: /config/usb-gadget/gadget/functions/uvc.name/control/header Date: Dec 2014 KernelVersion: 4.0 diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 6e196e06181e..eca5f36dfa74 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -842,6 +842,13 @@ static struct usb_function_instance *uvc_alloc_inst(void) od->bSourceID = 2; od->iTerminal = 0; + /* + * With the ability to add XUs to the UVC function graph, we need to be + * able to allocate unique unit IDs to them. The IDs are 1-based, with + * the CT, PU and OT above consuming the first 3. + */ + opts->last_unit_id = 3; + md = &opts->uvc_color_matching; md->bLength = UVC_DT_COLOR_MATCHING_SIZE; md->bDescriptorType = USB_DT_CS_INTERFACE; @@ -870,6 +877,8 @@ static struct usb_function_instance *uvc_alloc_inst(void) opts->ss_control = (const struct uvc_descriptor_header * const *)ctl_cls; + INIT_LIST_HEAD(&opts->extension_units); + opts->streaming_interval = 1; opts->streaming_maxpacket = 1024; snprintf(opts->function_name, sizeof(opts->function_name), "UVC Camera"); diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 24b8681b0d6f..5119cfe5ee4e 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -28,6 +28,7 @@ struct f_uvc_opts { unsigned int control_interface; unsigned int streaming_interface; char function_name[32]; + unsigned int last_unit_id; /* * Control descriptors array pointers for full-/high-speed and @@ -64,6 +65,12 @@ struct f_uvc_opts { struct uvc_descriptor_header *uvc_fs_control_cls[5]; struct uvc_descriptor_header *uvc_ss_control_cls[5]; + /* + * Control descriptors for extension units. There could be any number + * of these, including none at all. + */ + struct list_head extension_units; + /* * Streaming descriptors for full-speed, high-speed and super-speed. * Used by configfs only, must not be touched by legacy gadgets. The diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index d542dd251633..0a69eb6cf221 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -646,8 +646,487 @@ static int __uvcg_iter_item_entries_u##bits(const char *page, size_t len,\ return 0; \ } +UVCG_ITEM_ENTRY_FUNCS(8) UVCG_ITEM_ENTRY_FUNCS(32) +/* ----------------------------------------------------------------------------- + * control/extensions + */ + +#define UVCG_EXTENSION_ATTR(cname, aname, ro...) \ +static ssize_t uvcg_extension_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item->ci_parent); \ + struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ + struct uvcg_extension *xu = to_uvcg_extension(item); \ + struct config_item *opts_item; \ + struct f_uvc_opts *opts; \ + int ret; \ + \ + mutex_lock(su_mutex); \ + \ + opts_item = item->ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + ret = sprintf(page, "%u\n", xu->desc.aname); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + \ + return ret; \ +} \ +UVC_ATTR##ro(uvcg_extension_, cname, aname) + +UVCG_EXTENSION_ATTR(b_length, bLength, _RO); +UVCG_EXTENSION_ATTR(b_unit_id, bUnitID, _RO); +UVCG_EXTENSION_ATTR(i_extension, iExtension, _RO); + +static ssize_t uvcg_extension_b_num_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + u8 num; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + ret = kstrtou8(page, 0, &num); + if (ret) + return ret; + + mutex_lock(&opts->lock); + xu->desc.bNumControls = num; + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return len; +} +UVCG_EXTENSION_ATTR(b_num_controls, bNumControls); + +/* + * In addition to storing bNrInPins, this function needs to realloc the + * memory for the baSourceID array and additionally expand bLength. + */ +static ssize_t uvcg_extension_b_nr_in_pins_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + void *tmp_buf; + int ret; + u8 num; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + ret = kstrtou8(page, 0, &num); + if (ret) + return ret; + + mutex_lock(&opts->lock); + + if (num == xu->desc.bNrInPins) { + ret = len; + goto unlock; + } + + tmp_buf = krealloc_array(xu->desc.baSourceID, num, sizeof(u8), + GFP_KERNEL); + if (!tmp_buf) { + ret = -ENOMEM; + goto unlock; + } + + if (num >= xu->desc.bNrInPins) + memset(tmp_buf + xu->desc.bNrInPins, 0, + (num - xu->desc.bNrInPins) * sizeof(u8)); + + xu->desc.baSourceID = tmp_buf; + xu->desc.bNrInPins = num; + xu->desc.bLength = 24 + xu->desc.bNrInPins + xu->desc.bControlSize; + + ret = len; + +unlock: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} +UVCG_EXTENSION_ATTR(b_nr_in_pins, bNrInPins); + +/* + * In addition to storing bControlSize, this function needs to realloc the + * memory for the bmControls array and additionally expand bLength. + */ +static ssize_t uvcg_extension_b_control_size_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + void *tmp_buf; + int ret; + u8 num; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + ret = kstrtou8(page, 0, &num); + if (ret) + return ret; + + mutex_lock(&opts->lock); + + if (num == xu->desc.bControlSize) { + ret = len; + goto unlock; + } + + tmp_buf = krealloc_array(xu->desc.bmControls, num, sizeof(u8), + GFP_KERNEL); + if (!tmp_buf) { + ret = -ENOMEM; + goto unlock; + } + + if (num >= xu->desc.bControlSize) + memset(tmp_buf + xu->desc.bControlSize, 0, + (num - xu->desc.bControlSize) * sizeof(u8)); + + xu->desc.bmControls = tmp_buf; + xu->desc.bControlSize = num; + xu->desc.bLength = 24 + xu->desc.bNrInPins + xu->desc.bControlSize; + + ret = len; + +unlock: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVCG_EXTENSION_ATTR(b_control_size, bControlSize); + +static ssize_t uvcg_extension_guid_extension_code_show(struct config_item *item, + char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, xu->desc.guidExtensionCode, sizeof(xu->desc.guidExtensionCode)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(xu->desc.guidExtensionCode); +} + +static ssize_t uvcg_extension_guid_extension_code_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(xu->desc.guidExtensionCode, page, + min(sizeof(xu->desc.guidExtensionCode), len)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + ret = sizeof(xu->desc.guidExtensionCode); + + return ret; +} + +UVC_ATTR(uvcg_extension_, guid_extension_code, guidExtensionCode); + +static ssize_t uvcg_extension_ba_source_id_show(struct config_item *item, + char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + char *pg = page; + int ret, i; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + for (ret = 0, i = 0; i < xu->desc.bNrInPins; ++i) { + ret += sprintf(pg, "%u\n", xu->desc.baSourceID[i]); + pg = page + ret; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} + +static ssize_t uvcg_extension_ba_source_id_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + u8 *source_ids, *tmp; + int ret, n = 0; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + ret = __uvcg_iter_item_entries_u8(page, len, __uvcg_count_item_entries, &n); + if (ret) + goto unlock; + + tmp = source_ids = kcalloc(n, sizeof(u8), GFP_KERNEL); + if (!source_ids) { + ret = -ENOMEM; + goto unlock; + } + + ret = __uvcg_iter_item_entries_u8(page, len, __uvcg_fill_item_entries_u8, &tmp); + if (ret) { + kfree(source_ids); + goto unlock; + } + + kfree(xu->desc.baSourceID); + xu->desc.baSourceID = source_ids; + xu->desc.bNrInPins = n; + xu->desc.bLength = 24 + xu->desc.bNrInPins + xu->desc.bControlSize; + + ret = len; + +unlock: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} +UVC_ATTR(uvcg_extension_, ba_source_id, baSourceID); + +static ssize_t uvcg_extension_bm_controls_show(struct config_item *item, + char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + char *pg = page; + int ret, i; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + for (ret = 0, i = 0; i < xu->desc.bControlSize; ++i) { + ret += sprintf(pg, "0x%02x\n", xu->desc.bmControls[i]); + pg = page + ret; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} + +static ssize_t uvcg_extension_bm_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + u8 *bm_controls, *tmp; + int ret, n = 0; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + ret = __uvcg_iter_item_entries_u8(page, len, __uvcg_count_item_entries, &n); + if (ret) + goto unlock; + + tmp = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL); + if (!bm_controls) { + ret = -ENOMEM; + goto unlock; + } + + ret = __uvcg_iter_item_entries_u8(page, len, __uvcg_fill_item_entries_u8, &tmp); + if (ret) { + kfree(bm_controls); + goto unlock; + } + + kfree(xu->desc.bmControls); + xu->desc.bmControls = bm_controls; + xu->desc.bControlSize = n; + xu->desc.bLength = 24 + xu->desc.bNrInPins + xu->desc.bControlSize; + + ret = len; + +unlock: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_extension_, bm_controls, bmControls); + +static struct configfs_attribute *uvcg_extension_attrs[] = { + &uvcg_extension_attr_b_length, + &uvcg_extension_attr_b_unit_id, + &uvcg_extension_attr_b_num_controls, + &uvcg_extension_attr_b_nr_in_pins, + &uvcg_extension_attr_b_control_size, + &uvcg_extension_attr_guid_extension_code, + &uvcg_extension_attr_ba_source_id, + &uvcg_extension_attr_bm_controls, + &uvcg_extension_attr_i_extension, + NULL, +}; + +static void uvcg_extension_release(struct config_item *item) +{ + struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item); + + kfree(xu); +} + +static struct configfs_item_operations uvcg_extension_item_ops = { + .release = uvcg_extension_release, +}; + +static const struct config_item_type uvcg_extension_type = { + .ct_item_ops = &uvcg_extension_item_ops, + .ct_attrs = uvcg_extension_attrs, + .ct_owner = THIS_MODULE, +}; + +static void uvcg_extension_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + + opts_item = group->cg_item.ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + config_item_put(item); + list_del(&xu->list); + kfree(xu->desc.baSourceID); + kfree(xu->desc.bmControls); + + mutex_unlock(&opts->lock); +} + +static struct config_item *uvcg_extension_make(struct config_group *group, const char *name) +{ + struct config_item *opts_item; + struct uvcg_extension *xu; + struct f_uvc_opts *opts; + + opts_item = group->cg_item.ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + xu = kzalloc(sizeof(*xu), GFP_KERNEL); + if (!xu) + return ERR_PTR(-ENOMEM); + + xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(0, 0); + xu->desc.bDescriptorType = USB_DT_CS_INTERFACE; + xu->desc.bDescriptorSubType = UVC_VC_EXTENSION_UNIT; + xu->desc.bUnitID = ++opts->last_unit_id; + xu->desc.bNumControls = 0; + xu->desc.bNrInPins = 0; + xu->desc.baSourceID = NULL; + xu->desc.bControlSize = 0; + xu->desc.bmControls = NULL; + + config_item_init_type_name(&xu->item, name, &uvcg_extension_type); + list_add_tail(&xu->list, &opts->extension_units); + + mutex_unlock(&opts->lock); + + return &xu->item; +} + +static struct configfs_group_operations uvcg_extensions_grp_ops = { + .make_item = uvcg_extension_make, + .drop_item = uvcg_extension_drop, +}; + +static const struct uvcg_config_group_type uvcg_extensions_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_extensions_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "extensions", +}; + /* ----------------------------------------------------------------------------- * control/class/{fs|ss} */ @@ -842,6 +1321,7 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { &uvcg_processing_grp_type, &uvcg_terminal_grp_type, &uvcg_control_class_grp_type, + &uvcg_extensions_grp_type, NULL, }, }; diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index ad2ec8c4c78c..c9a4182fb26f 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -132,6 +132,35 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); } +/* ----------------------------------------------------------------------------- + * control/extensions/ + */ + +struct uvcg_extension_unit_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + u8 bUnitID; + u8 guidExtensionCode[16]; + u8 bNumControls; + u8 bNrInPins; + u8 *baSourceID; + u8 bControlSize; + u8 *bmControls; + u8 iExtension; +} __packed; + +struct uvcg_extension { + struct config_item item; + struct list_head list; + struct uvcg_extension_unit_descriptor desc; +}; + +static inline struct uvcg_extension *to_uvcg_extension(struct config_item *item) +{ + return container_of(item, struct uvcg_extension, item); +} + int uvcg_attach_configfs(struct f_uvc_opts *opts); #endif /* UVC_CONFIGFS_H */ From patchwork Mon Nov 21 09:25:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050529 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F294DC433FE for ; Mon, 21 Nov 2022 09:25:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230193AbiKUJZq (ORCPT ); Mon, 21 Nov 2022 04:25:46 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40538 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230191AbiKUJZo (ORCPT ); Mon, 21 Nov 2022 04:25:44 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 19DF927DC0 for ; Mon, 21 Nov 2022 01:25:43 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6A4111221; Mon, 21 Nov 2022 10:25:37 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022737; bh=Dt5JT/JR5B1p0r4QWKNtKfzx30qHivKh1ksLo73KsvU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kUOT+xd5amhR1hex0aGjIZRhrZ+4u1Bh/78y43lLFDdmcu413D4f43kGnTA5OHDaf y9QG+jWznULozj4CArhu9pecQMy4fpp69/zPMUDldkHvuggbKdpKgsmRojH1Vzb/1i z8rJBYJI0nSKkD11rIiHZNrxBKDWxvSjqM/rQVV0= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 4/9] usb: gadget: uvc: Copy XU descriptors during .bind() Date: Mon, 21 Nov 2022 09:25:12 +0000 Message-Id: <20221121092517.225242-5-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Now that extension unit support is available through configfs we need to copy the descriptors for the XUs during uvc_function_bind() so that they're exposed to the usb subsystem. Signed-off-by: Daniel Scally Reviewed-by: Laurent Pinchart --- Changes in v2: - none drivers/usb/gadget/function/f_uvc.c | 35 +++++++++++++++++++++++++++++ drivers/usb/gadget/function/uvc.h | 1 + 2 files changed, 36 insertions(+) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index eca5f36dfa74..e0a308f1355c 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -464,6 +464,25 @@ uvc_register_video(struct uvc_device *uvc) } \ } while (0) +#define UVC_COPY_XU_DESCRIPTOR(mem, dst, desc) \ + do { \ + *(dst)++ = mem; \ + memcpy(mem, desc, 22); /* bLength to bNrInPins */ \ + mem += 22; \ + \ + memcpy(mem, desc->baSourceID, desc->bNrInPins); \ + mem += desc->bNrInPins; \ + \ + memcpy(mem, &desc->bControlSize, 1); \ + mem++; \ + \ + memcpy(mem, desc->bmControls, desc->bControlSize); \ + mem += desc->bControlSize; \ + \ + memcpy(mem, &desc->iExtension, 1); \ + mem++; \ + } while (0) + static struct usb_descriptor_header ** uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) { @@ -475,6 +494,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) const struct usb_descriptor_header * const *src; struct usb_descriptor_header **dst; struct usb_descriptor_header **hdr; + struct uvcg_extension *xu; unsigned int control_size; unsigned int streaming_size; unsigned int n_desc; @@ -539,6 +559,13 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) bytes += (*src)->bLength; n_desc++; } + + list_for_each_entry(xu, uvc->desc.extension_units, list) { + control_size += xu->desc.bLength; + bytes += xu->desc.bLength; + n_desc++; + } + for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; *src; ++src) { streaming_size += (*src)->bLength; @@ -565,6 +592,13 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_header = mem; UVC_COPY_DESCRIPTORS(mem, dst, (const struct usb_descriptor_header **)uvc_control_desc); + + list_for_each_entry(xu, uvc->desc.extension_units, list) { + struct uvcg_extension_unit_descriptor *desc = &xu->desc; + + UVC_COPY_XU_DESCRIPTOR(mem, dst, desc); + } + uvc_control_header->wTotalLength = cpu_to_le16(control_size); uvc_control_header->bInCollection = 1; uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; @@ -988,6 +1022,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->desc.fs_streaming = opts->fs_streaming; uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.ss_streaming = opts->ss_streaming; + uvc->desc.extension_units = &opts->extension_units; streaming = config_group_find_item(&opts->func_inst.group, "streaming"); if (!streaming) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 40226b1f7e14..f1a016d20bb6 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -143,6 +143,7 @@ struct uvc_device { const struct uvc_descriptor_header * const *fs_streaming; const struct uvc_descriptor_header * const *hs_streaming; const struct uvc_descriptor_header * const *ss_streaming; + struct list_head *extension_units; } desc; unsigned int control_intf; From patchwork Mon Nov 21 09:25:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050531 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CBF9EC43217 for ; Mon, 21 Nov 2022 09:25:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230202AbiKUJZs (ORCPT ); Mon, 21 Nov 2022 04:25:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40560 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230191AbiKUJZq (ORCPT ); Mon, 21 Nov 2022 04:25:46 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 295078DA78 for ; Mon, 21 Nov 2022 01:25:44 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2392C13FF; Mon, 21 Nov 2022 10:25:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022738; bh=M4RUnKIxyhOz/QWC4Ne3ZHQNRWTpH4gOSL0xfbtYq3A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FRLPUTJlYPhLzSgHOogTx+fLtuqvcZeSOAdUWJo4642Um9QYm0RTIlZ5IjAkCebhx WgEfKCIOkQKSJyQfoM7Nqtfk0e7ymlQKPb18i3qDZa/vJo4BGmQnSFXIFTwOWiM8jg Q4bHZ30g5SeHo7mzFG0f9BshVHi1va4M1IJb1T88= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 5/9] usb: gadget: uvc: Support arbitrary string descriptors Date: Mon, 21 Nov 2022 09:25:13 +0000 Message-Id: <20221121092517.225242-6-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Currently string descriptors for the UVC function are largely hard coded. It's not practically possible to support string descriptors that describe Extension Units that way, so add a mechanism to the configfs tree that allows the definition of arbitrary string descriptors. Signed-off-by: Daniel Scally --- Changes in v2: - New patch .../ABI/testing/configfs-usb-gadget-uvc | 20 ++ drivers/usb/gadget/function/f_uvc.c | 1 + drivers/usb/gadget/function/u_uvc.h | 7 + drivers/usb/gadget/function/uvc_configfs.c | 302 ++++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 30 ++ 5 files changed, 360 insertions(+) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 045c57e7e245..5faa049ed759 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -10,6 +10,26 @@ Description: UVC function directory function_name string [32] =================== ============================= +What: /config/usb-gadget/gadget/functions/uvc.name/strings +Date: Nov 2022 +KernelVersion: 6.1 +Description: String descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/strings/langid +Date: Nov 2022 +KernelVersion: 6.1 +Description: String descriptors for langid (e.g. 0x409) + +What: /config/usb-gadget/gadget/functions/uvc.name/strings/langid/name +Date: Nov 2022 +KernelVersion: 6.1 +Description: String descriptor + + =================== ============================= + id id of the string descriptor + s 126 character string + =================== ============================= + What: /config/usb-gadget/gadget/functions/uvc.name/control Date: Dec 2014 KernelVersion: 4.0 diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e0a308f1355c..1b8871a24be8 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -912,6 +912,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) (const struct uvc_descriptor_header * const *)ctl_cls; INIT_LIST_HEAD(&opts->extension_units); + INIT_LIST_HEAD(&opts->languages); opts->streaming_interval = 1; opts->streaming_maxpacket = 1024; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 5119cfe5ee4e..c1c9ea5931d3 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -81,6 +81,13 @@ struct f_uvc_opts { struct uvc_descriptor_header **uvc_hs_streaming_cls; struct uvc_descriptor_header **uvc_ss_streaming_cls; + /* + * A list of languages, associated with which may be string descriptors + * for various parts of the gadget, including the IAD and XUs. + */ + struct list_head languages; + unsigned int nlangs; + /* * Read/write access to configfs attributes is handled by configfs. * diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 0a69eb6cf221..da2f70036993 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -13,6 +13,8 @@ #include "uvc_configfs.h" #include +#include + /* ----------------------------------------------------------------------------- * Global Utility Structures and Macros @@ -2824,6 +2826,305 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { }, }; +/* ----------------------------------------------------------------------------- + * strings/ + */ + +static ssize_t uvcg_string_id_show(struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_string *string = to_uvcg_string(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + ret = sprintf(page, "%u\n", string->usb_string.id); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} +UVC_ATTR_RO(uvcg_string_, id, id); + +static ssize_t uvcg_string_s_show(struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_string *string = to_uvcg_string(item); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + ret = snprintf(page, sizeof(string->string), "%s\n", string->string); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} + +static ssize_t uvcg_string_s_store(struct config_item *item, const char *page, + size_t len) +{ + struct config_group *group = to_config_group(item->ci_parent); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct uvcg_string *string = to_uvcg_string(item); + int size = min(sizeof(string->string), len + 1); + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + + if (len > USB_MAX_STRING_LEN) + return -EINVAL; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + ret = strscpy(string->string, page, size); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return len; +} +UVC_ATTR(uvcg_string_, s, s); + +static struct configfs_attribute *uvcg_string_attrs[] = { + &uvcg_string_attr_id, + &uvcg_string_attr_s, + NULL, +}; + +static void uvcg_string_release(struct config_item *item) +{ + struct uvcg_string *string = to_uvcg_string(item); + + kfree(string); +} + +static struct configfs_item_operations uvcg_string_item_ops = { + .release = uvcg_string_release, +}; + +static const struct config_item_type uvcg_string_type = { + .ct_item_ops = &uvcg_string_item_ops, + .ct_attrs = uvcg_string_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *uvcg_string_make(struct config_group *group, + const char *name) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct uvcg_string *string; + struct f_uvc_opts *opts; + + language = to_uvcg_language(group); + + string = kzalloc(sizeof(*string), GFP_KERNEL); + if (!string) + return ERR_PTR(-ENOMEM); + + opts_item = group->cg_item.ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + string->usb_string.id = language->nstrings++; + string->usb_string.s = string->string; + list_add_tail(&string->list, &language->strings); + + config_item_init_type_name(&string->item, name, &uvcg_string_type); + + mutex_unlock(&opts->lock); + + return &string->item; +} + +static void uvcg_string_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct uvcg_string *string; + struct f_uvc_opts *opts; + unsigned int i = 1; + + language = to_uvcg_language(group); + string = to_uvcg_string(item); + + opts_item = group->cg_item.ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + list_del(&string->list); + language->nstrings--; + + /* Reset the ids for the language's strings to guarantee a continuous set */ + list_for_each_entry(string, &language->strings, list) + string->usb_string.id = i++; + + mutex_unlock(&opts->lock); +} + +/* ----------------------------------------------------------------------------- + * strings/ + */ + +static struct configfs_group_operations uvcg_language_group_ops = { + .make_item = uvcg_string_make, + .drop_item = uvcg_string_drop, +}; + +static void uvcg_language_release(struct config_item *item) +{ + struct uvcg_language *language = to_uvcg_language(to_config_group(item)); + + kfree(language); +} + +static struct configfs_item_operations uvcg_language_item_ops = { + .release = uvcg_language_release, +}; + +/* + * The strings attribute is really a helper for users - having defined some string + * descriptors for this language, actually checking what's set when they're all + * in separate directories would be a bit...suboptimal. This read-only attribute + * gives a summary at language-level. + */ +static ssize_t uvcg_language_strings_show(struct config_item *item, char *page) +{ + struct config_group *group = to_config_group(item); + struct uvcg_language *language = to_uvcg_language(group); + struct mutex *su_mutex = &group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct uvcg_string *string; + struct f_uvc_opts *opts; + char *pg = page; + int ret = 0; + + mutex_lock(su_mutex); + + opts_item = item->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + list_for_each_entry(string, &language->strings, list) { + ret += sprintf(pg, "%s: %s [%u]\n", string->item.ci_name, + string->usb_string.s, string->usb_string.id); + pg = page + ret; + } + + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return ret; +} +UVC_ATTR_RO(uvcg_language_, strings, strings); + +static struct configfs_attribute *uvcg_language_attrs[] = { + &uvcg_language_attr_strings, + NULL +}; + +static const struct config_item_type uvcg_language_type = { + .ct_item_ops = &uvcg_language_item_ops, + .ct_group_ops = &uvcg_language_group_ops, + .ct_attrs = uvcg_language_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_language_make(struct config_group *group, + const char *name) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret; + u16 num; + + ret = kstrtou16(name, 0, &num); + if (ret) + return ERR_PTR(ret); + + if (!usb_validate_langid(num)) + return ERR_PTR(-EINVAL); + + language = kzalloc(sizeof(*language), GFP_KERNEL); + if (!language) + return ERR_PTR(-ENOMEM); + + opts_item = group->cg_item.ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + language->stringtab.language = num; + list_add_tail(&language->list, &opts->languages); + opts->nlangs++; + INIT_LIST_HEAD(&language->strings); + + config_group_init_type_name(&language->group, name, &uvcg_language_type); + + mutex_unlock(&opts->lock); + + return &language->group; +} + +static void uvcg_language_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_language *language; + struct config_item *opts_item; + struct f_uvc_opts *opts; + + language = to_uvcg_language(to_config_group(item)); + + opts_item = group->cg_item.ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + list_del(&language->list); + config_item_put(item); + + mutex_unlock(&opts->lock); +} + +static struct configfs_group_operations uvcg_strings_grp_ops = { + .make_group = uvcg_language_make, + .drop_item = uvcg_language_drop, +}; + +static const struct uvcg_config_group_type uvcg_strings_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_strings_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "strings", +}; + /* ----------------------------------------------------------------------------- * UVC function */ @@ -2951,6 +3252,7 @@ static const struct uvcg_config_group_type uvc_func_type = { .children = (const struct uvcg_config_group_type*[]) { &uvcg_control_grp_type, &uvcg_streaming_grp_type, + &uvcg_strings_grp_type, NULL, }, }; diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index c9a4182fb26f..a714426a174a 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -13,6 +13,7 @@ #define UVC_CONFIGFS_H #include +#include #include "u_uvc.h" @@ -132,6 +133,35 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); } +/* ----------------------------------------------------------------------------- + * strings/ + */ + +struct uvcg_language { + struct config_group group; + unsigned int nstrings; + struct list_head list; + struct list_head strings; + struct usb_gadget_strings stringtab; +}; + +#define to_uvcg_language(language) \ +container_of(language, struct uvcg_language, group) + +/* ----------------------------------------------------------------------------- + * strings/ + */ + +struct uvcg_string { + struct config_item item; + struct list_head list; + char string[USB_MAX_STRING_LEN]; + struct usb_string usb_string; +}; + +#define to_uvcg_string(str_item)\ +container_of(str_item, struct uvcg_string, item) + /* ----------------------------------------------------------------------------- * control/extensions/ */ From patchwork Mon Nov 21 09:25:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050530 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 26EF8C4332F for ; Mon, 21 Nov 2022 09:25:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230201AbiKUJZs (ORCPT ); Mon, 21 Nov 2022 04:25:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40562 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230197AbiKUJZq (ORCPT ); Mon, 21 Nov 2022 04:25:46 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 44EB427DE4 for ; Mon, 21 Nov 2022 01:25:45 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BFADD1905; Mon, 21 Nov 2022 10:25:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022739; bh=28K8LD+f0JZGAxJz0Tf5aEHpV0AcAjYiab70NsIvnbY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cB3ph/tjBIrTRmuqZpxGQ55HWxVSVGKefYwgJkia6Gqa5ZxB+D9JdnIM0IQxduf5v 5LLZ4oEKaL1eNtxEQCp37+WMeJMsLQjq4oSFNh3/vFmMJS+SPiyNG5MAsJIRdzet0r p00VZeu5tRShNq/CZKojhgDgt5Dls3ppvoPKiss0= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 6/9] usb: gadget: uvc: Allow linking XUs to string descriptors Date: Mon, 21 Nov 2022 09:25:14 +0000 Message-Id: <20221121092517.225242-7-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Add .allow_link() and .drop_link() callbacks to allow users to link an extension unit descriptor to a string descriptor. Signed-off-by: Daniel Scally --- Changes in v2: - New patch drivers/usb/gadget/function/uvc_configfs.c | 60 ++++++++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 1 + 2 files changed, 61 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index da2f70036993..5c51862150c4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1053,8 +1053,68 @@ static void uvcg_extension_release(struct config_item *item) kfree(xu); } +static int uvcg_extension_allow_link(struct config_item *src, struct config_item *tgt) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(src); + struct config_item *strings; + struct uvcg_string *string; + struct config_item *opts_item; + struct f_uvc_opts *opts; + int ret = 0; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + /* Validate that the target of the link is an entry in strings/ */ + opts_item = src->ci_parent->ci_parent->ci_parent; + + strings = config_group_find_item(to_config_group(opts_item), "strings"); + if (!strings || tgt->ci_parent->ci_parent != strings) { + ret = -EINVAL; + goto put_strings; + } + + string = to_uvcg_string(tgt); + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + xu->string_descriptor_index = string->usb_string.id; + + mutex_unlock(&opts->lock); + +put_strings: + config_item_put(strings); + mutex_unlock(su_mutex); + + return ret; +} + +static void uvcg_extension_drop_link(struct config_item *src, struct config_item *tgt) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvcg_extension *xu = to_uvcg_extension(src); + struct config_item *opts_item; + struct f_uvc_opts *opts; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = src->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + xu->string_descriptor_index = 0; + + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); +} + static struct configfs_item_operations uvcg_extension_item_ops = { .release = uvcg_extension_release, + .allow_link = uvcg_extension_allow_link, + .drop_link = uvcg_extension_drop_link, }; static const struct config_item_type uvcg_extension_type = { diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index a714426a174a..e1308026aed6 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -183,6 +183,7 @@ struct uvcg_extension_unit_descriptor { struct uvcg_extension { struct config_item item; struct list_head list; + u8 string_descriptor_index; struct uvcg_extension_unit_descriptor desc; }; From patchwork Mon Nov 21 09:25:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050532 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 69B39C4332F for ; Mon, 21 Nov 2022 09:25:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230209AbiKUJZu (ORCPT ); Mon, 21 Nov 2022 04:25:50 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40618 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230191AbiKUJZt (ORCPT ); Mon, 21 Nov 2022 04:25:49 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 035C58F3C2 for ; Mon, 21 Nov 2022 01:25:47 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6506A1929; Mon, 21 Nov 2022 10:25:39 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022739; bh=QiqTDLml8o8JhNkflKyyqaqrHzrPjH/a0fzolPjCV0s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F1fBogi6GpWd/IRiJv6BtnugcNJZDf6saUxTIWnxXbuLxVKjqKjyX93nFmod5yxJr +QsVkvU9Jk9rWuSFIWM1V440DBN96gRT3dZuwGK56Sis7M1E0t6QCBPmwTmPLFKGJH oTtqY/OymfYZausT5xygWTTPDuXv4JzwVbvAD+o4= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 7/9] usb: gadget: uvc: Attach custom string descriptors Date: Mon, 21 Nov 2022 09:25:15 +0000 Message-Id: <20221121092517.225242-8-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org 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 --- 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)); From patchwork Mon Nov 21 09:25:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050533 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 33F5EC433FE for ; Mon, 21 Nov 2022 09:25:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230211AbiKUJZv (ORCPT ); Mon, 21 Nov 2022 04:25:51 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40620 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230207AbiKUJZt (ORCPT ); Mon, 21 Nov 2022 04:25:49 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1D76B29359 for ; Mon, 21 Nov 2022 01:25:48 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 063292D9; Mon, 21 Nov 2022 10:25:39 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022740; bh=8gN8M4WanXpSSG/9fB+2p0usCZzFfitjbtVpmKrLjFM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Yb87Gdxg5T1l1+mlZpStoa3auYQ+k4V6NJTwSO2lHn/ygsXkKs4eM+jWPjrmx+i+i GCqjVaqYv+/zpT6MwrzgHpTAvEDJ3grM23sl7dYZe2VMCBF097t0GSOjjOTMIRjVwS LJ785JFR9KoHBI3OvFXFnvKRKIOwvqW9GVdbDiwo= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 8/9] usb: gadget: uvc: Allow linking function to string descs Date: Mon, 21 Nov 2022 09:25:16 +0000 Message-Id: <20221121092517.225242-9-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Currently the string descriptors for the IAD, VideoControl Interface and VideoStreaming Interfaces are hardcoded into f_uvc. Now that we can create arbitrary string descriptors, add a mechanism to define string descriptors for the IAD, VC and VS interfaces by linking to the appropriate directory at function level. Signed-off-by: Daniel Scally --- Changes in v2: - New patch drivers/usb/gadget/function/u_uvc.h | 8 +++ drivers/usb/gadget/function/uvc_configfs.c | 59 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index c1c9ea5931d3..331cdf5ba9c8 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -88,6 +88,14 @@ struct f_uvc_opts { struct list_head languages; unsigned int nlangs; + /* + * Indexes into the function's string descriptors allowing users to set + * custom descriptions rather than the hard-coded defaults. + */ + u8 iad_index; + u8 vs0_index; + u8 vs1_index; + /* * Read/write access to configfs attributes is handled by configfs. * diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 5c51862150c4..e8faa31998fa 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -3197,8 +3197,67 @@ static void uvc_func_item_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } +static int uvc_func_allow_link(struct config_item *src, struct config_item *tgt) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct config_item *strings; + struct uvcg_string *string; + struct f_uvc_opts *opts; + int ret = 0; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + /* Validate that the target is an entry in strings/ */ + strings = config_group_find_item(to_config_group(src), "strings"); + if (!strings || tgt->ci_parent->ci_parent != strings) { + ret = -EINVAL; + goto put_strings; + } + + string = to_uvcg_string(tgt); + + opts = to_f_uvc_opts(src); + mutex_lock(&opts->lock); + + if (!strcmp(tgt->ci_name, "iad_desc")) + opts->iad_index = string->usb_string.id; + else if (!strcmp(tgt->ci_name, "vs0_desc")) + opts->vs0_index = string->usb_string.id; + else if (!strcmp(tgt->ci_name, "vs1_desc")) + opts->vs1_index = string->usb_string.id; + else + ret = -EINVAL; + + mutex_unlock(&opts->lock); + +put_strings: + config_item_put(strings); + mutex_unlock(su_mutex); + + return ret; +} + +static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt) +{ + struct f_uvc_opts *opts; + + opts = to_f_uvc_opts(src); + mutex_lock(&opts->lock); + + if (!strcmp(tgt->ci_name, "iad_desc")) + opts->iad_index = 0; + else if (!strcmp(tgt->ci_name, "vs0_desc")) + opts->vs0_index = 0; + else if (!strcmp(tgt->ci_name, "vs1_desc")) + opts->vs1_index = 0; + + mutex_unlock(&opts->lock); +} + static struct configfs_item_operations uvc_func_item_ops = { .release = uvc_func_item_release, + .allow_link = uvc_func_allow_link, + .drop_link = uvc_func_drop_link, }; #define UVCG_OPTS_ATTR(cname, aname, limit) \ From patchwork Mon Nov 21 09:25:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13050534 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 63ABFC433FE for ; Mon, 21 Nov 2022 09:25:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230205AbiKUJZz (ORCPT ); Mon, 21 Nov 2022 04:25:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40718 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230222AbiKUJZx (ORCPT ); Mon, 21 Nov 2022 04:25:53 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C7C4629375 for ; Mon, 21 Nov 2022 01:25:50 -0800 (PST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A0294987; Mon, 21 Nov 2022 10:25:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669022741; bh=I9+HvzKpiTZp4KSPl/FkgoMqAqvRpV91iNhWp7eoreA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hnH0PkRrDwAx96qoLjmt8kra4Er17VTRvHz0gjX+PPIhGTgwFjCpjzC4j/9bAQxqc uBbGQsRRW03GwrShIAsZ9Nu5LWdV0MElpJL1W7HpLE8dSg8gmSDCDj5GceVndwYr1p Rjz/GNh9xlFNnC6nu9RvQTcaFQy+hN1+kwoZSBkk= From: Daniel Scally To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com, torleiv@huddly.com, mgr@pengutronix.de, Daniel Scally Subject: [PATCH v2 9/9] usb: gadget: uvc: Use custom strings if available Date: Mon, 21 Nov 2022 09:25:17 +0000 Message-Id: <20221121092517.225242-10-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221121092517.225242-1-dan.scally@ideasonboard.com> References: <20221121092517.225242-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org If the user has defined a custom string descriptor for the IAD or the VideoControl or VideoStreaming interfaces then set their index field to point to the custom descriptor instead of the hardcoded defaults. If no custom descriptors have been linked to, then use the default ones. Signed-off-by: Daniel Scally --- Changes in v2: - New patch drivers/usb/gadget/function/f_uvc.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 7821fe8f9120..ac6df8458cab 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -784,6 +784,10 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) list_for_each_entry(xu, &opts->extension_units, list) xu->desc.iExtension = cus[xu->string_descriptor_index].id; + /* + * We attach the hard-coded defaults incase the user does not provide + * any more appropriate strings through configfs. + */ 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)); @@ -791,11 +795,15 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) ret = PTR_ERR(us); goto error; } - uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id; - uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id; - ret = us[UVC_STRING_STREAMING_IDX].id; - uvc_streaming_intf_alt0.iInterface = ret; - uvc_streaming_intf_alt1.iInterface = ret; + + uvc_iad.iFunction = opts->iad_index ? cus[opts->iad_index].id : + us[UVC_STRING_CONTROL_IDX].id; + uvc_control_intf.iInterface = opts->iad_index ? cus[opts->iad_index].id : + us[UVC_STRING_CONTROL_IDX].id; + uvc_streaming_intf_alt0.iInterface = opts->vs0_index ? cus[opts->vs0_index].id : + us[UVC_STRING_STREAMING_IDX].id; + uvc_streaming_intf_alt1.iInterface = opts->vs1_index ? cus[opts->vs1_index].id : + us[UVC_STRING_STREAMING_IDX].id; /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0)