On Wed, Sep 28, 2016 at 05:22:44PM +0300, Felipe Balbi wrote:
>
> Hi,
>
> yfw <[email protected]> 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 <[email protected]>
*
* 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;
}