Skip to content

Commit

Permalink
V4L/DVB (9133): v4l: disconnect kernel number from minor
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Hans Verkuil authored and Mauro Carvalho Chehab committed Oct 12, 2008
1 parent e86a93d commit dd89601
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 88 deletions.
9 changes: 9 additions & 0 deletions drivers/media/video/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ config VIDEO_ADV_DEBUG
V4L devices.
In doubt, say N.

config VIDEO_FIXED_MINOR_RANGES
bool "Enable old-style fixed minor ranges for video devices"
default n
---help---
Say Y here to enable the old-style fixed-range minor assignments.
Only useful if you rely on the old behavior and use mknod instead of udev.

When in doubt, say N.

config VIDEO_HELPER_CHIPS_AUTO
bool "Autoselect pertinent encoders/decoders and other helper chips"
default y
Expand Down
4 changes: 2 additions & 2 deletions drivers/media/video/cx18/cx18-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ MODULE_PARM_DESC(enc_pcm_buffers,
"Encoder PCM buffers (in MB)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));

MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card");
MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");

MODULE_AUTHOR("Hans Verkuil");
MODULE_DESCRIPTION("CX23418 driver");
Expand Down Expand Up @@ -959,7 +959,7 @@ static int module_start(void)

/* Validate parameters */
if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n",
printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
CX18_MAX_CARDS - 1);
return -1;
}
Expand Down
41 changes: 22 additions & 19 deletions drivers/media/video/cx18/cx18-streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static struct file_operations cx18_v4l2_enc_fops = {
static struct {
const char *name;
int vfl_type;
int minor_offset;
int num_offset;
int dma;
enum v4l2_buf_type buf_type;
struct file_operations *fops;
Expand Down Expand Up @@ -144,8 +144,8 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
u32 cap = cx->v4l2_cap;
int minor_offset = cx18_stream_info[type].minor_offset;
int minor;
int num_offset = cx18_stream_info[type].num_offset;
int num = cx->num + cx18_first_minor + num_offset;

/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
Expand All @@ -164,9 +164,6 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
!(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
return 0;

/* card number + user defined offset + device offset */
minor = cx->num + cx18_first_minor + minor_offset;

/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
Expand All @@ -177,7 +174,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)

cx18_stream_init(cx, type);

if (minor_offset == -1)
if (num_offset == -1)
return 0;

/* allocate and initialize the v4l2 video device structure */
Expand All @@ -191,7 +188,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d",
cx->num);

s->v4l2dev->minor = minor;
s->v4l2dev->num = num;
s->v4l2dev->parent = &cx->dev->dev;
s->v4l2dev->fops = cx18_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
Expand Down Expand Up @@ -227,7 +224,7 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
int vfl_type = cx18_stream_info[type].vfl_type;
int minor;
int num;

/* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
* We need a VFL_TYPE_TS defined.
Expand All @@ -245,38 +242,44 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
if (s->v4l2dev == NULL)
return 0;

minor = s->v4l2dev->minor;
num = s->v4l2dev->num;
/* card number + user defined offset + device offset */
if (type != CX18_ENC_STREAM_TYPE_MPG) {
struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];

if (s_mpg->v4l2dev)
num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset;
}

/* Register device. First try the desired minor, then any free one. */
if (video_register_device(s->v4l2dev, vfl_type, minor) &&
video_register_device(s->v4l2dev, vfl_type, -1)) {
CX18_ERR("Couldn't register v4l2 device for %s minor %d\n",
s->name, minor);
if (video_register_device(s->v4l2dev, vfl_type, num)) {
CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
s->name, num);
video_device_release(s->v4l2dev);
s->v4l2dev = NULL;
return -ENOMEM;
}
minor = s->v4l2dev->minor;
num = s->v4l2dev->num;

switch (vfl_type) {
case VFL_TYPE_GRABBER:
CX18_INFO("Registered device video%d for %s (%d MB)\n",
minor, s->name, cx->options.megabytes[type]);
num, s->name, cx->options.megabytes[type]);
break;

case VFL_TYPE_RADIO:
CX18_INFO("Registered device radio%d for %s\n",
minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
num, s->name);
break;

case VFL_TYPE_VBI:
if (cx->options.megabytes[type])
CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
minor - MINOR_VFL_TYPE_VBI_MIN,
num,
s->name, cx->options.megabytes[type]);
else
CX18_INFO("Registered device vbi%d for %s\n",
minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
num, s->name);
break;
}

Expand Down
8 changes: 3 additions & 5 deletions drivers/media/video/em28xx/em28xx-video.c
Original file line number Diff line number Diff line change
Expand Up @@ -1600,8 +1600,7 @@ static void em28xx_release_resources(struct em28xx *dev)
/*FIXME: I2C IR should be disconnected */

em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
dev->vdev->num, dev->vbi_dev->num);
list_del(&dev->devlist);
if (dev->sbutton_input_dev)
em28xx_deregister_snapshot_button(dev);
Expand Down Expand Up @@ -2073,8 +2072,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
video_mux(dev, 0);

em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
dev->vdev->num, dev->vbi_dev->num);

mutex_lock(&em28xx_extension_devlist_lock);
if (!list_empty(&em28xx_extension_devlist)) {
Expand Down Expand Up @@ -2274,7 +2272,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
em28xx_warn
("device /dev/video%d is open! Deregistration and memory "
"deallocation are deferred on close.\n",
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
dev->vdev->num);

dev->state |= DEV_MISCONFIGURED;
em28xx_uninit_isoc(dev);
Expand Down
6 changes: 3 additions & 3 deletions drivers/media/video/ivtv/ivtv-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@
#include "tuner-xc2028.h"

/* var to keep track of the number of array elements in use */
int ivtv_cards_active = 0;
int ivtv_cards_active;

/* If you have already X v4l cards, then set this to X. This way
the device numbers stay matched. Example: you have a WinTV card
without radio and a PVR-350 with. Normally this would give a
video1 device together with a radio0 device for the PVR. By
setting this to 1 you ensure that radio0 is now also radio1. */
int ivtv_first_minor = 0;
int ivtv_first_minor;

/* Master variable for all ivtv info */
struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
Expand Down Expand Up @@ -251,7 +251,7 @@ MODULE_PARM_DESC(newi2c,
"\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
"\t\t\tDefault is autodetect");

MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card");
MODULE_PARM_DESC(ivtv_first_minor, "Set kernel number assigned to first card");

MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
MODULE_DESCRIPTION("CX23415/CX23416 driver");
Expand Down
40 changes: 22 additions & 18 deletions drivers/media/video/ivtv/ivtv-streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static const struct file_operations ivtv_v4l2_dec_fops = {
static struct {
const char *name;
int vfl_type;
int minor_offset;
int num_offset;
int dma, pio;
enum v4l2_buf_type buf_type;
const struct file_operations *fops;
Expand Down Expand Up @@ -171,8 +171,8 @@ static void ivtv_stream_init(struct ivtv *itv, int type)
static int ivtv_prep_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int minor_offset = ivtv_stream_info[type].minor_offset;
int minor;
int num_offset = ivtv_stream_info[type].num_offset;
int num = itv->num + ivtv_first_minor + num_offset;

/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
Expand All @@ -188,9 +188,6 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return 0;

/* card number + user defined offset + device offset */
minor = itv->num + ivtv_first_minor + minor_offset;

/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
Expand All @@ -211,7 +208,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
itv->num, s->name);

s->v4l2dev->minor = minor;
s->v4l2dev->num = num;
s->v4l2dev->parent = &itv->dev->dev;
s->v4l2dev->fops = ivtv_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
Expand Down Expand Up @@ -250,39 +247,46 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int vfl_type = ivtv_stream_info[type].vfl_type;
int minor;
int num;

if (s->v4l2dev == NULL)
return 0;

minor = s->v4l2dev->minor;
num = s->v4l2dev->num;
/* card number + user defined offset + device offset */
if (type != IVTV_ENC_STREAM_TYPE_MPG) {
struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];

if (s_mpg->v4l2dev)
num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset;
}

/* Register device. First try the desired minor, then any free one. */
if (video_register_device(s->v4l2dev, vfl_type, minor) &&
video_register_device(s->v4l2dev, vfl_type, -1)) {
IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
s->name, minor);
if (video_register_device(s->v4l2dev, vfl_type, num)) {
IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
s->name, num);
video_device_release(s->v4l2dev);
s->v4l2dev = NULL;
return -ENOMEM;
}
num = s->v4l2dev->num;

switch (vfl_type) {
case VFL_TYPE_GRABBER:
IVTV_INFO("Registered device video%d for %s (%d kB)\n",
s->v4l2dev->minor, s->name, itv->options.kilobytes[type]);
num, s->name, itv->options.kilobytes[type]);
break;
case VFL_TYPE_RADIO:
IVTV_INFO("Registered device radio%d for %s\n",
s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
num, s->name);
break;
case VFL_TYPE_VBI:
if (itv->options.kilobytes[type])
IVTV_INFO("Registered device vbi%d for %s (%d kB)\n",
s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN,
s->name, itv->options.kilobytes[type]);
num, s->name, itv->options.kilobytes[type]);
else
IVTV_INFO("Registered device vbi%d for %s\n",
s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
num, s->name);
break;
}
return 0;
Expand Down
Loading

0 comments on commit dd89601

Please sign in to comment.