From patchwork Tue Mar 15 14:33:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Grzeschik X-Patchwork-Id: 551565 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 66AFCC433F5 for ; Tue, 15 Mar 2022 14:34:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349295AbiCOOfW (ORCPT ); Tue, 15 Mar 2022 10:35:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44100 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349244AbiCOOfN (ORCPT ); Tue, 15 Mar 2022 10:35:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5824738180 for ; Tue, 15 Mar 2022 07:34:01 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nU8FL-0004wn-H1; Tue, 15 Mar 2022 15:33:59 +0100 Received: from mgr by dude.hi.pengutronix.de with local (Exim 4.94.2) (envelope-from ) id 1nU8FK-00GRow-KK; Tue, 15 Mar 2022 15:33:58 +0100 From: Michael Grzeschik To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, laurent.pinchart@ideasonboard.com, paul.elder@ideasonboard.com, kernel@pengutronix.de, nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com Subject: [PATCH v7 3/7] usb: gadget: uvc: prevent index variables to start from 0 Date: Tue, 15 Mar 2022 15:33:52 +0100 Message-Id: <20220315143356.3919911-4-m.grzeschik@pengutronix.de> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> References: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: mgr@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-usb@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Some configfs variables like bDefaultFrameIndex are always starting by 1. This patch adds a check to prevent setting those variables to 0. Signed-off-by: Michael Grzeschik --- v1 -> v7: - drivers/usb/gadget/function/uvc_configfs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 77d64031aa9c2a..b6bb5706299951 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1565,6 +1565,12 @@ uvcg_uncompressed_##cname##_store(struct config_item *item, \ if (ret) \ goto end; \ \ + /* index values in uvc are never 0 */ \ + if (!num) { \ + ret = -EINVAL; \ + goto end; \ + } \ + \ u->desc.aname = num; \ ret = len; \ end: \ @@ -1758,6 +1764,12 @@ uvcg_mjpeg_##cname##_store(struct config_item *item, \ if (ret) \ goto end; \ \ + /* index values in uvc are never 0 */ \ + if (!num) { \ + ret = -EINVAL; \ + goto end; \ + } \ + \ u->desc.aname = num; \ ret = len; \ end: \ From patchwork Tue Mar 15 14:33:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Grzeschik X-Patchwork-Id: 551564 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 AEC34C433F5 for ; Tue, 15 Mar 2022 14:34:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349264AbiCOOfY (ORCPT ); Tue, 15 Mar 2022 10:35:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44724 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241770AbiCOOfV (ORCPT ); Tue, 15 Mar 2022 10:35:21 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5834C381B7 for ; Tue, 15 Mar 2022 07:34:01 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nU8FL-0004wo-H4; Tue, 15 Mar 2022 15:33:59 +0100 Received: from mgr by dude.hi.pengutronix.de with local (Exim 4.94.2) (envelope-from ) id 1nU8FK-00GRoz-Kv; Tue, 15 Mar 2022 15:33:58 +0100 From: Michael Grzeschik To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, laurent.pinchart@ideasonboard.com, paul.elder@ideasonboard.com, kernel@pengutronix.de, nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com Subject: [PATCH v7 4/7] usb: gadget: uvc: move structs to common header Date: Tue, 15 Mar 2022 15:33:53 +0100 Message-Id: <20220315143356.3919911-5-m.grzeschik@pengutronix.de> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> References: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: mgr@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-usb@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org The functions and structs of the configfs interface should also be used by the uvc gadget driver. This patch prepares the stack by moving the common structs and functions to the common header file. Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Michael Grzeschik --- v1 -> v2: - moved uvcg_attach_configfs declaration to end of header file - moved header uvc_configfs to beginning of uvc_configfs.c - simplified call of to_uvcg_uncompressed - simplified call of to_uvcg_mjpeg - moved uvc_configfs.h to the beginning to show that the header is self-contained v2 -> v3: - fixed include of uvcg_format_names back to uvc_configfs.c v3 -> v7: - drivers/usb/gadget/function/f_uvc.c | 1 - drivers/usb/gadget/function/uvc_configfs.c | 111 +------------------- drivers/usb/gadget/function/uvc_configfs.h | 114 ++++++++++++++++++++- 3 files changed, 115 insertions(+), 111 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 71bb5e477dbad7..37fdf8b3495a48 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -24,7 +24,6 @@ #include #include -#include "u_uvc.h" #include "uvc.h" #include "uvc_configfs.h" #include "uvc_v4l2.h" diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index b6bb5706299951..05dc5764020006 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -10,17 +10,14 @@ * Author: Andrzej Pietrasiewicz */ -#include - -#include "u_uvc.h" #include "uvc_configfs.h" +#include + /* ----------------------------------------------------------------------------- * Global Utility Structures and Macros */ -#define UVCG_STREAMING_CONTROL_SIZE 1 - #define UVC_ATTR(prefix, cname, aname) \ static struct configfs_attribute prefix##attr_##cname = { \ .ca_name = __stringify(aname), \ @@ -49,12 +46,6 @@ static int uvcg_config_compare_u32(const void *l, const void *r) return li < ri ? -1 : li == ri ? 0 : 1; } -static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) -{ - return container_of(to_config_group(item), struct f_uvc_opts, - func_inst.group); -} - struct uvcg_config_group_type { struct config_item_type type; const char *name; @@ -125,19 +116,6 @@ static void uvcg_config_remove_children(struct config_group *group) * control/header */ -DECLARE_UVC_HEADER_DESCRIPTOR(1); - -struct uvcg_control_header { - struct config_item item; - struct UVC_HEADER_DESCRIPTOR(1) desc; - unsigned linked; -}; - -static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) -{ - return container_of(item, struct uvcg_control_header, item); -} - #define UVCG_CTRL_HDR_ATTR(cname, aname, bits, limit) \ static ssize_t uvcg_control_header_##cname##_show( \ struct config_item *item, char *page) \ @@ -769,24 +747,6 @@ static const char * const uvcg_format_names[] = { "mjpeg", }; -enum uvcg_format_type { - UVCG_UNCOMPRESSED = 0, - UVCG_MJPEG, -}; - -struct uvcg_format { - struct config_group group; - enum uvcg_format_type type; - unsigned linked; - unsigned num_frames; - __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; -}; - -static struct uvcg_format *to_uvcg_format(struct config_item *item) -{ - return container_of(to_config_group(item), struct uvcg_format, group); -} - static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page) { struct f_uvc_opts *opts; @@ -845,29 +805,11 @@ static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch, return ret; } -struct uvcg_format_ptr { - struct uvcg_format *fmt; - struct list_head entry; -}; - /* ----------------------------------------------------------------------------- * streaming/header/ * streaming/header */ -struct uvcg_streaming_header { - struct config_item item; - struct uvc_input_header_descriptor desc; - unsigned linked; - struct list_head formats; - unsigned num_fmt; -}; - -static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) -{ - return container_of(item, struct uvcg_streaming_header, item); -} - static void uvcg_format_set_indices(struct config_group *fmt); static int uvcg_streaming_header_allow_link(struct config_item *src, @@ -1059,31 +1001,6 @@ static const struct uvcg_config_group_type uvcg_streaming_header_grp_type = { * streaming/// */ -struct uvcg_frame { - struct config_item item; - enum uvcg_format_type fmt_type; - struct { - u8 b_length; - u8 b_descriptor_type; - u8 b_descriptor_subtype; - u8 b_frame_index; - u8 bm_capabilities; - u16 w_width; - u16 w_height; - u32 dw_min_bit_rate; - u32 dw_max_bit_rate; - u32 dw_max_video_frame_buffer_size; - u32 dw_default_frame_interval; - u8 b_frame_interval_type; - } __attribute__((packed)) frame; - u32 *dw_frame_interval; -}; - -static struct uvcg_frame *to_uvcg_frame(struct config_item *item) -{ - return container_of(item, struct uvcg_frame, item); -} - #define UVCG_FRAME_ATTR(cname, aname, bits) \ static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\ { \ @@ -1420,18 +1337,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) * streaming/uncompressed/ */ -struct uvcg_uncompressed { - struct uvcg_format fmt; - struct uvc_format_uncompressed desc; -}; - -static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) -{ - return container_of( - container_of(to_config_group(item), struct uvcg_format, group), - struct uvcg_uncompressed, fmt); -} - static struct configfs_group_operations uvcg_uncompressed_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, @@ -1675,18 +1580,6 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = { * streaming/mjpeg/ */ -struct uvcg_mjpeg { - struct uvcg_format fmt; - struct uvc_format_mjpeg desc; -}; - -static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) -{ - return container_of( - container_of(to_config_group(item), struct uvcg_format, group), - struct uvcg_mjpeg, fmt); -} - static struct configfs_group_operations uvcg_mjpeg_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index 7e1d7ca29bf210..1ec8529ff1e4fe 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -12,7 +12,119 @@ #ifndef UVC_CONFIGFS_H #define UVC_CONFIGFS_H -struct f_uvc_opts; +#include + +#include "u_uvc.h" + +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uvc_opts, + func_inst.group); +} + +#define UVCG_STREAMING_CONTROL_SIZE 1 + +DECLARE_UVC_HEADER_DESCRIPTOR(1); + +struct uvcg_control_header { + struct config_item item; + struct UVC_HEADER_DESCRIPTOR(1) desc; + unsigned linked; +}; + +static inline struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) +{ + return container_of(item, struct uvcg_control_header, item); +} + +enum uvcg_format_type { + UVCG_UNCOMPRESSED = 0, + UVCG_MJPEG, +}; + +struct uvcg_format { + struct config_group group; + enum uvcg_format_type type; + unsigned linked; + unsigned num_frames; + __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; +}; + +struct uvcg_format_ptr { + struct uvcg_format *fmt; + struct list_head entry; +}; + +static inline struct uvcg_format *to_uvcg_format(struct config_item *item) +{ + return container_of(to_config_group(item), struct uvcg_format, group); +} + +struct uvcg_streaming_header { + struct config_item item; + struct uvc_input_header_descriptor desc; + unsigned linked; + struct list_head formats; + unsigned num_fmt; +}; + +static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) +{ + return container_of(item, struct uvcg_streaming_header, item); +} + +struct uvcg_frame { + struct config_item item; + enum uvcg_format_type fmt_type; + struct { + u8 b_length; + u8 b_descriptor_type; + u8 b_descriptor_subtype; + u8 b_frame_index; + u8 bm_capabilities; + u16 w_width; + u16 w_height; + u32 dw_min_bit_rate; + u32 dw_max_bit_rate; + u32 dw_max_video_frame_buffer_size; + u32 dw_default_frame_interval; + u8 b_frame_interval_type; + } __attribute__((packed)) frame; + u32 *dw_frame_interval; +}; + +static inline struct uvcg_frame *to_uvcg_frame(struct config_item *item) +{ + return container_of(item, struct uvcg_frame, item); +} + +/* ----------------------------------------------------------------------------- + * streaming/uncompressed/ + */ + +struct uvcg_uncompressed { + struct uvcg_format fmt; + struct uvc_format_uncompressed desc; +}; + +static inline struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_uncompressed, fmt); +} + +/* ----------------------------------------------------------------------------- + * streaming/mjpeg/ + */ + +struct uvcg_mjpeg { + struct uvcg_format fmt; + struct uvc_format_mjpeg desc; +}; + +static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); +} int uvcg_attach_configfs(struct f_uvc_opts *opts); From patchwork Tue Mar 15 14:33:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Grzeschik X-Patchwork-Id: 551563 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 051AEC4332F for ; Tue, 15 Mar 2022 14:34:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349291AbiCOOf0 (ORCPT ); Tue, 15 Mar 2022 10:35:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44744 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349180AbiCOOfV (ORCPT ); Tue, 15 Mar 2022 10:35:21 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 57BA336326 for ; Tue, 15 Mar 2022 07:34:01 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nU8FL-0004wp-H3; Tue, 15 Mar 2022 15:33:59 +0100 Received: from mgr by dude.hi.pengutronix.de with local (Exim 4.94.2) (envelope-from ) id 1nU8FK-00GRp2-Ld; Tue, 15 Mar 2022 15:33:58 +0100 From: Michael Grzeschik To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, laurent.pinchart@ideasonboard.com, paul.elder@ideasonboard.com, kernel@pengutronix.de, nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com Subject: [PATCH v7 5/7] usb: gadget: uvc: track frames in format entries Date: Tue, 15 Mar 2022 15:33:54 +0100 Message-Id: <20220315143356.3919911-6-m.grzeschik@pengutronix.de> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> References: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: mgr@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-usb@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Just like the header is tracking the formats in a linked list, in this patch we track the frames in a linked list of the formats. It simplifies the parsing of the configfs structure. Signed-off-by: Michael Grzeschik --- v1 -> v7: - drivers/usb/gadget/function/uvc_configfs.c | 25 +++++++++++++++++++++- drivers/usb/gadget/function/uvc_configfs.h | 6 ++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 05dc5764020006..41c1876921bb65 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1262,6 +1262,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, struct uvcg_format *fmt; struct f_uvc_opts *opts; struct config_item *opts_item; + struct uvcg_frame_ptr *frame_ptr; h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) @@ -1292,6 +1293,16 @@ static struct config_item *uvcg_frame_make(struct config_group *group, kfree(h); return ERR_PTR(-EINVAL); } + + frame_ptr = kzalloc(sizeof(*frame_ptr), GFP_KERNEL); + if (!frame_ptr) { + mutex_unlock(&opts->lock); + kfree(h); + return ERR_PTR(-ENOMEM); + } + + frame_ptr->frm = h; + list_add_tail(&frame_ptr->entry, &fmt->frames); ++fmt->num_frames; mutex_unlock(&opts->lock); @@ -1305,13 +1316,23 @@ static void uvcg_frame_drop(struct config_group *group, struct config_item *item struct uvcg_format *fmt; struct f_uvc_opts *opts; struct config_item *opts_item; + struct uvcg_frame *target_frm = NULL; + struct uvcg_frame_ptr *frame_ptr, *tmp; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); mutex_lock(&opts->lock); + target_frm = container_of(item, struct uvcg_frame, item); fmt = to_uvcg_format(&group->cg_item); - --fmt->num_frames; + + list_for_each_entry_safe(frame_ptr, tmp, &fmt->frames, entry) + if (frame_ptr->frm == target_frm) { + list_del(&frame_ptr->entry); + kfree(frame_ptr); + --fmt->num_frames; + break; + } mutex_unlock(&opts->lock); config_item_put(item); @@ -1556,6 +1577,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, h->desc.bmInterfaceFlags = 0; h->desc.bCopyProtect = 0; + INIT_LIST_HEAD(&h->fmt.frames); h->fmt.type = UVCG_UNCOMPRESSED; config_group_init_type_name(&h->fmt.group, name, &uvcg_uncompressed_type); @@ -1736,6 +1758,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, h->desc.bmInterfaceFlags = 0; h->desc.bCopyProtect = 0; + INIT_LIST_HEAD(&h->fmt.frames); h->fmt.type = UVCG_MJPEG; config_group_init_type_name(&h->fmt.group, name, &uvcg_mjpeg_type); diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index 1ec8529ff1e4fe..ad2ec8c4c78c35 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -46,6 +46,7 @@ struct uvcg_format { struct config_group group; enum uvcg_format_type type; unsigned linked; + struct list_head frames; unsigned num_frames; __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; }; @@ -73,6 +74,11 @@ static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct conf return container_of(item, struct uvcg_streaming_header, item); } +struct uvcg_frame_ptr { + struct uvcg_frame *frm; + struct list_head entry; +}; + struct uvcg_frame { struct config_item item; enum uvcg_format_type fmt_type; From patchwork Tue Mar 15 14:33:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Grzeschik X-Patchwork-Id: 551562 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 9E41DC43217 for ; Tue, 15 Mar 2022 14:34:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349310AbiCOOf3 (ORCPT ); Tue, 15 Mar 2022 10:35:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349281AbiCOOfW (ORCPT ); Tue, 15 Mar 2022 10:35:22 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5818C36B75 for ; Tue, 15 Mar 2022 07:34:01 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nU8FL-0004wq-HE; Tue, 15 Mar 2022 15:33:59 +0100 Received: from mgr by dude.hi.pengutronix.de with local (Exim 4.94.2) (envelope-from ) id 1nU8FK-00GRp5-MI; Tue, 15 Mar 2022 15:33:58 +0100 From: Michael Grzeschik To: linux-usb@vger.kernel.org Cc: balbi@kernel.org, laurent.pinchart@ideasonboard.com, paul.elder@ideasonboard.com, kernel@pengutronix.de, nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com Subject: [PATCH v7 6/7] usb: gadget: uvc: add VIDIOC function Date: Tue, 15 Mar 2022 15:33:55 +0100 Message-Id: <20220315143356.3919911-7-m.grzeschik@pengutronix.de> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> References: <20220315143356.3919911-1-m.grzeschik@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: mgr@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-usb@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org This patch adds support to the v4l2 VIDIOC for enum_format, enum_framesizes, enum_frameintervals and try_fmt. The linked/active configfs userspace setup is used in the v4l2 interface functions. With the v4l2 vidiocontrols the userspace can use the v4l2 api to negotiate and allocate the data. It doesn't have to bring its own configfs parser. Also it only needs to be extended with subscription for streamon, streamoff, connect and disconnect for stream handling and become able to serve the uvc device. Signed-off-by: Michael Grzeschik --- v1 -> v2: - fixed indentation of find_frame/format_by_index - fixed function name find_frm_by_size to find_frame_by_size - fixed indentation of _uvc_v4l2_try_fmt - fixed indentation in uvc_v4l2_enum_frameintervals - removed unneeded declaration of uvc_v4l2_get_bytesperline in uvc_v4l2.h - checked return values on config_group_find_item, handling refcount - fixed sizeof using variables instead of types - removed unsused def_format variable - wrting grp, hdr, fmt and frm in full - added proper ival handling - removed analyze_configfs function - added linked list of frames to uvcg_format - added functon find_frame_by_index v2 -> v3: - fixed usage of u_uvc.h - removed unused variable i in _try_fmt - made uvc_v4l2_get_bytesperline static v3 -> v4: - conditionally return current or all frames/formats/frameintervals on enum - dropped setting format and frame with set_format - combined try and set format function to one call v4 -> v5: - fixed uninitialized return values reported by kernel test robot - added local video variable to uvc_v4l2_enum_frameintervals v5 -> v6: - v6 -> v7: - fixed unlocking in f_uvc uvc_alloc function - add uvc_get_frame_size function for sizeimage calculation - add fallback to frame.dw_max_video_frame_buffer_size drivers/usb/gadget/function/f_uvc.c | 33 +++ drivers/usb/gadget/function/uvc.h | 20 +- drivers/usb/gadget/function/uvc_queue.c | 3 +- drivers/usb/gadget/function/uvc_v4l2.c | 346 +++++++++++++++++++++--- drivers/usb/gadget/function/uvc_video.c | 61 ++++- 5 files changed, 412 insertions(+), 51 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 37fdf8b3495a48..64f20fe6967099 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -322,6 +322,8 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->video.ep) usb_ep_disable(uvc->video.ep); + uvc->streamon = 0; + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; v4l2_event_queue(&uvc->vdev, &v4l2_event); @@ -345,6 +347,8 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) return ret; usb_ep_enable(uvc->video.ep); + uvc->streamon = 1; + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(&uvc->vdev, &v4l2_event); @@ -880,6 +884,7 @@ static void uvc_free(struct usb_function *f) struct uvc_device *uvc = to_uvc(f); struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, func_inst); + config_item_put(&uvc->header->item); --opts->refcnt; kfree(uvc); } @@ -907,6 +912,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) struct uvc_device *uvc; struct f_uvc_opts *opts; struct uvc_descriptor_header **strm_cls; + struct config_item *streaming, *header, *h; uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); if (uvc == NULL) @@ -938,6 +944,33 @@ 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; + + streaming = config_group_find_item(&opts->func_inst.group, "streaming"); + if (!streaming) { + config_item_put(streaming); + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); + } + header = config_group_find_item(to_config_group(streaming), "header"); + config_item_put(streaming); + if (!header) { + config_item_put(header); + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); + } + h = config_group_find_item(to_config_group(header), "h"); + config_item_put(header); + if (!h) { + config_item_put(h); + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); + } + uvc->header = to_uvcg_streaming_header(h); + if (!uvc->header->linked) { + mutex_unlock(&opts->lock); + return ERR_PTR(-EBUSY); + } + ++opts->refcnt; mutex_unlock(&opts->lock); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index c3607a32b98624..5a6e3e493d0630 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -88,11 +88,10 @@ struct uvc_video { struct work_struct pump; /* Frame parameters */ - u8 bpp; - u32 fcc; - unsigned int width; - unsigned int height; - unsigned int imagesize; + struct uvcg_format *cur_format; + struct uvcg_frame *cur_frame; + unsigned int cur_ival; + struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; @@ -130,6 +129,8 @@ struct uvc_device { struct uvc_video video; bool func_connected; + struct uvcg_streaming_header *header; + /* Descriptors */ struct { const struct uvc_descriptor_header * const *fs_control; @@ -139,6 +140,8 @@ struct uvc_device { const struct uvc_descriptor_header * const *ss_streaming; } desc; + bool streamon; + unsigned int control_intf; struct usb_ep *control_ep; struct usb_request *control_req; @@ -175,4 +178,11 @@ extern void uvc_endpoint_stream(struct uvc_device *dev); extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); +extern int uvc_get_frame_size(struct uvc_video *video); +extern struct uvcg_format *find_format_by_index(struct uvc_device *uvc, + int index); +extern struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, + struct uvcg_format *uformat, + int index); + #endif /* _UVC_GADGET_H_ */ diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index d852ac9e47e72c..d6f97788859f32 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -21,6 +21,7 @@ #include #include "uvc.h" +#include "uvc_configfs.h" /* ------------------------------------------------------------------------ * Video buffers queue management. @@ -51,7 +52,7 @@ static int uvc_queue_setup(struct vb2_queue *vq, *nplanes = 1; - sizes[0] = video->imagesize; + sizes[0] = uvc_get_frame_size(video); if (cdev->gadget->speed < USB_SPEED_SUPER) video->uvc_num_requests = 4; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index a2c78690c5c288..7ec63e929496fd 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -18,12 +18,161 @@ #include #include #include +#include #include "f_uvc.h" #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" #include "uvc_v4l2.h" +#include "uvc_configfs.h" + +static struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) +{ + char guid[16] = UVC_GUID_FORMAT_MJPEG; + struct uvc_format_desc *format; + struct uvcg_uncompressed *unc; + + if (uformat->type == UVCG_UNCOMPRESSED) { + unc = to_uvcg_uncompressed(&uformat->group.cg_item); + if (!unc) + return ERR_PTR(-EINVAL); + + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); + } + + format = uvc_format_by_guid(guid); + if (!format) + return ERR_PTR(-EINVAL); + + return format; +} + +static int uvc_v4l2_get_bytesperline(struct uvcg_format *uformat, + struct uvcg_frame *uframe) +{ + struct uvcg_uncompressed *u; + + if (uformat->type == UVCG_UNCOMPRESSED) { + u = to_uvcg_uncompressed(&uformat->group.cg_item); + if (!u) + return 0; + + return u->desc.bBitsPerPixel * uframe->frame.w_width / 8; + } + + return 0; +} + +int uvc_get_frame_size(struct uvc_video *video) +{ + unsigned int bpl = uvc_v4l2_get_bytesperline(video->cur_format, + video->cur_frame); + + return bpl ? bpl * video->cur_frame->frame.w_height : + video->cur_frame->frame.dw_max_video_frame_buffer_size; +} + +struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index) +{ + struct uvcg_format_ptr *format; + struct uvcg_format *uformat = NULL; + int i = 1; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (index == i) { + uformat = format->fmt; + break; + } + i++; + } + + return uformat; +} + +struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, + struct uvcg_format *uformat, + int index) +{ + struct uvcg_format_ptr *format; + struct uvcg_frame_ptr *frame; + struct uvcg_frame *uframe = NULL; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (format->fmt->type != uformat->type) + continue; + list_for_each_entry(frame, &format->fmt->frames, entry) { + if (index == frame->frm->frame.b_frame_index) { + uframe = frame->frm; + break; + } + } + } + + return uframe; +} + +static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, + u32 pixelformat) +{ + struct uvcg_format_ptr *format; + struct uvcg_format *uformat = NULL; + + list_for_each_entry(format, &uvc->header->formats, entry) { + struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); + + if (fmtdesc->fcc == pixelformat) { + uformat = format->fmt; + break; + } + } + + return uformat; +} + +static struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc, + struct uvcg_format *uformat, + u16 rw, u16 rh) +{ + struct uvc_video *video = &uvc->video; + struct uvcg_format_ptr *format; + struct uvcg_frame_ptr *frame; + struct uvcg_frame *uframe = NULL; + unsigned int d, maxd; + + /* Find the closest image size. The distance between image sizes is + * the size in pixels of the non-overlapping regions between the + * requested size and the frame-specified size. + */ + maxd = (unsigned int)-1; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (format->fmt->type != uformat->type) + continue; + + list_for_each_entry(frame, &format->fmt->frames, entry) { + u16 w, h; + + w = frame->frm->frame.w_width; + h = frame->frm->frame.w_height; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + uframe = frame->frm; + } + + if (maxd == 0) + break; + } + } + + if (!uframe) + uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh); + + return uframe; +} /* -------------------------------------------------------------------------- * Requests handling @@ -50,16 +199,6 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) * V4L2 ioctls */ -struct uvc_format { - u8 bpp; - u32 fcc; -}; - -static struct uvc_format uvc_formats[] = { - { 16, V4L2_PIX_FMT_YUYV }, - { 0, V4L2_PIX_FMT_MJPEG }, -}; - static int uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { @@ -80,13 +219,17 @@ uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; + struct uvc_format_desc *fmtdesc; + + fmtdesc = to_uvc_format(video->cur_format); - fmt->fmt.pix.pixelformat = video->fcc; - fmt->fmt.pix.width = video->width; - fmt->fmt.pix.height = video->height; + fmt->fmt.pix.pixelformat = fmtdesc->fcc; + fmt->fmt.pix.width = video->cur_frame->frame.w_width; + fmt->fmt.pix.height = video->cur_frame->frame.w_height; fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = video->bpp * video->width / 8; - fmt->fmt.pix.sizeimage = video->imagesize; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(video->cur_format, + video->cur_frame); + fmt->fmt.pix.sizeimage = uvc_get_frame_size(video); fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; fmt->fmt.pix.priv = 0; @@ -94,46 +237,165 @@ uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) } static int -uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) +uvc_v4l2_try_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; - struct uvc_format *format; - unsigned int imagesize; - unsigned int bpl; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) { - format = &uvc_formats[i]; - if (format->fcc == fmt->fmt.pix.pixelformat) - break; - } + struct uvcg_format *uformat; + struct uvcg_frame *uframe; + u8 *fcc; - if (i == ARRAY_SIZE(uvc_formats)) { - uvcg_info(&uvc->func, "Unsupported format 0x%08x.\n", - fmt->fmt.pix.pixelformat); + if (fmt->type != video->queue.queue.type) return -EINVAL; - } - bpl = format->bpp * fmt->fmt.pix.width / 8; - imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage; + fcc = (u8 *)&fmt->fmt.pix.pixelformat; + uvcg_dbg(&uvc->func, "Trying format 0x%08x (%c%c%c%c): %ux%u\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); + + uformat = find_format_by_pix(uvc, fmt->fmt.pix.pixelformat); + if (!uformat) + return -EINVAL; - video->fcc = format->fcc; - video->bpp = format->bpp; - video->width = fmt->fmt.pix.width; - video->height = fmt->fmt.pix.height; - video->imagesize = imagesize; + uframe = find_closest_frame_by_size(uvc, uformat, + fmt->fmt.pix.width, fmt->fmt.pix.height); + if (!uframe) + return -EINVAL; + fmt->fmt.pix.width = uframe->frame.w_width; + fmt->fmt.pix.height = uframe->frame.w_height; fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = bpl; - fmt->fmt.pix.sizeimage = imagesize; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe); + fmt->fmt.pix.sizeimage = uvc_get_frame_size(video); + fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; fmt->fmt.pix.priv = 0; return 0; } +static int +uvc_v4l2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct uvcg_format *uformat = NULL; + struct uvcg_frame *uframe = NULL; + struct uvcg_frame_ptr *frame; + + uformat = find_format_by_pix(uvc, fival->pixel_format); + if (!uformat) + return -EINVAL; + + list_for_each_entry(frame, &uformat->frames, entry) { + if (frame->frm->frame.w_width == fival->width && + frame->frm->frame.w_height == fival->height) { + uframe = frame->frm; + break; + } + } + if (!uframe) + return -EINVAL; + + if (uvc->streamon) { + if (fival->index >= 1) + return -EINVAL; + + fival->discrete.numerator = + uframe->dw_frame_interval[video->cur_ival - 1]; + } else { + if (fival->index >= uframe->frame.b_frame_interval_type) + return -EINVAL; + + fival->discrete.numerator = + uframe->dw_frame_interval[fival->index]; + } + + /* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */ + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.denominator = 10000000; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + return 0; +} + +static int +uvc_v4l2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct uvcg_format *uformat = NULL; + struct uvcg_frame *uframe = NULL; + + if (uvc->streamon) { + if (fsize->index >= 1) + return -EINVAL; + + uformat = video->cur_format; + uframe = video->cur_frame; + } else { + uformat = find_format_by_pix(uvc, fsize->pixel_format); + if (!uformat) + return -EINVAL; + + if (fsize->index >= uformat->num_frames) + return -EINVAL; + + uframe = find_frame_by_index(uvc, uformat, fsize->index + 1); + if (!uframe) + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = uframe->frame.w_width; + fsize->discrete.height = uframe->frame.w_height; + + return 0; +} + +static int +uvc_v4l2_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct uvc_format_desc *fmtdesc; + struct uvcg_format *uformat; + + if (uvc->streamon) { + if (f->index >= 1) + return -EINVAL; + + uformat = video->cur_format; + } else { + if (f->index >= uvc->header->num_fmt) + return -EINVAL; + + uformat = find_format_by_index(uvc, f->index + 1); + if (!uformat) + return -EINVAL; + + } + + if (uformat->type != UVCG_UNCOMPRESSED) + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + + fmtdesc = to_uvc_format(uformat); + f->pixelformat = fmtdesc->fcc; + + strscpy(f->description, fmtdesc->name, sizeof(f->description)); + f->description[strlen(fmtdesc->name) - 1] = 0; + + return 0; +} + static int uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { @@ -297,8 +559,12 @@ uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_querycap = uvc_v4l2_querycap, + .vidioc_try_fmt_vid_out = uvc_v4l2_try_set_fmt, .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, - .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, + .vidioc_s_fmt_vid_out = uvc_v4l2_try_set_fmt, + .vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals, + .vidioc_enum_framesizes = uvc_v4l2_enum_framesizes, + .vidioc_enum_fmt_vid_out = uvc_v4l2_enum_fmt, .vidioc_reqbufs = uvc_v4l2_reqbufs, .vidioc_querybuf = uvc_v4l2_querybuf, .vidioc_qbuf = uvc_v4l2_qbuf, diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 7f59a0c4740209..9fb2b9c575ed34 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -19,6 +19,7 @@ #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" +#include "uvc_configfs.h" /* -------------------------------------------------------------------------- * Video codecs @@ -474,21 +475,71 @@ int uvcg_video_enable(struct uvc_video *video, int enable) return ret; } +static int uvc_frame_default(struct uvcg_format *uformat) +{ + struct uvcg_uncompressed *u; + struct uvcg_mjpeg *m; + + switch (uformat->type) { + case UVCG_UNCOMPRESSED: + u = to_uvcg_uncompressed(&uformat->group.cg_item); + if (u) + return u->desc.bDefaultFrameIndex; + break; + case UVCG_MJPEG: + m = to_uvcg_mjpeg(&uformat->group.cg_item); + if (m) + return m->desc.bDefaultFrameIndex; + break; + } + + return 0; +} + +static int uvc_default_frame_interval(struct uvc_video *video) +{ + int i; + + for (i = 0; i < video->cur_frame->frame.b_frame_interval_type; i++) { + if (video->cur_frame->frame.dw_default_frame_interval == + video->cur_frame->dw_frame_interval[i]) { + video->cur_ival = i + 1; + return i + 1; + } + } + + /* fallback */ + return 1; +} + /* * Initialize the UVC video stream. */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + int iframe; + INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); + if (list_empty(&uvc->header->formats)) + return -EINVAL; + video->uvc = uvc; - video->fcc = V4L2_PIX_FMT_YUYV; - video->bpp = 16; - video->width = 320; - video->height = 240; - video->imagesize = 320 * 240 * 2; + video->cur_format = find_format_by_index(uvc, 1); + if (!video->cur_format) + return -EINVAL; + + iframe = uvc_frame_default(video->cur_format); + if (!iframe) + return -EINVAL; + + video->cur_frame = find_frame_by_index(uvc, video->cur_format, iframe); + if (!video->cur_frame) + return -EINVAL; + + video->cur_ival = uvc_default_frame_interval(video); /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,