From patchwork Sun May 30 22:22:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Grzeschik X-Patchwork-Id: 450442 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EA4D5C47093 for ; Sun, 30 May 2021 22:22:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CBC9960BBB for ; Sun, 30 May 2021 22:22:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229933AbhE3WYc (ORCPT ); Sun, 30 May 2021 18:24:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55710 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229891AbhE3WY3 (ORCPT ); Sun, 30 May 2021 18:24:29 -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 599EDC061760 for ; Sun, 30 May 2021 15:22:50 -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 1lnTpW-0000Et-36; Mon, 31 May 2021 00:22:46 +0200 Received: from mgr by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lnTpV-0002cg-8M; Mon, 31 May 2021 00:22:45 +0200 From: Michael Grzeschik Cc: linux-usb@vger.kernel.org, laurent.pinchart@ideasonboard.com, caleb.connolly@ideasonboard.com, paul.elder@ideasonboard.com, balbi@kernel.org, kernel@pengutronix.de Subject: [PATCH 1/3] usb: gadget: uvc: move structs to common header Date: Mon, 31 May 2021 00:22:37 +0200 Message-Id: <20210530222239.8793-2-m.grzeschik@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210530222239.8793-1-m.grzeschik@pengutronix.de> References: <20210530222239.8793-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 To: unlisted-recipients:; (no To-header on input) 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. Signed-off-by: Michael Grzeschik Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart --- drivers/usb/gadget/function/uvc_configfs.c | 111 ------------------- drivers/usb/gadget/function/uvc_configfs.h | 119 +++++++++++++++++++++ 2 files changed, 119 insertions(+), 111 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 77d64031aa9c2..86463bb2639ed 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -19,8 +19,6 @@ * 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 +47,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 +117,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) \ @@ -764,29 +743,6 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { * streaming/mjpeg */ -static const char * const uvcg_format_names[] = { - "uncompressed", - "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 +801,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 +997,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 +1333,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, @@ -1669,18 +1570,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 7e1d7ca29bf21..f905d29570eb4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -16,4 +16,123 @@ struct f_uvc_opts; int uvcg_attach_configfs(struct f_uvc_opts *opts); +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); +} + +static const char * const uvcg_format_names[] = { + "uncompressed", + "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]; +}; + +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( + container_of(to_config_group(item), struct uvcg_format, group), + 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( + container_of(to_config_group(item), struct uvcg_format, group), + struct uvcg_mjpeg, fmt); +} + #endif /* UVC_CONFIGFS_H */ From patchwork Sun May 30 22:22:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Grzeschik X-Patchwork-Id: 450443 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D5DF2C47092 for ; Sun, 30 May 2021 22:22:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AB8B661205 for ; Sun, 30 May 2021 22:22:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229917AbhE3WYb (ORCPT ); Sun, 30 May 2021 18:24:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55704 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229873AbhE3WY3 (ORCPT ); Sun, 30 May 2021 18:24:29 -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 4BC51C061574 for ; Sun, 30 May 2021 15:22:50 -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 1lnTpW-0000Eu-36; Mon, 31 May 2021 00:22:46 +0200 Received: from mgr by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lnTpV-0002cj-8r; Mon, 31 May 2021 00:22:45 +0200 From: Michael Grzeschik Cc: linux-usb@vger.kernel.org, laurent.pinchart@ideasonboard.com, caleb.connolly@ideasonboard.com, paul.elder@ideasonboard.com, balbi@kernel.org, kernel@pengutronix.de Subject: [PATCH 2/3] usb: gadget: uvc: add VIDIOC function Date: Mon, 31 May 2021 00:22:38 +0200 Message-Id: <20210530222239.8793-3-m.grzeschik@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210530222239.8793-1-m.grzeschik@pengutronix.de> References: <20210530222239.8793-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 To: unlisted-recipients:; (no To-header on input) 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 configfs userspace setup gets parsed and this configfs data is used in the v4l2 interface functions. Signed-off-by: Michael Grzeschik --- drivers/usb/gadget/function/f_uvc.c | 54 ++++ drivers/usb/gadget/function/uvc.h | 18 +- drivers/usb/gadget/function/uvc_configfs.c | 5 + drivers/usb/gadget/function/uvc_configfs.h | 2 + drivers/usb/gadget/function/uvc_queue.c | 4 +- drivers/usb/gadget/function/uvc_v4l2.c | 325 ++++++++++++++++++--- drivers/usb/gadget/function/uvc_v4l2.h | 1 + drivers/usb/gadget/function/uvc_video.c | 10 +- 8 files changed, 369 insertions(+), 50 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f48a00e497945..7945ea93a775a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -410,6 +410,44 @@ static ssize_t function_name_show(struct device *dev, static DEVICE_ATTR_RO(function_name); +static int +uvc_analyze_configfs(struct uvc_device *uvc) +{ + struct uvcg_streaming_header *src_hdr = uvc->h; + struct config_item *item; + struct config_group *grp; + struct uvcg_format_ptr *f; + int i = 0, j = 0; + + if (!src_hdr->linked) + return -EBUSY; + + list_for_each_entry(f, &src_hdr->formats, entry) + uvc->nframes += f->fmt->num_frames; + + uvc->nformats = src_hdr->num_fmt; + + uvc->frm = kcalloc(uvc->nframes, sizeof(struct uvcg_frame *), GFP_KERNEL); + if (!uvc->frm) + return -ENOMEM; + + uvc->fmt = kcalloc(uvc->nformats, sizeof(struct uvcg_format *), GFP_KERNEL); + if (!uvc->fmt) { + kfree(uvc->frm); + return -ENOMEM; + } + + list_for_each_entry(f, &src_hdr->formats, entry) { + uvc->fmt[i++] = f->fmt; + grp = &f->fmt->group; + list_for_each_entry(item, &grp->cg_children, ci_entry) { + uvc->frm[j++] = to_uvcg_frame(item); + } + } + + return 0; +} + static int uvc_register_video(struct uvc_device *uvc) { @@ -742,6 +780,13 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) goto error; } + /* Register configfs formats and frames. */ + ret = uvc_analyze_configfs(uvc); + if (ret < 0) { + uvcg_err(f, "failed to read configfs\n"); + goto v4l2_error; + } + /* Initialise video. */ ret = uvcg_video_init(&uvc->video, uvc); if (ret < 0) @@ -905,6 +950,8 @@ 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 *item; + struct config_group *grp; uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); if (uvc == NULL) @@ -936,6 +983,13 @@ 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; + + grp = &opts->func_inst.group; + item = config_group_find_item(grp, "streaming"); + item = config_group_find_item(to_config_group(item), "header"); + item = config_group_find_item(to_config_group(item), "h"); + uvc->h = to_uvcg_streaming_header(item); + ++opts->refcnt; mutex_unlock(&opts->lock); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 23ee25383c1f7..62d7420a25666 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -80,11 +80,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 *def_format; + struct uvcg_format *cur_format; + struct uvcg_frame *cur_frame; + struct mutex mutex; /* protects frame parameters */ /* Requests */ @@ -118,6 +117,14 @@ struct uvc_device { struct usb_function func; struct uvc_video video; + struct uvcg_streaming_header *h; + + struct uvcg_frame **frm; + int nframes; + + struct uvcg_format **fmt; + int nformats; + /* Descriptors */ struct { const struct uvc_descriptor_header * const *fs_control; @@ -162,4 +169,5 @@ 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_frame_default(struct uvcg_format *ufmt); #endif /* _UVC_GADGET_H_ */ diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 86463bb2639ed..009c80d0e1780 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -11,6 +11,7 @@ */ #include +#include #include "u_uvc.h" #include "uvc_configfs.h" @@ -1547,6 +1548,8 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, h->desc.bCopyProtect = 0; h->fmt.type = UVCG_UNCOMPRESSED; + h->fmt.fcc = V4L2_PIX_FMT_YUYV; + h->fmt.name = "YUV 4:2:2 (YUYV)"; config_group_init_type_name(&h->fmt.group, name, &uvcg_uncompressed_type); @@ -1721,6 +1724,8 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, h->desc.bCopyProtect = 0; h->fmt.type = UVCG_MJPEG; + h->fmt.fcc = V4L2_PIX_FMT_MJPEG; + h->fmt.name = "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 f905d29570eb4..8ed966275f838 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -52,6 +52,8 @@ struct uvcg_format { enum uvcg_format_type type; unsigned linked; unsigned num_frames; + char *name; + u32 fcc; __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; }; diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 61e2c94cc0b0c..6afc4b79adfe9 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -20,6 +20,8 @@ #include #include "uvc.h" +#include "u_uvc.h" +#include "uvc_configfs.h" /* ------------------------------------------------------------------------ * Video buffers queue management. @@ -49,7 +51,7 @@ static int uvc_queue_setup(struct vb2_queue *vq, *nplanes = 1; - sizes[0] = video->imagesize; + sizes[0] = video->cur_frame->frame.dw_max_video_frame_buffer_size; return 0; } diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 4ca89eab61590..83830b8864a6e 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -24,6 +24,127 @@ #include "uvc_queue.h" #include "uvc_video.h" #include "uvc_v4l2.h" +#include "u_uvc.h" +#include "uvc_configfs.h" + +u32 uvc_v4l2_get_bytesperline(struct uvcg_format *fmt, struct uvcg_frame *frm) +{ + struct uvcg_uncompressed *u; + + switch (fmt->type) { + case UVCG_UNCOMPRESSED: + u = to_uvcg_uncompressed(&fmt->group.cg_item); + if (!u) + return 0; + + return u->desc.bBitsPerPixel * frm->frame.w_width / 8; + case UVCG_MJPEG: + return frm->frame.w_width; + } + + return 0; +} + +struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, + struct uvcg_format *ufmt, + int index) +{ + int i; + + for (i = 0; i < uvc->nframes; i++) { + if (uvc->frm[i]->fmt_type != ufmt->type) + continue; + + if (index == uvc->frm[i]->frame.b_frame_index) + break; + } + + if (i == uvc->nframes) + return NULL; + + return uvc->frm[i]; +} + +static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, + u32 pixelformat) +{ + int i; + + for (i = 0; i < uvc->nformats; i++) + if (uvc->fmt[i]->fcc == pixelformat) + break; + + if (i == uvc->nformats) + return NULL; + + return uvc->fmt[i]; +} + +int uvc_frame_default(struct uvcg_format *ufmt) +{ + struct uvcg_uncompressed *m; + struct uvcg_uncompressed *u; + int ret = 1; + + switch (ufmt->type) { + case UVCG_UNCOMPRESSED: + u = to_uvcg_uncompressed(&ufmt->group.cg_item); + if (u) + ret = u->desc.bDefaultFrameIndex; + break; + case UVCG_MJPEG: + m = to_uvcg_uncompressed(&ufmt->group.cg_item); + if (m) + ret = m->desc.bDefaultFrameIndex; + break; + } + + if (!ret) + ret = 1; + + return ret; +} + +static struct uvcg_frame *find_frm_by_size(struct uvc_device *uvc, + struct uvcg_format *ufmt, + u16 rw, u16 rh) +{ + struct uvc_video *video = &uvc->video; + struct uvcg_frame *ufrm = NULL; + unsigned int d, maxd; + int i; + + /* 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; + + for (i = 0; i < uvc->nframes; i++) { + u16 w, h; + + if (uvc->frm[i]->fmt_type != ufmt->type) + continue; + + w = uvc->frm[i]->frame.w_width; + h = uvc->frm[i]->frame.w_height; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + ufrm = uvc->frm[i]; + } + + if (maxd == 0) + break; + } + + if (ufrm == NULL) + uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh); + + return ufrm; +} /* -------------------------------------------------------------------------- * Requests handling @@ -50,16 +171,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) { @@ -81,55 +192,187 @@ uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; - fmt->fmt.pix.pixelformat = video->fcc; - fmt->fmt.pix.width = video->width; - fmt->fmt.pix.height = video->height; + fmt->fmt.pix.pixelformat = video->cur_format->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 = uvc_v4l2_get_bytesperline(video->cur_format, video->cur_frame); + fmt->fmt.pix.sizeimage = video->cur_frame->frame.dw_max_video_frame_buffer_size; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int _uvc_v4l2_try_fmt(struct uvc_video *video, + struct v4l2_format *fmt, struct uvcg_format **uvc_format, struct uvcg_frame **uvc_frame) +{ + struct uvc_device *uvc = video->uvc; + struct uvcg_format *ufmt; + struct uvcg_frame *ufrm; + u8 *fcc; + int i; + + if (fmt->type != video->queue.queue.type) + return -EINVAL; + + 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); + + for (i = 0; i < uvc->nformats; i++) + if (uvc->fmt[i]->fcc == fmt->fmt.pix.pixelformat) + break; + + if (i == uvc->nformats) + ufmt = video->def_format; + + ufmt = uvc->fmt[i]; + + ufrm = find_frm_by_size(uvc, ufmt, + fmt->fmt.pix.width, fmt->fmt.pix.height); + if (!ufrm) + return -EINVAL; + + fmt->fmt.pix.width = ufrm->frame.w_width; + fmt->fmt.pix.height = ufrm->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(ufmt, ufrm); + fmt->fmt.pix.sizeimage = ufrm->frame.dw_max_video_frame_buffer_size; + fmt->fmt.pix.pixelformat = ufmt->fcc; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; fmt->fmt.pix.priv = 0; + if (!fmt->fmt.pix.sizeimage && fmt->fmt.pix.bytesperline) + fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * + fmt->fmt.pix.height; + + if (uvc_format != NULL) + *uvc_format = ufmt; + if (uvc_frame != NULL) + *uvc_frame = ufrm; + return 0; } +static int +uvc_v4l2_try_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; + + return _uvc_v4l2_try_fmt(video, fmt, NULL, NULL); +} + static int uvc_v4l2_set_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 *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) + struct uvcg_format *ufmt; + struct uvcg_frame *ufrm; + int ret; + + ret = _uvc_v4l2_try_fmt(video, fmt, &ufmt, &ufrm); + if (ret) + return ret; + + video->cur_format = ufmt; + video->cur_frame = ufrm; + + return ret; +} + +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 uvcg_format *ufmt = NULL; + struct uvcg_frame *ufrm = NULL; + int i; + + ufmt = find_format_by_pix(uvc, fival->pixel_format); + if (!ufmt) + return -EINVAL; + + for (i = 0; i < uvc->nframes; ++i) { + if (uvc->frm[i]->fmt_type != ufmt->type) + continue; + + if (uvc->frm[i]->frame.w_width == fival->width && + uvc->frm[i]->frame.w_height == fival->height) { + ufrm = uvc->frm[i]; break; + } } + if (!ufrm) + return -EINVAL; - if (i == ARRAY_SIZE(uvc_formats)) { - uvcg_info(&uvc->func, "Unsupported format 0x%08x.\n", - fmt->fmt.pix.pixelformat); + if (fival->index >= ufrm->frame.b_frame_interval_type) return -EINVAL; - } - bpl = format->bpp * fmt->fmt.pix.width / 8; - imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage; + /* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */ + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = ufrm->dw_frame_interval[fival->index]; + fival->discrete.denominator = 10000000; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); - video->fcc = format->fcc; - video->bpp = format->bpp; - video->width = fmt->fmt.pix.width; - video->height = fmt->fmt.pix.height; - video->imagesize = imagesize; + return 0; +} - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = bpl; - fmt->fmt.pix.sizeimage = imagesize; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - fmt->fmt.pix.priv = 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 uvcg_format *ufmt = NULL; + struct uvcg_frame *ufrm = NULL; + + ufmt = find_format_by_pix(uvc, fsize->pixel_format); + if (!ufmt) + return -EINVAL; + + if (fsize->index >= ufmt->num_frames) + return -EINVAL; + + ufrm = find_frame_by_index(uvc, ufmt, fsize->index + 1); + if (!ufrm) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = ufrm->frame.w_width; + fsize->discrete.height = ufrm->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 uvcg_format *ufmt; + + if (f->index >= uvc->nformats) + return -EINVAL; + + ufmt = uvc->fmt[f->index]; + if (!ufmt) + return -EINVAL; + + f->pixelformat = ufmt->fcc; + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + + strscpy(f->description, ufmt->name, sizeof(f->description)); + f->description[sizeof(f->description) - 1] = 0; return 0; } @@ -258,8 +501,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_fmt, .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, + .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_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h index 1576005b61fd3..6e45103bbf793 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.h +++ b/drivers/usb/gadget/function/uvc_v4l2.h @@ -15,5 +15,6 @@ extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops; extern const struct v4l2_file_operations uvc_v4l2_fops; +extern u32 uvc_v4l2_get_bytesperline(struct uvcg_format *fmt, struct uvcg_frame *frm); #endif /* __UVC_V4L2_H__ */ diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 633e23d58d868..b14780bddd838 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -17,7 +17,10 @@ #include "uvc.h" #include "uvc_queue.h" +#include "uvc_v4l2.h" #include "uvc_video.h" +#include "u_uvc.h" +#include "uvc_configfs.h" /* -------------------------------------------------------------------------- * Video codecs @@ -348,11 +351,8 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) INIT_WORK(&video->pump, uvcg_video_pump); video->uvc = uvc; - video->fcc = V4L2_PIX_FMT_YUYV; - video->bpp = 16; - video->width = 320; - video->height = 240; - video->imagesize = 320 * 240 * 2; + video->def_format = video->cur_format = uvc->fmt[0]; + video->cur_frame = uvc->frm[uvc_frame_default(video->def_format) - 1]; /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT,