From patchwork Mon Jan 30 09:34:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120796 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 D44C3C54EAA for ; Mon, 30 Jan 2023 09:36:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236529AbjA3Jgd (ORCPT ); Mon, 30 Jan 2023 04:36:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41196 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236527AbjA3JgI (ORCPT ); Mon, 30 Jan 2023 04:36:08 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 959862DE78 for ; Mon, 30 Jan 2023 01:35:25 -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 CDD008B8; Mon, 30 Jan 2023 10:35:01 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071302; bh=r2iFMMvOVyXfvQMxewjf7qAFnWf6soSrG6ZqDcTyoM8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j5fg1LKnx76QwqfBo+zgtsTk3SK02QCn8zJNv03PFtsFw0Y6H7f73jTX2JtYRw8sG 3lnldROVNM1B78qA9P450Pd3vsZJrgTUQXN3AI4PcRyzacOFXjhGUn3lw0EiftXTMD OK5FzdCRFVELKroAtfitJIzIEYFtbNVvhHPzSzdc= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 01/11] usb: gadget: uvc: Make bSourceID read/write Date: Mon, 30 Jan 2023 09:34:33 +0000 Message-Id: <20230130093443.25644-2-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 v3: - None 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 | 59 ++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index f00cff6d8c5c..c25cc2823fc8 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 76cb60d13049..0a3095c0450b 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -483,11 +483,68 @@ 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 Jan 30 09:34:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120797 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 B07FBC636CC for ; Mon, 30 Jan 2023 09:36:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236187AbjA3Jgy (ORCPT ); Mon, 30 Jan 2023 04:36:54 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41716 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236287AbjA3Jgi (ORCPT ); Mon, 30 Jan 2023 04:36:38 -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 04B385B85 for ; Mon, 30 Jan 2023 01:35:59 -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 7FAB78CC; Mon, 30 Jan 2023 10:35:02 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071303; bh=swPU7HfqQBT0a6AzHMsYV304wwC3GgG+TFnrEx9fOeA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EiyrhsF/FT1rtsOp3rdW7OVza1damgo012GeH/QuFm7SVWN/IimLHTKDJNAdxR8wL eU7qrCWr6LOgSpMkf2iEbiFDz4xRiDW4Ts/VJM6lFFvxYRar15YC6tDxM1uFN1q2ZA p0cL9Tonc3gypwjT9E62hqfnkBnpvFsYZwvR9nWo= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 02/11] usb: gadget: uvc: Generalise helper functions for reuse Date: Mon, 30 Jan 2023 09:34:34 +0000 Message-Id: <20230130093443.25644-3-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 functions and move them higher in the file for better coverage. Signed-off-by: Daniel Scally --- Changes in v3 (Laurent): - Spelling - Renamed the "interv" variable to "values" - Switched to functions taking a size argument rather than a macro that duplicated the implementation. Changes in v2: - none drivers/usb/gadget/function/uvc_configfs.c | 120 ++++++++++++--------- 1 file changed, 67 insertions(+), 53 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 0a3095c0450b..cbb3a71a6351 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -46,6 +46,71 @@ static int uvcg_config_compare_u32(const void *l, const void *r) return li < ri ? -1 : li == ri ? 0 : 1; } +static inline int __uvcg_count_item_entries(char *buf, void *priv, unsigned int size) +{ + ++*((int *)priv); + return 0; +} + +static inline int __uvcg_fill_item_entries(char *buf, void *priv, unsigned int size) +{ + unsigned int num; + u8 **values; + int ret; + + ret = kstrtouint(buf, 0, &num); + if (ret) + return ret; + + if (num != (num & GENMASK((size * 8) - 1, 0))) + return -ERANGE; + + values = priv; + memcpy(*values, &num, size); + *values += size; + + return 0; +} + +static int __uvcg_iter_item_entries(const char *page, size_t len, + int (*fun)(char *, void *, unsigned int), + void *priv, unsigned int size) +{ + /* sign, base 2 representation, newline, terminator */ + unsigned int bufsize = 1 + size * 8 + 1 + 1; + const char *pg = page; + int i, ret = 0; + char *buf; + + if (!fun) + return -EINVAL; + + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + while (pg - page < len) { + i = 0; + while (i < sizeof(buf) && (pg - page < len) && + *pg != '\0' && *pg != '\n') + buf[i++] = *pg++; + if (i == sizeof(buf)) { + ret = -EINVAL; + goto out_free_buf; + } + while ((pg - page < len) && (*pg == '\0' || *pg == '\n')) + ++pg; + buf[i] = '\0'; + ret = fun(buf, priv, size); + if (ret) + goto out_free_buf; + } + +out_free_buf: + kfree(buf); + return ret; +} + struct uvcg_config_group_type { struct config_item_type type; const char *name; @@ -1188,57 +1253,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) { @@ -1262,7 +1276,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(page, len, __uvcg_count_item_entries, &n, sizeof(u32)); if (ret) goto end; @@ -1272,7 +1286,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(page, len, __uvcg_fill_item_entries, &tmp, sizeof(u32)); if (ret) { kfree(frm_intrv); goto end; From patchwork Mon Jan 30 09:34:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120798 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 78759C54EED for ; Mon, 30 Jan 2023 09:37:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236526AbjA3Jg6 (ORCPT ); Mon, 30 Jan 2023 04:36:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40584 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236320AbjA3Jgj (ORCPT ); Mon, 30 Jan 2023 04:36:39 -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 555911206E for ; Mon, 30 Jan 2023 01:36:02 -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 3439ED5F; Mon, 30 Jan 2023 10:35:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071303; bh=Y2lser9qc1pRBTrXdTZqTY+0dIM/6u9ODvSk67CMX1g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=d6EMVp3N9fsKTuGyatWDW9gP61aKiGhttZ7GfFL70GscFLDfHsA8rGH2UwrleRFCb IbZo7WXAUNUyaZQ3zsPr/jed4M/U1n6+ExuBF3epHIEK2V5BBgKqcoCq8ZCQiatCLv WQvTzyZfYzY8ro2YQ06tUWhO6zVS6oPdEhn/tbGc= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 03/11] usb: gadget: uvc: Allow definition of XUs in configfs Date: Mon, 30 Jan 2023 09:34:35 +0000 Message-Id: <20230130093443.25644-4-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 v3 (Laurent): - Spelling - Passed __GFP_ZERO to krealloc_array() instead of memsetting the new entries to zero afterwards. - Switched to using the bLength calculation macro instead of open coding the same thing - Fixed a return without mutex_unlock() 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 c25cc2823fc8..31636695f2d0 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 32f2c1645467..64a9e35ff075 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -852,6 +852,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; @@ -880,6 +887,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 cbb3a71a6351..a81613e9652d 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -661,6 +661,485 @@ static const struct uvcg_config_group_type uvcg_terminal_grp_type = { }, }; +/* ----------------------------------------------------------------------------- + * 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 | __GFP_ZERO); + if (!tmp_buf) { + ret = -ENOMEM; + goto unlock; + } + + xu->desc.baSourceID = tmp_buf; + xu->desc.bNrInPins = num; + xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(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 | __GFP_ZERO); + if (!tmp_buf) { + ret = -ENOMEM; + goto unlock; + } + + xu->desc.bmControls = tmp_buf; + xu->desc.bControlSize = num; + xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(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, *iter; + 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(page, len, __uvcg_count_item_entries, &n, + sizeof(u8)); + if (ret) + goto unlock; + + iter = source_ids = kcalloc(n, sizeof(u8), GFP_KERNEL); + if (!source_ids) { + ret = -ENOMEM; + goto unlock; + } + + ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &iter, + sizeof(u8)); + if (ret) { + kfree(source_ids); + goto unlock; + } + + kfree(xu->desc.baSourceID); + xu->desc.baSourceID = source_ids; + xu->desc.bNrInPins = n; + xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(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(page, len, __uvcg_count_item_entries, &n, + sizeof(u8)); + 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(page, len, __uvcg_fill_item_entries, &tmp, + sizeof(u8)); + if (ret) { + kfree(bm_controls); + goto unlock; + } + + kfree(xu->desc.bmControls); + xu->desc.bmControls = bm_controls; + xu->desc.bControlSize = n; + xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(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); + + 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.bNumControls = 0; + xu->desc.bNrInPins = 0; + xu->desc.baSourceID = NULL; + xu->desc.bControlSize = 0; + xu->desc.bmControls = NULL; + + mutex_lock(&opts->lock); + + xu->desc.bUnitID = ++opts->last_unit_id; + + 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} */ @@ -855,6 +1334,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 Jan 30 09:34:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120799 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 9E3BEC61DA4 for ; Mon, 30 Jan 2023 09:37:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236497AbjA3JhT (ORCPT ); Mon, 30 Jan 2023 04:37:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40136 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236363AbjA3JhA (ORCPT ); Mon, 30 Jan 2023 04:37:00 -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 E07EC2FCD9 for ; Mon, 30 Jan 2023 01:36:26 -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 E4CB7E68; Mon, 30 Jan 2023 10:35:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071304; bh=QYBftZwbivgbIVf8Zad9f80Eo9svBQlkOxUos4eorrs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=olrA5f7ZymCqf+8lp6Rs5J05fwSqWtKqWJYHEPmTUhl2mHzBEHw9zJkvRv2jqY4tn mTZjP0hSQLwSAMFt1/qHTeC/gGfgWUWN8sar9NBxvYoTguxs6IEmgB1XiWnQmn+btk zRpuN1ZvHP6w3ckj7SB1LFE/Xyq5/zGI/dCqD06g= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 04/11] usb: gadget: uvc: Copy XU descriptors during .bind() Date: Mon, 30 Jan 2023 09:34:36 +0000 Message-Id: <20230130093443.25644-5-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 --- Laurent - I didn't add your tag because having switched it to a function I actually decided I preferred it this way and switched it back; too many pointers to pointers of things made it less easy to follow I think, but if you disagree let me know and I'll go ahead. Changes in v3: - Dropped a local variable. Changes in v2: - none drivers/usb/gadget/function/f_uvc.c | 33 +++++++++++++++++++++++++++++ drivers/usb/gadget/function/uvc.h | 1 + 2 files changed, 34 insertions(+) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 64a9e35ff075..ca548974e5a0 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -474,6 +474,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) { @@ -485,6 +504,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; @@ -549,6 +569,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; @@ -575,6 +602,10 @@ 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) + UVC_COPY_XU_DESCRIPTOR(mem, dst, &xu->desc); + uvc_control_header->wTotalLength = cpu_to_le16(control_size); uvc_control_header->bInCollection = 1; uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; @@ -1020,6 +1051,8 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) return ERR_PTR(-EBUSY); } + uvc->desc.extension_units = &opts->extension_units; + ++opts->refcnt; mutex_unlock(&opts->lock); 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 Jan 30 09:34:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120800 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 D6A2CC54EED for ; Mon, 30 Jan 2023 09:37:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233090AbjA3Jhu (ORCPT ); Mon, 30 Jan 2023 04:37:50 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41192 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234878AbjA3Jhd (ORCPT ); Mon, 30 Jan 2023 04:37:33 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AC6B12B0AA for ; Mon, 30 Jan 2023 01:36:59 -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 A1075121A; Mon, 30 Jan 2023 10:35:04 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071305; bh=pegICoaCTjb8bnQP3K5BwXaEfXH2uU9fBd1WMAqGmJU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z+Ff4/L9oMD5t0guiFG9EGTs9t2z9pz+ZN+B0cHJZDzFC3E0bJfBT32irtKzKWE4o ulFzCVLDIed+nQujdO64MYFpTE2I4j9iusifnorAVp7vpsTIdkVrSGnve4bOUuqpFu TLJbapzK+460QLfS7TejW3vRXcntUp6o5M2lbr/s= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 05/11] usb: gadget: configfs: Rename struct gadget_strings Date: Mon, 30 Jan 2023 09:34:37 +0000 Message-Id: <20230130093443.25644-6-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org The struct gadget_strings really represents a single language in configfs. Rename it to make that more clear. Signed-off-by: Daniel Scally --- Changes in v3: - New patch drivers/usb/gadget/configfs.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 0853536cbf2e..e0f93c42cde6 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -79,7 +79,7 @@ static inline struct gadget_info *cfg_to_gadget_info(struct config_usb_cfg *cfg) return container_of(cfg->c.cdev, struct gadget_info, cdev); } -struct gadget_strings { +struct gadget_language { struct usb_gadget_strings stringtab_dev; struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; char *manufacturer; @@ -365,9 +365,9 @@ static struct configfs_attribute *gadget_root_attrs[] = { NULL, }; -static inline struct gadget_strings *to_gadget_strings(struct config_item *item) +static inline struct gadget_language *to_gadget_language(struct config_item *item) { - return container_of(to_config_group(item), struct gadget_strings, + return container_of(to_config_group(item), struct gadget_language, group); } @@ -755,20 +755,20 @@ static const struct config_item_type config_desc_type = { .ct_owner = THIS_MODULE, }; -GS_STRINGS_RW(gadget_strings, manufacturer); -GS_STRINGS_RW(gadget_strings, product); -GS_STRINGS_RW(gadget_strings, serialnumber); +GS_STRINGS_RW(gadget_language, manufacturer); +GS_STRINGS_RW(gadget_language, product); +GS_STRINGS_RW(gadget_language, serialnumber); -static struct configfs_attribute *gadget_strings_langid_attrs[] = { - &gadget_strings_attr_manufacturer, - &gadget_strings_attr_product, - &gadget_strings_attr_serialnumber, +static struct configfs_attribute *gadget_language_langid_attrs[] = { + &gadget_language_attr_manufacturer, + &gadget_language_attr_product, + &gadget_language_attr_serialnumber, NULL, }; -static void gadget_strings_attr_release(struct config_item *item) +static void gadget_language_attr_release(struct config_item *item) { - struct gadget_strings *gs = to_gadget_strings(item); + struct gadget_language *gs = to_gadget_language(item); kfree(gs->manufacturer); kfree(gs->product); @@ -778,8 +778,8 @@ static void gadget_strings_attr_release(struct config_item *item) kfree(gs); } -USB_CONFIG_STRING_RW_OPS(gadget_strings); -USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); +USB_CONFIG_STRING_RW_OPS(gadget_language); +USB_CONFIG_STRINGS_LANG(gadget_language, gadget_info); static inline struct gadget_info *os_desc_item_to_gadget_info( struct config_item *item) @@ -1316,7 +1316,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget, /* init all strings */ if (!list_empty(&gi->string_list)) { - struct gadget_strings *gs; + struct gadget_language *gs; i = 0; list_for_each_entry(gs, &gi->string_list, list) { @@ -1598,7 +1598,7 @@ static struct config_group *gadgets_make( configfs_add_default_group(&gi->configs_group, &gi->group); config_group_init_type_name(&gi->strings_group, "strings", - &gadget_strings_strings_type); + &gadget_language_strings_type); configfs_add_default_group(&gi->strings_group, &gi->group); config_group_init_type_name(&gi->os_desc_group, "os_desc", From patchwork Mon Jan 30 09:34:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120801 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 E1CF3C54EAA for ; Mon, 30 Jan 2023 09:38:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235583AbjA3JiM (ORCPT ); Mon, 30 Jan 2023 04:38:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44196 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236281AbjA3Jh4 (ORCPT ); Mon, 30 Jan 2023 04:37:56 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0C7604210 for ; Mon, 30 Jan 2023 01:37:28 -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 4F42C1802; Mon, 30 Jan 2023 10:35:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071305; bh=ZUWScFNRy+KPZS8awvny5eJ8KLrXuaGBXfH9458tiCM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S3iAyjAiAMT2BdrIADkDTT3C4BEAfBF77/lnheaX+CDQUKIFCoMXYNE/X7SV2sljw rwerRBWqwuqbx0wZ/o4or8z+m6m4cgud0YlKCQ4JEF3zgyfkVRJpaVp79zoKK8ccu1 pBEKF1gqB5I8Enx5A7TaQQVNIFAINkWg+FQ/tQFo= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 06/11] usb: gadget: configfs: Support arbitrary string descriptors Date: Mon, 30 Jan 2023 09:34:38 +0000 Message-Id: <20230130093443.25644-7-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Add a framework to allow users to define arbitrary string descriptors for a USB Gadget. This is modelled as a new type of config item rather than as hardcoded attributes so as to be as flexible as possible. Signed-off-by: Daniel Scally --- Changes in v3: - Moved this functionality from the UVC function to usb gadget core. Changes in v2: - New patch drivers/usb/gadget/configfs.c | 172 +++++++++++++++++++++++++++++++++- include/linux/usb/gadget.h | 11 +++ 2 files changed, 181 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index e0f93c42cde6..7c8b8ab5dfa3 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -88,6 +88,8 @@ struct gadget_language { struct config_group group; struct list_head list; + struct list_head gadget_strings; + unsigned int nstrings; }; struct gadget_config_name { @@ -778,8 +780,174 @@ static void gadget_language_attr_release(struct config_item *item) kfree(gs); } -USB_CONFIG_STRING_RW_OPS(gadget_language); -USB_CONFIG_STRINGS_LANG(gadget_language, gadget_info); +static struct configfs_item_operations gadget_language_langid_item_ops = { + .release = gadget_language_attr_release, +}; + +static ssize_t gadget_string_id_show(struct config_item *item, char *page) +{ + struct gadget_string *string = to_gadget_string(item); + int ret; + + ret = sprintf(page, "%u\n", string->usb_string.id); + return ret; +} +CONFIGFS_ATTR_RO(gadget_string_, id); + +static ssize_t gadget_string_s_show(struct config_item *item, char *page) +{ + struct gadget_string *string = to_gadget_string(item); + int ret; + + ret = snprintf(page, sizeof(string->string), "%s\n", string->string); + return ret; +} + +static ssize_t gadget_string_s_store(struct config_item *item, const char *page, + size_t len) +{ + struct gadget_string *string = to_gadget_string(item); + int size = min(sizeof(string->string), len + 1); + int ret; + + if (len > USB_MAX_STRING_LEN) + return -EINVAL; + + ret = strscpy(string->string, page, size); + return len; +} +CONFIGFS_ATTR(gadget_string_, s); + +static struct configfs_attribute *gadget_string_attrs[] = { + &gadget_string_attr_id, + &gadget_string_attr_s, + NULL, +}; + +static void gadget_string_release(struct config_item *item) +{ + struct gadget_string *string = to_gadget_string(item); + + kfree(string); +} + +static struct configfs_item_operations gadget_string_item_ops = { + .release = gadget_string_release, +}; + +static const struct config_item_type gadget_string_type = { + .ct_item_ops = &gadget_string_item_ops, + .ct_attrs = gadget_string_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *gadget_language_string_make(struct config_group *group, + const char *name) +{ + struct gadget_language *language; + struct gadget_string *string; + + language = to_gadget_language(&group->cg_item); + + string = kzalloc(sizeof(*string), GFP_KERNEL); + if (!string) + return ERR_PTR(-ENOMEM); + + string->usb_string.id = language->nstrings++; + string->usb_string.s = string->string; + list_add_tail(&string->list, &language->gadget_strings); + + config_item_init_type_name(&string->item, name, &gadget_string_type); + + return &string->item; +} + +static void gadget_language_string_drop(struct config_group *group, + struct config_item *item) +{ + struct gadget_language *language; + struct gadget_string *string; + unsigned int i = USB_GADGET_FIRST_AVAIL_IDX; + + language = to_gadget_language(&group->cg_item); + string = to_gadget_string(item); + + 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->gadget_strings, list) + string->usb_string.id = i++; +} + +static struct configfs_group_operations gadget_language_langid_group_ops = { + .make_item = gadget_language_string_make, + .drop_item = gadget_language_string_drop, +}; + +static struct config_item_type gadget_language_type = { + .ct_item_ops = &gadget_language_langid_item_ops, + .ct_group_ops = &gadget_language_langid_group_ops, + .ct_attrs = gadget_language_langid_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *gadget_language_make(struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct gadget_language *gs; + struct gadget_language *new; + int langs = 0; + int ret; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + ret = check_user_usb_string(name, &new->stringtab_dev); + if (ret) + goto err; + config_group_init_type_name(&new->group, name, + &gadget_language_type); + + gi = container_of(group, struct gadget_info, strings_group); + ret = -EEXIST; + list_for_each_entry(gs, &gi->string_list, list) { + if (gs->stringtab_dev.language == new->stringtab_dev.language) + goto err; + langs++; + } + ret = -EOVERFLOW; + if (langs >= MAX_USB_STRING_LANGS) + goto err; + + list_add_tail(&new->list, &gi->string_list); + INIT_LIST_HEAD(&new->gadget_strings); + + /* We have the default manufacturer, product and serialnumber strings */ + new->nstrings = 3; + return &new->group; +err: + kfree(new); + return ERR_PTR(ret); +} + +static void gadget_language_drop(struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations gadget_language_group_ops = { + .make_group = &gadget_language_make, + .drop_item = &gadget_language_drop, +}; + +static struct config_item_type gadget_language_strings_type = { + .ct_group_ops = &gadget_language_group_ops, + .ct_owner = THIS_MODULE, +}; static inline struct gadget_info *os_desc_item_to_gadget_info( struct config_item *item) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index dc3092cea99e..00750f7020f3 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -15,6 +15,7 @@ #ifndef __LINUX_USB_GADGET_H #define __LINUX_USB_GADGET_H +#include #include #include #include @@ -821,6 +822,16 @@ int usb_gadget_get_string(const struct usb_gadget_strings *table, int id, u8 *bu /* check if the given language identifier is valid */ bool usb_validate_langid(u16 langid); +struct gadget_string { + struct config_item item; + struct list_head list; + char string[USB_MAX_STRING_LEN]; + struct usb_string usb_string; +}; + +#define to_gadget_string(str_item)\ +container_of(str_item, struct gadget_string, item) + /*-------------------------------------------------------------------------*/ /* utility to simplify managing config descriptors */ From patchwork Mon Jan 30 09:34:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120802 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 CBD69C54EED for ; Mon, 30 Jan 2023 09:38:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234878AbjA3Ji2 (ORCPT ); Mon, 30 Jan 2023 04:38:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45106 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235037AbjA3JiY (ORCPT ); Mon, 30 Jan 2023 04:38:24 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CBE9793F0 for ; Mon, 30 Jan 2023 01:37:53 -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 07225185A; Mon, 30 Jan 2023 10:35:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071306; bh=OibA0AHJbaJNuFVSV7nGcAehjfs+FN4OjiDL0H8f0r4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iKpBc5imhbEuHPQ+JHMLEuOEHcW0eB3TI6+52Ziu2dr9j3V4uxtwsDlDlrznEcDTN e3IQqyvd7ifUZNZdyMFFUhs1d3H/qLkPEl/AR/4heDzkXXOVwzl57KTz94j69763Dg aeKYeX+Sa7f0+yUAnecYEOAUK68lixlPezfp0qgA= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 07/11] usb: gadget: configfs: Attach arbitrary strings to cdev Date: Mon, 30 Jan 2023 09:34:39 +0000 Message-Id: <20230130093443.25644-8-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Attach any arbitrary strings that are defined to the composite dev. We handle the old-style manufacturer, product and serialnumbers strings in the same function for simplicity. Signed-off-by: Daniel Scally --- Changes in v3: - Was 7/9 in version 2, moved the same functionality from the UVC function to usb gadget core. Changes in v2: - New patch drivers/usb/gadget/configfs.c | 95 ++++++++++++++++++++++++++++------- include/linux/usb/composite.h | 1 + 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 7c8b8ab5dfa3..c2f23a63ab10 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1441,6 +1441,80 @@ static void purge_configs_funcs(struct gadget_info *gi) } } +static struct usb_string * +configfs_attach_gadget_strings(struct gadget_info *gi) +{ + struct usb_gadget_strings **gadget_strings; + struct gadget_language *language; + struct gadget_string *string; + unsigned int nlangs = 0; + struct list_head *iter; + struct usb_string *us; + unsigned int i = 0; + int nstrings = -1; + unsigned int j; + + list_for_each(iter, &gi->string_list) + nlangs++; + + /* Bail out early if no languages are configured */ + if (!nlangs) + return NULL; + + gadget_strings = kcalloc(nlangs + 1, /* including NULL terminator */ + sizeof(struct usb_gadget_strings *), GFP_KERNEL); + if (!gadget_strings) + return ERR_PTR(-ENOMEM); + + list_for_each_entry(language, &gi->string_list, list) { + struct usb_string *stringtab; + + if (nstrings == -1) { + nstrings = language->nstrings; + } else if (nstrings != language->nstrings) { + pr_err("languages must contain the same number of strings\n"); + us = ERR_PTR(-EINVAL); + goto cleanup; + } + + stringtab = kcalloc(language->nstrings + 1, sizeof(struct usb_string), + GFP_KERNEL); + if (!stringtab) { + us = ERR_PTR(-ENOMEM); + goto cleanup; + } + + stringtab[USB_GADGET_MANUFACTURER_IDX].id = USB_GADGET_MANUFACTURER_IDX; + stringtab[USB_GADGET_MANUFACTURER_IDX].s = language->manufacturer; + stringtab[USB_GADGET_PRODUCT_IDX].id = USB_GADGET_PRODUCT_IDX; + stringtab[USB_GADGET_PRODUCT_IDX].s = language->product; + stringtab[USB_GADGET_SERIAL_IDX].id = USB_GADGET_SERIAL_IDX; + stringtab[USB_GADGET_SERIAL_IDX].s = language->serialnumber; + + j = USB_GADGET_FIRST_AVAIL_IDX; + list_for_each_entry(string, &language->gadget_strings, list) { + memcpy(&stringtab[j], &string->usb_string, sizeof(struct usb_string)); + j++; + } + + language->stringtab_dev.strings = stringtab; + gadget_strings[i] = &language->stringtab_dev; + i++; + } + + us = usb_gstrings_attach(&gi->cdev, gadget_strings, nstrings); + +cleanup: + list_for_each_entry(language, &gi->string_list, list) { + kfree(language->stringtab_dev.strings); + language->stringtab_dev.strings = NULL; + } + + kfree(gadget_strings); + + return us; +} + static int configfs_composite_bind(struct usb_gadget *gadget, struct usb_gadget_driver *gdriver) { @@ -1484,23 +1558,8 @@ static int configfs_composite_bind(struct usb_gadget *gadget, /* init all strings */ if (!list_empty(&gi->string_list)) { - struct gadget_language *gs; - - i = 0; - list_for_each_entry(gs, &gi->string_list, list) { - - gi->gstrings[i] = &gs->stringtab_dev; - gs->stringtab_dev.strings = gs->strings; - gs->strings[USB_GADGET_MANUFACTURER_IDX].s = - gs->manufacturer; - gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; - gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; - i++; - } - gi->gstrings[i] = NULL; - s = usb_gstrings_attach(&gi->cdev, gi->gstrings, - USB_GADGET_FIRST_AVAIL_IDX); - if (IS_ERR(s)) { + s = configfs_attach_gadget_strings(gi); + if (IS_ERR_OR_NULL(s)) { ret = PTR_ERR(s); goto err_comp_cleanup; } @@ -1508,6 +1567,8 @@ static int configfs_composite_bind(struct usb_gadget *gadget, gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; + + gi->cdev.usb_strings = s; } if (gi->use_os_desc) { diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 43ac3fa760db..85c7f6036933 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -483,6 +483,7 @@ struct usb_composite_dev { struct usb_composite_driver *driver; u8 next_string_id; char *def_manufacturer; + struct usb_string *usb_strings; /* the gadget driver won't enable the data pullup * while the deactivation count is nonzero. From patchwork Mon Jan 30 09:34:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120803 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 99BFAC54EAA for ; Mon, 30 Jan 2023 09:38:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235349AbjA3Jih (ORCPT ); Mon, 30 Jan 2023 04:38:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45458 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231455AbjA3Jif (ORCPT ); Mon, 30 Jan 2023 04:38:35 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2E6761164F for ; Mon, 30 Jan 2023 01:38:03 -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 AB2E4192D; Mon, 30 Jan 2023 10:35:06 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071307; bh=3VjcWjQDoupM5MTqgg81P+flMEsJDKLq1YR0103cqew=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=stGba8gCQevgbOh0hImDHbVadtyN+weR2y5UUc1eNm7IiNXNKmuzTDzniZSgzQnYr ERfZCMeJbKEA+kk9g9C+XcmO0f8OP4R017z67STiStpkCiNuHuSawbZ5g8c9KWnHT2 edfXqWgC3CnJoampAo7EtqdwNnqPQ8ODDQyxvBBE= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 08/11] usb: gadget: uvc: Allow linking XUs to string descriptors Date: Mon, 30 Jan 2023 09:34:40 +0000 Message-Id: <20230130093443.25644-9-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 v3: - Changed the target of the links to in /strings Changes in v2: - New patch drivers/usb/gadget/function/uvc_configfs.c | 52 ++++++++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 1 + 2 files changed, 53 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index a81613e9652d..9da3b784f954 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1063,8 +1063,60 @@ 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 *gadget_item; + struct gadget_string *string; + struct config_item *strings; + int ret = 0; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + /* Validate that the target of the link is an entry in strings/ */ + gadget_item = src->ci_parent->ci_parent->ci_parent->ci_parent->ci_parent; + strings = config_group_find_item(to_config_group(gadget_item), "strings"); + if (!strings || tgt->ci_parent->ci_parent != strings) { + ret = -EINVAL; + goto put_strings; + } + + string = to_gadget_string(tgt); + xu->string_descriptor_index = string->usb_string.id; + +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 c9a4182fb26f..bf188080034c 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -153,6 +153,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 Jan 30 09:34:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120810 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 A6712C54EED for ; Mon, 30 Jan 2023 09:39:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235991AbjA3JjK (ORCPT ); Mon, 30 Jan 2023 04:39:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45998 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236293AbjA3JjH (ORCPT ); Mon, 30 Jan 2023 04:39:07 -0500 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E1D315576 for ; Mon, 30 Jan 2023 01:38: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 6166A1943; Mon, 30 Jan 2023 10:35:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071307; bh=nNA2Xm+u5Y5+pZWBXV0x9IAva2SW4wBan/cSw3YGP2c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=D4Y/IFekoQ3VNvZFBp5zBDblDZS9zw8nopi8Jz5nXu7uOHIJwluppJI1BHd/KGvcd l/GsH6+c8wK5YOScPdKwvwsKBrzv9pDJo071Q49oty1RwYxsoY0VsG3Z7Mg+7/maQI J48xKdd8hWlSYVRhmdUpRZzN5TVf9v5xfepe0NlM= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 09/11] usb: gadget: uvc: Pick up custom string descriptor IDs Date: Mon, 30 Jan 2023 09:34:41 +0000 Message-Id: <20230130093443.25644-10-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org If any custom string descriptors have been linked to from the extension unit, pick up the string ID that was returned when the strings were attached to the composite dev and use it to set the iExtension field of the Extension Unit Descriptor. Signed-off-by: Daniel Scally --- Changes in v3: - New patch drivers/usb/gadget/function/f_uvc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index ca548974e5a0..642ab210abb1 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -634,6 +634,7 @@ 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 uvcg_extension *xu; struct usb_string *us; unsigned int max_packet_mult; unsigned int max_packet_size; @@ -722,6 +723,14 @@ 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; + /* + * XUs can have an arbitrary string descriptor describing them. If they + * have one pick up the ID. + */ + list_for_each_entry(xu, &opts->extension_units, list) + if (xu->string_descriptor_index) + xu->desc.iExtension = cdev->usb_strings[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 Jan 30 09:34:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120809 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 7CD29C54EAA for ; Mon, 30 Jan 2023 09:39:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235595AbjA3JjI (ORCPT ); Mon, 30 Jan 2023 04:39:08 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45946 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236046AbjA3JjH (ORCPT ); Mon, 30 Jan 2023 04:39:07 -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 A36C11167D for ; Mon, 30 Jan 2023 01:38:38 -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 1EFD5196A; Mon, 30 Jan 2023 10:35:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071308; bh=nfLpNBoxvKPWJmQVI5sriCXz0TxUC0aTyatB097X70g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RuCyhRyL6PTyw2yuuudaZXotAUa9JZWOXo5+zfRWPNtAiNGgctEnolpV3ZvJzHmxS Fhl0p2IFIDZioQB9EesIRImY0obH4zAiO9ypwRT1XYz0SbTbflUJkl+ttHu19tTduf 8xiZATZaZtIZGp8hJ7SPFBJOqu1JFREk0wtosBEc= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 10/11] usb: gadget: uvc: Allow linking function to string descs Date: Mon, 30 Jan 2023 09:34:42 +0000 Message-Id: <20230130093443.25644-11-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 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 v3: - Changed target of link to be within /strings - Dropped the VideoControl description since there's an attribute for that now Changes in v2: - New patch drivers/usb/gadget/function/u_uvc.h | 8 +++ drivers/usb/gadget/function/uvc_configfs.c | 60 ++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 5119cfe5ee4e..565c14fb3d73 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -81,6 +81,14 @@ struct f_uvc_opts { struct uvc_descriptor_header **uvc_hs_streaming_cls; struct uvc_descriptor_header **uvc_ss_streaming_cls; + /* + * 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 9da3b784f954..ffa338643cc8 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -2901,8 +2901,68 @@ 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 gadget_string *string; + struct config_item *strings; + 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->ci_parent->ci_parent), + "strings"); + if (!strings || tgt->ci_parent->ci_parent != strings) { + ret = -EINVAL; + goto put_strings; + } + + string = to_gadget_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 Jan 30 09:34:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 13120811 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 9F62CC54EED for ; Mon, 30 Jan 2023 09:39:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236045AbjA3Jjj (ORCPT ); Mon, 30 Jan 2023 04:39:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46662 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236118AbjA3Jjh (ORCPT ); Mon, 30 Jan 2023 04:39:37 -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 875E112864 for ; Mon, 30 Jan 2023 01:39:10 -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 C2CCE1B95; Mon, 30 Jan 2023 10:35:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1675071309; bh=P92nEko+pSY5vJqt1k9b9W6otxAi+gfn0g8zGEOgD4A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ucw/I23MrPC/B7s8fBqBDlZmlh0rBAd4gj+DXH0XA/vcbYcjZf1na6iWs8CbGDncq YARxHZa0UQcx+kShldqaM0uKIbibgFsP8rK33ScN6XnmxjtZek1pQn6K1PR5sPPzsW 6yls2DwuNc7QR0zSJ7H2VZk7o4Y/oIPk4jzojGb8= From: Daniel Scally To: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org, laurent.pinchart@ideasonboard.com Cc: mgr@pengutronix.de, balbi@kernel.org, kieran.bingham@ideasonboard.com, torleiv@huddly.com, stern@rowland.harvard.edu, Daniel Scally Subject: [PATCH v3 11/11] usb: gadget: uvc: Use custom strings if available Date: Mon, 30 Jan 2023 09:34:43 +0000 Message-Id: <20230130093443.25644-12-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130093443.25644-1-dan.scally@ideasonboard.com> References: <20230130093443.25644-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 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 v3: - Dropped the VideoControl description since there's an attribute for that now 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 642ab210abb1..7bce5e5f226a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -731,6 +731,10 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (xu->string_descriptor_index) xu->desc.iExtension = cdev->usb_strings[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)); @@ -738,11 +742,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 ? cdev->usb_strings[opts->iad_index].id : + us[UVC_STRING_CONTROL_IDX].id; + uvc_streaming_intf_alt0.iInterface = opts->vs0_index ? + cdev->usb_strings[opts->vs0_index].id : + us[UVC_STRING_STREAMING_IDX].id; + uvc_streaming_intf_alt1.iInterface = opts->vs1_index ? + cdev->usb_strings[opts->vs1_index].id : + us[UVC_STRING_STREAMING_IDX].id; /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0)