On Wed, Nov 17, 2021 at 01:24:35PM +0100, Michael Grzeschik wrote:
The Hostside format selection is currently only done in userspace, as the events for SET_CUR and GET_CUR are allways moved to the application layer. Since the v4l2 device parses the configfs data, the format negotiation can be done in the kernel. This patch adds the functions to set the current configuration while continuing to forward all unknown events to the userspace level. Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx> --- v1 -> v2: - fixed the commit message - changed pr_debug to pr_err in events_process_data - aligned many indentations - simplified uvc_events_process_data - fixed uvc_fill_streaming_control calls in uvcg_video_init - added setup_subcribed to decide if userspace takes over on EOPNOTSUPP - added data_subscribed to decide if userspace takes over on EOPNOTSUPP - removed duplicate send_response - wrting fmt and frm in full v2 -> v3: - added find_format_index to set the right probe drivers/usb/gadget/function/f_uvc.c | 232 +++++++++++++++++++++++- drivers/usb/gadget/function/uvc.h | 18 ++ drivers/usb/gadget/function/uvc_v4l2.c | 52 +++++- drivers/usb/gadget/function/uvc_video.c | 12 +- 4 files changed, 305 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f1fd44e6062980..62fef6e002271a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -16,7 +16,6 @@ #include <linux/string.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -#include <linux/usb/g_uvc.h> #include <linux/usb/video.h> #include <linux/vmalloc.h> #include <linux/wait.h> @@ -200,16 +199,228 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = { * Control requests */ +void uvc_fill_streaming_control(struct uvc_device *uvc, + struct uvc_streaming_control *ctrl, + int iframe, int iformat, unsigned int ival) +{ + struct uvcg_format *uformat; + struct uvcg_frame *uframe; + + /* Restrict the iformat, iframe and ival to valid values. Negative + * values for ifrmat and iframe will result in the maximum valid value + * being selected + */ + iformat = clamp((unsigned int)iformat, 1U, + (unsigned int)uvc->header->num_fmt); + uformat = find_format_by_index(uvc, iformat); + if (!uformat) + return; + + iframe = clamp((unsigned int)iframe, 1U, + (unsigned int)uformat->num_frames); + uframe = find_frame_by_index(uvc, uformat, iframe); + if (!uframe) + return; + + ival = clamp((unsigned int)ival, 1U, + (unsigned int)uframe->frame.b_frame_interval_type); + if (!uframe->dw_frame_interval[ival - 1]) + return; + + memset(ctrl, 0, sizeof(*ctrl)); + + ctrl->bmHint = 1; + ctrl->bFormatIndex = iformat; + ctrl->bFrameIndex = iframe; + ctrl->dwFrameInterval = uframe->dw_frame_interval[ival - 1]; + ctrl->dwMaxVideoFrameSize = + uframe->frame.dw_max_video_frame_buffer_size; + + if (uvc->video.ep->desc) + ctrl->dwMaxPayloadTransferSize = + uvc->video.ep->desc->wMaxPacketSize; + ctrl->bmFramingInfo = 3; + ctrl->bPreferedVersion = 1; + ctrl->bMaxVersion = 1; +} + +static int uvc_events_process_data(struct uvc_device *uvc, + struct usb_request *req) +{ + struct uvc_video *video = &uvc->video; + struct uvc_streaming_control *target; + struct uvc_streaming_control *ctrl; + struct uvcg_frame *uframe; + struct uvcg_format *uformat; + + switch (video->control) { + case UVC_VS_PROBE_CONTROL: + pr_debug("setting probe control, length = %d\n", req->actual); + target = &video->probe; + break; + + case UVC_VS_COMMIT_CONTROL: + pr_debug("setting commit control, length = %d\n", req->actual); + target = &video->commit; + break; + + default: + pr_err("setting unknown control, length = %d\n", req->actual); + return -EOPNOTSUPP; + } + + ctrl = (struct uvc_streaming_control *)req->buf; + + uvc_fill_streaming_control(uvc, target, ctrl->bFormatIndex, + ctrl->bFrameIndex, ctrl->dwFrameInterval); + + if (video->control == UVC_VS_COMMIT_CONTROL) { + uformat = find_format_by_index(uvc, target->bFormatIndex); + if (!uformat) + return -EINVAL; + + uframe = find_frame_by_index(uvc, uformat, ctrl->bFrameIndex); + if (!uframe) + return -EINVAL; + + spin_lock(&video->frame_lock); + + video->cur_frame = uframe; + video->cur_format = uformat; + video->cur_ival = ctrl->dwFrameInterval;
I just realized that this is broken. It obviously should return the index for the dwFrameInterval. I prepared an helper find_ival_index and will rework it in the next round after some further review.
+ + spin_unlock(&video->frame_lock); + } + + return 0; +}
-- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
Attachment:
signature.asc
Description: PGP signature