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