Mercurial > vloopback
comparison vloopback.c @ 0:5f21a4dddc0c
Initial checkin
| author | KennethLavrsen |
|---|---|
| date | Sun, 01 Apr 2007 05:22:43 +0000 |
| parents | |
| children | dc1f4ad7010c |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:5f21a4dddc0c |
|---|---|
| 1 /* | |
| 2 * vloopback.c | |
| 3 * | |
| 4 * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000 | |
| 5 * Additional copyright by the contributing authors in the | |
| 6 * change history below, 2000-2007 | |
| 7 * | |
| 8 * Published under the GNU Public License. | |
| 9 * | |
| 10 * The Video Loopback Device is no longer systematically maintained. | |
| 11 * The project is a secondary project for the project "motion" found at | |
| 12 * http://motion.sourceforge.net/ and | |
| 13 * http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome | |
| 14 * and with the vloopback stored at | |
| 15 * http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice | |
| 16 * | |
| 17 * CHANGE HISTORY | |
| 18 * | |
| 19 * UPDATED: Jeroen Vreeken. | |
| 20 * Added locks for smp machines. UNTESTED! | |
| 21 * Made the driver much more cpu friendly by using | |
| 22 * a wait queue. | |
| 23 * Went from vmalloc to rvmalloc (yes, I stole the code | |
| 24 * like everybody else) and implemented mmap. | |
| 25 * Implemented VIDIOCGUNIT and removed size/palette checks | |
| 26 * in VIDIOCSYNC. | |
| 27 * Cleaned up a lot of code. | |
| 28 * Changed locks to semaphores. | |
| 29 * Disabled changing size while somebody is using mmap | |
| 30 * Changed mapped check to open check, also don't allow | |
| 31 * a open for write while somebody is reading. | |
| 32 * Added /proc support | |
| 33 * Set dumped count to zero at open. | |
| 34 * Modified /proc layout (added vloopbacks entry) | |
| 35 * | |
| 36 * 05.10.00 (MTS) Added Linux 2.2 support | |
| 37 * 06.10.00 (J Vreeken) Fixed 2.2 support to make things work under 2.4 again. | |
| 38 * 17.10.00 (J Vreeken) Added zero copy mode | |
| 39 * 19.10.00 (J Vreeken) Added SIGIO on device close. | |
| 40 * 24.10.00 (J Vreeken) Modified 2.2 stuff and removed spinlock.h | |
| 41 * released 0.81 | |
| 42 * 27.10.00 (J Vreeken) Implemented poll | |
| 43 * released 0.82 | |
| 44 * 17.01.01 (J Vreeken) support for xawtv | |
| 45 * Implemented VIDIOCGFBUF | |
| 46 * Additional checks on framebuffer freeing. | |
| 47 * released 0.83 | |
| 48 * 31.01.01 (J Vreeken) Removed need for 'struct ioctl', use _IOC_SIZE() and | |
| 49 * IOC_IN instead. | |
| 50 * Change the ioctlnr passing to 'unsigned long int' | |
| 51 * Instead of just one byte. | |
| 52 * THIS BREAKS COMPATIBILITY WITH PREVIOUS VERSIONS!!! | |
| 53 * 29.06.01 (J Vreeken) Added dev_offset module option | |
| 54 * Made vloopback_template sane | |
| 55 * Added double buffering support | |
| 56 * Made vloopback less verbose | |
| 57 * 20.11.01 (tibit) Made dev_offset option sane | |
| 58 * "Fixed" zerocopy mode by defining the ioctl | |
| 59 * VIDIOCSINVALID. An application which provides data | |
| 60 * has to issue it when it encounters an error in | |
| 61 * ioctl processing. See dummy.c for examples. | |
| 62 * 26.11.03 (Kenneth Lavrsen) | |
| 63 * released 0.91 | |
| 64 * 0.91 is the combination of the 0.90-tibit by | |
| 65 * Tilmann Bitterberg and an update of the Makefile by | |
| 66 * Roberto Carvajal. | |
| 67 * 23.01.05 (W Brack) | |
| 68 * (don't know what happened to the comments for 0.92 | |
| 69 * and 0.93, but I tentatively named this one as 0.99) | |
| 70 * enhanced for linux-2.6, with #ifdef to keep it | |
| 71 * compatible with linux-2.4. For linux versions | |
| 72 * > 2.5, I changed the memory management | |
| 73 * routines to the "more modern" way, most of it | |
| 74 * shamelessly copied from other drivers. I also | |
| 75 * added in the code necessary to avoid the "videodev | |
| 76 * has no release callback" message when installing. | |
| 77 * For versions < 2.5, I updated the routines to be | |
| 78 * closer to several other drivers. | |
| 79 * | |
| 80 * 04.02.05 (Angel Carpintero) | |
| 81 * Fixed version number to 0.93-pre1. | |
| 82 * Fixed warning for interruptible_sleep_on() deprecated and added | |
| 83 * wait_event_interruptible compatible with 2.6.x and 2.7. | |
| 84 * Fixed memory manager for kernel version > 2.6.9. | |
| 85 * | |
| 86 * 07.02.05 (Kenneth Lavrsen) | |
| 87 * Changed version to 0.94. | |
| 88 * Released as formal released version | |
| 89 * | |
| 90 * 20.02.05 (W Brack) | |
| 91 * Fixed error with wait_event_interruptible. | |
| 92 * Fixed crash when pipe source was stopped before dest. | |
| 93 * | |
| 94 * 20.02.05 (Angel Carpintero) | |
| 95 * Added install and uninstall in Makefile. | |
| 96 * | |
| 97 * | |
| 98 * 25.04.05 (Angel Carpintero) | |
| 99 * Included Samuel Audet's patch, it checks if the input is already | |
| 100 * opened in write mode. | |
| 101 * | |
| 102 * 02.05.05 (Kenneth Lavrsen) | |
| 103 * Released 0.95-snap2 formerly as 0.95 | |
| 104 * | |
| 105 * 10.05.05 (Angel Carpintero) | |
| 106 * Added MODULE_VERSION(), fixed create_pipes when video_register_device() returns | |
| 107 * -ENFILE . | |
| 108 * Fix warnings about checking return value from copy_to_user() and copy_from_user() functions. | |
| 109 * | |
| 110 * 14.11.05 (Angel Carpintero) | |
| 111 * Added <linux/version.h> that includes LINUX_VERSION_CODE and KERNEL_VERSION to fix | |
| 112 * compilation agains kernel 2.6.14 , change version to 0.97-snap1 | |
| 113 * | |
| 114 * 19.12.05 (Angel Carpintero) | |
| 115 * Added to example option to choose between rgb24 or yuv420p palettes. | |
| 116 * | |
| 117 * 31.12.05 (Angel Carpintero) | |
| 118 * Fixed examples, remove perror calls and add support to dummy.c for sysfs. | |
| 119 * | |
| 120 * 04.06.06 (Angel Carpintero) | |
| 121 * Add module_param() for kernel > 2.5 because MODULE_PARAM() macro is obsolete. | |
| 122 * | |
| 123 * 17.06.06 (Angel Carpintero) | |
| 124 * Release version 1.0 with some fixes and code clean up. Added a Jack Bates contribution | |
| 125 * to allow build a kernel module in debian way. | |
| 126 * | |
| 127 * 26.06.06 (Angel Carpintero) | |
| 128 * Added some improvements in Makefile. Fix a problem to compile in Suse. | |
| 129 * | |
| 130 * | |
| 131 * 02.11.06 (Angel Carpintero) | |
| 132 * Make compatible with new kernel stable version 2.6.18, Many functions and declarations has | |
| 133 * been moved to media/v42l-dev.h and remove from videodev.h/videodev2.h | |
| 134 * | |
| 135 * 18.01.07 (Angel Carpintero) | |
| 136 * Change -ENOIOCTLCMD by more appropiate error -ENOTTY. | |
| 137 */ | |
| 138 | |
| 139 | |
| 140 #define VLOOPBACK_VERSION "1.1-rc1" | |
| 141 | |
| 142 /* Include files common to 2.4 and 2.6 versions */ | |
| 143 #include <linux/version.h> /* >= 2.6.14 LINUX_VERSION_CODE */ | |
| 144 #include <linux/errno.h> | |
| 145 #include <linux/kernel.h> | |
| 146 #include <linux/module.h> | |
| 147 #include <linux/pagemap.h> | |
| 148 | |
| 149 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) | |
| 150 #include <media/v4l2-common.h> | |
| 151 #endif | |
| 152 | |
| 153 #include <linux/videodev.h> | |
| 154 #include <linux/vmalloc.h> | |
| 155 #include <linux/wait.h> | |
| 156 | |
| 157 /* Include files which are unique to versions */ | |
| 158 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 159 #include <asm/ioctl.h> | |
| 160 #include <asm/page.h> | |
| 161 #include <asm/pgtable.h> | |
| 162 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) | |
| 163 #ifndef remap_pfn_range | |
| 164 #define remap_pfn_range(a,b,c,d,e) \ | |
| 165 remap_page_range((a),(b),(c)<<PAGE_SHIFT,(d),(e)) | |
| 166 #endif | |
| 167 #ifndef vmalloc_to_pfn | |
| 168 #define vmalloc_to_pfn(a) page_to_pfn(vmalloc_to_page((a))) | |
| 169 #endif | |
| 170 #endif | |
| 171 #include <asm/uaccess.h> | |
| 172 #include <linux/init.h> | |
| 173 #include <linux/device.h> | |
| 174 #else | |
| 175 #include <linux/mm.h> | |
| 176 #include <linux/slab.h> | |
| 177 #include <linux/wrapper.h> | |
| 178 #include <asm/io.h> | |
| 179 #endif | |
| 180 | |
| 181 #define VIDIOCSINVALID _IO('v',BASE_VIDIOCPRIVATE+1) | |
| 182 | |
| 183 #define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg) | |
| 184 | |
| 185 struct vloopback_private { | |
| 186 int pipenr; | |
| 187 int in; /* bool */ | |
| 188 }; | |
| 189 typedef struct vloopback_private *priv_ptr; | |
| 190 | |
| 191 struct vloopback_pipe { | |
| 192 struct video_device *vloopin; | |
| 193 struct video_device *vloopout; | |
| 194 char *buffer; | |
| 195 unsigned long buflength; | |
| 196 unsigned int width, height; | |
| 197 unsigned int palette; | |
| 198 unsigned long frameswrite; | |
| 199 unsigned long framesread; | |
| 200 unsigned long framesdumped; | |
| 201 unsigned int wopen; | |
| 202 unsigned int ropen; | |
| 203 struct semaphore lock; | |
| 204 wait_queue_head_t wait; | |
| 205 unsigned int frame; | |
| 206 unsigned int pid; | |
| 207 unsigned int zerocopy; | |
| 208 unsigned long int ioctlnr; | |
| 209 unsigned int invalid_ioctl; /* 0 .. none invalid; 1 .. invalid */ | |
| 210 unsigned int ioctllength; | |
| 211 char *ioctldata; | |
| 212 char *ioctlretdata; | |
| 213 }; | |
| 214 | |
| 215 #define MAX_PIPES 16 | |
| 216 #define N_BUFFS 2 /* Number of buffers used for pipes */ | |
| 217 | |
| 218 static struct vloopback_pipe *loops[MAX_PIPES]; | |
| 219 static int nr_o_pipes=0; | |
| 220 static int pipes=-1; | |
| 221 static int spares=0; | |
| 222 static int pipesused=0; | |
| 223 static int dev_offset=-1; | |
| 224 | |
| 225 /********************************************************************** | |
| 226 * | |
| 227 * Memory management - revised for 2.6 kernels | |
| 228 * | |
| 229 **********************************************************************/ | |
| 230 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 231 /* Here we want the physical address of the memory. | |
| 232 * This is used when initializing the contents of the | |
| 233 * area and marking the pages as reserved. | |
| 234 */ | |
| 235 static inline unsigned long kvirt_to_pa(unsigned long adr) | |
| 236 { | |
| 237 unsigned long kva; | |
| 238 | |
| 239 kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); | |
| 240 kva |= adr & (PAGE_SIZE-1); /* restore the offset */ | |
| 241 return __pa(kva); | |
| 242 } | |
| 243 #endif | |
| 244 | |
| 245 static void *rvmalloc(unsigned long size) | |
| 246 { | |
| 247 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) | |
| 248 struct page *page; | |
| 249 #endif | |
| 250 void *mem; | |
| 251 unsigned long adr; | |
| 252 | |
| 253 size = PAGE_ALIGN(size); | |
| 254 mem = vmalloc_32(size); | |
| 255 if (!mem) | |
| 256 return NULL; | |
| 257 memset(mem, 0, size); /* Clear the ram out, no junk to the user */ | |
| 258 adr = (unsigned long) mem; | |
| 259 while (size > 0) { | |
| 260 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) | |
| 261 page = vmalloc_to_page((void *)adr); | |
| 262 mem_map_reserve(page); | |
| 263 #else | |
| 264 SetPageReserved(vmalloc_to_page((void *)adr)); | |
| 265 #endif | |
| 266 adr += PAGE_SIZE; | |
| 267 size -= PAGE_SIZE; | |
| 268 } | |
| 269 | |
| 270 return mem; | |
| 271 } | |
| 272 | |
| 273 static void rvfree(void *mem, unsigned long size) | |
| 274 { | |
| 275 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) | |
| 276 struct page *page; | |
| 277 #endif | |
| 278 unsigned long adr; | |
| 279 | |
| 280 if (!mem) | |
| 281 return; | |
| 282 | |
| 283 adr = (unsigned long) mem; | |
| 284 while ((long) size > 0) { | |
| 285 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) | |
| 286 page = vmalloc_to_page((void *)adr); | |
| 287 mem_map_unreserve(page); | |
| 288 #else | |
| 289 ClearPageReserved(vmalloc_to_page((void *)adr)); | |
| 290 #endif | |
| 291 adr += PAGE_SIZE; | |
| 292 size -= PAGE_SIZE; | |
| 293 } | |
| 294 vfree(mem); | |
| 295 } | |
| 296 | |
| 297 | |
| 298 static int create_pipe(int nr); | |
| 299 | |
| 300 static int fake_ioctl(int nr, unsigned long int cmd, void *arg) | |
| 301 { | |
| 302 unsigned long fw; | |
| 303 | |
| 304 loops[nr]->ioctlnr=cmd; | |
| 305 memcpy(loops[nr]->ioctldata, arg, _IOC_SIZE(cmd)); | |
| 306 loops[nr]->ioctllength=_IOC_SIZE(cmd); | |
| 307 kill_proc(loops[nr]->pid, SIGIO, 1); /* Signal the pipe feeder */ | |
| 308 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 309 fw = loops[nr]->frameswrite; | |
| 310 wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); | |
| 311 #else | |
| 312 interruptible_sleep_on(&loops[nr]->wait); | |
| 313 #endif | |
| 314 if (cmd & IOC_IN) { | |
| 315 if (memcmp (arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd))) | |
| 316 return 1; | |
| 317 } else { | |
| 318 memcpy (arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd)); | |
| 319 } | |
| 320 return 0; | |
| 321 } | |
| 322 | |
| 323 static int vloopback_open(struct inode *inod, struct file *f) | |
| 324 { | |
| 325 struct video_device *loopdev=video_devdata(f); | |
| 326 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 327 int nr=ptr->pipenr; | |
| 328 | |
| 329 | |
| 330 /* Only allow a output to be opened if there is someone feeding | |
| 331 * the pipe. | |
| 332 */ | |
| 333 if (!ptr->in) { | |
| 334 if (loops[nr]->buffer==NULL) { | |
| 335 return -EINVAL; | |
| 336 } | |
| 337 loops[nr]->framesread=0; | |
| 338 loops[nr]->ropen=1; | |
| 339 } else { | |
| 340 if (loops[nr]->ropen || loops[nr]->wopen) | |
| 341 return -EBUSY; | |
| 342 loops[nr]->framesdumped=0; | |
| 343 loops[nr]->frameswrite=0; | |
| 344 loops[nr]->wopen=1; | |
| 345 loops[nr]->zerocopy=0; | |
| 346 loops[nr]->ioctlnr=-1; | |
| 347 pipesused++; | |
| 348 if (nr_o_pipes-pipesused<spares) { | |
| 349 if (!create_pipe(nr_o_pipes)) { | |
| 350 info("Creating extra spare pipe"); | |
| 351 info("Loopback %d registered, input: video%d, output: video%d", | |
| 352 nr_o_pipes, | |
| 353 loops[nr_o_pipes]->vloopin->minor, | |
| 354 loops[nr_o_pipes]->vloopout->minor | |
| 355 ); | |
| 356 nr_o_pipes++; | |
| 357 } | |
| 358 } | |
| 359 loops[nr]->pid=current->pid; | |
| 360 } | |
| 361 return 0; | |
| 362 } | |
| 363 | |
| 364 static int vloopback_release(struct inode * inod, struct file *f) | |
| 365 { | |
| 366 struct video_device *loopdev=video_devdata(f); | |
| 367 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 368 int nr=ptr->pipenr; | |
| 369 | |
| 370 if (ptr->in) { | |
| 371 down(&loops[nr]->lock); | |
| 372 if (loops[nr]->buffer && !loops[nr]->ropen) { | |
| 373 rvfree(loops[nr]->buffer, | |
| 374 loops[nr]->buflength*N_BUFFS); | |
| 375 loops[nr]->buffer=NULL; | |
| 376 } | |
| 377 up(&loops[nr]->lock); | |
| 378 loops[nr]->frameswrite++; | |
| 379 if (waitqueue_active(&loops[nr]->wait)) | |
| 380 wake_up(&loops[nr]->wait); | |
| 381 | |
| 382 loops[nr]->width=0; | |
| 383 loops[nr]->height=0; | |
| 384 loops[nr]->palette=0; | |
| 385 loops[nr]->wopen=0; | |
| 386 pipesused--; | |
| 387 } else { | |
| 388 down(&loops[nr]->lock); | |
| 389 if (loops[nr]->buffer && !loops[nr]->wopen) { | |
| 390 rvfree(loops[nr]->buffer, | |
| 391 loops[nr]->buflength*N_BUFFS); | |
| 392 loops[nr]->buffer=NULL; | |
| 393 } | |
| 394 up(&loops[nr]->lock); | |
| 395 loops[nr]->ropen=0; | |
| 396 if (loops[nr]->zerocopy && loops[nr]->buffer) { | |
| 397 loops[nr]->ioctlnr=0; | |
| 398 loops[nr]->ioctllength=0; | |
| 399 kill_proc(loops[nr]->pid, SIGIO, 1); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 return 0; | |
| 404 } | |
| 405 | |
| 406 static ssize_t vloopback_write(struct file *f, const char *buf, | |
| 407 size_t count, loff_t *offset) | |
| 408 { | |
| 409 struct video_device *loopdev=video_devdata(f); | |
| 410 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 411 int nr=ptr->pipenr; | |
| 412 unsigned long realcount=count; | |
| 413 | |
| 414 if (!ptr->in) | |
| 415 return -EINVAL; | |
| 416 if (loops[nr]->zerocopy) | |
| 417 return -EINVAL; | |
| 418 | |
| 419 if (loops[nr]->buffer==NULL) { | |
| 420 return -EINVAL; | |
| 421 } | |
| 422 | |
| 423 /* Anybody want some pictures??? */ | |
| 424 if (!waitqueue_active(&loops[nr]->wait)) { | |
| 425 loops[nr]->framesdumped++; | |
| 426 return realcount; | |
| 427 } | |
| 428 | |
| 429 down(&loops[nr]->lock); | |
| 430 if (!loops[nr]->buffer) { | |
| 431 up(&loops[nr]->lock); | |
| 432 return -EINVAL; | |
| 433 } | |
| 434 if (realcount > loops[nr]->buflength) { | |
| 435 realcount = loops[nr]->buflength; | |
| 436 info("Too much data! Only %ld bytes used.", realcount); | |
| 437 } | |
| 438 | |
| 439 if (copy_from_user( | |
| 440 loops[nr]->buffer+loops[nr]->frame*loops[nr]->buflength, | |
| 441 buf, realcount | |
| 442 )) return -EFAULT; | |
| 443 | |
| 444 loops[nr]->frame=0; | |
| 445 up(&loops[nr]->lock); | |
| 446 | |
| 447 loops[nr]->frameswrite++; | |
| 448 wake_up(&loops[nr]->wait); | |
| 449 | |
| 450 return realcount; | |
| 451 } | |
| 452 | |
| 453 static ssize_t vloopback_read (struct file * f, char * buf, size_t count, loff_t *offset) | |
| 454 { | |
| 455 struct video_device *loopdev=video_devdata(f); | |
| 456 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 457 int nr=ptr->pipenr; | |
| 458 unsigned long realcount=count; | |
| 459 | |
| 460 if (loops[nr]->zerocopy) { | |
| 461 if (ptr->in) { | |
| 462 if (realcount > loops[nr]->ioctllength+sizeof(unsigned long int)) | |
| 463 realcount=loops[nr]->ioctllength+sizeof(unsigned long int); | |
| 464 if (copy_to_user(buf , &loops[nr]->ioctlnr, sizeof(unsigned long int))) | |
| 465 return -EFAULT; | |
| 466 if (copy_to_user(buf+sizeof(unsigned long int) , loops[nr]->ioctldata, | |
| 467 realcount-sizeof(unsigned long int))) | |
| 468 return -EFAULT; | |
| 469 if (loops[nr]->ioctlnr==0) | |
| 470 loops[nr]->ioctlnr=-1; | |
| 471 return realcount; | |
| 472 } else { | |
| 473 struct video_window vidwin; | |
| 474 struct video_mmap vidmmap; | |
| 475 struct video_picture vidpic; | |
| 476 | |
| 477 fake_ioctl(nr, VIDIOCGWIN, &vidwin); | |
| 478 fake_ioctl(nr, VIDIOCGPICT, &vidpic); | |
| 479 | |
| 480 vidmmap.height=vidwin.height; | |
| 481 vidmmap.width=vidwin.width; | |
| 482 vidmmap.format=vidpic.palette; | |
| 483 vidmmap.frame=0; | |
| 484 if (fake_ioctl(nr, VIDIOCMCAPTURE, &vidmmap)) | |
| 485 return 0; | |
| 486 if (fake_ioctl(nr, VIDIOCSYNC, &vidmmap)) | |
| 487 return 0; | |
| 488 realcount=vidwin.height*vidwin.width*vidpic.depth; | |
| 489 } | |
| 490 } | |
| 491 if (ptr->in) | |
| 492 return -EINVAL; | |
| 493 | |
| 494 if (realcount > loops[nr]->buflength) { | |
| 495 realcount = loops[nr]->buflength; | |
| 496 info("Not so much data in buffer!"); | |
| 497 } | |
| 498 | |
| 499 if (!loops[nr]->zerocopy) { | |
| 500 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 501 unsigned long fw=loops[nr]->frameswrite; | |
| 502 | |
| 503 wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); | |
| 504 #else | |
| 505 interruptible_sleep_on(&loops[nr]->wait); | |
| 506 #endif | |
| 507 } | |
| 508 | |
| 509 down(&loops[nr]->lock); | |
| 510 if (!loops[nr]->buffer) { | |
| 511 up(&loops[nr]->lock); | |
| 512 return 0; | |
| 513 } | |
| 514 if (copy_to_user(buf, loops[nr]->buffer, realcount)) | |
| 515 return -EFAULT; | |
| 516 up(&loops[nr]->lock); | |
| 517 | |
| 518 loops[nr]->framesread++; | |
| 519 return realcount; | |
| 520 } | |
| 521 | |
| 522 static int vloopback_mmap(struct file *f, struct vm_area_struct *vma) | |
| 523 { | |
| 524 struct video_device *loopdev=video_devdata(f); | |
| 525 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 526 int nr=ptr->pipenr; | |
| 527 unsigned long start = (unsigned long)vma->vm_start; | |
| 528 long size = vma->vm_end - vma->vm_start; | |
| 529 unsigned long page, pos; | |
| 530 | |
| 531 down(&loops[nr]->lock); | |
| 532 if (ptr->in) { | |
| 533 loops[nr]->zerocopy=1; | |
| 534 if (loops[nr]->ropen) { | |
| 535 info("Can't change size while opened for read"); | |
| 536 up(&loops[nr]->lock); | |
| 537 return -EINVAL; | |
| 538 } | |
| 539 if (!size) { | |
| 540 up(&loops[nr]->lock); | |
| 541 return -EINVAL; | |
| 542 } | |
| 543 if (loops[nr]->buffer) | |
| 544 rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS); | |
| 545 loops[nr]->buflength=size; | |
| 546 loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS); | |
| 547 } | |
| 548 if (loops[nr]->buffer == NULL) { | |
| 549 up(&loops[nr]->lock); | |
| 550 return -EINVAL; | |
| 551 } | |
| 552 | |
| 553 if (size > (((N_BUFFS * loops[nr]->buflength) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { | |
| 554 up(&loops[nr]->lock); | |
| 555 return -EINVAL; | |
| 556 } | |
| 557 | |
| 558 pos = (unsigned long)loops[nr]->buffer; | |
| 559 while (size > 0) { | |
| 560 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) | |
| 561 page = kvirt_to_pa(pos); | |
| 562 if (remap_page_range(vma,start, page, PAGE_SIZE, PAGE_SHARED)) { | |
| 563 #else | |
| 564 page = vmalloc_to_pfn((void *)pos); | |
| 565 if (remap_pfn_range(vma, start, page, PAGE_SIZE, | |
| 566 PAGE_SHARED)) { | |
| 567 #endif | |
| 568 up(&loops[nr]->lock); | |
| 569 return -EAGAIN; | |
| 570 } | |
| 571 start += PAGE_SIZE; | |
| 572 pos += PAGE_SIZE; | |
| 573 size -= PAGE_SIZE; | |
| 574 } | |
| 575 up(&loops[nr]->lock); | |
| 576 | |
| 577 return 0; | |
| 578 } | |
| 579 | |
| 580 static int vloopback_ioctl(struct inode *inod, struct file *f, unsigned int cmd, unsigned long arg) | |
| 581 { | |
| 582 struct video_device *loopdev=video_devdata(f); | |
| 583 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 584 int nr=ptr->pipenr; | |
| 585 int i; | |
| 586 | |
| 587 if (loops[nr]->zerocopy) { | |
| 588 if (!ptr->in) { | |
| 589 loops[nr]->ioctlnr=cmd; | |
| 590 loops[nr]->ioctllength=_IOC_SIZE(cmd); | |
| 591 /* info("DEBUG: vl_ioctl: !loop->in"); */ | |
| 592 /* info("DEBUG: vl_ioctl: cmd %lu", cmd); */ | |
| 593 /* info("DEBUG: vl_ioctl: len %lu", loops[nr]->ioctllength); */ | |
| 594 if(copy_from_user(loops[nr]->ioctldata, (void*)arg, _IOC_SIZE(cmd))) | |
| 595 return -EFAULT; | |
| 596 kill_proc(loops[nr]->pid, SIGIO, 1); | |
| 597 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 598 wait_event_interruptible(loops[nr]->wait, loops[nr]->ioctlnr==-1); | |
| 599 #else | |
| 600 interruptible_sleep_on(&loops[nr]->wait); | |
| 601 #endif | |
| 602 | |
| 603 if (loops[nr]->invalid_ioctl) { | |
| 604 //info ("DEBUG: There was an invalid ioctl"); | |
| 605 loops[nr]->invalid_ioctl = 0; | |
| 606 return -ENOTTY; | |
| 607 } | |
| 608 if (cmd & IOC_IN && !(cmd & IOC_OUT)) { | |
| 609 //info("DEBUG: vl_ioctl: cmd & IOC_IN 1"); | |
| 610 if (memcmp(loops[nr]->ioctlretdata, loops[nr]->ioctldata, _IOC_SIZE(cmd))) { | |
| 611 return -EINVAL; | |
| 612 } | |
| 613 //info("DEBUG: vl_ioctl: cmd & IOC_IN 2"); | |
| 614 return 0; | |
| 615 } else { | |
| 616 if (copy_to_user((void*)arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd))) | |
| 617 return -EFAULT; | |
| 618 //info("DEBUG: vl_ioctl: !(cmd & IOC_IN) 1"); | |
| 619 return 0; | |
| 620 } | |
| 621 } else { | |
| 622 if ( (loops[nr]->ioctlnr!=cmd) && (cmd != (VIDIOCSINVALID))) { | |
| 623 /* wrong ioctl */ | |
| 624 info("DEBUG: vo_ioctl: Wrong IOCTL"); | |
| 625 return 0; | |
| 626 } | |
| 627 if (cmd == VIDIOCSINVALID) { | |
| 628 loops[nr]->invalid_ioctl = 1; | |
| 629 } else { | |
| 630 if (copy_from_user(loops[nr]->ioctlretdata, (void*)arg, loops[nr]->ioctllength)) | |
| 631 return -EFAULT; | |
| 632 } | |
| 633 loops[nr]->ioctlnr=-1; | |
| 634 if (waitqueue_active(&loops[nr]->wait)) | |
| 635 wake_up(&loops[nr]->wait); | |
| 636 return 0; | |
| 637 } | |
| 638 } | |
| 639 switch(cmd) | |
| 640 { | |
| 641 /* Get capabilities */ | |
| 642 case VIDIOCGCAP: | |
| 643 { | |
| 644 struct video_capability b; | |
| 645 if (ptr->in) { | |
| 646 sprintf(b.name, "Video loopback %d input", | |
| 647 ptr->pipenr); | |
| 648 b.type = 0; | |
| 649 } else { | |
| 650 sprintf(b.name, "Video loopback %d output", | |
| 651 ptr->pipenr); | |
| 652 b.type = VID_TYPE_CAPTURE; | |
| 653 } | |
| 654 b.channels=1; | |
| 655 b.audios=0; | |
| 656 b.maxwidth=loops[nr]->width; | |
| 657 b.maxheight=loops[nr]->height; | |
| 658 b.minwidth=20; | |
| 659 b.minheight=20; | |
| 660 if(copy_to_user((void*)arg, &b, sizeof(b))) | |
| 661 return -EFAULT; | |
| 662 return 0; | |
| 663 } | |
| 664 /* Get channel info (sources) */ | |
| 665 case VIDIOCGCHAN: | |
| 666 { | |
| 667 struct video_channel v; | |
| 668 if(copy_from_user(&v, (void*)arg, sizeof(v))) | |
| 669 return -EFAULT; | |
| 670 if(v.channel!=0) { | |
| 671 info("VIDIOCGCHAN: Invalid Channel, was %d", v.channel); | |
| 672 v.channel=0; | |
| 673 //return -EINVAL; | |
| 674 } | |
| 675 v.flags=0; | |
| 676 v.tuners=0; | |
| 677 v.norm=0; | |
| 678 v.type = VIDEO_TYPE_CAMERA; | |
| 679 /*strcpy(v.name, "Loopback"); -- tibit */ | |
| 680 strcpy(v.name, "Composite1"); | |
| 681 if(copy_to_user((void*)arg, &v, sizeof(v))) | |
| 682 return -EFAULT; | |
| 683 return 0; | |
| 684 } | |
| 685 /* Set channel */ | |
| 686 case VIDIOCSCHAN: | |
| 687 { | |
| 688 int v; | |
| 689 if(copy_from_user(&v, (void*)arg, sizeof(v))) | |
| 690 return -EFAULT; | |
| 691 if(v!=0) { | |
| 692 info("VIDIOCSCHAN: Invalid Channel, was %d", v); | |
| 693 return -EINVAL; | |
| 694 } | |
| 695 return 0; | |
| 696 } | |
| 697 /* Get tuner abilities */ | |
| 698 case VIDIOCGTUNER: | |
| 699 { | |
| 700 struct video_tuner v; | |
| 701 if(copy_from_user(&v, (void*)arg, sizeof(v))!=0) | |
| 702 return -EFAULT; | |
| 703 if(v.tuner) { | |
| 704 info("VIDIOCGTUNER: Invalid Tuner, was %d", v.tuner); | |
| 705 return -EINVAL; | |
| 706 } | |
| 707 strcpy(v.name, "Format"); | |
| 708 v.rangelow=0; | |
| 709 v.rangehigh=0; | |
| 710 v.flags=0; | |
| 711 v.mode=VIDEO_MODE_AUTO; | |
| 712 if(copy_to_user((void*)arg,&v, sizeof(v))!=0) | |
| 713 return -EFAULT; | |
| 714 return 0; | |
| 715 } | |
| 716 /* Get picture properties */ | |
| 717 case VIDIOCGPICT: | |
| 718 { | |
| 719 struct video_picture p; | |
| 720 p.colour=0x8000; | |
| 721 p.hue=0x8000; | |
| 722 p.brightness=0x8000; | |
| 723 p.contrast=0x8000; | |
| 724 p.whiteness=0x8000; | |
| 725 p.depth=0x8000; | |
| 726 p.palette=loops[nr]->palette; | |
| 727 if(copy_to_user((void*)arg, &p, sizeof(p))) | |
| 728 return -EFAULT; | |
| 729 return 0; | |
| 730 | |
| 731 } | |
| 732 /* Set picture properties */ | |
| 733 case VIDIOCSPICT: | |
| 734 { | |
| 735 struct video_picture p; | |
| 736 if(copy_from_user(&p, (void*)arg, sizeof(p))) | |
| 737 return -EFAULT; | |
| 738 if (!ptr->in) { | |
| 739 if (p.palette!=loops[nr]->palette) | |
| 740 return -EINVAL; | |
| 741 } else | |
| 742 loops[nr]->palette=p.palette; | |
| 743 return 0; | |
| 744 } | |
| 745 /* Get the video overlay window */ | |
| 746 case VIDIOCGWIN: | |
| 747 { | |
| 748 struct video_window vw; | |
| 749 vw.x=0; | |
| 750 vw.y=0; | |
| 751 vw.width=loops[nr]->width; | |
| 752 vw.height=loops[nr]->height; | |
| 753 vw.chromakey=0; | |
| 754 vw.flags=0; | |
| 755 vw.clipcount=0; | |
| 756 if(copy_to_user((void*)arg, &vw, sizeof(vw))) | |
| 757 return -EFAULT; | |
| 758 return 0; | |
| 759 } | |
| 760 /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ | |
| 761 case VIDIOCSWIN: | |
| 762 { | |
| 763 struct video_window vw; | |
| 764 | |
| 765 if(copy_from_user(&vw, (void*)arg, sizeof(vw))) | |
| 766 return -EFAULT; | |
| 767 if(vw.flags) | |
| 768 return -EINVAL; | |
| 769 if(vw.clipcount) | |
| 770 return -EINVAL; | |
| 771 if (loops[nr]->height==vw.height && | |
| 772 loops[nr]->width==vw.width) | |
| 773 return 0; | |
| 774 if(!ptr->in) { | |
| 775 return -EINVAL; | |
| 776 } else { | |
| 777 loops[nr]->height=vw.height; | |
| 778 loops[nr]->width=vw.width; | |
| 779 /* Make sure nobody is using the buffer while we | |
| 780 fool around with it. | |
| 781 We are also not allowing changes while | |
| 782 somebody using mmap has the output open. | |
| 783 */ | |
| 784 down(&loops[nr]->lock); | |
| 785 if (loops[nr]->ropen) { | |
| 786 info("Can't change size while opened for read"); | |
| 787 up(&loops[nr]->lock); | |
| 788 return -EINVAL; | |
| 789 } | |
| 790 if (loops[nr]->buffer) | |
| 791 rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS); | |
| 792 loops[nr]->buflength=vw.width*vw.height*4; | |
| 793 loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS); | |
| 794 up(&loops[nr]->lock); | |
| 795 } | |
| 796 return 0; | |
| 797 } | |
| 798 /* Memory map buffer info */ | |
| 799 case VIDIOCGMBUF: | |
| 800 { | |
| 801 struct video_mbuf vm; | |
| 802 | |
| 803 vm.size=loops[nr]->buflength*N_BUFFS; | |
| 804 vm.frames=N_BUFFS; | |
| 805 for (i=0; i<vm.frames; i++) | |
| 806 vm.offsets[i]=i*loops[nr]->buflength; | |
| 807 if(copy_to_user((void*)arg, &vm, sizeof(vm))) | |
| 808 return -EFAULT; | |
| 809 return 0; | |
| 810 } | |
| 811 /* Grab frames */ | |
| 812 case VIDIOCMCAPTURE: | |
| 813 { | |
| 814 struct video_mmap vm; | |
| 815 | |
| 816 if (ptr->in) | |
| 817 return -EINVAL; | |
| 818 if (!loops[nr]->buffer) | |
| 819 return -EINVAL; | |
| 820 if (copy_from_user(&vm, (void*)arg, sizeof(vm))) | |
| 821 return -EFAULT; | |
| 822 if (vm.format!=loops[nr]->palette) | |
| 823 return -EINVAL; | |
| 824 if (vm.frame > N_BUFFS) | |
| 825 return -EINVAL; | |
| 826 return 0; | |
| 827 } | |
| 828 /* Sync with mmap grabbing */ | |
| 829 case VIDIOCSYNC: | |
| 830 { | |
| 831 int frame; | |
| 832 unsigned long fw; | |
| 833 | |
| 834 if (copy_from_user((void *)&frame, (void*)arg, sizeof(int))) | |
| 835 return -EFAULT; | |
| 836 if (ptr->in) | |
| 837 return -EINVAL; | |
| 838 if (!loops[nr]->buffer) | |
| 839 return -EINVAL; | |
| 840 /* Ok, everything should be alright since the program | |
| 841 should have called VIDIOMCAPTURE and we are ready to | |
| 842 do the 'capturing' */ | |
| 843 if (frame > 1) | |
| 844 return -EINVAL; | |
| 845 loops[nr]->frame=frame; | |
| 846 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 847 fw = loops[nr]->frameswrite; | |
| 848 wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); | |
| 849 #else | |
| 850 interruptible_sleep_on(&loops[nr]->wait); | |
| 851 #endif | |
| 852 if (!loops[nr]->buffer) /* possibly released during sleep */ | |
| 853 return -EINVAL; | |
| 854 loops[nr]->framesread++; | |
| 855 return 0; | |
| 856 } | |
| 857 /* Get attached units */ | |
| 858 case VIDIOCGUNIT: | |
| 859 { | |
| 860 struct video_unit vu; | |
| 861 | |
| 862 if (ptr->in) | |
| 863 vu.video=loops[nr]->vloopout->minor; | |
| 864 else | |
| 865 vu.video=loops[nr]->vloopin->minor; | |
| 866 vu.vbi=VIDEO_NO_UNIT; | |
| 867 vu.radio=VIDEO_NO_UNIT; | |
| 868 vu.audio=VIDEO_NO_UNIT; | |
| 869 vu.teletext=VIDEO_NO_UNIT; | |
| 870 if (copy_to_user((void*)arg, &vu, sizeof(vu))) | |
| 871 return -EFAULT; | |
| 872 return 0; | |
| 873 } | |
| 874 /* Get frame buffer */ | |
| 875 case VIDIOCGFBUF: | |
| 876 { | |
| 877 struct video_buffer vb; | |
| 878 | |
| 879 memset(&vb, 0, sizeof(vb)); | |
| 880 vb.base=NULL; | |
| 881 | |
| 882 if(copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) | |
| 883 return -EFAULT; | |
| 884 | |
| 885 return 0; | |
| 886 } | |
| 887 /* Start, end capture */ | |
| 888 case VIDIOCCAPTURE: | |
| 889 { | |
| 890 int start; | |
| 891 if (copy_from_user(&start, (void*)arg, sizeof(int))) | |
| 892 return -EFAULT; | |
| 893 if (start) info ("Capture started"); | |
| 894 else info ("Capture stopped"); | |
| 895 | |
| 896 return 0; | |
| 897 } | |
| 898 | |
| 899 case VIDIOCGFREQ: | |
| 900 case VIDIOCSFREQ: | |
| 901 case VIDIOCGAUDIO: | |
| 902 case VIDIOCSAUDIO: | |
| 903 return -EINVAL; | |
| 904 case VIDIOCKEY: | |
| 905 return 0; | |
| 906 default: | |
| 907 return -ENOTTY; | |
| 908 //return -ENOIOCTLCMD; | |
| 909 } | |
| 910 return 0; | |
| 911 } | |
| 912 | |
| 913 static unsigned int vloopback_poll(struct file *f, struct poll_table_struct *wait) | |
| 914 { | |
| 915 struct video_device *loopdev=video_devdata(f); | |
| 916 priv_ptr ptr=(priv_ptr)loopdev->priv; | |
| 917 int nr=ptr->pipenr; | |
| 918 | |
| 919 if (loopdev==NULL) | |
| 920 return -EFAULT; | |
| 921 if (!ptr->in) | |
| 922 return 0; | |
| 923 | |
| 924 if (loops[nr]->ioctlnr!=-1) { | |
| 925 if (loops[nr]->zerocopy) { | |
| 926 return (POLLIN | POLLPRI | POLLOUT | POLLRDNORM); | |
| 927 } else { | |
| 928 return (POLLOUT); | |
| 929 } | |
| 930 } | |
| 931 return 0; | |
| 932 } | |
| 933 | |
| 934 static struct file_operations fileops_template= | |
| 935 { | |
| 936 owner: THIS_MODULE, | |
| 937 open: vloopback_open, | |
| 938 release: vloopback_release, | |
| 939 read: vloopback_read, | |
| 940 write: vloopback_write, | |
| 941 poll: vloopback_poll, | |
| 942 ioctl: vloopback_ioctl, | |
| 943 mmap: vloopback_mmap, | |
| 944 }; | |
| 945 | |
| 946 static struct video_device vloopback_template= | |
| 947 { | |
| 948 owner: THIS_MODULE, | |
| 949 name: "Video Loopback", | |
| 950 type: VID_TYPE_CAPTURE, | |
| 951 fops: &fileops_template, | |
| 952 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 953 release: video_device_release, | |
| 954 #endif | |
| 955 }; | |
| 956 | |
| 957 static int create_pipe(int nr) | |
| 958 { | |
| 959 int minor_in, minor_out , ret; | |
| 960 | |
| 961 if (dev_offset == -1) | |
| 962 minor_in = minor_out = -1; /* autoassign */ | |
| 963 else { | |
| 964 minor_in = 2*nr + dev_offset; | |
| 965 minor_out = 2*nr+1 + dev_offset; | |
| 966 } | |
| 967 | |
| 968 /* allocate space for this pipe */ | |
| 969 loops[nr]= kmalloc(sizeof(struct vloopback_pipe), GFP_KERNEL); | |
| 970 if (!loops[nr]) | |
| 971 return -ENOMEM; | |
| 972 /* set up a new video device plus our private area */ | |
| 973 loops[nr]->vloopin= video_device_alloc(); | |
| 974 if (loops[nr]->vloopin == NULL) | |
| 975 return -ENOMEM; | |
| 976 *loops[nr]->vloopin = vloopback_template; | |
| 977 loops[nr]->vloopin->priv= kmalloc(sizeof(struct vloopback_private), | |
| 978 GFP_KERNEL); | |
| 979 if (loops[nr]->vloopin->priv == NULL) { | |
| 980 kfree(loops[nr]->vloopin); | |
| 981 return -ENOMEM; | |
| 982 } | |
| 983 /* repeat for the output device */ | |
| 984 loops[nr]->vloopout= video_device_alloc(); | |
| 985 if (loops[nr]->vloopout == NULL) { | |
| 986 kfree(loops[nr]->vloopin->priv); | |
| 987 kfree(loops[nr]->vloopin); | |
| 988 return -ENOMEM; | |
| 989 } | |
| 990 *loops[nr]->vloopout = vloopback_template; | |
| 991 loops[nr]->vloopout->priv= kmalloc(sizeof(struct vloopback_private), | |
| 992 GFP_KERNEL); | |
| 993 if (loops[nr]->vloopout->priv == NULL) { | |
| 994 kfree(loops[nr]->vloopin->priv); | |
| 995 kfree(loops[nr]->vloopin); | |
| 996 kfree(loops[nr]->vloopout); | |
| 997 return -ENOMEM; | |
| 998 } | |
| 999 | |
| 1000 ((priv_ptr)loops[nr]->vloopin->priv)->pipenr=nr; | |
| 1001 ((priv_ptr)loops[nr]->vloopout->priv)->pipenr=nr; | |
| 1002 loops[nr]->invalid_ioctl = 0; /* tibit */ | |
| 1003 loops[nr]->buffer=NULL; | |
| 1004 loops[nr]->width=0; | |
| 1005 loops[nr]->height=0; | |
| 1006 loops[nr]->palette=0; | |
| 1007 loops[nr]->frameswrite=0; | |
| 1008 loops[nr]->framesread=0; | |
| 1009 loops[nr]->framesdumped=0; | |
| 1010 loops[nr]->wopen=0; | |
| 1011 loops[nr]->ropen=0; | |
| 1012 loops[nr]->frame=0; | |
| 1013 | |
| 1014 ((priv_ptr)loops[nr]->vloopin->priv)->in=1; | |
| 1015 ((priv_ptr)loops[nr]->vloopout->priv)->in=0; | |
| 1016 loops[nr]->vloopin->type=0; | |
| 1017 sprintf(loops[nr]->vloopin->name, "Video loopback %d input", nr); | |
| 1018 loops[nr]->vloopout->type=VID_TYPE_CAPTURE; | |
| 1019 sprintf(loops[nr]->vloopout->name, "Video loopback %d output", nr); | |
| 1020 init_waitqueue_head(&loops[nr]->wait); | |
| 1021 init_MUTEX(&loops[nr]->lock); | |
| 1022 | |
| 1023 ret = video_register_device(loops[nr]->vloopin, VFL_TYPE_GRABBER,minor_in); | |
| 1024 | |
| 1025 if ((ret == -1 ) || ( ret == -23 )) { | |
| 1026 info("error registering device %s",loops[nr]->vloopin->name); | |
| 1027 kfree(loops[nr]->vloopin->priv); | |
| 1028 kfree(loops[nr]->vloopin); | |
| 1029 kfree(loops[nr]->vloopout->priv); | |
| 1030 kfree(loops[nr]->vloopout); | |
| 1031 kfree(loops[nr]); | |
| 1032 loops[nr]=NULL; | |
| 1033 return ret; | |
| 1034 } | |
| 1035 | |
| 1036 ret = video_register_device(loops[nr]->vloopout, VFL_TYPE_GRABBER,minor_out); | |
| 1037 | |
| 1038 if ((ret ==-1) || (ret == -23)) { | |
| 1039 info("error registering device %s", loops[nr]->vloopout->name); | |
| 1040 kfree(loops[nr]->vloopin->priv); | |
| 1041 video_unregister_device(loops[nr]->vloopin); | |
| 1042 kfree(loops[nr]->vloopout->priv); | |
| 1043 kfree(loops[nr]->vloopout); | |
| 1044 kfree(loops[nr]); | |
| 1045 loops[nr]=NULL; | |
| 1046 return ret; | |
| 1047 } | |
| 1048 | |
| 1049 loops[nr]->ioctldata=kmalloc(1024, GFP_KERNEL); | |
| 1050 loops[nr]->ioctlretdata=kmalloc(1024, GFP_KERNEL); | |
| 1051 return 0; | |
| 1052 } | |
| 1053 | |
| 1054 | |
| 1055 /**************************************************************************** | |
| 1056 * init stuff | |
| 1057 ****************************************************************************/ | |
| 1058 | |
| 1059 | |
| 1060 MODULE_AUTHOR("J.B. Vreeken (pe1rxq@amsat.org)"); | |
| 1061 MODULE_DESCRIPTION("Video4linux loopback device."); | |
| 1062 | |
| 1063 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 1064 module_param(pipes, int, 000); | |
| 1065 #else | |
| 1066 MODULE_PARM(pipes, "i"); | |
| 1067 #endif | |
| 1068 | |
| 1069 MODULE_PARM_DESC(pipes, "Nr of pipes to create (each pipe uses two video devices)"); | |
| 1070 | |
| 1071 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 1072 module_param(spares, int, 000); | |
| 1073 #else | |
| 1074 MODULE_PARM(spares, "i"); | |
| 1075 #endif | |
| 1076 | |
| 1077 MODULE_PARM_DESC(spares, "Nr of spare pipes that should be created"); | |
| 1078 | |
| 1079 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) | |
| 1080 module_param(dev_offset, int, 000); | |
| 1081 #else | |
| 1082 MODULE_PARM(dev_offset_param, "i"); | |
| 1083 #endif | |
| 1084 | |
| 1085 MODULE_PARM_DESC(dev_offset, "Prefered offset for video device numbers"); | |
| 1086 MODULE_LICENSE("GPL"); | |
| 1087 MODULE_VERSION( VLOOPBACK_VERSION ); | |
| 1088 | |
| 1089 static int __init vloopback_init(void) | |
| 1090 { | |
| 1091 int i,ret; | |
| 1092 | |
| 1093 info("Video4linux loopback driver v"VLOOPBACK_VERSION); | |
| 1094 | |
| 1095 if (pipes==-1) pipes=1; | |
| 1096 if (pipes > MAX_PIPES) { | |
| 1097 pipes=MAX_PIPES; | |
| 1098 info("Nr of pipes is limited to: %d", MAX_PIPES); | |
| 1099 } | |
| 1100 | |
| 1101 for (i=0; i<pipes; i++) { | |
| 1102 | |
| 1103 ret = create_pipe(i); | |
| 1104 | |
| 1105 if (ret == 0) { | |
| 1106 info("Loopback %d registered, input: video%d," | |
| 1107 "output: video%d", | |
| 1108 i, loops[i]->vloopin->minor, | |
| 1109 loops[i]->vloopout->minor); | |
| 1110 nr_o_pipes=i+1; | |
| 1111 }else{ | |
| 1112 return ret; | |
| 1113 } | |
| 1114 } | |
| 1115 return 0; | |
| 1116 } | |
| 1117 | |
| 1118 static void __exit cleanup_vloopback_module(void) | |
| 1119 { | |
| 1120 int i; | |
| 1121 | |
| 1122 info("Unregistering video4linux loopback devices"); | |
| 1123 for (i=0; i<nr_o_pipes; i++) if (loops[i]) { | |
| 1124 kfree(loops[i]->vloopin->priv); | |
| 1125 video_unregister_device(loops[i]->vloopin); | |
| 1126 kfree(loops[i]->vloopout->priv); | |
| 1127 video_unregister_device(loops[i]->vloopout); | |
| 1128 if (loops[i]->buffer) rvfree(loops[i]->buffer, loops[i]->buflength*N_BUFFS); | |
| 1129 kfree(loops[i]->ioctldata); | |
| 1130 kfree(loops[i]->ioctlretdata); | |
| 1131 kfree(loops[i]); | |
| 1132 } | |
| 1133 } | |
| 1134 | |
| 1135 module_init(vloopback_init); | |
| 1136 module_exit(cleanup_vloopback_module); |
