Commit 35f02a68 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

V4L/DVB (12378): uvcvideo: Restructure the driver to support multiple simultaneous streams.

As a first step towards multiple streaming interfaces support, reorganize the
driver's data structures to cleanly separate video control and video streaming
data.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 6c428b57
...@@ -551,6 +551,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, ...@@ -551,6 +551,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
} }
mutex_init(&streaming->mutex); mutex_init(&streaming->mutex);
streaming->dev = dev;
streaming->intf = usb_get_intf(intf); streaming->intf = usb_get_intf(intf);
streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
...@@ -751,7 +752,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, ...@@ -751,7 +752,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
streaming->maxpsize = psize; streaming->maxpsize = psize;
} }
list_add_tail(&streaming->list, &dev->streaming); list_add_tail(&streaming->list, &dev->streams);
return 0; return 0;
error: error:
...@@ -1167,13 +1168,75 @@ static int uvc_parse_control(struct uvc_device *dev) ...@@ -1167,13 +1168,75 @@ static int uvc_parse_control(struct uvc_device *dev)
*/ */
static void uvc_unregister_video(struct uvc_device *dev) static void uvc_unregister_video(struct uvc_device *dev)
{ {
if (dev->video.vdev) { struct uvc_streaming *streaming;
if (dev->video.vdev->minor == -1)
video_device_release(dev->video.vdev); list_for_each_entry(streaming, &dev->streams, list) {
if (streaming->vdev == NULL)
continue;
if (streaming->vdev->minor == -1)
video_device_release(streaming->vdev);
else else
video_unregister_device(dev->video.vdev); video_unregister_device(streaming->vdev);
dev->video.vdev = NULL; streaming->vdev = NULL;
}
}
static int uvc_register_video(struct uvc_device *dev,
struct uvc_streaming *stream)
{
struct video_device *vdev;
struct uvc_entity *term;
int ret;
if (uvc_trace_param & UVC_TRACE_PROBE) {
uvc_printk(KERN_INFO, "Found a valid video chain (");
list_for_each_entry(term, &dev->video.iterms, chain) {
printk("%d", term->id);
if (term->chain.next != &dev->video.iterms)
printk(",");
}
printk(" -> %d).\n", dev->video.oterm->id);
}
/* Initialize the streaming interface with default streaming
* parameters.
*/
ret = uvc_video_init(stream);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device "
"(%d).\n", ret);
return ret;
}
/* Register the device with V4L. */
vdev = video_device_alloc();
if (vdev == NULL)
return -1;
/* We already hold a reference to dev->udev. The video device will be
* unregistered before the reference is released, so we don't need to
* get another one.
*/
vdev->parent = &dev->intf->dev;
vdev->minor = -1;
vdev->fops = &uvc_fops;
vdev->release = video_device_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
/* Set the driver data before calling video_register_device, otherwise
* uvc_v4l2_open might race us.
*/
stream->vdev = vdev;
video_set_drvdata(vdev, stream);
if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
stream->vdev = NULL;
video_device_release(vdev);
return -1;
} }
return 0;
} }
/* /*
...@@ -1419,7 +1482,7 @@ static int uvc_scan_chain(struct uvc_video_device *video) ...@@ -1419,7 +1482,7 @@ static int uvc_scan_chain(struct uvc_video_device *video)
} }
/* /*
* Register the video devices. * Scan the device for video chains and register video devices.
* *
* The driver currently supports a single video device per control interface * The driver currently supports a single video device per control interface
* only. The terminal and units must match the following structure: * only. The terminal and units must match the following structure:
...@@ -1432,15 +1495,14 @@ static int uvc_scan_chain(struct uvc_video_device *video) ...@@ -1432,15 +1495,14 @@ static int uvc_scan_chain(struct uvc_video_device *video)
* Extension Units connected to the main chain as single-unit branches are * Extension Units connected to the main chain as single-unit branches are
* also supported. * also supported.
*/ */
static int uvc_register_video(struct uvc_device *dev) static int uvc_scan_device(struct uvc_device *dev)
{ {
struct video_device *vdev;
struct uvc_entity *term; struct uvc_entity *term;
int found = 0, ret; int found = 0;
/* Check if the control interface matches the structure we expect. */ /* Check if the control interface matches the structure we expect. */
list_for_each_entry(term, &dev->entities, list) { list_for_each_entry(term, &dev->entities, list) {
struct uvc_streaming *streaming; struct uvc_streaming *stream;
if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
continue; continue;
...@@ -1454,17 +1516,14 @@ static int uvc_register_video(struct uvc_device *dev) ...@@ -1454,17 +1516,14 @@ static int uvc_register_video(struct uvc_device *dev)
if (uvc_scan_chain(&dev->video) < 0) if (uvc_scan_chain(&dev->video) < 0)
continue; continue;
list_for_each_entry(streaming, &dev->streaming, list) { list_for_each_entry(stream, &dev->streams, list) {
if (streaming->header.bTerminalLink == if (stream->header.bTerminalLink ==
dev->video.sterm->id) { dev->video.sterm->id) {
dev->video.streaming = streaming; uvc_register_video(dev, stream);
found = 1; found = 1;
break; break;
} }
} }
if (found)
break;
} }
if (!found) { if (!found) {
...@@ -1472,55 +1531,6 @@ static int uvc_register_video(struct uvc_device *dev) ...@@ -1472,55 +1531,6 @@ static int uvc_register_video(struct uvc_device *dev)
return -1; return -1;
} }
if (uvc_trace_param & UVC_TRACE_PROBE) {
uvc_printk(KERN_INFO, "Found a valid video chain (");
list_for_each_entry(term, &dev->video.iterms, chain) {
printk("%d", term->id);
if (term->chain.next != &dev->video.iterms)
printk(",");
}
printk(" -> %d).\n", dev->video.oterm->id);
}
/* Initialize the video buffers queue. */
uvc_queue_init(&dev->video.queue, dev->video.streaming->type);
/* Initialize the streaming interface with default streaming
* parameters.
*/
if ((ret = uvc_video_init(&dev->video)) < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device "
"(%d).\n", ret);
return ret;
}
/* Register the device with V4L. */
vdev = video_device_alloc();
if (vdev == NULL)
return -1;
/* We already hold a reference to dev->udev. The video device will be
* unregistered before the reference is released, so we don't need to
* get another one.
*/
vdev->parent = &dev->intf->dev;
vdev->minor = -1;
vdev->fops = &uvc_fops;
vdev->release = video_device_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
/* Set the driver data before calling video_register_device, otherwise
* uvc_v4l2_open might race us.
*/
dev->video.vdev = vdev;
video_set_drvdata(vdev, &dev->video);
if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
dev->video.vdev = NULL;
video_device_release(vdev);
return -1;
}
return 0; return 0;
} }
...@@ -1559,7 +1569,7 @@ void uvc_delete(struct kref *kref) ...@@ -1559,7 +1569,7 @@ void uvc_delete(struct kref *kref)
kfree(entity); kfree(entity);
} }
list_for_each_safe(p, n, &dev->streaming) { list_for_each_safe(p, n, &dev->streams) {
struct uvc_streaming *streaming; struct uvc_streaming *streaming;
streaming = list_entry(p, struct uvc_streaming, list); streaming = list_entry(p, struct uvc_streaming, list);
usb_driver_release_interface(&uvc_driver.driver, usb_driver_release_interface(&uvc_driver.driver,
...@@ -1593,7 +1603,7 @@ static int uvc_probe(struct usb_interface *intf, ...@@ -1593,7 +1603,7 @@ static int uvc_probe(struct usb_interface *intf,
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->streaming); INIT_LIST_HEAD(&dev->streams);
kref_init(&dev->kref); kref_init(&dev->kref);
atomic_set(&dev->users, 0); atomic_set(&dev->users, 0);
...@@ -1634,8 +1644,8 @@ static int uvc_probe(struct usb_interface *intf, ...@@ -1634,8 +1644,8 @@ static int uvc_probe(struct usb_interface *intf,
if (uvc_ctrl_init_device(dev) < 0) if (uvc_ctrl_init_device(dev) < 0)
goto error; goto error;
/* Register the video devices. */ /* Scan the device for video chains and register video devices. */
if (uvc_register_video(dev) < 0) if (uvc_scan_device(dev) < 0)
goto error; goto error;
/* Save our data pointer in the interface data. */ /* Save our data pointer in the interface data. */
...@@ -1689,6 +1699,7 @@ static void uvc_disconnect(struct usb_interface *intf) ...@@ -1689,6 +1699,7 @@ static void uvc_disconnect(struct usb_interface *intf)
static int uvc_suspend(struct usb_interface *intf, pm_message_t message) static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
{ {
struct uvc_device *dev = usb_get_intfdata(intf); struct uvc_device *dev = usb_get_intfdata(intf);
struct uvc_streaming *stream;
uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber); intf->cur_altsetting->desc.bInterfaceNumber);
...@@ -1698,18 +1709,20 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1698,18 +1709,20 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
UVC_SC_VIDEOCONTROL) UVC_SC_VIDEOCONTROL)
return uvc_status_suspend(dev); return uvc_status_suspend(dev);
if (dev->video.streaming->intf != intf) { list_for_each_entry(stream, &dev->streams, list) {
uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " if (stream->intf == intf)
"interface mismatch.\n"); return uvc_video_suspend(stream);
return -EINVAL;
} }
return uvc_video_suspend(&dev->video); uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
"mismatch.\n");
return -EINVAL;
} }
static int __uvc_resume(struct usb_interface *intf, int reset) static int __uvc_resume(struct usb_interface *intf, int reset)
{ {
struct uvc_device *dev = usb_get_intfdata(intf); struct uvc_device *dev = usb_get_intfdata(intf);
struct uvc_streaming *stream;
uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber); intf->cur_altsetting->desc.bInterfaceNumber);
...@@ -1726,13 +1739,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset) ...@@ -1726,13 +1739,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
return uvc_status_resume(dev); return uvc_status_resume(dev);
} }
if (dev->video.streaming->intf != intf) { list_for_each_entry(stream, &dev->streams, list) {
uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " if (stream->intf == intf)
"interface mismatch.\n"); return uvc_video_resume(stream);
return -EINVAL;
} }
return uvc_video_resume(&dev->video); uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
"mismatch.\n");
return -EINVAL;
} }
static int uvc_resume(struct usb_interface *intf) static int uvc_resume(struct usb_interface *intf)
......
...@@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, ...@@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
return 0; return 0;
} }
void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf) struct uvc_buffer *buf)
{ {
int ret, i; int ret, i;
...@@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, ...@@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
* processes the data of the first payload of the new frame. * processes the data of the first payload of the new frame.
*/ */
do { do {
ret = isight_decode(&video->queue, buf, ret = isight_decode(&stream->queue, buf,
urb->transfer_buffer + urb->transfer_buffer +
urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length); urb->iso_frame_desc[i].actual_length);
...@@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, ...@@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
if (buf->state == UVC_BUF_STATE_DONE || if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR) buf->state == UVC_BUF_STATE_ERROR)
buf = uvc_queue_next_buffer(&video->queue, buf); buf = uvc_queue_next_buffer(&stream->queue,
buf);
} while (ret == -EAGAIN); } while (ret == -EAGAIN);
} }
} }
This diff is collapsed.
This diff is collapsed.
...@@ -361,26 +361,6 @@ struct uvc_streaming_header { ...@@ -361,26 +361,6 @@ struct uvc_streaming_header {
__u8 bTriggerUsage; __u8 bTriggerUsage;
}; };
struct uvc_streaming {
struct list_head list;
struct usb_interface *intf;
int intfnum;
__u16 maxpsize;
struct uvc_streaming_header header;
enum v4l2_buf_type type;
unsigned int nformats;
struct uvc_format *format;
struct uvc_streaming_control ctrl;
struct uvc_format *cur_format;
struct uvc_frame *cur_frame;
struct mutex mutex;
};
enum uvc_buffer_state { enum uvc_buffer_state {
UVC_BUF_STATE_IDLE = 0, UVC_BUF_STATE_IDLE = 0,
UVC_BUF_STATE_QUEUED = 1, UVC_BUF_STATE_QUEUED = 1,
...@@ -422,26 +402,31 @@ struct uvc_video_queue { ...@@ -422,26 +402,31 @@ struct uvc_video_queue {
struct list_head irqqueue; struct list_head irqqueue;
}; };
struct uvc_video_device { struct uvc_streaming {
struct list_head list;
struct uvc_device *dev; struct uvc_device *dev;
struct video_device *vdev; struct video_device *vdev;
atomic_t active; atomic_t active;
unsigned int frozen : 1;
struct list_head iterms; /* Input terminals */ struct usb_interface *intf;
struct uvc_entity *oterm; /* Output terminal */ int intfnum;
struct uvc_entity *sterm; /* USB streaming terminal */ __u16 maxpsize;
struct uvc_entity *processing;
struct uvc_entity *selector;
struct list_head extensions;
struct mutex ctrl_mutex;
struct uvc_video_queue queue; struct uvc_streaming_header header;
enum v4l2_buf_type type;
unsigned int nformats;
struct uvc_format *format;
/* Video streaming object, must always be non-NULL. */ struct uvc_streaming_control ctrl;
struct uvc_streaming *streaming; struct uvc_format *cur_format;
struct uvc_frame *cur_frame;
struct mutex mutex;
void (*decode) (struct urb *urb, struct uvc_video_device *video, unsigned int frozen : 1;
struct uvc_video_queue queue;
void (*decode) (struct urb *urb, struct uvc_streaming *video,
struct uvc_buffer *buf); struct uvc_buffer *buf);
/* Context data used by the bulk completion handler. */ /* Context data used by the bulk completion handler. */
...@@ -461,6 +446,18 @@ struct uvc_video_device { ...@@ -461,6 +446,18 @@ struct uvc_video_device {
__u8 last_fid; __u8 last_fid;
}; };
struct uvc_video_device {
struct uvc_device *dev;
struct list_head iterms; /* Input terminals */
struct uvc_entity *oterm; /* Output terminal */
struct uvc_entity *sterm; /* USB streaming terminal */
struct uvc_entity *processing;
struct uvc_entity *selector;
struct list_head extensions;
struct mutex ctrl_mutex;
};
enum uvc_device_state { enum uvc_device_state {
UVC_DEV_DISCONNECTED = 1, UVC_DEV_DISCONNECTED = 1,
}; };
...@@ -486,15 +483,15 @@ struct uvc_device { ...@@ -486,15 +483,15 @@ struct uvc_device {
struct uvc_video_device video; struct uvc_video_device video;
/* Video Streaming interfaces */
struct list_head streams;
/* Status Interrupt Endpoint */ /* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep; struct usb_host_endpoint *int_ep;
struct urb *int_urb; struct urb *int_urb;
__u8 *status; __u8 *status;
struct input_dev *input; struct input_dev *input;
char input_phys[64]; char input_phys[64];
/* Video Streaming interfaces */
struct list_head streaming;
}; };
enum uvc_handle_state { enum uvc_handle_state {
...@@ -503,7 +500,8 @@ enum uvc_handle_state { ...@@ -503,7 +500,8 @@ enum uvc_handle_state {
}; };
struct uvc_fh { struct uvc_fh {
struct uvc_video_device *device; struct uvc_video_device *video;
struct uvc_streaming *stream;
enum uvc_handle_state state; enum uvc_handle_state state;
}; };
...@@ -600,13 +598,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) ...@@ -600,13 +598,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
extern const struct v4l2_file_operations uvc_fops; extern const struct v4l2_file_operations uvc_fops;
/* Video */ /* Video */
extern int uvc_video_init(struct uvc_video_device *video); extern int uvc_video_init(struct uvc_streaming *stream);
extern int uvc_video_suspend(struct uvc_video_device *video); extern int uvc_video_suspend(struct uvc_streaming *stream);
extern int uvc_video_resume(struct uvc_video_device *video); extern int uvc_video_resume(struct uvc_streaming *stream);
extern int uvc_video_enable(struct uvc_video_device *video, int enable); extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
extern int uvc_probe_video(struct uvc_video_device *video, extern int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe); struct uvc_streaming_control *probe);
extern int uvc_commit_video(struct uvc_video_device *video, extern int uvc_commit_video(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl); struct uvc_streaming_control *ctrl);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size); __u8 intfnum, __u8 cs, void *data, __u16 size);
...@@ -660,7 +658,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint( ...@@ -660,7 +658,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
struct usb_host_interface *alts, __u8 epaddr); struct usb_host_interface *alts, __u8 epaddr);
/* Quirks support */ /* Quirks support */
void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf); struct uvc_buffer *buf);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment