Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Hans Verkuil and committed by
Mauro Carvalho Chehab
dd89601d e86a93dc

+128 -88
+9
drivers/media/video/Kconfig
··· 72 72 V4L devices. 73 73 In doubt, say N. 74 74 75 + config VIDEO_FIXED_MINOR_RANGES 76 + bool "Enable old-style fixed minor ranges for video devices" 77 + default n 78 + ---help--- 79 + Say Y here to enable the old-style fixed-range minor assignments. 80 + Only useful if you rely on the old behavior and use mknod instead of udev. 81 + 82 + When in doubt, say N. 83 + 75 84 config VIDEO_HELPER_CHIPS_AUTO 76 85 bool "Autoselect pertinent encoders/decoders and other helper chips" 77 86 default y
+2 -2
drivers/media/video/cx18/cx18-driver.c
··· 175 175 "Encoder PCM buffers (in MB)\n" 176 176 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); 177 177 178 - MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card"); 178 + MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card"); 179 179 180 180 MODULE_AUTHOR("Hans Verkuil"); 181 181 MODULE_DESCRIPTION("CX23418 driver"); ··· 959 959 960 960 /* Validate parameters */ 961 961 if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { 962 - printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n", 962 + printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n", 963 963 CX18_MAX_CARDS - 1); 964 964 return -1; 965 965 }
+22 -19
drivers/media/video/cx18/cx18-streams.c
··· 57 57 static struct { 58 58 const char *name; 59 59 int vfl_type; 60 - int minor_offset; 60 + int num_offset; 61 61 int dma; 62 62 enum v4l2_buf_type buf_type; 63 63 struct file_operations *fops; ··· 144 144 { 145 145 struct cx18_stream *s = &cx->streams[type]; 146 146 u32 cap = cx->v4l2_cap; 147 - int minor_offset = cx18_stream_info[type].minor_offset; 148 - int minor; 147 + int num_offset = cx18_stream_info[type].num_offset; 148 + int num = cx->num + cx18_first_minor + num_offset; 149 149 150 150 /* These four fields are always initialized. If v4l2dev == NULL, then 151 151 this stream is not in use. In that case no other fields but these ··· 164 164 !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) 165 165 return 0; 166 166 167 - /* card number + user defined offset + device offset */ 168 - minor = cx->num + cx18_first_minor + minor_offset; 169 - 170 167 /* User explicitly selected 0 buffers for these streams, so don't 171 168 create them. */ 172 169 if (cx18_stream_info[type].dma != PCI_DMA_NONE && ··· 174 177 175 178 cx18_stream_init(cx, type); 176 179 177 - if (minor_offset == -1) 180 + if (num_offset == -1) 178 181 return 0; 179 182 180 183 /* allocate and initialize the v4l2 video device structure */ ··· 188 191 snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d", 189 192 cx->num); 190 193 191 - s->v4l2dev->minor = minor; 194 + s->v4l2dev->num = num; 192 195 s->v4l2dev->parent = &cx->dev->dev; 193 196 s->v4l2dev->fops = cx18_stream_info[type].fops; 194 197 s->v4l2dev->release = video_device_release; ··· 224 227 { 225 228 struct cx18_stream *s = &cx->streams[type]; 226 229 int vfl_type = cx18_stream_info[type].vfl_type; 227 - int minor; 230 + int num; 228 231 229 232 /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something? 230 233 * We need a VFL_TYPE_TS defined. ··· 242 245 if (s->v4l2dev == NULL) 243 246 return 0; 244 247 245 - minor = s->v4l2dev->minor; 248 + num = s->v4l2dev->num; 249 + /* card number + user defined offset + device offset */ 250 + if (type != CX18_ENC_STREAM_TYPE_MPG) { 251 + struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; 252 + 253 + if (s_mpg->v4l2dev) 254 + num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset; 255 + } 246 256 247 257 /* Register device. First try the desired minor, then any free one. */ 248 - if (video_register_device(s->v4l2dev, vfl_type, minor) && 249 - video_register_device(s->v4l2dev, vfl_type, -1)) { 250 - CX18_ERR("Couldn't register v4l2 device for %s minor %d\n", 251 - s->name, minor); 258 + if (video_register_device(s->v4l2dev, vfl_type, num)) { 259 + CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n", 260 + s->name, num); 252 261 video_device_release(s->v4l2dev); 253 262 s->v4l2dev = NULL; 254 263 return -ENOMEM; 255 264 } 256 - minor = s->v4l2dev->minor; 265 + num = s->v4l2dev->num; 257 266 258 267 switch (vfl_type) { 259 268 case VFL_TYPE_GRABBER: 260 269 CX18_INFO("Registered device video%d for %s (%d MB)\n", 261 - minor, s->name, cx->options.megabytes[type]); 270 + num, s->name, cx->options.megabytes[type]); 262 271 break; 263 272 264 273 case VFL_TYPE_RADIO: 265 274 CX18_INFO("Registered device radio%d for %s\n", 266 - minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); 275 + num, s->name); 267 276 break; 268 277 269 278 case VFL_TYPE_VBI: 270 279 if (cx->options.megabytes[type]) 271 280 CX18_INFO("Registered device vbi%d for %s (%d MB)\n", 272 - minor - MINOR_VFL_TYPE_VBI_MIN, 281 + num, 273 282 s->name, cx->options.megabytes[type]); 274 283 else 275 284 CX18_INFO("Registered device vbi%d for %s\n", 276 - minor - MINOR_VFL_TYPE_VBI_MIN, s->name); 285 + num, s->name); 277 286 break; 278 287 } 279 288
+3 -5
drivers/media/video/em28xx/em28xx-video.c
··· 1600 1600 /*FIXME: I2C IR should be disconnected */ 1601 1601 1602 1602 em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n", 1603 - dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, 1604 - dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); 1603 + dev->vdev->num, dev->vbi_dev->num); 1605 1604 list_del(&dev->devlist); 1606 1605 if (dev->sbutton_input_dev) 1607 1606 em28xx_deregister_snapshot_button(dev); ··· 2072 2073 video_mux(dev, 0); 2073 2074 2074 2075 em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", 2075 - dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, 2076 - dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); 2076 + dev->vdev->num, dev->vbi_dev->num); 2077 2077 2078 2078 mutex_lock(&em28xx_extension_devlist_lock); 2079 2079 if (!list_empty(&em28xx_extension_devlist)) { ··· 2272 2274 em28xx_warn 2273 2275 ("device /dev/video%d is open! Deregistration and memory " 2274 2276 "deallocation are deferred on close.\n", 2275 - dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN); 2277 + dev->vdev->num); 2276 2278 2277 2279 dev->state |= DEV_MISCONFIGURED; 2278 2280 em28xx_uninit_isoc(dev);
+3 -3
drivers/media/video/ivtv/ivtv-driver.c
··· 61 61 #include "tuner-xc2028.h" 62 62 63 63 /* var to keep track of the number of array elements in use */ 64 - int ivtv_cards_active = 0; 64 + int ivtv_cards_active; 65 65 66 66 /* If you have already X v4l cards, then set this to X. This way 67 67 the device numbers stay matched. Example: you have a WinTV card 68 68 without radio and a PVR-350 with. Normally this would give a 69 69 video1 device together with a radio0 device for the PVR. By 70 70 setting this to 1 you ensure that radio0 is now also radio1. */ 71 - int ivtv_first_minor = 0; 71 + int ivtv_first_minor; 72 72 73 73 /* Master variable for all ivtv info */ 74 74 struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; ··· 251 251 "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" 252 252 "\t\t\tDefault is autodetect"); 253 253 254 - MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card"); 254 + MODULE_PARM_DESC(ivtv_first_minor, "Set kernel number assigned to first card"); 255 255 256 256 MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); 257 257 MODULE_DESCRIPTION("CX23415/CX23416 driver");
+22 -18
drivers/media/video/ivtv/ivtv-streams.c
··· 75 75 static struct { 76 76 const char *name; 77 77 int vfl_type; 78 - int minor_offset; 78 + int num_offset; 79 79 int dma, pio; 80 80 enum v4l2_buf_type buf_type; 81 81 const struct file_operations *fops; ··· 171 171 static int ivtv_prep_dev(struct ivtv *itv, int type) 172 172 { 173 173 struct ivtv_stream *s = &itv->streams[type]; 174 - int minor_offset = ivtv_stream_info[type].minor_offset; 175 - int minor; 174 + int num_offset = ivtv_stream_info[type].num_offset; 175 + int num = itv->num + ivtv_first_minor + num_offset; 176 176 177 177 /* These four fields are always initialized. If v4l2dev == NULL, then 178 178 this stream is not in use. In that case no other fields but these ··· 187 187 return 0; 188 188 if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) 189 189 return 0; 190 - 191 - /* card number + user defined offset + device offset */ 192 - minor = itv->num + ivtv_first_minor + minor_offset; 193 190 194 191 /* User explicitly selected 0 buffers for these streams, so don't 195 192 create them. */ ··· 208 211 snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", 209 212 itv->num, s->name); 210 213 211 - s->v4l2dev->minor = minor; 214 + s->v4l2dev->num = num; 212 215 s->v4l2dev->parent = &itv->dev->dev; 213 216 s->v4l2dev->fops = ivtv_stream_info[type].fops; 214 217 s->v4l2dev->release = video_device_release; ··· 247 250 { 248 251 struct ivtv_stream *s = &itv->streams[type]; 249 252 int vfl_type = ivtv_stream_info[type].vfl_type; 250 - int minor; 253 + int num; 251 254 252 255 if (s->v4l2dev == NULL) 253 256 return 0; 254 257 255 - minor = s->v4l2dev->minor; 258 + num = s->v4l2dev->num; 259 + /* card number + user defined offset + device offset */ 260 + if (type != IVTV_ENC_STREAM_TYPE_MPG) { 261 + struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; 262 + 263 + if (s_mpg->v4l2dev) 264 + num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset; 265 + } 266 + 256 267 /* Register device. First try the desired minor, then any free one. */ 257 - if (video_register_device(s->v4l2dev, vfl_type, minor) && 258 - video_register_device(s->v4l2dev, vfl_type, -1)) { 259 - IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n", 260 - s->name, minor); 268 + if (video_register_device(s->v4l2dev, vfl_type, num)) { 269 + IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n", 270 + s->name, num); 261 271 video_device_release(s->v4l2dev); 262 272 s->v4l2dev = NULL; 263 273 return -ENOMEM; 264 274 } 275 + num = s->v4l2dev->num; 265 276 266 277 switch (vfl_type) { 267 278 case VFL_TYPE_GRABBER: 268 279 IVTV_INFO("Registered device video%d for %s (%d kB)\n", 269 - s->v4l2dev->minor, s->name, itv->options.kilobytes[type]); 280 + num, s->name, itv->options.kilobytes[type]); 270 281 break; 271 282 case VFL_TYPE_RADIO: 272 283 IVTV_INFO("Registered device radio%d for %s\n", 273 - s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); 284 + num, s->name); 274 285 break; 275 286 case VFL_TYPE_VBI: 276 287 if (itv->options.kilobytes[type]) 277 288 IVTV_INFO("Registered device vbi%d for %s (%d kB)\n", 278 - s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, 279 - s->name, itv->options.kilobytes[type]); 289 + num, s->name, itv->options.kilobytes[type]); 280 290 else 281 291 IVTV_INFO("Registered device vbi%d for %s\n", 282 - s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name); 292 + num, s->name); 283 293 break; 284 294 } 285 295 return 0;
+65 -31
drivers/media/video/v4l2-dev.c
··· 65 65 */ 66 66 static struct video_device *video_device[VIDEO_NUM_DEVICES]; 67 67 static DEFINE_MUTEX(videodev_lock); 68 + static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); 68 69 69 70 struct video_device *video_device_alloc(void) 70 71 { ··· 100 99 101 100 /* Free up this device for reuse */ 102 101 video_device[vfd->minor] = NULL; 102 + clear_bit(vfd->num, video_nums[vfd->vfl_type]); 103 103 mutex_unlock(&videodev_lock); 104 104 105 105 /* Release the character device */ ··· 219 217 int index) 220 218 { 221 219 int i = 0; 222 - int base; 223 - int end; 224 220 int ret; 225 - char *name_base; 221 + int minor_offset = 0; 222 + int minor_cnt = VIDEO_NUM_DEVICES; 223 + const char *name_base; 226 224 void *priv = video_get_drvdata(vfd); 227 225 228 226 /* the release callback MUST be present */ ··· 233 231 234 232 switch (type) { 235 233 case VFL_TYPE_GRABBER: 236 - base = MINOR_VFL_TYPE_GRABBER_MIN; 237 - end = MINOR_VFL_TYPE_GRABBER_MAX+1; 238 234 name_base = "video"; 239 235 break; 240 236 case VFL_TYPE_VTX: 241 - base = MINOR_VFL_TYPE_VTX_MIN; 242 - end = MINOR_VFL_TYPE_VTX_MAX+1; 243 237 name_base = "vtx"; 244 238 break; 245 239 case VFL_TYPE_VBI: 246 - base = MINOR_VFL_TYPE_VBI_MIN; 247 - end = MINOR_VFL_TYPE_VBI_MAX+1; 248 240 name_base = "vbi"; 249 241 break; 250 242 case VFL_TYPE_RADIO: 251 - base = MINOR_VFL_TYPE_RADIO_MIN; 252 - end = MINOR_VFL_TYPE_RADIO_MAX+1; 253 243 name_base = "radio"; 254 244 break; 255 245 default: ··· 250 256 return -EINVAL; 251 257 } 252 258 259 + vfd->vfl_type = type; 260 + 261 + #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES 262 + /* Keep the ranges for the first four types for historical 263 + * reasons. 264 + * Newer devices (not yet in place) should use the range 265 + * of 128-191 and just pick the first free minor there 266 + * (new style). */ 267 + switch (type) { 268 + case VFL_TYPE_GRABBER: 269 + minor_offset = 0; 270 + minor_cnt = 64; 271 + break; 272 + case VFL_TYPE_RADIO: 273 + minor_offset = 64; 274 + minor_cnt = 64; 275 + break; 276 + case VFL_TYPE_VTX: 277 + minor_offset = 192; 278 + minor_cnt = 32; 279 + break; 280 + case VFL_TYPE_VBI: 281 + minor_offset = 224; 282 + minor_cnt = 32; 283 + break; 284 + default: 285 + minor_offset = 128; 286 + minor_cnt = 64; 287 + break; 288 + } 289 + #endif 290 + 253 291 /* Initialize the character device */ 254 292 cdev_init(&vfd->cdev, vfd->fops); 255 293 vfd->cdev.owner = vfd->fops->owner; 256 294 /* pick a minor number */ 257 295 mutex_lock(&videodev_lock); 258 - if (nr >= 0 && nr < end-base) { 259 - /* use the one the driver asked for */ 260 - i = base + nr; 261 - if (NULL != video_device[i]) { 262 - mutex_unlock(&videodev_lock); 263 - return -ENFILE; 264 - } 265 - } else { 266 - /* use first free */ 267 - for (i = base; i < end; i++) 268 - if (NULL == video_device[i]) 269 - break; 270 - if (i == end) { 271 - mutex_unlock(&videodev_lock); 272 - return -ENFILE; 273 - } 296 + nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); 297 + if (nr == minor_cnt) 298 + nr = find_first_zero_bit(video_nums[type], minor_cnt); 299 + if (nr == minor_cnt) { 300 + printk(KERN_ERR "could not get a free kernel number\n"); 301 + mutex_unlock(&videodev_lock); 302 + return -ENFILE; 274 303 } 275 - video_device[i] = vfd; 276 - vfd->vfl_type = type; 277 - vfd->minor = i; 304 + #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES 305 + /* 1-on-1 mapping of kernel number to minor number */ 306 + i = nr; 307 + #else 308 + /* The kernel number and minor numbers are independent */ 309 + for (i = 0; i < VIDEO_NUM_DEVICES; i++) 310 + if (video_device[i] == NULL) 311 + break; 312 + if (i == VIDEO_NUM_DEVICES) { 313 + mutex_unlock(&videodev_lock); 314 + printk(KERN_ERR "could not get a free minor\n"); 315 + return -ENFILE; 316 + } 317 + #endif 318 + vfd->minor = i + minor_offset; 319 + vfd->num = nr; 320 + set_bit(nr, video_nums[type]); 321 + BUG_ON(video_device[vfd->minor]); 322 + video_device[vfd->minor] = vfd; 278 323 279 324 ret = get_index(vfd, index); 280 325 vfd->index = ret; ··· 339 306 vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); 340 307 if (vfd->parent) 341 308 vfd->dev.parent = vfd->parent; 342 - sprintf(vfd->dev.bus_id, "%s%d", name_base, i - base); 309 + sprintf(vfd->dev.bus_id, "%s%d", name_base, nr); 343 310 ret = device_register(&vfd->dev); 344 311 if (ret < 0) { 345 312 printk(KERN_ERR "%s: device_register failed\n", __func__); ··· 357 324 fail_minor: 358 325 mutex_lock(&videodev_lock); 359 326 video_device[vfd->minor] = NULL; 327 + clear_bit(vfd->num, video_nums[type]); 360 328 mutex_unlock(&videodev_lock); 361 329 vfd->minor = -1; 362 330 return ret;
+2 -10
include/media/v4l2-dev.h
··· 18 18 19 19 #define VIDEO_MAJOR 81 20 20 21 - /* Minor device allocation */ 22 - #define MINOR_VFL_TYPE_GRABBER_MIN 0 23 - #define MINOR_VFL_TYPE_GRABBER_MAX 63 24 - #define MINOR_VFL_TYPE_RADIO_MIN 64 25 - #define MINOR_VFL_TYPE_RADIO_MAX 127 26 - #define MINOR_VFL_TYPE_VTX_MIN 192 27 - #define MINOR_VFL_TYPE_VTX_MAX 223 28 - #define MINOR_VFL_TYPE_VBI_MIN 224 29 - #define MINOR_VFL_TYPE_VBI_MAX 255 30 - 31 21 #define VFL_TYPE_GRABBER 0 32 22 #define VFL_TYPE_VBI 1 33 23 #define VFL_TYPE_RADIO 2 34 24 #define VFL_TYPE_VTX 3 25 + #define VFL_TYPE_MAX 4 35 26 36 27 struct v4l2_ioctl_callbacks; 37 28 ··· 47 56 char name[32]; 48 57 int vfl_type; 49 58 int minor; 59 + u16 num; 50 60 /* attribute to differentiate multiple indices on one physical device */ 51 61 int index; 52 62