On Wed, Sep 28, 2016 at 05:22:44PM +0300, Felipe Balbi wrote: > > Hi, > > yfw <nh26223@xxxxxxxxx> writes: > >>>>>>>>>>>> Does this mean the issue of isoc high bandwidth transfer was fixed by > >>>>>>>>>>>> this patchset per your test? > >>>>>>>>>>> > >>>>>>>>>>> No, I couldn't get g_webcam to work yet. > >>>>>>>>>> > >>>>>>>>>> In mainline, g_webcam is broken with DWC3. Also these two patches don't > >>>>>>>>>> fix the issue on v4.4.21. > >>>>>>>>> > >>>>>>>>> care to send tracepoint output? Best if you could cherry pick my latest > >>>>>>>>> tracepoint changes so we have the best output possible. > >>>>>>>>> > >>>>>>>>> I have also built a branch with v4.4.21 + all dwc3 patches (except for > >>>>>>>>> device properties and PCI stuff) which you could use for testing. If you > >>>>>>>>> run with that, then I can get proper trace output and I can try to > >>>>>>>>> figure out what's missing. > >>>>>>>>> > >>>>>>>>> Branch is here: > >>>>>>>>> > >>>>>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git usb/v4.4.21+dwc3 > >>>>>>>> > >>>>>>>> This branch does not generate the isoch traffic (on ep2in). ftrace > >>>>>>>> attached (dwc3-g_webcam-v4.4.21+tp.ftrace). > >>>>>>>> > >>>>>>>> I also attached the ftrace log for v4.4.21 tag > >>>>>>>> (dwc3-g_webcam-v4.4.21.ftrace) for comparison, which has ep2in isoch > >>>>>>>> traffic, but only one transaction per SOF. > >>>>>>> > >>>>>>> Sorry, I didn't realize the ftrace file size is huge. Attached the > >>>>>>> tarball here. > >>>>>> > >>>>>> looking at webcam.c, it'll set wMaxPacket correctly (to 0x1400) if you > >>>>>> pass streaming_maxpacket=3072. So that's one thing. > >>>>>> > >>>>>> I'll look at this log with more detail tomorrow, though. > >>>>> > >>>>> here's a bug in composite.c because of a bug in usb_endpoint_maxp(). > >>>>> > >>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c > >>>>> index 32176f779861..f6a7583ab6d1 100644 > >>>>> --- a/drivers/usb/gadget/composite.c > >>>>> +++ b/drivers/usb/gadget/composite.c > >>>>> @@ -197,7 +197,7 @@ int config_ep_by_speed(struct usb_gadget *g, > >>>>> > >>>>> ep_found: > >>>>> /* commit results */ > >>>>> - _ep->maxpacket = usb_endpoint_maxp(chosen_desc); > >>>>> + _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff; > >>>> > >>>> uvc_video set the each request size (you could check function > >>>> uvc_video_alloc_requests()) for uvc: > >>>> req_size = video->ep->maxpacket > >>>> * max_t(unsigned int, video->ep->maxburst, 1) > >>>> * (video->ep->mult + 1); > >>>> > >>>> > >>>> If we change the ep->maxpacket like this, uvc layer will only set the > >>>> req size to 1024 (while it should be 3072 if we want to use high > >>>> bindwidth isoc transfer). > >>> > >>> it'll be 1024 * (mult + 1). Hmm, mult isn't set for highspeed isoc > >>> endpoints. So here you go: > >> > >> Great! all the 4 patches you sent so far fix the issue on v4.4.21 tag! > >> Now I see 3 1024-bytes transactions in each SOF. > > Great news. But the same changes still can't work in my side. I need to > > look at other registers. > > > > Do you mind dumping the dwc3 registers in your side and share to me? Thanks > > in advance. > > tracepoints contain all that information. We trace every single register > access. > > Frankly, I think you'd be much better off trying to cherry-pick changes > back to your kernel. > > Now I need to find a way to get g_webcam working with today's mainline > so I can test this periodically. Bin, what else did you change in > uvc-gadget.c? Here's what I have: Attached is my uvc-gadget.c. Sorry for the mess in the code. It should be cleaned up. I am not sure my following change is important or not. @@ -374,7 +388,7 @@ uvc_fill_streaming_control(struct uvc_device *dev, ctrl->dwMaxVideoFrameSize = dev->imgsize; break; } - ctrl->dwMaxPayloadTransferSize = 512; /* TODO this should be filled by the driver. */ + ctrl->dwMaxPayloadTransferSize = 3072; /* TODO this should be filled by the driver. */ ctrl->bmFramingInfo = 3; ctrl->bPreferedVersion = 1; ctrl->bMaxVersion = 1; > > diff --git a/uvc-gadget.c b/uvc-gadget.c > index 9ef315cf0166..26ce9cdf4ea5 100644 > --- a/uvc-gadget.c > +++ b/uvc-gadget.c > @@ -319,7 +319,7 @@ struct uvc_format_info > }; > > static const struct uvc_frame_info uvc_frames_yuyv[] = { > - { 640, 360, { 666666, 10000000, 50000000, 0 }, }, > + { 640, 480, { 333333, 666666, 1000000, 0 }, }, > { 1280, 720, { 50000000, 0 }, }, > { 0, 0, { 0, }, }, > }; > @@ -397,6 +397,8 @@ uvc_events_process_control(struct uvc_device *dev, uint8_t req, uint8_t cs, > printf("control request (req %02x cs %02x)\n", req, cs); > (void)dev; > (void)resp; > + > + resp->length = 0; > } > > static void > @@ -732,6 +734,9 @@ int main(int argc, char *argv[]) > fd_set wfds = fds; > > ret = select(dev->fd + 1, NULL, &wfds, &efds, NULL); > + if (ret < 0) > + fprintf(stderr, "Select failed: %s\n", strerror(errno)); > + > if (FD_ISSET(dev->fd, &efds)) > uvc_events_process(dev); > if (FD_ISSET(dev->fd, &wfds)) > > > I also have this quick hack in webcam gadget: > > diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c > index 29b41b5dee04..935fd76ba83e 100644 > --- a/drivers/usb/gadget/function/f_uvc.c > +++ b/drivers/usb/gadget/function/f_uvc.c > @@ -396,6 +399,9 @@ uvc_function_connect(struct uvc_device *uvc) > struct usb_composite_dev *cdev = uvc->func.config->cdev; > int ret; > > + if (cdev->deactivations == 0) > + return; > + > if ((ret = usb_function_activate(&uvc->func)) < 0) > INFO(cdev, "UVC connect failed with %d\n", ret); > } > diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c > index f4ccbd56f4d2..6c0ac0525b80 100644 > --- a/drivers/usb/gadget/function/uvc_v4l2.c > +++ b/drivers/usb/gadget/function/uvc_v4l2.c > @@ -298,7 +300,6 @@ uvc_v4l2_open(struct file *file) > handle->device = &uvc->video; > file->private_data = &handle->vfh; > > - uvc_function_connect(uvc); > return 0; > } > > @@ -340,6 +341,7 @@ uvc_v4l2_poll(struct file *file, poll_table *wait) > struct video_device *vdev = video_devdata(file); > struct uvc_device *uvc = video_get_drvdata(vdev); > > + uvc_function_connect(uvc); > return uvcg_queue_poll(&uvc->video.queue, file, wait); > } I don't change here. > > diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c > index 72c976bf3530..bfbe7f10175a 100644 > --- a/drivers/usb/gadget/legacy/webcam.c > +++ b/drivers/usb/gadget/legacy/webcam.c > @@ -191,15 +191,15 @@ static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { > .bFrameIndex = 1, > .bmCapabilities = 0, > .wWidth = cpu_to_le16(640), > - .wHeight = cpu_to_le16(360), > + .wHeight = cpu_to_le16(480), > .dwMinBitRate = cpu_to_le32(18432000), > .dwMaxBitRate = cpu_to_le32(55296000), > - .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), > - .dwDefaultFrameInterval = cpu_to_le32(666666), > + .dwMaxVideoFrameBufferSize = cpu_to_le32(614400), > + .dwDefaultFrameInterval = cpu_to_le32(333333), > .bFrameIntervalType = 3, > - .dwFrameInterval[0] = cpu_to_le32(666666), > - .dwFrameInterval[1] = cpu_to_le32(1000000), > - .dwFrameInterval[2] = cpu_to_le32(5000000), > + .dwFrameInterval[0] = cpu_to_le32(333333), > + .dwFrameInterval[1] = cpu_to_le32(666666), > + .dwFrameInterval[2] = cpu_to_le32(1000000), > }; But I have this one. Regards, -Bin. > > static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { > > Still no luck. I'll continue hacking on this tomorrow. > > -- > balbi
/* * UVC gadget test application * * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@xxxxxxxxxxxxxxxx> * * 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/time.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/select.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <time.h> #include <linux/usb/ch9.h> #include <linux/usb/video.h> #include <linux/videodev2.h> #include "uvc.h" #define clamp(val, min, max) ({ \ typeof(val) __val = (val); \ typeof(min) __min = (min); \ typeof(max) __max = (max); \ (void) (&__val == &__min); \ (void) (&__val == &__max); \ __val = __val < __min ? __min: __val; \ __val > __max ? __max: __val; }) #define ARRAY_SIZE(a) ((sizeof(a) / sizeof(a[0]))) struct uvc_device { int fd; struct uvc_streaming_control probe; struct uvc_streaming_control commit; int control; unsigned int fcc; unsigned int width; unsigned int height; void **mem; unsigned int nbufs; unsigned int bufsize; unsigned int bulk; uint8_t color; unsigned int imgsize; void *imgdata; }; static struct uvc_device * uvc_open(const char *devname) { struct uvc_device *dev; struct v4l2_capability cap; int ret; int fd; fd = open(devname, O_RDWR | O_NONBLOCK); if (fd == -1) { printf("v4l2 open failed: %s (%d)\n", strerror(errno), errno); return NULL; } printf("open succeeded, file descriptor = %d\n", fd); ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { printf("unable to query device: %s (%d)\n", strerror(errno), errno); close(fd); return NULL; } printf("device is %s on bus %s\n", cap.card, cap.bus_info); dev = malloc(sizeof *dev); if (dev == NULL) { close(fd); return NULL; } memset(dev, 0, sizeof *dev); dev->fd = fd; return dev; } static void uvc_close(struct uvc_device *dev) { close(dev->fd); free(dev->imgdata); free(dev->mem); free(dev); } /* --------------------------------------------------------------------------- * Video streaming */ static void uvc_video_fill_buffer(struct uvc_device *dev, struct v4l2_buffer *buf) { unsigned int bpl; unsigned int i; #if 0 /* struct timespec t;*/ static int cnt = 0; if (cnt++ % 10) { buf->bytesused = 0; return; } /* t.tv_sec = 0;*/ /* t.tv_nsec = 200000000; */ /* 100ms -> 10fps */ /* nanosleep(&t, NULL);*/ #endif switch (dev->fcc) { case V4L2_PIX_FMT_YUYV: /* Fill the buffer with video data. */ bpl = dev->width * 2; for (i = 0; i < dev->height; ++i) memset(dev->mem[buf->index] + i*bpl, dev->color++, bpl); buf->bytesused = bpl * dev->height; break; case V4L2_PIX_FMT_MJPEG: memcpy(dev->mem[buf->index], dev->imgdata, dev->imgsize); buf->bytesused = dev->imgsize; break; } } static int uvc_video_process(struct uvc_device *dev) { struct v4l2_buffer buf; int ret; memset(&buf, 0, sizeof buf); buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; buf.memory = V4L2_MEMORY_MMAP; if ((ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf)) < 0) { /* printf("Unable to dequeue buffer: %s (%d).\n", strerror(errno),*/ /* errno);*/ return ret; } uvc_video_fill_buffer(dev, &buf); if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0) { printf("Unable to requeue buffer: %s (%d).\n", strerror(errno), errno); return ret; } return 0; } static int uvc_video_reqbufs(struct uvc_device *dev, int nbufs) { struct v4l2_requestbuffers rb; struct v4l2_buffer buf; unsigned int i; int ret; for (i = 0; i < dev->nbufs; ++i) munmap(dev->mem[i], dev->bufsize); free(dev->mem); dev->mem = 0; dev->nbufs = 0; memset(&rb, 0, sizeof rb); rb.count = nbufs; rb.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; rb.memory = V4L2_MEMORY_MMAP; ret = ioctl(dev->fd, VIDIOC_REQBUFS, &rb); if (ret < 0) { printf("Unable to allocate buffers: %s (%d).\n", strerror(errno), errno); return ret; } printf("%u buffers allocated.\n", rb.count); /* Map the buffers. */ dev->mem = malloc(rb.count * sizeof dev->mem[0]); for (i = 0; i < rb.count; ++i) { memset(&buf, 0, sizeof buf); buf.index = i; buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); if (ret < 0) { printf("Unable to query buffer %u: %s (%d).\n", i, strerror(errno), errno); return -1; } printf("length: %u offset: %u\n", buf.length, buf.m.offset); dev->mem[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, buf.m.offset); if (dev->mem[i] == MAP_FAILED) { printf("Unable to map buffer %u: %s (%d)\n", i, strerror(errno), errno); return -1; } printf("Buffer %u mapped at address %p.\n", i, dev->mem[i]); } dev->bufsize = buf.length; dev->nbufs = rb.count; return 0; } static int uvc_video_stream(struct uvc_device *dev, int enable) { struct v4l2_buffer buf; unsigned int i; int type = V4L2_BUF_TYPE_VIDEO_OUTPUT; int ret; if (!enable) { printf("Stopping video stream.\n"); ioctl(dev->fd, VIDIOC_STREAMOFF, &type); return 0; } printf("Starting video stream.\n"); for (i = 0; i < dev->nbufs; ++i) { memset(&buf, 0, sizeof buf); buf.index = i; buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; buf.memory = V4L2_MEMORY_MMAP; uvc_video_fill_buffer(dev, &buf); printf("Queueing buffer %u.\n", i); if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0) { printf("Unable to queue buffer: %s (%d).\n", strerror(errno), errno); break; } } ioctl(dev->fd, VIDIOC_STREAMON, &type); return ret; } static int uvc_video_set_format(struct uvc_device *dev) { struct v4l2_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.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; fmt.fmt.pix.width = dev->width; fmt.fmt.pix.height = dev->height; fmt.fmt.pix.pixelformat = dev->fcc; fmt.fmt.pix.field = V4L2_FIELD_NONE; if (dev->fcc == V4L2_PIX_FMT_MJPEG) fmt.fmt.pix.sizeimage = dev->imgsize * 1.5; if ((ret = ioctl(dev->fd, VIDIOC_S_FMT, &fmt)) < 0) printf("Unable to set format: %s (%d).\n", strerror(errno), errno); return ret; } static int uvc_video_init(struct uvc_device *dev __attribute__((__unused__))) { return 0; } /* --------------------------------------------------------------------------- * Request processing */ struct uvc_frame_info { unsigned int width; unsigned int height; unsigned int intervals[8]; }; struct uvc_format_info { unsigned int fcc; const struct uvc_frame_info *frames; }; static const struct uvc_frame_info uvc_frames_yuyv[] = { /* { 640, 360, { 666666, 10000000, 50000000, 0 }, },*/ { 640, 480, { 333333, 0 }, }, /* 30fps */ { 0, 0, { 0, }, }, }; static const struct uvc_frame_info uvc_frames_mjpeg[] = { /* { 640, 360, { 666666, 10000000, 50000000, 0 }, },*/ { 640, 480, { 333333, 0 }, }, { 1280, 720, { 50000000, 0 }, }, { 0, 0, { 0, }, }, }; static const struct uvc_format_info uvc_formats[] = { { V4L2_PIX_FMT_YUYV, uvc_frames_yuyv }, /* { V4L2_PIX_FMT_MJPEG, uvc_frames_mjpeg },*/ }; static void uvc_fill_streaming_control(struct uvc_device *dev, struct uvc_streaming_control *ctrl, int iframe, int iformat) { const struct uvc_format_info *format; const struct uvc_frame_info *frame; unsigned int nframes; if (iformat < 0) iformat = ARRAY_SIZE(uvc_formats) + iformat; if (iformat < 0 || iformat >= (int)ARRAY_SIZE(uvc_formats)) return; format = &uvc_formats[iformat]; nframes = 0; while (format->frames[nframes].width != 0) ++nframes; if (iframe < 0) iframe = nframes + iframe; if (iframe < 0 || iframe >= (int)nframes) return; frame = &format->frames[iframe]; memset(ctrl, 0, sizeof *ctrl); ctrl->bmHint = 1; ctrl->bFormatIndex = iformat + 1; ctrl->bFrameIndex = iframe + 1; ctrl->dwFrameInterval = frame->intervals[0]; switch (format->fcc) { case V4L2_PIX_FMT_YUYV: ctrl->dwMaxVideoFrameSize = frame->width * frame->height * 2; break; case V4L2_PIX_FMT_MJPEG: ctrl->dwMaxVideoFrameSize = dev->imgsize; break; } ctrl->dwMaxPayloadTransferSize = 3072; /* TODO this should be filled by the driver. */ ctrl->bmFramingInfo = 3; ctrl->bPreferedVersion = 1; ctrl->bMaxVersion = 1; } static void uvc_events_process_standard(struct uvc_device *dev, 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 : 0, req == UVC_GET_MAX ? -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, struct usb_ctrlrequest *ctrl, struct uvc_request_data *resp) { if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE) return; switch (ctrl->wIndex & 0xff) { case UVC_INTF_CONTROL: uvc_events_process_control(dev, ctrl->bRequest, ctrl->wValue >> 8, resp); break; case UVC_INTF_STREAMING: uvc_events_process_streaming(dev, ctrl->bRequest, ctrl->wValue >> 8, resp); break; default: break; } } static void uvc_events_process_setup(struct uvc_device *dev, 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, struct uvc_request_data *data) { struct uvc_streaming_control *target; struct uvc_streaming_control *ctrl; const struct uvc_format_info *format; const struct uvc_frame_info *frame; const unsigned int *interval; unsigned int iformat, iframe; unsigned int nframes; 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; } ctrl = (struct uvc_streaming_control *)&data->data; iformat = clamp((unsigned int)ctrl->bFormatIndex, 1U, (unsigned int)ARRAY_SIZE(uvc_formats)); format = &uvc_formats[iformat-1]; nframes = 0; while (format->frames[nframes].width != 0) ++nframes; iframe = clamp((unsigned int)ctrl->bFrameIndex, 1U, nframes); frame = &format->frames[iframe-1]; interval = frame->intervals; while (interval[0] < ctrl->dwFrameInterval && interval[1]) ++interval; target->bFormatIndex = iformat; target->bFrameIndex = iframe; switch (format->fcc) { case V4L2_PIX_FMT_YUYV: target->dwMaxVideoFrameSize = frame->width * frame->height * 2; break; case V4L2_PIX_FMT_MJPEG: if (dev->imgsize == 0) printf("WARNING: MJPEG requested and no image loaded.\n"); target->dwMaxVideoFrameSize = dev->imgsize; break; } target->dwFrameInterval = *interval; if (dev->control == UVC_VS_COMMIT_CONTROL) { dev->fcc = format->fcc; dev->width = frame->width; dev->height = frame->height; uvc_video_set_format(dev); if (dev->bulk) uvc_video_stream(dev, 1); } } static void uvc_events_process(struct uvc_device *dev) { struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; struct uvc_request_data resp; int ret; ret = ioctl(dev->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_video_reqbufs(dev, 4); uvc_video_stream(dev, 1); break; case UVC_EVENT_STREAMOFF: uvc_video_stream(dev, 0); uvc_video_reqbufs(dev, 0); break; } ioctl(dev->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; uvc_fill_streaming_control(dev, &dev->probe, 0, 0); uvc_fill_streaming_control(dev, &dev->commit, 0, 0); if (dev->bulk) { /* FIXME Crude hack, must be negotiated with the driver. */ dev->probe.dwMaxPayloadTransferSize = 16 * 1024; dev->commit.dwMaxPayloadTransferSize = 16 * 1024; } memset(&sub, 0, sizeof sub); sub.type = UVC_EVENT_SETUP; ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); sub.type = UVC_EVENT_DATA; ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); sub.type = UVC_EVENT_STREAMON; ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); sub.type = UVC_EVENT_STREAMOFF; ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); } /* --------------------------------------------------------------------------- * main */ static void image_load(struct uvc_device *dev, const char *img) { int fd = -1; if (img == NULL) return; fd = open(img, O_RDONLY); if (fd == -1) { printf("Unable to open MJPEG image '%s'\n", img); return; } dev->imgsize = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); dev->imgdata = malloc(dev->imgsize); if (dev->imgdata == NULL) { printf("Unable to allocate memory for MJPEG image\n"); dev->imgsize = 0; return; } read(fd, dev->imgdata, dev->imgsize); close(fd); } static void usage(const char *argv0) { fprintf(stderr, "Usage: %s [options]\n", argv0); fprintf(stderr, "Available options are\n"); fprintf(stderr, " -b Use bulk mode\n"); fprintf(stderr, " -d device Video device\n"); fprintf(stderr, " -h Print this help screen and exit\n"); fprintf(stderr, " -i image MJPEG image\n"); } #define MAX_ERRCNT 10 int main(int argc, char *argv[]) { char *device = "/dev/video0"; struct uvc_device *dev; int bulk_mode = 0; char *mjpeg_image = NULL; fd_set fds; int ret, opt; int errcnt = 0; while ((opt = getopt(argc, argv, "bd:hi:")) != -1) { switch (opt) { case 'b': bulk_mode = 1; break; case 'd': device = optarg; break; case 'h': usage(argv[0]); return 0; case 'i': mjpeg_image = optarg; break; default: fprintf(stderr, "Invalid option '-%c'\n", opt); usage(argv[0]); return 1; } } dev = uvc_open(device); if (dev == NULL) return 1; image_load(dev, mjpeg_image); dev->bulk = bulk_mode; uvc_events_init(dev); uvc_video_init(dev); FD_ZERO(&fds); FD_SET(dev->fd, &fds); while (1) { fd_set efds = fds; fd_set wfds = fds; ret = select(dev->fd + 1, NULL, &wfds, &efds, NULL); if (FD_ISSET(dev->fd, &efds)) uvc_events_process(dev); if (FD_ISSET(dev->fd, &wfds)) { ret = uvc_video_process(dev); /* if (ret) {*/ /* if (++errcnt > MAX_ERRCNT)*/ /* return 1;*/ /* } else {*/ /* errcnt = 0;*/ /* }*/ } } uvc_close(dev); return 0; }