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
 
                 "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");
 
        /* 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;
        }
 
 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;
 {
        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
            !(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 &&
 
        cx18_stream_init(cx, type);
 
-       if (minor_offset == -1)
+       if (num_offset == -1)
                return 0;
 
        /* allocate and initialize the v4l2 video device structure */
        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;
 {
        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.
        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;
        }
 
 
        /*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);
        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)) {
                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);
 
 #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];
                 "\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");
 
 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;
 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
        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 &&
        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;
 {
        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;
 
  */
 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)
 {
 
        /* 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 */
                                        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 */
 
        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:
                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;
        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__);
 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;
 
 
 #define VIDEO_MAJOR    81
 
-/* Minor device allocation */
-#define MINOR_VFL_TYPE_GRABBER_MIN   0
-#define MINOR_VFL_TYPE_GRABBER_MAX  63
-#define MINOR_VFL_TYPE_RADIO_MIN    64
-#define MINOR_VFL_TYPE_RADIO_MAX   127
-#define MINOR_VFL_TYPE_VTX_MIN     192
-#define MINOR_VFL_TYPE_VTX_MAX     223
-#define MINOR_VFL_TYPE_VBI_MIN     224
-#define MINOR_VFL_TYPE_VBI_MAX     255
-
 #define VFL_TYPE_GRABBER       0
 #define VFL_TYPE_VBI           1
 #define VFL_TYPE_RADIO         2
 #define VFL_TYPE_VTX           3
+#define VFL_TYPE_MAX           4
 
 struct v4l2_ioctl_callbacks;
 
        char name[32];
        int vfl_type;
        int minor;
+       u16 num;
        /* attribute to differentiate multiple indices on one physical device */
        int index;