This commit was manufactured by cvs2svn to create branch 'vserver'.
authorPlanet-Lab Support <support@planet-lab.org>
Mon, 2 Apr 2007 21:21:35 +0000 (21:21 +0000)
committerPlanet-Lab Support <support@planet-lab.org>
Mon, 2 Apr 2007 21:21:35 +0000 (21:21 +0000)
40 files changed:
Documentation/filesystems/v9fs.txt [new file with mode: 0644]
Documentation/usb/et61x251.txt [new file with mode: 0644]
arch/arm/mach-ixp2000/uengine.c [new file with mode: 0644]
arch/arm/mach-omap1/board-netstar.c [new file with mode: 0644]
arch/arm/mach-omap2/prcm.h [new file with mode: 0644]
arch/arm/plat-omap/sleep.S [new file with mode: 0644]
arch/mips/kernel/smp_mt.c [new file with mode: 0644]
arch/mips/mips-boards/sim/cmdline.c [new file with mode: 0644]
arch/mips/philips/pnx8550/common/mipsIRQ.S [new file with mode: 0644]
arch/mips/qemu/q-int.S [new file with mode: 0644]
arch/mips/sibyte/bcm1480/irq_handler.S [new file with mode: 0644]
arch/mips/tx4938/common/irq_handler.S [new file with mode: 0644]
arch/powerpc/kernel/idle_64.c [new file with mode: 0644]
arch/ppc/platforms/chrp_nvram.c [new file with mode: 0644]
arch/um/scripts/Makefile.unmap [new file with mode: 0644]
drivers/i2c/chips/x1205.c [new file with mode: 0644]
drivers/media/video/bttv-input.c [new file with mode: 0644]
drivers/media/video/cx25840/cx25840.h [new file with mode: 0644]
drivers/media/video/rds.h [new file with mode: 0644]
drivers/usb/media/et61x251.h [new file with mode: 0644]
drivers/usb/media/et61x251_core.c [new file with mode: 0644]
drivers/usb/media/et61x251_sensor.h [new file with mode: 0644]
drivers/usb/media/et61x251_tas5130d1b.c [new file with mode: 0644]
drivers/usb/media/sn9c102_ov7630.c [new file with mode: 0644]
drivers/w1/w1_ds2433.c [new file with mode: 0644]
fs/9p/9p.c [new file with mode: 0644]
fs/9p/trans_sock.c [new file with mode: 0644]
fs/relayfs/buffers.c [new file with mode: 0644]
fs/relayfs/buffers.h [new file with mode: 0644]
fs/relayfs/relay.h [new file with mode: 0644]
include/asm-arm/arch-aaec2000/param.h [new file with mode: 0644]
include/asm-arm/arch-at91rm9200/param.h [new file with mode: 0644]
include/asm-arm/arch-ixp2000/uengine.h [new file with mode: 0644]
include/asm-arm/arch-realview/param.h [new file with mode: 0644]
include/asm-powerpc/numnodes.h [new file with mode: 0644]
include/asm-um/ldt-i386.h [new file with mode: 0644]
include/asm-um/ldt-x86_64.h [new file with mode: 0644]
include/linux/x1205.h [new file with mode: 0644]
net/ipv4/netfilter/ipt_policy.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_policy.c [new file with mode: 0644]

diff --git a/Documentation/filesystems/v9fs.txt b/Documentation/filesystems/v9fs.txt
new file mode 100644 (file)
index 0000000..24c7a9c
--- /dev/null
@@ -0,0 +1,99 @@
+                       V9FS: 9P2000 for Linux
+                       ======================
+
+ABOUT
+=====
+
+v9fs is a Unix implementation of the Plan 9 9p remote filesystem protocol.
+
+This software was originally developed by Ron Minnich <rminnich@lanl.gov>
+and Maya Gokhale <maya@lanl.gov>.  Additional development by Greg Watson
+<gwatson@lanl.gov> and most recently Eric Van Hensbergen
+<ericvh@gmail.com> and Latchesar Ionkov <lucho@ionkov.net>.
+
+USAGE
+=====
+
+For remote file server:
+
+       mount -t 9P 10.10.1.2 /mnt/9
+
+For Plan 9 From User Space applications (http://swtch.com/plan9)
+
+       mount -t 9P `namespace`/acme /mnt/9 -o proto=unix,name=$USER
+
+OPTIONS
+=======
+
+  proto=name   select an alternative transport.  Valid options are
+               currently:
+                       unix - specifying a named pipe mount point
+                       tcp  - specifying a normal TCP/IP connection
+                       fd   - used passed file descriptors for connection
+                                (see rfdno and wfdno)
+
+  name=name    user name to attempt mount as on the remote server.  The
+               server may override or ignore this value.  Certain user
+               names may require authentication.
+
+  aname=name   aname specifies the file tree to access when the server is
+               offering several exported file systems.
+
+  debug=n      specifies debug level.  The debug level is a bitmask.
+                       0x01 = display verbose error messages
+                       0x02 = developer debug (DEBUG_CURRENT)
+                       0x04 = display 9P trace
+                       0x08 = display VFS trace
+                       0x10 = display Marshalling debug
+                       0x20 = display RPC debug
+                       0x40 = display transport debug
+                       0x80 = display allocation debug
+
+  rfdno=n      the file descriptor for reading with proto=fd
+
+  wfdno=n      the file descriptor for writing with proto=fd
+
+  maxdata=n    the number of bytes to use for 9P packet payload (msize)
+
+  port=n       port to connect to on the remote server
+
+  noextend     force legacy mode (no 9P2000.u semantics)
+
+  uid          attempt to mount as a particular uid
+
+  gid          attempt to mount with a particular gid
+
+  afid         security channel - used by Plan 9 authentication protocols
+
+  nodevmap     do not map special files - represent them as normal files.
+               This can be used to share devices/named pipes/sockets between
+               hosts.  This functionality will be expanded in later versions.
+
+RESOURCES
+=========
+
+The Linux version of the 9P server is now maintained under the npfs project
+on sourceforge (http://sourceforge.net/projects/npfs).
+
+There are user and developer mailing lists available through the v9fs project
+on sourceforge (http://sourceforge.net/projects/v9fs).
+
+News and other information is maintained on SWiK (http://swik.net/v9fs).
+
+Bug reports may be issued through the kernel.org bugzilla 
+(http://bugzilla.kernel.org)
+
+For more information on the Plan 9 Operating System check out
+http://plan9.bell-labs.com/plan9
+
+For information on Plan 9 from User Space (Plan 9 applications and libraries
+ported to Linux/BSD/OSX/etc) check out http://swtch.com/plan9
+
+
+STATUS
+======
+
+The 2.6 kernel support is working on PPC and x86.
+
+PLEASE USE THE SOURCEFORGE BUG-TRACKER TO REPORT PROBLEMS.
+
diff --git a/Documentation/usb/et61x251.txt b/Documentation/usb/et61x251.txt
new file mode 100644 (file)
index 0000000..b44dda4
--- /dev/null
@@ -0,0 +1,306 @@
+
+                       ET61X[12]51 PC Camera Controllers
+                                Driver for Linux
+                       =================================
+
+                               - Documentation -
+
+
+Index
+=====
+1.  Copyright
+2.  Disclaimer
+3.  License
+4.  Overview and features
+5.  Module dependencies
+6.  Module loading
+7.  Module parameters
+8.  Optional device control through "sysfs"
+9.  Supported devices
+10. Notes for V4L2 application developers
+11. Contact information
+
+
+1. Copyright
+============
+Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>
+
+
+2. Disclaimer
+=============
+Etoms is a trademark of Etoms Electronics Corp.
+This software is not developed or sponsored by Etoms Electronics.
+
+
+3. License
+==========
+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.
+
+
+4. Overview and features
+========================
+This driver supports the video interface of the devices mounting the ET61X151
+or ET61X251 PC Camera Controllers.
+
+It's worth to note that Etoms Electronics has never collaborated with the
+author during the development of this project; despite several requests,
+Etoms Electronics also refused to release enough detailed specifications of
+the video compression engine.
+
+The driver relies on the Video4Linux2 and USB core modules. It has been
+designed to run properly on SMP systems as well.
+
+The latest version of the ET61X[12]51 driver can be found at the following URL:
+http://www.linux-projects.org/
+
+Some of the features of the driver are:
+
+- full compliance with the Video4Linux2 API (see also "Notes for V4L2
+  application developers" paragraph);
+- available mmap or read/poll methods for video streaming through isochronous
+  data transfers;
+- automatic detection of image sensor;
+- support for any window resolutions and optional panning within the maximum
+  pixel area of image sensor;
+- image downscaling with arbitrary scaling factors from 1 and 2 in both
+  directions (see "Notes for V4L2 application developers" paragraph);
+- two different video formats for uncompressed or compressed data in low or
+  high compression quality (see also "Notes for V4L2 application developers"
+  paragraph);
+- full support for the capabilities of every possible image sensors that can
+  be connected to the ET61X[12]51 bridges, including, for istance, red, green,
+  blue and global gain adjustments and exposure control (see "Supported
+  devices" paragraph for details);
+- use of default color settings for sunlight conditions;
+- dynamic I/O interface for both ET61X[12]51 and image sensor control (see
+  "Optional device control through 'sysfs'" paragraph);
+- dynamic driver control thanks to various module parameters (see "Module
+  parameters" paragraph);
+- up to 64 cameras can be handled at the same time; they can be connected and
+  disconnected from the host many times without turning off the computer, if
+  the system supports hotplugging;
+- no known bugs.
+
+
+5. Module dependencies
+======================
+For it to work properly, the driver needs kernel support for Video4Linux and
+USB.
+
+The following options of the kernel configuration file must be enabled and
+corresponding modules must be compiled:
+
+       # Multimedia devices
+       #
+       CONFIG_VIDEO_DEV=m
+
+To enable advanced debugging functionality on the device through /sysfs:
+
+       # Multimedia devices
+       #
+       CONFIG_VIDEO_ADV_DEBUG=y
+
+       # USB support
+       #
+       CONFIG_USB=m
+
+In addition, depending on the hardware being used, the modules below are
+necessary:
+
+       # USB Host Controller Drivers
+       #
+       CONFIG_USB_EHCI_HCD=m
+       CONFIG_USB_UHCI_HCD=m
+       CONFIG_USB_OHCI_HCD=m
+
+And finally:
+
+       # USB Multimedia devices
+       #
+       CONFIG_USB_ET61X251=m
+
+
+6. Module loading
+=================
+To use the driver, it is necessary to load the "et61x251" module into memory
+after every other module required: "videodev", "usbcore" and, depending on
+the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd".
+
+Loading can be done as shown below:
+
+       [root@localhost home]# modprobe et61x251
+
+At this point the devices should be recognized. You can invoke "dmesg" to
+analyze kernel messages and verify that the loading process has gone well:
+
+       [user@localhost home]$ dmesg
+
+
+7. Module parameters
+====================
+Module parameters are listed below:
+-------------------------------------------------------------------------------
+Name:           video_nr
+Type:           short array (min = 0, max = 64)
+Syntax:         <-1|n[,...]>
+Description:    Specify V4L2 minor mode number:
+                -1 = use next available
+                 n = use minor number n
+                You can specify up to 64 cameras this way.
+                For example:
+                video_nr=-1,2,-1 would assign minor number 2 to the second
+                registered camera and use auto for the first one and for every
+                other camera.
+Default:        -1
+-------------------------------------------------------------------------------
+Name:           force_munmap
+Type:           bool array (min = 0, max = 64)
+Syntax:         <0|1[,...]>
+Description:    Force the application to unmap previously mapped buffer memory
+                before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not
+                all the applications support this feature. This parameter is
+                specific for each detected camera.
+                0 = do not force memory unmapping
+                1 = force memory unmapping (save memory)
+Default:        0
+-------------------------------------------------------------------------------
+Name:           debug
+Type:           ushort
+Syntax:         <n>
+Description:    Debugging information level, from 0 to 3:
+                0 = none (use carefully)
+                1 = critical errors
+                2 = significant informations
+                3 = more verbose messages
+                Level 3 is useful for testing only, when only one device
+                is used at the same time. It also shows some more informations
+                about the hardware being detected. This module parameter can be
+                changed at runtime thanks to the /sys filesystem interface.
+Default:        2
+-------------------------------------------------------------------------------
+
+
+8. Optional device control through "sysfs"
+==========================================
+If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled,
+it is possible to read and write both the ET61X[12]51 and the image sensor
+registers by using the "sysfs" filesystem interface.
+
+There are four files in the /sys/class/video4linux/videoX directory for each
+registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files
+control the ET61X[12]51 bridge, while the other two control the sensor chip.
+"reg" and "i2c_reg" hold the values of the current register index where the
+following reading/writing operations are addressed at through "val" and
+"i2c_val". Their use is not intended for end-users, unless you know what you
+are doing. Remember that you must be logged in as root before writing to them.
+
+As an example, suppose we were to want to read the value contained in the
+register number 1 of the sensor register table - which is usually the product
+identifier - of the camera registered as "/dev/video0":
+
+       [root@localhost #] cd /sys/class/video4linux/video0
+       [root@localhost #] echo 1 > i2c_reg
+       [root@localhost #] cat i2c_val
+
+Note that if the sensor registers can not be read, "cat" will fail.
+To avoid race conditions, all the I/O accesses to the files are serialized.
+
+
+9. Supported devices
+====================
+None of the names of the companies as well as their products will be mentioned
+here. They have never collaborated with the author, so no advertising.
+
+From the point of view of a driver, what unambiguously identify a device are
+its vendor and product USB identifiers. Below is a list of known identifiers of
+devices mounting the ET61X[12]51 PC camera controllers:
+
+Vendor ID  Product ID
+---------  ----------
+0x102c     0x6151
+0x102c     0x6251
+0x102c     0x6253
+0x102c     0x6254
+0x102c     0x6255
+0x102c     0x6256
+0x102c     0x6257
+0x102c     0x6258
+0x102c     0x6259
+0x102c     0x625a
+0x102c     0x625b
+0x102c     0x625c
+0x102c     0x625d
+0x102c     0x625e
+0x102c     0x625f
+0x102c     0x6260
+0x102c     0x6261
+0x102c     0x6262
+0x102c     0x6263
+0x102c     0x6264
+0x102c     0x6265
+0x102c     0x6266
+0x102c     0x6267
+0x102c     0x6268
+0x102c     0x6269
+
+The following image sensors are supported:
+
+Model       Manufacturer
+-----       ------------
+TAS5130D1B  Taiwan Advanced Sensor Corporation
+
+All the available control settings of each image sensor are supported through
+the V4L2 interface.
+
+
+10. Notes for V4L2 application developers
+========================================
+This driver follows the V4L2 API specifications. In particular, it enforces two
+rules:
+
+- exactly one I/O method, either "mmap" or "read", is associated with each
+file descriptor. Once it is selected, the application must close and reopen the
+device to switch to the other I/O method;
+
+- although it is not mandatory, previously mapped buffer memory should always
+be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's.
+The same number of buffers as before will be allocated again to match the size
+of the new video frames, so you have to map the buffers again before any I/O
+attempts on them.
+
+Consistently with the hardware limits, this driver also supports image
+downscaling with arbitrary scaling factors from 1 and 2 in both directions.
+However, the V4L2 API specifications don't correctly define how the scaling
+factor can be chosen arbitrarily by the "negotiation" of the "source" and
+"target" rectangles. To work around this flaw, we have added the convention
+that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the
+scaling factor is restored to 1.
+
+This driver supports two different video formats: the first one is the "8-bit
+Sequential Bayer" format and can be used to obtain uncompressed video data
+from the device through the current I/O method, while the second one provides
+"raw" compressed video data (without frame headers not related to the
+compressed data). The current compression quality may vary from 0 to 1 and can
+be selected or queried thanks to the VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP
+V4L2 ioctl's.
+
+
+11. Contact information
+=======================
+The author may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
+
+GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is
+'FCE635A4'; the public 1024-bit key should be available at any keyserver;
+the fingerprint is: '88E8 F32F 7244 68BA 3958  5D40 99DA 5D2A FCE6 35A4'.
diff --git a/arch/arm/mach-ixp2000/uengine.c b/arch/arm/mach-ixp2000/uengine.c
new file mode 100644 (file)
index 0000000..ec4e007
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Generic library functions for the microengines found on the Intel
+ * IXP2000 series of network processors.
+ *
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <asm/hardware.h>
+#include <asm/arch/ixp2000-regs.h>
+#include <asm/arch/uengine.h>
+#include <asm/io.h>
+
+#define USTORE_ADDRESS                 0x000
+#define USTORE_DATA_LOWER              0x004
+#define USTORE_DATA_UPPER              0x008
+#define CTX_ENABLES                    0x018
+#define CC_ENABLE                      0x01c
+#define CSR_CTX_POINTER                        0x020
+#define INDIRECT_CTX_STS               0x040
+#define ACTIVE_CTX_STS                 0x044
+#define INDIRECT_CTX_SIG_EVENTS                0x048
+#define INDIRECT_CTX_WAKEUP_EVENTS     0x050
+#define NN_PUT                         0x080
+#define NN_GET                         0x084
+#define TIMESTAMP_LOW                  0x0c0
+#define TIMESTAMP_HIGH                 0x0c4
+#define T_INDEX_BYTE_INDEX             0x0f4
+#define LOCAL_CSR_STATUS               0x180
+
+u32 ixp2000_uengine_mask;
+
+static void *ixp2000_uengine_csr_area(int uengine)
+{
+       return ((void *)IXP2000_UENGINE_CSR_VIRT_BASE) + (uengine << 10);
+}
+
+/*
+ * LOCAL_CSR_STATUS=1 after a read or write to a microengine's CSR
+ * space means that the microengine we tried to access was also trying
+ * to access its own CSR space on the same clock cycle as we did.  When
+ * this happens, we lose the arbitration process by default, and the
+ * read or write we tried to do was not actually performed, so we try
+ * again until it succeeds.
+ */
+u32 ixp2000_uengine_csr_read(int uengine, int offset)
+{
+       void *uebase;
+       u32 *local_csr_status;
+       u32 *reg;
+       u32 value;
+
+       uebase = ixp2000_uengine_csr_area(uengine);
+
+       local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS);
+       reg = (u32 *)(uebase + offset);
+       do {
+               value = ixp2000_reg_read(reg);
+       } while (ixp2000_reg_read(local_csr_status) & 1);
+
+       return value;
+}
+EXPORT_SYMBOL(ixp2000_uengine_csr_read);
+
+void ixp2000_uengine_csr_write(int uengine, int offset, u32 value)
+{
+       void *uebase;
+       u32 *local_csr_status;
+       u32 *reg;
+
+       uebase = ixp2000_uengine_csr_area(uengine);
+
+       local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS);
+       reg = (u32 *)(uebase + offset);
+       do {
+               ixp2000_reg_write(reg, value);
+       } while (ixp2000_reg_read(local_csr_status) & 1);
+}
+EXPORT_SYMBOL(ixp2000_uengine_csr_write);
+
+void ixp2000_uengine_reset(u32 uengine_mask)
+{
+       ixp2000_reg_wrb(IXP2000_RESET1, uengine_mask & ixp2000_uengine_mask);
+       ixp2000_reg_wrb(IXP2000_RESET1, 0);
+}
+EXPORT_SYMBOL(ixp2000_uengine_reset);
+
+void ixp2000_uengine_set_mode(int uengine, u32 mode)
+{
+       /*
+        * CTL_STR_PAR_EN: unconditionally enable parity checking on
+        * control store.
+        */
+       mode |= 0x10000000;
+       ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mode);
+
+       /*
+        * Enable updating of condition codes.
+        */
+       ixp2000_uengine_csr_write(uengine, CC_ENABLE, 0x00002000);
+
+       /*
+        * Initialise other per-microengine registers.
+        */
+       ixp2000_uengine_csr_write(uengine, NN_PUT, 0x00);
+       ixp2000_uengine_csr_write(uengine, NN_GET, 0x00);
+       ixp2000_uengine_csr_write(uengine, T_INDEX_BYTE_INDEX, 0);
+}
+EXPORT_SYMBOL(ixp2000_uengine_set_mode);
+
+static int make_even_parity(u32 x)
+{
+       return hweight32(x) & 1;
+}
+
+static void ustore_write(int uengine, u64 insn)
+{
+       /*
+        * Generate even parity for top and bottom 20 bits.
+        */
+       insn |= (u64)make_even_parity((insn >> 20) & 0x000fffff) << 41;
+       insn |= (u64)make_even_parity(insn & 0x000fffff) << 40;
+
+       /*
+        * Write to microstore.  The second write auto-increments
+        * the USTORE_ADDRESS index register.
+        */
+       ixp2000_uengine_csr_write(uengine, USTORE_DATA_LOWER, (u32)insn);
+       ixp2000_uengine_csr_write(uengine, USTORE_DATA_UPPER, (u32)(insn >> 32));
+}
+
+void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns)
+{
+       int i;
+
+       /*
+        * Start writing to microstore at address 0.
+        */
+       ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x80000000);
+       for (i = 0; i < insns; i++) {
+               u64 insn;
+
+               insn = (((u64)ucode[0]) << 32) |
+                       (((u64)ucode[1]) << 24) |
+                       (((u64)ucode[2]) << 16) |
+                       (((u64)ucode[3]) << 8) |
+                       ((u64)ucode[4]);
+               ucode += 5;
+
+               ustore_write(uengine, insn);
+       }
+
+       /*
+        * Pad with a few NOPs at the end (to avoid the microengine
+        * aborting as it prefetches beyond the last instruction), unless
+        * we run off the end of the instruction store first, at which
+        * point the address register will wrap back to zero.
+        */
+       for (i = 0; i < 4; i++) {
+               u32 addr;
+
+               addr = ixp2000_uengine_csr_read(uengine, USTORE_ADDRESS);
+               if (addr == 0x80000000)
+                       break;
+               ustore_write(uengine, 0xf0000c0300ULL);
+       }
+
+       /*
+        * End programming.
+        */
+       ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x00000000);
+}
+EXPORT_SYMBOL(ixp2000_uengine_load_microcode);
+
+void ixp2000_uengine_init_context(int uengine, int context, int pc)
+{
+       /*
+        * Select the right context for indirect access.
+        */
+       ixp2000_uengine_csr_write(uengine, CSR_CTX_POINTER, context);
+
+       /*
+        * Initialise signal masks to immediately go to Ready state.
+        */
+       ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_SIG_EVENTS, 1);
+       ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_WAKEUP_EVENTS, 1);
+
+       /*
+        * Set program counter.
+        */
+       ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_STS, pc);
+}
+EXPORT_SYMBOL(ixp2000_uengine_init_context);
+
+void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask)
+{
+       u32 mask;
+
+       /*
+        * Enable the specified context to go to Executing state.
+        */
+       mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES);
+       mask |= ctx_mask << 8;
+       ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask);
+}
+EXPORT_SYMBOL(ixp2000_uengine_start_contexts);
+
+void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask)
+{
+       u32 mask;
+
+       /*
+        * Disable the Ready->Executing transition.  Note that this
+        * does not stop the context until it voluntarily yields.
+        */
+       mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES);
+       mask &= ~(ctx_mask << 8);
+       ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask);
+}
+EXPORT_SYMBOL(ixp2000_uengine_stop_contexts);
+
+static int check_ixp_type(struct ixp2000_uengine_code *c)
+{
+       u32 product_id;
+       u32 rev;
+
+       product_id = ixp2000_reg_read(IXP2000_PRODUCT_ID);
+       if (((product_id >> 16) & 0x1f) != 0)
+               return 0;
+
+       switch ((product_id >> 8) & 0xff) {
+       case 0:         /* IXP2800 */
+               if (!(c->cpu_model_bitmask & 4))
+                       return 0;
+               break;
+
+       case 1:         /* IXP2850 */
+               if (!(c->cpu_model_bitmask & 8))
+                       return 0;
+               break;
+
+       case 2:         /* IXP2400 */
+               if (!(c->cpu_model_bitmask & 2))
+                       return 0;
+               break;
+
+       default:
+               return 0;
+       }
+
+       rev = product_id & 0xff;
+       if (rev < c->cpu_min_revision || rev > c->cpu_max_revision)
+               return 0;
+
+       return 1;
+}
+
+static void generate_ucode(u8 *ucode, u32 *gpr_a, u32 *gpr_b)
+{
+       int offset;
+       int i;
+
+       offset = 0;
+
+       for (i = 0; i < 128; i++) {
+               u8 b3;
+               u8 b2;
+               u8 b1;
+               u8 b0;
+
+               b3 = (gpr_a[i] >> 24) & 0xff;
+               b2 = (gpr_a[i] >> 16) & 0xff;
+               b1 = (gpr_a[i] >> 8) & 0xff;
+               b0 = gpr_a[i] & 0xff;
+
+               // immed[@ai, (b1 << 8) | b0]
+               // 11110000 0000VVVV VVVV11VV VVVVVV00 1IIIIIII
+               ucode[offset++] = 0xf0;
+               ucode[offset++] = (b1 >> 4);
+               ucode[offset++] = (b1 << 4) | 0x0c | (b0 >> 6);
+               ucode[offset++] = (b0 << 2);
+               ucode[offset++] = 0x80 | i;
+
+               // immed_w1[@ai, (b3 << 8) | b2]
+               // 11110100 0100VVVV VVVV11VV VVVVVV00 1IIIIIII
+               ucode[offset++] = 0xf4;
+               ucode[offset++] = 0x40 | (b3 >> 4);
+               ucode[offset++] = (b3 << 4) | 0x0c | (b2 >> 6);
+               ucode[offset++] = (b2 << 2);
+               ucode[offset++] = 0x80 | i;
+       }
+
+       for (i = 0; i < 128; i++) {
+               u8 b3;
+               u8 b2;
+               u8 b1;
+               u8 b0;
+
+               b3 = (gpr_b[i] >> 24) & 0xff;
+               b2 = (gpr_b[i] >> 16) & 0xff;
+               b1 = (gpr_b[i] >> 8) & 0xff;
+               b0 = gpr_b[i] & 0xff;
+
+               // immed[@bi, (b1 << 8) | b0]
+               // 11110000 0000VVVV VVVV001I IIIIII11 VVVVVVVV
+               ucode[offset++] = 0xf0;
+               ucode[offset++] = (b1 >> 4);
+               ucode[offset++] = (b1 << 4) | 0x02 | (i >> 6);
+               ucode[offset++] = (i << 2) | 0x03;
+               ucode[offset++] = b0;
+
+               // immed_w1[@bi, (b3 << 8) | b2]
+               // 11110100 0100VVVV VVVV001I IIIIII11 VVVVVVVV
+               ucode[offset++] = 0xf4;
+               ucode[offset++] = 0x40 | (b3 >> 4);
+               ucode[offset++] = (b3 << 4) | 0x02 | (i >> 6);
+               ucode[offset++] = (i << 2) | 0x03;
+               ucode[offset++] = b2;
+       }
+
+       // ctx_arb[kill]
+       ucode[offset++] = 0xe0;
+       ucode[offset++] = 0x00;
+       ucode[offset++] = 0x01;
+       ucode[offset++] = 0x00;
+       ucode[offset++] = 0x00;
+}
+
+static int set_initial_registers(int uengine, struct ixp2000_uengine_code *c)
+{
+       int per_ctx_regs;
+       u32 *gpr_a;
+       u32 *gpr_b;
+       u8 *ucode;
+       int i;
+
+       gpr_a = kmalloc(128 * sizeof(u32), GFP_KERNEL);
+       gpr_b = kmalloc(128 * sizeof(u32), GFP_KERNEL);
+       ucode = kmalloc(513 * 5, GFP_KERNEL);
+       if (gpr_a == NULL || gpr_b == NULL || ucode == NULL) {
+               kfree(ucode);
+               kfree(gpr_b);
+               kfree(gpr_a);
+               return 1;
+       }
+
+       per_ctx_regs = 16;
+       if (c->uengine_parameters & IXP2000_UENGINE_4_CONTEXTS)
+               per_ctx_regs = 32;
+
+       memset(gpr_a, 0, sizeof(gpr_a));
+       memset(gpr_b, 0, sizeof(gpr_b));
+       for (i = 0; i < 256; i++) {
+               struct ixp2000_reg_value *r = c->initial_reg_values + i;
+               u32 *bank;
+               int inc;
+               int j;
+
+               if (r->reg == -1)
+                       break;
+
+               bank = (r->reg & 0x400) ? gpr_b : gpr_a;
+               inc = (r->reg & 0x80) ? 128 : per_ctx_regs;
+
+               j = r->reg & 0x7f;
+               while (j < 128) {
+                       bank[j] = r->value;
+                       j += inc;
+               }
+       }
+
+       generate_ucode(ucode, gpr_a, gpr_b);
+       ixp2000_uengine_load_microcode(uengine, ucode, 513);
+       ixp2000_uengine_init_context(uengine, 0, 0);
+       ixp2000_uengine_start_contexts(uengine, 0x01);
+       for (i = 0; i < 100; i++) {
+               u32 status;
+
+               status = ixp2000_uengine_csr_read(uengine, ACTIVE_CTX_STS);
+               if (!(status & 0x80000000))
+                       break;
+       }
+       ixp2000_uengine_stop_contexts(uengine, 0x01);
+
+       kfree(ucode);
+       kfree(gpr_b);
+       kfree(gpr_a);
+
+       return !!(i == 100);
+}
+
+int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c)
+{
+       int ctx;
+
+       if (!check_ixp_type(c))
+               return 1;
+
+       if (!(ixp2000_uengine_mask & (1 << uengine)))
+               return 1;
+
+       ixp2000_uengine_reset(1 << uengine);
+       ixp2000_uengine_set_mode(uengine, c->uengine_parameters);
+       if (set_initial_registers(uengine, c))
+               return 1;
+       ixp2000_uengine_load_microcode(uengine, c->insns, c->num_insns);
+
+       for (ctx = 0; ctx < 8; ctx++)
+               ixp2000_uengine_init_context(uengine, ctx, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL(ixp2000_uengine_load);
+
+
+static int __init ixp2000_uengine_init(void)
+{
+       int uengine;
+       u32 value;
+
+       /*
+        * Determine number of microengines present.
+        */
+       switch ((ixp2000_reg_read(IXP2000_PRODUCT_ID) >> 8) & 0x1fff) {
+       case 0:         /* IXP2800 */
+       case 1:         /* IXP2850 */
+               ixp2000_uengine_mask = 0x00ff00ff;
+               break;
+
+       case 2:         /* IXP2400 */
+               ixp2000_uengine_mask = 0x000f000f;
+               break;
+
+       default:
+               printk(KERN_INFO "Detected unknown IXP2000 model (%.8x)\n",
+                       (unsigned int)ixp2000_reg_read(IXP2000_PRODUCT_ID));
+               ixp2000_uengine_mask = 0x00000000;
+               break;
+       }
+
+       /*
+        * Reset microengines.
+        */
+       ixp2000_uengine_reset(ixp2000_uengine_mask);
+
+       /*
+        * Synchronise timestamp counters across all microengines.
+        */
+       value = ixp2000_reg_read(IXP2000_MISC_CONTROL);
+       ixp2000_reg_wrb(IXP2000_MISC_CONTROL, value & ~0x80);
+       for (uengine = 0; uengine < 32; uengine++) {
+               if (ixp2000_uengine_mask & (1 << uengine)) {
+                       ixp2000_uengine_csr_write(uengine, TIMESTAMP_LOW, 0);
+                       ixp2000_uengine_csr_write(uengine, TIMESTAMP_HIGH, 0);
+               }
+       }
+       ixp2000_reg_wrb(IXP2000_MISC_CONTROL, value | 0x80);
+
+       return 0;
+}
+
+subsys_initcall(ixp2000_uengine_init);
diff --git a/arch/arm/mach-omap1/board-netstar.c b/arch/arm/mach-omap1/board-netstar.c
new file mode 100644 (file)
index 0000000..60d5f8a
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Modified from board-generic.c
+ *
+ * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl <michl@2n.cz>
+ *
+ * Code for Netstar OMAP board.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/usb.h>
+#include <asm/arch/common.h>
+
+extern void __init omap_init_time(void);
+extern int omap_gpio_init(void);
+
+static struct resource netstar_smc91x_resources[] = {
+       [0] = {
+               .start  = OMAP_CS1_PHYS + 0x300,
+               .end    = OMAP_CS1_PHYS + 0x300 + 16,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = OMAP_GPIO_IRQ(8),
+               .end    = OMAP_GPIO_IRQ(8),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device netstar_smc91x_device = {
+       .name           = "smc91x",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(netstar_smc91x_resources),
+       .resource       = netstar_smc91x_resources,
+};
+
+static struct platform_device *netstar_devices[] __initdata = {
+       &netstar_smc91x_device,
+};
+
+static struct omap_uart_config netstar_uart_config __initdata = {
+       .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static struct omap_board_config_kernel netstar_config[] = {
+       { OMAP_TAG_UART,        &netstar_uart_config },
+};
+
+static void __init netstar_init_irq(void)
+{
+       omap1_init_common_hw();
+       omap_init_irq();
+       omap_gpio_init();
+}
+
+static void __init netstar_init(void)
+{
+       /* green LED */
+       omap_request_gpio(4);
+       omap_set_gpio_direction(4, 0);
+       /* smc91x reset */
+       omap_request_gpio(7);
+       omap_set_gpio_direction(7, 0);
+       omap_set_gpio_dataout(7, 1);
+       udelay(2);      /* wait at least 100ns */
+       omap_set_gpio_dataout(7, 0);
+       mdelay(50);     /* 50ms until PHY ready */
+       /* smc91x interrupt pin */
+       omap_request_gpio(8);
+
+       omap_request_gpio(12);
+       omap_request_gpio(13);
+       omap_request_gpio(14);
+       omap_request_gpio(15);
+       set_irq_type(OMAP_GPIO_IRQ(12), IRQT_FALLING);
+       set_irq_type(OMAP_GPIO_IRQ(13), IRQT_FALLING);
+       set_irq_type(OMAP_GPIO_IRQ(14), IRQT_FALLING);
+       set_irq_type(OMAP_GPIO_IRQ(15), IRQT_FALLING);
+
+       platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices));
+
+       /* Switch on green LED */
+       omap_set_gpio_dataout(4, 0);
+       /* Switch off red LED */
+       omap_writeb(0x00, OMAP_LPG1_PMR);       /* Disable clock */
+       omap_writeb(0x80, OMAP_LPG1_LCR);
+
+       omap_board_config = netstar_config;
+       omap_board_config_size = ARRAY_SIZE(netstar_config);
+       omap_serial_init();
+}
+
+static void __init netstar_map_io(void)
+{
+       omap1_map_common_io();
+}
+
+#define MACHINE_PANICED                1
+#define MACHINE_REBOOTING      2
+#define MACHINE_REBOOT         4
+static unsigned long machine_state;
+
+static int panic_event(struct notifier_block *this, unsigned long event,
+        void *ptr)
+{
+       if (test_and_set_bit(MACHINE_PANICED, &machine_state))
+               return NOTIFY_DONE;
+
+       /* Switch off green LED */
+       omap_set_gpio_dataout(4, 1);
+       /* Flash red LED */
+       omap_writeb(0x78, OMAP_LPG1_LCR);
+       omap_writeb(0x01, OMAP_LPG1_PMR);       /* Enable clock */
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+       .notifier_call  = panic_event,
+};
+
+static int __init netstar_late_init(void)
+{
+       /* TODO: Setup front panel switch here */
+
+       /* Setup panic notifier */
+       notifier_chain_register(&panic_notifier_list, &panic_block);
+
+       return 0;
+}
+
+postcore_initcall(netstar_late_init);
+
+MACHINE_START(NETSTAR, "NetStar OMAP5910")
+       /* Maintainer: Ladislav Michl <michl@2n.cz> */
+       .phys_io        = 0xfff00000,
+       .io_pg_offst    = ((0xfef00000) >> 18) & 0xfffc,
+       .boot_params    = 0x10000100,
+       .map_io         = netstar_map_io,
+       .init_irq       = netstar_init_irq,
+       .init_machine   = netstar_init,
+       .timer          = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/prcm.h b/arch/arm/mach-omap2/prcm.h
new file mode 100644 (file)
index 0000000..2eb89b9
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * prcm.h - Access definations for use in OMAP24XX clock and power management
+ *
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARM_ARCH_DPM_PRCM_H
+#define __ASM_ARM_ARCH_DPM_PRCM_H
+
+/* SET_PERFORMANCE_LEVEL PARAMETERS */
+#define PRCM_HALF_SPEED 1
+#define PRCM_FULL_SPEED 2
+
+#ifndef __ASSEMBLER__
+
+#define PRCM_REG32(offset)     __REG32(OMAP24XX_PRCM_BASE + (offset))
+
+#define PRCM_REVISION          PRCM_REG32(0x000)
+#define PRCM_SYSCONFIG         PRCM_REG32(0x010)
+#define PRCM_IRQSTATUS_MPU     PRCM_REG32(0x018)
+#define PRCM_IRQENABLE_MPU     PRCM_REG32(0x01C)
+#define PRCM_VOLTCTRL          PRCM_REG32(0x050)
+#define PRCM_VOLTST            PRCM_REG32(0x054)
+#define PRCM_CLKSRC_CTRL       PRCM_REG32(0x060)
+#define PRCM_CLKOUT_CTRL       PRCM_REG32(0x070)
+#define PRCM_CLKEMUL_CTRL      PRCM_REG32(0x078)
+#define PRCM_CLKCFG_CTRL       PRCM_REG32(0x080)
+#define PRCM_CLKCFG_STATUS     PRCM_REG32(0x084)
+#define PRCM_VOLTSETUP         PRCM_REG32(0x090)
+#define PRCM_CLKSSETUP         PRCM_REG32(0x094)
+#define PRCM_POLCTRL           PRCM_REG32(0x098)
+
+/* GENERAL PURPOSE */
+#define GENERAL_PURPOSE1       PRCM_REG32(0x0B0)
+#define GENERAL_PURPOSE2       PRCM_REG32(0x0B4)
+#define GENERAL_PURPOSE3       PRCM_REG32(0x0B8)
+#define GENERAL_PURPOSE4       PRCM_REG32(0x0BC)
+#define GENERAL_PURPOSE5       PRCM_REG32(0x0C0)
+#define GENERAL_PURPOSE6       PRCM_REG32(0x0C4)
+#define GENERAL_PURPOSE7       PRCM_REG32(0x0C8)
+#define GENERAL_PURPOSE8       PRCM_REG32(0x0CC)
+#define GENERAL_PURPOSE9       PRCM_REG32(0x0D0)
+#define GENERAL_PURPOSE10      PRCM_REG32(0x0D4)
+#define GENERAL_PURPOSE11      PRCM_REG32(0x0D8)
+#define GENERAL_PURPOSE12      PRCM_REG32(0x0DC)
+#define GENERAL_PURPOSE13      PRCM_REG32(0x0E0)
+#define GENERAL_PURPOSE14      PRCM_REG32(0x0E4)
+#define GENERAL_PURPOSE15      PRCM_REG32(0x0E8)
+#define GENERAL_PURPOSE16      PRCM_REG32(0x0EC)
+#define GENERAL_PURPOSE17      PRCM_REG32(0x0F0)
+#define GENERAL_PURPOSE18      PRCM_REG32(0x0F4)
+#define GENERAL_PURPOSE19      PRCM_REG32(0x0F8)
+#define GENERAL_PURPOSE20      PRCM_REG32(0x0FC)
+
+/* MPU */
+#define CM_CLKSEL_MPU          PRCM_REG32(0x140)
+#define CM_CLKSTCTRL_MPU       PRCM_REG32(0x148)
+#define RM_RSTST_MPU           PRCM_REG32(0x158)
+#define PM_WKDEP_MPU           PRCM_REG32(0x1C8)
+#define PM_EVGENCTRL_MPU       PRCM_REG32(0x1D4)
+#define PM_EVEGENONTIM_MPU     PRCM_REG32(0x1D8)
+#define PM_EVEGENOFFTIM_MPU    PRCM_REG32(0x1DC)
+#define PM_PWSTCTRL_MPU                PRCM_REG32(0x1E0)
+#define PM_PWSTST_MPU          PRCM_REG32(0x1E4)
+
+/* CORE */
+#define CM_FCLKEN1_CORE                PRCM_REG32(0x200)
+#define CM_FCLKEN2_CORE                PRCM_REG32(0x204)
+#define CM_FCLKEN3_CORE                PRCM_REG32(0x208)
+#define CM_ICLKEN1_CORE                PRCM_REG32(0x210)
+#define CM_ICLKEN2_CORE                PRCM_REG32(0x214)
+#define CM_ICLKEN3_CORE                PRCM_REG32(0x218)
+#define CM_ICLKEN4_CORE                PRCM_REG32(0x21C)
+#define CM_IDLEST1_CORE                PRCM_REG32(0x220)
+#define CM_IDLEST2_CORE                PRCM_REG32(0x224)
+#define CM_IDLEST3_CORE                PRCM_REG32(0x228)
+#define CM_IDLEST4_CORE                PRCM_REG32(0x22C)
+#define CM_AUTOIDLE1_CORE      PRCM_REG32(0x230)
+#define CM_AUTOIDLE2_CORE      PRCM_REG32(0x234)
+#define CM_AUTOIDLE3_CORE      PRCM_REG32(0x238)
+#define CM_AUTOIDLE4_CORE      PRCM_REG32(0x23C)
+#define CM_CLKSEL1_CORE                PRCM_REG32(0x240)
+#define CM_CLKSEL2_CORE                PRCM_REG32(0x244)
+#define CM_CLKSTCTRL_CORE      PRCM_REG32(0x248)
+#define PM_WKEN1_CORE          PRCM_REG32(0x2A0)
+#define PM_WKEN2_CORE          PRCM_REG32(0x2A4)
+#define PM_WKST1_CORE          PRCM_REG32(0x2B0)
+#define PM_WKST2_CORE          PRCM_REG32(0x2B4)
+#define PM_WKDEP_CORE          PRCM_REG32(0x2C8)
+#define PM_PWSTCTRL_CORE       PRCM_REG32(0x2E0)
+#define PM_PWSTST_CORE         PRCM_REG32(0x2E4)
+
+/* GFX */
+#define CM_FCLKEN_GFX          PRCM_REG32(0x300)
+#define CM_ICLKEN_GFX          PRCM_REG32(0x310)
+#define CM_IDLEST_GFX          PRCM_REG32(0x320)
+#define CM_CLKSEL_GFX          PRCM_REG32(0x340)
+#define CM_CLKSTCTRL_GFX       PRCM_REG32(0x348)
+#define RM_RSTCTRL_GFX         PRCM_REG32(0x350)
+#define RM_RSTST_GFX           PRCM_REG32(0x358)
+#define PM_WKDEP_GFX           PRCM_REG32(0x3C8)
+#define PM_PWSTCTRL_GFX                PRCM_REG32(0x3E0)
+#define PM_PWSTST_GFX          PRCM_REG32(0x3E4)
+
+/* WAKE-UP */
+#define CM_FCLKEN_WKUP         PRCM_REG32(0x400)
+#define CM_ICLKEN_WKUP         PRCM_REG32(0x410)
+#define CM_IDLEST_WKUP         PRCM_REG32(0x420)
+#define CM_AUTOIDLE_WKUP       PRCM_REG32(0x430)
+#define CM_CLKSEL_WKUP         PRCM_REG32(0x440)
+#define RM_RSTCTRL_WKUP                PRCM_REG32(0x450)
+#define RM_RSTTIME_WKUP                PRCM_REG32(0x454)
+#define RM_RSTST_WKUP          PRCM_REG32(0x458)
+#define PM_WKEN_WKUP           PRCM_REG32(0x4A0)
+#define PM_WKST_WKUP           PRCM_REG32(0x4B0)
+
+/* CLOCKS */
+#define CM_CLKEN_PLL           PRCM_REG32(0x500)
+#define CM_IDLEST_CKGEN                PRCM_REG32(0x520)
+#define CM_AUTOIDLE_PLL                PRCM_REG32(0x530)
+#define CM_CLKSEL1_PLL         PRCM_REG32(0x540)
+#define CM_CLKSEL2_PLL         PRCM_REG32(0x544)
+
+/* DSP */
+#define CM_FCLKEN_DSP          PRCM_REG32(0x800)
+#define CM_ICLKEN_DSP          PRCM_REG32(0x810)
+#define CM_IDLEST_DSP          PRCM_REG32(0x820)
+#define CM_AUTOIDLE_DSP                PRCM_REG32(0x830)
+#define CM_CLKSEL_DSP          PRCM_REG32(0x840)
+#define CM_CLKSTCTRL_DSP       PRCM_REG32(0x848)
+#define RM_RSTCTRL_DSP         PRCM_REG32(0x850)
+#define RM_RSTST_DSP           PRCM_REG32(0x858)
+#define PM_WKEN_DSP            PRCM_REG32(0x8A0)
+#define PM_WKDEP_DSP           PRCM_REG32(0x8C8)
+#define PM_PWSTCTRL_DSP                PRCM_REG32(0x8E0)
+#define PM_PWSTST_DSP          PRCM_REG32(0x8E4)
+#define PRCM_IRQSTATUS_DSP     PRCM_REG32(0x8F0)
+#define PRCM_IRQENABLE_DSP     PRCM_REG32(0x8F4)
+
+/* IVA */
+#define PRCM_IRQSTATUS_IVA     PRCM_REG32(0x8F8)
+#define PRCM_IRQENABLE_IVA     PRCM_REG32(0x8FC)
+
+/* Modem on 2430 */
+#define CM_FCLKEN_MDM          PRCM_REG32(0xC00)
+#define CM_ICLKEN_MDM          PRCM_REG32(0xC10)
+#define CM_IDLEST_MDM          PRCM_REG32(0xC20)
+#define CM_CLKSEL_MDM          PRCM_REG32(0xC40)
+
+/* FIXME: Move to header for 2430 */
+#define DISP_BASE              (OMAP24XX_L4_IO_BASE+0x50000)
+#define DISP_REG32(offset)     __REG32(DISP_BASE + (offset))
+
+#define GPMC_BASE              (OMAP24XX_GPMC_BASE)
+#define GPMC_REG32(offset)     __REG32(GPMC_BASE + (offset))
+
+#define GPT1_BASE              (OMAP24XX_GPT1)
+#define GPT1_REG32(offset)     __REG32(GPT1_BASE + (offset))
+
+/* Misc sysconfig */
+#define DISPC_SYSCONFIG                DISP_REG32(0x410)
+#define SPI_BASE               (OMAP24XX_L4_IO_BASE+0x98000)
+#define MCSPI1_SYSCONFIG       __REG32(SPI_BASE + 0x10)
+#define MCSPI2_SYSCONFIG       __REG32(SPI_BASE+0x2000 + 0x10)
+
+//#define DSP_MMU_SYSCONFIG    0x5A000010
+#define CAMERA_MMU_SYSCONFIG   __REG32(DISP_BASE+0x2C10)
+//#define IVA_MMU_SYSCONFIG    0x5D000010
+//#define DSP_DMA_SYSCONFIG    0x00FCC02C
+#define CAMERA_DMA_SYSCONFIG   __REG32(DISP_BASE+0x282C)
+#define SYSTEM_DMA_SYSCONFIG   __REG32(DISP_BASE+0x602C)
+#define GPMC_SYSCONFIG         GPMC_REG32(0x010)
+#define MAILBOXES_SYSCONFIG    __REG32(OMAP24XX_L4_IO_BASE+0x94010)
+#define UART1_SYSCONFIG                __REG32(OMAP24XX_L4_IO_BASE+0x6A054)
+#define UART2_SYSCONFIG                __REG32(OMAP24XX_L4_IO_BASE+0x6C054)
+#define UART3_SYSCONFIG                __REG32(OMAP24XX_L4_IO_BASE+0x6E054)
+//#define IVA_SYSCONFIG                0x5C060010
+#define SDRC_SYSCONFIG         __REG32(OMAP24XX_SDRC_BASE+0x10)
+#define SMS_SYSCONFIG          __REG32(OMAP24XX_SMS_BASE+0x10)
+#define SSI_SYSCONFIG          __REG32(DISP_BASE+0x8010)
+//#define VLYNQ_SYSCONFIG      0x67FFFE10
+
+/* rkw - good cannidates for PM_ to start what nm was trying */
+#define OMAP24XX_GPT2          (OMAP24XX_L4_IO_BASE+0x2A000)
+#define OMAP24XX_GPT3          (OMAP24XX_L4_IO_BASE+0x78000)
+#define OMAP24XX_GPT4          (OMAP24XX_L4_IO_BASE+0x7A000)
+#define OMAP24XX_GPT5          (OMAP24XX_L4_IO_BASE+0x7C000)
+#define OMAP24XX_GPT6          (OMAP24XX_L4_IO_BASE+0x7E000)
+#define OMAP24XX_GPT7          (OMAP24XX_L4_IO_BASE+0x80000)
+#define OMAP24XX_GPT8          (OMAP24XX_L4_IO_BASE+0x82000)
+#define OMAP24XX_GPT9          (OMAP24XX_L4_IO_BASE+0x84000)
+#define OMAP24XX_GPT10         (OMAP24XX_L4_IO_BASE+0x86000)
+#define OMAP24XX_GPT11         (OMAP24XX_L4_IO_BASE+0x88000)
+#define OMAP24XX_GPT12         (OMAP24XX_L4_IO_BASE+0x8A000)
+
+#define GPTIMER1_SYSCONFIG     GPT1_REG32(0x010)
+#define GPTIMER2_SYSCONFIG     __REG32(OMAP24XX_GPT2 + 0x10)
+#define GPTIMER3_SYSCONFIG     __REG32(OMAP24XX_GPT3 + 0x10)
+#define GPTIMER4_SYSCONFIG     __REG32(OMAP24XX_GPT4 + 0x10)
+#define GPTIMER5_SYSCONFIG     __REG32(OMAP24XX_GPT5 + 0x10)
+#define GPTIMER6_SYSCONFIG     __REG32(OMAP24XX_GPT6 + 0x10)
+#define GPTIMER7_SYSCONFIG     __REG32(OMAP24XX_GPT7 + 0x10)
+#define GPTIMER8_SYSCONFIG     __REG32(OMAP24XX_GPT8 + 0x10)
+#define GPTIMER9_SYSCONFIG     __REG32(OMAP24XX_GPT9 + 0x10)
+#define GPTIMER10_SYSCONFIG    __REG32(OMAP24XX_GPT10 + 0x10)
+#define GPTIMER11_SYSCONFIG    __REG32(OMAP24XX_GPT11 + 0x10)
+#define GPTIMER12_SYSCONFIG    __REG32(OMAP24XX_GPT12 + 0x10)
+
+#define GPIOX_BASE(X)          (OMAP24XX_GPIO_BASE+(0x2000*((X)-1)))
+
+#define GPIO1_SYSCONFIG                __REG32((GPIOX_BASE(1)+0x10))
+#define GPIO2_SYSCONFIG                __REG32((GPIOX_BASE(2)+0x10))
+#define GPIO3_SYSCONFIG                __REG32((GPIOX_BASE(3)+0x10))
+#define GPIO4_SYSCONFIG                __REG32((GPIOX_BASE(4)+0x10))
+
+/* GP TIMER 1 */
+#define GPTIMER1_TISTAT                GPT1_REG32(0x014)
+#define GPTIMER1_TISR          GPT1_REG32(0x018)
+#define GPTIMER1_TIER          GPT1_REG32(0x01C)
+#define GPTIMER1_TWER          GPT1_REG32(0x020)
+#define GPTIMER1_TCLR          GPT1_REG32(0x024)
+#define GPTIMER1_TCRR          GPT1_REG32(0x028)
+#define GPTIMER1_TLDR          GPT1_REG32(0x02C)
+#define GPTIMER1_TTGR          GPT1_REG32(0x030)
+#define GPTIMER1_TWPS          GPT1_REG32(0x034)
+#define GPTIMER1_TMAR          GPT1_REG32(0x038)
+#define GPTIMER1_TCAR1         GPT1_REG32(0x03C)
+#define GPTIMER1_TSICR         GPT1_REG32(0x040)
+#define GPTIMER1_TCAR2         GPT1_REG32(0x044)
+
+/* rkw -- base fix up please... */
+#define GPTIMER3_TISR          __REG32(OMAP24XX_L4_IO_BASE+0x78018)
+
+/* SDRC */
+#define SDRC_DLLA_CTRL         __REG32(OMAP24XX_SDRC_BASE+0x060)
+#define SDRC_DLLA_STATUS       __REG32(OMAP24XX_SDRC_BASE+0x064)
+#define SDRC_DLLB_CTRL         __REG32(OMAP24XX_SDRC_BASE+0x068)
+#define SDRC_DLLB_STATUS       __REG32(OMAP24XX_SDRC_BASE+0x06C)
+#define SDRC_POWER             __REG32(OMAP24XX_SDRC_BASE+0x070)
+#define SDRC_MR_0              __REG32(OMAP24XX_SDRC_BASE+0x084)
+
+/* GPIO 1 */
+#define GPIO1_BASE             GPIOX_BASE(1)
+#define GPIO1_REG32(offset)    __REG32(GPIO1_BASE + (offset))
+#define GPIO1_IRQENABLE1       GPIO1_REG32(0x01C)
+#define GPIO1_IRQSTATUS1       GPIO1_REG32(0x018)
+#define GPIO1_IRQENABLE2       GPIO1_REG32(0x02C)
+#define GPIO1_IRQSTATUS2       GPIO1_REG32(0x028)
+#define GPIO1_WAKEUPENABLE     GPIO1_REG32(0x020)
+#define GPIO1_RISINGDETECT     GPIO1_REG32(0x048)
+#define GPIO1_DATAIN           GPIO1_REG32(0x038)
+#define GPIO1_OE               GPIO1_REG32(0x034)
+#define GPIO1_DATAOUT          GPIO1_REG32(0x03C)
+
+/* GPIO2 */
+#define GPIO2_BASE             GPIOX_BASE(2)
+#define GPIO2_REG32(offset)    __REG32(GPIO2_BASE + (offset))
+#define GPIO2_IRQENABLE1       GPIO2_REG32(0x01C)
+#define GPIO2_IRQSTATUS1       GPIO2_REG32(0x018)
+#define GPIO2_IRQENABLE2       GPIO2_REG32(0x02C)
+#define GPIO2_IRQSTATUS2       GPIO2_REG32(0x028)
+#define GPIO2_WAKEUPENABLE     GPIO2_REG32(0x020)
+#define GPIO2_RISINGDETECT     GPIO2_REG32(0x048)
+#define GPIO2_DATAIN           GPIO2_REG32(0x038)
+#define GPIO2_OE               GPIO2_REG32(0x034)
+#define GPIO2_DATAOUT          GPIO2_REG32(0x03C)
+
+/* GPIO 3 */
+#define GPIO3_BASE             GPIOX_BASE(3)
+#define GPIO3_REG32(offset)    __REG32(GPIO3_BASE + (offset))
+#define GPIO3_IRQENABLE1       GPIO3_REG32(0x01C)
+#define GPIO3_IRQSTATUS1       GPIO3_REG32(0x018)
+#define GPIO3_IRQENABLE2       GPIO3_REG32(0x02C)
+#define GPIO3_IRQSTATUS2       GPIO3_REG32(0x028)
+#define GPIO3_WAKEUPENABLE     GPIO3_REG32(0x020)
+#define GPIO3_RISINGDETECT     GPIO3_REG32(0x048)
+#define GPIO3_FALLINGDETECT    GPIO3_REG32(0x04C)
+#define GPIO3_DATAIN           GPIO3_REG32(0x038)
+#define GPIO3_OE               GPIO3_REG32(0x034)
+#define GPIO3_DATAOUT          GPIO3_REG32(0x03C)
+#define GPIO3_DEBOUNCENABLE    GPIO3_REG32(0x050)
+#define GPIO3_DEBOUNCINGTIME   GPIO3_REG32(0x054)
+
+/* GPIO 4 */
+#define GPIO4_BASE             GPIOX_BASE(4)
+#define GPIO4_REG32(offset)    __REG32(GPIO4_BASE + (offset))
+#define GPIO4_IRQENABLE1       GPIO4_REG32(0x01C)
+#define GPIO4_IRQSTATUS1       GPIO4_REG32(0x018)
+#define GPIO4_IRQENABLE2       GPIO4_REG32(0x02C)
+#define GPIO4_IRQSTATUS2       GPIO4_REG32(0x028)
+#define GPIO4_WAKEUPENABLE     GPIO4_REG32(0x020)
+#define GPIO4_RISINGDETECT     GPIO4_REG32(0x048)
+#define GPIO4_FALLINGDETECT    GPIO4_REG32(0x04C)
+#define GPIO4_DATAIN           GPIO4_REG32(0x038)
+#define GPIO4_OE               GPIO4_REG32(0x034)
+#define GPIO4_DATAOUT          GPIO4_REG32(0x03C)
+#define GPIO4_DEBOUNCENABLE    GPIO4_REG32(0x050)
+#define GPIO4_DEBOUNCINGTIME   GPIO4_REG32(0x054)
+
+
+/* IO CONFIG */
+#define CONTROL_BASE           (OMAP24XX_CTRL_BASE)
+#define CONTROL_REG32(offset)  __REG32(CONTROL_BASE + (offset))
+
+#define CONTROL_PADCONF_SPI1_NCS2      CONTROL_REG32(0x104)
+#define CONTROL_PADCONF_SYS_XTALOUT    CONTROL_REG32(0x134)
+#define CONTROL_PADCONF_UART1_RX       CONTROL_REG32(0x0C8)
+#define CONTROL_PADCONF_MCBSP1_DX      CONTROL_REG32(0x10C)
+#define CONTROL_PADCONF_GPMC_NCS4      CONTROL_REG32(0x090)
+#define CONTROL_PADCONF_DSS_D5         CONTROL_REG32(0x0B8)
+#define CONTROL_PADCONF_DSS_D9         CONTROL_REG32(0x0BC)
+#define CONTROL_PADCONF_DSS_D13                CONTROL_REG32(0x0C0)
+#define CONTROL_PADCONF_DSS_VSYNC      CONTROL_REG32(0x0CC)
+
+/* CONTROL */
+#define CONTROL_DEVCONF                CONTROL_REG32(0x274)
+
+/* INTERRUPT CONTROLLER */
+#define INTC_BASE              (OMAP24XX_L4_IO_BASE+0xfe000)
+#define INTC_REG32(offset)     __REG32(INTC_BASE + (offset))
+
+#define INTC1_U_BASE           INTC_REG32(0x000)
+#define INTC_MIR0              INTC_REG32(0x084)
+#define INTC_MIR_SET0          INTC_REG32(0x08C)
+#define INTC_MIR_CLEAR0                INTC_REG32(0x088)
+#define INTC_ISR_CLEAR0                INTC_REG32(0x094)
+#define INTC_MIR1              INTC_REG32(0x0A4)
+#define INTC_MIR_SET1          INTC_REG32(0x0AC)
+#define INTC_MIR_CLEAR1                INTC_REG32(0x0A8)
+#define INTC_ISR_CLEAR1                INTC_REG32(0x0B4)
+#define INTC_MIR2              INTC_REG32(0x0C4)
+#define INTC_MIR_SET2          INTC_REG32(0x0CC)
+#define INTC_MIR_CLEAR2                INTC_REG32(0x0C8)
+#define INTC_ISR_CLEAR2                INTC_REG32(0x0D4)
+#define INTC_SIR_IRQ           INTC_REG32(0x040)
+#define INTC_CONTROL           INTC_REG32(0x048)
+#define INTC_ILR11             INTC_REG32(0x12C)
+#define INTC_ILR32             INTC_REG32(0x180)
+#define INTC_ILR37             INTC_REG32(0x194)
+#define INTC_SYSCONFIG         INTC_REG32(0x010)
+
+/* RAM FIREWALL */
+#define RAMFW_BASE             (0x68005000)
+#define RAMFW_REG32(offset)    __REG32(RAMFW_BASE + (offset))
+
+#define RAMFW_REQINFOPERM0     RAMFW_REG32(0x048)
+#define RAMFW_READPERM0                RAMFW_REG32(0x050)
+#define RAMFW_WRITEPERM0       RAMFW_REG32(0x058)
+
+/* GPMC CS1 FPGA ON USER INTERFACE MODULE */
+//#define DEBUG_BOARD_LED_REGISTER 0x04000014
+
+/* GPMC CS0 */
+#define GPMC_CONFIG1_0         GPMC_REG32(0x060)
+#define GPMC_CONFIG2_0         GPMC_REG32(0x064)
+#define GPMC_CONFIG3_0         GPMC_REG32(0x068)
+#define GPMC_CONFIG4_0         GPMC_REG32(0x06C)
+#define GPMC_CONFIG5_0         GPMC_REG32(0x070)
+#define GPMC_CONFIG6_0         GPMC_REG32(0x074)
+#define GPMC_CONFIG7_0         GPMC_REG32(0x078)
+
+/* DSS */
+#define DSS_CONTROL            DISP_REG32(0x040)
+#define DISPC_CONTROL          DISP_REG32(0x440)
+#define DISPC_SYSSTATUS                DISP_REG32(0x414)
+#define DISPC_IRQSTATUS                DISP_REG32(0x418)
+#define DISPC_IRQENABLE                DISP_REG32(0x41C)
+#define DISPC_CONFIG           DISP_REG32(0x444)
+#define DISPC_DEFAULT_COLOR0   DISP_REG32(0x44C)
+#define DISPC_DEFAULT_COLOR1   DISP_REG32(0x450)
+#define DISPC_TRANS_COLOR0     DISP_REG32(0x454)
+#define DISPC_TRANS_COLOR1     DISP_REG32(0x458)
+#define DISPC_LINE_NUMBER      DISP_REG32(0x460)
+#define DISPC_TIMING_H         DISP_REG32(0x464)
+#define DISPC_TIMING_V         DISP_REG32(0x468)
+#define DISPC_POL_FREQ         DISP_REG32(0x46C)
+#define DISPC_DIVISOR          DISP_REG32(0x470)
+#define DISPC_SIZE_DIG         DISP_REG32(0x478)
+#define DISPC_SIZE_LCD         DISP_REG32(0x47C)
+#define DISPC_GFX_BA0          DISP_REG32(0x480)
+#define DISPC_GFX_BA1          DISP_REG32(0x484)
+#define DISPC_GFX_POSITION     DISP_REG32(0x488)
+#define DISPC_GFX_SIZE         DISP_REG32(0x48C)
+#define DISPC_GFX_ATTRIBUTES   DISP_REG32(0x4A0)
+#define DISPC_GFX_FIFO_THRESHOLD       DISP_REG32(0x4A4)
+#define DISPC_GFX_ROW_INC      DISP_REG32(0x4AC)
+#define DISPC_GFX_PIXEL_INC    DISP_REG32(0x4B0)
+#define DISPC_GFX_WINDOW_SKIP  DISP_REG32(0x4B4)
+#define DISPC_GFX_TABLE_BA     DISP_REG32(0x4B8)
+#define DISPC_DATA_CYCLE1      DISP_REG32(0x5D4)
+#define DISPC_DATA_CYCLE2      DISP_REG32(0x5D8)
+#define DISPC_DATA_CYCLE3      DISP_REG32(0x5DC)
+
+/* Wake up define for board */
+#define GPIO97                 (1 << 1)
+#define GPIO88                 (1 << 24)
+
+#endif /* __ASSEMBLER__ */
+
+#endif
+
+
+
+
+
diff --git a/arch/arm/plat-omap/sleep.S b/arch/arm/plat-omap/sleep.S
new file mode 100644 (file)
index 0000000..4cd7d29
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * linux/arch/arm/plat-omap/sleep.S
+ *
+ * Low-level OMAP730/1510/1610 sleep/wakeUp support
+ *
+ * Initial SA1110 code:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Adapted for PXA by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/arch/io.h>
+#include <asm/arch/pm.h>
+
+               .text
+
+/*
+ * Forces OMAP into idle state
+ *
+ * omapXXXX_idle_loop_suspend()
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ *      wakes up it continues execution at the point it went to sleep.
+ *
+ * Note: Because of slightly different configuration values we have
+ *       processor specific functions here.
+ */
+
+#if defined(CONFIG_ARCH_OMAP730)
+ENTRY(omap730_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP730_IDLECT2_SLEEP_VAL & 0xff
+       orr     r5, r5, #OMAP730_IDLECT2_SLEEP_VAL & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP730_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_730: subs    r5, r5, #1
+       bne     l_730
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap730_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}             @ restore regs and return
+
+ENTRY(omap730_idle_loop_suspend_sz)
+       .word   . - omap730_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP730 */
+
+#ifdef CONFIG_ARCH_OMAP15XX
+ENTRY(omap1510_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5, r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1510:        subs    r5, r5, #1
+       bne     l_1510
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1510_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}             @ restore regs and return
+
+ENTRY(omap1510_idle_loop_suspend_sz)
+       .word   . - omap1510_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP15XX */
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+ENTRY(omap1610_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff
+       orr     r5, r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1610:        subs    r5, r5, #1
+       bne     l_1610
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1610_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}             @ restore regs and return
+
+ENTRY(omap1610_idle_loop_suspend_sz)
+       .word   . - omap1610_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP16XX */
+
+/*
+ * Forces OMAP into deep sleep state
+ *
+ * omapXXXX_cpu_suspend()
+ *
+ * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed
+ * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1
+ * in register r1.
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ *      wakes up it continues execution at the point it went to sleep.
+ *
+ * Note: Because of errata work arounds we have processor specific functions
+ *       here. They are mostly the same, but slightly different.
+ *
+ */
+
+#if defined(CONFIG_ARCH_OMAP730)
+ENTRY(omap730_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ Drain write cache
+       mov     r4, #0
+       mcr     p15, 0, r0, c7, c10, 4
+       nop
+
+       @ load base address of Traffic Controller
+       mov     r6, #TCMIF_ASM_BASE & 0xff000000
+       orr     r6, r6, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r6, r6, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r9, r7, #SELF_REFRESH_MODE & 0xff000000
+       orr     r9, r9, #SELF_REFRESH_MODE & 0x000000ff
+       str     r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r9, r8, #IDLE_EMIFS_REQUEST & 0xff
+       str     r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ do not disable PERCK (0x04)
+       mov     r5, #OMAP730_IDLECT2_SLEEP_VAL & 0xff
+       orr     r5, r5, #OMAP730_IDLECT2_SLEEP_VAL & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP730_IDLECT1_SLEEP_VAL & 0xff
+       orr     r3, r3, #OMAP730_IDLECT1_SLEEP_VAL & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ disable instruction cache
+       mrc     p15, 0, r9, c1, c0, 0
+       bic     r2, r9, #0x1000
+       mcr     p15, 0, r2, c1, c0, 0
+       nop
+
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap730_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack.
+ */
+       @ re-enable Icache
+       mcr     p15, 0, r9, c1, c0, 0
+
+       @ reset the ARM_IDLECT1 and ARM_IDLECT2.
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ Restore EMIFF controls
+       str     r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       str     r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap730_cpu_suspend_sz)
+       .word   . - omap730_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP730 */
+
+#ifdef CONFIG_ARCH_OMAP15XX
+ENTRY(omap1510_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ load base address of Traffic Controller
+       mov     r4, #TCMIF_ASM_BASE & 0xff000000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ work around errata of OMAP1510 PDE bit for TC shut down
+       @ clear PDE bit
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       bic     r5, r5, #PDE_BIT & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ set PWD_EN bit
+       and     r5, r5, #PWD_EN_BIT & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #SELF_REFRESH_MODE & 0xff000000
+       orr     r5, r5, #SELF_REFRESH_MODE & 0x000000ff
+       str     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #IDLE_EMIFS_REQUEST & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       mov     r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5, r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff
+       orr     r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1510_2:
+       subs    r5, r5, #1
+       bne     l_1510_2
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1510_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap1510_cpu_suspend_sz)
+       .word   . - omap1510_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP15XX */
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+ENTRY(omap1610_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ Drain write cache
+       mov     r4, #0
+       mcr     p15, 0, r0, c7, c10, 4
+       nop
+
+       @ load base address of Traffic Controller
+       mov     r6, #TCMIF_ASM_BASE & 0xff000000
+       orr     r6, r6, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r6, r6, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r9, r7, #SELF_REFRESH_MODE & 0xff000000
+       orr     r9, r9, #SELF_REFRESH_MODE & 0x000000ff
+       str     r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r9, r8, #IDLE_EMIFS_REQUEST & 0xff
+       str     r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ do not disable PERCK (0x04)
+       mov     r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff
+       orr     r5, r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff
+       orr     r3, r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ disable instruction cache
+       mrc     p15, 0, r9, c1, c0, 0
+       bic     r2, r9, #0x1000
+       mcr     p15, 0, r2, c1, c0, 0
+       nop
+
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1610_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack.
+ */
+       @ re-enable Icache
+       mcr     p15, 0, r9, c1, c0, 0
+
+       @ reset the ARM_IDLECT1 and ARM_IDLECT2.
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ Restore EMIFF controls
+       str     r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       str     r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap1610_cpu_suspend_sz)
+       .word   . - omap1610_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP16XX */
diff --git a/arch/mips/kernel/smp_mt.c b/arch/mips/kernel/smp_mt.c
new file mode 100644 (file)
index 0000000..993b8bf
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2004, 2005 MIPS Technologies, Inc.  All rights reserved.
+ *
+ *  Elizabeth Clarke (beth@mips.com)
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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.,
+ *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cpumask.h>
+#include <linux/interrupt.h>
+#include <linux/compiler.h>
+
+#include <asm/atomic.h>
+#include <asm/cpu.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/hardirq.h>
+#include <asm/mmu_context.h>
+#include <asm/smp.h>
+#include <asm/time.h>
+#include <asm/mipsregs.h>
+#include <asm/mipsmtregs.h>
+#include <asm/cacheflush.h>
+#include <asm/mips-boards/maltaint.h>
+
+#define MIPS_CPU_IPI_RESCHED_IRQ 0
+#define MIPS_CPU_IPI_CALL_IRQ 1
+
+static int cpu_ipi_resched_irq, cpu_ipi_call_irq;
+
+#if 0
+static void dump_mtregisters(int vpe, int tc)
+{
+       printk("vpe %d tc %d\n", vpe, tc);
+
+       settc(tc);
+
+       printk("  c0 status  0x%lx\n", read_vpe_c0_status());
+       printk("  vpecontrol 0x%lx\n", read_vpe_c0_vpecontrol());
+       printk("  vpeconf0    0x%lx\n", read_vpe_c0_vpeconf0());
+       printk("  tcstatus 0x%lx\n", read_tc_c0_tcstatus());
+       printk("  tcrestart 0x%lx\n", read_tc_c0_tcrestart());
+       printk("  tcbind 0x%lx\n", read_tc_c0_tcbind());
+       printk("  tchalt 0x%lx\n", read_tc_c0_tchalt());
+}
+#endif
+
+void __init sanitize_tlb_entries(void)
+{
+       int i, tlbsiz;
+       unsigned long mvpconf0, ncpu;
+
+       if (!cpu_has_mipsmt)
+               return;
+
+       set_c0_mvpcontrol(MVPCONTROL_VPC);
+
+       back_to_back_c0_hazard();
+
+       /* Disable TLB sharing */
+       clear_c0_mvpcontrol(MVPCONTROL_STLB);
+
+       mvpconf0 = read_c0_mvpconf0();
+
+       printk(KERN_INFO "MVPConf0 0x%lx TLBS %lx PTLBE %ld\n", mvpconf0,
+                  (mvpconf0 & MVPCONF0_TLBS) >> MVPCONF0_TLBS_SHIFT,
+                          (mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT);
+
+       tlbsiz = (mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT;
+       ncpu = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
+
+       printk(" tlbsiz %d ncpu %ld\n", tlbsiz, ncpu);
+
+       if (tlbsiz > 0) {
+               /* share them out across the vpe's */
+               tlbsiz /= ncpu;
+
+               printk(KERN_INFO "setting Config1.MMU_size to %d\n", tlbsiz);
+
+               for (i = 0; i < ncpu; i++) {
+                       settc(i);
+
+                       if (i == 0)
+                               write_c0_config1((read_c0_config1() & ~(0x3f << 25)) | (tlbsiz << 25));
+                       else
+                               write_vpe_c0_config1((read_vpe_c0_config1() & ~(0x3f << 25)) |
+                                                  (tlbsiz << 25));
+               }
+       }
+
+       clear_c0_mvpcontrol(MVPCONTROL_VPC);
+}
+
+static void ipi_resched_dispatch (struct pt_regs *regs)
+{
+       do_IRQ(MIPS_CPU_IPI_RESCHED_IRQ, regs);
+}
+
+static void ipi_call_dispatch (struct pt_regs *regs)
+{
+       do_IRQ(MIPS_CPU_IPI_CALL_IRQ, regs);
+}
+
+irqreturn_t ipi_resched_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       return IRQ_HANDLED;
+}
+
+irqreturn_t ipi_call_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       smp_call_function_interrupt();
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction irq_resched = {
+       .handler        = ipi_resched_interrupt,
+       .flags          = SA_INTERRUPT,
+       .name           = "IPI_resched"
+};
+
+static struct irqaction irq_call = {
+       .handler        = ipi_call_interrupt,
+       .flags          = SA_INTERRUPT,
+       .name           = "IPI_call"
+};
+
+/*
+ * Common setup before any secondaries are started
+ * Make sure all CPU's are in a sensible state before we boot any of the
+ * secondarys
+ */
+void plat_smp_setup(void)
+{
+       unsigned long val;
+       int i, num;
+
+       if (!cpu_has_mipsmt)
+               return;
+
+       /* disable MT so we can configure */
+       dvpe();
+       dmt();
+
+       /* Put MVPE's into 'configuration state' */
+       set_c0_mvpcontrol(MVPCONTROL_VPC);
+
+       val = read_c0_mvpconf0();
+
+       /* we'll always have more TC's than VPE's, so loop setting everything
+          to a sensible state */
+       for (i = 0, num = 0; i <= ((val & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT); i++) {
+               settc(i);
+
+               /* VPE's */
+               if (i <= ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)) {
+
+                       /* deactivate all but vpe0 */
+                       if (i != 0) {
+                               unsigned long tmp = read_vpe_c0_vpeconf0();
+
+                               tmp &= ~VPECONF0_VPA;
+
+                               /* master VPE */
+                               tmp |= VPECONF0_MVP;
+                               write_vpe_c0_vpeconf0(tmp);
+
+                               /* Record this as available CPU */
+                               cpu_set(i, phys_cpu_present_map);
+                               __cpu_number_map[i]     = ++num;
+                               __cpu_logical_map[num]  = i;
+                       }
+
+                       /* disable multi-threading with TC's */
+                       write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
+
+                       if (i != 0) {
+                               write_vpe_c0_status((read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0);
+                               write_vpe_c0_cause(read_vpe_c0_cause() & ~CAUSEF_IP);
+
+                               /* set config to be the same as vpe0, particularly kseg0 coherency alg */
+                               write_vpe_c0_config( read_c0_config());
+
+                               /* Propagate Config7 */
+                               write_vpe_c0_config7(read_c0_config7());
+                       }
+
+               }
+
+               /* TC's */
+
+               if (i != 0) {
+                       unsigned long tmp;
+
+                       /* bind a TC to each VPE, May as well put all excess TC's
+                          on the last VPE */
+                       if ( i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1) )
+                               write_tc_c0_tcbind(read_tc_c0_tcbind() | ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) );
+                       else {
+                               write_tc_c0_tcbind( read_tc_c0_tcbind() | i);
+
+                               /* and set XTC */
+                               write_vpe_c0_vpeconf0( read_vpe_c0_vpeconf0() | (i << VPECONF0_XTC_SHIFT));
+                       }
+
+                       tmp = read_tc_c0_tcstatus();
+
+                       /* mark not allocated and not dynamically allocatable */
+                       tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
+                       tmp |= TCSTATUS_IXMT;           /* interrupt exempt */
+                       write_tc_c0_tcstatus(tmp);
+
+                       write_tc_c0_tchalt(TCHALT_H);
+               }
+       }
+
+       /* Release config state */
+       clear_c0_mvpcontrol(MVPCONTROL_VPC);
+
+       /* We'll wait until starting the secondaries before starting MVPE */
+
+       printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
+
+       /* set up ipi interrupts */
+       if (cpu_has_vint) {
+               set_vi_handler (MIPS_CPU_IPI_RESCHED_IRQ, ipi_resched_dispatch);
+               set_vi_handler (MIPS_CPU_IPI_CALL_IRQ, ipi_call_dispatch);
+       }
+}
+
+void __init plat_prepare_cpus(unsigned int max_cpus)
+{
+       cpu_ipi_resched_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_RESCHED_IRQ;
+       cpu_ipi_call_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_CALL_IRQ;
+
+       setup_irq(cpu_ipi_resched_irq, &irq_resched);
+       setup_irq(cpu_ipi_call_irq, &irq_call);
+
+       /* need to mark IPI's as IRQ_PER_CPU */
+       irq_desc[cpu_ipi_resched_irq].status |= IRQ_PER_CPU;
+       irq_desc[cpu_ipi_call_irq].status |= IRQ_PER_CPU;
+}
+
+/*
+ * Setup the PC, SP, and GP of a secondary processor and start it
+ * running!
+ * smp_bootstrap is the place to resume from
+ * __KSTK_TOS(idle) is apparently the stack pointer
+ * (unsigned long)idle->thread_info the gp
+ * assumes a 1:1 mapping of TC => VPE
+ */
+void prom_boot_secondary(int cpu, struct task_struct *idle)
+{
+       struct thread_info *gp = task_thread_info(idle);
+       dvpe();
+       set_c0_mvpcontrol(MVPCONTROL_VPC);
+
+       settc(cpu);
+
+       /* restart */
+       write_tc_c0_tcrestart((unsigned long)&smp_bootstrap);
+
+       /* enable the tc this vpe/cpu will be running */
+       write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A);
+
+       write_tc_c0_tchalt(0);
+
+       /* enable the VPE */
+       write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
+
+       /* stack pointer */
+       write_tc_gpr_sp( __KSTK_TOS(idle));
+
+       /* global pointer */
+       write_tc_gpr_gp((unsigned long)gp);
+
+       flush_icache_range((unsigned long)gp, (unsigned long)(gp + 1));
+
+       /* finally out of configuration and into chaos */
+       clear_c0_mvpcontrol(MVPCONTROL_VPC);
+
+       evpe(EVPE_ENABLE);
+}
+
+void prom_init_secondary(void)
+{
+       write_c0_status((read_c0_status() & ~ST0_IM ) |
+                       (STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP7));
+}
+
+void prom_smp_finish(void)
+{
+       write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ));
+
+       local_irq_enable();
+}
+
+void prom_cpus_done(void)
+{
+}
+
+void core_send_ipi(int cpu, unsigned int action)
+{
+       int i;
+       unsigned long flags;
+       int vpflags;
+
+       local_irq_save (flags);
+
+       vpflags = dvpe();       /* cant access the other CPU's registers whilst MVPE enabled */
+
+       switch (action) {
+       case SMP_CALL_FUNCTION:
+               i = C_SW1;
+               break;
+
+       case SMP_RESCHEDULE_YOURSELF:
+       default:
+               i = C_SW0;
+               break;
+       }
+
+       /* 1:1 mapping of vpe and tc... */
+       settc(cpu);
+       write_vpe_c0_cause(read_vpe_c0_cause() | i);
+       evpe(vpflags);
+
+       local_irq_restore(flags);
+}
diff --git a/arch/mips/mips-boards/sim/cmdline.c b/arch/mips/mips-boards/sim/cmdline.c
new file mode 100644 (file)
index 0000000..fef9fbd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Kernel command line creation using the prom monitor (YAMON) argc/argv.
+ */
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+
+extern int prom_argc;
+extern int *_prom_argv;
+
+/*
+ * YAMON (32-bit PROM) pass arguments and environment as 32-bit pointer.
+ * This macro take care of sign extension.
+ */
+#define prom_argv(index) ((char *)(((int *)(int)_prom_argv)[(index)]))
+
+char arcs_cmdline[CL_SIZE];
+
+char * __init prom_getcmdline(void)
+{
+       return &(arcs_cmdline[0]);
+}
+
+
+void  __init prom_init_cmdline(void)
+{
+       char *cp;
+       int actr;
+
+       actr = 1; /* Always ignore argv[0] */
+
+       cp = &(arcs_cmdline[0]);
+       while(actr < prom_argc) {
+               strcpy(cp, prom_argv(actr));
+               cp += strlen(prom_argv(actr));
+               *cp++ = ' ';
+               actr++;
+       }
+       if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */
+               --cp;
+       *cp = '\0';
+}
diff --git a/arch/mips/philips/pnx8550/common/mipsIRQ.S b/arch/mips/philips/pnx8550/common/mipsIRQ.S
new file mode 100644 (file)
index 0000000..338bffd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2002 Philips, Inc. All rights.
+ * Copyright (c) 2002 Red Hat, Inc. All rights.
+ *
+ * This software may be freely redistributed under the terms of the
+ * GNU General Public License.
+ *
+ * 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.
+ *
+ * Based upon arch/mips/galileo-boards/ev64240/int-handler.S
+ *
+ */
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+/*
+ * cp0_irqdispatch
+ *
+ *    Code to handle in-core interrupt exception.
+ */
+
+               .align  5
+               .set    reorder
+               .set    noat
+               NESTED(cp0_irqdispatch, PT_SIZE, sp)
+               SAVE_ALL
+               CLI
+               .set    at
+               mfc0    t0,CP0_CAUSE
+               mfc0    t2,CP0_STATUS
+
+               and     t0,t2
+
+               andi    t1,t0,STATUSF_IP2 /* int0 hardware line */
+               bnez    t1,ll_hw0_irq
+               nop
+
+               andi    t1,t0,STATUSF_IP7 /* int5 hardware line */
+               bnez    t1,ll_timer_irq
+               nop
+
+               /* wrong alarm or masked ... */
+
+               j       spurious_interrupt
+               nop
+               END(cp0_irqdispatch)
+
+               .align  5
+               .set    reorder
+ll_hw0_irq:
+               li      a0,2
+               move    a1,sp
+               jal     hw0_irqdispatch
+               nop
+               j       ret_from_irq
+               nop
+
+               .align  5
+               .set    reorder
+ll_timer_irq:
+               mfc0    t3,CP0_CONFIG,7
+               andi    t4,t3,0x01c0
+               beqz    t4,ll_timer_out
+               nop
+               li      a0,7
+               move    a1,sp
+               jal     timer_irqdispatch
+               nop
+
+ll_timer_out:  j       ret_from_irq
+               nop
diff --git a/arch/mips/qemu/q-int.S b/arch/mips/qemu/q-int.S
new file mode 100644 (file)
index 0000000..6e3dfe5
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Qemu interrupt handler code.
+ *
+ * Copyright (C) 2005 by Ralf Baechle
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+       .align  5
+       NESTED(qemu_handle_int, PT_SIZE, sp)
+       SAVE_ALL
+       CLI
+       move    a0, sp
+       PTR_LA  ra, ret_from_irq
+       j       do_qemu_int
+       END(qemu_handle_int)
diff --git a/arch/mips/sibyte/bcm1480/irq_handler.S b/arch/mips/sibyte/bcm1480/irq_handler.S
new file mode 100644 (file)
index 0000000..408db88
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2000,2001,2002,2003,2004 Broadcom Corporation
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * bcm1480_irq_handler() is the routine that is actually called when an
+ * interrupt occurs.  It is installed as the exception vector handler in
+ * init_IRQ() in arch/mips/sibyte/bcm1480/irq.c
+ *
+ * In the handle we figure out which interrupts need handling, and use that
+ * to call the dispatcher, which will take care of actually calling
+ * registered handlers
+ *
+ * Note that we take care of all raised interrupts in one go at the handler.
+ * This is more BSDish than the Indy code, and also, IMHO, more sane.
+ */
+#include <linux/config.h>
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/sibyte/sb1250_defs.h>
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_int.h>
+
+/*
+ * What a pain. We have to be really careful saving the upper 32 bits of any
+ * register across function calls if we don't want them trashed--since were
+ * running in -o32, the calling routing never saves the full 64 bits of a
+ * register across a function call.  Being the interrupt handler, we're
+ * guaranteed that interrupts are disabled during this code so we don't have
+ * to worry about random interrupts blasting the high 32 bits.
+ */
+
+       .text
+       .set    push
+       .set    noreorder
+       .set    noat
+       .set    mips64
+       #.set   mips4
+       .align  5
+       NESTED(bcm1480_irq_handler, PT_SIZE, sp)
+       SAVE_ALL
+       CLI
+
+#ifdef CONFIG_SIBYTE_BCM1480_PROF
+       /* Set compare to count to silence count/compare timer interrupts */
+       mfc0    t1, CP0_COUNT
+       mtc0    t1, CP0_COMPARE /* pause to clear IP[7] bit of cause ? */
+#endif
+       /* Read cause */
+       mfc0    s0, CP0_CAUSE
+
+#ifdef CONFIG_SIBYTE_BCM1480_PROF
+       /* Cpu performance counter interrupt is routed to IP[7] */
+       andi    t1, s0, CAUSEF_IP7
+       beqz    t1, 0f
+        srl    t1, s0, (CAUSEB_BD-2)   /* Shift BD bit to bit 2 */
+       and     t1, t1, 0x4             /* mask to get just BD bit */
+#ifdef CONFIG_MIPS64
+       dmfc0   a0, CP0_EPC
+       daddu   a0, a0, t1              /* a0 = EPC + (BD ? 4 : 0) */
+#else
+       mfc0    a0, CP0_EPC
+       addu    a0, a0, t1              /* a0 = EPC + (BD ? 4 : 0) */
+#endif
+       jal     sbprof_cpu_intr
+        nop
+       j       ret_from_irq
+        nop
+0:
+#endif
+
+       /* Timer interrupt is routed to IP[4] */
+       andi    t1, s0, CAUSEF_IP4
+       beqz    t1, 1f
+        nop
+       jal     bcm1480_timer_interrupt
+        move   a0, sp                  /* Pass the registers along */
+       j       ret_from_irq
+        nop                            /* delay slot  */
+1:
+
+#ifdef CONFIG_SMP
+       /* Mailbox interrupt is routed to IP[3] */
+       andi     t1, s0, CAUSEF_IP3
+       beqz     t1, 2f
+        nop
+       jal      bcm1480_mailbox_interrupt
+        move    a0, sp
+       j        ret_from_irq
+        nop                            /* delay slot  */
+2:
+#endif
+
+#ifdef CONFIG_KGDB
+       /* KGDB (uart 1) interrupt is routed to IP[6] */
+       andi     t1, s0, CAUSEF_IP6
+       beqz     t1, 3f
+        nop                            /* delay slot  */
+       jal      bcm1480_kgdb_interrupt
+        move    a0, sp
+       j        ret_from_irq
+        nop                            /* delay slot  */
+3:
+#endif
+
+       and      t1, s0, CAUSEF_IP2
+       beqz     t1, 9f
+        nop
+
+       /*
+        * Default...we've hit an IP[2] interrupt, which means we've got
+        * to check the 1480 interrupt registers to figure out what to do
+        * Need to detect which CPU we're on, now that smp_affinity is
+        * supported.
+        */
+       PTR_LA   v0, CKSEG1 + A_BCM1480_IMR_CPU0_BASE
+#ifdef CONFIG_SMP
+       lw       t1, TI_CPU($28)
+       sll      t1, t1, BCM1480_IMR_REGISTER_SPACING_SHIFT
+       addu     v0, v0, t1
+#endif
+
+       /* Read IP[2] status (get both high and low halves of status) */
+       ld       s0, R_BCM1480_IMR_INTERRUPT_STATUS_BASE_H(v0)
+       ld       s1, R_BCM1480_IMR_INTERRUPT_STATUS_BASE_L(v0)
+
+       move     s2, zero       /* intr number  */
+       li       s3, 64
+
+       beqz     s0, 9f         /* No interrupts.  Return.  */
+        move    a1, sp
+
+       xori     s4, s0, 1      /* if s0 (_H) == 1, it's a low intr, so...  */
+       movz     s2, s3, s4     /* start the intr number at 64, and  */
+       movz     s0, s1, s4     /* look at the low status value.  */
+
+       dclz     s1, s0         /* Find the next interrupt.  */
+       dsubu    a0, zero, s1
+       daddiu   a0, a0, 63
+       jal      do_IRQ
+        daddu   a0, a0, s2
+
+9:     j        ret_from_irq
+        nop
+
+       .set pop
+       END(bcm1480_irq_handler)
diff --git a/arch/mips/tx4938/common/irq_handler.S b/arch/mips/tx4938/common/irq_handler.S
new file mode 100644 (file)
index 0000000..1b2f72b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * linux/arch/mips/tx4938/common/handler.S
+ *
+ * Primary interrupt handler for tx4938 based systems
+ * Copyright (C) 2000-2001 Toshiba Corporation
+ *
+ * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
+ */
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/tx4938/rbtx4938.h>
+
+
+               .align  5
+               NESTED(tx4938_irq_handler, PT_SIZE, sp)
+               SAVE_ALL
+               CLI
+               .set    at
+
+               mfc0    t0, CP0_CAUSE
+               mfc0    t1, CP0_STATUS
+               and     t0, t1
+
+               andi    t1, t0, STATUSF_IP7     /* cpu timer */
+               bnez    t1, ll_ip7
+
+               /* IP6..IP3 multiplexed -- do not use */
+
+               andi    t1, t0, STATUSF_IP2     /* tx4938 pic */
+               bnez    t1, ll_ip2
+
+               andi    t1, t0, STATUSF_IP1     /* user line 1 */
+               bnez    t1, ll_ip1
+
+               andi    t1, t0, STATUSF_IP0     /* user line 0 */
+               bnez    t1, ll_ip0
+
+               .set    reorder
+
+               nop
+               END(tx4938_irq_handler)
+
+               .align  5
+
+
+ll_ip7:
+               li      a0, TX4938_IRQ_CPU_TIMER
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+
+ll_ip2:
+               jal     tx4938_irq_nested
+               nop
+               beqz    v0, goto_spurious_interrupt
+               nop
+               move    a0, v0
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+goto_spurious_interrupt:
+               j       ret_from_irq
+
+ll_ip1:
+               li      a0, TX4938_IRQ_USER1
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_ip0:
+               li      a0, TX4938_IRQ_USER0
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
diff --git a/arch/powerpc/kernel/idle_64.c b/arch/powerpc/kernel/idle_64.c
new file mode 100644 (file)
index 0000000..b879d30
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Idle daemon for PowerPC.  Idle daemon will handle any action
+ * that needs to be taken when the system becomes idle.
+ *
+ * Originally Written by Cort Dougan (cort@cs.nmt.edu)
+ *
+ * iSeries supported added by Mike Corrigan <mikejc@us.ibm.com>
+ *
+ * Additional shared processor, SMT, and firmware support
+ *    Copyright (c) 2003 Dave Engebretsen <engebret@us.ibm.com>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/sysctl.h>
+
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/smp.h>
+
+extern void power4_idle(void);
+
+void default_idle(void)
+{
+       unsigned int cpu = smp_processor_id();
+       set_thread_flag(TIF_POLLING_NRFLAG);
+
+       while (1) {
+               if (!need_resched()) {
+                       while (!need_resched() && !cpu_is_offline(cpu)) {
+                               ppc64_runlatch_off();
+
+                               /*
+                                * Go into low thread priority and possibly
+                                * low power mode.
+                                */
+                               HMT_low();
+                               HMT_very_low();
+                       }
+
+                       HMT_medium();
+               }
+
+               ppc64_runlatch_on();
+               preempt_enable_no_resched();
+               schedule();
+               preempt_disable();
+               if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
+                       cpu_die();
+       }
+}
+
+void native_idle(void)
+{
+       while (1) {
+               ppc64_runlatch_off();
+
+               if (!need_resched())
+                       power4_idle();
+
+               if (need_resched()) {
+                       ppc64_runlatch_on();
+                       preempt_enable_no_resched();
+                       schedule();
+                       preempt_disable();
+               }
+
+               if (cpu_is_offline(smp_processor_id()) &&
+                   system_state == SYSTEM_RUNNING)
+                       cpu_die();
+       }
+}
+
+void cpu_idle(void)
+{
+       BUG_ON(NULL == ppc_md.idle_loop);
+       ppc_md.idle_loop();
+}
+
+int powersave_nap;
+
+#ifdef CONFIG_SYSCTL
+/*
+ * Register the sysctl to set/clear powersave_nap.
+ */
+static ctl_table powersave_nap_ctl_table[]={
+       {
+               .ctl_name       = KERN_PPC_POWERSAVE_NAP,
+               .procname       = "powersave-nap",
+               .data           = &powersave_nap,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec,
+       },
+       { 0, },
+};
+static ctl_table powersave_nap_sysctl_root[] = {
+       { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, },
+       { 0,},
+};
+
+static int __init
+register_powersave_nap_sysctl(void)
+{
+       register_sysctl_table(powersave_nap_sysctl_root, 0);
+
+       return 0;
+}
+__initcall(register_powersave_nap_sysctl);
+#endif
diff --git a/arch/ppc/platforms/chrp_nvram.c b/arch/ppc/platforms/chrp_nvram.c
new file mode 100644 (file)
index 0000000..465ba9b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  c 2001 PPC 64 Team, IBM Corp
+ *
+ *      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.
+ *
+ * /dev/nvram driver for PPC
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+
+static unsigned int nvram_size;
+static unsigned char nvram_buf[4];
+static DEFINE_SPINLOCK(nvram_lock);
+
+static unsigned char chrp_nvram_read(int addr)
+{
+       unsigned long done, flags;
+       unsigned char ret;
+
+       if (addr >= nvram_size) {
+               printk(KERN_DEBUG "%s: read addr %d > nvram_size %u\n",
+                      current->comm, addr, nvram_size);
+               return 0xff;
+       }
+       spin_lock_irqsave(&nvram_lock, flags);
+       if ((call_rtas("nvram-fetch", 3, 2, &done, addr, __pa(nvram_buf), 1) != 0) || 1 != done)
+               ret = 0xff;
+       else
+               ret = nvram_buf[0];
+       spin_unlock_irqrestore(&nvram_lock, flags);
+
+       return ret;
+}
+
+static void chrp_nvram_write(int addr, unsigned char val)
+{
+       unsigned long done, flags;
+
+       if (addr >= nvram_size) {
+               printk(KERN_DEBUG "%s: write addr %d > nvram_size %u\n",
+                      current->comm, addr, nvram_size);
+               return;
+       }
+       spin_lock_irqsave(&nvram_lock, flags);
+       nvram_buf[0] = val;
+       if ((call_rtas("nvram-store", 3, 2, &done, addr, __pa(nvram_buf), 1) != 0) || 1 != done)
+               printk(KERN_DEBUG "rtas IO error storing 0x%02x at %d", val, addr);
+       spin_unlock_irqrestore(&nvram_lock, flags);
+}
+
+void __init chrp_nvram_init(void)
+{
+       struct device_node *nvram;
+       unsigned int *nbytes_p, proplen;
+
+       nvram = of_find_node_by_type(NULL, "nvram");
+       if (nvram == NULL)
+               return;
+
+       nbytes_p = (unsigned int *)get_property(nvram, "#bytes", &proplen);
+       if (nbytes_p == NULL || proplen != sizeof(unsigned int))
+               return;
+
+       nvram_size = *nbytes_p;
+
+       printk(KERN_INFO "CHRP nvram contains %u bytes\n", nvram_size);
+       of_node_put(nvram);
+
+       ppc_md.nvram_read_val = chrp_nvram_read;
+       ppc_md.nvram_write_val = chrp_nvram_write;
+
+       return;
+}
diff --git a/arch/um/scripts/Makefile.unmap b/arch/um/scripts/Makefile.unmap
new file mode 100644 (file)
index 0000000..b216518
--- /dev/null
@@ -0,0 +1,22 @@
+clean-files += unmap_tmp.o unmap_fin.o unmap.o
+
+ifdef CONFIG_MODE_TT
+
+#Always build unmap_fin.o
+extra-y += unmap_fin.o
+#Do dependency tracking for unmap.o (it will be always built, but won't get the tracking unless we use this).
+targets += unmap.o
+
+#XXX: partially copied from arch/um/scripts/Makefile.rules
+$(obj)/unmap.o: _c_flags = $(call unprofile,$(CFLAGS))
+
+quiet_cmd_wrapld = LD      $@
+define cmd_wrapld
+       $(LD) $(LDFLAGS) -r -o $(obj)/unmap_tmp.o $< ; \
+       $(OBJCOPY) $(UML_OBJCOPYFLAGS) $(obj)/unmap_tmp.o $@ -G switcheroo
+endef
+
+$(obj)/unmap_fin.o : $(obj)/unmap.o FORCE
+       $(call if_changed,wrapld)
+
+endif
diff --git a/drivers/i2c/chips/x1205.c b/drivers/i2c/chips/x1205.c
new file mode 100644 (file)
index 0000000..245fffa
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ *  x1205.c - An i2c driver for the Xicor X1205 RTC
+ *  Copyright 2004 Karen Spearel
+ *  Copyright 2005 Alessandro Zummo
+ *
+ *  please send all reports to:
+ *     kas11 at tampabay dot rr dot com
+ *      a dot zummo at towertech dot it
+ *
+ *  based on the other drivers in this same directory.
+ *
+ *  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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/list.h>
+
+#include <linux/x1205.h>
+
+#define DRV_VERSION "0.9.9"
+
+/* Addresses to scan: none. This chip is located at
+ * 0x6f and uses a two bytes register addressing.
+ * Two bytes need to be written to read a single register,
+ * while most other chips just require one and take the second
+ * one as the data to be written. To prevent corrupting
+ * unknown chips, the user must explicitely set the probe parameter.
+ */
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
+I2C_CLIENT_MODULE_PARM(hctosys,
+       "Set the system time from the hardware clock upon initialization");
+
+/* offsets into CCR area */
+
+#define CCR_SEC                        0
+#define CCR_MIN                        1
+#define CCR_HOUR               2
+#define CCR_MDAY               3
+#define CCR_MONTH              4
+#define CCR_YEAR               5
+#define CCR_WDAY               6
+#define CCR_Y2K                        7
+
+#define X1205_REG_SR           0x3F    /* status register */
+#define X1205_REG_Y2K          0x37
+#define X1205_REG_DW           0x36
+#define X1205_REG_YR           0x35
+#define X1205_REG_MO           0x34
+#define X1205_REG_DT           0x33
+#define X1205_REG_HR           0x32
+#define X1205_REG_MN           0x31
+#define X1205_REG_SC           0x30
+#define X1205_REG_DTR          0x13
+#define X1205_REG_ATR          0x12
+#define X1205_REG_INT          0x11
+#define X1205_REG_0            0x10
+#define X1205_REG_Y2K1         0x0F
+#define X1205_REG_DWA1         0x0E
+#define X1205_REG_YRA1         0x0D
+#define X1205_REG_MOA1         0x0C
+#define X1205_REG_DTA1         0x0B
+#define X1205_REG_HRA1         0x0A
+#define X1205_REG_MNA1         0x09
+#define X1205_REG_SCA1         0x08
+#define X1205_REG_Y2K0         0x07
+#define X1205_REG_DWA0         0x06
+#define X1205_REG_YRA0         0x05
+#define X1205_REG_MOA0         0x04
+#define X1205_REG_DTA0         0x03
+#define X1205_REG_HRA0         0x02
+#define X1205_REG_MNA0         0x01
+#define X1205_REG_SCA0         0x00
+
+#define X1205_CCR_BASE         0x30    /* Base address of CCR */
+#define X1205_ALM0_BASE                0x00    /* Base address of ALARM0 */
+
+#define X1205_SR_RTCF          0x01    /* Clock failure */
+#define X1205_SR_WEL           0x02    /* Write Enable Latch */
+#define X1205_SR_RWEL          0x04    /* Register Write Enable */
+
+#define X1205_DTR_DTR0         0x01
+#define X1205_DTR_DTR1         0x02
+#define X1205_DTR_DTR2         0x04
+
+#define X1205_HR_MIL           0x80    /* Set in ccr.hour for 24 hr mode */
+
+/* Prototypes */
+static int x1205_attach(struct i2c_adapter *adapter);
+static int x1205_detach(struct i2c_client *client);
+static int x1205_probe(struct i2c_adapter *adapter, int address, int kind);
+static int x1205_command(struct i2c_client *client, unsigned int cmd,
+       void *arg);
+
+static struct i2c_driver x1205_driver = {
+       .driver = {
+               .name   = "x1205",
+       },
+       .attach_adapter = &x1205_attach,
+       .detach_client  = &x1205_detach,
+};
+
+struct x1205_data {
+       struct i2c_client client;
+       struct list_head list;
+       unsigned int epoch;
+};
+
+static const unsigned char days_in_mo[] =
+       { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static LIST_HEAD(x1205_clients);
+
+/* Workaround until the I2C subsytem will allow to send
+ * commands to a specific client. This function will send the command
+ * to the first client.
+ */
+int x1205_do_command(unsigned int cmd, void *arg)
+{
+       struct list_head *walk;
+       struct list_head *tmp;
+       struct x1205_data *data;
+
+       list_for_each_safe(walk, tmp, &x1205_clients) {
+               data = list_entry(walk, struct x1205_data, list);
+               return x1205_command(&data->client, cmd, arg);
+       }
+
+       return -ENODEV;
+}
+
+#define is_leap(year) \
+       ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+/* make sure the rtc_time values are in bounds */
+static int x1205_validate_tm(struct rtc_time *tm)
+{
+       int year = tm->tm_year + 1900;
+
+       if ((tm->tm_year < 70) || (tm->tm_year > 255))
+               return -EINVAL;
+
+       if ((tm->tm_mon > 11) || (tm->tm_mday == 0))
+               return -EINVAL;
+
+       if (tm->tm_mday > days_in_mo[tm->tm_mon]
+               + ((tm->tm_mon == 1) && is_leap(year)))
+               return -EINVAL;
+
+       if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60))
+               return -EINVAL;
+
+       return 0;
+}
+
+/*
+ * In the routines that deal directly with the x1205 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+ * Epoch is initialized as 2000. Time is set to UTC.
+ */
+static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
+                               u8 reg_base)
+{
+       unsigned char dt_addr[2] = { 0, reg_base };
+       static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
+
+       unsigned char buf[8], sr;
+
+       struct i2c_msg msgs[] = {
+               { client->addr, 0, 2, sr_addr },        /* setup read ptr */
+               { client->addr, I2C_M_RD, 1, &sr },     /* read status */
+               { client->addr, 0, 2, dt_addr },        /* setup read ptr */
+               { client->addr, I2C_M_RD, 8, buf },     /* read date */
+       };
+
+       struct x1205_data *data = i2c_get_clientdata(client);
+
+       /* read status register */
+       if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+               dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+               return -EIO;
+       }
+
+       /* check for battery failure */
+       if (sr & X1205_SR_RTCF) {
+               dev_warn(&client->dev,
+                       "Clock had a power failure, you must set the date.\n");
+               return -EINVAL;
+       }
+
+       /* read date registers */
+       if ((i2c_transfer(client->adapter, &msgs[2], 2)) != 2) {
+               dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+               return -EIO;
+       }
+
+       dev_dbg(&client->dev,
+               "%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
+               "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
+               __FUNCTION__,
+               buf[0], buf[1], buf[2], buf[3],
+               buf[4], buf[5], buf[6], buf[7]);
+
+       tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
+       tm->tm_min = BCD2BIN(buf[CCR_MIN]);
+       tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
+       tm->tm_mday = BCD2BIN(buf[CCR_MDAY]);
+       tm->tm_mon = BCD2BIN(buf[CCR_MONTH]);
+       data->epoch = BCD2BIN(buf[CCR_Y2K]) * 100;
+       tm->tm_year = BCD2BIN(buf[CCR_YEAR]) + data->epoch - 1900;
+       tm->tm_wday = buf[CCR_WDAY];
+
+       dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+               "mday=%d, mon=%d, year=%d, wday=%d\n",
+               __FUNCTION__,
+               tm->tm_sec, tm->tm_min, tm->tm_hour,
+               tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+       return 0;
+}
+
+static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
+                               int datetoo, u8 reg_base)
+{
+       int i, err, xfer;
+
+       unsigned char buf[8];
+
+       static const unsigned char wel[3] = { 0, X1205_REG_SR,
+                                               X1205_SR_WEL };
+
+       static const unsigned char rwel[3] = { 0, X1205_REG_SR,
+                                               X1205_SR_WEL | X1205_SR_RWEL };
+
+       static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
+
+       struct x1205_data *data = i2c_get_clientdata(client);
+
+       /* check if all values in the tm struct are correct */
+       if ((err = x1205_validate_tm(tm)) < 0)
+               return err;
+
+       dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+               "mday=%d, mon=%d, year=%d, wday=%d\n",
+               __FUNCTION__,
+               tm->tm_sec, tm->tm_min, tm->tm_hour,
+               tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+       buf[CCR_SEC] = BIN2BCD(tm->tm_sec);
+       buf[CCR_MIN] = BIN2BCD(tm->tm_min);
+
+       /* set hour and 24hr bit */
+       buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL;
+
+       /* should we also set the date? */
+       if (datetoo) {
+               buf[CCR_MDAY] = BIN2BCD(tm->tm_mday);
+
+               /* month, 0 - 11 */
+               buf[CCR_MONTH] = BIN2BCD(tm->tm_mon);
+
+               /* year, since 1900 */
+               buf[CCR_YEAR] = BIN2BCD(tm->tm_year + 1900 - data->epoch);
+               buf[CCR_WDAY] = tm->tm_wday & 0x07;
+               buf[CCR_Y2K] = BIN2BCD(data->epoch / 100);
+       }
+
+       /* this sequence is required to unlock the chip */
+       xfer = i2c_master_send(client, wel, 3);
+       if (xfer != 3) {
+               dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer);
+               return -EIO;
+       }
+
+       xfer = i2c_master_send(client, rwel, 3);
+       if (xfer != 3) {
+               dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer);
+               return -EIO;
+       }
+
+       /* write register's data */
+       for (i = 0; i < (datetoo ? 8 : 3); i++) {
+               unsigned char rdata[3] = { 0, reg_base + i, buf[i] };
+
+               xfer = i2c_master_send(client, rdata, 3);
+               if (xfer != 3) {
+                       dev_err(&client->dev,
+                               "%s: xfer=%d addr=%02x, data=%02x\n",
+                               __FUNCTION__,
+                                xfer, rdata[1], rdata[2]);
+                       return -EIO;
+               }
+       };
+
+       /* disable further writes */
+       xfer = i2c_master_send(client, diswe, 3);
+       if (xfer != 3) {
+               dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int x1205_get_dtrim(struct i2c_client *client, int *trim)
+{
+       unsigned char dtr;
+       static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR };
+
+       struct i2c_msg msgs[] = {
+               { client->addr, 0, 2, dtr_addr },       /* setup read ptr */
+               { client->addr, I2C_M_RD, 1, &dtr },    /* read dtr */
+       };
+
+       /* read dtr register */
+       if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+               dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+               return -EIO;
+       }
+
+       dev_dbg(&client->dev, "%s: raw dtr=%x\n", __FUNCTION__, dtr);
+
+       *trim = 0;
+
+       if (dtr & X1205_DTR_DTR0)
+               *trim += 20;
+
+       if (dtr & X1205_DTR_DTR1)
+               *trim += 10;
+
+       if (dtr & X1205_DTR_DTR2)
+               *trim = -*trim;
+
+       return 0;
+}
+
+static int x1205_get_atrim(struct i2c_client *client, int *trim)
+{
+       s8 atr;
+       static unsigned char atr_addr[2] = { 0, X1205_REG_ATR };
+
+       struct i2c_msg msgs[] = {
+               { client->addr, 0, 2, atr_addr },       /* setup read ptr */
+               { client->addr, I2C_M_RD, 1, &atr },    /* read atr */
+       };
+
+       /* read atr register */
+       if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+               dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+               return -EIO;
+       }
+
+       dev_dbg(&client->dev, "%s: raw atr=%x\n", __FUNCTION__, atr);
+
+       /* atr is a two's complement value on 6 bits,
+        * perform sign extension. The formula is
+        * Catr = (atr * 0.25pF) + 11.00pF.
+        */
+       if (atr & 0x20)
+               atr |= 0xC0;
+
+       dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr);
+
+       *trim = (atr * 250) + 11000;
+
+       dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim);
+
+       return 0;
+}
+
+static int x1205_hctosys(struct i2c_client *client)
+{
+       int err;
+
+       struct rtc_time tm;
+       struct timespec tv;
+
+       err = x1205_command(client, X1205_CMD_GETDATETIME, &tm);
+
+       if (err) {
+               dev_err(&client->dev,
+                       "Unable to set the system clock\n");
+               return err;
+       }
+
+       /* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
+        * whether it stores the most close value or the value with partial
+        * seconds truncated. However, it is important that we use it to store
+        * the truncated value. This is because otherwise it is necessary,
+        * in an rtc sync function, to read both xtime.tv_sec and
+        * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
+        * of >32bits is not possible. So storing the most close value would
+        * slow down the sync API. So here we have the truncated value and
+        * the best guess is to add 0.5s.
+        */
+
+       tv.tv_nsec = NSEC_PER_SEC >> 1;
+
+       /* WARNING: this is not the C library 'mktime' call, it is a built in
+        * inline function from include/linux/time.h.  It expects (requires)
+        * the month to be in the range 1-12
+        */
+
+       tv.tv_sec  = mktime(tm.tm_year + 1900, tm.tm_mon + 1,
+                               tm.tm_mday, tm.tm_hour,
+                               tm.tm_min, tm.tm_sec);
+
+       do_settimeofday(&tv);
+
+       dev_info(&client->dev,
+               "setting the system clock to %d-%d-%d %d:%d:%d\n",
+               tm.tm_year + 1900, tm.tm_mon + 1,
+               tm.tm_mday, tm.tm_hour, tm.tm_min,
+               tm.tm_sec);
+
+       return 0;
+}
+
+struct x1205_limit
+{
+       unsigned char reg;
+       unsigned char mask;
+       unsigned char min;
+       unsigned char max;
+};
+
+static int x1205_validate_client(struct i2c_client *client)
+{
+       int i, xfer;
+
+       /* Probe array. We will read the register at the specified
+        * address and check if the given bits are zero.
+        */
+       static const unsigned char probe_zero_pattern[] = {
+               /* register, mask */
+               X1205_REG_SR,   0x18,
+               X1205_REG_DTR,  0xF8,
+               X1205_REG_ATR,  0xC0,
+               X1205_REG_INT,  0x18,
+               X1205_REG_0,    0xFF,
+       };
+
+       static const struct x1205_limit probe_limits_pattern[] = {
+               /* register, mask, min, max */
+               { X1205_REG_Y2K,        0xFF,   19,     20      },
+               { X1205_REG_DW,         0xFF,   0,      6       },
+               { X1205_REG_YR,         0xFF,   0,      99      },
+               { X1205_REG_MO,         0xFF,   0,      12      },
+               { X1205_REG_DT,         0xFF,   0,      31      },
+               { X1205_REG_HR,         0x7F,   0,      23      },
+               { X1205_REG_MN,         0xFF,   0,      59      },
+               { X1205_REG_SC,         0xFF,   0,      59      },
+               { X1205_REG_Y2K1,       0xFF,   19,     20      },
+               { X1205_REG_Y2K0,       0xFF,   19,     20      },
+       };
+
+       /* check that registers have bits a 0 where expected */
+       for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) {
+               unsigned char buf;
+
+               unsigned char addr[2] = { 0, probe_zero_pattern[i] };
+
+               struct i2c_msg msgs[2] = {
+                       { client->addr, 0, 2, addr },
+                       { client->addr, I2C_M_RD, 1, &buf },
+               };
+
+               xfer = i2c_transfer(client->adapter, msgs, 2);
+               if (xfer != 2) {
+                       dev_err(&client->adapter->dev,
+                               "%s: could not read register %x\n",
+                               __FUNCTION__, addr[1]);
+
+                       return -EIO;
+               }
+
+               if ((buf & probe_zero_pattern[i+1]) != 0) {
+                       dev_err(&client->adapter->dev,
+                               "%s: register=%02x, zero pattern=%d, value=%x\n",
+                               __FUNCTION__, addr[1], i, buf);
+
+                       return -ENODEV;
+               }
+       }
+
+       /* check limits (only registers with bcd values) */
+       for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
+               unsigned char reg, value;
+
+               unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };
+
+               struct i2c_msg msgs[2] = {
+                       { client->addr, 0, 2, addr },
+                       { client->addr, I2C_M_RD, 1, &reg },
+               };
+
+               xfer = i2c_transfer(client->adapter, msgs, 2);
+
+               if (xfer != 2) {
+                       dev_err(&client->adapter->dev,
+                               "%s: could not read register %x\n",
+                               __FUNCTION__, addr[1]);
+
+                       return -EIO;
+               }
+
+               value = BCD2BIN(reg & probe_limits_pattern[i].mask);
+
+               if (value > probe_limits_pattern[i].max ||
+                       value < probe_limits_pattern[i].min) {
+                       dev_dbg(&client->adapter->dev,
+                               "%s: register=%x, lim pattern=%d, value=%d\n",
+                               __FUNCTION__, addr[1], i, value);
+
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+static int x1205_attach(struct i2c_adapter *adapter)
+{
+       dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+
+       return i2c_probe(adapter, &addr_data, x1205_probe);
+}
+
+int x1205_direct_attach(int adapter_id,
+       struct i2c_client_address_data *address_data)
+{
+       int err;
+       struct i2c_adapter *adapter = i2c_get_adapter(adapter_id);
+
+       if (adapter) {
+               err = i2c_probe(adapter,
+                       address_data, x1205_probe);
+
+               i2c_put_adapter(adapter);
+
+               return err;
+       }
+
+       return -ENODEV;
+}
+
+static int x1205_probe(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct x1205_data *data;
+
+       int err = 0;
+
+       dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
+               err = -ENODEV;
+               goto exit;
+       }
+
+       if (!(data = kzalloc(sizeof(struct x1205_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* Initialize our structures */
+       data->epoch = 2000;
+
+       client = &data->client;
+       client->addr = address;
+       client->driver = &x1205_driver;
+       client->adapter = adapter;
+
+       strlcpy(client->name, "x1205", I2C_NAME_SIZE);
+
+       i2c_set_clientdata(client, data);
+
+       /* Verify the chip is really an X1205 */
+       if (kind < 0) {
+               if (x1205_validate_client(client) < 0) {
+                       err = -ENODEV;
+                       goto exit_kfree;
+               }
+       }
+
+       /* Inform the i2c layer */
+       if ((err = i2c_attach_client(client)))
+               goto exit_kfree;
+
+       list_add(&data->list, &x1205_clients);
+
+       dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+       /* If requested, set the system time */
+       if (hctosys)
+               x1205_hctosys(client);
+
+       return 0;
+
+exit_kfree:
+       kfree(data);
+
+exit:
+       return err;
+}
+
+static int x1205_detach(struct i2c_client *client)
+{
+       int err;
+       struct x1205_data *data = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "%s\n", __FUNCTION__);
+
+       if ((err = i2c_detach_client(client)))
+               return err;
+
+       list_del(&data->list);
+
+       kfree(data);
+
+       return 0;
+}
+
+static int x1205_command(struct i2c_client *client, unsigned int cmd,
+       void *param)
+{
+       if (param == NULL)
+               return -EINVAL;
+
+       if (!capable(CAP_SYS_TIME))
+               return -EACCES;
+
+       dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd);
+
+       switch (cmd) {
+       case X1205_CMD_GETDATETIME:
+               return x1205_get_datetime(client, param, X1205_CCR_BASE);
+
+       case X1205_CMD_SETTIME:
+               return x1205_set_datetime(client, param, 0,
+                               X1205_CCR_BASE);
+
+       case X1205_CMD_SETDATETIME:
+               return x1205_set_datetime(client, param, 1,
+                               X1205_CCR_BASE);
+
+       case X1205_CMD_GETALARM:
+               return x1205_get_datetime(client, param, X1205_ALM0_BASE);
+
+       case X1205_CMD_SETALARM:
+               return x1205_set_datetime(client, param, 1,
+                               X1205_ALM0_BASE);
+
+       case X1205_CMD_GETDTRIM:
+               return x1205_get_dtrim(client, param);
+
+       case X1205_CMD_GETATRIM:
+               return x1205_get_atrim(client, param);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int __init x1205_init(void)
+{
+       return i2c_add_driver(&x1205_driver);
+}
+
+static void __exit x1205_exit(void)
+{
+       i2c_del_driver(&x1205_driver);
+}
+
+MODULE_AUTHOR(
+       "Karen Spearel <kas11@tampabay.rr.com>, "
+       "Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Xicor X1205 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+EXPORT_SYMBOL_GPL(x1205_do_command);
+EXPORT_SYMBOL_GPL(x1205_direct_attach);
+
+module_init(x1205_init);
+module_exit(x1205_exit);
diff --git a/drivers/media/video/bttv-input.c b/drivers/media/video/bttv-input.c
new file mode 100644 (file)
index 0000000..221b36e
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ *
+ * Copyright (c) 2003 Gerd Knorr
+ * Copyright (c) 2003 Pavel Machek
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#include "bttv.h"
+#include "bttvp.h"
+
+/* ---------------------------------------------------------------------- */
+
+static IR_KEYTAB_TYPE ir_codes_avermedia[IR_KEYTAB_SIZE] = {
+       [ 34 ] = KEY_KP0,
+       [ 40 ] = KEY_KP1,
+       [ 24 ] = KEY_KP2,
+       [ 56 ] = KEY_KP3,
+       [ 36 ] = KEY_KP4,
+       [ 20 ] = KEY_KP5,
+       [ 52 ] = KEY_KP6,
+       [ 44 ] = KEY_KP7,
+       [ 28 ] = KEY_KP8,
+       [ 60 ] = KEY_KP9,
+
+       [ 48 ] = KEY_EJECTCD,     // Unmarked on my controller
+       [  0 ] = KEY_POWER,
+       [ 18 ] = BTN_LEFT,        // DISPLAY/L
+       [ 50 ] = BTN_RIGHT,       // LOOP/R
+       [ 10 ] = KEY_MUTE,
+       [ 38 ] = KEY_RECORD,
+       [ 22 ] = KEY_PAUSE,
+       [ 54 ] = KEY_STOP,
+       [ 30 ] = KEY_VOLUMEDOWN,
+       [ 62 ] = KEY_VOLUMEUP,
+
+       [ 32 ] = KEY_TUNER,       // TV/FM
+       [ 16 ] = KEY_CD,
+       [  8 ] = KEY_VIDEO,
+       [  4 ] = KEY_AUDIO,
+       [ 12 ] = KEY_ZOOM,        // full screen
+       [  2 ] = KEY_INFO,        // preview
+       [ 42 ] = KEY_SEARCH,      // autoscan
+       [ 26 ] = KEY_STOP,        // freeze
+       [ 58 ] = KEY_RECORD,      // capture
+       [  6 ] = KEY_PLAY,        // unmarked
+       [ 46 ] = KEY_RED,         // unmarked
+       [ 14 ] = KEY_GREEN,       // unmarked
+
+       [ 33 ] = KEY_YELLOW,      // unmarked
+       [ 17 ] = KEY_CHANNELDOWN,
+       [ 49 ] = KEY_CHANNELUP,
+       [  1 ] = KEY_BLUE,        // unmarked
+};
+
+/* Matt Jesson <dvb@jesson.eclipse.co.uk */
+static IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = {
+       [ 0x28 ] = KEY_KP0,         //'0' / 'enter'
+       [ 0x22 ] = KEY_KP1,         //'1'
+       [ 0x12 ] = KEY_KP2,         //'2' / 'up arrow'
+       [ 0x32 ] = KEY_KP3,         //'3'
+       [ 0x24 ] = KEY_KP4,         //'4' / 'left arrow'
+       [ 0x14 ] = KEY_KP5,         //'5'
+       [ 0x34 ] = KEY_KP6,         //'6' / 'right arrow'
+       [ 0x26 ] = KEY_KP7,         //'7'
+       [ 0x16 ] = KEY_KP8,         //'8' / 'down arrow'
+       [ 0x36 ] = KEY_KP9,         //'9'
+
+       [ 0x20 ] = KEY_LIST,        // 'source'
+       [ 0x10 ] = KEY_TEXT,        // 'teletext'
+       [ 0x00 ] = KEY_POWER,       // 'power'
+       [ 0x04 ] = KEY_AUDIO,       // 'audio'
+       [ 0x06 ] = KEY_ZOOM,        // 'full screen'
+       [ 0x18 ] = KEY_VIDEO,       // 'display'
+       [ 0x38 ] = KEY_SEARCH,      // 'loop'
+       [ 0x08 ] = KEY_INFO,        // 'preview'
+       [ 0x2a ] = KEY_REWIND,      // 'backward <<'
+       [ 0x1a ] = KEY_FASTFORWARD, // 'forward >>'
+       [ 0x3a ] = KEY_RECORD,      // 'capture'
+       [ 0x0a ] = KEY_MUTE,        // 'mute'
+       [ 0x2c ] = KEY_RECORD,      // 'record'
+       [ 0x1c ] = KEY_PAUSE,       // 'pause'
+       [ 0x3c ] = KEY_STOP,        // 'stop'
+       [ 0x0c ] = KEY_PLAY,        // 'play'
+       [ 0x2e ] = KEY_RED,         // 'red'
+       [ 0x01 ] = KEY_BLUE,        // 'blue' / 'cancel'
+       [ 0x0e ] = KEY_YELLOW,      // 'yellow' / 'ok'
+       [ 0x21 ] = KEY_GREEN,       // 'green'
+       [ 0x11 ] = KEY_CHANNELDOWN, // 'channel -'
+       [ 0x31 ] = KEY_CHANNELUP,   // 'channel +'
+       [ 0x1e ] = KEY_VOLUMEDOWN,  // 'volume -'
+       [ 0x3e ] = KEY_VOLUMEUP,    // 'volume +'
+};
+
+/* Attila Kondoros <attila.kondoros@chello.hu> */
+static IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = {
+
+       [  1 ] = KEY_KP1,
+       [  2 ] = KEY_KP2,
+       [  3 ] = KEY_KP3,
+       [  4 ] = KEY_KP4,
+       [  5 ] = KEY_KP5,
+       [  6 ] = KEY_KP6,
+       [  7 ] = KEY_KP7,
+       [  8 ] = KEY_KP8,
+       [  9 ] = KEY_KP9,
+       [  0 ] = KEY_KP0,
+       [ 23 ] = KEY_LAST,        // +100
+       [ 10 ] = KEY_LIST,        // recall
+
+
+       [ 28 ] = KEY_TUNER,       // TV/FM
+       [ 21 ] = KEY_SEARCH,      // scan
+       [ 18 ] = KEY_POWER,       // power
+       [ 31 ] = KEY_VOLUMEDOWN,  // vol up
+       [ 27 ] = KEY_VOLUMEUP,    // vol down
+       [ 30 ] = KEY_CHANNELDOWN, // chn up
+       [ 26 ] = KEY_CHANNELUP,   // chn down
+
+       [ 17 ] = KEY_VIDEO,       // video
+       [ 15 ] = KEY_ZOOM,        // full screen
+       [ 19 ] = KEY_MUTE,        // mute/unmute
+       [ 16 ] = KEY_TEXT,        // min
+
+       [ 13 ] = KEY_STOP,        // freeze
+       [ 14 ] = KEY_RECORD,      // record
+       [ 29 ] = KEY_PLAYPAUSE,   // stop
+       [ 25 ] = KEY_PLAY,        // play
+
+       [ 22 ] = KEY_GOTO,        // osd
+       [ 20 ] = KEY_REFRESH,     // default
+       [ 12 ] = KEY_KPPLUS,      // fine tune >>>>
+       [ 24 ] = KEY_KPMINUS      // fine tune <<<<
+};
+
+/* ---------------------------------------------------------------------- */
+
+static IR_KEYTAB_TYPE ir_codes_conceptronic[IR_KEYTAB_SIZE] = {
+
+       [ 30 ] = KEY_POWER,       // power
+       [ 7  ] = KEY_MEDIA,       // source
+       [ 28 ] = KEY_SEARCH,      // scan
+
+/* FIXME: duplicate keycodes?
+ *
+ * These four keys seem to share the same GPIO as CH+, CH-, <<< and >>>
+ * The GPIO values are
+ * 6397fb for both "Scan <" and "CH -",
+ * 639ffb for "Scan >" and "CH+",
+ * 6384fb for "Tune <" and "<<<",
+ * 638cfb for "Tune >" and ">>>", regardless of the mask.
+ *
+ *     [ 23 ] = KEY_BACK,        // fm scan <<
+ *     [ 31 ] = KEY_FORWARD,     // fm scan >>
+ *
+ *     [ 4  ] = KEY_LEFT,        // fm tuning <
+ *     [ 12 ] = KEY_RIGHT,       // fm tuning >
+ *
+ * For now, these four keys are disabled. Pressing them will generate
+ * the CH+/CH-/<<</>>> events
+ */
+
+       [ 3  ] = KEY_TUNER,       // TV/FM
+
+       [ 0  ] = KEY_RECORD,
+       [ 8  ] = KEY_STOP,
+       [ 17 ] = KEY_PLAY,
+
+       [ 26 ] = KEY_PLAYPAUSE,   // freeze
+       [ 25 ] = KEY_ZOOM,        // zoom
+       [ 15 ] = KEY_TEXT,        // min
+
+       [ 1  ] = KEY_KP1,
+       [ 11 ] = KEY_KP2,
+       [ 27 ] = KEY_KP3,
+       [ 5  ] = KEY_KP4,
+       [ 9  ] = KEY_KP5,
+       [ 21 ] = KEY_KP6,
+       [ 6  ] = KEY_KP7,
+       [ 10 ] = KEY_KP8,
+       [ 18 ] = KEY_KP9,
+       [ 2  ] = KEY_KP0,
+       [ 16 ] = KEY_LAST,        // +100
+       [ 19 ] = KEY_LIST,        // recall
+
+       [ 31 ] = KEY_CHANNELUP,   // chn down
+       [ 23 ] = KEY_CHANNELDOWN, // chn up
+       [ 22 ] = KEY_VOLUMEUP,    // vol down
+       [ 20 ] = KEY_VOLUMEDOWN,  // vol up
+
+       [ 4  ] = KEY_KPMINUS,     // <<<
+       [ 14 ] = KEY_SETUP,       // function
+       [ 12 ] = KEY_KPPLUS,      // >>>
+
+       [ 13 ] = KEY_GOTO,        // mts
+       [ 29 ] = KEY_REFRESH,     // reset
+       [ 24 ] = KEY_MUTE         // mute/unmute
+};
+
+static IR_KEYTAB_TYPE ir_codes_nebula[IR_KEYTAB_SIZE] = {
+       [0x00] = KEY_KP0,
+       [0x01] = KEY_KP1,
+       [0x02] = KEY_KP2,
+       [0x03] = KEY_KP3,
+       [0x04] = KEY_KP4,
+       [0x05] = KEY_KP5,
+       [0x06] = KEY_KP6,
+       [0x07] = KEY_KP7,
+       [0x08] = KEY_KP8,
+       [0x09] = KEY_KP9,
+       [0x0a] = KEY_TV,
+       [0x0b] = KEY_AUX,
+       [0x0c] = KEY_DVD,
+       [0x0d] = KEY_POWER,
+       [0x0e] = KEY_MHP,       /* labelled 'Picture' */
+       [0x0f] = KEY_AUDIO,
+       [0x10] = KEY_INFO,
+       [0x11] = KEY_F13,       /* 16:9 */
+       [0x12] = KEY_F14,       /* 14:9 */
+       [0x13] = KEY_EPG,
+       [0x14] = KEY_EXIT,
+       [0x15] = KEY_MENU,
+       [0x16] = KEY_UP,
+       [0x17] = KEY_DOWN,
+       [0x18] = KEY_LEFT,
+       [0x19] = KEY_RIGHT,
+       [0x1a] = KEY_ENTER,
+       [0x1b] = KEY_CHANNELUP,
+       [0x1c] = KEY_CHANNELDOWN,
+       [0x1d] = KEY_VOLUMEUP,
+       [0x1e] = KEY_VOLUMEDOWN,
+       [0x1f] = KEY_RED,
+       [0x20] = KEY_GREEN,
+       [0x21] = KEY_YELLOW,
+       [0x22] = KEY_BLUE,
+       [0x23] = KEY_SUBTITLE,
+       [0x24] = KEY_F15,       /* AD */
+       [0x25] = KEY_TEXT,
+       [0x26] = KEY_MUTE,
+       [0x27] = KEY_REWIND,
+       [0x28] = KEY_STOP,
+       [0x29] = KEY_PLAY,
+       [0x2a] = KEY_FASTFORWARD,
+       [0x2b] = KEY_F16,       /* chapter */
+       [0x2c] = KEY_PAUSE,
+       [0x2d] = KEY_PLAY,
+       [0x2e] = KEY_RECORD,
+       [0x2f] = KEY_F17,       /* picture in picture */
+       [0x30] = KEY_KPPLUS,    /* zoom in */
+       [0x31] = KEY_KPMINUS,   /* zoom out */
+       [0x32] = KEY_F18,       /* capture */
+       [0x33] = KEY_F19,       /* web */
+       [0x34] = KEY_EMAIL,
+       [0x35] = KEY_PHONE,
+       [0x36] = KEY_PC
+};
+
+static int debug;
+module_param(debug, int, 0644);    /* debug level (0,1,2) */
+static int repeat_delay = 500;
+module_param(repeat_delay, int, 0644);
+static int repeat_period = 33;
+module_param(repeat_period, int, 0644);
+
+#define DEVNAME "bttv-input"
+
+/* ---------------------------------------------------------------------- */
+
+static void ir_handle_key(struct bttv *btv)
+{
+       struct bttv_ir *ir = btv->remote;
+       u32 gpio,data;
+
+       /* read gpio value */
+       gpio = bttv_gpio_read(&btv->c);
+       if (ir->polling) {
+               if (ir->last_gpio == gpio)
+                       return;
+               ir->last_gpio = gpio;
+       }
+
+       /* extract data */
+       data = ir_extract_bits(gpio, ir->mask_keycode);
+       dprintk(KERN_INFO DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n",
+               gpio, data,
+               ir->polling               ? "poll"  : "irq",
+               (gpio & ir->mask_keydown) ? " down" : "",
+               (gpio & ir->mask_keyup)   ? " up"   : "");
+
+       if ((ir->mask_keydown  &&  (0 != (gpio & ir->mask_keydown))) ||
+           (ir->mask_keyup    &&  (0 == (gpio & ir->mask_keyup)))) {
+               ir_input_keydown(ir->dev,&ir->ir,data,data);
+       } else {
+               ir_input_nokey(ir->dev,&ir->ir);
+       }
+
+}
+
+void bttv_input_irq(struct bttv *btv)
+{
+       struct bttv_ir *ir = btv->remote;
+
+       if (!ir->polling)
+               ir_handle_key(btv);
+}
+
+static void bttv_input_timer(unsigned long data)
+{
+       struct bttv *btv = (struct bttv*)data;
+       struct bttv_ir *ir = btv->remote;
+       unsigned long timeout;
+
+       ir_handle_key(btv);
+       timeout = jiffies + (ir->polling * HZ / 1000);
+       mod_timer(&ir->timer, timeout);
+}
+
+/* ---------------------------------------------------------------*/
+
+static int rc5_remote_gap = 885;
+module_param(rc5_remote_gap, int, 0644);
+static int rc5_key_timeout = 200;
+module_param(rc5_key_timeout, int, 0644);
+
+#define RC5_START(x)   (((x)>>12)&3)
+#define RC5_TOGGLE(x)  (((x)>>11)&1)
+#define RC5_ADDR(x)    (((x)>>6)&31)
+#define RC5_INSTR(x)   ((x)&63)
+
+/* decode raw bit pattern to RC5 code */
+static u32 rc5_decode(unsigned int code)
+{
+       unsigned int org_code = code;
+       unsigned int pair;
+       unsigned int rc5 = 0;
+       int i;
+
+       code = (code << 1) | 1;
+       for (i = 0; i < 14; ++i) {
+               pair = code & 0x3;
+               code >>= 2;
+
+               rc5 <<= 1;
+               switch (pair) {
+               case 0:
+               case 2:
+                       break;
+               case 1:
+                       rc5 |= 1;
+                       break;
+               case 3:
+                       dprintk(KERN_WARNING "bad code: %x\n", org_code);
+                       return 0;
+               }
+       }
+       dprintk(KERN_WARNING "code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+               "instr=%x\n", rc5, org_code, RC5_START(rc5),
+               RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
+       return rc5;
+}
+
+static int bttv_rc5_irq(struct bttv *btv)
+{
+       struct bttv_ir *ir = btv->remote;
+       struct timeval tv;
+       u32 gpio;
+       u32 gap;
+       unsigned long current_jiffies, timeout;
+
+       /* read gpio port */
+       gpio = bttv_gpio_read(&btv->c);
+
+       /* remote IRQ? */
+       if (!(gpio & 0x20))
+               return 0;
+
+       /* get time of bit */
+       current_jiffies = jiffies;
+       do_gettimeofday(&tv);
+
+       /* avoid overflow with gap >1s */
+       if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+               gap = 200000;
+       } else {
+               gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+                   tv.tv_usec - ir->base_time.tv_usec;
+       }
+
+       /* active code => add bit */
+       if (ir->active) {
+               /* only if in the code (otherwise spurious IRQ or timer
+                  late) */
+               if (ir->last_bit < 28) {
+                       ir->last_bit = (gap - rc5_remote_gap / 2) /
+                           rc5_remote_gap;
+                       ir->code |= 1 << ir->last_bit;
+               }
+               /* starting new code */
+       } else {
+               ir->active = 1;
+               ir->code = 0;
+               ir->base_time = tv;
+               ir->last_bit = 0;
+
+               timeout = current_jiffies + (500 + 30 * HZ) / 1000;
+               mod_timer(&ir->timer_end, timeout);
+       }
+
+       /* toggle GPIO pin 4 to reset the irq */
+       bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+       bttv_gpio_write(&btv->c, gpio | (1 << 4));
+       return 1;
+}
+
+
+static void bttv_rc5_timer_end(unsigned long data)
+{
+       struct bttv_ir *ir = (struct bttv_ir *)data;
+       struct timeval tv;
+       unsigned long current_jiffies, timeout;
+       u32 gap;
+
+       /* get time */
+       current_jiffies = jiffies;
+       do_gettimeofday(&tv);
+
+       /* avoid overflow with gap >1s */
+       if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+               gap = 200000;
+       } else {
+               gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+                   tv.tv_usec - ir->base_time.tv_usec;
+       }
+
+       /* Allow some timmer jitter (RC5 is ~24ms anyway so this is ok) */
+       if (gap < 28000) {
+               dprintk(KERN_WARNING "spurious timer_end\n");
+               return;
+       }
+
+       ir->active = 0;
+       if (ir->last_bit < 20) {
+               /* ignore spurious codes (caused by light/other remotes) */
+               dprintk(KERN_WARNING "short code: %x\n", ir->code);
+       } else {
+               u32 rc5 = rc5_decode(ir->code);
+
+               /* two start bits? */
+               if (RC5_START(rc5) != 3) {
+                       dprintk(KERN_WARNING "rc5 start bits invalid: %u\n", RC5_START(rc5));
+
+                       /* right address? */
+               } else if (RC5_ADDR(rc5) == 0x0) {
+                       u32 toggle = RC5_TOGGLE(rc5);
+                       u32 instr = RC5_INSTR(rc5);
+
+                       /* Good code, decide if repeat/repress */
+                       if (toggle != RC5_TOGGLE(ir->last_rc5) ||
+                           instr != RC5_INSTR(ir->last_rc5)) {
+                               dprintk(KERN_WARNING "instruction %x, toggle %x\n", instr,
+                                       toggle);
+                               ir_input_nokey(ir->dev, &ir->ir);
+                               ir_input_keydown(ir->dev, &ir->ir, instr,
+                                                instr);
+                       }
+
+                       /* Set/reset key-up timer */
+                       timeout = current_jiffies + (500 + rc5_key_timeout
+                                                    * HZ) / 1000;
+                       mod_timer(&ir->timer_keyup, timeout);
+
+                       /* Save code for repeat test */
+                       ir->last_rc5 = rc5;
+               }
+       }
+}
+
+static void bttv_rc5_timer_keyup(unsigned long data)
+{
+       struct bttv_ir *ir = (struct bttv_ir *)data;
+
+       dprintk(KERN_DEBUG "key released\n");
+       ir_input_nokey(ir->dev, &ir->ir);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int bttv_input_init(struct bttv *btv)
+{
+       struct bttv_ir *ir;
+       IR_KEYTAB_TYPE *ir_codes = NULL;
+       struct input_dev *input_dev;
+       int ir_type = IR_TYPE_OTHER;
+
+       if (!btv->has_remote)
+               return -ENODEV;
+
+       ir = kzalloc(sizeof(*ir),GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!ir || !input_dev) {
+               kfree(ir);
+               input_free_device(input_dev);
+               return -ENOMEM;
+       }
+       memset(ir,0,sizeof(*ir));
+
+       /* detect & configure */
+       switch (btv->c.type) {
+       case BTTV_BOARD_AVERMEDIA:
+       case BTTV_BOARD_AVPHONE98:
+       case BTTV_BOARD_AVERMEDIA98:
+               ir_codes         = ir_codes_avermedia;
+               ir->mask_keycode = 0xf88000;
+               ir->mask_keydown = 0x010000;
+               ir->polling      = 50; // ms
+               break;
+
+       case BTTV_BOARD_AVDVBT_761:
+       case BTTV_BOARD_AVDVBT_771:
+               ir_codes         = ir_codes_avermedia_dvbt;
+               ir->mask_keycode = 0x0f00c0;
+               ir->mask_keydown = 0x000020;
+               ir->polling      = 50; // ms
+               break;
+
+       case BTTV_BOARD_PXELVWPLTVPAK:
+               ir_codes         = ir_codes_pixelview;
+               ir->mask_keycode = 0x003e00;
+               ir->mask_keyup   = 0x010000;
+               ir->polling      = 50; // ms
+               break;
+       case BTTV_BOARD_PV_BT878P_9B:
+       case BTTV_BOARD_PV_BT878P_PLUS:
+               ir_codes         = ir_codes_pixelview;
+               ir->mask_keycode = 0x001f00;
+               ir->mask_keyup   = 0x008000;
+               ir->polling      = 50; // ms
+               break;
+
+       case BTTV_BOARD_WINFAST2000:
+               ir_codes         = ir_codes_winfast;
+               ir->mask_keycode = 0x1f8;
+               break;
+       case BTTV_BOARD_MAGICTVIEW061:
+       case BTTV_BOARD_MAGICTVIEW063:
+               ir_codes         = ir_codes_winfast;
+               ir->mask_keycode = 0x0008e000;
+               ir->mask_keydown = 0x00200000;
+               break;
+       case BTTV_BOARD_APAC_VIEWCOMP:
+               ir_codes         = ir_codes_apac_viewcomp;
+               ir->mask_keycode = 0x001f00;
+               ir->mask_keyup   = 0x008000;
+               ir->polling      = 50; // ms
+               break;
+       case BTTV_BOARD_CONCEPTRONIC_CTVFMI2:
+               ir_codes         = ir_codes_conceptronic;
+               ir->mask_keycode = 0x001F00;
+               ir->mask_keyup   = 0x006000;
+               ir->polling      = 50; // ms
+               break;
+       case BTTV_BOARD_NEBULA_DIGITV:
+               ir_codes = ir_codes_nebula;
+               btv->custom_irq = bttv_rc5_irq;
+               ir->rc5_gpio = 1;
+               break;
+       case BTTV_BOARD_MACHTV_MAGICTV:
+               ir_codes         = ir_codes_apac_viewcomp;
+               ir->mask_keycode = 0x001F00;
+               ir->mask_keyup   = 0x004000;
+               ir->polling      = 50; /* ms */
+               break;
+       }
+       if (NULL == ir_codes) {
+               dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n",btv->c.type);
+               kfree(ir);
+               input_free_device(input_dev);
+               return -ENODEV;
+       }
+
+       if (ir->rc5_gpio) {
+               u32 gpio;
+               /* enable remote irq */
+               bttv_gpio_inout(&btv->c, (1 << 4), 1 << 4);
+               gpio = bttv_gpio_read(&btv->c);
+               bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+               bttv_gpio_write(&btv->c, gpio | (1 << 4));
+       } else {
+               /* init hardware-specific stuff */
+               bttv_gpio_inout(&btv->c, ir->mask_keycode | ir->mask_keydown, 0);
+       }
+
+       /* init input device */
+       ir->dev = input_dev;
+
+       snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)",
+                btv->c.type);
+       snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+                pci_name(btv->c.pci));
+
+       ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       input_dev->name = ir->name;
+       input_dev->phys = ir->phys;
+       input_dev->id.bustype = BUS_PCI;
+       input_dev->id.version = 1;
+       if (btv->c.pci->subsystem_vendor) {
+               input_dev->id.vendor  = btv->c.pci->subsystem_vendor;
+               input_dev->id.product = btv->c.pci->subsystem_device;
+       } else {
+               input_dev->id.vendor  = btv->c.pci->vendor;
+               input_dev->id.product = btv->c.pci->device;
+       }
+       input_dev->cdev.dev = &btv->c.pci->dev;
+
+       btv->remote = ir;
+       if (ir->polling) {
+               init_timer(&ir->timer);
+               ir->timer.function = bttv_input_timer;
+               ir->timer.data     = (unsigned long)btv;
+               ir->timer.expires  = jiffies + HZ;
+               add_timer(&ir->timer);
+       } else if (ir->rc5_gpio) {
+               /* set timer_end for code completion */
+               init_timer(&ir->timer_end);
+               ir->timer_end.function = bttv_rc5_timer_end;
+               ir->timer_end.data = (unsigned long)ir;
+
+               init_timer(&ir->timer_keyup);
+               ir->timer_keyup.function = bttv_rc5_timer_keyup;
+               ir->timer_keyup.data = (unsigned long)ir;
+       }
+
+       /* all done */
+       input_register_device(btv->remote->dev);
+       printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys);
+
+       /* the remote isn't as bouncy as a keyboard */
+       ir->dev->rep[REP_DELAY] = repeat_delay;
+       ir->dev->rep[REP_PERIOD] = repeat_period;
+
+       return 0;
+}
+
+void bttv_input_fini(struct bttv *btv)
+{
+       if (btv->remote == NULL)
+               return;
+
+       if (btv->remote->polling) {
+               del_timer_sync(&btv->remote->timer);
+               flush_scheduled_work();
+       }
+
+
+       if (btv->remote->rc5_gpio) {
+               u32 gpio;
+
+               del_timer_sync(&btv->remote->timer_end);
+               flush_scheduled_work();
+
+               gpio = bttv_gpio_read(&btv->c);
+               bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+       }
+
+       input_unregister_device(btv->remote->dev);
+       kfree(btv->remote);
+       btv->remote = NULL;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx25840/cx25840.h b/drivers/media/video/cx25840/cx25840.h
new file mode 100644 (file)
index 0000000..fd22f30
--- /dev/null
@@ -0,0 +1,106 @@
+/* cx25840 API header
+ *
+ * Copyright (C) 2003-2004 Chris Kennedy
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _CX25840_H_
+#define _CX25840_H_
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
+   present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
+   certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
+   audio autodetect fails on some channels for these models and the workaround
+   is to select the audio standard explicitly. Many thanks to Hauppauge for
+   providing this information. */
+#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
+
+enum cx25840_video_input {
+       /* Composite video inputs In1-In8 */
+       CX25840_COMPOSITE1 = 1,
+       CX25840_COMPOSITE2,
+       CX25840_COMPOSITE3,
+       CX25840_COMPOSITE4,
+       CX25840_COMPOSITE5,
+       CX25840_COMPOSITE6,
+       CX25840_COMPOSITE7,
+       CX25840_COMPOSITE8,
+
+       /* S-Video inputs consist of one luma input (In1-In4) ORed with one
+          chroma input (In5-In8) */
+       CX25840_SVIDEO_LUMA1 = 0x10,
+       CX25840_SVIDEO_LUMA2 = 0x20,
+       CX25840_SVIDEO_LUMA3 = 0x30,
+       CX25840_SVIDEO_LUMA4 = 0x40,
+       CX25840_SVIDEO_CHROMA4 = 0x400,
+       CX25840_SVIDEO_CHROMA5 = 0x500,
+       CX25840_SVIDEO_CHROMA6 = 0x600,
+       CX25840_SVIDEO_CHROMA7 = 0x700,
+       CX25840_SVIDEO_CHROMA8 = 0x800,
+
+       /* S-Video aliases for common luma/chroma combinations */
+       CX25840_SVIDEO1 = 0x510,
+       CX25840_SVIDEO2 = 0x620,
+       CX25840_SVIDEO3 = 0x730,
+       CX25840_SVIDEO4 = 0x840,
+};
+
+enum cx25840_audio_input {
+       /* Audio inputs: serial or In4-In8 */
+       CX25840_AUDIO_SERIAL,
+       CX25840_AUDIO4 = 4,
+       CX25840_AUDIO5,
+       CX25840_AUDIO6,
+       CX25840_AUDIO7,
+       CX25840_AUDIO8,
+};
+
+struct cx25840_state {
+       int pvr150_workaround;
+       int radio;
+       enum cx25840_video_input vid_input;
+       enum cx25840_audio_input aud_input;
+       u32 audclk_freq;
+};
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-core.c                                                         */
+int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
+int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
+u8 cx25840_read(struct i2c_client *client, u16 addr);
+u32 cx25840_read4(struct i2c_client *client, u16 addr);
+int cx25840_and_or(struct i2c_client *client, u16 addr, u8 mask, u8 value);
+v4l2_std_id cx25840_get_v4lstd(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-firmware.c                                                      */
+int cx25840_loadfw(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-audio.c                                                         */
+int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg);
+void cx25840_audio_set_path(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-vbi.c                                                           */
+void cx25840_vbi_setup(struct i2c_client *client);
+int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg);
+
+#endif
diff --git a/drivers/media/video/rds.h b/drivers/media/video/rds.h
new file mode 100644 (file)
index 0000000..0d30eb7
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+
+    Types and defines needed for RDS. This is included by
+    saa6588.c and every driver (e.g. bttv-driver.c) that wants
+    to use the saa6588 module.
+
+    Instead of having a seperate rds.h, I'd prefer to include
+    this stuff in one of the already existing files like tuner.h
+
+    (c) 2005 by Hans J. Koch
+
+    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.
+
+*/
+
+#ifndef _RDS_H
+#define _RDS_H
+
+struct rds_command {
+       unsigned int  block_count;
+       int           result;
+       unsigned char __user *buffer;
+       struct file   *instance;
+       poll_table    *event_list;
+};
+
+#define RDS_CMD_OPEN   _IOW('R',1,int)
+#define RDS_CMD_CLOSE  _IOW('R',2,int)
+#define RDS_CMD_READ   _IOR('R',3,int)
+#define RDS_CMD_POLL   _IOR('R',4,int)
+
+#endif
+
+
+
+
diff --git a/drivers/usb/media/et61x251.h b/drivers/usb/media/et61x251.h
new file mode 100644 (file)
index 0000000..652238f
--- /dev/null
@@ -0,0 +1,220 @@
+/***************************************************************************
+ * V4L2 driver for ET61X[12]51 PC Camera Controllers                       *
+ *                                                                         *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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.               *
+ ***************************************************************************/
+
+#ifndef _ET61X251_H_
+#define _ET61X251_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+#include <asm/semaphore.h>
+
+#include "et61x251_sensor.h"
+
+/*****************************************************************************/
+
+#define ET61X251_DEBUG
+#define ET61X251_DEBUG_LEVEL         2
+#define ET61X251_MAX_DEVICES         64
+#define ET61X251_PRESERVE_IMGSCALE   0
+#define ET61X251_FORCE_MUNMAP        0
+#define ET61X251_MAX_FRAMES          32
+#define ET61X251_COMPRESSION_QUALITY 0
+#define ET61X251_URBS                2
+#define ET61X251_ISO_PACKETS         7
+#define ET61X251_ALTERNATE_SETTING   13
+#define ET61X251_URB_TIMEOUT         msecs_to_jiffies(2 * ET61X251_ISO_PACKETS)
+#define ET61X251_CTRL_TIMEOUT        100
+
+/*****************************************************************************/
+
+static const struct usb_device_id et61x251_id_table[] = {
+       { USB_DEVICE(0x102c, 0x6151), },
+       { USB_DEVICE(0x102c, 0x6251), },
+       { USB_DEVICE(0x102c, 0x6253), },
+       { USB_DEVICE(0x102c, 0x6254), },
+       { USB_DEVICE(0x102c, 0x6255), },
+       { USB_DEVICE(0x102c, 0x6256), },
+       { USB_DEVICE(0x102c, 0x6257), },
+       { USB_DEVICE(0x102c, 0x6258), },
+       { USB_DEVICE(0x102c, 0x6259), },
+       { USB_DEVICE(0x102c, 0x625a), },
+       { USB_DEVICE(0x102c, 0x625b), },
+       { USB_DEVICE(0x102c, 0x625c), },
+       { USB_DEVICE(0x102c, 0x625d), },
+       { USB_DEVICE(0x102c, 0x625e), },
+       { USB_DEVICE(0x102c, 0x625f), },
+       { USB_DEVICE(0x102c, 0x6260), },
+       { USB_DEVICE(0x102c, 0x6261), },
+       { USB_DEVICE(0x102c, 0x6262), },
+       { USB_DEVICE(0x102c, 0x6263), },
+       { USB_DEVICE(0x102c, 0x6264), },
+       { USB_DEVICE(0x102c, 0x6265), },
+       { USB_DEVICE(0x102c, 0x6266), },
+       { USB_DEVICE(0x102c, 0x6267), },
+       { USB_DEVICE(0x102c, 0x6268), },
+       { USB_DEVICE(0x102c, 0x6269), },
+       { }
+};
+
+ET61X251_SENSOR_TABLE
+
+/*****************************************************************************/
+
+enum et61x251_frame_state {
+       F_UNUSED,
+       F_QUEUED,
+       F_GRABBING,
+       F_DONE,
+       F_ERROR,
+};
+
+struct et61x251_frame_t {
+       void* bufmem;
+       struct v4l2_buffer buf;
+       enum et61x251_frame_state state;
+       struct list_head frame;
+       unsigned long vma_use_count;
+};
+
+enum et61x251_dev_state {
+       DEV_INITIALIZED = 0x01,
+       DEV_DISCONNECTED = 0x02,
+       DEV_MISCONFIGURED = 0x04,
+};
+
+enum et61x251_io_method {
+       IO_NONE,
+       IO_READ,
+       IO_MMAP,
+};
+
+enum et61x251_stream_state {
+       STREAM_OFF,
+       STREAM_INTERRUPT,
+       STREAM_ON,
+};
+
+struct et61x251_sysfs_attr {
+       u8 reg, i2c_reg;
+};
+
+struct et61x251_module_param {
+       u8 force_munmap;
+};
+
+static DECLARE_MUTEX(et61x251_sysfs_lock);
+static DECLARE_RWSEM(et61x251_disconnect);
+
+struct et61x251_device {
+       struct video_device* v4ldev;
+
+       struct et61x251_sensor* sensor;
+
+       struct usb_device* usbdev;
+       struct urb* urb[ET61X251_URBS];
+       void* transfer_buffer[ET61X251_URBS];
+       u8* control_buffer;
+
+       struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES];
+       struct list_head inqueue, outqueue;
+       u32 frame_count, nbuffers, nreadbuffers;
+
+       enum et61x251_io_method io;
+       enum et61x251_stream_state stream;
+
+       struct v4l2_jpegcompression compression;
+
+       struct et61x251_sysfs_attr sysfs;
+       struct et61x251_module_param module_param;
+
+       enum et61x251_dev_state state;
+       u8 users;
+
+       struct semaphore dev_sem, fileop_sem;
+       spinlock_t queue_lock;
+       wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+void
+et61x251_attach_sensor(struct et61x251_device* cam,
+                       struct et61x251_sensor* sensor)
+{
+       cam->sensor = sensor;
+       cam->sensor->usbdev = cam->usbdev;
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef ET61X251_DEBUG
+#      define DBG(level, fmt, args...)                                       \
+do {                                                                          \
+       if (debug >= (level)) {                                               \
+               if ((level) == 1)                                             \
+                       dev_err(&cam->usbdev->dev, fmt "\n", ## args);        \
+               else if ((level) == 2)                                        \
+                       dev_info(&cam->usbdev->dev, fmt "\n", ## args);       \
+               else if ((level) >= 3)                                        \
+                       dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n",      \
+                                __FUNCTION__, __LINE__ , ## args);           \
+       }                                                                     \
+} while (0)
+#      define KDBG(level, fmt, args...)                                      \
+do {                                                                          \
+       if (debug >= (level)) {                                               \
+               if ((level) == 1 || (level) == 2)                             \
+                       pr_info("et61x251: " fmt "\n", ## args);              \
+               else if ((level) == 3)                                        \
+                       pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \
+                                __LINE__ , ## args);                         \
+       }                                                                     \
+} while (0)
+#      define V4LDBG(level, name, cmd)                                       \
+do {                                                                          \
+       if (debug >= (level))                                                 \
+               v4l_print_ioctl(name, cmd);                                   \
+} while (0)
+#else
+#      define DBG(level, fmt, args...) do {;} while(0)
+#      define KDBG(level, fmt, args...) do {;} while(0)
+#      define V4LDBG(level, name, cmd) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...)                                                    \
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+
+#endif /* _ET61X251_H_ */
diff --git a/drivers/usb/media/et61x251_core.c b/drivers/usb/media/et61x251_core.c
new file mode 100644 (file)
index 0000000..2c0171a
--- /dev/null
@@ -0,0 +1,2605 @@
+/***************************************************************************
+ * V4L2 driver for ET61X[12]51 PC Camera Controllers                       *
+ *                                                                         *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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.               *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "et61x251.h"
+
+/*****************************************************************************/
+
+#define ET61X251_MODULE_NAME    "V4L2 driver for ET61X[12]51 "                \
+                                "PC Camera Controllers"
+#define ET61X251_MODULE_AUTHOR  "(C) 2006 Luca Risolia"
+#define ET61X251_AUTHOR_EMAIL   "<luca.risolia@studio.unibo.it>"
+#define ET61X251_MODULE_LICENSE "GPL"
+#define ET61X251_MODULE_VERSION "1:1.01"
+#define ET61X251_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 1)
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, et61x251_id_table);
+
+MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(ET61X251_MODULE_NAME);
+MODULE_VERSION(ET61X251_MODULE_VERSION);
+MODULE_LICENSE(ET61X251_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+                 "\n<-1|n[,...]> Specify V4L2 minor mode number."
+                 "\n -1 = use next available (default)"
+                 "\n  n = use minor number n (integer >= 0)"
+                 "\nYou can specify up to "
+                 __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way."
+                 "\nFor example:"
+                 "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+                 "\nthe second registered camera and use auto for the first"
+                 "\none and for every other camera."
+                 "\n");
+
+static short force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] =
+                               ET61X251_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+                 "\n<0|1[,...]> Force the application to unmap previously"
+                 "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+                 "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+                 "\nthis feature. This parameter is specific for each"
+                 "\ndetected camera."
+                 "\n 0 = do not force memory unmapping"
+                 "\n 1 = force memory unmapping (save memory)"
+                 "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+                 "\n");
+
+#ifdef ET61X251_DEBUG
+static unsigned short debug = ET61X251_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+                 "\n<n> Debugging information level, from 0 to 3:"
+                 "\n0 = none (use carefully)"
+                 "\n1 = critical errors"
+                 "\n2 = significant informations"
+                 "\n3 = more verbose messages"
+                 "\nLevel 3 is useful for testing only, when only "
+                 "one device is used."
+                 "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"."
+                 "\n");
+#endif
+
+/*****************************************************************************/
+
+static u32
+et61x251_request_buffers(struct et61x251_device* cam, u32 count,
+                         enum et61x251_io_method io)
+{
+       struct v4l2_pix_format* p = &(cam->sensor->pix_format);
+       struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+       const size_t imagesize = cam->module_param.force_munmap ||
+                                io == IO_READ ?
+                                (p->width * p->height * p->priv) / 8 :
+                                (r->width * r->height * p->priv) / 8;
+       void* buff = NULL;
+       u32 i;
+
+       if (count > ET61X251_MAX_FRAMES)
+               count = ET61X251_MAX_FRAMES;
+
+       cam->nbuffers = count;
+       while (cam->nbuffers > 0) {
+               if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
+                       break;
+               cam->nbuffers--;
+       }
+
+       for (i = 0; i < cam->nbuffers; i++) {
+               cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+               cam->frame[i].buf.index = i;
+               cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+               cam->frame[i].buf.length = imagesize;
+               cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cam->frame[i].buf.sequence = 0;
+               cam->frame[i].buf.field = V4L2_FIELD_NONE;
+               cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+               cam->frame[i].buf.flags = 0;
+       }
+
+       return cam->nbuffers;
+}
+
+
+static void et61x251_release_buffers(struct et61x251_device* cam)
+{
+       if (cam->nbuffers) {
+               vfree(cam->frame[0].bufmem);
+               cam->nbuffers = 0;
+       }
+       cam->frame_current = NULL;
+}
+
+
+static void et61x251_empty_framequeues(struct et61x251_device* cam)
+{
+       u32 i;
+
+       INIT_LIST_HEAD(&cam->inqueue);
+       INIT_LIST_HEAD(&cam->outqueue);
+
+       for (i = 0; i < ET61X251_MAX_FRAMES; i++) {
+               cam->frame[i].state = F_UNUSED;
+               cam->frame[i].buf.bytesused = 0;
+       }
+}
+
+
+static void et61x251_requeue_outqueue(struct et61x251_device* cam)
+{
+       struct et61x251_frame_t *i;
+
+       list_for_each_entry(i, &cam->outqueue, frame) {
+               i->state = F_QUEUED;
+               list_add(&i->frame, &cam->inqueue);
+       }
+
+       INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void et61x251_queue_unusedframes(struct et61x251_device* cam)
+{
+       unsigned long lock_flags;
+       u32 i;
+
+       for (i = 0; i < cam->nbuffers; i++)
+               if (cam->frame[i].state == F_UNUSED) {
+                       cam->frame[i].state = F_QUEUED;
+                       spin_lock_irqsave(&cam->queue_lock, lock_flags);
+                       list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+                       spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+               }
+}
+
+/*****************************************************************************/
+
+int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index)
+{
+       struct usb_device* udev = cam->usbdev;
+       u8* buff = cam->control_buffer;
+       int res;
+
+       *buff = value;
+
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
+       if (res < 0) {
+               DBG(3, "Failed to write a register (value 0x%02X, index "
+                      "0x%02X, error %d)", value, index, res);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int et61x251_read_reg(struct et61x251_device* cam, u16 index)
+{
+       struct usb_device* udev = cam->usbdev;
+       u8* buff = cam->control_buffer;
+       int res;
+
+       res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+                             0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+                   index, res);
+
+       return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+static int
+et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor)
+{
+       int i, r;
+
+       for (i = 1; i <= 8; i++) {
+               if (sensor->interface == ET61X251_I2C_3WIRES) {
+                       r = et61x251_read_reg(cam, 0x8e);
+                       if (!(r & 0x02) && (r >= 0))
+                               return 0;
+               } else {
+                       r = et61x251_read_reg(cam, 0x8b);
+                       if (!(r & 0x01) && (r >= 0))
+                               return 0;
+               }
+               if (r < 0)
+                       return -EIO;
+               udelay(8*8); /* minimum for sensors at 400kHz */
+       }
+
+       return -EBUSY;
+}
+
+
+int
+et61x251_i2c_try_read(struct et61x251_device* cam,
+                      struct et61x251_sensor* sensor, u8 address)
+{
+       struct usb_device* udev = cam->usbdev;
+       u8* data = cam->control_buffer;
+       int err = 0, res;
+
+       data[0] = address;
+       data[1] = cam->sensor->i2c_slave_id;
+       data[2] = cam->sensor->rsta | 0x10;
+       data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       err += et61x251_i2c_wait(cam, sensor);
+
+       res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+                             0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       if (err)
+               DBG(3, "I2C read failed for %s image sensor", sensor->name);
+
+       PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
+
+       return err ? -1 : (int)data[0];
+}
+
+
+int
+et61x251_i2c_try_write(struct et61x251_device* cam,
+                       struct et61x251_sensor* sensor, u8 address, u8 value)
+{
+       struct usb_device* udev = cam->usbdev;
+       u8* data = cam->control_buffer;
+       int err = 0, res;
+
+       data[0] = address;
+       data[1] = cam->sensor->i2c_slave_id;
+       data[2] = cam->sensor->rsta | 0x12;
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       data[0] = value;
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       err += et61x251_i2c_wait(cam, sensor);
+
+       if (err)
+               DBG(3, "I2C write failed for %s image sensor", sensor->name);
+
+       PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
+
+       return err ? -1 : 0;
+}
+
+
+int
+et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
+                       u8 data3, u8 data4, u8 data5, u8 data6, u8 data7,
+                       u8 data8, u8 address)
+{
+       struct usb_device* udev = cam->usbdev;
+       u8* data = cam->control_buffer;
+       int err = 0, res;
+
+       if (!cam->sensor)
+               return -1;
+
+       data[0] = data2;
+       data[1] = data3;
+       data[2] = data4;
+       data[3] = data5;
+       data[4] = data6;
+       data[5] = data7;
+       data[6] = data8;
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       data[0] = address;
+       data[1] = cam->sensor->i2c_slave_id;
+       data[2] = cam->sensor->rsta | 0x02 | (n << 4);
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       /* Start writing through the serial interface */
+       data[0] = data1;
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+                             0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+       if (res < 0)
+               err += res;
+
+       err += et61x251_i2c_wait(cam, cam->sensor);
+
+       if (err)
+               DBG(3, "I2C raw write failed for %s image sensor",
+                   cam->sensor->name);
+
+       PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, "
+             "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X,"
+             " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address,
+             data1, data2, data3, data4, data5, data6, data7, data8);
+
+       return err ? -1 : 0;
+
+}
+
+
+int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
+{
+       if (!cam->sensor)
+               return -1;
+
+       return et61x251_i2c_try_read(cam, cam->sensor, address);
+}
+
+
+int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value)
+{
+       if (!cam->sensor)
+               return -1;
+
+       return et61x251_i2c_try_write(cam, cam->sensor, address, value);
+}
+
+/*****************************************************************************/
+
+static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+       struct et61x251_device* cam = urb->context;
+       struct et61x251_frame_t** f;
+       size_t imagesize;
+       u8 i;
+       int err = 0;
+
+       if (urb->status == -ENOENT)
+               return;
+
+       f = &cam->frame_current;
+
+       if (cam->stream == STREAM_INTERRUPT) {
+               cam->stream = STREAM_OFF;
+               if ((*f))
+                       (*f)->state = F_QUEUED;
+               DBG(3, "Stream interrupted");
+               wake_up_interruptible(&cam->wait_stream);
+       }
+
+       if (cam->state & DEV_DISCONNECTED)
+               return;
+
+       if (cam->state & DEV_MISCONFIGURED) {
+               wake_up_interruptible(&cam->wait_frame);
+               return;
+       }
+
+       if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+               goto resubmit_urb;
+
+       if (!(*f))
+               (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t,
+                                 frame);
+
+       imagesize = (cam->sensor->pix_format.width *
+                    cam->sensor->pix_format.height *
+                    cam->sensor->pix_format.priv) / 8;
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               unsigned int len, status;
+               void *pos;
+               u8* b1, * b2, sof;
+               const u8 VOID_BYTES = 6;
+               size_t imglen;
+
+               len = urb->iso_frame_desc[i].actual_length;
+               status = urb->iso_frame_desc[i].status;
+               pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+               if (status) {
+                       DBG(3, "Error in isochronous frame");
+                       (*f)->state = F_ERROR;
+                       continue;
+               }
+
+               b1 = pos++;
+               b2 = pos++;
+               sof = ((*b1 & 0x3f) == 63);
+               imglen = ((*b1 & 0xc0) << 2) | *b2;
+
+               PDBGG("Isochrnous frame: length %u, #%u i, image length %zu",
+                     len, i, imglen);
+
+               if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
+start_of_frame:
+                       if (sof) {
+                               (*f)->state = F_GRABBING;
+                               (*f)->buf.bytesused = 0;
+                               do_gettimeofday(&(*f)->buf.timestamp);
+                               pos += 22;
+                               DBG(3, "SOF detected: new video frame");
+                       }
+
+               if ((*f)->state == F_GRABBING) {
+                       if (sof && (*f)->buf.bytesused) {
+                               if (cam->sensor->pix_format.pixelformat ==
+                                                        V4L2_PIX_FMT_ET61X251)
+                                       goto end_of_frame;
+                               else {
+                                       DBG(3, "Not expected SOF detected "
+                                              "after %lu bytes",
+                                          (unsigned long)(*f)->buf.bytesused);
+                                       (*f)->state = F_ERROR;
+                                       continue;
+                               }
+                       }
+
+                       if ((*f)->buf.bytesused + imglen > imagesize) {
+                               DBG(3, "Video frame size exceeded");
+                               (*f)->state = F_ERROR;
+                               continue;
+                       }
+
+                       pos += VOID_BYTES;
+
+                       memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen);
+                       (*f)->buf.bytesused += imglen;
+
+                       if ((*f)->buf.bytesused == imagesize) {
+                               u32 b;
+end_of_frame:
+                               b = (*f)->buf.bytesused;
+                               (*f)->state = F_DONE;
+                               (*f)->buf.sequence= ++cam->frame_count;
+                               spin_lock(&cam->queue_lock);
+                               list_move_tail(&(*f)->frame, &cam->outqueue);
+                               if (!list_empty(&cam->inqueue))
+                                       (*f) = list_entry(cam->inqueue.next,
+                                                      struct et61x251_frame_t,
+                                                         frame);
+                               else
+                                       (*f) = NULL;
+                               spin_unlock(&cam->queue_lock);
+                               DBG(3, "Video frame captured: : %lu bytes",
+                                      (unsigned long)(b));
+
+                               if (!(*f))
+                                       goto resubmit_urb;
+
+                               if (sof &&
+                                   cam->sensor->pix_format.pixelformat ==
+                                                        V4L2_PIX_FMT_ET61X251)
+                                       goto start_of_frame;
+                       }
+               }
+       }
+
+resubmit_urb:
+       urb->dev = cam->usbdev;
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err < 0 && err != -EPERM) {
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "usb_submit_urb() failed");
+       }
+
+       wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int et61x251_start_transfer(struct et61x251_device* cam)
+{
+       struct usb_device *udev = cam->usbdev;
+       struct urb* urb;
+       const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832,
+                                              864, 896, 920, 956, 980, 1000,
+                                              1022};
+       const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING];
+       s8 i, j;
+       int err = 0;
+
+       for (i = 0; i < ET61X251_URBS; i++) {
+               cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz,
+                                                 GFP_KERNEL);
+               if (!cam->transfer_buffer[i]) {
+                       err = -ENOMEM;
+                       DBG(1, "Not enough memory");
+                       goto free_buffers;
+               }
+       }
+
+       for (i = 0; i < ET61X251_URBS; i++) {
+               urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL);
+               cam->urb[i] = urb;
+               if (!urb) {
+                       err = -ENOMEM;
+                       DBG(1, "usb_alloc_urb() failed");
+                       goto free_urbs;
+               }
+               urb->dev = udev;
+               urb->context = cam;
+               urb->pipe = usb_rcvisocpipe(udev, 1);
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->number_of_packets = ET61X251_ISO_PACKETS;
+               urb->complete = et61x251_urb_complete;
+               urb->transfer_buffer = cam->transfer_buffer[i];
+               urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS;
+               urb->interval = 1;
+               for (j = 0; j < ET61X251_ISO_PACKETS; j++) {
+                       urb->iso_frame_desc[j].offset = psz * j;
+                       urb->iso_frame_desc[j].length = psz;
+               }
+       }
+
+       err = et61x251_write_reg(cam, 0x01, 0x03);
+       err = et61x251_write_reg(cam, 0x00, 0x03);
+       err = et61x251_write_reg(cam, 0x08, 0x03);
+       if (err) {
+               err = -EIO;
+               DBG(1, "I/O hardware error");
+               goto free_urbs;
+       }
+
+       err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING);
+       if (err) {
+               DBG(1, "usb_set_interface() failed");
+               goto free_urbs;
+       }
+
+       cam->frame_current = NULL;
+
+       for (i = 0; i < ET61X251_URBS; i++) {
+               err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+               if (err) {
+                       for (j = i-1; j >= 0; j--)
+                               usb_kill_urb(cam->urb[j]);
+                       DBG(1, "usb_submit_urb() failed, error %d", err);
+                       goto free_urbs;
+               }
+       }
+
+       return 0;
+
+free_urbs:
+       for (i = 0; (i < ET61X251_URBS) &&  cam->urb[i]; i++)
+               usb_free_urb(cam->urb[i]);
+
+free_buffers:
+       for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++)
+               kfree(cam->transfer_buffer[i]);
+
+       return err;
+}
+
+
+static int et61x251_stop_transfer(struct et61x251_device* cam)
+{
+       struct usb_device *udev = cam->usbdev;
+       s8 i;
+       int err = 0;
+
+       if (cam->state & DEV_DISCONNECTED)
+               return 0;
+
+       for (i = ET61X251_URBS-1; i >= 0; i--) {
+               usb_kill_urb(cam->urb[i]);
+               usb_free_urb(cam->urb[i]);
+               kfree(cam->transfer_buffer[i]);
+       }
+
+       err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+       if (err)
+               DBG(3, "usb_set_interface() failed");
+
+       return err;
+}
+
+
+static int et61x251_stream_interrupt(struct et61x251_device* cam)
+{
+       int err = 0;
+
+       cam->stream = STREAM_INTERRUPT;
+       err = wait_event_timeout(cam->wait_stream,
+                                (cam->stream == STREAM_OFF) ||
+                                (cam->state & DEV_DISCONNECTED),
+                                ET61X251_URB_TIMEOUT);
+       if (cam->state & DEV_DISCONNECTED)
+               return -ENODEV;
+       else if (err) {
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "URB timeout reached. The camera is misconfigured. To "
+                      "use it, close and open /dev/video%d again.",
+                   cam->v4ldev->minor);
+               return err;
+       }
+
+       return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
+{
+       char str[5];
+       char* endp;
+       unsigned long val;
+
+       if (len < 4) {
+               strncpy(str, buff, len);
+               str[len+1] = '\0';
+       } else {
+               strncpy(str, buff, 4);
+               str[4] = '\0';
+       }
+
+       val = simple_strtoul(str, &endp, 0);
+
+       *count = 0;
+       if (val <= 0xff)
+               *count = (ssize_t)(endp - str);
+       if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+               *count += 1;
+
+       return (u8)val;
+}
+
+/*
+   NOTE 1: being inside one of the following methods implies that the v4l
+           device exists for sure (see kobjects and reference counters)
+   NOTE 2: buffers are PAGE_SIZE long
+*/
+
+static ssize_t et61x251_show_reg(struct class_device* cd, char* buf)
+{
+       struct et61x251_device* cam;
+       ssize_t count;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t
+et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
+{
+       struct et61x251_device* cam;
+       u8 index;
+       ssize_t count;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       index = et61x251_strtou8(buf, len, &count);
+       if (index > 0x8e || !count) {
+               up(&et61x251_sysfs_lock);
+               return -EINVAL;
+       }
+
+       cam->sysfs.reg = index;
+
+       DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg);
+       DBG(3, "Written bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
+{
+       struct et61x251_device* cam;
+       ssize_t count;
+       int val;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) {
+               up(&et61x251_sysfs_lock);
+               return -EIO;
+       }
+
+       count = sprintf(buf, "%d\n", val);
+
+       DBG(3, "Read bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t
+et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
+{
+       struct et61x251_device* cam;
+       u8 value;
+       ssize_t count;
+       int err;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       value = et61x251_strtou8(buf, len, &count);
+       if (!count) {
+               up(&et61x251_sysfs_lock);
+               return -EINVAL;
+       }
+
+       err = et61x251_write_reg(cam, value, cam->sysfs.reg);
+       if (err) {
+               up(&et61x251_sysfs_lock);
+               return -EIO;
+       }
+
+       DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X",
+           cam->sysfs.reg, value);
+       DBG(3, "Written bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
+{
+       struct et61x251_device* cam;
+       ssize_t count;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+       DBG(3, "Read bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t
+et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
+{
+       struct et61x251_device* cam;
+       u8 index;
+       ssize_t count;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       index = et61x251_strtou8(buf, len, &count);
+       if (!count) {
+               up(&et61x251_sysfs_lock);
+               return -EINVAL;
+       }
+
+       cam->sysfs.i2c_reg = index;
+
+       DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
+       DBG(3, "Written bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
+{
+       struct et61x251_device* cam;
+       ssize_t count;
+       int val;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) {
+               up(&et61x251_sysfs_lock);
+               return -ENOSYS;
+       }
+
+       if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+               up(&et61x251_sysfs_lock);
+               return -EIO;
+       }
+
+       count = sprintf(buf, "%d\n", val);
+
+       DBG(3, "Read bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static ssize_t
+et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
+{
+       struct et61x251_device* cam;
+       u8 value;
+       ssize_t count;
+       int err;
+
+       if (down_interruptible(&et61x251_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&et61x251_sysfs_lock);
+               return -ENODEV;
+       }
+
+       if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) {
+               up(&et61x251_sysfs_lock);
+               return -ENOSYS;
+       }
+
+       value = et61x251_strtou8(buf, len, &count);
+       if (!count) {
+               up(&et61x251_sysfs_lock);
+               return -EINVAL;
+       }
+
+       err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value);
+       if (err) {
+               up(&et61x251_sysfs_lock);
+               return -EIO;
+       }
+
+       DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+           cam->sysfs.i2c_reg, value);
+       DBG(3, "Written bytes: %zd", count);
+
+       up(&et61x251_sysfs_lock);
+
+       return count;
+}
+
+
+static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+                         et61x251_show_reg, et61x251_store_reg);
+static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+                         et61x251_show_val, et61x251_store_val);
+static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+                         et61x251_show_i2c_reg, et61x251_store_i2c_reg);
+static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+                         et61x251_show_i2c_val, et61x251_store_i2c_val);
+
+
+static void et61x251_create_sysfs(struct et61x251_device* cam)
+{
+       struct video_device *v4ldev = cam->v4ldev;
+
+       video_device_create_file(v4ldev, &class_device_attr_reg);
+       video_device_create_file(v4ldev, &class_device_attr_val);
+       if (cam->sensor && cam->sensor->sysfs_ops) {
+               video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
+               video_device_create_file(v4ldev, &class_device_attr_i2c_val);
+       }
+}
+#endif /* CONFIG_VIDEO_ADV_DEBUG */
+
+/*****************************************************************************/
+
+static int
+et61x251_set_pix_format(struct et61x251_device* cam,
+                        struct v4l2_pix_format* pix)
+{
+       int r, err = 0;
+
+       if ((r = et61x251_read_reg(cam, 0x12)) < 0)
+               err += r;
+       if (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
+               err += et61x251_write_reg(cam, r & 0xfd, 0x12);
+       else
+               err += et61x251_write_reg(cam, r | 0x02, 0x12);
+
+       return err ? -EIO : 0;
+}
+
+
+static int
+et61x251_set_compression(struct et61x251_device* cam,
+                         struct v4l2_jpegcompression* compression)
+{
+       int r, err = 0;
+
+       if ((r = et61x251_read_reg(cam, 0x12)) < 0)
+               err += r;
+       if (compression->quality == 0)
+               err += et61x251_write_reg(cam, r & 0xfb, 0x12);
+       else
+               err += et61x251_write_reg(cam, r | 0x04, 0x12);
+
+       return err ? -EIO : 0;
+}
+
+
+static int et61x251_set_scale(struct et61x251_device* cam, u8 scale)
+{
+       int r = 0, err = 0;
+
+       r = et61x251_read_reg(cam, 0x12);
+       if (r < 0)
+               err += r;
+
+       if (scale == 1)
+               err += et61x251_write_reg(cam, r & ~0x01, 0x12);
+       else if (scale == 2)
+               err += et61x251_write_reg(cam, r | 0x01, 0x12);
+
+       if (err)
+               return -EIO;
+
+       PDBGG("Scaling factor: %u", scale);
+
+       return 0;
+}
+
+
+static int
+et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left +
+                          s->active_pixel.left),
+           fmw_sy = (u16)(rect->top - s->cropcap.bounds.top +
+                          s->active_pixel.top),
+           fmw_length = (u16)(rect->width),
+           fmw_height = (u16)(rect->height);
+       int err = 0;
+
+       err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69);
+       err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a);
+       err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b);
+       err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c);
+       err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6)
+                                      | ((fmw_length & 0x300) >> 4)
+                                      | ((fmw_height & 0x300) >> 2), 0x6d);
+       if (err)
+               return -EIO;
+
+       PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u",
+             fmw_sx, fmw_sy, fmw_length, fmw_height);
+
+       return 0;
+}
+
+
+static int et61x251_init(struct et61x251_device* cam)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_control ctrl;
+       struct v4l2_queryctrl *qctrl;
+       struct v4l2_rect* rect;
+       u8 i = 0;
+       int err = 0;
+
+       if (!(cam->state & DEV_INITIALIZED)) {
+               init_waitqueue_head(&cam->open);
+               qctrl = s->qctrl;
+               rect = &(s->cropcap.defrect);
+               cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
+       } else { /* use current values */
+               qctrl = s->_qctrl;
+               rect = &(s->_rect);
+       }
+
+       err += et61x251_set_scale(cam, rect->width / s->pix_format.width);
+       err += et61x251_set_crop(cam, rect);
+       if (err)
+               return err;
+
+       if (s->init) {
+               err = s->init(cam);
+               if (err) {
+                       DBG(3, "Sensor initialization failed");
+                       return err;
+               }
+       }
+
+       err += et61x251_set_compression(cam, &cam->compression);
+       err += et61x251_set_pix_format(cam, &s->pix_format);
+       if (s->set_pix_format)
+               err += s->set_pix_format(cam, &s->pix_format);
+       if (err)
+               return err;
+
+       if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251)
+               DBG(3, "Compressed video format is active, quality %d",
+                   cam->compression.quality);
+       else
+               DBG(3, "Uncompressed video format is active");
+
+       if (s->set_crop)
+               if ((err = s->set_crop(cam, rect))) {
+                       DBG(3, "set_crop() failed");
+                       return err;
+               }
+
+       if (s->set_ctrl) {
+               for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+                       if (s->qctrl[i].id != 0 &&
+                           !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+                               ctrl.id = s->qctrl[i].id;
+                               ctrl.value = qctrl[i].default_value;
+                               err = s->set_ctrl(cam, &ctrl);
+                               if (err) {
+                                       DBG(3, "Set %s control failed",
+                                           s->qctrl[i].name);
+                                       return err;
+                               }
+                               DBG(3, "Image sensor supports '%s' control",
+                                   s->qctrl[i].name);
+                       }
+       }
+
+       if (!(cam->state & DEV_INITIALIZED)) {
+               init_MUTEX(&cam->fileop_sem);
+               spin_lock_init(&cam->queue_lock);
+               init_waitqueue_head(&cam->wait_frame);
+               init_waitqueue_head(&cam->wait_stream);
+               cam->nreadbuffers = 2;
+               memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+               memcpy(&(s->_rect), &(s->cropcap.defrect),
+                      sizeof(struct v4l2_rect));
+               cam->state |= DEV_INITIALIZED;
+       }
+
+       DBG(2, "Initialization succeeded");
+       return 0;
+}
+
+
+static void et61x251_release_resources(struct et61x251_device* cam)
+{
+       down(&et61x251_sysfs_lock);
+
+       DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
+       video_set_drvdata(cam->v4ldev, NULL);
+       video_unregister_device(cam->v4ldev);
+
+       up(&et61x251_sysfs_lock);
+
+       kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int et61x251_open(struct inode* inode, struct file* filp)
+{
+       struct et61x251_device* cam;
+       int err = 0;
+
+       /*
+          This is the only safe way to prevent race conditions with
+          disconnect
+       */
+       if (!down_read_trylock(&et61x251_disconnect))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(video_devdata(filp));
+
+       if (down_interruptible(&cam->dev_sem)) {
+               up_read(&et61x251_disconnect);
+               return -ERESTARTSYS;
+       }
+
+       if (cam->users) {
+               DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
+               if ((filp->f_flags & O_NONBLOCK) ||
+                   (filp->f_flags & O_NDELAY)) {
+                       err = -EWOULDBLOCK;
+                       goto out;
+               }
+               up(&cam->dev_sem);
+               err = wait_event_interruptible_exclusive(cam->open,
+                                                 cam->state & DEV_DISCONNECTED
+                                                        || !cam->users);
+               if (err) {
+                       up_read(&et61x251_disconnect);
+                       return err;
+               }
+               if (cam->state & DEV_DISCONNECTED) {
+                       up_read(&et61x251_disconnect);
+                       return -ENODEV;
+               }
+               down(&cam->dev_sem);
+       }
+
+
+       if (cam->state & DEV_MISCONFIGURED) {
+               err = et61x251_init(cam);
+               if (err) {
+                       DBG(1, "Initialization failed again. "
+                              "I will retry on next open().");
+                       goto out;
+               }
+               cam->state &= ~DEV_MISCONFIGURED;
+       }
+
+       if ((err = et61x251_start_transfer(cam)))
+               goto out;
+
+       filp->private_data = cam;
+       cam->users++;
+       cam->io = IO_NONE;
+       cam->stream = STREAM_OFF;
+       cam->nbuffers = 0;
+       cam->frame_count = 0;
+       et61x251_empty_framequeues(cam);
+
+       DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
+
+out:
+       up(&cam->dev_sem);
+       up_read(&et61x251_disconnect);
+       return err;
+}
+
+
+static int et61x251_release(struct inode* inode, struct file* filp)
+{
+       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+
+       down(&cam->dev_sem); /* prevent disconnect() to be called */
+
+       et61x251_stop_transfer(cam);
+
+       et61x251_release_buffers(cam);
+
+       if (cam->state & DEV_DISCONNECTED) {
+               et61x251_release_resources(cam);
+               up(&cam->dev_sem);
+               kfree(cam);
+               return 0;
+       }
+
+       cam->users--;
+       wake_up_interruptible_nr(&cam->open, 1);
+
+       DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
+
+       up(&cam->dev_sem);
+
+       return 0;
+}
+
+
+static ssize_t
+et61x251_read(struct file* filp, char __user * buf,
+              size_t count, loff_t* f_pos)
+{
+       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+       struct et61x251_frame_t* f, * i;
+       unsigned long lock_flags;
+       int err = 0;
+
+       if (down_interruptible(&cam->fileop_sem))
+               return -ERESTARTSYS;
+
+       if (cam->state & DEV_DISCONNECTED) {
+               DBG(1, "Device not present");
+               up(&cam->fileop_sem);
+               return -ENODEV;
+       }
+
+       if (cam->state & DEV_MISCONFIGURED) {
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
+               up(&cam->fileop_sem);
+               return -EIO;
+       }
+
+       if (cam->io == IO_MMAP) {
+               DBG(3, "Close and open the device again to choose the read "
+                      "method");
+               up(&cam->fileop_sem);
+               return -EINVAL;
+       }
+
+       if (cam->io == IO_NONE) {
+               if (!et61x251_request_buffers(cam, cam->nreadbuffers,
+                                             IO_READ)) {
+                       DBG(1, "read() failed, not enough memory");
+                       up(&cam->fileop_sem);
+                       return -ENOMEM;
+               }
+               cam->io = IO_READ;
+               cam->stream = STREAM_ON;
+       }
+
+       if (list_empty(&cam->inqueue)) {
+               if (!list_empty(&cam->outqueue))
+                       et61x251_empty_framequeues(cam);
+               et61x251_queue_unusedframes(cam);
+       }
+
+       if (!count) {
+               up(&cam->fileop_sem);
+               return 0;
+       }
+
+       if (list_empty(&cam->outqueue)) {
+               if (filp->f_flags & O_NONBLOCK) {
+                       up(&cam->fileop_sem);
+                       return -EAGAIN;
+               }
+               err = wait_event_interruptible
+                     ( cam->wait_frame,
+                       (!list_empty(&cam->outqueue)) ||
+                       (cam->state & DEV_DISCONNECTED) ||
+                       (cam->state & DEV_MISCONFIGURED) );
+               if (err) {
+                       up(&cam->fileop_sem);
+                       return err;
+               }
+               if (cam->state & DEV_DISCONNECTED) {
+                       up(&cam->fileop_sem);
+                       return -ENODEV;
+               }
+               if (cam->state & DEV_MISCONFIGURED) {
+                       up(&cam->fileop_sem);
+                       return -EIO;
+               }
+       }
+
+       f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame);
+
+       if (count > f->buf.bytesused)
+               count = f->buf.bytesused;
+
+       if (copy_to_user(buf, f->bufmem, count)) {
+               err = -EFAULT;
+               goto exit;
+       }
+       *f_pos += count;
+
+exit:
+       spin_lock_irqsave(&cam->queue_lock, lock_flags);
+       list_for_each_entry(i, &cam->outqueue, frame)
+               i->state = F_UNUSED;
+       INIT_LIST_HEAD(&cam->outqueue);
+       spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+       et61x251_queue_unusedframes(cam);
+
+       PDBGG("Frame #%lu, bytes read: %zu",
+             (unsigned long)f->buf.index, count);
+
+       up(&cam->fileop_sem);
+
+       return err ? err : count;
+}
+
+
+static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
+{
+       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+       struct et61x251_frame_t* f;
+       unsigned long lock_flags;
+       unsigned int mask = 0;
+
+       if (down_interruptible(&cam->fileop_sem))
+               return POLLERR;
+
+       if (cam->state & DEV_DISCONNECTED) {
+               DBG(1, "Device not present");
+               goto error;
+       }
+
+       if (cam->state & DEV_MISCONFIGURED) {
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
+               goto error;
+       }
+
+       if (cam->io == IO_NONE) {
+               if (!et61x251_request_buffers(cam, cam->nreadbuffers,
+                                             IO_READ)) {
+                       DBG(1, "poll() failed, not enough memory");
+                       goto error;
+               }
+               cam->io = IO_READ;
+               cam->stream = STREAM_ON;
+       }
+
+       if (cam->io == IO_READ) {
+               spin_lock_irqsave(&cam->queue_lock, lock_flags);
+               list_for_each_entry(f, &cam->outqueue, frame)
+                       f->state = F_UNUSED;
+               INIT_LIST_HEAD(&cam->outqueue);
+               spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+               et61x251_queue_unusedframes(cam);
+       }
+
+       poll_wait(filp, &cam->wait_frame, wait);
+
+       if (!list_empty(&cam->outqueue))
+               mask |= POLLIN | POLLRDNORM;
+
+       up(&cam->fileop_sem);
+
+       return mask;
+
+error:
+       up(&cam->fileop_sem);
+       return POLLERR;
+}
+
+
+static void et61x251_vm_open(struct vm_area_struct* vma)
+{
+       struct et61x251_frame_t* f = vma->vm_private_data;
+       f->vma_use_count++;
+}
+
+
+static void et61x251_vm_close(struct vm_area_struct* vma)
+{
+       /* NOTE: buffers are not freed here */
+       struct et61x251_frame_t* f = vma->vm_private_data;
+       f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct et61x251_vm_ops = {
+       .open = et61x251_vm_open,
+       .close = et61x251_vm_close,
+};
+
+
+static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+       unsigned long size = vma->vm_end - vma->vm_start,
+                     start = vma->vm_start;
+       void *pos;
+       u32 i;
+
+       if (down_interruptible(&cam->fileop_sem))
+               return -ERESTARTSYS;
+
+       if (cam->state & DEV_DISCONNECTED) {
+               DBG(1, "Device not present");
+               up(&cam->fileop_sem);
+               return -ENODEV;
+       }
+
+       if (cam->state & DEV_MISCONFIGURED) {
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
+               up(&cam->fileop_sem);
+               return -EIO;
+       }
+
+       if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+           size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+               up(&cam->fileop_sem);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < cam->nbuffers; i++) {
+               if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+                       break;
+       }
+       if (i == cam->nbuffers) {
+               up(&cam->fileop_sem);
+               return -EINVAL;
+       }
+
+       vma->vm_flags |= VM_IO;
+       vma->vm_flags |= VM_RESERVED;
+
+       pos = cam->frame[i].bufmem;
+       while (size > 0) { /* size is page-aligned */
+               if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+                       up(&cam->fileop_sem);
+                       return -EAGAIN;
+               }
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       vma->vm_ops = &et61x251_vm_ops;
+       vma->vm_private_data = &cam->frame[i];
+
+       et61x251_vm_open(vma);
+
+       up(&cam->fileop_sem);
+
+       return 0;
+}
+
+/*****************************************************************************/
+
+static int
+et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_capability cap = {
+               .driver = "et61x251",
+               .version = ET61X251_MODULE_VERSION_CODE,
+               .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+                               V4L2_CAP_STREAMING,
+       };
+
+       strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+       if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+               strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+                       sizeof(cap.bus_info));
+
+       if (copy_to_user(arg, &cap, sizeof(cap)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_input i;
+
+       if (copy_from_user(&i, arg, sizeof(i)))
+               return -EFAULT;
+
+       if (i.index)
+               return -EINVAL;
+
+       memset(&i, 0, sizeof(i));
+       strcpy(i.name, "Camera");
+
+       if (copy_to_user(arg, &i, sizeof(i)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_gs_input(struct et61x251_device* cam, void __user * arg)
+{
+       int index;
+
+       if (copy_from_user(&index, arg, sizeof(index)))
+               return -EFAULT;
+
+       if (index != 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_queryctrl qc;
+       u8 i;
+
+       if (copy_from_user(&qc, arg, sizeof(qc)))
+               return -EFAULT;
+
+       for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+               if (qc.id && qc.id == s->qctrl[i].id) {
+                       memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+                       if (copy_to_user(arg, &qc, sizeof(qc)))
+                               return -EFAULT;
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+
+
+static int
+et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_control ctrl;
+       int err = 0;
+       u8 i;
+
+       if (!s->get_ctrl && !s->set_ctrl)
+               return -EINVAL;
+
+       if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+               return -EFAULT;
+
+       if (!s->get_ctrl) {
+               for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+                       if (ctrl.id == s->qctrl[i].id) {
+                               ctrl.value = s->_qctrl[i].default_value;
+                               goto exit;
+                       }
+               return -EINVAL;
+       } else
+               err = s->get_ctrl(cam, &ctrl);
+
+exit:
+       if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+               return -EFAULT;
+
+       return err;
+}
+
+
+static int
+et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_control ctrl;
+       u8 i;
+       int err = 0;
+
+       if (!s->set_ctrl)
+               return -EINVAL;
+
+       if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+               return -EFAULT;
+
+       for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+               if (ctrl.id == s->qctrl[i].id) {
+                       if (ctrl.value < s->qctrl[i].minimum ||
+                           ctrl.value > s->qctrl[i].maximum)
+                               return -ERANGE;
+                       ctrl.value -= ctrl.value % s->qctrl[i].step;
+                       break;
+               }
+
+       if ((err = s->set_ctrl(cam, &ctrl)))
+               return err;
+
+       s->_qctrl[i].default_value = ctrl.value;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+
+       cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       cc->pixelaspect.numerator = 1;
+       cc->pixelaspect.denominator = 1;
+
+       if (copy_to_user(arg, cc, sizeof(*cc)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_crop crop = {
+               .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+       };
+
+       memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+       if (copy_to_user(arg, &crop, sizeof(crop)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_crop crop;
+       struct v4l2_rect* rect;
+       struct v4l2_rect* bounds = &(s->cropcap.bounds);
+       struct v4l2_pix_format* pix_format = &(s->pix_format);
+       u8 scale;
+       const enum et61x251_stream_state stream = cam->stream;
+       const u32 nbuffers = cam->nbuffers;
+       u32 i;
+       int err = 0;
+
+       if (copy_from_user(&crop, arg, sizeof(crop)))
+               return -EFAULT;
+
+       rect = &(crop.c);
+
+       if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (cam->module_param.force_munmap)
+               for (i = 0; i < cam->nbuffers; i++)
+                       if (cam->frame[i].vma_use_count) {
+                               DBG(3, "VIDIOC_S_CROP failed. "
+                                      "Unmap the buffers first.");
+                               return -EINVAL;
+                       }
+
+       /* Preserve R,G or B origin */
+       rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
+       rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
+
+       if (rect->width < 4)
+               rect->width = 4;
+       if (rect->height < 4)
+               rect->height = 4;
+       if (rect->width > bounds->width)
+               rect->width = bounds->width;
+       if (rect->height > bounds->height)
+               rect->height = bounds->height;
+       if (rect->left < bounds->left)
+               rect->left = bounds->left;
+       if (rect->top < bounds->top)
+               rect->top = bounds->top;
+       if (rect->left + rect->width > bounds->left + bounds->width)
+               rect->left = bounds->left+bounds->width - rect->width;
+       if (rect->top + rect->height > bounds->top + bounds->height)
+               rect->top = bounds->top+bounds->height - rect->height;
+
+       rect->width &= ~3L;
+       rect->height &= ~3L;
+
+       if (ET61X251_PRESERVE_IMGSCALE) {
+               /* Calculate the actual scaling factor */
+               u32 a, b;
+               a = rect->width * rect->height;
+               b = pix_format->width * pix_format->height;
+               scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+       } else
+               scale = 1;
+
+       if (cam->stream == STREAM_ON)
+               if ((err = et61x251_stream_interrupt(cam)))
+                       return err;
+
+       if (copy_to_user(arg, &crop, sizeof(crop))) {
+               cam->stream = stream;
+               return -EFAULT;
+       }
+
+       if (cam->module_param.force_munmap || cam->io == IO_READ)
+               et61x251_release_buffers(cam);
+
+       err = et61x251_set_crop(cam, rect);
+       if (s->set_crop)
+               err += s->set_crop(cam, rect);
+       err += et61x251_set_scale(cam, scale);
+
+       if (err) { /* atomic, no rollback in ioctl() */
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+                      "use the camera, close and open /dev/video%d again.",
+                   cam->v4ldev->minor);
+               return -EIO;
+       }
+
+       s->pix_format.width = rect->width/scale;
+       s->pix_format.height = rect->height/scale;
+       memcpy(&(s->_rect), rect, sizeof(*rect));
+
+       if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
+           nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+                      "use the camera, close and open /dev/video%d again.",
+                   cam->v4ldev->minor);
+               return -ENOMEM;
+       }
+
+       if (cam->io == IO_READ)
+               et61x251_empty_framequeues(cam);
+       else if (cam->module_param.force_munmap)
+               et61x251_requeue_outqueue(cam);
+
+       cam->stream = stream;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_fmtdesc fmtd;
+
+       if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+               return -EFAULT;
+
+       if (fmtd.index == 0) {
+               strcpy(fmtd.description, "bayer rgb");
+               fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
+       } else if (fmtd.index == 1) {
+               strcpy(fmtd.description, "compressed");
+               fmtd.pixelformat = V4L2_PIX_FMT_ET61X251;
+               fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+       } else
+               return -EINVAL;
+
+       fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+       if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_format format;
+       struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+
+       if (copy_from_user(&format, arg, sizeof(format)))
+               return -EFAULT;
+
+       if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
+                            ? 0 : (pfmt->width * pfmt->priv) / 8;
+       pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+       pfmt->field = V4L2_FIELD_NONE;
+       memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+       if (copy_to_user(arg, &format, sizeof(format)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
+                          void __user * arg)
+{
+       struct et61x251_sensor* s = cam->sensor;
+       struct v4l2_format format;
+       struct v4l2_pix_format* pix;
+       struct v4l2_pix_format* pfmt = &(s->pix_format);
+       struct v4l2_rect* bounds = &(s->cropcap.bounds);
+       struct v4l2_rect rect;
+       u8 scale;
+       const enum et61x251_stream_state stream = cam->stream;
+       const u32 nbuffers = cam->nbuffers;
+       u32 i;
+       int err = 0;
+
+       if (copy_from_user(&format, arg, sizeof(format)))
+               return -EFAULT;
+
+       pix = &(format.fmt.pix);
+
+       if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       memcpy(&rect, &(s->_rect), sizeof(rect));
+
+       { /* calculate the actual scaling factor */
+               u32 a, b;
+               a = rect.width * rect.height;
+               b = pix->width * pix->height;
+               scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+       }
+
+       rect.width = scale * pix->width;
+       rect.height = scale * pix->height;
+
+       if (rect.width < 4)
+               rect.width = 4;
+       if (rect.height < 4)
+               rect.height = 4;
+       if (rect.width > bounds->left + bounds->width - rect.left)
+               rect.width = bounds->left + bounds->width - rect.left;
+       if (rect.height > bounds->top + bounds->height - rect.top)
+               rect.height = bounds->top + bounds->height - rect.top;
+
+       rect.width &= ~3L;
+       rect.height &= ~3L;
+
+       { /* adjust the scaling factor */
+               u32 a, b;
+               a = rect.width * rect.height;
+               b = pix->width * pix->height;
+               scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+       }
+
+       pix->width = rect.width / scale;
+       pix->height = rect.height / scale;
+
+       if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 &&
+           pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+               pix->pixelformat = pfmt->pixelformat;
+       pix->priv = pfmt->priv; /* bpp */
+       pix->colorspace = pfmt->colorspace;
+       pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
+                           ? 0 : (pix->width * pix->priv) / 8;
+       pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+       pix->field = V4L2_FIELD_NONE;
+
+       if (cmd == VIDIOC_TRY_FMT) {
+               if (copy_to_user(arg, &format, sizeof(format)))
+                       return -EFAULT;
+               return 0;
+       }
+
+       if (cam->module_param.force_munmap)
+               for (i = 0; i < cam->nbuffers; i++)
+                       if (cam->frame[i].vma_use_count) {
+                               DBG(3, "VIDIOC_S_FMT failed. "
+                                      "Unmap the buffers first.");
+                               return -EINVAL;
+                       }
+
+       if (cam->stream == STREAM_ON)
+               if ((err = et61x251_stream_interrupt(cam)))
+                       return err;
+
+       if (copy_to_user(arg, &format, sizeof(format))) {
+               cam->stream = stream;
+               return -EFAULT;
+       }
+
+       if (cam->module_param.force_munmap || cam->io == IO_READ)
+               et61x251_release_buffers(cam);
+
+       err += et61x251_set_pix_format(cam, pix);
+       err += et61x251_set_crop(cam, &rect);
+       if (s->set_pix_format)
+               err += s->set_pix_format(cam, pix);
+       if (s->set_crop)
+               err += s->set_crop(cam, &rect);
+       err += et61x251_set_scale(cam, scale);
+
+       if (err) { /* atomic, no rollback in ioctl() */
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+                      "use the camera, close and open /dev/video%d again.",
+                   cam->v4ldev->minor);
+               return -EIO;
+       }
+
+       memcpy(pfmt, pix, sizeof(*pix));
+       memcpy(&(s->_rect), &rect, sizeof(rect));
+
+       if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
+           nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+                      "use the camera, close and open /dev/video%d again.",
+                   cam->v4ldev->minor);
+               return -ENOMEM;
+       }
+
+       if (cam->io == IO_READ)
+               et61x251_empty_framequeues(cam);
+       else if (cam->module_param.force_munmap)
+               et61x251_requeue_outqueue(cam);
+
+       cam->stream = stream;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg)
+{
+       if (copy_to_user(arg, &cam->compression,
+                        sizeof(cam->compression)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_jpegcompression jc;
+       const enum et61x251_stream_state stream = cam->stream;
+       int err = 0;
+
+       if (copy_from_user(&jc, arg, sizeof(jc)))
+               return -EFAULT;
+
+       if (jc.quality != 0 && jc.quality != 1)
+               return -EINVAL;
+
+       if (cam->stream == STREAM_ON)
+               if ((err = et61x251_stream_interrupt(cam)))
+                       return err;
+
+       err += et61x251_set_compression(cam, &jc);
+       if (err) { /* atomic, no rollback in ioctl() */
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+                      "problems. To use the camera, close and open "
+                      "/dev/video%d again.", cam->v4ldev->minor);
+               return -EIO;
+       }
+
+       cam->compression.quality = jc.quality;
+
+       cam->stream = stream;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_requestbuffers rb;
+       u32 i;
+       int err;
+
+       if (copy_from_user(&rb, arg, sizeof(rb)))
+               return -EFAULT;
+
+       if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           rb.memory != V4L2_MEMORY_MMAP)
+               return -EINVAL;
+
+       if (cam->io == IO_READ) {
+               DBG(3, "Close and open the device again to choose the mmap "
+                      "I/O method");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < cam->nbuffers; i++)
+               if (cam->frame[i].vma_use_count) {
+                       DBG(3, "VIDIOC_REQBUFS failed. "
+                              "Previous buffers are still mapped.");
+                       return -EINVAL;
+               }
+
+       if (cam->stream == STREAM_ON)
+               if ((err = et61x251_stream_interrupt(cam)))
+                       return err;
+
+       et61x251_empty_framequeues(cam);
+
+       et61x251_release_buffers(cam);
+       if (rb.count)
+               rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP);
+
+       if (copy_to_user(arg, &rb, sizeof(rb))) {
+               et61x251_release_buffers(cam);
+               cam->io = IO_NONE;
+               return -EFAULT;
+       }
+
+       cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_buffer b;
+
+       if (copy_from_user(&b, arg, sizeof(b)))
+               return -EFAULT;
+
+       if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           b.index >= cam->nbuffers || cam->io != IO_MMAP)
+               return -EINVAL;
+
+       memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+       if (cam->frame[b.index].vma_use_count)
+               b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+       if (cam->frame[b.index].state == F_DONE)
+               b.flags |= V4L2_BUF_FLAG_DONE;
+       else if (cam->frame[b.index].state != F_UNUSED)
+               b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+       if (copy_to_user(arg, &b, sizeof(b)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_buffer b;
+       unsigned long lock_flags;
+
+       if (copy_from_user(&b, arg, sizeof(b)))
+               return -EFAULT;
+
+       if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           b.index >= cam->nbuffers || cam->io != IO_MMAP)
+               return -EINVAL;
+
+       if (cam->frame[b.index].state != F_UNUSED)
+               return -EINVAL;
+
+       cam->frame[b.index].state = F_QUEUED;
+
+       spin_lock_irqsave(&cam->queue_lock, lock_flags);
+       list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+       spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+       PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
+                      void __user * arg)
+{
+       struct v4l2_buffer b;
+       struct et61x251_frame_t *f;
+       unsigned long lock_flags;
+       int err = 0;
+
+       if (copy_from_user(&b, arg, sizeof(b)))
+               return -EFAULT;
+
+       if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+               return -EINVAL;
+
+       if (list_empty(&cam->outqueue)) {
+               if (cam->stream == STREAM_OFF)
+                       return -EINVAL;
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               err = wait_event_interruptible
+                     ( cam->wait_frame,
+                       (!list_empty(&cam->outqueue)) ||
+                       (cam->state & DEV_DISCONNECTED) ||
+                       (cam->state & DEV_MISCONFIGURED) );
+               if (err)
+                       return err;
+               if (cam->state & DEV_DISCONNECTED)
+                       return -ENODEV;
+               if (cam->state & DEV_MISCONFIGURED)
+                       return -EIO;
+       }
+
+       spin_lock_irqsave(&cam->queue_lock, lock_flags);
+       f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame);
+       list_del(cam->outqueue.next);
+       spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+       f->state = F_UNUSED;
+
+       memcpy(&b, &f->buf, sizeof(b));
+       if (f->vma_use_count)
+               b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+       if (copy_to_user(arg, &b, sizeof(b)))
+               return -EFAULT;
+
+       PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
+{
+       int type;
+
+       if (copy_from_user(&type, arg, sizeof(type)))
+               return -EFAULT;
+
+       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+               return -EINVAL;
+
+       if (list_empty(&cam->inqueue))
+               return -EINVAL;
+
+       cam->stream = STREAM_ON;
+
+       DBG(3, "Stream on");
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg)
+{
+       int type, err;
+
+       if (copy_from_user(&type, arg, sizeof(type)))
+               return -EFAULT;
+
+       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+               return -EINVAL;
+
+       if (cam->stream == STREAM_ON)
+               if ((err = et61x251_stream_interrupt(cam)))
+                       return err;
+
+       et61x251_empty_framequeues(cam);
+
+       DBG(3, "Stream off");
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_streamparm sp;
+
+       if (copy_from_user(&sp, arg, sizeof(sp)))
+               return -EFAULT;
+
+       if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       sp.parm.capture.extendedmode = 0;
+       sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+       if (copy_to_user(arg, &sp, sizeof(sp)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int
+et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_streamparm sp;
+
+       if (copy_from_user(&sp, arg, sizeof(sp)))
+               return -EFAULT;
+
+       if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       sp.parm.capture.extendedmode = 0;
+
+       if (sp.parm.capture.readbuffers == 0)
+               sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+       if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES)
+               sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES;
+
+       if (copy_to_user(arg, &sp, sizeof(sp)))
+               return -EFAULT;
+
+       cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+       return 0;
+}
+
+
+static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
+                               unsigned int cmd, void __user * arg)
+{
+       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+
+       switch (cmd) {
+
+       case VIDIOC_QUERYCAP:
+               return et61x251_vidioc_querycap(cam, arg);
+
+       case VIDIOC_ENUMINPUT:
+               return et61x251_vidioc_enuminput(cam, arg);
+
+       case VIDIOC_G_INPUT:
+       case VIDIOC_S_INPUT:
+               return et61x251_vidioc_gs_input(cam, arg);
+
+       case VIDIOC_QUERYCTRL:
+               return et61x251_vidioc_query_ctrl(cam, arg);
+
+       case VIDIOC_G_CTRL:
+               return et61x251_vidioc_g_ctrl(cam, arg);
+
+       case VIDIOC_S_CTRL_OLD:
+       case VIDIOC_S_CTRL:
+               return et61x251_vidioc_s_ctrl(cam, arg);
+
+       case VIDIOC_CROPCAP_OLD:
+       case VIDIOC_CROPCAP:
+               return et61x251_vidioc_cropcap(cam, arg);
+
+       case VIDIOC_G_CROP:
+               return et61x251_vidioc_g_crop(cam, arg);
+
+       case VIDIOC_S_CROP:
+               return et61x251_vidioc_s_crop(cam, arg);
+
+       case VIDIOC_ENUM_FMT:
+               return et61x251_vidioc_enum_fmt(cam, arg);
+
+       case VIDIOC_G_FMT:
+               return et61x251_vidioc_g_fmt(cam, arg);
+
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_S_FMT:
+               return et61x251_vidioc_try_s_fmt(cam, cmd, arg);
+
+       case VIDIOC_G_JPEGCOMP:
+               return et61x251_vidioc_g_jpegcomp(cam, arg);
+
+       case VIDIOC_S_JPEGCOMP:
+               return et61x251_vidioc_s_jpegcomp(cam, arg);
+
+       case VIDIOC_REQBUFS:
+               return et61x251_vidioc_reqbufs(cam, arg);
+
+       case VIDIOC_QUERYBUF:
+               return et61x251_vidioc_querybuf(cam, arg);
+
+       case VIDIOC_QBUF:
+               return et61x251_vidioc_qbuf(cam, arg);
+
+       case VIDIOC_DQBUF:
+               return et61x251_vidioc_dqbuf(cam, filp, arg);
+
+       case VIDIOC_STREAMON:
+               return et61x251_vidioc_streamon(cam, arg);
+
+       case VIDIOC_STREAMOFF:
+               return et61x251_vidioc_streamoff(cam, arg);
+
+       case VIDIOC_G_PARM:
+               return et61x251_vidioc_g_parm(cam, arg);
+
+       case VIDIOC_S_PARM_OLD:
+       case VIDIOC_S_PARM:
+               return et61x251_vidioc_s_parm(cam, arg);
+
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_QUERYSTD:
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_QUERYMENU:
+               return -EINVAL;
+
+       default:
+               return -EINVAL;
+
+       }
+}
+
+
+static int et61x251_ioctl(struct inode* inode, struct file* filp,
+                         unsigned int cmd, unsigned long arg)
+{
+       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+       int err = 0;
+
+       if (down_interruptible(&cam->fileop_sem))
+               return -ERESTARTSYS;
+
+       if (cam->state & DEV_DISCONNECTED) {
+               DBG(1, "Device not present");
+               up(&cam->fileop_sem);
+               return -ENODEV;
+       }
+
+       if (cam->state & DEV_MISCONFIGURED) {
+               DBG(1, "The camera is misconfigured. Close and open it "
+                      "again.");
+               up(&cam->fileop_sem);
+               return -EIO;
+       }
+
+       V4LDBG(3, "et61x251", cmd);
+
+       err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
+
+       up(&cam->fileop_sem);
+
+       return err;
+}
+
+
+static struct file_operations et61x251_fops = {
+       .owner = THIS_MODULE,
+       .open =    et61x251_open,
+       .release = et61x251_release,
+       .ioctl =   et61x251_ioctl,
+       .read =    et61x251_read,
+       .poll =    et61x251_poll,
+       .mmap =    et61x251_mmap,
+       .llseek =  no_llseek,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct et61x251_device* cam;
+       static unsigned int dev_nr = 0;
+       unsigned int i;
+       int err = 0;
+
+       if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL)))
+               return -ENOMEM;
+
+       cam->usbdev = udev;
+
+       if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
+               DBG(1, "kmalloc() failed");
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       if (!(cam->v4ldev = video_device_alloc())) {
+               DBG(1, "video_device_alloc() failed");
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       init_MUTEX(&cam->dev_sem);
+
+       DBG(2, "ET61X[12]51 PC Camera Controller detected "
+              "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
+
+       for  (i = 0; et61x251_sensor_table[i]; i++) {
+               err = et61x251_sensor_table[i](cam);
+               if (!err)
+                       break;
+       }
+
+       if (!err && cam->sensor)
+               DBG(2, "%s image sensor detected", cam->sensor->name);
+       else {
+               DBG(1, "No supported image sensor detected");
+               err = -ENODEV;
+               goto fail;
+       }
+
+       if (et61x251_init(cam)) {
+               DBG(1, "Initialization failed. I will retry on open().");
+               cam->state |= DEV_MISCONFIGURED;
+       }
+
+       strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera");
+       cam->v4ldev->owner = THIS_MODULE;
+       cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+       cam->v4ldev->hardware = 0;
+       cam->v4ldev->fops = &et61x251_fops;
+       cam->v4ldev->minor = video_nr[dev_nr];
+       cam->v4ldev->release = video_device_release;
+       video_set_drvdata(cam->v4ldev, cam);
+
+       down(&cam->dev_sem);
+
+       err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+                                   video_nr[dev_nr]);
+       if (err) {
+               DBG(1, "V4L2 device registration failed");
+               if (err == -ENFILE && video_nr[dev_nr] == -1)
+                       DBG(1, "Free /dev/videoX node not found");
+               video_nr[dev_nr] = -1;
+               dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
+               up(&cam->dev_sem);
+               goto fail;
+       }
+
+       DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
+
+       cam->module_param.force_munmap = force_munmap[dev_nr];
+
+       dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       et61x251_create_sysfs(cam);
+       DBG(2, "Optional device control through 'sysfs' interface ready");
+#endif
+
+       usb_set_intfdata(intf, cam);
+
+       up(&cam->dev_sem);
+
+       return 0;
+
+fail:
+       if (cam) {
+               kfree(cam->control_buffer);
+               if (cam->v4ldev)
+                       video_device_release(cam->v4ldev);
+               kfree(cam);
+       }
+       return err;
+}
+
+
+static void et61x251_usb_disconnect(struct usb_interface* intf)
+{
+       struct et61x251_device* cam = usb_get_intfdata(intf);
+
+       if (!cam)
+               return;
+
+       down_write(&et61x251_disconnect);
+
+       down(&cam->dev_sem);
+
+       DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+       wake_up_interruptible_all(&cam->open);
+
+       if (cam->users) {
+               DBG(2, "Device /dev/video%d is open! Deregistration and "
+                      "memory deallocation are deferred on close.",
+                   cam->v4ldev->minor);
+               cam->state |= DEV_MISCONFIGURED;
+               et61x251_stop_transfer(cam);
+               cam->state |= DEV_DISCONNECTED;
+               wake_up_interruptible(&cam->wait_frame);
+               wake_up_interruptible(&cam->wait_stream);
+       } else {
+               cam->state |= DEV_DISCONNECTED;
+               et61x251_release_resources(cam);
+       }
+
+       up(&cam->dev_sem);
+
+       if (!cam->users)
+               kfree(cam);
+
+       up_write(&et61x251_disconnect);
+}
+
+
+static struct usb_driver et61x251_usb_driver = {
+       .name =       "et61x251",
+       .id_table =   et61x251_id_table,
+       .probe =      et61x251_usb_probe,
+       .disconnect = et61x251_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init et61x251_module_init(void)
+{
+       int err = 0;
+
+       KDBG(2, ET61X251_MODULE_NAME " v" ET61X251_MODULE_VERSION);
+       KDBG(3, ET61X251_MODULE_AUTHOR);
+
+       if ((err = usb_register(&et61x251_usb_driver)))
+               KDBG(1, "usb_register() failed");
+
+       return err;
+}
+
+
+static void __exit et61x251_module_exit(void)
+{
+       usb_deregister(&et61x251_usb_driver);
+}
+
+
+module_init(et61x251_module_init);
+module_exit(et61x251_module_exit);
diff --git a/drivers/usb/media/et61x251_sensor.h b/drivers/usb/media/et61x251_sensor.h
new file mode 100644 (file)
index 0000000..b9df910
--- /dev/null
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * API for image sensors connected to ET61X[12]51 PC Camera Controllers    *
+ *                                                                         *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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.               *
+ ***************************************************************************/
+
+#ifndef _ET61X251_SENSOR_H_
+#define _ET61X251_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct et61x251_device;
+struct et61x251_sensor;
+
+/*****************************************************************************/
+
+extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam);
+
+#define ET61X251_SENSOR_TABLE                                                 \
+/* Weak detections must go at the end of the list */                          \
+static int (*et61x251_sensor_table[])(struct et61x251_device*) = {            \
+       &et61x251_probe_tas5130d1b,                                           \
+       NULL,                                                                 \
+};
+
+extern void
+et61x251_attach_sensor(struct et61x251_device* cam,
+                       struct et61x251_sensor* sensor);
+
+/*****************************************************************************/
+
+extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index);
+extern int et61x251_read_reg(struct et61x251_device*, u16 index);
+extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value);
+extern int et61x251_i2c_read(struct et61x251_device*, u8 address);
+extern int et61x251_i2c_try_write(struct et61x251_device*,
+                                  struct et61x251_sensor*, u8 address,
+                                  u8 value);
+extern int et61x251_i2c_try_read(struct et61x251_device*,
+                                 struct et61x251_sensor*, u8 address);
+extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
+                                  u8 data2, u8 data3, u8 data4, u8 data5,
+                                  u8 data6, u8 data7, u8 data8, u8 address);
+
+/*****************************************************************************/
+
+enum et61x251_i2c_sysfs_ops {
+       ET61X251_I2C_READ = 0x01,
+       ET61X251_I2C_WRITE = 0x02,
+};
+
+enum et61x251_i2c_interface {
+       ET61X251_I2C_2WIRES,
+       ET61X251_I2C_3WIRES,
+};
+
+/* Repeat start condition when RSTA is high */
+enum et61x251_i2c_rsta {
+       ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */
+       ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */
+};
+
+#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10
+
+struct et61x251_sensor {
+       char name[32];
+
+       enum et61x251_i2c_sysfs_ops sysfs_ops;
+
+       enum et61x251_i2c_interface interface;
+       u8 i2c_slave_id;
+       enum et61x251_i2c_rsta rsta;
+       struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */
+
+       struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS];
+       struct v4l2_cropcap cropcap;
+       struct v4l2_pix_format pix_format;
+
+       int (*init)(struct et61x251_device* cam);
+       int (*get_ctrl)(struct et61x251_device* cam,
+                       struct v4l2_control* ctrl);
+       int (*set_ctrl)(struct et61x251_device* cam,
+                       const struct v4l2_control* ctrl);
+       int (*set_crop)(struct et61x251_device* cam,
+                       const struct v4l2_rect* rect);
+       int (*set_pix_format)(struct et61x251_device* cam,
+                             const struct v4l2_pix_format* pix);
+
+       const struct usb_device* usbdev;
+
+       /* Private */
+       struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS];
+       struct v4l2_rect _rect;
+};
+
+#endif /* _ET61X251_SENSOR_H_ */
diff --git a/drivers/usb/media/et61x251_tas5130d1b.c b/drivers/usb/media/et61x251_tas5130d1b.c
new file mode 100644 (file)
index 0000000..65f1ae9
--- /dev/null
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51        *
+ * PC Camera Controllers                                                   *
+ *                                                                         *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ *                                                                         *
+ * 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.               *
+ ***************************************************************************/
+
+#include "et61x251_sensor.h"
+
+
+static int tas5130d1b_init(struct et61x251_device* cam)
+{
+       int err = 0;
+
+       err += et61x251_write_reg(cam, 0x14, 0x01);
+       err += et61x251_write_reg(cam, 0x1b, 0x02);
+       err += et61x251_write_reg(cam, 0x02, 0x12);
+       err += et61x251_write_reg(cam, 0x0e, 0x60);
+       err += et61x251_write_reg(cam, 0x80, 0x61);
+       err += et61x251_write_reg(cam, 0xf0, 0x62);
+       err += et61x251_write_reg(cam, 0x03, 0x63);
+       err += et61x251_write_reg(cam, 0x14, 0x64);
+       err += et61x251_write_reg(cam, 0xf4, 0x65);
+       err += et61x251_write_reg(cam, 0x01, 0x66);
+       err += et61x251_write_reg(cam, 0x05, 0x67);
+       err += et61x251_write_reg(cam, 0x8f, 0x68);
+       err += et61x251_write_reg(cam, 0x0f, 0x8d);
+       err += et61x251_write_reg(cam, 0x08, 0x8e);
+
+       return err;
+}
+
+
+static int tas5130d1b_set_ctrl(struct et61x251_device* cam,
+                               const struct v4l2_control* ctrl)
+{
+       int err = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_GAIN:
+               err += et61x251_i2c_raw_write(cam, 2, 0x20,
+                                             0xf6-ctrl->value, 0, 0, 0,
+                                             0, 0, 0, 0);
+               break;
+       case V4L2_CID_EXPOSURE:
+               err += et61x251_i2c_raw_write(cam, 2, 0x40,
+                                             0x47-ctrl->value, 0, 0, 0,
+                                             0, 0, 0, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return err ? -EIO : 0;
+}
+
+
+static struct et61x251_sensor tas5130d1b = {
+       .name = "TAS5130D1B",
+       .interface = ET61X251_I2C_3WIRES,
+       .rsta = ET61X251_I2C_RSTA_STOP,
+       .active_pixel = {
+               .left = 106,
+               .top = 13,
+       },
+       .init = &tas5130d1b_init,
+       .qctrl = {
+               {
+                       .id = V4L2_CID_GAIN,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "global gain",
+                       .minimum = 0x00,
+                       .maximum = 0xf6,
+                       .step = 0x02,
+                       .default_value = 0x0d,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "exposure",
+                       .minimum = 0x00,
+                       .maximum = 0x47,
+                       .step = 0x01,
+                       .default_value = 0x23,
+                       .flags = 0,
+               },
+       },
+       .set_ctrl = &tas5130d1b_set_ctrl,
+       .cropcap = {
+               .bounds = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+               .defrect = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+       },
+       .pix_format = {
+               .width = 640,
+               .height = 480,
+               .pixelformat = V4L2_PIX_FMT_SBGGR8,
+               .priv = 8,
+       },
+};
+
+
+int et61x251_probe_tas5130d1b(struct et61x251_device* cam)
+{
+       /* This sensor has no identifiers, so let's attach it anyway */
+       et61x251_attach_sensor(cam, &tas5130d1b);
+
+       /* Sensor detection is based on USB pid/vid */
+       if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6251)
+               return -ENODEV;
+
+       return 0;
+}
diff --git a/drivers/usb/media/sn9c102_ov7630.c b/drivers/usb/media/sn9c102_ov7630.c
new file mode 100644 (file)
index 0000000..4a36519
--- /dev/null
@@ -0,0 +1,396 @@
+/***************************************************************************
+ * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera      *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2005-2006 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ *                                                                         *
+ * 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.               *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor ov7630;
+
+
+static int ov7630_init(struct sn9c102_device* cam)
+{
+       int err = 0;
+
+       err += sn9c102_write_reg(cam, 0x00, 0x14);
+       err += sn9c102_write_reg(cam, 0x60, 0x17);
+       err += sn9c102_write_reg(cam, 0x0f, 0x18);
+       err += sn9c102_write_reg(cam, 0x50, 0x19);
+
+       err += sn9c102_i2c_write(cam, 0x12, 0x8d);
+       err += sn9c102_i2c_write(cam, 0x11, 0x00);
+       err += sn9c102_i2c_write(cam, 0x15, 0x34);
+       err += sn9c102_i2c_write(cam, 0x16, 0x03);
+       err += sn9c102_i2c_write(cam, 0x17, 0x1c);
+       err += sn9c102_i2c_write(cam, 0x18, 0xbd);
+       err += sn9c102_i2c_write(cam, 0x19, 0x06);
+       err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
+       err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+       err += sn9c102_i2c_write(cam, 0x20, 0x44);
+       err += sn9c102_i2c_write(cam, 0x23, 0xee);
+       err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+       err += sn9c102_i2c_write(cam, 0x27, 0x9a);
+       err += sn9c102_i2c_write(cam, 0x28, 0x20);
+       err += sn9c102_i2c_write(cam, 0x29, 0x30);
+       err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
+       err += sn9c102_i2c_write(cam, 0x30, 0x24);
+       err += sn9c102_i2c_write(cam, 0x32, 0x86);
+       err += sn9c102_i2c_write(cam, 0x60, 0xa9);
+       err += sn9c102_i2c_write(cam, 0x61, 0x42);
+       err += sn9c102_i2c_write(cam, 0x65, 0x00);
+       err += sn9c102_i2c_write(cam, 0x69, 0x38);
+       err += sn9c102_i2c_write(cam, 0x6f, 0x88);
+       err += sn9c102_i2c_write(cam, 0x70, 0x0b);
+       err += sn9c102_i2c_write(cam, 0x71, 0x00);
+       err += sn9c102_i2c_write(cam, 0x74, 0x21);
+       err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+
+       return err;
+}
+
+
+static int ov7630_set_ctrl(struct sn9c102_device* cam,
+                           const struct v4l2_control* ctrl)
+{
+       int err = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               err += sn9c102_i2c_write(cam, 0x10, ctrl->value >> 2);
+               err += sn9c102_i2c_write(cam, 0x76, ctrl->value & 0x03);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x02, ctrl->value);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x03, ctrl->value);
+               break;
+       case V4L2_CID_GAIN:
+               err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
+               break;
+       case V4L2_CID_CONTRAST:
+               err += ctrl->value ? sn9c102_i2c_write(cam, 0x05,
+                                                      (ctrl->value-1) | 0x20)
+                                  : sn9c102_i2c_write(cam, 0x05, 0x00);
+               break;
+       case V4L2_CID_BRIGHTNESS:
+               err += sn9c102_i2c_write(cam, 0x06, ctrl->value);
+               break;
+       case V4L2_CID_SATURATION:
+               err += sn9c102_i2c_write(cam, 0x03, ctrl->value << 4);
+               break;
+       case V4L2_CID_HUE:
+               err += ctrl->value ? sn9c102_i2c_write(cam, 0x04,
+                                                      (ctrl->value-1) | 0x20)
+                                  : sn9c102_i2c_write(cam, 0x04, 0x00);
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+               break;
+       case V4L2_CID_WHITENESS:
+               err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x09);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               err += sn9c102_i2c_write(cam, 0x13, ctrl->value);
+               break;
+       case V4L2_CID_VFLIP:
+               err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7));
+               break;
+       case V4L2_CID_BLACK_LEVEL:
+               err += sn9c102_i2c_write(cam, 0x25, ctrl->value);
+               break;
+       case SN9C102_V4L2_CID_BRIGHT_LEVEL:
+               err += sn9c102_i2c_write(cam, 0x24, ctrl->value);
+               break;
+       case SN9C102_V4L2_CID_GAMMA:
+               err += sn9c102_i2c_write(cam, 0x14, (ctrl->value << 2) | 0x80);
+               break;
+       case SN9C102_V4L2_CID_BAND_FILTER:
+               err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return err ? -EIO : 0;
+}
+
+
+static int ov7630_set_crop(struct sn9c102_device* cam,
+                           const struct v4l2_rect* rect)
+{
+       struct sn9c102_sensor* s = &ov7630;
+       int err = 0;
+       u8 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+       err += sn9c102_write_reg(cam, v_start, 0x13);
+
+       return err;
+}
+
+
+static int ov7630_set_pix_format(struct sn9c102_device* cam,
+                                 const struct v4l2_pix_format* pix)
+{
+       int err = 0;
+
+       if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+               err += sn9c102_write_reg(cam, 0x20, 0x19);
+       else
+               err += sn9c102_write_reg(cam, 0x50, 0x19);
+
+       return err;
+}
+
+
+static struct sn9c102_sensor ov7630 = {
+       .name = "OV7630",
+       .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+       .sysfs_ops = SN9C102_I2C_WRITE,
+       .frequency = SN9C102_I2C_100KHZ,
+       .interface = SN9C102_I2C_2WIRES,
+       .i2c_slave_id = 0x21,
+       .init = &ov7630_init,
+       .qctrl = {
+               {
+                       .id = V4L2_CID_GAIN,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "global gain",
+                       .minimum = 0x00,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x14,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_HUE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "hue",
+                       .minimum = 0x00,
+                       .maximum = 0x1f+1,
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_SATURATION,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "saturation",
+                       .minimum = 0x00,
+                       .maximum = 0x0f,
+                       .step = 0x01,
+                       .default_value = 0x08,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_CONTRAST,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "contrast",
+                       .minimum = 0x00,
+                       .maximum = 0x1f+1,
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "exposure",
+                       .minimum = 0x000,
+                       .maximum = 0x3ff,
+                       .step = 0x001,
+                       .default_value = 0x83<<2,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_RED_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "red balance",
+                       .minimum = 0x00,
+                       .maximum = 0xff,
+                       .step = 0x01,
+                       .default_value = 0x3a,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_BLUE_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "blue balance",
+                       .minimum = 0x00,
+                       .maximum = 0xff,
+                       .step = 0x01,
+                       .default_value = 0x77,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_BRIGHTNESS,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "brightness",
+                       .minimum = 0x00,
+                       .maximum = 0xff,
+                       .step = 0x01,
+                       .default_value = 0xa0,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_DO_WHITE_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "white balance background: blue",
+                       .minimum = 0x00,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x20,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_WHITENESS,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "white balance background: red",
+                       .minimum = 0x00,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x20,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_AUTO_WHITE_BALANCE,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "auto white balance",
+                       .minimum = 0x00,
+                       .maximum = 0x01,
+                       .step = 0x01,
+                       .default_value = 0x01,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_AUTOGAIN,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "gain & exposure mode",
+                       .minimum = 0x00,
+                       .maximum = 0x03,
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_VFLIP,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "vertical flip",
+                       .minimum = 0x00,
+                       .maximum = 0x01,
+                       .step = 0x01,
+                       .default_value = 0x01,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_BLACK_LEVEL,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "black pixel ratio",
+                       .minimum = 0x01,
+                       .maximum = 0x9a,
+                       .step = 0x01,
+                       .default_value = 0x8a,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_BRIGHT_LEVEL,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "bright pixel ratio",
+                       .minimum = 0x01,
+                       .maximum = 0x9a,
+                       .step = 0x01,
+                       .default_value = 0x10,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_BAND_FILTER,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "band filter",
+                       .minimum = 0x00,
+                       .maximum = 0x01,
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_GAMMA,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "rgb gamma",
+                       .minimum = 0x00,
+                       .maximum = 0x01,
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+       },
+       .set_ctrl = &ov7630_set_ctrl,
+       .cropcap = {
+               .bounds = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+               .defrect = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+       },
+       .set_crop = &ov7630_set_crop,
+       .pix_format = {
+               .width = 640,
+               .height = 480,
+               .pixelformat = V4L2_PIX_FMT_SBGGR8,
+               .priv = 8,
+       },
+       .set_pix_format = &ov7630_set_pix_format
+};
+
+
+int sn9c102_probe_ov7630(struct sn9c102_device* cam)
+{
+       int err = 0;
+
+       sn9c102_attach_sensor(cam, &ov7630);
+
+       if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c &&
+           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602d &&
+           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f &&
+           le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x60b0)
+               return -ENODEV;
+
+       err += sn9c102_write_reg(cam, 0x01, 0x01);
+       err += sn9c102_write_reg(cam, 0x00, 0x01);
+       err += sn9c102_write_reg(cam, 0x28, 0x17);
+
+       if (err)
+               return -EIO;
+
+       err += sn9c102_i2c_write(cam, 0x0b, 0);
+       if (err)
+               return -ENODEV;
+
+       return 0;
+}
diff --git a/drivers/w1/w1_ds2433.c b/drivers/w1/w1_ds2433.c
new file mode 100644 (file)
index 0000000..1e3d98a
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ *     w1_ds2433.c - w1 family 23 (DS2433) driver
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#ifdef CONFIG_W1_F23_CRC
+#include <linux/crc16.h>
+
+#define CRC16_INIT             0
+#define CRC16_VALID            0xb001
+
+#endif
+
+#include "w1.h"
+#include "w1_io.h"
+#include "w1_int.h"
+#include "w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM");
+
+#define W1_EEPROM_SIZE         512
+#define W1_PAGE_COUNT          16
+#define W1_PAGE_SIZE           32
+#define W1_PAGE_BITS           5
+#define W1_PAGE_MASK           0x1F
+
+#define W1_F23_TIME            300
+
+#define W1_F23_READ_EEPROM     0xF0
+#define W1_F23_WRITE_SCRATCH   0x0F
+#define W1_F23_READ_SCRATCH    0xAA
+#define W1_F23_COPY_SCRATCH    0x55
+
+struct w1_f23_data {
+       u8      memory[W1_EEPROM_SIZE];
+       u32     validcrc;
+};
+
+/**
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size)
+{
+       if (off > size)
+               return 0;
+
+       if ((off + count) > size)
+               return (size - off);
+
+       return count;
+}
+
+#ifdef CONFIG_W1_F23_CRC
+static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,
+                               int block)
+{
+       u8      wrbuf[3];
+       int     off = block * W1_PAGE_SIZE;
+
+       if (data->validcrc & (1 << block))
+               return 0;
+
+       if (w1_reset_select_slave(sl)) {
+               data->validcrc = 0;
+               return -EIO;
+       }
+
+       wrbuf[0] = W1_F23_READ_EEPROM;
+       wrbuf[1] = off & 0xff;
+       wrbuf[2] = off >> 8;
+       w1_write_block(sl->master, wrbuf, 3);
+       w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+       /* cache the block if the CRC is valid */
+       if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+               data->validcrc |= (1 << block);
+
+       return 0;
+}
+#endif /* CONFIG_W1_F23_CRC */
+
+static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off,
+                              size_t count)
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+#ifdef CONFIG_W1_F23_CRC
+       struct w1_f23_data *data = sl->family_data;
+       int i, min_page, max_page;
+#else
+       u8 wrbuf[3];
+#endif
+
+       if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+               return 0;
+
+       atomic_inc(&sl->refcnt);
+       if (down_interruptible(&sl->master->mutex)) {
+               count = 0;
+               goto out_dec;
+       }
+
+#ifdef CONFIG_W1_F23_CRC
+
+       min_page = (off >> W1_PAGE_BITS);
+       max_page = (off + count - 1) >> W1_PAGE_BITS;
+       for (i = min_page; i <= max_page; i++) {
+               if (w1_f23_refresh_block(sl, data, i)) {
+                       count = -EIO;
+                       goto out_up;
+               }
+       }
+       memcpy(buf, &data->memory[off], count);
+
+#else  /* CONFIG_W1_F23_CRC */
+
+       /* read directly from the EEPROM */
+       if (w1_reset_select_slave(sl)) {
+               count = -EIO;
+               goto out_up;
+       }
+
+       wrbuf[0] = W1_F23_READ_EEPROM;
+       wrbuf[1] = off & 0xff;
+       wrbuf[2] = off >> 8;
+       w1_write_block(sl->master, wrbuf, 3);
+       w1_read_block(sl->master, buf, count);
+
+#endif /* CONFIG_W1_F23_CRC */
+
+out_up:
+       up(&sl->master->mutex);
+out_dec:
+       atomic_dec(&sl->refcnt);
+
+       return count;
+}
+
+/**
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be on one page.
+ * The master must be locked.
+ *
+ * @param sl   The slave structure
+ * @param addr Address for the write
+ * @param len   length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
+ * @param data The data to write
+ * @return     0=Success -1=failure
+ */
+static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+       u8 wrbuf[4];
+       u8 rdbuf[W1_PAGE_SIZE + 3];
+       u8 es = (addr + len - 1) & 0x1f;
+
+       /* Write the data to the scratchpad */
+       if (w1_reset_select_slave(sl))
+               return -1;
+
+       wrbuf[0] = W1_F23_WRITE_SCRATCH;
+       wrbuf[1] = addr & 0xff;
+       wrbuf[2] = addr >> 8;
+
+       w1_write_block(sl->master, wrbuf, 3);
+       w1_write_block(sl->master, data, len);
+
+       /* Read the scratchpad and verify */
+       if (w1_reset_select_slave(sl))
+               return -1;
+
+       w1_write_8(sl->master, W1_F23_READ_SCRATCH);
+       w1_read_block(sl->master, rdbuf, len + 3);
+
+       /* Compare what was read against the data written */
+       if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+           (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
+               return -1;
+
+       /* Copy the scratchpad to EEPROM */
+       if (w1_reset_select_slave(sl))
+               return -1;
+
+       wrbuf[0] = W1_F23_COPY_SCRATCH;
+       wrbuf[3] = es;
+       w1_write_block(sl->master, wrbuf, 4);
+
+       /* Sleep for 5 ms to wait for the write to complete */
+       msleep(5);
+
+       /* Reset the bus to wake up the EEPROM (this may not be needed) */
+       w1_reset_bus(sl->master);
+
+       return 0;
+}
+
+static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off,
+                               size_t count)
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       int addr, len, idx;
+
+       if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+               return 0;
+
+#ifdef CONFIG_W1_F23_CRC
+       /* can only write full blocks in cached mode */
+       if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
+               dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
+                       (int)off, count);
+               return -EINVAL;
+       }
+
+       /* make sure the block CRCs are valid */
+       for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
+               if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) {
+                       dev_err(&sl->dev, "bad CRC at offset %d\n", (int)off);
+                       return -EINVAL;
+               }
+       }
+#endif /* CONFIG_W1_F23_CRC */
+
+       atomic_inc(&sl->refcnt);
+       if (down_interruptible(&sl->master->mutex)) {
+               count = 0;
+               goto out_dec;
+       }
+
+       /* Can only write data to one page at a time */
+       idx = 0;
+       while (idx < count) {
+               addr = off + idx;
+               len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
+               if (len > (count - idx))
+                       len = count - idx;
+
+               if (w1_f23_write(sl, addr, len, &buf[idx]) < 0) {
+                       count = -EIO;
+                       goto out_up;
+               }
+               idx += len;
+       }
+
+out_up:
+       up(&sl->master->mutex);
+out_dec:
+       atomic_dec(&sl->refcnt);
+
+       return count;
+}
+
+static struct bin_attribute w1_f23_bin_attr = {
+       .attr = {
+               .name = "eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+               .owner = THIS_MODULE,
+       },
+       .size = W1_EEPROM_SIZE,
+       .read = w1_f23_read_bin,
+       .write = w1_f23_write_bin,
+};
+
+static int w1_f23_add_slave(struct w1_slave *sl)
+{
+       int err;
+#ifdef CONFIG_W1_F23_CRC
+       struct w1_f23_data *data;
+
+       data = kmalloc(sizeof(struct w1_f23_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       memset(data, 0, sizeof(struct w1_f23_data));
+       sl->family_data = data;
+
+#endif /* CONFIG_W1_F23_CRC */
+
+       err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
+
+#ifdef CONFIG_W1_F23_CRC
+       if (err)
+               kfree(data);
+#endif /* CONFIG_W1_F23_CRC */
+
+       return err;
+}
+
+static void w1_f23_remove_slave(struct w1_slave *sl)
+{
+#ifdef CONFIG_W1_F23_CRC
+       kfree(sl->family_data);
+       sl->family_data = NULL;
+#endif /* CONFIG_W1_F23_CRC */
+       sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
+}
+
+static struct w1_family_ops w1_f23_fops = {
+       .add_slave      = w1_f23_add_slave,
+       .remove_slave   = w1_f23_remove_slave,
+};
+
+static struct w1_family w1_family_23 = {
+       .fid = W1_EEPROM_DS2433,
+       .fops = &w1_f23_fops,
+};
+
+static int __init w1_f23_init(void)
+{
+       return w1_register_family(&w1_family_23);
+}
+
+static void __exit w1_f23_fini(void)
+{
+       w1_unregister_family(&w1_family_23);
+}
+
+module_init(w1_f23_init);
+module_exit(w1_f23_fini);
diff --git a/fs/9p/9p.c b/fs/9p/9p.c
new file mode 100644 (file)
index 0000000..f86a28d
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ *  linux/fs/9p/9p.c
+ *
+ *  This file contains functions to perform synchronous 9P calls
+ *
+ *  Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net>
+ *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
+ *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
+ *
+ *  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:
+ *  Free Software Foundation
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02111-1301  USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+
+#include "debug.h"
+#include "v9fs.h"
+#include "9p.h"
+#include "conv.h"
+#include "mux.h"
+
+/**
+ * v9fs_t_version - negotiate protocol parameters with sever
+ * @v9ses: 9P2000 session information
+ * @msize: requested max size packet
+ * @version: requested version.extension string
+ * @fcall: pointer to response fcall pointer
+ *
+ */
+
+int
+v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize,
+              char *version, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version);
+       tc = v9fs_create_tversion(msize, version);
+
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_attach - mount the server
+ * @v9ses: 9P2000 session information
+ * @uname: user name doing the attach
+ * @aname: remote name being attached to
+ * @fid: mount fid to attatch to root node
+ * @afid: authentication fid (in this case result key)
+ * @fcall: pointer to response fcall pointer
+ *
+ */
+
+int
+v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname,
+             u32 fid, u32 afid, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall* tc;
+
+       dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname,
+               aname, fid, afid);
+
+       tc = v9fs_create_tattach(fid, afid, uname, aname);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+static void v9fs_t_clunk_cb(void *a, struct v9fs_fcall *tc,
+       struct v9fs_fcall *rc, int err)
+{
+       int fid;
+       struct v9fs_session_info *v9ses;
+
+       if (err)
+               return;
+
+       fid = tc->params.tclunk.fid;
+       kfree(tc);
+
+       if (!rc)
+               return;
+
+       v9ses = a;
+       if (rc->id == RCLUNK)
+               v9fs_put_idpool(fid, &v9ses->fidpool);
+
+       kfree(rc);
+}
+
+/**
+ * v9fs_t_clunk - release a fid (finish a transaction)
+ * @v9ses: 9P2000 session information
+ * @fid: fid to release
+ * @fcall: pointer to response fcall pointer
+ *
+ */
+
+int
+v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid)
+{
+       int ret;
+       struct v9fs_fcall *tc, *rc;
+
+       dprintk(DEBUG_9P, "fid %d\n", fid);
+
+       rc = NULL;
+       tc = v9fs_create_tclunk(fid);
+       if (!IS_ERR(tc))
+               ret = v9fs_mux_rpc(v9ses->mux, tc, &rc);
+       else
+               ret = PTR_ERR(tc);
+
+       if (ret)
+               dprintk(DEBUG_ERROR, "failed fid %d err %d\n", fid, ret);
+
+       v9fs_t_clunk_cb(v9ses, tc, rc, ret);
+       return ret;
+}
+
+/**
+ * v9fs_v9fs_t_flush - flush a pending transaction
+ * @v9ses: 9P2000 session information
+ * @tag: tid to release
+ *
+ */
+
+int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "oldtag %d\n", oldtag);
+
+       tc = v9fs_create_tflush(oldtag);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, NULL);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_stat - read a file's meta-data
+ * @v9ses: 9P2000 session information
+ * @fid: fid pointing to file or directory to get info about
+ * @fcall: pointer to response fcall
+ *
+ */
+
+int
+v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "fid %d\n", fid);
+
+       ret = -ENOMEM;
+       tc = v9fs_create_tstat(fid);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_wstat - write a file's meta-data
+ * @v9ses: 9P2000 session information
+ * @fid: fid pointing to file or directory to write info about
+ * @stat: metadata
+ * @fcall: pointer to response fcall
+ *
+ */
+
+int
+v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid,
+            struct v9fs_wstat *wstat, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "fid %d\n", fid);
+
+       tc = v9fs_create_twstat(fid, wstat, v9ses->extended);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_walk - walk a fid to a new file or directory
+ * @v9ses: 9P2000 session information
+ * @fid: fid to walk
+ * @newfid: new fid (for clone operations)
+ * @name: path to walk fid to
+ * @fcall: pointer to response fcall
+ *
+ */
+
+/* TODO: support multiple walk */
+
+int
+v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid,
+           char *name, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+       int nwname;
+
+       dprintk(DEBUG_9P, "fid %d newfid %d wname '%s'\n", fid, newfid, name);
+
+       if (name)
+               nwname = 1;
+       else
+               nwname = 0;
+
+       tc = v9fs_create_twalk(fid, newfid, nwname, &name);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_open - open a file
+ *
+ * @v9ses - 9P2000 session information
+ * @fid - fid to open
+ * @mode - mode to open file (R, RW, etc)
+ * @fcall - pointer to response fcall
+ *
+ */
+
+int
+v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode,
+           struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode);
+
+       tc = v9fs_create_topen(fid, mode);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_remove - remove a file or directory
+ * @v9ses: 9P2000 session information
+ * @fid: fid to remove
+ * @fcall: pointer to response fcall
+ *
+ */
+
+int
+v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid,
+             struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "fid %d\n", fid);
+
+       tc = v9fs_create_tremove(fid);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_create - create a file or directory
+ * @v9ses: 9P2000 session information
+ * @fid: fid to create
+ * @name: name of the file or directory to create
+ * @perm: permissions to create with
+ * @mode: mode to open file (R, RW, etc)
+ * @fcall: pointer to response fcall
+ *
+ */
+
+int
+v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name,
+             u32 perm, u8 mode, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc;
+
+       dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n",
+               fid, name, perm, mode);
+
+       tc = v9fs_create_tcreate(fid, name, perm, mode);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, rcp);
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_read - read data
+ * @v9ses: 9P2000 session information
+ * @fid: fid to read from
+ * @offset: offset to start read at
+ * @count: how many bytes to read
+ * @fcall: pointer to response fcall (with data)
+ *
+ */
+
+int
+v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset,
+           u32 count, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc, *rc;
+
+       dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid,
+               (long long unsigned) offset, count);
+
+       tc = v9fs_create_tread(fid, offset, count);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, &rc);
+               if (!ret)
+                       ret = rc->params.rread.count;
+               if (rcp)
+                       *rcp = rc;
+               else
+                       kfree(rc);
+
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
+/**
+ * v9fs_t_write - write data
+ * @v9ses: 9P2000 session information
+ * @fid: fid to write to
+ * @offset: offset to start write at
+ * @count: how many bytes to write
+ * @fcall: pointer to response fcall
+ *
+ */
+
+int
+v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count,
+       const char __user *data, struct v9fs_fcall **rcp)
+{
+       int ret;
+       struct v9fs_fcall *tc, *rc;
+
+       dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid,
+               (long long unsigned) offset, count);
+
+       tc = v9fs_create_twrite(fid, offset, count, data);
+       if (!IS_ERR(tc)) {
+               ret = v9fs_mux_rpc(v9ses->mux, tc, &rc);
+
+               if (!ret)
+                       ret = rc->params.rwrite.count;
+               if (rcp)
+                       *rcp = rc;
+               else
+                       kfree(rc);
+
+               kfree(tc);
+       } else
+               ret = PTR_ERR(tc);
+
+       return ret;
+}
+
diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c
new file mode 100644 (file)
index 0000000..44e8306
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * linux/fs/9p/trans_socket.c
+ *
+ * Socket Transport Layer
+ *
+ *  Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
+ *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
+ *  Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
+ *  Copyright (C) 1995, 1996 by Olaf Kirch <okir@monad.swb.de>
+ *
+ *  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:
+ *  Free Software Foundation
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02111-1301  USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/in.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/ipv6.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/un.h>
+#include <asm/uaccess.h>
+#include <linux/inet.h>
+#include <linux/idr.h>
+#include <linux/file.h>
+
+#include "debug.h"
+#include "v9fs.h"
+#include "transport.h"
+
+#define V9FS_PORT 564
+
+struct v9fs_trans_sock {
+       struct socket *s;
+       struct file *filp;
+};
+
+/**
+ * v9fs_sock_recv - receive from a socket
+ * @v9ses: session information
+ * @v: buffer to receive data into
+ * @len: size of receive buffer
+ *
+ */
+
+static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len)
+{
+       int ret;
+       struct v9fs_trans_sock *ts;
+
+       if (!trans || trans->status == Disconnected) {
+               dprintk(DEBUG_ERROR, "disconnected ...\n");
+               return -EREMOTEIO;
+       }
+
+       ts = trans->priv;
+
+       if (!(ts->filp->f_flags & O_NONBLOCK))
+               dprintk(DEBUG_ERROR, "blocking read ...\n");
+
+       ret = kernel_read(ts->filp, ts->filp->f_pos, v, len);
+       if (ret <= 0) {
+               if (ret != -ERESTARTSYS && ret != -EAGAIN)
+                       trans->status = Disconnected;
+       }
+
+       return ret;
+}
+
+/**
+ * v9fs_sock_send - send to a socket
+ * @v9ses: session information
+ * @v: buffer to send data from
+ * @len: size of send buffer
+ *
+ */
+
+static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len)
+{
+       int ret;
+       mm_segment_t oldfs;
+       struct v9fs_trans_sock *ts;
+
+       if (!trans || trans->status == Disconnected) {
+               dprintk(DEBUG_ERROR, "disconnected ...\n");
+               return -EREMOTEIO;
+       }
+
+       ts = trans->priv;
+       if (!ts) {
+               dprintk(DEBUG_ERROR, "no transport ...\n");
+               return -EREMOTEIO;
+       }
+
+       if (!(ts->filp->f_flags & O_NONBLOCK))
+               dprintk(DEBUG_ERROR, "blocking write ...\n");
+
+       oldfs = get_fs();
+       set_fs(get_ds());
+       ret = vfs_write(ts->filp, (void __user *)v, len, &ts->filp->f_pos);
+       set_fs(oldfs);
+
+       if (ret < 0) {
+               if (ret != -ERESTARTSYS)
+                       trans->status = Disconnected;
+       }
+
+       return ret;
+}
+
+static unsigned int v9fs_sock_poll(struct v9fs_transport *trans,
+       struct poll_table_struct *pt) {
+
+       int ret;
+       struct v9fs_trans_sock *ts;
+       mm_segment_t oldfs;
+
+       if (!trans) {
+               dprintk(DEBUG_ERROR, "no transport\n");
+               return -EIO;
+       }
+
+       ts = trans->priv;
+       if (trans->status != Connected || !ts) {
+               dprintk(DEBUG_ERROR, "transport disconnected: %d\n", trans->status);
+               return -EIO;
+       }
+
+       oldfs = get_fs();
+       set_fs(get_ds());
+
+       if (!ts->filp->f_op || !ts->filp->f_op->poll) {
+               dprintk(DEBUG_ERROR, "no poll operation\n");
+               ret = -EIO;
+               goto end;
+       }
+
+       ret = ts->filp->f_op->poll(ts->filp, pt);
+
+end:
+       set_fs(oldfs);
+       return ret;
+}
+
+
+/**
+ * v9fs_tcp_init - initialize TCP socket
+ * @v9ses: session information
+ * @addr: address of server to mount
+ * @data: mount options
+ *
+ */
+
+static int
+v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data)
+{
+       struct socket *csocket = NULL;
+       struct sockaddr_in sin_server;
+       int rc = 0;
+       struct v9fs_trans_sock *ts = NULL;
+       struct v9fs_transport *trans = v9ses->transport;
+       int fd;
+
+       trans->status = Disconnected;
+
+       ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
+
+       if (!ts)
+               return -ENOMEM;
+
+       trans->priv = ts;
+       ts->s = NULL;
+       ts->filp = NULL;
+
+       if (!addr)
+               return -EINVAL;
+
+       dprintk(DEBUG_TRANS, "Connecting to %s\n", addr);
+
+       sin_server.sin_family = AF_INET;
+       sin_server.sin_addr.s_addr = in_aton(addr);
+       sin_server.sin_port = htons(v9ses->port);
+       sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
+       rc = csocket->ops->connect(csocket,
+                                  (struct sockaddr *)&sin_server,
+                                  sizeof(struct sockaddr_in), 0);
+       if (rc < 0) {
+               eprintk(KERN_ERR,
+                       "v9fs_trans_tcp: problem connecting socket to %s\n",
+                       addr);
+               return rc;
+       }
+       csocket->sk->sk_allocation = GFP_NOIO;
+
+       fd = sock_map_fd(csocket);
+       if (fd < 0) {
+               sock_release(csocket);
+               kfree(ts);
+               trans->priv = NULL;
+               return fd;
+       }
+
+       ts->s = csocket;
+       ts->filp = fget(fd);
+       ts->filp->f_flags |= O_NONBLOCK;
+       trans->status = Connected;
+
+       return 0;
+}
+
+/**
+ * v9fs_unix_init - initialize UNIX domain socket
+ * @v9ses: session information
+ * @dev_name: path to named pipe
+ * @data: mount options
+ *
+ */
+
+static int
+v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name,
+              char *data)
+{
+       int rc, fd;
+       struct socket *csocket;
+       struct sockaddr_un sun_server;
+       struct v9fs_transport *trans;
+       struct v9fs_trans_sock *ts;
+
+       rc = 0;
+       csocket = NULL;
+       trans = v9ses->transport;
+
+       trans->status = Disconnected;
+
+       if (strlen(dev_name) > UNIX_PATH_MAX) {
+               eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n",
+                       dev_name);
+               return -ENOMEM;
+       }
+
+       ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       trans->priv = ts;
+       ts->s = NULL;
+       ts->filp = NULL;
+
+       sun_server.sun_family = PF_UNIX;
+       strcpy(sun_server.sun_path, dev_name);
+       sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
+       rc = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
+               sizeof(struct sockaddr_un) - 1, 0);     /* -1 *is* important */
+       if (rc < 0) {
+               eprintk(KERN_ERR,
+                       "v9fs_trans_unix: problem connecting socket: %s: %d\n",
+                       dev_name, rc);
+               return rc;
+       }
+       csocket->sk->sk_allocation = GFP_NOIO;
+
+       fd = sock_map_fd(csocket);
+       if (fd < 0) {
+               sock_release(csocket);
+               kfree(ts);
+               trans->priv = NULL;
+               return fd;
+       }
+
+       ts->s = csocket;
+       ts->filp = fget(fd);
+       ts->filp->f_flags |= O_NONBLOCK;
+       trans->status = Connected;
+
+       return 0;
+}
+
+/**
+ * v9fs_sock_close - shutdown socket
+ * @trans: private socket structure
+ *
+ */
+
+static void v9fs_sock_close(struct v9fs_transport *trans)
+{
+       struct v9fs_trans_sock *ts;
+
+       if (!trans)
+               return;
+
+       ts = trans->priv;
+
+       if ((ts) && (ts->filp)) {
+               fput(ts->filp);
+               ts->filp = NULL;
+               ts->s = NULL;
+               trans->status = Disconnected;
+       }
+
+       kfree(ts);
+
+       trans->priv = NULL;
+}
+
+struct v9fs_transport v9fs_trans_tcp = {
+       .init = v9fs_tcp_init,
+       .write = v9fs_sock_send,
+       .read = v9fs_sock_recv,
+       .close = v9fs_sock_close,
+       .poll = v9fs_sock_poll,
+};
+
+struct v9fs_transport v9fs_trans_unix = {
+       .init = v9fs_unix_init,
+       .write = v9fs_sock_send,
+       .read = v9fs_sock_recv,
+       .close = v9fs_sock_close,
+       .poll = v9fs_sock_poll,
+};
diff --git a/fs/relayfs/buffers.c b/fs/relayfs/buffers.c
new file mode 100644 (file)
index 0000000..1018781
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * RelayFS buffer management code.
+ *
+ * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
+ * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/relayfs_fs.h>
+#include "relay.h"
+#include "buffers.h"
+
+/*
+ * close() vm_op implementation for relayfs file mapping.
+ */
+static void relay_file_mmap_close(struct vm_area_struct *vma)
+{
+       struct rchan_buf *buf = vma->vm_private_data;
+       buf->chan->cb->buf_unmapped(buf, vma->vm_file);
+}
+
+/*
+ * nopage() vm_op implementation for relayfs file mapping.
+ */
+static struct page *relay_buf_nopage(struct vm_area_struct *vma,
+                                    unsigned long address,
+                                    int *type)
+{
+       struct page *page;
+       struct rchan_buf *buf = vma->vm_private_data;
+       unsigned long offset = address - vma->vm_start;
+
+       if (address > vma->vm_end)
+               return NOPAGE_SIGBUS; /* Disallow mremap */
+       if (!buf)
+               return NOPAGE_OOM;
+
+       page = vmalloc_to_page(buf->start + offset);
+       if (!page)
+               return NOPAGE_OOM;
+       get_page(page);
+
+       if (type)
+               *type = VM_FAULT_MINOR;
+
+       return page;
+}
+
+/*
+ * vm_ops for relay file mappings.
+ */
+static struct vm_operations_struct relay_file_mmap_ops = {
+       .nopage = relay_buf_nopage,
+       .close = relay_file_mmap_close,
+};
+
+/**
+ *     relay_mmap_buf: - mmap channel buffer to process address space
+ *     @buf: relay channel buffer
+ *     @vma: vm_area_struct describing memory to be mapped
+ *
+ *     Returns 0 if ok, negative on error
+ *
+ *     Caller should already have grabbed mmap_sem.
+ */
+int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma)
+{
+       unsigned long length = vma->vm_end - vma->vm_start;
+       struct file *filp = vma->vm_file;
+
+       if (!buf)
+               return -EBADF;
+
+       if (length != (unsigned long)buf->chan->alloc_size)
+               return -EINVAL;
+
+       vma->vm_ops = &relay_file_mmap_ops;
+       vma->vm_private_data = buf;
+       buf->chan->cb->buf_mapped(buf, filp);
+
+       return 0;
+}
+
+/**
+ *     relay_alloc_buf - allocate a channel buffer
+ *     @buf: the buffer struct
+ *     @size: total size of the buffer
+ *
+ *     Returns a pointer to the resulting buffer, NULL if unsuccessful
+ */
+static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size)
+{
+       void *mem;
+       unsigned int i, j, n_pages;
+
+       size = PAGE_ALIGN(size);
+       n_pages = size >> PAGE_SHIFT;
+
+       buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL);
+       if (!buf->page_array)
+               return NULL;
+
+       for (i = 0; i < n_pages; i++) {
+               buf->page_array[i] = alloc_page(GFP_KERNEL);
+               if (unlikely(!buf->page_array[i]))
+                       goto depopulate;
+       }
+       mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL);
+       if (!mem)
+               goto depopulate;
+
+       memset(mem, 0, size);
+       buf->page_count = n_pages;
+       return mem;
+
+depopulate:
+       for (j = 0; j < i; j++)
+               __free_page(buf->page_array[j]);
+       kfree(buf->page_array);
+       return NULL;
+}
+
+/**
+ *     relay_create_buf - allocate and initialize a channel buffer
+ *     @alloc_size: size of the buffer to allocate
+ *     @n_subbufs: number of sub-buffers in the channel
+ *
+ *     Returns channel buffer if successful, NULL otherwise
+ */
+struct rchan_buf *relay_create_buf(struct rchan *chan)
+{
+       struct rchan_buf *buf = kcalloc(1, sizeof(struct rchan_buf), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL);
+       if (!buf->padding)
+               goto free_buf;
+
+       buf->start = relay_alloc_buf(buf, chan->alloc_size);
+       if (!buf->start)
+               goto free_buf;
+
+       buf->chan = chan;
+       kref_get(&buf->chan->kref);
+       return buf;
+
+free_buf:
+       kfree(buf->padding);
+       kfree(buf);
+       return NULL;
+}
+
+/**
+ *     relay_destroy_buf - destroy an rchan_buf struct and associated buffer
+ *     @buf: the buffer struct
+ */
+void relay_destroy_buf(struct rchan_buf *buf)
+{
+       struct rchan *chan = buf->chan;
+       unsigned int i;
+
+       if (likely(buf->start)) {
+               vunmap(buf->start);
+               for (i = 0; i < buf->page_count; i++)
+                       __free_page(buf->page_array[i]);
+               kfree(buf->page_array);
+       }
+       kfree(buf->padding);
+       kfree(buf);
+       kref_put(&chan->kref, relay_destroy_channel);
+}
+
+/**
+ *     relay_remove_buf - remove a channel buffer
+ *
+ *     Removes the file from the relayfs fileystem, which also frees the
+ *     rchan_buf_struct and the channel buffer.  Should only be called from
+ *     kref_put().
+ */
+void relay_remove_buf(struct kref *kref)
+{
+       struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
+       buf->chan->cb->remove_buf_file(buf->dentry);
+       relay_destroy_buf(buf);
+}
diff --git a/fs/relayfs/buffers.h b/fs/relayfs/buffers.h
new file mode 100644 (file)
index 0000000..37a1249
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _BUFFERS_H
+#define _BUFFERS_H
+
+/* This inspired by rtai/shmem */
+#define FIX_SIZE(x) (((x) - 1) & PAGE_MASK) + PAGE_SIZE
+
+extern int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma);
+extern struct rchan_buf *relay_create_buf(struct rchan *chan);
+extern void relay_destroy_buf(struct rchan_buf *buf);
+extern void relay_remove_buf(struct kref *kref);
+
+#endif/* _BUFFERS_H */
diff --git a/fs/relayfs/relay.h b/fs/relayfs/relay.h
new file mode 100644 (file)
index 0000000..0993d3e
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _RELAY_H
+#define _RELAY_H
+
+extern int relayfs_remove(struct dentry *dentry);
+extern int relay_buf_empty(struct rchan_buf *buf);
+extern void relay_destroy_channel(struct kref *kref);
+
+#endif /* _RELAY_H */
diff --git a/include/asm-arm/arch-aaec2000/param.h b/include/asm-arm/arch-aaec2000/param.h
new file mode 100644 (file)
index 0000000..139936c
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ *  linux/include/asm-arm/arch-aaec2000/param.h
+ *
+ *  Copyright (c) 2005 Nicolas Bellido Y Ortega
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_ARCH_PARAM_H
+#define __ASM_ARCH_PARAM_H
+
+#endif /* __ASM_ARCH_PARAM_H */
+
diff --git a/include/asm-arm/arch-at91rm9200/param.h b/include/asm-arm/arch-at91rm9200/param.h
new file mode 100644 (file)
index 0000000..9480f84
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * include/asm-arm/arch-at91rm9200/param.h
+ *
+ *  Copyright (C) 2003 SAN People
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __ASM_ARCH_PARAM_H
+#define __ASM_ARCH_PARAM_H
+
+/*
+ * We use default params
+ */
+
+#endif
diff --git a/include/asm-arm/arch-ixp2000/uengine.h b/include/asm-arm/arch-ixp2000/uengine.h
new file mode 100644 (file)
index 0000000..b442d65
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Generic library functions for the microengines found on the Intel
+ * IXP2000 series of network processors.
+ *
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ */
+
+#ifndef __IXP2000_UENGINE_H
+#define __IXP2000_UENGINE_H
+
+extern u32 ixp2000_uengine_mask;
+
+struct ixp2000_uengine_code
+{
+       u32     cpu_model_bitmask;
+       u8      cpu_min_revision;
+       u8      cpu_max_revision;
+
+       u32     uengine_parameters;
+
+       struct ixp2000_reg_value {
+               int     reg;
+               u32     value;
+       } *initial_reg_values;
+
+       int     num_insns;
+       u8      *insns;
+};
+
+u32 ixp2000_uengine_csr_read(int uengine, int offset);
+void ixp2000_uengine_csr_write(int uengine, int offset, u32 value);
+void ixp2000_uengine_reset(u32 uengine_mask);
+void ixp2000_uengine_set_mode(int uengine, u32 mode);
+void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns);
+void ixp2000_uengine_init_context(int uengine, int context, int pc);
+void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask);
+void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask);
+int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c);
+
+#define IXP2000_UENGINE_8_CONTEXTS             0x00000000
+#define IXP2000_UENGINE_4_CONTEXTS             0x80000000
+#define IXP2000_UENGINE_PRN_UPDATE_EVERY       0x40000000
+#define IXP2000_UENGINE_PRN_UPDATE_ON_ACCESS   0x00000000
+#define IXP2000_UENGINE_NN_FROM_SELF           0x00100000
+#define IXP2000_UENGINE_NN_FROM_PREVIOUS       0x00000000
+#define IXP2000_UENGINE_ASSERT_EMPTY_AT_3      0x000c0000
+#define IXP2000_UENGINE_ASSERT_EMPTY_AT_2      0x00080000
+#define IXP2000_UENGINE_ASSERT_EMPTY_AT_1      0x00040000
+#define IXP2000_UENGINE_ASSERT_EMPTY_AT_0      0x00000000
+#define IXP2000_UENGINE_LM_ADDR1_GLOBAL                0x00020000
+#define IXP2000_UENGINE_LM_ADDR1_PER_CONTEXT   0x00000000
+#define IXP2000_UENGINE_LM_ADDR0_GLOBAL                0x00010000
+#define IXP2000_UENGINE_LM_ADDR0_PER_CONTEXT   0x00000000
+
+
+#endif
diff --git a/include/asm-arm/arch-realview/param.h b/include/asm-arm/arch-realview/param.h
new file mode 100644 (file)
index 0000000..89b1235
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *  linux/include/asm-arm/arch-realview/param.h
+ *
+ *  Copyright (C) 2002 ARM Limited
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
diff --git a/include/asm-powerpc/numnodes.h b/include/asm-powerpc/numnodes.h
new file mode 100644 (file)
index 0000000..e138eda
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _ASM_POWERPC_MAX_NUMNODES_H
+#define _ASM_POWERPC_MAX_NUMNODES_H
+#ifdef __KERNEL__
+
+/* Max 16 Nodes */
+#define NODES_SHIFT    4
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_MAX_NUMNODES_H */
diff --git a/include/asm-um/ldt-i386.h b/include/asm-um/ldt-i386.h
new file mode 100644 (file)
index 0000000..175722a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2004 Fujitsu Siemens Computers GmbH
+ * Licensed under the GPL
+ *
+ * Author: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
+ */
+
+#ifndef __ASM_LDT_I386_H
+#define __ASM_LDT_I386_H
+
+#include "asm/semaphore.h"
+#include "asm/arch/ldt.h"
+
+struct mmu_context_skas;
+extern void ldt_host_info(void);
+extern long init_new_ldt(struct mmu_context_skas * to_mm,
+                        struct mmu_context_skas * from_mm);
+extern void free_ldt(struct mmu_context_skas * mm);
+
+#define LDT_PAGES_MAX \
+       ((LDT_ENTRIES * LDT_ENTRY_SIZE)/PAGE_SIZE)
+#define LDT_ENTRIES_PER_PAGE \
+       (PAGE_SIZE/LDT_ENTRY_SIZE)
+#define LDT_DIRECT_ENTRIES \
+       ((LDT_PAGES_MAX*sizeof(void *))/LDT_ENTRY_SIZE)
+
+struct ldt_entry {
+       __u32 a;
+       __u32 b;
+};
+
+typedef struct uml_ldt {
+       int entry_count;
+       struct semaphore semaphore;
+       union {
+               struct ldt_entry * pages[LDT_PAGES_MAX];
+               struct ldt_entry entries[LDT_DIRECT_ENTRIES];
+       } u;
+} uml_ldt_t;
+
+/*
+ * macros stolen from include/asm-i386/desc.h
+ */
+#define LDT_entry_a(info) \
+       ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff))
+
+#define LDT_entry_b(info) \
+       (((info)->base_addr & 0xff000000) | \
+       (((info)->base_addr & 0x00ff0000) >> 16) | \
+       ((info)->limit & 0xf0000) | \
+       (((info)->read_exec_only ^ 1) << 9) | \
+       ((info)->contents << 10) | \
+       (((info)->seg_not_present ^ 1) << 15) | \
+       ((info)->seg_32bit << 22) | \
+       ((info)->limit_in_pages << 23) | \
+       ((info)->useable << 20) | \
+       0x7000)
+
+#define LDT_empty(info) (\
+       (info)->base_addr       == 0    && \
+       (info)->limit           == 0    && \
+       (info)->contents        == 0    && \
+       (info)->read_exec_only  == 1    && \
+       (info)->seg_32bit       == 0    && \
+       (info)->limit_in_pages  == 0    && \
+       (info)->seg_not_present == 1    && \
+       (info)->useable         == 0    )
+
+#endif
diff --git a/include/asm-um/ldt-x86_64.h b/include/asm-um/ldt-x86_64.h
new file mode 100644 (file)
index 0000000..96b35aa
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2004 Fujitsu Siemens Computers GmbH
+ * Licensed under the GPL
+ *
+ * Author: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
+ */
+
+#ifndef __ASM_LDT_X86_64_H
+#define __ASM_LDT_X86_64_H
+
+#include "asm/semaphore.h"
+#include "asm/arch/ldt.h"
+
+struct mmu_context_skas;
+extern void ldt_host_info(void);
+extern long init_new_ldt(struct mmu_context_skas * to_mm,
+                        struct mmu_context_skas * from_mm);
+extern void free_ldt(struct mmu_context_skas * mm);
+
+#define LDT_PAGES_MAX \
+       ((LDT_ENTRIES * LDT_ENTRY_SIZE)/PAGE_SIZE)
+#define LDT_ENTRIES_PER_PAGE \
+       (PAGE_SIZE/LDT_ENTRY_SIZE)
+#define LDT_DIRECT_ENTRIES \
+       ((LDT_PAGES_MAX*sizeof(void *))/LDT_ENTRY_SIZE)
+
+struct ldt_entry {
+       __u32 a;
+       __u32 b;
+};
+
+typedef struct uml_ldt {
+       int entry_count;
+       struct semaphore semaphore;
+       union {
+               struct ldt_entry * pages[LDT_PAGES_MAX];
+               struct ldt_entry entries[LDT_DIRECT_ENTRIES];
+       } u;
+} uml_ldt_t;
+
+/*
+ * macros stolen from include/asm-x86_64/desc.h
+ */
+#define LDT_entry_a(info) \
+       ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff))
+
+/* Don't allow setting of the lm bit. It is useless anyways because
+ * 64bit system calls require __USER_CS. */
+#define LDT_entry_b(info) \
+       (((info)->base_addr & 0xff000000) | \
+       (((info)->base_addr & 0x00ff0000) >> 16) | \
+       ((info)->limit & 0xf0000) | \
+       (((info)->read_exec_only ^ 1) << 9) | \
+       ((info)->contents << 10) | \
+       (((info)->seg_not_present ^ 1) << 15) | \
+       ((info)->seg_32bit << 22) | \
+       ((info)->limit_in_pages << 23) | \
+       ((info)->useable << 20) | \
+       /* ((info)->lm << 21) | */ \
+       0x7000)
+
+#define LDT_empty(info) (\
+       (info)->base_addr       == 0    && \
+       (info)->limit           == 0    && \
+       (info)->contents        == 0    && \
+       (info)->read_exec_only  == 1    && \
+       (info)->seg_32bit       == 0    && \
+       (info)->limit_in_pages  == 0    && \
+       (info)->seg_not_present == 1    && \
+       (info)->useable         == 0    && \
+       (info)->lm              == 0)
+
+#endif
diff --git a/include/linux/x1205.h b/include/linux/x1205.h
new file mode 100644 (file)
index 0000000..64fd3af
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  x1205.h - defines for drivers/i2c/chips/x1205.c
+ *  Copyright 2004 Karen Spearel
+ *  Copyright 2005 Alessandro Zummo
+ *
+ *  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.
+ */
+
+#ifndef __LINUX_X1205_H__
+#define __LINUX_X1205_H__
+
+/* commands */
+
+#define X1205_CMD_GETDATETIME  0
+#define X1205_CMD_SETTIME      1
+#define X1205_CMD_SETDATETIME  2
+#define X1205_CMD_GETALARM     3
+#define X1205_CMD_SETALARM     4
+#define X1205_CMD_GETDTRIM     5
+#define X1205_CMD_SETDTRIM     6
+#define X1205_CMD_GETATRIM     7
+#define X1205_CMD_SETATRIM     8
+
+extern int x1205_do_command(unsigned int cmd, void *arg);
+extern int x1205_direct_attach(int adapter_id,
+       struct i2c_client_address_data *address_data);
+
+#endif /* __LINUX_X1205_H__ */
diff --git a/net/ipv4/netfilter/ipt_policy.c b/net/ipv4/netfilter/ipt_policy.c
new file mode 100644 (file)
index 0000000..5a7a265
--- /dev/null
@@ -0,0 +1,176 @@
+/* IP tables module for matching IPsec policy
+ *
+ * Copyright (c) 2004,2005 Patrick McHardy, <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <net/xfrm.h>
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_policy.h>
+
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("IPtables IPsec policy matching module");
+MODULE_LICENSE("GPL");
+
+
+static inline int
+match_xfrm_state(struct xfrm_state *x, const struct ipt_policy_elem *e)
+{
+#define MATCH_ADDR(x,y,z)      (!e->match.x ||                              \
+                                ((e->x.a4.s_addr == (e->y.a4.s_addr & (z))) \
+                                 ^ e->invert.x))
+#define MATCH(x,y)             (!e->match.x || ((e->x == (y)) ^ e->invert.x))
+
+       return MATCH_ADDR(saddr, smask, x->props.saddr.a4) &&
+              MATCH_ADDR(daddr, dmask, x->id.daddr.a4) &&
+              MATCH(proto, x->id.proto) &&
+              MATCH(mode, x->props.mode) &&
+              MATCH(spi, x->id.spi) &&
+              MATCH(reqid, x->props.reqid);
+}
+
+static int
+match_policy_in(const struct sk_buff *skb, const struct ipt_policy_info *info)
+{
+       const struct ipt_policy_elem *e;
+       struct sec_path *sp = skb->sp;
+       int strict = info->flags & IPT_POLICY_MATCH_STRICT;
+       int i, pos;
+
+       if (sp == NULL)
+               return -1;
+       if (strict && info->len != sp->len)
+               return 0;
+
+       for (i = sp->len - 1; i >= 0; i--) {
+               pos = strict ? i - sp->len + 1 : 0;
+               if (pos >= info->len)
+                       return 0;
+               e = &info->pol[pos];
+
+               if (match_xfrm_state(sp->x[i].xvec, e)) {
+                       if (!strict)
+                               return 1;
+               } else if (strict)
+                       return 0;
+       }
+
+       return strict ? 1 : 0;
+}
+
+static int
+match_policy_out(const struct sk_buff *skb, const struct ipt_policy_info *info)
+{
+       const struct ipt_policy_elem *e;
+       struct dst_entry *dst = skb->dst;
+       int strict = info->flags & IPT_POLICY_MATCH_STRICT;
+       int i, pos;
+
+       if (dst->xfrm == NULL)
+               return -1;
+
+       for (i = 0; dst && dst->xfrm; dst = dst->child, i++) {
+               pos = strict ? i : 0;
+               if (pos >= info->len)
+                       return 0;
+               e = &info->pol[pos];
+
+               if (match_xfrm_state(dst->xfrm, e)) {
+                       if (!strict)
+                               return 1;
+               } else if (strict)
+                       return 0;
+       }
+
+       return strict ? i == info->len : 0;
+}
+
+static int match(const struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 const void *matchinfo,
+                 int offset,
+                 unsigned int protoff,
+                 int *hotdrop)
+{
+       const struct ipt_policy_info *info = matchinfo;
+       int ret;
+
+       if (info->flags & IPT_POLICY_MATCH_IN)
+               ret = match_policy_in(skb, info);
+       else
+               ret = match_policy_out(skb, info);
+
+       if (ret < 0)
+               ret = info->flags & IPT_POLICY_MATCH_NONE ? 1 : 0;
+       else if (info->flags & IPT_POLICY_MATCH_NONE)
+               ret = 0;
+
+       return ret;
+}
+
+static int checkentry(const char *tablename, const void *ip_void,
+                      void *matchinfo, unsigned int matchsize,
+                      unsigned int hook_mask)
+{
+       struct ipt_policy_info *info = matchinfo;
+
+       if (matchsize != IPT_ALIGN(sizeof(*info))) {
+               printk(KERN_ERR "ipt_policy: matchsize %u != %zu\n",
+                      matchsize, IPT_ALIGN(sizeof(*info)));
+               return 0;
+       }
+       if (!(info->flags & (IPT_POLICY_MATCH_IN|IPT_POLICY_MATCH_OUT))) {
+               printk(KERN_ERR "ipt_policy: neither incoming nor "
+                               "outgoing policy selected\n");
+               return 0;
+       }
+       if (hook_mask & (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN)
+           && info->flags & IPT_POLICY_MATCH_OUT) {
+               printk(KERN_ERR "ipt_policy: output policy not valid in "
+                               "PRE_ROUTING and INPUT\n");
+               return 0;
+       }
+       if (hook_mask & (1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT)
+           && info->flags & IPT_POLICY_MATCH_IN) {
+               printk(KERN_ERR "ipt_policy: input policy not valid in "
+                               "POST_ROUTING and OUTPUT\n");
+               return 0;
+       }
+       if (info->len > IPT_POLICY_MAX_ELEM) {
+               printk(KERN_ERR "ipt_policy: too many policy elements\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct ipt_match policy_match = {
+       .name           = "policy",
+       .match          = match,
+       .checkentry     = checkentry,
+       .me             = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+       return ipt_register_match(&policy_match);
+}
+
+static void __exit fini(void)
+{
+       ipt_unregister_match(&policy_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv6/netfilter/ip6t_policy.c b/net/ipv6/netfilter/ip6t_policy.c
new file mode 100644 (file)
index 0000000..3d39ec9
--- /dev/null
@@ -0,0 +1,176 @@
+/* IP tables module for matching IPsec policy
+ *
+ * Copyright (c) 2004,2005 Patrick McHardy, <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <net/xfrm.h>
+
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_policy.h>
+
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("IPtables IPsec policy matching module");
+MODULE_LICENSE("GPL");
+
+
+static inline int
+match_xfrm_state(struct xfrm_state *x, const struct ip6t_policy_elem *e)
+{
+#define MATCH_ADDR(x,y,z)      (!e->match.x ||                                \
+                                ((!ip6_masked_addrcmp(&e->x.a6, &e->y.a6, z)) \
+                                 ^ e->invert.x))
+#define MATCH(x,y)             (!e->match.x || ((e->x == (y)) ^ e->invert.x))
+       
+       return MATCH_ADDR(saddr, smask, (struct in6_addr *)&x->props.saddr.a6) &&
+              MATCH_ADDR(daddr, dmask, (struct in6_addr *)&x->id.daddr.a6) &&
+              MATCH(proto, x->id.proto) &&
+              MATCH(mode, x->props.mode) &&
+              MATCH(spi, x->id.spi) &&
+              MATCH(reqid, x->props.reqid);
+}
+
+static int
+match_policy_in(const struct sk_buff *skb, const struct ip6t_policy_info *info)
+{
+       const struct ip6t_policy_elem *e;
+       struct sec_path *sp = skb->sp;
+       int strict = info->flags & IP6T_POLICY_MATCH_STRICT;
+       int i, pos;
+
+       if (sp == NULL)
+               return -1;
+       if (strict && info->len != sp->len)
+               return 0;
+
+       for (i = sp->len - 1; i >= 0; i--) {
+               pos = strict ? i - sp->len + 1 : 0;
+               if (pos >= info->len)
+                       return 0;
+               e = &info->pol[pos];
+
+               if (match_xfrm_state(sp->x[i].xvec, e)) {
+                       if (!strict)
+                               return 1;
+               } else if (strict)
+                       return 0;
+       }
+
+       return strict ? 1 : 0;
+}
+
+static int
+match_policy_out(const struct sk_buff *skb, const struct ip6t_policy_info *info)
+{
+       const struct ip6t_policy_elem *e;
+       struct dst_entry *dst = skb->dst;
+       int strict = info->flags & IP6T_POLICY_MATCH_STRICT;
+       int i, pos;
+
+       if (dst->xfrm == NULL)
+               return -1;
+
+       for (i = 0; dst && dst->xfrm; dst = dst->child, i++) {
+               pos = strict ? i : 0;
+               if (pos >= info->len)
+                       return 0;
+               e = &info->pol[pos];
+
+               if (match_xfrm_state(dst->xfrm, e)) {
+                       if (!strict)
+                               return 1;
+               } else if (strict)
+                       return 0;
+       }
+
+       return strict ? i == info->len : 0;
+}
+
+static int match(const struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 const void *matchinfo,
+                int offset,
+                unsigned int protoff,
+                int *hotdrop)
+{
+       const struct ip6t_policy_info *info = matchinfo;
+       int ret;
+
+       if (info->flags & IP6T_POLICY_MATCH_IN)
+               ret = match_policy_in(skb, info);
+       else
+               ret = match_policy_out(skb, info);
+
+       if (ret < 0)
+               ret = info->flags & IP6T_POLICY_MATCH_NONE ? 1 : 0;
+       else if (info->flags & IP6T_POLICY_MATCH_NONE)
+               ret = 0;
+
+       return ret;
+}
+
+static int checkentry(const char *tablename, const void *ip_void,
+                      void *matchinfo, unsigned int matchsize,
+                      unsigned int hook_mask)
+{
+       struct ip6t_policy_info *info = matchinfo;
+
+       if (matchsize != IP6T_ALIGN(sizeof(*info))) {
+               printk(KERN_ERR "ip6t_policy: matchsize %u != %zu\n",
+                      matchsize, IP6T_ALIGN(sizeof(*info)));
+               return 0;
+       }
+       if (!(info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT))) {
+               printk(KERN_ERR "ip6t_policy: neither incoming nor "
+                               "outgoing policy selected\n");
+               return 0;
+       }
+       if (hook_mask & (1 << NF_IP6_PRE_ROUTING | 1 << NF_IP6_LOCAL_IN)
+           && info->flags & IP6T_POLICY_MATCH_OUT) {
+               printk(KERN_ERR "ip6t_policy: output policy not valid in "
+                               "PRE_ROUTING and INPUT\n");
+               return 0;
+       }
+       if (hook_mask & (1 << NF_IP6_POST_ROUTING | 1 << NF_IP6_LOCAL_OUT)
+           && info->flags & IP6T_POLICY_MATCH_IN) {
+               printk(KERN_ERR "ip6t_policy: input policy not valid in "
+                               "POST_ROUTING and OUTPUT\n");
+               return 0;
+       }
+       if (info->len > IP6T_POLICY_MAX_ELEM) {
+               printk(KERN_ERR "ip6t_policy: too many policy elements\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match policy_match = {
+       .name           = "policy",
+       .match          = match,
+       .checkentry     = checkentry,
+       .me             = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+       return ip6t_register_match(&policy_match);
+}
+
+static void __exit fini(void)
+{
+       ip6t_unregister_match(&policy_match);
+}
+
+module_init(init);
+module_exit(fini);