UPSTREAM: usb: gadget: uvc: add v4l2 enumeration api calls
This patch adds support to the v4l2 VIDIOCs for enum_format, enum_framesizes and enum_frameintervals. This way, the userspace application can use these VIDIOCS to query the via configfs exported frame capabilities. With thes callbacks the userspace doesn't have to bring its own configfs parser. Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Link: https://lore.kernel.org/r/20220909221335.15033-4-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> (cherry picked from commit 588b9e85609bcb2f84a2be83591480aa943943b6) Bug: 259171206 Change-Id: I26bf3a509158da6f521c2e62076ef014cf0235b8 Signed-off-by: Avichal Rakesh <arakesh@google.com> (cherry picked from commit 55c758d518b810a1f9d7026372f3965c1a75db24)
This commit is contained in:
committed by
Treehugger Robot
parent
4aa53ed66b
commit
5f385970be
@@ -888,6 +888,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);
|
||||
}
|
||||
@@ -945,6 +946,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)
|
||||
@@ -977,6 +979,29 @@ 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)
|
||||
goto err_config;
|
||||
|
||||
header = config_group_find_item(to_config_group(streaming), "header");
|
||||
config_item_put(streaming);
|
||||
if (!header)
|
||||
goto err_config;
|
||||
|
||||
h = config_group_find_item(to_config_group(header), "h");
|
||||
config_item_put(header);
|
||||
if (!h)
|
||||
goto err_config;
|
||||
|
||||
uvc->header = to_uvcg_streaming_header(h);
|
||||
config_item_put(h);
|
||||
if (!uvc->header->linked) {
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(uvc);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
@@ -992,6 +1017,11 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
|
||||
uvc->func.bind_deactivated = true;
|
||||
|
||||
return &uvc->func;
|
||||
|
||||
err_config:
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(uvc);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
|
||||
|
||||
@@ -134,6 +134,8 @@ struct uvc_device {
|
||||
bool func_connected;
|
||||
wait_queue_head_t func_connected_queue;
|
||||
|
||||
struct uvcg_streaming_header *header;
|
||||
|
||||
/* Descriptors */
|
||||
struct {
|
||||
const struct uvc_descriptor_header * const *fs_control;
|
||||
|
||||
@@ -18,12 +18,92 @@
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-uvc.h>
|
||||
|
||||
#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 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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Requests handling
|
||||
@@ -134,6 +214,99 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
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 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 (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 uvcg_format *uformat = NULL;
|
||||
struct uvcg_frame *uframe = NULL;
|
||||
|
||||
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_format(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_format_desc *fmtdesc;
|
||||
struct uvcg_format *uformat;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -300,6 +473,9 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
|
||||
.vidioc_querycap = uvc_v4l2_querycap,
|
||||
.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_format,
|
||||
.vidioc_reqbufs = uvc_v4l2_reqbufs,
|
||||
.vidioc_querybuf = uvc_v4l2_querybuf,
|
||||
.vidioc_qbuf = uvc_v4l2_qbuf,
|
||||
|
||||
Reference in New Issue
Block a user