From 10805a55f67471be8b309807f2f9fd8d46c1df46 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:20:48 -0500 Subject: [PATCH] Linux 2.2.15pre6 o SMC-mca updates (David Monro) o Stallion driver update (Wayne Meissner) o DAC960 updates (Leonard Zubkoff) o CPQ Array updates (adds SA431) (Charles White) o Fix creative joystick crash o Fix bogus RST when fins crossed (Dave Miller) o Gemini serial driver update (Cort Dougan) o Work around some MediaGX DMA bugs (Alan Cox) - Info thanks to Nat Semi o Fix type clashes between irda and Ingo HSM (Dag Brattli) o IDE DMA abort handling fixes (Andre Hedrick) o Cyrix 5530 IDE support (Mark Lord) o 3Ware raid controller driver (Adam Radford) o Fix sunrpc queue corruption bug (Trond Myklebust) o Sparc config updates (Dave Miller) o Fix SMP powermac booting (Paul Mackerras) o Fix bonding documentation (Lennie Besselink) o CPIA parallel camera driver (Peter Pregler) o Alternative page allocation hang fixes (Andrea Arcangeli) | Reverted some of the other fixes. This wants | sorting nicely for 2.3.x, for 2.2.x Andrea's | approach is easier to verify o Fix shm/remap bug (Eric Biederman) --- CREDITS | 8 +- Documentation/Configure.help | 27 + Documentation/README.DAC960 | 18 +- Documentation/cpqarray.txt | 7 +- Documentation/networking/bonding.txt | 2 +- Documentation/video4linux/README.cpia | 204 ++ MAINTAINERS | 2 - Makefile | 2 +- arch/i386/kernel/setup.c | 12 +- arch/ppc/kernel/head.S | 1 + arch/sparc/config.in | 1 + arch/sparc/defconfig | 1 + arch/sparc64/config.in | 1 + arch/sparc64/defconfig | 1 + arch/sparc64/kernel/binfmt_elf32.c | 2 +- arch/sparc64/kernel/dtlb_backend.S | 4 +- arch/sparc64/kernel/dtlb_base.S | 4 +- arch/sparc64/kernel/dtlb_prot.S | 4 +- arch/sparc64/kernel/itlb_base.S | 4 +- arch/sparc64/kernel/starfire.c | 4 +- arch/sparc64/lib/debuglocks.c | 4 +- arch/sparc64/lib/memscan.S | 4 +- drivers/block/Config.in | 1 + drivers/block/DAC960.c | 260 +- drivers/block/DAC960.h | 183 +- drivers/block/Makefile | 4 + drivers/block/cmd646.c | 4 +- drivers/block/cpqarray.c | 7 +- drivers/block/cpqarray.h | 1 - drivers/block/cs5530.c | 467 ++++ drivers/block/ida_cmd.h | 4 +- drivers/block/ide-disk.c | 2 +- drivers/block/ide-pci.c | 10 + drivers/block/ide.c | 16 +- drivers/block/ide_modes.h | 2 +- drivers/block/ll_rw_blk.c | 3 +- drivers/block/raid1.c | 12 + drivers/char/Config.in | 11 + drivers/char/Makefile | 16 + drivers/char/cpia.c | 3375 +++++++++++++++++++++++++ drivers/char/cpia_pp.c | 1497 +++++++++++ drivers/char/istallion.c | 47 +- drivers/char/joystick/joy-creative.c | 2 +- drivers/char/stallion.c | 60 +- drivers/char/videodev.c | 6 + drivers/net/smc-mca.c | 363 ++- drivers/net/smc-mca.h | 26 +- drivers/scsi/3w-xxxx.c | 2225 ++++++++++++++++ drivers/scsi/3w-xxxx.h | 369 +++ drivers/scsi/Config.in | 3 + drivers/scsi/Makefile | 8 + drivers/scsi/hosts.c | 8 + drivers/sound/dmabuf.c | 8 + include/asm-ppc/gemini_serial.h | 4 +- include/asm-sparc/ethtool.h | 4 +- include/asm-sparc64/ethtool.h | 4 +- include/linux/cd1400.h | 2 +- include/linux/cdk.h | 2 +- include/linux/comstats.h | 2 +- include/linux/cpia.h | 220 ++ include/linux/hdreg.h | 1 + include/linux/istallion.h | 2 +- include/linux/proc_fs.h | 1 + include/linux/stallion.h | 2 +- include/net/irda/parameters.h | 12 +- init/main.c | 24 + ipc/shm.c | 7 +- kernel/exit.c | 2 - mm/page_alloc.c | 10 - net/ipv4/tcp.c | 11 +- net/ipv4/tcp_input.c | 5 +- net/ipv4/tcp_output.c | 8 +- net/irda/ircomm/ircomm_param.c | 66 +- net/irda/irttp.c | 8 +- net/irda/parameters.c | 20 +- net/irda/qos.c | 41 +- net/sunrpc/sched.c | 11 +- 77 files changed, 9379 insertions(+), 407 deletions(-) create mode 100644 Documentation/video4linux/README.cpia create mode 100644 drivers/block/cs5530.c create mode 100644 drivers/char/cpia.c create mode 100644 drivers/char/cpia_pp.c create mode 100644 drivers/scsi/3w-xxxx.c create mode 100644 drivers/scsi/3w-xxxx.h create mode 100644 include/linux/cpia.h diff --git a/CREDITS b/CREDITS index b843fbc7d92f..464a94463cf5 100644 --- a/CREDITS +++ b/CREDITS @@ -1408,7 +1408,7 @@ S: 7546 JA Enschede S: Netherlands N: David S. Miller -E: davem@dm.cobaltmicro.com +E: davem@redhat.com D: Sparc and blue box hacker D: Vger Linux mailing list co-maintainer D: Linux Emacs elf/qmagic support + other libc/gcc things @@ -2058,12 +2058,8 @@ S: 97078 Wuerzburg S: Germany N: Greg Ungerer -E: gerg@stallion.com +E: gerg@moreton.com.au D: Author of Stallion multiport serial drivers -S: Stallion Technologies -S: 33 Woodstock Rd -S: Toowong, QLD. 4066 -S: Australia N: Jeffrey A. Uphoff E: juphoff@nrao.edu diff --git a/Documentation/Configure.help b/Documentation/Configure.help index e8ad07be6418..86bc2c8ae808 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -12103,6 +12103,33 @@ CONFIG_PHONE_IXJ If you do not have any Quicknet telephony cards, you can safely ignore this option. +CPiA Video For Linux +CONFIG_VIDEO_CPIA + This is the video4linux driver for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Labs Video + Blaster Webcam II. If you have one of these cameras, say Y here + and select parallel port lowlevel support below (the USB-version, + is not supported on this kernel-version) otherwise say N. This will + not work with the Creative Webcam III. It is also available as a + module (cpia.o). + + For more information (supported camera models, module autoloading, DMA + interrupt settings, supported applications, etc.) read + Documentation/video4linux/README.cpia. + +CPiA Parallel Port Lowlevel Support +CONFIG_VIDEO_CPIA_PP + This is the lowlevel parallel port support for cameras based on + Vision's CPiA (Colour Processor Interface ASIC), such as the + Creative Webcam II. If you have the parallel port version of one + of these cameras, say Y here, otherwise say N. It is also available + as a module (cpia_pp.o). + +CPiA Parallel Port DMA Support +CONFIG_VIDEO_CPIA_PP_DMA + This will use DMA if possible to reduce CPU usage. If in doubt, + say Y here. + # # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, diff --git a/Documentation/README.DAC960 b/Documentation/README.DAC960 index 2a80042dcfc3..332580368932 100644 --- a/Documentation/README.DAC960 +++ b/Documentation/README.DAC960 @@ -1,17 +1,17 @@ Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux - Version 2.2.4 for Linux 2.2.11 - Version 2.0.4 for Linux 2.0.37 + Version 2.2.5 for Linux 2.2.14 + Version 2.0.5 for Linux 2.0.38 PRODUCTION RELEASE - 23 August 1999 + 23 January 2000 Leonard N. Zubkoff Dandelion Digital lnz@dandelion.com - Copyright 1998-1999 by Leonard N. Zubkoff + Copyright 1998-2000 by Leonard N. Zubkoff INTRODUCTION @@ -87,7 +87,9 @@ Drive and 3 bits for the partition. The following list comprises the supported DAC960 and DAC1100 PCI RAID Controllers as of the date of this document. It is recommended that anyone purchasing a Mylex PCI RAID Controller not in the following table contact the -author beforehand to verify that it is or will be supported. +author beforehand to verify that it is or will be supported. The eXtremeRAID +2000, eXtremeRAID 3000, and AcceleRAID 352 have an entirely new firmware +interface and are not yet supported by this driver. eXtremeRAID 1100 (DAC1164P) 3 Wide Ultra-2/LVD SCSI channels @@ -163,16 +165,16 @@ ftp://ftp.mylex.com/pub/dac960/diskcomp.html. DRIVER INSTALLATION -This distribution was prepared for Linux kernel version 2.2.11 or 2.0.37. +This distribution was prepared for Linux kernel version 2.2.14 or 2.0.38. To install the DAC960 RAID driver, you may use the following commands, replacing "/usr/src" with wherever you keep your Linux kernel source tree: cd /usr/src - tar -xvzf DAC960-2.2.4.tar.gz (or DAC960-2.0.4.tar.gz) + tar -xvzf DAC960-2.2.5.tar.gz (or DAC960-2.0.5.tar.gz) mv README.DAC960 linux/Documentation mv DAC960.[ch] linux/drivers/block - patch -p0 < DAC960.patch + patch -p0 < DAC960.patch (driver 2.0.5 only) cd linux make config make depend diff --git a/Documentation/cpqarray.txt b/Documentation/cpqarray.txt index 45a53216fb1b..67db480cb4ee 100644 --- a/Documentation/cpqarray.txt +++ b/Documentation/cpqarray.txt @@ -1,11 +1,5 @@ This driver is for Compaq's SMART2 Intellegent Disk Array Controllers. -WARNING: --------- - -This driver comes with NO WARRANTY. It is not officially supported by -Compaq. Do not call technical support. Use at your own risk. - Supported Cards: ---------------- @@ -22,6 +16,7 @@ This driver is known to work with the following cards: * Integrated Smart Array Controller * SA 4200 * SA 4250ES + * SA 431 It should also work with some really old Disk array adapters, but I am unable to test against these cards: diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index 44c31defb36c..ecdc091285f0 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -1,7 +1,7 @@ Installation: Apply patch. (if your reading this in -/usr/src/linux/Documentation/network/bonding.txt, then it's already +/usr/src/linux/Documentation/networking/bonding.txt, then it's already applied.) Run make menuconfig/xconfig/config, and select 'bonding device' in diff --git a/Documentation/video4linux/README.cpia b/Documentation/video4linux/README.cpia new file mode 100644 index 000000000000..e9c95850eec3 --- /dev/null +++ b/Documentation/video4linux/README.cpia @@ -0,0 +1,204 @@ +This is a driver for the CPiA PPC2 driven parallel connected +Camera. For example the Creative WebcamII is CPiA driven. + + © [1]Peter Pregler, Linz 2000, published under the [2]GNU GPL + +--------------------------------------------------------------------------- + +USAGE: + +General: +======== + +1) Make sure you have created the video devices (/dev/video*): + +- if you have a recent MAKEDEV do a 'cd /dev;./MAKEDEV video' +- otherwise do a: + +cd /dev +mknod video0 c 81 0 +ln -s video0 video + +2) Compile the kernel (see below for the list of options to use), + configure your parport and reboot. + +3) If all worked well you should get messages similar + to the following (your versions may be different) on the console: + +V4L-Driver for Vision CPiA based cameras v0.5.0 +parport0: read2 timeout. +parport0: Multimedia device, VLSI Vision Ltd PPC2 +Parallel port driver for Vision CPiA based camera + using DMA mode (irq 7, DMA 3) + CPIA Version: 1.20 (2.0) + CPIA PnP-ID: 0553:0002:0100 + VP-Version: 1.0 0100 + 1 camera(s) found + + +As modules: +=========== + +Make sure you have selected the following kernel options (you can +select all stuff as modules): + +The cpia-stuff is in the section 'Character devices -> Video For Linux'. +The PnP-stuff is in the section 'Plug and Play support'. +The parport-stuff is in the section 'General setup'. + +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PNP=y +CONFIG_PNP_PARPORT=m +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_CPIA=m +CONFIG_VIDEO_CPIA_PP=m +CONFIG_VIDEO_CPIA_PP_DMA=y + +For autoloading of all those modules you need to tell kerneld some +stuff. Add the following line to your kerneld config-file +(e.g. /etc/modules.conf or wherever your distribution does store that +stuff): + +options parport_pc dma=3 irq=7 +alias char-major-81 cpia_pp + +The first line tells the dma/irq channels to use. Those _must_ match +the settings of your BIOS. Do NOT simply use the values above. See +Documentation/parport.txt for more information about this. The second +line associates the video-device file with the driver. Of cause you +can also load the modules once upon boot (usually done in /etc/modules). + +Linked into the kernel: +======================= + +Make sure you have selected the following kernel options. Note that +you cannot compile the parport-stuff as modules and the cpia-driver +statically (the other way round is okay though). + +The cpia-stuff is in the section 'Character devices -> Video For Linux'. +The PnP-stuff is in the section 'Plug and Play support'. +The parport-stuff is in the section 'General setup'. + +CONFIG_PARPORT=y +CONFIG_PARPORT_PC=y +CONFIG_PNP=y +CONFIG_PNP_PARPORT=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_CPIA=y +CONFIG_VIDEO_CPIA_PP=y +CONFIG_VIDEO_CPIA_PP_DMA=y + +To use DMA/irq you will need to tell the kernel upon boot time the +hardware configuration of the parport. You can give the boot-parameter +at the LILO-prompt or specify it in lilo.conf. I use the following +append-line in lilo.conf: + + append="parport=0x378,7,3" + +See Documentation/parport.txt for more information about the +configuration of the parport and the values given above. Do not simply +use the values given above. + +--------------------------------------------------------------------------- +FEATURES: + +- mmap/read v4l-interface (but no overlay) +- image formats: CIF/QCIF, SIF/QSIF, various others used by isabel; + note: all sizes except CIF/QCIF are implemented by clipping, i.e. + pixels are not uploaded from the camera +- palettes: VIDEO_PALETTE_GRAY, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB555, + VIDEO_PALETTE_RGB24, VIDEO_PALETTE_RGB32, VIDEO_PALETTE_YUYV, + VIDEO_PALETTE_UYVY, VIDEO_PALETTE_YUV422 +- DMA-transfer for Intel-architecture +- state information (color balance, exposure, ...) is preserved between + device opens +- complete control over camera via proc-interface (_all_ camera settings are + supported), there is also a python-gtk application available for this +- works under SMP (but the driver is completly serialized and synchronous) + so you get no benefit from SMP +- might work for non-Intel architecture, let me know about this + +--------------------------------------------------------------------------- +TESTED APPLICATIONS: + +- a simple test application based on Xt is available at [3] +- another test-application based on gqcam-0.4 (uses GTK) +- gqcam-0.5 should work +- xawtv-3.x (also the webcam) +- xawtv-2.46 +- w3cam (cgi-interface and vidcat, e.g. you may try out 'vidcat |xv + -maxpect -root -quit +noresetroot -rmode 5 -') +- vic, the MBONE video conferencing tool (version 2.8ucl4-1) +- isabel 3R4beta (barely working, but AFAICT all the problems are on + their side) +- camserv-0.40 + +See [3] for pointers to v4l-applications. + +--------------------------------------------------------------------------- +KNOWN PROBLEMS: + +- some applications do not handle the image format correctly, you will + see strange horizontal stripes instead of a nice picture -> make sure + your application does use a supported image size or queries the driver + for the actually used size (reason behind this: the camera cannot + provide any image format, so if size NxM is requested the driver will + use a format to the closest fitting N1xM1, the application should now + query for this granted size, most applications do not). +- all the todo ;) +- if there is not enough light and the picture is too dark try to + adjust the SetSensorFPS setting, automatic frame rate adjustment + has its price +- do not try out isabel 3R4beta (built 135), you will be disappointed +- my linux box crashed during a ppp-dialup session lately for an unknown + reason, the driver was idle, but who knows. So there might be + so buffer overflow somewhere, you are warned anyway + +--------------------------------------------------------------------------- +TODO: + +- port to linux 2.3.X +- using ieee1284 support in the kernel (when it works) +- multiple camera support (struct camera or something) - This should work, + but hasn't been tested yet. +- architecture independence +- integrate USB support +- SMP-safe asynchronous mmap interface +- nibble mode for old parport interfaces +- streamin capture, this should give a performance gain + +--------------------------------------------------------------------------- +IMPLEMENTATION NOTES: + +The camera can act in two modes, streaming or grabbing. Right now a +polling grab-scheme is used. Maybe interrupt driven streaming will be +used for a ansychronous mmap interface in the next major release of the +driver. This might give a better frame rate. + +--------------------------------------------------------------------------- +THANKS (in no particular order): + +- Karoly Erdei and RISC-Linz for being + my boss ;) resp. my employer and for providing me the hardware and + allow me to devote some working time to this project +- Bas Huisman for writing the initial parport code +- Scott J. Bertin for cleanups, the proc-filesystem + and much more +- Jarl Totland for setting up the mailing list + and maintaining the web-server[3] +- Manuel J. Petit de Gabriel for providing help + with Isabel (http://isabel.dit.upm.es/) +- Henry Bruce for providing developers information about + the CPiA chip, I wish all companies would treat Linux as seriously +- FIXME for fixes related to the 1.02 firmware +- special kudos to all the tester whose machines crashed and/or + will crash. :) + +--------------------------------------------------------------------------- +REFERENCES + + 1. http://www.risc.uni-linz.ac.at/people/ppregler + mailto:Peter_Pregler@email.com + 2. see the file COPYING in the top directory of the kernel tree + 3. http://home.eunet.no/~jtotland/vision/ diff --git a/MAINTAINERS b/MAINTAINERS index 76ec2ba2260d..6fc7319155d0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -815,9 +815,7 @@ L: linux-net@vger.rutgers.edu S: Supported STALLION TECHNOLOGIES MULTIPORT SERIAL BOARDS -P: Greg Ungerer M: support@stallion.oz.au -M: gerg@stallion.com W: http://www.stallion.com S: Supported diff --git a/Makefile b/Makefile index e1652de79499..56cc972fa979 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 SUBLEVEL = 15 -EXTRAVERSION = pre5 +EXTRAVERSION = pre6 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index e2d6a35a22b7..ed0f60eb9bc1 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -667,10 +667,18 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) /* It isnt really a PCI quirk directly, but the cure is the same. The MediaGX has deep magic SMM stuff that handles the SB emulation. It thows away the fifo on disable_dma() which - is wrong and ruins the audio. */ + is wrong and ruins the audio. + + Bug2: VSA1 has a wrap bug so that using maximum sized DMA + causes bad things. According to NatSemi VSA2 has another + bug to do with 'hlt'. I've not seen any boards using VSA2 + and X doesn't seem to support it either so who cares 8). + VSA1 we work around however. + + */ printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bug.\n"); - isa_dma_bridge_buggy = 1; + isa_dma_bridge_buggy = 2; #endif /* GXm supports extended cpuid levels 'ala' AMD */ diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 898ce1d2768a..adcbb82423d0 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -1739,6 +1739,7 @@ __secondary_start_gemini: .globl __secondary_start_psurge __secondary_start_psurge: li r24,1 /* cpu # */ + li r3,0 /* starting physical address */ b __secondary_start .globl __secondary_hold diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 46ac520242cc..119184ab62a5 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -173,6 +173,7 @@ if [ "$CONFIG_NET" = "y" ]; then bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then tristate 'Dummy net driver support' CONFIG_DUMMY + tristate 'Bonding driver support' CONFIG_BONDING tristate 'PPP (point-to-point) support' CONFIG_PPP if [ ! "$CONFIG_PPP" = "n" ]; then comment 'CCP compressors for PPP are only built as modules.' diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 4b0afd362821..06d896dacd4c 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -203,6 +203,7 @@ CONFIG_SCSI_FCAL=m # CONFIG_NETDEVICES=y CONFIG_DUMMY=m +CONFIG_BONDING=m CONFIG_PPP=m # diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index f1c2417f5016..dafefbb7e305 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -217,6 +217,7 @@ if [ "$CONFIG_NET" = "y" ]; then bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then tristate 'Dummy net driver support' CONFIG_DUMMY + tristate 'Bonding driver support' CONFIG_BONDING tristate 'PPP (point-to-point) support' CONFIG_PPP if [ ! "$CONFIG_PPP" = "n" ]; then comment 'CCP compressors for PPP are only built as modules.' diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index a9f3272b1103..427676772dc0 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -240,6 +240,7 @@ CONFIG_SCSI_FCAL=m # CONFIG_NETDEVICES=y CONFIG_DUMMY=m +CONFIG_BONDING=m CONFIG_PPP=m # diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index f0c36d1a72d6..b3eb5c78b75f 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c @@ -1,7 +1,7 @@ /* * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra. * - * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) */ diff --git a/arch/sparc64/kernel/dtlb_backend.S b/arch/sparc64/kernel/dtlb_backend.S index c7d6a88460ad..32eb7e20b104 100644 --- a/arch/sparc64/kernel/dtlb_backend.S +++ b/arch/sparc64/kernel/dtlb_backend.S @@ -1,8 +1,8 @@ -/* $Id: dtlb_backend.S,v 1.7.2.1 1999/12/05 10:41:57 davem Exp $ +/* $Id: dtlb_backend.S,v 1.7.2.2 2000/01/31 05:02:35 davem Exp $ * dtlb_backend.S: Back end to DTLB miss replacement strategy. * This is included directly into the trap table. * - * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com) * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz) */ diff --git a/arch/sparc64/kernel/dtlb_base.S b/arch/sparc64/kernel/dtlb_base.S index 71e5b14d77d7..1843d4abf27b 100644 --- a/arch/sparc64/kernel/dtlb_base.S +++ b/arch/sparc64/kernel/dtlb_base.S @@ -1,8 +1,8 @@ -/* $Id: dtlb_base.S,v 1.4 1998/06/15 16:59:30 jj Exp $ +/* $Id: dtlb_base.S,v 1.4.2.1 2000/01/31 05:02:35 davem Exp $ * dtlb_base.S: Front end to DTLB miss replacement strategy. * This is included directly into the trap table. * - * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com) * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz) */ diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S index 067a1d0511fb..95225dd6ed9d 100644 --- a/arch/sparc64/kernel/dtlb_prot.S +++ b/arch/sparc64/kernel/dtlb_prot.S @@ -1,8 +1,8 @@ -/* $Id: dtlb_prot.S,v 1.18 1999/03/02 15:42:14 jj Exp $ +/* $Id: dtlb_prot.S,v 1.18.2.1 2000/01/31 05:02:35 davem Exp $ * dtlb_prot.S: DTLB protection trap strategy. * This is included directly into the trap table. * - * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com) * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz) */ diff --git a/arch/sparc64/kernel/itlb_base.S b/arch/sparc64/kernel/itlb_base.S index eefc1c0748c0..f0ee2d95766a 100644 --- a/arch/sparc64/kernel/itlb_base.S +++ b/arch/sparc64/kernel/itlb_base.S @@ -1,8 +1,8 @@ -/* $Id: itlb_base.S,v 1.7 1999/03/02 15:42:12 jj Exp $ +/* $Id: itlb_base.S,v 1.7.2.1 2000/01/31 05:02:35 davem Exp $ * itlb_base.S: Front end to ITLB miss replacement strategy. * This is included directly into the trap table. * - * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com) * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz) */ diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c index 38f33ecd6a9b..a516fc165f67 100644 --- a/arch/sparc64/kernel/starfire.c +++ b/arch/sparc64/kernel/starfire.c @@ -1,7 +1,7 @@ -/* $Id: starfire.c,v 1.2 1998/12/09 18:53:11 davem Exp $ +/* $Id: starfire.c,v 1.2.2.1 2000/01/31 05:02:35 davem Exp $ * starfire.c: Starfire/E10000 support. * - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ #include diff --git a/arch/sparc64/lib/debuglocks.c b/arch/sparc64/lib/debuglocks.c index 69286c4b7d0d..cdc81815965d 100644 --- a/arch/sparc64/lib/debuglocks.c +++ b/arch/sparc64/lib/debuglocks.c @@ -1,7 +1,7 @@ -/* $Id: debuglocks.c,v 1.2 1998/10/13 09:07:27 davem Exp $ +/* $Id: debuglocks.c,v 1.2.2.1 2000/01/31 05:02:31 davem Exp $ * debuglocks.c: Debugging versions of SMP locking primitives. * - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ #include diff --git a/arch/sparc64/lib/memscan.S b/arch/sparc64/lib/memscan.S index 423bc1409cad..28358aaf1631 100644 --- a/arch/sparc64/lib/memscan.S +++ b/arch/sparc64/lib/memscan.S @@ -1,8 +1,8 @@ -/* $Id: memscan.S,v 1.2 1998/05/21 14:42:22 jj Exp $ +/* $Id: memscan.S,v 1.2.2.1 2000/01/31 05:02:32 davem Exp $ * memscan.S: Optimized memscan for Sparc64. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz) - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ #define HI_MAGIC 0x8080808080808080 diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 3c9dee4de4bc..287c3ddea64f 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -44,6 +44,7 @@ else bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586 bool ' CMD646 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CMD646 + bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 fi fi fi diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 5ba9cdbe9e14..950f1fa6dd7a 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -19,8 +19,8 @@ */ -#define DAC960_DriverVersion "2.2.4" -#define DAC960_DriverDate "23 August 1999" +#define DAC960_DriverVersion "2.2.5" +#define DAC960_DriverDate "23 January 2000" #include @@ -120,7 +120,7 @@ static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller) DAC960_Announce("***** DAC960 RAID Driver Version " DAC960_DriverVersion " of " DAC960_DriverDate " *****\n", Controller); - DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff " + DAC960_Announce("Copyright 1998-2000 by Leonard N. Zubkoff " "\n", Controller); } @@ -193,6 +193,23 @@ static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command) } +/* + DAC960_WaitForCommand waits for a wake_up on Controller's Command Wait Queue. +*/ + +static void DAC960_WaitForCommand(DAC960_Controller_T *Controller) +{ + WaitQueue_T WaitQueueEntry = { current, NULL }; + add_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry); + current->state = TASK_UNINTERRUPTIBLE; + spin_unlock(&io_request_lock); + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry); + spin_lock_irq(&io_request_lock); +} + + /* DAC960_QueueCommand queues Command. */ @@ -316,6 +333,62 @@ static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller, } +/* + DAC960_ReportErrorStatus reports Controller BIOS Messages passed through + the Error Status Register when the driver performs the BIOS handshaking. + It returns true for fatal errors and false otherwise. +*/ + +static boolean DAC960_ReportErrorStatus(DAC960_Controller_T *Controller, + unsigned char ErrorStatus, + unsigned char Parameter0, + unsigned char Parameter1) +{ + switch (ErrorStatus) + { + case 0x00: + DAC960_Notice("Physical Drive %d:%d Not Responding\n", + Controller, Parameter1, Parameter0); + break; + case 0x08: + if (Controller->DriveSpinUpMessageDisplayed) break; + DAC960_Notice("Spinning Up Drives\n", Controller); + Controller->DriveSpinUpMessageDisplayed = true; + break; + case 0x30: + DAC960_Notice("Configuration Checksum Error\n", Controller); + break; + case 0x60: + DAC960_Notice("Mirror Race Recovery Failed\n", Controller); + break; + case 0x70: + DAC960_Notice("Mirror Race Recovery In Progress\n", Controller); + break; + case 0x90: + DAC960_Notice("Physical Drive %d:%d COD Mismatch\n", + Controller, Parameter1, Parameter0); + break; + case 0xA0: + DAC960_Notice("Logical Drive Installation Aborted\n", Controller); + break; + case 0xB0: + DAC960_Notice("Mirror Race On A Critical Logical Drive\n", Controller); + break; + case 0xD0: + DAC960_Notice("New Controller Configuration Found\n", Controller); + break; + case 0xF0: + DAC960_Error("Fatal Memory Parity Error for Controller at\n", Controller); + return true; + default: + DAC960_Error("Unknown Initialization Error %02X for Controller at\n", + Controller, ErrorStatus); + return true; + } + return false; +} + + /* DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface. */ @@ -382,7 +455,7 @@ static boolean DAC960_EnableMemoryMailboxInterface(DAC960_Controller_T case DAC960_V5_Controller: while (--TimeoutCounter >= 0) { - if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress)) + if (!DAC960_V5_HardwareMailboxFullP(ControllerBaseAddress)) break; udelay(10); } @@ -474,11 +547,13 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) unsigned char DeviceFunction = PCI_Device->devfn; unsigned char Device = DeviceFunction >> 3; unsigned char Function = DeviceFunction & 0x7; + unsigned char ErrorStatus, Parameter0, Parameter1; unsigned int IRQ_Channel = PCI_Device->irq; unsigned long BaseAddress0 = PCI_Device->base_address[0]; unsigned long BaseAddress1 = PCI_Device->base_address[1]; unsigned short SubsystemVendorID, SubsystemDeviceID; int CommandIdentifier; + void *BaseAddress; pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_VENDOR_ID, &SubsystemVendorID); pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_ID, @@ -523,24 +598,6 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) Controller->Device = Device; Controller->Function = Function; sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber); - /* - Acquire shared access to the IRQ Channel. - */ - if (IRQ_Channel == 0) - { - DAC960_Error("IRQ Channel %d illegal for Controller at\n", - Controller, IRQ_Channel); - goto Failure; - } - strcpy(Controller->FullModelName, "DAC960"); - if (request_irq(IRQ_Channel, DAC960_InterruptHandler, - SA_SHIRQ, Controller->FullModelName, Controller) < 0) - { - DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n", - Controller, IRQ_Channel); - goto Failure; - } - Controller->IRQ_Channel = IRQ_Channel; /* Map the Controller Register Window. */ @@ -556,34 +613,87 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) "Controller at\n", Controller); goto Failure; } + BaseAddress = Controller->BaseAddress; switch (ControllerType) { case DAC960_V5_Controller: - DAC960_V5_DisableInterrupts(Controller->BaseAddress); + DAC960_V5_DisableInterrupts(BaseAddress); + DAC960_V5_AcknowledgeHardwareMailboxStatus(BaseAddress); + udelay(1000); + while (DAC960_V5_InitializationInProgressP(BaseAddress)) + { + if (DAC960_V5_ReadErrorStatus(BaseAddress, &ErrorStatus, + &Parameter0, &Parameter1) && + DAC960_ReportErrorStatus(Controller, ErrorStatus, + Parameter0, Parameter1)) + goto Failure; + udelay(10); + } if (!DAC960_EnableMemoryMailboxInterface(Controller)) { DAC960_Error("Unable to Enable Memory Mailbox Interface " "for Controller at\n", Controller); goto Failure; } - DAC960_V5_EnableInterrupts(Controller->BaseAddress); + DAC960_V5_EnableInterrupts(BaseAddress); break; case DAC960_V4_Controller: - DAC960_V4_DisableInterrupts(Controller->BaseAddress); + DAC960_V4_DisableInterrupts(BaseAddress); + DAC960_V4_AcknowledgeHardwareMailboxStatus(BaseAddress); + udelay(1000); + while (DAC960_V4_InitializationInProgressP(BaseAddress)) + { + if (DAC960_V4_ReadErrorStatus(BaseAddress, &ErrorStatus, + &Parameter0, &Parameter1) && + DAC960_ReportErrorStatus(Controller, ErrorStatus, + Parameter0, Parameter1)) + goto Failure; + udelay(10); + } if (!DAC960_EnableMemoryMailboxInterface(Controller)) { DAC960_Error("Unable to Enable Memory Mailbox Interface " "for Controller at\n", Controller); goto Failure; } - DAC960_V4_EnableInterrupts(Controller->BaseAddress); + DAC960_V4_EnableInterrupts(BaseAddress); break; case DAC960_V3_Controller: request_region(Controller->IO_Address, 0x80, Controller->FullModelName); - DAC960_V3_EnableInterrupts(Controller->BaseAddress); + DAC960_V3_DisableInterrupts(BaseAddress); + DAC960_V3_AcknowledgeStatus(BaseAddress); + udelay(1000); + while (DAC960_V3_InitializationInProgressP(BaseAddress)) + { + if (DAC960_V3_ReadErrorStatus(BaseAddress, &ErrorStatus, + &Parameter0, &Parameter1) && + DAC960_ReportErrorStatus(Controller, ErrorStatus, + Parameter0, Parameter1)) + goto Failure; + udelay(10); + } + DAC960_V3_EnableInterrupts(BaseAddress); break; } + /* + Acquire shared access to the IRQ Channel. + */ + if (IRQ_Channel == 0) + { + DAC960_Error("IRQ Channel %d illegal for Controller at\n", + Controller, IRQ_Channel); + goto Failure; + } + strcpy(Controller->FullModelName, "DAC960"); + if (request_irq(IRQ_Channel, DAC960_InterruptHandler, + SA_SHIRQ, Controller->FullModelName, Controller) < 0) + { + DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n", + Controller, IRQ_Channel); + goto Failure; + } + Controller->IRQ_Channel = IRQ_Channel; DAC960_ActiveControllerCount++; for (CommandIdentifier = 0; CommandIdentifier < DAC960_MaxChannels; @@ -604,11 +714,11 @@ static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) "0x%X PCI Address 0x%X\n", Controller, Bus, Device, Function, IO_Address, PCI_Address); if (Controller == NULL) break; - if (Controller->IRQ_Channel > 0) - free_irq(IRQ_Channel, Controller); if (Controller->MemoryMappedAddress != NULL) iounmap(Controller->MemoryMappedAddress); DAC960_Controllers[Controller->ControllerNumber] = NULL; + if (Controller->IRQ_Channel > 0) + free_irq(IRQ_Channel, Controller); Ignore: kfree(Controller); } @@ -1282,9 +1392,7 @@ static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller, Command = DAC960_AllocateCommand(Controller); if (Command != NULL) break; if (!WaitForCommand) return false; - spin_unlock(&io_request_lock); - sleep_on(&Controller->CommandWaitQueue); - spin_lock_irq(&io_request_lock); + DAC960_WaitForCommand(Controller); } DAC960_ClearCommand(Command); if (Request->cmd == READ) @@ -1836,6 +1944,20 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) } else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress) Controller->NeedConsistencyCheckProgress = true; + if (CommandType != DAC960_MonitoringCommand && + Controller->RebuildFlagPending) + { + DAC960_Enquiry_T *Enquiry = (DAC960_Enquiry_T *) + Bus_to_Virtual(Command->CommandMailbox.Type3.BusAddress); + Enquiry->RebuildFlag = Controller->PendingRebuildFlag; + Controller->RebuildFlagPending = false; + } + else if (CommandType == DAC960_MonitoringCommand && + NewEnquiry->RebuildFlag > DAC960_BackgroundCheckInProgress) + { + Controller->PendingRebuildFlag = NewEnquiry->RebuildFlag; + Controller->RebuildFlagPending = true; + } } else if (CommandOpcode == DAC960_PerformEventLogOperation) { @@ -2019,6 +2141,9 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) Controller->RebuildProgress.LogicalDriveSize; unsigned int BlocksCompleted = LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks; + if (CommandStatus == DAC960_NoRebuildOrCheckInProgress && + Controller->LastRebuildStatus == DAC960_NormalCompletion) + CommandStatus = DAC960_RebuildSuccessful; switch (CommandStatus) { case DAC960_NormalCompletion: @@ -2046,13 +2171,28 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) "Failure of Drive Being Rebuilt\n", Controller); break; case DAC960_NoRebuildOrCheckInProgress: - if (Controller->LastRebuildStatus != DAC960_NormalCompletion) - break; + break; case DAC960_RebuildSuccessful: DAC960_Progress("Rebuild Completed Successfully\n", Controller); break; + case DAC960_RebuildSuccessfullyTerminated: + DAC960_Progress("Rebuild Successfully Terminated\n", Controller); + break; } Controller->LastRebuildStatus = CommandStatus; + if (CommandType != DAC960_MonitoringCommand && + Controller->RebuildStatusPending) + { + Command->CommandStatus = Controller->PendingRebuildStatus; + Controller->RebuildStatusPending = false; + } + else if (CommandType == DAC960_MonitoringCommand && + CommandStatus != DAC960_NormalCompletion && + CommandStatus != DAC960_NoRebuildOrCheckInProgress) + { + Controller->PendingRebuildStatus = CommandStatus; + Controller->RebuildStatusPending = true; + } } else if (CommandOpcode == DAC960_RebuildStat) { @@ -2267,7 +2407,7 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) if (CommandType == DAC960_QueuedCommand) { DAC960_KernelCommand_T *KernelCommand = Command->KernelCommand; - KernelCommand->CommandStatus = CommandStatus; + KernelCommand->CommandStatus = Command->CommandStatus; Command->KernelCommand = NULL; if (CommandOpcode == DAC960_DCDB) Controller->DirectCommandActive[KernelCommand->DCDB->Channel] @@ -2288,9 +2428,12 @@ static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) return; } /* - Deallocate the Command, and wake up any processes waiting on a free Command. + Deallocate the Command. */ DAC960_DeallocateCommand(Command); + /* + Wake up any processes waiting on a free Command. + */ wake_up(&Controller->CommandWaitQueue); } @@ -2696,19 +2839,14 @@ static int DAC960_UserIOCTL(Inode_T *Inode, File_T *File, } if (CommandOpcode == DAC960_DCDB) { - while (true) - { - DAC960_AcquireControllerLock(Controller, &ProcessorFlags); - if (!Controller->DirectCommandActive[DCDB.Channel] - [DCDB.TargetID]) - Command = DAC960_AllocateCommand(Controller); - if (Command != NULL) - Controller->DirectCommandActive[DCDB.Channel] - [DCDB.TargetID] = true; - DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); - if (Command != NULL) break; - sleep_on(&Controller->CommandWaitQueue); - } + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + while (Controller->DirectCommandActive[DCDB.Channel] + [DCDB.TargetID] || + (Command = DAC960_AllocateCommand(Controller)) == NULL) + DAC960_WaitForCommand(Controller); + Controller->DirectCommandActive[DCDB.Channel] + [DCDB.TargetID] = true; + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); DAC960_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox, @@ -2718,14 +2856,10 @@ static int DAC960_UserIOCTL(Inode_T *Inode, File_T *File, } else { - while (true) - { - DAC960_AcquireControllerLock(Controller, &ProcessorFlags); - Command = DAC960_AllocateCommand(Controller); - DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); - if (Command != NULL) break; - sleep_on(&Controller->CommandWaitQueue); - } + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + while ((Command = DAC960_AllocateCommand(Controller)) == NULL) + DAC960_WaitForCommand(Controller); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); DAC960_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox, @@ -3127,14 +3261,10 @@ static boolean DAC960_ExecuteUserCommand(DAC960_Controller_T *Controller, DAC960_CommandMailbox_T *CommandMailbox; ProcessorFlags_T ProcessorFlags; unsigned char Channel, TargetID, LogicalDriveNumber; - while (true) - { - DAC960_AcquireControllerLock(Controller, &ProcessorFlags); - Command = DAC960_AllocateCommand(Controller); - DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); - if (Command != NULL) break; - sleep_on(&Controller->CommandWaitQueue); - } + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + while ((Command = DAC960_AllocateCommand(Controller)) == NULL) + DAC960_WaitForCommand(Controller); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); Controller->UserStatusLength = 0; DAC960_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; diff --git a/drivers/block/DAC960.h b/drivers/block/DAC960.h index a96757db61f2..bba1799141aa 100644 --- a/drivers/block/DAC960.h +++ b/drivers/block/DAC960.h @@ -55,17 +55,17 @@ typedef enum { false, true } __attribute__ ((packed)) boolean; /* - Define a 32 bit I/O Address data type. + Define a 32/64 bit I/O Address data type. */ -typedef unsigned int DAC960_IO_Address_T; +typedef unsigned long DAC960_IO_Address_T; /* - Define a 32 bit PCI Bus Address data type. + Define a 32/64 bit PCI Bus Address data type. */ -typedef unsigned int DAC960_PCI_Address_T; +typedef unsigned long DAC960_PCI_Address_T; /* @@ -196,6 +196,7 @@ typedef unsigned char DAC960_CommandIdentifier_T; #define DAC960_RebuildFailed_BadBlocksOnOther 0x0003 /* Consistency */ #define DAC960_RebuildFailed_NewDriveFailed 0x0004 /* Consistency */ #define DAC960_RebuildSuccessful 0x0100 /* Consistency */ +#define DAC960_RebuildSuccessfullyTerminated 0x0107 /* Consistency */ #define DAC960_AddCapacityInProgress 0x0004 /* Consistency */ #define DAC960_AddCapacityFailedOrSuspended 0x00F4 /* Consistency */ #define DAC960_Config2ChecksumError 0x0002 /* Configuration */ @@ -513,7 +514,7 @@ typedef struct DAC960_DeviceState unsigned char SynchronousMultiplier; /* Byte 4 */ unsigned char SynchronousOffset:5; /* Byte 5 Bits 0-4 */ unsigned char :3; /* Byte 5 Bits 5-7 */ - unsigned long DiskSize __attribute__ ((packed)); /* Bytes 6-9 */ + unsigned int DiskSize __attribute__ ((packed)); /* Bytes 6-9 */ } DAC960_DeviceState_T; @@ -1212,6 +1213,7 @@ typedef struct DAC960_Controller unsigned char LogicalDriveCount; unsigned char GeometryTranslationHeads; unsigned char GeometryTranslationSectors; + unsigned char PendingRebuildFlag; unsigned short ControllerQueueDepth; unsigned short DriverQueueDepth; unsigned short MaxBlocksPerCommand; @@ -1247,6 +1249,9 @@ typedef struct DAC960_Controller boolean NeedRebuildProgress; boolean NeedConsistencyCheckProgress; boolean EphemeralProgressMessage; + boolean RebuildFlagPending; + boolean RebuildStatusPending; + boolean DriveSpinUpMessageDisplayed; Timer_T MonitoringTimer; GenericDiskInfo_T GenericDiskInfo; DAC960_Command_T *FreeCommands; @@ -1269,6 +1274,7 @@ typedef struct DAC960_Controller DAC960_EventLogEntry_T EventLogEntry; DAC960_RebuildProgress_T RebuildProgress; DAC960_CommandStatus_T LastRebuildStatus; + DAC960_CommandStatus_T PendingRebuildStatus; DAC960_LogicalDriveInformation_T LogicalDriveInformation[2][DAC960_MaxLogicalDrives]; DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives]; @@ -1393,7 +1399,8 @@ typedef enum DAC960_V5_MailboxRegister11Offset = 0x5B, DAC960_V5_MailboxRegister12Offset = 0x5C, DAC960_V5_StatusCommandIdentifierRegOffset = 0x5D, - DAC960_V5_StatusRegisterOffset = 0x5E + DAC960_V5_StatusRegisterOffset = 0x5E, + DAC960_V5_ErrorStatusRegisterOffset = 0x63 } DAC960_V5_RegisterOffsets_T; @@ -1415,7 +1422,8 @@ typedef union DAC960_V5_InboundDoorBellRegister } Write; struct { boolean HardwareMailboxEmpty:1; /* Bit 0 */ - unsigned char :7; /* Bits 1-7 */ + boolean InitializationNotInProgress:1; /* Bit 1 */ + unsigned char :6; /* Bits 2-7 */ } Read; } DAC960_V5_InboundDoorBellRegister_T; @@ -1458,6 +1466,22 @@ typedef union DAC960_V5_InterruptMaskRegister DAC960_V5_InterruptMaskRegister_T; +/* + Define the structure of the DAC960 V5 Error Status Register. +*/ + +typedef union DAC960_V5_ErrorStatusRegister +{ + unsigned char All; + struct { + unsigned int :2; /* Bits 0-1 */ + boolean ErrorStatusPending:1; /* Bit 2 */ + unsigned int :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V5_ErrorStatusRegister_T; + + /* Define inline functions to provide an abstraction for reading and writing the DAC960 V5 Controller Interface Registers. @@ -1514,12 +1538,21 @@ void DAC960_V5_MemoryMailboxNewCommand(void *ControllerBaseAddress) } static inline -boolean DAC960_V5_HardwareMailboxEmptyP(void *ControllerBaseAddress) +boolean DAC960_V5_HardwareMailboxFullP(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); + return !InboundDoorBellRegister.Read.HardwareMailboxEmpty; +} + +static inline +boolean DAC960_V5_InitializationInProgressP(void *ControllerBaseAddress) { DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); - return InboundDoorBellRegister.Read.HardwareMailboxEmpty; + return !InboundDoorBellRegister.Read.InitializationNotInProgress; } static inline @@ -1575,7 +1608,7 @@ static inline void DAC960_V5_EnableInterrupts(void *ControllerBaseAddress) { DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; - InterruptMaskRegister.All = 0; + InterruptMaskRegister.All = 0xFF; InterruptMaskRegister.Bits.DisableInterrupts = false; writeb(InterruptMaskRegister.All, ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); @@ -1585,7 +1618,7 @@ static inline void DAC960_V5_DisableInterrupts(void *ControllerBaseAddress) { DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; - InterruptMaskRegister.All = 0; + InterruptMaskRegister.All = 0xFF; InterruptMaskRegister.Bits.DisableInterrupts = true; writeb(InterruptMaskRegister.All, ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); @@ -1607,7 +1640,9 @@ void DAC960_V5_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + wmb(); NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; + mb(); } static inline @@ -1637,6 +1672,26 @@ DAC960_V5_ReadStatusRegister(void *ControllerBaseAddress) return readw(ControllerBaseAddress + DAC960_V5_StatusRegisterOffset); } +static inline boolean +DAC960_V5_ReadErrorStatus(void *ControllerBaseAddress, + unsigned char *ErrorStatus, + unsigned char *Parameter0, + unsigned char *Parameter1) +{ + DAC960_V5_ErrorStatusRegister_T ErrorStatusRegister; + ErrorStatusRegister.All = + readb(ControllerBaseAddress + DAC960_V5_ErrorStatusRegisterOffset); + if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false; + ErrorStatusRegister.Bits.ErrorStatusPending = false; + *ErrorStatus = ErrorStatusRegister.All; + *Parameter0 = + readb(ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset); + *Parameter1 = + readb(ControllerBaseAddress + DAC960_V5_CommandIdentifierRegisterOffset); + writeb(0xFF, ControllerBaseAddress + DAC960_V5_ErrorStatusRegisterOffset); + return true; +} + static inline void DAC960_V5_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller) { @@ -1695,7 +1750,8 @@ typedef enum DAC960_V4_MailboxRegister11Offset = 0x100B, DAC960_V4_MailboxRegister12Offset = 0x100C, DAC960_V4_StatusCommandIdentifierRegOffset = 0x1018, - DAC960_V4_StatusRegisterOffset = 0x101A + DAC960_V4_StatusRegisterOffset = 0x101A, + DAC960_V4_ErrorStatusRegisterOffset = 0x103F } DAC960_V4_RegisterOffsets_T; @@ -1717,7 +1773,8 @@ typedef union DAC960_V4_InboundDoorBellRegister } Write; struct { boolean HardwareMailboxFull:1; /* Bit 0 */ - unsigned int :31; /* Bits 1-31 */ + boolean InitializationInProgress:1; /* Bit 1 */ + unsigned int :30; /* Bits 2-31 */ } Read; } DAC960_V4_InboundDoorBellRegister_T; @@ -1761,6 +1818,22 @@ typedef union DAC960_V4_InterruptMaskRegister DAC960_V4_InterruptMaskRegister_T; +/* + Define the structure of the DAC960 V4 Error Status Register. +*/ + +typedef union DAC960_V4_ErrorStatusRegister +{ + unsigned char All; + struct { + unsigned int :2; /* Bits 0-1 */ + boolean ErrorStatusPending:1; /* Bit 2 */ + unsigned int :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V4_ErrorStatusRegister_T; + + /* Define inline functions to provide an abstraction for reading and writing the DAC960 V4 Controller Interface Registers. @@ -1825,6 +1898,15 @@ boolean DAC960_V4_HardwareMailboxFullP(void *ControllerBaseAddress) return InboundDoorBellRegister.Read.HardwareMailboxFull; } +static inline +boolean DAC960_V4_InitializationInProgressP(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.InitializationInProgress; +} + static inline void DAC960_V4_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) { @@ -1914,7 +1996,9 @@ void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + wmb(); NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; + mb(); } static inline @@ -1944,11 +2028,31 @@ DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress) return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset); } +static inline boolean +DAC960_V4_ReadErrorStatus(void *ControllerBaseAddress, + unsigned char *ErrorStatus, + unsigned char *Parameter0, + unsigned char *Parameter1) +{ + DAC960_V4_ErrorStatusRegister_T ErrorStatusRegister; + ErrorStatusRegister.All = + readb(ControllerBaseAddress + DAC960_V4_ErrorStatusRegisterOffset); + if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false; + ErrorStatusRegister.Bits.ErrorStatusPending = false; + *ErrorStatus = ErrorStatusRegister.All; + *Parameter0 = + readb(ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); + *Parameter1 = + readb(ControllerBaseAddress + DAC960_V4_CommandIdentifierRegisterOffset); + writeb(0, ControllerBaseAddress + DAC960_V4_ErrorStatusRegisterOffset); + return true; +} + static inline void DAC960_V4_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller) { void *ControllerBaseAddress = Controller->BaseAddress; - writel(0xAABBFFFF, + writel(0x743C485E, ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); writel((unsigned long) Controller->FirstCommandMailbox, ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); @@ -1966,7 +2070,7 @@ void DAC960_V4_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller, { void *ControllerBaseAddress = Controller->BaseAddress; if (readl(ControllerBaseAddress - + DAC960_V4_CommandOpcodeRegisterOffset) != 0xAABBFFFF) + + DAC960_V4_CommandOpcodeRegisterOffset) != 0x743C485E) return; *MemoryMailboxAddress = (void *) readl(ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); @@ -2000,6 +2104,7 @@ typedef enum DAC960_V3_MailboxRegister12Offset = 0x0C, DAC960_V3_StatusCommandIdentifierRegOffset = 0x0D, DAC960_V3_StatusRegisterOffset = 0x0E, + DAC960_V3_ErrorStatusRegisterOffset = 0x3F, DAC960_V3_InboundDoorBellRegisterOffset = 0x40, DAC960_V3_OutboundDoorBellRegisterOffset = 0x41, DAC960_V3_InterruptEnableRegisterOffset = 0x43 @@ -2023,7 +2128,8 @@ typedef union DAC960_V3_InboundDoorBellRegister } Write; struct { boolean MailboxFull:1; /* Bit 0 */ - unsigned char :7; /* Bits 1-7 */ + boolean InitializationInProgress:1; /* Bit 1 */ + unsigned char :6; /* Bits 2-7 */ } Read; } DAC960_V3_InboundDoorBellRegister_T; @@ -2063,6 +2169,22 @@ typedef union DAC960_V3_InterruptEnableRegister DAC960_V3_InterruptEnableRegister_T; +/* + Define the structure of the DAC960 V3 Error Status Register. +*/ + +typedef union DAC960_V3_ErrorStatusRegister +{ + unsigned char All; + struct { + unsigned int :2; /* Bits 0-1 */ + boolean ErrorStatusPending:1; /* Bit 2 */ + unsigned int :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V3_ErrorStatusRegister_T; + + /* Define inline functions to provide an abstraction for reading and writing the DAC960 V3 Controller Interface Registers. @@ -2117,6 +2239,15 @@ boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress) return InboundDoorBellRegister.Read.MailboxFull; } +static inline +boolean DAC960_V3_InitializationInProgressP(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.InitializationInProgress; +} + static inline void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress) { @@ -2192,6 +2323,26 @@ DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress) return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset); } +static inline boolean +DAC960_V3_ReadErrorStatus(void *ControllerBaseAddress, + unsigned char *ErrorStatus, + unsigned char *Parameter0, + unsigned char *Parameter1) +{ + DAC960_V3_ErrorStatusRegister_T ErrorStatusRegister; + ErrorStatusRegister.All = + readb(ControllerBaseAddress + DAC960_V3_ErrorStatusRegisterOffset); + if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false; + ErrorStatusRegister.Bits.ErrorStatusPending = false; + *ErrorStatus = ErrorStatusRegister.All; + *Parameter0 = + readb(ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset); + *Parameter1 = + readb(ControllerBaseAddress + DAC960_V3_CommandIdentifierRegisterOffset); + writeb(0, ControllerBaseAddress + DAC960_V3_ErrorStatusRegisterOffset); + return true; +} + /* Define compatibility macros between Linux 2.0 and Linux 2.1. diff --git a/drivers/block/Makefile b/drivers/block/Makefile index d050b53ac9e4..875c8ebe5aae 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -162,6 +162,10 @@ ifeq ($(CONFIG_BLK_DEV_CMD646),y) IDE_OBJS += cmd646.o endif +ifeq ($(CONFIG_BLK_DEV_CS5530),y) +IDE_OBJS += cs5530.o +endif + ifeq ($(CONFIG_BLK_DEV_SL82C105),y) IDE_OBJS += sl82c105.o endif diff --git a/drivers/block/cmd646.c b/drivers/block/cmd646.c index 57b5931319ff..fa97ff5fe10a 100644 --- a/drivers/block/cmd646.c +++ b/drivers/block/cmd646.c @@ -1,4 +1,4 @@ -/* $Id: cmd646.c,v 1.11 1998/12/13 08:36:54 davem Exp $ +/* $Id: cmd646.c,v 1.11.2.1 2000/01/31 05:02:39 davem Exp $ * cmd646.c: Enable interrupts at initialization time on Ultra/PCI machines. * Note, this driver is not used at all on other systems because * there the "BIOS" has done all of the following already. @@ -6,7 +6,7 @@ * on the 646U2 and not on the 646U. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ #include diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 0de70f00cefa..466730186be1 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -41,8 +41,8 @@ #define SMART2_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.5)" -#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,5) +#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.6)" +#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,6) #define MAJOR_NR COMPAQ_SMART2_MAJOR #include #include @@ -84,6 +84,7 @@ struct board_type products[] = { { 0x40400E11, "Integrated Array", &smart4_access }, { 0x40500E11, "Smart Array 4200", &smart4_access }, { 0x40510E11, "Smart Array 4250ES", &smart4_access }, + { 0x40580E11, "Smart Array 431", &smart4_access }, }; static struct hd_struct * ida; @@ -1223,7 +1224,7 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) return(error); } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); - c->req.bp = virt_to_bus(&(io->c)); + c->req.hdr.blk = virt_to_bus(&(io->c)); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; diff --git a/drivers/block/cpqarray.h b/drivers/block/cpqarray.h index fa51d3dee402..31e9786f3fa2 100644 --- a/drivers/block/cpqarray.h +++ b/drivers/block/cpqarray.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #endif diff --git a/drivers/block/cs5530.c b/drivers/block/cs5530.c new file mode 100644 index 000000000000..785f7931c66f --- /dev/null +++ b/drivers/block/cs5530.c @@ -0,0 +1,467 @@ +/* + * linux/drivers/block/cs5530.c Version 0.4 Feb 6, 2000 + * + * Copyright (C) 2000 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + * + * Development of this chipset driver was funded + * by the nice folks at National Semiconductor. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SETFEATURES_XFER + #define CS5530_2_2_KERNEL +#endif + +#ifdef CS5530_2_2_KERNEL + #include "ide.h" + #define XFER_UDMA_6 0x46 /* 0100|0110 */ + #define XFER_UDMA_5 0x45 /* 0100|0101 */ + #define XFER_UDMA_4 0x44 /* 0100|0100 */ + #define XFER_UDMA_3 0x43 /* 0100|0011 */ + #define XFER_UDMA_2 0x42 /* 0100|0010 */ + #define XFER_UDMA_1 0x41 /* 0100|0001 */ + #define XFER_UDMA_0 0x40 /* 0100|0000 */ + #define XFER_MW_DMA_2 0x22 /* 0010|0010 */ + #define XFER_MW_DMA_1 0x21 /* 0010|0001 */ + #define XFER_MW_DMA_0 0x20 /* 0010|0000 */ + #define XFER_SW_DMA_2 0x12 /* 0001|0010 */ + #define XFER_SW_DMA_1 0x11 /* 0001|0001 */ + #define XFER_SW_DMA_0 0x10 /* 0001|0000 */ + #define XFER_PIO_4 0x0C /* 0000|1100 */ + #define XFER_PIO_3 0x0B /* 0000|1011 */ + #define XFER_PIO_2 0x0A /* 0000|1010 */ + #define XFER_PIO_1 0x09 /* 0000|1001 */ + #define XFER_PIO_0 0x08 /* 0000|1000 */ + #define XFER_PIO_SLOW 0x00 /* 0000|0000 */ + #define SETFEATURES_XFER 0x03 /* Set transfer mode */ + #define pci_for_each_dev(dev) \ + for(dev = pci_devices; dev; dev=dev->next) +#else + #include +#endif /* CS5530_2_2_KERNEL */ + +#include +#include +#include "ide_modes.h" + +/* + * Return the mode name for a drive transfer mode value: + */ +static const char *strmode (byte mode) +{ + switch (mode) { + case XFER_UDMA_4: return("UDMA4"); + case XFER_UDMA_3: return("UDMA3"); + case XFER_UDMA_2: return("UDMA2"); + case XFER_UDMA_1: return("UDMA1"); + case XFER_UDMA_0: return("UDMA0"); + case XFER_MW_DMA_2: return("MDMA2"); + case XFER_MW_DMA_1: return("MDMA1"); + case XFER_MW_DMA_0: return("MDMA0"); + case XFER_SW_DMA_2: return("SDMA2"); + case XFER_SW_DMA_1: return("SDMA1"); + case XFER_SW_DMA_0: return("SDMA0"); + case XFER_PIO_4: return("PIO4"); + case XFER_PIO_3: return("PIO3"); + case XFER_PIO_2: return("PIO2"); + case XFER_PIO_1: return("PIO1"); + case XFER_PIO_0: return("PIO0"); + default: return("???"); + } +} + +/* + * Set a new transfer mode at the drive + */ +int cs5530_set_xfer_mode (ide_drive_t *drive, byte mode) +{ + int i, error = 1; + byte stat; + ide_hwif_t *hwif = HWIF(drive); + + printk("%s: cs5530_set_xfer_mode(%s)\n", drive->name, strmode(mode)); + /* + * If this is a DMA mode setting, then turn off all DMA bits. + * We will set one of them back on afterwards, if all goes well. + * + * Not sure why this is needed (it looks very silly), + * but other IDE chipset drivers also do this fiddling. ???? -ml + */ + switch (mode) { + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + } + + /* + * Select the drive, and issue the SETFEATURES command + */ + disable_irq(hwif->irq); + udelay(1); + SELECT_DRIVE(HWIF(drive), drive); + udelay(1); + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG); + OUT_BYTE(mode, IDE_NSECTOR_REG); + OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); + OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ + + /* + * Wait for drive to become non-BUSY + */ + if ((stat = GET_STAT()) & BUSY_STAT) { + unsigned long flags, timeout; + __save_flags(flags); /* local CPU only */ + ide__sti(); /* local CPU only -- for jiffies */ + timeout = jiffies + WAIT_CMD; + while ((stat = GET_STAT()) & BUSY_STAT) { + if (0 < (signed long)(jiffies - timeout)) + break; + } + __restore_flags(flags); /* local CPU only */ + } + + /* + * Allow status to settle, then read it again. + * A few rare drives vastly violate the 400ns spec here, + * so we'll wait up to 10usec for a "good" status + * rather than expensively fail things immediately. + */ + for (i = 0; i < 10; i++) { + udelay(1); + if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) { + error = 0; + break; + } + } + enable_irq(hwif->irq); + + /* + * Turn dma bit on if all is okay + */ + if (error) { + (void) ide_dump_status(drive, "cs5530_set_xfer_mode", stat); + } else { + switch (mode) { + case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break; + case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break; + case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break; + case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break; + case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break; + case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break; + case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break; + case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break; + case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break; + case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break; + case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break; + } + } + return error; +} + +/* + * Here are the standard PIO mode 0-4 timings for each "format". + * Format-0 uses fast data reg timings, with slower command reg timings. + * Format-1 uses fast timings for all registers, but won't work with all drives. + */ +static unsigned int cs5530_pio_timings[2][5] = + {{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, + {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}}; + +/* + * After chip reset, the PIO timings are set to 0x0000e132, which is not valid. + */ +#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132) +#define CS5530_BASEREG(hwif) (((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20)) + +/* + * cs5530_tuneproc() handles selection/setting of PIO modes + * for both the chipset and drive. + * + * The ide_init_cs5530() routine guarantees that all drives + * will have valid default PIO timings set up before we get here. + */ +static void cs5530_tuneproc (ide_drive_t *drive, byte pio) /* pio=255 means "autotune" */ +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int format, basereg = CS5530_BASEREG(hwif); + static byte modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4}; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + if (!cs5530_set_xfer_mode(drive, modes[pio])) { + format = (inl(basereg+4) >> 31) & 1; + outl(cs5530_pio_timings[format][pio], basereg+(drive->select.b.unit<<3)); + } +} + +/* + * cs5530_config_dma() handles selection/setting of DMA/UDMA modes + * for both the chipset and drive. + */ +static int cs5530_config_dma (ide_drive_t *drive) +{ + int udma_ok = 1, mode = 0; + ide_hwif_t *hwif = HWIF(drive); + int unit = drive->select.b.unit; + ide_drive_t *mate = &hwif->drives[unit^1]; + struct hd_driveid *id = drive->id; + unsigned int basereg, reg, timings; + + + /* + * Default to DMA-off in case we run into trouble here. + */ + (void)hwif->dmaproc(ide_dma_off_quietly, drive); /* turn off DMA while we fiddle */ + outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */ + + /* + * The CS5530 specifies that two drives sharing a cable cannot + * mix UDMA/MDMA. It has to be one or the other, for the pair, + * though different timings can still be chosen for each drive. + * We could set the appropriate timing bits on the fly, + * but that might be a bit confusing. So, for now we statically + * handle this requirement by looking at our mate drive to see + * what it is capable of, before choosing a mode for our own drive. + */ + if (mate->present) { + struct hd_driveid *mateid = mate->id; + if (mateid && (mateid->capability & 1)) { +#ifndef CS5530_2_2_KERNEL + if (!hwif->dmaproc(ide_dma_bad_drive, mate)) +#endif /* CS5530_2_2_KERNEL */ + { + if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7)) + udma_ok = 1; + else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7)) + udma_ok = 0; + else + udma_ok = 1; + } + } + } + + /* + * Now see what the current drive is capable of, + * selecting UDMA only if the mate said it was ok. + */ + if (id && (id->capability & 1) && hwif->autodma) { +#ifndef CS5530_2_2_KERNEL + if (!hwif->dmaproc(ide_dma_bad_drive, drive)) +#endif /* CS5530_2_2_KERNEL */ + { + if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) { + if (id->dma_ultra & 4) + mode = XFER_UDMA_2; + else if (id->dma_ultra & 2) + mode = XFER_UDMA_1; + else if (id->dma_ultra & 1) + mode = XFER_UDMA_0; + } + if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) { + if (id->dma_mword & 4) + mode = XFER_MW_DMA_2; + else if (id->dma_mword & 2) + mode = XFER_MW_DMA_1; + else if (id->dma_mword & 1) + mode = XFER_MW_DMA_0; + } + } + } + + /* + * Tell the drive to switch to the new mode; abort on failure. + */ + if (!mode || cs5530_set_xfer_mode(drive, mode)) + return 1; /* failure */ + + /* + * Now tune the chipset to match the drive: + */ + switch (mode) { + case XFER_UDMA_0: timings = 0x00921250; break; + case XFER_UDMA_1: timings = 0x00911140; break; + case XFER_UDMA_2: timings = 0x00911030; break; + case XFER_MW_DMA_0: timings = 0x00077771; break; + case XFER_MW_DMA_1: timings = 0x00012121; break; + case XFER_MW_DMA_2: timings = 0x00002020; break; + default: + printk("%s: cs5530_config_dma: huh? mode=%02x\n", drive->name, mode); + return 1; /* failure */ + } + basereg = CS5530_BASEREG(hwif); + reg = inl(basereg+4); /* get drive0 config register */ + timings |= reg & 0x80000000; /* preserve PIO format bit */ + if (unit == 0) { /* are we configuring drive0? */ + outl(timings, basereg+4); /* write drive0 config register */ + } else { + if (timings & 0x00100000) + reg |= 0x00100000; /* enable UDMA timings for both drives */ + else + reg &= ~0x00100000; /* disable UDMA timings for both drives */ + outl(reg, basereg+4); /* write drive0 config register */ + outl(timings, basereg+12); /* write drive1 config register */ + } + outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2); /* set DMA_capable bit */ + + /* + * Finally, turn DMA on in software, and exit. + */ + return hwif->dmaproc(ide_dma_on, drive); /* success */ +} + +/* + * This is a CS5530-specific wrapper for the standard ide_dmaproc(). + * We need it for our custom "ide_dma_check" function. + * All other requests are forwarded to the standard ide_dmaproc(). + */ +int cs5530_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return cs5530_config_dma(drive); + default: + return ide_dmaproc(func, drive); + } +} + +/* + * Initialize the cs5530 bridge for reliable IDE DMA operation. + */ +unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name) +{ + struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; + unsigned short pcicmd = 0; + unsigned long flags; + + pci_for_each_dev (dev) { + if (dev->vendor == PCI_VENDOR_ID_CYRIX) { + switch (dev->device) { + case PCI_DEVICE_ID_CYRIX_PCI_MASTER: + master_0 = dev; + break; + case PCI_DEVICE_ID_CYRIX_5530_LEGACY: + cs5530_0 = dev; + break; + } + } + } + if (!master_0) { + printk("%s: unable to locate PCI MASTER function\n", name); + return 0; + } + if (!cs5530_0) { + printk("%s: unable to locate CS5530 LEGACY function\n", name); + return 0; + } + + save_flags(flags); + cli(); /* all CPUs (there should only be one CPU with this chipset) */ + + /* + * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530: + * --> OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530 + */ + pci_read_config_word (cs5530_0, PCI_COMMAND, &pcicmd); + pci_write_config_word(cs5530_0, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); + + /* + * Set PCI CacheLineSize to 16-bytes: + * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530 + */ + pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04); + + /* + * Disable trapping of UDMA register accesses (Win98 hack): + * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530 + */ + pci_write_config_word(cs5530_0, 0xd0, 0x5006); + + /* + * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus: + * The other settings are what is necessary to get the register + * into a sane state for IDE DMA operation. + */ + pci_write_config_byte(master_0, 0x40, 0x1e); + + /* + * Set max PCI burst size (16-bytes seems to work best): + * 16bytes: set bit-1 at 0x41 (reg value of 0x16) + * all others: clear bit-1 at 0x41, and do: + * 128bytes: OR 0x00 at 0x41 + * 256bytes: OR 0x04 at 0x41 + * 512bytes: OR 0x08 at 0x41 + * 1024bytes: OR 0x0c at 0x41 + */ + pci_write_config_byte(master_0, 0x41, 0x14); + + /* + * These settings are necessary to get the chip + * into a sane state for IDE DMA operation. + */ + pci_write_config_byte(master_0, 0x42, 0x00); + pci_write_config_byte(master_0, 0x43, 0xc1); + + restore_flags(flags); + return 0; +} + +/* + * This gets invoked by the IDE driver once for each channel, + * and performs channel-specific pre-initialization before drive probing. + */ +void __init ide_init_cs5530 (ide_hwif_t *hwif) +{ +#ifndef CS5530_2_2_KERNEL + static int already_done = 0; + if (!already_done) { + ++already_done; + (void) pci_init_cs5530 (hwif->pci_dev, "CS5530"); + } +#endif /* CS5530_2_2_KERNEL */ + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; + if (!hwif->dma_base) { + hwif->autodma = 0; + } else { + unsigned int basereg, d0_timings; + + hwif->dmaproc = &cs5530_dmaproc; + hwif->tuneproc = &cs5530_tuneproc; + basereg = CS5530_BASEREG(hwif); + d0_timings = inl(basereg+0); + if (CS5530_BAD_PIO(d0_timings)) { /* PIO timings not initialized? */ + outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0); + if (!hwif->drives[0].autotune) + hwif->drives[0].autotune = 1; /* needs autotuning later */ + } + if (CS5530_BAD_PIO(inl(basereg+8))) { /* PIO timings not initialized? */ + outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8); + if (!hwif->drives[1].autotune) + hwif->drives[1].autotune = 1; /* needs autotuning later */ + } + } +} diff --git a/drivers/block/ida_cmd.h b/drivers/block/ida_cmd.h index 056863b600ad..6d57a85ee5b6 100644 --- a/drivers/block/ida_cmd.h +++ b/drivers/block/ida_cmd.h @@ -191,7 +191,7 @@ typedef struct { __u8 expn_fail; __u8 unit_flags; __u16 big_fail_map[8]; - __u16 big_remap_map[8]; + __u16 big_remap_map[128]; __u16 big_repl_map[8]; __u16 big_act_spare_map[8]; __u8 big_spar_repl_map[128]; @@ -336,7 +336,7 @@ typedef struct { __u32 sense_info; __u8 sense_code; __u8 sense_qual; - __u8 residual; + __u32 residual; __u8 reserved[4]; __u8 cdb[12]; } scsi_param_t; diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index 0e8571e9386d..abd3bd223091 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -571,7 +571,7 @@ static void idedisk_pre_reset (ide_drive_t *drive) drive->special.b.recalibrate = 1; if (OK_TO_RESET_CONTROLLER) drive->mult_count = 0; - if (!drive->keep_settings) + if (!drive->keep_settings && !drive->using_dma) drive->mult_req = 0; if (drive->mult_req != drive->mult_count) drive->special.b.set_multmode = 1; diff --git a/drivers/block/ide-pci.c b/drivers/block/ide-pci.c index df9c715e1d17..71d4801188a0 100644 --- a/drivers/block/ide-pci.c +++ b/drivers/block/ide-pci.c @@ -47,6 +47,7 @@ #define DEVID_UM8886A ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A}) #define DEVID_UM8886BF ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF}) #define DEVID_HPT343 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343}) +#define DEVID_CS5530 ((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE}) #define IDE_IGNORE ((void *)-1) @@ -103,6 +104,13 @@ extern void ide_init_via82c586(ide_hwif_t *); #define INIT_VIA82C586 NULL #endif +#ifdef CONFIG_BLK_DEV_CS5530 +extern void ide_init_cs5530(ide_hwif_t *); +#define INIT_CS5530 &ide_init_cs5530 +#else +#define INIT_CS5530 NULL +#endif + typedef struct ide_pci_enablebit_s { byte reg; /* byte pci reg holding the enable-bit */ byte mask; /* mask to isolate the enable-bit */ @@ -141,6 +149,7 @@ static ide_pci_device_t ide_pci_chipsets[] __initdata = { {DEVID_UM8886A, "UM8886A", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_UM8886BF,"UM8886BF", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_HPT343, "HPT343", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 }, + {DEVID_CS5530, "CS5530", INIT_CS5530, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 112 }, {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }}; /* @@ -393,6 +402,7 @@ check_if_enabled: if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) || IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT343) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) || ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) { unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name); if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) { diff --git a/drivers/block/ide.c b/drivers/block/ide.c index cf8f58ee3910..bd06258111bc 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -598,14 +598,16 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) static void pre_reset (ide_drive_t *drive) { + if (drive->driver != NULL) + DRIVER(drive)->pre_reset(drive); if (!drive->keep_settings) { - drive->unmask = 0; - drive->io_32bit = 0; - if (drive->using_dma) + if (drive->using_dma) { (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } else { + drive->unmask = 0; + drive->io_32bit = 0; + } } - if (drive->driver != NULL) - DRIVER(drive)->pre_reset(drive); } /* @@ -830,8 +832,8 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat) if ((stat & DRQ_STAT) && rq->cmd != WRITE) try_to_flush_leftover_data(drive); } - if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) - rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */ + if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) /* possible timing problem? */ + OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ if (rq->errors >= ERROR_MAX) { if (drive->driver != NULL) diff --git a/drivers/block/ide_modes.h b/drivers/block/ide_modes.h index 49a4997e38bc..f4b03be22c74 100644 --- a/drivers/block/ide_modes.h +++ b/drivers/block/ide_modes.h @@ -15,7 +15,7 @@ * breaking the fragile cmd640.c support. */ -#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) || defined(CONFIG_BLK_DEV_IDE_PMAC) +#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) || defined(CONFIG_BLK_DEV_IDE_PMAC) || defined(CONFIG_BLK_DEV_CS5530) /* * Standard (generic) timings for PIO modes, from ATA2 specification. diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 0fb743d5bb4a..2bd46f627bdc 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -847,8 +847,7 @@ __initfunc(int blk_dev_init(void)) #ifdef CONFIG_BLK_DEV_FD floppy_init(); #else -#if !defined (__mc68000__) && !defined(CONFIG_PMAC) && !defined(__sparc__)\ - && !defined(CONFIG_APUS) +#if !defined (__mc68000__) && !defined(CONFIG_PPC) && !defined(__sparc__) outb_p(0xc, 0x3f2); #endif #endif diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c index 890584dcdd68..28e7b953a87e 100644 --- a/drivers/block/raid1.c +++ b/drivers/block/raid1.c @@ -211,7 +211,11 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) while (!( /* FIXME: now we are rather fault tolerant than nice */ r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL) ) ) + { printk ("raid1_make_request(#1): out of memory\n"); + current->policy |= SCHED_YIELD; + schedule(); + } memset (r1_bh, 0, sizeof (struct raid1_bh)); /* @@ -299,7 +303,11 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) while (!( /* FIXME: now we are rather fault tolerant than nice */ mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL) ) ) + { printk ("raid1_make_request(#2): out of memory\n"); + current->policy |= SCHED_YIELD; + schedule(); + } memset (mirror_bh[i], 0, sizeof (struct buffer_head)); /* @@ -711,7 +719,11 @@ static int raid1_run (int minor, struct md_dev *mddev) while (!( /* FIXME: now we are rather fault tolerant than nice */ mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL) ) ) + { printk ("raid1_run(): out of memory\n"); + current->policy |= SCHED_YIELD; + schedule(); + } raid_conf = mddev->private; memset(raid_conf, 0, sizeof(*raid_conf)); diff --git a/drivers/char/Config.in b/drivers/char/Config.in index c28389334f71..6b9b87070bb9 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -154,6 +154,17 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'CPiA Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV + if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then + if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate 'CPiA Parallel Port Lowlevel Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT + if [ "$CONFIG_VIDEO_CPIA_PP" != "n" ]; then + bool 'CPiA Parallel Port DMA Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP_DMA + fi + fi + fi + fi dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV if [ "$CONFIG_PMAC" = "y" ]; then dep_tristate 'PlanB Video-In on PowerMac' CONFIG_VIDEO_PLANB $CONFIG_VIDEO_DEV diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f193eef5fdea..eaa5a6a8d4d8 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -475,6 +475,22 @@ else endif endif +ifeq ($(CONFIG_VIDEO_CPIA),y) +LX_OBJS += cpia.o +else + ifeq ($(CONFIG_VIDEO_CPIA),m) + MX_OBJS += cpia.o + endif +endif + +ifeq ($(CONFIG_VIDEO_CPIA_PP),y) +L_OBJS += cpia_pp.o +else + ifeq ($(CONFIG_VIDEO_CPIA_PP),m) + M_OBJS += cpia_pp.o + endif +endif + ifeq ($(CONFIG_RADIO_AZTECH),y) L_OBJS += radio-aztech.o else diff --git a/drivers/char/cpia.c b/drivers/char/cpia.c new file mode 100644 index 000000000000..c107590fe4d4 --- /dev/null +++ b/drivers/char/cpia.c @@ -0,0 +1,3375 @@ +/* + * cpia CPiA driver + * + * Supports CPiA based Video Camera's. + * + * (C) 1999 Peter Pregler, + * Scott J. Bertin, + * Johannes Erdfelt + * + * 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 + +#ifdef CONFIG_KMOD +#include +#endif + +#undef _CPIA_DEBUG_ /* define for verbose debug output */ +#include + +#ifdef CONFIG_VIDEO_CPIA_PP +extern int cpia_pp_init(void); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB +extern int cpia_usb_init(void); +#endif + +#ifdef MODULE +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler "); +MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_SUPPORTED_DEVICE("video"); +#endif + +#define ABOUT "V4L-Driver for Vision CPiA based cameras" + +#ifndef VID_HARDWARE_CPIA +#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */ +#endif + +#define CPIA_MODULE_CPIA (0<<5) +#define CPIA_MODULE_SYSTEM (1<<5) +#define CPIA_MODULE_VP_CTRL (5<<5) +#define CPIA_MODULE_CAPTURE (6<<5) +#define CPIA_MODULE_DEBUG (7<<5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceMode; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 flickerMode; + u8 coarseJump; + u8 allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum { + FRAME_READY, /* Ready to grab into */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ +}; + +#define FRAME_NUM 2 /* double buffering for now */ +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define COMMAND_NONE 0x0000 +#define COMMAND_SETCOMPRESSION 0x0001 +#define COMMAND_SETCOMPRESSIONTARGET 0x0002 +#define COMMAND_SETCOLOURPARAMS 0x0004 +#define COMMAND_SETFORMAT 0x0008 +#define COMMAND_PAUSE 0x0010 +#define COMMAND_RESUME 0x0020 +#define COMMAND_SETYUVTHRESH 0x0040 +#define COMMAND_SETECPTIMING 0x0080 +#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 +#define COMMAND_SETEXPOSURE 0x0200 +#define COMMAND_SETCOLOURBALANCE 0x0400 +#define COMMAND_SETSENSORFPS 0x0800 +#define COMMAND_SETAPCOR 0x1000 +#define COMMAND_SETFLICKERCTRL 0x2000 +#define COMMAND_SETVLOFFSET 0x4000 + +struct cam_data { + int index; /* which camera is this */ + struct semaphore busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct semaphore param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + volatile u32 cmd_queue; /* queued commands */ +}; + +static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL }; + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +/* forward declaration of local function */ +static void reset_camera_struct(struct cam_data *cam); + +/********************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.13 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + **********************************************************************/ + +static inline unsigned long uvirt_to_phys(unsigned long adr) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + + pgd = pgd_offset(current->mm, adr); + if (pgd_none(*pgd)) return 0; + pmd = pmd_offset(pgd, adr); + if (pmd_none(*pmd)) return 0; + ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); + pte = *ptep; + if(pte_present(pte)) + return + virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); + return 0; +} + +static inline unsigned long kvirt_to_phys(unsigned long adr) +{ + return uvirt_to_phys(VMALLOC_VMADDR(adr)); +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem=vmalloc(size); + if (mem) { + /* Clear the ram out, no junk to the user */ + memset(mem, 0, size); + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_phys(adr); + mem_map_reserve(MAP_NR(phys_to_virt(page))); + adr+=PAGE_SIZE; + if (size > PAGE_SIZE) size-=PAGE_SIZE; + else size=0; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + if (mem) { + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_phys(adr); + mem_map_unreserve(MAP_NR(phys_to_virt(page))); + adr+=PAGE_SIZE; + if (size > PAGE_SIZE) size-=PAGE_SIZE; + else size=0; + } + vfree(mem); + } +} + +/********************************************************************** + * + * /proc interface + * + **********************************************************************/ +static struct proc_dir_entry *cpia_proc_root=NULL; + +static int cpia_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len, tmp; + struct cam_data *cam = data; + char tmpstr[20]; + + /* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?) + * or we need to get more sophisticated. */ + + out += sprintf(out, "read-only\n-----------------------\n"); + out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); + out += sprintf(out, "VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + out += sprintf(out, "system_state: %#04x\n", + cam->params.status.systemState); + out += sprintf(out, "grab_state: %#04x\n", + cam->params.status.grabState); + out += sprintf(out, "stream_state: %#04x\n", + cam->params.status.streamState); + out += sprintf(out, "fatal_error: %#04x\n", + cam->params.status.fatalError); + out += sprintf(out, "cmd_error: %#04x\n", + cam->params.status.cmdError); + out += sprintf(out, "debug_flags: %#04x\n", + cam->params.status.debugFlags); + out += sprintf(out, "vp_status: %#04x\n", + cam->params.status.vpStatus); + out += sprintf(out, "error_code: %#04x\n", + cam->params.status.errorCode); + out += sprintf(out, "video_size: %s\n", + cam->params.format.videoSize == VIDEOSIZE_CIF ? + "CIF " : "QCIF"); + out += sprintf(out, "sub_sample: %s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422"); + out += sprintf(out, "yuv_order: %s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY"); + out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n", + cam->params.roi.colStart*8, + cam->params.roi.rowStart*4, + cam->params.roi.colEnd*8, + cam->params.roi.rowEnd*4); + out += sprintf(out, "actual_fps: %d\n", cam->fps); + out += sprintf(out, "transfer_rate: %dkB/s\n", + cam->transfer_rate); + + out += sprintf(out, "\nread-write\n"); + out += sprintf(out, "----------------------- current min" + " max default comment\n"); + out += sprintf(out, "brightness: %8d %8d %8d %8d\n", + cam->params.colourParams.brightness, 0, 100, 50); + if(cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) { + /* 1-02 firmware limits contrast to 80 */ + tmp = 80; + } else { + tmp = 96; + } + out += sprintf(out, "contrast: %8d %8d %8d %8d" + " steps of 8\n", + cam->params.colourParams.contrast, 0, tmp, 48); + out += sprintf(out, "saturation: %8d %8d %8d %8d\n", + cam->params.colourParams.saturation, 0, 100, 50); + tmp = (25000+5000*cam->params.sensorFps.baserate)/ + (1<params.sensorFps.divisor); + out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n", + tmp/1000, tmp%1000, 3, 30, 15); + out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n", + 2*cam->params.streamStartLine, 0, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n", + cam->params.ecpTiming ? "slow" : "normal", "slow", + "normal", "normal"); + + switch(cam->params.colourBalance.balanceMode) { + case 1: + // FIXME case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + out += sprintf(out, "color_balance_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + out += sprintf(out, "red_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.redGain, 0, 212, 32); + out += sprintf(out, "green_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.greenGain, 0, 212, 6); + out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.blueGain, 0, 212, 92); + + if(cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) { + /* 1-02 firmware limits gain to 2 */ + sprintf(tmpstr, "%8d %8d", 1, 2); + } else { + sprintf(tmpstr, "1,2,4,8"); + } + if(cam->params.exposure.gainMode == 0) { + out += sprintf(out, "max_gain: unknown %18s" + " %8d\n", tmpstr, 2); + } else { + out += sprintf(out, "max_gain: %8d %18s %8d\n", + 1<<(cam->params.exposure.gainMode-1), tmpstr, 2); + } + switch(cam->params.exposure.expMode) { + case 1: + case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + out += sprintf(out, "exposure_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n", + (2-cam->params.exposure.centreWeight) ? "on" : "off", + "off", "on", "on"); + out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", + 1<params.exposure.gain, 1, 1); + if(cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) { + /* 1-02 firmware limits fineExp to 127 */ + tmp = 255; + } else { + tmp = 511; + } + out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n", + cam->params.exposure.fineExp*2, 0, tmp, 0); + if(cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) { + /* 1-02 firmware limits coarseExpHi to 0 */ + tmp = 255; + } else { + tmp = 65535; + } + out += sprintf(out, "coarse_exp: %8d %8d %8d" + " %8d\n", cam->params.exposure.coarseExpLo+ + 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); + out += sprintf(out, "red_comp: %8d %8d %8d %8d\n", + cam->params.exposure.redComp, 220, 255, 220); + out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green1Comp, 214, 255, 214); + out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green2Comp, 214, 255, 214); + out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n", + cam->params.exposure.blueComp, 230, 255, 230); + + out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain1, 0, 0xff, 0x1c); + out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain2, 0, 0xff, 0x1a); + out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain4, 0, 0xff, 0x2d); + out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain8, 0, 0xff, 0x2a); + out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain1, 0, 255, 24); + out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain2, 0, 255, 28); + out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain4, 0, 255, 30); + out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain8, 0, 255, 30); + out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n", + cam->params.flickerControl.flickerMode ? "on" : "off", + "off", "on", "off"); + out += sprintf(out, "mains_frequency: %8d %8d %8d %8d" + " only 50/60\n", + cam->mainsFreq ? 60 : 50, 50, 60, 50); + out += sprintf(out, "allowable_overexposure: %8d %8d %8d %8d\n", + cam->params.flickerControl.allowableOverExposure, 0, + 255, 0); + out += sprintf(out, "compression_mode: "); + switch(cam->params.compression.mode) { + case CPIA_COMPRESSION_NONE: + out += sprintf(out, "%8s", "none"); + break; + case CPIA_COMPRESSION_AUTO: + out += sprintf(out, "%8s", "auto"); + break; + case CPIA_COMPRESSION_MANUAL: + out += sprintf(out, "%8s", "manual"); + break; + default: + out += sprintf(out, "%8s", "unknown"); + break; + } + out += sprintf(out, " none,auto,manual auto\n"); + out += sprintf(out, "decimation: %8s %8s %8s %8s\n", + cam->params.compression.decimation == + DECIMATION_ENAB ? "on":"off", "off", "off", + "off"); + out += sprintf(out, "compression_target: %9s %9s %9s %9s\n", + cam->params.compressionTarget.frTargeting == + CPIA_COMPRESSION_TARGET_FRAMERATE ? + "framerate":"quality", + "framerate", "quality", "quality"); + out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetFR, 0, 30, 7); + out += sprintf(out, "target_quality: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetQ, 0, 255, 10); + out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.yThreshold, 0, 31, 15); + out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.uvThreshold, 0, 31, 15); + out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.hysteresis, 0, 255, 3); + out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n", + cam->params.compressionParams.threshMax, 0, 255, 11); + out += sprintf(out, "small_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.smallStep, 0, 255, 1); + out += sprintf(out, "large_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.largeStep, 0, 255, 3); + out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationHysteresis, + 0, 255, 2); + out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.frDiffStepThresh, + 0, 255, 5); + out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.qDiffStepThresh, + 0, 255, 3); + out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationThreshMod, + 0, 255, 2); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else { + len = count; + } + *start = page + off; + return len; +} + +static int cpia_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct cam_data *cam = data; + struct cam_params new_params; + int retval, len, colon_found; + int size = count; + char *p; + unsigned long val; + u32 command_flags = 0; + u8 new_mains; + + if(down_interruptible(&cam->param_lock)) { + return -ERESTARTSYS; + } + + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + + memcpy(&new_params, &cam->params, sizeof(struct cam_params)); + new_mains = cam->mainsFreq; + +#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0) +#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) +#define VALUE \ + simple_strtoul(buffer, &p, 0); \ + if(p == buffer) { \ + retval = -EINVAL; \ + } else { \ + count -= p-buffer; \ + buffer = p; \ + } +#define FIND_VALUE \ + buffer += len;\ + count -= len; \ + colon_found=0; \ + while(count && (*buffer == ' ' || *buffer == '\t' || \ + (!colon_found && *buffer == ':'))) { \ + if(*buffer == ':') colon_found = 1; \ + --count; \ + ++buffer; \ + } \ + if(!count || !colon_found) { \ + retval = -EINVAL; \ + } +#define FIND_END \ + if(retval == 0) { \ + while(count && isspace(*buffer) && *buffer != '\n'){ \ + --count; \ + ++buffer; \ + } \ + if(count) { \ + if(*buffer != '\n' && *buffer != ';') { \ + retval = -EINVAL; \ + } else { \ + --count; \ + ++buffer; \ + } \ + } \ + } + retval = 0; + while(count && !retval) { + if(MATCH("brightness")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 100) + new_params.colourParams.brightness=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + FIND_END + } else if(MATCH("contrast")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 100) { + /* contrast is in steps of 8, so round*/ + val = ((val + 3) / 8) * 8; + /* 1-02 firmware limits contrast to 80*/ + if(FIRMWARE_VERSION(1,2) && val > 80) + val = 80; + new_params.colourParams.contrast = val; + } else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + FIND_END + } else if(MATCH("saturation")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 100) + new_params.colourParams.saturation=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + FIND_END + } else if(MATCH("sensor_fps")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + /* find values so that sensorFPS is minimized, + * but >= val */ + if(val > 30) { + retval = -EINVAL; + } else if(val > 25) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 1; + } else if(val > 15) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 0; + } else if(val > 12) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 1; + } else if(val > 7) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 0; + } else if(val > 6) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 1; + } else if(val > 3) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 0; + } else { + new_params.sensorFps.divisor = 3; + /* Either base rate would work here */ + new_params.sensorFps.baserate = 1; + } + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if(new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } + command_flags |= COMMAND_SETSENSORFPS; + FIND_END + } else if(MATCH("stream_start_line")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + int max_line = 288; + if(new_params.format.videoSize==VIDEOSIZE_QCIF) + max_line = 144; + if(val <= max_line) + new_params.streamStartLine = val/2; + else retval = -EINVAL; + } + FIND_END + } else if(MATCH("ecp_timing")) { + FIND_VALUE + if(!retval && MATCH("normal")) { + buffer += len; + count -= len; + new_params.ecpTiming = 0; + } else if(!retval && MATCH("slow")) { + buffer += len; + count -= len; + new_params.ecpTiming = 1; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETECPTIMING; + FIND_END + } else if(MATCH("color_balance_mode")) { + FIND_VALUE + if(!retval && MATCH("manual")) { + buffer += len; + count -= len; + new_params.colourBalance.balanceMode=1; + } else if(!retval && MATCH("auto")) { + buffer += len; + count -= len; + new_params.colourBalance.balanceMode=2; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + FIND_END + } else if(MATCH("red_gain")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 212) + new_params.colourBalance.redGain = val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + FIND_END + } else if(MATCH("green_gain")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 212) + new_params.colourBalance.greenGain=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + FIND_END + } else if(MATCH("blue_gain")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 212) + new_params.colourBalance.blueGain = val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + FIND_END + } else if(MATCH("max_gain")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + /* 1-02 firmware limits gain to 2 */ + if(FIRMWARE_VERSION(1,2) && val > 2) + val = 2; + switch(val) { + case 1: + new_params.exposure.gainMode = 1; + break; + case 2: + new_params.exposure.gainMode = 2; + break; + case 4: + new_params.exposure.gainMode = 3; + break; + case 8: + new_params.exposure.gainMode = 4; + break; + default: + retval = -EINVAL; + break; + } + } + command_flags |= COMMAND_SETEXPOSURE; + FIND_END + } else if(MATCH("exposure_mode")) { + FIND_VALUE + if(!retval && MATCH("auto")) { + buffer += len; + count -= len; + new_params.exposure.expMode = 2; + } else if(!retval && MATCH("manual")) { + buffer += len; + count -= len; + if(new_params.exposure.expMode == 2) + new_params.exposure.expMode = 3; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETEXPOSURE; + FIND_END + } else if(MATCH("centre_weight")) { + FIND_VALUE + if(!retval && MATCH("on")) { + buffer += len; + count -= len; + new_params.exposure.centreWeight = 1; + } else if(!retval && MATCH("off")) { + buffer += len; + count -= len; + new_params.exposure.centreWeight = 2; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETEXPOSURE; + FIND_END + } else if(MATCH("gain")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + switch(val) { + case 1: + new_params.exposure.gain = 0; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + case 2: + new_params.exposure.gain = 1; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + case 4: + new_params.exposure.gain = 2; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + case 8: + new_params.exposure.gain = 3; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + default: + retval = -EINVAL; + break; + } + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.exposure.gain > + new_params.exposure.gainMode-1) + retval = -EINVAL; + } + FIND_END + } else if(MATCH("fine_exp")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val < 256) { + /* 1-02 firmware limits fineExp to 127*/ + if(FIRMWARE_VERSION(1,2) && val > 127) + val = 127; + new_params.exposure.fineExp = val; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("coarse_exp")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val < 65536) { + /* 1-02 firmware limits + * coarseExp to 255 */ + if(FIRMWARE_VERSION(1,2) && val > 255) + val = 255; + new_params.exposure.coarseExpLo = + val & 0xff; + new_params.exposure.coarseExpHi = + val >> 8; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("red_comp")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val >= 220 && val <= 255) { + new_params.exposure.redComp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("green1_comp")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val >= 214 && val <= 255) { + new_params.exposure.green1Comp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("green2_comp")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val >= 214 && val <= 255) { + new_params.exposure.green2Comp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("blue_comp")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val >= 230 && val <= 255) { + new_params.exposure.blueComp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("apcor_gain1")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + command_flags |= COMMAND_SETAPCOR; + if(val <= 0xff) new_params.apcor.gain1 = val; + else retval = -EINVAL; + } + FIND_END + } else if(MATCH("apcor_gain2")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + command_flags |= COMMAND_SETAPCOR; + if(val <= 0xff) new_params.apcor.gain2 = val; + else retval = -EINVAL; + } + FIND_END + } else if(MATCH("apcor_gain4")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + command_flags |= COMMAND_SETAPCOR; + if(val <= 0xff) new_params.apcor.gain4 = val; + else retval = -EINVAL; + } + FIND_END + } else if(MATCH("apcor_gain8")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + command_flags |= COMMAND_SETAPCOR; + if(val <= 0xff) new_params.apcor.gain8 = val; + else retval = -EINVAL; + } + FIND_END + } else if(MATCH("vl_offset_gain1")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 0xff) new_params.vlOffset.gain1 = val; + else retval = -EINVAL; + } + FIND_END + command_flags |= COMMAND_SETVLOFFSET; + } else if(MATCH("vl_offset_gain2")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 0xff) new_params.vlOffset.gain2 = val; + else retval = -EINVAL; + } + FIND_END + command_flags |= COMMAND_SETVLOFFSET; + } else if(MATCH("vl_offset_gain4")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 0xff) new_params.vlOffset.gain4 = val; + else retval = -EINVAL; + } + FIND_END + command_flags |= COMMAND_SETVLOFFSET; + } else if(MATCH("vl_offset_gain8")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 0xff) new_params.vlOffset.gain8 = val; + else retval = -EINVAL; + } + FIND_END + command_flags |= COMMAND_SETVLOFFSET; + } else if(MATCH("flicker_control")) { + FIND_VALUE + if(!retval && MATCH("on")) { + buffer += len; + count -= len; + new_params.flickerControl.flickerMode = 1; + new_params.exposure.expMode = 2; + command_flags |= COMMAND_SETEXPOSURE; + } else if(!retval && MATCH("off")) { + buffer += len; + count -= len; + new_params.flickerControl.flickerMode = 0; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETFLICKERCTRL; + FIND_END + } else if(MATCH("mains_frequency")) { + FIND_VALUE + if(!retval && MATCH("50")) { + buffer += len; + count -= len; + new_mains = 0; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if(new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else if(!retval && MATCH("60")) { + buffer += len; + count -= len; + new_mains = 1; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if(new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else { + retval = -EINVAL; + } + FIND_END + } else if(MATCH("allowable_overexposure")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val <= 0xff) { + new_params.flickerControl. + allowableOverExposure = val; + command_flags |= COMMAND_SETFLICKERCTRL; + } else retval = -EINVAL; + } + FIND_END + } else if(MATCH("compression_mode")) { + FIND_VALUE + if(!retval && MATCH("none")) { + buffer += len; + count -= len; + new_params.compression.mode = + CPIA_COMPRESSION_NONE; + } else if(!retval && MATCH("auto")) { + buffer += len; + count -= len; + new_params.compression.mode = + CPIA_COMPRESSION_AUTO; + } else if(!retval && MATCH("manual")) { + buffer += len; + count -= len; + new_params.compression.mode = + CPIA_COMPRESSION_MANUAL; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSION; + FIND_END + } else if(MATCH("decimation")) { + FIND_VALUE + if(!retval && MATCH("off")) { + buffer += len; + count -= len; + new_params.compression.decimation = 0; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSION; + FIND_END + } else if(MATCH("compression_target")) { + FIND_VALUE + if(!retval && MATCH("quality")) { + buffer += len; + count -= len; + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + } else if(!retval && MATCH("framerate")) { + buffer += len; + count -= len; + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_FRAMERATE; + } else { + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + FIND_END + } else if(MATCH("target_framerate")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) + new_params.compressionTarget.targetFR = val; + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + FIND_END + } else if(MATCH("target_quality")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) new_params.compressionTarget.targetQ = val; + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + FIND_END + } else if(MATCH("y_threshold")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val < 32) + new_params.yuvThreshold.yThreshold=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + FIND_END + } else if(MATCH("uv_threshold")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(val < 32) + new_params.yuvThreshold.uvThreshold=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + FIND_END + } else if(MATCH("hysteresis")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams.hysteresis=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("threshold_max")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams.threshMax=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("small_step")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams.smallStep=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("large_step")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams.largeStep=val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("decimation_hysteresis")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams. + decimationHysteresis = val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("fr_diff_step_thresh")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams. + frDiffStepThresh = val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("q_diff_step_thresh")) { + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams. + qDiffStepThresh = val; + else retval = -EINVAL; + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else if(MATCH("decimation_thresh_mod")) { + } + FIND_VALUE + if(!retval) { val = VALUE } + if(!retval) { + if(retval <= 0xff) + new_params.compressionParams. + decimationThreshMod = val; + else retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + FIND_END + } else { + retval = -EINVAL; + } + } +#undef MATCH +#undef FIRMWARE_VERSION +#undef VALUE +#undef FIND_VALUE +#undef FIND_END + if(retval == 0) { + if(command_flags & COMMAND_SETCOLOURPARAMS) { + /* Adjust cam->vp to reflect these changes */ + cam->vp.brightness = + new_params.colourParams.brightness*65535/100; + cam->vp.contrast = + new_params.colourParams.contrast*65535/100; + cam->vp.colour = + new_params.colourParams.saturation*65535/100; + } + + memcpy(&cam->params, &new_params, sizeof(struct cam_params)); + cam->mainsFreq = new_mains; + cam->cmd_queue |= command_flags; + retval = size; + } else { + DBG("error: %d\n", retval); + } + + up(&cam->param_lock); + + return retval; +} + +static void create_proc_cpia_cam(struct cam_data *cam) +{ + char name[7]; + struct proc_dir_entry *ent; + + if(cpia_proc_root == NULL || cam == NULL) { + return; + } + sprintf(name, "video%d", cam->vdev.minor); + + ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); + if (!ent) return; + ent->data = cam; + ent->read_proc = cpia_read_proc; + ent->write_proc = cpia_write_proc; + ent->size = 3623; + cam->proc_entry = ent; +} + +static void destroy_proc_cpia_cam(struct cam_data *cam) +{ + char name[7]; + + if(cam->proc_entry == NULL) return; + + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry(name, cpia_proc_root); +} + +static void proc_cpia_create(void) +{ + cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0); +} + +#ifdef MODULE +static void proc_cpia_destroy(void) +{ + remove_proc_entry("cpia", 0); +} +#endif + +/* ----------------------- debug functions ---------------------- */ + +#define printstatus(cam) \ + DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ + cam->params.status.systemState, cam->params.status.grabState, \ + cam->params.status.streamState, cam->params.status.fatalError, \ + cam->params.status.cmdError, cam->params.status.debugFlags, \ + cam->params.status.vpStatus, cam->params.status.errorCode); + +/* ----------------------- v4l helpers -------------------------- */ + +/* supported frame palettes and depths */ +static inline int valid_mode(u16 palette, u16 depth) +{ + return (palette == VIDEO_PALETTE_GREY && depth == 8) || + (palette == VIDEO_PALETTE_RGB555 && depth == 16) || + (palette == VIDEO_PALETTE_RGB565 && depth == 16) || + (palette == VIDEO_PALETTE_RGB24 && depth == 24) || + (palette == VIDEO_PALETTE_RGB32 && depth == 32) || + (palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16) || + (palette == VIDEO_PALETTE_UYVY && depth == 16); +} + +static int match_videosize( int width, int height ) +{ + /* return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. */ + if(width>=352 && height>=288) { + return VIDEOSIZE_352_288; /* CIF */ + } + if(width>=320 && height>=240) { + return VIDEOSIZE_320_240; /* SIF */ + } + if(width>=288 && height>=216) { + return VIDEOSIZE_288_216; + } + if(width>=256 && height>=192) { + return VIDEOSIZE_256_192; + } + if(width>=224 && height>=168) { + return VIDEOSIZE_224_168; + } + if(width>=192 && height>=144) { + return VIDEOSIZE_192_144; + } + if(width>=176 && height>=144) { + return VIDEOSIZE_176_144; /* QCIF */ + } + if(width>=160 && height>=120) { + return VIDEOSIZE_160_120; /* QSIF */ + } + if(width>=128 && height>=96) { + return VIDEOSIZE_128_96; + } + if(width>=64 && height>=48) { + return VIDEOSIZE_64_48; + } + if(width>=48 && height>=48) { + return VIDEOSIZE_48_48; + } + + return -1; +} + +/* these are the capture sizes we support */ +static void set_vw_size(struct cam_data *cam) +{ + /* the col/row/start/end values are the result of simple math */ + /* study the SetROI-command in cpia developers guide p 2-22 */ + /* streamStartLine is set to the recommended value in the cpia */ + /* developers guide p 3-37 */ + switch(cam->video_size) { + case VIDEOSIZE_CIF: + cam->vw.width = 352; + cam->vw.height = 288; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=0; + cam->params.roi.colEnd=44; + cam->params.roi.rowStart=0; + cam->params.roi.rowEnd=72; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_SIF: + cam->vw.width = 320; + cam->vw.height = 240; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=2; + cam->params.roi.colEnd=42; + cam->params.roi.rowStart=6; + cam->params.roi.rowEnd=66; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_288_216: + cam->vw.width = 288; + cam->vw.height = 216; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=4; + cam->params.roi.colEnd=40; + cam->params.roi.rowStart=9; + cam->params.roi.rowEnd=63; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_256_192: + cam->vw.width = 256; + cam->vw.height = 192; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=6; + cam->params.roi.colEnd=38; + cam->params.roi.rowStart=12; + cam->params.roi.rowEnd=60; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_224_168: + cam->vw.width = 224; + cam->vw.height = 168; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=8; + cam->params.roi.colEnd=36; + cam->params.roi.rowStart=15; + cam->params.roi.rowEnd=57; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_192_144: + cam->vw.width = 192; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=10; + cam->params.roi.colEnd=34; + cam->params.roi.rowStart=18; + cam->params.roi.rowEnd=54; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_QCIF: + cam->vw.width = 176; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=0; + cam->params.roi.colEnd=22; + cam->params.roi.rowStart=0; + cam->params.roi.rowEnd=36; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_QSIF: + cam->vw.width = 160; + cam->vw.height = 120; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=1; + cam->params.roi.colEnd=21; + cam->params.roi.rowStart=3; + cam->params.roi.rowEnd=33; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_128_96: + cam->vw.width = 128; + cam->vw.height = 96; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=3; + cam->params.roi.colEnd=19; + cam->params.roi.rowStart=6; + cam->params.roi.rowEnd=30; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_64_48: + cam->vw.width = 64; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=7; + cam->params.roi.colEnd=15; + cam->params.roi.rowStart=12; + cam->params.roi.rowEnd=24; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_48_48: + cam->vw.width = 48; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=8; + cam->params.roi.colEnd=14; + cam->params.roi.rowStart=6; + cam->params.roi.rowEnd=30; + cam->params.streamStartLine = 60; + break; + default: + LOG("bad videosize value: %d\n", cam->video_size); + } + + return; +} + +static int allocate_frame_buf(struct cam_data *cam) { + int i; + + cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) { + return -ENOBUFS; + } + for( i=0; iframe[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE; + } + return 0; +} + +static int free_frame_buf(struct cam_data *cam) { + int i; + + rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); + cam->frame_buf=0; + for( i=0; iframe[i].data = NULL; + } + return 0; +} + + +static void inline free_frames(struct cpia_frame frame[FRAME_NUM]) +{ + int i; + for( i=0; iparam_lock); + datasize=8; + break; + default: + datasize=0; + break; + } + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if(retval) { + LOG("%x - failed\n", command); + } else { + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + cam->params.version.firmwareVersion = data[0]; + cam->params.version.firmwareRevision = data[1]; + cam->params.version.vcVersion = data[2]; + cam->params.version.vcRevision = data[3]; + break; + case CPIA_COMMAND_GetPnPID: + cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); + cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); + cam->params.pnpID.deviceRevision = + data[4]+(((u16)data[5])<<8); + break; + case CPIA_COMMAND_GetCameraStatus: + cam->params.status.systemState = data[0]; + cam->params.status.grabState = data[1]; + cam->params.status.streamState = data[2]; + cam->params.status.fatalError = data[3]; + cam->params.status.cmdError = data[4]; + cam->params.status.debugFlags = data[5]; + cam->params.status.vpStatus = data[6]; + cam->params.status.errorCode = data[7]; + break; + case CPIA_COMMAND_GetVPVersion: + cam->params.vpVersion.vpVersion = data[0]; + cam->params.vpVersion.vpRevision = data[1]; + cam->params.vpVersion.cameraHeadID = + data[2]+(((u16)data[3])<<8); + break; + case CPIA_COMMAND_GetColourParams: + cam->params.colourParams.brightness = data[0]; + cam->params.colourParams.contrast = data[1]; + cam->params.colourParams.saturation = data[2]; + up(&cam->param_lock); + break; + case CPIA_COMMAND_GetColourBalance: + cam->params.colourBalance.redGain = data[0]; + cam->params.colourBalance.greenGain = data[1]; + cam->params.colourBalance.blueGain = data[2]; + up(&cam->param_lock); + break; + case CPIA_COMMAND_GetExposure: + cam->params.exposure.gain = data[0]; + cam->params.exposure.fineExp = data[1]; + cam->params.exposure.coarseExpLo = data[2]; + cam->params.exposure.coarseExpHi = data[3]; + cam->params.exposure.redComp = data[4]; + cam->params.exposure.green1Comp = data[5]; + cam->params.exposure.green2Comp = data[6]; + cam->params.exposure.blueComp = data[7]; + up(&cam->param_lock); + break; + default: + break; + } + } + return retval; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct cam_data *cam, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + int retval; + u8 cmd[8], data[8]; + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + data[0] = e; + data[1] = f; + data[2] = g; + data[3] = h; + data[4] = i; + data[5] = j; + data[6] = k; + data[7] = l; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if(retval) { + LOG("%x - failed\n", command); + } + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int in_uyvy) +{ + int y, u, v, r, g, b, y1; + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + if(in_uyvy) { + u = *yuv++ - 128; + y = (*yuv++ - 16) * 76310; + v = *yuv++ - 128; + y1 = (*yuv - 16) * 76310; + } else { + y = (*yuv++ - 16) * 76310; + u = *yuv++ - 128; + y1 = (*yuv++ - 16) * 76310; + v = *yuv - 128; + } + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + default: + y = *yuv++; + u = *yuv++; + y1 = *yuv++; + v = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + return 6; + case VIDEO_PALETTE_RGB32: + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + return 0; + } +} + +static int skipcount(int count, int fmt) { + switch(fmt) { + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + return 2*count; + case VIDEO_PALETTE_RGB24: + return 3*count; + case VIDEO_PALETTE_RGB32: + return 4*count; + default: + return 0; + } +} + +static int parse_picture(struct cam_data *cam, int size) +{ + u8 *obuf, *ibuf, *end_obuf; + int ll, in_uyvy, compressed, origsize, out_fmt; + + /* make sure params don't change while we are decoding */ + down(&cam->param_lock); + + obuf = cam->decompressed_frame.data; + end_obuf = obuf+CPIA_MAX_FRAME_SIZE; + ibuf = cam->raw_image; + origsize = size; + out_fmt = cam->vp.palette; + + if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + LOG("header not found\n"); + up(&cam->param_lock); + return -1; + } + + if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + LOG("wrong video size\n"); + up(&cam->param_lock); + return -1; + } + + if(ibuf[17] != SUBSAMPLE_422) { + LOG("illegal subtype %d\n",ibuf[17]); + up(&cam->param_lock); + return -1; + } + + if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + LOG("illegal yuvorder %d\n",ibuf[18]); + up(&cam->param_lock); + return -1; + } + in_uyvy = ibuf[18] == YUVORDER_UYVY; + +#if 0 + /* FIXME: ROI mismatch occurs when switching capture sizes */ + if((ibuf[24] != cam->params.roi.colStart) || + (ibuf[25] != cam->params.roi.colEnd) || + (ibuf[26] != cam->params.roi.rowStart) || + (ibuf[27] != cam->params.roi.rowEnd)) { + LOG("ROI mismatch\n"); + up(&cam->param_lock); + return -1; + } +#endif + + if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + LOG("illegal compression %d\n",ibuf[28]); + up(&cam->param_lock); + return -1; + } + compressed = (ibuf[28] == COMPRESSED); + + if(ibuf[29] != NO_DECIMATION) { + LOG("decimation not supported\n"); + up(&cam->param_lock); + return -1; + } + + cam->params.yuvThreshold.yThreshold = ibuf[30]; + cam->params.yuvThreshold.uvThreshold = ibuf[31]; + cam->params.status.systemState = ibuf[32]; + cam->params.status.grabState = ibuf[33]; + cam->params.status.streamState = ibuf[34]; + cam->params.status.fatalError = ibuf[35]; + cam->params.status.cmdError = ibuf[36]; + cam->params.status.debugFlags = ibuf[37]; + cam->params.status.vpStatus = ibuf[38]; + cam->params.status.errorCode = ibuf[39]; + cam->fps = ibuf[41]; + + ibuf += 64; + size -= 64; + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; + while(size > 0) { + size -= (ll+2); + if(size < 0) { + LOG("Insufficient data in buffer\n"); + up(&cam->param_lock); + return -1; + } + while(ll > 1) { + if(!compressed || (compressed && !(*ibuf & 1))) { + obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy); + ibuf += 4; + ll -= 4; + } else { + /*skip compressed interval from previous frame*/ + int skipsize = skipcount(*ibuf >> 1, out_fmt); + obuf += skipsize; + if( obuf > end_obuf ) { + LOG("Insufficient data in buffer\n"); + up(&cam->param_lock); + return -1; + } + ++ibuf; + ll--; + } + } + if(ll == 1) { + if(*ibuf != EOL) { + LOG("EOL not found giving up after %d/%d" + " bytes\n", origsize-size, origsize); + up(&cam->param_lock); + return -1; + } + + ibuf++; /* skip over EOL */ + + if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + (ibuf[2] == EOI) && (ibuf[3] == EOI)) { + size -= 4; + break; + } + + if(size > 1) { + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; /* skip over line length */ + } + } else { + LOG("line length was not 1 but %d after %d/%d bytes\n", + ll, origsize-size, origsize); + up(&cam->param_lock); + return -1; + } + } + + cam->decompressed_frame.count = obuf-cam->decompressed_frame.data; + + up(&cam->param_lock); + + return cam->decompressed_frame.count; +} + +/* InitStreamCap wrapper to select correct start line */ +static inline int init_stream_cap(struct cam_data *cam) { + return do_command(cam, CPIA_COMMAND_InitStreamCap, + 0, cam->params.streamStartLine, 0, 0); +} + +/* update various camera modes and settings */ +static void dispatch_commands(struct cam_data *cam ) { + down(&cam->param_lock); + if( cam->cmd_queue==COMMAND_NONE ) { + up(&cam->param_lock); + return; + } + DEB_BYTE(cam->cmd_queue); + DEB_BYTE(cam->cmd_queue>>8); + if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) { + do_command(cam, CPIA_COMMAND_SetColourParams, + cam->params.colourParams.brightness, + cam->params.colourParams.contrast, + cam->params.colourParams.saturation, 0); + } + if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) { + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + } + if( cam->cmd_queue & COMMAND_SETFORMAT ) { + do_command(cam, CPIA_COMMAND_SetFormat, + cam->params.format.videoSize, + cam->params.format.subSample, + cam->params.format.yuvOrder, 0); + do_command(cam, CPIA_COMMAND_SetROI, + cam->params.roi.colStart, cam->params.roi.colEnd, + cam->params.roi.rowStart, cam->params.roi.rowEnd); + cam->first_frame = 1; + } + if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) { + do_command(cam, CPIA_COMMAND_SetCompressionTarget, + cam->params.compressionTarget.frTargeting, + cam->params.compressionTarget.targetFR, + cam->params.compressionTarget.targetQ, 0); + } + if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) { + do_command(cam, CPIA_COMMAND_SetYUVThresh, + cam->params.yuvThreshold.yThreshold, + cam->params.yuvThreshold.uvThreshold, 0, 0); + } + if( cam->cmd_queue & COMMAND_SETECPTIMING ) { + do_command(cam, CPIA_COMMAND_SetECPTiming, + cam->params.ecpTiming, 0, 0, 0); + } + if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) { + do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + cam->params.compressionParams.hysteresis, + cam->params.compressionParams.threshMax, + cam->params.compressionParams.smallStep, + cam->params.compressionParams.largeStep, + cam->params.compressionParams.decimationHysteresis, + cam->params.compressionParams.frDiffStepThresh, + cam->params.compressionParams.qDiffStepThresh, + cam->params.compressionParams.decimationThreshMod); + } + if( cam->cmd_queue & COMMAND_SETEXPOSURE ) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, + cam->params.exposure.expMode, + cam->params.exposure.compMode, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + } + if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + cam->params.colourBalance.balanceMode, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + } + if( cam->cmd_queue & COMMAND_SETSENSORFPS ) { + do_command(cam, CPIA_COMMAND_SetSensorFPS, + cam->params.sensorFps.divisor, + cam->params.sensorFps.baserate, 0, 0); + } + if( cam->cmd_queue & COMMAND_SETAPCOR ) { + do_command(cam, CPIA_COMMAND_SetApcor, + cam->params.apcor.gain1, + cam->params.apcor.gain2, + cam->params.apcor.gain4, + cam->params.apcor.gain8); + } + if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) { + do_command(cam, CPIA_COMMAND_SetFlickerCtrl, + cam->params.flickerControl.flickerMode, + cam->params.flickerControl.coarseJump, + cam->params.flickerControl.allowableOverExposure, 0); + } + if( cam->cmd_queue & COMMAND_SETVLOFFSET ) { + do_command(cam, CPIA_COMMAND_SetVLOffset, + cam->params.vlOffset.gain1, + cam->params.vlOffset.gain2, + cam->params.vlOffset.gain4, + cam->params.vlOffset.gain8); + } + if( cam->cmd_queue & COMMAND_PAUSE ) { + do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); + } + if( cam->cmd_queue & COMMAND_RESUME ) { + init_stream_cap( cam ); + } + up(&cam->param_lock); + cam->cmd_queue=COMMAND_NONE; + return; +} + +/* kernel thread function to read image from camera */ +static void fetch_frame(void *data) +{ + int image_size; + struct cam_data *cam = (struct cam_data *)data; + unsigned long oldjif, rate, diff; + + /* load first frame always uncompressed */ + if( cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE ) { + do_command(cam, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + } + + /* init camera upload */ + if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS, + 0, 0, 0)) goto end; + if(do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) goto end; + + /* loop until image ready */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + while(cam->params.status.streamState != STREAM_READY) { + if(current->need_resched) {schedule();} + current->state=TASK_INTERRUPTIBLE; + schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */ + if(signal_pending(current)) { + goto end; + } + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + } + + /* grab image from camera */ + if(current->need_resched) {schedule();} + oldjif = jiffies; + image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); + if(image_size<=0) { + DBG("read frame failed\n"); + goto end; + } + rate = image_size * HZ / 1024; + diff = jiffies-oldjif; + rate = diff==0 ? rate : rate/diff; /* unlikely but possible */ + /* Keep track of transfer_rate as a runnung average over 3 frames + * to smooth out any inconsistencies */ + cam->transfer_rate = (2*cam->transfer_rate + rate) / 3; + + /* camera idle now so dispatch queued commands */ + dispatch_commands( cam ); + + /* Update our knowledge of the camera state - FIXME: necessary? */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + if(current->need_resched) {schedule();} + cam->image_size = parse_picture(cam, image_size); + if( cam->image_size <= 0 ) { + DBG("parse frame failed\n"); + goto end; + } + + /* FIXME: this only works for double buffering */ + if( cam->frame[cam->curframe].state == FRAME_READY ) { + memcpy(cam->frame[cam->curframe].data, + cam->decompressed_frame.data, + cam->decompressed_frame.count); + cam->frame[cam->curframe].state = FRAME_DONE; + } else { + cam->decompressed_frame.state = FRAME_DONE; + } + +#if 0 + if( cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE ) { + cam->first_frame = 0; + cam->cmd_queue |= COMMAND_SETCOMPRESSION; + } +#else + if( cam->first_frame ) { + cam->first_frame = 0; + cam->cmd_queue |= COMMAND_SETCOMPRESSION; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } +#endif +end: + return; +} + +static int capture_frame(struct cam_data *cam, struct video_mmap *vm) +{ + int retval = 0; + + if( cam->frame_buf == NULL ) { /* we do lazy allocation */ + if( (retval = allocate_frame_buf(cam)) ) { + return retval; + } + } + + /* FIXME: the first frame seems to be captured by the camera + without regards to any initial settings, so we throw away + that one, the next one is generated with our settings + (exposure, color balance, ...) + */ + if(cam->first_frame) { + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + fetch_frame(cam); + if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO; + } + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + fetch_frame(cam); + if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO; + + return retval; +} + +static int goto_high_power(struct cam_data *cam) +{ + if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1; + if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1; + if(cam->params.status.systemState == HI_POWER_STATE) { + DBG("camera now in HIGH power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static int goto_low_power(struct cam_data *cam) +{ + if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1; + if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1; + if(cam->params.status.systemState == LO_POWER_STATE) { + DBG("camera now in LOW power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static void save_camera_state(struct cam_data *cam) +{ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + DBG("%d/%d/%d\n", + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + + return; +} + +static void set_camera_state(struct cam_data *cam) +{ + if(cam->params.colourBalance.balanceMode != 1) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + } + if(cam->params.colourBalance.balanceMode == 0) + cam->params.colourBalance.balanceMode = 2; + + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, 1, 1, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + do_command_extended(cam, CPIA_COMMAND_SetExposure, + 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + + if(cam->params.exposure.gainMode == 0) + cam->params.exposure.gainMode = 2; + if(cam->params.exposure.expMode == 0) + cam->params.exposure.expMode = 2; + if(cam->params.exposure.centreWeight == 0) + cam->params.exposure.centreWeight = 1; + + cam->cmd_queue = COMMAND_SETCOMPRESSION | + COMMAND_SETCOMPRESSIONTARGET | + COMMAND_SETCOLOURPARAMS | + COMMAND_SETFORMAT | + COMMAND_SETYUVTHRESH | + COMMAND_SETECPTIMING | + COMMAND_SETCOMPRESSIONPARAMS | +#if 0 + COMMAND_SETEXPOSURE | +#endif + COMMAND_SETCOLOURBALANCE | + COMMAND_SETSENSORFPS | + COMMAND_SETAPCOR | + COMMAND_SETFLICKERCTRL | + COMMAND_SETVLOFFSET; + dispatch_commands(cam); + save_camera_state(cam); + + return; +} + +static void get_version_information(struct cam_data *cam) +{ + /* GetCPIAVersion */ + do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + printk(KERN_INFO " CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + + /* GetPnPID */ + do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); + printk(KERN_INFO " CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); +} + +/* initialize camera */ +static int reset_camera(struct cam_data *cam) +{ + /* Start the camera in low power mode */ + if(goto_low_power(cam)) { + if( cam->params.status.systemState != 0x4 ) { + return -ENODEV; + } + /* FIXME: this is just dirty trial and error */ + reset_camera_struct(cam); + goto_high_power(cam); + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + if(goto_low_power(cam)) return -NODEV; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version FIXME: should we check PNPID? */ + cam->params.version.firmwareVersion = 0; + get_version_information(cam); + if(cam->params.version.firmwareVersion != 1) { + return -ENODEV; + } + + /* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, + STREAM_NOT_READY, 0); + + /* GotoHiPower */ + if(goto_high_power(cam)) + return -ENODEV; + + + /* Check the camera status */ + if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + if(cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n",cam->params.status.fatalError); + DBG("vp_status: %#04x\n",cam->params.status.vpStatus); + if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + /* Fatal error in camera */ + return -EIO; + } else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){ + /* Firmware 1-02 may do this for parallel port cameras, + * just clear the flags (developer's guide p 3-38) */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, + FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); + } + } + + /* Check the camera status again */ + if(cam->params.status.fatalError) { + if(cam->params.status.fatalError) + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + /* set camera to a known state */ + set_camera_state(cam); + + return 0; +} + +/* ------------------------- V4L interface --------------------- */ +static int cpia_open(struct video_device *dev, int flags) +{ + int i; + struct cam_data *cam = dev->priv; + + DBG("cpia_open\n"); + + if(cam->open_count > 0) { + DBG("Camera[%d] already open\n",cam->index); + return -EBUSY; + } + + if(cam->raw_image == NULL) { + if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){ + return -ENOMEM; + } + } + if(cam->decompressed_frame.data == NULL) { + cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE); + if(cam->decompressed_frame.data == NULL){ + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image=NULL; + return -ENOMEM; + } + } + + /* open cpia */ + if (cam->ops->open(cam->index, &cam->lowlevel_data)) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data=NULL; + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image=NULL; + return -ENODEV; + } + + /* reset the camera */ + if((i = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data=NULL; + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image=NULL; + return i; + } + + /* Set ownership of /proc/cpia/videoX to current user */ + cam->proc_entry->uid = current->uid; + + /* set mark for loading first frame uncompressed */ + cam->first_frame = 1; + cam->busy_lock = MUTEX; + + ++cam->open_count; +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + return 0; +} + +/* FIXME */ +static void cpia_close(struct video_device *dev) +{ + struct cam_data *cam; + + DBG("cpia_close\n"); + cam = dev->priv; + + if(cam->ops == NULL) { + if(--cam->open_count == 0) { + int i; + video_unregister_device(dev); + /* Need a lock when adding/removing cameras */ + lock_kernel(); + if(cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image=0; + } + if(cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, + CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data=0; + } + if(cam->frame_buf) { + free_frame_buf(cam); + } + for(i=0; iproc_entry->uid = 0; + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(cam); + + /* GotoLoPower */ + goto_low_power(cam); + + /* cleanup internal state stuff */ + free_frames(cam->frame); + + /* Update the camera ststus */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + + /* close cpia */ + cam->ops->close(cam->lowlevel_data); + + if(--cam->open_count == 0) { + /* clean up capture-buffers */ + if(cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = 0; + } + if(cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = 0; + } + if(cam->frame_buf) { + free_frame_buf(cam); + } + } + + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return; +} + +static long cpia_read(struct video_device *dev, char *buf, + unsigned long count, int noblock) +{ + struct cam_data *cam = dev->priv; + + /* make this _really_ smp and multithredi-safe */ + if( down_interruptible(&cam->busy_lock) ) { + return -EINTR; + } + + if (!buf) { + DBG("buf NULL\n"); + up(&cam->busy_lock); + return -EINVAL; + } + if(count == 0) { + DBG("count 0\n"); + up(&cam->busy_lock); + return 0; + } + if(!cam->ops) { + DBG("ops NULL\n"); + up(&cam->busy_lock); + return -ENODEV; + } + + /* upload frame */ + cam->decompressed_frame.state = FRAME_READY; + fetch_frame(cam); + if( cam->decompressed_frame.state != FRAME_DONE ) { + DBG("upload failed %d/%d\n", cam->decompressed_frame.count, + cam->decompressed_frame.state); + up(&cam->busy_lock); + return -EIO; + } + cam->decompressed_frame.state = FRAME_UNUSED; + + /* copy data to user space */ + if(cam->decompressed_frame.count > count) { + DBG("count wrong\n"); + up(&cam->busy_lock); + return -EFAULT; + } + if(copy_to_user(buf, cam->decompressed_frame.data, + cam->decompressed_frame.count)) { + DBG("copy_to_user failed\n"); + up(&cam->busy_lock); + return -EFAULT; + } + + up(&cam->busy_lock); + return cam->decompressed_frame.count; +} + +static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) +{ + struct cam_data *cam = dev->priv; + int retval = 0; + + /* make this _really_ smp-safe */ + if( down_interruptible(&cam->busy_lock) ) { + return -EINTR; + } + //DBG("cpia_ioctl: %u\n", ioctlnr); + + switch (ioctlnr) { + + /* query capabilites */ + case VIDIOCGCAP: + { + struct video_capability b; + + DBG("VIDIOCGCAP\n"); + strcpy(b.name, "CPiA Parport Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 352; /* VIDEOSIZE_CIF */ + b.maxheight = 288; + b.minwidth = 48; /* VIDEOSIZE_48_48 */ + b.minheight = 48; + + if (copy_to_user(arg, &b, sizeof(b))) + retval = -EFAULT; + break; + } + + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; + + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, arg, sizeof(v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(arg, &v, sizeof(v))) + retval = -EFAULT; + break; + } + + case VIDIOCSCHAN: + { + int v; + + DBG("VIDIOCSCHAN\n"); + if (copy_from_user(&v, arg, sizeof(v))) + retval = -EFAULT; + + if (retval == 0 && v != 0) + retval = -EINVAL; + + break; + } + + /* image properties */ + case VIDIOCGPICT: + DBG("VIDIOCGPICT\n"); + if (copy_to_user(arg, &cam->vp, sizeof(struct video_picture))) + retval = -EFAULT; + break; + + case VIDIOCSPICT: + { + struct video_picture vp; + + DBG("VIDIOCSPICT\n"); + + /* copy_from_user */ + if (copy_from_user(&vp, arg, sizeof(vp))) { + retval = -EFAULT; + break; + } + /* check validity */ + DBG("palette: %d\n", vp.palette); + DBG("depth: %d\n", vp.depth); + if ( !valid_mode(vp.palette, vp.depth) ) { + retval = -EINVAL; + break; + } + down(&cam->param_lock); + /* brightness, colour, contrast need no check 0-65535 */ + memcpy( &cam->vp, &vp, sizeof(vp) ); + /* update cam->params.colourParams */ + cam->params.colourParams.brightness = vp.brightness*100/65535; + cam->params.colourParams.contrast = vp.contrast*100/65535; + cam->params.colourParams.saturation = vp.colour*100/65535; + /* contrast is in steps of 8, so round */ + cam->params.colourParams.contrast = + ((cam->params.colourParams.contrast + 3) / 8) * 8; + if(cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2 && + cam->params.colourParams.contrast > 80) { + /* 1-02 firmware limits contrast to 80 */ + cam->params.colourParams.contrast = 80; + } + /* queue command to update camera */ + cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; + up(&cam->param_lock); + DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", + vp.depth, vp.palette, vp.brightness, vp.hue, vp.colour, + vp.contrast); + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + DBG("VIDIOCGWIN\n"); + + if (copy_to_user(arg, &cam->vw, sizeof(struct video_window))) + retval = -EFAULT; + break; + + case VIDIOCSWIN: + { + /* copy_from_user, check validity, copy to internal structure */ + struct video_window vw; + DBG("VIDIOCSWIN\n"); + if (copy_from_user(&vw, arg, sizeof(vw))) { + retval = -EFAULT; + break; + } + + if (vw.clipcount != 0) { /* clipping not supported */ + retval = -EINVAL; + break; + } + if (vw.clips != NULL) { /* clipping not supported */ + retval = -EINVAL; + break; + } + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + down(&cam->param_lock); + if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) { + int video_size = match_videosize(vw.width, vw.height); + if(video_size < 0) { + retval = -EINVAL; + up(&cam->param_lock); + break; + } + cam->video_size = video_size; + set_vw_size(cam); + DBG("%d / %d\n", cam->vw.width, cam->vw.height); + cam->cmd_queue |= COMMAND_SETFORMAT; + } + + // FIXME needed??? memcpy(&cam->vw, &vw, sizeof(vw)); + up(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + if(cam->cmd_queue & COMMAND_SETFORMAT) { + DBG("\n"); + dispatch_commands(cam); + } + DBG("%d/%d:%d\n", cam->video_size, + cam->vw.width, cam->vw.height); + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof(vm)); + vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; + vm.frames = FRAME_NUM; + for( i=0; iFRAME_NUM) { + retval = -EINVAL; + break; + } + + /* set video format */ + cam->vp.palette = vm.format; + switch(vm.format) { + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + cam->vp.depth = 16; + break; + case VIDEO_PALETTE_RGB24: + cam->vp.depth = 24; + break; + case VIDEO_PALETTE_RGB32: + cam->vp.depth = 32; + break; + default: + retval = -EINVAL; + break; + } + if(retval != 0) break; + + /* set video size */ + video_size = match_videosize(vm.width, vm.height); + if(cam->video_size<0) { + retval = -EINVAL; + break; + } + if( video_size != cam->video_size ) { + cam->video_size = video_size; + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + dispatch_commands(cam); + } +#if 0 + DBG("VIDIOCMCAPTURE: %d / %d/%d\n", cam->video_size, + cam->vw.width, cam->vw.height); +#endif + /* according to v4l-spec we must start streaming here */ + retval = capture_frame(cam, &vm); + + break; + } + + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) { + retval = -EFAULT; + break; + } + //DBG("VIDIOCSYNC: %d\n", frame); + + if (frame<0||frame>=FRAME_NUM) { + retval = -EINVAL; + break; + } + + switch (cam->frame[frame].state) { + case FRAME_UNUSED: + case FRAME_READY: + case FRAME_GRABBING: + DBG("sync to unused frame %d\n", frame); + retval = -EINVAL; + break; + + case FRAME_DONE: + cam->frame[frame].state = FRAME_UNUSED; + //DBG("VIDIOCSYNC: %d synced\n", frame); + break; + } + if(retval == -EINTR) { + /* FIXME - xawtv does not handle this nice */ + retval = 0; + } + break; + } + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + retval = -EINVAL; + break; + case VIDIOCGFBUF: + retval = -EINVAL; + break; + case VIDIOCSFBUF: + retval = -EINVAL; + break; + case VIDIOCKEY: + retval = -EINVAL; + break; + + /* tuner interface - we have none */ + case VIDIOCGTUNER: + retval = -EINVAL; + break; + case VIDIOCSTUNER: + retval = -EINVAL; + break; + case VIDIOCGFREQ: + retval = -EINVAL; + break; + case VIDIOCSFREQ: + retval = -EINVAL; + break; + + /* audio interface - we have none */ + case VIDIOCGAUDIO: + retval = -EINVAL; + break; + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + up(&cam->param_lock); + up(&cam->busy_lock); + return retval; +} + +/* FIXME */ +static int cpia_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + struct cam_data *cam; + int retval; + + DBG("cpia_mmap: %ld\n", size); + + if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){ + return -EINVAL; + } + + cam = dev->priv; + + /* make this _really_ smp-safe */ + if( down_interruptible(&cam->busy_lock) ) { + return -EINTR; + } + + if( cam->frame_buf == NULL ) { /* we do lazy allocation */ + if( (retval = allocate_frame_buf(cam)) ) { + up(&cam->busy_lock); + return retval; + } + } + pos = (unsigned long)(cam->frame_buf); + while (size > 0) { + page = kvirt_to_phys(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { + up(&cam->busy_lock); + return -EAGAIN; + } + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + if (size > PAGE_SIZE) size-=PAGE_SIZE; + else size=0; + } + + DBG("cpia_mmap: %ld\n", size); + up(&cam->busy_lock); + return 0; +} + +int cpia_video_init(struct video_device *vdev) +{ + create_proc_cpia_cam(vdev->priv); + return 0; +} + +static struct video_device cpia_template = { + "CPiA Camera", + VID_TYPE_CAPTURE, + VID_HARDWARE_CPIA, /* FIXME */ + cpia_open, /* open */ + cpia_close, /* close */ + cpia_read, /* read */ + NULL, /* no write */ + NULL, /* no poll */ + cpia_ioctl, /* ioctl */ + cpia_mmap, /* mmap */ + cpia_video_init, /* initialize */ + NULL, /* priv */ + 0, /* busy */ + -1 /* minor - unset */ +}; + +/* initialise cam_data structure */ +static void reset_camera_struct(struct cam_data *cam) +{ + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + cam->params.colourParams.brightness = 50; + cam->params.colourParams.contrast = 48; + cam->params.colourParams.saturation = 50; + cam->params.exposure.gainMode = 2; + cam->params.exposure.expMode = 2; /* AEC */ + cam->params.exposure.compMode = 1; + cam->params.exposure.centreWeight = 1; + cam->params.exposure.gain = 0; + cam->params.exposure.fineExp = 0; + cam->params.exposure.coarseExpLo = 185; + cam->params.exposure.coarseExpHi = 0; + cam->params.exposure.redComp = 220; + cam->params.exposure.green1Comp = 214; + cam->params.exposure.green2Comp = 214; + cam->params.exposure.blueComp = 230; + cam->params.colourBalance.balanceMode = 2; /* auto color balance */ + cam->params.colourBalance.redGain = 32; + cam->params.colourBalance.greenGain = 6; + cam->params.colourBalance.blueGain = 92; + cam->params.apcor.gain1 = 0x1c; + cam->params.apcor.gain2 = 0x1a; + cam->params.apcor.gain4 = 0x2d; + cam->params.apcor.gain8 = 0x2a; + cam->params.flickerControl.flickerMode = 0; + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->params.vlOffset.gain1 = 24; + cam->params.vlOffset.gain2 = 28; + cam->params.vlOffset.gain4 = 30; + cam->params.vlOffset.gain8 = 30; + cam->params.compressionParams.hysteresis = 3; + cam->params.compressionParams.threshMax = 11; + cam->params.compressionParams.smallStep = 1; + cam->params.compressionParams.largeStep = 3; + cam->params.compressionParams.decimationHysteresis = 2; + cam->params.compressionParams.frDiffStepThresh = 5; + cam->params.compressionParams.qDiffStepThresh = 3; + cam->params.compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + /* Start with a reasonable transfer rate */ + cam->transfer_rate = 500; + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + cam->params.sensorFps.divisor = 1; + cam->params.sensorFps.baserate = 1; + + cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */ + cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */ + + cam->params.format.subSample = SUBSAMPLE_422; + cam->params.format.yuvOrder = YUVORDER_YUYV; + + cam->params.compression.mode = CPIA_COMPRESSION_AUTO; + cam->params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + cam->params.compressionTarget.targetFR = 7; /* FIXME? */ + cam->params.compressionTarget.targetQ = 10; /* FIXME? */ + + cam->video_size = VIDEOSIZE_CIF; + + cam->vp.colour = 32768; /* 50% */ + cam->vp.hue = 32768; /* 50% */ + cam->vp.brightness = 32768; /* 50% */ + cam->vp.contrast = 32768; /* 50% */ + cam->vp.whiteness = 0; /* not used -> grayscale only */ + cam->vp.depth = 0; /* FIXME: to be set by user? */ + cam->vp.palette = 0; /* FIXME: to be set by user? */ + + cam->vw.x = 0; + cam->vw.y = 0; + set_vw_size(cam); + cam->vw.chromakey = 0; + /* PP NOTE: my extension to use vw.flags for this, bear it! */ + cam->vw.flags = 0; + cam->vw.clipcount = 0; + cam->vw.clips = NULL; + + cam->cmd_queue = COMMAND_NONE; + cam->first_frame = 0; + + return; +} + +/* initialize cam_data structure */ +static void init_camera_struct(struct cam_data *cam, + struct cpia_camera_ops *ops ) +{ + int i; + /* Default everything to 0 */ + memset(cam, 0, sizeof(struct cam_data)); + + cam->ops = ops; + cam->param_lock = MUTEX; + + reset_camera_struct(cam); + + cam->proc_entry = NULL; + + memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); + cam->vdev.priv = cam; + + cam->curframe = 0; + for( i=0; iframe[i].width = 0; + cam->frame[i].height = 0; + cam->frame[i].state = FRAME_UNUSED; + cam->frame[i].data = NULL; + } + cam->decompressed_frame.width = 0; + cam->decompressed_frame.height = 0; + cam->decompressed_frame.state = FRAME_UNUSED; + cam->decompressed_frame.data = NULL; + + return; +} + +int cpia_register_camera(struct cpia_camera_ops *ops) +{ + int i; + struct cam_data *cam; + /* Need a lock when adding/removing cameras. This doesn't happen + * often and doesn't take very long, so grabbing the kernel lock + * should be OK. */ + lock_kernel(); + for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */ + if(i == CPIA_MAXCAMS) { + unlock_kernel(); + return -ENODEV; + } + + if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) { + unlock_kernel(); + return -ENOMEM; + } + + init_camera_struct( camera[i], ops ); + camera[i]->index = i; + + /* register v4l device */ + if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) { + kfree(camera[i]); + camera[i] = NULL; + unlock_kernel(); + printk(KERN_DEBUG "video_register_device failed\n"); + return -ENODEV; + } + + unlock_kernel(); + + /* FIXME */ +#if 1 + cam = camera[i]; + /* open cpia */ + if (cam->ops->open(cam->index, &cam->lowlevel_data)) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data=NULL; + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image=NULL; + return -ENODEV; + } + + /* reset the camera */ + if((i = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data=NULL; + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image=NULL; + return i; + } +#endif + return i; +} + +void cpia_unregister_camera(int camnr) +{ + struct cam_data *cam; + if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return; + cam = camera[camnr]; + + DBG("unregistering video\n"); + /* FIXME: Is this safe if the device is open? */ + video_unregister_device(&cam->vdev); + + /* Need a lock when adding/removing cameras. This doesn't happen + * often and doesn't take very long, so grabbing the kernel lock + * should be OK. */ + lock_kernel(); + + DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor); + destroy_proc_cpia_cam(cam); + + if(cam->open_count == 0) { + DBG("freeing camera\n"); + kfree(camera[camnr]); + camera[camnr] = NULL; + } else { + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } + unlock_kernel(); +} + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +#ifdef MODULE +int init_module(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + proc_cpia_create(); +#ifdef CONFIG_KMOD +#ifdef CONFIG_VIDEO_CPIA_PP_MODULE + request_module("cpia_pp"); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB_MODULE + request_module("cpia_usb"); +#endif +#endif +return 0; +} + +void cleanup_module(void) +{ + int i; + for(i=0; ivdev); + } + } + + proc_cpia_destroy(); +} + +#else + +int cpia_init(struct video_init *unused) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + proc_cpia_create(); + +#ifdef MODULE +#ifdef CONFIG_KMOD +#ifdef CONFIG_VIDEO_CPIA_PP_MODULE + request_module("cpia_pp"); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB_MODULE + request_module("cpia_usb"); +#endif +#endif /* CONFIG_KMOD */ +#else /* !MODULE */ +#ifdef CONFIG_VIDEO_CPIA_PP + cpia_pp_init(); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB + cpia_usb_init(); +#endif +#endif /* !MODULE */ + return 0; +} + +/* Exported symbols for modules. */ + +EXPORT_SYMBOL(cpia_register_camera); +EXPORT_SYMBOL(cpia_unregister_camera); + +#endif diff --git a/drivers/char/cpia_pp.c b/drivers/char/cpia_pp.c new file mode 100644 index 000000000000..3e35028a53a5 --- /dev/null +++ b/drivers/char/cpia_pp.c @@ -0,0 +1,1497 @@ +/* + * cpia_pp CPiA Parallel Port driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) 1999 Bas Huisman + * Scott J. Bertin , + * Peter Pregler + * Johannes Erdfelt + * + * 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 +#ifdef CONFIG_VIDEO_CPIA_PP_DMA +#include +#endif + +#ifdef CONFIG_KMOD +#include +#endif + + +/* If this is a module and parport_pc is not, some parport_pc_* functions + * are not directly availbale. parport.h messes this up. + * This fixes what we need. + */ +#if defined(MODULE) && !defined(CONFIG_PARPORT_PC_MODULE) +#define PARPORT_NEED_GENERIC_OPS +#undef parport_enable_irq +#undef parport_disable_irq +#undef parport_read_fifo +#define parport_read_fifo(p) (p)->ops->read_fifo(p) +#endif + +/* These should be defined in linux/parport.h, but aren't. The #ifndef + * parport_enable_irq is in case they are at some future time. + */ +#ifdef PARPORT_NEED_GENERIC_OPS +#ifndef parport_enable_irq +#define parport_enable_irq(p) (p)->ops->enable_irq(p) +#define parport_disable_irq(p) (p)->ops->disable_irq(p) +#endif /* parport_enable_irq */ +#else /* !PARPORT_NEED_GENERIC_OPS */ +#ifndef parport_enable_irq +#define parport_enable_irq(p) parport_pc_enable_irq(p) +#define parport_disable_irq(p) parport_pc_disable_irq(p) +#endif /* parport_enable_irq */ +#endif /* !PARPORT_NEED_GENERIC_OPS */ + +#undef _CPIA_DEBUG_ /* define for verbose debug output */ +#include + +static int cpia_pp_open(int camnr, void **privdata); +static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_pp_streamStart(void *privdata); +static int cpia_pp_streamStop(void *privdata); +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); +static int cpia_pp_close(void *privdata); + +#define ABOUT "Parallel port driver for Vision CPiA based cameras" + +/* IEEE 1284 Compatiblity Mode signal names */ +#define nStrobe PARPORT_CONTROL_STROBE /* inverted */ +#define nAutoFd PARPORT_CONTROL_AUTOFD /* inverted */ +#define nInit PARPORT_CONTROL_INIT +#define nSelectIn PARPORT_CONTROL_SELECT +#define IntrEnable PARPORT_CONTROL_INTEN /* normally zero for no IRQ */ +#define DirBit PARPORT_CONTROL_DIRECTION /* 0 = Forward, 1 = Reverse */ + +#define nFault PARPORT_STATUS_ERROR +#define Select PARPORT_STATUS_SELECT +#define PError PARPORT_STATUS_PAPEROUT +#define nAck PARPORT_STATUS_ACK +#define Busy PARPORT_STATUS_BUSY /* inverted */ + +/* some more */ +#define HostClk nStrobe +#define HostAck nAutoFd +#define nReverseRequest nInit +#define Active_1284 nSelectIn +#define nPeriphRequest nFault +#define XFlag Select +#define nAckReverse PError +#define PeriphClk nAck +#define PeriphAck Busy + +/* these can be used to correct for the inversion on some bits */ +#define STATUS_INVERSION_MASK (Busy) +#define CONTROL_INVERSION_MASK (nStrobe|nAutoFd|nSelectIn) + +#define ECR_empty 0x01 +#define ECR_full 0x02 +#define ECR_serviceIntr 0x04 +#define ECR_dmaEn 0x08 +#define ECR_nErrIntrEn 0x10 + +#define ECR_mode_mask 0xE0 +#define ECR_SPP_mode 0x00 +#define ECR_PS2_mode 0x20 +#define ECR_FIFO_mode 0x40 +#define ECR_ECP_mode 0x60 + +#define ECP_FIFO_SIZE 16 +#define DMA_BUFFER_SIZE PAGE_SIZE + /* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */ + +#define GetECRMasked(port,mask) (parport_read_econtrol(port) & (mask)) +#define GetStatus(port) ((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8)) +#define SetStatus(port,val) parport_write_status(port,(val)^STATUS_INVERSION_MASK) +#define GetControl(port) ((parport_read_control(port)^CONTROL_INVERSION_MASK)&(0x3f)) +#define SetControl(port,val) parport_write_control(port,(val)^CONTROL_INVERSION_MASK) + +#define GetStatusMasked(port,mask) (GetStatus(port) & (mask)) +#define GetControlMasked(port,mask) (GetControl(port) & (mask)) +#define SetControlMasked(port,mask) SetControl(port,GetControl(port) | (mask)); +#define ClearControlMasked(port,mask) SetControl(port,GetControl(port)&~(mask)); +#define FrobControlBit(port,mask,value) SetControl(port,(GetControl(port)&~(mask))|((value)&(mask))); + +#define PP_MAXCAMS (PARPORT_MAXport; + unsigned long endjif; + + if (GetECRMasked(port, ECR_full)) { + return 1; + } + + endjif = jiffies + WHILE_OUT_TIMEOUT; + while( jiffiesport; + u8 status = GetStatus(port); + u8 control = GetControl(port); + + cam->camstate = CPIA_PHASE_neg2s; + + if (control & nSelectIn) DBG("nSelIn should be low at beginning\n"); + if (status & PError) DBG("PError should be low at beginning\n"); + if (!(control & nAutoFd)) DBG("nAutFb should be high at beginning\n"); + if (!(control & nStrobe)) DBG("nStrobe should be high at beginning\n"); + + /* + a high nack idicates a new image is ready (see the docs) + this line can be used for a ISR. + */ + //if (!(status & nAck)) DBG("nAck should be high at beginning\n"); + + if (!(status & Busy)) DBG("Busy should be high at beginning\n"); + // FIXME ?if (status & nFault) DBG("nFault should be low at beginning\n"); + if (!(status & Select)) DBG("Select should be high at beginning\n"); + if (!(control & nInit)) DBG("nInit should be high at beginning\n"); + + if (cam->state != CPIA_FORWARD) + DBG("port was not in CPIA_FORWARD direction\n"); + + parport_write_data(port, extensibility ? 0x80 : 0x10); + + FrobControlBit(port, nSelectIn | nAutoFd, nSelectIn); //A nAutoFd low,nSelectin high + + if (my_wait_peripheral(port, PError | nFault | Select | nAck, PError | nFault | Select)) { //B + DBG("B failed\n"); + return -1; + } + + ClearControlMasked(port, nStrobe); + ClearControlMasked(port, nStrobe); + ClearControlMasked(port, nStrobe); + ClearControlMasked(port, nStrobe); + + SetControlMasked(port, nAutoFd | nStrobe); // C + + if (my_wait_peripheral(port, nAck | Select | PError, nAck | Select)) { // D niet perror+busy low ? nfault high ? + DBG("D failed nAck,nInit should become high, Perror low\n"); + return -1; + } + + if (extensibility) { + parport_write_data(port, 8 | 3); + ClearControlMasked(port, nStrobe); + if (my_wait_peripheral(port, nAck, 0)) { + DBG("nAck should go low in asking for extensibility mode\n"); + DEB_PORT(port); + return -1; + } + SetControlMasked(port, nStrobe); + if (my_wait_peripheral(port, nAck | Select, nAck | Select)) { + DBG("nAck,Xflag should go high in asking for extensibility mode\n"); + DEB_PORT(port); + return -1; + } + } + return 0; +} + +/**************************************************************************** + * + * EcpSetupPhase + * + ***************************************************************************/ +static int ECPSetupPhase(struct pp_cam_entry *cam) +{ + cam->camstate = CPIA_PHASE_setup; + ClearControlMasked(cam->port, nAutoFd); // E + //if (my_wait_peripheral(cam->port,PError,PError)) //pport style + + if (my_wait_peripheral(cam->port, PError | nAck | Busy, PError | nAck)) { // F + DBG("F failed PError,nAck high Busy Low\n"); + DEB_PORT(cam->port); + return -1; + } + EmptyFifo(cam->port); + return 0; +} + +/**************************************************************************** + * + * Forward2Reverse + * + ***************************************************************************/ +static int Forward2Reverse(struct pp_cam_entry *cam) +{ + struct parport *port = cam->port; + + cam->camstate = CPIA_PHASE_f2rev; + + SetControlMasked(port, DirBit); // door ClearControlBit(port,nReverseRequest) komt de eerste byte beter als DirBit hier wordt gezet + //mdelay(3); + + ClearControlMasked(port, nReverseRequest); // G + //if (my_wait_peripheral(port,PError,0)) //pportq style + + if (my_wait_peripheral(port, PError | Busy | nFault | Select, Busy | Select)) { //H + DBG("H failed\n"); + DEB_PORT(port); + ClearControlMasked(port, DirBit); // DirBit repareren; + //mdelay(3); + + return -1; + } + cam->state = CPIA_REVERSE; + return 0; +} + + +/**************************************************************************** + * + * Reverse2Forward + * + ***************************************************************************/ +static void Reverse2Forward(struct pp_cam_entry *cam) +{ + struct parport *port = cam->port; + + cam->camstate = CPIA_PHASE_r2for; + + if (cam->state != CPIA_REVERSE) + DBG("port was not in CPIA_REVERSE direction\n"); + + SetControlMasked(port, nReverseRequest); + if (my_wait_peripheral(port, nAckReverse, nAckReverse)) + DBG("nAckReverse should fo high in Reverse2Forward\n"); + + ClearControlMasked(port, DirBit); + //mdelay(3); + + cam->state = CPIA_FORWARD; +} + + +/**************************************************************************** + * + * Valid1284Termination + * + ***************************************************************************/ +static void Valid1284Termination(struct pp_cam_entry *cam) +{ + struct parport *port = cam->port; + + cam->camstate = CPIA_PHASE_term; + + if (cam->state != CPIA_FORWARD) + DBG("port was not in CPIA_FORWARD direction\n"); + + FrobControlBit(port, nSelectIn | nAutoFd | nStrobe | nInit, nAutoFd | nStrobe | nInit); //20 + + if (my_wait_peripheral(port, nAck, 0)) { //21 + DBG("nAck should go low in Valid1284Termination\n"); + DEB_PORT(port); + } + + ClearControlMasked(port, nAutoFd); //22 + + if (my_wait_peripheral(port, nAck, nAck)) //23 + DBG("nAck should go high again in Valid1284Termination\n"); + + SetControlMasked(port, nAutoFd); //24 + + if (my_wait_peripheral(port, PError | nAck | Busy | nFault | Select, + nAck | Busy | Select)) + DBG("status bit did not go to the correct value\n"); + + cam->camstate = CPIA_PHASE_idle; +} + +/**************************************************************************** + * + * SimECPReadBuffer (does termination) + * + ***************************************************************************/ +static int SimECPReadBuffer(struct pp_cam_entry *cam, u8 *buf, int bytes) +{ + int readbytes = 0; + struct parport *port = cam->port; + + cam->camstate = CPIA_PHASE_secpread; + + if (cam->state != CPIA_REVERSE) DBG("cam not in CPIA_REVERSE\n"); + if (!GetControlMasked(port, DirBit)) + DBG("parport is in forward mode, " + "guess you won't be reading much\n"); + + while (readbytes < bytes) { + ClearControlMasked(port, nAutoFd); //moet hier staan: als de while loop exit moet nAutoFd high blijven ... + + if (my_wait_peripheral(port, nAck, 0)) { + DBG("nAck didn't went down after read %d bytes no more data ?\n", readbytes); + break; + } + *buf++ = parport_read_data(port); + SetControlMasked(port, nAutoFd); + + readbytes++; + if (my_wait_peripheral(port, nAck, nAck)) { + DBG("nAck didn't went up after read %d bytes\n", readbytes); + break; + } + } + return readbytes; +} + +/**************************************************************************** + * + * ECPReadBuffer_PIO + * + ***************************************************************************/ +static int ECPReadBuffer_PIO(struct pp_cam_entry *cam, u8 *buf, int bytes) +{ + int readbytes = 0, endseen = 0, j; + struct parport *port = cam->port; + + if (my_wait_peripheral(port, nAck, 0)) { + DBG("nAck didn't went down after read %d bytes no more data ?\n", readbytes); + DEB_PORT(port); + goto WhileoutError; + } + + *buf = parport_read_data(port); // we take the first byte manually + if (*buf == EOI) { + endseen++; + } else { + endseen = 0; + } + buf++; + readbytes++; + + /* acknowledge for this BYTE */ + SetControlMasked(port, nAutoFd); + if (my_wait_peripheral(port, nAck, nAck)) { + DBG("nAck didn't went up after read %d bytes\n", readbytes); + goto WhileoutError; + } + + parport_frob_econtrol(port, + ECR_mode_mask|ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn, + ECR_ECP_mode|ECR_serviceIntr|ECR_nErrIntrEn); + + while ((((bytes - readbytes) / ECP_FIFO_SIZE) > 0) && (endseen < 4)) { + if( current->need_resched ) { + schedule(); + } + if (while_out(cam) <= 0) { + goto WhileoutError; + } + for (j = 0; j < ECP_FIFO_SIZE; j++) { + *buf = parport_read_fifo(port); + if (*buf == EOI) { + endseen++; + } else { + endseen = 0; + } + buf++; + readbytes++; + } + if( current->need_resched ) schedule(); + } + + /* switch off automatic filling of the FIFO */ + ClearControlMasked(port, nAutoFd); + + while ((!GetECRMasked(port, ECR_empty)) && (readbytes < bytes)) { + *buf = parport_read_fifo(port); + if (*buf == EOI) { + endseen++; + } else { + endseen = 0; + } + buf++; + readbytes++; + } + +WhileoutError: + + parport_write_econtrol(port, PARPORT_MODE_PCECR); + parport_frob_econtrol(port, + ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn, + ECR_serviceIntr|ECR_nErrIntrEn); + + if( endseen > 3 ) { + cam->image_complete=1; + } + + return readbytes; +} + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA +/**************************************************************************** + * + * ECPReadBuffer_DMA + * + ****************************************************************************/ + +int ECPReadBuffer_DMA(struct pp_cam_entry *cam, u8 *buf, int bytes) +{ + struct parport *port = cam->port; + unsigned long flags; + int channel = port->dma; + int end_jiffies; + + cam->buf=buf; + cam->bytes=bytes; + cam->endseen=0; + cam->readbytes=0; + cam->image_complete=0; + + if (my_wait_peripheral(port,nAck,0)) { + DBG("nAck didn't went down after read %d bytes no more data ?\n",cam->readbytes); + DEB_PORT(port); + return cam->readbytes; + } + + *(cam->buf) = parport_read_data(port); // we take the first byte manually + if (*(cam->buf) == EOI) { + cam->endseen++; + } else { + cam->endseen = 0; + } + cam->buf++; + cam->readbytes++; + + /* acknowledge for this BYTE */ + SetControlMasked( port, nAutoFd ); + if (my_wait_peripheral(port,nAck,nAck)) { + DBG("nAck didn't went up after read %d bytes\n",cam->readbytes); + return cam->readbytes; + } + + /* switch port to EPC mode, disable all interrupts for now */ + parport_frob_econtrol(port, ECR_mode_mask, ECR_ECP_mode); + parport_frob_econtrol(port, + ECR_nErrIntrEn|ECR_serviceIntr|ECR_dmaEn, + ECR_nErrIntrEn|ECR_serviceIntr); + + /* prepare controller for dma - align for 16bit DMA */ + cam->block_size= + bytesdma_buf)); + set_dma_count(channel, cam->block_size); + enable_dma(channel); + + cam->dma_on=1; + cam->dma_intr = 0; + + /* switch on dma transfer */ + parport_frob_econtrol(port, ECR_dmaEn, ECR_dmaEn); + parport_frob_econtrol(port,ECR_serviceIntr, 0); + + release_dma_lock(flags); + + end_jiffies = jiffies+DMA_TIMEOUT; + while(cam->dma_on && !cam->image_complete && + jiffies < end_jiffies + /*&& !signal_pending(current)*/) { + interruptible_sleep_on(&cam->wq_dma); + } + + /* we are done with dma */ + flags=claim_dma_lock(); + if(cam->dma_on) { + cam->dma_on=0; + interruptible_sleep_on(&cam->wq_dma); + } + + disable_dma(channel); + + parport_frob_econtrol(port,ECR_mode_mask, ECR_PS2_mode); + parport_frob_econtrol(port,ECR_serviceIntr, ECR_serviceIntr); + parport_frob_econtrol(port, ECR_dmaEn, 0); + + release_dma_lock(flags); + return cam->readbytes; +} +#endif + +/**************************************************************************** + * + * ECPReadBuffer (does termination) (stops when reading EOI) + * + ****************************************************************************/ + +int ECPReadBuffer(struct pp_cam_entry *cam, u8 *buf, int bytes) +{ + struct parport *port = cam->port; + int readbytes; + + cam->camstate = CPIA_PHASE_ecpread; + + if (cam->state != CPIA_REVERSE) DBG("cam not in CPIA_REVERSE\n"); + if (!GetControlMasked(port,DirBit)) DBG("parport is in forward mode, guess you won't be reading much\n"); + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if(cam->dma_buf) { + readbytes = ECPReadBuffer_DMA(cam, buf, bytes); + } else { +#endif + readbytes = ECPReadBuffer_PIO(cam, buf, bytes); +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + } +#endif + + return readbytes; +}; + +/**************************************************************************** + * + * SimECPWriteBuffer (does termination) + * + ***************************************************************************/ +static int SimECPWriteBuffer(struct pp_cam_entry *cam, const u8 *buf, int size) +{ + int written = 0; + struct parport *port = cam->port; + + cam->camstate = CPIA_PHASE_secpwrite; + + if (cam->state != CPIA_FORWARD) DBG("cam not in CPIA_FORWARD\n"); + if (GetControlMasked(port, DirBit)) + DBG("parport is in reverse mode, " + "guess you won't be writing much\n"); + + parport_write_data(port, *buf); + SetControlMasked(port, HostAck); + for (written = 0; written < size; written++) { + parport_write_data(port, *buf++); + ClearControlMasked(port, HostClk); + if (my_wait_peripheral(port, PeriphAck, PeriphAck)) { + DBG("PeriphAck never went up signaling read a byte\n"); + break; + } + //DBG("written byte: %02x\n",*(u8*)(buf-1)); + SetControlMasked(port, HostClk); + if (my_wait_peripheral(port, PeriphAck, 0)) { + DBG("PeriphAck never went down signaling ready for next byte\n"); + break; + } + } + + if (written != size) + DBG("failed; written %db should be %db\n", written, size); + + return written; +} + +/**************************************************************************** + * + * EndTransferMode + * + ***************************************************************************/ +static void EndTransferMode(struct pp_cam_entry *cam) +{ + if (!cam) return; + if (cam->state == CPIA_REVERSE) Reverse2Forward(cam); + Valid1284Termination(cam); + if(cam->stream_irq) cpia_parport_enable_irq(cam->port); +} + +/**************************************************************************** + * + * ForwardSetup + * + ***************************************************************************/ +static int ForwardSetup(struct pp_cam_entry *cam) +{ + if(cam->stream_irq) cpia_parport_disable_irq(cam->port); + if (!Negotiate2SetupPhase(cam, 0)) { + if (!ECPSetupPhase(cam)) { + return 0; + } else { + DBG("could not finish setup phase\n"); + } + } else { + DBG("could not negotiate for setup phase\n"); + } + EndTransferMode(cam); + return -1; +} + +/**************************************************************************** + * + * ReverseSetup + * + ***************************************************************************/ +static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) +{ + if(cam->stream_irq) cpia_parport_disable_irq(cam->port); + if (!Negotiate2SetupPhase(cam, extensibility)) { + if (!ECPSetupPhase(cam)) { + if (!Forward2Reverse(cam)) { + return 0; /* don't terminate */ + } else { + DBG("could not forward to reverse the port in order to read anything\n"); + } + } else { + DBG("could not finish setup phase\n"); + } + } else { + DBG("could not negotiate for setup phase\n"); + } + EndTransferMode(cam); + return -1; +} + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA +/**************************************************************************** + * + * DMA routines + * + ****************************************************************************/ + +/**************************************************************************** + * + * Initiate a DMA read buffer + * + ****************************************************************************/ + +static inline void receive_buf_dma(struct pp_cam_entry *cam, int num_bytes) +{ + int channel = cam->port->dma; + + disable_dma(channel); + clear_dma_ff(channel); + set_dma_mode(channel, DMA_MODE_READ); + set_dma_addr(channel, virt_to_bus(cam->dma_buf)); + set_dma_count(channel, num_bytes); + enable_dma(channel); + + parport_frob_econtrol(cam->port,ECR_serviceIntr, 0); + + return; +} + +static inline void stop_receive_buf_dma(struct pp_cam_entry *cam) +{ + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(cam->port->dma); + cam->dma_on=0; + + parport_frob_econtrol(cam->port,ECR_mode_mask, ECR_PS2_mode); + parport_frob_econtrol(cam->port,ECR_serviceIntr, + ECR_serviceIntr); + parport_frob_econtrol(cam->port, ECR_dmaEn, 0); + + if( waitqueue_active(&cam->wq_dma) ) { + wake_up_interruptible(&cam->wq_dma); + } + release_dma_lock(flags); + + return; +} + +/**************************************************************************** + * + * DMA bottom half + * + ****************************************************************************/ + +static void dma_handler(void *handle) +{ + struct pp_cam_entry *cam = handle; + int i; + unsigned char *dma_ptr, *buf_ptr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(cam->port->dma); + parport_frob_econtrol(cam->port,ECR_serviceIntr, + ECR_serviceIntr); + release_dma_lock(flags); + + cam->dma_intr--; + if(cam->dma_intr) { + DBG("extra interrupt: %d\n", cam->dma_intr); + stop_receive_buf_dma(cam); + return; + } + if(cam->dma_on==0 || cam->image_complete) { + return; + } + flags=claim_dma_lock(); + clear_dma_ff(cam->port->dma); + if((i=get_dma_residue(cam->port->dma))!=0) { + DBG("dma_residue: %d\n", i); + stop_receive_buf_dma(cam); + release_dma_lock(flags); + return; + } + release_dma_lock(flags); + if((i=cam->readbytes+cam->block_size)>cam->bytes) { + DBG("read too many bytes: %d/%d/%d\n", + i, cam->readbytes, cam->bytes); + stop_receive_buf_dma(cam); + return; + } + dma_ptr=cam->dma_buf; + buf_ptr=cam->buf; + i=-1; + while(++iblock_size && cam->endseen<4) { + if(*dma_ptr==EOI) { + cam->endseen++; + } else { + cam->endseen=0; + } + (*buf_ptr++)=(*dma_ptr++); + } + cam->readbytes+=i; + cam->buf+=i; + if( cam->endseen==4 ) { + cam->image_complete=1; + stop_receive_buf_dma(cam); + return; + } + + if( cam->bytes-cam->readbytes <= DMA_BUFFER_SIZE ) { + cam->block_size=cam->bytes-cam->readbytes; + cam->block_size=cam->block_size-(cam->block_size%2); + if( cam->block_size==0 ) { + stop_receive_buf_dma(cam); + return; + } + } + + if( cam->dma_on) { + receive_buf_dma(cam, cam->block_size); + } else { + stop_receive_buf_dma(cam); + } + return; +} +#endif + +/**************************************************************************** + * + * IRQ handler + * + ***************************************************************************/ + +static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b) +{ + struct pp_cam_entry *cam = handle; + if (cam==NULL) { + return; + } +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if(cam->dma_on) { + cam->dma_intr++; + if( cam->dma_intr == 1 ) { + queue_task(&cam->dma_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } else { + DBG("%d\n", cam->dma_intr); + } + return; + } +#endif + if (cam->camstate == CPIA_PHASE_ecpread) return; + if( cam->camstate != CPIA_PHASE_idle ) + DBG("got IRQ(%d) when in %s\n", + irq, camstatesstr[cam->camstate]); + + cam->image_ready++; + if(cam->image_ready > 1) { + cam->image_ready=0; + DBG("image skipped?\n"); + } + if(cam->cb_task.routine!=0) { + queue_task(&cam->cb_task, &tq_scheduler); + } + + if(waitqueue_active(&cam->wq_stream)) { + wake_up_interruptible(&cam->wq_stream); + } + return; +} + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) +{ + int retval=0; + if (packet == NULL) { + return -EINVAL; + } + if (ForwardSetup(cam)) { + return -EIO; + } + if (SimECPWriteBuffer(cam, packet, size) != size) { + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) +{ + int retval=0; + if (packet == NULL) { + return -EINVAL; + } + if (ReverseSetup(cam, 0)) { + return -EIO; + } + if (SimECPReadBuffer(cam, packet, size) != size) { + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * cpia_pp_streamStart + * + ***************************************************************************/ +static int cpia_pp_streamStart(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + DBG("\n"); + cam->streaming=1; + cam->image_ready=0; + //if (ReverseSetup(cam,1)) return -EIO; + if(cam->stream_irq) cpia_parport_enable_irq(cam->port); + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamStop + * + ***************************************************************************/ +static int cpia_pp_streamStop(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + + DBG("\n"); + cam->streaming=0; + cpia_parport_disable_irq(cam->port); + //EndTransferMode(cam); + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamRead + * + ***************************************************************************/ +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock) +{ + struct pp_cam_entry *cam = privdata; + int read_bytes = 0; + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(buffer == NULL) { + DBG("Internal driver error: buffer is NULL\n"); + return -EINVAL; + } + //if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock); + if( cam->stream_irq ) { + DBG("%d\n", cam->image_ready); + cam->image_ready--; + } + cam->image_complete=0; + if (0/*cam->streaming*/) { + if(!cam->image_ready) { + if(noblock) return -EWOULDBLOCK; + interruptible_sleep_on(&cam->wq_stream); + if( signal_pending(current) ) return -EINTR; + DBG("%d\n", cam->image_ready); + } + } else { + if (ReverseSetup(cam, 1)) return -EIO; + } + read_bytes = ECPReadBuffer(cam, buffer, CPIA_MAX_IMAGE_SIZE); + if( 1/*!cam->streaming*/) { + EndTransferMode(cam); + } + if(!cam->image_complete) { + if( !signal_pending(current) ) + DBG("incomplete image: %ld / %d / %d\n", + jiffies, cam->image_complete, read_bytes); + } + return cam->image_complete ? read_bytes : -EIO; +} + +/**************************************************************************** + * + * cpia_pp_transferCmd + * + ***************************************************************************/ +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err; + int retval=0; + int databytes; + struct pp_cam_entry *cam = privdata; + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(command == NULL) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + databytes = (((int)command[7])<<8) | command[6]; + if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + return err; + } + if(command[0] == DATA_IN) { + u8 buffer[8]; + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + err = ReadPacket(cam, buffer, 8); + if(err < 0) { + return err; + } + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) { + if(databytes > 0) { + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + retval = -EINVAL; + } else { + WritePacket(cam, data, databytes); + } + } + } else { + DBG("Unexpected first byte of command: %x\n", command[0]); + retval = -EINVAL; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_open + * + ***************************************************************************/ +static int cpia_pp_open(int camnr, void **privdata) +{ + struct pp_cam_entry *cam=NULL; + int i; + for(i=0; icamnr == camnr) { + DBG("Found camera[%d]\n", camnr); + break; + } + } + + *privdata = cam; + + if(cam->open_count == 0) { + if (parport_claim(cam->pdev)) { + DBG("failed to claim the port\n"); + return -EBUSY; + } + parport_write_econtrol(cam->port, PARPORT_MODE_PCECR); + parport_frob_econtrol(cam->port, + ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn, + ECR_serviceIntr|ECR_nErrIntrEn); +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + cam->dma_task.routine=dma_handler; + cam->dma_task.data=cam; +#endif + cam->stream_irq=0; + cpia_parport_disable_irq(cam->port); + } + + ++cam->open_count; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + return 0; +} + +/**************************************************************************** + * + * cpia_pp_registerCallback + * + ***************************************************************************/ +static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) +{ + struct pp_cam_entry *cam = privdata; + int retval = 0; + + if(cam->port->irq != PARPORT_IRQ_NONE) { + cam->cb_task.routine = cb; + cam->cb_task.data = cbdata; + } else { + retval = -1; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_close + * + ***************************************************************************/ +static int cpia_pp_close(void *privdata) +{ + struct pp_cam_entry *cam = privdata; +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + unsigned int flags; +#endif +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + if (--cam->open_count == 0) { + if (cam->port->irq != PARPORT_IRQ_NONE) { + cpia_parport_disable_irq(cam->port); + } + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + save_flags(flags); + cli(); + if (cam->dma_on) { + cam->dma_on=0; + interruptible_sleep_on(&cam->wq_dma); + } + restore_flags(flags); +#endif + if (waitqueue_active(&cam->wq_stream)) { /* FIXME */ + wake_up(&cam->wq_stream); + } + + parport_release(cam->pdev); + } + return 0; +} + +/**************************************************************************** + * + * cpia_pp_register + * + ***************************************************************************/ +static int cpia_pp_register(int nr, struct parport *port) +{ + struct pardevice *pdev = NULL; + int camnr; + + if (!(port->modes & PARPORT_MODE_PCECP)) { + LOG("port is not ECP capable\n"); + return -ENXIO; + } + + if((camnr = cpia_register_camera(&cpia_pp_ops)) < 0) { + LOG("failed to cpia_register_camera\n"); + return -ENXIO; + } + camera[nr] = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (camera[nr] == NULL) { + LOG("failed to allocate camera structure\n"); + cpia_unregister_camera(camnr); + return -ENOMEM; + } + memset(camera[nr],0,sizeof(struct pp_cam_entry)); + + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + cpia_pp_irq_handler, 0, camera[nr]); + + if (!pdev) { + LOG("failed to parport_register_device\n"); + cpia_unregister_camera(camnr); + kfree(camera[nr]); + return -ENXIO; + } + + camera[nr]->pdev = pdev; + camera[nr]->port = port; + camera[nr]->state = CPIA_FORWARD; + camera[nr]->camstate = CPIA_PHASE_idle; + camera[nr]->camnr = camnr; + init_waitqueue(&camera[nr]->wq_stream); + + camera[nr]->streaming = 0; + camera[nr]->stream_irq = 0; + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if (pdev->port->irq != PARPORT_IRQ_NONE && + pdev->port->dma != PARPORT_DMA_NONE) { + if(request_dma(pdev->port->dma, "cpia_pp")) { + LOG("failed to register dma %d\n", pdev->port->dma); + parport_unregister_device(pdev); + cpia_unregister_camera(camnr); + kfree(camera[nr]); + return -ENXIO; + } + camera[nr]->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA); + if(camera[nr]->dma_buf == NULL) { + free_dma(camera[nr]->pdev->port->dma); + LOG("failed to allocate dma buffer, using FIFO mode\n"); + } else { + init_waitqueue(&camera[nr]->wq_dma); + printk(KERN_INFO " using DMA mode (irq %d, DMA %d)\n", pdev->port->irq, pdev->port->dma); + } + memset(camera[nr]->dma_buf, 0, DMA_BUFFER_SIZE); + } else { + printk(KERN_INFO " using PIO mode\n"); + } +#endif + + return 0; +} + +int cpia_pp_init(void) +{ + struct parport *port; + int i, count = 0; + + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); + + switch (parport_nr[0]) { + case PPCPIA_PARPORT_OFF: + printk(" disabled\n"); + return 0; + + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + for (port = parport_enumerate(); port; port = port->next) { + +#if defined(CONFIG_PNP_PARPORT) || \ + (defined(MODULE) && defined(CONFIG_PNP_PARPORT_MODULE)) + if (port->probe_info.class != PARPORT_CLASS_MEDIA || + port->probe_info.cmdset == NULL || + strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){ + continue; + } +#endif + + if (cpia_pp_register(count, port) == 0 && + ++count == PP_MAXCAMS) { + break; + } + } + break; + + default: + for (i = 0; i < PP_MAXCAMS; i++) { + for (port = parport_enumerate(); port; + port = port->next) { + if (port->number == parport_nr[i]) { + if (!cpia_pp_register(i, port)) { + count++; + } + break; + } + } + } + break; + } + + printk(" %d camera(s) found\n", count); + if (count == 0) { + return -ENODEV; + } + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + if (parport[0]) { + /* The user gave some parameters. Let's see what they were. */ + if (!strncmp(parport[0], "auto", 4)) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else { + int n; + for (n = 0; n < PP_MAXCAMS && parport[n]; n++) { + if (!strncmp(parport[n], "none", 4)) { + parport_nr[n] = PPCPIA_PARPORT_NONE; + } else { + char *ep; + unsigned long r = simple_strtoul(parport[n], &ep, 0); + if (ep != parport[n]) { + parport_nr[n] = r; + } else { + LOG("bad port specifier `%s'\n", parport[n]); + return -ENODEV; + } + } + } + } + } +#if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE) + request_module("parport_probe"); +#endif + return cpia_pp_init(); +} + +void cleanup_module(void) +{ + int i; + for (i = 0; camera[i] != NULL && i < PP_MAXCAMS; i++) { + while(camera[i]->open_count > 0) { + LOG("You forgot to close camera %d, will do it for you\n", camera[i]->camnr); + cpia_pp_close(camera[i]); + } + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if (camera[i]->dma_buf) { + free_dma(camera[i]->pdev->port->dma); + kfree(camera[i]->dma_buf); + } +#endif + + cpia_unregister_camera(camera[i]->camnr); + parport_unregister_device(camera[i]->pdev); + kfree(camera[i]); + } + return; +} + +#else /* !MODULE */ + +__initfunc(void cpia_pp_setup(char *str, int *ints)) +{ + if (!str) { + if (ints[0] == 0 || ints[1] == 0) { + /* disable driver on "cpia_pp=" or "cpia_pp=0" */ + parport_nr[0] = PPCPIA_PARPORT_OFF; + } + } else if (!strncmp(str, "parport", 7)) { + int n = simple_strtoul(str + 7, NULL, 10); + if (parport_ptr < PP_MAXCAMS) { + parport_nr[parport_ptr++] = n; + } else { + LOG("too many ports, %s ignored.\n", str); + } + } else if (!strcmp(str, "auto")) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else if (!strcmp(str, "none")) { + parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; + } +} + +#endif /* !MODULE */ diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index b689bb7a58bc..afeb8e09e5d9 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -3,8 +3,8 @@ /* * istallion.c -- stallion intelligent multiport serial driver. * - * Copyright (C) 1996-1999 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1996-2000 Stallion Technologies (support@stallion.oz.au) + * Copyright (C) 1994-1996 Greg Ungerer. * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. @@ -167,7 +167,7 @@ static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); */ static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; static char *stli_drvname = "istallion"; -static char *stli_drvversion = "5.5.1"; +static char *stli_drvversion = "5.6.0"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -383,7 +383,7 @@ static stlibrdtype_t stli_brdstr[] = { /* * Define the module agruments. */ -MODULE_AUTHOR("Greg Ungerer"); +MODULE_AUTHOR("Stallion Technologies (support@stallion.oz.au)"); MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver"); MODULE_PARM(board0, "1-3s"); @@ -711,6 +711,8 @@ static int stli_getbrdstats(combrd_t *bp); static int stli_getportstats(stliport_t *portp, comstats_t *cp); static int stli_portcmdstats(stliport_t *portp); static int stli_clrportstats(stliport_t *portp, comstats_t *cp); +static int stli_geticounters(stliport_t *portp, + struct serial_icounter_struct *cp); static int stli_getportstruct(unsigned long arg); static int stli_getbrdstruct(unsigned long arg); static void *stli_memalloc(int len); @@ -2170,6 +2172,13 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm sizeof(comstats_t))) == 0) rc = stli_clrportstats(portp, (comstats_t *) arg); break; + case TIOCGICOUNT: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_icounter_struct *))) == 0) + rc = stli_geticounters(portp, + (struct serial_icounter_struct *) arg); + break; + case TIOCSERCONFIG: case TIOCSERGWILD: case TIOCSERSWILD: @@ -3375,6 +3384,9 @@ static inline int stli_initports(stlibrd_t *brdp) portp->closing_wait = 30 * HZ; portp->tqhangup.routine = stli_dohangup; portp->tqhangup.data = portp; + portp->open_wait = 0; + portp->close_wait = 0; + portp->raw_wait = 0; portp->normaltermios = stli_deftermios; portp->callouttermios = stli_deftermios; panelport++; @@ -5140,6 +5152,33 @@ static int stli_clrportstats(stliport_t *portp, comstats_t *cp) return(0); } +static int stli_geticounters(stliport_t *portp, + struct serial_icounter_struct *icp) +{ + struct serial_icounter_struct icount; + comstats_t *s = &stli_comstats; + int rc; + + if ((rc = stli_portcmdstats(portp)) < 0) + return(rc); + + memset(&icount, 0, sizeof(icount)); + + /* + * we only support a subset of the icounters - the others are handled + * by the uart and we don't get interrupted for them + */ + icount.dcd = s->modem; + icount.rx = s->rxtotal; + icount.tx = s->txtotal; + icount.frame = s->rxframing; + icount.overrun = s->rxoverrun; + icount.parity = s->rxparity; + icount.brk = s->rxbreaks; + + return (copy_to_user(icp, &icount, sizeof(icount))); +} + /*****************************************************************************/ /* diff --git a/drivers/char/joystick/joy-creative.c b/drivers/char/joystick/joy-creative.c index 7e97a677dcbf..8faec5f82e90 100644 --- a/drivers/char/joystick/joy-creative.c +++ b/drivers/char/joystick/joy-creative.c @@ -66,7 +66,7 @@ static int js_cr_read_packet(int io, unsigned int *data) int r[2], t[2], p[2]; int i, j, ret; - for (i = 0; i < 2; i++); { + for (i = 0; i < 2; i++) { r[i] = buf[i] = 0; p[i] = t[i] = JS_CR_MAX_STROBE; p[i] += JS_CR_MAX_STROBE; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 64bacb0bd662..a98afcdd11ac 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -3,8 +3,8 @@ /* * stallion.c -- stallion multiport serial driver. * - * Copyright (C) 1996-1999 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1996-2000 Stallion Technologies (support@stallion.oz.au) + * Copyright (C) 1994-1996 Greg Ungerer. * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #endif + /*****************************************************************************/ /* @@ -127,6 +129,7 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); #define STL_TXBUFLOW 512 #define STL_TXBUFSIZE 4096 +#define STL_IRQ_DEVID (&stl_brds[0]) /*****************************************************************************/ /* @@ -135,7 +138,7 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); */ static char *stl_drvtitle = "Stallion Multiport Serial Driver"; static char *stl_drvname = "stallion"; -static char *stl_drvversion = "5.5.1"; +static char *stl_drvversion = "5.6.0"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -313,7 +316,7 @@ static stlbrdtype_t stl_brdstr[] = { /* * Define the module agruments. */ -MODULE_AUTHOR("Greg Ungerer"); +MODULE_AUTHOR("Stallion Technologies (support@stallion.oz.au)"); MODULE_DESCRIPTION("Stallion Multiport Serial Driver"); MODULE_PARM(board0, "1-4s"); @@ -345,6 +348,7 @@ MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]"); #define ID_BRD4 0x10 #define ID_BRD8 0x20 #define ID_BRD16 0x30 +#define ID_BRD4_422 0x40 /* 4 port EIO supporting RS422*/ #define EIO_INTRPEND 0x08 #define EIO_INTEDGE 0x00 @@ -523,6 +527,8 @@ static int stl_setserial(stlport_t *portp, struct serial_struct *sp); static int stl_getbrdstats(combrd_t *bp); static int stl_getportstats(stlport_t *portp, comstats_t *cp); static int stl_clrportstats(stlport_t *portp, comstats_t *cp); +static int stl_geticounters(stlport_t *portp, + struct serial_icounter_struct *cp); static int stl_getportstruct(unsigned long arg); static int stl_getbrdstruct(unsigned long arg); static int stl_waitcarrier(stlport_t *portp, struct file *filp); @@ -853,7 +859,7 @@ void cleanup_module() } for (i = 0; (i < stl_numintrs); i++) - free_irq(stl_gotintrs[i], NULL); + free_irq(stl_gotintrs[i], STL_IRQ_DEVID); restore_flags(flags); } @@ -1599,7 +1605,8 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd return(-ENODEV); if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS) && + (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return(-EIO); } @@ -1671,6 +1678,13 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd sizeof(comstats_t))) == 0) rc = stl_clrportstats(portp, (comstats_t *) arg); break; + case TIOCGICOUNT: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_icounter_struct *))) == 0) + rc = stl_geticounters(portp, + (struct serial_icounter_struct *) arg); + break; + case TIOCSERCONFIG: case TIOCSERGWILD: case TIOCSERSWILD: @@ -2294,7 +2308,8 @@ __initfunc(static int stl_mapirq(int irq, char *name)) break; } if (i >= stl_numintrs) { - if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) { + if (request_irq(irq, stl_intr, SA_SHIRQ, name, + STL_IRQ_DEVID) != 0) { printk("STALLION: failed to register interrupt " "routine for %s irq=%d\n", name, irq); rc = -ENODEV; @@ -2348,6 +2363,8 @@ __initfunc(static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)) portp->callouttermios = stl_deftermios; portp->tqueue.routine = stl_offintr; portp->tqueue.data = portp; + portp->open_wait = 0; + portp->close_wait = 0; portp->stats.brd = portp->brdnr; portp->stats.panel = portp->panelnr; portp->stats.port = portp->portnr; @@ -2445,6 +2462,9 @@ static inline int stl_initeio(stlbrd_t *brdp) case ID_BRD16: brdp->nrports = 16; break; + case ID_BRD4_422: + brdp->nrports = 4; + break; default: return(-ENODEV); } @@ -2780,7 +2800,7 @@ static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp) #if DEBUG printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype, - dev->bus->number, dev->devfn); + devp->bus->number, devp->devfn); #endif if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL) @@ -3084,6 +3104,28 @@ static int stl_clrportstats(stlport_t *portp, comstats_t *cp) return(0); } +static int stl_geticounters(stlport_t *portp, + struct serial_icounter_struct *icp) +{ + struct serial_icounter_struct icount; + comstats_t *s = &portp->stats; + + memset(&icount, 0, sizeof(icount)); + /* + * we only support a subset of the icounters - the others are handled + * by the uart and we don't get interrupted for them + */ + icount.dcd = s->modem; + icount.rx = s->rxtotal; + icount.tx = s->txtotal; + icount.frame = s->rxframing; + icount.overrun = s->rxoverrun; + icount.parity = s->rxparity; + icount.brk = s->rxbreaks; + + return (copy_to_user(icp, &icount, sizeof(icount))); +} + /*****************************************************************************/ /* @@ -4209,6 +4251,7 @@ static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr) if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + len = MIN(len, sizeof(stl_unwanted)); outb((RDSR + portp->uartaddr), ioaddr); insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; @@ -5175,6 +5218,7 @@ static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack) if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + len = MIN(len, sizeof(stl_unwanted)); outb(GRXFIFO, (ioaddr + XP_ADDR)); insb((ioaddr + XP_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 968a60f27f87..86f804f6b441 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -54,6 +54,9 @@ extern int init_colour_qcams(struct video_init *); #ifdef CONFIG_VIDEO_BWQCAM extern int init_bw_qcams(struct video_init *); #endif +#ifdef CONFIG_VIDEO_CPIA +extern int cpia_init(struct video_init *); +#endif #ifdef CONFIG_VIDEO_PLANB extern int init_planbs(struct video_init *); #endif @@ -104,6 +107,9 @@ static struct video_init video_init_list[]={ #endif #ifdef CONFIG_VIDEO_BWQCAM {"bw-qcam", init_bw_qcams}, +#endif +#ifdef CONFIG_VIDEO_CPIA + {"cpia", cpia_init}, #endif #ifdef CONFIG_VIDEO_PMS {"PMS", init_pms_cards}, diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c index 8395f9ae34b7..1a6d25252c0f 100644 --- a/drivers/net/smc-mca.c +++ b/drivers/net/smc-mca.c @@ -1,4 +1,4 @@ -/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ +/* smc-mca.c: A SMC Ultra ethernet driver for linux. */ /* Most of this driver, except for ultramca_probe is nearly verbatim from smc-ultra.c by Donald Becker. The rest is @@ -8,7 +8,7 @@ This driver uses the cards in the 8390-compatible, shared memory mode. Most of the run-time complexity is handled by the generic code in - 8390.c. The code in this file is responsible for + 8390.c. This driver enables the shared memory only when doing the actual data transfers to avoid a bug in early version of the card that corrupted @@ -20,10 +20,21 @@ Changelog: - Paul Gortmaker : multiple card support for module users. - David Weis : Micro Channel-ized it. - David Monro : More MCA support 19/01/2000 davidm@amberdata.demon.co.uk - + Paul Gortmaker : multiple card support for module users. + David Weis : Micro Channel-ized it. + Tom Sightler : Added support for IBM PS/2 Ethernet Adapter/A + Christopher Turcksin : Changed MCA-probe so that multiple adapters are + found correctly (Jul 16, 1997) + Chris Beauregard : Tried to merge the two changes above (Dec 15, 1997) + Tom Sightler : Fixed minor detection bug caused by above merge + Tom Sightler : Added support for three more Western Digital + MCA-adapters + Tom Sightler : Added support for 2.2.x mca_find_unused_adapter + Hartmut Schmidt : - Modified parameter detection to handle each + card differently depending on a switch-list + - 'card_ver' removed from the adapter list + - Some minor bug fixes + David Monro : Backport from 2.3 to 2.2, other minor fixes. */ @@ -67,64 +78,183 @@ static int ultramca_close_card(struct device *dev); #define ULTRA_IO_EXTENT 32 #define EN0_ERWCNT 0x08 /* Early receive warning count. */ +#define _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A 0 +#define _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A 1 +#define _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A 2 +#define _6fc1_WD_Starcard_PLUS_A_WD8003ST_A 3 +#define _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A 4 +#define _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A 5 +#define _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A 6 +#define _efe5_IBM_PS2_Adapter_A_for_Ethernet 7 + +struct smc_mca_adapters_t { + unsigned int id; + char *name; +}; + +const struct smc_mca_adapters_t smc_mca_adapters[] = { + { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" }, + { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" }, + { 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" }, + { 0x6fc1, "WD Starcard PLUS/A (WD8003ST/A)" }, + { 0x6fc2, "WD Ethercard PLUS 10T/A (WD8003W/A)" }, + { 0xefd4, "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)" }, + { 0xefd5, "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)" }, + { 0xefe5, "IBM PS/2 Adapter/A for Ethernet" }, + { 0x0000, NULL } +}; -__initfunc(int ultramca_probe(struct device *dev)) +int __init ultramca_probe(struct device *dev) { unsigned short ioaddr; unsigned char reg4, num_pages; - char slot; - unsigned char pos2, pos3, pos4, pos5; - int i; - char *adapter_name; - struct ei_device *ei_local; - - /* Look for five types of adapters - thanks to - * Zunfire@mail.bip.net for the IDs. */ - if( (slot=mca_find_adapter(0x61c8,0)) != MCA_NOTFOUND) - { - adapter_name = "SMC/WD EtherCard PLUS Elite/A (8013EP/A)"; + char slot = -1; + unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff; + int i, j; + int adapter_found = 0; + int adapter = 0; + int tbase = 0; + int tirq = 0; + int base_addr = dev ? dev->base_addr : 0; + int irq = dev ? dev->irq : 0; + + if (!MCA_bus) { + return ENODEV; } - else if( (slot=mca_find_adapter(0x61c9,0)) != MCA_NOTFOUND) - { - adapter_name = "SMC/WD EtherCard PLUS Elite 10T/A (8013WP/A)"; + + /* This is a hack, snd should go away when the "0xffe0 means + * don't do ISA probe" code goes away. We aren't ISA, and 0xffe0 + * is not a possible port for us. + */ + if (base_addr == 0xffe0) { + base_addr = 0; } - else if( (slot=mca_find_adapter(0xefd4,0)) != MCA_NOTFOUND) - { - adapter_name = "IBM Adapter/A for Ethernet UTP/AUI (8013WP/A)"; + if (base_addr || irq) { + printk(KERN_INFO "Probing for SMC MCA adapter"); + if (base_addr) { + printk(" at I/O address 0x%04x%c", + base_addr, irq ? ' ' : '\n'); + } + if (irq) { + printk("using irq %d\n", irq); + } } - else if( (slot=mca_find_adapter(0xefd5,0)) != MCA_NOTFOUND) - { - adapter_name = "IBM Adapter/A for Ethernet BNC/AUI (8013EP/A)"; + + /* proper multicard detection by ZP Gu (zpg@castle.net) */ + + for (j = 0; (smc_mca_adapters[j].name != NULL) && !adapter_found; j++) { + slot = mca_find_unused_adapter(smc_mca_adapters[j].id, 0); + + while((slot != MCA_NOTFOUND) && !adapter_found) { + tirq = 0; + tbase = 0; + + /* If we're trying to match a specificied irq or + * io address, we'll reject the adapter + * found unless it's the one we're looking for + */ + + pos2 = mca_read_stored_pos(slot, 2); /* io_addr */ + pos3 = mca_read_stored_pos(slot, 3); /* shared mem */ + pos4 = mca_read_stored_pos(slot, 4); /* ROM bios addr + * range */ + pos5 = mca_read_stored_pos(slot, 5); /* irq, media + * and RIPL */ + + /* Test the following conditions: + * - If an irq parameter is supplied, compare it + * with the irq of the adapter we found + * - If a base_addr paramater is given, compare it + * with the base_addr of the adapter we found + * - Check that the irq and the base_addr of the + * adapter we found is not already in use by + * this driver + */ + switch (j) { /* j = card-idx (card array above) [hs] */ + case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A: + case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A: + case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A: + case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A: + { + tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr; + tirq = irq_table[(pos5 & 0xc) >> 2].new_irq; + break; + } + case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A: + case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A: + case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A: + case _efe5_IBM_PS2_Adapter_A_for_Ethernet: + { + tbase = ((pos2 & 0x0fe) * 0x10); + tirq = irq_table[(pos5 & 3)].old_irq; + break; + } + } + + if(!tirq || !tbase || (irq && irq != tirq) || (base_addr && tbase != base_addr)) { + slot = mca_find_unused_adapter(smc_mca_adapters[j].id, slot + 1); + } else { + adapter_found = 1; + adapter = j; + } + } } - else if( (slot=mca_find_adapter(0xefe5,0)) != MCA_NOTFOUND) - { - adapter_name = "IBM Adapter/A for Ethernet"; + /* Correct j now, before we forget */ + j--; + + if(!adapter_found) { + return ((base_addr || irq) ? ENXIO : ENODEV); } - else - return -ENODEV; - mca_set_adapter_name(slot, adapter_name); + /* Adapter found. */ - pos2 = mca_read_stored_pos(slot, 2); /* IO range */ - pos3 = mca_read_stored_pos(slot, 3); /* shared mem */ - pos4 = mca_read_stored_pos(slot, 4); /* bios base */ - pos5 = mca_read_stored_pos(slot, 5); /* irq and media */ + printk(KERN_INFO "%s: %s found in slot %d\n", + dev->name, smc_mca_adapters[adapter].name, slot + 1); - dev->base_addr = ioaddr = addr_table[pos2 >> 4].base_addr; - dev->irq = irq_table[(pos5 & ~IRQ_MASK) >> 2].irq; + mca_set_adapter_name(slot, smc_mca_adapters[adapter].name); + + dev->base_addr = ioaddr = tbase; + dev->irq = tirq; dev->mem_start = 0; - num_pages = 40; - for (i = 0; i < 15; i++) - { - if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) + num_pages = 40; + + switch (j) { /* 'j' = card-# in const array above [hs] */ + case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A: + case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A: + { + for (i = 0; i < 16; i++) { /* taking 16 counts + * up to 15 [hs] */ + if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) { + dev->mem_start = mem_table[i].mem_start; + num_pages = mem_table[i].num_pages; + } + } + break; + } + case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A: + case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A: + case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A: + case _efe5_IBM_PS2_Adapter_A_for_Ethernet: { - dev->mem_start = mem_table[i].mem_start; - num_pages = mem_table[i].num_pages; + dev->mem_start = ((pos3 & 0xfc) * 0x1000); + num_pages = 0x40; + break; + } + case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A: + case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A: + { + /* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates + * the index of the 0x2000 step. + * beware different number of pages [hs] + */ + dev->mem_start = 0xc0000 + (0x2000 * (pos3 & 0xf)); + num_pages = 0x20 + (2 * (pos3 & 0x10)); + break; } } - if (dev->mem_start == 0) /* sanity check, shouldn't happen */ + if (dev->mem_start == 0) /* sanity check, shouldn't happen */ return -ENODEV; reg4 = inb(ioaddr + 4) & 0x7f; @@ -133,62 +263,46 @@ __initfunc(int ultramca_probe(struct device *dev)) if (load_8390_module("wd.c")) return -ENOSYS; - printk("%s: %s at %#3x,", dev->name, adapter_name, ioaddr); + printk(KERN_INFO "%s: Parameters: %#3x,", dev->name, ioaddr); for (i = 0; i < 6; i++) printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i)); - /* - * Switch from the station address to the alternate register set and - * read the useful registers there. + /* Switch from the station address to the alternate register set + * and read the useful registers there. */ outb(0x80 | reg4, ioaddr + 4); - /* - * Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. + /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); - /* - * Switch back to the station address register set so that the MS-DOS driver - * can find the card after a warm boot. + /* Switch back to the station address register set so that + * the MS-DOS driver can find the card after a warm boot. */ outb(reg4, ioaddr + 4); - /* - * Allocate dev->priv and fill in 8390 specific dev fields. + /* Allocate dev->priv and fill in 8390 specific dev fields. */ - if (ethdev_init(dev)) - { + if (ethdev_init(dev)) { printk (", no memory for dev->priv.\n"); return -ENOMEM; } - /* - * OK, we are certain this is going to work. Setup the device. + /* OK, we are certain this is going to work. Setup the device. */ - /* - * We store the MCA slot number in the driver-private part of - * the structure 8390.c attaches to dev->priv so we can release - * it later if needed. - */ - - ei_local = (struct ei_device *) dev->priv; - ei_local->priv = slot; mca_mark_as_used(slot); - request_region(ioaddr, ULTRA_IO_EXTENT, "smc-mca"); - /* - * The 8390 isn't at the base address, so fake the offset + /* The 8390 isn't at the base address, so fake the offset */ - dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; + dev->base_addr = ioaddr + ULTRA_NIC_OFFSET; ei_status.name = "SMC Ultra MCA"; ei_status.word16 = 1; @@ -196,16 +310,20 @@ __initfunc(int ultramca_probe(struct device *dev)) ei_status.rx_start_page = START_PG + TX_PAGES; ei_status.stop_page = num_pages; - dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->rmem_start = dev->mem_start + TX_PAGES * 256; dev->mem_end = dev->rmem_end = - dev->mem_start + (ei_status.stop_page - START_PG)*256; + dev->mem_start + (ei_status.stop_page - START_PG) * 256; - printk(", IRQ %d memory %#lx-%#lx.\n", dev->irq, dev->mem_start, dev->mem_end-1); + printk(", IRQ %d memory %#lx-%#lx.\n", + dev->irq, dev->mem_start, dev->mem_end - 1); ei_status.reset_8390 = &ultramca_reset_8390; ei_status.block_input = &ultramca_block_input; ei_status.block_output = &ultramca_block_output; ei_status.get_8390_hdr = &ultramca_get_8390_hdr; + + ei_status.priv = slot; + dev->open = &ultramca_open; dev->stop = &ultramca_close_card; NS8390_init(dev, 0); @@ -218,21 +336,19 @@ static int ultramca_open(struct device *dev) int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) - return -EAGAIN; + return -EAGAIN; outb(ULTRA_MEMENB, ioaddr); /* Enable memory */ outb(0x80, ioaddr + 5); /* ??? */ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */ outb(0x04, ioaddr + 5); /* ??? */ - /* - * Set the early receive warning level in window 0 high enough not - * to receive ERW interrupts. + /* Set the early receive warning level in window 0 high enough not + * to receive ERW interrupts. */ - /* - * outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); - * outb(0xff, dev->base_addr + EN0_ERWCNT); + /* outb_p(E8390_NODMA + E8390_PAGE0, dev->base_addr); + * outb(0xff, dev->base_addr + EN0_ERWCNT); */ ei_open(dev); @@ -245,7 +361,8 @@ static void ultramca_reset_8390(struct device *dev) int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ outb(ULTRA_RESET, ioaddr); - if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies); + if (ei_debug > 1) + printk("resetting Ultra, t=%ld...", jiffies); ei_status.txing = 0; outb(0x80, ioaddr + 5); /* ??? */ @@ -257,12 +374,13 @@ static void ultramca_reset_8390(struct device *dev) } /* Grab the 8390 specific header. Similar to the block_input routine, but - we don't need to be concerned with ring wrap as the header will be at - the start of a page, so we optimize accordingly. */ + * we don't need to be concerned with ring wrap as the header will be at + * the start of a page, so we optimize accordingly. + */ static void ultramca_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { - unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8); + unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG) << 8); #ifdef notdef /* Officially this is what we are doing, but the readl() is faster */ @@ -273,22 +391,20 @@ static void ultramca_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, } /* Block input and output are easy on shared memory ethercards, the only - complication is when the ring buffer wraps. */ + * complication is when the ring buffer wraps. + */ static void ultramca_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) { - unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8); + unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG << 8); - if (xfer_start + count > dev->rmem_end) - { - /* We must wrap the input move. */ + if (xfer_start + count > dev->rmem_end) { + /* We must wrap the input move. */ int semi_count = dev->rmem_end - xfer_start; memcpy_fromio(skb->data, xfer_start, semi_count); count -= semi_count; memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); - } - else - { + } else { /* Packet is in one chunk -- we can copy + cksum. */ eth_io_copy_and_sum(skb, xfer_start, count, 0); } @@ -298,7 +414,7 @@ static void ultramca_block_input(struct device *dev, int count, struct sk_buff * static void ultramca_block_output(struct device *dev, int count, const unsigned char *buf, int start_page) { - unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8); + unsigned long shmem = dev->mem_start + ((start_page - START_PG) << 8); memcpy_toio(shmem, buf, count); } @@ -318,7 +434,8 @@ static int ultramca_close_card(struct device *dev) NS8390_init(dev, 0); /* We should someday disable shared memory and change to 8-bit mode - "just in case"... */ + * "just in case"... + */ MOD_DEC_USE_COUNT; @@ -329,8 +446,9 @@ static int ultramca_close_card(struct device *dev) #ifdef MODULE #undef MODULE /* don't want to bother now! */ -#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */ -#define NAMELEN 8 /* # of chars for storing dev->name */ +#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ + static char namelist[NAMELEN * MAX_ULTRAMCA_CARDS] = { 0, }; static struct device dev_ultra[MAX_ULTRAMCA_CARDS] = @@ -349,60 +467,42 @@ static int irq[MAX_ULTRAMCA_CARDS] = { 0, }; MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i"); -/* This is set up so that only a single autoprobe takes place per call. -ISA device autoprobes on a running machine are not recommended. */ - int init_module(void) { int this_dev, found = 0; - for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) - { + for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) { struct device *dev = &dev_ultra[this_dev]; - dev->name = namelist+(NAMELEN*this_dev); + dev->name = namelist + (NAMELEN * this_dev); dev->irq = irq[this_dev]; dev->base_addr = io[this_dev]; dev->init = ultramca_probe; - if (io[this_dev] == 0) - { - if (this_dev != 0) - break; /* only autoprobe 1st one */ - printk(KERN_NOTICE "smc-mca.c: Presently autoprobing (not recommended) for a single card.\n"); - } - if (register_netdev(dev) != 0) - { - printk(KERN_WARNING "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); - if (found != 0) { /* Got at least one. */ - lock_8390_module(); - return 0; - } - return -ENXIO; + + if (register_netdev(dev) != 0) { + break; } found++; } - lock_8390_module(); - return 0; + if (found != 0) { /* Got at least one. */ + lock_8390_module(); + return 0; + } + printk(KERN_NOTICE "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); + return -ENXIO; } void cleanup_module(void) { int this_dev; - for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) - { + for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) { struct device *dev = &dev_ultra[this_dev]; - if (dev->priv != NULL) - { + if (dev->priv != NULL) { void *priv = dev->priv; - /* Need this to find the mca slot number we - * stored in it earlier */ - struct ei_device *ei_local = (struct ei_device *) dev->priv; /* NB: ultra_close_card() does free_irq */ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; + mca_mark_as_unused(ei_status.priv); release_region(ioaddr, ULTRA_IO_EXTENT); - /* Tell the mca subsystem we have finished with - * the card */ - mca_mark_as_unused(ei_local->priv); unregister_netdev(dev); kfree(priv); } @@ -411,7 +511,6 @@ void cleanup_module(void) } #endif /* MODULE */ - /* * Local variables: * compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-mca.c" diff --git a/drivers/net/smc-mca.h b/drivers/net/smc-mca.h index 80ea4f03fda8..f578843ce939 100644 --- a/drivers/net/smc-mca.h +++ b/drivers/net/smc-mca.h @@ -1,10 +1,7 @@ -/* - djweis weisd3458@uni.edu - most of this file was taken from ps2esdi.h -*/ - -/* Fixed 19/01/2000 davidm@amberdata.demon.co.uk - num_pages should have - * been in hex */ +/* + * djweis weisd3458@uni.edu + * most of this file was taken from ps2esdi.h + */ struct { unsigned int base_addr; @@ -28,6 +25,7 @@ struct { }; #define MEM_MASK 64 + struct { unsigned char mem_index; unsigned long mem_start; @@ -53,13 +51,11 @@ struct { #define IRQ_MASK 243 struct { - unsigned char irq; + unsigned char new_irq; + unsigned char old_irq; } irq_table[] = { - { 3 }, - { 4 }, - { 10 }, - { 14 } + { 3, 3 }, + { 4, 4 }, + { 10, 10 }, + { 14, 15 } }; - - - diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c new file mode 100644 index 000000000000..a548e2ee6581 --- /dev/null +++ b/drivers/scsi/3w-xxxx.c @@ -0,0 +1,2225 @@ +/* + 3w-xxxx.c -- 3ware Storage Controller device driver for Linux. + + Written By: Adam Radford + Copyright (C) 1999-2000 3ware Inc. + + Kernel compatablity By: Andre Hedrick + Non-Copyright (C) 2000 Andre Hedrick + + Further tiny build fixes and trivial hoovering Alan Cox + + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + 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 + + Bugs/Comments/Suggestions should be mailed to: + linux@3ware.com + + For more information, goto: + http://www.3ware.com +*/ + +#include + +MODULE_AUTHOR ("3ware Inc."); +MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define __3W_C /* let 3w-xxxx.h know it is use */ + +#include "sd.h" +#include "scsi.h" +#include "hosts.h" + +#include "3w-xxxx.h" + +static int tw_copy_info(TW_Info *info, char *fmt, ...); +static void tw_copy_mem_info(TW_Info *info, char *data, int len); +static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs); + +struct proc_dir_entry tw_scsi_proc_entry = { + PROC_SCSI_3W_XXXX, + 7, "3w-xxxx", + S_IFDIR|S_IRUGO|S_IXUGO, 2 +}; + +/* Globals */ +char *tw_driver_version="0.4.001"; +TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; +int tw_device_extension_count = 0; + +/* Functions */ + +/* This function will complete an aen request from the isr */ +int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id) +{ + TW_Param *param; + unsigned short aen, aen_code; + + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + aen = *(unsigned short *)(param->data); + aen_code = (aen & 0x0ff); + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen_code); + /* Now queue the code */ + tw_dev->aen_queue[tw_dev->aen_tail] = aen_code; + if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { + tw_dev->aen_tail = TW_Q_START; + } else { + tw_dev->aen_tail = tw_dev->aen_tail + 1; + } + if (tw_dev->aen_head == tw_dev->aen_tail) { + if (tw_dev->aen_head == TW_Q_LENGTH - 1) { + tw_dev->aen_head = TW_Q_START; + } else { + tw_dev->aen_head = tw_dev->aen_head + 1; + } + } + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + + return 0; +} /* End tw_aen_complete() */ + +/* This function will drain the aen queue after a soft reset */ +int tw_aen_drain_queue(TW_Device_Extension *tw_dev) +{ + TW_Command *command_packet; + TW_Param *param; + int tries = 0; + int request_id = 0; + u32 command_que_value = 0, command_que_addr; + u32 status_reg_value = 0, status_reg_addr; + u32 param_value; + TW_Response_Queue response_queue; + u32 response_que_addr; + unsigned short aen; + unsigned short aen_code; + int finished = 0; + int first_reset = 0; + int queue = 0; + int imax, i; + int found = 0; + + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n"); + + command_que_addr = tw_dev->registers.command_que_addr; + status_reg_addr = tw_dev->registers.status_reg_addr; + response_que_addr = tw_dev->registers.response_que_addr; + + if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d\n", tw_dev->host->host_no); + return 1; + } + + /* Initialize command packet */ + if (tw_dev->command_packet_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n"); + return 1; + } + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.parameter_count = 1; + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n"); + return 1; + } + + /* Now setup the param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + param->table_id = 0x401; /* AEN table */ + param->parameter_id = 2; /* Unit code */ + param->parameter_size_bytes = 2; + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n"); + return 1; + } + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + + imax = TW_POLL_MAX_RETRIES; + + /* Now drain the controller's aen queue */ + do { + /* Post command packet */ + outl(command_que_value, command_que_addr); + + /* Now poll for completion */ + for (i=0;istatus != 0) { + if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) { + /* Bad response */ + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, flags = 0x%x.\n", command_packet->flags); + return 1; + } else { + /* We know this is a 3w-1x00, and doesn't support aen's */ + return 0; + } + } + + /* Now check the aen */ + aen = *(unsigned short *)(param->data); + aen_code = (aen & 0x0ff); + queue = 0; + switch (aen_code) { + case TW_AEN_QUEUE_EMPTY: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n"); + if (first_reset != 1) { + continue; + } else { + finished = 1; + } + break; + case TW_AEN_SOFT_RESET: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n"); + if (first_reset == 0) { + first_reset = 1; + } else { + queue = 1; + } + break; + case TW_AEN_DEGRADED_MIRROR: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n"); + queue = 1; + break; + case TW_AEN_CONTROLLER_ERROR: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n"); + queue = 1; + break; + case TW_AEN_REBUILD_FAIL: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n"); + queue = 1; + break; + case TW_AEN_REBUILD_DONE: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n"); + queue = 1; + break; + case TW_AEN_QUEUE_FULL: + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n"); + queue = 1; + break; + default: + dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code); + queue = 1; + } + + /* Now put the aen on the aen_queue */ + if (queue == 1) { + tw_dev->aen_queue[tw_dev->aen_tail] = aen_code; + if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { + tw_dev->aen_tail = TW_Q_START; + } else { + tw_dev->aen_tail = tw_dev->aen_tail + 1; + } + if (tw_dev->aen_head == tw_dev->aen_tail) { + if (tw_dev->aen_head == TW_Q_LENGTH - 1) { + tw_dev->aen_head = TW_Q_START; + } else { + tw_dev->aen_head = tw_dev->aen_head + 1; + } + } + } + found = 1; + break; + } + } + if (found == 0) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n"); + return 1; + } + tries++; + } while ((tries < TW_MAX_AEN_TRIES) && (finished == 0)); + + if (tries >=TW_MAX_AEN_TRIES) { + printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Aen queue error.\n"); + return 1; + } + + return 0; +} /* End tw_aen_drain_queue() */ + +/* This function will read the aen queue from the isr */ +int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) +{ + TW_Command *command_packet; + TW_Param *param; + u32 command_que_value = 0, command_que_addr; + u32 status_reg_value = 0, status_reg_addr; + u32 param_value = 0; + + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n"); + command_que_addr = tw_dev->registers.command_que_addr; + status_reg_addr = tw_dev->registers.status_reg_addr; + + status_reg_value = inl(status_reg_addr); + if (tw_check_bits(status_reg_value)) { + printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n"); + return 1; + } + if (tw_dev->command_packet_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n"); + return 1; + } + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.parameter_count = 1; + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n"); + return 1; + } + /* Now setup the param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + param->table_id = 0x401; /* AEN table */ + param->parameter_id = 2; /* Unit code */ + param->parameter_size_bytes = 2; + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment physical address.\n"); + return 1; + } + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + + /* Now post the command packet */ + if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) { + dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n"); + tw_dev->srb[request_id] = 0; /* Flag internal command */ + tw_dev->state[request_id] = TW_S_POSTED; + outl(command_que_value, command_que_addr); + } else { + printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n"); + return 1; + } + + return 0; +} /* End tw_aen_read_queue() */ + +/* This function will allocate memory and check if it is 16 d-word aligned */ +int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which) +{ + u32 *virt_addr; + + dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n"); + + if (which == 0) { + /* Allocate command packet memory */ + virt_addr = kmalloc(size, GFP_ATOMIC); + if (virt_addr == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n"); + return 1; + } + if ((u32)virt_addr % TW_ALIGNMENT) { + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n"); + return 1; + } + tw_dev->command_packet_virtual_address[request_id] = virt_addr; + tw_dev->command_packet_physical_address[request_id] = + virt_to_bus(virt_addr); + } else { + /* Allocate generic buffer */ + virt_addr = kmalloc(size, GFP_ATOMIC); + if (virt_addr == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n"); + return 1; + } + if ((u32)virt_addr % TW_ALIGNMENT) { + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n"); + return 1; + } + tw_dev->alignment_virtual_address[request_id] = virt_addr; + tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr); + } + return 0; +} /* End tw_allocate_memory() */ + +/* This function will check the status register for unexpected bits */ +int tw_check_bits(u32 status_reg_value) +{ + if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) { + printk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value); + return 1; + } + if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) { + printk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value); + return 1; + } + + return 0; +} /* End tw_check_bits() */ + +/* This function will report controller error status */ +int tw_check_errors(TW_Device_Extension *tw_dev) +{ + u32 status_reg_addr, status_reg_value; + + status_reg_addr = tw_dev->registers.status_reg_addr; + status_reg_value = inl(status_reg_addr); + + if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) + return 1; + + return 0; +} /* End tw_check_errors() */ + +/* This function will clear the attention interrupt */ +void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev) +{ + u32 control_reg_addr, control_reg_value; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = TW_CONTROL_CLEAR_ATTENTION_INTERRUPT; + outl(control_reg_value, control_reg_addr); +} /* End tw_clear_attention_interrupt() */ + +/* This function will clear the host interrupt */ +void tw_clear_host_interrupt(TW_Device_Extension *tw_dev) +{ + u32 control_reg_addr, control_reg_value; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = TW_CONTROL_CLEAR_HOST_INTERRUPT; + outl(control_reg_value, control_reg_addr); +} /* End tw_clear_host_interrupt() */ + +/* This function is called by tw_scsi_proc_info */ +static int tw_copy_info(TW_Info *info, char *fmt, ...) +{ + va_list args; + char buf[81]; + int len; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); + tw_copy_mem_info(info, buf, len); + return len; +} /* End tw_copy_info() */ + +/* This function is called by tw_scsi_proc_info */ +static void tw_copy_mem_info(TW_Info *info, char *data, int len) +{ + if (info->position + len > info->length) + len = info->length - info->position; + + if (info->position + len < info->offset) { + info->position += len; + return; + } + if (info->position < info->offset) { + data += (info->offset - info->position); + len -= (info->offset - info->position); + } + if (len > 0) { + memcpy(info->buffer + info->position, data, len); + info->position += len; + } +} /* End tw_copy_mem_info() */ + +/* This function will disable interrupts on the controller */ +void tw_disable_interrupts(TW_Device_Extension *tw_dev) +{ + u32 control_reg_value, control_reg_addr; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = TW_CONTROL_DISABLE_INTERRUPTS; + outl(control_reg_value, control_reg_addr); +} /* End tw_disable_interrupts() */ + +/* This function will empty the response que */ +int tw_empty_response_que(TW_Device_Extension *tw_dev) +{ + u32 status_reg_addr, status_reg_value; + u32 response_que_addr, response_que_value; + + status_reg_addr = tw_dev->registers.status_reg_addr; + response_que_addr = tw_dev->registers.response_que_addr; + + status_reg_value = inl(status_reg_addr); + + if (tw_check_bits(status_reg_value)) { + printk(KERN_WARNING "3w-xxxx: tw_empty_response_queue(): Unexpected bits 1.\n"); + return 1; + } + + while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) { + response_que_value = inl(response_que_addr); + status_reg_value = inl(status_reg_addr); + if (tw_check_bits(status_reg_value)) { + printk(KERN_WARNING "3w-xxxx: tw_empty_response_queue(): Unexpected bits 2.\n"); + return 1; + } + } + return 0; +} /* End tw_empty_response_que() */ + +/* This function will enable interrupts on the controller */ +void tw_enable_interrupts(TW_Device_Extension *tw_dev) +{ + u32 control_reg_value, control_reg_addr; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = (TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | + TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | + TW_CONTROL_ENABLE_INTERRUPTS); + outl(control_reg_value, control_reg_addr); +} /* End tw_enable_interrupts() */ + +/* This function will find and initialize all cards */ +int tw_findcards(Scsi_Host_Template *tw_host) +{ + int numcards = 0, tries = 0, error = 0; + struct Scsi_Host *host; + TW_Device_Extension *tw_dev; + TW_Device_Extension *tw_dev2; + struct pci_dev *tw_pci_dev = pci_devices; + u32 status_reg_value; + + dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n"); + while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, TW_DEVICE_ID, tw_pci_dev))) { + /* Prepare temporary device extension */ + tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC); + if (tw_dev == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards); + continue; + } + memset(tw_dev, 0, sizeof(TW_Device_Extension)); + + error = tw_initialize_device_extension(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + /* Calculate the cards register addresses */ + tw_dev->registers.base_addr = tw_pci_dev->base_address[0]; + tw_dev->registers.control_reg_addr = (tw_pci_dev->base_address[0] & ~15); + tw_dev->registers.status_reg_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x4); + tw_dev->registers.command_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x8); + tw_dev->registers.response_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0xC); + /* Save pci_dev struct to device extension */ + tw_dev->tw_pci_dev = tw_pci_dev; + + /* Poll status register for 60 secs for 'Controller Ready' flag */ + if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + /* Disable interrupts on the card */ + tw_disable_interrupts(tw_dev); + + while (tries < TW_MAX_RESET_TRIES) { + /* Do soft reset */ + tw_soft_reset(tw_dev); + + error = tw_aen_drain_queue(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards); + tries++; + continue; + } + + /* Check for controller errors */ + if (tw_check_errors(tw_dev)) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards); + tries++; + continue; + } + + /* Empty the response queue */ + error = tw_empty_response_que(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards); + tries++; + continue; + } + + /* Now the controller is in a good state */ + break; + } + + if (tries >= TW_MAX_RESET_TRIES) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + /* Make sure that io region isn't already taken */ + if (check_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE)) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n", + (tw_dev->tw_pci_dev->base_address[0]), + (tw_dev->tw_pci_dev->base_address[0]) + + TW_IO_ADDRESS_RANGE, numcards); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + /* Reserve the io address space */ + request_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME); + error = tw_initialize_units(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards); + release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + error = tw_initconnection(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards); + release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + /* Calculate max cmds per lun */ + if (tw_dev->num_units > 0) + tw_host->cmd_per_lun = (TW_Q_LENGTH-2)/tw_dev->num_units; + + /* Register the card with the kernel SCSI layer */ + host = scsi_register(tw_host, sizeof(TW_Device_Extension)); + + /* FIXME - check for NULL */ + + status_reg_value = inl(tw_dev->registers.status_reg_addr); + + dprintk(KERN_NOTICE "scsi%d : Found a 3ware Storage Controller at 0x%x, IRQ: %d P-chip: %d.%d\n", host->host_no, + (u32)(tw_pci_dev->base_address[0]), tw_pci_dev->irq, + (status_reg_value & TW_STATUS_MAJOR_VERSION_MASK) >> 28, + (status_reg_value & TW_STATUS_MINOR_VERSION_MASK) >> 24); + + if (host->hostdata) { + tw_dev2 = (TW_Device_Extension *)host->hostdata; + memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension)); + tw_device_extension_list[tw_device_extension_count] = tw_dev2; + numcards++; + tw_device_extension_count = numcards; + tw_dev2->host = host; + } else { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1); + scsi_unregister(host); + release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE); + tw_free_device_extension(tw_dev); + kfree(tw_dev); + continue; + } + + /* Re-enable interrupts on the card */ + tw_enable_interrupts(tw_dev2); + + /* Now setup the interrupt handler */ + error = tw_setup_irq(tw_dev2); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1); + scsi_unregister(host); + release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE); + + tw_free_device_extension(tw_dev); + kfree(tw_dev); + numcards--; + continue; + } + + /* Free the temporary device extension */ + if (tw_dev) + kfree(tw_dev); + } + + if (numcards == 0) + printk(KERN_WARNING "3w-xxxx: tw_findcards(): No cards found.\n"); + + return numcards; +} /* End tw_findcards() */ + +/* This function will free up device extension resources */ +void tw_free_device_extension(TW_Device_Extension *tw_dev) +{ + int i, imax; + imax = TW_Q_LENGTH; + + dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n"); + /* Free command packet and generic buffer memory */ + for (i=0;icommand_packet_virtual_address[i]) + kfree(tw_dev->command_packet_virtual_address[i]); + + if (tw_dev->alignment_virtual_address[i]) + kfree(tw_dev->alignment_virtual_address[i]); + } +} /* End tw_free_device_extension() */ + +/* This function will send an initconnection command to controller */ +int tw_initconnection(TW_Device_Extension *tw_dev) +{ + u32 command_que_addr, command_que_value; + u32 status_reg_addr, status_reg_value; + u32 response_que_addr; + TW_Command *command_packet; + TW_Response_Queue response_queue; + int request_id = 0; + int i = 0; + int imax = 0; + + dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n"); + command_que_addr = tw_dev->registers.command_que_addr; + status_reg_addr = tw_dev->registers.status_reg_addr; + response_que_addr = tw_dev->registers.response_que_addr; + + /* Initialize InitConnection command packet */ + if (tw_dev->command_packet_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet virtual address.\n"); + return 1; + } + + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_INIT_CONNECTION; + command_packet->byte0.sgl_offset = 0x0; + command_packet->size = TW_INIT_COMMAND_PACKET_SIZE; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0x0; + command_packet->byte3.host_id = 0x0; + command_packet->status = 0x0; + command_packet->flags = 0x0; + command_packet->byte6.message_credits = TW_INIT_MESSAGE_CREDITS; + command_packet->byte8.init_connection.response_queue_pointer = 0x0; + command_que_value = tw_dev->command_packet_physical_address[request_id]; + + if (command_que_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n"); + return 1; + } + + /* Send command packet to the board */ + outl(command_que_value, command_que_addr); + + /* Poll for completion */ + imax = TW_POLL_MAX_RETRIES; + for (i=0;istatus != 0) { + /* bad response */ + printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, flags = 0x%x.\n", command_packet->flags); + return 1; + } + break; /* Response was okay, so we exit */ + } + } + return 0; +} /* End tw_initconnection() */ + +/* This function will initialize the fields of a device extension */ +int tw_initialize_device_extension(TW_Device_Extension *tw_dev) +{ + int i, imax; + + dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n"); + imax = TW_Q_LENGTH; + + for (i=0; icommand_packet_virtual_address[i] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad command packet virtual address.\n"); + return 1; + } + memset(tw_dev->command_packet_virtual_address[i], 0, sizeof(TW_Sector)); + + /* Initialize generic buffer */ + tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 1); + if (tw_dev->alignment_virtual_address[i] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad alignment virtual address.\n"); + return 1; + } + memset(tw_dev->alignment_virtual_address[i], 0, sizeof(TW_Sector)); + + tw_dev->free_queue[i] = i; + tw_dev->state[i] = TW_S_INITIAL; + tw_dev->ioctl_size[i] = 0; + tw_dev->aen_queue[i] = 0; + } + + for (i=0;iis_unit_present[i] = 0; + + tw_dev->num_units = 0; + tw_dev->num_aborts = 0; + tw_dev->num_resets = 0; + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_LENGTH - 1; + tw_dev->posted_request_count = 0; + tw_dev->max_posted_request_count = 0; + tw_dev->max_sgl_entries = 0; + tw_dev->sgl_entries = 0; + tw_dev->host = NULL; + tw_dev->pending_head = TW_Q_START; + tw_dev->pending_tail = TW_Q_START; + tw_dev->aen_head = 0; + tw_dev->aen_tail = 0; + tw_dev->sector_count = 0; + tw_dev->max_sector_count = 0; + spin_lock_init(&tw_dev->tw_lock); + return 0; +} /* End tw_initialize_device_extension() */ + +/* This function will get unit info from the controller */ +int tw_initialize_units(TW_Device_Extension *tw_dev) +{ + int found = 0; + unsigned char request_id = 0; + TW_Command *command_packet; + TW_Param *param; + int i, imax, num_units = 0; + u32 status_reg_addr, status_reg_value; + u32 command_que_addr, command_que_value; + u32 response_que_addr; + TW_Response_Queue response_queue; + u32 param_value; + unsigned char *is_unit_present; + + dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units()\n"); + + status_reg_addr = tw_dev->registers.status_reg_addr; + command_que_addr = tw_dev->registers.command_que_addr; + response_que_addr = tw_dev->registers.response_que_addr; + + /* Setup the command packet */ + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet virtual address.\n"); + return 1; + } + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.block_count = 1; + + /* Now setup the param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + param->table_id = 3; /* unit summary table */ + param->parameter_id = 3; /* unitstatus parameter */ + param->parameter_size_bytes = TW_MAX_UNITS; + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment physical address.\n"); + return 1; + } + + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + + /* Post the command packet to the board */ + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet physical address.\n"); + return 1; + } + outl(command_que_value, command_que_addr); + + /* Poll for completion */ + imax = TW_POLL_MAX_RETRIES; + for(i=0; istatus != 0) { + /* bad response */ + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, flags = 0x%x.\n", command_packet->flags); + return 1; + } + found = 1; + break; + } + } + if (found == 0) { + /* response never received */ + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): No response.\n"); + return 1; + } + + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + is_unit_present = (unsigned char *)&(param->data[0]); + + /* Show all units present */ + imax = TW_MAX_UNITS; + for(i=0; iis_unit_present[i] = FALSE; + } else { + dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): Unit %d found.\n", i); + tw_dev->is_unit_present[i] = TRUE; + num_units++; + } + } + tw_dev->num_units = num_units; + + if (num_units == 0) { + printk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): No units found.\n"); + return 1; + } + + return 0; +} /* End tw_initialize_units() */ + +/* This function is the interrupt service routine */ +static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + int request_id; + u32 status_reg_addr, status_reg_value; + u32 response_que_addr; + TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; + TW_Response_Queue response_que; + int error = 0; + int do_response_interrupt=0; + int do_attention_interrupt=0; + int do_host_interrupt=0; + int do_command_interrupt=0; + int flags = 0; + int flags2 = 0; + TW_Command *command_packet; + spin_lock_irqsave(&io_request_lock, flags); + + if (tw_dev->tw_pci_dev->irq == irq) { + spin_lock_irqsave(&tw_dev->tw_lock, flags2); + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt()\n"); + + /* Read the registers */ + status_reg_addr = tw_dev->registers.status_reg_addr; + response_que_addr = tw_dev->registers.response_que_addr; + status_reg_value = inl(status_reg_addr); + + /* Check which interrupt */ + if (status_reg_value & TW_STATUS_HOST_INTERRUPT) + do_host_interrupt=1; + if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) + do_attention_interrupt=1; + if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) + do_command_interrupt=1; + if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) + do_response_interrupt=1; + + /* Handle host interrupt */ + if (do_host_interrupt) { + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n"); + tw_clear_host_interrupt(tw_dev); + } + + /* Handle attention interrupt */ + if (do_attention_interrupt) { + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n"); + tw_state_request_start(tw_dev, &request_id); + error = tw_aen_read_queue(tw_dev, request_id); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Error reading aen queue.\n"); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + } else { + dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n"); + tw_clear_attention_interrupt(tw_dev); + } + } + + /* Handle command interrupt */ + if (do_command_interrupt) { + /* Drain as many pending commands as we can */ + while (tw_dev->pending_request_count > 0) { + request_id = tw_dev->pending_queue[tw_dev->pending_head]; + if (tw_dev->state[request_id] != TW_S_PENDING) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found request id that wasn't pending.\n"); + break; + } + if (tw_post_command_packet(tw_dev, request_id)==0) { + if (tw_dev->pending_head == TW_Q_LENGTH-1) { + tw_dev->pending_head = TW_Q_START; + } else { + tw_dev->pending_head = tw_dev->pending_head + 1; + } + tw_dev->pending_request_count--; + } else { + break; + } + } + /* If there are no more pending requests, we mask command interrupt */ + if (tw_dev->pending_request_count == 0) + tw_mask_command_interrupt(tw_dev); + } + + /* Handle response interrupt */ + if (do_response_interrupt) { + /* Drain the response queue from the board */ + while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) { + response_que.value = inl(response_que_addr); + request_id = response_que.u.response_id; + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet->status != 0) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, flags = 0x%x.\n", command_packet->flags); + } + if (tw_dev->state[request_id] != TW_S_POSTED) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", request_id, command_packet->byte0.opcode); + } + error = 0; + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id); + /* Check for internal command */ + if (tw_dev->srb[request_id] == 0) { + dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n"); + error = tw_aen_complete(tw_dev, request_id); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Error completing aen.\n"); + } + status_reg_value = inl(status_reg_addr); + if (tw_check_bits(status_reg_value)) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); + } + } else { + switch (tw_dev->srb[request_id]->cmnd[0]) { + case READ_10: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_10\n"); + case READ_6: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_6\n"); + break; + case WRITE_10: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10\n"); + case WRITE_6: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_6\n"); + break; + case INQUIRY: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n"); + error = tw_scsiop_inquiry_complete(tw_dev, request_id); + break; + case READ_CAPACITY: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n"); + error = tw_scsiop_read_capacity_complete(tw_dev, request_id); + break; + case TW_IOCTL: + dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TW_IOCTL\n"); + error = tw_ioctl_complete(tw_dev, request_id); + break; + default: + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unknown scsi opcode: 0x%x.\n", tw_dev->srb[request_id]->cmnd[0]); + tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + } + if (error) { + /* Tell scsi layer there was an error */ + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n"); + tw_dev->srb[request_id]->result = (DID_ERROR << 16); + } else { + /* Tell scsi layer command was a success */ + tw_dev->srb[request_id]->result = (DID_OK << 16); + } + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->posted_request_count--; + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + status_reg_value = inl(status_reg_addr); + if (tw_check_bits(status_reg_value)) { + printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); + } + } + } + } + spin_unlock_irqrestore(&tw_dev->tw_lock, flags2); + } + spin_unlock_irqrestore(&io_request_lock, flags); +} /* End tw_interrupt() */ + +/* This function handles ioctls from userspace to the driver */ +int tw_ioctl(TW_Device_Extension *tw_dev, int request_id) +{ + unsigned char opcode; + int bufflen; + TW_Param *param; + TW_Command *command_packet; + u32 param_value; + TW_Ioctl *ioctl = NULL; + int tw_aen_code; + + ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer; + if (ioctl == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Request buffer NULL.\n"); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + return 0; + } + bufflen = tw_dev->srb[request_id]->request_bufflen; + + /* Initialize command packet */ + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet == NULL) { + printk(KERN_WARNING "3w-xxxx: twioctl(): Bad command packet virtual address.\n"); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + return 0; + } + memset(command_packet, 0, sizeof(TW_Sector)); + + /* Initialize param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad alignment virtual address.\n"); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + return 0; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + + dprintk(KERN_NOTICE "opcode = %d table_id = %d parameter_id = %d parameter_size_bytes = %d\n", ioctl->opcode, ioctl->table_id, ioctl->parameter_id,, ioctl->parameter_size_bytes); + opcode = ioctl->opcode; + + switch (opcode) { + case TW_OP_NOP: + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_NOP.\n"); + command_packet->byte0.opcode = TW_OP_NOP; + break; + case TW_OP_GET_PARAM: + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_GET_PARAM.\n"); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + param->table_id = ioctl->table_id; + param->parameter_id = ioctl->parameter_id; + param->parameter_size_bytes = ioctl->parameter_size_bytes; + tw_dev->ioctl_size[request_id] = ioctl->parameter_size_bytes; + dprintk(KERN_NOTICE "table_id = %d parameter_id = %d parameter_size_bytes %d\n", param->table_id, param->parameter_id, param->parameter_size_bytes); + break; + case TW_OP_SET_PARAM: + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_SET_PARAM: table_id = %d, parameter_id = %d, parameter_size_bytes = %d.\n", + ioctl->table_id, ioctl->parameter_id, ioctl->parameter_size_bytes); + command_packet->byte0.opcode = TW_OP_SET_PARAM; + param->table_id = ioctl->table_id; + param->parameter_id = ioctl->parameter_id; + param->parameter_size_bytes = ioctl->parameter_size_bytes; + memcpy(param->data, ioctl->data, ioctl->parameter_size_bytes); + break; + case TW_OP_AEN_LISTEN: + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_AEN_LISTEN.\n"); + if (tw_dev->aen_head == tw_dev->aen_tail) { + /* aen queue empty */ + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): Aen queue empty.\n"); + tw_aen_code = TW_AEN_QUEUE_EMPTY; + memcpy(tw_dev->srb[request_id]->request_buffer, &tw_aen_code, ioctl->parameter_size_bytes); + } else { + /* Copy aen queue entry to request buffer */ + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): Returning aen 0x%x\n", tw_dev->aen_queue[tw_dev->aen_head]); + tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head]; + memcpy(tw_dev->srb[request_id]->request_buffer, &tw_aen_code, ioctl->parameter_size_bytes); + if (tw_dev->aen_head == TW_Q_LENGTH - 1) { + tw_dev->aen_head = TW_Q_START; + } else { + tw_dev->aen_head = tw_dev->aen_head + 1; + } + } + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + return 0; + default: + printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + return 0; + } + + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad alignment physical address.\n"); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + } + + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.parameter_count = 1; + + /* Now try to post the command to the board */ + tw_post_command_packet(tw_dev, request_id); + + return 0; +} /* End tw_ioctl() */ + +/* This function is called by the isr to complete ioctl requests */ +int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id) +{ + unsigned char *param_data; + unsigned char *buff; + TW_Param *param; + + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete()\n"); + buff = tw_dev->srb[request_id]->request_buffer; + if (buff == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): Request buffer NULL.\n"); + return 1; + } + dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete(): Request_bufflen = %d\n", tw_dev->srb[request_id]->request_bufflen); + memset(buff, 0, tw_dev->srb[request_id]->request_bufflen); + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + if (param == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n"); + return 1; + } + param_data = &(param->data[0]); + + memcpy(buff, param_data, tw_dev->ioctl_size[request_id]); + + return 0; +} /* End tw_ioctl_complete() */ + +/* This function will mask the command interrupt */ +void tw_mask_command_interrupt(TW_Device_Extension *tw_dev) +{ + u32 control_reg_addr, control_reg_value; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = TW_CONTROL_MASK_COMMAND_INTERRUPT; + outl(control_reg_value, control_reg_addr); +} /* End tw_mask_command_interrupt() */ + +/* This function will poll the status register for a flag */ +int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds) +{ + u32 status_reg_addr, status_reg_value; + struct timeval before, timeout; + + status_reg_addr = tw_dev->registers.status_reg_addr; + do_gettimeofday(&before); + status_reg_value = inl(status_reg_addr); + + while ((status_reg_value & flag) != flag) { + status_reg_value = inl(status_reg_addr); + do_gettimeofday(&timeout); + if (before.tv_sec + seconds < timeout.tv_sec) { + printk(KERN_WARNING "3w-xxxx: tw_poll_status(): Flag 0x%x not found.\n", flag); + return 1; + } + mdelay(1); + } + return 0; +} /* End tw_poll_status() */ + +/* This function will attempt to post a command packet to the board */ +int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id) +{ + u32 status_reg_addr, status_reg_value; + u32 command_que_addr, command_que_value; + + dprintk(KERN_NOTICE "3w-xxxx: tw_post_command_packet()\n"); + command_que_addr = tw_dev->registers.command_que_addr; + command_que_value = tw_dev->command_packet_physical_address[request_id]; + status_reg_addr = tw_dev->registers.status_reg_addr; + status_reg_value = inl(status_reg_addr); + + if (tw_check_bits(status_reg_value)) + printk(KERN_WARNING "3w-xxxx: tw_post_command_packet(): Unexpected bits.\n"); + + if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) { + /* We successfully posted the command packet */ + outl(command_que_value, command_que_addr); + tw_dev->state[request_id] = TW_S_POSTED; + tw_dev->posted_request_count++; + if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) { + tw_dev->max_posted_request_count = tw_dev->posted_request_count; + } + } else { + /* Couldn't post the command packet, so we do it in the isr */ + if (tw_dev->state[request_id] != TW_S_PENDING) { + tw_dev->state[request_id] = TW_S_PENDING; + tw_dev->pending_request_count++; + if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) { + tw_dev->max_pending_request_count = tw_dev->pending_request_count; + } + tw_dev->pending_queue[tw_dev->pending_tail] = request_id; + if (tw_dev->pending_tail == TW_Q_LENGTH-1) { + tw_dev->pending_tail = TW_Q_START; + } else { + tw_dev->pending_tail = tw_dev->pending_tail + 1; + } + } + tw_unmask_command_interrupt(tw_dev); + return 1; + } + return 0; +} /* End tw_post_command_packet() */ + +/* This function will reset a device extension */ +int tw_reset_device_extension(TW_Device_Extension *tw_dev) +{ + int imax = 0; + int i = 0; + Scsi_Cmnd *srb; + + dprintk(KERN_NOTICE "3w-xxxx: tw_reset_device_extension()\n"); + imax = TW_Q_LENGTH; + + if (tw_reset_sequence(tw_dev)) { + printk(KERN_WARNING "3w-xxxx: tw_reset_device_extension(): Reset sequence failed for card %d.\n", tw_dev->host->host_no); + return 1; + } + + /* Abort all requests that are in progress */ + for (i=0;istate[i] != TW_S_FINISHED) && + (tw_dev->state[i] != TW_S_INITIAL) && + (tw_dev->state[i] != TW_S_COMPLETED)) { + srb = tw_dev->srb[i]; + srb->result = (DID_RESET << 16); + tw_dev->srb[i]->scsi_done(tw_dev->srb[i]); + } + } + + /* Reset queues and counts */ + for (i=0;ifree_queue[i] = i; + tw_dev->state[i] = TW_S_INITIAL; + } + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_LENGTH - 1; + tw_dev->posted_request_count = 0; + tw_dev->pending_request_count = 0; + tw_dev->pending_head = TW_Q_START; + tw_dev->pending_tail = TW_Q_START; + + return 0; +} /* End tw_reset_device_extension() */ + +/* This function will reset a controller */ +int tw_reset_sequence(TW_Device_Extension *tw_dev) +{ + int error = 0; + int tries = 0; + + /* Disable interrupts */ + tw_disable_interrupts(tw_dev); + + /* Reset the board */ + while (tries < TW_MAX_RESET_TRIES) { + tw_soft_reset(tw_dev); + + error = tw_aen_drain_queue(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): No attention interrupt for card %d.\n", tw_dev->host->host_no); + tries++; + continue; + } + + /* Check for controller errors */ + if (tw_check_errors(tw_dev)) { + printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Controller errors found, soft resetting card %d.\n", tw_dev->host->host_no); + tries++; + continue; + } + + /* Empty the response queue again */ + error = tw_empty_response_que(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Couldn't empty response queue for card %d.\n", tw_dev->host->host_no); + tries++; + continue; + } + + /* Now the controller is in a good state */ + break; + } + + if (tries >= TW_MAX_RESET_TRIES) { + printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Controller error or no attention interrupt: giving up for card %d.\n", tw_dev->host->host_no); + return 1; + } + + error = tw_initconnection(tw_dev); + if (error) { + printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Couldn't initconnection for card %d.\n", tw_dev->host->host_no); + return 1; + } + + /* Re-enable interrupts */ + tw_enable_interrupts(tw_dev); + + return 0; +} /* End tw_reset_sequence() */ + +/* This funciton returns unit geometry in cylinders/heads/sectors */ +int tw_scsi_biosparam(Disk *disk, kdev_t dev, int geom[]) +{ + int heads, sectors, cylinders; + TW_Device_Extension *tw_dev; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam()\n"); + tw_dev = (TW_Device_Extension *)disk->device->host->hostdata; + + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + if (disk->capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam(): heads = %d, sectors = %d, cylinders = %d\n", heads, sectors, cylinders); + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return 0; +} /* End tw_scsi_biosparam() */ + +/* This function will find and initialize any cards */ +int tw_scsi_detect(Scsi_Host_Template *tw_host) +{ + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_detect()\n"); + + /* Check if the kernel has PCI interface compiled in */ + if (!pci_present()) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_detect(): No pci interface present.\n"); + return 0; + } + + return(tw_findcards(tw_host)); +} /* End tw_scsi_detect() */ + +/* This is the new scsi eh abort function */ +int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt) +{ + TW_Device_Extension *tw_dev=NULL; + int i = 0; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_eh_abort()\n"); + + if (!SCpnt) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Invalid Scsi_Cmnd.\n"); + return (FAILED); + } + + tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata; + if (tw_dev == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Invalid device extension.\n"); + return (FAILED); + } + + spin_lock(&tw_dev->tw_lock); + tw_dev->num_aborts++; + + /* If the command hasn't been posted yet, we can do the abort */ + for (i=0;isrb[i] == SCpnt) { + if (tw_dev->state[i] == TW_S_STARTED) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Abort succeeded for started Scsi_Cmnd 0x%x\n", (u32)tw_dev->srb[i]); + tw_dev->state[i] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, i); + spin_unlock(&tw_dev->tw_lock); + return (SUCCESS); + } + if (tw_dev->state[i] == TW_S_PENDING) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Abort succeeded for pending Scsi_Cmnd 0x%x\n", (u32)tw_dev->srb[i]); + if (tw_dev->pending_head == TW_Q_LENGTH-1) { + tw_dev->pending_head = TW_Q_START; + } else { + tw_dev->pending_head = tw_dev->pending_head + 1; + } + tw_dev->pending_request_count--; + tw_dev->state[i] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, i); + spin_unlock(&tw_dev->tw_lock); + return (SUCCESS); + } + } + } + + /* If the command has already been posted, we have to reset the card */ + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Abort failed for unknown Scsi_Cmnd 0x%x, resetting card %d.\n", (u32)SCpnt, tw_dev->host->host_no); + + if (tw_reset_device_extension(tw_dev)) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Reset failed for card %d.\n", tw_dev->host->host_no); + spin_unlock(&tw_dev->tw_lock); + return (FAILED); + } + spin_unlock(&tw_dev->tw_lock); + + return (SUCCESS); +} /* End tw_scsi_eh_abort() */ + +/* This is the new scsi eh reset function */ +int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt) +{ + TW_Device_Extension *tw_dev=NULL; + int flags = 0; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_eh_reset()\n"); + + if (!SCpnt) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Invalid Scsi_Cmnd.\n"); + return (FAILED); + } + + tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata; + if (tw_dev == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Invalid device extension.\n"); + return (FAILED); + } + + spin_lock_irqsave(&tw_dev->tw_lock, flags); + tw_dev->num_resets++; + + /* Now reset the card and some of the device extension data */ + if (tw_reset_device_extension(tw_dev)) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset failed for card %d.\n", tw_dev->host->host_no); + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + return (FAILED); + } + printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset succeeded for card %d.\n", tw_dev->host->host_no); + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + + return (SUCCESS); +} /* End tw_scsi_eh_reset() */ + +/* This function handles input and output from /proc/scsi/3w-xxxx/x */ +int tw_scsi_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout) +{ + TW_Device_Extension *tw_dev = NULL; + TW_Info info; + int i; + int j; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_proc_info()\n"); + + /* Find the correct device extension */ + for (i=0;ihost->host_no == hostno) + tw_dev = tw_device_extension_list[i]; + if (tw_dev == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_proc_info(): Couldn't locate device extension.\n"); + return (-EINVAL); + } + + info.buffer = buffer; + info.length = length; + info.offset = offset; + info.position = 0; + + if (inout) { + /* Write */ + if (strncmp(buffer, "debug", 5) == 0) { + printk(KERN_INFO "3w-xxxx: Posted commands:\n"); + for (j=0;jstate[j] == TW_S_POSTED) { + TW_Command *command = (TW_Command *)tw_dev->command_packet_virtual_address[j]; + printk(KERN_INFO "3w-xxxx: Request_id: %d\n", j); + printk(KERN_INFO "Opcode: 0x%x\n", command->byte0.opcode); + printk(KERN_INFO "Block_count: 0x%x\n", command->byte6.block_count); + printk(KERN_INFO "LBA: 0x%x\n", (u32)command->byte8.io.lba); + printk(KERN_INFO "Physical command packet addr: 0x%x\n", tw_dev->command_packet_physical_address[j]); + printk(KERN_INFO "Scsi_Cmnd: 0x%x\n", (u32)tw_dev->srb[j]); + } + } + printk(KERN_INFO "3w-xxxx: Free_head: %3d\n", tw_dev->free_head); + printk(KERN_INFO "3w-xxxx: Free_tail: %3d\n", tw_dev->free_tail); + } + return length; + } else { + /* Read */ + if (start) { + *start = buffer; + } + tw_copy_info(&info, "scsi%d: 3ware Storage Controller\n", hostno); + tw_copy_info(&info, "Driver version: %s\n", tw_driver_version); + tw_copy_info(&info, "Current commands posted: %3d\n", tw_dev->posted_request_count); + tw_copy_info(&info, "Max commands posted: %3d\n", tw_dev->max_posted_request_count); + tw_copy_info(&info, "Current pending commands: %3d\n", tw_dev->pending_request_count); + tw_copy_info(&info, "Max pending commands: %3d\n", tw_dev->max_pending_request_count); + tw_copy_info(&info, "Last sgl length: %3d\n", tw_dev->sgl_entries); + tw_copy_info(&info, "Max sgl length: %3d\n", tw_dev->max_sgl_entries); + tw_copy_info(&info, "Last sector count: %3d\n", tw_dev->sector_count); + tw_copy_info(&info, "Max sector count: %3d\n", tw_dev->max_sector_count); + tw_copy_info(&info, "Resets: %3d\n", tw_dev->num_resets); + tw_copy_info(&info, "Aborts: %3d\n", tw_dev->num_aborts); + } + if (info.position > info.offset) { + return (info.position - info.offset); + } else { + return 0; + } +} /* End tw_scsi_proc_info() */ + +/* This is the main scsi queue function to handle scsi opcodes */ +int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + unsigned char *command = SCpnt->cmnd; + int request_id = 0; + int error = 0; + int flags = 0; + TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata; + + spin_lock_irqsave(&tw_dev->tw_lock, flags); + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n"); + + /* Skip scsi command if it isn't for us */ + if ((tw_dev->is_unit_present[SCpnt->target] == FALSE) || (SCpnt->lun != 0)) { + SCpnt->result = (DID_BAD_TARGET << 16); + done(SCpnt); + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + return 0; + } + if (done == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid done function.\n"); + SCpnt->result = (DID_ERROR << 16); + done(SCpnt); + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + return 0; + } + if (tw_dev == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid device extension.\n"); + SCpnt->result = (DID_ERROR << 16); + done(SCpnt); + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + return 0; + } + + /* Save done function into Scsi_Cmnd struct */ + SCpnt->scsi_done = done; + + /* Queue the command and get a request id */ + tw_state_request_start(tw_dev, &request_id); + + /* Save the scsi command for use by the ISR */ + tw_dev->srb[request_id] = SCpnt; + + switch (*command) { + case READ_10: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_10.\n"); + case READ_6: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_6.\n"); + case WRITE_10: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught WRITE_10.\n"); + case WRITE_6: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught WRITE_6.\n"); + error = tw_scsiop_read_write(tw_dev, request_id); + break; + case TEST_UNIT_READY: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n"); + error = tw_scsiop_test_unit_ready(tw_dev, request_id); + break; + case INQUIRY: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n"); + error = tw_scsiop_inquiry(tw_dev, request_id); + break; + case READ_CAPACITY: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n"); + error = tw_scsiop_read_capacity(tw_dev, request_id); + break; + case TW_IOCTL: + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TW_SCSI_IOCTL.\n"); + error = tw_ioctl(tw_dev, request_id); + break; + default: + printk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): Unknown scsi opcode: 0x%x\n", *command); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + SCpnt->result = (DID_BAD_TARGET << 16); + done(SCpnt); + } + if (error) { + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + SCpnt->result = (DID_ERROR << 16); + done(SCpnt); + } + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + + return 0; +} /* End tw_scsi_queue() */ + +/* This function will release the resources on an rmmod call */ +int tw_scsi_release(struct Scsi_Host *tw_host) +{ + TW_Device_Extension *tw_dev; + tw_dev = (TW_Device_Extension *)tw_host->hostdata; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_release()\n"); + + /* Free up the IO region */ + release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE); + + /* Free up the IRQ */ + free_irq(tw_dev->tw_pci_dev->irq, tw_dev); + + /* Free up device extension resources */ + tw_free_device_extension(tw_dev); + + /* Tell kernel scsi-layer we are gone */ + scsi_unregister(tw_host); + + return 0; +} /* End tw_scsi_release() */ + +/* This function handles scsi inquiry commands */ +int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id) +{ + TW_Param *param; + TW_Command *command_packet; + u32 command_que_value, command_que_addr; + u32 param_value; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry()\n"); + + /* Initialize command packet */ + command_que_addr = tw_dev->registers.command_que_addr; + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet virtual address.\n"); + return 1; + } + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.parameter_count = 1; + + /* Now setup the param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + param->table_id = 3; /* unit summary table */ + param->parameter_id = 3; /* unitsstatus parameter */ + param->parameter_size_bytes = TW_MAX_UNITS; + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment physical address.\n"); + return 1; + } + + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet physical address.\n"); + return 1; + } + + /* Now try to post the command packet */ + tw_post_command_packet(tw_dev, request_id); + + return 0; +} /* End tw_scsiop_inquiry() */ + +/* This function is called by the isr to complete an inquiry command */ +int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) +{ + unsigned char *is_unit_present; + unsigned char *request_buffer; + int i; + TW_Param *param; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n"); + + /* Fill request buffer */ + if (tw_dev->srb[request_id]->request_buffer == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Request buffer NULL.\n"); + return 1; + } + request_buffer = tw_dev->srb[request_id]->request_buffer; + memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + request_buffer[0] = TYPE_DISK; /* Peripheral device type */ + request_buffer[1] = 0; /* Device type modifier */ + request_buffer[2] = 0; /* No ansi/iso compliance */ + request_buffer[4] = 31; /* Additional length */ + memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */ + memcpy(&request_buffer[16], "3w-xxxx ", 16); /* Product ID */ + memcpy(&request_buffer[32], tw_driver_version, 3); + + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + if (param == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Bad alignment virtual address.\n"); + return 1; + } + is_unit_present = &(param->data[0]); + + for (i=0 ; iis_unit_present[i] = FALSE; + } else { + tw_dev->is_unit_present[i] = TRUE; + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i); + } + } + + return 0; +} /* End tw_scsiop_inquiry_complete() */ + +/* This function handles scsi read_capacity commands */ +int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id) +{ + TW_Param *param; + TW_Command *command_packet; + u32 command_que_addr, command_que_value; + u32 param_value; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity()\n"); + + /* Initialize command packet */ + command_que_addr = tw_dev->registers.command_que_addr; + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + + if (command_packet == NULL) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet virtual address.\n"); + return 1; + } + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = tw_dev->srb[request_id]->target; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.block_count = 1; + + /* Now setup the param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + param->table_id = TW_UNIT_INFORMATION_TABLE_BASE + + tw_dev->srb[request_id]->target; + param->parameter_id = 4; /* unitcapacity parameter */ + param->parameter_size_bytes = 4; + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment physical address.\n"); + return 1; + } + + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet physical address.\n"); + return 1; + } + + /* Now try to post the command to the board */ + tw_post_command_packet(tw_dev, request_id); + + return 0; +} /* End tw_scsiop_read_capacity() */ + +/* This function is called by the isr to complete a readcapacity command */ +int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id) +{ + unsigned char *param_data; + u32 capacity; + char *buff; + TW_Param *param; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n"); + + buff = tw_dev->srb[request_id]->request_buffer; + if (buff == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Request buffer NULL.\n"); + return 1; + } + memset(buff, 0, tw_dev->srb[request_id]->request_bufflen); + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + if (param == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n"); + return 1; + } + param_data = &(param->data[0]); + + capacity = (param_data[3] << 24) | (param_data[2] << 16) | + (param_data[1] << 8) | param_data[0]; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity); + + /* Number of LBA's */ + buff[0] = (capacity >> 24); + buff[1] = (capacity >> 16) & 0xff; + buff[2] = (capacity >> 8) & 0xff; + buff[3] = capacity & 0xff; + + /* Block size in bytes (512) */ + buff[4] = (TW_BLOCK_SIZE >> 24); + buff[5] = (TW_BLOCK_SIZE >> 16) & 0xff; + buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff; + buff[7] = TW_BLOCK_SIZE & 0xff; + + return 0; +} /* End tw_scsiop_read_capacity_complete() */ + +/* This function handles scsi read or write commands */ +int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) +{ + TW_Command *command_packet; + u32 command_que_addr, command_que_value = 0; + u32 lba = 0x0, num_sectors = 0x0; + int i; + Scsi_Cmnd *srb; + struct scatterlist *sglist; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write()\n"); + + if (tw_dev->srb[request_id]->request_buffer == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Request buffer NULL.\n"); + return 1; + } + sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + srb = tw_dev->srb[request_id]; + + /* Initialize command packet */ + command_que_addr = tw_dev->registers.command_que_addr; + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet == NULL) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): Bad command packet virtual address.\n"); + return 1; + } + + if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == READ_10) { + command_packet->byte0.opcode = TW_OP_READ; + } else { + command_packet->byte0.opcode = TW_OP_WRITE; + } + + command_packet->byte0.sgl_offset = 3; + command_packet->size = 5; + command_packet->request_id = request_id; + command_packet->byte3.unit = srb->target; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + + if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6) { + lba = ((u32)srb->cmnd[1] << 16) | ((u32)srb->cmnd[2] << 8) | (u32)srb->cmnd[3]; + num_sectors = (u32)srb->cmnd[4]; + } else { + lba = ((u32)srb->cmnd[2] << 24) | ((u32)srb->cmnd[3] << 16) | + ((u32)srb->cmnd[4] << 8) | (u32)srb->cmnd[5]; + num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8); + } + + /* Update sector statistic */ + tw_dev->sector_count = num_sectors; + if (tw_dev->sector_count > tw_dev->max_sector_count) + tw_dev->max_sector_count = tw_dev->sector_count; + + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): lba = 0x%x num_sectors = 0x%x\n", lba, num_sectors); + command_packet->byte8.io.lba = lba; + command_packet->byte6.block_count = num_sectors; + + /* Do this if there are no sg list entries */ + if (tw_dev->srb[request_id]->use_sg == 0) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n"); + command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer); + command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen; + } + + /* Do this if we have multiple sg list entries */ + if (tw_dev->srb[request_id]->use_sg > 0) { + for (i=0;isrb[request_id]->use_sg; i++) { + command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address); + command_packet->byte8.io.sgl[i].length = sglist[i].length; + command_packet->size+=2; + } + if (tw_dev->srb[request_id]->use_sg > 1) + command_packet->size-=2; + } + + /* Update SG statistics */ + tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; + if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) + tw_dev->max_sgl_entries = tw_dev->sgl_entries; + + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n"); + return 1; + } + + /* Now try to post the command to the board */ + tw_post_command_packet(tw_dev, request_id); + + return 0; +} /* End tw_scsiop_read_write() */ + +/* This function will handle test unit ready scsi command */ +int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id) +{ + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n"); + + /* Tell the scsi layer were done */ + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->srb[request_id]->result = (DID_OK << 16); + tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); + + return 0; +} /* End tw_scsiop_test_unit_ready() */ + +/* This function will setup the interrupt handler */ +int tw_setup_irq(TW_Device_Extension *tw_dev) +{ + char *device = TW_DEVICE_NAME; + int error; + + dprintk(KERN_NOTICE "3w-xxxx: tw_setup_irq()\n"); + error = request_irq(tw_dev->tw_pci_dev->irq, tw_interrupt, SA_SHIRQ, device, tw_dev); + + if (error < 0) { + printk(KERN_WARNING "3w-xxxx: tw_setup_irq(): Error requesting IRQ: %d for card %d.\n", tw_dev->tw_pci_dev->irq, tw_dev->host->host_no); + return 1; + } + return 0; +} /* End tw_setup_irq() */ + +/* This function will soft reset the controller */ +void tw_soft_reset(TW_Device_Extension *tw_dev) +{ + u32 control_reg_addr, control_reg_value; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = ( TW_CONTROL_ISSUE_SOFT_RESET | + TW_CONTROL_CLEAR_HOST_INTERRUPT | + TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | + TW_CONTROL_MASK_COMMAND_INTERRUPT | + TW_CONTROL_MASK_RESPONSE_INTERRUPT | + TW_CONTROL_CLEAR_ERROR_STATUS | + TW_CONTROL_DISABLE_INTERRUPTS); + outl(control_reg_value, control_reg_addr); +} /* End tw_soft_reset() */ + +/* This function will free a request_id */ +int tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id) +{ + dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish()\n"); + + do { + if (tw_dev->free_tail == TW_Q_LENGTH-1) { + tw_dev->free_tail = TW_Q_START; + } else { + tw_dev->free_tail = tw_dev->free_tail + 1; + } + } while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_tail]] != TW_S_COMPLETED)); + + tw_dev->free_queue[tw_dev->free_tail] = request_id; + + tw_dev->state[request_id] = TW_S_FINISHED; + dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish(): Freeing request_id %d\n", request_id); + + return 0; +} /* End tw_state_request_finish() */ + +/* This function will assign an available request_id */ +int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id) +{ + int id = 0; + + dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n"); + + /* Obtain next free request_id */ + do { + if (tw_dev->free_head == TW_Q_LENGTH - 1) { + tw_dev->free_head = TW_Q_START; + } else { + tw_dev->free_head = tw_dev->free_head + 1; + } + } while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_STARTED) || + (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_POSTED) || + (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_PENDING) || + (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_COMPLETED)); + + id = tw_dev->free_queue[tw_dev->free_head]; + + if (tw_dev->free_head == TW_Q_LENGTH - 1) { + tw_dev->free_head = TW_Q_START; + } else { + tw_dev->free_head = tw_dev->free_head + 1; + } + + dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id); + *request_id = id; + tw_dev->state[id] = TW_S_STARTED; + + return 0; +} /* End tw_state_request_start() */ + +/* This function will unmask the command interrupt on the controller */ +void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev) +{ + u32 control_reg_addr, control_reg_value; + + control_reg_addr = tw_dev->registers.control_reg_addr; + control_reg_value = TW_CONTROL_UNMASK_COMMAND_INTERRUPT; + outl(control_reg_value, control_reg_addr); +} /* End tw_unmask_command_interrupt() */ + +/* Now get things going */ + +#ifdef MODULE +Scsi_Host_Template driver_template = TWXXXX; +#include "scsi_module.c" +#endif diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h new file mode 100644 index 000000000000..b3989c71cf48 --- /dev/null +++ b/drivers/scsi/3w-xxxx.h @@ -0,0 +1,369 @@ +/* + 3w-xxxx.h -- 3ware Storage Controller device driver for Linux. + + Written By: Adam Radford + Copyright (C) 1999 3ware Inc. + + Kernel compatablity By: Andre Hedrick + Non-Copyright (C) 2000 Andre Hedrick + + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + 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 + + Bugs/Comments/Suggestions should be mailed to: + linux@3ware.com + + For more information, goto: + http://www.3ware.com +*/ + +#ifndef _3W_XXXX_H +#define _3W_XXXX_H + +#include +#include +#include + +/* Control register bit definitions */ +#define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000 +#define TW_CONTROL_CLEAR_ATTENTION_INTERRUPT 0x00040000 +#define TW_CONTROL_MASK_COMMAND_INTERRUPT 0x00020000 +#define TW_CONTROL_MASK_RESPONSE_INTERRUPT 0x00010000 +#define TW_CONTROL_UNMASK_COMMAND_INTERRUPT 0x00008000 +#define TW_CONTROL_UNMASK_RESPONSE_INTERRUPT 0x00004000 +#define TW_CONTROL_CLEAR_ERROR_STATUS 0x00000200 +#define TW_CONTROL_ISSUE_SOFT_RESET 0x00000100 +#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080 +#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040 +#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020 + +/* Status register bit definitions */ +#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000 +#define TW_STATUS_MINOR_VERSION_MASK 0x0F000000 +#define TW_STATUS_PCI_PARITY_ERROR 0x00800000 +#define TW_STATUS_QUEUE_ERROR 0x00400000 +#define TW_STATUS_MICROCONTROLLER_ERROR 0x00200000 +#define TW_STATUS_PCI_ABORT 0x00100000 +#define TW_STATUS_HOST_INTERRUPT 0x00080000 +#define TW_STATUS_ATTENTION_INTERRUPT 0x00040000 +#define TW_STATUS_COMMAND_INTERRUPT 0x00020000 +#define TW_STATUS_RESPONSE_INTERRUPT 0x00010000 +#define TW_STATUS_COMMAND_QUEUE_FULL 0x00008000 +#define TW_STATUS_RESPONSE_QUEUE_EMPTY 0x00004000 +#define TW_STATUS_MICROCONTROLLER_READY 0x00002000 +#define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000 +#define TW_STATUS_ALL_INTERRUPTS 0x000F0000 +#define TW_STATUS_CLEARABLE_BITS 0x00D00000 +#define TW_STATUS_EXPECTED_BITS 0x00002000 +#define TW_STATUS_UNEXPECTED_BITS 0x00F80000 + +/* RESPONSE QUEUE BIT DEFINITIONS */ +#define TW_RESPONSE_ID_MASK 0x00000FF0 + +/* PCI related defines */ +#define TW_IO_ADDRESS_RANGE 0xD +#define TW_DEVICE_NAME "3ware Storage Controller" +#define TW_VENDOR_ID (0x13C1) /* 3ware */ +#define TW_DEVICE_ID (0x1000) /* Storage Controller */ + +/* Command packet opcodes */ +#define TW_OP_NOP 0x0 +#define TW_OP_INIT_CONNECTION 0x1 +#define TW_OP_READ 0x2 +#define TW_OP_WRITE 0x3 +#define TW_OP_VERIFY 0x4 +#define TW_OP_GET_PARAM 0x12 +#define TW_OP_SET_PARAM 0x13 +#define TW_OP_SECTOR_INFO 0x1a +#define TW_OP_AEN_LISTEN 0x1c + +/* Asynchronous Event Notification (AEN) Codes */ +#define TW_AEN_QUEUE_EMPTY 0x0000 +#define TW_AEN_SOFT_RESET 0x0001 +#define TW_AEN_DEGRADED_MIRROR 0x0002 +#define TW_AEN_CONTROLLER_ERROR 0x0003 +#define TW_AEN_REBUILD_FAIL 0x0004 +#define TW_AEN_REBUILD_DONE 0x0005 +#define TW_AEN_QUEUE_FULL 0x00ff +#define TW_AEN_TABLE_UNDEFINED 0x15 + +/* Misc defines */ +#define TW_ALIGNMENT 0x200 /* 16 D-WORDS */ +#define TW_MAX_UNITS 16 +#define TW_COMMAND_ALIGNMENT_MASK 0x1ff +#define TW_INIT_MESSAGE_CREDITS 0x100 +#define TW_INIT_COMMAND_PACKET_SIZE 0x3 +#define TW_POLL_MAX_RETRIES 10000 +#define TW_MAX_SGL_LENGTH 62 +#define TW_Q_LENGTH 256 +#define TW_Q_START 0 +#define TW_MAX_SLOT 32 +#define TW_MAX_PCI_BUSES 255 +#define TW_MAX_RESET_TRIES 3 +#define TW_UNIT_INFORMATION_TABLE_BASE 0x300 +#define TW_MAX_CMDS_PER_LUN (TW_Q_LENGTH-2)/TW_MAX_UNITS +#define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */ +#define TW_IOCTL 0x80 +#define TW_MAX_AEN_TRIES 100 + +#define TW_IN_INTR 1 + +/* Macros */ +#define TW_STATUS_ERRORS(x) \ + (((x & TW_STATUS_PCI_ABORT) || \ + (x & TW_STATUS_PCI_PARITY_ERROR) || \ + (x & TW_STATUS_QUEUE_ERROR) || \ + (x & TW_STATUS_MICROCONTROLLER_ERROR)) && \ + (x & TW_STATUS_MICROCONTROLLER_READY)) + +#ifdef TW_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0); +#endif + +extern struct proc_dir_entry tw_scsi_proc_entry; + +/* Scatter Gather List Entry */ +typedef struct TAG_TW_SG_Entry { + unsigned long address; + unsigned long length; +} TW_SG_Entry; + +typedef unsigned char TW_Sector[512]; + +/* Command Packet */ +typedef struct TW_Command { + /* First DWORD */ + struct { + unsigned char opcode:5; + unsigned char sgl_offset:3; + } byte0; + unsigned char size; + unsigned char request_id; + struct { + unsigned char unit:4; + unsigned char host_id:4; + } byte3; + /* Second DWORD */ + unsigned char status; + unsigned char flags; + union { + unsigned short block_count; + unsigned short parameter_count; + unsigned short message_credits; + } byte6; + union { + struct { + unsigned long lba; + TW_SG_Entry sgl[TW_MAX_SGL_LENGTH]; + unsigned long padding; /* pad to 512 bytes */ + } io; + struct { + TW_SG_Entry sgl[TW_MAX_SGL_LENGTH]; + unsigned long padding[2]; + } param; + struct { + unsigned long response_queue_pointer; + unsigned long padding[125]; + } init_connection; + struct { + char version[504]; + } ioctl_miniport_version; + } byte8; +} TW_Command; + +typedef struct TAG_TW_Ioctl { + int buffer; + unsigned char opcode; + unsigned short table_id; + unsigned char parameter_id; + unsigned char parameter_size_bytes; + unsigned char data[1]; +} TW_Ioctl; + +/* GetParam descriptor */ +typedef struct { + unsigned short table_id; + unsigned char parameter_id; + unsigned char parameter_size_bytes; + unsigned char data[1]; +} TW_Param, *PTW_Param; + +/* Response queue */ +typedef union TAG_TW_Response_Queue { + struct { + u32 undefined_1: 4; + u32 response_id: 8; + u32 undefined_2: 20; + } u; + u32 value; +} TW_Response_Queue; + +typedef struct TAG_TW_Registers { + u32 base_addr; + u32 control_reg_addr; + u32 status_reg_addr; + u32 command_que_addr; + u32 response_que_addr; +} TW_Registers; + +typedef struct TAG_TW_Info { + char *buffer; + int length; + int offset; + int position; +} TW_Info; + +typedef enum TAG_TW_Cmd_State { + TW_S_INITIAL, /* Initial state */ + TW_S_STARTED, /* Id in use */ + TW_S_POSTED, /* Posted to the controller */ + TW_S_PENDING, /* Waiting to be posted in isr */ + TW_S_COMPLETED, /* Completed by isr */ + TW_S_FINISHED, /* I/O completely done */ +} TW_Cmd_State; + +typedef struct TAG_TW_Device_Extension { + TW_Registers registers; + u32 *alignment_virtual_address[TW_Q_LENGTH]; + u32 alignment_physical_address[TW_Q_LENGTH]; + int is_unit_present[TW_MAX_UNITS]; + int num_units; + u32 *command_packet_virtual_address[TW_Q_LENGTH]; + u32 command_packet_physical_address[TW_Q_LENGTH]; + struct pci_dev *tw_pci_dev; + Scsi_Cmnd *srb[TW_Q_LENGTH]; + unsigned char free_queue[TW_Q_LENGTH]; + unsigned char free_head; + unsigned char free_tail; + unsigned char pending_queue[TW_Q_LENGTH]; + unsigned char pending_head; + unsigned char pending_tail; + TW_Cmd_State state[TW_Q_LENGTH]; + u32 posted_request_count; + u32 max_posted_request_count; + u32 request_count_marked_pending; + u32 pending_request_count; + u32 max_pending_request_count; + u32 max_sgl_entries; + u32 sgl_entries; + u32 num_aborts; + u32 num_resets; + u32 sector_count; + u32 max_sector_count; + struct Scsi_Host *host; + spinlock_t tw_lock; + unsigned char ioctl_size[TW_Q_LENGTH]; + unsigned short aen_queue[TW_Q_LENGTH]; + unsigned char aen_head; + unsigned char aen_tail; + u32 flags; +} TW_Device_Extension; + +/* Function prototypes */ +int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id); +int tw_aen_drain_queue(TW_Device_Extension *tw_dev); +int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id); +int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which); +int tw_check_bits(u32 status_reg_value); +int tw_check_errors(TW_Device_Extension *tw_dev); +void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev); +void tw_clear_host_interrupt(TW_Device_Extension *tw_dev); +void tw_disable_interrupts(TW_Device_Extension *tw_dev); +int tw_empty_response_que(TW_Device_Extension *tw_dev); +void tw_enable_interrupts(TW_Device_Extension *tw_dev); +int tw_findcards(Scsi_Host_Template *tw_host); +void tw_free_device_extension(TW_Device_Extension *tw_dev); +int tw_initconnection(TW_Device_Extension *tw_dev); +int tw_initialize_device_extension(TW_Device_Extension *tw_dev); +int tw_initialize_units(TW_Device_Extension *tw_dev); +int tw_ioctl(TW_Device_Extension *tw_dev, int request_id); +int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id); +void tw_mask_command_interrupt(TW_Device_Extension *tw_dev); +int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds); +int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id); +int tw_reset_device_extension(TW_Device_Extension *tw_dev); +int tw_reset_sequence(TW_Device_Extension *tw_dev); +int tw_scsi_biosparam(Disk *disk, kdev_t dev, int geom[]); +int tw_scsi_detect(Scsi_Host_Template *tw_host); +int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt); +int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt); +int tw_scsi_proc_info(char *buffer, char **start, off_t offset, int length, int inode, int inout); +int tw_scsi_queue(Scsi_Cmnd *cmd, void (*done) (Scsi_Cmnd *)); +int tw_scsi_release(struct Scsi_Host *tw_host); +int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id); +int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id); +int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id); +int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id); +int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id); +int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id); +int tw_setup_irq(TW_Device_Extension *tw_dev); +void tw_soft_reset(TW_Device_Extension *tw_dev); +int tw_state_request_finish(TW_Device_Extension *tw_dev,int request_id); +int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id); +void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev); + +#if defined(HOSTS_C) || defined(MODULE) +/* Scsi_Host_Template Initializer */ +#define TWXXXX { \ + next : NULL, \ + module : NULL, \ + proc_dir : &tw_scsi_proc_entry, \ + proc_info : tw_scsi_proc_info, \ + name : "3ware Storage Controller", \ + detect : tw_scsi_detect, \ + release : tw_scsi_release, \ + info : NULL, \ + ioctl : NULL, \ + command : NULL, \ + queuecommand : tw_scsi_queue, \ + eh_strategy_handler : NULL, \ + eh_abort_handler : tw_scsi_eh_abort, \ + eh_device_reset_handler : NULL, \ + abort : NULL, \ + reset : NULL, \ + slave_attach : NULL, \ + bios_param : tw_scsi_biosparam, \ + can_queue : TW_Q_LENGTH, \ + this_id: -1, \ + sg_tablesize : TW_MAX_SGL_LENGTH, \ + cmd_per_lun: TW_MAX_CMDS_PER_LUN, \ + present : 0, \ + unchecked_isa_dma : 0, \ + use_clustering : ENABLE_CLUSTERING, \ + use_new_eh_code : 1, \ + emulated : 1 \ +} +#endif /* HOSTS_C */ +#endif /* _3W_XXXX_H */ diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index 3460ab7430ef..f18655b3fd1a 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -18,6 +18,9 @@ bool 'SCSI logging facility' CONFIG_SCSI_LOGGING mainmenu_option next_comment comment 'SCSI low-level drivers' +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate '3Ware Hardware ATA-RAID support (EXPERIMENTAL)' CONFIG_BLK_DEV_3W_XXXX_RAID $CONFIG_SCSI +fi dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index c77a6ea63605..8558d08eebcc 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -639,6 +639,14 @@ ifeq ($(CONFIG_JAZZ_ESP),y) L_OBJS += NCR53C9x.o jazz_esp.o endif +ifeq ($(CONFIG_BLK_DEV_3W_XXXX_RAID),y) +L_OBJS += 3w-xxxx.o +else + ifeq ($(CONFIG_BLK_DEV_3W_XXXX_RAID),m) + M_OBJS += 3w-xxxx.o + endif +endif + include $(TOPDIR)/Rules.make 53c8xx_d.h: 53c7,8xx.scr script_asm.pl diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 557cb6594629..b563022d1417 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -339,6 +339,10 @@ #include "../net/fc/iph5526_scsi.h" #endif +#ifdef CONFIG_BLK_DEV_3W_XXXX_RAID +#include "3w-xxxx.h" +#endif + /* * Moved ppa driver to the end of the probe list * since it is a removable host adapter. @@ -610,6 +614,10 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_IPHASE5526 IPH5526_SCSI_FC, #endif +#ifdef CONFIG_BLK_DEV_3W_XXXX_RAID + TWXXXX, +#endif + /* "Removable host adapters" below this line (Parallel Port/USB/other) */ #ifdef CONFIG_SCSI_PPA PPA, diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c index e70c108411e5..d2a9416cf955 100644 --- a/drivers/sound/dmabuf.c +++ b/drivers/sound/dmabuf.c @@ -70,6 +70,14 @@ static int sound_alloc_dmap(struct dma_buffparms *dmap) if (dma_buffsize < 4096) dma_buffsize = 4096; dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); + + /* + * Now check for the Cyrix problem. + */ + + if(isa_dma_bridge_buggy==2) + dma_pagesize=32768; + dmap->raw_buf = NULL; dmap->buffsize = dma_buffsize; if (dmap->buffsize > dma_pagesize) diff --git a/include/asm-ppc/gemini_serial.h b/include/asm-ppc/gemini_serial.h index b030a5e58ce2..ded86cef1edc 100644 --- a/include/asm-ppc/gemini_serial.h +++ b/include/asm-ppc/gemini_serial.h @@ -7,10 +7,10 @@ #define BASE_BAUD (24576000 / 16) #ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#define STD_COM_FLAGS (/*ASYNC_BOOT_AUTOCONF|*/ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) #else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#define STD_COM_FLAGS (/*ASYNC_BOOT_AUTOCONF|*/ASYNC_SKIP_TEST) #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) #endif diff --git a/include/asm-sparc/ethtool.h b/include/asm-sparc/ethtool.h index bea36b6c63d3..ec4ee3884e10 100644 --- a/include/asm-sparc/ethtool.h +++ b/include/asm-sparc/ethtool.h @@ -1,7 +1,7 @@ -/* $Id: ethtool.h,v 1.1 1998/12/19 15:09:38 davem Exp $ +/* $Id: ethtool.h,v 1.1.2.1 2000/01/31 05:02:42 davem Exp $ * ethtool.h: Defines for SparcLinux ethtool. * - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ #ifndef _SPARC_ETHTOOL_H diff --git a/include/asm-sparc64/ethtool.h b/include/asm-sparc64/ethtool.h index b070ea1a4b8f..d6fafffea754 100644 --- a/include/asm-sparc64/ethtool.h +++ b/include/asm-sparc64/ethtool.h @@ -1,7 +1,7 @@ -/* $Id: ethtool.h,v 1.1 1998/12/19 15:09:40 davem Exp $ +/* $Id: ethtool.h,v 1.1.2.1 2000/01/31 05:02:43 davem Exp $ * ethtool.h: Defines for SparcLinux ethtool. * - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ #ifndef _SPARC64_ETHTOOL_H diff --git a/include/linux/cd1400.h b/include/linux/cd1400.h index d07d1e61a6fe..69f9730eb955 100644 --- a/include/linux/cd1400.h +++ b/include/linux/cd1400.h @@ -4,7 +4,7 @@ * cd1400.h -- cd1400 UART hardware info. * * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * 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 diff --git a/include/linux/cdk.h b/include/linux/cdk.h index 2180e433023c..b7576643d63b 100644 --- a/include/linux/cdk.h +++ b/include/linux/cdk.h @@ -4,7 +4,7 @@ * cdk.h -- CDK interface definitions. * * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * 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 diff --git a/include/linux/comstats.h b/include/linux/comstats.h index 066888599ae1..84ef9b2b03bc 100644 --- a/include/linux/comstats.h +++ b/include/linux/comstats.h @@ -4,7 +4,7 @@ * comstats.h -- Serial Port Stats. * * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * 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 diff --git a/include/linux/cpia.h b/include/linux/cpia.h new file mode 100644 index 000000000000..c7fbfc07c4b1 --- /dev/null +++ b/include/linux/cpia.h @@ -0,0 +1,220 @@ +#ifndef cpia_h +#define cpia_h + +/* + * CPiA Parallel Port Video4Linux driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman, + * Peter Pregler, + * Scott J. Bertin, + * VLSI Vision Ltd. + * + * 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. + */ + +#define CPIA_MAJ_VER 0 +#define CPIA_MIN_VER 5 +#define CPIA_PATCH_VER 0 + +#define CPIA_PP_MAJ_VER 0 +#define CPIA_PP_MIN_VER 5 +#define CPIA_PP_PATCH_VER 0 + +#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ +#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ + +#ifdef __KERNEL__ + +#include + +struct cpia_camera_ops +{ + /* open sets privdata to point to structure for this camera. + * Returns negative value on error, otherwise 0. + */ + int (*open)(int camnr, void **privdata); + + /* Registers callback function cb to be called with cbdata + * when an image is ready. If cb is NULL, only single image grabs + * should be used. cb should immediately call streamRead to read + * the data or data may be lost. Returns negative value on error, + * otherwise 0. + */ + int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), + void *cbdata); + + /* transferCmd sends commands to the camera. command MUST point to + * an 8 byte buffer in kernel space. data can be NULL if no extra + * data is needed. The size of the data is given by the last 2 + * bytes of comand. data must also point to memory in kernel space. + * Returns negative value on error, otherwise 0. + */ + int (*transferCmd)(void *privdata, u8 *command, u8 *data); + + /* streamStart initiates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStart)(void *privdata); + + /* streamStop terminates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStop)(void *privdata); + + /* streamRead reads a frame from the camera. buffer points to a + * buffer large enough to hold a complete frame in kernel space. + * noblock indicates if this should be a non blocking read. + * Returns the number of bytes read, or negative value on error. + */ + int (*streamRead)(void *privdata, u8 *buffer, int noblock); + + /* close disables the device until open() is called again. + * Returns negative value on error, otherwise 0. + */ + int (*close)(void *privdata); +}; + +/* cpia_register_camera is called by low level driver for each camera. + * A unique camera number is returned, or a negative value on error */ +int cpia_register_camera(struct cpia_camera_ops *ops); + +/* cpia_unregister_camera is called by low level driver when a camera + * is removed. This must not fail. */ +void cpia_unregister_camera(int camnr); + +#define CPIA_MAXCAMS 4 + +/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + + * one byte 16bit DMA alignment + */ +#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define VIDEOSIZE_SIF 2 /* 320x240 */ +#define VIDEOSIZE_QSIF 3 /* 160x120 */ +#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ +#define VIDEOSIZE_64_48 5 +#define VIDEOSIZE_128_96 6 +#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF +#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF +#define VIDEOSIZE_192_144 7 +#define VIDEOSIZE_224_168 8 +#define VIDEOSIZE_256_192 9 +#define VIDEOSIZE_288_216 10 +#define VIDEOSIZE_320_240 VIDEOSIZE_SIF +#define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINUOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ + +#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args) +#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO __FILE__":"__FUNCTION__"(%d):"fmt,##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args) +#else +#define DBG(fmn,args...) {} +#endif + +#define DEB_BYTE(p)\ + DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ + (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ + (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index e467ae9b6545..2c5a3b826063 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -49,6 +49,7 @@ #define WIN_SEEK 0x70 #define WIN_DIAGNOSE 0x90 #define WIN_SPECIFY 0x91 /* set drive geometry translation */ +#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */ #define WIN_SETIDLE1 0xE3 #define WIN_SETIDLE2 0x97 diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 269ef88ba166..e00f1fcc6e41 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -4,7 +4,7 @@ * istallion.h -- stallion intelligent multiport serial driver. * * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * 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 diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 77d7d7413636..4822bf7fc6d2 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -210,6 +210,7 @@ enum scsi_directory_inos { PROC_SCSI_IPH5526_FC, PROC_SCSI_FCAL, PROC_SCSI_I2O, + PROC_SCSI_3W_XXXX, PROC_SCSI_USB_SCSI, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, diff --git a/include/linux/stallion.h b/include/linux/stallion.h index 35274488dadf..2dc09798c1fd 100644 --- a/include/linux/stallion.h +++ b/include/linux/stallion.h @@ -4,7 +4,7 @@ * stallion.h -- stallion multiport serial driver. * * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * 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 diff --git a/include/net/irda/parameters.h b/include/net/irda/parameters.h index 0bb658942781..20f8a23c1f54 100644 --- a/include/net/irda/parameters.h +++ b/include/net/irda/parameters.h @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli * Created at: Mon Jun 7 08:47:28 1999 - * Modified at: Mon Dec 13 11:51:59 1999 + * Modified at: Sun Jan 30 14:05:14 2000 * Modified by: Dag Brattli * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -61,15 +61,15 @@ typedef union { __u8 *bp; __u16 *sp; __u32 *ip; -} pv_t; +} irda_pv_t; typedef struct { __u8 pi; __u8 pl; - pv_t pv; -} param_t; + irda_pv_t pv; +} irda_param_t; -typedef int (*PI_HANDLER)(void *self, param_t *param, int get); +typedef int (*PI_HANDLER)(void *self, irda_param_t *param, int get); typedef int (*PV_HANDLER)(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func); diff --git a/init/main.c b/init/main.c index 9abc81849bfe..dc546ec84a9c 100644 --- a/init/main.c +++ b/init/main.c @@ -111,6 +111,9 @@ extern void console_setup(char *str, int *ints); #ifdef CONFIG_PRINTER extern void lp_setup(char *str, int *ints); #endif +#ifdef CONFIG_VIDEO_CPIA_PP +extern void cpia_pp_setup(char *str, int *ints); +#endif #ifdef CONFIG_JOY_AMIGA extern void js_am_setup(char *str, int *ints); #endif @@ -492,6 +495,24 @@ static struct dev_name_struct { { "rd/c0d14p",0x3070 }, { "rd/c0d15p",0x3078 }, #endif +#if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE) + { "ida/c0d0p",0x4800 }, + { "ida/c0d1p",0x4810 }, + { "ida/c0d2p",0x4820 }, + { "ida/c0d3p",0x4830 }, + { "ida/c0d4p",0x4840 }, + { "ida/c0d5p",0x4850 }, + { "ida/c0d6p",0x4860 }, + { "ida/c0d7p",0x4870 }, + { "ida/c0d8p",0x4880 }, + { "ida/c0d9p",0x4890 }, + { "ida/c0d10p",0x48A0 }, + { "ida/c0d11p",0x48B0 }, + { "ida/c0d12p",0x48C0 }, + { "ida/c0d13p",0x48D0 }, + { "ida/c0d14p",0x48E0 }, + { "ida/c0d15p",0x48F0 }, +#endif #ifdef CONFIG_ATARI_ACSI { "ada", 0x1c00 }, { "adb", 0x1c10 }, @@ -693,6 +714,9 @@ static struct kernel_param cooked_params[] __initdata = { #ifdef CONFIG_PRINTER { "lp=", lp_setup }, #endif +#ifdef CONFIG_VIDEO_CPIA_PP + { "cpia_pp=", cpia_pp_setup }, +#endif #ifdef CONFIG_JOY_AMIGA { "js_am=", js_am_setup }, #endif diff --git a/ipc/shm.c b/ipc/shm.c index 7c21bae24d6f..4deae66d7fec 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -630,11 +630,12 @@ static unsigned long shm_nopage(struct vm_area_struct * shmd, unsigned long addr printk ("shm_nopage: id=%d invalid. Race.\n", id); return 0; } +#endif + /* This can occur on a remap */ + if (idx >= shp->shm_npages) { - printk ("shm_nopage : too large page index. id=%d\n", id); - return 0; + return NULL; } -#endif pte = __pte(shp->shm_pages[idx]); if (!pte_present(pte)) { diff --git a/kernel/exit.c b/kernel/exit.c index 66fc6fa6e0d7..8343731c951f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -473,7 +473,6 @@ repeat: if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED)) continue; read_unlock(&tasklist_lock); - current->state = TASK_RUNNING; /* We *must* do this before touching userspace! */ retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; if (!retval && stat_addr) retval = put_user((p->exit_code << 8) | 0x7f, stat_addr); @@ -486,7 +485,6 @@ repeat: current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime; current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime; read_unlock(&tasklist_lock); - current->state = TASK_RUNNING; /* We *must* do this before touching userspace! */ retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; if (!retval && stat_addr) retval = put_user(p->exit_code, stat_addr); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6b785b4c2907..0b84bfa89f96 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -245,16 +245,6 @@ ok_to_allocate: RMQUEUE_TYPE(order, 1); spin_unlock_irqrestore(&page_alloc_lock, flags); - /* - * If we can schedule, do so, and make sure to yield. - * We may be a real-time process, and if kswapd is - * waiting for us we need to allow it to run a bit. - */ - if (gfp_mask & __GFP_WAIT) { - current->policy |= SCHED_YIELD; - schedule(); - } - nopage: return 0; } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0a9e1ce6a6b3..e8fdf2b5dbe7 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.140.2.7 2000/01/13 23:44:00 davem Exp $ + * Version: $Id: tcp.c,v 1.140.2.8 2000/01/27 22:33:35 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -1283,9 +1283,6 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, continue; found_ok_skb: - /* Below we'll be accessing user memory, which might sleep, so... */ - current->state = TASK_RUNNING; - /* Lock the buffer. We can be fairly relaxed as * an interrupt will never steal a buffer we are * using unless I've missed something serious in @@ -1367,9 +1364,6 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, break; } - remove_wait_queue(sk->sleep, &wait); - current->state = TASK_RUNNING; - if(copied >= 0 && msg->msg_name) { tp->af_specific->addr2sockaddr(sk, (struct sockaddr *) msg->msg_name); @@ -1377,6 +1371,9 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, *addr_len = tp->af_specific->sockaddr_len; } + remove_wait_queue(sk->sleep, &wait); + current->state = TASK_RUNNING; + /* Clean up data we have read: This will do ACK frames. */ cleanup_rbuf(sk, copied); release_sock(sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4bf48a53c054..0b7ede133acd 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.164.2.11 2000/01/13 04:27:59 davem Exp $ + * Version: $Id: tcp_input.c,v 1.164.2.12 2000/01/31 20:43:36 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -2415,7 +2415,8 @@ step6: * BSD 4.4 also does reset. */ if ((sk->shutdown & RCV_SHUTDOWN) && sk->dead) { - if (after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { + if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && + after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { tcp_reset(sk); return 1; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9817b475c7dc..1c17a87fc2f5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -753,12 +753,16 @@ void tcp_send_fin(struct sock *sk) } } else { /* Socket is locked, keep trying until memory is available. */ - do { + for (;;) { skb = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header), 1, GFP_KERNEL); - } while (skb == NULL); + if (skb) + break; + current->policy |= SCHED_YIELD; + schedule(); + } /* Reserve space for headers and prepare control bits. */ skb_reserve(skb, MAX_HEADER + sk->prot->max_header); diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c index 3c9ff35da70e..1cef4d5d8a80 100644 --- a/net/irda/ircomm/ircomm_param.c +++ b/net/irda/ircomm/ircomm_param.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli * Created at: Mon Jun 7 10:25:11 1999 - * Modified at: Tue Dec 14 15:26:30 1999 + * Modified at: Sun Jan 30 14:32:03 2000 * Modified by: Dag Brattli * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -40,19 +40,27 @@ #include -static int ircomm_param_service_type(void *instance, param_t *param, int get); -static int ircomm_param_port_type(void *instance, param_t *param, int get); -static int ircomm_param_port_name(void *instance, param_t *param, int get); -static int ircomm_param_service_type(void *instance, param_t *param, int get); -static int ircomm_param_data_rate(void *instance, param_t *param, int get); -static int ircomm_param_data_format(void *instance, param_t *param, int get); -static int ircomm_param_flow_control(void *instance, param_t *param, int get); -static int ircomm_param_xon_xoff(void *instance, param_t *param, int get); -static int ircomm_param_enq_ack(void *instance, param_t *param, int get); -static int ircomm_param_line_status(void *instance, param_t *param, int get); -static int ircomm_param_dte(void *instance, param_t *param, int get); -static int ircomm_param_dce(void *instance, param_t *param, int get); -static int ircomm_param_poll(void *instance, param_t *param, int get); +static int ircomm_param_service_type(void *instance, irda_param_t *param, + int get); +static int ircomm_param_port_type(void *instance, irda_param_t *param, + int get); +static int ircomm_param_port_name(void *instance, irda_param_t *param, + int get); +static int ircomm_param_service_type(void *instance, irda_param_t *param, + int get); +static int ircomm_param_data_rate(void *instance, irda_param_t *param, + int get); +static int ircomm_param_data_format(void *instance, irda_param_t *param, + int get); +static int ircomm_param_flow_control(void *instance, irda_param_t *param, + int get); +static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get); +static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get); +static int ircomm_param_line_status(void *instance, irda_param_t *param, + int get); +static int ircomm_param_dte(void *instance, irda_param_t *param, int get); +static int ircomm_param_dce(void *instance, irda_param_t *param, int get); +static int ircomm_param_poll(void *instance, irda_param_t *param, int get); static pi_minor_info_t pi_minor_call_table_common[] = { { ircomm_param_service_type, PV_INT_8_BITS }, @@ -170,7 +178,8 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) * query and then the remote device sends its initial paramters * */ -static int ircomm_param_service_type(void *instance, param_t *param, int get) +static int ircomm_param_service_type(void *instance, irda_param_t *param, + int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; __u8 service_type = param->pv.b; /* We know it's a one byte integer */ @@ -229,7 +238,7 @@ static int ircomm_param_service_type(void *instance, param_t *param, int get) * Since we only advertise serial service, this parameter should only * be equal to IRCOMM_SERIAL. */ -static int ircomm_param_port_type(void *instance, param_t *param, int get) +static int ircomm_param_port_type(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -253,7 +262,7 @@ static int ircomm_param_port_type(void *instance, param_t *param, int get) * Exchange port name * */ -static int ircomm_param_port_name(void *instance, param_t *param, int get) +static int ircomm_param_port_name(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -276,7 +285,7 @@ static int ircomm_param_port_name(void *instance, param_t *param, int get) * Exchange data rate to be used in this settings * */ -static int ircomm_param_data_rate(void *instance, param_t *param, int get) +static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -299,7 +308,8 @@ static int ircomm_param_data_rate(void *instance, param_t *param, int get) * Exchange data format to be used in this settings * */ -static int ircomm_param_data_format(void *instance, param_t *param, int get) +static int ircomm_param_data_format(void *instance, irda_param_t *param, + int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -320,7 +330,8 @@ static int ircomm_param_data_format(void *instance, param_t *param, int get) * Exchange flow control settings to be used in this settings * */ -static int ircomm_param_flow_control(void *instance, param_t *param, int get) +static int ircomm_param_flow_control(void *instance, irda_param_t *param, + int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -343,7 +354,7 @@ static int ircomm_param_flow_control(void *instance, param_t *param, int get) * Exchange XON/XOFF characters * */ -static int ircomm_param_xon_xoff(void *instance, param_t *param, int get) +static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -370,7 +381,7 @@ static int ircomm_param_xon_xoff(void *instance, param_t *param, int get) * Exchange ENQ/ACK characters * */ -static int ircomm_param_enq_ack(void *instance, param_t *param, int get) +static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; @@ -397,7 +408,8 @@ static int ircomm_param_enq_ack(void *instance, param_t *param, int get) * * */ -static int ircomm_param_line_status(void *instance, param_t *param, int get) +static int ircomm_param_line_status(void *instance, irda_param_t *param, + int get) { IRDA_DEBUG(2, __FUNCTION__ "(), not impl.\n"); @@ -410,7 +422,7 @@ static int ircomm_param_line_status(void *instance, param_t *param, int get) * If we get here, there must be some sort of null-modem connection, and * we are probably working in server mode as well. */ -static int ircomm_param_dte(void *instance, param_t *param, int get) +static int ircomm_param_dte(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; __u8 dte; @@ -453,7 +465,7 @@ static int ircomm_param_dte(void *instance, param_t *param, int get) * * */ -static int ircomm_param_dce(void *instance, param_t *param, int get) +static int ircomm_param_dce(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; __u8 dce; @@ -485,7 +497,7 @@ static int ircomm_param_dce(void *instance, param_t *param, int get) * Called when the peer device is polling for the line settings * */ -static int ircomm_param_poll(void *instance, param_t *param, int get) +static int ircomm_param_poll(void *instance, irda_param_t *param, int get) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; diff --git a/net/irda/irttp.c b/net/irda/irttp.c index c7333d90dda4..b2e3750b2b9c 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -6,7 +6,7 @@ * Status: Stable * Author: Dag Brattli * Created at: Sun Aug 31 20:14:31 1997 - * Modified at: Sun Jan 16 22:46:33 2000 + * Modified at: Sun Jan 30 14:30:32 2000 * Modified by: Dag Brattli * * Copyright (c) 1998-2000 Dag Brattli , @@ -60,7 +60,8 @@ static void irttp_flush_queues(struct tsap_cb *self); static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb); static void irttp_start_todo_timer(struct tsap_cb *self, int timeout); static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self); -static int irttp_param_max_sdu_size(void *instance, param_t *param, int get); +static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, + int get); /* Information for parsing parameters in IrTTP */ static pi_minor_info_t pi_minor_call_table[] = { @@ -1399,7 +1400,8 @@ static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb) * will be called both when this parameter needs to be inserted into, and * extracted from the connect frames */ -static int irttp_param_max_sdu_size(void *instance, param_t *param, int get) +static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, + int get) { struct tsap_cb *self; diff --git a/net/irda/parameters.c b/net/irda/parameters.c index 19f76eae48f6..6d2d19289f03 100644 --- a/net/irda/parameters.c +++ b/net/irda/parameters.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli * Created at: Mon Jun 7 10:25:11 1999 - * Modified at: Tue Dec 14 16:03:57 1999 + * Modified at: Sun Jan 30 14:08:39 2000 * Modified by: Dag Brattli * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -78,7 +78,7 @@ static PV_HANDLER pv_insert_table[] = { static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func) { - param_t p; + irda_param_t p; int ret; p.pi = pi; @@ -105,7 +105,7 @@ static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func) { - param_t p; + irda_param_t p; int ret; /* Extract values anyway, since handler may need them */ @@ -129,7 +129,7 @@ static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func) { - param_t p; + irda_param_t p; int n = 0; int err; @@ -202,7 +202,7 @@ static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func) { - param_t p; + irda_param_t p; int n = 0; int err; @@ -273,7 +273,7 @@ static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func) { char str[33]; - param_t p; + irda_param_t p; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); @@ -319,7 +319,7 @@ static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, PV_TYPE type, PI_HANDLER func) { - param_t p; + irda_param_t p; p.pi = pi; /* In case handler needs to know */ p.pl = buf[1]; /* Extract lenght of value */ @@ -346,10 +346,10 @@ static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, */ int irda_param_pack(__u8 *buf, char *fmt, ...) { + irda_pv_t arg; va_list args; char *p; int n = 0; - pv_t arg; va_start(args, fmt); @@ -390,10 +390,10 @@ int irda_param_pack(__u8 *buf, char *fmt, ...) */ int irda_param_unpack(__u8 *buf, char *fmt, ...) { + irda_pv_t arg; va_list args; char *p; int n = 0; - pv_t arg; va_start(args, fmt); diff --git a/net/irda/qos.c b/net/irda/qos.c index 2c2c39b85a66..10a7765cb8cb 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -6,10 +6,10 @@ * Status: Stable * Author: Dag Brattli * Created at: Tue Sep 9 00:00:26 1997 - * Modified at: Sun Dec 12 13:47:09 1999 + * Modified at: Sun Jan 30 14:29:16 2000 * Modified by: Dag Brattli * - * Copyright (c) 1998-1999 Dag Brattli , + * Copyright (c) 1998-2000 Dag Brattli , * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -43,13 +43,18 @@ #define CI_BZIP2 27 /* Random pick */ #endif -static int irlap_param_baud_rate(void *instance, param_t *param, int get); -static int irlap_param_link_disconnect(void *instance, param_t *parm, int get); -static int irlap_param_max_turn_time(void *instance, param_t *param, int get); -static int irlap_param_data_size(void *instance, param_t *param, int get); -static int irlap_param_window_size(void *instance, param_t *param, int get); -static int irlap_param_additional_bofs(void *instance, param_t *parm, int get); -static int irlap_param_min_turn_time(void *instance, param_t *param, int get); +static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get); +static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, + int get); +static int irlap_param_max_turn_time(void *instance, irda_param_t *param, + int get); +static int irlap_param_data_size(void *instance, irda_param_t *param, int get); +static int irlap_param_window_size(void *instance, irda_param_t *param, + int get); +static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, + int get); +static int irlap_param_min_turn_time(void *instance, irda_param_t *param, + int get); __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, @@ -345,7 +350,7 @@ int irlap_insert_qos_negotiation_params(struct irlap_cb *self, * Negotiate data-rate * */ -static int irlap_param_baud_rate(void *instance, param_t *param, int get) +static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get) { __u16 final; @@ -380,7 +385,8 @@ static int irlap_param_baud_rate(void *instance, param_t *param, int get) * Negotiate link disconnect/threshold time. * */ -static int irlap_param_link_disconnect(void *instance, param_t *param, int get) +static int irlap_param_link_disconnect(void *instance, irda_param_t *param, + int get) { __u16 final; @@ -413,7 +419,8 @@ static int irlap_param_link_disconnect(void *instance, param_t *param, int get) * will be negotiated independently for each station * */ -static int irlap_param_max_turn_time(void *instance, param_t *param, int get) +static int irlap_param_max_turn_time(void *instance, irda_param_t *param, + int get) { struct irlap_cb *self = (struct irlap_cb *) instance; @@ -435,7 +442,7 @@ static int irlap_param_max_turn_time(void *instance, param_t *param, int get) * will be negotiated independently for each station * */ -static int irlap_param_data_size(void *instance, param_t *param, int get) +static int irlap_param_data_size(void *instance, irda_param_t *param, int get) { struct irlap_cb *self = (struct irlap_cb *) instance; @@ -457,7 +464,8 @@ static int irlap_param_data_size(void *instance, param_t *param, int get) * will be negotiated independently for each station * */ -static int irlap_param_window_size(void *instance, param_t *param, int get) +static int irlap_param_window_size(void *instance, irda_param_t *param, + int get) { struct irlap_cb *self = (struct irlap_cb *) instance; @@ -478,7 +486,7 @@ static int irlap_param_window_size(void *instance, param_t *param, int get) * Negotiate additional BOF characters. This is a type 1 parameter and * will be negotiated independently for each station. */ -static int irlap_param_additional_bofs(void *instance, param_t *param, int get) +static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get) { struct irlap_cb *self = (struct irlap_cb *) instance; @@ -499,7 +507,8 @@ static int irlap_param_additional_bofs(void *instance, param_t *param, int get) * Negotiate the minimum turn around time. This is a type 1 parameter and * will be negotiated independently for each station */ -static int irlap_param_min_turn_time(void *instance, param_t *param, int get) +static int irlap_param_min_turn_time(void *instance, irda_param_t *param, + int get) { struct irlap_cb *self = (struct irlap_cb *) instance; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 222a3f9ecfe8..eaaf85640fee 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -412,19 +412,16 @@ __rpc_execute(struct rpc_task *task) } } - /* - * No handler for next step means exit. - */ - if (!task->tk_action) - break; - /* * Perform the next FSM step. * tk_action may be NULL when the task has been killed * by someone else. */ - if (RPC_IS_RUNNING(task) && task->tk_action) + if (RPC_IS_RUNNING(task)) { + if (!task->tk_action) + break; task->tk_action(task); + } /* * Check whether task is sleeping. -- 2.39.5