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