diff options
Diffstat (limited to 'uvc-gadget.c')
-rw-r--r-- | uvc-gadget.c | 707 |
1 files changed, 0 insertions, 707 deletions
diff --git a/uvc-gadget.c b/uvc-gadget.c deleted file mode 100644 index 8ccfa07..0000000 --- a/uvc-gadget.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * UVC gadget test application - * - * 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 <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/select.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <linux/usb/ch9.h> -#include <linux/usb/g_uvc.h> -#include <linux/usb/video.h> -#include <linux/videodev2.h> - -#include "configfs.h" -#include "events.h" -#include "tools.h" -#include "v4l2.h" - -struct uvc_device -{ - struct v4l2_device *vdev; - - 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_stream -{ - struct v4l2_device *cap; - struct uvc_device *uvc; - - struct events *events; -}; - -static struct uvc_device * -uvc_open(const char *devname) -{ - struct uvc_device *dev; - - dev = malloc(sizeof *dev); - if (dev == NULL) - return NULL; - - memset(dev, 0, sizeof *dev); - - dev->vdev = v4l2_open(devname); - if (dev->vdev == NULL) { - free(dev); - return NULL; - } - - return dev; -} - -static void -uvc_close(struct uvc_device *dev) -{ - v4l2_close(dev->vdev); - dev->vdev = NULL; - - free(dev); -} - -/* --------------------------------------------------------------------------- - * Video streaming - */ - -static void capture_video_process(void *d) -{ - struct uvc_stream *stream = d; - struct v4l2_video_buffer buf; - int ret; - - ret = v4l2_dequeue_buffer(stream->cap, &buf); - if (ret < 0) - return; - - v4l2_queue_buffer(stream->uvc->vdev, &buf); -} - -static void -uvc_video_process(void *d) -{ - struct uvc_stream *stream = d; - struct v4l2_video_buffer buf; - int ret; - - ret = v4l2_dequeue_buffer(stream->uvc->vdev, &buf); - if (ret < 0) - return; - - v4l2_queue_buffer(stream->cap, &buf); -} - -static int -uvc_video_stream(struct uvc_stream *stream, int enable) -{ - struct v4l2_device *cap = stream->cap; - struct uvc_device *dev = stream->uvc; - int ret; - - if (!enable) { - printf("Stopping video stream.\n"); - events_unwatch_fd(stream->events, dev->vdev->fd, EVENT_WRITE); - v4l2_stream_off(dev->vdev); - v4l2_free_buffers(dev->vdev); - return 0; - } - - printf("Starting video stream.\n"); - - ret = v4l2_alloc_buffers(dev->vdev, V4L2_MEMORY_DMABUF, 4); - if (ret < 0) { - printf("Failed to allocate buffers: %s (%d)\n", strerror(-ret), -ret); - return ret; - } - - ret = v4l2_import_buffers(dev->vdev, dev->vdev->nbufs, cap->buffers); - if (ret < 0) { - printf("Failed to import buffers: %s (%d)\n", strerror(-ret), -ret); - goto error; - } - - v4l2_stream_on(dev->vdev); - events_watch_fd(stream->events, dev->vdev->fd, EVENT_WRITE, - uvc_video_process, stream); - - return 0; - -error: - v4l2_free_buffers(dev->vdev); - return ret; -} - -static int capture_video_stream(struct uvc_stream *stream, int enable) -{ - struct v4l2_device *cap = stream->cap; - unsigned int i; - int ret; - - if (!enable) { - printf("Stopping video capture stream.\n"); - events_unwatch_fd(stream->events, cap->fd, EVENT_READ); - v4l2_stream_off(cap); - v4l2_free_buffers(cap); - return 0; - } - - printf("Starting video capture stream.\n"); - - ret = v4l2_alloc_buffers(cap, V4L2_MEMORY_MMAP, 4); - if (ret < 0) { - printf("Failed to allocate capture buffers.\n"); - return ret; - } - - ret = v4l2_export_buffers(cap); - if (ret < 0) { - printf("Failed to export buffers.\n"); - goto error; - } - - for (i = 0; i < cap->nbufs; ++i) { - struct v4l2_video_buffer *buf = &cap->buffers[i]; - - ret = v4l2_queue_buffer(cap, buf); - if (ret < 0) - goto error; - } - - v4l2_stream_on(cap); - events_watch_fd(stream->events, cap->fd, EVENT_READ, - capture_video_process, stream); - - return 0; - -error: - v4l2_free_buffers(cap); - return ret; -} - -static int -uvc_video_set_format(struct uvc_stream *stream) -{ - struct v4l2_device *cap = stream->cap; - struct uvc_device *dev = stream->uvc; - struct v4l2_pix_format fmt; - int ret; - - printf("Setting format to 0x%08x %ux%u\n", - dev->fcc, dev->width, dev->height); - - memset(&fmt, 0, sizeof fmt); - fmt.width = dev->width; - fmt.height = dev->height; - fmt.pixelformat = dev->fcc; - fmt.field = V4L2_FIELD_NONE; - if (dev->fcc == V4L2_PIX_FMT_MJPEG) - fmt.sizeimage = dev->maxsize * 1.5; - - ret = v4l2_set_format(dev->vdev, &fmt); - if (ret < 0) - return ret; - - return v4l2_set_format(cap, &fmt); -} - -static int -uvc_video_init(struct uvc_device *dev __attribute__((__unused__))) -{ - return 0; -} - -/* --------------------------------------------------------------------------- - * 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_stream *stream, - const struct uvc_request_data *data) -{ - struct uvc_device *dev = stream->uvc; - 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; - - 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; - - uvc_video_set_format(stream); - } -} - -static void -uvc_events_process(void *d) -{ - struct uvc_stream *stream = d; - struct uvc_device *dev = stream->uvc; - 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(stream, &uvc_event->data); - return; - - case UVC_EVENT_STREAMON: - capture_video_stream(stream, 1); - uvc_video_stream(stream, 1); - return; - - case UVC_EVENT_STREAMOFF: - uvc_video_stream(stream, 0); - capture_video_stream(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; - } -} - -static void -uvc_events_init(struct uvc_device *dev) -{ - 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); -} - -/* --------------------------------------------------------------------------- - * Stream handling - */ - -static struct uvc_stream *uvc_stream_new(const char *uvc_device, - const char *cap_device) -{ - struct uvc_stream *stream; - - stream = malloc(sizeof(*stream)); - if (stream == NULL) - return NULL; - - memset(stream, 0, sizeof(*stream)); - - stream->cap = v4l2_open(cap_device); - if (stream->cap == NULL) - goto error; - - stream->uvc = uvc_open(uvc_device); - if (stream->uvc == NULL) - goto error; - - return stream; - -error: - if (stream->cap) - v4l2_close(stream->cap); - - free(stream); - return NULL; -} - -static void uvc_stream_delete(struct uvc_stream *stream) -{ - if (stream == NULL) - return; - - v4l2_close(stream->cap); - uvc_close(stream->uvc); - - free(stream); -} - -static void uvc_stream_init_uvc(struct uvc_stream *stream, - struct uvc_function_config *fc) -{ - /* - * FIXME: The maximum size should be specified per format and frame. - */ - stream->uvc->maxsize = 0; - stream->uvc->fc = fc; - - uvc_events_init(stream->uvc); - uvc_video_init(stream->uvc); -} - -static void uvc_stream_set_event_handler(struct uvc_stream *stream, - struct events *events) -{ - stream->events = events; - - events_watch_fd(stream->events, stream->uvc->vdev->fd, EVENT_EXCEPTION, - uvc_events_process, stream); -} - -/* --------------------------------------------------------------------------- - * main - */ - -static void usage(const char *argv0) -{ - fprintf(stderr, "Usage: %s [options] <uvc device>\n", argv0); - fprintf(stderr, "Available options are\n"); - fprintf(stderr, " -c device V4L2 source device\n"); - fprintf(stderr, " -h Print this help screen and exit\n"); - fprintf(stderr, " -i image MJPEG image\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " <uvc device> UVC device instance specifier\n"); - fprintf(stderr, "\n"); - - fprintf(stderr, " For ConfigFS devices the <uvc device> parameter can take the form of a shortened\n"); - fprintf(stderr, " function specifier such as: 'uvc.0', or if multiple gadgets are configured, the\n"); - fprintf(stderr, " gadget name should be included to prevent ambiguity: 'g1/functions/uvc.0'.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " For legacy g_webcam UVC instances, this parameter will identify the UDC that the\n"); - fprintf(stderr, " UVC function is bound to.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " The parameter is optional, and if not provided the first UVC function on the first\n"); - fprintf(stderr, " gadget identified will be used.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Example usage:\n"); - fprintf(stderr, " uvc-gadget uvc.1\n"); - fprintf(stderr, " uvc-gadget g1/functions/uvc.1\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " uvc-gadget musb-hdrc.0.auto\n"); -} - -/* Necessary for and only used by signal handler. */ -static struct events *sigint_events; - -static void sigint_handler(int signal __attribute__((__unused__))) -{ - /* Stop the main loop when the user presses CTRL-C */ - events_stop(sigint_events); -} - -int main(int argc, char *argv[]) -{ - char *function = NULL; - char *cap_device = "/dev/video1"; - struct uvc_function_config *fc; - struct uvc_stream *stream; - struct events events; - int ret = 0; - int opt; - - while ((opt = getopt(argc, argv, "c:h")) != -1) { - switch (opt) { - case 'c': - cap_device = optarg; - break; - - case 'h': - usage(argv[0]); - return 0; - - default: - fprintf(stderr, "Invalid option '-%c'\n", opt); - usage(argv[0]); - return 1; - } - } - - if (argv[optind] != NULL) - function = argv[optind]; - - fc = configfs_parse_uvc_function(function); - if (!fc) { - printf("Failed to identify function configuration\n"); - return 1; - } - - /* - * Create the events handler. Register a signal handler for SIGINT, - * received when the user presses CTRL-C. This will allow the main loop - * to be interrupted, and resources to be freed cleanly. - */ - events_init(&events); - - sigint_events = &events; - signal(SIGINT, sigint_handler); - - /* Create and initialise the stream. */ - stream = uvc_stream_new(fc->video, cap_device); - if (stream == NULL) { - ret = 1; - goto done; - } - - uvc_stream_init_uvc(stream, fc); - uvc_stream_set_event_handler(stream, &events); - - /* Main capture loop */ - events_loop(&events); - -done: - /* Cleanup */ - uvc_stream_delete(stream); - events_cleanup(&events); - configfs_free_uvc_function(fc); - - return ret; -} |