--- /dev/null
+ 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.
+
--- /dev/null
+
+ 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'.
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
+
+
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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';
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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
--- /dev/null
+/*
+ * 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, ® },
+ };
+
+ 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);
--- /dev/null
+/*
+ *
+ * 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:
+ */
--- /dev/null
+/* 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
--- /dev/null
+/*
+
+ 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
+
+
+
+
--- /dev/null
+/***************************************************************************
+ * 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_ */
--- /dev/null
+/***************************************************************************
+ * 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);
--- /dev/null
+/***************************************************************************
+ * 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_ */
--- /dev/null
+/***************************************************************************
+ * 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;
+}
--- /dev/null
+/***************************************************************************
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/* 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);
--- /dev/null
+/* 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);