diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2018-06-09 14:29:41 +0300 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2018-06-12 21:19:58 +0300 |
commit | 2bb0cfbf8137e02cc32aae3b36f85ef7300e8936 (patch) | |
tree | 42899dbeb32459722d27606bd79848a25a8c5e16 /uvc.c | |
parent | df21a9349a256fd2bea1f8701af198b312682d39 (diff) |
Split UVC gadget into a library and test application
Split the project into a UVC gadget library and a test application. To
avoid rolling out a custom build system, switch to CMake.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'uvc.c')
-rw-r--r-- | uvc.c | 395 |
1 files changed, 0 insertions, 395 deletions
@@ -1,395 +0,0 @@ -/* - * UVC protocol handling - * - * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#include <errno.h> -#include <linux/usb/ch9.h> -#include <linux/usb/g_uvc.h> -#include <linux/usb/video.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "configfs.h" -#include "events.h" -#include "stream.h" -#include "tools.h" -#include "uvc.h" -#include "v4l2.h" - -struct uvc_device -{ - struct v4l2_device *vdev; - - struct uvc_stream *stream; - struct uvc_function_config *fc; - - struct uvc_streaming_control probe; - struct uvc_streaming_control commit; - - int control; - - unsigned int fcc; - unsigned int width; - unsigned int height; - unsigned int maxsize; -}; - -struct uvc_device *uvc_open(const char *devname, struct uvc_stream *stream) -{ - struct uvc_device *dev; - - dev = malloc(sizeof *dev); - if (dev == NULL) - return NULL; - - memset(dev, 0, sizeof *dev); - dev->stream = stream; - - dev->vdev = v4l2_open(devname); - if (dev->vdev == NULL) { - free(dev); - return NULL; - } - - return dev; -} - -void uvc_close(struct uvc_device *dev) -{ - v4l2_close(dev->vdev); - dev->vdev = NULL; - - free(dev); -} - -/* --------------------------------------------------------------------------- - * Request processing - */ - -static void -uvc_fill_streaming_control(struct uvc_device *dev, - struct uvc_streaming_control *ctrl, - int iformat, int iframe, unsigned int ival) -{ - const struct uvc_function_config_format *format; - const struct uvc_function_config_frame *frame; - unsigned int i; - - /* - * Restrict the iformat, iframe and ival to valid values. Negative - * values for iformat or iframe will result in the maximum valid value - * being selected. - */ - iformat = clamp((unsigned int)iformat, 1U, - dev->fc->streaming.num_formats); - format = &dev->fc->streaming.formats[iformat-1]; - - iframe = clamp((unsigned int)iframe, 1U, format->num_frames); - frame = &format->frames[iframe-1]; - - for (i = 0; i < frame->num_intervals; ++i) { - if (ival <= frame->intervals[i]) { - ival = frame->intervals[i]; - break; - } - } - - if (i == frame->num_intervals) - ival = frame->intervals[frame->num_intervals-1]; - - memset(ctrl, 0, sizeof *ctrl); - - ctrl->bmHint = 1; - ctrl->bFormatIndex = iformat; - ctrl->bFrameIndex = iframe ; - ctrl->dwFrameInterval = ival; - - switch (format->fcc) { - case V4L2_PIX_FMT_YUYV: - ctrl->dwMaxVideoFrameSize = frame->width * frame->height * 2; - break; - case V4L2_PIX_FMT_MJPEG: - ctrl->dwMaxVideoFrameSize = dev->maxsize; - break; - } - - ctrl->dwMaxPayloadTransferSize = dev->fc->streaming.ep.wMaxPacketSize; - ctrl->bmFramingInfo = 3; - ctrl->bPreferedVersion = 1; - ctrl->bMaxVersion = 1; -} - -static void -uvc_events_process_standard(struct uvc_device *dev, - const struct usb_ctrlrequest *ctrl, - struct uvc_request_data *resp) -{ - printf("standard request\n"); - (void)dev; - (void)ctrl; - (void)resp; -} - -static void -uvc_events_process_control(struct uvc_device *dev, uint8_t req, uint8_t cs, - struct uvc_request_data *resp) -{ - printf("control request (req %02x cs %02x)\n", req, cs); - (void)dev; - (void)resp; -} - -static void -uvc_events_process_streaming(struct uvc_device *dev, uint8_t req, uint8_t cs, - struct uvc_request_data *resp) -{ - struct uvc_streaming_control *ctrl; - - printf("streaming request (req %02x cs %02x)\n", req, cs); - - if (cs != UVC_VS_PROBE_CONTROL && cs != UVC_VS_COMMIT_CONTROL) - return; - - ctrl = (struct uvc_streaming_control *)&resp->data; - resp->length = sizeof *ctrl; - - switch (req) { - case UVC_SET_CUR: - dev->control = cs; - resp->length = 34; - break; - - case UVC_GET_CUR: - if (cs == UVC_VS_PROBE_CONTROL) - memcpy(ctrl, &dev->probe, sizeof *ctrl); - else - memcpy(ctrl, &dev->commit, sizeof *ctrl); - break; - - case UVC_GET_MIN: - case UVC_GET_MAX: - case UVC_GET_DEF: - uvc_fill_streaming_control(dev, ctrl, req == UVC_GET_MAX ? -1 : 1, - req == UVC_GET_MAX ? -1 : 1, 0); - break; - - case UVC_GET_RES: - memset(ctrl, 0, sizeof *ctrl); - break; - - case UVC_GET_LEN: - resp->data[0] = 0x00; - resp->data[1] = 0x22; - resp->length = 2; - break; - - case UVC_GET_INFO: - resp->data[0] = 0x03; - resp->length = 1; - break; - } -} - -static void -uvc_events_process_class(struct uvc_device *dev, - const struct usb_ctrlrequest *ctrl, - struct uvc_request_data *resp) -{ - unsigned int interface = ctrl->wIndex & 0xff; - - if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE) - return; - - if (interface == dev->fc->control.intf.bInterfaceNumber) - uvc_events_process_control(dev, ctrl->bRequest, ctrl->wValue >> 8, resp); - else if (interface == dev->fc->streaming.intf.bInterfaceNumber) - uvc_events_process_streaming(dev, ctrl->bRequest, ctrl->wValue >> 8, resp); -} - -static void -uvc_events_process_setup(struct uvc_device *dev, - const struct usb_ctrlrequest *ctrl, - struct uvc_request_data *resp) -{ - dev->control = 0; - - printf("bRequestType %02x bRequest %02x wValue %04x wIndex %04x " - "wLength %04x\n", ctrl->bRequestType, ctrl->bRequest, - ctrl->wValue, ctrl->wIndex, ctrl->wLength); - - switch (ctrl->bRequestType & USB_TYPE_MASK) { - case USB_TYPE_STANDARD: - uvc_events_process_standard(dev, ctrl, resp); - break; - - case USB_TYPE_CLASS: - uvc_events_process_class(dev, ctrl, resp); - break; - - default: - break; - } -} - -static void -uvc_events_process_data(struct uvc_device *dev, - const struct uvc_request_data *data) -{ - const struct uvc_streaming_control *ctrl = - (const struct uvc_streaming_control *)&data->data; - struct uvc_streaming_control *target; - - switch (dev->control) { - case UVC_VS_PROBE_CONTROL: - printf("setting probe control, length = %d\n", data->length); - target = &dev->probe; - break; - - case UVC_VS_COMMIT_CONTROL: - printf("setting commit control, length = %d\n", data->length); - target = &dev->commit; - break; - - default: - printf("setting unknown control, length = %d\n", data->length); - return; - } - - uvc_fill_streaming_control(dev, target, ctrl->bFormatIndex, - ctrl->bFrameIndex, ctrl->dwFrameInterval); - - if (dev->control == UVC_VS_COMMIT_CONTROL) { - const struct uvc_function_config_format *format; - const struct uvc_function_config_frame *frame; - struct v4l2_pix_format pixfmt; - - format = &dev->fc->streaming.formats[target->bFormatIndex-1]; - frame = &format->frames[target->bFrameIndex-1]; - - dev->fcc = format->fcc; - dev->width = frame->width; - dev->height = frame->height; - - memset(&pixfmt, 0, sizeof pixfmt); - pixfmt.width = frame->width; - pixfmt.height = frame->height; - pixfmt.pixelformat = format->fcc; - pixfmt.field = V4L2_FIELD_NONE; - if (format->fcc == V4L2_PIX_FMT_MJPEG) - pixfmt.sizeimage = dev->maxsize * 1.5; - - uvc_stream_set_format(dev->stream, &pixfmt); - } -} - -static void uvc_events_process(void *d) -{ - struct uvc_device *dev = d; - struct v4l2_event v4l2_event; - const struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; - struct uvc_request_data resp; - int ret; - - ret = ioctl(dev->vdev->fd, VIDIOC_DQEVENT, &v4l2_event); - if (ret < 0) { - printf("VIDIOC_DQEVENT failed: %s (%d)\n", strerror(errno), - errno); - return; - } - - memset(&resp, 0, sizeof resp); - resp.length = -EL2HLT; - - switch (v4l2_event.type) { - case UVC_EVENT_CONNECT: - case UVC_EVENT_DISCONNECT: - return; - - case UVC_EVENT_SETUP: - uvc_events_process_setup(dev, &uvc_event->req, &resp); - break; - - case UVC_EVENT_DATA: - uvc_events_process_data(dev, &uvc_event->data); - return; - - case UVC_EVENT_STREAMON: - uvc_stream_enable(dev->stream, 1); - return; - - case UVC_EVENT_STREAMOFF: - uvc_stream_enable(dev->stream, 0); - return; - } - - ioctl(dev->vdev->fd, UVCIOC_SEND_RESPONSE, &resp); - if (ret < 0) { - printf("UVCIOC_S_EVENT failed: %s (%d)\n", strerror(errno), - errno); - return; - } -} - -/* --------------------------------------------------------------------------- - * Initialization and setup - */ - -void uvc_events_init(struct uvc_device *dev, struct events *events) -{ - struct v4l2_event_subscription sub; - - /* Default to the minimum values. */ - uvc_fill_streaming_control(dev, &dev->probe, 1, 1, 0); - uvc_fill_streaming_control(dev, &dev->commit, 1, 1, 0); - - memset(&sub, 0, sizeof sub); - sub.type = UVC_EVENT_SETUP; - ioctl(dev->vdev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); - sub.type = UVC_EVENT_DATA; - ioctl(dev->vdev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); - sub.type = UVC_EVENT_STREAMON; - ioctl(dev->vdev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); - sub.type = UVC_EVENT_STREAMOFF; - ioctl(dev->vdev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); - - events_watch_fd(events, dev->vdev->fd, EVENT_EXCEPTION, - uvc_events_process, dev); -} - -void uvc_set_config(struct uvc_device *dev, struct uvc_function_config *fc) -{ - /* FIXME: The maximum size should be specified per format and frame. */ - dev->maxsize = 0; - dev->fc = fc; -} - -int uvc_set_format(struct uvc_device *dev, struct v4l2_pix_format *format) -{ - return v4l2_set_format(dev->vdev, format); -} - -struct v4l2_device *uvc_v4l2_device(struct uvc_device *dev) -{ - /* - * TODO: The V4L2 device shouldn't be exposed. We should replace this - * with an abstract video sink class when one will be avaiilable. - */ - return dev->vdev; -} |