You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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: Laurent Pinchart <laurent.pinchart@skynet.be> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
committed by
Mauro Carvalho Chehab
parent
6c428b578b
commit
35f02a681b
@@ -551,6 +551,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
|
||||
}
|
||||
|
||||
mutex_init(&streaming->mutex);
|
||||
streaming->dev = dev;
|
||||
streaming->intf = usb_get_intf(intf);
|
||||
streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||
|
||||
@@ -751,7 +752,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
|
||||
streaming->maxpsize = psize;
|
||||
}
|
||||
|
||||
list_add_tail(&streaming->list, &dev->streaming);
|
||||
list_add_tail(&streaming->list, &dev->streams);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@@ -1167,15 +1168,77 @@ next_descriptor:
|
||||
*/
|
||||
static void uvc_unregister_video(struct uvc_device *dev)
|
||||
{
|
||||
if (dev->video.vdev) {
|
||||
if (dev->video.vdev->minor == -1)
|
||||
video_device_release(dev->video.vdev);
|
||||
struct uvc_streaming *streaming;
|
||||
|
||||
list_for_each_entry(streaming, &dev->streams, list) {
|
||||
if (streaming->vdev == NULL)
|
||||
continue;
|
||||
|
||||
if (streaming->vdev->minor == -1)
|
||||
video_device_release(streaming->vdev);
|
||||
else
|
||||
video_unregister_device(dev->video.vdev);
|
||||
dev->video.vdev = NULL;
|
||||
video_unregister_device(streaming->vdev);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the UVC descriptors to locate a chain starting at an Output Terminal
|
||||
* and containing the following units:
|
||||
@@ -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
|
||||
* only. The terminal and units must match the following structure:
|
||||
@@ -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
|
||||
* 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;
|
||||
int found = 0, ret;
|
||||
int found = 0;
|
||||
|
||||
/* Check if the control interface matches the structure we expect. */
|
||||
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))
|
||||
continue;
|
||||
@@ -1454,17 +1516,14 @@ static int uvc_register_video(struct uvc_device *dev)
|
||||
if (uvc_scan_chain(&dev->video) < 0)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(streaming, &dev->streaming, list) {
|
||||
if (streaming->header.bTerminalLink ==
|
||||
list_for_each_entry(stream, &dev->streams, list) {
|
||||
if (stream->header.bTerminalLink ==
|
||||
dev->video.sterm->id) {
|
||||
dev->video.streaming = streaming;
|
||||
uvc_register_video(dev, stream);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@@ -1472,55 +1531,6 @@ static int uvc_register_video(struct uvc_device *dev)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1559,7 +1569,7 @@ void uvc_delete(struct kref *kref)
|
||||
kfree(entity);
|
||||
}
|
||||
|
||||
list_for_each_safe(p, n, &dev->streaming) {
|
||||
list_for_each_safe(p, n, &dev->streams) {
|
||||
struct uvc_streaming *streaming;
|
||||
streaming = list_entry(p, struct uvc_streaming, list);
|
||||
usb_driver_release_interface(&uvc_driver.driver,
|
||||
@@ -1593,7 +1603,7 @@ static int uvc_probe(struct usb_interface *intf,
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&dev->entities);
|
||||
INIT_LIST_HEAD(&dev->streaming);
|
||||
INIT_LIST_HEAD(&dev->streams);
|
||||
kref_init(&dev->kref);
|
||||
atomic_set(&dev->users, 0);
|
||||
|
||||
@@ -1634,8 +1644,8 @@ static int uvc_probe(struct usb_interface *intf,
|
||||
if (uvc_ctrl_init_device(dev) < 0)
|
||||
goto error;
|
||||
|
||||
/* Register the video devices. */
|
||||
if (uvc_register_video(dev) < 0)
|
||||
/* Scan the device for video chains and register video devices. */
|
||||
if (uvc_scan_device(dev) < 0)
|
||||
goto error;
|
||||
|
||||
/* Save our data pointer in the interface data. */
|
||||
@@ -1689,6 +1699,7 @@ static void uvc_disconnect(struct usb_interface *intf)
|
||||
static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct uvc_device *dev = usb_get_intfdata(intf);
|
||||
struct uvc_streaming *stream;
|
||||
|
||||
uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
|
||||
intf->cur_altsetting->desc.bInterfaceNumber);
|
||||
@@ -1698,18 +1709,20 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
UVC_SC_VIDEOCONTROL)
|
||||
return uvc_status_suspend(dev);
|
||||
|
||||
if (dev->video.streaming->intf != intf) {
|
||||
uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB "
|
||||
"interface mismatch.\n");
|
||||
return -EINVAL;
|
||||
list_for_each_entry(stream, &dev->streams, list) {
|
||||
if (stream->intf == intf)
|
||||
return uvc_video_suspend(stream);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct uvc_device *dev = usb_get_intfdata(intf);
|
||||
struct uvc_streaming *stream;
|
||||
|
||||
uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
|
||||
intf->cur_altsetting->desc.bInterfaceNumber);
|
||||
@@ -1726,13 +1739,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
|
||||
return uvc_status_resume(dev);
|
||||
}
|
||||
|
||||
if (dev->video.streaming->intf != intf) {
|
||||
uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB "
|
||||
"interface mismatch.\n");
|
||||
return -EINVAL;
|
||||
list_for_each_entry(stream, &dev->streams, list) {
|
||||
if (stream->intf == intf)
|
||||
return uvc_video_resume(stream);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
|
||||
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)
|
||||
{
|
||||
int ret, i;
|
||||
@@ -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.
|
||||
*/
|
||||
do {
|
||||
ret = isight_decode(&video->queue, buf,
|
||||
ret = isight_decode(&stream->queue, buf,
|
||||
urb->transfer_buffer +
|
||||
urb->iso_frame_desc[i].offset,
|
||||
urb->iso_frame_desc[i].actual_length);
|
||||
@@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
|
||||
|
||||
if (buf->state == UVC_BUF_STATE_DONE ||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+214
-201
File diff suppressed because it is too large
Load Diff
@@ -361,26 +361,6 @@ struct uvc_streaming_header {
|
||||
__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 {
|
||||
UVC_BUF_STATE_IDLE = 0,
|
||||
UVC_BUF_STATE_QUEUED = 1,
|
||||
@@ -422,26 +402,31 @@ struct uvc_video_queue {
|
||||
struct list_head irqqueue;
|
||||
};
|
||||
|
||||
struct uvc_video_device {
|
||||
struct uvc_streaming {
|
||||
struct list_head list;
|
||||
struct uvc_device *dev;
|
||||
struct video_device *vdev;
|
||||
atomic_t active;
|
||||
|
||||
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;
|
||||
|
||||
unsigned int frozen : 1;
|
||||
|
||||
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;
|
||||
|
||||
struct uvc_video_queue queue;
|
||||
|
||||
/* Video streaming object, must always be non-NULL. */
|
||||
struct uvc_streaming *streaming;
|
||||
|
||||
void (*decode) (struct urb *urb, struct uvc_video_device *video,
|
||||
void (*decode) (struct urb *urb, struct uvc_streaming *video,
|
||||
struct uvc_buffer *buf);
|
||||
|
||||
/* Context data used by the bulk completion handler. */
|
||||
@@ -461,6 +446,18 @@ struct uvc_video_device {
|
||||
__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 {
|
||||
UVC_DEV_DISCONNECTED = 1,
|
||||
};
|
||||
@@ -486,15 +483,15 @@ struct uvc_device {
|
||||
|
||||
struct uvc_video_device video;
|
||||
|
||||
/* Video Streaming interfaces */
|
||||
struct list_head streams;
|
||||
|
||||
/* Status Interrupt Endpoint */
|
||||
struct usb_host_endpoint *int_ep;
|
||||
struct urb *int_urb;
|
||||
__u8 *status;
|
||||
struct input_dev *input;
|
||||
char input_phys[64];
|
||||
|
||||
/* Video Streaming interfaces */
|
||||
struct list_head streaming;
|
||||
};
|
||||
|
||||
enum uvc_handle_state {
|
||||
@@ -503,7 +500,8 @@ enum uvc_handle_state {
|
||||
};
|
||||
|
||||
struct uvc_fh {
|
||||
struct uvc_video_device *device;
|
||||
struct uvc_video_device *video;
|
||||
struct uvc_streaming *stream;
|
||||
enum uvc_handle_state state;
|
||||
};
|
||||
|
||||
@@ -600,13 +598,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
|
||||
extern const struct v4l2_file_operations uvc_fops;
|
||||
|
||||
/* Video */
|
||||
extern int uvc_video_init(struct uvc_video_device *video);
|
||||
extern int uvc_video_suspend(struct uvc_video_device *video);
|
||||
extern int uvc_video_resume(struct uvc_video_device *video);
|
||||
extern int uvc_video_enable(struct uvc_video_device *video, int enable);
|
||||
extern int uvc_probe_video(struct uvc_video_device *video,
|
||||
extern int uvc_video_init(struct uvc_streaming *stream);
|
||||
extern int uvc_video_suspend(struct uvc_streaming *stream);
|
||||
extern int uvc_video_resume(struct uvc_streaming *stream);
|
||||
extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
|
||||
extern int uvc_probe_video(struct uvc_streaming *stream,
|
||||
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);
|
||||
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
|
||||
__u8 intfnum, __u8 cs, void *data, __u16 size);
|
||||
@@ -660,7 +658,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
|
||||
struct usb_host_interface *alts, __u8 epaddr);
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
Reference in New Issue
Block a user