summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/v4l2-dev.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-10-04 08:36:54 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-10-12 09:37:16 -0200
commitdd89601d47e2eeab7c17b25f2549444751bcffe4 (patch)
treeda21e8b17e49d175f5b8cbd14a772bf00f7fb63a /drivers/media/video/v4l2-dev.c
parente86a93dc3c870c412592c1f298c1425d80c58c6e (diff)
V4L/DVB (9133): v4l: disconnect kernel number from minor
The v4l core creates four different video devices (video, vbi, radio, vtx) and each has its own range of minor numbers. However, modern devices keep increasing the number of devices that they need so a maximum of 64 video devices will not be enough in the future. In addition this scheme makes it very hard to add new device types. This patch disconnects the kernel number allocation (e.g. video0, video1, etc.) from the actual minor number (just pick the first free minor). This allows for much more flexibility in the future. However, it does require the use of udev. For those who cannot use udev a new CONFIG option was created that changes the allocation scheme back to the old behavior. Thanks to Greg KH for suggesting this approach during the 2008 LPC. In addition, several bugs were fixed in the ivtv and cx18 drivers: these drivers try to allocate specific kernel numbers but that scheme contained a bug which caused what should have been e.g. video17 to appear as e.g. video2. Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/v4l2-dev.c')
-rw-r--r--drivers/media/video/v4l2-dev.c96
1 files changed, 65 insertions, 31 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 7addf2fd55d..ccd6566a515 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -65,6 +65,7 @@ static struct device_attribute video_device_attrs[] = {
*/
static struct video_device *video_device[VIDEO_NUM_DEVICES];
static DEFINE_MUTEX(videodev_lock);
+static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
struct video_device *video_device_alloc(void)
{
@@ -99,6 +100,7 @@ static void v4l2_chardev_release(struct kobject *kobj)
/* Free up this device for reuse */
video_device[vfd->minor] = NULL;
+ clear_bit(vfd->num, video_nums[vfd->vfl_type]);
mutex_unlock(&videodev_lock);
/* Release the character device */
@@ -217,10 +219,10 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
int index)
{
int i = 0;
- int base;
- int end;
int ret;
- char *name_base;
+ int minor_offset = 0;
+ int minor_cnt = VIDEO_NUM_DEVICES;
+ const char *name_base;
void *priv = video_get_drvdata(vfd);
/* the release callback MUST be present */
@@ -231,23 +233,15 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
switch (type) {
case VFL_TYPE_GRABBER:
- base = MINOR_VFL_TYPE_GRABBER_MIN;
- end = MINOR_VFL_TYPE_GRABBER_MAX+1;
name_base = "video";
break;
case VFL_TYPE_VTX:
- base = MINOR_VFL_TYPE_VTX_MIN;
- end = MINOR_VFL_TYPE_VTX_MAX+1;
name_base = "vtx";
break;
case VFL_TYPE_VBI:
- base = MINOR_VFL_TYPE_VBI_MIN;
- end = MINOR_VFL_TYPE_VBI_MAX+1;
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
- base = MINOR_VFL_TYPE_RADIO_MIN;
- end = MINOR_VFL_TYPE_RADIO_MAX+1;
name_base = "radio";
break;
default:
@@ -256,31 +250,70 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -EINVAL;
}
+ vfd->vfl_type = type;
+
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* Keep the ranges for the first four types for historical
+ * reasons.
+ * Newer devices (not yet in place) should use the range
+ * of 128-191 and just pick the first free minor there
+ * (new style). */
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ minor_offset = 0;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_RADIO:
+ minor_offset = 64;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_VTX:
+ minor_offset = 192;
+ minor_cnt = 32;
+ break;
+ case VFL_TYPE_VBI:
+ minor_offset = 224;
+ minor_cnt = 32;
+ break;
+ default:
+ minor_offset = 128;
+ minor_cnt = 64;
+ break;
+ }
+#endif
+
/* Initialize the character device */
cdev_init(&vfd->cdev, vfd->fops);
vfd->cdev.owner = vfd->fops->owner;
/* pick a minor number */
mutex_lock(&videodev_lock);
- if (nr >= 0 && nr < end-base) {
- /* use the one the driver asked for */
- i = base + nr;
- if (NULL != video_device[i]) {
- mutex_unlock(&videodev_lock);
- return -ENFILE;
- }
- } else {
- /* use first free */
- for (i = base; i < end; i++)
- if (NULL == video_device[i])
- break;
- if (i == end) {
- mutex_unlock(&videodev_lock);
- return -ENFILE;
- }
+ nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
+ if (nr == minor_cnt)
+ nr = find_first_zero_bit(video_nums[type], minor_cnt);
+ if (nr == minor_cnt) {
+ printk(KERN_ERR "could not get a free kernel number\n");
+ mutex_unlock(&videodev_lock);
+ return -ENFILE;
}
- video_device[i] = vfd;
- vfd->vfl_type = type;
- vfd->minor = i;
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* 1-on-1 mapping of kernel number to minor number */
+ i = nr;
+#else
+ /* The kernel number and minor numbers are independent */
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++)
+ if (video_device[i] == NULL)
+ break;
+ if (i == VIDEO_NUM_DEVICES) {
+ mutex_unlock(&videodev_lock);
+ printk(KERN_ERR "could not get a free minor\n");
+ return -ENFILE;
+ }
+#endif
+ vfd->minor = i + minor_offset;
+ vfd->num = nr;
+ set_bit(nr, video_nums[type]);
+ BUG_ON(video_device[vfd->minor]);
+ video_device[vfd->minor] = vfd;
ret = get_index(vfd, index);
vfd->index = ret;
@@ -306,7 +339,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
if (vfd->parent)
vfd->dev.parent = vfd->parent;
- sprintf(vfd->dev.bus_id, "%s%d", name_base, i - base);
+ sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
ret = device_register(&vfd->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
@@ -324,6 +357,7 @@ del_cdev:
fail_minor:
mutex_lock(&videodev_lock);
video_device[vfd->minor] = NULL;
+ clear_bit(vfd->num, video_nums[type]);
mutex_unlock(&videodev_lock);
vfd->minor = -1;
return ret;