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); |