changeset 0:5f21a4dddc0c

Initial checkin
author KennethLavrsen
date Sun, 01 Apr 2007 05:22:43 +0000
parents
children bcf5fe83f332
files COPYING Makefile Modules.symvers README debian/README.Debian debian/changelog debian/compat debian/control.modules.in debian/copyright debian/rules example/Makefile example/dummy.c example/feed.c example/invert.c example/resize.c vloopback.c vloopback.html
diffstat 16 files changed, 3053 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,40 @@
+ifneq ($(KERNELRELEASE),)
+
+obj-m	:= vloopback.o
+
+else
+
+KVER  := $(shell uname -r)
+KLINK := $(shell test -e /lib/modules/${KVER}/source/ && echo source || echo "build")
+KSRC  := /lib/modules/$(KVER)/$(KLINK)
+PWD   := $(shell pwd)
+DEST  := /lib/modules/$(KVER)/kernel/drivers/misc
+
+# Fix some problem with suse < 9.2 and suse >= 9.2
+is_suse := $(shell test -e /etc/SuSE-release && echo 1 || echo 0)
+ifeq ($(is_suse),1)
+  suse_version := $(shell grep VERSION /etc/SuSE-release | cut -f 3 -d " "| tr -d .)
+  is_suse_92_or_greater := $(shell test $(suse_version) -ge 92 && echo 1)
+  ifeq ($(is_suse_92_or_greater),1)
+	KSRC := /lib/modules/$(KVER)/build
+  endif
+endif
+             
+
+
+all default:
+	$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules
+
+install:
+	install -d $(DEST)
+	install -m 644 -c vloopback.ko $(DEST)
+	-/sbin/depmod -a
+
+	
+uninstall:
+	rm -f $(DEST)/vloopback.ko
+	-/sbin/depmod -a
+clean:
+	rm -f .*.cmd *.o *.mod.c *.ko .v* *~ core
+	rm -rf .tmp_versions/
+endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,68 @@
+vloopback is a video4linux driver providing video pipes.
+With the driver you can use the output of a user program to feed a program that
+would normally communicate with a video4linux device.
+To achieve this a video pipe consists out of two video4linux devices:
+one for the generating program to write its data to and one for a normal
+video4linux program to read from.
+
+At the moment there are only few programs that can feed the input of the pipe:
+invert and resize, the example programs with the driver and
+motion, my motion detection program.
+
+Just type 'make' and then 'insmod vloopback'.
+If you want more pipes use 'insmod vloopback pipes=N' with N between 1 and 16.
+If you want to create spare pipes use 'spares=N'.
+
+Type 'dmesg' to see which video devices have been created as input and output.
+For example: if you have a camera on /dev/video0 the input pipe will most likely
+be /dev/video1 and the output on /dev/video2.
+e.g. if you want to watch an inverted image of the camera you would start invert
+with /dev/video0 as its input and /dev/video1 as its output.
+Then start a viewing app (such as camstream or xawtv) with as input /dev/video2
+and you can see yourself inverted.
+
+This is a very experimental device driver!
+SMP should work from 0.2 on..... but is untested!
+If it troubles you fix it or tell me how to fix it :)
+
+Using vloopback with xawtv:
+As of 0.83 xawtv will work with vloopback outputs, but you will have to specify
+the size to use. For example: 'xawtv -c /dev/video2 -geometry 320x240'
+
+Jeroen Vreeken, pe1rxq@amsat.org
+
+-------------------------------------------------------------------------------
+
+Module has experimental support for kernel 2.6.x .
+The current version 1.0 has been tested with kernel 2.6.x ( not with >= 2.6.16 ).
+
+To compile and install :
+
+make ; su - ; make install
+/sbin/modprobe videodev 
+/sbin/modprobe vloopback
+
+To uninstall :
+
+make uninstall ( as root ).
+
+- Problems :
+
+* If you got this message after 'insmod ./vloopback.ko' :
+
+ "insmod: error inserting './vloopback.ko': -1 Unknown symbol in module" 
+
+  Solution : You must load videodev module :
+
+ 'insmod videodev ; insmod ./vloopback.ko'
+
+* If you got this message after 'modprobe vloopback [options]' :
+
+ "FATAL: Error inserting vloopback (/lib/modules/2.6.8-1-k7/kernel/drivers/misc/vloopback.ko): Too many open files in system"
+
+ You probably used dev_offset= as a option , but the video device that vloopback was trying to register was already in use, maybe the value used for dev_offeset was too high.
+
+  Solution : Try to use a lower value for dev_offset= or even better don't use it .
+
+Angel Carpintero, ack@telefonica.net
+Kenneth Lavrsen, kenneth@lavrsen.dk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/README.Debian	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,61 @@
+I added as little glue as possible to the vloopback source to build with the
+Debian kernel module tools kernel-package [0] or module-assistant [1]
+
+
+kernel-package
+==============
+
+kernel-package is a utility for building Linux kernel related Debian packages
+just by running `make-kpkg kernel_image`
+
+Documentation on building extra kernel modules like vloopback is in the
+kernel-package package in `/usr/share/doc/kernel-package/README.modules`
+
+Essentially, you must download the vloopback source, which comes with the
+necessary Debian glue [2]
+
+Now you can add to the `make-kpkg` command line `--added-modules
+<vloopback_location> modules_image`
+
+No matter where you downloaded vloopback, you can use the absolute path to the
+source for <vloopback_location>
+
+If you downloaded vloopback to `/usr/src/modules/<vloopback_source>s`', you can
+use just <vloopback_source> for <vloopback_location>
+
+When `make-kpkg` is finished, you should find
+'vloopback-modules-<kernel_version>_<vloopback_version>+<kernel_revision>_<architecture>.deb'
+in the directory below that which you ran `make-kpkg`
+
+Use this package however you would ordinarily use a Debian package - for
+instance: `dpkg -i
+vloopback-modules-<kernel_version>_<vloopback_version>+<kernel_revision>_<architecture>.deb`
+
+
+module-assistant
+================
+
+The module-assistant tool helps users & package maintainers with managing
+external kernel modules packaged for Debian
+
+Documentation on building modules with module-assistant is in the
+module-assistant package in `/usr/share/doc/module-assistant/HOWTO`
+
+Essentially, you must download the vloopback source, as with kernel-package
+
+Then you can run `module-assistant build <vloopback_source>`
+
+It's not yet clear to me whether you must download vloopback to
+`/usr/src/module/<vloopback_source>`, whether you can use
+`MODULE_LOC=<other_path> module-assistant build <vloopback_source>` if
+vloopback is downloaded to `<other_path>/<vloopback_source` or whether you can
+run `module-assistant build <other_path>/<vloopback_source>`
+
+Consult the module-assistant documentation or contact the module-assistant
+maintainer for more information
+
+ -- Jack Bates <ms419@freezone.co.uk>
+
+[0] http://packages.debian.org/kernel-package
+[1] http://packages.debian.org/module-assistant
+[2] http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/changelog	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,17 @@
+vloopback (1.0-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Jack Bates <ms419@freezone.co.uk>  Wed, 30 Aug 2006 09:40:36 -0700
+
+vloopback (0.97-snap3-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Jack Bates <ms419@freezone.co.uk>  Mon,  1 May 2006 19:48:36 -0700
+
+vloopback (0.97-snap1-1) unstable; urgency=low
+
+  * Initial release
+
+ -- Jack Bates <ms419@freezone.co.uk>  Thu,  8 Dec 2005 16:43:57 -0800
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/compat	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,1 @@
+4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/control.modules.in	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,14 @@
+Source: vloopback
+Section: unknown
+Priority: optional
+Maintainer: Jack Bates <ms419@freezone.co.uk>
+Build-Depends: debhelper (>> 4.0.0) # module-assistant?
+Standards-Version: 3.6.2
+
+Package: vloopback-modules-_KVERS_
+Architecture: any
+Provides: vloopback-modules
+Description: vloopback modules for Linux _KVERS_
+ vloopback is a video4linux driver providing video pipes.  With this driver you
+ can use the output of a user program as input to another program which would
+ normally communicate with a video4linux device.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/copyright	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,23 @@
+Copyright Holder:
+
+    Angel Carpintero, ack@telefonica.net
+    Kenneth Lavrsen, kenneth@lavrsen.dk
+
+License:
+
+   This package is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This package is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this package; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/rules	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,52 @@
+#!/usr/bin/make -f
+
+# Uncomment to turn on verbose mode
+#export DH_VERBOSE = 1
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+	CFLAGS += -O0
+else
+	CFLAGS += -O2
+endif
+
+# Prefix of the target package name
+PACKAGE = vloopback-modules
+
+# Load generic variable handling
+-include /usr/share/modass/include/generic.make
+
+# Load default rules, including kdist, kdist_image, ...
+-include /usr/share/modass/include/common-rules.make
+
+binary-modules:
+	dh_testdir
+	dh_testroot
+	dh_installdirs lib/modules/$(KVERS)/misc
+
+	# Build the module
+	$(MAKE) -C $(KSRC) M=$(PWD) modules
+
+	# Install the module
+	$(MAKE) -C $(KSRC) M=$(PWD) \
+		DEST=debian/$(PKGNAME)/lib/modules/$(KVERS)/misc modules_install
+
+	dh_installdocs README vloopback.html
+	dh_installmodules
+	dh_installchangelogs
+	dh_link
+	dh_strip
+	dh_compress
+	dh_fixperms
+	dh_installdeb
+	dh_gencontrol -- -v$(VERSION)
+	dh_md5sums
+	dh_builddeb --destdir=$(DEB_DESTDIR)
+
+kdist_clean:
+	-$(MAKE) clean
+
+	# kdist_clean may be run before prep-deb-files
+	-dh_clean
+	rm -f debian/control
+
+kdist_config: prep-deb-files
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example/Makefile	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,14 @@
+CFLAGS=-g -Wall
+
+all:	invert resize feed dummy
+
+clean:
+	rm -f dummy resize invert feed *o *~
+
+invert:	invert.c
+
+resize:	resize.c
+
+feed: feed.c
+
+dummy:	dummy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example/dummy.c	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,435 @@
+/*	dummy.c
+ *
+ *	Example program for using a videoloopback device in zero-copy mode.
+ *	Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
+ *	Copyright 2005 by Angel Carpintero (ack@telefonica.net)
+ *	This software is distributed under the GNU public license version 2
+ *	See also the file 'COPYING'.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <dirent.h>
+#include <sys/utsname.h>
+#include <linux/videodev.h>
+
+/* all seem reasonable, or not? */
+#define MAXIOCTL 1024
+#define MAXWIDTH 640
+#define MAXHEIGHT 480
+int width;
+int height;
+int fmt=0;
+char ioctlbuf[MAXIOCTL];
+int v4ldev;
+char *image_out;
+	
+
+int get_frame(void)
+{
+	int i;
+	char colour = 0;
+
+	memset(image_out, 0x128, width*height*3);
+	
+	for (i=10; i<width-10; i++) {
+		image_out[10*width*3+i*3]=colour++;
+		image_out[10*width*3+i*3+1]=0;
+		image_out[10*width*3+i*3+2]=-colour;
+	}
+	for (i=10; i<width-10; i++) {
+		image_out[(height-10)*width*3+i*3]=colour;
+		image_out[(height-10)*width*3+i*3+1]=0;
+		image_out[(height-10)*width*3+i*3+2]=-colour++;
+	}
+	/*
+	*/
+	usleep(500); /* BIG XXX */
+	return 0;
+}
+
+char *v4l_create (int dev, int memsize)
+{
+	char *map;
+	
+	map=mmap(0, memsize, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	if ((unsigned char *)-1 == (unsigned char *)map)
+		return NULL;
+	return map;	
+}
+
+int v4l_ioctl(unsigned long int cmd, void *arg)
+{
+	int i;
+	switch (cmd) {
+		case VIDIOCGCAP:
+		{
+			struct video_capability *vidcap=arg;
+
+			sprintf(vidcap->name, "Jeroen's dummy v4l driver");
+			vidcap->type= VID_TYPE_CAPTURE;
+			vidcap->channels=1;
+			vidcap->audios=0;
+			vidcap->maxwidth=MAXWIDTH;
+			vidcap->maxheight=MAXHEIGHT;
+			vidcap->minwidth=20;
+			vidcap->minheight=20;
+			return 0;
+		}
+		case VIDIOCGCHAN:
+		{
+			struct video_channel *vidchan= (struct video_channel *)arg;
+			
+			printf("VIDIOCGCHAN called\n");
+			if (vidchan->channel!=0)
+				;//return 1;
+			vidchan->channel=0;
+			vidchan->flags=0;
+			vidchan->tuners=0;
+			vidchan->norm=0;
+			vidchan->type=VIDEO_TYPE_CAMERA;
+			strcpy(vidchan->name, "Loopback");
+			
+			return 0;
+		}
+		case VIDIOCSCHAN:
+		{
+			int *v=arg;
+			
+			if (v[0]!=0)
+				return 1;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner *v = arg;
+
+			if(v->tuner) {
+				printf("VIDIOCGTUNER: Invalid Tuner, was %d\n", v->tuner);
+				//return -EINVAL;
+			}
+			v->tuner=0;
+			strcpy(v->name, "Format");
+			v->rangelow=0;
+			v->rangehigh=0;
+			v->flags=0;
+			v->mode=VIDEO_MODE_AUTO;
+			return 1;
+		}
+		case VIDIOCGPICT:
+		{
+			struct video_picture *vidpic=arg;
+
+			vidpic->colour=0x8000;
+			vidpic->hue=0x8000;
+			vidpic->brightness=0x8000;
+			vidpic->contrast=0x8000;
+			vidpic->whiteness=0x8000;
+			vidpic->depth=0x8000;
+			vidpic->palette=fmt;
+			return 0;
+		}
+		case VIDIOCSPICT:
+		{
+			struct video_picture *vidpic=arg;
+			
+			if (vidpic->palette!=fmt)
+				return 1;
+			return 0;
+		}
+		case VIDIOCGWIN:
+		{
+			struct video_window *vidwin=arg;
+
+			vidwin->x=0;
+			vidwin->y=0;
+			vidwin->width=width;
+			vidwin->height=height;
+			vidwin->chromakey=0;
+			vidwin->flags=0;
+			vidwin->clipcount=0;
+			return 0;
+		}
+		case VIDIOCSWIN:
+		{
+			struct video_window *vidwin=arg;
+			
+			if (vidwin->width > MAXWIDTH ||
+			    vidwin->height > MAXHEIGHT )
+				return 1;
+			if (vidwin->flags)
+				return 1;
+			width=vidwin->width;
+			height=vidwin->height;
+			printf("new size: %dx%d\n", width, height);
+			return 0;
+		}
+		case VIDIOCGMBUF:
+		{
+			struct video_mbuf *vidmbuf=arg;
+
+			vidmbuf->size=width*height*3;
+			vidmbuf->frames=1;
+			for (i=0; i<vidmbuf->frames; i++)
+				vidmbuf->offsets[i]=i*vidmbuf->size;
+			return 0;
+		}
+		case VIDIOCMCAPTURE:
+		{
+			struct video_mmap *vidmmap=arg;
+
+			//return 0;
+			if (vidmmap->height>MAXHEIGHT ||
+			    vidmmap->width>MAXWIDTH ||
+			    vidmmap->format!=fmt )
+				return 1;
+			if (vidmmap->height!=height ||
+			    vidmmap->width!=width) {
+				height=vidmmap->height;
+				width=vidmmap->width;
+				printf("new size: %dx%d\n", width, height);
+			}
+			// check if 'vidmmap->frame' is valid
+			// initiate capture for 'vidmmap->frame' frames
+			return 0;
+		}
+		case VIDIOCSYNC:
+		{
+			//struct video_mmap *vidmmap=arg;
+
+			// check if frames are ready.
+			// wait until ready.
+			get_frame();
+			return 0;
+		}
+		default:
+		{
+			printf("unknown ioctl: %ld\n", cmd & 0xff);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+#define VIDIOCSINVALID	_IO('v',BASE_VIDIOCPRIVATE+1)
+
+void sighandler(int signo)
+{
+	int size, ret;
+	unsigned long int cmd;
+	struct pollfd ufds;
+
+	if (signo!=SIGIO)
+		return;
+	ufds.fd=v4ldev;
+	ufds.events=POLLIN;
+	ufds.revents=0;
+	poll(&ufds, 1, 1000);
+	if (!ufds.revents & POLLIN) {
+		printf("Received signal but got negative on poll?!?!?!?\n");
+		return;
+	}
+	size=read(v4ldev, ioctlbuf, MAXIOCTL);
+	if (size >= sizeof(unsigned long int)) {
+		memcpy(&cmd, ioctlbuf, sizeof(unsigned long int));
+		if (cmd==0) {
+			printf("Client closed device\n");
+			return;
+		}
+		ret=v4l_ioctl(cmd, ioctlbuf+sizeof(unsigned long int));
+		if (ret) {
+			memset(ioctlbuf+sizeof(unsigned long int), MAXIOCTL-sizeof(unsigned long int), 0xff);
+			printf("ioctl %lx unsuccesfull, lets issue VIDIOCSINVALID (%x)\n", cmd, VIDIOCSINVALID);
+			ioctl(v4ldev, VIDIOCSINVALID);
+		} else
+			ioctl(v4ldev, cmd, ioctlbuf+sizeof(unsigned long int));
+	}
+	return;
+}
+
+int open_vidpipe(void)
+{
+	int pipe_fd = -1;
+	FILE *vloopbacks;
+	char pipepath[255];
+	char buffer[255];
+	char *loop;
+	char *input;
+	char *istatus;
+	char *output;
+	char *ostatus;
+	char *major;
+	char *minor;
+	struct utsname uts;
+
+	if (uname(&uts) < 0) {
+		printf("Unable to execute uname\nError[%s]\n",strerror(errno));
+		return -1;
+	}
+	
+	major = strtok(uts.release, ".");
+	minor = strtok(NULL, ".");
+	if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) {
+		printf("Unable to decipher OS version\n");
+		return -1;
+	}
+
+	if (strcmp(minor, "5") < 0) {
+	
+		vloopbacks=fopen("/proc/video/vloopback/vloopbacks", "r");
+		if (!vloopbacks) {
+			printf ("Failed to open '/proc/video/vloopback/vloopbacks");
+			return -1;
+		}
+		/* Read vloopback version */
+		fgets(buffer, 255, vloopbacks);
+		printf("%s", buffer);
+		/* Read explaination line */
+		fgets(buffer, 255, vloopbacks);
+		while (fgets(buffer, 255, vloopbacks)) {
+			if (strlen(buffer)>1) {
+				buffer[strlen(buffer)-1]=0;
+				loop=strtok(buffer, "\t");
+				input=strtok(NULL, "\t");
+				istatus=strtok(NULL, "\t");
+				output=strtok(NULL, "\t");
+				ostatus=strtok(NULL, "\t");
+				if (istatus[0]=='-') {
+					sprintf(pipepath, "/dev/%s", input);
+					pipe_fd=open(pipepath, O_RDWR);
+					if (pipe_fd>=0) {
+						printf("Input: /dev/%s\n", input);
+						printf("Output: /dev/%s\n", output);
+						return pipe_fd;
+					}
+				}
+			} 
+		}
+
+	}else{
+		DIR *dir;
+		struct dirent *dirp;
+		const char prefix[]="/sys/class/video4linux/";
+		char *ptr, *io;
+		int fd;
+		int low=9999;
+		int tfd;
+		int tnum;
+
+		if ((dir=opendir(prefix))== NULL) {
+			printf( "Failed to open '%s'", prefix);
+			return -1;
+		}
+
+		while ((dirp=readdir(dir)) != NULL) {
+                        if (!strncmp(dirp->d_name, "video", 5)) {
+                                strcpy(buffer, prefix);
+                                strcat(buffer, dirp->d_name);
+                                strcat(buffer, "/name");
+                                if ((fd=open(buffer, O_RDONLY)) >= 0) {
+                                        if ((read(fd, buffer, sizeof(buffer)-1))<0) {
+                                                close(fd);
+                                                continue;
+                                        }
+                                        ptr = strtok(buffer, " ");
+                                        if (strcmp(ptr,"Video")) {
+                                                close(fd);
+                                                continue;
+                                        }
+                                        major = strtok(NULL, " ");
+                                        minor = strtok(NULL, " ");
+                                        io  = strtok(NULL, " \n");
+                                        if (strcmp(major, "loopback") || strcmp(io, "input")) {
+                                                close(fd);
+                                                continue;
+                                        }
+                                        if ((ptr=strtok(buffer, " "))==NULL) {
+                                                close(fd);
+                                                continue;
+                                        }
+                                        tnum = atoi(minor);
+                                        if (tnum < low) {
+                                                strcpy(buffer, "/dev/");
+                                                strcat(buffer, dirp->d_name);
+                                                if ((tfd=open(buffer, O_RDWR))>=0) {
+                                                        strcpy(pipepath, buffer);
+                                                        if (pipe_fd>=0) {
+                                                                close(pipe_fd);
+                                                        }
+                                                        pipe_fd = tfd;
+                                                        low = tnum;
+                                                }
+                                        }
+                                        close(fd);
+                                }
+                        }
+                }
+		
+
+		closedir(dir);
+                if (pipe_fd >= 0)
+                        printf("Opened input of %s", pipepath);
+        }
+	
+	return pipe_fd;
+}
+
+int main (int argc, char **argv)
+{
+	char palette[10]={'\0'};
+
+	if (argc != 3) {
+		printf("dummy.c\n");
+		printf("A example for using a video4linux loopback in zero-copy mode\n");
+		printf("Written by Jeroen Vreeken, 2000\n");
+		printf("Updated to vloopback API v0.97\n\n");
+		printf("Usage:\n\n");
+		printf("dummy widthxheight rgb24|yuv420p\n\n");
+		printf("example: dummy 352x288 yuv420p\n\n");
+		exit(1);
+	}
+	
+	sscanf(argv[1], "%dx%d", &width, &height);
+	sscanf(argv[2], "%s", palette);
+
+	if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24;
+	else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P;
+	else fmt = VIDEO_PALETTE_RGB24;
+	
+	/* Default startup values, nothing special 
+	width=352;
+	height=288;
+	*/
+
+	v4ldev=open_vidpipe();
+	if (v4ldev < 0) {
+		printf ("Failed to open video loopback device\nError[%s]\n",strerror(errno));
+		exit(1);
+	}
+	image_out=v4l_create(v4ldev, MAXWIDTH*MAXHEIGHT*3);
+	if (!image_out) {
+		exit(1);
+		printf ("Failed to set device to zero-copy mode\nError[%s]\n",strerror(errno));
+	}
+
+	signal (SIGIO, sighandler);
+
+	printf("\nListening.\n");
+	while (1) {
+		sleep(1000);
+	}
+
+	close (v4ldev);
+	free(image_out);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example/feed.c	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,217 @@
+/*	feed.c
+ *
+ *	Example program for videoloopback device.
+ *	Copyright 2006 by Angel Carpintero (ack@telefonica.net)
+ *	This software is distributed under the GNU public license version 2
+ *	See also the file 'COPYING'.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <linux/videodev.h>
+
+
+int fmt=0;
+int noexit = 1;
+int read_img=0;
+
+char *start_capture (int dev, int width, int height)
+{
+        struct video_capability vid_caps;
+	struct video_window vid_win;
+	struct video_mbuf vid_buf;
+	char *map;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno));
+		return (NULL);
+	}
+	if (vid_caps.type & VID_TYPE_MONOCHROME) fmt=VIDEO_PALETTE_GREY;
+	if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) {
+		fprintf(stderr, "no mmap falling back on read\n");
+		if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+			printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno));
+			return (NULL);
+		}
+		vid_win.width=width;
+		vid_win.height=height;
+		if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+			printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno));
+			return (NULL);
+		}
+		read_img=1;
+		map=malloc(width*height*3);
+		return (map);
+	}
+	/* If we are going to capture greyscale we need room to blow the image up */
+	if (fmt==VIDEO_PALETTE_GREY)
+		map=mmap(0, vid_buf.size*3, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	else
+		map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	
+	if ((unsigned char *)-1 == (unsigned char *)map)
+		return (NULL);
+	return map;
+}
+
+int start_pipe (int dev, int width, int height)
+{
+        struct video_capability vid_caps;
+	struct video_window vid_win;
+	struct video_picture vid_pic;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) {
+		printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	vid_pic.palette=fmt;
+	if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) {
+		printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+		printf ("ioctl VIDIOCGWIN");
+		return (1);
+	}
+	vid_win.width=width;
+	vid_win.height=height;
+	if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+		printf ("ioctl VIDIOCSWIN");
+		return (1);
+	}
+	return 0;
+}
+
+char *next_capture (int dev, char *map, int width, int height)
+{
+	int i;
+	char *grey, *rgb;
+	struct video_mmap vid_mmap;
+
+    	sigset_t    set, old;
+
+	if (read_img) {
+		if (fmt==VIDEO_PALETTE_GREY) {
+			if (read(dev, map, width*height) != width*height)
+				return NULL;
+		} else {
+			if (read(dev, map, width*height*3) != width*height*3)
+				return NULL;
+		}
+	} else {
+		vid_mmap.format=fmt;
+		vid_mmap.frame=0;
+		vid_mmap.width=width;
+		vid_mmap.height=height;
+    
+        	sigemptyset (&set);	     //BTTV hates signals during IOCTL
+	        sigaddset (&set, SIGCHLD);   //block SIGCHLD & SIGALRM
+       		sigaddset (&set, SIGALRM);   //for the time of ioctls
+        	sigprocmask (SIG_BLOCK, &set, &old);
+                    
+		if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) {
+	        	sigprocmask (SIG_UNBLOCK, &old, NULL);
+			return (NULL);
+		}
+		if (ioctl(dev, VIDIOCSYNC, &vid_mmap) == -1) {
+	        	sigprocmask (SIG_UNBLOCK, &old, NULL);
+			return (NULL);
+		}
+		
+        	sigprocmask (SIG_UNBLOCK, &old, NULL); //undo the signal blocking
+	}
+	/* Blow up a grey */
+	if (fmt==VIDEO_PALETTE_GREY) {
+		i=width*height;
+		grey=map+i-1;
+		rgb=map+i*3;
+		for (; i>=0; i--, grey--) {
+			*(rgb--)=*grey;
+			*(rgb--)=*grey;
+			*(rgb--)=*grey;
+		}
+	}
+	return map;
+}
+
+int put_image(int dev, char *image, int width, int height)
+{
+	if (write(dev, image, width*height*3)!=width*height*3) {
+		printf("Error writing image to pipe!\nError[%s]\n",strerror(errno));
+		return 0;
+	}
+	return 1;
+}
+
+void sig_handler(int signo)
+{
+	noexit = 0;
+}
+
+
+int main (int argc, char **argv)
+{
+	int devin, devout;
+	int width;
+	int height;
+	char *image_new;
+	char palette[10]={'\0'};
+	
+	if (argc != 5) {
+		printf("Usage:\n\n");
+		printf("feed input output widthxheight(in) rgb24|yuv420p\n\n");
+		printf("example: feed /dev/video0 /dev/video1 352x288 yuv420p\n\n");
+		exit(1);
+	}
+	sscanf(argv[3], "%dx%d", &width, &height);
+	sscanf(argv[4], "%s", palette);
+
+	if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24;
+	else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P;		
+	else fmt = VIDEO_PALETTE_RGB24; 
+		
+	devin=open (argv[1], O_RDWR);
+	if (devin < 0) {
+		printf ("Failed to open video device %s\nError[%s]\n",argv[1],strerror(errno));
+		exit(1);
+	}
+
+	devout=open (argv[2], O_RDWR);
+	if (devout < 0) {
+		printf ("Failed to open video device%s \nError[%s]\n",argv[2],strerror(errno));
+		exit(1);
+	}
+
+	image_new=start_capture (devin, width, height);
+	if (!image_new) {
+		printf("Capture error\nError[%s]\n",strerror(errno));
+		exit(1);
+	}
+
+	start_pipe(devout, width, height);
+
+	signal(SIGTERM, sig_handler);
+	
+	printf("Starting video stream.\n");
+	while ( (next_capture(devin, image_new, width, height)) && (noexit)) {
+		if (put_image(devout, image_new, width, height)==0)
+			exit(1);
+	}
+	printf("You bought vaporware!\nError[%s]\n",strerror(errno));
+	close (devin);
+	close (devout);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example/invert.c	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,223 @@
+/*	invert.c
+ *
+ *	Example program for videoloopback device.
+ *	Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
+ *	Copyright 2005 by Angel Carpintero (ack@telefonica.net)
+ *	This software is distributed under the GNU public license version 2
+ *	See also the file 'COPYING'.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <linux/videodev.h>
+
+
+int fmt=0;
+int noexit = 1;
+int read_img=0;
+
+char *start_capture (int dev, int width, int height)
+{
+        struct video_capability vid_caps;
+	struct video_window vid_win;
+	struct video_mbuf vid_buf;
+	char *map;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno));
+		return (NULL);
+	}
+	if (vid_caps.type & VID_TYPE_MONOCHROME) fmt=VIDEO_PALETTE_GREY;
+	if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) {
+		fprintf(stderr, "no mmap falling back on read\n");
+		if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+			printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno));
+			return (NULL);
+		}
+		vid_win.width=width;
+		vid_win.height=height;
+		if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+			printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno));
+			return (NULL);
+		}
+		read_img=1;
+		map=malloc(width*height*3);
+		return (map);
+	}
+	/* If we are going to capture greyscale we need room to blow the image up */
+	if (fmt==VIDEO_PALETTE_GREY)
+		map=mmap(0, vid_buf.size*3, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	else
+		map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	
+	if ((unsigned char *)-1 == (unsigned char *)map)
+		return (NULL);
+	return map;
+}
+
+int start_pipe (int dev, int width, int height)
+{
+        struct video_capability vid_caps;
+	struct video_window vid_win;
+	struct video_picture vid_pic;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) {
+		printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	vid_pic.palette=fmt;
+	if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) {
+		printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+		printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	vid_win.width=width;
+	vid_win.height=height;
+	if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+		printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	return 0;
+}
+
+char *next_capture (int dev, char *map, int width, int height)
+{
+	int i;
+	char *grey, *rgb;
+	struct video_mmap vid_mmap;
+
+    	sigset_t    set, old;
+
+	if (read_img) {
+		if (fmt==VIDEO_PALETTE_GREY) {
+			if (read(dev, map, width*height) != width*height)
+				return NULL;
+		} else {
+			if (read(dev, map, width*height*3) != width*height*3)
+				return NULL;
+		}
+	} else {
+		vid_mmap.format=fmt;
+		vid_mmap.frame=0;
+		vid_mmap.width=width;
+		vid_mmap.height=height;
+    
+        	sigemptyset (&set);	     //BTTV hates signals during IOCTL
+	        sigaddset (&set, SIGCHLD);   //block SIGCHLD & SIGALRM
+       		sigaddset (&set, SIGALRM);   //for the time of ioctls
+        	sigprocmask (SIG_BLOCK, &set, &old);
+                    
+		if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) {
+	        	sigprocmask (SIG_UNBLOCK, &old, NULL);
+			return (NULL);
+		}
+		if (ioctl(dev, VIDIOCSYNC, &vid_mmap) == -1) {
+	        	sigprocmask (SIG_UNBLOCK, &old, NULL);
+			return (NULL);
+		}
+		
+        	sigprocmask (SIG_UNBLOCK, &old, NULL); //undo the signal blocking
+	}
+	/* Blow up a grey */
+	if (fmt==VIDEO_PALETTE_GREY) {
+		i=width*height;
+		grey=map+i-1;
+		rgb=map+i*3;
+		for (; i>=0; i--, grey--) {
+			*(rgb--)=*grey;
+			*(rgb--)=*grey;
+			*(rgb--)=*grey;
+		}
+	}
+	return map;
+}
+
+int put_image(int dev, char *image, int width, int height)
+{
+	if (write(dev, image, width*height*3)!=width*height*3) {
+		printf("Error writing image to pipe!\nError[%s]\n",strerror(errno));
+		return 0;
+	}
+	return 1;
+}
+
+
+void sig_handler(int signo)
+{
+	noexit = 0;
+}
+
+int main (int argc, char **argv)
+{
+	int i, devin, devout;
+	int width;
+	int height;
+	char *image_out, *image_new;
+	char palette[10]={'\0'};
+	
+	if (argc != 5) {
+		printf("Usage:\n\n");
+		printf("invert input output widthxheight rgb24|yuv420p\n\n");
+		printf("example: invert /dev/video0 /dev/video1 352x288 yuv420p\n\n");
+		exit(1);
+	}
+	sscanf(argv[3], "%dx%d", &width, &height);
+	sscanf(argv[4], "%s", palette);
+
+	if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24;
+	else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P;
+	else fmt = VIDEO_PALETTE_RGB24;
+
+	
+	image_out=malloc(width*height*3);
+
+	devin=open (argv[1], O_RDWR);
+	if (devin < 0) {
+		printf("Failed to open input video device [%s]\nError:[%s]\n",argv[1],strerror(errno));
+		exit(1);
+	}
+
+	devout=open (argv[2], O_RDWR);
+	if (devout < 0) {
+		printf ("Failed to open output video device [%s]\nError:[%s]\n",argv[2],strerror(errno));
+		exit(1);
+	}
+
+	image_new=start_capture (devin, width, height);
+	if (!image_new) {
+		printf("Capture error \n Error[%s]\n",strerror(errno));
+		exit(1);
+	}
+
+	start_pipe(devout, width, height);
+
+	signal(SIGTERM, sig_handler);
+	
+	printf("Starting video stream.\n");
+	while ( (next_capture(devin, image_new, width, height)) && (noexit) ){
+		for (i=width*height*3; i>=0; i--) image_out[i]=-image_new[i];
+		if (put_image(devout, image_out, width, height)==0)
+			exit(1);
+	}
+	printf("You bought vaporware!\nError[%s]\n",strerror(errno));
+	close (devin);
+	close (devout);
+	free(image_out);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example/resize.c	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,265 @@
+/*	resize.c
+ *
+ *	Example program for videoloopback device.
+ *	Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
+ *	Copyright 2005 by Angel Carpintero (ack@telefonica.net)
+ *	This software is distributed under the GNU public license version 2
+ *	See also the file 'COPYING'.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <linux/videodev.h>
+
+
+int fmt=0;
+int noexit = 1;
+int read_img=0;
+
+char *start_capture (int dev, int width, int height)
+{
+        struct video_capability vid_caps;
+	struct video_window vid_win;
+	struct video_mbuf vid_buf;
+	char *map;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno));
+		return (NULL);
+	}
+	if (vid_caps.type & VID_TYPE_MONOCHROME) fmt=VIDEO_PALETTE_GREY;
+	if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) {
+		fprintf(stderr, "no mmap falling back on read\n");
+		if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+			printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno));
+			return (NULL);
+		}
+		vid_win.width=width;
+		vid_win.height=height;
+		if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+			printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno));
+			return (NULL);
+		}
+		read_img=1;
+		map=malloc(width*height*3);
+		return (map);
+	}
+	/* If we are going to capture greyscale we need room to blow the image up */
+	if (fmt==VIDEO_PALETTE_GREY)
+		map=mmap(0, vid_buf.size*3, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	else
+		map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
+	
+	if ((unsigned char *)-1 == (unsigned char *)map)
+		return (NULL);
+	return map;
+}
+
+int start_pipe (int dev, int width, int height)
+{
+        struct video_capability vid_caps;
+	struct video_window vid_win;
+	struct video_picture vid_pic;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) {
+		printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	vid_pic.palette=fmt;
+	if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) {
+		printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno));
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+		printf ("ioctl VIDIOCGWIN");
+		return (1);
+	}
+	vid_win.width=width;
+	vid_win.height=height;
+	if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+		printf ("ioctl VIDIOCSWIN");
+		return (1);
+	}
+	return 0;
+}
+
+char *next_capture (int dev, char *map, int width, int height)
+{
+	int i;
+	char *grey, *rgb;
+	struct video_mmap vid_mmap;
+
+    	sigset_t    set, old;
+
+	if (read_img) {
+		if (fmt==VIDEO_PALETTE_GREY) {
+			if (read(dev, map, width*height) != width*height)
+				return NULL;
+		} else {
+			if (read(dev, map, width*height*3) != width*height*3)
+				return NULL;
+		}
+	} else {
+		vid_mmap.format=fmt;
+		vid_mmap.frame=0;
+		vid_mmap.width=width;
+		vid_mmap.height=height;
+    
+        	sigemptyset (&set);	     //BTTV hates signals during IOCTL
+	        sigaddset (&set, SIGCHLD);   //block SIGCHLD & SIGALRM
+       		sigaddset (&set, SIGALRM);   //for the time of ioctls
+        	sigprocmask (SIG_BLOCK, &set, &old);
+                    
+		if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) {
+	        	sigprocmask (SIG_UNBLOCK, &old, NULL);
+			return (NULL);
+		}
+		if (ioctl(dev, VIDIOCSYNC, &vid_mmap) == -1) {
+	        	sigprocmask (SIG_UNBLOCK, &old, NULL);
+			return (NULL);
+		}
+		
+        	sigprocmask (SIG_UNBLOCK, &old, NULL); //undo the signal blocking
+	}
+	/* Blow up a grey */
+	if (fmt==VIDEO_PALETTE_GREY) {
+		i=width*height;
+		grey=map+i-1;
+		rgb=map+i*3;
+		for (; i>=0; i--, grey--) {
+			*(rgb--)=*grey;
+			*(rgb--)=*grey;
+			*(rgb--)=*grey;
+		}
+	}
+	return map;
+}
+
+int put_image(int dev, char *image, int width, int height)
+{
+	if (write(dev, image, width*height*3)!=width*height*3) {
+		printf("Error writing image to pipe!\nError[%s]\n",strerror(errno));
+		return 0;
+	}
+	return 1;
+}
+
+void sig_handler(int signo)
+{
+	noexit = 0;
+}
+
+int main (int argc, char **argv)
+{
+	int x, y, devin, devout;
+	int width, realwidth;
+	int height;
+	int widthout, realwidthout;
+	int heightout;
+	int **newy, **newx, **line;
+	char *image_out, *image_new;
+	char palette[10]={'\0'};
+	
+	if (argc != 6) {
+		printf("Usage:\n\n");
+		printf("resize input output widthxheight(in) widthxheight(out) rgb24|yuv420p\n\n");
+		printf("example: resize /dev/video0 /dev/video1 352x288 176x144 yuv420p\n\n");
+		exit(1);
+	}
+	sscanf(argv[3], "%dx%d", &width, &height);
+	sscanf(argv[4], "%dx%d", &widthout, &heightout);
+	sscanf(argv[5], "%s", palette);
+
+	if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24;
+	else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P;		
+	else fmt = VIDEO_PALETTE_RGB24; 
+		
+	realwidth=width*3;
+	realwidthout=widthout*3;
+
+	image_out=malloc(widthout*heightout*3);
+	line=malloc(sizeof(int*)*heightout);
+	newy=malloc(sizeof(int*)*heightout);
+	newx=malloc(sizeof(int*)*realwidthout);
+	for (y=0; y<heightout; y++) {
+		line[y]=malloc(sizeof(int));
+		line[y][0]=y*realwidthout;
+		newy[y]=malloc(sizeof(int));
+		newy[y][0]=y*height/heightout*realwidth;
+	}
+	for (x=0; x<widthout; x++) {
+		newx[x*3]=malloc(sizeof(int));
+		newx[x*3+1]=malloc(sizeof(int));
+		newx[x*3+2]=malloc(sizeof(int));
+		newx[x*3][0]=x*width/widthout*3;
+		newx[x*3+1][0]=x*width/widthout*3+1;
+		newx[x*3+2][0]=x*width/widthout*3+2;
+	}
+	
+	devin=open (argv[1], O_RDWR);
+	if (devin < 0) {
+		printf ("Failed to open video device %s\nError[%s]\n",argv[1],strerror(errno));
+		exit(1);
+	}
+
+	devout=open (argv[2], O_RDWR);
+	if (devout < 0) {
+		printf ("Failed to open video device%s \nError[%s]\n",argv[2],strerror(errno));
+		exit(1);
+	}
+
+	image_new=start_capture (devin, width, height);
+	if (!image_new) {
+		printf("Capture error\nError[%s]\n",strerror(errno));
+		exit(1);
+	}
+
+	start_pipe(devout, widthout, heightout);
+
+	signal(SIGTERM, sig_handler);
+	
+	printf("Starting video stream.\n");
+	while ( (next_capture(devin, image_new, width, height)) && (noexit)) {
+		for (y=0; y<heightout; y++) {
+			for (x=0; x<realwidthout; x++) {
+				image_out[line[y][0]+x]=
+				    image_new[newy[y][0]+newx[x][0]];
+			}
+		}
+		if (put_image(devout, image_out, widthout, heightout)==0)
+			exit(1);
+	}
+	printf("You bought vaporware!\nError[%s]\n",strerror(errno));
+	close (devin);
+	close (devout);
+
+	for (y=0; y<heightout; y++) {
+		free(line[y]);
+		free(newy[y]);
+	}
+	for (x=0; x<widthout; x++) {
+		free(newx[x*3]);
+		free(newx[x*3+1]);
+		free(newx[x*3+2]);
+	}
+
+	free(line);
+	free(newx);
+	free(newy);
+	
+	free(image_out);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vloopback.c	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,1136 @@
+/*
+ *	vloopback.c
+ *
+ *	Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000
+ *	Additional copyright by the contributing authors in the
+ *	change history below, 2000-2007
+ *
+ *	Published under the GNU Public License.
+ *
+ *	The Video Loopback Device is no longer systematically maintained.
+ *	The project is a secondary project for the project "motion" found at
+ *	http://motion.sourceforge.net/ and
+ *	http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome
+ *	and with the vloopback stored at
+ *	http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice
+ *
+ *	CHANGE HISTORY
+ *
+ *	UPDATED:	Jeroen Vreeken.
+ *			Added locks for smp machines. UNTESTED!
+ *			Made the driver much more cpu friendly by using
+ *			a wait queue.
+ *			Went from vmalloc to rvmalloc (yes, I stole the code
+ *			like everybody else) and implemented mmap.
+ *			Implemented VIDIOCGUNIT and removed size/palette checks
+ *			in VIDIOCSYNC.
+ *			Cleaned up a lot of code.
+ *			Changed locks to semaphores.
+ *			Disabled changing size while somebody is using mmap
+ *			Changed mapped check to open check, also don't allow
+ *			a open for write while somebody is reading.
+ *			Added /proc support
+ *			Set dumped count to zero at open.
+ *			Modified /proc layout (added vloopbacks entry)
+ *
+ * 05.10.00 (MTS)	Added Linux 2.2 support
+ * 06.10.00 (J Vreeken)	Fixed 2.2 support to make things work under 2.4 again.
+ * 17.10.00 (J Vreeken)	Added zero copy mode
+ * 19.10.00 (J Vreeken) Added SIGIO on device close.
+ * 24.10.00 (J Vreeken) Modified 2.2 stuff and removed spinlock.h
+ *			released 0.81
+ * 27.10.00 (J Vreeken) Implemented poll
+ *			released 0.82
+ * 17.01.01 (J Vreeken) support for xawtv
+ * 			Implemented VIDIOCGFBUF
+ *			Additional checks on framebuffer freeing.
+ *			released 0.83
+ * 31.01.01 (J Vreeken)	Removed need for 'struct ioctl', use _IOC_SIZE() and
+ *			IOC_IN instead.
+ *			Change the ioctlnr passing to 'unsigned long int'
+ *			Instead of just one byte.
+ *			THIS BREAKS COMPATIBILITY WITH PREVIOUS VERSIONS!!! 
+ * 29.06.01 (J Vreeken)	Added dev_offset module option
+ *			Made vloopback_template sane
+ *			Added double buffering support
+ *			Made vloopback less verbose
+ * 20.11.01	(tibit)	Made dev_offset option sane
+ *			"Fixed" zerocopy mode by defining the ioctl 
+ *			VIDIOCSINVALID. An application which provides data
+ *			has to issue it when it encounters an error in
+ *			ioctl processing. See dummy.c for examples.
+ * 26.11.03	(Kenneth Lavrsen)
+ *			released 0.91
+ *			0.91 is the combination of the 0.90-tibit by
+ *			Tilmann Bitterberg and an update of the Makefile by
+ *			Roberto Carvajal.
+ * 23.01.05	(W Brack)
+ *			(don't know what happened to the comments for 0.92
+ *			 and 0.93, but I tentatively named this one as 0.99)
+ *			enhanced for linux-2.6, with #ifdef to keep it
+ *			compatible with linux-2.4.  For linux versions
+ *			> 2.5, I changed the memory management
+ *			routines to the "more modern" way, most of it
+ *			shamelessly copied from other drivers.  I also
+ *			added in the code necessary to avoid the "videodev
+ *			has no release callback" message when installing.
+ *			For versions < 2.5, I updated the routines to be
+ *			closer to several other drivers.
+ *
+ * 04.02.05	(Angel Carpintero)
+ *			Fixed version number to 0.93-pre1.
+ *			Fixed warning for interruptible_sleep_on() deprecated and added 
+ *			wait_event_interruptible compatible with 2.6.x and 2.7.
+ *			Fixed memory manager for kernel version > 2.6.9.
+ *
+ * 07.02.05	(Kenneth Lavrsen)
+ *			Changed version to 0.94.
+ *			Released as formal released version
+ *
+ * 20.02.05	(W Brack)
+ *			Fixed error with wait_event_interruptible.
+ *			Fixed crash when pipe source was stopped before dest.
+ *
+ * 20.02.05	(Angel Carpintero)     
+ *			Added install and uninstall in Makefile.
+ *
+ *
+ * 25.04.05	(Angel Carpintero)
+ *			Included Samuel Audet's patch, it checks if the input is already
+ *			opened in write mode.
+ *
+ * 02.05.05	(Kenneth Lavrsen)
+ *			Released 0.95-snap2 formerly as 0.95
+ *	
+ * 10.05.05	(Angel Carpintero)
+ *			Added MODULE_VERSION(), fixed create_pipes when video_register_device() returns
+ *			-ENFILE . 
+ *			Fix warnings about checking return value from copy_to_user() and copy_from_user() functions.
+ *
+ * 14.11.05	(Angel Carpintero)
+ *			Added <linux/version.h> that includes LINUX_VERSION_CODE and KERNEL_VERSION to fix 
+ *			compilation agains kernel 2.6.14 , change version to 0.97-snap1
+ *
+ * 19.12.05	(Angel Carpintero)
+ *			Added to example option to choose between rgb24 or yuv420p palettes.
+ *
+ * 31.12.05	(Angel Carpintero)
+ * 			Fixed examples, remove perror calls and add support to dummy.c for sysfs.
+ * 			
+ * 04.06.06	(Angel Carpintero)
+ * 			Add module_param() for kernel > 2.5 because MODULE_PARAM() macro is obsolete.
+ *
+ * 17.06.06	(Angel Carpintero)
+ *			Release version 1.0 with some fixes and code clean up. Added a Jack Bates contribution
+ *			to allow build a kernel module in debian way.
+ *
+ * 26.06.06	(Angel Carpintero)
+ *			Added some improvements in Makefile. Fix a problem to compile in Suse.
+ *
+ *
+ * 02.11.06	(Angel Carpintero)
+ * 			Make compatible with new kernel stable version 2.6.18, Many functions and declarations has
+ * 			been moved to media/v42l-dev.h and remove from videodev.h/videodev2.h
+ *
+ * 18.01.07	(Angel Carpintero)	
+ * 			Change -ENOIOCTLCMD by more appropiate error -ENOTTY.					
+ */
+
+
+#define VLOOPBACK_VERSION "1.1-rc1"
+
+/* Include files common to 2.4 and 2.6 versions */
+#include <linux/version.h>	/* >= 2.6.14 LINUX_VERSION_CODE */ 
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+#include <media/v4l2-common.h>
+#endif
+
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+/* Include files which are unique to versions */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+ #include <asm/ioctl.h>
+ #include <asm/page.h> 
+ #include <asm/pgtable.h>
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+  #ifndef	remap_pfn_range
+    #define	remap_pfn_range(a,b,c,d,e) \
+	 remap_page_range((a),(b),(c)<<PAGE_SHIFT,(d),(e))
+  #endif
+  #ifndef	vmalloc_to_pfn
+    #define	vmalloc_to_pfn(a) page_to_pfn(vmalloc_to_page((a)))
+  #endif
+ #endif
+ #include <asm/uaccess.h>
+ #include <linux/init.h>
+ #include <linux/device.h>
+#else
+ #include <linux/mm.h>
+ #include <linux/slab.h>
+ #include <linux/wrapper.h>
+ #include <asm/io.h>
+#endif
+ 
+#define VIDIOCSINVALID	_IO('v',BASE_VIDIOCPRIVATE+1)
+
+#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg)
+
+struct vloopback_private {
+	int pipenr;
+	int in; /* bool */
+};
+typedef struct vloopback_private *priv_ptr;
+
+struct vloopback_pipe {
+	struct video_device *vloopin;
+ 	struct video_device *vloopout;
+	char *buffer;
+	unsigned long buflength;
+	unsigned int width, height;
+	unsigned int palette;
+	unsigned long frameswrite;
+	unsigned long framesread;
+	unsigned long framesdumped;
+	unsigned int wopen;
+	unsigned int ropen;
+	struct semaphore lock;
+	wait_queue_head_t wait;
+	unsigned int frame;
+	unsigned int pid;
+	unsigned int zerocopy;
+	unsigned long int ioctlnr;
+	unsigned int invalid_ioctl; /* 0 .. none invalid; 1 .. invalid */
+	unsigned int ioctllength;
+	char *ioctldata;
+	char *ioctlretdata;
+};
+
+#define MAX_PIPES 16
+#define	N_BUFFS	2	/* Number of buffers used for pipes */
+
+static struct vloopback_pipe *loops[MAX_PIPES];
+static int nr_o_pipes=0;
+static int pipes=-1;
+static int spares=0;
+static int pipesused=0;
+static int dev_offset=-1;
+
+/**********************************************************************
+ *
+ * Memory management - revised for 2.6 kernels
+ *
+ **********************************************************************/
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr) 
+{
+        unsigned long kva;
+
+	kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
+	kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+	return __pa(kva);
+}
+#endif
+
+static void *rvmalloc(unsigned long size)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	struct page *page;
+#endif
+	void *mem;
+	unsigned long adr;
+
+	size = PAGE_ALIGN(size);
+	mem = vmalloc_32(size);
+	if (!mem)
+		return NULL;
+	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+		page = vmalloc_to_page((void *)adr);
+		mem_map_reserve(page);
+#else
+		SetPageReserved(vmalloc_to_page((void *)adr));
+#endif
+		adr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	struct page *page;
+#endif
+	unsigned long adr;
+
+	if (!mem)
+		return;
+
+	adr = (unsigned long) mem;
+	while ((long) size > 0) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+		page = vmalloc_to_page((void *)adr);
+		mem_map_unreserve(page);
+#else
+		ClearPageReserved(vmalloc_to_page((void *)adr));
+#endif
+		adr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+	vfree(mem);
+}
+
+
+static int create_pipe(int nr);
+
+static int fake_ioctl(int nr, unsigned long int cmd, void *arg)
+{
+	unsigned long fw;
+	
+	loops[nr]->ioctlnr=cmd;
+	memcpy(loops[nr]->ioctldata, arg, _IOC_SIZE(cmd));
+	loops[nr]->ioctllength=_IOC_SIZE(cmd);
+	kill_proc(loops[nr]->pid, SIGIO, 1);	/* Signal the pipe feeder */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+	fw = loops[nr]->frameswrite;
+	wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite);
+#else
+	interruptible_sleep_on(&loops[nr]->wait);
+#endif	
+	if (cmd & IOC_IN) {
+		if (memcmp (arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd)))
+			return 1;
+	} else {
+		memcpy (arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd));
+	}
+	return 0;
+}
+
+static int vloopback_open(struct inode *inod, struct file *f)
+{	
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+	
+
+	/* Only allow a output to be opened if there is someone feeding
+	 * the pipe.
+	 */
+	if (!ptr->in) {
+		if (loops[nr]->buffer==NULL) {
+			return -EINVAL;
+		}
+		loops[nr]->framesread=0;
+		loops[nr]->ropen=1;
+	} else {
+		if (loops[nr]->ropen || loops[nr]->wopen) 
+			return -EBUSY;
+		loops[nr]->framesdumped=0;
+		loops[nr]->frameswrite=0;
+		loops[nr]->wopen=1;
+		loops[nr]->zerocopy=0;
+		loops[nr]->ioctlnr=-1;
+		pipesused++;
+		if (nr_o_pipes-pipesused<spares) {
+			if (!create_pipe(nr_o_pipes)) {
+				info("Creating extra spare pipe");
+				info("Loopback %d registered, input: video%d, output: video%d",
+				    nr_o_pipes,
+				    loops[nr_o_pipes]->vloopin->minor,
+				    loops[nr_o_pipes]->vloopout->minor
+				);
+				nr_o_pipes++;
+			}
+		}
+		loops[nr]->pid=current->pid;
+	}
+	return 0;
+}
+
+static int vloopback_release(struct inode * inod, struct file *f)
+{
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+	
+	if (ptr->in) {
+		down(&loops[nr]->lock);
+		if (loops[nr]->buffer && !loops[nr]->ropen) {
+			rvfree(loops[nr]->buffer,
+			    loops[nr]->buflength*N_BUFFS);
+			loops[nr]->buffer=NULL;
+		}
+		up(&loops[nr]->lock);
+		loops[nr]->frameswrite++;
+		if (waitqueue_active(&loops[nr]->wait))
+			wake_up(&loops[nr]->wait);
+
+		loops[nr]->width=0;
+		loops[nr]->height=0;
+		loops[nr]->palette=0;
+		loops[nr]->wopen=0;
+		pipesused--;
+	} else {
+		down(&loops[nr]->lock);
+		if (loops[nr]->buffer && !loops[nr]->wopen) {
+			rvfree(loops[nr]->buffer,
+			    loops[nr]->buflength*N_BUFFS);
+			loops[nr]->buffer=NULL;
+		}
+		up(&loops[nr]->lock);
+		loops[nr]->ropen=0;
+		if (loops[nr]->zerocopy && loops[nr]->buffer) {
+			loops[nr]->ioctlnr=0;
+			loops[nr]->ioctllength=0;
+			kill_proc(loops[nr]->pid, SIGIO, 1);
+		}
+	}
+
+	return 0;
+}
+
+static ssize_t vloopback_write(struct file *f, const char *buf,
+		size_t count, loff_t *offset)
+{
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+	unsigned long realcount=count;
+	
+	if (!ptr->in)
+		return -EINVAL;
+	if (loops[nr]->zerocopy)
+		return -EINVAL;
+	
+	if (loops[nr]->buffer==NULL) {
+		return -EINVAL;
+	}
+
+	/* Anybody want some pictures??? */
+	if (!waitqueue_active(&loops[nr]->wait)) {
+		loops[nr]->framesdumped++;
+		return realcount;
+	}
+	
+	down(&loops[nr]->lock);
+	if (!loops[nr]->buffer) {
+		up(&loops[nr]->lock);
+		return -EINVAL;
+	}
+	if (realcount > loops[nr]->buflength) {
+		realcount = loops[nr]->buflength;
+		info("Too much data! Only %ld bytes used.", realcount);
+	}
+	
+	if (copy_from_user(
+	    loops[nr]->buffer+loops[nr]->frame*loops[nr]->buflength,
+	    buf, realcount
+	)) return -EFAULT;
+
+	loops[nr]->frame=0;
+	up(&loops[nr]->lock);
+
+	loops[nr]->frameswrite++;
+	wake_up(&loops[nr]->wait);
+
+	return realcount;
+}
+
+static ssize_t vloopback_read (struct file * f, char * buf, size_t count, loff_t *offset)
+{
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+	unsigned long realcount=count;
+
+	if (loops[nr]->zerocopy) {
+		if (ptr->in) {
+			if (realcount > loops[nr]->ioctllength+sizeof(unsigned long int))
+				realcount=loops[nr]->ioctllength+sizeof(unsigned long int);
+			if (copy_to_user(buf , &loops[nr]->ioctlnr, sizeof(unsigned long int)))
+				return -EFAULT;
+			if (copy_to_user(buf+sizeof(unsigned long int) , loops[nr]->ioctldata, 
+					realcount-sizeof(unsigned long int)))
+				return -EFAULT;	
+			if (loops[nr]->ioctlnr==0)
+				loops[nr]->ioctlnr=-1;
+			return realcount;
+		} else {
+			struct video_window vidwin;
+			struct video_mmap vidmmap;
+			struct video_picture vidpic;
+			
+			fake_ioctl(nr, VIDIOCGWIN, &vidwin);
+			fake_ioctl(nr, VIDIOCGPICT, &vidpic);
+
+			vidmmap.height=vidwin.height;
+			vidmmap.width=vidwin.width;
+			vidmmap.format=vidpic.palette;
+			vidmmap.frame=0;
+			if (fake_ioctl(nr, VIDIOCMCAPTURE, &vidmmap))
+				return 0;
+			if (fake_ioctl(nr, VIDIOCSYNC, &vidmmap))
+				return 0;
+			realcount=vidwin.height*vidwin.width*vidpic.depth;
+		}
+	}
+	if (ptr->in)
+		return -EINVAL;
+
+	if (realcount > loops[nr]->buflength) {
+		realcount = loops[nr]->buflength;
+		info("Not so much data in buffer!");
+	}
+
+	if (!loops[nr]->zerocopy) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+		unsigned long fw=loops[nr]->frameswrite;
+		
+		wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite);
+#else
+		interruptible_sleep_on(&loops[nr]->wait);
+#endif
+	}
+
+	down(&loops[nr]->lock);
+	if (!loops[nr]->buffer) {
+		up(&loops[nr]->lock);
+		return 0;
+	}
+	if (copy_to_user(buf, loops[nr]->buffer, realcount))
+		return -EFAULT;
+	up(&loops[nr]->lock);
+
+	loops[nr]->framesread++;
+	return realcount;
+}
+
+static int vloopback_mmap(struct file *f, struct vm_area_struct *vma)
+{
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+	unsigned long start = (unsigned long)vma->vm_start;
+	long size = vma->vm_end - vma->vm_start;
+	unsigned long page, pos;
+
+	down(&loops[nr]->lock);
+	if (ptr->in) {
+		loops[nr]->zerocopy=1;
+		if (loops[nr]->ropen) {
+			info("Can't change size while opened for read");
+			up(&loops[nr]->lock);
+			return -EINVAL;
+		}
+		if (!size) {
+			up(&loops[nr]->lock);
+			return -EINVAL;
+		}
+		if (loops[nr]->buffer)
+			rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS);
+		loops[nr]->buflength=size;
+		loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS);
+	}
+        if (loops[nr]->buffer == NULL) {
+		up(&loops[nr]->lock);
+                return -EINVAL;
+	}
+
+        if (size > (((N_BUFFS * loops[nr]->buflength) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
+		up(&loops[nr]->lock);
+                return -EINVAL;
+	}
+
+        pos = (unsigned long)loops[nr]->buffer;
+        while (size > 0) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
+                page = kvirt_to_pa(pos);
+                if (remap_page_range(vma,start, page, PAGE_SIZE, PAGE_SHARED)) {
+#else
+		page = vmalloc_to_pfn((void *)pos);
+		if (remap_pfn_range(vma, start, page, PAGE_SIZE,
+				PAGE_SHARED)) {
+#endif
+			up(&loops[nr]->lock);
+                        return -EAGAIN;
+		}
+                start += PAGE_SIZE;
+                pos += PAGE_SIZE;
+		size -= PAGE_SIZE;
+        }
+	up(&loops[nr]->lock);
+
+	return 0;
+}
+
+static int vloopback_ioctl(struct inode *inod, struct file *f, unsigned int cmd, unsigned long arg)
+{
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+	int i;
+
+	if (loops[nr]->zerocopy) {
+		if (!ptr->in) {
+			loops[nr]->ioctlnr=cmd;
+			loops[nr]->ioctllength=_IOC_SIZE(cmd);
+			/* info("DEBUG: vl_ioctl: !loop->in"); */
+			/* info("DEBUG: vl_ioctl: cmd %lu", cmd); */
+			/* info("DEBUG: vl_ioctl: len %lu", loops[nr]->ioctllength); */
+			if(copy_from_user(loops[nr]->ioctldata, (void*)arg, _IOC_SIZE(cmd)))
+				return -EFAULT;
+			kill_proc(loops[nr]->pid, SIGIO, 1);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+			wait_event_interruptible(loops[nr]->wait, loops[nr]->ioctlnr==-1);
+#else
+			interruptible_sleep_on(&loops[nr]->wait);
+#endif			
+			
+			if (loops[nr]->invalid_ioctl) {
+				//info ("DEBUG: There was an invalid ioctl");
+				loops[nr]->invalid_ioctl = 0;
+				return -ENOTTY;
+			}
+			if (cmd & IOC_IN && !(cmd & IOC_OUT)) {
+				//info("DEBUG: vl_ioctl: cmd & IOC_IN 1"); 
+				if (memcmp(loops[nr]->ioctlretdata, loops[nr]->ioctldata, _IOC_SIZE(cmd))) {
+					return -EINVAL;
+				}
+			 	//info("DEBUG: vl_ioctl: cmd & IOC_IN 2"); 
+				return 0;
+			} else {
+				if (copy_to_user((void*)arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd)))
+					return -EFAULT;
+				//info("DEBUG: vl_ioctl: !(cmd & IOC_IN) 1");
+				return 0;
+			}
+		} else {
+			if ( (loops[nr]->ioctlnr!=cmd) && (cmd != (VIDIOCSINVALID))) {
+				/* wrong ioctl */
+				info("DEBUG: vo_ioctl: Wrong IOCTL");
+				return 0;
+			}
+			if (cmd == VIDIOCSINVALID) {
+				loops[nr]->invalid_ioctl = 1;
+			} else {
+				if (copy_from_user(loops[nr]->ioctlretdata, (void*)arg, loops[nr]->ioctllength))
+					return -EFAULT;
+			}
+			loops[nr]->ioctlnr=-1;
+			if (waitqueue_active(&loops[nr]->wait))
+				wake_up(&loops[nr]->wait);
+			return 0;
+		}
+	}
+	switch(cmd)
+	{
+		/* Get capabilities */
+		case VIDIOCGCAP:
+		{
+			struct video_capability b;
+			if (ptr->in) {
+				sprintf(b.name, "Video loopback %d input",
+				    ptr->pipenr);
+				b.type = 0;
+			} else {
+				sprintf(b.name, "Video loopback %d output",
+				    ptr->pipenr);
+				b.type = VID_TYPE_CAPTURE;
+			}
+			b.channels=1;
+			b.audios=0;
+			b.maxwidth=loops[nr]->width;
+			b.maxheight=loops[nr]->height;
+			b.minwidth=20;
+			b.minheight=20;
+			if(copy_to_user((void*)arg, &b, sizeof(b)))
+				return -EFAULT;
+			return 0;
+		}
+		/* Get channel info (sources) */
+		case VIDIOCGCHAN:
+		{
+			struct video_channel v;
+			if(copy_from_user(&v, (void*)arg, sizeof(v)))
+				return -EFAULT;
+			if(v.channel!=0) {
+				info("VIDIOCGCHAN: Invalid Channel, was %d", v.channel);
+				v.channel=0;
+				//return -EINVAL;
+			}
+			v.flags=0;
+			v.tuners=0;
+			v.norm=0;
+			v.type = VIDEO_TYPE_CAMERA;
+			/*strcpy(v.name, "Loopback"); -- tibit */
+			strcpy(v.name, "Composite1");
+			if(copy_to_user((void*)arg, &v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		/* Set channel 	*/
+		case VIDIOCSCHAN:
+		{
+			int v;
+			if(copy_from_user(&v, (void*)arg, sizeof(v)))
+				return -EFAULT;
+			if(v!=0) {
+				info("VIDIOCSCHAN: Invalid Channel, was %d", v);
+				return -EINVAL;
+			}
+			return 0;
+		}
+		/* Get tuner abilities */
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, (void*)arg, sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner) {
+				info("VIDIOCGTUNER: Invalid Tuner, was %d", v.tuner);
+				return -EINVAL;
+			}
+			strcpy(v.name, "Format");
+			v.rangelow=0;
+			v.rangehigh=0;
+			v.flags=0;
+			v.mode=VIDEO_MODE_AUTO;
+			if(copy_to_user((void*)arg,&v, sizeof(v))!=0)
+				return -EFAULT;
+			return 0;
+		}
+		/* Get picture properties */
+		case VIDIOCGPICT:
+		{
+			struct video_picture p;
+			p.colour=0x8000;
+			p.hue=0x8000;
+			p.brightness=0x8000;
+			p.contrast=0x8000;
+			p.whiteness=0x8000;
+			p.depth=0x8000;
+			p.palette=loops[nr]->palette;
+			if(copy_to_user((void*)arg, &p, sizeof(p)))
+				return -EFAULT;
+			return 0;
+
+		}
+		/* Set picture properties */
+		case VIDIOCSPICT:
+		{
+			struct video_picture p;
+			if(copy_from_user(&p, (void*)arg, sizeof(p)))
+				return -EFAULT;
+			if (!ptr->in) {
+				if (p.palette!=loops[nr]->palette)
+					return -EINVAL;
+			} else
+				loops[nr]->palette=p.palette;
+			return 0;
+		}
+		/* Get the video overlay window */
+		case VIDIOCGWIN:
+		{
+			struct video_window vw;
+			vw.x=0;
+			vw.y=0;
+			vw.width=loops[nr]->width;
+			vw.height=loops[nr]->height;
+			vw.chromakey=0;
+			vw.flags=0;
+			vw.clipcount=0;
+			if(copy_to_user((void*)arg, &vw, sizeof(vw)))
+				return -EFAULT;
+			return 0;
+		}
+		/* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
+		case VIDIOCSWIN:
+		{
+			struct video_window vw;
+			
+			if(copy_from_user(&vw, (void*)arg, sizeof(vw)))
+				return -EFAULT;
+			if(vw.flags)
+				return -EINVAL;
+			if(vw.clipcount)
+				return -EINVAL;
+			if (loops[nr]->height==vw.height &&
+			    loops[nr]->width==vw.width)
+				return 0;
+			if(!ptr->in) {
+				return -EINVAL;
+			} else {
+				loops[nr]->height=vw.height;
+				loops[nr]->width=vw.width;
+				/* Make sure nobody is using the buffer while we
+				   fool around with it.
+				   We are also not allowing changes while
+				   somebody using mmap has the output open.
+				 */
+				down(&loops[nr]->lock);
+				if (loops[nr]->ropen) {
+					info("Can't change size while opened for read");
+					up(&loops[nr]->lock);
+					return -EINVAL;
+				}
+				if (loops[nr]->buffer)
+					rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS);
+				loops[nr]->buflength=vw.width*vw.height*4;
+				loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS);
+				up(&loops[nr]->lock);
+			}
+			return 0;
+		}
+		/* Memory map buffer info */
+		case VIDIOCGMBUF:
+		{
+			struct video_mbuf vm;
+			
+			vm.size=loops[nr]->buflength*N_BUFFS;
+			vm.frames=N_BUFFS;
+			for (i=0; i<vm.frames; i++)
+				vm.offsets[i]=i*loops[nr]->buflength;
+			if(copy_to_user((void*)arg, &vm, sizeof(vm)))
+				return -EFAULT;
+			return 0;
+		}
+		/* Grab frames */
+		case VIDIOCMCAPTURE:
+		{
+			struct video_mmap vm;
+
+			if (ptr->in)
+				return -EINVAL;
+			if (!loops[nr]->buffer)
+				return -EINVAL;
+			if (copy_from_user(&vm, (void*)arg, sizeof(vm)))
+				return -EFAULT;
+			if (vm.format!=loops[nr]->palette)
+				return -EINVAL;
+			if (vm.frame > N_BUFFS)
+				return -EINVAL;
+			return 0;
+		}
+		/* Sync with mmap grabbing */
+		case VIDIOCSYNC:
+		{
+			int frame;
+			unsigned long fw;
+
+			if (copy_from_user((void *)&frame, (void*)arg, sizeof(int)))
+				return -EFAULT;
+			if (ptr->in)
+				return -EINVAL;
+			if (!loops[nr]->buffer)
+				return -EINVAL;
+			/* Ok, everything should be alright since the program
+			   should have called VIDIOMCAPTURE and we are ready to
+			   do the 'capturing' */
+			if (frame > 1)
+				return -EINVAL;
+			loops[nr]->frame=frame;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+			fw = loops[nr]->frameswrite;
+			wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite);
+#else
+			interruptible_sleep_on(&loops[nr]->wait);
+#endif			
+			if (!loops[nr]->buffer)		/* possibly released during sleep */
+				return -EINVAL;
+			loops[nr]->framesread++;
+			return 0;
+		}
+		/* Get attached units */
+		case VIDIOCGUNIT:
+		{
+			struct video_unit vu;
+			
+			if (ptr->in)
+				vu.video=loops[nr]->vloopout->minor;
+			else
+				vu.video=loops[nr]->vloopin->minor;
+			vu.vbi=VIDEO_NO_UNIT;
+			vu.radio=VIDEO_NO_UNIT;
+			vu.audio=VIDEO_NO_UNIT;
+			vu.teletext=VIDEO_NO_UNIT;
+			if (copy_to_user((void*)arg, &vu, sizeof(vu)))
+				return -EFAULT;
+			return 0;
+		}
+		/* Get frame buffer */
+		case VIDIOCGFBUF:
+		{
+			struct video_buffer vb;
+
+			memset(&vb, 0, sizeof(vb));
+			vb.base=NULL;
+
+			if(copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+				return -EFAULT;
+
+			return 0;
+		}
+		/* Start, end capture */
+		case VIDIOCCAPTURE:
+		{
+			int start;
+			if (copy_from_user(&start, (void*)arg, sizeof(int)))
+				return -EFAULT;
+			if (start) info ("Capture started");
+			else info ("Capture stopped");
+
+			return 0;
+		}
+
+		case VIDIOCGFREQ:
+		case VIDIOCSFREQ:
+		case VIDIOCGAUDIO:
+		case VIDIOCSAUDIO:
+			return -EINVAL;
+		case VIDIOCKEY:
+			return 0;
+		default:
+			return -ENOTTY;
+			//return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static unsigned int vloopback_poll(struct file *f, struct poll_table_struct *wait)
+{
+	struct video_device *loopdev=video_devdata(f);
+	priv_ptr ptr=(priv_ptr)loopdev->priv;
+	int nr=ptr->pipenr;
+
+	if (loopdev==NULL)
+		return -EFAULT;
+	if (!ptr->in)
+		return 0;
+
+	if (loops[nr]->ioctlnr!=-1) {
+		if (loops[nr]->zerocopy) {
+			return (POLLIN | POLLPRI | POLLOUT | POLLRDNORM);
+		} else {
+			return (POLLOUT);
+		}
+	}
+	return 0;
+}
+
+static struct file_operations fileops_template=
+{
+	owner:		THIS_MODULE,
+	open:		vloopback_open,
+	release:	vloopback_release,
+	read:		vloopback_read,
+	write:		vloopback_write,
+	poll:		vloopback_poll,
+	ioctl:		vloopback_ioctl,
+	mmap:		vloopback_mmap,
+};
+
+static struct video_device vloopback_template=
+{
+	owner:		THIS_MODULE,
+	name:		"Video Loopback",
+	type:		VID_TYPE_CAPTURE,
+	fops:		&fileops_template,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+	release:	video_device_release,
+#endif
+};
+
+static int create_pipe(int nr)
+{
+	int minor_in, minor_out , ret;
+		
+	if (dev_offset == -1)
+		minor_in  = minor_out = -1; /* autoassign */
+	else {
+		minor_in  = 2*nr   + dev_offset;
+		minor_out = 2*nr+1 + dev_offset;
+	}
+
+	/* allocate space for this pipe */
+	loops[nr]= kmalloc(sizeof(struct vloopback_pipe), GFP_KERNEL);
+	if (!loops[nr])
+		return -ENOMEM;
+	/* set up a new video device plus our private area */
+	loops[nr]->vloopin= video_device_alloc();
+	if (loops[nr]->vloopin == NULL)
+		return -ENOMEM;
+	*loops[nr]->vloopin = vloopback_template;
+	loops[nr]->vloopin->priv= kmalloc(sizeof(struct vloopback_private),
+			GFP_KERNEL);
+	if (loops[nr]->vloopin->priv == NULL) {
+		kfree(loops[nr]->vloopin);
+		return -ENOMEM;
+	}
+	/* repeat for the output device */
+	loops[nr]->vloopout= video_device_alloc();
+	if (loops[nr]->vloopout == NULL) {
+		kfree(loops[nr]->vloopin->priv);
+		kfree(loops[nr]->vloopin);
+		return -ENOMEM;
+	}
+	*loops[nr]->vloopout = vloopback_template;
+	loops[nr]->vloopout->priv= kmalloc(sizeof(struct vloopback_private),
+			GFP_KERNEL);
+	if (loops[nr]->vloopout->priv == NULL) {
+		kfree(loops[nr]->vloopin->priv);
+		kfree(loops[nr]->vloopin);
+		kfree(loops[nr]->vloopout);
+		return -ENOMEM;
+	}
+
+	((priv_ptr)loops[nr]->vloopin->priv)->pipenr=nr;
+	((priv_ptr)loops[nr]->vloopout->priv)->pipenr=nr;
+	loops[nr]->invalid_ioctl = 0; /* tibit */
+	loops[nr]->buffer=NULL;
+	loops[nr]->width=0;
+	loops[nr]->height=0;
+	loops[nr]->palette=0;
+	loops[nr]->frameswrite=0;
+	loops[nr]->framesread=0;
+	loops[nr]->framesdumped=0;
+	loops[nr]->wopen=0;
+	loops[nr]->ropen=0;
+	loops[nr]->frame=0;
+	
+	((priv_ptr)loops[nr]->vloopin->priv)->in=1;
+	((priv_ptr)loops[nr]->vloopout->priv)->in=0;
+	loops[nr]->vloopin->type=0;
+	sprintf(loops[nr]->vloopin->name, "Video loopback %d input", nr);
+	loops[nr]->vloopout->type=VID_TYPE_CAPTURE;
+	sprintf(loops[nr]->vloopout->name, "Video loopback %d output", nr);
+	init_waitqueue_head(&loops[nr]->wait);
+	init_MUTEX(&loops[nr]->lock);
+	
+	ret = video_register_device(loops[nr]->vloopin, VFL_TYPE_GRABBER,minor_in);
+	
+	if ((ret == -1 ) || ( ret == -23 )) {
+		info("error registering device %s",loops[nr]->vloopin->name);
+		kfree(loops[nr]->vloopin->priv);
+		kfree(loops[nr]->vloopin);
+		kfree(loops[nr]->vloopout->priv);
+		kfree(loops[nr]->vloopout);
+		kfree(loops[nr]);
+		loops[nr]=NULL;
+		return ret;
+	}
+	
+	ret = video_register_device(loops[nr]->vloopout, VFL_TYPE_GRABBER,minor_out);
+	
+	if ((ret ==-1) || (ret == -23)) {
+		info("error registering device %s", loops[nr]->vloopout->name);
+		kfree(loops[nr]->vloopin->priv);
+		video_unregister_device(loops[nr]->vloopin);
+		kfree(loops[nr]->vloopout->priv);
+		kfree(loops[nr]->vloopout);
+		kfree(loops[nr]);
+		loops[nr]=NULL;
+		return ret;
+	}
+	
+	loops[nr]->ioctldata=kmalloc(1024, GFP_KERNEL);
+	loops[nr]->ioctlretdata=kmalloc(1024, GFP_KERNEL);
+	return 0;
+}
+
+
+/****************************************************************************
+ *	init stuff
+ ****************************************************************************/
+
+
+MODULE_AUTHOR("J.B. Vreeken (pe1rxq@amsat.org)");
+MODULE_DESCRIPTION("Video4linux loopback device.");
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+module_param(pipes, int, 000);
+#else
+MODULE_PARM(pipes, "i");
+#endif
+
+MODULE_PARM_DESC(pipes, "Nr of pipes to create (each pipe uses two video devices)");
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+module_param(spares, int, 000);
+#else
+MODULE_PARM(spares, "i");
+#endif
+
+MODULE_PARM_DESC(spares, "Nr of spare pipes that should be created");
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+module_param(dev_offset, int, 000);
+#else
+MODULE_PARM(dev_offset_param, "i");
+#endif
+
+MODULE_PARM_DESC(dev_offset, "Prefered offset for video device numbers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION( VLOOPBACK_VERSION );
+
+static int __init vloopback_init(void)
+{
+	int i,ret;
+
+	info("Video4linux loopback driver v"VLOOPBACK_VERSION);
+
+	if (pipes==-1) pipes=1;
+	if (pipes > MAX_PIPES) {
+		pipes=MAX_PIPES;
+		info("Nr of pipes is limited to: %d", MAX_PIPES);
+	}
+
+	for (i=0; i<pipes; i++) {
+		
+		ret = create_pipe(i);
+
+		if (ret == 0) {
+			info("Loopback %d registered, input: video%d,"
+			     "output: video%d",
+			     i, loops[i]->vloopin->minor,
+			     loops[i]->vloopout->minor);
+			nr_o_pipes=i+1;
+		}else{
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static void __exit cleanup_vloopback_module(void)
+{
+	int i;
+
+	info("Unregistering video4linux loopback devices");
+	for (i=0; i<nr_o_pipes; i++) if (loops[i]) {
+		kfree(loops[i]->vloopin->priv);
+		video_unregister_device(loops[i]->vloopin);
+		kfree(loops[i]->vloopout->priv);
+		video_unregister_device(loops[i]->vloopout);
+		if (loops[i]->buffer) rvfree(loops[i]->buffer, loops[i]->buflength*N_BUFFS);
+		kfree(loops[i]->ioctldata);
+		kfree(loops[i]->ioctlretdata);
+		kfree(loops[i]);
+	}
+}
+
+module_init(vloopback_init);
+module_exit(cleanup_vloopback_module);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vloopback.html	Sun Apr 01 05:22:43 2007 +0000
@@ -0,0 +1,148 @@
+<HTML>
+<HEAD>
+    <TITLE>
+	Video4Linux loopback API
+    </TITLE>
+    <H1>
+        Video4Linux loopback API
+    </H1>
+</HEAD>
+<BODY bgcolor="white">
+    <P>
+	Author: Jeroen Vreeken (pe1rxq@amsat.org)<BR>
+	Version: 0.90<BR>
+	Date: 31-01-2001<BR>
+    </P>
+    <P>
+        <H2>
+    	    Introduction:
+	</H2>
+	This document describes the API for the Video4Linux loopback driver.
+	The driver implements a video pipe using two video4linux devices.
+	The first device is used by the program supplying the data,
+	from now on this program will be refered to with <I>'pusher'</I>.<BR>
+	The second device acts as if it were a normall video4linux device,
+	it should be usable by any application that honours the video4linux
+	specifications. This application will from now on be refered to as
+	<I>'client'</I>.<BR>
+	The calls and ioctls mentioned in this document refer to those
+	as described in the Video4Linux API.
+	<BR>
+	The loopback device has two operating modes:
+	<UL>
+	    <LI>
+		A simple one-copy mode in which <I>pusher</I> specifies the
+		size of the images and the used palette and uses write to
+		push its images to the pipe.<BR>
+		This mode is mostly for feeding fixed size images without any
+		knowledge about <I>client</I>.
+	    </LI>
+	    <LI>
+		A zero-copy mode in which <I>pusher</I> regularly polls the
+		device if <I>client</I> does an ioctl.<BR> 
+		In this mode <I>pusher</I> has almost complete control over the
+		devices behaviour and it will be mainly used to implement
+		complex multiple tuner/channel/size configurations.
+		With this mode it should be possible to use the Xvideo
+		extensions as normal video4linux capture devices.
+	    </LI>
+	</UL>
+    </P>
+    <P align=left>
+	<H2>
+	    Locating a free pipe
+	</H2>
+	In order to find an unused pipe <I>pusher</I> will have to scan
+	the contents of /proc/video/vloopback/<BR>
+	Each pipe will have its own entry in the form of <I>vloopback0</I> to
+	<I>vloopbackN</I>, N being the total number of available pipes-1.<BR>
+	There will also be a general <I>vloopbacks</I> file which will contain
+	information on all entries.
+	<BR>
+	Each of these files will have references to the input and output
+	video device and will also indicate if either of them is currently
+	in use.<BR>
+	<BR>
+	Once <I>pusher</I> has found a free pipe it can claim it by simply
+	opening the input device.
+    </P>
+    <P align=left>
+	<H2>
+	    One-copy mode
+	</H2>
+	In this mode <I>pusher</I> only has to provide images using the 
+	<B>write()</B> call,
+	the driver will handle the communication with <I>client</I> or
+	will drop the images if the output is unused.
+	To <I>client</I> the device will closely resemble a webcam driver.<BR>
+	<BR>
+	In order to use it <I>pusher</I> will open the input device.
+	Before writing it will first have to set the palette it is going to use
+	by calling <B>VIDIOCSPICT</B>, and the size by calling 
+	<B>VIDIOCSWIN</B>.
+	After this it can call <B>write()</B> for each frame it wants to send
+	to the pipe.<BR>
+	<BR>
+	When there is no application using the device the driver will simply
+	drop the frames which will result in a 'no-copy' situation while
+	writing to an unused pipe.<BR>
+	<I>Note: when client is using read() instead of mmap() the driver will
+	actually use a double copy.</I>
+    </P>
+    <P align=left>
+	<H2>
+	    Zero-copy mode
+	</H2>
+	In this mode the driver will forward nearly all ioctls to
+	<I>pusher</I>.<BR>
+	<BR>
+	To initiate this mode <I>pusher</I> will have to call <B>mmap()</B>
+	with the size of the requested buffer.
+	The driver will allocate memory for this buffer and <I>pusher</I> will
+	gain access to it.<BR>
+	<I>Note: as the allocated memory might be in use by client, pusher is
+	NOT allowed to touch it under any circumstances with the only exeption
+	being between <B>VIDIOCMCAPTURE</B> and <B>VIDIOCSYNC</B>.</I><BR>
+	<BR>
+	<B>
+	    Handling ioctls
+	</B><BR>
+	<BR>
+	When <I>client</I> has issued an ioctl <I>pusher</I> will receive a
+	<B>SIGIO</B> signal.
+	Pusher may check to see if it is comming from vloopback by calling
+	<B>poll()</B> first.
+	It then has to respond by calling <B>read()</B>
+	with a large enough buffer for the largest possible ioctl data
+	structure plus <B>sizeof(unsigned long int)</B>. <I>(The largest ioctl data structure is 280 bytes
+	in linux kernel 2.4.0-test10pre1, a buffer of 1024 bytes is recommended)
+	</I><BR>
+	The first bytes of this buffer will be the ioctl number.
+	This number is an unsigned long int, the remaining data is the data supplied by <I>client</I>.
+	<I>Pusher</I> will now have to handle this ioctl.<BR>
+	<BR>
+	If it is an ioctl requesting data <I>pusher</I> will answer it by
+	calling the ioctl with the requested data.<BR>
+	If it is an ioctl setting data <I>pusher</I> will call the ioctl with
+	the exact same data to accept it.<BR>
+	<BR>
+	<B>
+	    Handling read
+	</B><BR>
+	<BR>
+	<I>Pusher</I> will not need to handle any read requests because the
+	kernel module will fake an mmap and sync call for it.<BR>
+	<BR>
+	<B>
+	    Starting and stopping capture
+	</B><BR>
+	<BR>
+	The first time <B>VIDIOCMCAPTURE</B> is called <I>pusher</I> should
+	initialize capture and start capturing of the requested frames into
+	the mmapped buffer.<BR>
+	When <I>client</I> closes its device an 'ioctl' 0 will be send with
+	no data, <I>pusher</I> will tell the hardware to stop.
+	<BR>
+    </P>
+</BODY>
+</HTML>
\ No newline at end of file