From e6a3000eff827e94ff6c29f795a7918ba5fcfa86 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:39:01 -0500 Subject: [PATCH] Linux 2.4.0-test9pre5 - Make SCSI initialization order be same as before. - fix cardbus bridge resources.. - don't disallow Onstream ide-scsi devices - byteorder: use statement expressions instead of macros, to avoid argument re-use. - codafs update - more USB updates - _fput/__fput are no longer used. - ixj telephony driver fixes - pmac SCSI driver init update - Andries: net device name allocation as in 2.2.x - sis900 driver update - more drivers synced to Alan's 2.2.x changes --- CREDITS | 1 + Documentation/Configure.help | 15 + Documentation/SubmittingDrivers | 112 + Documentation/cciss.txt | 47 + Documentation/mkdev.cciss | 40 + Documentation/usb/error-codes.txt | 4 + Documentation/usb/ov511.txt | 46 +- MAINTAINERS | 17 +- Makefile | 1 + arch/alpha/config.in | 2 + arch/arm/config.in | 1 + arch/arm/kernel/via82c505.c | 1 + arch/arm/mach-shark/arch.c | 1 - arch/arm/mach-shark/pci.c | 1 - arch/i386/config.in | 2 + arch/i386/defconfig | 58 +- arch/i386/kernel/bluesmoke.c | 10 +- arch/i386/kernel/mtrr.c | 50 +- arch/ia64/config.in | 1 + arch/m68k/config.in | 2 + arch/mips/config.in | 2 + arch/mips64/config.in | 2 + arch/ppc/8260_io/fcc_enet.c | 1 + arch/ppc/config.in | 1 + arch/ppc/lib/string.S | 1 + arch/ppc/mm/extable.c | 1 + arch/sh/config.in | 2 + arch/sparc/config.in | 10 +- arch/sparc64/config.in | 10 +- drivers/Makefile | 11 +- drivers/block/Config.in | 14 +- drivers/block/Makefile | 15 +- drivers/block/cciss.c | 1913 ++++++++ drivers/block/cciss.h | 201 + drivers/block/cciss_cmd.h | 254 ++ drivers/block/cpqarray.c | 30 +- drivers/block/cpqarray.h | 2 + drivers/block/ida_ioctl.h | 9 +- drivers/block/ll_rw_blk.c | 2 + drivers/block/loop.c | 3 +- drivers/block/swim3.c | 1 + drivers/char/drm/mga_dma.c | 11 +- drivers/char/drm/r128_drv.h | 2 +- drivers/char/qpmouse.c | 7 +- drivers/isdn/hisax/hfc_pci.c | 2 + drivers/macintosh/rtc.c | 1 - drivers/md/Config.in | 22 + drivers/md/Makefile | 35 + drivers/{block => md}/linear.c | 0 drivers/{block => md}/md.c | 0 drivers/{block => md}/raid0.c | 0 drivers/{block => md}/raid1.c | 0 drivers/{block => md}/raid5.c | 0 drivers/{block => md}/xor.c | 0 drivers/media/radio/Config.in | 1 + drivers/media/radio/Makefile | 1 + drivers/media/radio/radio-maestro.c | 384 ++ drivers/media/video/buz.c | 1 + drivers/net/3c505.c | 12 +- drivers/net/3c527.c | 2 +- drivers/net/Space.c | 2 +- drivers/net/acenic.c | 2 +- drivers/net/appletalk/ipddp.c | 3 + drivers/net/cs89x0.c | 1 + drivers/net/ne2.c | 6 +- drivers/net/net_init.c | 17 +- drivers/net/pppox.c | 1 - drivers/net/sis900.c | 154 +- drivers/net/sis900.h | 16 +- drivers/net/wan/sdla.c | 6 + drivers/pci/pci.c | 7 + drivers/s390/Config.in | 11 +- drivers/scsi/ChangeLog.ips | 17 +- drivers/scsi/Config.in | 27 +- drivers/scsi/Makefile | 111 +- drivers/scsi/cpqfc.Readme | 216 + drivers/scsi/cpqfcTS.h | 39 + drivers/scsi/cpqfcTSchip.h | 238 + drivers/scsi/cpqfcTScontrol.c | 2200 ++++++++++ drivers/scsi/cpqfcTSi2c.c | 494 +++ drivers/scsi/cpqfcTSinit.c | 1815 ++++++++ drivers/scsi/cpqfcTSioctl.h | 84 + drivers/scsi/cpqfcTSstructs.h | 1494 +++++++ drivers/scsi/cpqfcTStrigger.c | 30 + drivers/scsi/cpqfcTSworker.c | 6238 +++++++++++++++++++++++++++ drivers/scsi/cpqioctl.c | 76 + drivers/scsi/hosts.c | 2 - drivers/scsi/ide-scsi.c | 10 - drivers/scsi/ips.c | 3195 ++++++++++---- drivers/scsi/ips.h | 137 +- drivers/scsi/mac53c94.c | 22 + drivers/scsi/mac53c94.h | 2 + drivers/scsi/mesh.c | 25 + drivers/scsi/mesh.h | 2 + drivers/scsi/scsi.c | 15 +- drivers/scsi/scsi_module.c | 2 +- drivers/scsi/scsi_scan.c | 4 + drivers/scsi/scsi_syms.c | 3 - drivers/scsi/sd.c | 61 +- drivers/scsi/sym53c8xx.c | 1 + drivers/sound/softoss.c | 3 + drivers/sound/soundcard.c | 1 + drivers/sound/uart401.c | 2 +- drivers/telephony/ixj.c | 116 +- drivers/telephony/phonedev.c | 1 - drivers/usb/devio.c | 2 +- drivers/usb/hub.c | 534 ++- drivers/usb/hub.h | 29 +- drivers/usb/net1080.c | 1 + drivers/usb/ov511.c | 683 ++- drivers/usb/ov511.h | 11 +- drivers/usb/scanner.c | 8 + drivers/usb/scanner.h | 16 +- drivers/usb/storage/freecom.c | 206 +- drivers/usb/storage/scsiglue.c | 6 +- drivers/usb/storage/shuttle_usbat.c | 160 +- drivers/usb/storage/transport.c | 33 +- drivers/usb/storage/usb.c | 48 +- drivers/usb/uhci.c | 62 +- drivers/usb/usb.c | 2 - fs/Config.in | 7 +- fs/coda/cache.c | 172 +- fs/coda/cnode.c | 89 +- fs/coda/coda_linux.c | 7 - fs/coda/dir.c | 43 +- fs/coda/file.c | 9 +- fs/coda/inode.c | 128 +- fs/coda/pioctl.c | 5 +- fs/coda/psdev.c | 93 +- fs/coda/symlink.c | 1 - fs/coda/sysctl.c | 11 +- fs/coda/upcall.c | 25 +- fs/file_table.c | 57 +- fs/smbfs/inode.c | 9 +- fs/ufs/ialloc.c | 4 +- include/asm-ppc/prom.h | 2 + include/linux/byteorder/swab.h | 18 +- include/linux/cciss_ioctl.h | 186 + include/linux/coda.h | 90 +- include/linux/coda_cache.h | 14 +- include/linux/coda_fs_i.h | 7 +- include/linux/coda_linux.h | 16 +- include/linux/coda_psdev.h | 15 +- include/linux/major.h | 9 + include/linux/pci_ids.h | 2 + include/linux/usb.h | 1 - include/linux/usbdevice_fs.h | 1 + include/scsi/scsi_ioctl.h | 7 + net/core/dev.c | 70 +- net/ipv4/netfilter/ipt_REJECT.c | 9 +- net/ipv4/tcp_input.c | 1 + 151 files changed, 20920 insertions(+), 2276 deletions(-) create mode 100644 Documentation/SubmittingDrivers create mode 100644 Documentation/cciss.txt create mode 100644 Documentation/mkdev.cciss create mode 100644 drivers/block/cciss.c create mode 100644 drivers/block/cciss.h create mode 100644 drivers/block/cciss_cmd.h create mode 100644 drivers/md/Config.in create mode 100644 drivers/md/Makefile rename drivers/{block => md}/linear.c (100%) rename drivers/{block => md}/md.c (100%) rename drivers/{block => md}/raid0.c (100%) rename drivers/{block => md}/raid1.c (100%) rename drivers/{block => md}/raid5.c (100%) rename drivers/{block => md}/xor.c (100%) create mode 100644 drivers/media/radio/radio-maestro.c create mode 100644 drivers/scsi/cpqfc.Readme create mode 100644 drivers/scsi/cpqfcTS.h create mode 100644 drivers/scsi/cpqfcTSchip.h create mode 100644 drivers/scsi/cpqfcTScontrol.c create mode 100644 drivers/scsi/cpqfcTSi2c.c create mode 100644 drivers/scsi/cpqfcTSinit.c create mode 100644 drivers/scsi/cpqfcTSioctl.h create mode 100644 drivers/scsi/cpqfcTSstructs.h create mode 100644 drivers/scsi/cpqfcTStrigger.c create mode 100644 drivers/scsi/cpqfcTSworker.c create mode 100644 drivers/scsi/cpqioctl.c create mode 100644 include/linux/cciss_ioctl.h diff --git a/CREDITS b/CREDITS index 0b622d38194d..a9f3e219d564 100644 --- a/CREDITS +++ b/CREDITS @@ -734,6 +734,7 @@ S: USA N: Johannes Erdfelt E: jerdfelt@valinux.com +E: johannes@erdfelt.com D: Linux/IA-64 bootloader and kernel goop, USB S: 6350 Stoneridge Mall Road S: Pleasanton, CA 94588 diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 77db7bb7b7ac..b7c7d6438f2d 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -11339,6 +11339,21 @@ CONFIG_SMB_FS want), say M here and read Documentation/modules.txt. The module will be called smbfs.o. Most people say N, however. +use nls by default +CONFIG_SMB_NLS_DEFAULT + Enabling this will make smbfs use nls translations by default. You + need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls + settings and you need to give the default nls for the SMB server as + CONFIG_SMB_NLS_REMOTE. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + Currently no smbmount distributed with samba supports this, it is + assumed future versions will. In the meantime you can get an + unofficial patch for samba 2.0.7 from: + http://www.hojdpunkten.ac.se/054/samba/index.html + nls support setting CONFIG_SMB_NLS_REMOTE This setting allows you to specify a default value for which diff --git a/Documentation/SubmittingDrivers b/Documentation/SubmittingDrivers new file mode 100644 index 000000000000..29c5973851e7 --- /dev/null +++ b/Documentation/SubmittingDrivers @@ -0,0 +1,112 @@ +Submitting Drivers For The Linux Kernel +--------------------------------------- + +This document is intended to explain how to submit device drivers to the +Linux 2.2 and 2.4test kernel trees. Note that if you are interested in video +card drivers you should probably talk to XFree86 (http://wwww.xfree86.org) +instead. + +Allocating Device Numbers +------------------------- + +Major and minor numbers for devices are allocated by the Linux assigned name +and number authority (currently better known as H Peter Anvin). The +site is http://www.lanana.org/. This also deals with allocating numbers for +devices that are not going to be submitted to the mainstream kernel. + +If you don't use assigned numbers then when you device is submitted it will +get given an assigned number even if that is different from values you may +have shipped to customers before. + +Who To Submit Drivers To +------------------------ + +Linux 2.0: + No new drivers are accepted for this kernel tree + +Linux 2.2: + If the code area has a general maintainer then please submit it to + the maintainer listed in MAINTAINERS in the kernel file. If the + maintainer does not respond or you cannot find the appropriate + maintainer then please contact Alan Cox + +Linux 2.4test: + This kernel tree is under active development. The same rules apply + as 2.2 but you may wish to submit your driver via linux-kernel (see + resources) and follow that list to track changes in API's. These + should no longer be occuring as we are now in a code freeze. + The final contact point for Linux 2.4 submissions is + . + +What Criteria Determine Acceptance +---------------------------------- + +Licensing: The code must be released to us under the GNU public license. + We don't insist on any kind of exclusively GPL licensing, + and if you wish the driver to be useful to other communities + such as BSD you may well wish to release under multiple + licenses. + +Interfaces: If your driver uses existing interfaces and behaves like + other drivers in the same class it will be much more likely + to be accepted than if it invents gratuitous new ones. + If you need to implement a common API over Linux and NT + drivers do it in userspace. + +Code: Please use the Linux style of code formatting as documented + in Documentation/CodingStyle. If you have sections of code + that need to be in other formats, for example because they + are shared with a windows driver kit and you want to + maintain them just once seperate them out nicely and note + this fact. + +Portability: Pointers are not always 32bits, people do not all have + floating point and you shouldn't use inline x86 assembler in + your driver without careful thought. Pure x86 drivers + generally are not popular. If you only have x86 hardware it + is hard to test portability but it is easy to make sure the + code can easily be made portable. + +Clarity: It helps if anyone can see how to fix the driver. It helps + you because you get patches not bug reports. If you submit a + driver that intentionally obfuscates how the hardware works + it will go in the bitbucket. + +Control: In general if there is active maintainance of a driver by + the author then patches will be redirected to them unless + they are totally obvious and without need of checking. + If you want to be the contact and update point for the + driver it is a good idea to state this in the comments. + +What Criteria Do Not Determine Acceptance +----------------------------------------- + +Vendor: Being the hardware vendor and maintaining the driver is + often a good thing. If there is a stable working driver from + other people already in the tree don't expect 'we are the + vendor' to get your driver chosen. Ideally work with the + existing driver author to build a single perfect driver. + +Author: It doesn't matter if a large Linux company wrote the driver, + or you did. Nobody has any special access to the kernel + tree. Anyone who tells you otherwise isn't telling the + whole story. + + +Resources +--------- + +Linux kernel master tree: + ftp.kernel.org:/pub/linux/kernel/... + +Linux kernel mailing list: + linux-kernel@vger.kernel.org + [mail majordomo@vger.kernel.org to subscribe] + +Kernel traffic: + Weekly summary of kernel list activity (much easier to read) + [http://kt.linuxcare.com/kernel-traffic] + +Linux USB project: + http://sourceforge.net/projects/linux-usb/ + diff --git a/Documentation/cciss.txt b/Documentation/cciss.txt new file mode 100644 index 000000000000..56d4d7a6ae92 --- /dev/null +++ b/Documentation/cciss.txt @@ -0,0 +1,47 @@ +This driver is for Compaq's SMART Array Controllers. + +Supported Cards: +---------------- + +This driver is known to work with the following cards: + + * SA 5300 + +If notes are not already created in the /dev/cciss directory + +# mkdev.cciss [ctlrs] + +Where ctlrs is the number of controllers you have (defaults to 1 if not +specified). + +Device Naming: +-------------- + +You need some entries in /dev for the cciss device. The mkdev.cciss script +can make device nodes for you automatically. Currently the device setup +is as follows: + +Major numbers: + 104 cciss0 + 105 cciss1 + 106 cciss2 + etc... + +Minor numbers: + b7 b6 b5 b4 b3 b2 b1 b0 + |----+----| |----+----| + | | + | +-------- Partition ID (0=wholedev, 1-15 partition) + | + +-------------------- Logical Volume number + +The suggested device naming scheme is: +/dev/cciss/c0d0 Controller 0, disk 0, whole device +/dev/cciss/c0d0p1 Controller 0, disk 0, partition 1 +/dev/cciss/c0d0p2 Controller 0, disk 0, partition 2 +/dev/cciss/c0d0p3 Controller 0, disk 0, partition 3 + +/dev/cciss/c1d1 Controller 1, disk 1, whole device +/dev/cciss/c1d1p1 Controller 1, disk 1, partition 1 +/dev/cciss/c1d1p2 Controller 1, disk 1, partition 2 +/dev/cciss/c1d1p3 Controller 1, disk 1, partition 3 diff --git a/Documentation/mkdev.cciss b/Documentation/mkdev.cciss new file mode 100644 index 000000000000..fbbaf30a7175 --- /dev/null +++ b/Documentation/mkdev.cciss @@ -0,0 +1,40 @@ +#!/bin/sh +# Script to create device nodes for SMART array controllers +# Usage: +# mkdev.cciss [num controllers] [num log volumes] [num partitions] +# +# With no arguments, the script assumes 1 controller, 16 logical volumes, +# and 16 partitions/volume, which is adequate for most configurations. +# +# If you had 5 controllers and were planning on no more than 4 logical volumes +# each, using a maximum of 8 partitions per volume, you could say: +# +# mkdev.cciss 5 4 8 +# +# Of course, this has no real benefit over "mkdev.cciss 5" except that it +# doesn't create so many device nodes in /dev/cciss. + +NR_CTLR=${1-1} +NR_VOL=${2-16} +NR_PART=${3-16} + +if [ ! -d /dev/cciss ]; then + mkdir -p /dev/cciss +fi + +C=0; while [ $C -lt $NR_CTLR ]; do + MAJ=`expr $C + 104` + D=0; while [ $D -lt $NR_VOL ]; do + P=0; while [ $P -lt $NR_PART ]; do + MIN=`expr $D \* 16 + $P` + if [ $P -eq 0 ]; then + mknod /dev/cciss/c${C}d${D} b $MAJ $MIN + else + mknod /dev/cciss/c${C}d${D}p${P} b $MAJ $MIN + fi + P=`expr $P + 1` + done + D=`expr $D + 1` + done + C=`expr $C + 1` +done diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt index 836661375990..7d67ac67e087 100644 --- a/Documentation/usb/error-codes.txt +++ b/Documentation/usb/error-codes.txt @@ -42,6 +42,10 @@ USB_ST_URB_INVALID_ERROR -EMSGSIZE endpoint message size is zero, do interface/alternate setting +USB_ST_BANDWIDTH_ERROR +-ENOSPC The host controller's bandwidth is already consumed and + this request would push it past its allowed limit. + ************************************************************************** * Error codes returned by in urb->status * diff --git a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt index b15e6817652e..304a1ec9051b 100644 --- a/Documentation/usb/ov511.txt +++ b/Documentation/usb/ov511.txt @@ -6,14 +6,16 @@ Author: Mark McClelland Homepage: http://alpha.dyndns.org/ov511 NEW IN THIS VERSION: - o Sensor detection fixes - o More efficient/reliable buffer allocation - o Many minor fixes + o Stability improvements + o Support for hue control + o 160x120 mostly working + o OV6620 color problems fixed + o More WebCam 3 detection improvements INTRODUCTION: This is a driver for the OV511, a USB-only chip used in many "webcam" devices. -Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work.It +Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It supports streaming and capture of color or monochrome video via the Video4Linux API. Most V4L apps are compatible with it, but a few videoconferencing programs do not work yet. The following resolutions are supported: 640x480, 448x336, @@ -195,20 +197,28 @@ MODULE PARAMETERS: both OV511 and OV511+ cameras, trial-and-error may be necessary for finding the optimum setting. - + NAME: retry_sync + TYPE: boolean + DEFAULT: 0 + DESC: Prevent apps from timing out if frame is not done in time. This is + useful if you are having problems with Xawtv getting "stuck" on a frame + when your system is under heavy load. + WORKING FEATURES: - o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 - o YUV420 and YUV422P color + o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, 320x240, and + 160x120 + o RGB24, YUV420, YUV422, YUYV, and YUV422P color o Monochrome - o Setting/getting of saturation, contrast and brightness (no hue yet; only - works with OV7610, not the OV7620 or OV7620AE) + o Setting/getting of saturation, contrast, brightness, and hue (only some of + them work the OV7620 and OV7620AE) o proc status reporting EXPERIMENTAL FEATURES: o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and corrupted frames. If you have a very fast CPU, you can try it. o Snapshot mode (only works with some read() based apps; see below for more) - o read() support + o OV6620 sensor support + o GBR422 parsing TODO: o Fix the noise / grainy image problem. @@ -216,25 +226,23 @@ TODO: frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works, so we can't really work on that yet. Please kindly inform OmniVision that you would like them to release their specifications to the Linux community. - o Get 160x120 working - o YUV422 (and other color modes) + o YUV422 o Get snapshot mode working with mmap(). o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. - o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture() - and ov511_set_picture()) o Get autoadjust disable working o V4L2 support (Probably not until it goes into the kernel) - o Fix I2C initialization. Some people are reporting problems with reading the - 7610 registers. This could be due to timing differences, an excessive I2C - clock rate, or a problem with ov511_i2c_read(). + o Creative WebCam III has problems initializing its sensor. This should be + fixed now, but if you still have problems let me know. o Get rid of the memory management functions (put them in videodev.c??) - o Setting of contrast and brightness not working with 7620 + o Setting of contrast and brightness not working with 7620/7620AE o Driver/camera state save/restore for when USB supports suspend/resume o Unstable on SMP systems + o OV7620/OV6620 experience frame corruption with moving objects + o OV6620 is too dark HOW TO CONTACT ME: -You can email me at mmcclelland@delphi.com . Please prefix the subject line +You can email me at mwm@i.am . Please prefix the subject line with "OV511: " so that I am certain to notice your message. CREDITS: diff --git a/MAINTAINERS b/MAINTAINERS index 8449983b3a19..2991c4860bf7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -210,6 +210,20 @@ M: jgarzik@mandrakesoft.com L: linux-fbdev@vuser.vu.union.edu S: Maintained +COMPAQ SMART CISS RAID DRIVER +P: Charles White +M: Charles White +L: compaqandlinux@cpqlin.van-dijk.net +W: ftp.compaq.com/pub/products/drivers/linux +S: Supported + +COMPAQ FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA +P: Amy Vanzant-Hodge +M: Amy Vanzant-Hodge (fibrechannel@compaq.com) +L: compaqandlinux@cpqlin.van-dijk.net +W: ftp.compaq.com/pub/products/drivers/linux +S: Supported + COMPAQ SMART2 RAID DRIVER P: Charles White M: Charles White @@ -1221,7 +1235,8 @@ S: Supported USB HUB P: Johannes Erdfelt -M: jerdfelt@sventech.com +M: jerdfelt@valinux.com +M: johannes@erdfelt.com L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net S: Maintained diff --git a/Makefile b/Makefile index 5e5646f51322..d9bb0d0cced1 100644 --- a/Makefile +++ b/Makefile @@ -176,6 +176,7 @@ DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda.o DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o DRIVERS-$(CONFIG_ACPI_INTERPRETER) += drivers/acpi/acpi.o +DRIVERS-$(CONFIG_BLK_DEV_MD) += drivers/md/mddev.o DRIVERS += $(DRIVERS-y) diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 6485df4af2a8..3b0126e7d15e 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -239,6 +239,8 @@ source drivers/pnp/Config.in source drivers/block/Config.in +source drivers/md/Config.in + if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi diff --git a/arch/arm/config.in b/arch/arm/config.in index 2e229add2f43..1a79b55b997f 100644 --- a/arch/arm/config.in +++ b/arch/arm/config.in @@ -301,6 +301,7 @@ source drivers/parport/Config.in source drivers/mtd/Config.in source drivers/pnp/Config.in source drivers/block/Config.in +source drivers/md/Config.in if [ "$CONFIG_ARCH_ACORN" = "y" ]; then source drivers/acorn/block/Config.in diff --git a/arch/arm/kernel/via82c505.c b/arch/arm/kernel/via82c505.c index 1cdee4a3327f..42a2bb448a04 100644 --- a/arch/arm/kernel/via82c505.c +++ b/arch/arm/kernel/via82c505.c @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/arch/arm/mach-shark/arch.c b/arch/arm/mach-shark/arch.c index aa7e6f340658..87ebe0270903 100644 --- a/arch/arm/mach-shark/arch.c +++ b/arch/arm/mach-shark/arch.c @@ -6,7 +6,6 @@ * any additional architecture specific information * is pulled from the params struct. */ -#include #include #include #include diff --git a/arch/arm/mach-shark/pci.c b/arch/arm/mach-shark/pci.c index 67815a2c146d..601922ce8ffa 100644 --- a/arch/arm/mach-shark/pci.c +++ b/arch/arm/mach-shark/pci.c @@ -5,7 +5,6 @@ * * Bits taken from various places. */ -#include #include #include #include diff --git a/arch/i386/config.in b/arch/i386/config.in index 0ac5e1a250ac..6d1279ed7139 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -249,6 +249,8 @@ source drivers/pnp/Config.in source drivers/block/Config.in +source drivers/md/Config.in + if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 0c6ecf9a1282..5d579ef5e3b6 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -116,14 +116,19 @@ CONFIG_BLK_DEV_FD=y # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_LVM is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set # CONFIG_MD_RAID0 is not set # CONFIG_MD_RAID1 is not set # CONFIG_MD_RAID5 is not set -# CONFIG_BLK_DEV_RAM is not set -# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BLK_DEV_LVM is not set # # Networking options @@ -262,12 +267,12 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set # CONFIG_SCSI_AIC7XXX is not set -# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set # CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_DTC3280 is not set # CONFIG_SCSI_EATA is not set @@ -276,11 +281,10 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set -# CONFIG_SCSI_SYM53C416 is not set -# CONFIG_SCSI_SIM710 is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y @@ -299,6 +303,8 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set # CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set # CONFIG_SCSI_U14_34F is not set @@ -589,7 +595,45 @@ CONFIG_VGA_CONSOLE=y # # USB support # -# CONFIG_USB is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +CONFIG_USB_UHCI_ALT=y +# CONFIG_USB_OHCI is not set + +# +# USB Devices +# +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_DC2XX is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_USS720 is not set +# CONFIG_USB_DABUSB is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# # # Kernel hacking diff --git a/arch/i386/kernel/bluesmoke.c b/arch/i386/kernel/bluesmoke.c index 35738c5a0740..0fecf5851eae 100644 --- a/arch/i386/kernel/bluesmoke.c +++ b/arch/i386/kernel/bluesmoke.c @@ -2,7 +2,6 @@ * Machine Check Handler For PII/PIII */ -#include #include #include #include @@ -22,7 +21,7 @@ void mcheck_fault(void) if(mcgstl&(1<<0)) /* Recoverable ? */ recover=0; - printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x", smp_processor_id(), mcgstl, mcgsth); + printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl); for(i=0;i= 6) break; /* Athlon and post-Athlon CPUs */ /* else fall through */ case X86_VENDOR_CENTAUR: - return; + if(boot_cpu_data.x86 != 6) + return; /*break;*/ } /* Save value of CR4 and clear Page Global Enable (bit 7) */ @@ -380,6 +381,7 @@ static void set_mtrr_prepare (struct set_mtrr_context *ctxt) { case X86_VENDOR_AMD: case X86_VENDOR_INTEL: + case X86_VENDOR_CENTAUR: /* Disable MTRRs, and set the default type to uncached */ rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); @@ -403,8 +405,11 @@ static void set_mtrr_done (struct set_mtrr_context *ctxt) if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */ /* else fall through */ case X86_VENDOR_CENTAUR: - __restore_flags (ctxt->flags); - return; + if(boot_cpu_data.x86 != 6) + { + __restore_flags (ctxt->flags); + return; + } /*break;*/ } /* Flush caches and TLBs */ @@ -415,6 +420,7 @@ static void set_mtrr_done (struct set_mtrr_context *ctxt) { case X86_VENDOR_AMD: case X86_VENDOR_INTEL: + case X86_VENDOR_CENTAUR: wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); break; case X86_VENDOR_CYRIX: @@ -455,7 +461,14 @@ static unsigned int get_num_var_ranges (void) /* Cyrix have 8 ARRs */ case X86_VENDOR_CENTAUR: /* and Centaur has 8 MCR's */ - return 8; + if(boot_cpu_data.x86==5) + return 8; + /* the cyrix III has intel compatible MTRR */ + if(boot_cpu_data.x86==6) + { + rdmsr (MTRRcap_MSR, config, dummy); + return (config & 0xff); + } /*break;*/ } return 0; @@ -471,12 +484,15 @@ static int have_wrcomb (void) case X86_VENDOR_AMD: if (boot_cpu_data.x86 < 6) return 1; /* pre-Athlon CPUs */ /* else fall through */ + case X86_VENDOR_CENTAUR: + if (boot_cpu_data.x86 == 5) + return 1; /* C6 */ + /* CyrixIII is Intel like */ case X86_VENDOR_INTEL: rdmsr (MTRRcap_MSR, config, dummy); return (config & (1<<10)); /*break;*/ case X86_VENDOR_CYRIX: - case X86_VENDOR_CENTAUR: return 1; /*break;*/ } @@ -1194,7 +1210,7 @@ int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char inc printk ("mtrr: size: %lx base: %lx\n", size, base); return -EINVAL; } - if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) + if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR && boot_cpu_data.x86 == 5) { if (type != MTRR_TYPE_WRCOMB) { @@ -1779,8 +1795,16 @@ static void __init mtrr_setup(void) get_free_region = cyrix_get_free_region; break; case X86_VENDOR_CENTAUR: - get_mtrr = centaur_get_mcr; - set_mtrr_up = centaur_set_mcr_up; + if(boot_cpu_data.x86 == 5) + { + get_mtrr = centaur_get_mcr; + set_mtrr_up = centaur_set_mcr_up; + } + if(boot_cpu_data.x86 == 6) + { + get_mtrr = intel_get_mtrr; + set_mtrr_up = intel_set_mtrr_up; + } break; } } /* End Function mtrr_setup */ @@ -1804,8 +1828,11 @@ void __init mtrr_init_boot_cpu(void) case X86_VENDOR_CYRIX: cyrix_arr_init (); break; - case X86_VENDOR_CENTAUR: - centaur_mcr_init (); + case X86_VENDOR_CENTAUR: /* C6 and Cyrix III have different ones */ + if(boot_cpu_data.x86 == 5) + centaur_mcr_init (); + if(boot_cpu_data.x86 == 6) + get_mtrr_state(&smp_mtrr_state); break; } } /* End Function mtrr_init_boot_cpu */ @@ -1877,7 +1904,8 @@ int __init mtrr_init(void) cyrix_arr_init (); break; case X86_VENDOR_CENTAUR: - centaur_mcr_init (); + if(boot_cpu_data.x86 == 5) + centaur_mcr_init (); break; } #endif /* !CONFIG_SMP */ diff --git a/arch/ia64/config.in b/arch/ia64/config.in index 0fdf86c19669..97d622276ebd 100644 --- a/arch/ia64/config.in +++ b/arch/ia64/config.in @@ -99,6 +99,7 @@ source drivers/mtd/Config.in source drivers/pnp/Config.in source drivers/block/Config.in source drivers/i2o/Config.in +source drivers/md/Config.in mainmenu_option next_comment comment 'ATA/IDE/MFM/RLL support' diff --git a/arch/m68k/config.in b/arch/m68k/config.in index 076d9107cd33..83381857faa1 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -149,6 +149,8 @@ source drivers/mtd/Config.in source drivers/block/Config.in +source drivers/md/Config.in + if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi diff --git a/arch/mips/config.in b/arch/mips/config.in index 6a3b0eb3d525..ce9e6c5d1aba 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -189,6 +189,8 @@ source drivers/mtd/Config.in source drivers/block/Config.in +source drivers/md/Config.in + if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi diff --git a/arch/mips64/config.in b/arch/mips64/config.in index 7bfc0b3a3610..ad1bdb570166 100644 --- a/arch/mips64/config.in +++ b/arch/mips64/config.in @@ -129,6 +129,8 @@ source drivers/mtd/Config.in source drivers/block/Config.in +source drivers/md/Config.in + if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi diff --git a/arch/ppc/8260_io/fcc_enet.c b/arch/ppc/8260_io/fcc_enet.c index 5c7b61cf9cfe..da3991ae33ff 100644 --- a/arch/ppc/8260_io/fcc_enet.c +++ b/arch/ppc/8260_io/fcc_enet.c @@ -16,6 +16,7 @@ * */ +#include #include #include #include diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 21c02082e549..0a217d4c909b 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -182,6 +182,7 @@ endmenu source drivers/mtd/Config.in source drivers/pnp/Config.in source drivers/block/Config.in +source drivers/md/Config.in if [ "$CONFIG_NET" = "y" ]; then source net/Config.in diff --git a/arch/ppc/lib/string.S b/arch/ppc/lib/string.S index df99728be2e4..a922b43614d4 100644 --- a/arch/ppc/lib/string.S +++ b/arch/ppc/lib/string.S @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include "../kernel/ppc_asm.tmpl" +#include #include #include diff --git a/arch/ppc/mm/extable.c b/arch/ppc/mm/extable.c index 74f263f4857e..f43bfaff4b87 100644 --- a/arch/ppc/mm/extable.c +++ b/arch/ppc/mm/extable.c @@ -4,6 +4,7 @@ * from linux/arch/i386/mm/extable.c */ +#include #include #include diff --git a/arch/sh/config.in b/arch/sh/config.in index fa36d92f750e..f0a51f8d67d8 100644 --- a/arch/sh/config.in +++ b/arch/sh/config.in @@ -123,6 +123,8 @@ source drivers/mtd/Config.in source drivers/block/Config.in +source drivers/md/Config.in + if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi diff --git a/arch/sparc/config.in b/arch/sparc/config.in index ef948c18142c..0a7c2140a457 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -94,15 +94,7 @@ dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET # bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y #fi -tristate 'Multiple devices driver support' CONFIG_BLK_DEV_MD -dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD -#dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD -#if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_RAID0" = "y" -o "$CONFIG_MD_RAID1" = "y" -o "$CONFIG_MD_RAID5" = "y" ]; then -# bool ' Boot support' CONFIG_MD_BOOT -# bool ' Auto Detect support' CONFIG_AUTODETECT_RAID -#fi +include drivers/md/Config.in tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index bfcb973d7b38..c49a87ebf63b 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -94,15 +94,7 @@ dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET # bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y #fi -tristate 'Multiple devices driver support' CONFIG_BLK_DEV_MD -dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD -#dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD -#if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_RAID0" = "y" -o "$CONFIG_MD_RAID1" = "y" -o "$CONFIG_MD_RAID5" = "y" ]; then -# bool ' Boot support' CONFIG_MD_BOOT -# bool ' Auto Detect support' CONFIG_AUTODETECT_RAID -#fi +include drivers/md/Config.in tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then diff --git a/drivers/Makefile b/drivers/Makefile index cf9f91864d4c..30234032dc69 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -12,7 +12,7 @@ MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi ide scsi sbus cdrom isdn pnp i2o \ ieee1394 macintosh video dio zorro fc4 \ usb nubus tc atm pcmcia i2c telephony \ - acpi mtd input + acpi mtd input md ifdef CONFIG_DIO SUB_DIRS += dio @@ -131,6 +131,15 @@ else endif endif +ifeq ($(CONFIG_BLK_DEV_MD),y) +SUB_DIRS += md +MOD_SUB_DIRS += md +else + ifeq ($(CONFIG_BLK_DEV_MD),m) + MOD_SUB_DIRS += md + endif +endif + ifeq ($(CONFIG_IEEE1394),y) SUB_DIRS += ieee1394 MOD_SUB_DIRS += ieee1394 diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 4a92d53c2786..c9a3a1334df8 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -39,20 +39,10 @@ dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_D tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET -tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N -if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then - bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Compaq CISS Array support' CONFIG_BLK_CPQ_CISS_DA fi -tristate 'Multiple devices driver support' CONFIG_BLK_DEV_MD -dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD -dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD -if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_RAID0" = "y" -o "$CONFIG_MD_RAID1" = "y" -o "$CONFIG_MD_RAID5" = "y" ]; then - bool ' Boot support' CONFIG_MD_BOOT - bool ' Auto Detect support' CONFIG_AUTODETECT_RAID -fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 237d7ac64a94..31678292c99d 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -14,9 +14,7 @@ ALL_SUB_DIRS := $(SUB_DIRS) paride O_TARGET := block.o -export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o md.o xor.o -list-multi := lvm-mod.o -lvm-mod-objs := lvm.o lvm-snap.o +export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o @@ -33,14 +31,8 @@ obj-$(CONFIG_BLK_DEV_LOOP) += loop.o obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o +obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o -obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o - -obj-$(CONFIG_BLK_DEV_MD) += md.o -obj-$(CONFIG_MD_LINEAR) += linear.o -obj-$(CONFIG_MD_RAID0) += raid0.o -obj-$(CONFIG_MD_RAID1) += raid1.o -obj-$(CONFIG_MD_RAID5) += raid5.o xor.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o @@ -71,6 +63,3 @@ M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) include $(TOPDIR)/Rules.make - -lvm-mod.o: $(lvm-mod-objs) - $(LD) -r -o $@ $(lvm-mod-objs) diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c new file mode 100644 index 000000000000..ca884c158855 --- /dev/null +++ b/drivers/block/cciss.c @@ -0,0 +1,1913 @@ +/* + * Disk Array driver for Compaq SMART2 Controllers + * Copyright 2000 Compaq Computer Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. 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. + * + * Questions/Comments/Bugfixes to arrays@compaq.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) +#define DRIVER_NAME "Compaq CISS Driver (v 1.0.0)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(1,0,0) + +/* Embedded module documentation macros - see modules.h */ +MODULE_AUTHOR("Charles M. White III - Compaq Computer Corporation"); +MODULE_DESCRIPTION("Driver for Compaq Smart Array Controller 5300"); + +#include "cciss_cmd.h" +#include "cciss.h" +#include + +#define NR_PRODUCTS (sizeof(products)/sizeof(struct board_type)) + +/* board_id = Subsystem Device ID & Vendor ID + * product = Marketing Name for the board + * access = Address of the struct of function pointers + */ +static struct board_type products[] = { + { 0x40700E11, "Smart Array 5300", &SA5_access }, +}; + +/* How long to wait (in millesconds) for board to go into simple mode */ +#define MAX_CONFIG_WAIT 1000 + +#define READ_AHEAD 128 +#define NR_CMDS 128 /* #commands that can be outstanding */ +#define MAX_CTLR 8 +static int nr_ctlr =0; +static ctlr_info_t *hba[MAX_CTLR] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +static struct proc_dir_entry *proc_cciss = NULL; + +static void do_cciss_request(int i); +/* + * This is a hack. This driver eats a major number for each controller, and + * sets blkdev[xxx].request_fn to each one of these so the real request + * function knows what controller its working with. + */ +#define DO_CCISS_REQUEST(x) { do_cciss_request(x); } + +static void do_cciss_request0(request_queue_t *q) DO_CCISS_REQUEST(0); +static void do_cciss_request1(request_queue_t *q) DO_CCISS_REQUEST(1); +static void do_cciss_request2(request_queue_t *q) DO_CCISS_REQUEST(2); +static void do_cciss_request3(request_queue_t *q) DO_CCISS_REQUEST(3); +static void do_cciss_request4(request_queue_t *q) DO_CCISS_REQUEST(4); +static void do_cciss_request5(request_queue_t *q) DO_CCISS_REQUEST(5); +static void do_cciss_request6(request_queue_t *q) DO_CCISS_REQUEST(6); +static void do_cciss_request7(request_queue_t *q) DO_CCISS_REQUEST(7); + +static int cciss_open(struct inode *inode, struct file *filep); +static int cciss_release(struct inode *inode, struct file *filep); +static int cciss_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg); + +static int revalidate_allvol(kdev_t dev); +static int revalidate_logvol(kdev_t dev, int maxusage); +static int frevalidate_logvol(kdev_t dev); + +static void cciss_getgeometry(int cntl_num); + +static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c); +static void start_io( ctlr_info_t *h); + +static struct block_device_operations cciss_fops = { + ioctl: cciss_ioctl, /* ioctl */ + open: cciss_open, /* open code */ + release: cciss_release, /* release */ + revalidate: frevalidate_logvol, /* revalidate */ +}; + +/* + * Report information about this controller. + */ +static int cciss_proc_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +{ + off_t pos = 0; + off_t len = 0; + int size, i, ctlr; + ctlr_info_t *h = (ctlr_info_t*)data; + drive_info_struct *drv; + + ctlr = h->ctlr; + size = sprintf(buffer, "%s: Compaq %s Controller\n" + " Board ID: %08lx\n" + " Firmware Version: %c%c%c%c\n" + " Memory Address: %08lx\n" + " IRQ: 0x%x\n" + " Logical drives: %d\n" + " Current Q depth: %d\n" + " Current # commands on controller %d\n" + " Max Q depth since init: %d\n" + " Max # commands on controller since init: %d\n" + " Max SG entries since init: %d\n\n", + h->devname, + h->product_name, + (unsigned long)h->board_id, + h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3], + (unsigned long)h->vaddr, + (unsigned int)h->intr, + h->num_luns, + h->Qdepth, h->commands_outstanding, + h->maxQsinceinit, h->max_outstanding, h->maxSG); + + pos += size; len += size; + for(i=0; inum_luns; i++) { + drv = &h->drv[i]; + size = sprintf(buffer+len, "cciss/c%dd%d: blksz=%d nr_blocks=%d\n", + ctlr, i, drv->block_size, drv->nr_blocks); + pos += size; len += size; + } + + size = sprintf(buffer+len, "nr_allocs = %d\nnr_frees = %d\n", + h->nr_allocs, h->nr_frees); + pos += size; len += size; + + *eof = 1; + *start = buffer+offset; + len -= offset; + if (len>length) + len = length; + return len; +} + +/* + * Get us a file in /proc/cciss that says something about each controller. + * Create /proc/cciss if it doesn't exist yet. + */ +static void cciss_procinit(int i) +{ + struct proc_dir_entry *pd; + + if (proc_cciss == NULL) { + proc_cciss = proc_mkdir("cciss", NULL); + if (!proc_cciss) + return; + } + + pd = create_proc_read_entry(hba[i]->devname, 0, proc_cciss, + cciss_proc_get_info, hba[i]); +} + +/* + * For operations that cannot sleep, a command block is allocated at init, + * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track + * which ones are free or in use. For operations that can wait for kmalloc + * to possible sleep, this routine can be called with a NULL pointer. + * cmd_free() MUST be called with a NULL pointer if cmd_alloc was. + */ +static CommandList_struct * cmd_alloc(ctlr_info_t *h) +{ + CommandList_struct *c; + int i; + u64bit temp64; + + if (h == NULL) + { + c = (CommandList_struct *)kmalloc(sizeof(CommandList_struct), + GFP_KERNEL); + if(c==NULL) + return NULL; + memset(c, 0, sizeof(CommandList_struct)); + + c->err_info = (ErrorInfo_struct *)kmalloc( + sizeof(ErrorInfo_struct), GFP_KERNEL); + + if (c->err_info == NULL) + { + kfree(c); + return NULL; + } + memset(c->err_info, 0, sizeof(ErrorInfo_struct)); + } else /* get it out of the controllers pool */ + { + do { + i = find_first_zero_bit(h->cmd_pool_bits, NR_CMDS); + if (i == NR_CMDS) + return NULL; + } while(test_and_set_bit(i%32, h->cmd_pool_bits+(i/32)) != 0); +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: using command buffer %d\n", i); +#endif + c = h->cmd_pool + i; + memset(c, 0, sizeof(CommandList_struct)); + c->err_info = h->errinfo_pool + i; + memset(c->err_info, 0, sizeof(ErrorInfo_struct)); + h->nr_allocs++; + } + + + temp64.val = (__u64) virt_to_bus(c->err_info); + c->ErrDesc.Addr.lower = temp64.val32.lower; + c->ErrDesc.Addr.upper = temp64.val32.upper; + c->ErrDesc.Len = sizeof(ErrorInfo_struct); + c->busaddr = virt_to_bus(c); + return c; + + +} + +/* + * Frees a command block that was previously allocated with cmd_alloc(). + */ +static void cmd_free(ctlr_info_t *h, CommandList_struct *c) +{ + int i; + + if( h == NULL) + { + kfree(c->err_info); + kfree(c); + } else + { + i = c - h->cmd_pool; + clear_bit(i%32, h->cmd_pool_bits+(i/32)); + h->nr_frees++; + } +} + +/* + * fills in the disk information. + */ +static void cciss_geninit(int ctlr) +{ + drive_info_struct *drv; + int i,j; + + /* Loop through each real device */ + hba[ctlr]->gendisk.nr_real = 0; + for(i=0; i< NWD; i++) + { + drv = &(hba[ctlr]->drv[i]); + if( !(drv->nr_blocks)) + continue; + hba[ctlr]->hd[i << NWD_SHIFT].nr_sects = + hba[ctlr]->sizes[i << NWD_SHIFT] = drv->nr_blocks; + + /* for each partition */ + for(j=0; jblocksizes[(i<hardsizes[ (i<block_size; + } + hba[ctlr]->gendisk.nr_real++; + } +} +/* + * Open. Make sure the device is really there. + */ +static int cciss_open(struct inode *inode, struct file *filep) +{ + int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; + int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss_open %x (%x:%x)\n", inode->i_rdev, ctlr, dsk); +#endif /* CCISS_DEBUG */ + + if (ctlr > MAX_CTLR || hba[ctlr] == NULL) + return -ENXIO; + + if (!suser() && hba[ctlr]->sizes[ MINOR(inode->i_rdev)] == 0) + return -ENXIO; + + /* + * Root is allowed to open raw volume zero even if its not configured + * so array config can still work. I don't think I really like this, + * but I'm already using way to many device nodes to claim another one + * for "raw controller". + */ + if (suser() + && (hba[ctlr]->sizes[MINOR(inode->i_rdev)] == 0) + && (MINOR(inode->i_rdev)!= 0)) + return -ENXIO; + + hba[ctlr]->drv[dsk].usage_count++; + hba[ctlr]->usage_count++; + MOD_INC_USE_COUNT; + return 0; +} +/* + * Close. Sync first. + */ +static int cciss_release(struct inode *inode, struct file *filep) +{ + int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; + int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss_release %x (%x:%x)\n", inode->i_rdev, ctlr, dsk); +#endif /* CCISS_DEBUG */ + + fsync_dev(inode->i_rdev); + + hba[ctlr]->drv[dsk].usage_count--; + hba[ctlr]->usage_count--; + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * ioctl + */ +static int cciss_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; + int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; + int diskinfo[4]; + struct hd_geometry *geo = (struct hd_geometry *)arg; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg); +#endif /* CCISS_DEBUG */ + + switch(cmd) { + case HDIO_GETGEO: + if (hba[ctlr]->drv[dsk].cylinders) { + diskinfo[0] = hba[ctlr]->drv[dsk].heads; + diskinfo[1] = hba[ctlr]->drv[dsk].sectors; + diskinfo[2] = hba[ctlr]->drv[dsk].cylinders; + } else { + diskinfo[0] = 0xff; + diskinfo[1] = 0x3f; + diskinfo[2] = hba[ctlr]->drv[dsk].nr_blocks / (0xff*0x3f); } + put_user(diskinfo[0], &geo->heads); + put_user(diskinfo[1], &geo->sectors); + put_user(diskinfo[2], &geo->cylinders); + put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect, &geo->start); + return 0; + case BLKGETSIZE: + if (!arg) return -EINVAL; + put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].nr_sects, (long*)arg); + return 0; + case BLKRASET: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (!(inode->i_rdev)) return -EINVAL; + if (arg>0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + put_user(read_ahead[MAJOR(inode->i_rdev)], (int*)arg); + return 0; + case BLKRRPART: + return revalidate_logvol(inode->i_rdev, 1); + + case CCISS_GETPCIINFO: + { + cciss_pci_info_struct pciinfo; + + if (!arg) return -EINVAL; + pciinfo.bus = hba[ctlr]->pci_bus; + pciinfo.dev_fn = hba[ctlr]->pci_dev_fn; + pciinfo.board_id = hba[ctlr]->board_id; + if(copy_to_user((void *) arg, &pciinfo, sizeof( cciss_pci_info_struct))) + return -EFAULT; + return(0); + } + case CCISS_GETINTINFO: + { + cciss_coalint_struct intinfo; + ctlr_info_t *c = hba[ctlr]; + + if (!arg) return -EINVAL; + intinfo.delay = readl(&c->cfgtable->HostWrite.CoalIntDelay); + intinfo.count = readl(&c->cfgtable->HostWrite.CoalIntCount); + if(copy_to_user((void *) arg, &intinfo, sizeof( cciss_coalint_struct))) + return -EFAULT; + return(0); + } + case CCISS_SETINTINFO: + { + cciss_coalint_struct intinfo; + ctlr_info_t *c = hba[ctlr]; + unsigned long flags; + int i; + + if (!arg) return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if(copy_from_user(&intinfo, (void *) arg, sizeof( cciss_coalint_struct))) + return -EFAULT; + if ( (intinfo.delay == 0 ) && (intinfo.count == 0)) + + { +// printk("cciss_ioctl: delay and count cannot be 0\n"); + return( -EINVAL); + } + spin_lock_irqsave(&io_request_lock, flags); + /* Can only safely update if no commands outstanding */ + if (c->commands_outstanding > 0 ) + { +// printk("cciss_ioctl: cannot change coalasing " +// "%d commands outstanding on controller\n", +// c->commands_outstanding); + spin_unlock_irqrestore(&io_request_lock, flags); + return(-EINVAL); + } + /* Update the field, and then ring the doorbell */ + writel( intinfo.delay, + &(c->cfgtable->HostWrite.CoalIntDelay)); + writel( intinfo.count, + &(c->cfgtable->HostWrite.CoalIntCount)); + writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + + for(i=0;ivaddr + SA5_DOORBELL) + & CFGTBL_ChangeReq)) + break; + /* delay and try again */ + udelay(1000); + } + spin_unlock_irqrestore(&io_request_lock, flags); + if (i >= MAX_CONFIG_WAIT) + return( -EFAULT); + return(0); + } + case CCISS_GETNODENAME: + { + NodeName_type NodeName; + ctlr_info_t *c = hba[ctlr]; + int i; + + if (!arg) return -EINVAL; + for(i=0;i<16;i++) + NodeName[i] = readb(&c->cfgtable->ServerName[i]); + if(copy_to_user((void *) arg, NodeName, sizeof(NodeName_type))) + return -EFAULT; + return(0); + } + case CCISS_SETNODENAME: + { + NodeName_type NodeName; + ctlr_info_t *c = hba[ctlr]; + unsigned long flags; + int i; + + if (!arg) return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; + + if(copy_from_user(NodeName, (void *) arg, sizeof( NodeName_type))) + return -EFAULT; + + spin_lock_irqsave(&io_request_lock, flags); + + /* Update the field, and then ring the doorbell */ + for(i=0;i<16;i++) + writeb( NodeName[i], &c->cfgtable->ServerName[i]); + + writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + + for(i=0;ivaddr + SA5_DOORBELL) + & CFGTBL_ChangeReq)) + break; + /* delay and try again */ + udelay(1000); + } + spin_unlock_irqrestore(&io_request_lock, flags); + if (i >= MAX_CONFIG_WAIT) + return -EIO; + return(0); + } + + case CCISS_GETHEARTBEAT: + { + Heartbeat_type heartbeat; + ctlr_info_t *c = hba[ctlr]; + + if (!arg) return -EINVAL; + heartbeat = readl(&c->cfgtable->HeartBeat); + if(copy_to_user((void *) arg, &heartbeat, sizeof( Heartbeat_type))) + return -EFAULT; + return(0); + } + case CCISS_GETBUSTYPES: + { + BusTypes_type BusTypes; + ctlr_info_t *c = hba[ctlr]; + + if (!arg) return -EINVAL; + BusTypes = readl(&c->cfgtable->BusTypes); + if(copy_to_user((void *) arg, &BusTypes, sizeof( BusTypes_type))) + return -EFAULT; + return(0); + } + case CCISS_GETFIRMVER: + { + FirmwareVer_type firmware; + + if (!arg) return -EINVAL; + memcpy(firmware, hba[ctlr]->firm_ver, 4); + + if(copy_to_user((void *) arg, firmware, sizeof( FirmwareVer_type))) + return -EFAULT; + return(0); + } + case CCISS_GETDRIVVER: + { + DriverVer_type DriverVer = DRIVER_VERSION; + + if (!arg) return -EINVAL; + + if(copy_to_user((void *) arg, &DriverVer, sizeof( DriverVer_type))) + return -EFAULT; + return(0); + } + + case CCISS_REVALIDVOLS: + return( revalidate_allvol(inode->i_rdev)); + + case CCISS_PASSTHRU: + { + IOCTL_Command_struct iocommand; + ctlr_info_t *h = hba[ctlr]; + CommandList_struct *c; + char *buff = NULL; + u64bit temp64; + unsigned long flags; + + if (!arg) return -EINVAL; + + if (!capable(CAP_SYS_RAWIO)) return -EPERM; + + if(copy_from_user(&iocommand, (void *) arg, sizeof( IOCTL_Command_struct))) + return -EFAULT; + if((iocommand.buf_size < 1) && + (iocommand.Request.Type.Direction != XFER_NONE)) + { + return -EINVAL; + } + /* Check kmalloc limits */ + if(iocommand.buf_size > 128000) + return -EINVAL; + if(iocommand.buf_size > 0) + { + buff = kmalloc(iocommand.buf_size, GFP_KERNEL); + if( buff == NULL) + return -EFAULT; + } + if (iocommand.Request.Type.Direction == XFER_WRITE) + { + /* Copy the data into the buffer we created */ + if(copy_from_user(buff, iocommand.buf,iocommand.buf_size)) + return -EFAULT; + } + if ((c = cmd_alloc(NULL)) == NULL) + { + if(buff!=NULL) + kfree(buff); + return -ENOMEM; + } + // Fill in the command type + c->cmd_type = CMD_IOCTL_PEND; + // Fill in Command Header + c->Header.ReplyQueue = 0; // unused in simple mode + if( iocommand.buf_size > 0) // buffer to fill + { + c->Header.SGList = 1; + c->Header.SGTotal= 1; + } else // no buffers to fill + { + c->Header.SGList = 0; + c->Header.SGTotal= 0; + } + c->Header.LUN = iocommand.LUN_info; + c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag + + // Fill in Request block + c->Request = iocommand.Request; + + // Fill in the scatter gather information + if (iocommand.buf_size > 0 ) + { + temp64.val = (__u64) virt_to_bus(buff); + c->SG[0].Addr.lower = temp64.val32.lower; + c->SG[0].Addr.upper = temp64.val32.upper; + c->SG[0].Len = iocommand.buf_size; + c->SG[0].Ext = 0; // we are not chaining + } + /* Put the request on the tail of the request queue */ + spin_lock_irqsave(&io_request_lock, flags); + addQ(&h->reqQ, c); + h->Qdepth++; + start_io(h); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* Wait for completion */ + while(c->cmd_type != CMD_IOCTL_DONE) + { + set_task_state(current, TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + rmb(); + } + + /* Copy the error information out */ + iocommand.error_info = *(c->err_info); + if ( copy_to_user((void *) arg, &iocommand, + sizeof( IOCTL_Command_struct) ) < 0 ) + { + cmd_free(NULL, c); + if (buff != NULL) + kfree(buff); + return( -EFAULT); + } + + if (iocommand.Request.Type.Direction == XFER_READ) + { + /* Copy the data out of the buffer we created */ + if (copy_to_user(iocommand.buf, buff, + iocommand.buf_size) < 0) + { + cmd_free(NULL, c); + kfree(buff); + } + } + cmd_free(NULL, c); + if (buff != NULL) + kfree(buff); + return(0); + } + + case BLKFLSBUF: + case BLKROSET: + case BLKROGET: + case BLKPG: + return blk_ioctl(inode->i_rdev, cmd, arg); + + default: + return -EINVAL; + } + +} + +/* Borrowed and adapted from sd.c */ +static int revalidate_logvol(kdev_t dev, int maxusage) +{ + int ctlr, target; + struct gendisk *gdev; + unsigned long flags; + int max_p; + int start; + int i; + + target = MINOR(dev) >> NWD_SHIFT; + ctlr = MAJOR(dev) - MAJOR_NR; + gdev = &(hba[ctlr]->gendisk); + + spin_lock_irqsave(&io_request_lock, flags); + if (hba[ctlr]->drv[target].usage_count > maxusage) { + spin_unlock_irqrestore(&io_request_lock, flags); + printk(KERN_WARNING "cpqarray: Device busy for " + "revalidation (usage=%d)\n", + hba[ctlr]->drv[target].usage_count); + return -EBUSY; + } + hba[ctlr]->drv[target].usage_count++; + spin_unlock_irqrestore(&io_request_lock, flags); + + max_p = gdev->max_p; + start = target << gdev->minor_shift; + + for(i=max_p; i>=0; i--) { + int minor = start+i; + kdev_t devi = MKDEV(MAJOR_NR + ctlr, minor); + struct super_block *sb = get_super(devi); + sync_dev(devi); + if (sb) invalidate_inodes(sb); + invalidate_buffers(devi); + gdev->part[minor].start_sect = 0; + gdev->part[minor].nr_sects = 0; + + /* reset the blocksize so we can read the partition table */ + blksize_size[MAJOR_NR+ctlr][minor] = 1024; + } + gdev->part[start].nr_sects = hba[ctlr]->drv[target].nr_blocks; + /* 16 minors per disk... */ + grok_partitions(gdev, target, 16, hba[ctlr]->drv[target].nr_blocks); + hba[ctlr]->drv[target].usage_count--; + return 0; +} + +static int frevalidate_logvol(kdev_t dev) +{ +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: frevalidate has been called\n"); +#endif /* CCISS_DEBUG */ + return revalidate_logvol(dev, 0); +} + +/* + * revalidate_allvol is for online array config utilities. After a + * utility reconfigures the drives in the array, it can use this function + * (through an ioctl) to make the driver zap any previous disk structs for + * that controller and get new ones. + * + * Right now I'm using the getgeometry() function to do this, but this + * function should probably be finer grained and allow you to revalidate one + * particualar logical volume (instead of all of them on a particular + * controller). + */ +static int revalidate_allvol(kdev_t dev) +{ + int ctlr, i; + unsigned long flags; + + ctlr = MAJOR(dev) - MAJOR_NR; + if (MINOR(dev) != 0) + return -ENXIO; + + spin_lock_irqsave(&io_request_lock, flags); + if (hba[ctlr]->usage_count > 1) { + spin_unlock_irqrestore(&io_request_lock, flags); + printk(KERN_WARNING "cciss: Device busy for volume" + " revalidation (usage=%d)\n", hba[ctlr]->usage_count); + return -EBUSY; + } + spin_unlock_irqrestore(&io_request_lock, flags); + hba[ctlr]->usage_count++; + + /* + * Set the partition and block size structures for all volumes + * on this controller to zero. We will reread all of this data + */ + memset(hba[ctlr]->hd, 0, sizeof(struct hd_struct) * 256); + memset(hba[ctlr]->sizes, 0, sizeof(int) * 256); + memset(hba[ctlr]->blocksizes, 0, sizeof(int) * 256); + memset(hba[ctlr]->hardsizes, 0, sizeof(int) * 256); + memset(hba[ctlr]->drv, 0, sizeof(drive_info_struct) + * CISS_MAX_LUN); + hba[ctlr]->gendisk.nr_real = 0; + + /* + * Tell the array controller not to give us any interupts while + * we check the new geometry. Then turn interrupts back on when + * we're done. + */ + hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_OFF); + cciss_getgeometry(ctlr); + hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_ON); + + cciss_geninit(ctlr); + for(i=0; isizes[ i<usage_count--; + return 0; +} + + + +/* + * Wait polling for a command to complete. + * The memory mapped FIFO is polled for the completion. + * Used only at init time, interrupts disabled. + */ +static unsigned long pollcomplete(int ctlr) +{ + unsigned long done; + int i; + + /* Wait (up to 2 seconds) for a command to complete */ + + for (i = 200000; i > 0; i--) { + done = hba[ctlr]->access.command_completed(hba[ctlr]); + if (done == FIFO_EMPTY) { + udelay(10); /* a short fixed delay */ + } else + return (done); + } + /* Invalid address to tell caller we ran out of time */ + return 1; +} +/* + * Send a command to the controller, and wait for it to complete. + * Only used at init time. + */ +static int sendcmd( + __u8 cmd, + int ctlr, + void *buff, + size_t size, + unsigned int use_unit_num, + unsigned int log_unit, + __u8 page_code ) +{ + CommandList_struct *c; + int i; + unsigned long complete; + ctlr_info_t *info_p= hba[ctlr]; + u64bit temp64; + + c = cmd_alloc(info_p); + if (c == NULL) + { + printk(KERN_WARNING "cciss: unable to get memory"); + return(IO_ERROR); + } + // Fill in Command Header + c->Header.ReplyQueue = 0; // unused in simple mode + if( buff != NULL) // buffer to fill + { + c->Header.SGList = 1; + c->Header.SGTotal= 1; + } else // no buffers to fill + { + c->Header.SGList = 0; + c->Header.SGTotal= 0; + } + c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag + // Fill in Request block + switch(cmd) + { + case CISS_INQUIRY: + /* If the logical unit number is 0 then, this is going + to controller so It's a physical command + mode = 0 target = 0. + So we have nothing to write. + Otherwise + mode = 1 target = LUNID + */ + if(use_unit_num != 0) + { + c->Header.LUN.LogDev.VolId= + hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + } + /* are we trying to read a vital product page */ + if(page_code != 0) + { + c->Request.CDB[1] = 0x01; + c->Request.CDB[2] = page_code; + } + c->Request.CDBLen = 6; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CISS_INQUIRY; + c->Request.CDB[4] = size & 0xFF; + break; + case CISS_REPORT_LOG: + /* Talking to controller so It's a physical command + mode = 00 target = 0. + So we have nothing to write. + */ + c->Request.CDBLen = 12; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CISS_REPORT_LOG; + c->Request.CDB[6] = (size >> 24) & 0xFF; //MSB + c->Request.CDB[7] = (size >> 16) & 0xFF; + c->Request.CDB[8] = (size >> 8) & 0xFF; + c->Request.CDB[9] = size & 0xFF; + break; + + case CCISS_READ_CAPACITY: + c->Header.LUN.LogDev.VolId= + hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + c->Request.CDBLen = 10; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CCISS_READ_CAPACITY; + break; + default: + printk(KERN_WARNING + "cciss: Unknown Command 0x%c sent attempted\n", + cmd); + cmd_free(info_p, c); + return(IO_ERROR); + }; + // Fill in the scatter gather information + if (size > 0 ) + { + temp64.val = (__u64) virt_to_bus(buff); + c->SG[0].Addr.lower = temp64.val32.lower; + c->SG[0].Addr.upper = temp64.val32.upper; + c->SG[0].Len = size; + c->SG[0].Ext = 0; // we are not chaining + } + /* + * Disable interrupt + */ +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: turning intr off\n"); +#endif /* CCISS_DEBUG */ + info_p->access.set_intr_mask(info_p, CCISS_INTR_OFF); + + /* Make sure there is room in the command FIFO */ + /* Actually it should be completely empty at this time. */ + for (i = 200000; i > 0; i--) + { + /* if fifo isn't full go */ + if (!(info_p->access.fifo_full(info_p))) + { + + break; + } + udelay(10); + printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full," + " waiting!\n", ctlr); + } + /* + * Send the cmd + */ + info_p->access.submit_command(info_p, c); + complete = pollcomplete(ctlr); + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: command completed\n"); +#endif /* CCISS_DEBUG */ + + if (complete != 1) { + if ( (complete & CISS_ERROR_BIT) + && (complete & ~CISS_ERROR_BIT) == c->busaddr) + { + /* if data overrun or underun on Report command + ignore it + */ + if (((c->Request.CDB[0] == CISS_REPORT_LOG) || + (c->Request.CDB[0] == CISS_INQUIRY)) && + ((c->err_info->CommandStatus == + CMD_DATA_OVERRUN) || + (c->err_info->CommandStatus == + CMD_DATA_UNDERRUN) + )) + { + complete = c->busaddr; + } else + { + printk(KERN_WARNING "ciss ciss%d: sendcmd" + " Error %x \n", ctlr, + c->err_info->CommandStatus); + printk(KERN_WARNING "ciss ciss%d: sendcmd" + " offensive info\n" + " size %x\n num %x value %x\n", ctlr, + c->err_info->MoreErrInfo.Invalid_Cmd.offense_size, + c->err_info->MoreErrInfo.Invalid_Cmd.offense_num, + c->err_info->MoreErrInfo.Invalid_Cmd.offense_value); + cmd_free(info_p,c); + return(IO_ERROR); + } + } + if (complete != c->busaddr) { + printk( KERN_WARNING "cciss cciss%d: SendCmd " + "Invalid command list address returned! (%lx)\n", + ctlr, complete); + cmd_free(info_p, c); + return (IO_ERROR); + } + } else { + printk( KERN_WARNING + "cciss cciss%d: SendCmd Timeout out, " + "No command list address returned!\n", + ctlr); + cmd_free(info_p, c); + return (IO_ERROR); + } + cmd_free(info_p, c); + return (IO_OK); +} +/* + * Map (physical) PCI mem into (virtual) kernel space + */ +static ulong remap_pci_mem(ulong base, ulong size) +{ + ulong page_base = ((ulong) base) & PAGE_MASK; + ulong page_offs = ((ulong) base) - page_base; + ulong page_remapped = (ulong) ioremap(page_base, page_offs+size); + + return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL); +} + +/* + * Enqueuing and dequeuing functions for cmdlists. + */ +static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c) +{ + if (*Qptr == NULL) { + *Qptr = c; + c->next = c->prev = c; + } else { + c->prev = (*Qptr)->prev; + c->next = (*Qptr); + (*Qptr)->prev->next = c; + (*Qptr)->prev = c; + } +} + +static inline CommandList_struct *removeQ(CommandList_struct **Qptr, + CommandList_struct *c) +{ + if (c && c->next != c) { + if (*Qptr == c) *Qptr = c->next; + c->prev->next = c->next; + c->next->prev = c->prev; + } else { + *Qptr = NULL; + } + return c; +} + +/* + * Takes jobs of the Q and sends them to the hardware, then puts it on + * the Q to wait for completion. + */ +static void start_io( ctlr_info_t *h) +{ + CommandList_struct *c; + + while(( c = h->reqQ) != NULL ) + { + /* can't do anything if fifo is full */ + if ((h->access.fifo_full(h))) + { + printk(KERN_WARNING "cciss: fifo full \n"); + return; + } + /* Get the frist entry from the Request Q */ + removeQ(&(h->reqQ), c); + h->Qdepth--; + + /* Tell the controller execute command */ + h->access.submit_command(h, c); + + /* Put job onto the completed Q */ + addQ (&(h->cmpQ), c); + } +} + +static inline void complete_buffers( struct buffer_head *bh, int status) +{ + struct buffer_head *xbh; + + while(bh) + { + xbh = bh->b_reqnext; + bh->b_reqnext = NULL; + bh->b_end_io(bh, status); + bh = xbh; + } +} +/* checks the status of the job and calls complete buffers to mark all + * buffers for the completed job. + */ +static inline void complete_command( CommandList_struct *cmd, int timeout) +{ + int status = 1; + + if (timeout) + status = 0; + if(cmd->err_info->CommandStatus != 0) + { /* an error has occured */ + switch(cmd->err_info->CommandStatus) + { + case CMD_TARGET_STATUS: + printk(KERN_WARNING "cciss: cmd %p has " + " completed with errors\n", cmd); + if( cmd->err_info->ScsiStatus) + { + printk(KERN_WARNING "cciss: cmd %p " + "has SCSI Status = %x\n", + cmd, + cmd->err_info->ScsiStatus); + } + + break; + case CMD_DATA_UNDERRUN: + printk(KERN_WARNING "cciss: cmd %p has" + " completed with data underrun " + "reported\n", cmd); + break; + case CMD_DATA_OVERRUN: + printk(KERN_WARNING "cciss: cmd %p has" + " completed with data overrun " + "reported\n", cmd); + break; + case CMD_INVALID: + printk(KERN_WARNING "cciss: cmd %p is " + "reported invalid\n", cmd); + status = 0; + break; + case CMD_PROTOCOL_ERR: + printk(KERN_WARNING "cciss: cmd %p has " + "protocol error \n", cmd); + status = 0; + break; + case CMD_HARDWARE_ERR: + printk(KERN_WARNING "cciss: cmd %p had " + " hardware error\n", cmd); + status = 0; + break; + case CMD_CONNECTION_LOST: + printk(KERN_WARNING "cciss: cmd %p had " + "connection lost\n", cmd); + status=0; + break; + case CMD_ABORTED: + printk(KERN_WARNING "cciss: cmd %p was " + "aborted\n", cmd); + status=0; + break; + case CMD_ABORT_FAILED: + printk(KERN_WARNING "cciss: cmd %p reports " + "abort failed\n", cmd); + status=0; + break; + case CMD_UNSOLICITED_ABORT: + printk(KERN_WARNING "cciss: cmd %p aborted " + "do to an unsolicited abort\n", cmd); + status=0; + break; + case CMD_TIMEOUT: + printk(KERN_WARNING "cciss: cmd %p timedout\n", + cmd); + status=0; + break; + default: + printk(KERN_WARNING "cciss: cmd %p returned " + "unknown status %x\n", cmd, + cmd->err_info->CommandStatus); + status=0; + } + } + complete_buffers(cmd->bh, status); +} +/* + * Get a request and submit it to the controller. + * Currently we do one request at a time. Ideally we would like to send + * everything to the controller on the first call, but there is a danger + * of holding the io_request_lock for to long. + */ +static void do_cciss_request(int ctlr) +{ + ctlr_info_t *h= hba[ctlr]; + CommandList_struct *c; + int log_unit, start_blk, seg, sect; + char *lastdataend; + struct buffer_head *bh; + struct request *creq; + struct list_head *queue_head; + u64bit temp64; + + queue_head = &blk_dev[MAJOR_NR+ctlr].request_queue.queue_head; + + if(list_empty(queue_head)) + { + /* nothing to do... restart processing and return */ + start_io(h); + return; + } + creq = blkdev_entry_next_request(queue_head); + if (creq->rq_status == RQ_INACTIVE) + { + start_io(h); + return; + } + + if ((ctlr != (MAJOR(creq->rq_dev)-MAJOR_NR)) || (ctlr > nr_ctlr) + || (h == NULL)) + { +#ifdef CCISS_DEBUG + printk(KERN_WARNING "cciss: doreq cmd of %d, %x at %p\n", + ctlr, creq->rq_dev, creq); +#endif /* CCISS_DEBUG */ + complete_buffers(creq->bh, 0); + return; + } + if (( c = cmd_alloc(h)) == NULL) + { + start_io(h); + return; + } + c->cmd_type = CMD_RWREQ; + bh = c->bh = creq->bh; + + /* fill in the request */ + log_unit = MINOR(creq->rq_dev) >> NWD_SHIFT; + c->Header.ReplyQueue = 0; // unused in simple mode + c->Header.Tag.lower = c->busaddr; // use the physical address the cmd block for tag + c->Header.LUN.LogDev.VolId= hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + c->Request.CDBLen = 10; // 12 byte commands not in FW yet; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = + (creq->cmd == READ) ? XFER_READ: XFER_WRITE; + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = (creq->cmd == READ) ? CCISS_READ : CCISS_WRITE; + start_blk = hba[ctlr]->hd[MINOR(creq->rq_dev)].start_sect + creq->sector; + if (bh == NULL) + panic("cciss: bh== NULL?"); +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",(int) creq->sector, + (int) creq->nr_sectors); +#endif /* CCISS_DEBUG */ + seg = 0; + lastdataend = NULL; + sect = 0; + while(bh) + { + sect += bh->b_size/512; + if (bh->b_size % 512) + { + printk(KERN_CRIT "cciss: Oh Man. %d+%d, size=%d\n", + (int) creq->sector, sect, (int) bh->b_size); + panic("b_size 512 != 0\n"); + } + if (bh->b_data == lastdataend) + { // tack it on to the last segment + c->SG[seg-1].Len +=bh->b_size; + lastdataend += bh->b_size; + } else + { + c->SG[seg].Len = bh->b_size; + temp64.val = (__u64) virt_to_bus(bh->b_data); + c->SG[seg].Addr.lower = temp64.val32.lower; + c->SG[seg].Addr.upper = temp64.val32.upper; + c->SG[0].Ext = 0; // we are not chaining + lastdataend = bh->b_data + bh->b_size; + if( ++seg == MAXSGENTRIES) + { + break; + } + } + bh = bh->b_reqnext; + } + /* track how many SG entries we are using */ + if( seg > h->maxSG) + h->maxSG = seg; + + /* adjusting the remaining request, if any */ + creq-> sector+= sect; + creq->nr_sectors -= sect; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: Submitting %d sectors in %d segments\n", sect, seg); +#endif /* CCISS_DEBUG */ + + c->Header.SGList = c->Header.SGTotal = seg; + c->Request.CDB[1]= 0; + c->Request.CDB[2]= (start_blk >> 24) & 0xff; //MSB + c->Request.CDB[3]= (start_blk >> 16) & 0xff; + c->Request.CDB[4]= (start_blk >> 8) & 0xff; + c->Request.CDB[5]= start_blk & 0xff; + c->Request.CDB[6]= 0; // (sect >> 24) & 0xff; MSB + // c->Request.CDB[7]= (sect >> 16) & 0xff; + c->Request.CDB[7]= (sect >> 8) & 0xff; + c->Request.CDB[8]= sect & 0xff; + c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0; + + /* check to see if we going to complete the entire request */ + /* if so, mark this request as Done and ready the next one */ + if (creq->nr_sectors) + { +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: More to do on the same request %p %ld\n", + creq, creq->nr_sectors); +#endif /* CCISS_DEBUG */ + + creq->bh = bh->b_reqnext; + bh->b_reqnext = NULL; + } else + { +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: Done with %p, queueing %p\n", creq, creq->next); +#endif /* CCISS_DEBUG */ + blkdev_dequeue_request(creq); + end_that_request_last(creq); + } + addQ(&(h->reqQ),c); + h->Qdepth++; + if(h->Qdepth > h->maxQsinceinit) + h->maxQsinceinit = h->Qdepth; + start_io(h); +} + +static void do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + ctlr_info_t *h = dev_id; + CommandList_struct *c; + unsigned long flags; + __u32 a, a1; + + + /* Is this interrupt for us? */ + if ( h->access.intr_pending(h) == 0) + return; + + /* + * If there are completed commands in the completion queue, + * we had better do something about it. + */ + spin_lock_irqsave(&io_request_lock, flags); + while( h->access.intr_pending(h)) + { + while((a = h->access.command_completed(h)) != FIFO_EMPTY) + { + a1 = a; + a &= ~3; + if ((c = h->cmpQ) == NULL) + { + printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1); + continue; + } + while(c->busaddr != a) { + c = c->next; + if (c == h->cmpQ) + break; + } + /* + * If we've found the command, take it off the + * completion Q and free it + */ + if (c->busaddr == a) { + removeQ(&h->cmpQ, c); + if (c->cmd_type == CMD_RWREQ) { + complete_command(c, 0); + cmd_free(h, c); + } else if (c->cmd_type == CMD_IOCTL_PEND) { + c->cmd_type = CMD_IOCTL_DONE; + } + continue; + } + } + } + /* + * See if we can queue up some more IO + */ + do_cciss_request(h->ctlr); + spin_unlock_irqrestore(&io_request_lock, flags); +} +/* + * We cannot read the structure directly, for portablity we must use + * the io functions. + * This is for debug only. + */ +#ifdef CCISS_DEBUG +static void print_cfg_table( CfgTable_struct *tb) +{ + int i; + char temp_name[17]; + + printk("Controller Configuration information\n"); + printk("------------------------------------\n"); + for(i=0;i<4;i++) + temp_name[i] = readb(&(tb->Signature[i])); + temp_name[4]='\0'; + printk(" Signature = %s\n", temp_name); + printk(" Spec Number = %d\n", readl(&(tb->SpecValence))); + printk(" Transport methods supported = 0x%x\n", + readl(&(tb-> TransportSupport))); + printk(" Transport methods active = 0x%x\n", + readl(&(tb->TransportActive))); + printk(" Requested transport Method = 0x%x\n", + readl(&(tb->HostWrite.TransportRequest))); + printk(" Coalese Interrupt Delay = 0x%x\n", + readl(&(tb->HostWrite.CoalIntDelay))); + printk(" Coalese Interrupt Count = 0x%x\n", + readl(&(tb->HostWrite.CoalIntCount))); + printk(" Max outstanding commands = 0x%d\n", + readl(&(tb->CmdsOutMax))); + printk(" Bus Types = 0x%x\n", readl(&(tb-> BusTypes))); + for(i=0;i<16;i++) + temp_name[i] = readb(&(tb->ServerName[i])); + temp_name[16] = '\0'; + printk(" Server Name = %s\n", temp_name); + printk(" Heartbeat Counter = 0x%x\n\n\n", + readl(&(tb->HeartBeat))); +} +#endif /* CCISS_DEBUG */ + +static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn) +{ + ushort vendor_id, device_id, command; + unchar cache_line_size, latency_timer; + unchar irq, revision; + uint addr[6]; + __u32 board_id; + struct pci_dev *pdev; + + int i; + + pdev = pci_find_slot(bus, device_fn); + vendor_id = pdev->vendor; + device_id = pdev->device; + irq = pdev->irq; + + for(i=0; i<6; i++) + addr[i] = pdev->resource[i].start; + (void) pci_read_config_word(pdev, PCI_COMMAND,&command); + (void) pci_read_config_byte(pdev, PCI_CLASS_REVISION,&revision); + (void) pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); + (void) pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); + + (void) pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, &board_id); + +#ifdef CCISS_DEBUG + printk("vendor_id = %x\n", vendor_id); + printk("device_id = %x\n", device_id); + printk("command = %x\n", command); + for(i=0; i<6; i++) + printk("addr[%d] = %x\n", i, addr[i]); + printk("revision = %x\n", revision); + printk("irq = %x\n", irq); + printk("cache_line_size = %x\n", cache_line_size); + printk("latency_timer = %x\n", latency_timer); + printk("board_id = %x\n", board_id); +#endif /* CCISS_DEBUG */ + + c->intr = irq; + + /* + * Memory base addr is first addr , the second points to the config + * table + */ + c->paddr = addr[0]; + c->vaddr = remap_pci_mem(c->paddr, 128); + c->cfgtable = (CfgTable_struct *) remap_pci_mem(addr[1], + sizeof(CfgTable_struct)); + c->board_id = board_id; + +#ifdef CCISS_DEBUG + print_cfg_table(c->cfgtable); +#endif /* CCISS_DEBUG */ + for(i=0; iproduct_name = products[i].product_name; + c->access = *(products[i].access); + break; + } + } + if (i == NR_PRODUCTS) { + printk(KERN_WARNING "cciss: Sorry, I don't know how" + " to access the Smart Array controller %08lx\n", + (unsigned long)board_id); + return -1; + } +#ifdef CCISS_DEBUG + printk("Trying to put board into Simple mode\n"); +#endif /* CCISS_DEBUG */ + c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); + /* Update the field, and then ring the doorbell */ + writel( CFGTBL_Trans_Simple, + &(c->cfgtable->HostWrite.TransportRequest)); + writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + + for(i=0;ivaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) + break; + /* delay and try again */ + udelay(1000); + } + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "I counter got to %d %x\n", i, readl(c->vaddr + SA5_DOORBELL)); +#endif /* CCISS_DEBUG */ +#ifdef CCISS_DEBUG + print_cfg_table(c->cfgtable); +#endif /* CCISS_DEBUG */ + + if (!(readl(&(c->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) + { + printk(KERN_WARNING "cciss: unable to get board into" + " simple mode\n"); + return -1; + } + return 0; + +} +/* + * Scans PCI space for any controllers that this driver can control. + */ +static int cciss_pci_detect(void) +{ + + int index; + unchar bus=0, dev_fn=0; + + for(index=0; ; index++) { + if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_COMPAQ_CISS, + index, &bus, &dev_fn)) + break; + printk(KERN_DEBUG "cciss: Device %x has been found at %x %x\n", + PCI_DEVICE_ID_COMPAQ_CISS, bus, dev_fn); + if (index == 1000000) break; + if (nr_ctlr == 8) { + printk(KERN_WARNING "cciss: This driver" + " supports a maximum of 8 controllers.\n"); + break; + } + hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + if(hba[nr_ctlr]==NULL) + { + printk(KERN_ERR "cciss: out of memory.\n"); + continue; + } + memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); + if (cciss_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) + { + kfree(hba[nr_ctlr]); + continue; + } + sprintf(hba[nr_ctlr]->devname, "cciss%d", nr_ctlr); + hba[nr_ctlr]->ctlr = nr_ctlr; + hba[nr_ctlr]->pci_bus = bus; + hba[nr_ctlr]->pci_dev_fn = dev_fn; + nr_ctlr++; + + } + return nr_ctlr; + +} + +/* + * Gets information about the local volumes attached to the controller. + */ +static void cciss_getgeometry(int cntl_num) +{ + ReportLunData_struct *ld_buff; + ReadCapdata_struct *size_buff; + InquiryData_struct *inq_buff; + int return_code; + int i; + int listlength = 0; + int lunid = 0; + int block_size; + int total_size; + + ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL); + if (ld_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + return; + } + memset(ld_buff, 0, sizeof(ReportLunData_struct)); + size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL); + if (size_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + return; + } + inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + kfree(size_buff); + return; + } + /* Get the firmware version */ + return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, + sizeof(InquiryData_struct), 0, 0 ,0 ); + if (return_code == IO_OK) + { + hba[cntl_num]->firm_ver[0] = inq_buff->data_byte[32]; + hba[cntl_num]->firm_ver[1] = inq_buff->data_byte[33]; + hba[cntl_num]->firm_ver[2] = inq_buff->data_byte[34]; + hba[cntl_num]->firm_ver[3] = inq_buff->data_byte[35]; + } else /* send command failed */ + { + printk(KERN_WARNING "cciss: unable to determine firmware" + " version of controller\n"); + } + /* Get the number of logical volumes */ + return_code = sendcmd(CISS_REPORT_LOG, cntl_num, ld_buff, + sizeof(ReportLunData_struct), 0, 0, 0 ); + + if( return_code == IO_OK) + { +#ifdef CCISS_DEBUG + printk("LUN Data\n--------------------------\n"); +#endif /* CCISS_DEBUG */ + + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8; + listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]); + } else /* reading number of logical volumes failed */ + { + printk(KERN_WARNING "cciss: report logical volume" + " command failed\n"); + listlength = 0; + } + hba[cntl_num]->num_luns = listlength / 8; // 8 bytes pre entry + if (hba[cntl_num]->num_luns > CISS_MAX_LUN) + { + printk(KERN_ERR "ciss: only %d number of logical volumes supported\n", + CISS_MAX_LUN); + hba[cntl_num]->num_luns = CISS_MAX_LUN; + } +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "Length = %x %x %x %x = %d\n", ld_buff->LUNListLength[0], + ld_buff->LUNListLength[1], ld_buff->LUNListLength[2], + ld_buff->LUNListLength[3], hba[cntl_num]->num_luns); +#endif /* CCISS_DEBUG */ + for(i=0; i< hba[cntl_num]->num_luns ; i++) + { + lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8; + lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]); + hba[cntl_num]->drv[i].LunID = lunid; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "LUN[%d]: %x %x %x %x = %x\n", i, + ld_buff->LUN[i][0], ld_buff->LUN[i][1],ld_buff->LUN[i][2], + ld_buff->LUN[i][3], hba[cntl_num]->drv[i].LunID); +#endif /* CCISS_DEBUG */ + + memset(size_buff, 0, sizeof(ReadCapdata_struct)); + return_code = sendcmd(CCISS_READ_CAPACITY, cntl_num, size_buff, + sizeof( ReadCapdata_struct), 1, i, 0 ); + if (return_code == IO_OK) + { + total_size = (0xff & + (unsigned int)(size_buff->total_size[0])) << 24; + total_size |= (0xff & + (unsigned int)(size_buff->total_size[1])) << 16; + total_size |= (0xff & + (unsigned int)(size_buff->total_size[2])) << 8; + total_size |= (0xff & (unsigned int) + (size_buff->total_size[3])); + total_size++; // command returns highest block address + + block_size = (0xff & + (unsigned int)(size_buff->block_size[0])) << 24; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[1])) << 16; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[2])) << 8; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[3])); + } else /* read capacity command failed */ + { + printk(KERN_WARNING "cciss: read capacity failed\n"); + total_size = block_size = 0; + } + printk(" blocks= %d block_size= %d\n", total_size, + block_size); + + /* Execute the command to read the disk geometry */ + memset(inq_buff, 0, sizeof(InquiryData_struct)); + return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, + sizeof(InquiryData_struct), 1, i ,0xC1 ); + if (return_code == IO_OK) + { + if(inq_buff->data_byte[8] == 0xFF) + { + printk(KERN_WARNING "cciss: reading geometry failed, volume does not support reading geometry\n"); + + hba[cntl_num]->drv[i].block_size = block_size; + hba[cntl_num]->drv[i].nr_blocks = total_size; + hba[cntl_num]->drv[i].heads = 255; + hba[cntl_num]->drv[i].sectors = 32; // Sectors per track + hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32; } else + { + + hba[cntl_num]->drv[i].block_size = block_size; + hba[cntl_num]->drv[i].nr_blocks = total_size; + hba[cntl_num]->drv[i].heads = + inq_buff->data_byte[6]; + hba[cntl_num]->drv[i].sectors = + inq_buff->data_byte[7]; + hba[cntl_num]->drv[i].cylinders = + (inq_buff->data_byte[4] & 0xff) << 8; + hba[cntl_num]->drv[i].cylinders += + inq_buff->data_byte[5]; + } + } + else /* Get geometry failed */ + { + printk(KERN_WARNING "cciss: reading geometry failed, continuing with default geometry\n"); + + hba[cntl_num]->drv[i].block_size = block_size; + hba[cntl_num]->drv[i].nr_blocks = total_size; + hba[cntl_num]->drv[i].heads = 255; + hba[cntl_num]->drv[i].sectors = 32; // Sectors per track + hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32; + } + printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n", + hba[cntl_num]->drv[i].heads, + hba[cntl_num]->drv[i].sectors, + hba[cntl_num]->drv[i].cylinders); + + } + kfree(ld_buff); + kfree(size_buff); +} + +/* + * This is it. Find all the controllers and register them. I really hate + * stealing all these major device numbers. + * returns the number of block devices registered. + */ +int cciss_init(void) +{ + int num_cntlrs_reg = 0; + int i; + int j; + + void (*request_fns[MAX_CTLR])(request_queue_t *) = { + do_cciss_request0, do_cciss_request1, + do_cciss_request2, do_cciss_request3, + do_cciss_request4, do_cciss_request5, + do_cciss_request6, do_cciss_request7, + }; + + /* detect controllers */ + cciss_pci_detect(); + + if (nr_ctlr == 0) + return(num_cntlrs_reg); + + printk(KERN_INFO DRIVER_NAME "\n"); + printk(KERN_INFO "Found %d controller(s)\n", nr_ctlr); + for(i=0;idevname, &cciss_fops)) + { + printk(KERN_ERR "cciss: Unable to get major number " + "%d for %s\n", MAJOR_NR+i, hba[i]->devname); + continue; + } + /* make sure the board interrupts are off */ + hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); + if( request_irq(hba[i]->intr, do_cciss_intr, SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) + { + printk(KERN_ERR "ciss: Unable to get irq %d for %s\n", + hba[i]->intr, hba[i]->devname); + unregister_blkdev( MAJOR_NR+i, hba[i]->devname); + continue; + } + num_cntlrs_reg++; + hba[i]->cmd_pool_bits = (__u32*)kmalloc( + ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL); + hba[i]->cmd_pool = (CommandList_struct *)kmalloc( + NR_CMDS * sizeof(CommandList_struct), + GFP_KERNEL); + hba[i]->errinfo_pool = (ErrorInfo_struct *)kmalloc( + NR_CMDS * sizeof( ErrorInfo_struct), + GFP_KERNEL); + if((hba[i]->cmd_pool_bits == NULL) + || (hba[i]->cmd_pool == NULL) + || (hba[i]->errinfo_pool == NULL)) + { + nr_ctlr = i; + if(hba[i]->cmd_pool_bits) + kfree(hba[i]->cmd_pool_bits); + if(hba[i]->cmd_pool) + kfree(hba[i]->cmd_pool); + if(hba[i]->errinfo_pool) + kfree(hba[i]->errinfo_pool); + free_irq(hba[i]->intr, hba[i]); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + num_cntlrs_reg--; + printk( KERN_ERR "cciss: out of memory"); + return(num_cntlrs_reg); + } + + /* command and error info recs zeroed out before + they are used */ + memset(hba[i]->cmd_pool_bits, 0, + ((NR_CMDS+31)/32)*sizeof(__u32)); + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "Scanning for drives on controller cciss%d\n",i); +#endif /* CCISS_DEBUG */ + + cciss_getgeometry(i); + + /* Turn the interrupts on so we can service requests */ + hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON); + + cciss_procinit(i); + /* Fill in the gendisk data */ + hba[i]->gendisk.major = MAJOR_NR + i; + hba[i]->gendisk.major_name = "cciss"; + hba[i]->gendisk.minor_shift = NWD_SHIFT; + hba[i]->gendisk.max_p = MAX_PART; + hba[i]->gendisk.part = hba[i]->hd; + hba[i]->gendisk.sizes = hba[i]->sizes; + hba[i]->gendisk.nr_real = hba[i]->num_luns; + + /* fill in the other Kernel structs */ + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), + request_fns[i]); + blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); + blksize_size[MAJOR_NR+i] = hba[i]->blocksizes; + hardsect_size[MAJOR_NR+i] = hba[i]->hardsizes; + read_ahead[MAJOR_NR+i] = READ_AHEAD; + + /* Get on the disk list */ + hba[i]->gendisk.next = gendisk_head; + gendisk_head = &(hba[i]->gendisk); + + cciss_geninit(i); + for(j=0; jgendisk, + MKDEV(MAJOR_NR+i,j<<4), + 16, &cciss_fops, hba[i]->drv[j].nr_blocks); + } + return(nr_ctlr); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + + +/* This is a bit of a hack... */ +int init_module(void) +{ + if (cciss_init() == 0) /* all the block dev numbers already used */ + return -EIO; /* or no controllers were found */ + return 0; +} + +void cleanup_module(void) +{ + int i; + struct gendisk *g; + + for(i=0; iaccess.set_intr_mask(hba[i], CCISS_INTR_OFF); + free_irq(hba[i]->intr, hba[i]); + iounmap((void*)hba[i]->vaddr); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i)); + remove_proc_entry(hba[i]->devname, proc_cciss); + + /* remove it from the disk list */ + if (gendisk_head == &(hba[i]->gendisk)) + { + gendisk_head = hba[i]->gendisk.next; + } else + { + for(g=gendisk_head; g ; g=g->next) + { + if(g->next == &(hba[i]->gendisk)) + { + g->next = hba[i]->gendisk.next; + } + } + } + remove_proc_entry("cciss", &proc_root); + kfree(hba[i]->cmd_pool); + kfree(hba[i]->errinfo_pool); + kfree(hba[i]->cmd_pool_bits); + kfree(hba[i]); + } +} + +#endif /* MODULE */ + diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h new file mode 100644 index 000000000000..1053aefd3ae1 --- /dev/null +++ b/drivers/block/cciss.h @@ -0,0 +1,201 @@ +#ifndef CCISS_H +#define CCISS_H + +#include + +#include "cciss_cmd.h" + + +#define NWD 16 +#define NWD_SHIFT 4 +#define MAX_PART 16 + +#define IO_OK 0 +#define IO_ERROR 1 + +#define MAJOR_NR COMPAQ_CISS_MAJOR + +struct ctlr_info; +typedef struct ctlr_info ctlr_info_t; + +struct access_method { + void (*submit_command)(ctlr_info_t *h, CommandList_struct *c); + void (*set_intr_mask)(ctlr_info_t *h, unsigned long val); + unsigned long (*fifo_full)(ctlr_info_t *h); + unsigned long (*intr_pending)(ctlr_info_t *h); + unsigned long (*command_completed)(ctlr_info_t *h); +}; +typedef struct _drive_info_struct +{ + __u32 LunID; + int usage_count; + int nr_blocks; + int block_size; + int heads; + int sectors; + int cylinders; +} drive_info_struct; + +struct ctlr_info +{ + int ctlr; + char devname[8]; + char *product_name; + char firm_ver[4]; // Firmware version + unchar pci_bus; + unchar pci_dev_fn; + __u32 board_id; + __u32 vaddr; + __u32 paddr; + CfgTable_struct *cfgtable; + int intr; + + int max_commands; + int commands_outstanding; + int max_outstanding; /* Debug */ + int num_luns; + int usage_count; /* number of opens all all minor devices */ + + // information about each logical volume + drive_info_struct drv[CISS_MAX_LUN]; + + struct access_method access; + + /* queue and queue Info */ + CommandList_struct *reqQ; + CommandList_struct *cmpQ; + unsigned int Qdepth; + unsigned int maxQsinceinit; + unsigned int maxSG; + + //* pointers to command and error info pool */ + CommandList_struct *cmd_pool; + ErrorInfo_struct *errinfo_pool; + __u32 *cmd_pool_bits; + int nr_allocs; + int nr_frees; + + // Disk structures we need to pass back + struct gendisk gendisk; + // indexed by minor numbers + struct hd_struct hd[256]; + int sizes[256]; + int blocksizes[256]; + int hardsizes[256]; +}; + +/* Defining the diffent access_menthods */ +/* + * Memory mapped FIFO interface (SMART 53xx cards) + */ +#define SA5_DOORBELL 0x20 +#define SA5_REQUEST_PORT_OFFSET 0x40 +#define SA5_REPLY_INTR_MASK_OFFSET 0x34 +#define SA5_REPLY_PORT_OFFSET 0x44 +#define SA5_INTR_STATUS 0x30 + +#define SA5_INTR_OFF 0x08 +#define SA5_INTR_PENDING 0x08 +#define FIFO_EMPTY 0xffffffff + +#define CISS_ERROR_BIT 0x02 + +#define CCISS_INTR_ON 1 +#define CCISS_INTR_OFF 0 +/* + Send the command to the hardware +*/ +static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) +{ +#ifdef CCISS_DEBUG + printk("Sending %x - down to controller\n", c->busaddr ); +#endif /* CCISS_DEBUG */ + writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); + h->commands_outstanding++; + if ( h->commands_outstanding > h->max_outstanding) + h->max_outstanding = h->commands_outstanding; +} + +/* + * This card is the oposite of the other cards. + * 0 turns interrupts on... + * 0x08 turns them off... + */ +static void SA5_intr_mask(ctlr_info_t *h, unsigned long val) +{ + if (val) + { /* Turn interrupts on */ + writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET); + } else /* Turn them off */ + { + writel( SA5_INTR_OFF, + h->vaddr + SA5_REPLY_INTR_MASK_OFFSET); + } +} +/* + * Returns true if fifo is full. + * + */ +static unsigned long SA5_fifo_full(ctlr_info_t *h) +{ + if( h->commands_outstanding >= h->max_commands) + return(1); + else + return(0); + +} +/* + * returns value read from hardware. + * returns FIFO_EMPTY if there is nothing to read + */ +static unsigned long SA5_completed(ctlr_info_t *h) +{ + unsigned long register_value + = readl(h->vaddr + SA5_REPLY_PORT_OFFSET); + if(register_value != FIFO_EMPTY) + { + h->commands_outstanding--; +#ifdef CCISS_DEBUG + printk("cciss: Read %lx back from board\n", register_value); +#endif /* CCISS_DEBUG */ + } +#ifdef CCISS_DEBUG + else + { + printk("cciss: FIFO Empty read\n"); + } +#endif + return ( register_value); + +} +/* + * Returns true if an interrupt is pending.. + */ +static unsigned long SA5_intr_pending(ctlr_info_t *h) +{ + unsigned long register_value = + readl(h->vaddr + SA5_INTR_STATUS); +#ifdef CCISS_DEBUG + printk("cciss: intr_pending %lx\n", register_value); +#endif /* CCISS_DEBUG */ + if( register_value & SA5_INTR_PENDING) + return 1; + return 0 ; +} + + +static struct access_method SA5_access = { + SA5_submit_command, + SA5_intr_mask, + SA5_fifo_full, + SA5_intr_pending, + SA5_completed, +}; + +struct board_type { + __u32 board_id; + char *product_name; + struct access_method *access; +}; +#endif /* CCISS_H */ + diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h new file mode 100644 index 000000000000..456a3a021dc8 --- /dev/null +++ b/drivers/block/cciss_cmd.h @@ -0,0 +1,254 @@ +#ifndef CCISS_CMD_H +#define CCISS_CMD_H +//########################################################################### +//DEFINES +//########################################################################### +#define CISS_VERSION "1.00" + +//general boundary defintions +#define SENSEINFOBYTES 32//note that this value may vary between host implementations +#define MAXSGENTRIES 31 +#define MAXREPLYQS 256 + +//Command Status value +#define CMD_SUCCESS 0x0000 +#define CMD_TARGET_STATUS 0x0001 +#define CMD_DATA_UNDERRUN 0x0002 +#define CMD_DATA_OVERRUN 0x0003 +#define CMD_INVALID 0x0004 +#define CMD_PROTOCOL_ERR 0x0005 +#define CMD_HARDWARE_ERR 0x0006 +#define CMD_CONNECTION_LOST 0x0007 +#define CMD_ABORTED 0x0008 +#define CMD_ABORT_FAILED 0x0009 +#define CMD_UNSOLICITED_ABORT 0x000A +#define CMD_TIMEOUT 0x000B +#define CMD_UNABORTABLE 0x000C + +//transfer direction +#define XFER_NONE 0x00 +#define XFER_WRITE 0x01 +#define XFER_READ 0x02 +#define XFER_RSVD 0x03 + +//task attribute +#define ATTR_UNTAGGED 0x00 +#define ATTR_SIMPLE 0x04 +#define ATTR_HEADOFQUEUE 0x05 +#define ATTR_ORDERED 0x06 +#define ATTR_ACA 0x07 + +//cdb type +#define TYPE_CMD 0x00 +#define TYPE_MSG 0x01 + +//config space register offsets +#define CFG_VENDORID 0x00 +#define CFG_DEVICEID 0x02 +#define CFG_I2OBAR 0x10 +#define CFG_MEM1BAR 0x14 + +//i2o space register offsets +#define I2O_IBDB_SET 0x20 +#define I2O_IBDB_CLEAR 0x70 +#define I2O_INT_STATUS 0x30 +#define I2O_INT_MASK 0x34 +#define I2O_IBPOST_Q 0x40 +#define I2O_OBPOST_Q 0x44 + +//Configuration Table +#define CFGTBL_ChangeReq 0x00000001l +#define CFGTBL_AccCmds 0x00000001l + +#define CFGTBL_Trans_Simple 0x00000002l + +#define CFGTBL_BusType_Ultra2 0x00000001l +#define CFGTBL_BusType_Ultra3 0x00000002l +#define CFGTBL_BusType_Fibre1G 0x00000100l +#define CFGTBL_BusType_Fibre2G 0x00000200l +typedef struct _vals32 +{ + __u32 lower; + __u32 upper; +} vals32; + +typedef union _u64bit +{ + vals32 val32; + __u64 val; +} u64bit; + +// Type defs used in the following structs +#define BYTE __u8 +#define WORD __u16 +#define HWORD __u16 +#define DWORD __u32 +#define QWORD vals32 + +//########################################################################### +//STRUCTURES +//########################################################################### +#define CISS_MAX_LUN 16 +// SCSI-3 Cmmands + +#pragma pack(1) + +#define CISS_INQUIRY 0x12 +//Date returned +typedef struct _InquiryData_struct +{ + BYTE data_byte[36]; +} InquiryData_struct; + +#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */ +// Data returned +typedef struct _ReportLUNdata_struct +{ + BYTE LUNListLength[4]; + DWORD reserved; + BYTE LUN[CISS_MAX_LUN][8]; +} ReportLunData_struct; + +#define CCISS_READ_CAPACITY 0x25 /* Read Capacity */ +typedef struct _ReadCapdata_struct +{ + BYTE total_size[4]; // Total size in blocks + BYTE block_size[4]; // Size of blocks in bytes +} ReadCapdata_struct; + +// 12 byte commands not implemented in firmware yet. +// #define CCISS_READ 0xa8 // Read(12) +// #define CCISS_WRITE 0xaa // Write(12) + #define CCISS_READ 0x28 // Read(10) + #define CCISS_WRITE 0x2a // Write(10) + +//Command List Structure +typedef union _SCSI3Addr_struct { + struct { + BYTE Bus:6; + BYTE Mode:2; // b00 + BYTE Dev; + } PeripDev; + struct { + BYTE DevMSB:6; + BYTE Mode:2; // b01 + BYTE DevLSB; + } LogDev; + struct { + BYTE Targ:6; + BYTE Mode:2; // b10 + BYTE Dev:5; + BYTE Bus:3; + } LogUnit; +} SCSI3Addr_struct; + +typedef struct _PhysDevAddr_struct { + DWORD TargetId:24; + DWORD Bus:6; + DWORD Mode:2; + SCSI3Addr_struct Target[2]; //2 level target device addr +} PhysDevAddr_struct; + +typedef struct _LogDevAddr_struct { + DWORD VolId:30; + DWORD Mode:2; + BYTE reserved[4]; +} LogDevAddr_struct; + +typedef union _LUNAddr_struct { + BYTE LunAddrBytes[8]; + SCSI3Addr_struct SCSI3Lun[4]; + PhysDevAddr_struct PhysDev; + LogDevAddr_struct LogDev; +} LUNAddr_struct; + +typedef struct _CommandListHeader_struct { + BYTE ReplyQueue; + BYTE SGList; + HWORD SGTotal; + QWORD Tag; + LUNAddr_struct LUN; +} CommandListHeader_struct; +typedef struct _RequestBlock_struct { + BYTE CDBLen; + struct { + BYTE Type:3; + BYTE Attribute:3; + BYTE Direction:2; + } Type; + HWORD Timeout; + BYTE CDB[16]; +} RequestBlock_struct; +typedef struct _ErrDescriptor_struct { + QWORD Addr; + DWORD Len; +} ErrDescriptor_struct; +typedef struct _SGDescriptor_struct { + QWORD Addr; + DWORD Len; + DWORD Ext; +} SGDescriptor_struct; + +typedef union _MoreErrInfo_struct{ + struct { + BYTE Reserved[3]; + BYTE Type; + DWORD ErrorInfo; + }Common_Info; + struct{ + BYTE Reserved[2]; + BYTE offense_size;//size of offending entry + BYTE offense_num; //byte # of offense 0-base + DWORD offense_value; + }Invalid_Cmd; +}MoreErrInfo_struct; +typedef struct _ErrorInfo_struct { + BYTE ScsiStatus; + BYTE SenseLen; + HWORD CommandStatus; + DWORD ResidualCnt; + MoreErrInfo_struct MoreErrInfo; + BYTE SenseInfo[SENSEINFOBYTES]; +} ErrorInfo_struct; + +/* Command types */ +#define CMD_RWREQ 0x00 +#define CMD_IOCTL_PEND 0x01 +#define CMD_IOCTL_DONE 0x02 + +typedef struct _CommandList_struct { + CommandListHeader_struct Header; + RequestBlock_struct Request; + ErrDescriptor_struct ErrDesc; + SGDescriptor_struct SG[MAXSGENTRIES]; + /* information associated with the command */ + __u32 busaddr; /* physical addres of this record */ + ErrorInfo_struct * err_info; /* pointer to the allocated mem */ + int cmd_type; + struct _CommandList_struct *prev; + struct _CommandList_struct *next; + struct buffer_head * bh; +} CommandList_struct; + +//Configuration Table Structure +typedef struct _HostWrite_struct { + DWORD TransportRequest; + DWORD Reserved; + DWORD CoalIntDelay; + DWORD CoalIntCount; +} HostWrite_struct; + +typedef struct _CfgTable_struct { + BYTE Signature[4]; + DWORD SpecValence; + DWORD TransportSupport; + DWORD TransportActive; + HostWrite_struct HostWrite; + DWORD CmdsOutMax; + DWORD BusTypes; + DWORD Reserved; + BYTE ServerName[16]; + DWORD HeartBeat; +} CfgTable_struct; +#pragma pack() +#endif // CCISS_CMD_H diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 479f0b7dabdb..ad7c35d00486 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -46,6 +46,12 @@ #define DRIVER_NAME "Compaq SMART2 Driver (v 2.4.0)" #define DRIVER_VERSION SMART2_DRIVER_VERSION(2,4,0) + +/* Embedded module documentation macros - see modules.h */ +/* Original author Chris Frantz - Compaq Computer Corporation */ +MODULE_AUTHOR("Compaq Computer Corporation"); +MODULE_DESCRIPTION("Driver for Compaq Smart2 Array Controllers"); + #define MAJOR_NR COMPAQ_SMART2_MAJOR #include #include @@ -615,6 +621,8 @@ static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn) int i; + c->pci_bus = bus; + c->pci_dev_fn = device_fn; pdev = pci_find_slot(bus, device_fn); vendor_id = pdev->vendor; device_id = pdev->device; @@ -770,6 +778,8 @@ static int cpqarray_eisa_detect(void) hba[nr_ctlr]->access = *(products[j].access); hba[nr_ctlr]->ctlr = nr_ctlr; hba[nr_ctlr]->board_id = board_id; + hba[nr_ctlr]->pci_bus = 0; /* not PCI */ + hba[nr_ctlr]->pci_dev_fn = 0; /* not PCI */ DBGINFO( printk("i = %d, j = %d\n", i, j); @@ -899,7 +909,7 @@ static void do_ida_request(int ctlr) if (ctlr != MAJOR(creq->rq_dev)-MAJOR_NR || ctlr > nr_ctlr || h == NULL) { - printk("doreq cmd for %d, %x at %p\n", + printk(KERN_WARNING "doreq cmd for %d, %x at %p\n", ctlr, creq->rq_dev, creq); complete_buffers(creq->bh, 0); start_io(h); @@ -1189,6 +1199,20 @@ static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, if (!arg) return -EINVAL; put_user(DRIVER_VERSION, (unsigned long*)arg); return 0; + case IDAGETPCIINFO: + { + + ida_pci_info_struct pciinfo; + + if (!arg) return -EINVAL; + pciinfo.bus = hba[ctlr]->pci_bus; + pciinfo.dev_fn = hba[ctlr]->pci_dev_fn; + pciinfo.board_id = hba[ctlr]->board_id; + if(copy_to_user((void *) arg, &pciinfo, + sizeof( ida_pci_info_struct))) + return -EFAULT; + return(0); + } case BLKFLSBUF: case BLKROSET: @@ -1199,7 +1223,7 @@ static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, return blk_ioctl(inode->i_rdev, cmd, arg); default: - return -EBADRQC; + return -EINVAL; } } @@ -1379,6 +1403,8 @@ static int sendcmd( ctlr_info_t *info_p = hba[ctlr]; c = cmd_alloc(info_p); + if(!c) + return IO_ERROR; c->ctlr = ctlr; c->hdr.unit = log_unit; c->hdr.prio = 0; diff --git a/drivers/block/cpqarray.h b/drivers/block/cpqarray.h index 31e9786f3fa2..8b57c46a821e 100644 --- a/drivers/block/cpqarray.h +++ b/drivers/block/cpqarray.h @@ -87,6 +87,8 @@ struct ctlr_info { int log_drives; int phys_drives; + unsigned char pci_bus; /* 0 if EISA */ + unsigned char pci_dev_fn; /* 0 if EISA */ __u32 board_id; char *product_name; diff --git a/drivers/block/ida_ioctl.h b/drivers/block/ida_ioctl.h index 9c159df98384..5cc212431689 100644 --- a/drivers/block/ida_ioctl.h +++ b/drivers/block/ida_ioctl.h @@ -33,7 +33,14 @@ #define IDAGETCTLRSIG 0x29293030 #define IDAREVALIDATEVOLS 0x30303131 #define IDADRIVERVERSION 0x31313232 +#define IDAGETPCIINFO 0x32323333 +typedef struct _ida_pci_info_struct +{ + unsigned char bus; + unsigned char dev_fn; + __u32 board_id; +} ida_pci_info_struct; /* * Normally, the ioctl determines the logical unit for this command by * the major,minor number of the fd passed to ioctl. If you need to send @@ -60,7 +67,7 @@ typedef struct { union ctlr_cmds { drv_info_t drv; - unsigned char buf[512]; + unsigned char buf[1024]; id_ctlr_t id_ctlr; drv_param_t drv_param; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 8a61d1e84346..6e877dcd515c 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -602,6 +602,8 @@ static inline void add_request(request_queue_t * q, struct request * req, (q->request_fn)(q); if (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) (q->request_fn)(q); + if (major >= COMPAQ_CISS_MAJOR+0 && major <= COMPAQ_CISS_MAJOR+7) + (q->request_fn)(q); } /* diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 5037f517bdc7..da1d6629d1f0 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -584,6 +584,8 @@ static int loop_set_status(struct loop_device *lo, struct loop_info *arg) type = info.lo_encrypt_type; if (type >= MAX_LO_CRYPT || xfer_funcs[type] == NULL) return -EINVAL; + if (type == LO_CRYPT_XOR && info.lo_encrypt_key_size == 0) + return -EINVAL; err = loop_release_xfer(lo); if (!err) err = loop_init_xfer(lo, type, &info); @@ -793,7 +795,6 @@ int __init loop_init(void) max_loop = 8; } - printk(KERN_INFO "loop: registered device at major %d\n", MAJOR_NR); printk(KERN_INFO "loop: enabling %d loop devices\n", max_loop); loop_dev = kmalloc (max_loop * sizeof(struct loop_device), GFP_KERNEL); diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 7847cc9d84fb..6d07f3069c8f 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -16,6 +16,7 @@ * handle GCR disks */ +#include #include #include #include diff --git a/drivers/char/drm/mga_dma.c b/drivers/char/drm/mga_dma.c index f80fb4893d04..4daa03acbe1e 100644 --- a/drivers/char/drm/mga_dma.c +++ b/drivers/char/drm/mga_dma.c @@ -214,6 +214,7 @@ drm_buf_t *mga_freelist_get(drm_device_t *dev) drm_mga_freelist_t *prev; drm_mga_freelist_t *next; static int failed = 0; + int return_null = 0; DRM_DEBUG("%s : tail->age : %d last_prim_age : %d\n", __FUNCTION__, dev_priv->tail->age, dev_priv->last_prim_age); @@ -222,23 +223,25 @@ drm_buf_t *mga_freelist_get(drm_device_t *dev) DRM_DEBUG("I'm waiting on the freelist!!! %d\n", dev_priv->last_prim_age); set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&dev_priv->buf_queue, &entry); for (;;) { mga_dma_schedule(dev, 0); + current->state = TASK_INTERRUPTIBLE; if(!test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) break; atomic_inc(&dev->total_sleeps); schedule(); if (signal_pending(current)) { + ++return_null; clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); - goto failed_getbuf; + break; } } - current->state = TASK_RUNNING; + current->state = TASK_RUNNING; remove_wait_queue(&dev_priv->buf_queue, &entry); + if (return_null) return NULL; } if(dev_priv->tail->age < dev_priv->last_prim_age) { @@ -251,8 +254,6 @@ drm_buf_t *mga_freelist_get(drm_device_t *dev) failed = 0; return next->buf; } - -failed_getbuf: failed++; return NULL; } diff --git a/drivers/char/drm/r128_drv.h b/drivers/char/drm/r128_drv.h index 5b15dddfbe93..e0eef6d20c97 100644 --- a/drivers/char/drm/r128_drv.h +++ b/drivers/char/drm/r128_drv.h @@ -197,7 +197,7 @@ extern int r128_context_switch_complete(drm_device_t *dev, int new); #define R128_MAX_USEC_TIMEOUT 100000 /* 100 ms */ -#define R128_BASE(reg) ((u32)(dev_priv->mmio->handle)) +#define R128_BASE(reg) ((unsigned long)(dev_priv->mmio->handle)) #define R128_ADDR(reg) (R128_BASE(reg) + reg) #define R128_DEREF(reg) *(__volatile__ int *)R128_ADDR(reg) diff --git a/drivers/char/qpmouse.c b/drivers/char/qpmouse.c index 4db93b1fb2b6..bba9ddd31542 100644 --- a/drivers/char/qpmouse.c +++ b/drivers/char/qpmouse.c @@ -344,9 +344,14 @@ int __init qpmouse_init(void) printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n"); /* printk("82C710 address = %x (should be 0x310)\n", qp_data); */ + queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + if(queue==NULL) + { + printk(KERN_ERR "qpmouse: no queue memory.\n"); + return -ENOMEM; + } qp_present = 1; misc_register(&qp_mouse); - queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); memset(queue, 0, sizeof(*queue)); queue->head = queue->tail = 0; init_waitqueue_head(&queue->proc_list); diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index c0ff9b642f77..b61f397d2646 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1686,6 +1686,7 @@ __initfunc(int printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); return (0); } +#ifdef notdef if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) { printk(KERN_WARNING "HFC-PCI shared mem address will be corrected\n"); pcibios_write_config_word(cs->hw.hfcpci.pci_bus, @@ -1719,6 +1720,7 @@ __initfunc(int } dev_hfcpci->resource[1].start = (int) cs->hw.hfcpci.pci_io; } +#endif if (!cs->hw.hfcpci.pci_io) { printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); return (0); diff --git a/drivers/macintosh/rtc.c b/drivers/macintosh/rtc.c index 1d61793b7866..3e1435a6c809 100644 --- a/drivers/macintosh/rtc.c +++ b/drivers/macintosh/rtc.c @@ -13,7 +13,6 @@ * ftp://vger.rutgers.edu/pub/linux/Sparc/userland/clock.c */ -#include #include #include #include diff --git a/drivers/md/Config.in b/drivers/md/Config.in new file mode 100644 index 000000000000..fd284e718d5c --- /dev/null +++ b/drivers/md/Config.in @@ -0,0 +1,22 @@ +# +# Block device driver configuration +# +mainmenu_option next_comment +comment 'Multi-device support (RAID and LVM)' + +tristate 'Multiple devices driver support' CONFIG_BLK_DEV_MD +dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD +dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD +dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD +dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD +if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_RAID0" = "y" -o "$CONFIG_MD_RAID1" = "y" -o "$CONFIG_MD_RAID5" = "y" ]; then + bool ' Boot support' CONFIG_MD_BOOT + bool ' Auto Detect support' CONFIG_AUTODETECT_RAID +fi + +tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N +if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then + bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y +fi + +endmenu diff --git a/drivers/md/Makefile b/drivers/md/Makefile new file mode 100644 index 000000000000..69d65c2bdc59 --- /dev/null +++ b/drivers/md/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the kernel software RAID and LVM drivers. +# + +O_TARGET := mddev.o +SUB_DIRS := +ALL_SUB_DIRS := +MOD_SUB_DIRS := + +export-objs := md.o xor.o +list-multi := lvm-mod.o +lvm-mod-objs := lvm.o lvm-snap.o + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_BLK_DEV_MD) += md.o +obj-$(CONFIG_MD_LINEAR) += linear.o +obj-$(CONFIG_MD_RAID0) += raid0.o +obj-$(CONFIG_MD_RAID1) += raid1.o +obj-$(CONFIG_MD_RAID5) += raid5.o xor.o +obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o + +# Translate to Rules.make lists. +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + +include $(TOPDIR)/Rules.make + +lvm-mod.o: $(lvm-mod-objs) + $(LD) -r -o $@ $(lvm-mod-objs) diff --git a/drivers/block/linear.c b/drivers/md/linear.c similarity index 100% rename from drivers/block/linear.c rename to drivers/md/linear.c diff --git a/drivers/block/md.c b/drivers/md/md.c similarity index 100% rename from drivers/block/md.c rename to drivers/md/md.c diff --git a/drivers/block/raid0.c b/drivers/md/raid0.c similarity index 100% rename from drivers/block/raid0.c rename to drivers/md/raid0.c diff --git a/drivers/block/raid1.c b/drivers/md/raid1.c similarity index 100% rename from drivers/block/raid1.c rename to drivers/md/raid1.c diff --git a/drivers/block/raid5.c b/drivers/md/raid5.c similarity index 100% rename from drivers/block/raid5.c rename to drivers/md/raid5.c diff --git a/drivers/block/xor.c b/drivers/md/xor.c similarity index 100% rename from drivers/block/xor.c rename to drivers/md/xor.c diff --git a/drivers/media/radio/Config.in b/drivers/media/radio/Config.in index 34b396acf790..7d681854374b 100644 --- a/drivers/media/radio/Config.in +++ b/drivers/media/radio/Config.in @@ -21,6 +21,7 @@ dep_tristate ' GemTek Radio Card support' CONFIG_RADIO_GEMTEK $CONFIG_VIDEO_DEV if [ "$CONFIG_RADIO_GEMTEK" = "y" ]; then hex ' GemTek i/o port (0x20c, 0x30c, 0x24c or 0x34c)' CONFIG_RADIO_GEMTEK_PORT 34c fi +dep_tristate ' Maestro on board radio' CONFIG_RADIO_MAESTRO $CONFIG_VIDEO_DEV dep_tristate ' Miro PCM20 Radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV dep_tristate ' SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 1b74d6b8835f..03cc959eaf0d 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o +obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c new file mode 100644 index 000000000000..d4278fe9f397 --- /dev/null +++ b/drivers/media/radio/radio-maestro.c @@ -0,0 +1,384 @@ +/* Maestro PCI sound card radio driver for Linux support + * (c) 2000 A. Tlalka, atlka@pg.gda.pl + * Notes on the hardware + * + * + Frequency control is done digitally + * + No volume control - only mute/unmute - you have to use Aux line volume + * control on Maestro card to set the volume + * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after + * frequency setting (>100ms) and only when the radio is unmuted. + * version 0.02 + * + io port is automatically detected - only the first radio is used + * version 0.03 + * + thread access locking additions + * version 0.04 + * + code improvements + * + VIDEO_TUNER_LOW is permanent + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.04" + +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */ + +#define IO_MASK 4 /* mask register offset from GPIO_DATA + bits 1=unmask write to given bit */ +#define IO_DIR 8 /* direction register offset from GPIO_DATA + bits 0/1=read/write direction */ + +#define GPIO6 0x0040 /* mask bits for GPIO lines */ +#define GPIO7 0x0080 +#define GPIO8 0x0100 +#define GPIO9 0x0200 + +#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */ +#define STR_CLK GPIO7 +#define STR_WREN GPIO8 +#define STR_MOST GPIO9 + +#define FREQ_LO 50*16000 +#define FREQ_HI 150*16000 + +#define FREQ_IF 171200 /* 10.7*16000 */ +#define FREQ_STEP 200 /* 12.5*16 */ + +#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\ + /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */ + +#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) + + + +static int radio_open(struct video_device *, int); +static int radio_ioctl(struct video_device *, unsigned int, void *); +static void radio_close(struct video_device *); + +static struct video_device maestro_radio= +{ + "Maestro radio", + VID_TYPE_TUNER, + VID_HARDWARE_SF16MI, + radio_open, + radio_close, + NULL, + NULL, + NULL, + radio_ioctl, + NULL, + NULL +}; + +static struct radio_device +{ + __u16 io, /* base of Maestro card radio io (GPIO_DATA)*/ + muted, /* VIDEO_AUDIO_MUTE */ + stereo, /* VIDEO_TUNER_STEREO_ON */ + tuned; /* signal strength (0 or 0xffff) */ + struct semaphore lock; +} radio_unit = {0, 0, 0, 0, }; + +static int users = 0; + +static void sleep_125ms(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ >> 3); +} + +static void udelay2(void) +{ + udelay(2); +} + +static void udelay4(void) +{ + udelay(4); +} + +static void udelay16(void) +{ + udelay(16); +} + +static __u32 radio_bits_get(struct radio_device *dev) +{ + register __u16 io=dev->io, l, rdata; + register __u32 data=0; + __u16 omask; + omask = inw(io + IO_MASK); + outw(~(STR_CLK | STR_WREN), io + IO_MASK); + outw(0, io); + udelay16(); + for (l=24;l--;) { + outw(STR_CLK, io); /* HI state */ + udelay2(); + if(!l) + dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff; + outw(0, io); /* LO state */ + udelay2(); + data <<= 1; /* shift data */ + rdata = inw(io); + if(!l) + dev->stereo = rdata & STR_MOST ? + 0 : VIDEO_TUNER_STEREO_ON; + else + if(rdata & STR_DATA) + data++; + udelay2(); + } + if(dev->muted) + outw(STR_WREN, io); + udelay4(); + outw(omask, io + IO_MASK); + return data & 0x3ffe; +} + +static void radio_bits_set(struct radio_device *dev, __u32 data) +{ + register __u16 io=dev->io, l, bits; + __u16 omask, odir; + omask = inw(io + IO_MASK); + odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); + outw(odir | STR_DATA, io + IO_DIR); + outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); + udelay16(); + for (l=25;l;l--) { + bits = ((data >> 18) & STR_DATA) | STR_WREN ; + data <<= 1; /* shift data */ + outw(bits, io); /* start strobe */ + udelay2(); + outw(bits | STR_CLK, io); /* HI level */ + udelay2(); + outw(bits, io); /* LO level */ + udelay4(); + } + if(!dev->muted) + outw(0, io); + udelay4(); + outw(omask, io + IO_MASK); + outw(odir, io + IO_DIR); + sleep_125ms(); +} + +inline static int radio_function(struct video_device *dev, + unsigned int cmd, void *arg) +{ + struct radio_device *card=dev->priv; + switch(cmd) { + case VIDIOCGCAP: { + struct video_capability v; + strcpy(v.name, "Maestro radio"); + v.type=VID_TYPE_TUNER; + v.channels=v.audios=1; + v.maxwidth=v.maxheight=v.minwidth=v.minheight=0; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: { + struct video_tuner v; + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) + return -EINVAL; + (void)radio_bits_get(card); + v.flags = VIDEO_TUNER_LOW | card->stereo; + v.signal = card->tuned; + strcpy(v.name, "FM"); + v.rangelow = FREQ_LO; + v.rangehigh = FREQ_HI; + v.mode = VIDEO_MODE_AUTO; + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + return 0; + } + case VIDIOCGFREQ: { + unsigned long tmp=BITS2FREQ(radio_bits_get(card)); + if(copy_to_user(arg, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; + } + case VIDIOCSFREQ: { + unsigned long tmp; + if(copy_from_user(&tmp, arg, sizeof(tmp))) + return -EFAULT; + if ( tmpFREQ_HI ) + return -EINVAL; + radio_bits_set(card, FREQ2BITS(tmp)); + return 0; + } + case VIDIOCGAUDIO: { + struct video_audio v; + strcpy(v.name, "Radio"); + v.audio=v.volume=v.bass=v.treble=v.balance=v.step=0; + v.flags=VIDEO_AUDIO_MUTABLE | card->muted; + v.mode=VIDEO_SOUND_STEREO; + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + { + register __u16 io=card->io; + register __u16 omask = inw(io + IO_MASK); + outw(~STR_WREN, io + IO_MASK); + outw((card->muted = v.flags & VIDEO_AUDIO_MUTE) + ? STR_WREN : 0, io); + udelay4(); + outw(omask, io + IO_MASK); + sleep_125ms(); + return 0; + } + } + case VIDIOCGUNIT: { + struct video_unit v; + v.video=VIDEO_NO_UNIT; + v.vbi=VIDEO_NO_UNIT; + v.radio=dev->minor; + v.audio=0; + v.teletext=VIDEO_NO_UNIT; + if(copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + default: return -ENOIOCTLCMD; + } +} + +static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct radio_device *card=dev->priv; + int ret; + down(&card->lock); + ret = radio_function(dev, cmd, arg); + up(&card->lock); + return ret; +} + +static int radio_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void radio_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + + +inline static __u16 radio_install(struct pci_dev *pcidev); + +#ifdef MODULE +MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl"); +MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio."); + +EXPORT_NO_SYMBOLS; + +void cleanup_module(void) +{ + video_unregister_device(&maestro_radio); +} + +int init_module(void) +#else +int __init maestro_radio_init(struct video_init *v) +#endif +{ + register __u16 found=0; + struct pci_dev *pcidev = NULL; + if(!pci_present()) + return -ENODEV; + while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS, + PCI_DEVICE_ID_ESS_ESS1968, + pcidev))) + found |= radio_install(pcidev); + while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS, + PCI_DEVICE_ID_ESS_ESS1978, + pcidev))) + found |= radio_install(pcidev); + if(!found) { + printk(KERN_INFO "radio-maestro: no devices found.\n"); + return -ENODEV; + } + return 0; +} + +inline static __u16 radio_power_on(struct radio_device *dev) +{ + register __u16 io=dev->io; + register __u32 ofreq; + __u16 omask, odir; + omask = inw(io + IO_MASK); + odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); + outw(odir & ~STR_WREN, io + IO_DIR); + dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE; + outw(odir, io + IO_DIR); + outw(~(STR_WREN | STR_CLK), io + IO_MASK); + outw(dev->muted ? 0 : STR_WREN, io); + udelay16(); + outw(omask, io + IO_MASK); + ofreq = radio_bits_get(dev); + if((ofreqFREQ2BITS(FREQ_HI))) + ofreq = FREQ2BITS(FREQ_LO); + radio_bits_set(dev, ofreq); + return (ofreq == radio_bits_get(dev)); +} + +inline static __u16 radio_install(struct pci_dev *pcidev) +{ + if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return 0; + + radio_unit.io = pcidev->resource[0].start + GPIO_DATA; + maestro_radio.priv = &radio_unit; + init_MUTEX(&radio_unit.lock); + + if(radio_power_on(&radio_unit)) { + if(video_register_device(&maestro_radio, VFL_TYPE_RADIO)==-1) { + printk("radio-maestro: can't register device!"); + return 0; + } + printk(KERN_INFO "radio-maestro: version " + DRIVER_VERSION + " time " + __TIME__ " " + __DATE__ + "\n"); + printk(KERN_INFO "radio-maestro: radio chip initialized\n"); + return 1; + } else + return 0; +} + diff --git a/drivers/media/video/buz.c b/drivers/media/video/buz.c index ca3cb4f47449..e23346772870 100644 --- a/drivers/media/video/buz.c +++ b/drivers/media/video/buz.c @@ -202,6 +202,7 @@ static int v4l_fbuffer_alloc(struct zoran *zr) mem_map_reserve(virt_to_page(mem + off)); DEBUG(printk(BUZ_INFO ": V4L frame %d mem 0x%x (bus: 0x%x=%d)\n", i, mem, virt_to_bus(mem), virt_to_bus(mem))); } else { + v4l_fbuffer_free(zr); return -ENOBUFS; } } diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index e4b6ed4a099f..da8ba5e77276 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -130,15 +130,15 @@ static const char *invalid_pcb_msg = #define INVALID_PCB_MSG(len) \ printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__) -static const char *search_msg = "%s: Looking for 3c505 adapter at address %#x..."; +static char *search_msg __initdata = "%s: Looking for 3c505 adapter at address %#x..."; -static const char *stilllooking_msg = "still looking..."; +static char *stilllooking_msg __initdata = "still looking..."; -static const char *found_msg = "found.\n"; +static char *found_msg __initdata = "found.\n"; -static const char *notfound_msg = "not found (reason = %d)\n"; +static char *notfound_msg __initdata = "not found (reason = %d)\n"; -static const char *couldnot_msg = "%s: 3c505 not found\n"; +static char *couldnot_msg __initdata = "%s: 3c505 not found\n"; /********************************************************* * @@ -180,7 +180,7 @@ static const int elp_debug = 0; * Last element MUST BE 0! *****************************************************************/ -static const int addr_list[] __initdata = {0x300, 0x280, 0x310, 0}; +static int addr_list[] __initdata = {0x300, 0x280, 0x310, 0}; /* Dma Memory related stuff */ diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 94a268e0170c..d065c32731c2 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -146,7 +146,7 @@ struct mca_adapters_t { char *name; }; -const struct mca_adapters_t mc32_adapters[] = { +static struct mca_adapters_t mc32_adapters[] __initdata = { { 0x0041, "3COM EtherLink MC/32" }, { 0x8EF5, "IBM High Performance Lan Adapter" }, { 0x0000, NULL } diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 5a64da5426af..8fbbbac63379 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -440,7 +440,7 @@ static int __init fddiif_probe(struct net_device *dev) #endif -/* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is tring of 9 zeros. */ +/* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */ #define __PAD6 "\0\0\0\0\0\0\0\0\0" #define __PAD5 __PAD6 "\0" #define __PAD4 __PAD5 "\0" diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index dc8c06255229..b3a67b380378 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -3004,6 +3004,6 @@ static int __init read_eeprom_byte(struct net_device *dev, /* * Local variables: - * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" + * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index df5acebde5c7..713e4e4e65cf 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -242,7 +242,10 @@ static int ipddp_create(struct ipddp_route *new_rt) rt->next = NULL; rt->dev = atrtr_get_dev(&rt->at); if(rt->dev == NULL) + { + kfree(rt); return (-ENETUNREACH); + } test = ipddp_find_route(rt); if(test != NULL) diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 85bea90d7948..1b71c6e13f93 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -76,6 +76,7 @@ static char *version = /* Always include 'config.h' first in case the user wants to turn on or override something. */ +#include #ifdef MODULE #include #include diff --git a/drivers/net/ne2.c b/drivers/net/ne2.c index 5c3892f9f434..b780ee17570f 100644 --- a/drivers/net/ne2.c +++ b/drivers/net/ne2.c @@ -110,16 +110,16 @@ static const char *version = "ne2.c:v0.91 Nov 16 1998 Wim Dumon name[0] == '\0' || dev->name[0] == ' ') { strcpy(dev->name, mask); - if (!netdev_boot_setup_check(dev)) { - if (dev_alloc_name(dev, mask)<0) { - if (new_device) - kfree(dev); - return NULL; - } + if (dev_alloc_name(dev, mask)<0) { + if (new_device) + kfree(dev); + return NULL; } - } else { - netdev_boot_setup_check(dev); } + + netdev_boot_setup_check(dev); /* * Configure via the caller provided setup function then diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c index 207b029f3508..3be9272cb19e 100644 --- a/drivers/net/pppox.c +++ b/drivers/net/pppox.c @@ -17,7 +17,6 @@ * */ -#include #include #include diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index 3a03fa964beb..9185f428f4bc 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1,6 +1,6 @@ /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux. Copyright 1999 Silicon Integrated System Corporation - Revision: 1.07.01 Aug. 08 2000 + Revision: 1.07.04 Sep. 6 2000 Modified from the driver which is originally written by Donald Becker. @@ -18,7 +18,9 @@ preliminary Rev. 1.0 Jan. 18, 1998 http://www.sis.com.tw/support/databook.htm - Rev 1.07.01 Aug. 08 2000 Ollie Lho minor update fro SiS 630E and SiS 630E A1 + Rev 1.07.04 Sep. 6 2000 Lei-Chun Chang added ICS1893 PHY support + Rev 1.07.03 Aug. 24 2000 Lei-Chun Chang (lcchang@sis.com.tw) modified 630E eqaulizer workaroung rule + Rev 1.07.01 Aug. 08 2000 Ollie Lho minor update for SiS 630E and SiS 630E A1 Rev 1.07 Mar. 07 2000 Ollie Lho bug fix in Rx buffer ring Rev 1.06.04 Feb. 11 2000 Jeff Garzik softnet and init for kernel 2.4 Rev 1.06.03 Dec. 23 1999 Ollie Lho Third release @@ -54,7 +56,7 @@ #include "sis900.h" static const char *version = -"sis900.c: v1.07.01 08/08/2000\n"; +"sis900.c: v1.07.04 09/06/2000\n"; static int max_interrupt_work = 20; static int multicast_filter_limit = 128; @@ -88,6 +90,7 @@ MODULE_DEVICE_TABLE (pci, sis900_pci_tbl); static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex); static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex); +static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex); static struct mii_chip_info { const char * name; @@ -99,6 +102,7 @@ static struct mii_chip_info { {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode}, {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9, amd79c901_read_mode}, {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, amd79c901_read_mode}, + {"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf441,ics1893_read_mode}, {0,}, }; @@ -166,6 +170,7 @@ static struct net_device_stats *sis900_get_stats(struct net_device *net_dev); static u16 sis900_compute_hashtable_index(u8 *addr); static void set_rx_mode(struct net_device *net_dev); static void sis900_reset(struct net_device *net_dev); +static void sis630e_set_eq(struct net_device *net_dev); /* walk through every ethernet PCI devices to see if some of them are matched with our card list*/ static int __init sis900_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id) @@ -242,29 +247,6 @@ static int sis630e_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net return 1; } -/* SiS630E A1, The Mac address is hardcoded in the RFCR register so it is actually not necessary to - probe the MAC address */ -static int sis630ea1_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev) -{ - long ioaddr = pci_resource_start(pci_dev, 0); - u32 reg; - int i; - - /* reload MAC address */ - reg = inl(ioaddr + cr); - outl(reg | RELOAD, ioaddr + cr); - - reg = inl(ioaddr + cr); - outl(reg & ~RELOAD, ioaddr + cr); - - for (i = 0; i < 3; i++) { - outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr); - ((u16 *)(net_dev->dev_addr))[i] = inl(ioaddr + rfdr); - } - - return 1; -} - static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, char * card_name) { struct sis900_private *sis_priv; @@ -278,10 +260,10 @@ static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, ch return NULL; pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); - if (revision == SIS630E_REV) + if (revision == SIS630E_REV || revision == SIS630EA1_REV) + ret = sis630e_get_mac_addr(pci_dev, net_dev); + else if (revision == SIS630S_REV) ret = sis630e_get_mac_addr(pci_dev, net_dev); - else if (revision == SIS630EA1_REV) - ret = sis630ea1_get_mac_addr(pci_dev, net_dev); else ret = sis900_get_mac_addr(pci_dev, net_dev); @@ -301,7 +283,7 @@ static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, ch unregister_netdevice(net_dev); return NULL; } - + sis_priv = net_dev->priv; memset(sis_priv, 0, sizeof(struct sis900_private)); @@ -311,7 +293,7 @@ static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, ch net_dev->irq = irq; sis_priv->pci_dev = pci_dev; spin_lock_init(&sis_priv->lock); - + /* probe for mii transciver */ if (sis900_mii_probe(net_dev) == 0) { unregister_netdev(net_dev); @@ -366,7 +348,7 @@ static int __init sis900_mii_probe (struct net_device * net_dev) printk(KERN_INFO "%s: %s transceiver found at address %d.\n", net_dev->name, mii_chip_table[i].name, - phy_addr);; + phy_addr); if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) { mii_phy->chip_info = mii_chip_table+i; mii_phy->phy_addr = phy_addr; @@ -559,12 +541,18 @@ sis900_open(struct net_device *net_dev) { struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; + u8 revision; MOD_INC_USE_COUNT; /* Soft reset the chip. */ sis900_reset(net_dev); + /* Equalizer workaroung Rule */ + pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); + if (revision == SIS630E_REV || revision == SIS630EA1_REV) + sis630e_set_eq(net_dev); + if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) { MOD_DEC_USE_COUNT; return -EAGAIN; @@ -700,6 +688,64 @@ sis900_init_rx_ring(struct net_device *net_dev) printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", net_dev->name, inl(ioaddr + rxdp)); } + +/* 630E equalizer workaroung rule(Cyrus Huang 08/15) + PHY register 14h(Test) + Bit 14: 0 -- Automatically dectect (default) + 1 -- Manually set Equalizer filter + Bit 13: 0 -- (Default) + 1 -- Speed up convergence of equalizer setting + Bit 9 : 0 -- (Default) + 1 -- Disable Baseline Wander + Bit 3~7 -- Equalizer filter setting + + Link ON: Set Bit 9, 13 to 1, Bit 14 to 0 + Then calculate equalizer value + Then set equalizer value, and set Bit 14 to 1, Bit 9 to 0 + Link Off:Set Bit 13 to 1, Bit 14 to 0 + + Calculate Equalizer value: + When Link is ON and Bit 14 is 0, SIS900PHY will auto-dectect proper equalizer value. + When the equalizer is stable, this value is not a fixed value. It will be within + a small range(eg. 7~9). Then we get a minimum and a maximum value(eg. min=7, max=9) + 0 <= max <= 4 --> set equalizer to max + 5 <= max <= 14 --> set equalizer to max+1 or + set equalizer to max+2 if max == min + max >= 15 --> set equalizer to max+5 or + set equalizer to max+6 if max == min +*/ +static void sis630e_set_eq(struct net_device *net_dev) +{ + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + u16 reg14h, eq_value, max_value=0, min_value=0; + int i, maxcount=10; + + if (sis_priv->LinkOn == TRUE) { + reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); + mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (0x2200 | reg14h) & 0xBFFF); + for (i=0; i < maxcount; i++) { + eq_value=(0x00F8 & mdio_read(net_dev, sis_priv->cur_phy, MII_RESV)) >> 3; + max_value=(eq_value > max_value) ? eq_value : max_value; + min_value=(eq_value < min_value) ? eq_value : min_value; + } + if (max_value < 5) + eq_value=max_value; + else if (max_value >= 5 && max_value < 15) + eq_value=(max_value == min_value) ? max_value+2 : max_value+1; + else if (max_value >= 15) + eq_value=(max_value == min_value) ? max_value+6 : max_value+5; + reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); + reg14h=(reg14h & 0xFF07) | ((eq_value << 3) & 0x00F8); + reg14h=(reg14h | 0x6000) & 0xFDFF; + mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, reg14h); + } + else { + reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); + mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF); + } + return; +} + /* on each timer ticks we check two things, Link Status (ON/OFF) and Link Mode (10/100/Full/Half) */ @@ -710,6 +756,7 @@ static void sis900_timer(unsigned long data) struct mii_phy *mii_phy = sis_priv->mii; static int next_tick = 5*HZ; u16 status; + u8 revision; status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); @@ -721,6 +768,12 @@ static void sis900_timer(unsigned long data) /* link stat change from ON to OFF */ next_tick = HZ; sis_priv->LinkOn = FALSE; + + /* Equalizer workaroung Rule */ + pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); + if (revision == SIS630E_REV || revision == SIS630EA1_REV) + sis630e_set_eq(net_dev); + printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); } @@ -736,6 +789,12 @@ static void sis900_timer(unsigned long data) /* link stat change forn OFF to ON, read and report link mode */ sis_priv->LinkOn = TRUE; next_tick = 5*HZ; + + /* Equalizer workaroung Rule */ + pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); + if (revision == SIS630E_REV || revision == SIS630EA1_REV) + sis630e_set_eq(net_dev); + /* change what cur_phy means */ if (mii_phy->phy_addr != sis_priv->cur_phy) { printk(KERN_INFO "%s: Changing transceiver to %s\n", @@ -856,6 +915,37 @@ static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *s printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); } } +/* ICS1893 PHY use Quick Poll Detailed Status Register to get its status */ +static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex) +{ + int i = 0; + u32 status; + + /* MII_QPDSTS is Latched, read twice in succession will reflect the current state */ + for (i = 0; i < 2; i++) + status = mdio_read(net_dev, phy_addr, MII_QPDSTS); + + if (status & MII_STSICS_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + + if (status & MII_STSICS_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSICS_LINKSTS) + printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", + net_dev->name, + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); +} + static void sis900_tx_timeout(struct net_device *net_dev) { struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; diff --git a/drivers/net/sis900.h b/drivers/net/sis900.h index 93609cd0805e..136a51ed8e09 100644 --- a/drivers/net/sis900.h +++ b/drivers/net/sis900.h @@ -165,7 +165,13 @@ enum mii_registers { /* mii registers specific to SiS 900 */ enum sis_mii_registers { MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012, - MII_MASK = 0x0013 + MII_MASK = 0x0013, MII_RESV = 0x0014 +}; + +/* mii registers specific to ICS 1893 */ +enum ics_mii_registers { + MII_EXTCTRL = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012, + MII_EXTCTRL2 = 0x0013 }; /* mii registers specific to AMD 79C901 */ @@ -212,13 +218,19 @@ enum mii_stsout_register_bits { MII_STSOUT_SPD = 0x0080, MII_STSOUT_DPLX = 0x0040 }; +enum mii_stsics_register_bits { + MII_STSICS_SPD = 0x8000, MII_STSICS_DPLX = 0x4000, + MII_STSICS_LINKSTS = 0x0001 +}; + enum mii_stssum_register_bits { MII_STSSUM_LINK = 0x0008, MII_STSSUM_DPLX = 0x0004, MII_STSSUM_AUTO = 0x0002, MII_STSSUM_SPD = 0x0001 }; enum sis630_revision_id { - SIS630E_REV = 0x81, SIS630EA1_REV = 0x83 + SIS630E_REV = 0x81, SIS630EA1_REV = 0x83, + SIS630S_REV = 0x82 }; #define FDX_CAPABLE_DUPLEX_UNKNOWN 0 diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index 0454118a8db9..ef489b1a687f 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -1203,7 +1203,10 @@ static int sdla_xfer(struct net_device *dev, struct sdla_mem *info, int read) return(-ENOMEM); sdla_read(dev, mem.addr, temp, mem.len); if(copy_to_user(mem.data, temp, mem.len)) + { + kfree(temp); return -EFAULT; + } kfree(temp); } else @@ -1212,7 +1215,10 @@ static int sdla_xfer(struct net_device *dev, struct sdla_mem *info, int read) if (!temp) return(-ENOMEM); if(copy_from_user(temp, mem.data, mem.len)) + { + kfree(temp); return -EFAULT; + } sdla_write(dev, mem.addr, temp, mem.len); kfree(temp); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 446d3e239ce3..c0541c115ad5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -693,6 +693,10 @@ static int __init pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int if (!is_cardbus) { unsigned int cmax = pci_do_scan_bus(child); if (cmax > max) max = cmax; + } else { + int i; + for (i = 0; i < 4; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; } } else { /* @@ -718,12 +722,15 @@ static int __init pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int /* Now we can scan all subordinate buses... */ max = pci_do_scan_bus(child); } else { + int i; /* * For CardBus bridges, we leave 4 bus numbers * as cards with a PCI-to-PCI bridge can be * inserted later. */ max += 3; + for (i = 0; i < 4; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; } /* * Set the subordinate bus number to its real value. diff --git a/drivers/s390/Config.in b/drivers/s390/Config.in index 257c65d9a58b..d973c4e1bae4 100644 --- a/drivers/s390/Config.in +++ b/drivers/s390/Config.in @@ -5,16 +5,7 @@ tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP if [ "$CONFIG_NET" = "y" ]; then tristate 'Network block device support' CONFIG_BLK_DEV_NBD fi -bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD -if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then - tristate ' Linear (append) mode' CONFIG_MD_LINEAR - tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED - tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING - tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 -fi -if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then - bool ' Boot support (linear, striped)' CONFIG_MD_BOOT -fi +include drivers/md/Config.in tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD diff --git a/drivers/scsi/ChangeLog.ips b/drivers/scsi/ChangeLog.ips index 52c273673e08..481b4d4e50bf 100644 --- a/drivers/scsi/ChangeLog.ips +++ b/drivers/scsi/ChangeLog.ips @@ -1,7 +1,22 @@ IBM ServeRAID driver Change Log ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 4.20.14 - Update patch files for kernel 2.4.0-test5 + + 4.20.13 - Fix some failure cases / reset code + - Hook into the reboot_notifier to flush the controller + cache + + 4.20.03 - Rename version to coincide with new release schedules + - Performance fixes + - Fix truncation of /proc files with cat + - Merge in changes through kernel 2.4.0test1ac21 + + 4.10.13 - Fix for dynamic unload and proc file system + + 4.10.00 - Add support for ServeRAID 4M/4L + 4.00.06 - Fix timeout with initial FFDC command - + 4.00.05 - Remove wish_block from init routine - Use linux/spinlock.h instead of asm/spinlock.h for kernels 2.3.18 and later diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index a12560ec9a1b..e2a954f77c9c 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -55,9 +55,6 @@ if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5 fi -if [ "$CONFIG_X86" = "y" ]; then - dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI -fi dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI @@ -67,6 +64,9 @@ dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then bool ' Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT fi +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate 'Compaq Fibre Channel 64-bit/66Mhz HBA support' CONFIG_SCSI_CPQFCTS $CONFIG_SCSI +fi dep_tristate 'DMX3191D SCSI support' CONFIG_SCSI_DMX3191D $CONFIG_SCSI $CONFIG_PCI dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI @@ -89,6 +89,16 @@ if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then "Port CONFIG_SCSI_G_NCR5380_PORT \ Memory CONFIG_SCSI_G_NCR5380_MEM" Port fi +if [ "$CONFIG_MCA" = "y" ]; then + dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI + if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then + bool ' Standard SCSI-order' CONFIG_IBMMCA_SCSI_ORDER_STANDARD + bool ' Reset SCSI-devices at boottime' CONFIG_IBMMCA_SCSI_DEV_RESET + fi +fi +if [ "$CONFIG_X86" = "y" ]; then + dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI +fi dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI $CONFIG_PCI dep_tristate 'Initio INI-A100U2W support' CONFIG_SCSI_INIA100 $CONFIG_SCSI $CONFIG_PCI if [ "$CONFIG_PARPORT" != "n" ]; then @@ -100,8 +110,6 @@ if [ "$CONFIG_PARPORT" != "n" ]; then fi fi dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI -dep_tristate 'symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI -dep_tristate 'Simple 53c710 SCSI support (Compaq, NCR machines)' CONFIG_SCSI_SIM710 $CONFIG_SCSI dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync @@ -128,13 +136,6 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then fi fi fi -if [ "$CONFIG_MCA" = "y" ]; then - dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI - if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then - bool ' Standard SCSI-order' CONFIG_IBMMCA_SCSI_ORDER_STANDARD - bool ' Reset SCSI-devices at boottime' CONFIG_IBMMCA_SCSI_DEV_RESET - fi -fi if [ "$CONFIG_MCA" = "y" ]; then dep_tristate 'NCR MCA 53C9x SCSI support' CONFIG_SCSI_MCA_53C9X $CONFIG_SCSI fi @@ -151,6 +152,8 @@ fi if [ "$CONFIG_X86" = "y" ]; then dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI fi +dep_tristate 'Simple 53c710 SCSI support (Compaq, NCR machines)' CONFIG_SCSI_SIM710 $CONFIG_SCSI +dep_tristate 'Symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then dep_tristate 'Tekram DC390(T) and Am53/79C974 SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI if [ "$CONFIG_SCSI_DC390T" != "n" ]; then diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 586da616951a..7d07453dfec4 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -4,6 +4,8 @@ # 30 May 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. # +# 20 Sep 2000, Torben Mathiasen +# Changed link order to reflect new scsi initialization. O_TARGET := scsidrv.o @@ -22,25 +24,14 @@ else endif export-objs := scsi_syms.o -list-multi := scsi_mod.o sr_mod.o initio.o a100u2w.o +list-multi := scsi_mod.o initio.o a100u2w.o CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM -obj-$(CONFIG_SCSI) += scsi_mod.o -obj-$(CONFIG_CHR_DEV_ST) += st.o -obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o -obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o -obj-$(CONFIG_CHR_DEV_SG) += sg.o +obj-$(CONFIG_SCSI) += scsi_mod.o scsi_syms.o -obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o -obj-$(CONFIG_SCSI_PCI2000) += pci2000.o -obj-$(CONFIG_SCSI_PCI2220I) += pci2220i.o -obj-$(CONFIG_SCSI_PSI240I) += psi240i.o -obj-$(CONFIG_MVME16x_SCSI) += mvme16x.o 53c7xx.o -obj-$(CONFIG_BVME6000_SCSI) += bvme6000.o 53c7xx.o -obj-$(CONFIG_SCSI_SIM710) += sim710.o obj-$(CONFIG_A4000T_SCSI) += amiga7xx.o 53c7xx.o obj-$(CONFIG_A4091_SCSI) += amiga7xx.o 53c7xx.o obj-$(CONFIG_BLZ603EPLUS_SCSI) += amiga7xx.o 53c7xx.o @@ -48,8 +39,6 @@ obj-$(CONFIG_WARPENGINE_SCSI) += amiga7xx.o 53c7xx.o obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o -obj-$(CONFIG_SCSI_SGIWD93) += sgiwd93.o wd33c93.o -obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o obj-$(CONFIG_CYBERSTORM_SCSI) += NCR53C9x.o cyberstorm.o obj-$(CONFIG_CYBERSTORMII_SCSI) += NCR53C9x.o cyberstormII.o obj-$(CONFIG_BLZ2060_SCSI) += NCR53C9x.o blz2060.o @@ -58,69 +47,82 @@ obj-$(CONFIG_FASTLANE_SCSI) += NCR53C9x.o fastlane.o obj-$(CONFIG_OKTAGON_SCSI) += NCR53C9x.o oktagon_esp.o oktagon_io.o obj-$(CONFIG_ATARI_SCSI) += atari_scsi.o obj-$(CONFIG_MAC_SCSI) += mac_scsi.o -obj-$(CONFIG_SUN3_SCSI) += sun3_scsi.o obj-$(CONFIG_SCSI_MAC_ESP) += mac_esp.o NCR53C9x.o -obj-$(CONFIG_SCSI_PPA) += ppa.o -obj-$(CONFIG_SCSI_IMM) += imm.o -obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o -obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o -obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o -obj-$(CONFIG_SCSI_ACARD) += atp870u.o -obj-$(CONFIG_SCSI_INITIO) += initio.o -obj-$(CONFIG_SCSI_INIA100) += a100u2w.o -obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o +obj-$(CONFIG_SUN3_SCSI) += sun3_scsi.o +obj-$(CONFIG_MVME16x_SCSI) += mvme16x.o 53c7xx.o +obj-$(CONFIG_BVME6000_SCSI) += bvme6000.o 53c7xx.o +obj-$(CONFIG_SCSI_SIM710) += sim710.o +obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o +obj-$(CONFIG_SCSI_PCI2000) += pci2000.o +obj-$(CONFIG_SCSI_PCI2220I) += pci2220i.o +obj-$(CONFIG_SCSI_PSI240I) += psi240i.o +obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o +obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o +obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o obj-$(CONFIG_SCSI_AHA152X) += aha152x.o obj-$(CONFIG_SCSI_AHA1542) += aha1542.o obj-$(CONFIG_SCSI_AHA1740) += aha1740.o obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx.o obj-$(CONFIG_SCSI_IPS) += ips.o -obj-$(CONFIG_SCSI_DC390T) += tmscsim.o -obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o -obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o -obj-$(CONFIG_SCSI_EATA_DMA) += eata_dma.o -obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o -obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o -obj-$(CONFIG_SCSI_SUNESP) += esp.o -obj-$(CONFIG_SCSI_QLOGICPTI) += qlogicpti.o -obj-$(CONFIG_SCSI_MESH) += mesh.o -obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o -obj-$(CONFIG_SCSI_GDTH) += gdth.o - -obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o - +obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o obj-$(CONFIG_SCSI_IN2000) += in2000.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o -obj-$(CONFIG_SCSI_NCR53C7xx) += 53c7,8xx.o -obj-$(CONFIG_SCSI_NCR53C8XX) += ncr53c8xx.o -obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o +obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o +obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o +obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o +obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o +obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o +obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_SEAGATE) += seagate.o obj-$(CONFIG_SCSI_FD_8xx) += seagate.o -obj-$(CONFIG_SCSI_7000FASST) += wd7000.o -obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o -obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_DTC3280) += dtc.o -obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o -obj-$(CONFIG_SCSI_PLUTO) += pluto.o -obj-$(CONFIG_SCSI_FCAL) += fcal.o +obj-$(CONFIG_SCSI_NCR53C7xx) += 53c7,8xx.o +obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o +obj-$(CONFIG_SCSI_NCR53C8XX) += ncr53c8xx.o +obj-$(CONFIG_SCSI_EATA_DMA) += eata_dma.o +obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o +obj-$(CONFIG_SCSI_7000FASST) += wd7000.o +obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o +obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o obj-$(CONFIG_SCSI_EATA) += eata.o -obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o +obj-$(CONFIG_SCSI_DC390T) += tmscsim.o +obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o -obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o +obj-$(CONFIG_SCSI_ACARD) += atp870u.o +obj-$(CONFIG_SCSI_SUNESP) += esp.o +obj-$(CONFIG_SCSI_GDTH) += gdth.o +obj-$(CONFIG_SCSI_INITIO) += initio.o +obj-$(CONFIG_SCSI_INIA100) += a100u2w.o +obj-$(CONFIG_SCSI_QLOGICPTI) += qlogicpti.o obj-$(CONFIG_BLK_DEV_IDESCSI) += ide-scsi.o -obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o +obj-$(CONFIG_SCSI_MESH) += mesh.o +obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o +obj-$(CONFIG_SCSI_PLUTO) += pluto.o obj-$(CONFIG_SCSI_DECNCR) += NCR53C9x.o dec_esp.o -obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o +obj-$(CONFIG_SCSI_PPA) += ppa.o +obj-$(CONFIG_SCSI_IMM) += imm.o +obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o +obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o +obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o +obj-$(CONFIG_SCSI_FCAL) += fcal.o + +obj-$(CONFIG_CHR_DEV_ST) += st.o +obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o +obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o +obj-$(CONFIG_CHR_DEV_SG) += sg.o + + scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o \ scsicam.o scsi_proc.o scsi_error.o \ scsi_obsolete.o scsi_queue.o scsi_lib.o \ scsi_merge.o scsi_dma.o scsi_scan.o \ - scsi_syms.o + sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o initio-objs := ini9100u.o i91uscsi.o a100u2w-objs := inia100.o i60uscsi.o @@ -136,9 +138,6 @@ int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) obj-m := $(filter-out $(obj-y), $(obj-m)) int-m := $(filter-out $(int-y), $(int-m)) -# Take multi-part drivers out of obj-y and put components in. -obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) - O_OBJS := $(filter-out $(export-objs), $(obj-y)) OX_OBJS := $(filter $(export-objs), $(obj-y)) M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) diff --git a/drivers/scsi/cpqfc.Readme b/drivers/scsi/cpqfc.Readme new file mode 100644 index 000000000000..1d795a447a4d --- /dev/null +++ b/drivers/scsi/cpqfc.Readme @@ -0,0 +1,216 @@ +Notes for CPQFCTS driver for Compaq Tachyon TS +Fibre Channel Host Bus Adapter, PCI 64-bit, 66MHz +for Linux (RH 6.1, 6.2 kernel 2.2.12-32, 2.2.14-5) +SMP tested +Tested in single and dual HBA configuration, 32 and 64bit busses, +33 and 66MHz. Only supports FC-AL. +SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc() + max of 128k bytes contiguous. +Ver 1.3.4 Sep 7, 2000 + Added Modinfo information + Fixed problem with statically linking the driver + +Ver 1.3.3, Aug 23, 2000 + Fixed device/function number in ioctl + +Ver 1.3.2, July 27, 2000 + Add include for Alpha compile on 2.2.14 kernel (cpq*i2c.c) + Change logic for different FCP-RSP sense_buffer location for HSG80 target + And search for Agilent Tachyon XL2 HBAs (not finished! - in test) + +Tested with +(storage): + Compaq RA-4x000, RAID firmware ver 2.40 - 2.54 + Seagate FC drives model ST39102FC, rev 0006 + Hitachi DK31CJ-72FC rev J8A8 + IBM DDYF-T18350R rev F60K + Compaq FC-SCSI bridge w/ DLT 35/70 Gb DLT (tape) +(servers): + Compaq PL-1850R + Compaq PL-6500 Xeon (400MHz) + Compaq PL-8500 (500MHz, 66MHz, 64bit PCI) + Compaq Alpha DS20 (RH 6.1) +(hubs): + Vixel Rapport 1000 (7-port "dumb") + Gadzoox Gibralter (12-port "dumb") + Gadzoox Capellix 2000, 3000 +(switches): + Brocade 2010, 2400, 2800, rev 2.0.3a (& later) + Gadzoox 3210 (Fabric blade beta) + Vixel 7100 (Fabric beta firmare - known hot plug issues) +using "qa_test" (esp. io_test script) suite modified from Unix tests. + +Installation: +copy file cpqfcTS.patch to /usr/src/linux +patch -p1 < cpqfcTS.patch +make menuconfig + (select SCSI low-level, Compaq FC HBA) +make dep +make modules +make modules_install + +e.g. insmod -f cpqfc + +Due to Fabric/switch delays, driver requires 4 seconds +to initialize. If adapters are found, there will be a entries at +/proc/scsi/cpqfcTS/* + +sample contents of startup messages + +************************* + scsi_register allocating 3596 bytes for CPQFCHBA + ioremap'd Membase: c887e600 + HBA Tachyon RevId 1.2 +Allocating 119808 for 576 Exchanges @ c0dc0000 +Allocating 112904 for LinkQ @ c0c20000 (576 elements) +Allocating 110600 for TachSEST for 512 Exchanges + cpqfcTS: writing IMQ BASE 7C0000h PI 7C4000h + cpqfcTS: SEST c0e40000(virt): Wrote base E40000h @ c887e740 +cpqfcTS: New FC port 0000E8h WWN: 500507650642499D SCSI Chan/Trgt 0/0 +cpqfcTS: New FC port 0000EFh WWN: 50000E100000D5A6 SCSI Chan/Trgt 0/1 +cpqfcTS: New FC port 0000E4h WWN: 21000020370097BB SCSI Chan/Trgt 0/2 +cpqfcTS: New FC port 0000E2h WWN: 2100002037009946 SCSI Chan/Trgt 0/3 +cpqfcTS: New FC port 0000E1h WWN: 21000020370098FE SCSI Chan/Trgt 0/4 +cpqfcTS: New FC port 0000E0h WWN: 21000020370097B2 SCSI Chan/Trgt 0/5 +cpqfcTS: New FC port 0000DCh WWN: 2100002037006CC1 SCSI Chan/Trgt 0/6 +cpqfcTS: New FC port 0000DAh WWN: 21000020370059F6 SCSI Chan/Trgt 0/7 +cpqfcTS: New FC port 00000Fh WWN: 500805F1FADB0E20 SCSI Chan/Trgt 0/8 +cpqfcTS: New FC port 000008h WWN: 500805F1FADB0EBA SCSI Chan/Trgt 0/9 +cpqfcTS: New FC port 000004h WWN: 500805F1FADB1EB9 SCSI Chan/Trgt 0/10 +cpqfcTS: New FC port 000002h WWN: 500805F1FADB1ADE SCSI Chan/Trgt 0/11 +cpqfcTS: New FC port 000001h WWN: 500805F1FADBA2CA SCSI Chan/Trgt 0/12 +scsi4 : Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2: WWN 500508B200193F50 + on PCI bus 0 device 0xa0fc irq 5 IObaseL 0x3400, MEMBASE 0xc6ef8600 +PCI bus width 32 bits, bus speed 33 MHz +FCP-SCSI Driver v1.3.0 +GBIC detected: Short-wave. LPSM 0h Monitor +scsi : 5 hosts. + Vendor: IBM Model: DDYF-T18350R Rev: F60K + Type: Direct-Access ANSI SCSI revision: 03 +Detected scsi disk sdb at scsi4, channel 0, id 0, lun 0 + Vendor: HITACHI Model: DK31CJ-72FC Rev: J8A8 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdc at scsi4, channel 0, id 1, lun 0 + Vendor: SEAGATE Model: ST39102FC Rev: 0006 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdd at scsi4, channel 0, id 2, lun 0 + Vendor: SEAGATE Model: ST39102FC Rev: 0006 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sde at scsi4, channel 0, id 3, lun 0 + Vendor: SEAGATE Model: ST39102FC Rev: 0006 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdf at scsi4, channel 0, id 4, lun 0 + Vendor: SEAGATE Model: ST39102FC Rev: 0006 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdg at scsi4, channel 0, id 5, lun 0 + Vendor: SEAGATE Model: ST39102FC Rev: 0006 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdh at scsi4, channel 0, id 6, lun 0 + Vendor: SEAGATE Model: ST39102FC Rev: 0006 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdi at scsi4, channel 0, id 7, lun 0 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.48 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdj at scsi4, channel 0, id 8, lun 0 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.48 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdk at scsi4, channel 0, id 8, lun 1 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.40 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdl at scsi4, channel 0, id 9, lun 0 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.40 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdm at scsi4, channel 0, id 9, lun 1 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdn at scsi4, channel 0, id 10, lun 0 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdo at scsi4, channel 0, id 11, lun 0 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdp at scsi4, channel 0, id 11, lun 1 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdq at scsi4, channel 0, id 12, lun 0 + Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54 + Type: Direct-Access ANSI SCSI revision: 02 +Detected scsi disk sdr at scsi4, channel 0, id 12, lun 1 +resize_dma_pool: unknown device type 12 +resize_dma_pool: unknown device type 12 +SCSI device sdb: hdwr sector= 512 bytes. Sectors= 35843670 [17501 MB] [17.5 GB] + sdb: sdb1 +SCSI device sdc: hdwr sector= 512 bytes. Sectors= 144410880 [70513 MB] [70.5 GB] + sdc: sdc1 +SCSI device sdd: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB] + sdd: sdd1 +SCSI device sde: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB] + sde: sde1 +SCSI device sdf: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB] + sdf: sdf1 +SCSI device sdg: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB] + sdg: sdg1 +SCSI device sdh: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB] + sdh: sdh1 +SCSI device sdi: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB] + sdi: sdi1 +SCSI device sdj: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB] + sdj: sdj1 +SCSI device sdk: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB] + sdk: sdk1 +SCSI device sdl: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB] + sdl: sdl1 +SCSI device sdm: hdwr sector= 512 bytes. Sectors= 8380320 [4091 MB] [4.1 GB] + sdm: sdm1 +SCSI device sdn: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB] + sdn: sdn1 +SCSI device sdo: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB] + sdo: sdo1 +SCSI device sdp: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB] + sdp: sdp1 +SCSI device sdq: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB] + sdq: sdq1 +SCSI device sdr: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB] + sdr: sdr1 + +************************* + +If a GBIC of type Short-wave, Long-wave, or Copper is detected, it will +print out; otherwise, "none" is displayed. If the cabling is correct +and a loop circuit is completed, you should see "Monitor"; otherwise, +"LoopFail" (on open circuit) or some LPSM number/state with bit 3 set. + + +ERRATA: +1. Normally, Linux Scsi queries FC devices with INQUIRY strings. All LUNs +found according to INQUIRY should get READ commands at sector 0 to find +partition table, etc. Older kernels only query the first 4 devices. Some +Linux kernels only look for one LUN per target (i.e. FC device). + +2. Physically removing a device, or a malfunctioning system which hides a +device, leads to a 30-second timeout and subsequent _abort call. +In some process contexts, this will hang the kernel (crashing the system). +Single bit errors in frames and virtually all hot plugging events are +gracefully handled with internal driver timer and Abort processing. + +3. Some SCSI drives with error conditions will not handle the 7 second timeout +in this software driver, leading to infinite retries on timed out SCSI commands. +The 7 secs balances the need to quickly recover from lost frames (esp. on sequence +initiatives) and time needed by older/slower/error-state drives in responding. +This can be easily changed in "Exchanges[].timeOut". + +4. Due to the nature of FC soft addressing, there is no assurance that the +same LUNs (drives) will have the same path (e.g. /dev/sdb1) from one boot to +next. Dynamic soft address changes (i.e. 24-bit FC port_id) are +supported during run time (e.g. due to hot plug event) by the use of WWN to +SCSI Nexus (channel/target/LUN) mapping. + +5. Compaq RA4x00 firmware version 2.54 and later supports SSP (Selective +Storage Presentation), which maps LUNs to a WWN. If RA4x00 firmware prior +2.54 (e.g. older controller) is used, or the FC HBA is replaced (another WWN +is used), logical volumes on the RA4x00 will no longer be visible. + + +Send questions/comments to: +donald.zimmerman@compaq.com +dszimmerman@yahoo.com diff --git a/drivers/scsi/cpqfcTS.h b/drivers/scsi/cpqfcTS.h new file mode 100644 index 000000000000..966c63b8c3d5 --- /dev/null +++ b/drivers/scsi/cpqfcTS.h @@ -0,0 +1,39 @@ +#ifndef CPQFCTS_H +#define CPQFCTS_H +#include "cpqfcTSstructs.h" + +// These functions are required by the Linux SCSI layers +extern int cpqfcTS_detect(Scsi_Host_Template *); +extern int cpqfcTS_release(struct Scsi_Host *); +const char * cpqfcTS_info(struct Scsi_Host *); +extern int cpqfcTS_proc_info(char *, char **, off_t, int, int, int); +extern int cpqfcTS_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); +extern int cpqfcTS_abort(Scsi_Cmnd *); +extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int); +extern int cpqfcTS_biosparam(Disk *, kdev_t, int[]); +extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg); + +// note: since Tachyon TS supports an extended scatter/gather +// linked list of infinite length (with linked Ext S/G pages, +// limited only by available physical memory) we use SG_ALL. + +#define CPQFCTS { \ + detect: cpqfcTS_detect, \ + release: cpqfcTS_release, \ + info: cpqfcTS_info, \ + proc_info: cpqfcTS_proc_info, \ + ioctl: cpqfcTS_ioctl, \ + queuecommand: cpqfcTS_queuecommand, \ + eh_abort_handler: cpqfcTS_abort, \ + reset: cpqfcTS_reset, \ + bios_param: cpqfcTS_biosparam, \ + can_queue: CPQFCTS_REQ_QUEUE_LEN, \ + this_id: -1, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CPQFCTS_CMD_PER_LUN, \ + present: 0, \ + unchecked_isa_dma: 0, \ + use_clustering: ENABLE_CLUSTERING \ +} + +#endif /* CPQFCTS_H */ diff --git a/drivers/scsi/cpqfcTSchip.h b/drivers/scsi/cpqfcTSchip.h new file mode 100644 index 000000000000..14b83373861f --- /dev/null +++ b/drivers/scsi/cpqfcTSchip.h @@ -0,0 +1,238 @@ +/* Copyright(c) 2000, Compaq Computer Corporation + * Fibre Channel Host Bus Adapter + * 64-bit, 66MHz PCI + * Originally developed and tested on: + * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... + * SP# P225CXCBFIEL6T, Rev XC + * SP# 161290-001, Rev XD + * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 + * + * 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, 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. + * Written by Don Zimmerman +*/ +#ifndef CPQFCTSCHIP_H +#define CPQFCTSCHIP_H +#ifndef TACHYON_CHIP_INC + +// FC-PH (Physical) specification levels for Login payloads +// NOTE: These are NOT strictly complied with by any FC vendors + +#define FC_PH42 0x08 +#define FC_PH43 0x09 +#define FC_PH3 0x20 + +#define TACHLITE_TS_RX_SIZE 1024 // max inbound frame size +// "I" prefix is for Include + +#define IVENDID 0x00 // word +#define IDEVID 0x02 +#define ITLCFGCMD 0x04 +#define IMEMBASE 0x18 // Tachyon +#define ITLMEMBASE 0x1C // Tachlite +#define IIOBASEL 0x10 // Tachyon I/O base address, lower 256 bytes +#define IIOBASEU 0x14 // Tachyon I/O base address, upper 256 bytes +#define ITLIOBASEL 0x14 // TachLite I/O base address, lower 256 bytes +#define ITLIOBASEU 0x18 // TachLite I/O base address, upper 256 bytes +#define ITLRAMBASE 0x20 // TL on-board RAM start +#define ISROMBASE 0x24 +#define IROMBASE 0x30 + +#define ICFGCMD 0x04 // PCI config - PCI config access (word) +#define ICFGSTAT 0x06 // PCI status (R - word) +#define IRCTR_WCTR 0x1F2 // ROM control / pre-fetch wait counter +#define IPCIMCTR 0x1F3 // PCI master control register +#define IINTPEND 0x1FD // Interrupt pending (I/O Upper - Tachyon & TL) +#define IINTEN 0x1FE // Interrupt enable (I/O Upper - Tachyon & TL) +#define IINTSTAT 0x1FF // Interrupt status (I/O Upper - Tachyon & TL) + +#define IMQ_BASE 0x80 +#define IMQ_LENGTH 0x84 +#define IMQ_CONSUMER_INDEX 0x88 +#define IMQ_PRODUCER_INDEX 0x8C // Tach copies its INDX to bits 0-7 of value + +/* +// IOBASE UPPER +#define SFSBQ_BASE 0x00 // single-frame sequences +#define SFSBQ_LENGTH 0x04 +#define SFSBQ_PRODUCER_INDEX 0x08 +#define SFSBQ_CONSUMER_INDEX 0x0C // (R) +#define SFS_BUFFER_LENGTH 0X10 + // SCSI-FCP hardware assists +#define SEST_BASE 0x40 // SSCI Exchange State Table +#define SEST_LENGTH 0x44 +#define SCSI_BUFFER_LENGTH 0x48 +#define SEST_LINKED_LIST 0x4C + +#define TACHYON_My_ID 0x6C +#define TACHYON_CONFIGURATION 0x84 // (R/W) reset val 2 +#define TACHYON_CONTROL 0x88 +#define TACHYON_STATUS 0x8C // (R) +#define TACHYON_FLUSH_SEST 0x90 // (R/W) +#define TACHYON_EE_CREDIT_TMR 0x94 // (R) +#define TACHYON_BB_CREDIT_TMR 0x98 // (R) +#define TACHYON_RCV_FRAME_ERR 0x9C // (R) +#define FRAME_MANAGER_CONFIG 0xC0 // (R/W) +#define FRAME_MANAGER_CONTROL 0xC4 +#define FRAME_MANAGER_STATUS 0xC8 // (R) +#define FRAME_MANAGER_ED_TOV 0xCC +#define FRAME_MANAGER_LINK_ERR1 0xD0 // (R) +#define FRAME_MANAGER_LINK_ERR2 0xD4 // (R) +#define FRAME_MANAGER_TIMEOUT2 0xD8 // (W) +#define FRAME_MANAGER_BB_CREDIT 0xDC // (R) +#define FRAME_MANAGER_WWN_HI 0xE0 // (R/W) +#define FRAME_MANAGER_WWN_LO 0xE4 // (R/W) +#define FRAME_MANAGER_RCV_AL_PA 0xE8 // (R) +#define FRAME_MANAGER_PRIMITIVE 0xEC // {K28.5} byte1 byte2 byte3 +*/ + +#define TL_MEM_ERQ_BASE 0x0 //ERQ Base +#define TL_IO_ERQ_BASE 0x0 //ERQ base + +#define TL_MEM_ERQ_LENGTH 0x4 //ERQ Length +#define TL_IO_ERQ_LENGTH 0x4 //ERQ Length + +#define TL_MEM_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register +#define TL_IO_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register + +#define TL_MEM_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register +#define TL_IO_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register + +#define TL_MEM_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index +#define TL_IO_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index + +#define TL_MEM_SFQ_BASE 0x50 //SFQ Base +#define TL_IO_SFQ_BASE 0x50 //SFQ base + +#define TL_MEM_SFQ_LENGTH 0x54 //SFQ Length +#define TL_IO_SFQ_LENGTH 0x54 //SFQ Length + +#define TL_MEM_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index +#define TL_IO_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index + +#define TL_MEM_IMQ_BASE 0x80 //IMQ Base +#define TL_IO_IMQ_BASE 0x80 //IMQ base + +#define TL_MEM_IMQ_LENGTH 0x84 //IMQ Length +#define TL_IO_IMQ_LENGTH 0x84 //IMQ Length + +#define TL_MEM_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index +#define TL_IO_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index + +#define TL_MEM_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register +#define TL_IO_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register + +#define TL_MEM_SEST_BASE 0x140 //SFQ Base +#define TL_IO_SEST_BASE 0x40 //SFQ base + +#define TL_MEM_SEST_LENGTH 0x144 //SFQ Length +#define TL_IO_SEST_LENGTH 0x44 //SFQ Length + +#define TL_MEM_SEST_LINKED_LIST 0x14C + +#define TL_MEM_SEST_SG_PAGE 0x168 // Extended Scatter/Gather page size + +#define TL_MEM_TACH_My_ID 0x16C +#define TL_IO_TACH_My_ID 0x6C //My AL_PA ID + +#define TL_MEM_TACH_CONFIG 0x184 //Tachlite Configuration register +#define TL_IO_CONFIG 0x84 //Tachlite Configuration register + +#define TL_MEM_TACH_CONTROL 0x188 //Tachlite Control register +#define TL_IO_CTR 0x88 //Tachlite Control register + +#define TL_MEM_TACH_STATUS 0x18C //Tachlite Status register +#define TL_IO_STAT 0x8C //Tachlite Status register + +#define TL_MEM_FM_CONFIG 0x1C0 //Frame Manager Configuration register +#define TL_IO_FM_CONFIG 0xC0 //Frame Manager Configuration register + +#define TL_MEM_FM_CONTROL 0x1C4 //Frame Manager Control +#define TL_IO_FM_CTL 0xC4 //Frame Manager Control + +#define TL_MEM_FM_STATUS 0x1C8 //Frame Manager Status +#define TL_IO_FM_STAT 0xC8 //Frame Manager Status + +#define TL_MEM_FM_LINK_STAT1 0x1D0 //Frame Manager Link Status 1 +#define TL_IO_FM_LINK_STAT1 0xD0 //Frame Manager Link Status 1 + +#define TL_MEM_FM_LINK_STAT2 0x1D4 //Frame Manager Link Status 2 +#define TL_IO_FM_LINK_STAT2 0xD4 //Frame Manager Link Status 2 + +#define TL_MEM_FM_TIMEOUT2 0x1D8 // (W) + +#define TL_MEM_FM_BB_CREDIT0 0x1DC + +#define TL_MEM_FM_WWN_HI 0x1E0 //Frame Manager World Wide Name High +#define TL_IO_FM_WWN_HI 0xE0 //Frame Manager World Wide Name High + +#define TL_MEM_FM_WWN_LO 0x1E4 //Frame Manager World Wide Name LOW +#define TL_IO_FM_WWN_LO 0xE4 //Frame Manager World Wide Name Low + +#define TL_MEM_FM_RCV_AL_PA 0x1E8 //Frame Manager AL_PA Received register +#define TL_IO_FM_ALPA 0xE8 //Frame Manager AL_PA Received register + +#define TL_MEM_FM_ED_TOV 0x1CC + +#define TL_IO_ROMCTR 0xFA //TL PCI ROM Control Register +#define TL_IO_PCIMCTR 0xFB //TL PCI Master Control Register +#define TL_IO_SOFTRST 0xFC //Tachlite Configuration register +#define TL_MEM_SOFTRST 0x1FC //Tachlite Configuration register + +// completion message types (bit 8 set means Interrupt generated) +// CM_Type +#define OUTBOUND_COMPLETION 0 +#define ERROR_IDLE_COMPLETION 0x01 +#define OUT_HI_PRI_COMPLETION 0x01 +#define INBOUND_MFS_COMPLETION 0x02 +#define INBOUND_000_COMPLETION 0x03 +#define INBOUND_SFS_COMPLETION 0x04 // Tachyon & TachLite +#define ERQ_FROZEN_COMPLETION 0x06 // TachLite +#define INBOUND_C1_TIMEOUT 0x05 +#define INBOUND_BUSIED_FRAME 0x06 +#define SFS_BUF_WARN 0x07 +#define FCP_FROZEN_COMPLETION 0x07 // TachLite +#define MFS_BUF_WARN 0x08 +#define IMQ_BUF_WARN 0x09 +#define FRAME_MGR_INTERRUPT 0x0A +#define READ_STATUS 0x0B +#define INBOUND_SCSI_DATA_COMPLETION 0x0C +#define INBOUND_FCP_XCHG_COMPLETION 0x0C // TachLite +#define INBOUND_SCSI_DATA_COMMAND 0x0D +#define BAD_SCSI_FRAME 0x0E +#define INB_SCSI_STATUS_COMPLETION 0x0F +#define BUFFER_PROCESSED_COMPLETION 0x11 + +// FC-AL (Tachyon) Loop Port State Machine defs +// (loop "Up" states) +#define MONITORING 0x0 +#define ARBITRATING 0x1 +#define ARBITRAT_WON 0x2 +#define OPEN 0x3 +#define OPENED 0x4 +#define XMITTD_CLOSE 0x5 +#define RCVD_CLOSE 0x6 +#define TRANSFER 0x7 + +// (loop "Down" states) +#define INITIALIZING 0x8 +#define O_I_INIT 0x9 +#define O_I_PROTOCOL 0xa +#define O_I_LIP_RCVD 0xb +#define HOST_CONTROL 0xc +#define LOOP_FAIL 0xd +// (no 0xe) +#define OLD_PORT 0xf + + + +#define TACHYON_CHIP_INC +#endif +#endif /* CPQFCTSCHIP_H */ diff --git a/drivers/scsi/cpqfcTScontrol.c b/drivers/scsi/cpqfcTScontrol.c new file mode 100644 index 000000000000..bc90b51c3802 --- /dev/null +++ b/drivers/scsi/cpqfcTScontrol.c @@ -0,0 +1,2200 @@ +/* Copyright 2000, Compaq Computer Corporation + * Fibre Channel Host Bus Adapter + * 64-bit, 66MHz PCI + * Originally developed and tested on: + * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... + * SP# P225CXCBFIEL6T, Rev XC + * SP# 161290-001, Rev XD + * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 + * + * 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, 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. + * Written by Don Zimmerman +*/ +/* These functions control the host bus adapter (HBA) hardware. The main chip + control takes place in the interrupt handler where we process the IMQ + (Inbound Message Queue). The IMQ is Tachyon's way of communicating FC link + events and state information to the driver. The Single Frame Queue (SFQ) + buffers incoming FC frames for processing by the driver. References to + "TL/TS UG" are for: + "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed. + Hewlitt Packard Manual Part Number 5968-1083E. +*/ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#include +#include +#include +#include // request_region() prototype +#include +#include // need "kfree" for ext. S/G pages +#include +#include +#include +#include +#include // struct pt_regs for IRQ handler & Port I/O +#include +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +#include +#else +#include +#endif + +#include "sd.h" +#include "hosts.h" // Scsi_Host definition for INT handler +#include "cpqfcTSchip.h" +#include "cpqfcTSstructs.h" + +//#define IMQ_DEBUG 1 + +static void fcParseLinkStatusCounters(TACHYON * fcChip); +static void CpqTsGetSFQEntry(TACHYON * fcChip, + USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); + + +// Note special requirements for Q alignment! (TL/TS UG pg. 190) +// We place critical index pointers at end of QUE elements to assist +// in non-symbolic (i.e. memory dump) debugging +// opcode defines placement of Queues (e.g. local/external RAM) + +int CpqTsCreateTachLiteQues( void* pHBA, int opcode) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + + int iStatus=0; + unsigned long ulAddr; + + + // NOTE! fcMemManager() will return system virtual addresses. + // System (kernel) virtual addresses, though non-paged, still + // aren't physical addresses. Convert to PHYSICAL_ADDRESS for Tachyon's + // DMA use. + ENTER("CreateTachLiteQues"); + + + // Allocate primary EXCHANGES array... + + printk("Allocating %u for %u Exchanges ", + (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID); + fcChip->Exchanges = kmalloc( sizeof( FC_EXCHANGES), GFP_KERNEL ); + printk("@ %p\n", fcChip->Exchanges); + + if( fcChip->Exchanges == NULL ) // fatal error!! + { + printk("kmalloc failure on Exchanges: fatal error\n"); + return -1; + } + // zero out the entire EXCHANGE space + memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES)); + + + printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE)); + cpqfcHBAdata->fcLQ = kmalloc( sizeof( FC_LINK_QUE), GFP_KERNEL ); + printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH); + memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); + + if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!! + { + printk("kmalloc failure on fc Link Que: fatal error\n"); + return -1; + } + // zero out the entire EXCHANGE space + memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); + + + + + // Verify that basic Tach I/O registers are not NULL + + if( !fcChip->Registers.ReMapMemBase ) + { + printk("HBA base address NULL: fatal error\n"); + return -1; + } + + + // Initialize the fcMemManager memory pairs (stores allocated/aligned + // pairs for future freeing) + memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem)); + + + // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes) + + fcChip->ERQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L ); + if( !fcChip->ERQ ) + { + printk("kmalloc/alignment failure on ERQ: fatal error\n"); + return -1; + } + fcChip->ERQ->length = ERQ_LEN-1; + ulAddr = virt_to_bus( fcChip->ERQ); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->ERQ->base = (ULONG)ulAddr; // copy for quick reference + + + // Allocate Tach's Inbound Message Queue (32 bytes per entry) + + fcChip->IMQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L ); + if( !fcChip->IMQ ) + { + printk("kmalloc/alignment failure on IMQ: fatal error\n"); + return -1; + } + fcChip->IMQ->length = IMQ_LEN-1; + + ulAddr = virt_to_bus( fcChip->IMQ); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->IMQ->base = (ULONG)ulAddr; // copy for quick reference + + + // Allocate Tach's Single Frame Queue (64 bytes per entry) + fcChip->SFQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L ); + if( !fcChip->SFQ ) + { + printk("kmalloc/alignment failure on SFQ: fatal error\n"); + return -1; + } + fcChip->SFQ->length = SFQ_LEN-1; // i.e. Que length [# entries - + // min. 32; max. 4096 (0xffff)] + + ulAddr = virt_to_bus( fcChip->SFQ); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->SFQ->base = (ULONG)ulAddr; // copy for quick reference + + + // Allocate SCSI Exchange State Table; aligned nearest @sizeof + // power-of-2 boundary + // LIVE DANGEROUSLY! Assume the boundary for SEST mem will + // be on physical page (e.g. 4k) boundary. + printk("Allocating %u for TachSEST for %u Exchanges\n", + (ULONG)sizeof(TachSEST), TACH_SEST_LEN); + fcChip->SEST = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof(TachSEST), 4, 0L ); +// sizeof(TachSEST), 64*TACH_SEST_LEN, 0L ); + if( !fcChip->SEST ) + { + printk("kmalloc/alignment failure on SEST: fatal error\n"); + return -1; + } + + fcChip->SEST->length = TACH_SEST_LEN; // e.g. DON'T subtract one + // (TL/TS UG, pg 153) + + ulAddr = virt_to_bus( fcChip->SEST); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->SEST->base = (ULONG)ulAddr; // copy for quick reference + + + // Now that structures are defined, + // fill in Tachyon chip registers... + + // EEEEEEEE EXCHANGE REQUEST QUEUE + + writel( fcChip->ERQ->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); + + writel( fcChip->ERQ->length, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH)); + + + fcChip->ERQ->producerIndex = 0L; + writel( fcChip->ERQ->producerIndex, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX)); + + + // NOTE! write consumer index last, since the write + // causes Tachyon to process the other registers + + ulAddr = virt_to_bus( &fcChip->ERQ->consumerIndex); + + // NOTE! Tachyon DMAs to the ERQ consumer Index host + // address; must be correctly aligned + writel( (ULONG)ulAddr, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR)); + + + + // IIIIIIIIIIIII INBOUND MESSAGE QUEUE + // Tell Tachyon where the Que starts + + // set the Host's pointer for Tachyon to access + + printk(" cpqfcTS: writing IMQ BASE %Xh ", fcChip->IMQ->base ); + writel( fcChip->IMQ->base, + (fcChip->Registers.ReMapMemBase + IMQ_BASE)); + + writel( fcChip->IMQ->length, + (fcChip->Registers.ReMapMemBase + IMQ_LENGTH)); + + writel( fcChip->IMQ->consumerIndex, + (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); + + + // NOTE: TachLite DMAs to the producerIndex host address + // must be correctly aligned with address bits 1-0 cleared + // Writing the BASE register clears the PI register, so write it last + ulAddr = virt_to_bus( &fcChip->IMQ->producerIndex); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif +//#if DBG + printk(" PI %Xh\n", (ULONG)ulAddr ); +//#endif + writel( (ULONG)ulAddr, + (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX)); + + + + // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE + // Tell TachLite where the Que starts + + writel( fcChip->SFQ->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE)); + + writel( fcChip->SFQ->length, + (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH)); + + + // tell TachLite where SEST table is & how long + writel( fcChip->SEST->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE)); + + printk(" cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n", + fcChip->SEST, fcChip->SEST->base, + fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE); + + writel( fcChip->SEST->length, + (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH)); + + writel( (TL_EXT_SG_PAGE_COUNT-1), + (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE)); + + + LEAVE("CreateTachLiteQues"); + + return iStatus; +} + + + +// function to return TachLite to Power On state +// 1st - reset tachyon ('SOFT' reset) +// others - future + +int CpqTsResetTachLite(void *pHBA, int type) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + ULONG ulBuff, i; + int ret_status=0; // def. success + + ENTER("ResetTach"); + + switch(type) + { + + case CLEAR_FCPORTS: + + // in case he was running previously, mask Tach's interrupt + writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); + + // de-allocate mem for any Logged in ports + // (e.g., our module is unloading) + // search the forward linked list, de-allocating + // the memory we allocated when the port was initially logged in + { + PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort; + PFC_LOGGEDIN_PORT ptr; +// printk("checking for allocated LoggedInPorts...\n"); + + while( pLoggedInPort ) + { + ptr = pLoggedInPort; + pLoggedInPort = ptr->pNextPort; +// printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n", +// ptr, ptr->port_id); + kfree( ptr ); + } + } + // (continue resetting hardware...) + + case 1: // RESTART Tachyon (power-up state) + + // in case he was running previously, mask Tach's interrupt + writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); + // turn OFF laser (NOTE: laser is turned + // off during reset, because GPIO4 is cleared + // to 0 by reset action - see TLUM, sec 7.22) + // However, CPQ 64-bit HBAs have a "health + // circuit" which keeps laser ON for a brief + // period after it is turned off ( < 1s) + + fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0); + + + + // soft reset timing constraints require: + // 1. set RST to 1 + // 2. read SOFTRST register + // (128 times per R. Callison code) + // 3. clear PCI ints + // 4. clear RST to 0 + writel( 0xff000001L, + (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); + + for( i=0; i<128; i++) + ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST); + + // clear the soft reset + for( i=0; i<8; i++) + writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); + + + + // clear out our copy of Tach regs, + // because they must be invalid now, + // since TachLite reset all his regs. + CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs + cpqfcTSClearLinkStatusCounters(fcChip); // clear our s/w accumulators + // lower bits give GBIC info + fcChip->Registers.TYstatus.value = + readl( fcChip->Registers.TYstatus.address ); + break; + +/* + case 2: // freeze SCSI + case 3: // reset Outbound command que (ERQ) + case 4: // unfreeze OSM (Outbound Seq. Man.) 'er' + case 5: // report status + + break; +*/ + default: + ret_status = -1; // invalid option passed to RESET function + break; + } + LEAVE("ResetTach"); + return ret_status; +} + + + + + + +// 'addrBase' is IOBaseU for both TachLite and (older) Tachyon +int CpqTsLaserControl( void* addrBase, int opcode ) +{ + ULONG dwBuff; + + dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg + // (change only bit 4) + if( opcode == 1) + dwBuff |= ~0xffffffefL; // set - ON + else + dwBuff &= 0xffffffefL; // clear - OFF + writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg + return 0; +} + + + + + +// Use controller's "Options" field to determine loopback mode (if any) +// internal loopback (silicon - no GBIC) +// external loopback (GBIC - no FC loop) +// no loopback: L_PORT, external cable from GBIC required + +int CpqTsInitializeFrameManager( void *pChip, int opcode) +{ + PTACHYON fcChip; + int iStatus; + ULONG wwnLo, wwnHi; // for readback verification + + ENTER("InitializeFrameManager"); + fcChip = (PTACHYON)pChip; + if( !fcChip->Registers.ReMapMemBase ) // undefined controller? + return -1; + + // TL/TS UG, pg. 184 + // 0x0065 = 100ms for RT_TOV + // 0x01f5 = 500ms for ED_TOV + // 0x07D1 = 2000ms + fcChip->Registers.ed_tov.value = 0x006507D1; + writel( fcChip->Registers.ed_tov.value, + (fcChip->Registers.ed_tov.address)); + + + // Set LP_TOV to the FC-AL2 specified 2 secs. + // TL/TS UG, pg. 185 + writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); + + + // Now try to read the WWN from the adapter's NVRAM + iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ + + if( iStatus ) // NVRAM read failed? + { + printk(" WARNING! HBA NVRAM WWN read failed - make alias\n"); + // make up a WWN. If NULL or duplicated on loop, FC loop may hang! + + + fcChip->Registers.wwn_hi = (__u32)jiffies; + fcChip->Registers.wwn_hi |= 0x50000000L; + fcChip->Registers.wwn_lo = 0x44556677L; + } + + + writel( fcChip->Registers.wwn_hi, + fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI); + + writel( fcChip->Registers.wwn_lo, + fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); + + + // readback for verification: + wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI ); + + wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); + // test for correct chip register WRITE/READ + DEBUG_PCI( printk(" WWN %08X%08X\n", + fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) ); + + if( wwnHi != fcChip->Registers.wwn_hi || + wwnLo != fcChip->Registers.wwn_lo ) + { + printk( "cpqfcTS: WorldWideName register load failed\n"); + return -1; // FAILED! + } + + + + // set Frame Manager Initialize command + fcChip->Registers.FMcontrol.value = 0x06; + + // Note: for test/debug purposes, we may use "Hard" address, + // but we completely support "soft" addressing, including + // dynamically changing our address. + if( fcChip->Options.intLoopback == 1 ) // internal loopback + fcChip->Registers.FMconfig.value = 0x0f002080L; + else if( fcChip->Options.extLoopback == 1 ) // internal loopback + fcChip->Registers.FMconfig.value = 0x0f004080L; + else // L_Port + fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) +// fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick) +// fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) + + // write config to FM + + if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback ) + // (also need LASER for real LOOP) + fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER + + writel( fcChip->Registers.FMconfig.value, + fcChip->Registers.FMconfig.address); + + + // issue INITIALIZE command to FM - ACTION! + writel( fcChip->Registers.FMcontrol.value, + fcChip->Registers.FMcontrol.address); + + LEAVE("InitializeFrameManager"); + + return 0; +} + + + + + +// This "look ahead" function examines the IMQ for occurence of +// "type". Returns 1 if found, 0 if not. +static int PeekIMQEntry( PTACHYON fcChip, ULONG type) +{ + ULONG CI = fcChip->IMQ->consumerIndex; + ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes + + while( CI != PI ) + { // proceed with search + if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check + + switch( type ) + { + case ELS_LILP_FRAME: + { + // first, we need to find an Inbound Completion message, + // If we find it, check the incoming frame payload (1st word) + // for LILP frame + if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 ) + { + TachFCHDR_GCMND* fchs; + ULONG ulFibreFrame[2048/4]; // max DWORDS in incoming FC Frame + USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL); + + CpqTsGetSFQEntry( fcChip, + SFQpi, // SFQ producer ndx + ulFibreFrame, // contiguous dest. buffer + FALSE); // DON'T update chip--this is a "lookahead" + + fchs = (TachFCHDR_GCMND*)&ulFibreFrame; + if( fchs->pl[0] == ELS_LILP_FRAME) + { + return 1; // found the LILP frame! + } + else + { + // keep looking... + } + } + } + break; + + case OUTBOUND_COMPLETION: + if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 ) + { + + // any OCM errors? + if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L ) + return 1; // found OCM error + } + break; + + + + default: + break; + } + } + return 0; // failed to find "type" +} + + +static void SetTachTOV( CPQFCHBA* cpqfcHBAdata) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + + // TL/TS UG, pg. 184 + // 0x0065 = 100ms for RT_TOV + // 0x01f5 = 500ms for ED_TOV + // 0x07d1 = 2000ms for ED_TOV + + // SANMark Level 1 requires an "initialization backoff" + // (See "SANMark Test Suite Level 1": + // initialization_timeout.fcal.SANMark-1.fc) + // We have to use 2sec, 24sec, then 128sec when login/ + // port discovery processes fail to complete. + + // when port discovery completes (logins done), we set + // ED_TOV to 500ms -- this is the normal operational case + // On the first Link Down, we'll move to 2 secs (7D1 ms) + if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5) + fcChip->Registers.ed_tov.value = 0x006507D1; + + // If we get another LST after we moved TOV to 2 sec, + // increase to 24 seconds (5DC1 ms) per SANMark! + else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1) + fcChip->Registers.ed_tov.value = 0x00655DC1; + + // If we get still another LST, set the max TOV (Tachyon + // has only 16 bits for ms timer, so the max is 65.5 sec) + else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1) + fcChip->Registers.ed_tov.value = 0x0065FFFF; + + writel( fcChip->Registers.ed_tov.value, + (fcChip->Registers.ed_tov.address)); + // keep the same 2sec LP_TOV + writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); +} + + +// The IMQ is an array with IMQ_LEN length, each element (QEntry) +// with eight 32-bit words. Tachyon PRODUCES a QEntry with each +// message it wants to send to the host. The host CONSUMES IMQ entries + +// This function copies the current +// (or oldest not-yet-processed) QEntry to +// the caller, clears/ re-enables the interrupt, and updates the +// (Host) Consumer Index. +// Return value: +// 0 message processed, none remain (producer and consumer +// indexes match) +// 1 message processed, more messages remain +// -1 no message processed - none were available to process +// Remarks: +// TL/TS UG specifices that the following actions for +// INTA_L handling: +// 1. read PCI Interrupt Status register (0xff) +// 2. all IMQ messages should be processed before writing the +// IMQ consumer index. + + +int CpqTsProcessIMQEntry(void *host) +{ + struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + int iStatus; + USHORT i, RPCset, DPCset; + ULONG x_ID; + ULONG ulBuff, dwStatus; + TachFCHDR_GCMND* fchs; + ULONG ulFibreFrame[2048/4]; // max number of DWORDS in incoming Fibre Frame + UCHAR ucInboundMessageType; // Inbound CM, dword 3 "type" field + + ENTER("ProcessIMQEntry"); + + + // check TachLite's IMQ producer index - + // is a new message waiting for us? + // equal indexes means empty que + + if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex ) + { // need to process message + + +#ifdef IMQ_DEBUG + printk("PI %X, CI %X type: %X\n", + fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex, + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type); +#endif + // Examine Completion Messages in IMQ + // what CM_Type? + switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type + & 0xffL) ) + { + case OUTBOUND_COMPLETION: + + // Remarks: + // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries + // (starting at 0), and SFS entries (starting at + // SEST_LEN -- outside the SEST space). + // Psuedo code: + // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index + // range check - x_ID + // if x_ID outside 'Transactions' length, error - exit + // if any OCM error, copy error status to Exchange slot + // if FCP ASSIST transaction (x_ID within SEST), + // call fcComplete (to App) + // ... + + + ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]; + x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID + // Range check CM OX/RX_ID value... + if( x_ID < TACH_MAX_XID ) // don't go beyond array space + { + + + if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete? + RPCset = 1; // (SEST transactions only) + else + RPCset = 0; + + if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete? + DPCset = 1; // (SEST transactions only) + else + DPCset = 0; + // set the status for this Outbound transaction's ID + dwStatus = 0L; + if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error) + dwStatus |= SESTPROG_ERR; + + ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2]; + if( ulBuff & 0x7a000000L ) // any other errs? + { + if( ulBuff & 0x40000000L ) + dwStatus |= INV_ENTRY; + if( ulBuff & 0x20000000L ) + dwStatus |= FRAME_TO; // FTO + if( ulBuff & 0x10000000L ) + dwStatus |= HOSTPROG_ERR; + if( ulBuff & 0x08000000L ) + dwStatus |= LINKFAIL_TX; + if( ulBuff & 0x02000000L ) + dwStatus |= ABORTSEQ_NOTIFY; // ASN + } + + + if( dwStatus ) // any errors? + { + // set the Outbound Completion status + Exchanges->fcExchange[ x_ID ].status |= dwStatus; + + // if this Outbound frame was for a SEST entry, automatically + // reque it in the case of LINKFAIL (it will restart on PDISC) + if( x_ID < TACH_SEST_LEN ) + { + + printk(" #OCM error %Xh x_ID %X# ", + dwStatus, x_ID); + + Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default + + + // We Q ABTS for each exchange. + // NOTE: We can get FRAME_TO on bad alpa (device gone). Since + // bad alpa is reported before FRAME_TO, examine the status + // flags to see if the device is removed. If so, DON'T + // post an ABTS, since it will be terminated by the bad alpa + // message. + if( dwStatus & FRAME_TO ) // check for device removed... + { + if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) ) + { + // presumes device is still there: send ABTS. + + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); + } + } + else // Abort all other errors + { + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); + } + + // if the HPE bit is set, we have to CLose the LOOP + // (see TL/TS UG, pg. 239) + + if( dwStatus &= HOSTPROG_ERR ) + // set CL bit (see TL/TS UG, pg. 172) + writel( 4, fcChip->Registers.FMcontrol.address); + } + } + // NOTE: we don't necessarily care about ALL completion messages... + // SCSI resp. complete OR + if( ((x_ID < TACH_SEST_LEN) && RPCset)|| + (x_ID >= TACH_SEST_LEN) ) // non-SCSI command + { + // exchange done; complete to upper levels with status + // (if necessary) and free the exchange slot + + + if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame? + // A Request or Reply has been sent + { // signal waiting WorkerThread + + up( cpqfcHBAdata->TYOBcomplete); // frame is OUT of Tach + + // WorkerThread will complete Xchng + } + else // X_ID is for FCP assist (SEST) + { + // TBD (target mode) +// fcCompleteExchange( fcChip, x_ID); // TRE completed + } + } + } + else // ERROR CONDITION! bogus x_ID in completion message + { + + printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID); + + } + + + + // Load the Frame Manager's error counters. We check them here + // because presumably the link is up and healthy enough for the + // counters to be meaningful (i.e., don't check them while loop + // is initializing). + fcChip->Registers.FMLinkStatus1.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus1.address); + + fcChip->Registers.FMLinkStatus2.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus2.address); + + + fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators + break; + + + + case ERROR_IDLE_COMPLETION: // TachLite Error Idle... + + // We usually get this when the link goes down during heavy traffic. + // For now, presume that if SEST Exchanges are open, we will + // get this as our cue to INVALIDATE all SEST entries + // (and we OWN all the SEST entries). + // See TL/TS UG, pg. 53 + + for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) + { + + // Does this VALid SEST entry need to be invalidated for Abort? + fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; + } + + CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK + + break; + + + case INBOUND_SFS_COMPLETION: //0x04 + // NOTE! we must process this SFQ message to avoid SFQ filling + // up and stopping TachLite. Incoming commands are placed here, + // as well as 'unknown' frames (e.g. LIP loop position data) + // write this CM's producer index to global... + // TL/TS UG, pg 234: + // Type: 0 - reserved + // 1 - Unassisted FCP + // 2 - BAD FCP + // 3 - Unkown Frame + // 4-F reserved + + + fcChip->SFQ->producerIndex = (USHORT) + (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL); + + + ucInboundMessageType = 0; // default to useless frame + + // we can only process two Types: 1, Unassisted FCP, and 3, Unknown + // Also, we aren't interested in processing frame fragments + // so don't Que anything with 'LKF' bit set + if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] + & 0x40000000) ) // 'LKF' link failure bit clear? + { + ucInboundMessageType = (UCHAR) // ICM DWord3, "Type" + (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL); + } + else + { + fcChip->fcStats.linkFailRX++; +// printk("LKF (link failure) bit set on inbound message\n"); + } + + // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff + CpqTsGetSFQEntry( + fcChip, // i.e. this Device Object + (USHORT)fcChip->SFQ->producerIndex, // SFQ producer ndx + ulFibreFrame, TRUE); // contiguous destination buffer, update chip + + // analyze the incoming frame outside the INT handler... + // (i.e., Worker) + + if( ucInboundMessageType == 1 ) + { + fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame + // don't fill up our Q with garbage - only accept FCP-CMND + // or XRDY frames + if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND + { + // someone sent us a SCSI command + +// fcPutScsiQue( cpqfcHBAdata, +// SFQ_UNASSISTED_FCP, ulFibreFrame); + } + else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status) + (fchs->d_id & 0xFF000000) == 0x05000000 ) // XRDY + { + ULONG x_ID; + // Unfortunately, ABTS requires a Freeze on the chip so + // we can modify the shared memory SEST. When frozen, + // any received Exchange frames cannot be processed by + // Tachyon, so they will be dumped in here. It is too + // complex to attempt the reconstruct these frames in + // the correct Exchange context, so we simply seek to + // find status or transfer ready frames, and cause the + // exchange to complete with errors before the timeout + // expires. We use a Linux Scsi Cmnd result code that + // causes immediate retry. + + + // Do we have an open exchange that matches this s_id + // and ox_id? + for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) + { + if( (fchs->s_id & 0xFFFFFF) == + (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF) + && + (fchs->ox_rx_id & 0xFFFF0000) == + (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) ) + { + // printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id ); + // simulate the anticipated error - since the + // SEST was frozen, frames were lost... + Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME; + + // presumes device is still there: send ABTS. + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); + break; // done + } + } + } + + } + + else if( ucInboundMessageType == 3) + { + // FC Link Service frames (e.g. PLOGI, ACC) come in here. + cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame); + + } + + else if( ucInboundMessageType == 2 ) // "bad FCP"? + { +#ifdef IMQ_DEBUG + printk("Bad FCP incoming frame discarded\n"); +#endif + } + + else // don't know this type + { +#ifdef IMQ_DEBUG + printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType); +#endif + } + + // Check the Frame Manager's error counters. We check them here + // because presumably the link is up and healthy enough for the + // counters to be meaningful (i.e., don't check them while loop + // is initializing). + fcChip->Registers.FMLinkStatus1.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus1.address); + + + fcChip->Registers.FMLinkStatus2.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus2.address); + + + break; + + + + + // We get this CM because we issued a freeze + // command to stop outbound frames. We issue the + // freeze command at Link Up time; when this message + // is received, the ERQ base can be switched and PDISC + // frames can be sent. + + + case ERQ_FROZEN_COMPLETION: // note: expect ERQ followed immediately + // by FCP when freezing TL + fcChip->Registers.TYstatus.value = // read what's frozen + readl(fcChip->Registers.TYstatus.address); + // (do nothing; wait for FCP frozen message) + break; + case FCP_FROZEN_COMPLETION: + + fcChip->Registers.TYstatus.value = // read what's frozen + readl(fcChip->Registers.TYstatus.address); + + // Signal the kernel thread to proceed with SEST modification + up( cpqfcHBAdata->TachFrozen); + + break; + + + + case INBOUND_C1_TIMEOUT: + case MFS_BUF_WARN: + case IMQ_BUF_WARN: + break; + + + + + + // In older Tachyons, we 'clear' the internal 'core' interrupt state + // by reading the FMstatus register. In newer TachLite (Tachyon), + // we must WRITE the register + // to clear the condition (TL/TS UG, pg 179) + case FRAME_MGR_INTERRUPT: + { + PFC_LOGGEDIN_PORT pLoggedInPort; + + fcChip->Registers.FMstatus.value = + readl( fcChip->Registers.FMstatus.address ); + + // PROBLEM: It is possible, especially with "dumb" hubs that + // don't automatically LIP on by-pass of ports that are going + // away, for the hub by-pass process to destroy critical + // ordered sets of a frame. The result of this is a hung LPSM + // (Loop Port State Machine), which on Tachyon results in a + // (default 2 sec) Loop State Timeout (LST) FM message. We + // want to avoid this relatively huge timeout by detecting + // likely scenarios which will result in LST. + // To do this, we could examine FMstatus for Loss of Synchronization + // and/or Elastic Store (ES) errors. Of these, Elastic Store is better + // because we get this indication more quickly than the LOS. + // Not all ES errors are harmfull, so we don't want to LIP on every + // ES. Instead, on every ES, detect whether our LPSM in in one + // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE, + // or RECEIVED CLOSE. (See TL/TS UG, pg. 181) + // If any of these LPSM states are detected + // in combination with the LIP while LDn is not set, + // send an FM init (LIP F7,F7 for loops)! + // It is critical to the physical link stability NOT to reset (LIP) + // more than absolutely necessary; this is a basic premise of the + // SANMark level 1 spec. + { + ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4; + + if( (fcChip->Registers.FMstatus.value & 0x400) // ElasticStore? + && + !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn + && + !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF + { + if( (Lpsm != 0) || // not MONITORING? or + !(Lpsm & 0x8) )// not already offline? + { + // now check the particular LST states... + if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) || + (Lpsm == OPENED) || (Lpsm == XMITTD_CLOSE) || + (Lpsm == RCVD_CLOSE) ) + { + // re-init the loop before it hangs itself! + printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm); + + + fcChip->fcStats.FMinits++; + writel( 6, fcChip->Registers.FMcontrol.address); // LIP + } + } + } + else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST? + { + printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm); + + fcChip->fcStats.FMinits++; + writel( 6, fcChip->Registers.FMcontrol.address); // LIP + } + } + + + // clear only the 'interrupting' type bits for this REG read + writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L), + fcChip->Registers.FMstatus.address); + + + // copy frame manager status to unused ULONG slot + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] = + fcChip->Registers.FMstatus.value; // (for debugging) + + + // Load the Frame Manager's error counters. We check them here + // because presumably the link is up and healthy enough for the + // counters to be meaningful (i.e., don't check them while loop + // is initializing). + fcChip->Registers.FMLinkStatus1.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus1.address); + + fcChip->Registers.FMLinkStatus2.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus2.address); + + // Get FM BB_Credit Zero Reg - does not clear on READ + fcChip->Registers.FMBB_CreditZero.value = // get TL's counter + readl(fcChip->Registers.FMBB_CreditZero.address); + + + + fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators + + + // LINK DOWN + + if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit + { + +#ifdef IMQ_DEBUG + printk("LinkDn\n"); +#endif + printk(" #LDn# "); + + fcChip->fcStats.linkDown++; + + SetTachTOV( cpqfcHBAdata); // must set according to SANMark + + // Check the ERQ - force it to be "empty" to prevent Tach + // from sending out frames before we do logins. + + + if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex) + { +// printk("#ERQ PI != CI#"); + CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only + fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0; + writel( fcChip->ERQ->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); + // re-writing base forces ERQ PI to equal CI + + } + + // link down transition occurred -- port_ids can change + // on next LinkUp, so we must invalidate current logins + // (and any I/O in progress) until PDISC or PLOGI/PRLI + // completes + { + pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + + if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? + { + pLoggedInPort->LOGO_timer = 3; // we want 2 seconds + // but Timer granularity + // is 1 second + } + // suspend any I/O in progress until + // PDISC received... + pLoggedInPort->prli = FALSE; // block FCP-SCSI commands + + pLoggedInPort = pLoggedInPort->pNextPort; + } // ... all Previously known ports checked + } + + // since any hot plugging device may NOT support LILP frames + // (such as early Tachyon chips), clear this flag indicating + // we shouldn't use (our copy of) a LILP map. + // If we receive an LILP frame, we'll set it again. + fcChip->Options.LILPin = 0; // our LILPmap is invalid + cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports! + + // also, we want to invalidate (i.e. INITIATOR_ABORT) any + // open Login exchanges, in case the LinkDown happened in the + // middle of logins. It's possible that some ports already + // ACCepted login commands which we have not processed before + // another LinkDown occured. Any accepted Login exhanges are + // invalidated by LinkDown, even before they are acknowledged. + // It's also possible for a port to have a Queued Reply or Request + // for login which was interrupted by LinkDown; it may come later, + // but it will be unacceptable to us. + + // we must scan the entire exchange space, find every Login type + // originated by us, and abort it. This is NOT an abort due to + // timeout, so we don't actually send abort to the other port - + // we just complete it to free up the fcExchange slot. + + for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++) + { // looking for Extended Link Serv.Exchanges + if( Exchanges->fcExchange[i].type == ELS_PDISC || + Exchanges->fcExchange[i].type == ELS_PLOGI || + Exchanges->fcExchange[i].type == ELS_PRLI ) + { + // ABORT the exchange! +#ifdef IMQ_DEBUG + printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n", + i, Exchanges->fcExchange[i].type, + Exchanges->fcExchange[i].fchs.d_id); +#endif + + Exchanges->fcExchange[i].status |= INITIATOR_ABORT; + cpqfcTSCompleteExchange( fcChip, i); // abort on LDn + } + } + + } + + // ################ LINK UP ################## + if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit + { // AL_PA could have changed + + // We need the following code, duplicated from LinkDn condition, + // because it's possible for the Tachyon to re-initialize (hard + // reset) without ever getting a LinkDn indication. + pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? + { + pLoggedInPort->LOGO_timer = 3; // we want 2 seconds + // but Timer granularity + // is 1 second + + // suspend any I/O in progress until + // PDISC received... + + } + pLoggedInPort = pLoggedInPort->pNextPort; + } // ... all Previously known ports checked + + // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA) + fcChip->Registers.rcv_al_pa.value = + readl(fcChip->Registers.rcv_al_pa.address); + + // Now, if our acquired address is DIFFERENT from our + // previous one, we are not allow to do PDISC - we + // must go back to PLOGI, which will terminate I/O in + // progress for ALL logged in FC devices... + // (This is highly unlikely). + + if( (fcChip->Registers.my_al_pa & 0xFF) != + ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) ) + { + +// printk(" #our HBA port_id changed!# "); // FC port_id changed!! + + pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; + pLoggedInPort = pLoggedInPort->pNextPort; + } // ... all Previously known ports checked + + // when the port_id changes, we must terminate + // all open exchanges. + cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED); + + } + + // Replace the entire 24-bit port_id. We only know the + // lower 8 bits (alpa) from Tachyon; if a FLOGI is done, + // we'll get the upper 16-bits from the FLOGI ACC frame. + // If someone plugs into Fabric switch, we'll do FLOGI and + // get full 24-bit port_id; someone could then remove and + // hot-plug us into a dumb hub. If we send a 24-bit PLOGI + // to a "private" loop device, it might blow up. + // Consequently, we force the upper 16-bits of port_id to + // be re-set on every LinkUp transition + fcChip->Registers.my_al_pa = + (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF; + + + // copy frame manager status to unused ULONG slot + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = + fcChip->Registers.my_al_pa; // (for debugging) + + // for TachLite, we need to write the acquired al_pa + // back into the FMconfig register, because after + // first initialization, the AQ (prev. acq.) bit gets + // set, causing TL FM to use the AL_PA field in FMconfig. + // (In Tachyon, FM writes the acquired AL_PA for us.) + ulBuff = readl( fcChip->Registers.FMconfig.address); + ulBuff &= 0x00ffffffL; // mask out current al_pa + ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa + fcChip->Registers.FMconfig.value = ulBuff; // copy it back + writel( fcChip->Registers.FMconfig.value, // put in TachLite + fcChip->Registers.FMconfig.address); + + +#ifdef IMQ_DEBUG + printk("#LUp %Xh, FMstat 0x%08X#", + fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value); +#endif + + // also set the WRITE-ONLY My_ID Register (for Fabric + // initialization) + writel( fcChip->Registers.my_al_pa, + fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID); + + + fcChip->fcStats.linkUp++; + + // reset TL statistics counters + // (we ignore these error counters + // while link is down) + ulBuff = // just reset TL's counter + readl( fcChip->Registers.FMLinkStatus1.address); + + ulBuff = // just reset TL's counter + readl( fcChip->Registers.FMLinkStatus2.address); + + // for initiator, need to start verifying ports (e.g. PDISC) + + + + + + + CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK + + // Tachyon creates an interesting problem for us on LILP frames. + // Instead of writing the incoming LILP frame into the SFQ before + // indicating LINK UP (the actual order of events), Tachyon tells + // us LINK UP, and later us the LILP. So we delay, then examine the + // IMQ for an Inbound CM (x04); if found, we can set + // LINKACTIVE after processing the LILP. Otherwise, just proceed. + // Since Tachyon imposes this time delay (and doesn't tell us + // what it is), we have to impose a delay before "Peeking" the IMQ + // for Tach hardware (DMA) delivery. + // Processing LILP is required by SANMark + udelay( 1000); // microsec delay waiting for LILP (if it comes) + if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) ) + { // found SFQ LILP, which will post LINKACTIVE +// printk("skipping LINKACTIVE post\n"); + + } + else + cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame); + } + + + + // ******* Set Fabric Login indication ******** + if( fcChip->Registers.FMstatus.value & 0x2000 ) + { + printk(" #Fabric# "); + fcChip->Options.fabric = 1; + } + else + fcChip->Options.fabric = 0; + + + + // ******* LIP(F8,x) or BAD AL_PA? ******** + if( fcChip->Registers.FMstatus.value & 0x30000L ) + { + // copy the error AL_PAs + fcChip->Registers.rcv_al_pa.value = + readl(fcChip->Registers.rcv_al_pa.address); + + // Bad AL_PA? + if( fcChip->Registers.FMstatus.value & 0x10000L ) + { + PFC_LOGGEDIN_PORT pLoggedInPort; + + // copy "BAD" al_pa field + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = + (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8; + + pLoggedInPort = fcFindLoggedInPort( fcChip, + NULL, // DON'T search Scsi Nexus + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + if( pLoggedInPort ) + { + // Just in case we got this BAD_ALPA because a device + // quietly disappeared (can happen on non-managed hubs such + // as the Vixel Rapport 1000), + // do an Implicit Logout. We never expect this on a Logged + // in port (but do expect it on port discovery). + // (As a reasonable alternative, this could be changed to + // simply start the implicit logout timer, giving the device + // several seconds to "come back".) + // + printk(" #BAD alpa %Xh# ", + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]); + cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); + } + } + // LIP(f8,x)? + if( fcChip->Registers.FMstatus.value & 0x20000L ) + { + // for debugging, copy al_pa field + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] = + (fcChip->Registers.rcv_al_pa.value & 0xffL); + // get the other port's al_pa + // (one that sent LIP(F8,?) ) + } + } + + // Elastic store err + if( fcChip->Registers.FMstatus.value & 0x400L ) + { + // don't count e-s if loop is down! + if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) ) + fcChip->fcStats.e_stores++; + + } + } + break; + + + case INBOUND_FCP_XCHG_COMPLETION: // 0x0C + + // Remarks: + // On Tachlite TL/TS, we get this message when the data phase + // of a SEST inbound transfer is complete. For example, if a WRITE command + // was received with OX_ID 0, we might respond with XFER_RDY with + // RX_ID 8001. This would start the SEST controlled data phases. When + // all data frames are received, we get this inbound completion. This means + // we should send a status frame to complete the status phase of the + // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data + // frames. + // See Outbound CM discussion of x_IDs + // Psuedo Code + // Get SEST index (x_ID) + // x_ID out of range, return (err condition) + // set status bits from 2nd dword + // free transactionID & SEST entry + // call fcComplete with transactionID & status + + ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0]; + x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID + // (mask out MSB "direction" bit) + // Range check CM OX/RX_ID value... + if( x_ID < TACH_SEST_LEN ) // don't go beyond SEST array space + { + +//#define FCP_COMPLETION_DBG 1 +#ifdef FCP_COMPLETION_DBG + printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", + x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd); +#endif + if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or - + // time to send response frame? + RPCset = 1; // (SEST transaction) + else + RPCset = 0; + // set the status for this Inbound SCSI transaction's ID + dwStatus = 0L; + if( ulBuff & 0x70000000L ) // any errs? + { + + if( ulBuff & 0x40000000L ) + dwStatus |= LINKFAIL_RX; + + if( ulBuff & 0x20000000L ) + dwStatus |= COUNT_ERROR; + + if( ulBuff & 0x10000000L ) + dwStatus |= OVERFLOW; + } + + + // FCP transaction done - copy status + Exchanges->fcExchange[ x_ID ].status = dwStatus; + + + // Did the exchange get an FCP-RSP response frame? + // (Note the little endian/big endian FC payload difference) + + if( RPCset ) // SEST transaction Response frame rec'd + { + // complete the command in our driver... + cpqfcTSCompleteExchange( fcChip, x_ID); + + } // end "RPCset" + + else // ("target" logic) + { + // Tachlite says all data frames have been received - now it's time + // to analyze data transfer (successful?), then send a response + // frame for this exchange + + ulFibreFrame[0] = x_ID; // copy for later reference + + // if this was a TWE, we have to send satus response + if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE ) + { +// fcPutScsiQue( cpqfcHBAdata, +// NEED_FCP_RSP, ulFibreFrame); // (ulFibreFrame not used here) + } + } + } + else // ERROR CONDITION! bogus x_ID in completion message + { + printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID); + } + + break; + + + + + case INBOUND_SCSI_DATA_COMMAND: + case BAD_SCSI_FRAME: + case INB_SCSI_STATUS_COMPLETION: + case BUFFER_PROCESSED_COMPLETION: + break; + } + + // Tachyon is producing; + // we are consuming + fcChip->IMQ->consumerIndex++; // increment OUR consumerIndex + if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover + fcChip->IMQ->consumerIndex = 0L; // reset it + + + if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex ) + { // all Messages are processed - + iStatus = 0; // no more messages to process + + } + else + iStatus = 1; // more messages to process + + // update TachLite's ConsumerIndex... (clears INTA_L) + // NOTE: according to TL/TS UG, the + // "host must return completion messages in sequential order". + // Does this mean one at a time, in the order received? We + // presume so. + + writel( fcChip->IMQ->consumerIndex, + (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); + +#if IMQ_DEBUG + printk("Process IMQ: writing consumer ndx %d\n ", + fcChip->IMQ->consumerIndex); + printk("PI %X, CI %X\n", + fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex ); +#endif + + + + } + else + { + // hmmm... why did we get interrupted/called with no message? + iStatus = -1; // nothing to process +#if IMQ_DEBUG + printk("Process IMQ: no message PI %Xh CI %Xh", + fcChip->IMQ->producerIndex, + fcChip->IMQ->consumerIndex); +#endif + } + + LEAVE("ProcessIMQEntry"); + + return iStatus; +} + + + + + +// This routine initializes Tachyon according to the following +// options (opcode1): +// 1 - RESTART Tachyon, simulate power on condition by shutting +// down laser, resetting the hardware, de-allocating all buffers; +// continue +// 2 - Config Tachyon / PCI registers; +// continue +// 3 - Allocating memory and setting Tachyon queues (write Tachyon regs); +// continue +// 4 - Config frame manager registers, initialize, turn on laser +// +// Returns: +// -1 on fatal error +// 0 on success + +int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + ULONG ulBuff; + UCHAR bBuff; + int iStatus=-1; // assume failure + + ENTER("InitializeTachLite"); + + // verify board's base address (sanity check) + + if( !fcChip->Registers.ReMapMemBase) // NULL address for card? + return -1; // FATAL error! + + + + switch( opcode1 ) + { + case 1: // restore hardware to power-on (hard) restart + + + iStatus = fcChip->ResetTachyon( + cpqfcHBAdata, opcode2); // laser off, reset hardware + // de-allocate aligned buffers + + +/* TBD // reset FC link Q (producer and consumer = 0) + fcLinkQReset(cpqfcHBAdata); + +*/ + + if( iStatus ) + break; + + case 2: // Config PCI/Tachyon registers + // NOTE: For Tach TL/TS, bit 31 must be set to 1. For TS chips, a read + // of bit 31 indicates state of M66EN signal; if 1, chip may run at + // 33-66MHz (see TL/TS UG, pg 159) + + ulBuff = 0x80000000; // TachLite Configuration Register + + writel( ulBuff, fcChip->Registers.TYconfig.address); +// ulBuff = 0x0147L; // CpqTs PCI CFGCMD register +// WritePCIConfiguration( fcChip->Backplane.bus, +// fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4); +// ulBuff = 0x0L; // test! +// ReadPCIConfiguration( fcChip->Backplane.bus, +// fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4); + + // read back for reference... + fcChip->Registers.TYconfig.value = + readl( fcChip->Registers.TYconfig.address ); + + // what is the PCI bus width? + pci_read_config_byte( cpqfcHBAdata->PciDev, + 0x43, // PCIMCTR offset + &bBuff); + + fcChip->Registers.PCIMCTR = bBuff; + + // set string identifying the chip on the circuit board + + fcChip->Registers.TYstatus.value = + readl( fcChip->Registers.TYstatus.address); + + { +// Now that we are supporting multiple boards, we need to change +// this logic to check for PCI vendor/device IDs... +// for now, quick & dirty is simply checking Chip rev + + ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5; + UCHAR Minor = (UCHAR)(RevId & 0x3); + UCHAR Major = (UCHAR)((RevId & 0x1C) >>2); + + printk(" HBA Tachyon RevId %d.%d\n", Major, Minor); + if( (Major == 1) && (Minor == 2) ) + { + sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12); + + } + else if( (Major == 1) && (Minor == 3) ) + { + sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13); + } + else if( (Major == 2) && (Minor == 1) ) + { + sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21); + } + else + sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN); + } + + + + case 3: // allocate mem, set Tachyon Que registers + iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2); + + // now that the Queues exist, Tach can DMA to them, so + // we can begin processing INTs + // INTEN register - enable INT (TachLite interrupt) + writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN); + + + if( iStatus ) + break; + + + case 4: // Config Fame Manager, Init Loop Command, laser on + + // L_PORT or loopback + // depending on Options + iStatus = CpqTsInitializeFrameManager( fcChip,0 ); + if( iStatus ) + { + // failed to initialize Frame Manager + break; + } + + default: + break; + } + LEAVE("InitializeTachLite"); + + return iStatus; +} + + + + +// Depending on the type of platform memory allocation (e.g. dynamic), +// it's probably best to free memory in opposite order as it was allocated. +// Order of allocation: see other function + + +int CpqTsDestroyTachLiteQues( void *pHBA, int opcode) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + USHORT i, j, iStatus=0; + void* vPtr; // mem Align manager sets this to the freed address on success + unsigned long ulPtr; // for 64-bit pointer cast (e.g. Alpa machine) + + ENTER("DestroyTachLiteQues"); + + if( fcChip->SEST ) + { + // search out and free Pool for Extended S/G list pages + + for( i=0, j=0; i < TACH_SEST_LEN; i++, j=0) // for each exchange + { + // It's possible that extended S/G pages were allocated and + // not cleared due to error conditions or O/S driver termination. + // Make sure they're all gone. + while( fcChip->SEST->sgPages[i].PoolPage[j] && + (j < TL_MAX_SGPAGES)) + kfree( fcChip->SEST->sgPages[i].PoolPage[j++]); + + } + ulPtr = (unsigned long)fcChip->SEST; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->SEST = 0L; // null invalid ptr + if( !vPtr ) + { + printk("SEST mem not freed\n"); + iStatus = -1; + } + } + + if( fcChip->SFQ ) + { + + ulPtr = (unsigned long)fcChip->SFQ; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->SFQ = 0L; // null invalid ptr + if( !vPtr ) + { + printk("SFQ mem not freed\n"); + iStatus = -2; + } + } + + + if( fcChip->IMQ ) + { + // clear Indexes to show empty Queue + fcChip->IMQ->producerIndex = 0; + fcChip->IMQ->consumerIndex = 0; + + ulPtr = (unsigned long)fcChip->IMQ; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->IMQ = 0L; // null invalid ptr + if( !vPtr ) + { + printk("IMQ mem not freed\n"); + iStatus = -3; + } + } + + if( fcChip->ERQ ) // release memory blocks used by the queues + { + ulPtr = (unsigned long)fcChip->ERQ; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->ERQ = 0L; // null invalid ptr + if( !vPtr ) + { + printk("ERQ mem not freed\n"); + iStatus = -4; + } + } + + // free up the primary EXCHANGES struct + if( fcChip->Exchanges != NULL) + { +// printk("kfree() on Exchanges @%p\n", fcChip->Exchanges); + kfree( fcChip->Exchanges); + } + + // free up Link Q + if( cpqfcHBAdata->fcLQ != NULL ) + { +// printk("kfree() on LinkQ @%p\n", fcChip->fcLQ); + kfree( cpqfcHBAdata->fcLQ); + } + + LEAVE("DestroyTachLiteQues"); + + return iStatus; // non-zero (failed) if any memory not freed +} + + + + + +// The SFQ is an array with SFQ_LEN length, each element (QEntry) +// with eight 32-bit words. TachLite places incoming FC frames (i.e. +// a valid FC frame with our AL_PA ) in contiguous SFQ entries +// and sends a completion message telling the host where the frame is +// in the que. +// This function copies the current (or oldest not-yet-processed) QEntry to +// a caller's contiguous buffer and updates the Tachyon chip's consumer index +// +// NOTE: +// An FC frame may consume one or many SFQ entries. We know the total +// length from the completion message. The caller passes a buffer large +// enough for the complete message (max 2k). + +static void CpqTsGetSFQEntry( + PTACHYON fcChip, + USHORT producerNdx, + ULONG *ulDestPtr, // contiguous destination buffer + BOOLEAN UpdateChip) +{ + ULONG total_bytes=0; + ULONG consumerIndex = fcChip->SFQ->consumerIndex; + + // check passed copy of SFQ producer index - + // is a new message waiting for us? + // equal indexes means SFS is copied + + while( producerNdx != consumerIndex ) + { // need to process message + total_bytes += 64; // maintain count to prevent writing past buffer + // don't allow copies over Fibre Channel defined length! + if( total_bytes <= 2048 ) + { + memcpy( ulDestPtr, + &fcChip->SFQ->QEntry[consumerIndex], + 64 ); // each SFQ entry is 64 bytes + ulDestPtr += 16; // advance pointer to next 64 byte block + } + // Tachyon is producing, + // and we are consuming + + if( ++consumerIndex >= SFQ_LEN)// check for rollover + consumerIndex = 0L; // reset it + } + + // if specified, update the Tachlite chip ConsumerIndex... + if( UpdateChip ) + { + fcChip->SFQ->consumerIndex = consumerIndex; + writel( fcChip->SFQ->consumerIndex, + fcChip->Registers.SFQconsumerIndex.address); + } +} + + + +// TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO, +// and Exchange Request Queue (ERQ) on error recover - +// (e.g. whenever a LIP occurs). Here +// we routinely RESUME by clearing these bits, but only if the loop is up +// to avoid ERROR IDLE messages forever. + +void CpqTsUnFreezeTachlite( void *pChip, int type ) +{ + PTACHYON fcChip = (PTACHYON)pChip; + fcChip->Registers.TYcontrol.value = + readl(fcChip->Registers.TYcontrol.address); + + // (bit 4 of value is GBIC LASER) + // if we 'unfreeze' the core machines before the loop is healthy + // (i.e. FLT, OS, LS failure bits set in FMstatus) + // we can get 'error idle' messages forever. Verify that + // FMstatus (Link Status) is OK before unfreezing. + + if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear? + !(fcChip->Registers.FMstatus.value & 0x80 )) // Active LPSM? + { + fcChip->Registers.TYcontrol.value &= ~0x300L; // clear FEQ, FFA + if( type == 1 ) // unfreeze ERQ only + { +// printk("Unfreezing ERQ\n"); + fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ + } + else // unfreeze both ERQ and FCP-ASSIST (SEST) + { +// printk("Unfreezing ERQ & FCP-ASSIST\n"); + + // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ + fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ + } + + writel( fcChip->Registers.TYcontrol.value, + fcChip->Registers.TYcontrol.address); + + } + // readback for verify (TachLite still frozen?) + fcChip->Registers.TYstatus.value = + readl(fcChip->Registers.TYstatus.address); +} + + +// Whenever an FC Exchange Abort is required, we must manipulate the +// Host/Tachyon shared memory SEST table. Before doing this, we +// must freeze Tachyon, which flushes certain buffers and ensure we +// can manipulate the SEST without contention. +// This freeze function will result in FCP & ERQ FROZEN completion +// messages (per argument "type"). + +void CpqTsFreezeTachlite( void *pChip, int type ) +{ + PTACHYON fcChip = (PTACHYON)pChip; + fcChip->Registers.TYcontrol.value = + readl(fcChip->Registers.TYcontrol.address); + + //set FFA, FEQ - freezes SCSI assist and ERQ + if( type == 1) // freeze ERQ only + fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser) + else // freeze both FCP assists (SEST) and ERQ + fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser) + + writel( fcChip->Registers.TYcontrol.value, + fcChip->Registers.TYcontrol.address); + +} + + + + +// TL has two Frame Manager Link Status Registers, with three 8-bit +// fields each. These eight bit counters are cleared after each read, +// so we define six 32-bit accumulators for these TL counters. This +// function breaks out each 8-bit field and adds the value to the existing +// sum. (s/w counters cleared independently) + +void fcParseLinkStatusCounters(PTACHYON fcChip) +{ + UCHAR bBuff; + ULONG ulBuff; + + +// The BB0 timer usually increments when TL is initialized, resulting +// in an initially bogus count. If our own counter is ZERO, it means we +// are reading this thing for the first time, so we ignore the first count. +// Also, reading the register does not clear it, so we have to keep an +// additional static counter to detect rollover (yuk). + + if( fcChip->fcStats.lastBB0timer == 0L) // TL was reset? (ignore 1st values) + { + // get TL's register counter - the "last" count + fcChip->fcStats.lastBB0timer = + fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; + } + else // subsequent pass - check for rollover + { + // "this" count + ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; + if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened + { + // counter advanced to max... + fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer); + fcChip->fcStats.BB0_Timer += ulBuff; // plus some more + + + } + else // no rollover -- more counts or no change + { + fcChip->fcStats.BB0_Timer += (ulBuff - fcChip->fcStats.lastBB0timer); + + } + + fcChip->fcStats.lastBB0timer = ulBuff; + } + + + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24); + fcChip->fcStats.LossofSignal += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16); + fcChip->fcStats.BadRXChar += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8); + fcChip->fcStats.LossofSync += bBuff; + + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24); + fcChip->fcStats.Rx_EOFa += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16); + fcChip->fcStats.Dis_Frm += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8); + fcChip->fcStats.Bad_CRC += bBuff; +} + + +void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip) +{ + ENTER("ClearLinkStatusCounters"); + memset( &fcChip->fcStats, 0, sizeof( FCSTATS)); + LEAVE("ClearLinkStatusCounters"); + +} + + + + +// The following function reads the I2C hardware to get the adapter's +// World Wide Name (WWN). +// If the WWN is "500805f1fadb43e8" (as printed on the card), the +// Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register +// is fadb43e8. +// In the NVRAM, the bytes appear as: +// [2d] .. +// [2e] .. +// [2f] 50 +// [30] 08 +// [31] 05 +// [32] f1 +// [33] fa +// [34] db +// [35] 43 +// [36] e8 +// +// In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will +// be correctly loaded by Tachyon silicon. In the login payload, bytes +// must be correctly swapped for Big Endian format. + +int CpqTsReadWriteWWN( PVOID pChip, int Read) +{ + PTACHYON fcChip = (PTACHYON)pChip; +#define NVRAM_SIZE 512 + unsigned short i, count = NVRAM_SIZE; + UCHAR nvRam[NVRAM_SIZE], WWNbuf[8]; + ULONG ulBuff; + int iStatus=-1; // assume failure + int WWNoffset; + + ENTER("ReadWriteWWN"); + // Now try to read the WWN from the adapter's NVRAM + + if( Read ) // READing NVRAM WWN? + { + ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address, + fcChip->Registers.TYcontrol.address, + count, &nvRam[0] ); + + if( ulBuff ) // NVRAM read successful? + { + iStatus = 0; // success! + + // for engineering/ prototype boards, the data may be + // invalid (GIGO, usually all "FF"); this prevents the + // parse routine from working correctly, which means + // nothing will be written to our passed buffer. + + WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam ); + + if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly + { + printk( "CAUTION: Copying NVRAM data on fcChip\n"); + for( i= 0; i < 8; i++) + WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work + } + + fcChip->Registers.wwn_hi = 0L; + fcChip->Registers.wwn_lo = 0L; + for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM + { + ulBuff = 0L; + ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i)); + fcChip->Registers.wwn_hi |= ulBuff; + } + for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM + { + ulBuff = 0L; + ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i)); + fcChip->Registers.wwn_lo |= ulBuff; + } + } // done reading + else + { + + printk( "cpqfcTS: NVRAM read failed\n"); + + } + } + + else // WRITE + { + + // NOTE: WRITE not supported & not used in released driver. + + + printk("ReadWriteNRAM: can't write NVRAM; aborting write\n"); + } + + LEAVE("ReadWriteWWN"); + return iStatus; +} + + + + + +// The following function reads or writes the entire "NVRAM" contents of +// the I2C hardware (i.e. the NM24C03). Note that HP's 5121A (TS 66Mhz) +// adapter does not use the NM24C03 chip, so this function only works on +// Compaq's adapters. + +int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read) +{ + PTACHYON fcChip = (PTACHYON)pChip; +#define NVRAM_SIZE 512 + ULONG ulBuff; + UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array + int iStatus=-1; // assume failure + + + if( Read ) // READing NVRAM? + { + ulBuff = cpqfcTS_ReadNVRAM( // TRUE on success + fcChip->Registers.TYstatus.address, + fcChip->Registers.TYcontrol.address, + 256, // bytes to write + ucPtr ); // source ptr + + + if( ulBuff ) + iStatus = 0; // success + else + { +#ifdef DBG + printk( "CAUTION: NVRAM read failed\n"); +#endif + } + } // done reading + + else // WRITING NVRAM + { + + printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n"); + } + + return iStatus; +} diff --git a/drivers/scsi/cpqfcTSi2c.c b/drivers/scsi/cpqfcTSi2c.c new file mode 100644 index 000000000000..af9389d0796d --- /dev/null +++ b/drivers/scsi/cpqfcTSi2c.c @@ -0,0 +1,494 @@ +/* Copyright(c) 2000, Compaq Computer Corporation + * Fibre Channel Host Bus Adapter + * 64-bit, 66MHz PCI + * Originally developed and tested on: + * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... + * SP# P225CXCBFIEL6T, Rev XC + * SP# 161290-001, Rev XD + * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 + * + * 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, 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. + * Written by Don Zimmerman +*/ +// These functions control the NVRAM I2C hardware on +// non-intelligent Fibre Host Adapters. +// The primary purpose is to read the HBA's NVRAM to get adapter's +// manufactured WWN to copy into Tachyon chip registers +// Orignal source author unknown + +#include +enum boolean { FALSE, TRUE } ; + + +#ifndef UCHAR +typedef __u8 UCHAR; +#endif +#ifndef BOOLEAN +typedef __u8 BOOLEAN; +#endif +#ifndef USHORT +typedef __u16 USHORT; +#endif +#ifndef ULONG +typedef __u32 ULONG; +#endif + + +#include +#include +#include +#include +#include +#include // struct pt_regs for IRQ handler & Port I/O + +#include "cpqfcTSchip.h" + +static void tl_i2c_tx_byte( void* GPIOout, UCHAR data ); +/*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout, + USHORT startOffset, // e.g. 0x2f for WWN start + USHORT count, + UCHAR *buf ); +*/ + +// +// Tachlite GPIO2, GPIO3 (I2C) DEFINES +// The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data) +// GPIO2 drives SDA, and GPIO3 drives SCL +// +// Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0 +// and clear writes 1. The input lines (read in TL status) is NOT inverted +// This really helps confuse the code and debugging. + +#define SET_DATA_HI 0x0 +#define SET_DATA_LO 0x8 +#define SET_CLOCK_HI 0x0 +#define SET_CLOCK_LO 0x4 + +#define SENSE_DATA_HI 0x8 +#define SENSE_DATA_LO 0x0 +#define SENSE_CLOCK_HI 0x4 +#define SENSE_CLOCK_LO 0x0 + +#define SLAVE_READ_ADDRESS 0xA1 +#define SLAVE_WRITE_ADDRESS 0xA0 + + +static void i2c_delay(ULONG mstime); +static void tl_i2c_clock_pulse( UCHAR , void* GPIOout); +static UCHAR tl_read_i2c_data( void* ); + + +//----------------------------------------------------------------------------- +// +// Name: I2C_RX_ACK +// +// This routine receives an acknowledge over the I2C bus. +// +//----------------------------------------------------------------------------- +static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout ) +{ + unsigned long value; + + // do clock pulse, let data line float high + tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); + + // slave must drive data low for acknowledge + value = tl_read_i2c_data( GPIOin); + if (value & SENSE_DATA_HI ) + return( FALSE ); + + return( TRUE ); +} +//----------------------------------------------------------------------------- +// +// Name: READ_I2C_REG +// +// This routine reads the I2C control register using the global +// IO address stored in gpioreg. +// +//----------------------------------------------------------------------------- +static UCHAR tl_read_i2c_data( void* gpioreg ) +{ + return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3 +} +//----------------------------------------------------------------------------- +// +// Name: WRITE_I2C_REG +// +// This routine writes the I2C control register using the global +// IO address stored in gpioreg. +// In Tachlite, we don't want to modify other bits in TL Control reg. +// +//----------------------------------------------------------------------------- +static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value ) +{ + ULONG temp; + + // First read the register and clear out the old bits + temp = readl( gpioregOUT ) & 0xfffffff3L; + + // Now or in the new data and send it back out + writel( temp | value, gpioregOUT); +} +//----------------------------------------------------------------------------- +// +// Name: I2C_TX_START +// +// This routine transmits a start condition over the I2C bus. +// 1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH, +// wait 5us to stabilize. +// 2. With SCL still HIGH, drive SDA low. The low transition marks +// the start condition to NM24Cxx (the chip) +// NOTE! In TL control reg., output 1 means chip sees LOW +// +//----------------------------------------------------------------------------- +static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout ) +{ + unsigned short i; + ULONG value; + + if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI)) + { + // start with clock high, let data float high + tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI ); + + // keep sending clock pulses if slave is driving data line + for (i = 0; i < 10; i++) + { + tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); + + if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI ) + break; + } + + // if he's still driving data low after 10 clocks, abort + value = tl_read_i2c_data( GPIOin ); // read status + if (!(value & 0x08) ) + return( FALSE ); + } + + + // To START, bring data low while clock high + tl_write_i2c_reg( GPIOout, SET_CLOCK_HI | SET_DATA_LO ); + + i2c_delay(0); + + return( TRUE ); // TX start successful +} +//----------------------------------------------------------------------------- +// +// Name: I2C_TX_STOP +// +// This routine transmits a stop condition over the I2C bus. +// +//----------------------------------------------------------------------------- + +static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout ) +{ + int i; + + for (i = 0; i < 10; i++) + { + // Send clock pulse, drive data line low + tl_i2c_clock_pulse( SET_DATA_LO, GPIOout ); + + // To STOP, bring data high while clock high + tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI ); + + // Give the data line time to float high + i2c_delay(0); + + // If slave is driving data line low, there's a problem; retry + if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI ) + return( TRUE ); // TX STOP successful! + } + + return( FALSE ); // error +} +//----------------------------------------------------------------------------- +// +// Name: I2C_TX_uchar +// +// This routine transmits a byte across the I2C bus. +// +//----------------------------------------------------------------------------- +static void tl_i2c_tx_byte( void* GPIOout, UCHAR data ) +{ + UCHAR bit; + + for (bit = 0x80; bit; bit >>= 1) + { + if( data & bit ) + tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout); + else + tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout); + } +} +//----------------------------------------------------------------------------- +// +// Name: I2C_RX_uchar +// +// This routine receives a byte across the I2C bus. +// +//----------------------------------------------------------------------------- +static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout ) +{ + UCHAR bit; + UCHAR data = 0; + + + for (bit = 0x80; bit; bit >>= 1) { + // do clock pulse, let data line float high + tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); + + // read data line + if ( tl_read_i2c_data( GPIOin) & 0x08 ) + data |= bit; + } + + return (data); +} +//***************************************************************************** +//***************************************************************************** +// Function: read_i2c_nvram +// Arguments: UCHAR count number of bytes to read +// UCHAR *buf area to store the bytes read +// Returns: 0 - failed +// 1 - success +//***************************************************************************** +//***************************************************************************** +unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count, + UCHAR *buf ) +{ + unsigned short i; + + if( !( tl_i2c_tx_start(GPIOin, GPIOout) )) + return FALSE; + + // Select the NVRAM for "dummy" write, to set the address + tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS ); + if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) ) + return( FALSE ); + + // Now send the address where we want to start reading + tl_i2c_tx_byte( GPIOout , 0 ); + if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) ) + return( FALSE ); + + // Send a repeated start condition and select the + // slave for reading now. + if( tl_i2c_tx_start(GPIOin, GPIOout) ) + tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS ); + + if ( !tl_i2c_rx_ack(GPIOin, GPIOout) ) + return( FALSE ); + + // this loop will now read out the data and store it + // in the buffer pointed to by buf + for ( i=0; i> 3; + if (name == 0x0F) + done = TRUE; + } + else + { + name = z & 0x7F; + len = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8); + + switch (name) + { + case 0x0D: + // + j = i + 3; + // + if ( data_ptr[j] == 0x3b ) { + len = 6; + break; + } + + while ( j<(i+len) ) { + sub_name = (data_ptr[j] & 0x3f); + sub_len = data_ptr[j+1] + + (data_ptr[j+2] << 8); + ptr_inc = sub_len + 3; + switch (sub_name) + { + case 0x3C: + memcpy( wwnbuf, &data_ptr[j+3], 8); + iReturn = j+3; + break; + default: + break; + } + j += ptr_inc; + } + break; + default: + break; + } + } + // + i += len; + } // end while + return iReturn; +} + + + + + +// define a short 5 micro sec delay, and longer (ms) delay + +static void i2c_delay(ULONG mstime) +{ + ULONG i; + +// NOTE: we only expect to use these delays when reading +// our adapter's NVRAM, which happens only during adapter reset. +// Delay technique from "Linux Device Drivers", A. Rubini +// (1st Ed.) pg 137. + +// printk(" delay %lx ", mstime); + if( mstime ) // ms delay? + { + // delay technique + for( i=0; i < mstime; i++) + udelay(1000); // 1ms per loop + + } + else // 5 micro sec delay + + udelay( 5 ); // micro secs + +// printk("done\n"); +} + + + diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c new file mode 100644 index 000000000000..b51b515a639f --- /dev/null +++ b/drivers/scsi/cpqfcTSinit.c @@ -0,0 +1,1815 @@ +/* Copyright(c) 2000, Compaq Computer Corporation + * Fibre Channel Host Bus Adapter + * 64-bit, 66MHz PCI + * Originally developed and tested on: + * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... + * SP# P225CXCBFIEL6T, Rev XC + * SP# 161290-001, Rev XD + * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 + * + * 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, 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. + * Written by Don Zimmerman + * IOCTL and procfs added by Jouke Numan + * SMP testing by Chel Van Gennip + * + * portions copied from: + * QLogic CPQFCTS SCSI-FCP + * Written by Erik H. Moe, ehm@cris.com + * Copyright 1995, Erik H. Moe + * Renamed and updated to 1.3.x by Michael Griffith + * Chris Loveland to support the isp2100 and isp2200 +*/ + + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include // request_region() prototype +#include // ioremap() +#ifdef __alpha__ +#define __KERNEL_SYSCALLS__ +#endif +#include +#include +#include // ioctl related +#include +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +#include +#else +#include +#endif +#include "sd.h" +#include +#include "hosts.h" +#include "cpqfcTSchip.h" +#include "cpqfcTSstructs.h" + +#include "cpqfcTS.h" + +#include +/* Embedded module documentation macros - see module.h */ +MODULE_AUTHOR("Compaq Computer Corporation"); +MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA"); + +// This struct was originally defined in +// /usr/src/linux/include/linux/proc_fs.h +// since it's only partially implemented, we only use first +// few fields... +// NOTE: proc_fs changes in 2.4 kernel + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) +static struct proc_dir_entry proc_scsi_cpqfcTS = +{ + PROC_SCSI_CPQFCTS, // ushort low_ino (enumerated list) + 7, // ushort namelen + DEV_NAME, // const char* name + S_IFDIR | S_IRUGO | S_IXUGO, // mode_t mode + 2 // nlink_t nlink + // etc. ... +}; + + +#endif + + + +/* local function to load our per-HBA (local) data for chip + registers, FC link state, all FC exchanges, etc. + + We allocate space and compute address offsets for the + most frequently accessed addresses; others (like World Wide + Name) are not necessary. + +*/ +static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev ) +{ + + cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr + + // since x86 port space is 64k, we only need the lower 16 bits + cpqfcHBAdata->fcChip.Registers.IOBaseL = + PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + + cpqfcHBAdata->fcChip.Registers.IOBaseU = + PciDev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; + + // 32-bit memory addresses + cpqfcHBAdata->fcChip.Registers.MemBase = + PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK; + + cpqfcHBAdata->fcChip.Registers.ReMapMemBase = + ioremap( PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK, + 0x200); + + cpqfcHBAdata->fcChip.Registers.RAMBase = + PciDev->base_address[4]; + + cpqfcHBAdata->fcChip.Registers.SROMBase = // NULL for HP TS adapter + PciDev->base_address[5]; + + // now the Tachlite chip registers + // the REGISTER struct holds both the physical address & last + // written value (some TL registers are WRITE ONLY) + + cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_SFQ_CONSUMER_INDEX; + + cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX; + + // TL Frame Manager + cpqfcHBAdata->fcChip.Registers.FMconfig.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONFIG; + cpqfcHBAdata->fcChip.Registers.FMcontrol.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONTROL; + cpqfcHBAdata->fcChip.Registers.FMstatus.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_STATUS; + cpqfcHBAdata->fcChip.Registers.FMLinkStatus1.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT1; + cpqfcHBAdata->fcChip.Registers.FMLinkStatus2.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT2; + cpqfcHBAdata->fcChip.Registers.FMBB_CreditZero.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_BB_CREDIT0; + + // TL Control Regs + cpqfcHBAdata->fcChip.Registers.TYconfig.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONFIG; + cpqfcHBAdata->fcChip.Registers.TYcontrol.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONTROL; + cpqfcHBAdata->fcChip.Registers.TYstatus.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_STATUS; + cpqfcHBAdata->fcChip.Registers.rcv_al_pa.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_RCV_AL_PA; + cpqfcHBAdata->fcChip.Registers.ed_tov.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_ED_TOV; + + + cpqfcHBAdata->fcChip.Registers.INTEN.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTEN; + cpqfcHBAdata->fcChip.Registers.INTPEND.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTPEND; + cpqfcHBAdata->fcChip.Registers.INTSTAT.address = + cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTSTAT; + + DEBUG_PCI(printk(" cpqfcHBAdata->fcChip.Registers. :\n")); + DEBUG_PCI(printk(" IOBaseL = %x\n", + cpqfcHBAdata->fcChip.Registers.IOBaseL)); + DEBUG_PCI(printk(" IOBaseU = %x\n", + cpqfcHBAdata->fcChip.Registers.IOBaseU)); + + printk(" ioremap'd Membase: %p\n", cpqfcHBAdata->fcChip.Registers.ReMapMemBase); + + DEBUG_PCI(printk(" SFQconsumerIndex.address = %p\n", + cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address)); + DEBUG_PCI(printk(" ERQproducerIndex.address = %p\n", + cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address)); + DEBUG_PCI(printk(" TYconfig.address = %p\n", + cpqfcHBAdata->fcChip.Registers.TYconfig.address)); + DEBUG_PCI(printk(" FMconfig.address = %p\n", + cpqfcHBAdata->fcChip.Registers.FMconfig.address)); + DEBUG_PCI(printk(" FMcontrol.address = %p\n", + cpqfcHBAdata->fcChip.Registers.FMcontrol.address)); + + // set default options for FC controller (chip) + cpqfcHBAdata->fcChip.Options.initiator = 1; // default: SCSI initiator + cpqfcHBAdata->fcChip.Options.target = 0; // default: SCSI target + cpqfcHBAdata->fcChip.Options.extLoopback = 0;// default: no loopback @GBIC + cpqfcHBAdata->fcChip.Options.intLoopback = 0;// default: no loopback inside chip + + // set highest and lowest FC-PH version the adapter/driver supports + // (NOT strict compliance) + cpqfcHBAdata->fcChip.highest_FCPH_ver = FC_PH3; + cpqfcHBAdata->fcChip.lowest_FCPH_ver = FC_PH43; + + // set function points for this controller / adapter + cpqfcHBAdata->fcChip.ResetTachyon = CpqTsResetTachLite; + cpqfcHBAdata->fcChip.FreezeTachyon = CpqTsFreezeTachlite; + cpqfcHBAdata->fcChip.UnFreezeTachyon = CpqTsUnFreezeTachlite; + cpqfcHBAdata->fcChip.CreateTachyonQues = CpqTsCreateTachLiteQues; + cpqfcHBAdata->fcChip.DestroyTachyonQues = CpqTsDestroyTachLiteQues; + cpqfcHBAdata->fcChip.InitializeTachyon = CpqTsInitializeTachLite; + cpqfcHBAdata->fcChip.LaserControl = CpqTsLaserControl; + cpqfcHBAdata->fcChip.ProcessIMQEntry = CpqTsProcessIMQEntry; + cpqfcHBAdata->fcChip.InitializeFrameManager = CpqTsInitializeFrameManager;; + cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN; + cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM; + + + +} + + +/* (borrowed from linux/drivers/scsi/hosts.c) */ +static void launch_FCworker_thread(struct Scsi_Host *HostAdapter) +{ + DECLARE_MUTEX_LOCKED(sem); + + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + + ENTER("launch_FC_worker_thread"); + + cpqfcHBAdata->notify_wt = &sem; + + kernel_thread((int (*)(void *))cpqfcTSWorkerThread, + (void *) HostAdapter, 0); + /* + * Now wait for the kernel error thread to initialize itself + + */ + down (&sem); + cpqfcHBAdata->notify_wt = NULL; + + LEAVE("launch_FC_worker_thread"); + +} + + +/* "Entry" point to discover if any supported PCI + bus adapter can be found +*/ +// We're supporting: +// Compaq 64-bit, 66MHz HBA with Tachyon TS +// Agilent XL2 +#define HBA_TYPES 2 + + +int cpqfcTS_detect(Scsi_Host_Template *ScsiHostTemplate) +{ + int NumberOfAdapters=0; // how many of our PCI adapters are found? + struct pci_dev *PciDev = NULL; + struct Scsi_Host *HostAdapter = NULL; + CPQFCHBA *cpqfcHBAdata = NULL; + struct timer_list *cpqfcTStimer = NULL; + SupportedPCIcards PCIids[HBA_TYPES]; + int i; + + ENTER("cpqfcTS_detect"); + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) + ScsiHostTemplate->proc_dir = &proc_scsi_cpqfcTS; +#else + ScsiHostTemplate->proc_name = "cpqfcTS"; +#endif + + if( pci_present() == 0) // no PCI busses? + { + printk( " no PCI bus?@#!\n"); + return NumberOfAdapters; + } + + // what HBA adapters are we supporting? + PCIids[0].vendor_id = PCI_VENDOR_ID_COMPAQ; + PCIids[0].device_id = CPQ_DEVICE_ID; + PCIids[1].vendor_id = PCI_VENDOR_ID_HP; // i.e. 103Ch (Agilent == HP for now) + PCIids[1].device_id = AGILENT_XL2_ID; // i.e. 1029h + + for( i=0; i < HBA_TYPES; i++) + { + // look for all HBAs of each type + + while( (PciDev = + pci_find_device( PCIids[i].vendor_id, PCIids[i].device_id, PciDev) )) + { + // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes... + printk(" scsi_register allocating %d bytes for FC HBA\n", + (ULONG)sizeof(CPQFCHBA)); + + HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) ); + DEBUG_PCI( printk(" HBA found!\n")); + DEBUG_PCI( printk(" HostAdapter->PciDev->irq = %u\n", PciDev->irq) ); + DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[0])); + DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[1])); + DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[2])); + DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[3])); + + + HostAdapter->irq = PciDev->irq; // copy for Scsi layers + + // HP Tachlite uses two (255-byte) ranges of Port I/O (lower & upper), + // for a total I/O port address space of 512 bytes. + // mask out the I/O port address (lower) & record + HostAdapter->io_port = (unsigned int) + PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + HostAdapter->n_io_port = 0xff; + + // i.e., expect 128 targets (arbitrary number), while the + // RA-4000 supports 32 LUNs + HostAdapter->max_id = 0; // incremented as devices log in + HostAdapter->max_lun = CPQFCTS_MAX_LUN; // LUNs per FC device + HostAdapter->max_channel = CPQFCTS_MAX_CHANNEL; // multiple busses? + HostAdapter->hostt->use_new_eh_code = 1; // new error handling + + // get the pointer to our HBA specific data... (one for + // each HBA on the PCI bus(ses)). + cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + + // make certain our data struct is clear + memset( cpqfcHBAdata, 0, sizeof( CPQFCHBA ) ); + + + // initialize our HBA info + cpqfcHBAdata->HBAnum = NumberOfAdapters; + + cpqfcHBAdata->HostAdapter = HostAdapter; // back ptr + Cpqfc_initHBAdata( cpqfcHBAdata, PciDev ); // fill MOST fields + + cpqfcHBAdata->HBAnum = NumberOfAdapters; + + + // request necessary resources and check for conflicts + if( request_irq( HostAdapter->irq, + cpqfcTS_intr_handler, + SA_INTERRUPT | SA_SHIRQ, + DEV_NAME, + HostAdapter) ) + { + printk(" IRQ %u already used\n", HostAdapter->irq); + scsi_unregister( HostAdapter); + continue; + } + + // Since we have two 256-byte I/O port ranges (upper + // and lower), check them both + if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff) ) + { + printk(" cpqfcTS address in use: %x\n", + cpqfcHBAdata->fcChip.Registers.IOBaseU); + free_irq( HostAdapter->irq, HostAdapter); + scsi_unregister( HostAdapter); + continue; + } + + if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff) ) + { + printk(" cpqfcTS address in use: %x\n", + cpqfcHBAdata->fcChip.Registers.IOBaseL); + free_irq( HostAdapter->irq, HostAdapter); + scsi_unregister( HostAdapter); + continue; + } + + // OK, we should be able to grab everything we need now. + request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff, DEV_NAME); + request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff, DEV_NAME); + DEBUG_PCI(printk(" Requesting 255 I/O addresses @ %x\n", + cpqfcHBAdata->fcChip.Registers.IOBaseL )); + DEBUG_PCI(printk(" Requesting 255 I/O addresses @ %x\n", + cpqfcHBAdata->fcChip.Registers.IOBaseU )); + + + // start our kernel worker thread + + launch_FCworker_thread(HostAdapter); + + + // start our TimerTask... + + cpqfcTStimer = &cpqfcHBAdata->cpqfcTStimer; + + init_timer( cpqfcTStimer); // Linux clears next/prev values + cpqfcTStimer->expires = jiffies + HZ; // one second + cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter + cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping + + add_timer( cpqfcTStimer); // give it to Linux + + + // now initialize our hardware... + + cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1); + + cpqfcHBAdata->fcStatsTime = jiffies; // (for FC Statistics delta) + + // give our HBA time to initialize and login current devices... + { + // The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000, + // has the following algorithm for FL_Port startup: + // Time(sec) Action + // 0: Device Plugin and LIP(F7,F7) transmission + // 1.0 LIP incoming + // 1.027 LISA incoming, no CLS! (link not up) + // 1.028 NOS incoming (switch test for N_Port) + // 1.577 ED_TOV expired, transmit LIPs again + // 3.0 LIP(F8,F7) incoming (switch passes Tach Prim.Sig) + // 3.028 LILP received, link up, FLOGI starts + // slowest(worst) case, measured on 1Gb Finisar GT analyzer + + int wait_time; + for( wait_time = jiffies + 4*HZ; wait_time > jiffies; ) + schedule(); // (our worker task needs to run) + + } + + NumberOfAdapters++; + } // end of while() + } + + LEAVE("cpqfcTS_detect"); + + return NumberOfAdapters; +} + + +static void my_ioctl_done (Scsi_Cmnd * SCpnt) +{ + struct request * req; + + req = &SCpnt->request; + req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ + + if (req->sem != NULL) { + up(req->sem); + } +} + + + +int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) +{ + int result = 0; + struct Scsi_Host *HostAdapter = ScsiDev->host; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + PFC_LOGGEDIN_PORT pLoggedInPort; + Scsi_Cmnd DumCmnd; + int i, j; + VENDOR_IOCTL_REQ ioc; + cpqfc_passthru_t *vendor_cmd; + Scsi_Device *SDpnt; + Scsi_Cmnd *ScsiPassThruCmnd; + unsigned long flags; + + ENTER("cpqfcTS_ioctl"); + + // can we find an FC device mapping to this SCSI target? + DumCmnd.channel = ScsiDev->channel; // For searching + DumCmnd.target = ScsiDev->id; + pLoggedInPort = fcFindLoggedInPort( fcChip, + &DumCmnd, // search Scsi Nexus + 0, // DON'T search linked list for FC port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + if( pLoggedInPort == NULL ) // not found! + { + result = -ENXIO; + } + + else // we know what FC device to operate on... + { + switch (Cmnd) + { + // Passthrough provides a mechanism to bypass the RAID + // or other controller and talk directly to the devices + // (e.g. physical disk drive) + // Passthrough commands, unfortunately, tend to be vendor + // specific; this is tailored to COMPAQ's RAID (RA4x00) + case CPQFCTS_SCSI_PASSTHRU: + { + void *buf = NULL; // for kernel space buffer for user data + + if( !arg) + return -EINVAL; + + // must be super user to send stuff directly to the + // controller and/or physical drives... + if( !suser() ) + return -EPERM; + + // copy the caller's struct to our space. + copy_from_user_ret( &ioc, arg, + sizeof( VENDOR_IOCTL_REQ), -EFAULT); + + vendor_cmd = ioc.argp; // i.e., CPQ specific command struct + + // If necessary, grab a kernel/DMA buffer + if( vendor_cmd->len) + { + buf = kmalloc( vendor_cmd->len, GFP_KERNEL); + if( !buf) + return -ENOMEM; + } + + // Now build a SCSI_CMND to pass down... + // This function allocates and sets Scsi_Cmnd ptrs such as + // ->channel, ->target, ->host + ScsiPassThruCmnd = scsi_allocate_device(NULL, ScsiDev, 1); + + // Need data from user? + // make sure caller's buffer is in kernel space. + if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) && + vendor_cmd->len) + copy_from_user_ret( buf, vendor_cmd->bufp, vendor_cmd->len, -EFAULT); + + // copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below) + memcpy( &ScsiPassThruCmnd->cmnd[0], + &vendor_cmd->cdb[0], + MAX_COMMAND_SIZE); + // we want to copy all 16 bytes into the FCP-SCSI CDB, + // although the actual passthru only uses up to the + // first 12. + + ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB + + // Unfortunately, the SCSI command cmnd[] field has only + // 12 bytes. Ideally the MAX_COMMAND_SIZE should be increased + // to 16 for newer Fibre Channel and SCSI-3 larger CDBs. + // However, to avoid a mandatory kernel rebuild, we use the SCp + // spare field to store the extra 4 bytes ( ugly :-( + + if( MAX_COMMAND_SIZE < 16) + { + memcpy( &ScsiPassThruCmnd->SCp.buffers_residual, + &vendor_cmd->cdb[12], 4); + } + + + ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU! + // suppress LUN masking + // and VSA logic + + // Use spare fields to copy FCP-SCSI LUN address info... + ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus; + ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive; + + + + // We copy the scheme used by scsi.c to submit commands + // to our own HBA. We do this in order to stall the + // thread calling the IOCTL until it completes, and use + // the same "_quecommand" function for synchronizing + // FC Link events with our "worker thread". + + spin_lock_irqsave(&io_request_lock, flags); + { + DECLARE_MUTEX_LOCKED(sem); + ScsiPassThruCmnd->request.sem = &sem; + // eventually gets us to our own _quecommand routine + scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0], + buf, + vendor_cmd->len, + my_ioctl_done, + 10*HZ, 1);// timeout,retries + spin_unlock_irqrestore(&io_request_lock, flags); + // Other I/Os can now resume; we wait for our ioctl + // command to complete + down(&sem); + spin_lock_irqsave(&io_request_lock, flags); + ScsiPassThruCmnd->request.sem = NULL; + } + + result = ScsiPassThruCmnd->result; + + // copy any sense data back to caller + if( result != 0 ) + { + memcpy( vendor_cmd->sense_data, // see struct def - size=40 + ScsiPassThruCmnd->sense_buffer, + sizeof(ScsiPassThruCmnd->sense_buffer)); + } + SDpnt = ScsiPassThruCmnd->device; + scsi_release_command(ScsiPassThruCmnd); // "de-allocate" + ScsiPassThruCmnd = NULL; + + if (!SDpnt->was_reset && SDpnt->scsi_request_fn) + (*SDpnt->scsi_request_fn)(); + + wake_up(&SDpnt->device_wait); + spin_unlock_irqrestore(&io_request_lock, flags); + + // need to pass data back to user (space)? + if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) && + vendor_cmd->len ) + copy_to_user_ret( vendor_cmd->bufp, buf, vendor_cmd->len, -EFAULT); + + if( buf) + kfree( buf); + + return result; + } + + case CPQFCTS_GETPCIINFO: + { + cpqfc_pci_info_struct pciinfo; + + if( !arg) + return -EINVAL; + + + + pciinfo.bus = cpqfcHBAdata->PciDev->bus->number; + pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn; + pciinfo.board_id = cpqfcHBAdata->PciDev->device | + (cpqfcHBAdata->PciDev->vendor <<16); + + copy_to_user_ret( arg, &pciinfo, + sizeof(cpqfc_pci_info_struct), -EFAULT); + return 0; + } + + case CPQFCTS_GETDRIVVER: + { + DriverVer_type DriverVer = + CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR); + + if( !arg) + return -EINVAL; + + copy_to_user_ret( arg, &DriverVer, + sizeof(DriverVer), -EFAULT); + return 0; + } + + + + case SCSI_IOCTL_FC_TARGET_ADDRESS: + result = + verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)); + if (result) + break; + + put_user(pLoggedInPort->port_id, + &((Scsi_FCTargAddress *) arg)->host_port_id); + + for( i=3,j=0; i>=0; i--) // copy the LOGIN port's WWN + put_user(pLoggedInPort->u.ucWWN[i], + &((Scsi_FCTargAddress *) arg)->host_wwn[j++]); + for( i=7; i>3; i--) // copy the LOGIN port's WWN + put_user(pLoggedInPort->u.ucWWN[i], + &((Scsi_FCTargAddress *) arg)->host_wwn[j++]); + break; + default: + result = -EINVAL; + break; + } + } + + LEAVE("cpqfcTS_ioctl"); + return result; +} + + +/* "Release" the Host Bus Adapter... + disable interrupts, stop the HBA, release the interrupt, + and free all resources */ + +int cpqfcTS_release(struct Scsi_Host *HostAdapter) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + + + ENTER("cpqfcTS_release"); + + DEBUG_PCI( printk(" cpqfcTS: delete timer...\n")); + del_timer( &cpqfcHBAdata->cpqfcTStimer); + + // disable the hardware... + DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n")); + cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS); + + // kill kernel thread + if( cpqfcHBAdata->worker_thread ) // (only if exists) + { + DECLARE_MUTEX_LOCKED(sem); // synchronize thread kill + + cpqfcHBAdata->notify_wt = &sem; + DEBUG_PCI( printk(" killing kernel thread\n")); + send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1); + down( &sem); + cpqfcHBAdata->notify_wt = NULL; + + } + + // free Linux resources + DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n")); + free_irq( HostAdapter->irq, HostAdapter); + scsi_unregister( HostAdapter); + release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff); + release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff); + /* we get "vfree: bad address" executing this - need to investigate... + if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) != + cpqfcHBAdata->fcChip.Registers.ReMapMemBase) + vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase); +*/ + + LEAVE("cpqfcTS_release"); + return 0; +} + + +const char * cpqfcTS_info(struct Scsi_Host *HostAdapter) +{ + static char buf[300]; + CPQFCHBA *cpqfcHBA; + int BusSpeed, BusWidth; + + // get the pointer to our Scsi layer HBA buffer + cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata; + + BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ? + 64 : 32; + + if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000) + BusSpeed = 66; + else + BusSpeed = 33; + + sprintf(buf, +"%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d", + cpqfcHBA->fcChip.Name, + cpqfcHBA->fcChip.Registers.wwn_hi, + cpqfcHBA->fcChip.Registers.wwn_lo, + cpqfcHBA->PciDev->bus->number, + cpqfcHBA->PciDev->device, + HostAdapter->irq, + cpqfcHBA->fcChip.Registers.IOBaseL, + cpqfcHBA->fcChip.Registers.MemBase, + BusWidth, + BusSpeed, + VER_MAJOR, VER_MINOR, VER_SUBMINOR +); + + + cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); + cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); + return buf; +} + +// +// /proc/scsi support. The following routines allow us to do 'normal' +// sprintf like calls to return the currently requested piece (buflenght +// chars, starting at bufoffset) of the file. Although procfs allows for +// a 1 Kb bytes overflow after te supplied buffer, I consider it bad +// programming to use it to make programming a little simpler. This piece +// of coding is borrowed from ncr53c8xx.c with some modifications +// +struct info_str +{ + char *buffer; // Pointer to output buffer + int buflength; // It's length + int bufoffset; // File offset corresponding with buf[0] + int buffillen; // Current filled length + int filpos; // Current file offset +}; + +static void copy_mem_info(struct info_str *info, char *data, int datalen) +{ + + if (info->filpos < info->bufoffset) { // Current offset before buffer offset + if (info->filpos + datalen <= info->bufoffset) { + info->filpos += datalen; // Discard if completely before buffer + return; + } else { // Partial copy, set to begin + data += (info->bufoffset - info->filpos); + datalen -= (info->bufoffset - info->filpos); + info->filpos = info->bufoffset; + } + } + + info->filpos += datalen; // Update current offset + + if (info->buffillen == info->buflength) // Buffer full, discard + return; + + if (info->buflength - info->buffillen < datalen) // Overflows buffer ? + datalen = info->buflength - info->buffillen; + + memcpy(info->buffer + info->buffillen, data, datalen); + info->buffillen += datalen; +} + +static int copy_info(struct info_str *info, char *fmt, ...) +{ + va_list args; + char buf[400]; + int len; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); + + copy_mem_info(info, buf, len); + return len; +} + + +// Routine to get data for /proc RAM filesystem +// +int cpqfcTS_proc_info (char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + struct Scsi_Host *host; + Scsi_Cmnd DumCmnd; + int Chan, Targ, i; + struct info_str info; + CPQFCHBA *cpqfcHBA; + PTACHYON fcChip; + PFC_LOGGEDIN_PORT pLoggedInPort; + char buf[81]; + + // Search the Scsi host list for our controller + for (host=scsi_hostlist; host; host=host->next) + if (host->host_no == hostno) + break; + + if (!host) return -ESRCH; + + if (inout) return -EINVAL; + + // get the pointer to our Scsi layer HBA buffer + cpqfcHBA = (CPQFCHBA *)host->hostdata; + fcChip = &cpqfcHBA->fcChip; + + *start = buffer; + + info.buffer = buffer; + info.buflength = length; + info.bufoffset = offset; + info.filpos = 0; + info.buffillen = 0; + copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR); + cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]); + cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); + copy_info(&info, "%s\n", buf); + + +#define DISPLAY_WWN_INFO +#ifdef DISPLAY_WWN_INFO + copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n"); + for ( Chan=0; Chan <= host->max_channel; Chan++) { + DumCmnd.channel = Chan; + for (Targ=0; Targ <= host->max_id; Targ++) { + DumCmnd.target = Targ; + if ((pLoggedInPort = fcFindLoggedInPort( fcChip, + &DumCmnd, // search Scsi Nexus + 0, // DON'T search list for FC port id + NULL, // DON'T search list for FC WWN + NULL))){ // DON'T care about end of list + copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ", + hostno, Chan, Targ); + for( i=3; i>=0; i--) // copy the LOGIN port's WWN + copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]); + for( i=7; i>3; i--) // copy the LOGIN port's WWN + copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]); + copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id); + } + } + } +#endif + + +// Unfortunately, the proc_info buffer isn't big enough +// for everything we would like... +// For FC stats, compile this and turn off WWN stuff above +//#define DISPLAY_FC_STATS +#ifdef DISPLAY_FC_STATS +// get the Fibre Channel statistics + { + int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ; + int days,hours,minutes,secs; + + days = DeltaSecs / (3600*24); // days + hours = (DeltaSecs% (3600*24)) / 3600; // hours + minutes = (DeltaSecs%3600 /60); // minutes + secs = DeltaSecs%60; // secs +copy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n", + days, hours, minutes, secs); + } + + cpqfcHBA->fcStatsTime = jiffies; // (for next delta) + + copy_info( &info, " LinkUp %9u LinkDown %u\n", + fcChip->fcStats.linkUp, fcChip->fcStats.linkDown); + + copy_info( &info, " Loss of Signal %9u Loss of Sync %u\n", + fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync); + + copy_info( &info, " Discarded Frames %9u Bad CRC Frame %u\n", + fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC); + + copy_info( &info, " TACH LinkFailTX %9u TACH LinkFailRX %u\n", + fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX); + + copy_info( &info, " TACH RxEOFa %9u TACH Elastic Store %u\n", + fcChip->fcStats.Rx_EOFa, fcChip->fcStats.e_stores); + + copy_info( &info, " BufferCreditWait %9uus TACH FM Inits %u\n", + fcChip->fcStats.BB0_Timer*10, fcChip->fcStats.FMinits ); + + copy_info( &info, " FC-2 Timeouts %9u FC-2 Logouts %u\n", + fcChip->fcStats.timeouts, fcChip->fcStats.logouts); + + copy_info( &info, " FC-2 Aborts %9u FC-4 Aborts %u\n", + fcChip->fcStats.FC2aborted, fcChip->fcStats.FC4aborted); + + // clear the counters + cpqfcTSClearLinkStatusCounters( fcChip); +#endif + + return info.buffillen; +} + + +#if DEBUG_CMND + +UCHAR *ScsiToAscii( UCHAR ScsiCommand) +{ + +/*++ + +Routine Description: + + Converts a SCSI command to a text string for debugging purposes. + + +Arguments: + + ScsiCommand -- hex value SCSI Command + + +Return Value: + + An ASCII, null-terminated string if found, else returns NULL. + +Original code from M. McGowen, Compaq +--*/ + + + switch (ScsiCommand) + { + case 0x00: + return( "Test Unit Ready" ); + + case 0x01: + return( "Rezero Unit or Rewind" ); + + case 0x02: + return( "Request Block Address" ); + + case 0x03: + return( "Requese Sense" ); + + case 0x04: + return( "Format Unit" ); + + case 0x05: + return( "Read Block Limits" ); + + case 0x07: + return( "Reassign Blocks" ); + + case 0x08: + return( "Read (6)" ); + + case 0x0a: + return( "Write (6)" ); + + case 0x0b: + return( "Seek (6)" ); + + case 0x12: + return( "Inquiry" ); + + case 0x15: + return( "Mode Select (6)" ); + + case 0x16: + return( "Reserve" ); + + case 0x17: + return( "Release" ); + + case 0x1a: + return( "ModeSen(6)" ); + + case 0x1b: + return( "Start/Stop Unit" ); + + case 0x1c: + return( "Receive Diagnostic Results" ); + + case 0x1d: + return( "Send Diagnostic" ); + + case 0x25: + return( "Read Capacity" ); + + case 0x28: + return( "Read (10)" ); + + case 0x2a: + return( "Write (10)" ); + + case 0x2b: + return( "Seek (10)" ); + + case 0x2e: + return( "Write and Verify" ); + + case 0x2f: + return( "Verify" ); + + case 0x34: + return( "Pre-Fetch" ); + + case 0x35: + return( "Synchronize Cache" ); + + case 0x37: + return( "Read Defect Data (10)" ); + + case 0x3b: + return( "Write Buffer" ); + + case 0x3c: + return( "Read Buffer" ); + + case 0x3e: + return( "Read Long" ); + + case 0x3f: + return( "Write Long" ); + + case 0x41: + return( "Write Same" ); + + case 0x4c: + return( "Log Select" ); + + case 0x4d: + return( "Log Sense" ); + + case 0x56: + return( "Reserve (10)" ); + + case 0x57: + return( "Release (10)" ); + + case 0xa0: + return( "ReportLuns" ); + + case 0xb7: + return( "Read Defect Data (12)" ); + + case 0xca: + return( "Peripheral Device Addressing SCSI Passthrough" ); + + case 0xcb: + return( "Compaq Array Firmware Passthrough" ); + + default: + return( NULL ); + } + +} // end ScsiToAscii() + +void cpqfcTS_print_scsi_cmd(Scsi_Cmnd * cmd) +{ + +printk("cpqfcTS: (%s) chnl 0x%02x, trgt = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n", + ScsiToAscii( cmd->cmnd[0]), cmd->channel, cmd->target, cmd->lun, cmd->cmd_len); + +if( cmd->cmnd[0] == 0) // Test Unit Ready? +{ + int i; + + printk("Cmnd->request_bufflen = 0x%X, ->use_sg = %d, ->bufflen = %d\n", + cmd->request_bufflen, cmd->use_sg, cmd->bufflen); + printk("Cmnd->request_buffer = %p, ->sglist_len = %d, ->buffer = %p\n", + cmd->request_buffer, cmd->sglist_len, cmd->buffer); + for (i = 0; i < cmd->cmd_len; i++) + printk("0x%02x ", cmd->cmnd[i]); + printk("\n"); +} + +} + +#endif /* DEBUG_CMND */ + + + + +static void QueCmndOnBoardLock( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd) +{ + int i; + + for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++) + { // find spare slot + if( cpqfcHBAdata->BoardLockCmnd[i] == NULL ) + { + cpqfcHBAdata->BoardLockCmnd[i] = Cmnd; +// printk(" BoardLockCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n", +// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); + break; + } + } + if( i >= CPQFCTS_REQ_QUEUE_LEN) + { + printk(" cpqfcTS WARNING: Lost Cmnd %p on BoardLock Q full!", Cmnd); + } + +} + + +static void QueLinkDownCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd) +{ + int indx; + + // Remember the command ptr so we can return; we'll complete when + // the device comes back, causing immediate retry + for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++) + { + if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available? + { +#ifdef DUMMYCMND_DBG + printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx); +#endif + cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd; + break; + } + } + + if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd?? + { + // this will result in an _abort call later (with possible trouble) + printk("no buffer for LinkDnCmnd!! %p\n", Cmnd); + } +} + + + + + +// The file "hosts.h" says not to call scsi_done from +// inside _queuecommand, so we'll do it from the heartbeat timer + +static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd) +{ + int i; + // printk(" can't find target %d\n", Cmnd->target); + + for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) + { // find spare slot + if( cpqfcHBAdata->BadTargetCmnd[i] == NULL ) + { + cpqfcHBAdata->BadTargetCmnd[i] = Cmnd; +// printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n", +// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); + break; + } + } +} + + +// This is the "main" entry point for Linux Scsi commands -- +// it all starts here. + +int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *)) +{ + struct Scsi_Host *HostAdapter = Cmnd->host; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + TachFCHDR_GCMND fchs; // only use for FC destination id field + PFC_LOGGEDIN_PORT pLoggedInPort; + ULONG ulStatus, SESTtype; + LONG ExchangeID; + + + + + ENTER("cpqfcTS_queuecommand"); + + PCI_TRACEO( (ULONG)Cmnd, 0x98) + + + Cmnd->scsi_done = done; +#ifdef DEBUG_CMND + cpqfcTS_print_scsi_cmd( Cmnd); +#endif + + // prevent board contention with kernel thread... + + if( cpqfcHBAdata->BoardLock ) + { +// printk(" @BrdLck Hld@ "); + QueCmndOnBoardLock( cpqfcHBAdata, Cmnd); + } + + else + { + + // in the current system (2.2.12), this routine is called + // after spin_lock_irqsave(), so INTs are disabled. However, + // we might have something pending in the LinkQ, which + // might cause the WorkerTask to run. In case that + // happens, make sure we lock it out. + + + + PCI_TRACE( 0x98) + CPQ_SPINLOCK_HBA( cpqfcHBAdata) + PCI_TRACE( 0x98) + + // can we find an FC device mapping to this SCSI target? + pLoggedInPort = fcFindLoggedInPort( fcChip, + Cmnd, // search Scsi Nexus + 0, // DON'T search linked list for FC port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + if( pLoggedInPort == NULL ) // not found! + { +// printk(" @Q bad targ cmnd %p@ ", Cmnd); + QueBadTargetCmnd( cpqfcHBAdata, Cmnd); + } + + else // we know what FC device to send to... + { + + // does this device support FCP target functions? + // (determined by PRLI field) + + if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) ) + { + printk(" Doesn't support TARGET functions port_id %Xh\n", + pLoggedInPort->port_id ); + QueBadTargetCmnd( cpqfcHBAdata, Cmnd); + } + + // In this case (previous login OK), the device is temporarily + // unavailable waiting for re-login, in which case we expect it + // to be back in between 25 - 500ms. + // If the FC port doesn't log back in within several seconds + // (i.e. implicit "logout"), or we get an explicit logout, + // we set "device_blocked" in Scsi_Device struct; in this + // case 30 seconds will elapse before Linux/Scsi sends another + // command to the device. + else if( pLoggedInPort->prli != TRUE ) + { +// printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n", +// Cmnd->channel, Cmnd->target, pLoggedInPort->port_id); + QueLinkDownCmnd( cpqfcHBAdata, Cmnd); +// Need to use "blocked" flag?? +// Cmnd->device->device_blocked = TRUE; // just let it timeout + } + else // device supports TARGET functions, and is logged in... + { + // (context of fchs is to "reply" to...) + fchs.s_id = pLoggedInPort->port_id; // destination FC address + + // what is the data direction? For data TO the device, + // we need IWE (Intiator Write Entry). Otherwise, IRE. + + if( Cmnd->cmnd[0] == WRITE_10 || + Cmnd->cmnd[0] == WRITE_6 || + Cmnd->cmnd[0] == WRITE_BUFFER || + Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE || // CPQ specific + Cmnd->cmnd[0] == MODE_SELECT ) + { + SESTtype = SCSI_IWE; // data from HBA to Device + } + else + SESTtype = SCSI_IRE; // data from Device to HBA + + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + SESTtype, // e.g. Initiator Read Entry (IRE) + &fchs, // we are originator; only use d_id + Cmnd, // Linux SCSI command (with scatter/gather list) + &ExchangeID );// fcController->fcExchanges index, -1 if failed + + if( !ulStatus ) // Exchange setup? + + { + if( cpqfcHBAdata->BoardLock ) + { + TriggerHBA( fcChip->Registers.ReMapMemBase, 0); + printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID); + } + + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); + if( !ulStatus ) + { + PCI_TRACEO( ExchangeID, 0xB8) + // submitted to Tach's Outbound Que (ERQ PI incremented) + // waited for completion for ELS type (Login frames issued + // synchronously) + } + else + // check reason for Exchange not being started - we might + // want to Queue and start later, or fail with error + { + printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus ); + } + } // end good BuildExchange status + + else // SEST table probably full -- why? hardware hang? + { + printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus); + } + } // end can't do FCP-SCSI target functions + } // end can't find target (FC device) + + CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) + } + + PCI_TRACEO( (ULONG)Cmnd, 0x9C) + LEAVE("cpqfcTS_queuecommand"); + return 0; +} + + +// Entry point for upper Scsi layer intiated abort. Typically +// this is called if the command (for hard disk) fails to complete +// in 30 seconds. This driver intends to complete all disk commands +// within Exchange ".timeOut" seconds (now 7) with target status, or +// in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes +// immediate retry. +// If any disk commands get the _abort call, except for the case that +// the physical device was removed or unavailable due to hardware +// errors, it should be considered a driver error and reported to +// the author. + +int cpqfcTS_abort(Scsi_Cmnd *Cmnd) +{ + struct Scsi_Host *HostAdapter = Cmnd->host; + // get the pointer to our Scsi layer HBA buffer + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + int i; + ENTER("cpqfcTS_abort"); + + Cmnd->result = DID_ABORT <<16; // assume we'll find it + + printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd); + // See if we can find a Cmnd pointer that matches... + // The most likely case is we accepted the command + // from Linux Scsi (e.g. ceated a SEST entry) and it + // got lost somehow. If we can't find any reference + // to the passed pointer, we can only presume it + // got completed as far as our driver is concerned. + // If we found it, we will try to abort it through + // common mechanism. If FC ABTS is successful (ACC) + // or is rejected (RJT) by target, we will call + // Scsi "done" quickly. Otherwise, the ABTS will timeout + // and we'll call "done" later. + + // Search the SEST exchanges for a matching Cmnd ptr. + for( i=0; i< TACH_SEST_LEN; i++) + { + if( Exchanges->fcExchange[i].Cmnd == Cmnd ) + { + + // found it! + printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type); + + Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default + Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later) + + // Since we need to immediately return the aborted Cmnd to Scsi + // upper layers, we can't make future reference to any of it's + // fields (e.g the Nexus). + + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i); + + break; + } + } + + if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST? + { + // now search our non-SEST buffers (i.e. Cmnd waiting to + // start on the HBA or waiting to complete with error for retry). + + // first check BadTargetCmnd + for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) + { + if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd ) + { + cpqfcHBAdata->BadTargetCmnd[i] = NULL; + printk("in BadTargetCmnd Q\n"); + goto Done; // exit + } + } + + // if not found above... + + for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++) + { + if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd ) + { + cpqfcHBAdata->LinkDnCmnd[i] = NULL; + printk("in LinkDnCmnd Q\n"); + goto Done; + } + } + + + for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++) + { // find spare slot + if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd ) + { + cpqfcHBAdata->BoardLockCmnd[i] = NULL; + printk("in BoardLockCmnd Q\n"); + goto Done; + } + } + + Cmnd->result = DID_ERROR <<16; // Hmmm... + printk("Not found! "); +// panic("_abort"); + } + +Done: + +// panic("_abort"); + LEAVE("cpqfcTS_abort"); + return 0; // (see scsi.h) +} + + + + +// To be done... +int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags) +{ + int return_status = SUCCESS; + + ENTER("cpqfcTS_reset"); + + + + + LEAVE("cpqfcTS_reset"); + return return_status; +} + + + +/* This function determines the bios parameters for a given + harddisk. These tend to be numbers that are made up by the + host adapter. Parameters: + size, device number, list (heads, sectors,cylinders). + (from hosts.h) +*/ + +int cpqfcTS_biosparam(Disk *disk, kdev_t n, int ip[]) +{ + int size = disk->capacity; + + ENTER("cpqfcTS_biosparam"); + ip[0] = 64; + ip[1] = 32; + ip[2] = size >> 11; + + if( ip[2] > 1024 ) + { + ip[0] = 255; + ip[1] = 63; + ip[2] = size / (ip[0] * ip[1]); + } + + LEAVE("cpqfcTS_biosparam"); + return 0; +} + + + +void cpqfcTS_intr_handler( int irq, + void *dev_id, + struct pt_regs *regs) +{ + + unsigned long flags, InfLoopBrk=0; + struct Scsi_Host *HostAdapter = dev_id; + CPQFCHBA *cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata; + int MoreMessages = 1; // assume we have something to do + UCHAR IntPending; + + ENTER("intr_handler"); + + spin_lock_irqsave( &io_request_lock, flags); + // is this our INT? + IntPending = readb( cpqfcHBA->fcChip.Registers.INTPEND.address); + + // broken boards can generate messages forever, so + // prevent the infinite loop +#define INFINITE_IMQ_BREAK 10000 + if( IntPending ) + { + + // mask our HBA interrupts until we handle it... + writeb( 0, cpqfcHBA->fcChip.Registers.INTEN.address); + + if( IntPending & 0x4) // "INT" - Tach wrote to IMQ + { + while( (++InfLoopBrk < INFINITE_IMQ_BREAK) && (MoreMessages ==1) ) + { + MoreMessages = CpqTsProcessIMQEntry( HostAdapter); // ret 0 when done + } + if( InfLoopBrk >= INFINITE_IMQ_BREAK ) + { + printk("WARNING: Compaq FC adapter generating excessive INTs -REPLACE\n"); + printk("or investigate alternate causes (e.g. physical FC layer)\n"); + } + + else // working normally - re-enable INTs and continue + writeb( 0x1F, cpqfcHBA->fcChip.Registers.INTEN.address); + + } // (...ProcessIMQEntry() clears INT by writing IMQ consumer) + else // indications of errors or problems... + // these usually indicate critical system hardware problems. + { + if( IntPending & 0x10 ) + printk(" cpqfcTS adapter external memory parity error detected\n"); + if( IntPending & 0x8 ) + printk(" cpqfcTS adapter PCI master address crossed 45-bit boundary\n"); + if( IntPending & 0x2 ) + printk(" cpqfcTS adapter DMA error detected\n"); + if( IntPending & 0x1 ) + printk(" cpqfcTS adapter PCI error detected\n"); + } + } + spin_unlock_irqrestore( &io_request_lock, flags); + LEAVE("intr_handler"); +} + + + + +int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]) +{ + // Verify GBIC type (if any) and correct Tachyon Port State Machine + // (GBIC) module definition is: + // GPIO1, GPIO0, GPIO4 for MD2, MD1, MD0. The input states appear + // to be inverted -- i.e., a setting of 111 is read when there is NO + // GBIC present. The Module Def (MD) spec says 000 is "no GBIC" + // Hard code the bit states to detect Copper, + // Long wave (single mode), Short wave (multi-mode), and absent GBIC + + ULONG ulBuff; + + sprintf( cErrorString, "\nGBIC detected: "); + + ulBuff = fcChip->Registers.TYstatus.value & 0x13; + switch( ulBuff ) + { + case 0x13: // GPIO4, GPIO1, GPIO0 = 111; no GBIC! + sprintf( &cErrorString[ strlen( cErrorString)], + "NONE! "); + return FALSE; + + + case 0x11: // Copper GBIC detected + sprintf( &cErrorString[ strlen( cErrorString)], + "Copper. "); + break; + + case 0x10: // Long-wave (single mode) GBIC detected + sprintf( &cErrorString[ strlen( cErrorString)], + "Long-wave. "); + break; + case 0x1: // Short-wave (multi mode) GBIC detected + sprintf( &cErrorString[ strlen( cErrorString)], + "Short-wave. "); + break; + default: // unknown GBIC - presumably it will work (?) + sprintf( &cErrorString[ strlen( cErrorString)], + "Unknown. "); + + break; + } // end switch GBIC detection + + return TRUE; +} + + + + + + +int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]) +{ + // Tachyon's Frame Manager LPSM in LinkDown state? + // (For non-loop port, check PSM instead.) + // return string with state and FALSE is Link Down + + int LinkUp; + + if( fcChip->Registers.FMstatus.value & 0x80 ) + LinkUp = FALSE; + else + LinkUp = TRUE; + + sprintf( &cErrorString[ strlen( cErrorString)], + " LPSM %Xh ", + (fcChip->Registers.FMstatus.value >>4) & 0xf ); + + + switch( fcChip->Registers.FMstatus.value & 0xF0) + { + // bits set in LPSM + case 0x10: + sprintf( &cErrorString[ strlen( cErrorString)], "ARB"); + break; + case 0x20: + sprintf( &cErrorString[ strlen( cErrorString)], "ARBwon"); + break; + case 0x30: + sprintf( &cErrorString[ strlen( cErrorString)], "OPEN"); + break; + case 0x40: + sprintf( &cErrorString[ strlen( cErrorString)], "OPENed"); + break; + case 0x50: + sprintf( &cErrorString[ strlen( cErrorString)], "XmitCLS"); + break; + case 0x60: + sprintf( &cErrorString[ strlen( cErrorString)], "RxCLS"); + break; + case 0x70: + sprintf( &cErrorString[ strlen( cErrorString)], "Xfer"); + break; + case 0x80: + sprintf( &cErrorString[ strlen( cErrorString)], "Init"); + break; + case 0x90: + sprintf( &cErrorString[ strlen( cErrorString)], "O-IInitFin"); + break; + case 0xa0: + sprintf( &cErrorString[ strlen( cErrorString)], "O-IProtocol"); + break; + case 0xb0: + sprintf( &cErrorString[ strlen( cErrorString)], "O-ILipRcvd"); + break; + case 0xc0: + sprintf( &cErrorString[ strlen( cErrorString)], "HostControl"); + break; + case 0xd0: + sprintf( &cErrorString[ strlen( cErrorString)], "LoopFail"); + break; + case 0xe0: + sprintf( &cErrorString[ strlen( cErrorString)], "Offline"); + break; + case 0xf0: + sprintf( &cErrorString[ strlen( cErrorString)], "OldPort"); + break; + case 0: + default: + sprintf( &cErrorString[ strlen( cErrorString)], "Monitor"); + break; + + } + + return LinkUp; +} + + + + +#include "linux/malloc.h" + +// Dynamic memory allocation alignment routines +// HP's Tachyon Fibre Channel Controller chips require +// certain memory queues and register pointers to be aligned +// on various boundaries, usually the size of the Queue in question. +// Alignment might be on 2, 4, 8, ... or even 512 byte boundaries. +// Since most O/Ss don't allow this (usually only Cache aligned - +// 32-byte boundary), these routines provide generic alignment (after +// O/S allocation) at any boundary, and store the original allocated +// pointer for deletion (O/S free function). Typically, we expect +// these functions to only be called at HBA initialization and +// removal time (load and unload times) +// ALGORITHM notes: +// Memory allocation varies by compiler and platform. In the worst case, +// we are only assured BYTE allignment, but in the best case, we can +// request allocation on any desired boundary. Our strategy: pad the +// allocation request size (i.e. waste memory) so that we are assured +// of passing desired boundary near beginning of contiguous space, then +// mask out lower address bits. +// We define the following algorithm: +// allocBoundary - compiler/platform specific address alignment +// in number of bytes (default is single byte; i.e. 1) +// n_alloc - number of bytes application wants @ aligned address +// ab - alignment boundary, in bytes (e.g. 4, 32, ...) +// t_alloc - total allocation needed to ensure desired boundary +// mask - to clear least significant address bits for boundary +// Compute: +// t_alloc = n_alloc + (ab - allocBoundary) +// allocate t_alloc bytes @ alloc_address +// mask = NOT (ab - 1) +// (e.g. if ab=32 _0001 1111 -> _1110 0000 +// aligned_address = alloc_address & mask +// set n_alloc bytes to 0 +// return aligned_address (NULL if failed) +// +// If u32_AlignedAddress is non-zero, then search for BaseAddress (stored +// from previous allocation). If found, invoke call to FREE the memory. +// Return NULL if BaseAddress not found + +// we need about 8 allocations per HBA. Figuring at most 10 HBAs per server +// size the dynamic_mem array at 80. + +void* fcMemManager( ALIGNED_MEM *dynamic_mem, ULONG n_alloc, ULONG ab, + ULONG u32_AlignedAddress) +{ + USHORT allocBoundary=1; // compiler specific - worst case 1 + // best case - replace malloc() call + // with function that allocates exactly + // at desired boundary + + unsigned long ulAddress; + ULONG t_alloc, i; + void *alloc_address = 0; // def. error code / address not found + LONG mask; // must be 32-bits wide! + + ENTER("fcMemManager"); + if( u32_AlignedAddress ) // are we freeing existing memory? + { +// printk(" freeing AlignedAddress %Xh\n", u32_AlignedAddress); + for( i=0; i // timer declaration in our host data +#include // task queue sched +#include +#include "cpqfcTSioctl.h" + +#define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \ + for( wait_time=jiffies + (secs*HZ); \ + wait_time > jiffies ;) ; } +#define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) +#define VER_MAJOR 1 +#define VER_MINOR 3 +#define VER_SUBMINOR 4 + +// Macros for kernel (esp. SMP) tracing using a PCI analyzer +// (e.g. x86). +//#define PCI_KERNEL_TRACE +#ifdef PCI_KERNEL_TRACE +#define PCI_TRACE(x) inl( fcChip->Registers.IOBaseL +x); +#define PCI_TRACEO(x,y) outl( x, (fcChip->Registers.IOBaseL +y)); +#else + +#define PCI_TRACE(x) +#define PCI_TRACEO(x,y) +#endif + + +//#define DEBUG_CMND 1 // debug output for Linux Scsi CDBs +//#define DUMMYCMND_DBG 1 + +//#define DEBUG_CPQFCTS 1 +//#undef DEBUG_CPQFCTS +#ifdef DEBUG_CPQFCTS +#define ENTER(x) printk("cpqfcts : entering %s()\n", x); +#define LEAVE(x) printk("cpqfcts : leaving %s()\n", x); +#define DEBUG(x) x +#else +#define ENTER(x) +#define LEAVE(x) +#define DEBUG(x) +#endif /* DEBUG_CPQFCTS */ + +//#define DEBUG_CPQFCTS_PCI 1 +//#undef DEBUG_CPQFCTS_PCI +#if DEBUG_CPQFCTS_PCI +#define DEBUG_PCI(x) x +#else +#define DEBUG_PCI(x) +#endif /* DEBUG_CPQFCTS_PCI */ + +#define STACHLITE66_TS12 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2" +#define STACHLITE66_TS13 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.3" +#define STACHLITE_UNKNOWN "Compaq FibreChannel HBA Tachyon Chip/Board Ver??" +#define SAGILENT_XL2_21 "Agilent FC HBA, Tachyon XL2 HPFC-5200B/2.1" + +// PDA is Peripheral Device Address, VSA is Volume Set Addressing +// Linux SCSI parameters +#define CPQFCTS_MAX_TARGET_ID 64 +#define CPQFCTS_MAX_LUN 8 // The RA-4x00 supports 32 (Linux SCSI supports 8) +#define CPQFCTS_MAX_CHANNEL 0 // One FC port on cpqfcTS HBA + +#define CPQFCTS_CMD_PER_LUN 15 // power of 2 -1, must be >0 +#define CPQFCTS_REQ_QUEUE_LEN (TACH_SEST_LEN/2) // must be < TACH_SEST_LEN + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#ifndef DECLARE_MUTEX_LOCKED +#define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED +#endif + +#define DEV_NAME "cpqfcTS" + +#define CPQ_DEVICE_ID 0xA0FC +#define AGILENT_XL2_ID 0x1029 + +typedef struct +{ + __u16 vendor_id; + __u16 device_id; +} SupportedPCIcards; + +// nn:nn denotes bit field + // TachyonHeader struct def. + // the fields shared with ODB + // need to have same value + + + + +#ifndef BYTE +//typedef UCHAR BYTE; +typedef __u8 BYTE; +#endif +#ifndef UCHAR +typedef __u8 UCHAR; +#endif +#ifndef LONG +typedef __s32 LONG; +#endif +#ifndef ULONG +typedef __u32 ULONG; +#endif +#ifndef PVOID +typedef void * PVOID; +#endif +#ifndef USHORT +typedef __u16 USHORT; +#endif +#ifndef BOOLEAN +typedef __u8 BOOLEAN; +#endif + + +// macro for FC-PH reject codes +// payload format for LS_RJT (FC payloads are big endian): +// byte 0 1 2 3 (MSB) +// DWORD 0 01 00 00 00 +// DWORD 1 resvd code expl. vendor + +#define LS_RJT_REASON( code, expl) (( code<<8) | (expl <<16)) + + +#define TachLiteSTATUS 0x12 + +// Fibre Channel EXCHANGE status codes for Tachyon chips/ driver software +// 32-bit ERROR word defines +#define INVALID_ARGS 0x1 +#define LNKDWN_OSLS 0x2 +#define LNKDWN_LASER 0x4 +#define OUTQUE_FULL 0x8 +#define DRIVERQ_FULL 0x10 +#define SEST_FULL 0x20 +#define BAD_ALPA 0x40 +#define OVERFLOW 0x80 // inbound CM +#define COUNT_ERROR 0x100 // inbound CM +#define LINKFAIL_RX 0x200 // inbound CM +#define ABORTSEQ_NOTIFY 0x400 // outbound CM +#define LINKFAIL_TX 0x800 // outbound CM +#define HOSTPROG_ERR 0x1000 // outbound CM +#define FRAME_TO 0x2000 // outbound CM +#define INV_ENTRY 0x4000 // outbound CM +#define SESTPROG_ERR 0x8000 // outbound CM +#define OUTBOUND_TIMEOUT 0x10000L // timeout waiting for Tachyon outbound CM +#define INITIATOR_ABORT 0x20000L // initiator exchange timeout or O/S ABORT +#define MEMPOOL_FAIL 0x40000L // O/S memory pool allocation failed +#define FC2_TIMEOUT 0x80000L // driver timeout for lost frames +#define TARGET_ABORT 0x100000L // ABTS received from FC port +#define EXCHANGE_QUEUED 0x200000L // e.g. Link State was LDn on fcStart +#define PORTID_CHANGED 0x400000L // fc Port address changed +#define DEVICE_REMOVED 0x800000L // fc Port address changed +// Several error scenarios result in SEST Exchange frames +// unexpectedly arriving in the SFQ +#define SFQ_FRAME 0x1000000L // SFQ frames from open Exchange + +// Maximum number of Host Bus Adapters (HBA) / controllers supported +// only important for mem allocation dimensions - increase as necessary + +#define MAX_ADAPTERS 8 +#define MAX_RX_PAYLOAD 1024 // hardware dependent max frame payload +// Tach header struc defines +#define SOFi3 0x7 +#define SOFf 0x8 +#define SOFn3 0xB +#define EOFn 0x5 +#define EOFt 0x6 + +// FCP R_CTL defines +#define FCP_CMND 0x6 +#define FCP_XFER_RDY 0x5 +#define FCP_RSP 0x7 +#define FCP_RESPONSE 0x777 // (arbitrary #) +#define NEED_FCP_RSP 0x77 // (arbitrary #) +#define FCP_DATA 0x1 + +#define RESET_TACH 0x100 // Reset Tachyon/TachLite +#define SCSI_IWE 0x2000 // initiator write entry (for SEST) +#define SCSI_IRE 0x3000 // initiator read entry (for SEST) +#define SCSI_TRE 0x400 // target read entry (for SEST) +#define SCSI_TWE 0x500 // target write entry (for SEST) +#define TOGGLE_LASER 0x800 +#define LIP 0x900 +#define CLEAR_FCPORTS 99 // (arbitrary #) free mem for Logged in ports +#define FMINIT 0x707 // (arbitrary) for Frame Manager Init command + +// BLS == Basic Link Service +// ELS == Extended Link Service +#define BLS_NOP 4 +#define BLS_ABTS 0x10 // FC-PH Basic Link Service Abort Sequence +#define BLS_ABTS_ACC 0x100 // FC-PH Basic Link Service Abort Sequence Accept +#define BLS_ABTS_RJT 0x101 // FC-PH Basic Link Service Abort Sequence Reject +#define ELS_PLOGI 0x03 // FC-PH Port Login (arbitrary assign) +#define ELS_SCR 0x70 // (arb assign) State Change Registration (Fabric) +#define FCS_NSR 0x72 // (arb assign) Name Service Request (Fabric) +#define ELS_FLOGI 0x44 // (arb assign) Fabric Login +#define ELS_FDISC 0x41 // (arb assign) Fabric Discovery (Login) +#define ELS_PDISC 0x50 // FC-PH2 Port Discovery +#define ELS_ABTX 0x06 // FC-PH Abort Exchange +#define ELS_LOGO 0x05 // FC-PH Port Logout +#define ELS_PRLI 0x20 // FCP-SCSI Process Login +#define ELS_PRLO 0x21 // FCP-SCSI Process Logout +#define ELS_LOGO_ACC 0x07 // {FC-PH} Port Logout Accept +#define ELS_PLOGI_ACC 0x08 // {FC-PH} Port Login Accept +#define ELS_ACC 0x18 // {FC-PH} (generic) ACCept +#define ELS_PRLI_ACC 0x22 // {FCP-SCSI} Process Login Accept +#define ELS_RJT 0x1000000 +#define SCSI_REPORT_LUNS 0x0A0 +#define REPORT_LUNS 0xA0 // SCSI-3 command op-code + +#define ELS_LILP_FRAME 0x00000711 // 1st payload word of LILP frame + +#define SFQ_UNASSISTED_FCP 1 // ICM, DWord3, "Type" unassisted FCP +#define SFQ_UNKNOWN 0x31 // (arbitrary) ICM, DWord3, "Type" unknown + +// these "LINK" bits refer to loop or non-loop +#define LINKACTIVE 0x2 // fcLinkQ type - LINK UP Tachyon FM 'Lup' bit set +#define LINKDOWN 0xf2 // fcLinkQ type - LINK DOWN Tachyon FM 'Ldn' bit set + +//#define VOLUME_SET_ADDRESSING 1 // "channel" or "bus" 1 + +typedef struct // 32 bytes hdr ONLY (e.g. FCP_DATA buffer for SEST) +{ + ULONG reserved; // dword 0 (don't use) + ULONG sof_eof; + ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID + ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID + ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL + ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT + ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID + ULONG ro; // dword 7 - relative offset +} TachFCHDR; + + // NOTE!! the following struct MUST be 64 bytes. +typedef struct // 32 bytes hdr + 32 bytes payload +{ + ULONG reserved; // dword 0 (don't use - must clear to 0) + ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp + ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID + ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID + ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL + ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT + ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID + ULONG ro; // dword 7 - relative offset +//--------- + __u32 pl[8]; // dwords 8-15 frame data payload +} TachFCHDR_CMND; + + +typedef struct // 32 bytes hdr + 120 bytes payload +{ + ULONG reserved; // dword 0 (don't use - must clear to 0) + ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp + ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID + ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID + ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL + ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT + ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID + ULONG ro; // dword 7 - relative offset +//--------- + __u32 pl[30]; // largest necessary payload (for LOGIN cmnds) +} TachFCHDR_GCMND; + +typedef struct // 32 bytes hdr + 64 bytes payload +{ + ULONG reserved; // dword 0 (don't use) + ULONG sof_eof; + ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID + ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID + ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL + ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT + ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID + ULONG ro; // dword 7 - relative offset +//--------- + __u32 pl[18]; // payload for FCP-RSP (response buffer) RA-4x00 is 72bytes +} TachFCHDR_RSP; + + + + + + +// Inbound Message Queue structures... +typedef struct // each entry 8 words (32 bytes) +{ + ULONG type; // IMQ completion message types + ULONG word[7]; // remainder of structure + // interpreted by IMQ type +} TachyonIMQE; + + +// Queues for TachLite not in original Tachyon +// ERQ - Exchange Request Queue (for outbound commands) +// SFQ - Single Frame Queue (for incoming frames) + + // Define Tachyon Outbound Command Que + // (Since many Tachyon registers are Read + // only, maintain copies for debugging) + // most Tach ques need power-of-2 sizes, + // where registers are loaded with po2 -1 +#define TACH_SEST_LEN 512 // TachLite SEST + +#define ELS_EXCHANGES 64 // e.g. PLOGI, RSCN, ... +// define the total number of outstanding (simultaneous) exchanges +#define TACH_MAX_XID (TACH_SEST_LEN + ELS_EXCHANGES) // ELS exchanges + +#define ERQ_LEN 128 // power of 2, max 4096 + +// Inbound Message Queue structures... +#define IMQ_LEN 512 // minimum 4 entries [(power of 2) - 1] +typedef struct // 8 words - 32 bytes +{ + TachyonIMQE QEntry[IMQ_LEN]; + ULONG producerIndex; // IMQ Producer Index register + // @32 byte align + ULONG consumerIndex; // Consumer Index register (in Tachyon) + ULONG length; // Length register + ULONG base; +} TachyonIMQ; // @ 32 * IMQ_LEN align + + + +typedef struct // inbound completion message +{ + ULONG Type; + ULONG Index; + ULONG TransferLength; +} TachyonInbCM; + + + +// arbitrary numeric tags for TL structures +#define TL_FCHS 1 // TachLite Fibre Channel Header Structure +#define TL_IWE 2 // initiator write entry (for SEST) +#define TL_TWE 3 // target write entry (for SEST) +#define TL_IRE 4 // initiator read entry (for SEST) +#define TL_TRE 5 // target read entry (for SEST) +#define TL_IRB 6 // I/O request block + + // for INCOMING frames +#define SFQ_LEN 32 // minimum 32 entries, max 4096 + +typedef struct // Single Frame Que +{ + TachFCHDR_CMND QEntry[SFQ_LEN]; // must be 64 bytes!! + ULONG producerIndex; // IMQ Producer Index register + // @32 byte align + ULONG consumerIndex; // Consumer Index register (in Tachyon) + ULONG length; // Length register + ULONG base; +} TachLiteSFQ; + + +typedef struct // I/O Request Block flags +{ + UCHAR BRD : 1; + UCHAR : 1; // reserved + UCHAR SFA : 1; + UCHAR DNC : 1; + UCHAR DIN : 1; + UCHAR DCM : 1; + UCHAR CTS : 1; + UCHAR SBV : 1; // IRB entry valid - IRB'B' only +} IRBflags; + +typedef struct // I/O Request Block +{ // Request 'A' + ULONG Req_A_SFS_Len; // total frame len (hdr + payload), min 32 + ULONG Req_A_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent) + ULONG Req_A_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa) + ULONG Req_A_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST + // Request 'B' + ULONG Req_B_SFS_Len; // total frame len (hdr + payload), min 32 + ULONG Req_B_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent) + ULONG Req_B_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa) + ULONG Req_B_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST +} TachLiteIRB; + + +typedef struct // TachLite placeholder for IRBs +{ // aligned @sizeof(ERQ) for TachLite + // MAX commands is sum of SEST len and ERQ + // we know that each SEST entry requires an + // IRB (ERQ) entry; in addition, we provide + // ERQ_LEN + TachLiteIRB QEntry[ERQ_LEN]; // Base register; entries 32 bytes ea. + ULONG consumerIndex; // Consumer Index register + ULONG producerIndex; // ERQ Producer Index register + ULONG length; // Length register + ULONG base; // copy of base ptr for debug + // struct is sized for largest expected cmnd (LOGIN) +} TachLiteERQ; + + +#define TL_MAX_SGPAGES 4 // arbitrary limit to # of TL Ext. S/G pages + // stores array of allocated page blocks used + // in extended S/G lists. Affects amount of static + // memory consumed by driver. +#define TL_EXT_SG_PAGE_COUNT 256 // Number of Extended Scatter/Gather a/l PAIRS + // Tachyon register (IOBaseU 0x68) + // power-of-2 value ONLY! 4 min, 256 max + + // byte len is #Pairs * 2 ULONG/Pair * 4 bytes/ULONG +#define TL_EXT_SG_PAGE_BYTELEN (TL_EXT_SG_PAGE_COUNT *2 *4) + + + +// SEST entry types: IWE, IRE, TWE, TRE +typedef struct +{ + ULONG Hdr_Len; + ULONG Hdr_Addr; + ULONG RSP_Len; + ULONG RSP_Addr; + ULONG Buff_Off; + ULONG Link; + ULONG RX_ID; + ULONG Data_Len; + ULONG Exp_RO; + ULONG Exp_Byte_Cnt; + // --- extended/local Gather Len/Address pairs + ULONG GLen1; + ULONG GAddr1; + ULONG GLen2; + ULONG GAddr2; + ULONG GLen3; + ULONG GAddr3; +} TachLiteIWE; + + +typedef struct +{ + ULONG Seq_Accum; + ULONG reserved; // must clear to 0 + ULONG RSP_Len; + ULONG RSP_Addr; + ULONG Buff_Off; + ULONG Buff_Index; // ULONG 5 + ULONG Exp_RO; + ULONG Byte_Count; + ULONG reserved_; // ULONG 8 + ULONG Exp_Byte_Cnt; + // --- extended/local Scatter Len/Address pairs + ULONG SLen1; + ULONG SAddr1; + ULONG SLen2; + ULONG SAddr2; + ULONG SLen3; + ULONG SAddr3; +} TachLiteIRE; + + +typedef struct // Target Write Entry +{ + ULONG Seq_Accum; // dword 0 + ULONG reserved; // dword 1 must clear to 0 + ULONG Remote_Node_ID; + ULONG reserved1; // dword 3 must clear to 0 + ULONG Buff_Off; + ULONG Buff_Index; // ULONG 5 + ULONG Exp_RO; + ULONG Byte_Count; + ULONG reserved_; // ULONG 8 + ULONG Exp_Byte_Cnt; + // --- extended/local Scatter Len/Address pairs + ULONG SLen1; + ULONG SAddr1; + ULONG SLen2; + ULONG SAddr2; + ULONG SLen3; + ULONG SAddr3; +} TachLiteTWE; + +typedef struct +{ + ULONG Hdr_Len; + ULONG Hdr_Addr; + ULONG RSP_Len; // DWord 2 + ULONG RSP_Addr; + ULONG Buff_Off; + ULONG Buff_Index; // DWord 5 + ULONG reserved; + ULONG Data_Len; + ULONG reserved_; + ULONG reserved__; + // --- extended/local Gather Len/Address pairs + ULONG GLen1; // DWord A + ULONG GAddr1; + ULONG GLen2; + ULONG GAddr2; + ULONG GLen3; + ULONG GAddr3; +} TachLiteTRE; + +typedef struct +{ + void *PoolPage[TL_MAX_SGPAGES]; +} SGPAGES, *PSGPAGES; // linked list of S/G pairs, by Exchange + + + +typedef struct // SCSI Exchange State Table +{ + union // Entry can be IWE, IRE, TWE, TRE + { // 64 bytes per entry + TachLiteIWE IWE; + TachLiteIRE IRE; + TachLiteTWE TWE; + TachLiteTRE TRE; + } u[TACH_SEST_LEN]; + + TachFCHDR DataHDR[TACH_SEST_LEN]; // for SEST FCP_DATA frame hdr (no pl) + TachFCHDR_RSP RspHDR[TACH_SEST_LEN]; // space for SEST FCP_RSP frame + SGPAGES sgPages[TACH_SEST_LEN]; // array of Pool-allocations + ULONG length; // Length register + ULONG base; // copy of base ptr for debug +} TachSEST; + + + +typedef struct // each register has it's own address + // and value (used for write-only regs) +{ + void* address; + volatile ULONG value; +} FCREGISTER; + +typedef struct // Host copy - TachLite Registers +{ + ULONG IOBaseL, IOBaseU; // I/O port lower and upper TL register addresses + ULONG MemBase; // memory mapped register addresses + void* ReMapMemBase; // O/S VM reference for MemBase + ULONG wwn_hi; // WWN is set once at startup + ULONG wwn_lo; + ULONG my_al_pa; // al_pa received after LIP() + ULONG ROMCTR; // flags for on-board RAM/ROM + ULONG RAMBase; // on-board RAM (i.e. some Tachlites) + ULONG SROMBase; // on-board EEPROM (some Tachlites) + ULONG PCIMCTR; // PCI Master Control Reg (has bus width) + + FCREGISTER INTEN; // copy of interrupt enable mask + FCREGISTER INTPEND; // interrupt pending + FCREGISTER INTSTAT; // interrupt status + FCREGISTER SFQconsumerIndex; + FCREGISTER ERQproducerIndex; + FCREGISTER TYconfig; // TachYon (chip level) + FCREGISTER TYcontrol; + FCREGISTER TYstatus; + FCREGISTER FMconfig; // Frame Manager (FC loop level) + FCREGISTER FMcontrol; + FCREGISTER FMstatus; + FCREGISTER FMLinkStatus1; + FCREGISTER FMLinkStatus2; + FCREGISTER FMBB_CreditZero; + FCREGISTER status; + FCREGISTER ed_tov; // error detect time-out value + FCREGISTER rcv_al_pa; // received arb. loop physical address + FCREGISTER primitive; // e.g. LIP(), OPN(), ... +} TL_REGISTERS; + + + +typedef struct +{ + ULONG ok; + ULONG invalidArgs; + ULONG linkDown; + ULONG linkUp; + ULONG outQueFull; + ULONG SESTFull; + ULONG hpe; // host programming err (from Tach) + ULONG FC4aborted; // aborts from Application or upper driver layer + ULONG FC2aborted; // aborts from our driver's timeouts + ULONG timeouts; // our driver timeout (on individual exchanges) + ULONG logouts; // explicit - sent LOGO; implicit - device removed + ULONG retries; + ULONG linkFailTX; + ULONG linkFailRX; + ULONG CntErrors; // byte count expected != count received (typ. SEST) + ULONG e_stores; // elastic store errs + ULONG resets; // hard or soft controller resets + ULONG FMinits; // TACH Frame Manager Init (e.g. LIPs) + ULONG lnkQueFull; // too many LOGIN, loop commands + ULONG ScsiQueFull; // too many FCP-SCSI inbound frames + ULONG LossofSignal; // FM link status 1 regs + ULONG BadRXChar; // FM link status 1 regs + ULONG LossofSync; // FM link status 1 regs + ULONG Rx_EOFa; // FM link status 2 regs (received EOFa) + ULONG Dis_Frm; // FM link status 2 regs (discarded frames) + ULONG Bad_CRC; // FM link status 2 regs + ULONG BB0_Timer; // FM BB_Credit Zero Timer Reg + ULONG loopBreaks; // infinite loop exits + ULONG lastBB0timer; // static accum. buffer needed by Tachlite +} FCSTATS; + + +typedef struct // Config Options +{ // LS Bit first + USHORT : 1; // bit0: + USHORT flogi : 1; // bit1: We sent FLOGI - wait for Fabric logins + USHORT fabric: 1; // bit2: Tachyon detected Fabric (FM stat LG) + USHORT LILPin: 1; // bit3: We can use an FC-AL LILP frame + USHORT target: 1; // bit4: this Port has SCSI target capability + USHORT initiator: 1; // bit5: this Port has SCSI initiator capability + USHORT extLoopback: 1; // bit6: loopback at GBIC + USHORT intLoopback: 1; // bit7: loopback in HP silicon + USHORT : 1; // bit8: + USHORT : 1; // bit9: + USHORT : 1; // bit10: + USHORT : 1; // bit11: + USHORT : 1; // bit12: + USHORT : 1; // bit13: + USHORT : 1; // bit14: + USHORT : 1; // bit15: +} FC_OPTIONS; + + + +typedef struct dyn_mem_pair +{ + void *BaseAllocated; // address as allocated from O/S; + unsigned long AlignedAddress; // aligned address (used by Tachyon DMA) +} ALIGNED_MEM; + + + + +// these structs contain only CRUCIAL (stuff we actually use) parameters +// from FC-PH(n) logins. (Don't save entire LOGIN payload to save mem.) + +// Implicit logout happens when the loop goes down - we require PDISC +// to restore. Explicit logout is when WE decide never to talk to someone, +// or when a target refuses to talk to us, i.e. sends us a LOGO frame or +// LS_RJT reject in response to our PLOGI request. + +#define IMPLICIT_LOGOUT 1 +#define EXPLICIT_LOGOUT 2 + +typedef struct +{ + UCHAR channel; // SCSI "bus" + UCHAR target; + UCHAR InqDeviceType; // byte 0 from SCSI Inquiry response + UCHAR VolumeSetAddressing; // FCP-SCSI LUN coding (40h for VSA) + UCHAR LunMasking; // True if selective presentation supported + UCHAR lun[CPQFCTS_MAX_LUN]; +} SCSI_NEXUS; + + +typedef struct +{ + union + { + UCHAR ucWWN[8]; // a FC 64-bit World Wide Name/ PortID of target + // addressing of single target on single loop... + u64 liWWN; + } u; + + ULONG port_id; // a FC 24-bit address of port (lower 8 bits = al_pa) + + Scsi_Cmnd ScsiCmnd; // command buffer for Report Luns +#define REPORT_LUNS_PL 256 + UCHAR ReportLunsPayload[REPORT_LUNS_PL]; + + SCSI_NEXUS ScsiNexus; // LUNs per FC device + + ULONG LOGO_counter; // might try several times before logging out for good + ULONG LOGO_timer; // after LIP, ports expecting PDISC must time-out and + // LOGOut if successful PDISC not completed in 2 secs + + ULONG concurrent_seq; // must be 1 or greater + ULONG rx_data_size; // e.g. 128, 256, 1024, 2048 per FC-PH spec + ULONG BB_credit; + ULONG EE_credit; + + ULONG fcp_info; // from PRLI (i.e. INITIATOR/ TARGET flags) + // flags for login process + BOOLEAN Originator; // Login sequence Originated (if false, we + // responded to another port's login sequence) + BOOLEAN plogi; // PLOGI frame ACCepted (originated or responded) + BOOLEAN pdisc; // PDISC frame was ORIGINATED (self-login logic) + BOOLEAN prli; // PRLI frame ACCepted (originated or responded) + BOOLEAN flogi; // FLOGI frame ACCepted (originated or responded) + BOOLEAN logo; // port permanently logged out (invalid login param) + BOOLEAN flogiReq; // Fabric login required (set in LIP process) + UCHAR highest_ver; + UCHAR lowest_ver; + + + // when the "target" (actually FC Port) is waiting for login + // (e.g. after Link reset), set the device_blocked bit; + // after Port completes login, un-block target. + UCHAR device_blocked; // see Scsi_Device struct + + // define singly-linked list of logged-in ports + // once a port_id is identified, it is remembered, + // even if the port is removed indefinitely + PVOID pNextPort; // actually, type PFC_LOGGEDIN_PORT; void for Compiler + +} FC_LOGGEDIN_PORT, *PFC_LOGGEDIN_PORT; + + + +// This serves as the ESB (Exchange Status Block), +// and has timeout counter; used for ABORTs +typedef struct +{ // FC-1 X_IDs + ULONG type; // ELS_PLOGI, SCSI_IWE, ... (0 if free) + PFC_LOGGEDIN_PORT pLoggedInPort; // FC device on other end of Exchange + Scsi_Cmnd *Cmnd; // Linux SCSI command packet includes S/G list + ULONG timeOut; // units of ??, DEC by driver, Abort when 0 + ULONG reTries; // need one or more retries? + ULONG status; // flags indicating errors (0 if none) + TachLiteIRB IRB; // I/O Request Block, gets copied to ERQ + TachFCHDR_GCMND fchs; // location of IRB's Req_A_SFS_Addr +} FC_EXCHANGE, *PFC_EXCHANGE; + +// Unfortunately, Linux limits our kmalloc() allocations to 128k. +// Because of this and the fact that our ScsiRegister allocation +// is also constrained, we move this large structure out for +// allocation after Scsi Register. +// (In other words, this cumbersome indirection is necessary +// because of kernel memory allocation constraints!) + +typedef struct // we will allocate this dynamically +{ + FC_EXCHANGE fcExchange[ TACH_MAX_XID ]; +} FC_EXCHANGES; + + + + + + + + + + + +typedef struct +{ + char Name[64]; // name of controller ("HP Tachlite TL Rev2.0, 33MHz, 64bit bus") + //PVOID pAdapterDevExt; // back pointer to device object/extension + ULONG ChipType; // local numeric key for Tachyon Type / Rev. + ULONG status; // our Driver - logical status + + TL_REGISTERS Registers; // reg addresses & host memory copies + // FC-4 mapping of 'transaction' to X_IDs + UCHAR LILPmap[32*4]; // Loop Position Map of ALPAs (late FC-AL only) + FC_OPTIONS Options; // e.g. Target, Initiator, loopback... + UCHAR highest_FCPH_ver; // FC-PH version limits + UCHAR lowest_FCPH_ver; // FC-PH version limits + + FC_EXCHANGES *Exchanges; + ULONG fcLsExchangeLRU; // Least Recently Used counter (Link Service) + ULONG fcSestExchangeLRU; // Least Recently Used counter (FCP-SCSI) + FC_LOGGEDIN_PORT fcPorts; // linked list of every FC port ever seen + FCSTATS fcStats; // FC comm err counters + + // Host memory QUEUE pointers + TachLiteERQ *ERQ; // Exchange Request Que + TachyonIMQ *IMQ; // Inbound Message Que + TachLiteSFQ *SFQ; // Single Frame Queue + TachSEST *SEST; // SCSI Exchange State Table + + // these function pointers are for "generic" functions, which are + // replaced with Host Bus Adapter types at + // runtime. + int (*CreateTachyonQues)( void* , int); + int (*DestroyTachyonQues)( void* , int); + int (*LaserControl)(void*, int ); // e.g. On/Off + int (*ResetTachyon)(void*, int ); + void (*FreezeTachyon)(void*, int ); + void (*UnFreezeTachyon)(void*, int ); + int (*InitializeTachyon)(void*, int, int ); + int (*InitializeFrameManager)(void*, int ); + int (*ProcessIMQEntry)(void*); + int (*ReadWriteWWN)(void*, int ReadWrite); + int (*ReadWriteNVRAM)(void*, void*, int ReadWrite); + +} TACHYON, *PTACHYON; + + +void cpqfcTSClearLinkStatusCounters(TACHYON * fcChip); + +int CpqTsCreateTachLiteQues( void* pHBA, int opcode); +int CpqTsDestroyTachLiteQues( void* , int); +int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2); + +int CpqTsProcessIMQEntry(void* pHBA); +int CpqTsResetTachLite(void *pHBA, int type); +void CpqTsFreezeTachlite(void *pHBA, int type); +void CpqTsUnFreezeTachlite(void *pHBA, int type); +int CpqTsInitializeFrameManager(void *pHBA, int); +int CpqTsLaserControl( void* addrBase, int opcode ); +int CpqTsReadWriteWWN(void*, int ReadWrite); +int CpqTsReadWriteNVRAM(void*, void* data, int ReadWrite); + +void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter); +void cpqfcTSWorkerThread( void *host); + +int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf ); +ULONG cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count, + UCHAR *buf ); + +BOOLEAN tl_write_i2c_nvram( void* GPIOin, void* GPIOout, + USHORT startOffset, // e.g. 0x2f for WWN start + USHORT count, + UCHAR *buf ); + + +// define misc functions +int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]); +int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]); +void* fcMemManager( ALIGNED_MEM *dyn_mem_pair, ULONG n_alloc, ULONG ab, + ULONG ulAlignedAddress); + +void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt); + +//ULONG virt_to_phys( PVOID virtaddr ); + + +// Linux interrupt handler +void cpqfcTS_intr_handler( int irq,void *dev_id,struct pt_regs *regs); +void cpqfcTSheartbeat( unsigned long ptr ); + + + +// The biggest Q element we deal with is Aborts - we +// need 4 bytes for x_ID, and a Scsi_Cmnd (~284 bytes) +//#define LINKQ_ITEM_SIZE ((4+sizeof(Scsi_Cmnd)+3)/4) +#define LINKQ_ITEM_SIZE (3*16) +typedef struct +{ + ULONG Type; // e.g. LINKUP, SFQENTRY, PDISC, BLS_ABTS, ... + ULONG ulBuff[ LINKQ_ITEM_SIZE ]; +} LINKQ_ITEM; + +#define FC_LINKQ_DEPTH TACH_MAX_XID +typedef struct +{ + ULONG producer; + ULONG consumer; // when producer equals consumer, Q empty + + LINKQ_ITEM Qitem[ FC_LINKQ_DEPTH ]; + +} FC_LINK_QUE, *PFC_LINK_QUE; + + + // DPC routines post to here on Inbound SCSI frames + // User thread processes +#define FC_SCSIQ_DEPTH 32 + +typedef struct +{ + int Type; // e.g. SCSI + ULONG ulBuff[ 3*16 ]; +} SCSIQ_ITEM; + +typedef struct +{ + ULONG producer; + ULONG consumer; // when producer equals consumer, Q empty + + SCSIQ_ITEM Qitem[ FC_SCSIQ_DEPTH ]; + +} FC_SCSI_QUE, *PFC_SCSI_QUE; + + + + + +#define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST + +// Linux space allocated per HBA (chip state, etc.) +typedef struct +{ + struct Scsi_Host *HostAdapter; // back pointer to Linux Scsi struct + + TACHYON fcChip; // All Tachyon registers, Queues, functions + ALIGNED_MEM dynamic_mem[DYNAMIC_ALLOCATIONS]; + + struct pci_dev *PciDev; + + Scsi_Cmnd *LinkDnCmnd[CPQFCTS_REQ_QUEUE_LEN]; // collects Cmnds during LDn + // (for Acceptable targets) + Scsi_Cmnd *BoardLockCmnd[CPQFCTS_REQ_QUEUE_LEN]; // SEST was full + + Scsi_Cmnd *BadTargetCmnd[CPQFCTS_MAX_TARGET_ID]; // missing targets + + u_char HBAnum; // 0-based host number + + + struct timer_list cpqfcTStimer; // FC utility timer for implicit + // logouts, FC protocol timeouts, etc. + int fcStatsTime; // Statistics delta reporting time + + struct task_struct *worker_thread; // our kernel thread + int PortDiscDone; // set by SendLogins(), cleared by LDn + + struct semaphore *TachFrozen; + struct semaphore *TYOBcomplete; // handshake for Tach outbound frames + struct semaphore *fcQueReady; // FibreChannel work for our kernel thread + struct semaphore *notify_wt; // synchronizes kernel thread kill + struct semaphore *BoardLock; + + PFC_LINK_QUE fcLQ; // the WorkerThread operates on this + + spinlock_t hba_spinlock; // held/released by WorkerThread + +} CPQFCHBA; + +#define CPQ_SPINLOCK_HBA( x ) spin_lock(&x->hba_spinlock); +#define CPQ_SPINUNLOCK_HBA(x) spin_unlock(&x->hba_spinlock); + + + +void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata, + PFC_LOGGEDIN_PORT pFcPort); + + +void cpqfcTSTerminateExchange( CPQFCHBA*, SCSI_NEXUS *target, int ); + +PFC_LOGGEDIN_PORT fcPortLoggedIn( + CPQFCHBA *cpqfcHBAdata, + TachFCHDR_GCMND* fchs, + BOOLEAN, + BOOLEAN); +void fcProcessLoggedIn( + CPQFCHBA *cpqfcHBAdata, TachFCHDR_GCMND* fchs); + + +ULONG cpqfcTSBuildExchange( + CPQFCHBA *cpqfcHBAdata, + ULONG type, // e.g. PLOGI + TachFCHDR_GCMND* InFCHS, // incoming FCHS + void *Data, // the CDB, scatter/gather, etc. + LONG *ExchangeID ); // allocated exchange ID + +ULONG cpqfcTSStartExchange( + CPQFCHBA *cpqfcHBAdata, + LONG ExchangeID ); + +void cpqfcTSCompleteExchange( + PTACHYON fcChip, + ULONG exchange_ID); + + +PFC_LOGGEDIN_PORT fcFindLoggedInPort( + PTACHYON fcChip, + Scsi_Cmnd *Cmnd, // (We want the channel/target/lun Nexus from Cmnd) + ULONG port_id, // search linked list for al_pa, or + UCHAR wwn[8], // search linked list for WWN, or... + PFC_LOGGEDIN_PORT *pLastLoggedInPort +); + +// don't do this unless you have the right hardware! +#define TRIGGERABLE_HBA 1 +#ifdef TRIGGERABLE_HBA +void TriggerHBA( void*, int); +#endif + +void cpqfcTSPutLinkQue( + CPQFCHBA *cpqfcHBAdata, + int Type, + void *QueContent); + +void fcPutScsiQue( + CPQFCHBA *cpqfcHBAdata, + int Type, + void *QueContent); + +void fcLinkQReset( + CPQFCHBA *); +void fcScsiQReset( + CPQFCHBA *); +void fcSestReset( + CPQFCHBA *); + + + + + +extern const UCHAR valid_al_pa[]; +extern const int number_of_al_pa; + +#define FCP_RESID_UNDER 0x80000 +#define FCP_RESID_OVER 0x40000 +#define FCP_SNS_LEN_VALID 0x20000 +#define FCP_RSP_LEN_VALID 0x10000 + +// RSP_CODE definitions (dpANS Fibre Channel Protocol for SCSI, pg 34) +#define FCP_DATA_LEN_NOT_BURST_LEN 0x1000000 +#define FCP_CMND_FIELD_INVALID 0x2000000 +#define FCP_DATA_RO_NOT_XRDY_RO 0x3000000 +#define FCP_TASKFUNCTION_NS 0x4000000 +#define FCP_TASKFUNCTION_FAIL 0x5000000 + +// FCP-SCSI response status struct +typedef struct // see "TachFCHDR_RSP" definition - 64 bytes +{ + __u32 reserved; + __u32 reserved1; + __u32 fcp_status; // field validity and SCSI status + __u32 fcp_resid; + __u32 fcp_sns_len; // length of FCP_SNS_INFO field + __u32 fcp_rsp_len; // length of FCP_RSP_INFO field (expect 8) + __u32 fcp_rsp_info; // 4 bytes of FCP protocol response information + __u32 fcp_rsp_info2; // (4 more bytes, since most implementations use 8) + __u8 fcp_sns_info[36]; // bytes for SCSI sense (ASC, ASCQ) + +} FCP_STATUS_RESPONSE, *PFCP_STATUS_RESPONSE; + + +// Fabric State Change Registration +typedef struct scrpl +{ + __u32 command; + __u32 function; +} SCR_PL; + +// Fabric Name Service Request +typedef struct nsrpl +{ + __u32 CT_Rev; // (& IN_ID) WORD 0 + __u32 FCS_Type; // WORD 1 + __u32 Command_code; // WORD 2 + __u32 reason_code; // WORD 3 + __u32 FCP; // WORD 4 (lower byte) + +} NSR_PL; + + + +// "FC.H" +#define MAX_RX_SIZE 0x800 // Max Receive Buffer Size is 2048 +#define MIN_RX_SIZE 0x100 // Min Size is 256, per FC-PLDA Spec +#define MAX_TARGET_RXIDS SEST_DEPTH +#define TARGET_RX_SIZE SEST_BUFFER_LENGTH + +#define CLASS_1 0x01 +#define CLASS_2 0x02 +#define CLASS_3 0x03 + +#define FC_PH42 0x08 +#define FC_PH43 0x09 +#define FC_PH3 0x20 + +#define RR_TOV 2 // Minimum Time for target to wait for + // PDISC after a LIP. +#define E_D_TOV 2 // Minimum Time to wait for Sequence + // Completion. +#define R_A_TOV 0 // Minimum Time for Target to wait + // before reclaiming resources. +// +// R_CTL Field +// +// Routing Bits (31-28) +// +#define FC4_DEVICE_DATA 0x00000000 +#define EXT_LINK_DATA 0x20000000 +#define FC4_LINK_DATA 0x30000000 +#define VIDEO_DATA 0x40000000 +#define BASIC_LINK_DATA 0x80000000 +#define LINK_CONTROL 0xC0000000 +#define ROUTING_MASK 0xF0000000 + +// +// Information Bits (27-24) +// +#define UNCAT_INFORMATION 0x00000000 +#define SOLICITED_DATA 0x01000000 +#define UNSOLICITED_CONTROL 0x02000000 +#define SOLICITED_CONTROL 0x03000000 +#define UNSOLICITED_DATA 0x04000000 +#define DATA_DESCRIPTOR 0x05000000 +#define UNSOLICITED_COMMAND 0x06000000 +#define COMMAND_STATUS 0x07000000 +#define INFO_MASK 0x0F000000 +// +// (Link Control Codes) +// +#define ACK_1 0x00000000 +#define ACK_0_OR_N 0x01000000 +#define P_RJT 0x02000000 +#define F_RJT 0x03000000 +#define P_BSY 0x04000000 +#define FABRIC_BUSY_TO_DF 0x05000000 // Fabric Busy to Data Frame +#define FABRIC_BUSY_TO_LC 0x06000000 // Fabric Busy to Link Ctl Frame +#define LINK_CREDIT_RESET 0x07000000 +// +// (Link Service Command Codes) +// +//#define LS_RJT 0x01000000 // LS Reject + +#define LS_ACC 0x02000000 // LS Accept +#define LS_PLOGI 0x03000000 // N_PORT Login +#define LS_FLOGI 0x04000000 // F_PORT Login +#define LS_LOGO 0x05000000 // Logout +#define LS_ABTX 0x06000000 // Abort Exchange +#define LS_RCS 0x07000000 // Read Connection Status +#define LS_RES 0x08000000 // Read Exchange Status +#define LS_RSS 0x09000000 // Read Sequence Status +#define LS_RSI 0x0A000000 // Request Seq Initiative +#define LS_ESTS 0x0B000000 // Establish Steaming +#define LS_ESTC 0x0C000000 // Estimate Credit +#define LS_ADVC 0x0D000000 // Advice Credit +#define LS_RTV 0x0E000000 // Read Timeout Value +#define LS_RLS 0x0F000000 // Read Link Status +#define LS_ECHO 0x10000000 // Echo +#define LS_TEST 0x11000000 // Test +#define LS_RRQ 0x12000000 // Reinstate Rec. Qual. +#define LS_PRLI 0x20000000 // Process Login +#define LS_PRLO 0x21000000 // Process Logout +#define LS_TPRLO 0x24000000 // 3rd Party Process Logout +#define LS_PDISC 0x50000000 // Process Discovery +#define LS_FDISC 0x51000000 // Fabric Discovery +#define LS_ADISC 0x52000000 // Discover Address +#define LS_RNC 0x53000000 // Report Node Capability +#define LS_SCR 0x62000000 // State Change Registration +#define LS_MASK 0xFF000000 + +// +// TYPE Bit Masks +// +#define BASIC_LINK_SERVICE 0x00000000 +#define EXT_LINK_SERVICE 0x01000000 + +#define LLC 0x04000000 +#define LLC_SNAP 0x05000000 +#define SCSI_FCP 0x08000000 +#define SCSI_GPP 0x09000000 +#define IPI3_MASTER 0x11000000 +#define IPI3_SLAVE 0x12000000 +#define IPI3_PEER 0x13000000 +#define CP_IPI3_MASTER 0x15000000 +#define CP_IPI3_SLAVE 0x16000000 +#define CP_IPI3_PEER 0x17000000 +#define SBCCS_CHANNEL 0x19000000 +#define SBCCS_CONTROL 0x1A000000 +#define FIBRE_SERVICES 0x20000000 +#define FC_FG 0x21000000 +#define FC_XS 0x22000000 +#define FC_AL 0x23000000 +#define SNMP 0x24000000 +#define HIPPI_FP 0x40000000 +#define TYPE_MASK 0xFF000000 + +typedef struct { + UCHAR seq_id_valid; + UCHAR seq_id; + USHORT reserved; // 2 bytes reserved + ULONG ox_rx_id; + USHORT low_seq_cnt; + USHORT high_seq_cnt; +} BA_ACC_PAYLOAD; + +typedef struct { + UCHAR reserved; + UCHAR reason_code; + UCHAR reason_explain; + UCHAR vendor_unique; +} BA_RJT_PAYLOAD; + + +typedef struct { + ULONG command_code; + ULONG sid; + USHORT ox_id; + USHORT rx_id; +} RRQ_MESSAGE; + +typedef struct { + ULONG command_code; + UCHAR vendor; + UCHAR explain; + UCHAR reason; + UCHAR reserved; +} REJECT_MESSAGE; + + +#define N_OR_F_PORT 0x1000 +#define RANDOM_RELATIVE_OFFSET 0x4000 +#define CONTINUOSLY_INCREASING 0x8000 + +#define CLASS_VALID 0x8000 +#define INTERMIX_MODE 0x4000 +#define TRANSPARENT_STACKED 0x2000 +#define LOCKDOWN_STACKED 0x1000 +#define SEQ_DELIVERY 0x800 + +#define XID_NOT_SUPPORTED 0x00 +#define XID_SUPPORTED 0x4000 +#define XID_REQUIRED 0xC000 + +#define ASSOCIATOR_NOT_SUPPORTED 0x00 +#define ASSOCIATOR_SUPPORTED 0x1000 +#define ASSOCIATOR_REQUIRED 0x3000 + +#define INIT_ACK0_SUPPORT 0x800 +#define INIT_ACKN_SUPPORT 0x400 + +#define RECIP_ACK0_SUPPORT 0x8000 +#define RECIP_ACKN_SUPPORT 0x4000 + +#define X_ID_INTERLOCK 0x2000 + +#define ERROR_POLICY 0x1800 // Error Policy Supported +#define ERROR_DISCARD 0x00 // Only Discard Supported +#define ERROR_DISC_PROCESS 0x02 // Discard and process supported + +#define NODE_ID 0x01 +#define IEEE_EXT 0x20 + +// +// Categories Supported Per Sequence +// +#define CATEGORIES_PER_SEQUENCE 0x300 +#define ONE_CATEGORY_SEQUENCE 0x00 // 1 Category per Sequence +#define TWO_CATEGORY_SEQUENCE 0x01 // 2 Categories per Sequence +#define MANY_CATEGORY_SEQUENCE 0x03 // > 2 Categories/Sequence + +typedef struct { + + USHORT initiator_control; + USHORT service_options; + + USHORT rx_data_size; + USHORT recipient_control; + + USHORT ee_credit; + USHORT concurrent_sequences; + + USHORT reserved; + USHORT open_sequences; + +} CLASS_PARAMETERS; + +typedef struct { + ULONG login_cmd; + // + // Common Service Parameters + // + struct { + + USHORT bb_credit; + UCHAR lowest_ver; + UCHAR highest_ver; + + USHORT bb_rx_size; + USHORT common_features; + + USHORT rel_offset; + USHORT concurrent_seq; + + + ULONG e_d_tov; + } cmn_services; + + // + // Port Name + // + UCHAR port_name[8]; + + // + // Node/Fabric Name + // + UCHAR node_name[8]; + + // + // Class 1, 2 and 3 Service Parameters + // + CLASS_PARAMETERS class1; + CLASS_PARAMETERS class2; + CLASS_PARAMETERS class3; + + ULONG reserved[4]; + + // + // Vendor Version Level + // + UCHAR vendor_id[2]; + UCHAR vendor_version[6]; + ULONG buffer_size; + USHORT rxid_start; + USHORT total_rxids; +} LOGIN_PAYLOAD; + + +typedef struct +{ + ULONG cmd; // 4 bytes + UCHAR n_port_identifier[3]; + UCHAR reserved; + UCHAR port_name[8]; +} LOGOUT_PAYLOAD; + + +// +// PRLI Request Service Parameter Defines +// +#define PRLI_ACC 0x01 +#define PRLI_REQ 0x02 +#define ORIG_PROCESS_ASSOC_VALID 0x8000 +#define RESP_PROCESS_ASSOC_VALID 0x4000 +#define ESTABLISH_PAIR 0x2000 +#define DATA_OVERLAY_ALLOWED 0x40 +#define INITIATOR_FUNCTION 0x20 +#define TARGET_FUNCTION 0x10 +#define CMD_DATA_MIXED 0x08 +#define DATA_RESP_MIXED 0x04 +#define READ_XFER_RDY 0x02 +#define WRITE_XFER_RDY 0x01 + +#define RESPONSE_CODE_MASK 0xF00 +#define REQUEST_EXECUTED 0x100 +#define NO_RESOURCES 0x200 +#define INIT_NOT_COMPLETE 0x300 +#define IMAGE_DOES_NOT_EXIST 0x400 +#define BAD_PREDEFINED_COND 0x500 +#define REQ_EXEC_COND 0x600 +#define NO_MULTI_PAGE 0x700 + +typedef struct { + USHORT payload_length; + UCHAR page_length; + UCHAR cmd; + + + ULONG valid; + + ULONG orig_process_associator; + + ULONG resp_process_associator; + + ULONG fcp_info; +} PRLI_REQUEST; + +typedef struct { + + USHORT payload_length; + UCHAR page_length; + UCHAR cmd; + + ULONG valid; + ULONG orig_process_associator; + + ULONG resp_process_associator; + ULONG reserved; +} PRLO_REQUEST; + +typedef struct { + ULONG cmd; + + ULONG hard_address; + + UCHAR port_name[8]; + + UCHAR node_name[8]; + + ULONG s_id; +} ADISC_PAYLOAD; + +// J. McCarty's LINK.H +// +// LS_RJT Reason Codes +// + +#define INVALID_COMMAND_CODE 0x01 +#define LOGICAL_ERROR 0x03 +#define LOGICAL_BUSY 0x05 +#define PROTOCOL_ERROR 0x07 +#define UNABLE_TO_PERFORM 0x09 +#define COMMAND_NOT_SUPPORTED 0x0B +#define LS_VENDOR_UNIQUE 0xFF + +// +// LS_RJT Reason Codes Explanations +// +#define NO_REASON 0x00 +#define OPTIONS_ERROR 0x01 +#define INITIATOR_CTL_ERROR 0x03 +#define RECIPIENT_CTL_ERROR 0x05 +#define DATA_FIELD_SIZE_ERROR 0x07 +#define CONCURRENT_SEQ_ERROR 0x09 +#define CREDIT_ERROR 0x0B +#define INVALID_PORT_NAME 0x0D +#define INVALID_NODE_NAME 0x0E +#define INVALID_CSP 0x0F // Invalid Service Parameters +#define INVALID_ASSOC_HDR 0x11 // Invalid Association Header +#define ASSOC_HDR_REQUIRED 0x13 // Association Header Required +#define LS_INVALID_S_ID 0x15 +#define INVALID_OX_RX_ID 0x17 // Invalid OX_ID RX_ID Combination +#define CMD_IN_PROCESS 0x19 +#define INVALID_IDENTIFIER 0x1F // Invalid N_PORT Identifier +#define INVALID_SEQ_ID 0x21 +#define ABT_INVALID_XCHNG 0x23 // Attempt to Abort an invalid Exchange +#define ABT_INACTIVE_XCHNG 0x25 // Attempt to Abort an inactive Exchange +#define NEED_REC_QUAL 0x27 // Recovery Qualifier required +#define NO_LOGIN_RESOURCES 0x29 // No resources to support login +#define NO_DATA 0x2A // Unable to supply requested data +#define REQUEST_NOT_SUPPORTED 0x2C // Request Not Supported + +// +// Link Control Codes +// + +// +// P_BSY Action Codes +// +#define SEQUENCE_TERMINATED 0x01000000 +#define SEQUENCE_ACTIVE 0x02000000 + +// +// P_BSY Reason Codes +// +#define PHYS_NPORT_BUSY 0x010000 +#define NPORT_RESOURCE_BUSY 0x020000 + +// +// P_RJT, F_RJT Action Codes +// + +#define RETRYABLE_ERROR 0x01000000 +#define NON_RETRYABLE_ERROR 0x02000000 + +// +// P_RJT, F_RJT Reason Codes +// +#define INVALID_D_ID 0x010000 +#define INVALID_S_ID 0x020000 +#define NPORT_NOT_AVAIL_TMP 0x030000 +#define NPORT_NOT_AVAIL_PERM 0x040000 +#define CLASS_NOT_SUPPORTED 0x050000 +#define USAGE_ERROR 0x060000 +#define TYPE_NOT_SUPPORTED 0x070000 +#define INVAL_LINK_CONTROL 0x080000 +#define INVAL_R_CTL 0x090000 +#define INVAL_F_CTL 0x0A0000 +#define INVAL_OX_ID 0x0B0000 +#define INVAL_RX_ID 0x0C0000 +#define INVAL_SEQ_ID 0x0D0000 +#define INVAL_DF_CTL 0x0E0000 +#define INVAL_SEQ_CNT 0x0F0000 +#define INVAL_PARAMS 0x100000 +#define EXCHANGE_ERROR 0x110000 +#define LS_PROTOCOL_ERROR 0x120000 +#define INCORRECT_LENGTH 0x130000 +#define UNEXPECTED_ACK 0x140000 +#define LOGIN_REQ 0x160000 +#define EXCESSIVE_SEQ 0x170000 +#define NO_EXCHANGE 0x180000 +#define SEC_HDR_NOT_SUPPORTED 0x190000 +#define NO_FABRIC 0x1A0000 +#define P_VENDOR_UNIQUE 0xFF0000 + +// +// BA_RJT Reason Codes +// +#define BA_INVALID_COMMAND 0x00010000 +#define BA_LOGICAL_ERROR 0x00030000 +#define BA_LOGICAL_BUSY 0x00050000 +#define BA_PROTOCOL_ERROR 0x00070000 +#define BA_UNABLE_TO_PERFORM 0x00090000 + +// +// BA_RJT Reason Explanation Codes +// +#define BA_NO_REASON 0x00000000 +#define BA_INVALID_OX_RX 0x00000300 +#define BA_SEQUENCE_ABORTED 0x00000500 + + + +#endif /* CPQFCTSSTRUCTS_H */ + diff --git a/drivers/scsi/cpqfcTStrigger.c b/drivers/scsi/cpqfcTStrigger.c new file mode 100644 index 000000000000..4220d9d923e8 --- /dev/null +++ b/drivers/scsi/cpqfcTStrigger.c @@ -0,0 +1,30 @@ +// Routine to trigger Finisar GTA analyzer. Runs of GPIO2 +// NOTE: DEBUG ONLY! Could interfere with FCMNGR/Miniport operation +// since it writes directly to the Tachyon board. This function +// developed for Compaq HBA Tachyon TS v1.2 (Rev X5 PCB) + +#include +#include +#include +#include +#include + + +void TriggerHBA( void* IOBaseUpper, int Print) +{ + __u32 long value; + + // get initial value in hopes of not modifying any other GPIO line + IOBaseUpper += 0x188; // TachTL/TS Control reg + + value = readl( IOBaseUpper); + // set HIGH to trigger external analyzer (tested on Dolche Finisar 1Gb GTA) + // The Finisar anaylzer triggers on low-to-high TTL transition + value |= 0x01; // set bit 0 + + writel( value, IOBaseUpper); + + if( Print) + printk( " -GPIO0 set- "); +} + diff --git a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c new file mode 100644 index 000000000000..d3416ab6d7df --- /dev/null +++ b/drivers/scsi/cpqfcTSworker.c @@ -0,0 +1,6238 @@ +/* Copyright(c) 2000, Compaq Computer Corporation + * Fibre Channel Host Bus Adapter + * 64-bit, 66MHz PCI + * Originally developed and tested on: + * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... + * SP# P225CXCBFIEL6T, Rev XC + * SP# 161290-001, Rev XD + * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 + * + * 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, 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. + * Written by Don Zimmerman +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __KERNEL_SYSCALLS__ + +#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) + +#include + +#include +#include +#include + + + +#include "sd.h" +#include "hosts.h" // struct Scsi_Host definition for T handler +#include "cpqfcTSchip.h" +#include "cpqfcTSstructs.h" + +//#define LOGIN_DBG 1 + +// REMARKS: +// Since Tachyon chips may be permitted to wait from 500ms up to 2 sec +// to empty an outgoing frame from its FIFO to the Fibre Channel stream, +// we cannot do everything we need to in the interrupt handler. Specifically, +// every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be +// suspended until the login sequences have been completed. Login commands +// are frames just like SCSI commands are frames; they are subject to the same +// timeout issues and delays. Also, various specs provide up to 2 seconds for +// devices to log back in (i.e. respond with ACC to a login frame), so I/O to +// that device has to be suspended. +// A serious problem here occurs on highly loaded FC-AL systems. If our FC port +// has a low priority (e.g. high arbitrated loop physical address, alpa), and +// some other device is hogging bandwidth (permissible under FC-AL), we might +// time out thinking the link is hung, when it's simply busy. Many such +// considerations complicate the design. Although Tachyon assumes control +// (in silicon) for many link-specific issues, the Linux driver is left with the +// rest, which turns out to be a difficult, time critical chore. + +// These "worker" functions will handle things like FC Logins; all +// processes with I/O to our device must wait for the Login to complete +// and (if successful) I/O to resume. In the event of a malfunctioning or +// very busy loop, it may take hundreds of millisecs or even seconds to complete +// a frame send. We don't want to hang up the entire server (and all +// processes which don't depend on Fibre) during this wait. + +// The Tachyon chip can have around 30,000 I/O operations ("exchanges") +// open at one time. However, each exchange must be initiated +// synchronously (i.e. each of the 30k I/O had to be started one at a +// time by sending a starting frame via Tachyon's outbound que). + +// To accomodate kernel "module" build, this driver limits the exchanges +// to 256, because of the contiguous physical memory limitation of 128M. + +// Typical FC Exchanges are opened presuming the FC frames start without errors, +// while Exchange completion is handled in the interrupt handler. This +// optimizes performance for the "everything's working" case. +// However, when we have FC related errors or hot plugging of FC ports, we pause +// I/O and handle FC-specific tasks in the worker thread. These FC-specific +// functions will handle things like FC Logins and Aborts. As the Login sequence +// completes to each and every target, I/O can resume to that target. + +// Our kernel "worker thread" must share the HBA with threads calling +// "queuecommand". We define a "BoardLock" semaphore which indicates +// to "queuecommand" that the HBA is unavailable, and Cmnds are added to a +// board lock Q. When the worker thread finishes with the board, the board +// lock Q commands are completed with status causing immediate retry. +// Typically, the board is locked while Logins are in progress after an +// FC Link Down condition. When Cmnds are re-queued after board lock, the +// particular Scsi channel/target may or may not have logged back in. When +// the device is waiting for login, the "prli" flag is clear, in which case +// commands are passed to a Link Down Q. Whenever the login finally completes, +// the LinkDown Q is completed, again with status causing immediate retry. +// When FC devices are logged in, we build and start FC commands to the +// devices. + +// NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices +// that never log back in (e.g. physically removed) is NOT completely +// understood. I've still seen instances of system hangs on failed Write +// commands (possibly from the ext2 layer?) on device removal. Such special +// cases need to be evaluated from a system/application view - e.g., how +// exactly does the system want me to complete commands when the device is +// physically removed?? + +// local functions + +static void SetLoginFields( + PFC_LOGGEDIN_PORT pLoggedInPort, + TachFCHDR_GCMND* fchs, + BOOLEAN PDisc, + BOOLEAN Originator); + +static void AnalyzeIncomingFrame( + CPQFCHBA *cpqfcHBAdata, + ULONG QNdx ); + +static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds ); + +static int verify_PLOGI( PTACHYON fcChip, + TachFCHDR_GCMND* fchs, ULONG* reject_explain); +static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain); + +static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type); +static void BuildLinkServicePayload( + PTACHYON fcChip, ULONG type, void* payload); + +static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, + PFC_LOGGEDIN_PORT pLoggedInPort); + +static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID); + +static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata); + +static void RevalidateSEST( struct Scsi_Host *HostAdapter, + PFC_LOGGEDIN_PORT pLoggedInPort); + +static void IssueReportLunsCommand( + CPQFCHBA* cpqfcHBAdata, + TachFCHDR_GCMND* fchs); + + +// (see scsi_error.c comments on kernel task creation) + +void cpqfcTSWorkerThread( void *host) +{ + struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; +#ifdef PCI_KERNEL_TRACE + PTACHYON fcChip = &cpqfcHBAdata->fcChip; +#endif + struct fs_struct *fs; + DECLARE_MUTEX_LOCKED(fcQueReady); + DECLARE_MUTEX_LOCKED(fcTYOBcomplete); + DECLARE_MUTEX_LOCKED(TachFrozen); + DECLARE_MUTEX_LOCKED(BoardLock); + + ENTER("WorkerThread"); + + lock_kernel(); + /* + * If we were started as result of loading a module, close all of the + * user space pages. We don't need them, and if we didn't close them + * they would be locked into memory. + */ + exit_mm(current); + + current->session = 1; + current->pgrp = 1; + + /* Become as one with the init task */ + + exit_fs(current); /* current->fs->count--; */ + fs = init_task.fs; + // Some kernels compiled for SMP, while actually running + // on a uniproc machine, will return NULL for this call + if( !fs) + { + printk(" cpqfcTS FATAL: fs is NULL! Is this an SMP kernel on uniproc machine?\n "); + } + + else + { + current->fs = fs; + atomic_inc(&fs->count); + } + + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); + + + /* + * Set the name of this process. + */ + sprintf(current->comm, "cpqfcTS_wt_%d", HostAdapter->host_no); + + cpqfcHBAdata->fcQueReady = &fcQueReady; // primary wait point + cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete; + cpqfcHBAdata->TachFrozen = &TachFrozen; + + + cpqfcHBAdata->worker_thread = current; + + unlock_kernel(); + + if( cpqfcHBAdata->notify_wt != NULL ) + up( cpqfcHBAdata->notify_wt); // OK to continue + + while(1) + { + unsigned long flags; + + down_interruptible( &fcQueReady); // wait for something to do + + if (signal_pending(current) ) + break; + + PCI_TRACE( 0x90) + // first, take the IO lock so the SCSI upper layers can't call + // into our _quecommand function (this also disables INTs) + spin_lock_irqsave( &io_request_lock, flags); // STOP _que function + PCI_TRACE( 0x90) + + CPQ_SPINLOCK_HBA( cpqfcHBAdata) + // next, set this pointer to indicate to the _quecommand function + // that the board is in use, so it should que the command and + // immediately return (we don't actually require the semaphore function + // in this driver rev) + + cpqfcHBAdata->BoardLock = &BoardLock; + + PCI_TRACE( 0x90) + + // release the IO lock (and re-enable interrupts) + spin_unlock_irqrestore( &io_request_lock, flags); + + // disable OUR HBA interrupt (keep them off as much as possible + // during error recovery) + disable_irq( cpqfcHBAdata->HostAdapter->irq); + + // OK, let's process the Fibre Channel Link Q and do the work + cpqfcTS_WorkTask( HostAdapter); + + // hopefully, no more "work" to do; + // re-enable our INTs for "normal" completion processing + enable_irq( cpqfcHBAdata->HostAdapter->irq); + + + cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued + CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) + + + // Now, complete any Cmnd we Q'd up while BoardLock was held + + CompleteBoardLockCmnd( cpqfcHBAdata); + + + } + // hopefully, the signal was for our module exit... + if( cpqfcHBAdata->notify_wt != NULL ) + up( cpqfcHBAdata->notify_wt); // yep, we're outta here +} + + +// Freeze Tachyon routine. +// If Tachyon is already frozen, return FALSE +// If Tachyon is not frozen, call freeze function, return TRUE +// +static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + BOOLEAN FrozeTach = FALSE; + // It's possible that the chip is already frozen; if so, + // "Freezing" again will NOT! generate another Freeze + // Completion Message. + + if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000) + { // (need to freeze...) + fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists + + // 2. Get Tach freeze confirmation + // (synchronize SEST manipulation with Freeze Completion Message) + // we need INTs on so semaphore can be set. + enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore + down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem. + // can we TIMEOUT semaphore wait?? TBD + disable_irq( cpqfcHBAdata->HostAdapter->irq); + + FrozeTach = TRUE; + } // (else, already frozen) + + return FrozeTach; +} + + + + +// This is the kernel worker thread task, which processes FC +// tasks which were queued by the Interrupt handler or by +// other WorkTask functions. + +#define DBG 1 +//#undef DBG +void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG QconsumerNdx; + LONG ExchangeID; + ULONG ulStatus=0; + TachFCHDR_GCMND fchs; + PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; + + ENTER("WorkTask"); + + // copy current index to work on + QconsumerNdx = fcLQ->consumer; + + PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90) + + + // NOTE: when this switch completes, we will "consume" the Que item +// printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type); + switch( fcLQ->Qitem[QconsumerNdx].Type ) + { + // incoming frame - link service (ACC, UNSOL REQ, etc.) + // or FCP-SCSI command + case SFQ_UNKNOWN: + AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx ); + + break; + + + + case EXCHANGE_QUEUED: // an Exchange (i.e. FCP-SCSI) was previously + // Queued because the link was down. The + // heartbeat timer detected it and Queued it here. + // We attempt to start it again, and if + // successful we clear the EXCHANGE_Q flag. + // If the link doesn't come up, the Exchange + // will eventually time-out. + + ExchangeID = (LONG) // x_ID copied from DPC timeout function + fcLQ->Qitem[QconsumerNdx].ulBuff[0]; + + // It's possible that a Q'd exchange could have already + // been started by other logic (e.g. ABTS process) + // Don't start if already started (Q'd flag clear) + + if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED ) + { +// printk(" *Start Q'd x_ID %Xh: type %Xh ", +// ExchangeID, Exchanges->fcExchange[ExchangeID].type); + + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID); + if( !ulStatus ) + { +// printk("success* "); + } + else + { +#ifdef DBG + + if( ulStatus == EXCHANGE_QUEUED) + printk("Queued* "); + else + printk("failed* "); + +#endif + } + } + break; + + + case LINKDOWN: + // (lots of things already done in INT handler) future here? + break; + + + case LINKACTIVE: // Tachyon set the Lup bit in FM status + // NOTE: some misbehaving FC ports (like Tach2.1) + // can re-LIP immediately after a LIP completes. + + // if "initiator", need to verify LOGs with ports +// printk("\n*LNKUP* "); + + if( fcChip->Options.initiator ) + SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data + // if SendLogins successfully completes, PortDiscDone + // will be set. + + + // If SendLogins was successful, then we expect to get incoming + // ACCepts or REJECTs, which are handled below. + + break; + + // LinkService and Fabric request/reply processing + case ELS_FDISC: // need to send Fabric Discovery (Login) + case ELS_FLOGI: // need to send Fabric Login + case ELS_SCR: // need to send State Change Registration + case FCS_NSR: // need to send Name Service Request + case ELS_PLOGI: // need to send PLOGI + case ELS_ACC: // send generic ACCept + case ELS_PLOGI_ACC: // need to send ELS ACCept frame to recv'd PLOGI + case ELS_PRLI_ACC: // need to send ELS ACCept frame to recv'd PRLI + case ELS_LOGO: // need to send ELS LOGO (logout) + case ELS_LOGO_ACC: // need to send ELS ACCept frame to recv'd PLOGI + case ELS_RJT: // ReJecT reply + case ELS_PRLI: // need to send ELS PRLI + + +// printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type); + // if PortDiscDone is not set, it means the SendLogins routine + // failed to complete -- assume that LDn occured, so login frames + // are invalid + if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn + { + printk("Discard Q'd ELS login frame\n"); + break; + } + + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI + (TachFCHDR_GCMND*) + fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs + NULL, // no data (no scatter/gather list) + &ExchangeID );// fcController->fcExchanges index, -1 if failed + + if( !ulStatus ) // Exchange setup? + { + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); + if( !ulStatus ) + { + // submitted to Tach's Outbound Que (ERQ PI incremented) + // waited for completion for ELS type (Login frames issued + // synchronously) + } + else + // check reason for Exchange not being started - we might + // want to Queue and start later, or fail with error + { + + } + } + + else // Xchange setup failed... + printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); + + break; + + case SCSI_REPORT_LUNS: + // pass the incoming frame (actually, it's a PRLI frame) + // so we can send REPORT_LUNS, in order to determine VSA/PDU + // FCP-SCSI Lun address mode + IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*) + fcLQ->Qitem[QconsumerNdx].ulBuff); + + break; + + + + + case BLS_ABTS: // need to ABORT one or more exchanges + { + LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0]; + BOOLEAN FrozeTach = FALSE; + + if( x_ID > TACH_SEST_LEN ) // (in)sanity check + { +// printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID); + break; + } + + + if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE + { +// printk(" ABTS %Xh Scsi Cmnd null! ", x_ID); + + break; // nothing to abort! + } + +//#define ABTS_DBG +#ifdef ABTS_DBG + printk("INV SEST[%X] ", x_ID); + if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT) + { + printk("FC2TO"); + } + if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT) + { + printk("IA"); + } + if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED) + { + printk("PORTID"); + } + if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) + { + printk("DEVRM"); + } + if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX) + { + printk("LKF"); + } + if( Exchanges->fcExchange[x_ID].status & FRAME_TO) + { + printk("FRMTO"); + } + if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY) + { + printk("ABSQ"); + } + if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME) + { + printk("SFQFR"); + } + + if( Exchanges->fcExchange[ x_ID].type == 0x2000) + printk(" WR"); + else if( Exchanges->fcExchange[ x_ID].type == 0x3000) + printk(" RD"); + else if( Exchanges->fcExchange[ x_ID].type == 0x10) + printk(" ABTS"); + else + printk(" %Xh", Exchanges->fcExchange[ x_ID].type); + + if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)) + { + printk(" Cmd %p, ", + Exchanges->fcExchange[ x_ID].Cmnd); + + printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n", + cpqfcHBAdata->HBAnum, + Exchanges->fcExchange[ x_ID].Cmnd->channel, + Exchanges->fcExchange[ x_ID].Cmnd->target, + Exchanges->fcExchange[ x_ID].Cmnd->lun, + Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF); + } + else // assume that Cmnd ptr is invalid on _abort() + { + printk(" Cmd ptr invalid\n"); + } + +#endif + + + // Steps to ABORT a SEST exchange: + // 1. Freeze TL SCSI assists & ERQ (everything) + // 2. Receive FROZEN inbound CM (must succeed!) + // 3. Invalidate x_ID SEST entry + // 4. Resume TL SCSI assists & ERQ (everything) + // 5. Build/start on exchange - change "type" to BLS_ABTS, + // timeout to X sec (RA_TOV from PLDA is actually 0) + // 6. Set Exchange Q'd status if ABTS cannot be started, + // or simply complete Exchange in "Terminate" condition + + PCI_TRACEO( x_ID, 0xB4) + + // 1 & 2 . Freeze Tach & get confirmation of freeze + FrozeTach = FreezeTach( cpqfcHBAdata); + + // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange. + // FC2_TIMEOUT means we are originating the abort, while + // TARGET_ABORT means we are ACCepting an abort. + // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are + // all from Tachyon: + // Exchange was corrupted by LDn or other FC physical failure + // INITIATOR_ABORT means the upper layer driver/application + // requested the abort. + + + + // clear bit 31 (VALid), to invalidate & take control from TL + fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; + + + // examine and Tach's "Linked List" for IWEs that + // received (nearly) simultaneous transfer ready (XRDY) + // repair linked list if necessary (TBD!) + // (If we ignore the "Linked List", we will time out + // WRITE commands where we received the FCP-SCSI XFRDY + // frame (because Tachyon didn't processes it). Linked List + // management should be done as an optimization. + +// readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST )); + + + + + // 4. Resume all Tachlite functions (for other open Exchanges) + // as quickly as possible to allow other exchanges to other ports + // to resume. Freezing Tachyon may cause cascading errors, because + // any received SEST frame cannot be processed by the SEST. + // Don't "unfreeze" unless Link is operational + if( FrozeTach ) // did we just freeze it (above)? + fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists + + + PCI_TRACEO( x_ID, 0xB4) + + // Note there is no confirmation that the chip is "unfrozen". Also, + // if the Link is down when unfreeze is called, it has no effect. + // Chip will unfreeze when the Link is back up. + + // 5. Now send out Abort commands if possible + // Some Aborts can't be "sent" (Port_id changed or gone); + // if the device is gone, there is no port_id to send the ABTS to. + + if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED) + && + !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) ) + { + Exchanges->fcExchange[ x_ID].type = BLS_ABTS; + fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id; + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + BLS_ABTS, + &fchs, // (uses only s_id) + NULL, // (no scatter/gather list for ABTS) + &x_ID );// ABTS on this Exchange ID + + if( !ulStatus ) // Exchange setup build OK? + { + + // ABTS may be needed because an Exchange was corrupted + // by a Link disruption. If the Link is UP, we can + // presume that this ABTS can start immediately; otherwise, + // set Que'd status so the Login functions + // can restart it when the FC physical Link is restored + if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init? + { +// printk(" *set Q status x_ID %Xh on LDn* ", x_ID); + Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED; + } + + else // what FC device (port_id) does the Cmd belong to? + { + PFC_LOGGEDIN_PORT pLoggedInPort = + Exchanges->fcExchange[ x_ID].pLoggedInPort; + + // if Port is logged in, we might start the abort. + + if( (pLoggedInPort != NULL) + && + (pLoggedInPort->prli == TRUE) ) + { + // it's possible that an Exchange has already been Queued + // to start after Login completes. Check and don't + // start it (again) here if Q'd status set +// printk(" ABTS xchg %Xh ", x_ID); + if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED) + { +// printk("already Q'd "); + } + else + { +// printk("starting "); + + fcChip->fcStats.FC2aborted++; + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID ); + if( !ulStatus ) + { + // OK + // submitted to Tach's Outbound Que (ERQ PI incremented) + } + else + { +/* printk("ABTS exchange start failed -status %Xh, x_ID %Xh ", + ulStatus, x_ID); +*/ + } + } + } + else + { +/* printk(" ABTS NOT starting xchg %Xh, %p ", + x_ID, pLoggedInPort); + if( pLoggedInPort ) + printk("prli %d ", pLoggedInPort->prli); +*/ + } + } + } + else // what the #@! + { // how do we fail to build an Exchange for ABTS?? + printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n", + ulStatus, x_ID); + } + } + else // abort without ABTS -- just complete exchange/Cmnd to Linux + { +// printk(" *Terminating x_ID %Xh on %Xh* ", +// x_ID, Exchanges->fcExchange[x_ID].status); + cpqfcTSCompleteExchange( fcChip, x_ID); + + + } + } // end of ABTS case + break; + + + + case BLS_ABTS_ACC: // need to ACCept one ABTS + // (NOTE! this code not updated for Linux yet..) + + + printk(" *ABTS_ACC* "); + // 1. Freeze TL + + fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists + + memcpy( // copy the incoming ABTS frame + &fchs, + fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs + sizeof( fchs)); + + // 3. OK, Tachyon is frozen so we can invalidate SEST entry + // (if necessary) + // Status FC2_TIMEOUT means we are originating the abort, while + // TARGET_ABORT means we are ACCepting an abort + + ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange +// printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID); + + + // sanity check on received ExchangeID + if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT ) + { + // clear bit 31 (VALid), to invalidate & take control from TL +// printk("Invalidating SEST exchange %Xh\n", ExchangeID); + fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF; + } + + + // 4. Resume all Tachlite functions (for other open Exchanges) + // as quickly as possible to allow other exchanges to other ports + // to resume. Freezing Tachyon for too long may royally screw + // up everything! + fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists + + // Note there is no confirmation that the chip is "unfrozen". Also, + // if the Link is down when unfreeze is called, it has no effect. + // Chip will unfreeze when the Link is back up. + + // 5. Now send out Abort ACC reply for this exchange + Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC; + + fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id; + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + BLS_ABTS_ACC, + &fchs, + NULL, // no data (no scatter/gather list) + &ExchangeID );// fcController->fcExchanges index, -1 if failed + + if( !ulStatus ) // Exchange setup? + { + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); + if( !ulStatus ) + { + // submitted to Tach's Outbound Que (ERQ PI incremented) + // waited for completion for ELS type (Login frames issued + // synchronously) + } + else + // check reason for Exchange not being started - we might + // want to Queue and start later, or fail with error + { + + } + } + break; + + + case BLS_ABTS_RJT: // need to ReJecT one ABTS; reject implies the + // exchange doesn't exist in the TARGET context. + // ExchangeID has to come from LinkService space. + + printk(" *ABTS_RJT* "); + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + BLS_ABTS_RJT, + (TachFCHDR_GCMND*) + fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs + NULL, // no data (no scatter/gather list) + &ExchangeID );// fcController->fcExchanges index, -1 if failed + + if( !ulStatus ) // Exchange setup OK? + { + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); + // If it fails, we aren't required to retry. + } + if( ulStatus ) + { + printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID); + } + else + { + printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID); + + } + + break; + + + + default: + break; + } // end switch +//doNothing: + // done with this item - now set the NEXT index + + if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test + { + fcLQ->consumer = 0; + } + else + { + fcLQ->consumer++; + } + + PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94) + + LEAVE("WorkTask"); + return; +} + + + + +// When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login) +// commands come in, post to the LinkQ so that action can be taken outside the +// interrupt handler. +// This circular Q works like Tachyon's que - the producer points to the next +// (unused) entry. Called by Interrupt handler, WorkerThread, Timer +// sputlinkq +void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata, + int Type, + void *QueContent) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; +// FC_EXCHANGES *Exchanges = fcChip->Exchanges; + PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; + ULONG ndx; + + ENTER("cpqfcTSPutLinkQ"); + + ndx = fcLQ->producer; + + ndx += 1; // test for Que full + + + + if( ndx >= FC_LINKQ_DEPTH ) // rollover test + ndx = 0; + + if( ndx == fcLQ->consumer ) // QUE full test + { + // QUE was full! lost LK command (fatal to logic) + fcChip->fcStats.lnkQueFull++; + + printk("*LinkQ Full!*"); + TriggerHBA( fcChip->Registers.ReMapMemBase, 1); +/* + { + int i; + printk("LinkQ PI %d, CI %d\n", fcLQ->producer, + fcLQ->consumer); + + for( i=0; i< FC_LINKQ_DEPTH; ) + { + printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type); + if( (++i %8) == 0) printk("\n"); + } + + } +*/ + printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung + } + else // QUE next element + { + // Prevent certain multiple (back-to-back) requests. + // This is important in that we don't want to issue multiple + // ABTS for the same Exchange, or do multiple FM inits, etc. + // We can never be sure of the timing of events reported to + // us by Tach's IMQ, which can depend on system/bus speeds, + // FC physical link circumstances, etc. + + if( (fcLQ->producer != fcLQ->consumer) + && + (Type == FMINIT) ) + { + LONG lastNdx; // compute previous producer index + if( fcLQ->producer) + lastNdx = fcLQ->producer- 1; + else + lastNdx = FC_LINKQ_DEPTH-1; + + + if( fcLQ->Qitem[lastNdx].Type == FMINIT) + { +// printk(" *skip FMINIT Q post* "); +// goto DoneWithPutQ; + } + + } + + // OK, add the Q'd item... + + fcLQ->Qitem[fcLQ->producer].Type = Type; + + memcpy( + fcLQ->Qitem[fcLQ->producer].ulBuff, + QueContent, + sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff)); + + fcLQ->producer = ndx; // increment Que producer + + // set semaphore to wake up Kernel (worker) thread + // + up( cpqfcHBAdata->fcQueReady ); + } + +//DoneWithPutQ: + + LEAVE("cpqfcTSPutLinkQ"); +} + + + + +// reset device ext FC link Q +void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata) + +{ + PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; + fcLQ->producer = 0; + fcLQ->consumer = 0; + +} + + + + + +// When Tachyon gets an unassisted FCP-SCSI frame, post here so +// an arbitrary context thread (e.g. IOCTL loopback test function) +// can process it. + +// (NOTE: Not revised for Linux) +// This Q works like Tachyon's que - the producer points to the next +// (unused) entry. +void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata, + int Type, + void *QueContent) +{ +// CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; +// PTACHYON fcChip = &cpqfcHBAdata->fcChip; + +// ULONG ndx; + +// ULONG *pExchangeID; +// LONG ExchangeID; + +/* + KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock); + ndx = pDevExt->fcScsiQue.producer + 1; // test for Que full + + if( ndx >= FC_SCSIQ_DEPTH ) // rollover test + ndx = 0; + + if( ndx == pDevExt->fcScsiQue.consumer ) // QUE full test + { + // QUE was full! lost LK command (fatal to logic) + fcChip->fcStats.ScsiQueFull++; +#ifdef DBG + printk( "fcPutScsiQue - FULL!\n"); +#endif + + } + else // QUE next element + { + pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type; + + if( Type == FCP_RSP ) + { + // this TL inbound message type means that a TL SEST exchange has + // copied an FCP response frame into a buffer pointed to by the SEST + // entry. That buffer is allocated in the SEST structure at ->RspHDR. + // Copy the RspHDR for use by the Que handler. + pExchangeID = (ULONG *)QueContent; + + memcpy( + pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff, + &fcChip->SEST->RspHDR[ *pExchangeID ], + sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size) + + } + else + { + memcpy( + pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff, + QueContent, + sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff)); + } + + pDevExt->fcScsiQue.producer = ndx; // increment Que + + + KeSetEvent( &pDevExt->TYIBscsi, // signal any waiting thread + 0, // no priority boost + FALSE ); // no waiting later for this event + } + KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock); +*/ +} + + + + + + + +static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*); + +static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*); + +static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*); + +void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata, + PFC_LOGGEDIN_PORT pFcPort) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + + if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric + { + fcChip->fcStats.logouts++; + printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n", + (ULONG)pFcPort->u.liWWN, + (ULONG)(pFcPort->u.liWWN >>32), + pFcPort->port_id); + + // Terminate I/O with this (Linux) Scsi target + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pFcPort->ScsiNexus, + DEVICE_REMOVED); + } + + // Do an "implicit logout" - we can't really Logout the device + // (i.e. with LOGOut Request) because of port_id confusion + // (i.e. the Other port has no port_id). + // A new login for that WWN will have to re-write port_id (0 invalid) + pFcPort->port_id = 0; // invalid! + pFcPort->pdisc = FALSE; + pFcPort->prli = FALSE; + pFcPort->plogi = FALSE; + pFcPort->flogi = FALSE; + pFcPort->LOGO_timer = 0; + pFcPort->device_blocked = TRUE; // block Scsi Requests +} + + +// On FC-AL, there is a chance that a previously known device can +// be quietly removed (e.g. with non-managed hub), +// while a NEW device (with different WWN) took the same alpa or +// even 24-bit port_id. This chance is unlikely but we must always +// check for it. +static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata, + PFC_LOGGEDIN_PORT pLoggedInPort) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + // set "other port" at beginning of fcPorts list + PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort; + while( pOtherPortWithPortId ) + { + if( (pOtherPortWithPortId->port_id == + pLoggedInPort->port_id) + && + (pOtherPortWithPortId != pLoggedInPort) ) + { + // trouble! (Implicitly) Log the other guy out + printk(" *port_id %Xh is duplicated!* ", + pOtherPortWithPortId->port_id); + cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId); + } + pOtherPortWithPortId = pOtherPortWithPortId->pNextPort; + } +} + + + + + + +// Dynamic Memory Allocation for newly discovered FC Ports. +// For simplicity, maintain fcPorts structs for ALL +// for discovered devices, including those we never do I/O with +// (e.g. Fabric addresses) + +static PFC_LOGGEDIN_PORT CreateFcPort( + CPQFCHBA* cpqfcHBAdata, + PFC_LOGGEDIN_PORT pLastLoggedInPort, + TachFCHDR_GCMND* fchs, + LOGIN_PAYLOAD* plogi) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL; + int i; + + + printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id); + for( i=3; i>=0; i--) // copy the LOGIN port's WWN + printk("%02X", plogi->port_name[i]); + for( i=7; i>3; i--) // copy the LOGIN port's WWN + printk("%02X", plogi->port_name[i]); + + + // allocate mem for new port + // (these are small and rare allocations...) + pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC ); + + + // allocation succeeded? Fill out NEW PORT + if( pNextLoggedInPort ) + { + // clear out any garbage (sometimes exists) + memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT)); + + + // If we login to a Fabric, we don't want to treat it + // as a SCSI device... + if( (fchs->s_id & 0xFFF000) != 0xFFF000) + { + int i; + + // create a unique "virtual" SCSI Nexus (for now, just a + // new target ID) -- we will update channel/target on REPORT_LUNS + // special case for very first SCSI target... + if( cpqfcHBAdata->HostAdapter->max_id == 0) + { + pNextLoggedInPort->ScsiNexus.target = 0; + fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub" + } + else + { + pNextLoggedInPort->ScsiNexus.target = + cpqfcHBAdata->HostAdapter->max_id; + } + + // initialize the lun[] Nexus struct for lun masking + for( i=0; i< CPQFCTS_MAX_LUN; i++) + pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED + + pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port + + printk(" SCSI Chan/Trgt %d/%d", + pNextLoggedInPort->ScsiNexus.channel, + pNextLoggedInPort->ScsiNexus.target); + + // tell Scsi layers about the new target... + cpqfcHBAdata->HostAdapter->max_id++; +// printk("HostAdapter->max_id = %d\n", +// cpqfcHBAdata->HostAdapter->max_id); + } + else + { + // device is NOT SCSI (in case of Fabric) + pNextLoggedInPort->ScsiNexus.target = -1; // invalid + } + + // create forward link to new port + pLastLoggedInPort->pNextPort = pNextLoggedInPort; + printk("\n"); + + } + return pNextLoggedInPort; // NULL on allocation failure +} // end NEW PORT (WWN) logic + + + +// For certain cases, we want to terminate exchanges without +// sending ABTS to the device. Examples include when an FC +// device changed it's port_id after Loop re-init, or when +// the device sent us a logout. In the case of changed port_id, +// we want to complete the command and return SOFT_ERROR to +// force a re-try. In the case of LOGOut, we might return +// BAD_TARGET if the device is really gone. +// Since we must ensure that Tachyon is not operating on the +// exchange, we have to freeze the chip +// sterminateex +void cpqfcTSTerminateExchange( + CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG x_ID; + + if( ScsiNexus ) + { +// printk("TerminateExchange: ScsiNexus chan/target %d/%d\n", +// ScsiNexus->channel, ScsiNexus->target); + + } + + for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) + { + if( Exchanges->fcExchange[x_ID].type ) // in use? + { + if( ScsiNexus == NULL ) // our HBA changed - term. all + { + Exchanges->fcExchange[x_ID].status = TerminateStatus; + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); + } + else + { + // If a device, according to WWN, has been removed, it's + // port_id may be used by another working device, so we + // have to terminate by SCSI target, NOT port_id. + if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress? + { + if( (Exchanges->fcExchange[x_ID].Cmnd->target == ScsiNexus->target) + && + (Exchanges->fcExchange[x_ID].Cmnd->channel == ScsiNexus->channel)) + { + Exchanges->fcExchange[x_ID].status = TerminateStatus; + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out + } + } + + // (in case we ever need it...) + // all SEST structures have a remote node ID at SEST DWORD 2 + // if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8) + // == port_id) + } + } + } +} + + +static void ProcessELS_Request( + CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; +// FC_EXCHANGES *Exchanges = fcChip->Exchanges; +// ULONG ox_id = (fchs->ox_rx_id >>16); + PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort; + BOOLEAN NeedReject = FALSE; + ULONG ls_reject_code = 0; // default don'n know?? + + + // Check the incoming frame for a supported ELS type + switch( fchs->pl[0] & 0xFFFF) + { + case 0x0050: // PDISC? + + // Payload for PLOGI and PDISC is identical (request & reply) + if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload? + { + LOGIN_PAYLOAD logi; // FC-PH Port Login + + // PDISC payload OK. If critical login fields + // (e.g. WWN) matches last login for this port_id, + // we may resume any prior exchanges + // with the other port + + + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + 0, // don't search linked list for port_id + &logi.port_name[0], // search linked list for WWN + &pLastLoggedInPort); // must return non-NULL; when a port_id + // is not found, this pointer marks the + // end of the singly linked list + + if( pLoggedInPort != NULL) // WWN found (prior login OK) + { + + if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id) + { + // Yes. We were expecting PDISC? + if( pLoggedInPort->pdisc ) + { + // Yes; set fields accordingly. (PDISC, not Originator) + SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE); + + // send 'ACC' reply + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC) + fchs ); + + // OK to resume I/O... + } + else + { + printk("Not expecting PDISC (pdisc=FALSE)\n"); + NeedReject = TRUE; + // set reject reason code + ls_reject_code = + LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); + } + } + else + { + if( pLoggedInPort->port_id != 0) + { + printk("PDISC PortID change: old %Xh, new %Xh\n", + pLoggedInPort->port_id, fchs->s_id &0xFFFFFF); + } + NeedReject = TRUE; + // set reject reason code + ls_reject_code = + LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); + + } + } + else + { + printk("PDISC Request from unknown WWN\n"); + NeedReject = TRUE; + + // set reject reason code + ls_reject_code = + LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME); + } + + } + else // Payload unacceptable + { + printk("payload unacceptable\n"); + NeedReject = TRUE; // reject code already set + + } + + if( NeedReject) + { + ULONG port_id; + // The PDISC failed. Set login struct flags accordingly, + // terminate any I/O to this port, and Q a PLOGI + if( pLoggedInPort ) + { + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; + pLoggedInPort->plogi = FALSE; + + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pLoggedInPort->ScsiNexus, PORTID_CHANGED); + port_id = pLoggedInPort->port_id; + } + else + { + port_id = fchs->s_id &0xFFFFFF; + } + fchs->reserved = ls_reject_code; // borrow this (unused) field + cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs ); + } + + break; + + + + case 0x0003: // PLOGI? + + // Payload for PLOGI and PDISC is identical (request & reply) + if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload? + { + LOGIN_PAYLOAD logi; // FC-PH Port Login + BOOLEAN NeedReject = FALSE; + + // PDISC payload OK. If critical login fields + // (e.g. WWN) matches last login for this port_id, + // we may resume any prior exchanges + // with the other port + + + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + 0, // don't search linked list for port_id + &logi.port_name[0], // search linked list for WWN + &pLastLoggedInPort); // must return non-NULL; when a port_id + // is not found, this pointer marks the + // end of the singly linked list + + if( pLoggedInPort == NULL) // WWN not found -New Port + { + pLoggedInPort = CreateFcPort( + cpqfcHBAdata, + pLastLoggedInPort, + fchs, + &logi); + if( pLoggedInPort == NULL ) + { + printk(" cpqfcTS: New port allocation failed - lost FC device!\n"); + // Now Q a LOGOut Request, since we won't be talking to that device + + NeedReject = TRUE; + + // set reject reason code + ls_reject_code = + LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES); + + } + } + if( !NeedReject ) + { + + // OK - we have valid fcPort ptr; set fields accordingly. + // (not PDISC, not Originator) + SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); + + // send 'ACC' reply + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC) + fchs ); + } + } + else // Payload unacceptable + { + printk("payload unacceptable\n"); + NeedReject = TRUE; // reject code already set + } + + if( NeedReject) + { + // The PDISC failed. Set login struct flags accordingly, + // terminate any I/O to this port, and Q a PLOGI + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; + pLoggedInPort->plogi = FALSE; + + fchs->reserved = ls_reject_code; // borrow this (unused) field + + // send 'RJT' reply + cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs ); + } + + // terminate any exchanges with this device... + if( pLoggedInPort ) + { + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pLoggedInPort->ScsiNexus, PORTID_CHANGED); + } + break; + + + + case 0x1020: // PRLI? + { + BOOLEAN NeedReject = TRUE; + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + (fchs->s_id & 0xFFFFFF), // search linked list for port_id + NULL, // DON'T search linked list for WWN + NULL); // don't care + + if( pLoggedInPort == NULL ) + { + // huh? + printk(" Unexpected PRLI Request -not logged in!\n"); + + // set reject reason code + ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); + + // Q a LOGOut here? + } + else + { + // verify the PRLI ACC payload + if( !verify_PRLI( fchs, &ls_reject_code) ) + { + // PRLI Reply is acceptable; were we expecting it? + if( pLoggedInPort->plogi ) + { + // yes, we expected the PRLI ACC (not PDISC; not Originator) + SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); + + // Q an ACCept Reply + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_PRLI_ACC, + fchs ); + + NeedReject = FALSE; + } + else + { + // huh? + printk(" (unexpected) PRLI REQEST with plogi FALSE\n"); + + // set reject reason code + ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); + + // Q a LOGOut here? + + } + } + else + { + printk(" PRLI REQUEST payload failed verify\n"); + // (reject code set by "verify") + + // Q a LOGOut here? + } + } + + if( NeedReject ) + { + // Q a ReJecT Reply with reason code + fchs->reserved = ls_reject_code; + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_RJT, // Q Type + fchs ); + } + } + break; + + + + + case 0x0005: // LOGOut? + { + // was this LOGOUT because we sent a ELS_PDISC to an FC device + // with changed (or new) port_id, or does the port refuse + // to communicate to us? + // We maintain a logout counter - if we get 3 consecutive LOGOuts, + // give up! + LOGOUT_PAYLOAD logo; + BOOLEAN GiveUpOnDevice = FALSE; + ULONG ls_reject_code = 0; + + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo)); + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + 0, // don't search linked list for port_id + &logo.port_name[0], // search linked list for WWN + NULL); // don't care about end of list + + if( pLoggedInPort ) // found the device? + { + // Q an ACC reply + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_LOGO_ACC, // Q Type + fchs ); // device to respond to + + // set login struct fields (LOGO_counter increment) + SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); + + // are we an Initiator? + if( fcChip->Options.initiator) + { + // we're an Initiator, so check if we should + // try (another?) login + + // Fabrics routinely log out from us after + // getting device info - don't try to log them + // back in. + if( (fchs->s_id & 0xFFF000) == 0xFFF000 ) + { + ; // do nothing + } + else if( pLoggedInPort->LOGO_counter <= 3) + { + // try (another) login (PLOGI request) + + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_PLOGI, // Q Type + fchs ); + + // Terminate I/O with "retry" potential + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pLoggedInPort->ScsiNexus, + PORTID_CHANGED); + } + else + { + printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n", + fchs->s_id &&0xFFFFFF); + GiveUpOnDevice = TRUE; + } + } + else + { + GiveUpOnDevice = TRUE; + } + + + if( GiveUpOnDevice == TRUE ) + { + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pLoggedInPort->ScsiNexus, + DEVICE_REMOVED); + } + } + else // we don't know this WWN! + { + // Q a ReJecT Reply with reason code + fchs->reserved = ls_reject_code; + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_RJT, // Q Type + fchs ); + } + } + break; + + + + + // FABRIC only case + case 0x0461: // ELS RSCN (Registered State Change Notification)? + { + int Ports; + int i; + __u32 Buff; + // Typically, one or more devices have been added to or dropped + // from the Fabric. + // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997) + // The first 32-bit word has a 2-byte Payload Length, which + // includes the 4 bytes of the first word. Consequently, + // this PL len must never be less than 4, must be a multiple of 4, + // and has a specified max value 256. + // (Endianess!) + Ports = ((fchs->pl[0] >>24) - 4) / 4; + Ports = Ports > 63 ? 63 : Ports; + + printk(" RSCN ports: %d\n", Ports); + if( Ports <= 0 ) // huh? + { + // ReJecT the command + fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0); + + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_RJT, // Q Type + fchs ); + + break; + } + else // Accept the command + { + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_ACC, // Q Type + fchs ); + } + + // Check the "address format" to determine action. + // We have 3 cases: + // 0 = Port Address; 24-bit address of affected device + // 1 = Area Address; MS 16 bits valid + // 2 = Domain Address; MS 8 bits valid + for( i=0; ipl[i+1],(UCHAR*)&Buff, 4); + switch( Buff & 0xFF000000) + { + + case 0: // Port Address? + + case 0x01000000: // Area Domain? + case 0x02000000: // Domain Address + // For example, "port_id" 0x201300 + // OK, let's try a Name Service Request (Query) + fchs->s_id = 0xFFFFFC; // Name Server Address + cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs); + + break; + + + default: // huh? new value on version change? + break; + } + } + } + break; + + + + + default: // don't support this request (yet) + // set reject reason code + fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, + REQUEST_NOT_SUPPORTED); + + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_RJT, // Q Type + fchs ); + break; + } +} + + +static void ProcessELS_Reply( + CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG ox_id = (fchs->ox_rx_id >>16); + ULONG ls_reject_code; + PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort; + + // If this is a valid reply, then we MUST have sent a request. + // Verify that we can find a valid request OX_ID corresponding to + // this reply + + + if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0) + { + printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ", + ox_id, fchs->ox_rx_id & 0xffff); + goto Quit; // exit this routine + } + + + // Is the reply a RJT (reject)? + if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply? + { +// ****** REJECT REPLY ******** + switch( Exchanges->fcExchange[ox_id].type ) + { + + case ELS_FDISC: // we sent out Fabric Discovery + case ELS_FLOGI: // we sent out FLOGI + + printk("RJT received on Fabric Login from %Xh, reason %Xh\n", + fchs->s_id, fchs->pl[1]); + + break; + + default: + break; + } + + goto Done; + } + + // OK, we have an ACCept... + // What's the ACC type? (according to what we sent) + switch( Exchanges->fcExchange[ox_id].type ) + { + + case ELS_PLOGI: // we sent out PLOGI + if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) + { + LOGIN_PAYLOAD logi; // FC-PH Port Login + + // login ACC payload acceptable; search for WWN in our list + // of fcPorts + + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + 0, // don't search linked list for port_id + &logi.port_name[0], // search linked list for WWN + &pLastLoggedInPort); // must return non-NULL; when a port_id + // is not found, this pointer marks the + // end of the singly linked list + + if( pLoggedInPort == NULL) // WWN not found - new port + { + + pLoggedInPort = CreateFcPort( + cpqfcHBAdata, + pLastLoggedInPort, + fchs, + &logi); + + if( pLoggedInPort == NULL ) + { + printk(" cpqfcTS: New port allocation failed - lost FC device!\n"); + // Now Q a LOGOut Request, since we won't be talking to that device + + goto Done; // exit with error! dropped login frame + } + } + else // WWN was already known. Ensure that any open + // exchanges for this WWN are terminated. + // NOTE: It's possible that a device can change its + // 24-bit port_id after a Link init or Fabric change + // (e.g. LIP or Fabric RSCN). In that case, the old + // 24-bit port_id may be duplicated, or no longer exist. + { + + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pLoggedInPort->ScsiNexus, PORTID_CHANGED); + } + + // We have an fcPort struct - set fields accordingly + // not PDISC, originator + SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE); + + // We just set a "port_id"; is it duplicated? + TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort); + + // For Fabric operation, we issued PLOGI to 0xFFFFFC + // so we can send SCR (State Change Registration) + // Check for this special case... + if( fchs->s_id == 0xFFFFFC ) + { + // PLOGI ACC was a Fabric response... issue SCR + fchs->s_id = 0xFFFFFD; // address for SCR + cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs); + } + + else + { + // Now we need a PRLI to enable FCP-SCSI operation + // set flags and Q up a ELS_PRLI + cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs); + } + } + else + { + // login payload unacceptable - reason in ls_reject_code + // Q up a Logout Request + printk("Login Payload unacceptable\n"); + + } + break; + + + // PDISC logic very similar to PLOGI, except we never want + // to allocate mem for "new" port, and we set flags differently + // (might combine later with PLOGI logic for efficiency) + case ELS_PDISC: // we sent out PDISC + if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) + { + LOGIN_PAYLOAD logi; // FC-PH Port Login + BOOLEAN NeedLogin = FALSE; + + // login payload acceptable; search for WWN in our list + // of (previously seen) fcPorts + + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + 0, // don't search linked list for port_id + &logi.port_name[0], // search linked list for WWN + &pLastLoggedInPort); // must return non-NULL; when a port_id + // is not found, this pointer marks the + // end of the singly linked list + + if( pLoggedInPort != NULL) // WWN found? + { + // WWN has same port_id as last login? (Of course, a properly + // working FC device should NEVER ACCept a PDISC if it's + // port_id changed, but check just in case...) + if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id) + { + // Yes. We were expecting PDISC? + if( pLoggedInPort->pdisc ) + { + int i; + + + // PDISC expected -- set fields. (PDISC, Originator) + SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE); + + // We are ready to resume FCP-SCSI to this device... + // Do we need to start anything that was Queued? + + for( i=0; i< TACH_SEST_LEN; i++) + { + // see if any exchange for this PDISC'd port was queued + if( ((fchs->s_id &0xFFFFFF) == + (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF)) + && + (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED)) + { + fchs->reserved = i; // copy ExchangeID +// printk(" *Q x_ID %Xh after PDISC* ",i); + + cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs ); + } + } + + // Complete commands Q'd while we were waiting for Login + + UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort); + } + else + { + printk("Not expecting PDISC (pdisc=FALSE)\n"); + NeedLogin = TRUE; + } + } + else + { + printk("PDISC PortID change: old %Xh, new %Xh\n", + pLoggedInPort->port_id, fchs->s_id &0xFFFFFF); + NeedLogin = TRUE; + + } + } + else + { + printk("PDISC ACC from unknown WWN\n"); + NeedLogin = TRUE; + } + + if( NeedLogin) + { + + // The PDISC failed. Set login struct flags accordingly, + // terminate any I/O to this port, and Q a PLOGI + if( pLoggedInPort ) // FC device previously known? + { + + cpqfcTSPutLinkQue( cpqfcHBAdata, + ELS_LOGO, // Q Type + fchs ); // has port_id to send to + + // There are a variety of error scenarios which can result + // in PDISC failure, so as a catchall, add the check for + // duplicate port_id. + TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort); + +// TriggerHBA( fcChip->Registers.ReMapMemBase, 0); + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; + pLoggedInPort->plogi = FALSE; + + cpqfcTSTerminateExchange( cpqfcHBAdata, + &pLoggedInPort->ScsiNexus, PORTID_CHANGED); + } + cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs ); + } + } + else + { + // login payload unacceptable - reason in ls_reject_code + // Q up a Logout Request + printk("ERROR: Login Payload unacceptable!\n"); + + } + + break; + + + + case ELS_PRLI: // we sent out PRLI + + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search Scsi Nexus + (fchs->s_id & 0xFFFFFF), // search linked list for port_id + NULL, // DON'T search linked list for WWN + NULL); // don't care + + if( pLoggedInPort == NULL ) + { + // huh? + printk(" Unexpected PRLI ACCept frame!\n"); + + // Q a LOGOut here? + + goto Done; + } + + // verify the PRLI ACC payload + if( !verify_PRLI( fchs, &ls_reject_code) ) + { + // PRLI Reply is acceptable; were we expecting it? + if( pLoggedInPort->plogi ) + { + // yes, we expected the PRLI ACC (not PDISC; Originator) + SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE); + + // OK, let's send a REPORT_LUNS command to determine + // whether VSA or PDA FCP-LUN addressing is used. + + cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs ); + + // It's possible that a device we were talking to changed + // port_id, and has logged back in. This function ensures + // that I/O will resume. + UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort); + + } + else + { + // huh? + printk(" (unexpected) PRLI ACCept with plogi FALSE\n"); + + // Q a LOGOut here? + goto Done; + } + } + else + { + printk(" PRLI ACCept payload failed verify\n"); + + // Q a LOGOut here? + } + + break; + + case ELS_FLOGI: // we sent out FLOGI (Fabric Login) + + // update the upper 16 bits of our port_id in Tachyon + // the switch adds those upper 16 bits when responding + // to us (i.e. we are the destination_id) + fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF); + writel( fcChip->Registers.my_al_pa, + fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID); + + // now send out a PLOGI to the well known port_id 0xFFFFFC + fchs->s_id = 0xFFFFFC; + cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs); + + break; + + + case ELS_FDISC: // we sent out FDISC (Fabric Discovery (Login)) + + printk( " ELS_FDISC success "); + break; + + + case ELS_SCR: // we sent out State Change Registration + // now we can issue Name Service Request to find any + // Fabric-connected devices we might want to login to. + + + fchs->s_id = 0xFFFFFC; // Name Server Address + cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs); + + + break; + + + default: + printk(" *Discarding unknown ACC frame, xID %04X/%04X* ", + ox_id, fchs->ox_rx_id & 0xffff); + break; + } + + +Done: + // Regardless of whether the Reply is valid or not, the + // the exchange is done - complete + cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete + +Quit: + return; +} + + + + + + +// **************** Fibre Channel Services ************** +// This is where we process the Directory (Name) Service Reply +// to know which devices are on the Fabric + +static void ProcessFCS_Reply( + CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG ox_id = (fchs->ox_rx_id >>16); +// ULONG ls_reject_code; +// PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort; + + // If this is a valid reply, then we MUST have sent a request. + // Verify that we can find a valid request OX_ID corresponding to + // this reply + + if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0) + { + printk(" *Discarding Reply frame, xID %04X/%04X* ", + ox_id, fchs->ox_rx_id & 0xffff); + goto Quit; // exit this routine + } + + + // OK, we were expecting it. Now check to see if it's a + // "Name Service" Reply, and if so force a re-validation of + // Fabric device logins (i.e. Start the login timeout and + // send PDISC or PLOGI) + // (Endianess Byte Swap?) + if( fchs->pl[1] == 0x02FC ) // Name Service + { + // got a new (or NULL) list of Fabric attach devices... + // Invalidate current logins + + PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + + if( (pLoggedInPort->port_id & 0xFFFF00) // Fabric device? + && + (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port + { + pLoggedInPort->LOGO_timer = 6; // what's the Fabric timeout?? + // suspend any I/O in progress until + // PDISC received... + pLoggedInPort->prli = FALSE; // block FCP-SCSI commands + } + + pLoggedInPort = pLoggedInPort->pNextPort; + } + + if( fchs->pl[2] == 0x0280) // ACCept? + { + // Send PLOGI or PDISC to these Fabric devices + SendLogins( cpqfcHBAdata, &fchs->pl[4] ); + } + + + // As of this writing, the only reason to reject is because NO + // devices are left on the Fabric. We already started + // "logged out" timers; if the device(s) don't come + // back, we'll do the implicit logout in the heart beat + // timer routine + else // ReJecT + { + // this just means no Fabric device is visible at this instant + } + } + + // Regardless of whether the Reply is valid or not, the + // the exchange is done - complete + cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete + +Quit: + return; +} + + + + + + + +static void AnalyzeIncomingFrame( + CPQFCHBA *cpqfcHBAdata, + ULONG QNdx ) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; + TachFCHDR_GCMND* fchs = + (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff; +// ULONG ls_reject_code; // reason for rejecting login + LONG ExchangeID; +// FC_LOGGEDIN_PORT *pLoggedInPort; + BOOLEAN AbortAccept; + + ENTER("AnalyzeIncomingFrame"); + + + + switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown + { + + case SFQ_UNKNOWN: // unknown frame (e.g. LIP position frame, NOP, etc.) + + + // ********* FC-4 Device Data/ Fibre Channel Service ************* + if( ((fchs->d_id &0xF0000000) == 0) // R_CTL (upper nibble) 0x0? + && + (fchs->f_ctl & 0x20000000) ) // TYPE 20h is Fibre Channel Service + { + + // ************** FCS Reply ********************** + + if( (fchs->d_id & 0xff000000L) == 0x03000000L) // (31:23 R_CTL) + { + ProcessFCS_Reply( cpqfcHBAdata, fchs ); + + } // end of FCS logic + + } + + + // *********** Extended Link Service ************** + + else if( fchs->d_id & 0x20000000 // R_CTL 0x2? + && + (fchs->f_ctl & 0x01000000) ) // TYPE = 1 + { + + // these frames are either a response to + // something we sent (0x23) or "unsolicited" + // frames (0x22). + + + // **************Extended Link REPLY ********************** + // R_CTL Solicited Control Reply + + if( (fchs->d_id & 0xff000000L) == 0x23000000L) // (31:23 R_CTL) + { + + ProcessELS_Reply( cpqfcHBAdata, fchs ); + + } // end of "R_CTL Solicited Control Reply" + + + + + // **************Extended Link REQUEST ********************** + // (unsolicited commands from another port or task...) + + // R_CTL Ext Link REQUEST + else if( (fchs->d_id & 0xff000000L) == 0x22000000L && + (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame) + { + + + + ProcessELS_Request( cpqfcHBAdata, fchs ); + + } + + + + // ************** LILP ********************** + else if( (fchs->d_id & 0xff000000L) == 0x22000000L && + (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames) + + { + // SANMark specifies that when available, we must use + // the LILP frame to determine which ALPAs to send Port Discovery + // to... + + if( fchs->pl[0] == 0x0711L) // ELS_PLOGI? + { +// UCHAR *ptr = (UCHAR*)&fchs->pl[1]; +// printk(" %d ALPAs found\n", *ptr); + memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs + fcChip->Options.LILPin = 1; // our LILPmap is valid! + // now post to make Port Discovery happen... + cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs); + } + } + } + + + // ***************** BASIC LINK SERVICE ***************** + + else if( fchs->d_id & 0x80000000 // R_CTL: + && // Basic Link Service Request + !(fchs->f_ctl & 0xFF000000) ) // type=0 for BLS + { + + // Check for ABTS (Abort Sequence) + if( (fchs->d_id & 0x8F000000) == 0x81000000) + { + // look for OX_ID, S_ID pair that matches in our + // fcExchanges table; if found, reply with ACCept and complete + // the exchange + + // Per PLDA, an ABTS is sent by an initiator; therefore + // assume that if we have an exhange open to the port who + // sent ABTS, it will be the d_id of what we sent. + for( ExchangeID = 0, AbortAccept=FALSE; + ExchangeID < TACH_SEST_LEN; ExchangeID++) + { + // Valid "target" exchange 24-bit port_id matches? + // NOTE: For the case of handling Intiator AND Target + // functions on the same chip, we can have TWO Exchanges + // with the same OX_ID -- OX_ID/FFFF for the CMND, and + // OX_ID/RX_ID for the XRDY or DATA frame(s). Ideally, + // we would like to support ABTS from Initiators or Targets, + // but it's not clear that can be supported on Tachyon for + // all cases (requires more investigation). + + if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE || + Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE) + && + ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) == + (fchs->s_id & 0xFFFFFF)) ) + { + + // target xchnge port_id matches -- how about OX_ID? + if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000) + == (fchs->ox_rx_id & 0xFFFF0000) ) + // yes! post ACCept response; will be completed by fcStart + { + Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT; + + // copy (add) rx_id field for simplified ACCept reply + fchs->ox_rx_id = + Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id; + + cpqfcTSPutLinkQue( cpqfcHBAdata, + BLS_ABTS_ACC, // Q Type + fchs ); // void QueContent + AbortAccept = TRUE; + printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n", + fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id); + break; // ABTS can affect only ONE exchange -exit loop + } + } + } // end of FOR loop + if( !AbortAccept ) // can't ACCept ABTS - send Reject + { + printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n", + fchs->ox_rx_id); + if( Exchanges->fcExchange[ ExchangeID].type + && + !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len + & 0x80000000)) + { + cpqfcTSCompleteExchange( fcChip, ExchangeID); + } + else + { + printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n", + ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len); + } + } + } + + // Check for BLS {ABTS? (Abort Sequence)} ACCept + else if( (fchs->d_id & 0x8F000000) == 0x84000000) + { + // target has responded with ACC for our ABTS; + // complete the indicated exchange with ABORTED status + // Make no checks for correct RX_ID, since + // all we need to conform ABTS ACC is the OX_ID. + // Verify that the d_id matches! + + ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC +// printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n", +// fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff, +// Exchanges->fcExchange[ExchangeID].status); + + + + if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense + { + // Does "target" exchange 24-bit port_id match? + // (See "NOTE" above for handling Intiator AND Target in + // the same device driver) + // First, if this is a target response, then we originated + // (initiated) it with BLS_ABTS: + + if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS) + + && + // Second, does the source of this ACC match the destination + // of who we originally sent it to? + ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) == + (fchs->s_id & 0xFFFFFF)) ) + { + cpqfcTSCompleteExchange( fcChip, ExchangeID ); + } + } + } + // Check for BLS {ABTS? (Abort Sequence)} ReJecT + else if( (fchs->d_id & 0x8F000000) == 0x85000000) + { + // target has responded with RJT for our ABTS; + // complete the indicated exchange with ABORTED status + // Make no checks for correct RX_ID, since + // all we need to conform ABTS ACC is the OX_ID. + // Verify that the d_id matches! + + ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC +// printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n", +// fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff); + + if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense + { + // Does "target" exchange 24-bit port_id match? + // (See "NOTE" above for handling Intiator AND Target in + // the same device driver) + // First, if this is a target response, then we originated + // (initiated) it with BLS_ABTS: + + if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS) + + && + // Second, does the source of this ACC match the destination + // of who we originally sent it to? + ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) == + (fchs->s_id & 0xFFFFFF)) ) + { + // YES! NOTE: There is a bug in CPQ's RA-4000 box + // where the "reason code" isn't returned in the payload + // For now, simply presume the reject is because the target + // already completed the exchange... + +// printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID); + cpqfcTSCompleteExchange( fcChip, ExchangeID ); + } + } + } // end of ABTS check + } // end of Basic Link Service Request + break; + + default: + printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n", + fcLQ->Qitem[QNdx].Type, + fcLQ->Qitem[QNdx].Type); + break; + } +} + + +// Function for Port Discovery necessary after every FC +// initialization (e.g. LIP). +// Also may be called if from Fabric Name Service logic. + +static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds ) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG ulStatus=0; + TachFCHDR_GCMND fchs; // copy fields for transmission + int i; + ULONG loginType; + LONG ExchangeID; + PFC_LOGGEDIN_PORT pLoggedInPort; + __u32 PortIds[ number_of_al_pa]; + int NumberOfPorts=0; + + // We're going to presume (for now) that our limit of Fabric devices + // is the same as the number of alpa on a private loop (126 devices). + // (Of course this could be changed to support however many we have + // memory for). + memset( &PortIds[0], 0, sizeof(PortIds)); + + // First, check if this login is for our own Link Initialization + // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices + // from a switch. If we are logging into Fabric devices, we'll + // have a non-NULL FabricPortId pointer + + if( FabricPortIds != NULL) // may need logins + { + int LastPort=FALSE; + i = 0; + while( !LastPort) + { + // port IDs From NSR payload; byte swap needed? + BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4); + +// printk("FPortId[%d] %Xh ", i, PortIds[i]); + if( PortIds[i] & 0x80000000) + LastPort = TRUE; + + PortIds[i] &= 0xFFFFFF; // get 24-bit port_id + // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge) + // erroneously use ALPA 0. + if( PortIds[i] ) // need non-zero port_id... + i++; + + if( i >= number_of_al_pa ) // (in)sanity check + break; + FabricPortIds++; // next... + } + + NumberOfPorts = i; +// printk("NumberOf Fabric ports %d", NumberOfPorts); + } + + else // need to send logins on our "local" link + { + + // are we a loop port? If so, check for reception of LILP frame, + // and if received use it (SANMark requirement) + if( fcChip->Options.LILPin ) + { + int j=0; + // sanity check on number of ALPAs from LILP frame... + // For format of LILP frame, see FC-AL specs or + // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8) + // First byte is number of ALPAs + i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0]; + NumberOfPorts = i; +// printk(" LILP alpa count %d ", i); + while( i > 0) + { + PortIds[j] = fcChip->LILPmap[1+ j]; + j++; i--; + } + } + else // have to send login to everybody + { + int j=0; + i = number_of_al_pa; + NumberOfPorts = i; + while( i > 0) + { + PortIds[j] = valid_al_pa[j]; // all legal ALPAs + j++; i--; + } + } + } + + + // Now we have a copy of the port_ids (and how many)... + for( i = 0; i < NumberOfPorts; i++) + { + // 24-bit FC Port ID + fchs.s_id = PortIds[i]; // note: only 8-bits used for ALPA + + + // don't log into ourselves (Linux Scsi disk scan will stop on + // no TARGET support error on us, and quit trying for rest of devices) + if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) ) + continue; + + // fabric login needed? + if( (fchs.s_id == 0) || + (fcChip->Options.fabric == 1) ) + { + fcChip->Options.flogi = 1; // fabric needs longer for login + // Do we need FLOGI or FDISC? + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search SCSI Nexus + 0xFFFFFC, // search linked list for Fabric port_id + NULL, // don't search WWN + NULL); // (don't care about end of list) + + if( pLoggedInPort ) // If found, we have prior experience with + // this port -- check whether PDISC is needed + { + if( pLoggedInPort->flogi ) + { + // does the switch support FDISC?? (FLOGI for now...) + loginType = ELS_FLOGI; // prior FLOGI still valid + } + else + loginType = ELS_FLOGI; // expired FLOGI + } + else // first FLOGI? + loginType = ELS_FLOGI; + + + fchs.s_id = 0xFFFFFE; // well known F_Port address + + // Fabrics are not required to support FDISC, and + // it's not clear if that helps us anyway, since + // we'll want a Name Service Request to re-verify + // visible devices... + // Consequently, we always want our upper 16 bit + // port_id to be zero (we'll be rejected if we + // use our prior port_id if we've been plugged into + // a different switch port). + // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87) + // If our ALPA is 55h for instance, we want the FC frame + // s_id to be 0x000055, while Tach's my_al_pa register + // must be 0x000155, to force an OPN at ALPA 0 + // (the Fabric port) + fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI + writel( fcChip->Registers.my_al_pa | 0x0100, + fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID); + } + + else // not FLOGI... + { + // should we send PLOGI or PDISC? Check if any prior port_id + // (e.g. alpa) completed a PLOGI/PRLI exchange by checking + // the pdisc flag. + + pLoggedInPort = fcFindLoggedInPort( + fcChip, + NULL, // don't search SCSI Nexus + fchs.s_id, // search linked list for al_pa + NULL, // don't search WWN + NULL); // (don't care about end of list) + + + + if( pLoggedInPort ) // If found, we have prior experience with + // this port -- check whether PDISC is needed + { + if( pLoggedInPort->pdisc ) + { + loginType = ELS_PDISC; // prior PLOGI and PRLI maybe still valid + + } + else + loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC + } + else // never talked to this port_id before + loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC + } + + + + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + loginType, // e.g. PLOGI + &fchs, // no incoming frame (we are originator) + NULL, // no data (no scatter/gather list) + &ExchangeID );// fcController->fcExchanges index, -1 if failed + + if( !ulStatus ) // Exchange setup OK? + { + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); + if( !ulStatus ) + { + // submitted to Tach's Outbound Que (ERQ PI incremented) + // waited for completion for ELS type (Login frames issued + // synchronously) + + if( loginType == ELS_PDISC ) + { + // now, we really shouldn't Revalidate SEST exchanges until + // we get an ACC reply from our target and verify that + // the target address/WWN is unchanged. However, when a fast + // target gets the PDISC, they can send SEST Exchange data + // before we even get around to processing the PDISC ACC. + // Consequently, we lose the I/O. + // To avoid this, go ahead and Revalidate when the PDISC goes + // out, anticipating that the ACC will be truly acceptable + // (this happens 99.9999....% of the time). + // If we revalidate a SEST write, and write data goes to a + // target that is NOT the one we originated the WRITE to, + // that target is required (FCP-SCSI specs, etc) to discard + // our WRITE data. + + // Re-validate SEST entries (Tachyon hardware assists) + RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort); + //TriggerHBA( fcChip->Registers.ReMapMemBase, 1); + } + } + else // give up immediately on error + { +#ifdef LOGIN_DBG + printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus ); +#endif + break; + } + + + if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc. + { + ulStatus = LNKDWN_OSLS; +#ifdef LOGIN_DBG + printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id); +#endif + break; + } + // Check the exchange for bad status (i.e. FrameTimeOut), + // and complete on bad status (most likely due to BAD_ALPA) + // on LDn, DPC function may already complete (ABORT) a started + // exchange, so check type first (type = 0 on complete). + if( Exchanges->fcExchange[ExchangeID].status ) + { +#ifdef LOGIN_DBG + printk("completing x_ID %X on status %Xh\n", + ExchangeID, Exchanges->fcExchange[ExchangeID].status); +#endif + cpqfcTSCompleteExchange( fcChip, ExchangeID); + } + } + else // Xchange setup failed... + { +#ifdef LOGIN_DBG + printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); +#endif + break; + } + } + if( !ulStatus ) + { + // set the event signifying that all ALPAs were sent out. +#ifdef LOGIN_DBG + printk("SendLogins: PortDiscDone\n"); +#endif + cpqfcHBAdata->PortDiscDone = 1; + + + // TL/TS UG, pg. 184 + // 0x0065 = 100ms for RT_TOV + // 0x01f5 = 500ms for ED_TOV + fcChip->Registers.ed_tov.value = 0x006501f5L; + writel( fcChip->Registers.ed_tov.value, + (fcChip->Registers.ed_tov.address)); + + // set the LP_TOV back to ED_TOV (i.e. 500 ms) + writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); + } + else + { + printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n", + ExchangeID, fchs.s_id, ulStatus); + } + LEAVE("SendLogins"); + +} + + +// for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi", +// D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9) +static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd) +{ + struct Scsi_Host *HostAdapter = Cmnd->host; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + PFC_LOGGEDIN_PORT pLoggedInPort; + int LunListLen=0; + int i; + ULONG x_ID = 0xFFFFFFFF; + UCHAR *ucBuff = Cmnd->request_buffer; + +// printk("cpqfcTS: ReportLunsDone \n"); + // first, we need to find the Exchange for this command, + // so we can find the fcPort struct to make the indicated + // changes. + for( i=0; i< TACH_SEST_LEN; i++) + { + if( Exchanges->fcExchange[i].type // exchange defined? + && + (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches? + + { + x_ID = i; // found exchange! + break; + } + } + if( x_ID == 0xFFFFFFFF) + { +// printk("cpqfcTS: ReportLuns failed - no FC Exchange\n"); + goto Done; // Report Luns FC Exchange gone; + // exchange probably Terminated by Implicit logout + } + + + // search linked list for the port_id we sent INQUIRY to + pLoggedInPort = fcFindLoggedInPort( fcChip, + NULL, // DON'T search Scsi Nexus (we will set it) + Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF, + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + if( !pLoggedInPort ) + { +// printk("cpqfcTS: ReportLuns failed - device gone\n"); + goto Done; // error! can't find logged in Port + } + LunListLen = ucBuff[3]; + LunListLen += ucBuff[2]>>8; + + if( !LunListLen ) // failed + { + // generically speaking, a soft error means we should retry... + if( (Cmnd->result >> 16) == DID_SOFT_ERROR ) + { + if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) && + (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset" + { + TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs; + // did we fail because of "check condition, device reset?" + // e.g. the device was reset (i.e., at every power up) + // retry the Report Luns + + // who are we sending it to? + // we know this because we have a copy of the command + // frame from the original Report Lun command - + // switch the d_id/s_id fields, because the Exchange Build + // context is "reply to source". + + fchs->s_id = fchs->d_id; // (temporarily re-use the struct) + cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs ); + } + } + else // probably, the device doesn't support Report Luns + pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0; + } + else // we have LUN info - check VSA mode + { + // for now, assume all LUNs will have same addr mode + // for VSA, payload byte 8 will be 0x40; otherwise, 0 + pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8]; + + // Since we got a Report Luns answer, set lun masking flag + pLoggedInPort->ScsiNexus.LunMasking = 1; + + if( LunListLen > 8*CPQFCTS_MAX_LUN) // We expect CPQFCTS_MAX_LUN max + LunListLen = 8*CPQFCTS_MAX_LUN; + +/* + printk("Device WWN %08X%08X Reports Luns @: ", + (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), + (ULONG)(pLoggedInPort->u.liWWN>>32)); + + for( i=8; iScsiNexus.lun[j] != 0xFF ) + { + AppendLunList = 1; + break; + } + } + if( AppendLunList ) + { + int k; + int FreeLunIndex; +// printk("cpqfcTS: AppendLunList\n"); + + // If we get a new Report Luns, we cannot change + // any existing LUN mapping! (Only additive entry) + // For all LUNs in ReportLun list + // if RL lun != ScsiNexus lun + // if RL lun present in ScsiNexus lun[], continue + // else find ScsiNexus lun[]==FF and add, continue + + for( i=8, j=0; iScsiNexus.lun[j] != ucBuff[i+1] ) + { + // something changed from the last Report Luns + printk(" cpqfcTS: Report Lun change!\n"); + for( k=0, FreeLunIndex=CPQFCTS_MAX_LUN; + k < CPQFCTS_MAX_LUN; k++) + { + if( pLoggedInPort->ScsiNexus.lun[k] == 0xFF) + { + FreeLunIndex = k; + break; + } + if( pLoggedInPort->ScsiNexus.lun[k] == ucBuff[i+1] ) + break; // we already masked this lun + } + if( k >= CPQFCTS_MAX_LUN ) + { + printk(" no room for new LUN %d\n", ucBuff[i+1]); + } + else if( k == FreeLunIndex ) // need to add LUN + { + pLoggedInPort->ScsiNexus.lun[k] = ucBuff[i+1]; +// printk("add [%d]->%02d\n", k, pLoggedInPort->ScsiNexus.lun[k]); + + } + else + { + // lun already known + } + break; + } + } + // print out the new list... + for( j=0; j< CPQFCTS_MAX_LUN; j++) + { + if( pLoggedInPort->ScsiNexus.lun[j] == 0xFF) + break; // done +// printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]); + } + } + else + { +// printk("Linux SCSI LUNs[] -> Device LUNs: "); + // first time - this is easy + for( i=8, j=0; iScsiNexus.lun[j] = ucBuff[i+1]; +// printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]); + } +// printk("\n"); + } + } + } + +Done: +} + +// After successfully getting a "Process Login" (PRLI) from an +// FC port, we want to Discover the LUNs so that we know the +// addressing type (e.g., FCP-SCSI Volume Set Address, Peripheral +// Unit Device), and whether SSP (Selective Storage Presentation or +// Lun Masking) has made the LUN numbers non-zero based or +// non-contiguous. To remain backward compatible with the SCSI-2 +// driver model, which expects a contiguous LUNs starting at 0, +// will use the ReportLuns info to map from "device" to "Linux" +// LUNs. +static void IssueReportLunsCommand( + CPQFCHBA* cpqfcHBAdata, + TachFCHDR_GCMND* fchs) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + PFC_LOGGEDIN_PORT pLoggedInPort; + Scsi_Cmnd *Cmnd; + LONG x_ID; + ULONG ulStatus; + UCHAR *ucBuff; + + + if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn + { + printk("Discard Q'd ReportLun command\n"); + goto Done; + } + + // find the device (from port_id) we're talking to + pLoggedInPort = fcFindLoggedInPort( fcChip, + NULL, // DON'T search Scsi Nexus + fchs->s_id & 0xFFFFFF, + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + if( pLoggedInPort ) // we'd BETTER find it! + { + + + if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) ) + goto Done; // forget it - FC device not a "target" + + // now use the port's Scsi Command buffer for the + // Report Luns Command + + Cmnd = &pLoggedInPort->ScsiCmnd; + ucBuff = pLoggedInPort->ReportLunsPayload; + + memset( Cmnd, 0, sizeof(Scsi_Cmnd)); + memset( ucBuff, 0, REPORT_LUNS_PL); + + Cmnd->scsi_done = ScsiReportLunsDone; + Cmnd->host = cpqfcHBAdata->HostAdapter; + + Cmnd->request_buffer = pLoggedInPort->ReportLunsPayload; + Cmnd->request_bufflen = REPORT_LUNS_PL; + + Cmnd->cmnd[0] = 0xA0; + Cmnd->cmnd[8] = REPORT_LUNS_PL >> 8; + Cmnd->cmnd[9] = (UCHAR)REPORT_LUNS_PL; + Cmnd->cmd_len = 12; + + Cmnd->channel = pLoggedInPort->ScsiNexus.channel; + Cmnd->target = pLoggedInPort->ScsiNexus.target; + + + ulStatus = cpqfcTSBuildExchange( + cpqfcHBAdata, + SCSI_IRE, + fchs, + Cmnd, // buffer for Report Lun data + &x_ID );// fcController->fcExchanges index, -1 if failed + + if( !ulStatus ) // Exchange setup? + { + ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID ); + if( !ulStatus ) + { + // submitted to Tach's Outbound Que (ERQ PI incremented) + // waited for completion for ELS type (Login frames issued + // synchronously) + } + else + // check reason for Exchange not being started - we might + // want to Queue and start later, or fail with error + { + + } + } + + else // Xchange setup failed... + printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); + } + else // like, we just got a PRLI ACC, and now the port is gone? + { + printk(" can't send ReportLuns - no login for port_id %Xh\n", + fchs->s_id & 0xFFFFFF); + } + + + +Done: + +} + + + + + + + +static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata) +{ + int i; + for( i = CPQFCTS_REQ_QUEUE_LEN-1; i>= 0; i--) + { + if( cpqfcHBAdata->BoardLockCmnd[i] != NULL ) + { + Scsi_Cmnd *Cmnd = cpqfcHBAdata->BoardLockCmnd[i]; + cpqfcHBAdata->BoardLockCmnd[i] = NULL; + Cmnd->result = (DID_SOFT_ERROR << 16); // ask for retry +// printk(" BoardLockCmnd[%d] %p Complete, chnl/target/lun %d/%d/%d\n", +// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); + if( Cmnd->scsi_done != NULL) + (*Cmnd->scsi_done)(Cmnd); + } + } +} + + + + + + +// runs every 1 second for FC exchange timeouts and implicit FC device logouts + +void cpqfcTSheartbeat( unsigned long ptr ) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)ptr; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; + ULONG i; + unsigned long flags; + DECLARE_MUTEX_LOCKED(BoardLock); + + PCI_TRACE( 0xA8) + + if( cpqfcHBAdata->BoardLock) // Worker Task Running? + goto Skip; + + spin_lock_irqsave( &io_request_lock, flags); // STOP _que function + + PCI_TRACE( 0xA8) + + + cpqfcHBAdata->BoardLock = &BoardLock; // stop Linux SCSI command queuing + + // release the IO lock (and re-enable interrupts) + spin_unlock_irqrestore( &io_request_lock, flags); + + // Ensure no contention from _quecommand or Worker process + CPQ_SPINLOCK_HBA( cpqfcHBAdata) + + PCI_TRACE( 0xA8) + + + disable_irq( cpqfcHBAdata->HostAdapter->irq); // our IRQ + + // Complete the "bad target" commands (normally only used during + // initialization, since we aren't supposed to call "scsi_done" + // inside the queuecommand() function). + + for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) + { + if( cpqfcHBAdata->BadTargetCmnd[i] ) + { + Scsi_Cmnd *Cmnd = cpqfcHBAdata->BadTargetCmnd[i]; + cpqfcHBAdata->BadTargetCmnd[i] = NULL; + Cmnd->result = (DID_BAD_TARGET << 16); + if( Cmnd->scsi_done != NULL) + (*Cmnd->scsi_done)(Cmnd); + } + else + break; + } + + + // logged in ports -- re-login check (ports required to verify login with + // PDISC after LIP within 2 secs) + + // prevent contention + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, check to see if + // time is up! + { + // Important: we only detect "timeout" condition on TRANSITION + // from non-zero to zero + if( pLoggedInPort->LOGO_timer ) // time-out "armed"? + { + if( !(--pLoggedInPort->LOGO_timer) ) // DEC from 1 to 0? + { + // LOGOUT time! Per PLDA, PDISC hasn't complete in 2 secs, so + // issue LOGO request and destroy all I/O with other FC port(s). + +/* + printk(" ~cpqfcTS heartbeat: LOGOut!~ "); + printk("Linux SCSI Chanl/Target %d/%d (port_id %06Xh) WWN %08X%08X\n", + pLoggedInPort->ScsiNexus.channel, + pLoggedInPort->ScsiNexus.target, + pLoggedInPort->port_id, + (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), + (ULONG)(pLoggedInPort->u.liWWN>>32)); + +*/ + cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); + + } + // else simply decremented - maybe next time... + } + pLoggedInPort = pLoggedInPort->pNextPort; + } + + + + + + // ************ FC EXCHANGE TIMEOUT CHECK ************** + + for( i=0; i< TACH_MAX_XID; i++) + { + if( Exchanges->fcExchange[i].type ) // exchange defined? + { + + if( !Exchanges->fcExchange[i].timeOut ) // time expired + { + // Set Exchange timeout status + Exchanges->fcExchange[i].status |= FC2_TIMEOUT; + + if( i >= TACH_SEST_LEN ) // Link Service Exchange + { + cpqfcTSCompleteExchange( fcChip, i); // Don't "abort" LinkService + } + + else // SEST Exchange TO -- may post ABTS to Worker Thread Que + { + // (Make sure we don't keep timing it out; let other functions + // complete it or set the timeOut as needed) + Exchanges->fcExchange[i].timeOut = 30000; // seconds default + + if( Exchanges->fcExchange[i].type + & + (BLS_ABTS | BLS_ABTS_ACC ) ) + { + // For BLS_ABTS*, an upper level might still have + // an outstanding command waiting for low-level completion. + // Also, in the case of a WRITE, we MUST get confirmation + // of either ABTS ACC or RJT before re-using the Exchange. + // It's possible that the RAID cache algorithm can hang + // if we fail to complete a WRITE to a LBA, when a READ + // comes later to that same LBA. Therefore, we must + // ensure that the target verifies receipt of ABTS for + // the exchange + + printk("~TO Q'd ABTS (x_ID %Xh)~ ", i); +// TriggerHBA( fcChip->Registers.ReMapMemBase); + + // On timeout of a ABTS exchange, check to + // see if the FC device has a current valid login. + // If so, restart it. + pLoggedInPort = fcFindLoggedInPort( fcChip, + Exchanges->fcExchange[i].Cmnd, // find Scsi Nexus + 0, // DON'T search linked list for FC port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + // device exists? + if( pLoggedInPort ) // device exists? + { + if( pLoggedInPort->prli ) // logged in for FCP-SCSI? + { + // attempt to restart the ABTS + printk(" ~restarting ABTS~ "); + cpqfcTSStartExchange( cpqfcHBAdata, i ); + + } + } + } + else // not an ABTS + { + + // We expect the WorkerThread to change the xchng type to + // abort and set appropriate timeout. + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i ); // timed-out + } + } + } + else // time not expired... + { + // decrement timeout: 1 or more seconds left + --Exchanges->fcExchange[i].timeOut; + } + } + } + + + enable_irq( cpqfcHBAdata->HostAdapter->irq); + + + CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) + + cpqfcHBAdata->BoardLock = NULL; // Linux SCSI commands may be queued + + // Now, complete any Cmnd we Q'd up while BoardLock was held + + CompleteBoardLockCmnd( cpqfcHBAdata); + + + // restart the timer to run again (1 sec later) +Skip: + mod_timer( &cpqfcHBAdata->cpqfcTStimer, jiffies + HZ); + + PCI_TRACEO( i, 0xA8) + return; +} + + +// put valid FC-AL physical address in spec order +static const UCHAR valid_al_pa[]={ + 0xef, 0xe8, 0xe4, 0xe2, + 0xe1, 0xE0, 0xDC, 0xDA, + 0xD9, 0xD6, 0xD5, 0xD4, + 0xD3, 0xD2, 0xD1, 0xCe, + 0xCd, 0xCc, 0xCb, 0xCa, + 0xC9, 0xC7, 0xC6, 0xC5, + 0xC3, 0xBc, 0xBa, 0xB9, + 0xB6, 0xB5, 0xB4, 0xB3, + 0xB2, 0xB1, 0xae, 0xad, + 0xAc, 0xAb, 0xAa, 0xA9, + + 0xA7, 0xA6, 0xA5, 0xA3, + 0x9f, 0x9e, 0x9d, 0x9b, + 0x98, 0x97, 0x90, 0x8f, + 0x88, 0x84, 0x82, 0x81, + 0x80, 0x7c, 0x7a, 0x79, + 0x76, 0x75, 0x74, 0x73, + 0x72, 0x71, 0x6e, 0x6d, + 0x6c, 0x6b, 0x6a, 0x69, + 0x67, 0x66, 0x65, 0x63, + 0x5c, 0x5a, 0x59, 0x56, + + 0x55, 0x54, 0x53, 0x52, + 0x51, 0x4e, 0x4d, 0x4c, + 0x4b, 0x4a, 0x49, 0x47, + 0x46, 0x45, 0x43, 0x3c, + 0x3a, 0x39, 0x36, 0x35, + 0x34, 0x33, 0x32, 0x31, + 0x2e, 0x2d, 0x2c, 0x2b, + 0x2a, 0x29, 0x27, 0x26, + 0x25, 0x23, 0x1f, 0x1E, + 0x1d, 0x1b, 0x18, 0x17, + + 0x10, 0x0f, 8, 4, 2, 1 }; // ALPA 0 (Fabric) is special case + +const int number_of_al_pa = (sizeof(valid_al_pa) ); + + + +// this function looks up an al_pa from the table of valid al_pa's +// we decrement from the last decimal loop ID, because soft al_pa +// (our typical case) are assigned with highest priority (and high al_pa) +// first. See "In-Depth FC-AL", R. Kembel pg. 38 +// INPUTS: +// al_pa - 24 bit port identifier (8 bit al_pa on private loop) +// RETURN: +// Loop ID - serves are index to array of logged in ports +// -1 - invalid al_pa (not all 8 bit values are legal) + +#if (0) +static int GetLoopID( ULONG al_pa ) +{ + int i; + + for( i = number_of_al_pa -1; i >= 0; i--) // dec. + { + if( valid_al_pa[i] == (UCHAR)al_pa ) // take lowest 8 bits + return i; // success - found valid al_pa; return decimal LoopID + } + return -1; // failed - not found +} +#endif + + +// Search the singly (forward) linked list "fcPorts" looking for +// either the SCSI target (if != -1), port_id (if not NULL), +// or WWN (if not null), in that specific order. +// If we find a SCSI nexus (from Cmnd arg), set the SCp.phase +// field according to VSA or PDU +// RETURNS: +// Ptr to logged in port struct if found +// (NULL if not found) +// pLastLoggedInPort - ptr to last struct (for adding new ones) +// +PFC_LOGGEDIN_PORT fcFindLoggedInPort( + PTACHYON fcChip, + Scsi_Cmnd *Cmnd, // search linked list for Scsi Nexus (channel/target/lun) + ULONG port_id, // search linked list for al_pa, or + UCHAR wwn[8], // search linked list for WWN, or... + PFC_LOGGEDIN_PORT *pLastLoggedInPort ) + +{ + PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; + BOOLEAN target_id_valid=FALSE; + BOOLEAN port_id_valid=FALSE; + BOOLEAN wwn_valid=FALSE; + int i; + + + if( Cmnd != NULL ) + target_id_valid = TRUE; + + else if( port_id ) // note! 24-bit NULL address is illegal + port_id_valid = TRUE; + + else + { + for( i=0; i<8; i++) // valid WWN passed? NULL WWN invalid + { + if( wwn ) // non-null arg? (OK to pass NULL when not searching WWN) + { + if( wwn[i] != 0 ) + wwn_valid = TRUE; // any non-zero byte makes (presumably) valid + } + } + } + // check other options ... + + + // In case multiple search options are given, we use a priority + // scheme: + // While valid pLoggedIn Ptr + // If port_id is valid + // if port_id matches, return Ptr + // If wwn is valid + // if wwn matches, return Ptr + // Next Ptr in list + // + // Return NULL (not found) + + + while( pLoggedInPort ) // NULL marks end of list (1st ptr always valid) + { + if( pLastLoggedInPort ) // caller's pointer valid? + *pLastLoggedInPort = pLoggedInPort; // end of linked list + + if( target_id_valid ) + { + // check Linux Scsi Cmnd for channel/target Nexus match + // (all luns are accessed through matching "pLoggedInPort") + if( (pLoggedInPort->ScsiNexus.target == Cmnd->target) + && + (pLoggedInPort->ScsiNexus.channel == Cmnd->channel)) + { + // For "passthru" modes, the IOCTL caller is responsible + // for setting the FCP-LUN addressing + if( !Cmnd->SCp.sent_command ) // NOT passthru? + { + + // set the FCP-LUN addressing type + Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing; + + // set the Device Type we got from the snooped INQUIRY string + Cmnd->SCp.Message = pLoggedInPort->ScsiNexus.InqDeviceType; + + // handle LUN masking; if not "default" (illegal) lun value, + // the use it. These lun values are set by a successful + // Report Luns command + if( pLoggedInPort->ScsiNexus.LunMasking == 1) + { + // we KNOW all the valid LUNs... 0xFF is invalid! + Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun]; + } + else + Cmnd->SCp.have_data_in = Cmnd->lun; // Linux & target luns match + } + break; // found it! + } + } + + if( port_id_valid ) // look for alpa first + { + if( pLoggedInPort->port_id == port_id ) + break; // found it! + } + if( wwn_valid ) // look for wwn second + { + + if( !memcmp( &pLoggedInPort->u.ucWWN[0], &wwn[0], 8)) + { + // all 8 bytes of WWN match + break; // found it! + } + } + + pLoggedInPort = pLoggedInPort->pNextPort; // try next port + } + + return pLoggedInPort; +} + + + + +// +// We need to examine the SEST table and re-validate +// any open Exchanges for this LoggedInPort +// To make Tachyon pay attention, Freeze FCP assists, +// set VAL bits, Unfreeze FCP assists +static void RevalidateSEST( struct Scsi_Host *HostAdapter, + PFC_LOGGEDIN_PORT pLoggedInPort) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG x_ID; + BOOLEAN TachFroze = FALSE; + + + // re-validate any SEST exchanges that are permitted + // to survive the link down (e.g., good PDISC performed) + for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) + { + + // If the SEST entry port_id matches the pLoggedInPort, + // we need to re-validate + if( (Exchanges->fcExchange[ x_ID].type == SCSI_IRE) + || + (Exchanges->fcExchange[ x_ID].type == SCSI_IWE)) + { + + if( (Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF) // (24-bit port ID) + == pLoggedInPort->port_id) + { +// printk(" re-val xID %Xh ", x_ID); + if( !TachFroze ) // freeze if not already frozen + TachFroze |= FreezeTach( cpqfcHBAdata); + fcChip->SEST->u[ x_ID].IWE.Hdr_Len |= 0x80000000; // set VAL bit + } + } + } + + if( TachFroze) + { + fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists + } +} + + +// Complete an Linux Cmnds that we Queued because +// our FC link was down (cause immediate retry) + +static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, + PFC_LOGGEDIN_PORT pLoggedInPort) +{ +// Scsi_Device *sdev = HostAdapter->host_queue; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + Scsi_Cmnd* *SCptr = &cpqfcHBAdata->LinkDnCmnd[0]; + Scsi_Cmnd *Cmnd; + int indx; + + + + // if the device was previously "blocked", make sure + // we unblock it so Linux SCSI will resume + + pLoggedInPort->device_blocked = FALSE; // clear our flag + + // check the Link Down command ptr buffer; + // we can complete now causing immediate retry + for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++, SCptr++) + { + if( *SCptr != NULL ) // scsi command to complete? + { +#ifdef DUMMYCMND_DBG + printk("complete Cmnd %p in LinkDnCmnd[%d]\n", *SCptr,indx); +#endif + Cmnd = *SCptr; + + + // Are there any Q'd commands for this target? + if( (Cmnd->target == pLoggedInPort->ScsiNexus.target) + && + (Cmnd->channel == pLoggedInPort->ScsiNexus.channel) ) + { + Cmnd->result = (DID_SOFT_ERROR <<16); // force retry + if( Cmnd->scsi_done != NULL) + (*Cmnd->scsi_done)(Cmnd); + else + printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n", + pLoggedInPort->port_id); + *SCptr = NULL; // free this slot for next use + } + } + } +} + + +//#define WWN_DBG 1 + +static void SetLoginFields( + PFC_LOGGEDIN_PORT pLoggedInPort, + TachFCHDR_GCMND* fchs, + BOOLEAN PDisc, + BOOLEAN Originator) +{ + LOGIN_PAYLOAD logi; // FC-PH Port Login + PRLI_REQUEST prli; // copy for BIG ENDIAN switch + int i; +#ifdef WWN_DBG + ULONG ulBuff; +#endif + + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); + + pLoggedInPort->Originator = Originator; + pLoggedInPort->port_id = fchs->s_id & 0xFFFFFF; + + switch( fchs->pl[0] & 0xffff ) + { + case 0x00000002: // PLOGI or PDISC ACCept? + if( PDisc ) // PDISC accept + goto PDISC_case; + + case 0x00000003: // ELS_PLOGI or ELS_PLOGI_ACC + + // Login BB_credit typically 0 for Tachyons + pLoggedInPort->BB_credit = logi.cmn_services.bb_credit; + + // e.g. 128, 256, 1024, 2048 per FC-PH spec + // We have to use this when setting up SEST Writes, + // since that determines frame size we send. + pLoggedInPort->rx_data_size = logi.class3.rx_data_size; + pLoggedInPort->plogi = TRUE; + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; // ELS_PLOGI resets + pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets + pLoggedInPort->logo = FALSE; // ELS_PLOGI resets + pLoggedInPort->LOGO_counter = 0;// ELS_PLOGI resets + pLoggedInPort->LOGO_timer = 0;// ELS_PLOGI resets + + // was this PLOGI to a Fabric? + if( pLoggedInPort->port_id == 0xFFFFFC ) // well know address + pLoggedInPort->flogi = TRUE; + + + for( i=0; i<8; i++) // copy the LOGIN port's WWN + pLoggedInPort->u.ucWWN[i] = logi.port_name[i]; + +#ifdef WWN_DBG + ulBuff = (ULONG)pLoggedInPort->u.liWWN; + if( pLoggedInPort->Originator) + printk("o"); + else + printk("r"); + printk("PLOGI port_id %Xh, WWN %08X", + pLoggedInPort->port_id, ulBuff); + + ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); + printk("%08Xh fcPort %p\n", ulBuff, pLoggedInPort); +#endif + break; + + + + + case 0x00000005: // ELS_LOGO (logout) + + + pLoggedInPort->plogi = FALSE; + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; // ELS_PLOGI resets + pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets + pLoggedInPort->logo = TRUE; // ELS_PLOGI resets + pLoggedInPort->LOGO_counter++; // ELS_PLOGI resets + pLoggedInPort->LOGO_timer = 0; +#ifdef WWN_DBG + ulBuff = (ULONG)pLoggedInPort->u.liWWN; + if( pLoggedInPort->Originator) + printk("o"); + else + printk("r"); + printk("LOGO port_id %Xh, WWN %08X", + pLoggedInPort->port_id, ulBuff); + + ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); + printk("%08Xh\n", ulBuff); +#endif + break; + + + +PDISC_case: + case 0x00000050: // ELS_PDISC or ELS_PDISC_ACC + pLoggedInPort->LOGO_timer = 0; // stop the time-out + + pLoggedInPort->prli = TRUE; // ready to accept FCP-SCSI I/O + + + +#ifdef WWN_DBG + ulBuff = (ULONG)pLoggedInPort->u.liWWN; + if( pLoggedInPort->Originator) + printk("o"); + else + printk("r"); + printk("PDISC port_id %Xh, WWN %08X", + pLoggedInPort->port_id, ulBuff); + + ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); + printk("%08Xh\n", ulBuff); +#endif + + + + break; + + + + case 0x1020L: // PRLI? + case 0x1002L: // PRLI ACCept? + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli)); + + pLoggedInPort->fcp_info = prli.fcp_info; // target/initiator flags + pLoggedInPort->prli = TRUE; // PLOGI resets, PDISC doesn't + + pLoggedInPort->pdisc = TRUE; // expect to send (or receive) PDISC + // next time + pLoggedInPort->LOGO_timer = 0; // will be set next LinkDown +#ifdef WWN_DBG + ulBuff = (ULONG)pLoggedInPort->u.liWWN; + if( pLoggedInPort->Originator) + printk("o"); + else + printk("r"); + printk("PRLI port_id %Xh, WWN %08X", + pLoggedInPort->port_id, ulBuff); + + ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); + printk("%08Xh\n", ulBuff); +#endif + + break; + + } + + return; +} + + + + + + +static void BuildLinkServicePayload( PTACHYON fcChip, ULONG type, void* payload) +{ + LOGIN_PAYLOAD *plogi; // FC-PH Port Login + LOGIN_PAYLOAD PlogiPayload; // copy for BIG ENDIAN switch + PRLI_REQUEST *prli; // FCP-SCSI Process Login + PRLI_REQUEST PrliPayload; // copy for BIG ENDIAN switch + LOGOUT_PAYLOAD *logo; + LOGOUT_PAYLOAD LogoutPayload; +// PRLO_REQUEST *prlo; +// PRLO_REQUEST PrloPayload; + REJECT_MESSAGE rjt, *prjt; + + memset( &PlogiPayload, 0, sizeof( PlogiPayload)); + plogi = &PlogiPayload; // load into stack buffer, + // then BIG-ENDIAN switch a copy to caller + + + switch( type ) // payload type can be ELS_PLOGI, ELS_PRLI, ADISC, ... + { + case ELS_FDISC: + case ELS_FLOGI: + case ELS_PLOGI_ACC: // FC-PH PORT Login Accept + case ELS_PLOGI: // FC-PH PORT Login + case ELS_PDISC: // FC-PH2 Port Discovery - same payload as ELS_PLOGI + plogi->login_cmd = LS_PLOGI; + if( type == ELS_PDISC) + plogi->login_cmd = LS_PDISC; + else if( type == ELS_PLOGI_ACC ) + plogi->login_cmd = LS_ACC; + + plogi->cmn_services.bb_credit = 0x00; + plogi->cmn_services.lowest_ver = fcChip->lowest_FCPH_ver; + plogi->cmn_services.highest_ver = fcChip->highest_FCPH_ver; + plogi->cmn_services.bb_rx_size = TACHLITE_TS_RX_SIZE; + plogi->cmn_services.common_features = CONTINUOSLY_INCREASING | + RANDOM_RELATIVE_OFFSET; + + // fill in with World Wide Name based Port Name - 8 UCHARs + // get from Tach registers WWN hi & lo + LoadWWN( fcChip, plogi->port_name, 0); + // fill in with World Wide Name based Node/Fabric Name - 8 UCHARs + // get from Tach registers WWN hi & lo + LoadWWN( fcChip, plogi->node_name, 1); + + // For Seagate Drives. + // + plogi->cmn_services.common_features |= 0x800; + plogi->cmn_services.rel_offset = 0xFE; + plogi->cmn_services.concurrent_seq = 1; + plogi->class1.service_options = 0x00; + plogi->class2.service_options = 0x00; + plogi->class3.service_options = CLASS_VALID; + plogi->class3.initiator_control = 0x00; + plogi->class3.rx_data_size = MAX_RX_PAYLOAD; + plogi->class3.recipient_control = + ERROR_DISCARD | ONE_CATEGORY_SEQUENCE; + plogi->class3.concurrent_sequences = 1; + plogi->class3.open_sequences = 1; + plogi->vendor_id[0] = 'C'; plogi->vendor_id[1] = 'Q'; + plogi->vendor_version[0] = 'C'; plogi->vendor_version[1] = 'Q'; + plogi->vendor_version[2] = ' '; plogi->vendor_version[3] = '0'; + plogi->vendor_version[4] = '0'; plogi->vendor_version[5] = '0'; + + + // FLOGI specific fields... (see FC-FLA, Rev 2.7, Aug 1999, sec 5.1) + if( (type == ELS_FLOGI) || (type == ELS_FDISC) ) + { + if( type == ELS_FLOGI ) + plogi->login_cmd = LS_FLOGI; + else + plogi->login_cmd = LS_FDISC; + + plogi->cmn_services.lowest_ver = 0x20; + plogi->cmn_services.common_features = 0x0800; + plogi->cmn_services.rel_offset = 0; + plogi->cmn_services.concurrent_seq = 0; + + plogi->class3.service_options = 0x8800; + plogi->class3.rx_data_size = 0; + plogi->class3.recipient_control = 0; + plogi->class3.concurrent_sequences = 0; + plogi->class3.open_sequences = 0; + } + + // copy back to caller's buff, w/ BIG ENDIAN swap + BigEndianSwap( (UCHAR*)&PlogiPayload, payload, sizeof(PlogiPayload)); + break; + + + case ELS_ACC: // generic Extended Link Service ACCept + plogi->login_cmd = LS_ACC; + // copy back to caller's buff, w/ BIG ENDIAN swap + BigEndianSwap( (UCHAR*)&PlogiPayload, payload, 4); + break; + + + + case ELS_SCR: // Fabric State Change Registration + { + SCR_PL scr; // state change registration + + memset( &scr, 0, sizeof(scr)); + + scr.command = LS_SCR; // 0x62000000 + // see FC-FLA, Rev 2.7, Table A.22 (pg 82) + scr.function = 3; // 1 = Events detected by Fabric + // 2 = N_Port detected registration + // 3 = Full registration + + // copy back to caller's buff, w/ BIG ENDIAN swap + BigEndianSwap( (UCHAR*)&scr, payload, sizeof(SCR_PL)); + } + + break; + + + case FCS_NSR: // Fabric Name Service Request + { + NSR_PL nsr; // Name Server Req. payload + + memset( &nsr, 0, sizeof(NSR_PL)); + + // see Brocade Fabric Programming Guide, + // Rev 1.3, pg 4-44 + nsr.CT_Rev = 0x01000000; + nsr.FCS_Type = 0xFC020000; + nsr.Command_code = 0x01710000; + nsr.FCP = 8; + + // copy back to caller's buff, w/ BIG ENDIAN swap + BigEndianSwap( (UCHAR*)&nsr, payload, sizeof(NSR_PL)); + } + + break; + + + + + case ELS_LOGO: // FC-PH PORT LogOut + logo = &LogoutPayload; // load into stack buffer, + // then BIG-ENDIAN switch a copy to caller + logo->cmd = LS_LOGO; + // load the 3 UCHARs of the node name + // (if private loop, upper two UCHARs 0) + logo->reserved = 0; + + logo->n_port_identifier[0] = (UCHAR)(fcChip->Registers.my_al_pa); + logo->n_port_identifier[1] = + (UCHAR)(fcChip->Registers.my_al_pa>>8); + logo->n_port_identifier[2] = + (UCHAR)(fcChip->Registers.my_al_pa>>16); + // fill in with World Wide Name based Port Name - 8 UCHARs + // get from Tach registers WWN hi & lo + LoadWWN( fcChip, logo->port_name, 0); + + BigEndianSwap( (UCHAR*)&LogoutPayload, + payload, sizeof(LogoutPayload) ); // 16 UCHAR struct + break; + + + case ELS_LOGO_ACC: // Logout Accept (FH-PH pg 149, table 74) + logo = &LogoutPayload; // load into stack buffer, + // then BIG-ENDIAN switch a copy to caller + logo->cmd = LS_ACC; + BigEndianSwap( (UCHAR*)&LogoutPayload, payload, 4 ); // 4 UCHAR cmnd + break; + + + case ELS_RJT: // ELS_RJT link service reject (FH-PH pg 155) + + prjt = (REJECT_MESSAGE*)payload; // pick up passed data + rjt.command_code = ELS_RJT; + // reverse fields, because of Swap that follows... + rjt.vendor = prjt->reserved; // vendor specific + rjt.explain = prjt->reason; // + rjt.reason = prjt->explain; // + rjt.reserved = prjt->vendor; // + // BIG-ENDIAN switch a copy to caller + BigEndianSwap( (UCHAR*)&rjt, payload, 8 ); // 8 UCHAR cmnd + break; + + + + + + case ELS_PRLI_ACC: // Process Login ACCept + case ELS_PRLI: // Process Login + case ELS_PRLO: // Process Logout + memset( &PrliPayload, 0, sizeof( PrliPayload)); + prli = &PrliPayload; // load into stack buffer, + + if( type == ELS_PRLI ) + prli->cmd = 0x20; // Login + else if( type == ELS_PRLO ) + prli->cmd = 0x21; // Logout + else if( type == ELS_PRLI_ACC ) + { + prli->cmd = 0x02; // Login ACCept + prli->valid = REQUEST_EXECUTED; + } + + + prli->valid |= SCSI_FCP | ESTABLISH_PAIR; + prli->fcp_info = READ_XFER_RDY; + prli->page_length = 0x10; + prli->payload_length = 20; + // Can be initiator AND target + + if( fcChip->Options.initiator ) + prli->fcp_info |= INITIATOR_FUNCTION; + if( fcChip->Options.target ) + prli->fcp_info |= TARGET_FUNCTION; + + BigEndianSwap( (UCHAR*)&PrliPayload, payload, prli->payload_length); + break; + + + + default: // no can do - programming error + printk(" BuildLinkServicePayload unknown!\n"); + break; + } +} + +// loads 8 UCHARs for PORT name or NODE name base on +// controller's WWN. +void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type) +{ + UCHAR* bPtr, i; + + switch( type ) + { + case 0: // Port_Name + bPtr = (UCHAR*)&fcChip->Registers.wwn_hi; + for( i =0; i<4; i++) + dest[i] = *bPtr++; + bPtr = (UCHAR*)&fcChip->Registers.wwn_lo; + for( i =4; i<8; i++) + dest[i] = *bPtr++; + break; + case 1: // Node/Fabric _Name + bPtr = (UCHAR*)&fcChip->Registers.wwn_hi; + for( i =0; i<4; i++) + dest[i] = *bPtr++; + bPtr = (UCHAR*)&fcChip->Registers.wwn_lo; + for( i =4; i<8; i++) + dest[i] = *bPtr++; + break; + } + +} + + + +// We check the Port Login payload for required values. Note that +// ELS_PLOGI and ELS_PDISC (Port DISCover) use the same payload. + + +int verify_PLOGI( PTACHYON fcChip, + TachFCHDR_GCMND* fchs, + ULONG* reject_explain) +{ + LOGIN_PAYLOAD login; + + // source, dest, len (should be mult. of 4) + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&login, sizeof(login)); + + // check FC version + // if other port's highest supported version + // is less than our lowest, and + // if other port's lowest + if( login.cmn_services.highest_ver < fcChip->lowest_FCPH_ver || + login.cmn_services.lowest_ver > fcChip->highest_FCPH_ver ) + { + *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR); + return LOGICAL_ERROR; + } + + // Receive Data Field Size must be >=128 + // per FC-PH + if (login.cmn_services.bb_rx_size < 128) + { + *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, DATA_FIELD_SIZE_ERROR); + return LOGICAL_ERROR; + } + + // Only check Class 3 params + if( login.class3.service_options & CLASS_VALID) + { + if (login.class3.rx_data_size < 128) + { + *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INVALID_CSP); + return LOGICAL_ERROR; + } + if( login.class3.initiator_control & XID_REQUIRED) + { + *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INITIATOR_CTL_ERROR); + return LOGICAL_ERROR; + } + } + return 0; // success +} + + + + +int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain) +{ + PRLI_REQUEST prli; // buffer for BIG ENDIAN + + // source, dest, len (should be mult. of 4) + BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli)); + + if( prli.fcp_info == 0 ) // i.e., not target or initiator? + { + *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR); + return LOGICAL_ERROR; + } + + return 0; // success +} + + +// SWAP UCHARs as required by Fibre Channel (i.e. BIG ENDIAN) +// INPUTS: +// source - ptr to LITTLE ENDIAN ULONGS +// cnt - number of UCHARs to switch (should be mult. of ULONG) +// OUTPUTS: +// dest - ptr to BIG ENDIAN copy +// RETURN: +// none +// +void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt) +{ + int i,j; + + source+=3; // start at MSB of 1st ULONG + for( j=0; j < cnt; j+=4, source+=4, dest+=4) // every ULONG + { + for( i=0; i<4; i++) // every UCHAR in ULONG + *(dest+i) = *(source-i); + } +} + + + + +// Build FC Exchanges............ + +static void buildFCPstatus( + PTACHYON fcChip, + ULONG ExchangeID); + +static LONG FindFreeExchange( PTACHYON fcChip, ULONG type ); + +static ULONG build_SEST_sgList( + ULONG *SESTalPairStart, + Scsi_Cmnd *Cmnd, + ULONG *sgPairs, + PSGPAGES sgPages // link list of TL Ext. S/G pages from O/S Pool +); + +static int build_FCP_payload( Scsi_Cmnd *Cmnd, + UCHAR* payload, ULONG type, ULONG fcp_dl ); + + +/* + IRB + ERQ __________________ + | | / | Req_A_SFS_Len | ____________________ + |----------| / | Req_A_SFS_Addr |------->| Reserved | + | IRB | / | Req_A_D_ID | | SOF EOF TimeStamp | + |-----------/ | Req_A_SEST_Index |-+ | R_CTL | D_ID | + | IRB | | Req_B... | | | CS_CTL| S_ID | + |-----------\ | | | | TYPE | F_CTL | + | IRB | \ | | | | SEQ_ID | SEQ_CNT | + |----------- \ | | +-->+--| OX_ID | RX_ID | + | | \ |__________________| | | RO | + | | pl (payload/cmnd) | + | | ..... | + | |___________________| + | + | ++-------------------------------------------+ +| +| +| e.g. IWE +| SEST __________________ for FCP_DATA +| | | / | | Hdr_Len | ____________________ +| |----------| / | Hdr_Addr_Addr |------->| Reserved | +| | [0] | / |Remote_ID| RSP_Len| | SOF EOF TimeStamp | +| |-----------/ | RSP_Addr |---+ | R_CTL | D_ID | ++-> [1] | | | Buff_Off | | | CS_CTL| S_ID | + |-----------\ |BuffIndex| Link | | | TYPE | F_CTL | + | [2] | \ | Rsvd | RX_ID | | | SEQ_ID | SEQ_CNT | + |----------- \ | Data_Len | | | OX_ID | RX_ID | + | ... | \ | Exp_RO | | | RO | + |----------| | Exp_Byte_Cnt | | |___________________| + | SEST_LEN | +--| Len | | + |__________| | | Address | | + | | ... | | for FCP_RSP + | |__________________| | ____________________ + | +----| Reserved | + | | SOF EOF TimeStamp | + | | R_CTL | D_ID | + | | CS_CTL| S_ID | + +--- local or extended | .... | + scatter/gather lists + defining upper-layer + data (e.g. from user's App) + + +*/ +// All TachLite commands must start with a SFS (Single Frame Sequence) +// command. In the simplest case (a NOP Basic Link command), +// only one frame header and ERQ entry is required. The most complex +// case is the SCSI assisted command, which requires an ERQ entry, +// SEST entry, and several frame headers and data buffers all +// logically linked together. +// Inputs: +// cpqfcHBAdata - controller struct +// type - PLOGI, SCSI_IWE, etc. +// InFCHS - Incoming Tachlite FCHS which prompted this exchange +// (only s_id set if we are originating) +// Data - PVOID to data struct consistent with "type" +// fcExchangeIndex - pointer to OX/RD ID value of built exchange +// Return: +// fcExchangeIndex - OX/RD ID value if successful +// 0 - success +// INVALID_ARGS - NULL/ invalid passed args +// BAD_ALPA - Bad source al_pa address +// LNKDWN_OSLS - Link Down (according to this controller) +// OUTQUE_FULL - Outbound Que full +// DRIVERQ_FULL - controller's Exchange array full +// SEST_FULL - SEST table full +// +// Remarks: +// Psuedo code: +// Check for NULL pointers / bad args +// Build outgoing FCHS - the header/payload struct +// Build IRB (for ERQ entry) +// if SCSI command, build SEST entry (e.g. IWE, TRE,...) +// return success + +//sbuildex +ULONG cpqfcTSBuildExchange( + CPQFCHBA *cpqfcHBAdata, + ULONG type, // e.g. PLOGI + TachFCHDR_GCMND* InFCHS, // incoming FCHS + void *Data, // the CDB, scatter/gather, etc. + LONG *fcExchangeIndex ) // points to allocated exchange, +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG ulStatus = 0; // assume OK + USHORT ox_ID, rx_ID=0xFFFF; + ULONG SfsLen=0L; + TachLiteIRB* pIRB; + IRBflags IRB_flags; + UCHAR *pIRB_flags = (UCHAR*)&IRB_flags; + TachFCHDR_GCMND* CMDfchs; + TachFCHDR* dataHDR; // 32 byte HEADER ONLY FCP-DATA buffer + TachFCHDR_RSP* rspHDR; // 32 byte header + RSP payload + Scsi_Cmnd *Cmnd = (Scsi_Cmnd*)Data; // Linux Scsi CDB, S/G, ... + TachLiteIWE* pIWE; + TachLiteIRE* pIRE; + TachLiteTWE* pTWE; + TachLiteTRE* pTRE; + ULONG fcp_dl; // total byte length of DATA transfered + ULONG fl; // frame length (FC frame size, 128, 256, 512, 1024) + ULONG sgPairs; // number of valid scatter/gather pairs + int FCP_SCSI_command; + BA_ACC_PAYLOAD *ba_acc; + BA_RJT_PAYLOAD *ba_rjt; + + // check passed ARGS + if( !fcChip->ERQ ) // NULL ptr means uninitialized Tachlite chip + return INVALID_ARGS; + + + if( type == SCSI_IRE || + type == SCSI_TRE || + type == SCSI_IWE || + type == SCSI_TWE) + FCP_SCSI_command = 1; + + else + FCP_SCSI_command = 0; + + + // for commands that pass payload data (e.g. SCSI write) + // examine command struct - verify that the + // length of s/g buffers is adequate for total payload + // length (end of list is NULL address) + + if( FCP_SCSI_command ) + { + if( Data ) // must have data descriptor (S/G list -- at least + // one address with at least 1 byte of data) + { + // something to do (later)? + } + + else + return INVALID_ARGS; // invalid DATA ptr + } + + + + // we can build an Exchange for later Queuing (on the TL chip) + // if an empty slot is available in the DevExt for this controller + // look for available Exchange slot... + + if( type != FCP_RESPONSE && + type != BLS_ABTS && + type != BLS_ABTS_ACC ) // already have Exchange slot! + *fcExchangeIndex = FindFreeExchange( fcChip, type ); + + if( *fcExchangeIndex != -1 ) // Exchange is available? + { + // assign tmp ptr (shorthand) + CMDfchs = &Exchanges->fcExchange[ *fcExchangeIndex].fchs; + + + if( Cmnd != NULL ) // (necessary for ABTS cases) + { + Exchanges->fcExchange[ *fcExchangeIndex].Cmnd = Cmnd; // Linux Scsi + Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort = + fcFindLoggedInPort( fcChip, + Exchanges->fcExchange[ *fcExchangeIndex].Cmnd, // find Scsi Nexus + 0, // DON'T search linked list for FC port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + } + + + // Build the command frame header (& data) according + // to command type + + // fields common for all SFS frame types + CMDfchs->reserved = 0L; // must clear + CMDfchs->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; LCr=0, no TS + + // get the destination port_id from incoming FCHS + // (initialized before calling if we're Originator) + // Frame goes to port it was from - the source_id + + CMDfchs->d_id = InFCHS->s_id &0xFFFFFF; // destination (add R_CTL later) + CMDfchs->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 + + + // now enter command-specific fields + switch( type ) + { + + case BLS_NOP: // FC defined basic link service command NO-OP + // ensure unique X_IDs! (use tracking function) + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += 32L; // add len to LSB (header only - no payload) + + // TYPE[31-24] 00 Basic Link Service + // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. + CMDfchs->d_id |= 0x80000000L; // R_CTL = 80 for NOP (Basic Link Ser.) + CMDfchs->f_ctl = 0x00310000L; // xchng originator, 1st seq,.... + CMDfchs->seq_cnt = 0x0L; + CMDfchs->ox_rx_id = 0xFFFF; // RX_ID for now; OX_ID on start + CMDfchs->ro = 0x0L; // relative offset (n/a) + CMDfchs->pl[0] = 0xaabbccddL; // words 8-15 frame data payload (n/a) + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // seconds + // (NOP should complete ~instantly) + break; + + + + + case BLS_ABTS_ACC: // Abort Sequence ACCept + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload) + + CMDfchs->d_id |= 0x84000000L; // R_CTL = 84 for BASIC ACCept + // TYPE[31-24] 00 Basic Link Service + // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I. + CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI + // CMDfchs->seq_id & count might be set from DataHdr? + CMDfchs->ro = 0x0L; // relative offset (n/a) + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds + // (Timeout in case of weird error) + + // now set the ACCept payload... + ba_acc = (BA_ACC_PAYLOAD*)&CMDfchs->pl[0]; + memset( ba_acc, 0, sizeof( BA_ACC_PAYLOAD)); + // Since PLDA requires (only) entire Exchange aborts, we don't need + // to worry about what the last sequence was. + + // We expect that a "target" task is accepting the abort, so we + // can use the OX/RX ID pair + ba_acc->ox_rx_id = CMDfchs->ox_rx_id; + + // source, dest, #bytes + BigEndianSwap((UCHAR *)&CMDfchs->ox_rx_id, (UCHAR *)&ba_acc->ox_rx_id, 4); + + ba_acc->low_seq_cnt = 0; + ba_acc->high_seq_cnt = 0xFFFF; + + + break; + + + case BLS_ABTS_RJT: // Abort Sequence ACCept + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload) + + CMDfchs->d_id |= 0x85000000L; // R_CTL = 85 for BASIC ReJecT + // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I. + // TYPE[31-24] 00 Basic Link Service + CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI + // CMDfchs->seq_id & count might be set from DataHdr? + CMDfchs->ro = 0x0L; // relative offset (n/a) + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds + // (Timeout in case of weird error) + + CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // copy from sender! + + // now set the ReJecT payload... + ba_rjt = (BA_RJT_PAYLOAD*)&CMDfchs->pl[0]; + memset( ba_rjt, 0, sizeof( BA_RJT_PAYLOAD)); + + // We expect that a "target" task couldn't find the Exhange in the + // array of active exchanges, so we use a new LinkService X_ID. + // See Reject payload description in FC-PH (Rev 4.3), pg. 140 + ba_rjt->reason_code = 0x09; // "unable to perform command request" + ba_rjt->reason_explain = 0x03; // invalid OX/RX ID pair + + + break; + + + + case BLS_ABTS: // FC defined basic link service command ABTS + // Abort Sequence + + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += 32L; // add len to LSB (header only - no payload) + + // TYPE[31-24] 00 Basic Link Service + // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I. + CMDfchs->d_id |= 0x81000000L; // R_CTL = 81 for ABTS + CMDfchs->f_ctl = 0x00110000L; // xchnge originator, last seq, xfer SI + // CMDfchs->seq_id & count might be set from DataHdr? + CMDfchs->ro = 0x0L; // relative offset (n/a) + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds + // (ABTS must timeout when responder is gone) + break; + + + + case FCS_NSR: // Fabric Name Service Request + Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2; + + + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds + // OX_ID, linked to Driver Transaction ID + // (fix-up at Queing time) + CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify + // OX_ID set at ERQueing time + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += (32L + sizeof(NSR_PL)); // add len (header & NSR payload) + + CMDfchs->d_id |= 0x02000000L; // R_CTL = 02 for - + // Name Service Request: Unsolicited + // TYPE[31-24] 01 Extended Link Service + // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. + CMDfchs->f_ctl = 0x20210000L; + // OX_ID will be fixed-up at Tachyon enqueing time + CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt + CMDfchs->ro = 0x0L; // relative offset (n/a) + + BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]); + + + + + + + break; + + + + + case ELS_PLOGI: // FC-PH extended link service command Port Login + // (May, 2000) + // NOTE! This special case facilitates SANMark testing. The SANMark + // test script for initialization-timeout.fcal.SANMark-1.fc + // "eats" the OPN() primitive without issuing an R_RDY, causing + // Tachyon to report LST (loop state timeout), which causes a + // LIP. To avoid this, simply send out the frame (i.e. assuming a + // buffer credit of 1) without waiting for R_RDY. Many FC devices + // (other than Tachyon) have been doing this for years. We don't + // ever want to do this for non-Link Service frames unless the + // other device really did report non-zero login BB credit (i.e. + // in the PLOGI ACCept frame). +// CMDfchs->sof_eof |= 0x00000400L; // LCr=1 + + case ELS_FDISC: // Fabric Discovery (Login) + case ELS_FLOGI: // Fabric Login + case ELS_SCR: // Fabric State Change Registration + case ELS_LOGO: // FC-PH extended link service command Port Logout + case ELS_PDISC: // FC-PH extended link service cmnd Port Discovery + case ELS_PRLI: // FC-PH extended link service cmnd Process Login + + Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2; + + + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds + // OX_ID, linked to Driver Transaction ID + // (fix-up at Queing time) + CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify + // OX_ID set at ERQueing time + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + if( type == ELS_LOGO ) + SfsLen += (32L + 16L); // add len (header & PLOGI payload) + else if( type == ELS_PRLI ) + SfsLen += (32L + 20L); // add len (header & PRLI payload) + else if( type == ELS_SCR ) + SfsLen += (32L + sizeof(SCR_PL)); // add len (header & SCR payload) + else + SfsLen += (32L + 116L); // add len (header & PLOGI payload) + + CMDfchs->d_id |= 0x22000000L; // R_CTL = 22 for - + // Extended Link_Data: Unsolicited Control + // TYPE[31-24] 01 Extended Link Service + // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. + CMDfchs->f_ctl = 0x01210000L; + // OX_ID will be fixed-up at Tachyon enqueing time + CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt + CMDfchs->ro = 0x0L; // relative offset (n/a) + + BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]); + + break; + + + + case ELS_LOGO_ACC: // FC-PH extended link service logout accept + case ELS_RJT: // extended link service reject (add reason) + case ELS_ACC: // ext. link service generic accept + case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC) + case ELS_PRLI_ACC: // ext. link service process login accept + + + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // assume done + // ensure unique X_IDs! (use tracking function) + // OX_ID from initiator cmd + ox_ID = (USHORT)(InFCHS->ox_rx_id >> 16); + rx_ID = 0xFFFF; // RX_ID, linked to Driver Exchange ID + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (not SEST index) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + if( type == ELS_RJT ) + { + SfsLen += (32L + 8L); // add len (header + payload) + + // ELS_RJT reason codes (utilize unused "reserved" field) + CMDfchs->pl[0] = 1; + CMDfchs->pl[1] = InFCHS->reserved; + + } + else if( (type == ELS_LOGO_ACC) || (type == ELS_ACC) ) + SfsLen += (32L + 4L); // add len (header + payload) + else if( type == ELS_PLOGI_ACC ) + SfsLen += (32L + 116L); // add len (header + payload) + else if( type == ELS_PRLI_ACC ) + SfsLen += (32L + 20L); // add len (header + payload) + + CMDfchs->d_id |= 0x23000000L; // R_CTL = 23 for - + // Extended Link_Data: Control Reply + // TYPE[31-24] 01 Extended Link Service + // f_ctl[23:0] exchg responder, last seq, e_s, tsi + CMDfchs->f_ctl = 0x01990000L; + CMDfchs->seq_cnt = 0x0L; + CMDfchs->ox_rx_id = 0L; // clear + CMDfchs->ox_rx_id = ox_ID; // load upper 16 bits + CMDfchs->ox_rx_id <<= 16; // shift them + + CMDfchs->ro = 0x0L; // relative offset (n/a) + + BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]); + + break; + + + // Fibre Channel SCSI 'originator' sequences... + // (originator means 'initiator' in FCP-SCSI) + case SCSI_IWE: // TachLite Initiator Write Entry + { + PFC_LOGGEDIN_PORT pLoggedInPort = + Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort; + + Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1; + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // FC2 timeout + + // first, build FCP_CMND + // unique X_ID fix-ups in StartExchange + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index) + + // NOTE: unlike FC LinkService login frames, normal + // SCSI commands are sent without outgoing verification + IRB_flags.DCM = 1; // Disable completion message for Cmnd frame + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += 64L; // add len to LSB (header & CMND payload) + + CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command + + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. + // valid RO + CMDfchs->f_ctl = 0x08210008L; + CMDfchs->seq_cnt = 0x0L; + CMDfchs->ox_rx_id = 0L; // clear for now (-or- in later) + CMDfchs->ro = 0x0L; // relative offset (n/a) + + // now, fill out FCP-DATA header + // (use buffer inside SEST object) + dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ]; + dataHDR->reserved = 0L; // must clear + dataHDR->sof_eof = 0x75002000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS + dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA + dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] xfer S.I.| valid RO + dataHDR->f_ctl = 0x08010008L; + dataHDR->seq_cnt = 0x02000000L; // sequence ID: df_ctl : seqence count + dataHDR->ox_rx_id = 0L; // clear; fix-up dataHDR fields later + dataHDR->ro = 0x0L; // relative offset (n/a) + + // Now setup the SEST entry + pIWE = &fcChip->SEST->u[ *fcExchangeIndex ].IWE; + + // fill out the IWE: + + // VALid entry:Dir outbound:DCM:enable CM:enal INT: FC frame len + pIWE->Hdr_Len = 0x8e000020L; // data frame Len always 32 bytes + + + // from login parameters with other port, what's the largest frame + // we can send? + if( pLoggedInPort == NULL) + { + ulStatus = INVALID_ARGS; // failed! give up + break; + } + if( pLoggedInPort->rx_data_size >= 2048) + fl = 0x00020000; // 2048 code (only support 1024!) + else if( pLoggedInPort->rx_data_size >= 1024) + fl = 0x00020000; // 1024 code + else if( pLoggedInPort->rx_data_size >= 512) + fl = 0x00010000; // 512 code + else + fl = 0; // 128 bytes -- should never happen + + + pIWE->Hdr_Len |= fl; // add xmit FC frame len for data phase + pIWE->Hdr_Addr = virt_to_bus( dataHDR ); + pIWE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame) + pIWE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID + + memset( &fcChip->SEST->RspHDR[ *fcExchangeIndex].pl, 0, + sizeof( FCP_STATUS_RESPONSE) ); // clear out previous status + + pIWE->RSP_Addr = virt_to_bus( + &fcChip->SEST->RspHDR[ *fcExchangeIndex ]); + + // Do we need local or extended gather list? + // depends on size - we can handle 3 len/addr pairs + // locally. + + fcp_dl = build_SEST_sgList( + &pIWE->GLen1, + Cmnd, // S/G list + &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) + &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) + + if( !fcp_dl ) // error building S/G list? + { + ulStatus = MEMPOOL_FAIL; + break; // give up + } + + // Now that we know total data length in + // the passed S/G buffer, set FCP CMND frame + build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl ); + + + + if( sgPairs > 3 ) // need extended s/g list + pIWE->Buff_Off = 0x78000000L; // extended data | (no offset) + else // local data pointers (in SEST) + pIWE->Buff_Off = 0xf8000000L; // local data | (no offset) + + // ULONG 5 + pIWE->Link = 0x0000ffffL; // Buff_Index | Link + + pIWE->RX_ID = 0x0L; // DWord 6: RX_ID set by target XFER_RDY + + // DWord 7 + pIWE->Data_Len = 0L; // TL enters rcv'd XFER_RDY BURST_LEN + pIWE->Exp_RO = 0L; // DWord 8 + // DWord 9 + pIWE->Exp_Byte_Cnt = fcp_dl; // sum of gather buffers + } + break; + + + + + + case SCSI_IRE: // TachLite Initiator Read Entry + + if( Cmnd->timeout != 0) + { +// printk("Cmnd->timeout %d\n", Cmnd->timeout); + // per Linux Scsi + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = Cmnd->timeout; + } + else // use our best guess, based on FC & device + { + + if( Cmnd->SCp.Message == 1 ) // Tape device? (from INQUIRY) + { + // turn off our timeouts (for now...) + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 0xFFFFFFFF; + } + else + { + Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1; + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // per SCSI req. + } + } + + + // first, build FCP_CMND + + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index) + // NOTE: unlike FC LinkService login frames, + // normal SCSI commands are sent "open loop" + IRB_flags.DCM = 1; // Disable completion message for Cmnd frame + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += 64L; // add len to LSB (header & CMND payload) + + CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command + + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. + // valid RO + CMDfchs->f_ctl = 0x08210008L; + CMDfchs->seq_cnt = 0x0L; + // x_ID & data direction bit set later + CMDfchs->ox_rx_id = 0xFFFF; // clear + CMDfchs->ro = 0x0L; // relative offset (n/a) + + + + // Now setup the SEST entry + pIRE = &fcChip->SEST->u[ *fcExchangeIndex ].IRE; + + // fill out the IRE: + // VALid entry:Dir outbound:enable CM:enal INT: + pIRE->Seq_Accum = 0xCE000000L; // VAL,DIR inbound,DCM| INI,DAT,RSP + + pIRE->reserved = 0L; + pIRE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame) + pIRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID + + + pIRE->RSP_Addr = virt_to_bus( + &fcChip->SEST->RspHDR[ *fcExchangeIndex ]); + + + // Do we need local or extended gather list? + // depends on size - we can handle 3 len/addr pairs + // locally. + + fcp_dl = build_SEST_sgList( + &pIRE->SLen1, + Cmnd, // SCSI command Data desc. with S/G list + &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) + &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) + + + if( !fcp_dl ) // error building S/G list? + { + // It is permissible to have a ZERO LENGTH Read command. + // If there is the case, simply set fcp_dl (and Exp_Byte_Cnt) + // to 0 and continue. + if( Cmnd->request_bufflen == 0 ) + { + fcp_dl = 0; // no FC DATA frames expected + + } + else + { + ulStatus = MEMPOOL_FAIL; + break; // give up + } + } + + // now that we know the S/G length, build CMND payload + build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl ); + + + if( sgPairs > 3 ) // need extended s/g list + pIRE->Buff_Off = 0x00000000; // DWord 4: extended s/g list, no offset + else + pIRE->Buff_Off = 0x80000000; // local data, no offset + + pIRE->Buff_Index = 0x0L; // DWord 5: Buff_Index | Reserved + + pIRE->Exp_RO = 0x0L; // DWord 6: Expected Rel. Offset + + pIRE->Byte_Count = 0; // DWord 7: filled in by TL on err + pIRE->reserved_ = 0; // DWord 8: reserved + // NOTE: 0 length READ is OK. + pIRE->Exp_Byte_Cnt = fcp_dl;// DWord 9: sum of scatter buffers + + break; + + + + + // Fibre Channel SCSI 'responder' sequences... + // (originator means 'target' in FCP-SCSI) + case SCSI_TWE: // TachLite Target Write Entry + + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req. + + // first, build FCP_CMND + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (XFER_RDY) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload) + + CMDfchs->d_id |= (0x05000000L); // R_CTL = 5 for XFER_RDY + + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] exchg responder, 1st seq, xfer S.I. + // valid RO + CMDfchs->f_ctl = 0x08810008L; + CMDfchs->seq_cnt = 0x01000000; // sequence ID: df_ctl: sequence count + // use originator (other port's) OX_ID + CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // we want upper 16 bits + CMDfchs->ro = 0x0L; // relative offset (n/a) + + // now, fill out FCP-RSP header + // (use buffer inside SEST object) + + rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ]; + rspHDR->reserved = 0L; // must clear + rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS + rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP + rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] responder|last seq| xfer S.I. + rspHDR->f_ctl = 0x08910000L; + rspHDR->seq_cnt = 0x03000000; // sequence ID + rspHDR->ox_rx_id = InFCHS->ox_rx_id; // gives us OX_ID + rspHDR->ro = 0x0L; // relative offset (n/a) + + + // Now setup the SEST entry + + pTWE = &fcChip->SEST->u[ *fcExchangeIndex ].TWE; + + // fill out the TWE: + + // VALid entry:Dir outbound:enable CM:enal INT: + pTWE->Seq_Accum = 0xC4000000L; // upper word flags + pTWE->reserved = 0L; + pTWE->Remote_Node_ID = 0L; // no more auto RSP frame! (TL/TS change) + pTWE->Remote_Node_ID |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID + + + // Do we need local or extended gather list? + // depends on size - we can handle 3 len/addr pairs + // locally. + + fcp_dl = build_SEST_sgList( + &pTWE->SLen1, + Cmnd, // S/G list + &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) + &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) + + + if( !fcp_dl ) // error building S/G list? + { + ulStatus = MEMPOOL_FAIL; + break; // give up + } + + // now that we know the S/G length, build CMND payload + build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl ); + + + if( sgPairs > 3 ) // need extended s/g list + pTWE->Buff_Off = 0x00000000; // extended s/g list, no offset + else + pTWE->Buff_Off = 0x80000000; // local data, no offset + + pTWE->Buff_Index = 0; // Buff_Index | Link + pTWE->Exp_RO = 0; + pTWE->Byte_Count = 0; // filled in by TL on err + pTWE->reserved_ = 0; + pTWE->Exp_Byte_Cnt = fcp_dl;// sum of scatter buffers + + break; + + + + + + + case SCSI_TRE: // TachLite Target Read Entry + + // It doesn't make much sense for us to "time-out" a READ, + // but we'll use it for design consistency and internal error recovery. + Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req. + + // I/O request block settings... + *pIRB_flags = 0; // clear IRB flags + // check PRLI (process login) info + // to see if Initiator Requires XFER_RDY + // if not, don't send one! + // { PRLI check...} + IRB_flags.SFA = 0; // don't send XFER_RDY - start data + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload) + + + + // now, fill out FCP-DATA header + // (use buffer inside SEST object) + dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ]; + + dataHDR->reserved = 0L; // must clear + dataHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS,noLCr,no TS + dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA + dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 + + + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] exchg responder, not 1st seq, xfer S.I. + // valid RO + dataHDR->f_ctl = 0x08810008L; + dataHDR->seq_cnt = 0x01000000; // sequence ID (no XRDY) + dataHDR->ox_rx_id = InFCHS->ox_rx_id & 0xFFFF0000; // we want upper 16 bits + dataHDR->ro = 0x0L; // relative offset (n/a) + + // now, fill out FCP-RSP header + // (use buffer inside SEST object) + rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ]; + + rspHDR->reserved = 0L; // must clear + rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS + rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP + rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 + // TYPE[31-24] 8 for FCP SCSI + // f_ctl[23:0] responder|last seq| xfer S.I. + rspHDR->f_ctl = 0x08910000L; + rspHDR->seq_cnt = 0x02000000; // sequence ID: df_ctl: sequence count + + rspHDR->ro = 0x0L; // relative offset (n/a) + + + // Now setup the SEST entry + pTRE = &fcChip->SEST->u[ *fcExchangeIndex ].TRE; + + + // VALid entry:Dir outbound:enable CM:enal INT: + pTRE->Hdr_Len = 0x86010020L; // data frame Len always 32 bytes + pTRE->Hdr_Addr = virt_to_bus( dataHDR ); + pTRE->RSP_Len = 64L; // hdr+data (TL assisted RSP frame) + pTRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID + pTRE->RSP_Addr = virt_to_bus( rspHDR ); + + + // Do we need local or extended gather list? + // depends on size - we can handle 3 len/addr pairs + // locally. + + fcp_dl = build_SEST_sgList( + &pTRE->GLen1, + Cmnd, // S/G list + &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) + &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) + + + if( !fcp_dl ) // error building S/G list? + { + ulStatus = MEMPOOL_FAIL; + break; // give up + } + + // no payload or command to build -- READ doesn't need XRDY + + + if( sgPairs > 3 ) // need extended s/g list + pTRE->Buff_Off = 0x78000000L; // extended data | (no offset) + else // local data pointers (in SEST) + pTRE->Buff_Off = 0xf8000000L; // local data | (no offset) + + // ULONG 5 + pTRE->Buff_Index = 0L; // Buff_Index | reserved + pTRE->reserved = 0x0L; // DWord 6 + + // DWord 7: NOTE: zero length will + // hang TachLite! + pTRE->Data_Len = fcp_dl; // e.g. sum of scatter buffers + + pTRE->reserved_ = 0L; // DWord 8 + pTRE->reserved__ = 0L; // DWord 9 + + break; + + + + + + + + case FCP_RESPONSE: + // Target response frame: this sequence uses an OX/RX ID + // pair from a completed SEST exchange. We built most + // of the response frame when we created the TWE/TRE. + + *pIRB_flags = 0; // clear IRB flags + IRB_flags.SFA = 1; // send SFS (RSP) + SfsLen = *pIRB_flags; + + SfsLen <<= 24; // shift flags to MSB + SfsLen += sizeof(TachFCHDR_RSP);// add SFS len (header & RSP payload) + + + Exchanges->fcExchange[ *fcExchangeIndex].type = + FCP_RESPONSE; // change Exchange type to "response" phase + + // take advantage of prior knowledge of OX/RX_ID pair from + // previous XFER outbound frame (still in fchs of exchange) + fcChip->SEST->RspHDR[ *fcExchangeIndex ].ox_rx_id = + CMDfchs->ox_rx_id; + + // Check the status of the DATA phase of the exchange so we can report + // status to the initiator + buildFCPstatus( fcChip, *fcExchangeIndex); // set RSP payload fields + + memcpy( + CMDfchs, // re-use same XFER fchs for Response frame + &fcChip->SEST->RspHDR[ *fcExchangeIndex ], + sizeof( TachFCHDR_RSP )); + + + break; + + default: + printk("cpqfcTS: don't know how to build FC type: %Xh(%d)\n", type,type); + break; + + } + + + + if( !ulStatus) // no errors above? + { + // FCHS is built; now build IRB + + // link the just built FCHS (the "command") to the IRB entry + // for this Exchange. + pIRB = &Exchanges->fcExchange[ *fcExchangeIndex].IRB; + + // len & flags according to command type above + pIRB->Req_A_SFS_Len = SfsLen; // includes IRB flags & len + pIRB->Req_A_SFS_Addr = virt_to_bus(CMDfchs); // TL needs physical addr + // of frame to send + pIRB->Req_A_SFS_D_ID = CMDfchs->d_id << 8; // Dest_ID must be consistent! + + // Exchange is complete except for "fix-up" fields to be set + // at Tachyon Queuing time: + // IRB->Req_A_Trans_ID (OX_ID/ RX_ID): + // for SEST entry, lower bits correspond to actual FC Exchange ID + // fchs->OX_ID or RX_ID + } + else + { +#ifdef DBG + printk( "FC Error: SEST build Pool Allocation failed\n"); +#endif + // return resources... + cpqfcTSCompleteExchange( fcChip, *fcExchangeIndex); // SEST build failed + } + } + else // no Exchanges available + { + ulStatus = SEST_FULL; + printk( "FC Error: no fcExchanges available\n"); + } + return ulStatus; +} + + + + + + +// set RSP payload fields +static void buildFCPstatus( PTACHYON fcChip, ULONG ExchangeID) +{ + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ExchangeID]; // shorthand + PFCP_STATUS_RESPONSE pFcpStatus; + + memset( &fcChip->SEST->RspHDR[ ExchangeID ].pl, 0, + sizeof( FCP_STATUS_RESPONSE) ); + if( pExchange->status ) // something wrong? + { + pFcpStatus = (PFCP_STATUS_RESPONSE) // cast RSP buffer for this xchng + &fcChip->SEST->RspHDR[ ExchangeID ].pl; + if( pExchange->status & COUNT_ERROR ) + { + + // set FCP response len valid (so we can report count error) + pFcpStatus->fcp_status |= FCP_RSP_LEN_VALID; + pFcpStatus->fcp_rsp_len = 0x04000000; // 4 byte len (BIG Endian) + + pFcpStatus->fcp_rsp_info = FCP_DATA_LEN_NOT_BURST_LEN; // RSP_CODE + } + } +} + + + + +// This routine builds scatter/gather lists into SEST entries +// INPUTS: +// SESTalPair - SEST address @DWordA "Local Buffer Length" +// sgList - Scatter/Gather linked list of Len/Address data buffers +// OUTPUT: +// sgPairs - number of valid address/length pairs +// Remarks: +// The SEST data buffer pointers only depend on number of +// length/ address pairs, NOT on the type (IWE, TRE,...) +// Up to 3 pairs can be referenced in the SEST - more than 3 +// require this Extended S/G list page. The page holds 4, 8, 16... +// len/addr pairs, per Scatter/Gather List Page Length Reg. +// TachLite allows pages to be linked to any depth. + +//#define DBG_SEST_SGLIST 1 // for printing out S/G pairs with Ext. pages + +static ULONG build_SEST_sgList( + ULONG *SESTalPairStart, // the 3 len/address buffers in SEST + Scsi_Cmnd *Cmnd, + ULONG *sgPairs, + PSGPAGES sgPages) // link list of TL Ext. S/G pages from O/S Pool + +{ + ULONG i, AllocatedPages=0; // Tach Ext. S/G page allocations + ULONG* alPair = SESTalPairStart; + ULONG alignedPageAddress; // TL hardware alignment requirement + int PairCount; + unsigned long ulBuff; + ULONG total_data_len=0; // (in bytes) + ULONG bytes_to_go = Cmnd->request_bufflen; // total xfer (S/G sum) + ULONG thisMappingLen; + struct scatterlist *sgl; // S/G list (Linux format) + + + + if( !Cmnd->use_sg ) // no S/G list? + { + *sgPairs = 1; // use "local" S/G pair in SEST entry + // (for now, ignore address bits above #31) + *alPair++ = bytes_to_go & 0x7ffff; // bits 18-0, length + ulBuff = virt_to_bus( Cmnd->request_buffer); +#if BITS_PER_LONG > 32 + if( ulBuff >>32 ) + { + printk("FATAL! Tachyon DMA address %p exceeds 32 bits\n", (void*)ulBuff ); + return 0; + } +#endif + *alPair = (ULONG)ulBuff; + return bytes_to_go; + } + + + // [TBD - update for Linux to support > 32 bits addressing] + // since the format for local & extended S/G lists is different, + // check if S/G pairs exceeds 3. + *sgPairs = Cmnd->use_sg; + sgl = (struct scatterlist*)Cmnd->request_buffer; + + if( *sgPairs <= 3 ) // need "local" SEST list + { + while( bytes_to_go) + { + thisMappingLen = sgl->length; // we want them ALL on every pass + bytes_to_go = bytes_to_go - thisMappingLen; + + // we have L/A pair; L = thisMappingLen, A = physicalAddress + // load into SEST... + total_data_len += thisMappingLen & 0x7ffff; // mask in valid bits + // per SEST format + *alPair = thisMappingLen & 0x7ffff; // bits 18-0, length +// physicalAddress.HighPart <= 19; // shift to bit 19 + + // pick up bits 44-32 of upper 64-bit address + // and load into 31-19 LBAU (upper addr) of SEST entry +// *alPair++ |=(ULONG)((physicalAddress.HighPart & 0xFFF8)); + // on Tachlite TS's local S/G, we can handle 13 extra address bits + // i.e., bits 31-19 are actually bits 44-32 of physicalAddress + + alPair++; + + ulBuff = virt_to_bus( sgl->address); +#if BITS_PER_LONG > 32 + if( ulBuff >>32 ) + { + printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff ); + return 0; + } +#endif + *alPair++ = (ULONG)ulBuff; // lower 32 bits (31-0) + + ++sgl; // next S/G pair +#ifdef DBG_SEST_SGLIST + printk(" thisLen %d ", thisMappingLen); + printk(" remain %d\n", bytes_to_go); +#endif + + } + } + + + + + else // more than 3 pairs requires Extended S/G page (Pool Allocation) + { + // clear out SEST DWORDs (local S/G addr) C-F (A-B set in following logic) + + + + for( i=2; i<6; i++) + alPair[i] = 0; + + PairCount = TL_EXT_SG_PAGE_COUNT; // forces initial page allocation + + while( bytes_to_go ) + { + + + // Per SEST format, we can support 524287 byte lenghts per + // S/G pair. Typical user buffers are 4k, and very rarely + // exceed 12k due to fragmentation of physical memory pages. + // However, on certain O/S system (not "user") buffers (on platforms + // with huge memories like 256Meg), it's possible to exceed this + // length in a single S/G address/len mapping. + // + // Check for Tachyon length boundary + // + if( sgl->length > 0x7ffff ) + { + // never ask for more than we can handle + thisMappingLen = sgl->length & 0x7ffff; + } + else + thisMappingLen = sgl->length; + + + + // should we load into "this" extended S/G page, or allocate + // new page? + + if( PairCount >= TL_EXT_SG_PAGE_COUNT ) + { + // have we exceeded the max possible extended pages? + if( AllocatedPages >= TL_MAX_SGPAGES) + { + printk("Error: aborted loop on %d Ext. S/G page allocations\n", + AllocatedPages); + + total_data_len = 0; // failure!! Ext. S/G is All-or-none affair + break; // failed + } + + // Allocate the TL Extended S/G list page from O/S pool. We have + // to allocated twice what we want to ensure required TL alignment + // (Tachlite TL/TS User Man. Rev 6.0, p 168) + // We store the original allocated PVOID so we can free later + + sgPages->PoolPage[ AllocatedPages] = + kmalloc( TL_EXT_SG_PAGE_BYTELEN*2,GFP_ATOMIC); // double for alignment + + + if( !sgPages->PoolPage[ AllocatedPages] ) // Allocation failed? + { + + printk("Error: Allocation failed @ %d S/G page allocations\n", + AllocatedPages); + + total_data_len = 0; // failure!! Ext. S/G is All-or-none affair + break; // give up + } + // clear out memory we just allocated + memset( sgPages->PoolPage[AllocatedPages], 0, + TL_EXT_SG_PAGE_BYTELEN*2); + + + // align the memory - TL requires sizeof() Ext. S/G page alignment. + // We doubled the actual required size so we could mask off LSBs + // to get desired offset + + ulBuff = virt_to_bus( sgPages->PoolPage[AllocatedPages]); + +#if BITS_PER_LONG > 32 + if( ulBuff >>32 ) + { + printk("cqpfcTS: Tach ext. S/G DMA address %p > 32 bits\n", + (void*)ulBuff ); + return 0; + } +#endif + + ulBuff += TL_EXT_SG_PAGE_BYTELEN; // ensures we pass align. boundary + ulBuff &= (0xFFFFFFFF - (TL_EXT_SG_PAGE_BYTELEN -1) );// mask off LSBs + + alignedPageAddress = (ULONG)ulBuff; +#ifdef DBG_SEST_SGLIST + printk("new PoolPage: %p, alignedPageAddress %lXh\n", + sgPages->PoolPage[AllocatedPages], ulBuff); +#endif + + + // set pointer, in SEST if first Ext. S/G page, or in last pair + // of linked Ext. S/G pages... + // (Only 32-bit PVOIDs, so just load lower 32 bits) + // NOTE: the Len field must be '0' if this is the first Ext. S/G + // pointer in SEST, and not 0 otherwise. + if( alPair == SESTalPairStart) // initial Ext. S/G list? + *alPair = 0; + else // not the SEST entry... Len must be non-0, so + // arbitrarily set it to number bytes remaining + *alPair = ( bytes_to_go & 0x7ffff); + +#ifdef DBG_SEST_SGLIST + printk("PairCount %d @%p even %Xh, ", + PairCount, alPair, *alPair); +#endif + alPair++; // next DWORD + + *alPair = alignedPageAddress; // TL needs 32-bit physical +#ifdef DBG_SEST_SGLIST + printk("odd %Xh\n", *alPair); +#endif + + // now reset the pointer to the ACTUAL (Extended) S/G page + // which will accept the Len/ PhysicalAddress pairs + alPair = bus_to_virt(alignedPageAddress); + + AllocatedPages++; + PairCount = 1; // starting new Ext. S/G page + } // end of new TL Ext. S/G page allocation + + + *alPair = thisMappingLen; // bits 18-0, length (range check above) + + +// physicalAddress.HighPart <= 19; // shift to bit 19 + + // pick up bits 44-32 of upper 64-bit address + // and load into 31-19 LBAU (upper addr) of SEST entry +// *alPair |=(ULONG)((physicalAddress.HighPart & 0xFFF8)); + + +#ifdef DBG_SEST_SGLIST + printk("PairCount %d @%p, even %Xh, ", + PairCount, alPair, *alPair); +#endif + + alPair++; // next DWORD + // on Tachlite TS's local S/G, we can handle 13 extra address bits + // i.e., bits 31-19 are actually bits 44-32 of physicalAddress + + + ulBuff = virt_to_bus( sgl->address); +#if BITS_PER_LONG > 32 + if( ulBuff >>32 ) + { + printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff ); + return 0; + } +#endif + *alPair = (ULONG)ulBuff; // lower 32 bits (31-0) + + +#ifdef DBG_SEST_SGLIST + printk("odd %Xh\n", *alPair); +#endif + alPair++; // next DWORD + + + PairCount++; // next Length/Address pair + bytes_to_go -= thisMappingLen; + total_data_len += thisMappingLen; + sgl++; // next S/G pair + } + } + return total_data_len; +} + + + +// The Tachlite SEST table is referenced to OX_ID (or RX_ID). To optimize +// performance and debuggability, we index the Exchange structure to FC X_ID +// This enables us to build exchanges for later en-queing to Tachyon, +// provided we have an open X_ID slot. At Tachyon queing time, we only +// need an ERQ slot; then "fix-up" references in the +// IRB, FCHS, etc. as needed. +// RETURNS: +// 0 if successful +// non-zero on error +//sstartex +ULONG cpqfcTSStartExchange( + CPQFCHBA *cpqfcHBAdata, + LONG ExchangeID ) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ ExchangeID ]; // shorthand + USHORT producer, consumer; + ULONG ulStatus=0; + short int ErqIndex; + BOOLEAN CompleteExchange = FALSE; // e.g. ACC replies are complete + BOOLEAN SestType=FALSE; + ULONG InboundData=0; + + // We will manipulate Tachlite chip registers here to successfully + // start exchanges. + + // Check that link is not down -- we can't start an exchange on a + // down link! + + if( fcChip->Registers.FMstatus.value & 0x80) // LPSM offline? + { +printk("fcStartExchange: PSM offline (%Xh), x_ID %Xh, type %Xh, port_id %Xh\n", + fcChip->Registers.FMstatus.value & 0xFF, + ExchangeID, + pExchange->type, + pExchange->fchs.d_id); + + if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame? + { + // Our most popular LinkService commands are port discovery types + // (PLOGI/ PDISC...), which are implicitly nullified by Link Down + // events, so it makes no sense to Que them. However, ABTS should + // be queued, since exchange sequences are likely destroyed by + // Link Down events, and we want to notify other ports of broken + // sequences by aborting the corresponding exchanges. + if( pExchange->type != BLS_ABTS ) + { + ulStatus = LNKDWN_OSLS; + goto Done; + // don't Que most LinkServ exchanges on LINK DOWN + } + } + + printk("fcStartExchange: Que x_ID %Xh, type %Xh\n", + ExchangeID, pExchange->type); + pExchange->status |= EXCHANGE_QUEUED; + ulStatus = EXCHANGE_QUEUED; + goto Done; + } + + // Make sure ERQ has available space. + + producer = (USHORT)fcChip->ERQ->producerIndex; // copies for logical arith. + consumer = (USHORT)fcChip->ERQ->consumerIndex; + producer++; // We are testing for full que by incrementing + + if( producer >= ERQ_LEN ) // rollover condition? + producer = 0; + if( consumer != producer ) // ERQ not full? + { + // ****************** Need Atomic access to chip registers!!******** + + // remember ERQ PI for copying IRB + ErqIndex = (USHORT)fcChip->ERQ->producerIndex; + fcChip->ERQ->producerIndex = producer; // this is written to Tachyon + // we have an ERQ slot! If SCSI command, need SEST slot + // otherwise we are done. + + // Note that Tachyon requires that bit 15 of the OX_ID or RX_ID be + // set according to direction of data to/from Tachyon for SEST assists. + // For consistency, enforce this rule for Link Service (non-SEST) + // exchanges as well. + + // fix-up the X_ID field in IRB + pExchange->IRB.Req_A_Trans_ID = ExchangeID & 0x7FFF; // 15-bit field + + // fix-up the X_ID field in fchs -- depends on Originator or Responder, + // outgoing or incoming data? + switch( pExchange->type ) + { + // ORIGINATOR types... we're setting our OX_ID and + // defaulting the responder's RX_ID to 0xFFFF + + case SCSI_IRE: + // Requirement: set MSB of x_ID for Incoming TL data + // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50) + InboundData = 0x8000; + + case SCSI_IWE: + SestType = TRUE; + pExchange->fchs.ox_rx_id = (ExchangeID | InboundData); + pExchange->fchs.ox_rx_id <<= 16; // MSW shift + pExchange->fchs.ox_rx_id |= 0xffff; // add default RX_ID + + // now fix-up the Data HDR OX_ID (TL automatically does rx_id) + // (not necessary for IRE -- data buffer unused) + if( pExchange->type == SCSI_IWE) + { + fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id = + pExchange->fchs.ox_rx_id; + + } + + break; + + + case FCS_NSR: // ext. link service Name Service Request + case ELS_SCR: // ext. link service State Change Registration + case ELS_FDISC:// ext. link service login + case ELS_FLOGI:// ext. link service login + case ELS_LOGO: // FC-PH extended link service logout + case BLS_NOP: // Basic link service No OPeration + case ELS_PLOGI:// ext. link service login (PLOGI) + case ELS_PDISC:// ext. link service login (PDISC) + case ELS_PRLI: // ext. link service process login + + pExchange->fchs.ox_rx_id = ExchangeID; + pExchange->fchs.ox_rx_id <<= 16; // MSW shift + pExchange->fchs.ox_rx_id |= 0xffff; // and RX_ID + + break; + + + + + // RESPONDER types... we must set our RX_ID while preserving + // sender's OX_ID + // outgoing (or no) data + case ELS_RJT: // extended link service reject + case ELS_LOGO_ACC: // FC-PH extended link service logout accept + case ELS_ACC: // ext. generic link service accept + case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC) + case ELS_PRLI_ACC: // ext. link service process login accept + + CompleteExchange = TRUE; // Reply (ACC or RJT) is end of exchange + pExchange->fchs.ox_rx_id |= (ExchangeID & 0xFFFF); + + break; + + + // since we are a Responder, OX_ID should already be set by + // cpqfcTSBuildExchange(). We need to -OR- in RX_ID + case SCSI_TWE: + SestType = TRUE; + // Requirement: set MSB of x_ID for Incoming TL data + // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50) + + pExchange->fchs.ox_rx_id &= 0xFFFF0000; // clear RX_ID + // Requirement: set MSB of RX_ID for Incoming TL data + // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50) + pExchange->fchs.ox_rx_id |= (ExchangeID | 0x8000); + break; + + + case SCSI_TRE: + SestType = TRUE; + + // there is no XRDY for SEST target read; the data + // header needs to be updated. Also update the RSP + // exchange IDs for the status frame, in case it is sent automatically + fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id |= ExchangeID; + fcChip->SEST->RspHDR[ ExchangeID ].ox_rx_id = + fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id; + + // for easier FCP response logic (works for TWE and TRE), + // copy exchange IDs. (Not needed if TRE 'RSP' bit set) + pExchange->fchs.ox_rx_id = + fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id; + + break; + + + case FCP_RESPONSE: // using existing OX_ID/ RX_ID pair, + // start SFS FCP-RESPONSE frame + // OX/RX_ID should already be set! (See "fcBuild" above) + CompleteExchange = TRUE; // RSP is end of FCP-SCSI exchange + + + break; + + + case BLS_ABTS_RJT: // uses new RX_ID, since SEST x_ID non-existent + case BLS_ABTS_ACC: // using existing OX_ID/ RX_ID pair from SEST entry + CompleteExchange = TRUE; // ACC or RJT marks end of FCP-SCSI exchange + case BLS_ABTS: // using existing OX_ID/ RX_ID pair from SEST entry + + + break; + + + default: + printk("Error on fcStartExchange: undefined type %Xh(%d)\n", + pExchange->type, pExchange->type); + return INVALID_ARGS; + } + + + // X_ID fields are entered -- copy IRB to Tachyon's ERQ + + + memcpy( + &fcChip->ERQ->QEntry[ ErqIndex ], // dest. + &pExchange->IRB, + 32); // fixed (hardware) length! + + PCI_TRACEO( ExchangeID, 0xA0) + + // ACTION! May generate INT and IMQ entry + writel( fcChip->ERQ->producerIndex, + fcChip->Registers.ERQproducerIndex.address); + + + if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame? + { + + // wait for completion! (TDB -- timeout and chip reset) + + + PCI_TRACEO( ExchangeID, 0xA4) + + enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Sem. + + down_interruptible( cpqfcHBAdata->TYOBcomplete); + + disable_irq( cpqfcHBAdata->HostAdapter->irq); + PCI_TRACE( 0xA4) + + // On login exchanges, BAD_ALPA (non-existent port_id) results in + // FTO (Frame Time Out) on the Outbound Completion message. + // If we got an FTO status, complete the exchange (free up slot) + if( CompleteExchange || // flag from Reply frames + pExchange->status ) // typically, can get FRAME_TO + { + cpqfcTSCompleteExchange( fcChip, ExchangeID); + } + } + + else // SEST Exchange + { + ulStatus = 0; // ship & pray success (e.g. FCP-SCSI) + + if( CompleteExchange ) // by Type of exchange (e.g. end-of-xchng) + { + cpqfcTSCompleteExchange( fcChip, ExchangeID); + } + + else + pExchange->status &= ~EXCHANGE_QUEUED; // clear ExchangeQueued flag + + } + } + + + else // ERQ 'producer' = 'consumer' and QUE is full + { + ulStatus = OUTQUE_FULL; // Outbound (ERQ) Que full + } + +Done: + PCI_TRACE( 0xA0) + return ulStatus; +} + + + + + +// Scan fcController->fcExchanges array for a usuable index (a "free" +// exchange). +// Inputs: +// fcChip - pointer to TachLite chip structure +// Return: +// index - exchange array element where exchange can be built +// -1 - exchange array is full +// REMARKS: +// Although this is a (yuk!) linear search, we presume +// that the system will complete exchanges about as quickly as +// they are submitted. A full Exchange array (and hence, max linear +// search time for free exchange slot) almost guarantees a Fibre problem +// of some sort. +// In the interest of making exchanges easier to debug, we want a LRU +// (Least Recently Used) scheme. + + +static LONG FindFreeExchange( PTACHYON fcChip, ULONG type ) +{ + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + ULONG i; + ULONG ulStatus=-1; // assume failure + + + if( type == SCSI_IRE || + type == SCSI_TRE || + type == SCSI_IWE || + type == SCSI_TWE) + { + // SCSI type - X_IDs should be from 0 to TACH_SEST_LEN-1 + if( fcChip->fcSestExchangeLRU >= TACH_SEST_LEN) // rollover? + fcChip->fcSestExchangeLRU = 0; + i = fcChip->fcSestExchangeLRU; // typically it's already free! + + if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element + { + ulStatus = 0; // success! + } + + else + { // YUK! we need to do a linear search for free element. + // Fragmentation of the fcExchange array is due to excessively + // long completions or timeouts. + + while( TRUE ) + { + if( ++i >= TACH_SEST_LEN ) // rollover check + i = 0; // beginning of SEST X_IDs + +// printk( "looping for SCSI xchng ID: i=%d, type=%Xh\n", +// i, Exchanges->fcExchange[i].type); + + if( Exchanges->fcExchange[i].type == 0 ) // "free"? + { + ulStatus = 0; // success! + break; + } + if( i == fcChip->fcSestExchangeLRU ) // wrapped-around array? + { + printk( "SEST X_ID space full\n"); + break; // failed - prevent inf. loop + } + } + } + fcChip->fcSestExchangeLRU = i + 1; // next! (rollover check next pass) + } + + + + else // Link Service type - X_IDs should be from TACH_SEST_LEN + // to TACH_MAX_XID + { + if( fcChip->fcLsExchangeLRU >= TACH_MAX_XID || // range check + fcChip->fcLsExchangeLRU < TACH_SEST_LEN ) // (e.g. startup) + fcChip->fcLsExchangeLRU = TACH_SEST_LEN; + + i = fcChip->fcLsExchangeLRU; // typically it's already free! + if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element + { + ulStatus = 0; // success! + } + + else + { // YUK! we need to do a linear search for free element + // Fragmentation of the fcExchange array is due to excessively + // long completions or timeouts. + + while( TRUE ) + { + if( ++i >= TACH_MAX_XID ) // rollover check + i = TACH_SEST_LEN;// beginning of Link Service X_IDs + +// printk( "looping for xchng ID: i=%d, type=%Xh\n", +// i, Exchanges->fcExchange[i].type); + + if( Exchanges->fcExchange[i].type == 0 ) // "free"? + { + ulStatus = 0; // success! + break; + } + if( i == fcChip->fcLsExchangeLRU ) // wrapped-around array? + { + printk( "LinkService X_ID space full\n"); + break; // failed - prevent inf. loop + } + } + } + fcChip->fcLsExchangeLRU = i + 1; // next! (rollover check next pass) + + } + + if( !ulStatus ) // success? + Exchanges->fcExchange[i].type = type; // allocate it. + + else + i = -1; // error - all exchanges "open" + + return i; +} + + + + + +// We call this routine to free an Exchange for any reason: +// completed successfully, completed with error, aborted, etc. + +// returns FALSE if Exchange failed and "retry" is acceptable +// returns TRUE if Exchange was successful, or retry is impossible +// (e.g. port/device gone). +//scompleteexchange + +void cpqfcTSCompleteExchange( + PTACHYON fcChip, + ULONG x_ID) +{ + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + + if( x_ID < TACH_SEST_LEN ) // SEST-based (or LinkServ for FCP exchange) + { + if( Exchanges->fcExchange[ x_ID ].Cmnd == NULL ) // what#@! + { +// TriggerHBA( fcChip->Registers.ReMapMemBase, 0); + printk(" x_ID %Xh, type %Xh, NULL ptr!\n", x_ID, + Exchanges->fcExchange[ x_ID ].type); + + goto CleanUpSestResources; // this path should be very rare. + } + + // we have Linux Scsi Cmnd ptr..., now check our Exchange status + // to decide how to complete this SEST FCP exchange + + if( Exchanges->fcExchange[ x_ID ].status ) // perhaps a Tach indicated problem, + // or abnormal exchange completion + { + // set FCP Link statistics + + if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT) + fcChip->fcStats.timeouts++; + if( Exchanges->fcExchange[ x_ID ].status & INITIATOR_ABORT) + fcChip->fcStats.FC4aborted++; + if( Exchanges->fcExchange[ x_ID ].status & COUNT_ERROR) + fcChip->fcStats.CntErrors++; + if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) + fcChip->fcStats.linkFailTX++; + if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_RX) + fcChip->fcStats.linkFailRX++; + if( Exchanges->fcExchange[ x_ID ].status & OVERFLOW) + fcChip->fcStats.CntErrors++; + + // First, see if the Scsi upper level initiated an ABORT on this + // exchange... + if( Exchanges->fcExchange[ x_ID ].status == INITIATOR_ABORT ) + { + printk(" DID_ABORT, x_ID %Xh, Cmnd %p ", + x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); + goto CleanUpSestResources; // (we don't expect Linux _aborts) + } + + // Did our driver timeout the Exchange, or did Tachyon indicate + // a failure during transmission? Ask for retry with "SOFT_ERROR" + else if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT) + { +// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", +// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); + Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); + } + + // Did frame(s) for an open exchange arrive in the SFQ, + // meaning the SEST was unable to process them? + else if( Exchanges->fcExchange[ x_ID ].status & SFQ_FRAME) + { +// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", +// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); + Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); + } + + // Did our driver timeout the Exchange, or did Tachyon indicate + // a failure during transmission? Ask for retry with "SOFT_ERROR" + else if( + (Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) || + (Exchanges->fcExchange[ x_ID ].status & PORTID_CHANGED) || + (Exchanges->fcExchange[ x_ID ].status & FRAME_TO) || + (Exchanges->fcExchange[ x_ID ].status & INV_ENTRY) || + (Exchanges->fcExchange[ x_ID ].status & ABORTSEQ_NOTIFY) ) + + + { +// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", +// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); + Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); + + + } + + // e.g., a LOGOut happened, or device never logged back in. + else if( Exchanges->fcExchange[ x_ID ].status & DEVICE_REMOVED) + { +// printk(" *LOGOut or timeout on login!* "); + // trigger? +// TriggerHBA( fcChip->Registers.ReMapMemBase, 0); + + Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_BAD_TARGET <<16); + } + + + // Did Tachyon indicate a CNT error? We need further analysis + // to determine if the exchange is acceptable + else if( Exchanges->fcExchange[ x_ID ].status == COUNT_ERROR) + { + UCHAR ScsiStatus; + FCP_STATUS_RESPONSE *pFcpStatus = + (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl; + + ScsiStatus = pFcpStatus->fcp_status >>24; + + // If the command is a SCSI Read/Write type, we don't tolerate + // count errors of any kind; assume the count error is due to + // a dropped frame and ask for retry... + + if(( (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x8) || + (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x28) || + (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0xA) || + (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x2A) ) + && + ScsiStatus == 0 ) + { + // ask for retry +/* printk("COUNT_ERROR retry, x_ID %Xh, status %Xh, Cmnd %p\n", + x_ID, Exchanges->fcExchange[ x_ID ].status, + Exchanges->fcExchange[ x_ID ].Cmnd);*/ + Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); + } + + else // need more analysis + { + cpqfcTSCheckandSnoopFCP(fcChip, x_ID); // (will set ->result) + } + } + + // default: NOTE! We don't ever want to get here. Getting here + // implies something new is happening that we've never had a test + // case for. Need code maintenance! Return "ERROR" + else + { + printk("DEFAULT result %Xh, x_ID %Xh, Cmnd %p\n", + Exchanges->fcExchange[ x_ID ].status, x_ID, + Exchanges->fcExchange[ x_ID ].Cmnd); + Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_ERROR <<16); + } + } + else // definitely no Tach problem, but perhaps an FCP problem + { + // set FCP Link statistic + fcChip->fcStats.ok++; + cpqfcTSCheckandSnoopFCP( fcChip, x_ID); // (will set ->result) + } + + // OK, we've set the Scsi "->result" field, so proceed with calling + // Linux Scsi "done" (if not NULL), and free any kernel memory we + // may have allocated for the exchange. + + PCI_TRACEO( (ULONG)Exchanges->fcExchange[x_ID].Cmnd, 0xAC); + // complete the command back to upper Scsi drivers + if( Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done != NULL) + { + // Calling "done" on an Linux _abort() aborted + // Cmnd causes a kernel panic trying to re-free mem. + // Actually, we shouldn't do anything with an _abort CMND + if( Exchanges->fcExchange[ x_ID ].Cmnd->result != (DID_ABORT<<16) ) + { + PCI_TRACE(0xAC) + (*Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done) + (Exchanges->fcExchange[ x_ID ].Cmnd); + } + else + { + +// printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n", +// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); + } + } + else{ + printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID, + Exchanges->fcExchange[ x_ID ].type, + Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]); + printk(" cpqfcTS: Null scsi_done function pointer!\n"); + } + + + // Now, clean up non-Scsi_Cmnd items... +CleanUpSestResources: + + // Was an Extended Scatter/Gather page allocated? We know + // this by checking DWORD 4, bit 31 ("LOC") of SEST entry + if( !(fcChip->SEST->u[ x_ID ].IWE.Buff_Off & 0x80000000)) + { + int i = 0; + + // extended S/G list was used -- Free the allocated ext. S/G pages + + while( fcChip->SEST->sgPages[x_ID].PoolPage[i] && + (i < TL_MAX_SGPAGES) ) + { + kfree( fcChip->SEST->sgPages[x_ID].PoolPage[i]); + fcChip->SEST->sgPages[x_ID].PoolPage[i] = NULL; + i++; + } + } + + Exchanges->fcExchange[ x_ID ].Cmnd = NULL; + } // Done with FCP (SEST) exchanges + + + // the remaining logic is common to ALL Exchanges: + // FCP(SEST) and LinkServ. + + Exchanges->fcExchange[ x_ID ].type = 0; // there -- FREE! + Exchanges->fcExchange[ x_ID ].status = 0; + + PCI_TRACEO( x_ID, 0xAC) + + + return; +} // (END of CompleteExchange function) + + + + +// Unfortunately, we must snoop all command completions in +// order to manipulate certain return fields, and take note of +// device types, etc., to facilitate the Fibre-Channel to SCSI +// "mapping". +// (Watch for BIG Endian confusion on some payload fields) +void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID) +{ + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + Scsi_Cmnd *Cmnd = Exchanges->fcExchange[ x_ID].Cmnd; + FCP_STATUS_RESPONSE *pFcpStatus = + (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl; + UCHAR ScsiStatus; + + ScsiStatus = pFcpStatus->fcp_status >>24; + +#ifdef FCP_COMPLETION_DBG + printk("ScsiStatus = 0x%X\n", ScsiStatus); +#endif + + // First, check FCP status + if( pFcpStatus->fcp_status & FCP_RSP_LEN_VALID ) + { + // check response code (RSP_CODE) -- most popular is bad len + // 1st 4 bytes of rsp info -- only byte 3 interesting + if( pFcpStatus->fcp_rsp_info & FCP_DATA_LEN_NOT_BURST_LEN ) + { + + // do we EVER get here? + printk("cpqfcTS: FCP data len not burst len, x_ID %Xh\n", x_ID); + } + } + + // for now, go by the ScsiStatus, and manipulate certain + // commands when necessary... + if( ScsiStatus == 0) // SCSI status byte "good"? + { + Cmnd->result = 0; // everything's OK + + if( (Cmnd->cmnd[0] == INQUIRY)) + { + UCHAR *InquiryData = Cmnd->request_buffer; + PFC_LOGGEDIN_PORT pLoggedInPort; + + // We need to manipulate INQUIRY + // strings for COMPAQ RAID controllers to force + // Linux to scan additional LUNs. Namely, set + // the Inquiry string byte 2 (ANSI-approved version) + // to 2. + + if( !memcmp( &InquiryData[8], "COMPAQ", 6 )) + { + InquiryData[2] = 0x2; // claim SCSI-2 compliance, + // so multiple LUNs may be scanned. + // (no SCSI-2 problems known in CPQ) + } + + // snoop the Inquiry to detect Disk, Tape, etc. type + // (search linked list for the port_id we sent INQUIRY to) + pLoggedInPort = fcFindLoggedInPort( fcChip, + NULL, // DON'T search Scsi Nexus (we will set it) + Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF, + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + if( pLoggedInPort ) + { + pLoggedInPort->ScsiNexus.InqDeviceType = InquiryData[0]; + } + else + { + printk("cpqfcTS: can't find LoggedIn FC port %06X for INQUIRY\n", + Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF); + } + } + } + + + // Scsi Status not good -- pass it back to caller + + else + { + Cmnd->result = ScsiStatus; // SCSI status byte is 1st + + // check for valid "sense" data + + if( pFcpStatus->fcp_status & FCP_SNS_LEN_VALID ) + { // limit Scsi Sense field length! + int SenseLen = pFcpStatus->fcp_sns_len >>24; // (BigEndian) lower byte + + SenseLen = SenseLen > sizeof( Cmnd->sense_buffer) ? + sizeof( Cmnd->sense_buffer) : SenseLen; + + +#ifdef FCP_COMPLETION_DBG + printk("copy sense_buffer %p, len %d, result %Xh\n", + Cmnd->sense_buffer, SenseLen, Cmnd->result); +#endif + + // NOTE: There is some dispute over the FCP response + // format. Most FC devices assume that FCP_RSP_INFO + // is 8 bytes long, in spite of the fact that FCP_RSP_LEN + // is (virtually) always 0 and the field is "invalid". + // Some other devices assume that + // the FCP_SNS_INFO begins after FCP_RSP_LEN bytes (i.e. 0) + // when the FCP_RSP is invalid (this almost appears to be + // one of those "religious" issues). + // Consequently, we test the usual position of FCP_SNS_INFO + // for 7Xh, since the SCSI sense format says the first + // byte ("error code") should be 0x70 or 0x71. In practice, + // we find that every device does in fact have 0x70 or 0x71 + // in the first byte position, so this test works for all + // FC devices. + // (This logic is especially effective for the CPQ/DEC HSG80 + // & HSG60 controllers). + + if( (pFcpStatus->fcp_sns_info[0] & 0x70) == 0x70 ) + memcpy( Cmnd->sense_buffer, + &pFcpStatus->fcp_sns_info[0], SenseLen); + else + { + unsigned char *sbPtr = + (unsigned char *)&pFcpStatus->fcp_sns_info[0]; + sbPtr -= 8; // back up 8 bytes hoping to find the + // start of the sense buffer + memcpy( Cmnd->sense_buffer, sbPtr, SenseLen); + } + + // in the special case of Device Reset, tell upper layer + // to immediately retry (with SOFT_ERROR status) + // look for Sense Key Unit Attention (0x6) with ASC Device + // Reset (0x29) + // printk("SenseLen %d, Key = 0x%X, ASC = 0x%X\n", + // SenseLen, Cmnd->sense_buffer[2], + // Cmnd->sense_buffer[12]); + if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) && + (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset" + { + Cmnd->result |= (DID_SOFT_ERROR << 16); // "Host" status byte 3rd + } + + // check for SenseKey "HARDWARE ERROR", ASC InternalTargetFailure + else if( ((Cmnd->sense_buffer[2] & 0xF) == 0x4) && // "hardware error" + (Cmnd->sense_buffer[12] == 0x44) ) // Addtl. Sense Code + { +// printk("HARDWARE_ERROR, Channel/Target/Lun %d/%d/%d\n", +// Cmnd->channel, Cmnd->target, Cmnd->lun); + Cmnd->result |= (DID_ERROR << 16); // "Host" status byte 3rd + } + + } // (end of sense len valid) + + // there is no sense data to help out Linux's Scsi layers... + // We'll just return the Scsi status and hope he will "do the + // right thing" + else + { + // as far as we know, the Scsi status is sufficient + Cmnd->result |= (DID_OK << 16); // "Host" status byte 3rd + } + } +} + + + +//PPPPPPPPPPPPPPPPPPPPPPPPP PAYLOAD PPPPPPPPP +// build data PAYLOAD; SCSI FCP_CMND I.U. +// remember BIG ENDIAN payload - DWord values must be byte-reversed +// (hence the affinity for byte pointer building). + +static int build_FCP_payload( Scsi_Cmnd *Cmnd, + UCHAR* payload, ULONG type, ULONG fcp_dl ) +{ + int i; + + + switch( type) + { + + case SCSI_IWE: + case SCSI_IRE: + // 8 bytes FCP_LUN + // Peripheral Device or Volume Set addressing, and LUN mapping + // When the FC port was looked up, we copied address mode + // and any LUN mask to the scratch pad SCp.phase & .mode + + *payload++ = (UCHAR)Cmnd->SCp.phase; + + // Now, because of "lun masking" + // (aka selective storage presentation), + // the contiguous Linux Scsi lun number may not match the + // device's lun number, so we may have to "map". + + *payload++ = (UCHAR)Cmnd->SCp.have_data_in; + + // We don't know of anyone in the FC business using these + // extra "levels" of addressing. In fact, confusion still exists + // just using the FIRST level... ;-) + + *payload++ = 0; // 2nd level addressing + *payload++ = 0; + *payload++ = 0; // 3rd level addressing + *payload++ = 0; + *payload++ = 0; // 4th level addressing + *payload++ = 0; + + // 4 bytes Control Field FCP_CNTL + *payload++ = 0; // byte 0: (MSB) reserved + *payload++ = 0; // byte 1: task codes + *payload++ = 0; // byte 2: task management flags + // byte 3: (LSB) execution management codes + // bit 0 write, bit 1 read (don't set together) + + if( fcp_dl != 0 ) + { + if( type == SCSI_IWE ) // WRITE + *payload++ = 1; + else // READ + *payload++ = 2; + } + else + { + // On some devices, if RD or WR bits are set, + // and fcp_dl is 0, they will generate an error on the command. + // (i.e., if direction is specified, they insist on a length). + *payload++ = 0; // no data (necessary for CPQ) + } + + + // NOTE: clean this up if/when MAX_COMMAND_SIZE is increased to 16 + // FCP_CDB allows 16 byte SCSI command descriptor blk; + // Linux SCSI CDB array is MAX_COMMAND_SIZE (12 at this time...) + for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++) + *payload++ = Cmnd->cmnd[i]; + + if( Cmnd->cmd_len == 16 ) + { + memcpy( payload, &Cmnd->SCp.buffers_residual, 4); + } + payload+= (16 - i); + + // FCP_DL is largest number of expected data bytes + // per CDB (i.e. read/write command) + *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL + *payload++ = (UCHAR)(fcp_dl >>16); + *payload++ = (UCHAR)(fcp_dl >>8); + *payload++ = (UCHAR)fcp_dl; // (LSB) + break; + + case SCSI_TWE: // need FCP_XFER_RDY + *payload++ = 0; // (4 bytes) DATA_RO (MSB byte 0) + *payload++ = 0; + *payload++ = 0; + *payload++ = 0; // LSB (byte 3) + // (4 bytes) BURST_LEN + // size of following FCP_DATA payload + *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL + *payload++ = (UCHAR)(fcp_dl >>16); + *payload++ = (UCHAR)(fcp_dl >>8); + *payload++ = (UCHAR)fcp_dl; // (LSB) + // 4 bytes RESERVED + *payload++ = 0; + *payload++ = 0; + *payload++ = 0; + *payload++ = 0; + break; + + default: + break; + } + + return 0; +} + diff --git a/drivers/scsi/cpqioctl.c b/drivers/scsi/cpqioctl.c new file mode 100644 index 000000000000..7f09a4341979 --- /dev/null +++ b/drivers/scsi/cpqioctl.c @@ -0,0 +1,76 @@ +// Test program for CPQFCTS ioctl calls +// build with: +// gcc -o cpqioctl cpqioctl.c +// ld -o cpqioctl /lib/crt0.o cpqioctl.o -lc + +#include +#include +#include +#include +#include +#include +#include "../../include/scsi/scsi.h" +#include "cpqfcTSioctl.h" + +typedef struct scsi_fctargaddress { + unsigned long host_port_id; + unsigned char host_wwn[8]; +} Scsi_FCTargAddress; + +int main(int argc, char **argv) { + + int fd, i; + Scsi_FCTargAddress targ; + int uselect=0; + + + + if ( argc < 2 ) { + printf("usage: cpqioctl \n"); + exit(1); + } + + if ( (fd = open(argv[1], O_RDONLY)) == -1) { + perror("open"); + exit(1); + } + + if ( ioctl(fd, SCSI_IOCTL_FC_TARGET_ADDRESS, &targ) ) { + perror("ioctl"); + exit(1); + } + + + printf("portid: %08x. wwn: ", targ.host_port_id); + + for (i=0;i<8;i++) printf(" %02x", targ.host_wwn[i]); + printf("\n"); + + while( uselect != 27 ) // not ESC key + { + printf("\n IOCTL \n"); + printf( "1. Get PCI info\n"); + printf( "2. Send Passthru\n"); + printf( " ==> "); + scanf("%c", &uselect); + + switch( uselect ) + { + case '1': + { + cciss_pci_info_struct pciinfo; + + if( ioctl( fd, CCPQFCTS_GETPCIINFO ,&pciinfo )) + perror("ioctl"); + else + printf( "\nPCI bus %d, dev_fn %d, board_id %Xh\n", + pciinfo.bus, pciinfo.dev_fn, pciinfo.board_id); + } + + } + } + + + close(fd); + return 0; +} diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 1d0678714c6f..b04de5a8a0ec 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -26,8 +26,6 @@ #define __NO_VERSION__ #include - -#include #include #include #include diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 83c68cc044fa..ab84545c4f3e 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -582,16 +582,6 @@ int idescsi_init (void) failed = 0; while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) { -#ifndef CONFIG_BLK_DEV_IDETAPE - /* - * The Onstream DI-30 does not handle clean emulation, yet. - */ - if (strstr(drive->id->model, "OnStream DI-30")) { - printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model); - continue; - } -#endif /* CONFIG_BLK_DEV_IDETAPE */ - if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) { printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); continue; diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index 697264ec9bb7..8630e8ddf6f1 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -78,17 +78,40 @@ /* - Sync with other changes from the 2.3 kernels */ /* 4.00.06 - Fix timeout with initial FFDC command */ /* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig */ +/* 4.10.00 - Add support for ServeRAID 4M/4L */ +/* 4.10.13 - Fix for dynamic unload and proc file system */ +/* 4.20.03 - Rename version to coincide with new release schedules */ +/* Performance fixes */ +/* Fix truncation of /proc files with cat */ +/* Merge in changes through kernel 2.4.0test1ac21 */ +/* 4.20.13 - Fix some failure cases / reset code */ +/* - Hook into the reboot_notifier to flush the controller cache */ /* */ /*****************************************************************************/ /* * Conditional Compilation directives for this driver: * - * NO_IPS_RESET - Don't reset the controller (no matter what) - * IPS_DEBUG - More verbose error messages - * IPS_PCI_PROBE_DEBUG - Print out more detail on the PCI probe + * IPS_DEBUG - Turn on debugging info * + * + * Parameters: + * + * debug: - Set debug level to + * NOTE: only works when IPS_DEBUG compile directive + * is used. + * + * 1 - Normal debug messages + * 2 - Verbose debug messages + * 11 - Method trace (non interrupt) + * 12 - Method trace (includes interrupt) + * + * noreset - Don't reset the controller + * nocmdline - Turn off passthru support + * noi2o - Don't use I2O Queues (ServeRAID 4 only) + * nommap - Don't use memory mapped I/O */ + #include @@ -101,10 +124,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -127,25 +152,29 @@ #include #endif +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) +#include +#endif + #include +#ifdef MODULE + static char *ips = NULL; + MODULE_PARM(ips, "s"); +#endif + /* * DRIVER_VER */ -#define IPS_VERSION_HIGH "4.00" /* MUST be 4 chars */ -#define IPS_VERSION_LOW ".06 " /* MUST be 4 chars */ +#define IPS_VERSION_HIGH "4.20" +#define IPS_VERSION_LOW ".20 " #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) struct proc_dir_entry proc_scsi_ips = { -#if !defined(PROC_SCSI_IPS) - 0, /* Use dynamic inode allocation */ -#else - PROC_SCSI_IPS, -#endif + 0, 3, "ips", S_IFDIR | S_IRUGO | S_IXUGO, 2 -} -; +}; #endif #if !defined(__i386__) @@ -160,12 +189,14 @@ struct proc_dir_entry proc_scsi_ips = { #error "To use the command-line interface you need to define SG_BIG_BUFF" #endif -#if IPS_DEBUG >= 12 - #define DBG(s) printk(KERN_NOTICE s "\n"); MDELAY(2*IPS_ONE_SEC) -#elif IPS_DEBUG >= 11 - #define DBG(s) printk(KERN_NOTICE s "\n") +#ifdef IPS_DEBUG + #define METHOD_TRACE(s, i) if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n"); + #define DEBUG(i, s) if (ips_debug >= i) printk(KERN_NOTICE s "\n"); + #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v); #else - #define DBG(s) + #define METHOD_TRACE(s, i) + #define DEBUG(i, s) + #define DEBUG_VAR(i, s, v...) #endif /* @@ -174,11 +205,26 @@ struct proc_dir_entry proc_scsi_ips = { static const char * ips_name = "ips"; static struct Scsi_Host * ips_sh[IPS_MAX_ADAPTERS]; /* Array of host controller structures */ static ips_ha_t * ips_ha[IPS_MAX_ADAPTERS]; /* Array of HA structures */ +static unsigned int ips_next_controller = 0; static unsigned int ips_num_controllers = 0; +static unsigned int ips_released_controllers = 0; static int ips_cmd_timeout = 60; static int ips_reset_timeout = 60 * 5; +static int ips_force_memio = 1; /* Always use Memory Mapped I/O */ +static int ips_force_i2o = 1; /* Always use I2O command delivery */ +static int ips_resetcontroller = 1; /* Reset the controller */ +static int ips_cmdline = 1; /* Support for passthru */ + +#ifdef IPS_DEBUG +static int ips_debug = 0; /* Debug mode */ +#endif + +/* + * Necessary forward function protoypes + */ +static int ips_halt(struct notifier_block *nb, ulong event, void *buf); -#define MAX_ADAPTER_NAME 7 +#define MAX_ADAPTER_NAME 9 static char ips_adapter_name[][30] = { "ServeRAID", @@ -187,7 +233,13 @@ static char ips_adapter_name[][30] = { "ServeRAID on motherboard", "ServeRAID 3H", "ServeRAID 3L", - "ServeRAID 4H" + "ServeRAID 4H", + "ServeRAID 4M", + "ServeRAID 4L" +}; + +static struct notifier_block ips_notifier = { + ips_halt, NULL, 0 }; /* @@ -252,8 +304,6 @@ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK */ int ips_detect(Scsi_Host_Template *); int ips_release(struct Scsi_Host *); -int ips_abort(Scsi_Cmnd *); -int ips_reset(Scsi_Cmnd *, unsigned int); int ips_eh_abort(Scsi_Cmnd *); int ips_eh_reset(Scsi_Cmnd *); int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *)); @@ -261,21 +311,26 @@ int ips_biosparam(Disk *, kdev_t, int *); const char * ips_info(struct Scsi_Host *); void do_ipsintr(int, void *, struct pt_regs *); static int ips_hainit(ips_ha_t *); -static int ips_map_status(ips_scb_t *, ips_stat_t *); +static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *); static int ips_send(ips_ha_t *, ips_scb_t *, ips_scb_callback); static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int); static int ips_send_cmd(ips_ha_t *, ips_scb_t *); -static int ips_chkstatus(ips_ha_t *); static int ips_online(ips_ha_t *, ips_scb_t *); static int ips_inquiry(ips_ha_t *, ips_scb_t *); static int ips_rdcap(ips_ha_t *, ips_scb_t *); static int ips_msense(ips_ha_t *, ips_scb_t *); static int ips_reqsen(ips_ha_t *, ips_scb_t *); static int ips_allocatescbs(ips_ha_t *); -static int ips_reset_adapter(ips_ha_t *); -static int ips_statupd(ips_ha_t *); -static int ips_issue(ips_ha_t *, ips_scb_t *); -static int ips_isintr(ips_ha_t *); +static int ips_reset_copperhead(ips_ha_t *); +static int ips_reset_copperhead_memio(ips_ha_t *); +static int ips_reset_morpheus(ips_ha_t *); +static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *); +static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *); +static int ips_issue_i2o(ips_ha_t *, ips_scb_t *); +static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *); +static int ips_isintr_copperhead(ips_ha_t *); +static int ips_isintr_copperhead_memio(ips_ha_t *); +static int ips_isintr_morpheus(ips_ha_t *); static int ips_wait(ips_ha_t *, int, int); static int ips_write_driver_status(ips_ha_t *, int); static int ips_read_adapter_status(ips_ha_t *, int); @@ -283,7 +338,22 @@ static int ips_read_subsystem_parameters(ips_ha_t *, int); static int ips_read_config(ips_ha_t *, int); static int ips_clear_adapter(ips_ha_t *, int); static int ips_readwrite_page5(ips_ha_t *, int, int); -static void ips_intr(ips_ha_t *); +static int ips_init_copperhead(ips_ha_t *); +static int ips_init_copperhead_memio(ips_ha_t *); +static int ips_init_morpheus(ips_ha_t *); +static int ips_isinit_copperhead(ips_ha_t *); +static int ips_isinit_copperhead_memio(ips_ha_t *); +static int ips_isinit_morpheus(ips_ha_t *); +static u32 ips_statupd_copperhead(ips_ha_t *); +static u32 ips_statupd_copperhead_memio(ips_ha_t *); +static u32 ips_statupd_morpheus(ips_ha_t *); +static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *); +static void ips_chkstatus(ips_ha_t *, IPS_STATUS *); +static void ips_enable_int_copperhead(ips_ha_t *); +static void ips_enable_int_copperhead_memio(ips_ha_t *); +static void ips_enable_int_morpheus(ips_ha_t *); +static void ips_intr_copperhead(ips_ha_t *); +static void ips_intr_morpheus(ips_ha_t *); static void ips_next(ips_ha_t *, int); static void ipsintr_blocking(ips_ha_t *, struct ips_scb *); static void ipsintr_done(ips_ha_t *, struct ips_scb *); @@ -292,6 +362,7 @@ static void ips_free(ips_ha_t *); static void ips_init_scb(ips_ha_t *, ips_scb_t *); static void ips_freescb(ips_ha_t *, ips_scb_t *); static void ips_statinit(ips_ha_t *); +static void ips_statinit_memio(ips_ha_t *); static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t); static void ips_ffdc_reset(ips_ha_t *, int); static void ips_ffdc_time(ips_ha_t *, int); @@ -311,6 +382,9 @@ static inline ips_copp_wait_item_t * ips_removeq_copp_head(ips_copp_queue_t *); static int ips_erase_bios(ips_ha_t *); static int ips_program_bios(ips_ha_t *, char *, int); static int ips_verify_bios(ips_ha_t *, char *, int); +static int ips_erase_bios_memio(ips_ha_t *); +static int ips_program_bios_memio(ips_ha_t *, char *, int); +static int ips_verify_bios_memio(ips_ha_t *, char *, int); #ifndef NO_IPS_CMDLINE static int ips_is_passthru(Scsi_Cmnd *); @@ -329,6 +403,76 @@ static int copy_info(IPS_INFOSTR *, char *, ...); /* Exported Functions */ /*--------------------------------------------------------------------------*/ +/****************************************************************************/ +/* */ +/* Routine Name: ips_setup */ +/* */ +/* Routine Description: */ +/* */ +/* setup parameters to the driver */ +/* */ +/****************************************************************************/ +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) +static int +ips_setup(char *ips_str) { +#else +void +ips_setup(char *ips_str, int *dummy) { +#endif + int i; + char *p; + char *key; + char *value; + char tokens[3] = {',', '.', 0}; + IPS_OPTION options[] = { + {"noreset", &ips_resetcontroller, 0}, +#ifdef IPS_DEBUG + {"debug", &ips_debug, 1}, +#endif + {"noi2o", &ips_force_i2o, 0}, + {"nommap", &ips_force_memio, 0}, + {"nocmdline", &ips_cmdline, 0}, + }; + + METHOD_TRACE("ips_setup", 1); + + for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) { + p = key; + + /* Search for value */ + while ((p) && (*p != ':')) + p++; + + if (p) { + *p = '\0'; + value = p+1; + } else + value = NULL; + + /* + * We now have key/value pairs. + * Update the variables + */ + for (i = 0; i < (sizeof(options) / sizeof(options[0])); i++) { + if (strnicmp(key, options[i].option_name, strlen(ips_str)) == 0) { + if (value) + *options[i].option_flag = simple_strtoul(value, NULL, 0); + else + *options[i].option_flag = options[i].option_value; + + break; + } + } + } +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) + return (1); +#endif +} + +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) +__setup("ips=", ips_setup); +#endif + /****************************************************************************/ /* */ /* Routine Name: ips_detect */ @@ -345,15 +489,38 @@ ips_detect(Scsi_Host_Template *SHT) { struct Scsi_Host *sh; ips_ha_t *ha; u32 io_addr; + u32 mem_addr; + u32 io_len; + u32 mem_len; u16 planer; u8 revision_id; u8 bus; u8 func; u8 irq; - int index; - struct pci_dev *dev = NULL; + u16 deviceID[2]; + int i; + int j; + char *ioremap_ptr; + char *mem_ptr; + struct pci_dev *dev[2]; + struct pci_dev *morpheus = NULL; + struct pci_dev *trombone = NULL; +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,14) + u32 currbar; + u32 maskbar; + u8 barnum; +#endif - DBG("ips_detect"); + METHOD_TRACE("ips_detect", 1); + +#ifdef MODULE + if (ips) +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) + ips_setup(ips); +#else + ips_setup(ips, NULL); +#endif +#endif SHT->proc_info = ips_proc_info; #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) @@ -363,226 +530,524 @@ ips_detect(Scsi_Host_Template *SHT) { #endif #if defined(CONFIG_PCI) - + /* initalize number of controllers */ ips_num_controllers = 0; - + ips_next_controller = 0; + ips_released_controllers = 0; + if (!pci_present()) return (0); - for (index = 0; index < IPS_MAX_ADAPTERS; index++) { + morpheus = pci_find_device(IPS_VENDORID, IPS_MORPHEUS_DEVICEID, morpheus); + trombone = pci_find_device(IPS_VENDORID, IPS_COPPERHEAD_DEVICEID, trombone); + + /* determine which controller to probe first */ + if (!morpheus) { + /* we only have trombone */ + dev[0] = trombone; + dev[1] = NULL; + deviceID[0] = IPS_COPPERHEAD_DEVICEID; + } else if (!trombone) { + /* we only have morpheus */ + dev[0] = morpheus; + dev[1] = NULL; + deviceID[0] = IPS_MORPHEUS_DEVICEID; + } else { + /* we have both in the system */ + if (trombone->bus < morpheus->bus) { + dev[0] = trombone; + dev[1] = morpheus; + deviceID[0] = IPS_COPPERHEAD_DEVICEID; + deviceID[1] = IPS_MORPHEUS_DEVICEID; + } else if (trombone->bus > morpheus->bus) { + dev[0] = morpheus; + dev[1] = trombone; + deviceID[0] = IPS_MORPHEUS_DEVICEID; + deviceID[1] = IPS_COPPERHEAD_DEVICEID; + } else { + /* further detection required */ + if (trombone->devfn < morpheus->devfn) { + dev[0] = trombone; + dev[1] = morpheus; + deviceID[0] = IPS_COPPERHEAD_DEVICEID; + deviceID[1] = IPS_MORPHEUS_DEVICEID; + } else { + dev[0] = morpheus; + dev[1] = trombone; + deviceID[0] = IPS_MORPHEUS_DEVICEID; + deviceID[1] = IPS_COPPERHEAD_DEVICEID; + } + } + } - if (!(dev = pci_find_device(IPS_VENDORID, IPS_DEVICEID, dev))) + /* Now scan the controllers */ + for (i = 0; i < 2; i++) { + if (!dev[i]) break; - if (pci_enable_device(dev)) - break; - - /* stuff that we get in dev */ - irq = dev->irq; - bus = dev->bus->number; - func = dev->devfn; - io_addr = pci_resource_start(dev, 0); - - /* check I/O address */ - if (pci_resource_flags(dev, 0) & IORESOURCE_MEM) - continue; + do { + if (ips_next_controller >= IPS_MAX_ADAPTERS) + break; - /* get planer status */ - if (pci_read_config_word(dev, 0x04, &planer)) { - printk(KERN_WARNING "(%s%d) can't get planer status.\n", - ips_name, index); - continue; - } +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) + if (pci_enable_device(dev[i])) + break; +#endif - /* check to see if an onboard planer controller is disabled */ - if (!(planer & 0x000C)) { + /* stuff that we get in dev */ + irq = dev[i]->irq; + bus = dev[i]->bus->number; + func = dev[i]->devfn; - #ifdef IPS_PCI_PROBE_DEBUG - printk(KERN_NOTICE "(%s%d) detect, Onboard ServeRAID disabled by BIOS\n", - ips_name, index); - #endif + /* Init MEM/IO addresses to 0 */ + mem_addr = 0; + io_addr = 0; + mem_len = 0; + io_len = 0; - continue; - } + for (j = 0; j < 2; j++) { +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) + if (!pci_resource_start(dev[i], j)) + break; - #ifdef IPS_PCI_PROBE_DEBUG - printk(KERN_NOTICE "(%s%d) detect bus %d, func %x, irq %d, io %x\n", - ips_name, index, bus, func, irq, io_addr); - #endif + if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) { + io_addr = pci_resource_start(dev[i], j); + io_len = pci_resource_len(dev[i], j); + } else { + mem_addr = pci_resource_start(dev[i], j); + mem_len = pci_resource_len(dev[i], j); + } +#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,3,14) + if (!dev[i]->resource[j].start) + break; - /* get the revision ID */ - if (pci_read_config_byte(dev, 0x08, &revision_id)) { - printk(KERN_WARNING "(%s%d) can't get revision id.\n", - ips_name, index); + if ((dev[i]->resource[j].start & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + io_addr = dev[i]->resource[j].start; + io_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1; + } else { + mem_addr = dev[i]->resource[j].start; + mem_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1; + } +#else + if (!dev[i]->base_address[j]) + break; - continue; - } + if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + barnum = PCI_BASE_ADDRESS_0 + (j * 4); + io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK; - /* found a controller */ - sh = scsi_register(SHT, sizeof(ips_ha_t)); + /* Get Size */ + pci_read_config_dword(dev[i], barnum, &currbar); + pci_write_config_dword(dev[i], barnum, ~0); + pci_read_config_dword(dev[i], barnum, &maskbar); + pci_write_config_dword(dev[i], barnum, currbar); - if (sh == NULL) { - printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n", - ips_name, index); + io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1; + } else { + barnum = PCI_BASE_ADDRESS_0 + (j * 4); + mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK; - continue; - } + /* Get Size */ + pci_read_config_dword(dev[i], barnum, &currbar); + pci_write_config_dword(dev[i], barnum, ~0); + pci_read_config_dword(dev[i], barnum, &maskbar); + pci_write_config_dword(dev[i], barnum, currbar); - ha = IPS_HA(sh); - memset(ha, 0, sizeof(ips_ha_t)); + mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1; + } +#endif + } - /* Initialize spin lock */ - spin_lock_init(&ha->scb_lock); - spin_lock_init(&ha->copp_lock); - spin_lock_init(&ha->ips_lock); - spin_lock_init(&ha->copp_waitlist.lock); - spin_lock_init(&ha->scb_waitlist.lock); - spin_lock_init(&ha->scb_activelist.lock); + /* setup memory mapped area (if applicable) */ + if (mem_addr) { + u32 base; + u32 offs; - ips_sh[ips_num_controllers] = sh; - ips_ha[ips_num_controllers] = ha; - ips_num_controllers++; - ha->active = 1; + DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d", + ips_name, ips_next_controller, mem_addr, mem_len); - ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL|GFP_DMA); +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) + if (check_mem_region(mem_addr, mem_len)) { + /* Couldn't allocate io space */ + printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n", + ips_name, ips_next_controller, io_addr, io_len); - if (!ha->enq) { - printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n", - ips_name, index); + ips_next_controller++; - ha->active = 0; + continue; + } - continue; - } + request_mem_region(mem_addr, mem_len, "ips"); +#endif - ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL|GFP_DMA); + base = mem_addr & PAGE_MASK; + offs = mem_addr - base; - if (!ha->adapt) { - printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n", - ips_name, index); + ioremap_ptr = ioremap(base, PAGE_SIZE); + mem_ptr = ioremap_ptr + offs; + } else { + ioremap_ptr = NULL; + mem_ptr = NULL; + } - ha->active = 0; + /* setup I/O mapped area (if applicable) */ + if (io_addr) { + DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d", + ips_name, ips_next_controller, io_addr, io_len); - continue; - } + if (check_region(io_addr, io_len)) { + /* Couldn't allocate io space */ + printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n", + ips_name, ips_next_controller, io_addr, io_len); - ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL|GFP_DMA); + ips_next_controller++; - if (!ha->conf) { - printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n", - ips_name, index); + continue; + } - ha->active = 0; + request_region(io_addr, io_len, "ips"); + } - continue; - } + /* get planer status */ + if (pci_read_config_word(dev[i], 0x04, &planer)) { + printk(KERN_WARNING "(%s%d) can't get planer status.\n", + ips_name, ips_next_controller); - ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL|GFP_DMA); + ips_next_controller++; - if (!ha->nvram) { - printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n", - ips_name, index); + continue; + } + + /* check to see if an onboard planer controller is disabled */ + if (!(planer & 0x000C)) { - ha->active = 0; + DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS", + ips_name, ips_next_controller); - continue; - } + ips_next_controller++; - ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL|GFP_DMA); + continue; + } - if (!ha->subsys) { - printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n", - ips_name, index); + DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %x", + ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, (u32) mem_ptr); - ha->active = 0; + /* get the revision ID */ + if (pci_read_config_byte(dev[i], 0x08, &revision_id)) { + printk(KERN_WARNING "(%s%d) can't get revision id.\n", + ips_name, ips_next_controller); - continue; - } + ips_next_controller++; - ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL|GFP_DMA); + continue; + } - if (!ha->dummy) { - printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n", - ips_name, index); + /* found a controller */ + sh = scsi_register(SHT, sizeof(ips_ha_t)); - ha->active = 0; + if (sh == NULL) { + printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n", + ips_name, ips_next_controller); - continue; - } + ips_next_controller++; - ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_KERNEL|GFP_DMA); - ha->ioctl_datasize = IPS_IOCTL_SIZE; - if (!ha->ioctl_data) { - printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n", - ips_name, index); + continue; + } - ha->active = 0; + ha = IPS_HA(sh); + memset(ha, 0, sizeof(ips_ha_t)); - continue; - } + /* Initialize spin lock */ + spin_lock_init(&ha->scb_lock); + spin_lock_init(&ha->copp_lock); + spin_lock_init(&ha->ips_lock); + spin_lock_init(&ha->copp_waitlist.lock); + spin_lock_init(&ha->scb_waitlist.lock); + spin_lock_init(&ha->scb_activelist.lock); - /* Store away needed values for later use */ - sh->io_port = io_addr; - sh->n_io_port = 255; - sh->unique_id = io_addr; - sh->irq = irq; - sh->select_queue_depths = NULL; - sh->sg_tablesize = sh->hostt->sg_tablesize; - sh->can_queue = sh->hostt->can_queue; - sh->cmd_per_lun = sh->hostt->cmd_per_lun; - sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma; - sh->use_clustering = sh->hostt->use_clustering; - - /* Store info in HA structure */ - ha->io_addr = io_addr; - ha->irq = irq; - ha->host_num = index; - ha->revision_id = revision_id; - - /* install the interrupt handler */ - if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) { - printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n", - ips_name, index); + ips_sh[ips_next_controller] = sh; + ips_ha[ips_next_controller] = ha; + ips_num_controllers++; + ha->active = 1; - ha->active = 0; + ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_ATOMIC|GFP_DMA); - continue; - } + if (!ha->enq) { + printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n", + ips_name, ips_next_controller); - /* - * Allocate a temporary SCB for initialization - */ - ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA); - if (!ha->scbs) { - /* couldn't allocate a temp SCB */ - printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", - ips_name, index); + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; - ha->active = 0; + continue; + } - continue; - } + ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_ATOMIC|GFP_DMA); - memset(ha->scbs, 0, sizeof(ips_scb_t)); - ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA); - if (!ha->scbs->sg_list) { - /* couldn't allocate a temp SCB S/G list */ - printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", - ips_name, index); + if (!ha->adapt) { + printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n", + ips_name, ips_next_controller); + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; - ha->active = 0; + continue; + } + + ha->conf = kmalloc(sizeof(IPS_CONF), GFP_ATOMIC|GFP_DMA); + + if (!ha->conf) { + printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_ATOMIC|GFP_DMA); + + if (!ha->nvram) { + printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_ATOMIC|GFP_DMA); + + if (!ha->subsys) { + printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_ATOMIC|GFP_DMA); + + if (!ha->dummy) { + printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_ATOMIC|GFP_DMA); + ha->ioctl_datasize = IPS_IOCTL_SIZE; + if (!ha->ioctl_data) { + printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + /* Store away needed values for later use */ + sh->io_port = io_addr; + sh->n_io_port = io_addr ? 255 : 0; + sh->unique_id = (io_addr) ? io_addr : mem_addr; + sh->irq = irq; + sh->select_queue_depths = ips_select_queue_depth; + sh->sg_tablesize = sh->hostt->sg_tablesize; + sh->can_queue = sh->hostt->can_queue; + sh->cmd_per_lun = sh->hostt->cmd_per_lun; + sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma; + sh->use_clustering = sh->hostt->use_clustering; + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,32) + sh->wish_block = FALSE; +#endif + + /* Store info in HA structure */ + ha->irq = irq; + ha->io_addr = io_addr; + ha->io_len = io_len; + ha->mem_addr = mem_addr; + ha->mem_len = mem_len; + ha->mem_ptr = mem_ptr; + ha->ioremap_ptr = ioremap_ptr; + ha->host_num = ips_next_controller; + ha->revision_id = revision_id; + ha->device_id = deviceID[i]; + ha->pcidev = dev[i]; + + /* + * Setup Functions + */ + if (IPS_IS_MORPHEUS(ha)) { + /* morpheus */ + ha->func.isintr = ips_isintr_morpheus; + ha->func.isinit = ips_isinit_morpheus; + ha->func.issue = ips_issue_i2o_memio; + ha->func.init = ips_init_morpheus; + ha->func.statupd = ips_statupd_morpheus; + ha->func.reset = ips_reset_morpheus; + ha->func.intr = ips_intr_morpheus; + ha->func.enableint = ips_enable_int_morpheus; + } else if (IPS_USE_MEMIO(ha)) { + /* copperhead w/MEMIO */ + ha->func.isintr = ips_isintr_copperhead_memio; + ha->func.isinit = ips_isinit_copperhead_memio; + ha->func.init = ips_init_copperhead_memio; + ha->func.statupd = ips_statupd_copperhead_memio; + ha->func.statinit = ips_statinit_memio; + ha->func.reset = ips_reset_copperhead_memio; + ha->func.intr = ips_intr_copperhead; + ha->func.erasebios = ips_erase_bios_memio; + ha->func.programbios = ips_program_bios_memio; + ha->func.verifybios = ips_verify_bios_memio; + ha->func.enableint = ips_enable_int_copperhead_memio; + + if (IPS_USE_I2O_DELIVER(ha)) + ha->func.issue = ips_issue_i2o_memio; + else + ha->func.issue = ips_issue_copperhead_memio; + } else { + /* copperhead */ + ha->func.isintr = ips_isintr_copperhead; + ha->func.isinit = ips_isinit_copperhead; + ha->func.init = ips_init_copperhead; + ha->func.statupd = ips_statupd_copperhead; + ha->func.statinit = ips_statinit; + ha->func.reset = ips_reset_copperhead; + ha->func.intr = ips_intr_copperhead; + ha->func.erasebios = ips_erase_bios; + ha->func.programbios = ips_program_bios; + ha->func.verifybios = ips_verify_bios; + ha->func.enableint = ips_enable_int_copperhead; + + if (IPS_USE_I2O_DELIVER(ha)) + ha->func.issue = ips_issue_i2o; + else + ha->func.issue = ips_issue_copperhead; + } + + /* + * Initialize the card if it isn't already + */ + if (!(*ha->func.isinit)(ha)) { + if (!(*ha->func.init)(ha)) { + /* + * Initialization failed + */ + printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + } + + /* install the interrupt handler */ + if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) { + printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + /* + * Allocate a temporary SCB for initialization + */ + ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA); + if (!ha->scbs) { + /* couldn't allocate a temp SCB */ + printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + free_irq(ha->irq, ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + memset(ha->scbs, 0, sizeof(ips_scb_t)); + ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA); + if (!ha->scbs->sg_list) { + /* couldn't allocate a temp SCB S/G list */ + printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", + ips_name, ips_next_controller); + + ha->active = 0; + ips_free(ha); + free_irq(ha->irq, ha); + ips_next_controller++; + ips_num_controllers--; + + continue; + } + + ha->max_cmds = 1; + + ips_next_controller++; + } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i]))); + } + + /* + * Do Phase 2 Initialization + * Controller init + */ + for (i = 0; i < ips_next_controller; i++) { + ha = ips_ha[i]; + sh = ips_sh[i]; + + if (!ha->active) { + scsi_unregister(sh); + ips_ha[i] = NULL; + ips_sh[i] = NULL; continue; } - ha->max_cmds = 1; - if (!ips_hainit(ha)) { printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping\n", - ips_name, index); + ips_name, i); ha->active = 0; + ips_free(ha); + free_irq(ha->irq, ha); + scsi_unregister(sh); + ips_ha[i] = NULL; + ips_sh[i] = NULL; + ips_num_controllers--; continue; } @@ -597,9 +1062,15 @@ ips_detect(Scsi_Host_Template *SHT) { /* allocate CCBs */ if (!ips_allocatescbs(ha)) { printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", - ips_name, index); + ips_name, i); ha->active = 0; + ips_free(ha); + free_irq(ha->irq, ha); + scsi_unregister(sh); + ips_ha[i] = NULL; + ips_sh[i] = NULL; + ips_num_controllers--; continue; } @@ -607,9 +1078,12 @@ ips_detect(Scsi_Host_Template *SHT) { /* finish setting values */ sh->max_id = ha->ntargets; sh->max_lun = ha->nlun; - sh->max_channel = ha->nbus; + sh->max_channel = ha->nbus - 1; sh->can_queue = ha->max_cmds-1; - } /* end for */ + } + + if (ips_num_controllers > 0) + register_reboot_notifier(&ips_notifier); return (ips_num_controllers); @@ -635,7 +1109,7 @@ ips_release(struct Scsi_Host *sh) { ips_ha_t *ha; int i; - DBG("ips_release"); + METHOD_TRACE("ips_release", 1); for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++); @@ -678,15 +1152,83 @@ ips_release(struct Scsi_Host *sh) { /* free extra memory */ ips_free(ha); + /* Free I/O Region */ + if (ha->io_addr) + release_region(ha->io_addr, ha->io_len); + +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) + if (ha->mem_addr) + release_mem_region(ha->mem_addr, ha->mem_len); +#endif + /* free IRQ */ free_irq(ha->irq, ha); - /* unregister with SCSI sub system */ - scsi_unregister(sh); + ips_released_controllers++; + + if (ips_num_controllers == ips_released_controllers) + unregister_reboot_notifier(&ips_notifier); return (FALSE); } +/****************************************************************************/ +/* */ +/* Routine Name: ips_halt */ +/* */ +/* Routine Description: */ +/* */ +/* Perform cleanup when the system reboots */ +/* */ +/****************************************************************************/ +static int +ips_halt(struct notifier_block *nb, ulong event, void *buf) { + ips_scb_t *scb; + ips_ha_t *ha; + int i; + + if ((event != SYS_RESTART) && (event != SYS_HALT) && + (event != SYS_POWER_OFF)) + return (NOTIFY_DONE); + + for (i = 0; i < ips_next_controller; i++) { + ha = (ips_ha_t *) ips_ha[i]; + + if (!ha) + continue; + + if (!ha->active) + continue; + + /* flush the cache on the controller */ + scb = &ha->scbs[ha->max_cmds-1]; + + ips_init_scb(ha, scb); + + scb->timeout = ips_cmd_timeout; + scb->cdb[0] = IPS_CMD_FLUSH; + + scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; + scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb); + scb->cmd.flush_cache.state = IPS_NORM_STATE; + scb->cmd.flush_cache.reserved = 0; + scb->cmd.flush_cache.reserved2 = 0; + scb->cmd.flush_cache.reserved3 = 0; + scb->cmd.flush_cache.reserved4 = 0; + + printk("(%s%d) Flushing Cache.\n", ips_name, ha->host_num); + + /* send command */ + if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE) + printk("(%s%d) Incomplete Flush.\n", ips_name, ha->host_num); + else + printk("(%s%d) Flushing Complete.\n", ips_name, ha->host_num); + } + + unregister_reboot_notifier(&ips_notifier); + return (NOTIFY_OK); +} + /****************************************************************************/ /* */ /* Routine Name: ips_eh_abort */ @@ -701,7 +1243,7 @@ ips_eh_abort(Scsi_Cmnd *SC) { ips_ha_t *ha; ips_copp_wait_item_t *item; - DBG("ips_eh_abort"); + METHOD_TRACE("ips_eh_abort", 1); if (!SC) return (FAILED); @@ -716,9 +1258,7 @@ ips_eh_abort(Scsi_Cmnd *SC) { if (SC->serial_number != SC->serial_number_at_timeout) { /* HMM, looks like a bogus command */ -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "Abort called with bogus scsi command\n"); -#endif + DEBUG(1, "Abort called with bogus scsi command"); return (FAILED); } @@ -757,101 +1297,32 @@ ips_eh_abort(Scsi_Cmnd *SC) { /****************************************************************************/ /* */ -/* Routine Name: ips_abort */ +/* Routine Name: ips_eh_reset */ /* */ /* Routine Description: */ /* */ -/* Abort a command */ -/* */ -/****************************************************************************/ -int -ips_abort(Scsi_Cmnd *SC) { - ips_ha_t *ha; - ips_copp_wait_item_t *item; - - DBG("ips_abort"); - - if (!SC) - return (SCSI_ABORT_SNOOZE); - - ha = (ips_ha_t *) SC->host->hostdata; - - if (!ha) - return (SCSI_ABORT_SNOOZE); - - if (!ha->active) - return (SCSI_ABORT_SNOOZE); - - if (SC->serial_number != SC->serial_number_at_timeout) { - /* HMM, looks like a bogus command */ -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "Abort called with bogus scsi command\n"); -#endif - - return (SCSI_ABORT_NOT_RUNNING); - } - - if (test_and_set_bit(IPS_IN_ABORT, &ha->flags)) - return (SCSI_ABORT_SNOOZE); - - /* See if the command is on the copp queue */ - IPS_QUEUE_LOCK(&ha->copp_waitlist); - item = ha->copp_waitlist.head; - while ((item) && (item->scsi_cmd != SC)) - item = item->next; - IPS_QUEUE_UNLOCK(&ha->copp_waitlist); - - if (item) { - /* Found it */ - ips_removeq_copp(&ha->copp_waitlist, item); - clear_bit(IPS_IN_ABORT, &ha->flags); - - return (SCSI_ABORT_PENDING); - } - - /* See if the command is on the wait queue */ - if (ips_removeq_wait(&ha->scb_waitlist, SC)) { - /* command not sent yet */ - clear_bit(IPS_IN_ABORT, &ha->flags); - - return (SCSI_ABORT_PENDING); - } else { - /* command must have already been sent */ - clear_bit(IPS_IN_ABORT, &ha->flags); - - return (SCSI_ABORT_SNOOZE); - } -} - -/****************************************************************************/ -/* */ -/* Routine Name: ips_eh_reset */ -/* */ -/* Routine Description: */ -/* */ -/* Reset the controller (with new eh error code) */ -/* */ -/* NOTE: this routine is called under the io_request_lock spinlock */ +/* Reset the controller (with new eh error code) */ +/* */ +/* NOTE: this routine is called under the io_request_lock spinlock */ /* */ /****************************************************************************/ int ips_eh_reset(Scsi_Cmnd *SC) { + int ret; + int i; u32 cpu_flags; ips_ha_t *ha; ips_scb_t *scb; ips_copp_wait_item_t *item; - DBG("ips_eh_reset"); + METHOD_TRACE("ips_eh_reset", 1); #ifdef NO_IPS_RESET return (FAILED); #else if (!SC) { - -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "Reset called with NULL scsi command\n"); -#endif + DEBUG(1, "Reset called with NULL scsi command"); return (FAILED); } @@ -859,10 +1330,7 @@ ips_eh_reset(Scsi_Cmnd *SC) { ha = (ips_ha_t *) SC->host->hostdata; if (!ha) { - -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "Reset called with NULL ha struct\n"); -#endif + DEBUG(1, "Reset called with NULL ha struct"); return (FAILED); } @@ -900,149 +1368,72 @@ ips_eh_reset(Scsi_Cmnd *SC) { * command must have already been sent * reset the controller */ - if (!ips_reset_adapter(ha)) { - clear_bit(IPS_IN_RESET, &ha->flags); - - return (FAILED); - } - - if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { - clear_bit(IPS_IN_RESET, &ha->flags); - - return (FAILED); - } - - /* FFDC */ - if (ha->subsys->param[3] & 0x300000) { - struct timeval tv; - - do_gettimeofday(&tv); - IPS_HA_LOCK(cpu_flags); - ha->last_ffdc = tv.tv_sec; - ha->reset_count++; - IPS_HA_UNLOCK(cpu_flags); - ips_ffdc_reset(ha, IPS_INTR_IORL); - } - - /* Now fail all of the active commands */ -#if IPS_DEBUG >= 1 - printk(KERN_WARNING "(%s%d) Failing active commands\n", + printk(KERN_NOTICE "(%s%d) Resetting controller.\n", ips_name, ha->host_num); -#endif - while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { - scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24); - scb->scsi_cmd->scsi_done(scb->scsi_cmd); - ips_freescb(ha, scb); - } - - /* Reset the number of active IOCTLs */ - IPS_HA_LOCK(cpu_flags); - ha->num_ioctl = 0; - IPS_HA_UNLOCK(cpu_flags); - - clear_bit(IPS_IN_RESET, &ha->flags); - - if (!test_bit(IPS_IN_INTR, &ha->flags)) { - /* - * Only execute the next command when - * we are not being called from the - * interrupt handler. The interrupt - * handler wants to do this and since - * interrupts are turned off here.... - */ - ips_next(ha, IPS_INTR_IORL); - } - - return (SUCCESS); - -#endif /* NO_IPS_RESET */ - -} - -/****************************************************************************/ -/* */ -/* Routine Name: ips_reset */ -/* */ -/* Routine Description: */ -/* */ -/* Reset the controller */ -/* */ -/* NOTE: this routine is called under the io_request_lock spinlock */ -/* */ -/****************************************************************************/ -int -ips_reset(Scsi_Cmnd *SC, unsigned int flags) { - u32 cpu_flags; - ips_ha_t *ha; - ips_scb_t *scb; - ips_copp_wait_item_t *item; + ret = (*ha->func.reset)(ha); - DBG("ips_reset"); + if (!ret) { + Scsi_Cmnd *scsi_cmd; -#ifdef NO_IPS_RESET - return (SCSI_RESET_SNOOZE); -#else - - if (!SC) { + printk(KERN_NOTICE + "(%s%d) Controller reset failed - controller now offline.\n", + ips_name, ha->host_num); -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "Reset called with NULL scsi command\n"); -#endif + /* Now fail all of the active commands */ + DEBUG_VAR(1, "(%s%d) Failing active commands", + ips_name, ha->host_num); - return (SCSI_RESET_SNOOZE); - } + while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { + scb->scsi_cmd->result = DID_ERROR << 16; + scb->scsi_cmd->scsi_done(scb->scsi_cmd); + ips_freescb(ha, scb); + } - ha = (ips_ha_t *) SC->host->hostdata; + /* Now fail all of the pending commands */ + DEBUG_VAR(1, "(%s%d) Failing pending commands", + ips_name, ha->host_num); - if (!ha) { + while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) { + scsi_cmd->result = DID_ERROR; + scsi_cmd->scsi_done(scsi_cmd); + } -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "Reset called with NULL ha struct\n"); -#endif + ha->active = FALSE; + clear_bit(IPS_IN_RESET, &ha->flags); - return (SCSI_RESET_SNOOZE); + return (FAILED); } - if (!ha->active) - return (SCSI_RESET_SNOOZE); - - if (test_and_set_bit(IPS_IN_RESET, &ha->flags)) - return (SCSI_RESET_SNOOZE); - - /* See if the command is on the copp queue */ - IPS_QUEUE_LOCK(&ha->copp_waitlist); - item = ha->copp_waitlist.head; - while ((item) && (item->scsi_cmd != SC)) - item = item->next; - IPS_QUEUE_UNLOCK(&ha->copp_waitlist); - - if (item) { - /* Found it */ - ips_removeq_copp(&ha->copp_waitlist, item); - clear_bit(IPS_IN_RESET, &ha->flags); + if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { + Scsi_Cmnd *scsi_cmd; - return (SCSI_RESET_SNOOZE); - } + printk(KERN_NOTICE + "(%s%d) Controller reset failed - controller now offline.\n", + ips_name, ha->host_num); - /* See if the command is on the wait queue */ - if (ips_removeq_wait(&ha->scb_waitlist, SC)) { - /* command not sent yet */ - clear_bit(IPS_IN_RESET, &ha->flags); + /* Now fail all of the active commands */ + DEBUG_VAR(1, "(%s%d) Failing active commands", + ips_name, ha->host_num); - return (SCSI_RESET_SNOOZE); - } + while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { + scb->scsi_cmd->result = DID_ERROR << 16; + scb->scsi_cmd->scsi_done(scb->scsi_cmd); + ips_freescb(ha, scb); + } - /* reset the controller */ - if (!ips_reset_adapter(ha)) { - clear_bit(IPS_IN_RESET, &ha->flags); + /* Now fail all of the pending commands */ + DEBUG_VAR(1, "(%s%d) Failing pending commands", + ips_name, ha->host_num); - return (SCSI_RESET_ERROR); - } + while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) { + scsi_cmd->result = DID_ERROR << 16; + scsi_cmd->scsi_done(scsi_cmd); + } - if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { + ha->active = FALSE; clear_bit(IPS_IN_RESET, &ha->flags); - return (SCSI_RESET_ERROR); + return (FAILED); } /* FFDC */ @@ -1058,16 +1449,19 @@ ips_reset(Scsi_Cmnd *SC, unsigned int flags) { } /* Now fail all of the active commands */ -#if IPS_DEBUG >= 1 - printk(KERN_WARNING "(%s%d) Failing active commands\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) Failing active commands", + ips_name, ha->host_num); + while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24); scb->scsi_cmd->scsi_done(scb->scsi_cmd); ips_freescb(ha, scb); } + /* Reset DCDB active command bits */ + for (i = 1; i < ha->nbus; i++) + ha->dcdb_active[i-1] = 0; + /* Reset the number of active IOCTLs */ IPS_HA_LOCK(cpu_flags); ha->num_ioctl = 0; @@ -1086,7 +1480,7 @@ ips_reset(Scsi_Cmnd *SC, unsigned int flags) { ips_next(ha, IPS_INTR_IORL); } - return (SCSI_RESET_SUCCESS); + return (SUCCESS); #endif /* NO_IPS_RESET */ @@ -1110,7 +1504,7 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) { u32 cpu_flags; DECLARE_MUTEX_LOCKED(sem); - DBG("ips_queue"); + METHOD_TRACE("ips_queue", 1); ha = (ips_ha_t *) SC->host->hostdata; @@ -1118,7 +1512,7 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) { return (1); if (!ha->active) - return (1); + return (DID_ERROR); #ifndef NO_IPS_CMDLINE if (ips_is_passthru(SC)) { @@ -1151,24 +1545,28 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) { SC->scsi_done = done; -#if IPS_DEBUG >= 10 - printk(KERN_NOTICE "%s: ips_queue: cmd 0x%X (%d %d %d)\n", - ips_name, - SC->cmnd[0], - SC->channel, - SC->target, - SC->lun); -#if IPS_DEBUG >= 11 - MDELAY(2*IPS_ONE_SEC); -#endif -#endif + DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)", + ips_name, + ha->host_num, + SC->cmnd[0], + SC->channel, + SC->target, + SC->lun); + + /* Check for command to initiator IDs */ + if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) { + SC->result = DID_NO_CONNECT << 16; + done(SC); + + return (0); + } #ifndef NO_IPS_CMDLINE if (ips_is_passthru(SC)) { ips_copp_wait_item_t *scratch; /* allocate space for the scribble */ - scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_KERNEL); + scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_ATOMIC); if (!scratch) { SC->result = DID_ERROR << 16; @@ -1224,10 +1622,8 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) { datasize = *((u32 *) &SC->cmnd[8]); if (copy_to_user(user_area, kern_area, datasize) > 0) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy out user data\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy out user data", + ips_name, ha->host_num); SC->result = DID_ERROR << 16; SC->scsi_done(SC); @@ -1255,7 +1651,7 @@ ips_biosparam(Disk *disk, kdev_t dev, int geom[]) { int sectors; int cylinders; - DBG("ips_biosparam"); + METHOD_TRACE("ips_biosparam", 1); ha = (ips_ha_t *) disk->device->host->hostdata; @@ -1281,10 +1677,8 @@ ips_biosparam(Disk *disk, kdev_t dev, int geom[]) { cylinders = disk->capacity / (heads * sectors); -#if IPS_DEBUG >= 2 - printk(KERN_NOTICE "Geometry: heads: %d, sectors: %d, cylinders: %d\n", - heads, sectors, cylinders); -#endif + DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d", + heads, sectors, cylinders); geom[0] = heads; geom[1] = sectors; @@ -1293,6 +1687,40 @@ ips_biosparam(Disk *disk, kdev_t dev, int geom[]) { return (0); } +/****************************************************************************/ +/* */ +/* Routine Name: ips_select_queue_depth */ +/* */ +/* Routine Description: */ +/* */ +/* Select queue depths for the devices on the contoller */ +/* */ +/****************************************************************************/ +static void +ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) { + Scsi_Device *device; + ips_ha_t *ha; + int count = 0; + + ha = IPS_HA(host); + + for (device = scsi_devs; device; device = device->next) { + if (device->host == host) { + if ((device->channel == 0) && (device->type == 0)) + count++; + } + } + + for (device = scsi_devs; device; device = device->next) { + if (device->host == host) { + if ((device->channel == 0) && (device->type == 0)) + device->queue_depth = ha->max_cmds / count - 1; + else + device->queue_depth = 2; + } + } +} + /****************************************************************************/ /* */ /* Routine Name: do_ipsintr */ @@ -1307,7 +1735,7 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) { ips_ha_t *ha; u32 cpu_flags; - DBG("do_ipsintr"); + METHOD_TRACE("do_ipsintr", 2); ha = (ips_ha_t *) dev_id; @@ -1333,7 +1761,7 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) { return; } - ips_intr(ha); + (*ha->func.intr)(ha); clear_bit(IPS_IN_INTR, &ha->flags); @@ -1345,7 +1773,7 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) { /****************************************************************************/ /* */ -/* Routine Name: ips_intr */ +/* Routine Name: ips_intr_copperhead */ /* */ /* Routine Description: */ /* */ @@ -1355,13 +1783,14 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) { /* */ /****************************************************************************/ void -ips_intr(ips_ha_t *ha) { +ips_intr_copperhead(ips_ha_t *ha) { ips_stat_t *sp; ips_scb_t *scb; - int status; + IPS_STATUS cstatus; + int intrstatus; u32 cpu_flags; - DBG("ips_intr"); + METHOD_TRACE("ips_intr", 2); if (!ha) return; @@ -1370,16 +1799,36 @@ ips_intr(ips_ha_t *ha) { return; IPS_HA_LOCK(cpu_flags); - while (ips_isintr(ha)) { + + intrstatus = (*ha->func.isintr)(ha); + + if (!intrstatus) { + /* + * Unexpected/Shared interrupt + */ + IPS_HA_UNLOCK(cpu_flags); + + return; + } + + while (TRUE) { sp = &ha->sp; - if ((status = ips_chkstatus(ha)) < 0) { - /* unexpected interrupt - no ccb */ + intrstatus = (*ha->func.isintr)(ha); + + if (!intrstatus) + break; + else + cstatus.value = (*ha->func.statupd)(ha); + + if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n", ips_name, ha->host_num); - continue ; + + continue; } + ips_chkstatus(ha, &cstatus); scb = (ips_scb_t *) sp->scb_addr; /* @@ -1389,34 +1838,110 @@ ips_intr(ips_ha_t *ha) { IPS_HA_UNLOCK(cpu_flags); (*scb->callback) (ha, scb); IPS_HA_LOCK(cpu_flags); - } + } /* end while */ IPS_HA_UNLOCK(cpu_flags); } /****************************************************************************/ /* */ -/* Routine Name: ips_info */ +/* Routine Name: ips_intr_morpheus */ /* */ /* Routine Description: */ /* */ -/* Return info about the driver */ +/* Polling interrupt handler */ +/* */ +/* ASSUMES interrupts are disabled */ /* */ /****************************************************************************/ -const char * -ips_info(struct Scsi_Host *SH) { - static char buffer[256]; - char *bp; - ips_ha_t *ha; - - DBG("ips_info"); +void +ips_intr_morpheus(ips_ha_t *ha) { + ips_stat_t *sp; + ips_scb_t *scb; + IPS_STATUS cstatus; + int intrstatus; + u32 cpu_flags; - ha = IPS_HA(SH); + METHOD_TRACE("ips_intr_morpheus", 2); if (!ha) - return (NULL); + return; - bp = &buffer[0]; + if (!ha->active) + return; + + IPS_HA_LOCK(cpu_flags); + + intrstatus = (*ha->func.isintr)(ha); + + if (!intrstatus) { + /* + * Unexpected/Shared interrupt + */ + IPS_HA_UNLOCK(cpu_flags); + + return; + } + + while (TRUE) { + sp = &ha->sp; + + intrstatus = (*ha->func.isintr)(ha); + + if (!intrstatus) + break; + else + cstatus.value = (*ha->func.statupd)(ha); + + if (cstatus.value == 0xffffffff) + /* No more to process */ + break; + + if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { + printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n", + ips_name, ha->host_num); + + continue; + } + + ips_chkstatus(ha, &cstatus); + scb = (ips_scb_t *) sp->scb_addr; + + /* + * use the callback function to finish things up + * NOTE: interrupts are OFF for this + */ + IPS_HA_UNLOCK(cpu_flags); + (*scb->callback) (ha, scb); + IPS_HA_LOCK(cpu_flags); + } /* end while */ + + IPS_HA_UNLOCK(cpu_flags); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_info */ +/* */ +/* Routine Description: */ +/* */ +/* Return info about the driver */ +/* */ +/****************************************************************************/ +const char * +ips_info(struct Scsi_Host *SH) { + static char buffer[256]; + char *bp; + ips_ha_t *ha; + + METHOD_TRACE("ips_info", 1); + + ha = IPS_HA(SH); + + if (!ha) + return (NULL); + + bp = &buffer[0]; memset(bp, 0, sizeof(buffer)); strcpy(bp, "IBM PCI ServeRAID "); @@ -1449,10 +1974,10 @@ ips_proc_info(char *buffer, char **start, off_t offset, int ret; ips_ha_t *ha = NULL; - DBG("ips_proc_info"); + METHOD_TRACE("ips_proc_info", 1); /* Find our host structure */ - for (i = 0; i < ips_num_controllers; i++) { + for (i = 0; i < ips_next_controller; i++) { if (ips_sh[i] && ips_sh[i]->host_no == hostno) { ha = (ips_ha_t *) ips_sh[i]->hostdata; @@ -1494,7 +2019,7 @@ ips_proc_info(char *buffer, char **start, off_t offset, /****************************************************************************/ static int ips_is_passthru(Scsi_Cmnd *SC) { - DBG("ips_is_passthru"); + METHOD_TRACE("ips_is_passthru", 1); if (!SC) return (0); @@ -1528,24 +2053,20 @@ static int ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { ips_passthru_t *pt; - DBG("ips_make_passthru"); + METHOD_TRACE("ips_make_passthru", 1); if (!SC->request_bufflen || !SC->request_buffer) { /* no data */ -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) No passthru structure\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) No passthru structure", + ips_name, ha->host_num); return (IPS_FAILURE); } if (SC->request_bufflen < sizeof(ips_passthru_t)) { /* wrong size */ -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n", + DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", ips_name, ha->host_num); -#endif return (IPS_FAILURE); } @@ -1555,10 +2076,8 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { (((char *) SC->request_buffer)[2] != 'P') || (((char *) SC->request_buffer)[3] != 'P')) { /* signature doesn't match */ -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) Wrong signature on passthru structure.\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) Wrong signature on passthru structure.", + ips_name, ha->host_num); return (IPS_FAILURE); } @@ -1602,10 +2121,8 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { if (SC->cmnd[0] == IPS_IOCTL_COMMAND) { if (SC->request_bufflen < (sizeof(ips_passthru_t) + pt->CmdBSize)) { /* wrong size */ - #if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n", - ips_name, ha->host_num); - #endif + DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", + ips_name, ha->host_num); return (IPS_FAILURE); } @@ -1617,10 +2134,8 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { } else if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) { if (SC->request_bufflen < (sizeof(ips_passthru_t))) { /* wrong size */ - #if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n", - ips_name, ha->host_num); - #endif + DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", + ips_name, ha->host_num); return (IPS_FAILURE); } @@ -1639,14 +2154,23 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { return (IPS_FAILURE); /* don't flash the BIOS on future cards */ - if (ha->revision_id > IPS_REVID_TROMBONE64) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) flash bios failed - unsupported controller\n", - ips_name, ha->host_num); -#endif + if ((ha->device_id != IPS_COPPERHEAD_DEVICEID) || + (ha->revision_id > IPS_REVID_TROMBONE64)) { + DEBUG_VAR(1, "(%s%d) flash bios failed - unsupported controller", + ips_name, ha->host_num); + return (IPS_FAILURE); } + /* + * Check to make sure we have functions + * to handle the request + */ + if ((!ha->func.programbios) || + (!ha->func.erasebios) || + (!ha->func.verifybios)) + return (IPS_FAILURE); + /* copy in the size/buffer ptr from the scsi command */ memcpy(&pt->CmdBuffer, &SC->cmnd[4], 4); memcpy(&pt->CmdBSize, &SC->cmnd[8], 4); @@ -1660,7 +2184,7 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { void *bigger_struct; /* try to allocate a bigger struct */ - bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA); + bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA); if (bigger_struct) { /* free the old memory */ kfree(ha->ioctl_data); @@ -1674,37 +2198,29 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) { /* copy in the buffer */ if (copy_from_user(ha->ioctl_data, pt->CmdBuffer, pt->CmdBSize) > 0) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) flash bios failed - unable to copy user buffer\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) flash bios failed - unable to copy user buffer", + ips_name, ha->host_num); return (IPS_FAILURE); } - if (ips_erase_bios(ha)) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) flash bios failed - unable to erase flash\n", - ips_name, ha->host_num); -#endif + if ((*ha->func.erasebios)(ha)) { + DEBUG_VAR(1, "(%s%d) flash bios failed - unable to erase flash", + ips_name, ha->host_num); return (IPS_FAILURE); } - if (ips_program_bios(ha, ha->ioctl_data, pt->CmdBSize)) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) flash bios failed - unable to program flash\n", - ips_name, ha->host_num); -#endif + if ((*ha->func.programbios)(ha, ha->ioctl_data, pt->CmdBSize)) { + DEBUG_VAR(1, "(%s%d) flash bios failed - unable to program flash", + ips_name, ha->host_num); return (IPS_FAILURE); } - if (ips_verify_bios(ha, ha->ioctl_data, pt->CmdBSize)) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) flash bios failed - unable to verify flash\n", - ips_name, ha->host_num); -#endif + if ((*ha->func.verifybios)(ha, ha->ioctl_data, pt->CmdBSize)) { + DEBUG_VAR(1, "(%s%d) flash bios failed - unable to verify flash", + ips_name, ha->host_num); return (IPS_FAILURE); } @@ -1728,7 +2244,7 @@ static int ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { IPS_SG_LIST *sg_list; - DBG("ips_usrcmd"); + METHOD_TRACE("ips_usrcmd", 1); if ((!scb) || (!pt) || (!ha)) return (0); @@ -1811,7 +2327,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { char *kern_area; u32 datasize; - DBG("ips_usrcmd"); + METHOD_TRACE("ips_usrcmd", 1); if ((!scb) || (!pt) || (!ha)) return (0); @@ -1848,7 +2364,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { void *bigger_struct; /* try to allocate a bigger struct */ - bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA); + bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA); if (bigger_struct) { /* free the old memory */ kfree(ha->ioctl_data); @@ -1869,10 +2385,8 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { datasize = *((u32 *) &scb->scsi_cmd->cmnd[8]); if (copy_from_user(kern_area, user_area, datasize) > 0) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy in user data\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy in user data", + ips_name, ha->host_num); return (0); } @@ -1923,12 +2437,11 @@ static void ips_cleanup_passthru(ips_ha_t *ha, ips_scb_t *scb) { ips_passthru_t *pt; - DBG("ips_cleanup_passthru"); + METHOD_TRACE("ips_cleanup_passthru", 1); if ((!scb) || (!scb->scsi_cmd) || (!scb->scsi_cmd->request_buffer)) { -#if IPS_DEBUG_PT >= 1 - printk(KERN_NOTICE "IPS couldn't cleanup\n"); -#endif + DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru", + ips_name, ha->host_num); return ; } @@ -1962,12 +2475,13 @@ static int ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) { IPS_INFOSTR info; - DBG("ips_host_info"); + METHOD_TRACE("ips_host_info", 1); info.buffer = ptr; info.length = len; info.offset = offset; info.pos = 0; + info.localpos = 0; copy_info(&info, "\nIBM ServeRAID General Information:\n\n"); @@ -1977,7 +2491,16 @@ ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) { else copy_info(&info, "\tController Type : Unknown\n"); - copy_info(&info, "\tIO port address : 0x%lx\n", ha->io_addr); + if (ha->io_addr) + copy_info(&info, "\tIO region : 0x%lx (%d bytes)\n", + ha->io_addr, ha->io_len); + + if (ha->mem_addr) { + copy_info(&info, "\tMemory region : 0x%lx (%d bytes)\n", + ha->mem_addr, ha->mem_len); + copy_info(&info, "\tShared memory address : 0x%lx\n", ha->mem_ptr); + } + copy_info(&info, "\tIRQ number : %d\n", ha->irq); if (ha->nvram->signature == IPS_NVRAM_P5_SIG) @@ -2017,7 +2540,7 @@ ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) { copy_info(&info, "\n"); - return (info.pos > info.offset ? info.pos - info.offset : 0); + return (info.localpos); } /****************************************************************************/ @@ -2031,10 +2554,7 @@ ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) { /****************************************************************************/ static void copy_mem_info(IPS_INFOSTR *info, char *data, int len) { - DBG("copy_mem_info"); - - if (info->pos + len > info->length) - len = info->length - info->pos; + METHOD_TRACE("copy_mem_info", 1); if (info->pos + len < info->offset) { info->pos += len; @@ -2043,12 +2563,17 @@ copy_mem_info(IPS_INFOSTR *info, char *data, int len) { if (info->pos < info->offset) { data += (info->offset - info->pos); - len -= (info->offset - info->pos); + len -= (info->offset - info->pos); + info->pos += (info->offset - info->pos); } + if (info->localpos + len > info->length) + len = info->length - info->localpos; + if (len > 0) { - memcpy(info->buffer + info->pos, data, len); + memcpy(info->buffer + info->localpos, data, len); info->pos += len; + info->localpos += len; } } @@ -2064,10 +2589,10 @@ copy_mem_info(IPS_INFOSTR *info, char *data, int len) { static int copy_info(IPS_INFOSTR *info, char *fmt, ...) { va_list args; - char buf[81]; + char buf[128]; int len; - DBG("copy_info"); + METHOD_TRACE("copy_info", 1); va_start(args, fmt); len = vsprintf(buf, fmt, args); @@ -2091,57 +2616,32 @@ copy_info(IPS_INFOSTR *info, char *fmt, ...) { /****************************************************************************/ static int ips_hainit(ips_ha_t *ha) { - int i; + int i; + struct timeval tv; - DBG("ips_hainit"); + METHOD_TRACE("ips_hainit", 1); if (!ha) return (0); - /* initialize status queue */ - ips_statinit(ha); + if (ha->func.statinit) + (*ha->func.statinit)(ha); + if (ha->func.enableint) + (*ha->func.enableint)(ha); + + /* Send FFDC */ ha->reset_count = 1; + do_gettimeofday(&tv); + ha->last_ffdc = tv.tv_sec; + ips_ffdc_reset(ha, IPS_INTR_IORL); - /* Setup HBA ID's */ if (!ips_read_config(ha, IPS_INTR_IORL)) { - -#ifndef NO_IPS_RESET - - ha->reset_count++; - - /* Try to reset the controller and try again */ - if (!ips_reset_adapter(ha)) { - printk(KERN_WARNING "(%s%d) unable to reset controller.\n", - ips_name, ha->host_num); - - return (0); - } - - if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING "(%s%d) unable to initialize controller.\n", - ips_name, ha->host_num); - - return (0); - } - -#endif - - if (!ips_read_config(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING "(%s%d) unable to read config from controller.\n", - ips_name, ha->host_num); - - return (0); - } - } /* end if */ - - /* write driver version */ - if (!ips_write_driver_status(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n", + printk(KERN_WARNING "(%s%d) unable to read config from controller.\n", ips_name, ha->host_num); return (0); - } + } /* end if */ if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) { printk(KERN_WARNING "(%s%d) unable to read controller status.\n", @@ -2157,19 +2657,18 @@ ips_hainit(ips_ha_t *ha) { return (0); } - /* FFDC */ - if (ha->subsys->param[3] & 0x300000) { - struct timeval tv; + /* write nvram user page 5 */ + if (!ips_write_driver_status(ha, IPS_INTR_IORL)) { + printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n", + ips_name, ha->host_num); - do_gettimeofday(&tv); - ha->last_ffdc = tv.tv_sec; - ips_ffdc_reset(ha, IPS_INTR_IORL); + return (0); } /* set limits on SID, LUN, BUS */ ha->ntargets = IPS_MAX_TARGETS + 1; ha->nlun = 1; - ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS); + ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1; switch (ha->conf->logical_drive[0].ucStripeSize) { case 4: @@ -2240,13 +2739,14 @@ ips_next(ips_ha_t *ha, int intr) { ips_scb_t *scb; Scsi_Cmnd *SC; Scsi_Cmnd *p; + Scsi_Cmnd *q; ips_copp_wait_item_t *item; int ret; int intr_status; u32 cpu_flags; u32 cpu_flags2; - DBG("ips_next"); + METHOD_TRACE("ips_next", 1); if (!ha) return ; @@ -2392,7 +2892,8 @@ ips_next(ips_ha_t *ha, int intr) { IPS_HA_UNLOCK(cpu_flags); - SC = ips_removeq_wait(&ha->scb_waitlist, p); + q = p; + SC = ips_removeq_wait(&ha->scb_waitlist, q); SC->result = DID_OK; SC->host_scribble = NULL; @@ -2419,28 +2920,41 @@ ips_next(ips_ha_t *ha, int intr) { sg = SC->request_buffer; - for (i = 0; i < SC->use_sg; i++) { - scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address); - scb->sg_list[i].length = sg[i].length; + if (SC->use_sg == 1) { + if (sg[0].length > ha->max_xfer) { + scb->breakup = 1; + scb->data_len = ha->max_xfer; + } else + scb->data_len = sg[0].length; - if (scb->data_len + sg[i].length > ha->max_xfer) { - /* - * Data Breakup required - */ - scb->breakup = i; - break; - } + scb->dcdb.transfer_length = scb->data_len; + scb->data_busaddr = VIRT_TO_BUS(sg[0].address); + scb->sg_len = 0; + } else { - scb->data_len += sg[i].length; - } + for (i = 0; i < SC->use_sg; i++) { + scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address); + scb->sg_list[i].length = sg[i].length; - if (!scb->breakup) - scb->sg_len = SC->use_sg; - else - scb->sg_len = scb->breakup; + if (scb->data_len + sg[i].length > ha->max_xfer) { + /* + * Data Breakup required + */ + scb->breakup = i; + break; + } + + scb->data_len += sg[i].length; + } - scb->dcdb.transfer_length = scb->data_len; - scb->data_busaddr = VIRT_TO_BUS(scb->sg_list); + if (!scb->breakup) + scb->sg_len = SC->use_sg; + else + scb->sg_len = scb->breakup; + + scb->dcdb.transfer_length = scb->data_len; + scb->data_busaddr = VIRT_TO_BUS(scb->sg_list); + } } else { if (SC->request_bufflen) { if (SC->request_bufflen > ha->max_xfer) { @@ -2527,7 +3041,7 @@ ips_next(ips_ha_t *ha, int intr) { /****************************************************************************/ static inline void ips_putq_scb_head(ips_scb_queue_t *queue, ips_scb_t *item) { - DBG("ips_putq_scb_head"); + METHOD_TRACE("ips_putq_scb_head", 1); if (!item) return ; @@ -2558,7 +3072,7 @@ ips_putq_scb_head(ips_scb_queue_t *queue, ips_scb_t *item) { /****************************************************************************/ static inline void ips_putq_scb_tail(ips_scb_queue_t *queue, ips_scb_t *item) { - DBG("ips_putq_scb_tail"); + METHOD_TRACE("ips_putq_scb_tail", 1); if (!item) return ; @@ -2595,7 +3109,7 @@ static inline ips_scb_t * ips_removeq_scb_head(ips_scb_queue_t *queue) { ips_scb_t *item; - DBG("ips_removeq_scb_head"); + METHOD_TRACE("ips_removeq_scb_head", 1); IPS_QUEUE_LOCK(queue); @@ -2635,7 +3149,7 @@ static inline ips_scb_t * ips_removeq_scb(ips_scb_queue_t *queue, ips_scb_t *item) { ips_scb_t *p; - DBG("ips_removeq_scb"); + METHOD_TRACE("ips_removeq_scb", 1); if (!item) return (NULL); @@ -2686,7 +3200,7 @@ ips_removeq_scb(ips_scb_queue_t *queue, ips_scb_t *item) { /****************************************************************************/ static inline void ips_putq_wait_head(ips_wait_queue_t *queue, Scsi_Cmnd *item) { - DBG("ips_putq_wait_head"); + METHOD_TRACE("ips_putq_wait_head", 1); if (!item) return ; @@ -2717,7 +3231,7 @@ ips_putq_wait_head(ips_wait_queue_t *queue, Scsi_Cmnd *item) { /****************************************************************************/ static inline void ips_putq_wait_tail(ips_wait_queue_t *queue, Scsi_Cmnd *item) { - DBG("ips_putq_wait_tail"); + METHOD_TRACE("ips_putq_wait_tail", 1); if (!item) return ; @@ -2754,7 +3268,7 @@ static inline Scsi_Cmnd * ips_removeq_wait_head(ips_wait_queue_t *queue) { Scsi_Cmnd *item; - DBG("ips_removeq_wait_head"); + METHOD_TRACE("ips_removeq_wait_head", 1); IPS_QUEUE_LOCK(queue); @@ -2794,7 +3308,7 @@ static inline Scsi_Cmnd * ips_removeq_wait(ips_wait_queue_t *queue, Scsi_Cmnd *item) { Scsi_Cmnd *p; - DBG("ips_removeq_wait"); + METHOD_TRACE("ips_removeq_wait", 1); if (!item) return (NULL); @@ -2845,7 +3359,7 @@ ips_removeq_wait(ips_wait_queue_t *queue, Scsi_Cmnd *item) { /****************************************************************************/ static inline void ips_putq_copp_head(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) { - DBG("ips_putq_copp_head"); + METHOD_TRACE("ips_putq_copp_head", 1); if (!item) return ; @@ -2876,7 +3390,7 @@ ips_putq_copp_head(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) { /****************************************************************************/ static inline void ips_putq_copp_tail(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) { - DBG("ips_putq_copp_tail"); + METHOD_TRACE("ips_putq_copp_tail", 1); if (!item) return ; @@ -2913,7 +3427,7 @@ static inline ips_copp_wait_item_t * ips_removeq_copp_head(ips_copp_queue_t *queue) { ips_copp_wait_item_t *item; - DBG("ips_removeq_copp_head"); + METHOD_TRACE("ips_removeq_copp_head", 1); IPS_QUEUE_LOCK(queue); @@ -2953,7 +3467,7 @@ static inline ips_copp_wait_item_t * ips_removeq_copp(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) { ips_copp_wait_item_t *p; - DBG("ips_removeq_copp"); + METHOD_TRACE("ips_removeq_copp", 1); if (!item) return (NULL); @@ -3002,7 +3516,7 @@ ips_removeq_copp(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) { /****************************************************************************/ static void ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) { - DBG("ipsintr_blocking"); + METHOD_TRACE("ipsintr_blocking", 2); if ((ha->waitflag == TRUE) && (ha->cmd_in_progress == scb->cdb[0])) { @@ -3023,7 +3537,14 @@ ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) { /****************************************************************************/ static void ipsintr_done(ips_ha_t *ha, ips_scb_t *scb) { - DBG("ipsintr_done"); + METHOD_TRACE("ipsintr_done", 2); + + if (!scb) { + printk(KERN_WARNING "(%s%d) Spurious interrupt; scb NULL.\n", + ips_name, ha->host_num); + + return ; + } if (scb->scsi_cmd == NULL) { /* unexpected interrupt */ @@ -3050,7 +3571,7 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { int ret; u32 cpu_flags; - DBG("ips_done"); + METHOD_TRACE("ips_done", 1); if (!scb) return ; @@ -3082,31 +3603,46 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { sg = scb->scsi_cmd->request_buffer; - scb->data_len = 0; + if (scb->scsi_cmd->use_sg == 1) { + if (sg[0].length - (bk_save * ha->max_xfer)) { + /* Further breakup required */ + scb->data_len = ha->max_xfer; + scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer)); + scb->breakup = bk_save + 1; + } else { + scb->data_len = sg[0].length - (bk_save * ha->max_xfer); + scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer)); + } - for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) { - scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address); - scb->sg_list[i - bk_save].length = sg[i].length; + scb->dcdb.transfer_length = scb->data_len; + scb->sg_len = 0; + } else { + scb->data_len = 0; - if (scb->data_len + sg[i].length > ha->max_xfer) { - /* - * Data Breakup required - */ - scb->breakup = i; - break; - } + for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) { + scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address); + scb->sg_list[i - bk_save].length = sg[i].length; - scb->data_len += sg[i].length; - } + if (scb->data_len + sg[i].length > ha->max_xfer) { + /* + * Data Breakup required + */ + scb->breakup = i; + break; + } - if (!scb->breakup) - scb->sg_len = scb->scsi_cmd->use_sg - bk_save; - else - scb->sg_len = scb->breakup - bk_save; + scb->data_len += sg[i].length; + } - scb->dcdb.transfer_length = scb->data_len; - scb->data_busaddr = VIRT_TO_BUS(scb->sg_list); - } else { + if (!scb->breakup) + scb->sg_len = scb->scsi_cmd->use_sg - bk_save; + else + scb->sg_len = scb->breakup - bk_save; + + scb->dcdb.transfer_length = scb->data_len; + scb->data_busaddr = VIRT_TO_BUS(scb->sg_list); + } + } else { /* Non S/G Request */ if (scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) { /* Further breakup required */ @@ -3185,31 +3721,29 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { /* */ /****************************************************************************/ static int -ips_map_status(ips_scb_t *scb, ips_stat_t *sp) { +ips_map_status(ips_ha_t *ha, ips_scb_t *scb, ips_stat_t *sp) { int errcode; + int device_error; - DBG("ips_map_status"); + METHOD_TRACE("ips_map_status", 1); if (scb->bus) { -#if IPS_DEBUG >= 10 - printk(KERN_NOTICE "(%s) Physical device error: %x %x, Sense Key: %x, ASC: %x, ASCQ: %x\n", - ips_name, - scb->basic_status, - scb->extended_status, - scb->dcdb.sense_info[2] & 0xf, - scb->dcdb.sense_info[12], - scb->dcdb.sense_info[13]); -#endif - - /* copy SCSI status and sense data for DCDB commands */ - memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info, - sizeof(scb->scsi_cmd->sense_buffer)); - scb->scsi_cmd->result = scb->dcdb.scsi_status; - } else - scb->scsi_cmd->result = 0; + DEBUG_VAR(2, "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x", + ips_name, + ha->host_num, + scb->scsi_cmd->channel, + scb->scsi_cmd->target, + scb->scsi_cmd->lun, + scb->basic_status, + scb->extended_status, + scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0, + scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0, + scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0); + } /* default driver error */ errcode = DID_ERROR; + device_error = 0; switch (scb->basic_status & IPS_GSC_STATUS_MASK) { case IPS_CMD_TIMEOUT: @@ -3224,25 +3758,19 @@ ips_map_status(ips_scb_t *scb, ips_stat_t *sp) { break; case IPS_PHYS_DRV_ERROR: - /* - * For physical drive errors that - * are not on a logical drive should - * be DID_OK. The SCSI errcode will - * show what the real error is. - */ - if (scb->bus) - errcode = DID_OK; - switch (scb->extended_status) { case IPS_ERR_SEL_TO: - if (scb->bus) { - scb->scsi_cmd->result |= DID_TIME_OUT << 16; + if (scb->bus) + errcode = DID_NO_CONNECT; - return (0); - } break; + case IPS_ERR_OU_RUN: if ((scb->bus) && (scb->dcdb.transfer_length < scb->data_len)) { + /* Underrun - set default to no error */ + errcode = DID_OK; + + /* Restrict access to physical DASD */ if ((scb->scsi_cmd->cmnd[0] == INQUIRY) && ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) { /* underflow -- no error */ @@ -3250,23 +3778,16 @@ ips_map_status(ips_scb_t *scb, ips_stat_t *sp) { errcode = DID_TIME_OUT; break; } - - /* normal underflow Occured */ - if (scb->dcdb.transfer_length >= scb->scsi_cmd->underflow) { - scb->scsi_cmd->result |= DID_OK << 16; - - return (0); - } - } + } else + errcode = DID_ERROR; break; + case IPS_ERR_RECOVERY: /* don't fail recovered errors */ - if (scb->bus) { - scb->scsi_cmd->result |= DID_OK << 16; + if (scb->bus) + errcode = DID_OK; - return (0); - } break; case IPS_ERR_HOST_RESET: @@ -3275,11 +3796,25 @@ ips_map_status(ips_scb_t *scb, ips_stat_t *sp) { break; case IPS_ERR_CKCOND: + if (scb->bus) { + memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info, + sizeof(scb->scsi_cmd->sense_buffer)); + + device_error = 2; /* check condition */ + } + + errcode = DID_OK; + + break; + + default: + errcode = DID_ERROR; break; + } /* end switch */ } /* end switch */ - scb->scsi_cmd->result |= (errcode << 16); + scb->scsi_cmd->result = device_error | (errcode << 16); return (1); } @@ -3297,7 +3832,7 @@ static int ips_send(ips_ha_t *ha, ips_scb_t *scb, ips_scb_callback callback) { int ret; - DBG("ips_send"); + METHOD_TRACE("ips_send", 1); scb->callback = callback; @@ -3319,7 +3854,7 @@ static int ips_send_wait(ips_ha_t *ha, ips_scb_t *scb, int timeout, int intr) { int ret; - DBG("ips_send_wait"); + METHOD_TRACE("ips_send_wait", 1); ha->waitflag = TRUE; ha->cmd_in_progress = scb->cdb[0]; @@ -3347,7 +3882,7 @@ static int ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) { int ret; - DBG("ips_send_cmd"); + METHOD_TRACE("ips_send_cmd", 1); ret = IPS_SUCCESS; @@ -3576,7 +4111,7 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) { memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd, scb->scsi_cmd->cmd_len); } - return (ips_issue(ha, scb)); + return ((*ha->func.issue)(ha, scb)); } /****************************************************************************/ @@ -3588,60 +4123,57 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) { /* Check the status of commands to logical drives */ /* */ /****************************************************************************/ -static int -ips_chkstatus(ips_ha_t *ha) { +static void +ips_chkstatus(ips_ha_t *ha, IPS_STATUS *pstatus) { ips_scb_t *scb; - ips_stat_t *sp = &ha->sp; + ips_stat_t *sp; u8 basic_status; u8 ext_status; - int command_id; int errcode; - int ret; - - DBG("ips_chkstatus"); - command_id = ips_statupd(ha); + METHOD_TRACE("ips_chkstatus", 1); - if (command_id > (IPS_MAX_CMDS-1)) { - printk(KERN_NOTICE "(%s%d) invalid command id received: %d\n", - ips_name, ha->host_num, command_id); + scb = &ha->scbs[pstatus->fields.command_id]; + scb->basic_status = basic_status = pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK; + scb->extended_status = ext_status = pstatus->fields.extended_status; - return (-1); - } - - scb = &ha->scbs[command_id]; - sp->scb_addr = (u32) scb; + sp = &ha->sp; sp->residue_len = 0; - scb->basic_status = basic_status = ha->adapt->p_status_tail->basic_status & IPS_BASIC_STATUS_MASK; - scb->extended_status = ext_status = ha->adapt->p_status_tail->extended_status; + sp->scb_addr = (u32) scb; /* Remove the item from the active queue */ ips_removeq_scb(&ha->scb_activelist, scb); if (!scb->scsi_cmd) /* internal commands are handled in do_ipsintr */ - return (0); + return ; + + DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)", + ips_name, + ha->host_num, + scb->cdb[0], + scb->cmd.basic_io.command_id, + scb->bus, + scb->target_id, + scb->lun); #ifndef NO_IPS_CMDLINE if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) /* passthru - just returns the raw result */ - return (0); + return ; #endif errcode = DID_OK; - ret = 0; if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) || ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) { if (scb->bus == 0) { -#if IPS_DEBUG >= 1 if ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR) { - printk(KERN_NOTICE "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n", - ips_name, ha->host_num, - scb->cmd.basic_io.op_code, basic_status, ext_status); + DEBUG_VAR(1, "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x", + ips_name, ha->host_num, + scb->cmd.basic_io.op_code, basic_status, ext_status); } -#endif switch (scb->scsi_cmd->cmnd[0]) { case ALLOW_MEDIUM_REMOVAL: @@ -3650,7 +4182,6 @@ ips_chkstatus(ips_ha_t *ha) { case WRITE_FILEMARKS: case SPACE: errcode = DID_ERROR; - ret = 1; break; case START_STOP: @@ -3659,7 +4190,6 @@ ips_chkstatus(ips_ha_t *ha) { case TEST_UNIT_READY: if (!ips_online(ha, scb)) { errcode = DID_TIME_OUT; - ret = 1; } break; @@ -3668,7 +4198,6 @@ ips_chkstatus(ips_ha_t *ha) { ips_inquiry(ha, scb); } else { errcode = DID_TIME_OUT; - ret = 1; } break; @@ -3687,7 +4216,6 @@ ips_chkstatus(ips_ha_t *ha) { case MODE_SENSE: if (!ips_online(ha, scb) || !ips_msense(ha, scb)) { errcode = DID_ERROR; - ret = 1; } break; @@ -3696,7 +4224,6 @@ ips_chkstatus(ips_ha_t *ha) { ips_rdcap(ha, scb); else { errcode = DID_TIME_OUT; - ret = 1; } break; @@ -3706,7 +4233,6 @@ ips_chkstatus(ips_ha_t *ha) { case FORMAT_UNIT: errcode = DID_ERROR; - ret = 1; break; case SEEK_10: @@ -3718,7 +4244,6 @@ ips_chkstatus(ips_ha_t *ha) { default: errcode = DID_ERROR; - ret = 1; } /* end switch */ scb->scsi_cmd->result = errcode << 16; @@ -3728,23 +4253,17 @@ ips_chkstatus(ips_ha_t *ha) { ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) { scb->scsi_cmd->result = DID_TIME_OUT << 16; - - ret = 1; } } /* else */ } else { /* recovered error / success */ -#if IPS_DEBUG >= 1 if (scb->bus == 0) { - printk(KERN_NOTICE "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n", - ips_name, ha->host_num, - scb->cmd.basic_io.op_code, basic_status, ext_status); + DEBUG_VAR(1, "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x", + ips_name, ha->host_num, + scb->cmd.basic_io.op_code, basic_status, ext_status); } -#endif - ret = ips_map_status(scb, sp); + ips_map_status(ha, scb, sp); } /* else */ - - return (ret); } /****************************************************************************/ @@ -3758,7 +4277,7 @@ ips_chkstatus(ips_ha_t *ha) { /****************************************************************************/ static int ips_online(ips_ha_t *ha, ips_scb_t *scb) { - DBG("ips_online"); + METHOD_TRACE("ips_online", 1); if (scb->target_id >= IPS_MAX_LD) return (0); @@ -3792,7 +4311,7 @@ static int ips_inquiry(ips_ha_t *ha, ips_scb_t *scb) { IPS_INQ_DATA inq; - DBG("ips_inquiry"); + METHOD_TRACE("ips_inquiry", 1); memset(&inq, 0, sizeof(IPS_INQ_DATA)); @@ -3823,7 +4342,7 @@ static int ips_rdcap(ips_ha_t *ha, ips_scb_t *scb) { IPS_CAPACITY *cap; - DBG("ips_rdcap"); + METHOD_TRACE("ips_rdcap", 1); if (scb->scsi_cmd->bufflen < 8) return (0); @@ -3852,7 +4371,7 @@ ips_msense(ips_ha_t *ha, ips_scb_t *scb) { u32 cylinders; ips_mdata_t mdata; - DBG("ips_msense"); + METHOD_TRACE("ips_msense", 1); if (ha->enq->ulDriveSize[scb->target_id] > 0x400000 && (ha->enq->ucMiscFlag & 0x8) == 0) { @@ -3930,7 +4449,7 @@ static int ips_reqsen(ips_ha_t *ha, ips_scb_t *scb) { char *sp; - DBG("ips_reqsen"); + METHOD_TRACE("ips_reqsen", 1); sp = (char *) scb->scsi_cmd->sense_buffer; memset(sp, 0, sizeof(scb->scsi_cmd->sense_buffer)); @@ -3956,7 +4475,7 @@ static void ips_free(ips_ha_t *ha) { int i; - DBG("ips_free"); + METHOD_TRACE("ips_free", 1); if (ha) { if (ha->enq) { @@ -4004,6 +4523,14 @@ ips_free(ips_ha_t *ha) { kfree(ha->scbs); ha->scbs = NULL; } /* end if */ + + /* free memory mapped (if applicable) */ + if (ha->mem_ptr) { + iounmap(ha->ioremap_ptr); + ha->ioremap_ptr = NULL; + ha->mem_ptr = NULL; + ha->mem_addr = 0; + } } } @@ -4021,10 +4548,10 @@ ips_allocatescbs(ips_ha_t *ha) { ips_scb_t *scb_p; int i; - DBG("ips_allocatescbs"); + METHOD_TRACE("ips_allocatescbs", 1); /* Allocate memory for the CCBs */ - ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA); + ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA); memset(ha->scbs, 0, ha->max_cmds * sizeof(ips_scb_t)); @@ -4032,7 +4559,7 @@ ips_allocatescbs(ips_ha_t *ha) { scb_p = &ha->scbs[i]; /* allocate S/G list */ - scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA); + scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA); if (! scb_p->sg_list) return (0); @@ -4061,7 +4588,7 @@ static void ips_init_scb(ips_ha_t *ha, ips_scb_t *scb) { IPS_SG_LIST *sg_list; - DBG("ips_init_scb"); + METHOD_TRACE("ips_init_scb", 1); if (scb == NULL) return ; @@ -4102,7 +4629,7 @@ ips_getscb(ips_ha_t *ha) { ips_scb_t *scb; u32 cpu_flags; - DBG("ips_getscb"); + METHOD_TRACE("ips_getscb", 1); IPS_SCB_LOCK(cpu_flags); if ((scb = ha->scb_freelist) == NULL) { @@ -4136,7 +4663,7 @@ static void ips_freescb(ips_ha_t *ha, ips_scb_t *scb) { u32 cpu_flags; - DBG("ips_freescb"); + METHOD_TRACE("ips_freescb", 1); /* check to make sure this is not our "special" scb */ if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) { @@ -4149,7 +4676,7 @@ ips_freescb(ips_ha_t *ha, ips_scb_t *scb) { /****************************************************************************/ /* */ -/* Routine Name: ips_reset_adapter */ +/* Routine Name: ips_isinit_copperhead */ /* */ /* Routine Description: */ /* */ @@ -4157,21 +4684,402 @@ ips_freescb(ips_ha_t *ha, ips_scb_t *scb) { /* */ /****************************************************************************/ static int -ips_reset_adapter(ips_ha_t *ha) { - u8 Isr; - u8 Cbsp; +ips_isinit_copperhead(ips_ha_t *ha) { + u8 scpr; + u8 isr; + + METHOD_TRACE("ips_isinit_copperhead", 1); + + isr = inb(ha->io_addr + IPS_REG_HISR); + scpr = inb(ha->io_addr + IPS_REG_SCPR); + + if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0)) + return (0); + else + return (1); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_isinit_copperhead_memio */ +/* */ +/* Routine Description: */ +/* */ +/* Reset the controller */ +/* */ +/****************************************************************************/ +static int +ips_isinit_copperhead_memio(ips_ha_t *ha) { + u8 isr; + u8 scpr; + + METHOD_TRACE("ips_is_init_copperhead_memio", 1); + + isr = readb(ha->mem_ptr + IPS_REG_HISR); + scpr = readb(ha->mem_ptr + IPS_REG_SCPR); + + if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0)) + return (0); + else + return (1); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_isinit_morpheus */ +/* */ +/* Routine Description: */ +/* */ +/* Reset the controller */ +/* */ +/****************************************************************************/ +static int +ips_isinit_morpheus(ips_ha_t *ha) { + u32 post; + u32 bits; + + METHOD_TRACE("ips_is_init_morpheus", 1); + + post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); + bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR); + + if (post == 0) + return (0); + else if (bits & 0x3) + return (0); + else + return (1); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_enable_int_copperhead */ +/* */ +/* Routine Description: */ +/* Turn on interrupts */ +/* */ +/****************************************************************************/ +static void +ips_enable_int_copperhead(ips_ha_t *ha) { + METHOD_TRACE("ips_enable_int_copperhead", 1); + + outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_enable_int_copperhead_memio */ +/* */ +/* Routine Description: */ +/* Turn on interrupts */ +/* */ +/****************************************************************************/ +static void +ips_enable_int_copperhead_memio(ips_ha_t *ha) { + METHOD_TRACE("ips_enable_int_copperhead_memio", 1); + + writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_enable_int_morpheus */ +/* */ +/* Routine Description: */ +/* Turn on interrupts */ +/* */ +/****************************************************************************/ +static void +ips_enable_int_morpheus(ips_ha_t *ha) { + u32 Oimr; + + METHOD_TRACE("ips_enable_int_morpheus", 1); + + Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR); + Oimr &= ~0x08; + writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_init_copperhead */ +/* */ +/* Routine Description: */ +/* */ +/* Initialize a copperhead controller */ +/* */ +/****************************************************************************/ +static int +ips_init_copperhead(ips_ha_t *ha) { + u8 Isr; + u8 Cbsp; + u8 PostByte[IPS_MAX_POST_BYTES]; + u8 ConfigByte[IPS_MAX_CONFIG_BYTES]; + int i, j; + + METHOD_TRACE("ips_init_copperhead", 1); + + for (i = 0; i < IPS_MAX_POST_BYTES; i++) { + for (j = 0; j < 45; j++) { + Isr = inb(ha->io_addr + IPS_REG_HISR); + if (Isr & IPS_BIT_GHI) + break; + + MDELAY(IPS_ONE_SEC); + } + + if (j >= 45) + /* error occured */ + return (0); + + PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR); + outb(Isr, ha->io_addr + IPS_REG_HISR); + } + + if (PostByte[0] < IPS_GOOD_POST_STATUS) { + printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n", + ips_name, ha->host_num, PostByte[0], PostByte[1]); + + return (0); + } + + for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) { + for (j = 0; j < 240; j++) { + Isr = inb(ha->io_addr + IPS_REG_HISR); + if (Isr & IPS_BIT_GHI) + break; + + MDELAY(IPS_ONE_SEC); /* 100 msec */ + } + + if (j >= 240) + /* error occured */ + return (0); + + ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR); + outb(Isr, ha->io_addr + IPS_REG_HISR); + } + + for (i = 0; i < 240; i++) { + Cbsp = inb(ha->io_addr + IPS_REG_CBSP); + + if ((Cbsp & IPS_BIT_OP) == 0) + break; + + MDELAY(IPS_ONE_SEC); + } + + if (i >= 240) + /* reset failed */ + return (0); + + /* setup CCCR */ + outw(0x1010, ha->io_addr + IPS_REG_CCCR); + + /* Enable busmastering */ + outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR); + + if (ha->revision_id == IPS_REVID_TROMBONE64) + /* fix for anaconda64 */ + outl(0, ha->io_addr + IPS_REG_NDAE); + + /* Enable interrupts */ + outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR); + + return (1); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_init_copperhead_memio */ +/* */ +/* Routine Description: */ +/* */ +/* Initialize a copperhead controller with memory mapped I/O */ +/* */ +/****************************************************************************/ +static int +ips_init_copperhead_memio(ips_ha_t *ha) { + u8 Isr; + u8 Cbsp; u8 PostByte[IPS_MAX_POST_BYTES]; u8 ConfigByte[IPS_MAX_CONFIG_BYTES]; int i, j; + + METHOD_TRACE("ips_init_copperhead_memio", 1); + + for (i = 0; i < IPS_MAX_POST_BYTES; i++) { + for (j = 0; j < 45; j++) { + Isr = readb(ha->mem_ptr + IPS_REG_HISR); + if (Isr & IPS_BIT_GHI) + break; + + MDELAY(IPS_ONE_SEC); + } + + if (j >= 45) + /* error occured */ + return (0); + + PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR); + writeb(Isr, ha->mem_ptr + IPS_REG_HISR); + } + + if (PostByte[0] < IPS_GOOD_POST_STATUS) { + printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n", + ips_name, ha->host_num, PostByte[0], PostByte[1]); + + return (0); + } + + for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) { + for (j = 0; j < 240; j++) { + Isr = readb(ha->mem_ptr + IPS_REG_HISR); + if (Isr & IPS_BIT_GHI) + break; + + MDELAY(IPS_ONE_SEC); /* 100 msec */ + } + + if (j >= 240) + /* error occured */ + return (0); + + ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR); + writeb(Isr, ha->mem_ptr + IPS_REG_HISR); + } + + for (i = 0; i < 240; i++) { + Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP); + + if ((Cbsp & IPS_BIT_OP) == 0) + break; + + MDELAY(IPS_ONE_SEC); + } + + if (i >= 240) + /* error occured */ + return (0); + + /* setup CCCR */ + writel(0x1010, ha->mem_ptr + IPS_REG_CCCR); + + /* Enable busmastering */ + writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR); + + if (ha->revision_id == IPS_REVID_TROMBONE64) + /* fix for anaconda64 */ + writel(0, ha->mem_ptr + IPS_REG_NDAE); + + /* Enable interrupts */ + writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR); + + /* if we get here then everything went OK */ + return (1); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_init_morpheus */ +/* */ +/* Routine Description: */ +/* */ +/* Initialize a morpheus controller */ +/* */ +/****************************************************************************/ +static int +ips_init_morpheus(ips_ha_t *ha) { + u32 Post; + u32 Config; + u32 Isr; + u32 Oimr; + int i; + + METHOD_TRACE("ips_init_morpheus", 1); + + /* Wait up to 45 secs for Post */ + for (i = 0; i < 45; i++) { + Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR); + + if (Isr & IPS_BIT_I960_MSG0I) + break; + + MDELAY(IPS_ONE_SEC); + } + + if (i >= 45) { + /* error occured */ + printk(KERN_WARNING "(%s%d) timeout waiting for post.\n", + ips_name, ha->host_num); + + return (0); + } + + Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); + + /* Clear the interrupt bit */ + Isr = (u32) IPS_BIT_I960_MSG0I; + writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR); + + if (Post < (IPS_GOOD_POST_STATUS << 8)) { + printk(KERN_WARNING "(%s%d) reset controller fails (post status %x).\n", + ips_name, ha->host_num, Post); + + return (0); + } + + /* Wait up to 240 secs for config bytes */ + for (i = 0; i < 240; i++) { + Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR); + + if (Isr & IPS_BIT_I960_MSG1I) + break; + + MDELAY(IPS_ONE_SEC); /* 100 msec */ + } + + if (i >= 240) { + /* error occured */ + printk(KERN_WARNING "(%s%d) timeout waiting for config.\n", + ips_name, ha->host_num); + + return (0); + } + + Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1); + + /* Clear interrupt bit */ + Isr = (u32) IPS_BIT_I960_MSG1I; + writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR); + + /* Turn on the interrupts */ + Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR); + Oimr &= ~0x8; + writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR); + + /* if we get here then everything went OK */ + return (1); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_reset_copperhead */ +/* */ +/* Routine Description: */ +/* */ +/* Reset the controller */ +/* */ +/****************************************************************************/ +static int +ips_reset_copperhead(ips_ha_t *ha) { int reset_counter; u32 cpu_flags; - DBG("ips_reset_adapter"); + METHOD_TRACE("ips_reset_copperhead", 1); -#if IPS_DEBUG >= 1 - printk(KERN_WARNING "ips_reset_adapter: io addr: %x, irq: %d\n", - ha->io_addr, ha->irq); -#endif + DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d", + ips_name, ha->host_num, ha->io_addr, ha->irq); IPS_HA_LOCK(cpu_flags); @@ -4185,109 +5093,107 @@ ips_reset_adapter(ips_ha_t *ha) { outb(0, ha->io_addr + IPS_REG_SCPR); MDELAY(IPS_ONE_SEC); - for (i = 0; i < IPS_MAX_POST_BYTES; i++) { - for (j = 0; j < 45; j++) { - Isr = inb(ha->io_addr + IPS_REG_HISR); - if (Isr & IPS_BIT_GHI) - break; - - MDELAY(IPS_ONE_SEC); - } - - if (j >= 45) { - /* error occured */ - if (reset_counter < 2) - continue; - else { - /* reset failed */ - IPS_HA_UNLOCK(cpu_flags); - - return (0); - } - } + if ((*ha->func.init)(ha)) + break; + else if (reset_counter >= 2) { + IPS_HA_UNLOCK(cpu_flags); - PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR); - outb(Isr, ha->io_addr + IPS_REG_HISR); + return (0); } + } - if (PostByte[0] < IPS_GOOD_POST_STATUS) { - printk("(%s%d) reset controller fails (post status %x %x).\n", - ips_name, ha->host_num, PostByte[0], PostByte[1]); + IPS_HA_UNLOCK(cpu_flags); - IPS_HA_UNLOCK(cpu_flags); + return (1); +} - return (0); - } +/****************************************************************************/ +/* */ +/* Routine Name: ips_reset_copperhead_memio */ +/* */ +/* Routine Description: */ +/* */ +/* Reset the controller */ +/* */ +/****************************************************************************/ +static int +ips_reset_copperhead_memio(ips_ha_t *ha) { + int reset_counter; + u32 cpu_flags; - for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) { - for (j = 0; j < 240; j++) { - Isr = inb(ha->io_addr + IPS_REG_HISR); - if (Isr & IPS_BIT_GHI) - break; + METHOD_TRACE("ips_reset_copperhead_memio", 1); - MDELAY(IPS_ONE_SEC); /* 100 msec */ - } + DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d", + ips_name, ha->host_num, ha->mem_addr, ha->irq); - if (j >= 240) { - /* error occured */ - if (reset_counter < 2) - continue; - else { - /* reset failed */ - IPS_HA_UNLOCK(cpu_flags); + IPS_HA_LOCK(cpu_flags); - return (0); - } - } + reset_counter = 0; - ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR); - outb(Isr, ha->io_addr + IPS_REG_HISR); - } + while (reset_counter < 2) { + reset_counter++; - if (ConfigByte[0] == 0 && ConfigByte[1] == 2) { - printk("(%s%d) reset controller fails (status %x %x).\n", - ips_name, ha->host_num, ConfigByte[0], ConfigByte[1]); + writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR); + MDELAY(IPS_ONE_SEC); + writeb(0, ha->mem_ptr + IPS_REG_SCPR); + MDELAY(IPS_ONE_SEC); + if ((*ha->func.init)(ha)) + break; + else if (reset_counter >= 2) { IPS_HA_UNLOCK(cpu_flags); return (0); } + } - for (i = 0; i < 240; i++) { - Cbsp = inb(ha->io_addr + IPS_REG_CBSP); + IPS_HA_UNLOCK(cpu_flags); - if ((Cbsp & IPS_BIT_OP) == 0) - break; + return (1); +} - MDELAY(IPS_ONE_SEC); - } +/****************************************************************************/ +/* */ +/* Routine Name: ips_reset_morpheus */ +/* */ +/* Routine Description: */ +/* */ +/* Reset the controller */ +/* */ +/****************************************************************************/ +static int +ips_reset_morpheus(ips_ha_t *ha) { + int reset_counter; + u8 junk; + u32 cpu_flags; - if (i >= 240) { - /* error occured */ - if (reset_counter < 2) - continue; - else { - /* reset failed */ - IPS_HA_UNLOCK(cpu_flags); + METHOD_TRACE("ips_reset_morpheus", 1); - return (0); - } - } + DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d", + ips_name, ha->host_num, ha->mem_addr, ha->irq); - /* setup CCCR */ - outw(0x1010, ha->io_addr + IPS_REG_CCCR); + IPS_HA_LOCK(cpu_flags); - /* Enable busmastering */ - outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR); + reset_counter = 0; - /* setup status queues */ - ips_statinit(ha); + while (reset_counter < 2) { + reset_counter++; - /* Enable interrupts */ - outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR); + writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR); - /* if we get here then everything went OK */ - break; + /* Delay for 300 msec */ + MDELAY(300 * IPS_ONE_MSEC); + + /* Do a PCI config read to wait for adapter */ + pci_read_config_byte(ha->pcidev, 4, &junk); + + if ((*ha->func.init)(ha)) + break; + else if (reset_counter >= 2) { + IPS_HA_UNLOCK(cpu_flags); + + return (0); + } } IPS_HA_UNLOCK(cpu_flags); @@ -4308,7 +5214,7 @@ static void ips_statinit(ips_ha_t *ha) { u32 phys_status_start; - DBG("ips_statinit"); + METHOD_TRACE("ips_statinit", 1); ha->adapt->p_status_start = ha->adapt->status; ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS; @@ -4326,18 +5232,45 @@ ips_statinit(ips_ha_t *ha) { /****************************************************************************/ /* */ -/* Routine Name: ips_statupd */ +/* Routine Name: ips_statinit_memio */ /* */ /* Routine Description: */ /* */ -/* Remove an element from the status queue */ +/* Initialize the status queues on the controller */ /* */ /****************************************************************************/ -static int -ips_statupd(ips_ha_t *ha) { - int command_id; +static void +ips_statinit_memio(ips_ha_t *ha) { + u32 phys_status_start; + + METHOD_TRACE("ips_statinit_memio", 1); - DBG("ips_statupd"); + ha->adapt->p_status_start = ha->adapt->status; + ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS; + ha->adapt->p_status_tail = ha->adapt->status; + + phys_status_start = VIRT_TO_BUS(ha->adapt->status); + writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR); + writel(phys_status_start + IPS_STATUS_Q_SIZE, ha->mem_ptr + IPS_REG_SQER); + writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR); + writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR); + + ha->adapt->hw_status_start = phys_status_start; + ha->adapt->hw_status_tail = phys_status_start; +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_statupd_copperhead */ +/* */ +/* Routine Description: */ +/* */ +/* Remove an element from the status queue */ +/* */ +/****************************************************************************/ +static u32 +ips_statupd_copperhead(ips_ha_t *ha) { + METHOD_TRACE("ips_statupd_copperhead", 1); if (ha->adapt->p_status_tail != ha->adapt->p_status_end) { ha->adapt->p_status_tail++; @@ -4349,47 +5282,87 @@ ips_statupd(ips_ha_t *ha) { outl(ha->adapt->hw_status_tail, ha->io_addr + IPS_REG_SQTR); - command_id = ha->adapt->p_status_tail->command_id; + return (ha->adapt->p_status_tail->value); +} - return (command_id); +/****************************************************************************/ +/* */ +/* Routine Name: ips_statupd_copperhead_memio */ +/* */ +/* Routine Description: */ +/* */ +/* Remove an element from the status queue */ +/* */ +/****************************************************************************/ +static u32 +ips_statupd_copperhead_memio(ips_ha_t *ha) { + METHOD_TRACE("ips_statupd_copperhead_memio", 1); + + if (ha->adapt->p_status_tail != ha->adapt->p_status_end) { + ha->adapt->p_status_tail++; + ha->adapt->hw_status_tail += sizeof(IPS_STATUS); + } else { + ha->adapt->p_status_tail = ha->adapt->p_status_start; + ha->adapt->hw_status_tail = ha->adapt->hw_status_start; + } + + writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR); + + return (ha->adapt->p_status_tail->value); } /****************************************************************************/ /* */ -/* Routine Name: ips_issue */ +/* Routine Name: ips_statupd_morpheus */ /* */ /* Routine Description: */ /* */ -/* Send a command down to the controller */ +/* Remove an element from the status queue */ /* */ -/* ASSUMED to be called from within a lock */ +/****************************************************************************/ +static u32 +ips_statupd_morpheus(ips_ha_t *ha) { + u32 val; + + METHOD_TRACE("ips_statupd_morpheus", 1); + + val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ); + + return (val); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_issue_copperhead */ +/* */ +/* Routine Description: */ +/* */ +/* Send a command down to the controller */ /* */ /****************************************************************************/ static int -ips_issue(ips_ha_t *ha, ips_scb_t *scb) { +ips_issue_copperhead(ips_ha_t *ha, ips_scb_t *scb) { u32 TimeOut; u16 val; u32 cpu_flags; - DBG("ips_issue"); - -#if IPS_DEBUG >= 10 - if (scb->scsi_cmd) - printk(KERN_NOTICE "%s: ips_issue: cmd 0x%X id %d (%d %d %d)\n", - ips_name, - scb->cdb[0], - scb->cmd.basic_io.command_id, - scb->bus, - scb->target_id, - scb->lun); - else - printk(KERN_NOTICE "%s: ips_issue: logical cmd id %d\n", - ips_name, - scb->cmd.basic_io.command_id); -#if IPS_DEBUG >= 11 - MDELAY(IPS_ONE_SEC); -#endif -#endif + METHOD_TRACE("ips_issue_copperhead", 1); + + if (scb->scsi_cmd) { + DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", + ips_name, + ha->host_num, + scb->cdb[0], + scb->cmd.basic_io.command_id, + scb->bus, + scb->target_id, + scb->lun); + } else { + DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d", + ips_name, + ha->host_num, + scb->cmd.basic_io.command_id); + } IPS_HA_LOCK(cpu_flags); @@ -4416,14 +5389,189 @@ ips_issue(ips_ha_t *ha, ips_scb_t *scb) { outl(scb->scb_busaddr, ha->io_addr + IPS_REG_CCSAR); outw(IPS_BIT_START_CMD, ha->io_addr + IPS_REG_CCCR); - IPS_HA_UNLOCK(cpu_flags); + IPS_HA_UNLOCK(cpu_flags); + + return (IPS_SUCCESS); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_issue_copperhead_memio */ +/* */ +/* Routine Description: */ +/* */ +/* Send a command down to the controller */ +/* */ +/****************************************************************************/ +static int +ips_issue_copperhead_memio(ips_ha_t *ha, ips_scb_t *scb) { + u32 TimeOut; + u32 val; + u32 cpu_flags; + + METHOD_TRACE("ips_issue_copperhead_memio", 1); + + if (scb->scsi_cmd) { + DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", + ips_name, + ha->host_num, + scb->cdb[0], + scb->cmd.basic_io.command_id, + scb->bus, + scb->target_id, + scb->lun); + } else { + DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d", + ips_name, + ha->host_num, + scb->cmd.basic_io.command_id); + } + + IPS_HA_LOCK(cpu_flags); + + TimeOut = 0; + + while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) { + UDELAY(1000); + + if (++TimeOut >= IPS_SEM_TIMEOUT) { + if (!(val & IPS_BIT_START_STOP)) + break; + + printk(KERN_WARNING "(%s%d) ips_issue val [0x%x].\n", + ips_name, ha->host_num, val); + printk(KERN_WARNING "(%s%d) ips_issue semaphore chk timeout.\n", + ips_name, ha->host_num); + + IPS_HA_UNLOCK(cpu_flags); + + return (IPS_FAILURE); + } /* end if */ + } /* end while */ + + writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR); + writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR); + + IPS_HA_UNLOCK(cpu_flags); + + return (IPS_SUCCESS); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_issue_i2o */ +/* */ +/* Routine Description: */ +/* */ +/* Send a command down to the controller */ +/* */ +/****************************************************************************/ +static int +ips_issue_i2o(ips_ha_t *ha, ips_scb_t *scb) { + u32 cpu_flags; + + METHOD_TRACE("ips_issue_i2o", 1); + + if (scb->scsi_cmd) { + DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", + ips_name, + ha->host_num, + scb->cdb[0], + scb->cmd.basic_io.command_id, + scb->bus, + scb->target_id, + scb->lun); + } else { + DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d", + ips_name, + ha->host_num, + scb->cmd.basic_io.command_id); + } + + IPS_HA_LOCK(cpu_flags); + + outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ); + + IPS_HA_UNLOCK(cpu_flags); + + return (IPS_SUCCESS); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_issue_i2o_memio */ +/* */ +/* Routine Description: */ +/* */ +/* Send a command down to the controller */ +/* */ +/****************************************************************************/ +static int +ips_issue_i2o_memio(ips_ha_t *ha, ips_scb_t *scb) { + u32 cpu_flags; + + METHOD_TRACE("ips_issue_i2o_memio", 1); + + if (scb->scsi_cmd) { + DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)", + ips_name, + ha->host_num, + scb->cdb[0], + scb->cmd.basic_io.command_id, + scb->bus, + scb->target_id, + scb->lun); + } else { + DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d", + ips_name, + ha->host_num, + scb->cmd.basic_io.command_id); + } + + IPS_HA_LOCK(cpu_flags); + + writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ); + + IPS_HA_UNLOCK(cpu_flags); + + return (IPS_SUCCESS); +} + +/****************************************************************************/ +/* */ +/* Routine Name: ips_isintr_copperhead */ +/* */ +/* Routine Description: */ +/* */ +/* Test to see if an interrupt is for us */ +/* */ +/****************************************************************************/ +static int +ips_isintr_copperhead(ips_ha_t *ha) { + u8 Isr; + + METHOD_TRACE("ips_isintr_copperhead", 2); + + Isr = inb(ha->io_addr + IPS_REG_HISR); + + if (Isr == 0xFF) + /* ?!?! Nothing really there */ + return (0); + + if (Isr & IPS_BIT_SCE) + return (1); + else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) { + /* status queue overflow or GHI */ + /* just clear the interrupt */ + outb(Isr, ha->io_addr + IPS_REG_HISR); + } - return (IPS_SUCCESS); + return (0); } /****************************************************************************/ /* */ -/* Routine Name: ips_isintr */ +/* Routine Name: ips_isintr_copperhead_memio */ /* */ /* Routine Description: */ /* */ @@ -4431,12 +5579,12 @@ ips_issue(ips_ha_t *ha, ips_scb_t *scb) { /* */ /****************************************************************************/ static int -ips_isintr(ips_ha_t *ha) { +ips_isintr_copperhead_memio(ips_ha_t *ha) { u8 Isr; - DBG("ips_isintr"); + METHOD_TRACE("ips_isintr_memio", 2); - Isr = inb(ha->io_addr + IPS_REG_HISR); + Isr = readb(ha->mem_ptr + IPS_REG_HISR); if (Isr == 0xFF) /* ?!?! Nothing really there */ @@ -4447,12 +5595,35 @@ ips_isintr(ips_ha_t *ha) { else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) { /* status queue overflow or GHI */ /* just clear the interrupt */ - outb(Isr, ha->io_addr + IPS_REG_HISR); + writeb(Isr, ha->mem_ptr + IPS_REG_HISR); } return (0); } +/****************************************************************************/ +/* */ +/* Routine Name: ips_isintr_morpheus */ +/* */ +/* Routine Description: */ +/* */ +/* Test to see if an interrupt is for us */ +/* */ +/****************************************************************************/ +static int +ips_isintr_morpheus(ips_ha_t *ha) { + u32 Isr; + + METHOD_TRACE("ips_isintr_morpheus", 2); + + Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR); + + if (Isr & IPS_BIT_I2O_OPQI) + return (1); + else + return (0); +} + /****************************************************************************/ /* */ /* Routine Name: ips_wait */ @@ -4467,7 +5638,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) { int ret; u8 done; - DBG("ips_wait"); + METHOD_TRACE("ips_wait", 1); ret = IPS_FAILURE; done = FALSE; @@ -4502,7 +5673,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) { while (test_and_set_bit(IPS_IN_INTR, &ha->flags)) UDELAY(1000); - ips_intr(ha); + (*ha->func.intr)(ha); clear_bit(IPS_IN_INTR, &ha->flags); } else if (intr == IPS_INTR_HAL) { @@ -4528,7 +5699,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) { while (test_and_set_bit(IPS_IN_INTR, &ha->flags)) UDELAY(1000); - ips_intr(ha); + (*ha->func.intr)(ha); clear_bit(IPS_IN_INTR, &ha->flags); @@ -4553,7 +5724,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) { /****************************************************************************/ static int ips_write_driver_status(ips_ha_t *ha, int intr) { - DBG("ips_write_driver_status"); + METHOD_TRACE("ips_write_driver_status", 1); if (!ips_readwrite_page5(ha, FALSE, intr)) { printk(KERN_WARNING "(%s%d) unable to read NVRAM page 5.\n", @@ -4565,21 +5736,18 @@ ips_write_driver_status(ips_ha_t *ha, int intr) { /* check to make sure the page has a valid */ /* signature */ if (ha->nvram->signature != IPS_NVRAM_P5_SIG) { -#if IPS_DEBUG >= 1 - printk("(%s%d) NVRAM page 5 has an invalid signature: %X.\n", - ips_name, ha->host_num, ha->nvram->signature); -#endif + DEBUG_VAR(1, "(%s%d) NVRAM page 5 has an invalid signature: %X.", + ips_name, ha->host_num, ha->nvram->signature); + return (1); } -#if IPS_DEBUG >= 2 - printk("(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.\n", - ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot, - ha->nvram->bios_high[0], ha->nvram->bios_high[1], - ha->nvram->bios_high[2], ha->nvram->bios_high[3], - ha->nvram->bios_low[0], ha->nvram->bios_low[1], - ha->nvram->bios_low[2], ha->nvram->bios_low[3]); -#endif + DEBUG_VAR(2, "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.", + ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot, + ha->nvram->bios_high[0], ha->nvram->bios_high[1], + ha->nvram->bios_high[2], ha->nvram->bios_high[3], + ha->nvram->bios_low[0], ha->nvram->bios_low[1], + ha->nvram->bios_low[2], ha->nvram->bios_low[3]); /* save controller type */ ha->ad_type = ha->nvram->adapter_type; @@ -4614,7 +5782,7 @@ ips_read_adapter_status(ips_ha_t *ha, int intr) { ips_scb_t *scb; int ret; - DBG("ips_read_adapter_status"); + METHOD_TRACE("ips_read_adapter_status", 1); scb = &ha->scbs[ha->max_cmds-1]; @@ -4633,8 +5801,9 @@ ips_read_adapter_status(ips_ha_t *ha, int intr) { scb->cmd.basic_io.reserved = 0; /* send command */ - ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr); - if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM)) + if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) || + (ret == IPS_SUCCESS_IMM) || + ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); return (1); @@ -4654,7 +5823,7 @@ ips_read_subsystem_parameters(ips_ha_t *ha, int intr) { ips_scb_t *scb; int ret; - DBG("ips_read_subsystem_parameters"); + METHOD_TRACE("ips_read_subsystem_parameters", 1); scb = &ha->scbs[ha->max_cmds-1]; @@ -4673,8 +5842,9 @@ ips_read_subsystem_parameters(ips_ha_t *ha, int intr) { scb->cmd.basic_io.reserved = 0; /* send command */ - ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr); - if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM)) + if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) || + (ret == IPS_SUCCESS_IMM) || + ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); return (1); @@ -4695,7 +5865,7 @@ ips_read_config(ips_ha_t *ha, int intr) { int i; int ret; - DBG("ips_read_config"); + METHOD_TRACE("ips_read_config", 1); /* set defaults for initiator IDs */ ha->conf->init_id[0] = IPS_ADAPTER_ID; @@ -4745,7 +5915,7 @@ ips_readwrite_page5(ips_ha_t *ha, int write, int intr) { ips_scb_t *scb; int ret; - DBG("ips_readwrite_page5"); + METHOD_TRACE("ips_readwrite_page5", 1); scb = &ha->scbs[ha->max_cmds-1]; @@ -4789,7 +5959,7 @@ ips_clear_adapter(ips_ha_t *ha, int intr) { ips_scb_t *scb; int ret; - DBG("ips_clear_adapter"); + METHOD_TRACE("ips_clear_adapter", 1); scb = &ha->scbs[ha->max_cmds-1]; @@ -4807,8 +5977,9 @@ ips_clear_adapter(ips_ha_t *ha, int intr) { scb->cmd.config_sync.reserved3 = 0; /* issue command */ - ret = ips_send_wait(ha, scb, ips_reset_timeout, intr); - if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM)) + if (((ret = ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE) || + (ret == IPS_SUCCESS_IMM) || + ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); /* send unlock stripe command */ @@ -4826,8 +5997,9 @@ ips_clear_adapter(ips_ha_t *ha, int intr) { scb->cmd.unlock_stripe.reserved3 = 0; /* issue command */ - ret = ips_send_wait(ha, scb, ips_reset_timeout, intr); - if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM)) + if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) || + (ret == IPS_SUCCESS_IMM) || + ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); return (1); @@ -4846,7 +6018,7 @@ static void ips_ffdc_reset(ips_ha_t *ha, int intr) { ips_scb_t *scb; - DBG("ips_ffdc_reset"); + METHOD_TRACE("ips_ffdc_reset", 1); scb = &ha->scbs[ha->max_cmds-1]; @@ -4879,12 +6051,10 @@ static void ips_ffdc_time(ips_ha_t *ha, int intr) { ips_scb_t *scb; - DBG("ips_ffdc_time"); + METHOD_TRACE("ips_ffdc_time", 1); -#if IPS_DEBUG >= 1 - printk(KERN_NOTICE "(%s%d) Sending time update.\n", - ips_name, ha->host_num); -#endif + DEBUG_VAR(1, "(%s%d) Sending time update.", + ips_name, ha->host_num); scb = &ha->scbs[ha->max_cmds-1]; @@ -4933,6 +6103,8 @@ ips_fix_ffdc_time(ips_ha_t *ha, ips_scb_t *scb, time_t current_time) { {30, 30}, {31, 31} }; + METHOD_TRACE("ips_fix_ffdc_time", 1); + days = current_time / IPS_SECS_DAY; rem = current_time % IPS_SECS_DAY; @@ -4981,6 +6153,8 @@ ips_erase_bios(ips_ha_t *ha) { int timeout; u8 status; + METHOD_TRACE("ips_erase_bios", 1); + /* Clear the status register */ outl(0, ha->io_addr + IPS_REG_FLAP); if (ha->revision_id == IPS_REVID_TROMBONE64) @@ -5075,6 +6249,115 @@ ips_erase_bios(ips_ha_t *ha) { return (0); } +/****************************************************************************/ +/* */ +/* Routine Name: ips_erase_bios_memio */ +/* */ +/* Routine Description: */ +/* Erase the BIOS on the adapter */ +/* */ +/****************************************************************************/ +static int +ips_erase_bios_memio(ips_ha_t *ha) { + int timeout; + u8 status; + + METHOD_TRACE("ips_erase_bios_memio", 1); + + /* Clear the status register */ + writel(0, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + writeb(0x50, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + /* Erase Setup */ + writeb(0x20, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + /* Erase Confirm */ + writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + /* Erase Status */ + writeb(0x70, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + timeout = 80000; /* 80 seconds */ + + while (timeout > 0) { + if (ha->revision_id == IPS_REVID_TROMBONE64) { + writel(0, ha->mem_ptr + IPS_REG_FLAP); + UDELAY(5); /* 5 us */ + } + + status = readb(ha->mem_ptr + IPS_REG_FLDP); + + if (status & 0x80) + break; + + MDELAY(1); + timeout--; + } + + /* check for timeout */ + if (timeout <= 0) { + /* timeout */ + + /* try to suspend the erase */ + writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + /* wait for 10 seconds */ + timeout = 10000; + while (timeout > 0) { + if (ha->revision_id == IPS_REVID_TROMBONE64) { + writel(0, ha->mem_ptr + IPS_REG_FLAP); + UDELAY(5); /* 5 us */ + } + + status = readb(ha->mem_ptr + IPS_REG_FLDP); + + if (status & 0xC0) + break; + + MDELAY(1); + timeout--; + } + + return (1); + } + + /* check for valid VPP */ + if (status & 0x08) + /* VPP failure */ + return (1); + + /* check for succesful flash */ + if (status & 0x30) + /* sequence error */ + return (1); + + /* Otherwise, we were successful */ + /* clear status */ + writeb(0x50, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + /* enable reads */ + writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + return (0); +} + /****************************************************************************/ /* */ /* Routine Name: ips_program_bios */ @@ -5089,6 +6372,8 @@ ips_program_bios(ips_ha_t *ha, char *buffer, int buffersize) { int timeout; u8 status; + METHOD_TRACE("ips_program_bios", 1); + for (i = 0; i < buffersize; i++) { /* write a byte */ outl(i, ha->io_addr + IPS_REG_FLAP); @@ -5160,6 +6445,93 @@ ips_program_bios(ips_ha_t *ha, char *buffer, int buffersize) { return (0); } +/****************************************************************************/ +/* */ +/* Routine Name: ips_program_bios_memio */ +/* */ +/* Routine Description: */ +/* Program the BIOS on the adapter */ +/* */ +/****************************************************************************/ +static int +ips_program_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) { + int i; + int timeout; + u8 status; + + METHOD_TRACE("ips_program_bios_memio", 1); + + for (i = 0; i < buffersize; i++) { + /* write a byte */ + writel(i, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + writeb(0x40, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + /* wait up to one second */ + timeout = 1000; + while (timeout > 0) { + if (ha->revision_id == IPS_REVID_TROMBONE64) { + writel(0, ha->mem_ptr + IPS_REG_FLAP); + UDELAY(5); /* 5 us */ + } + + status = readb(ha->mem_ptr + IPS_REG_FLDP); + + if (status & 0x80) + break; + + MDELAY(1); + timeout--; + } + + if (timeout == 0) { + /* timeout error */ + writel(0, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + return (1); + } + + /* check the status */ + if (status & 0x18) { + /* programming error */ + writel(0, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + return (1); + } + } /* end for */ + + /* Enable reading */ + writel(0, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + return (0); +} + /****************************************************************************/ /* */ /* Routine Name: ips_verify_bios */ @@ -5173,6 +6545,8 @@ ips_verify_bios(ips_ha_t *ha, char *buffer, int buffersize) { u8 checksum; int i; + METHOD_TRACE("ips_verify_bios", 1); + /* test 1st byte */ outl(0, ha->io_addr + IPS_REG_FLAP); if (ha->revision_id == IPS_REVID_TROMBONE64) @@ -5205,6 +6579,53 @@ ips_verify_bios(ips_ha_t *ha, char *buffer, int buffersize) { return (0); } +/****************************************************************************/ +/* */ +/* Routine Name: ips_verify_bios_memio */ +/* */ +/* Routine Description: */ +/* Verify the BIOS on the adapter */ +/* */ +/****************************************************************************/ +static int +ips_verify_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) { + u8 checksum; + int i; + + METHOD_TRACE("ips_verify_bios_memio", 1); + + /* test 1st byte */ + writel(0, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55) + return (1); + + writel(1, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA) + return (1); + + checksum = 0xff; + for (i = 2; i < buffersize; i++) { + + writel(i, ha->mem_ptr + IPS_REG_FLAP); + if (ha->revision_id == IPS_REVID_TROMBONE64) + UDELAY(5); /* 5 us */ + + checksum = (u8) checksum + readb(ha->mem_ptr + IPS_REG_FLDP); + } + + if (checksum != 0) + /* failure */ + return (1); + else + /* success */ + return (0); +} + static Scsi_Host_Template driver_template = IPS; #include "scsi_module.c" diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index bf738e91fb51..5aaa15d0c711 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -1,5 +1,5 @@ /*****************************************************************************/ -/* ips.h -- driver for the IBM ServeRAID adapter */ +/* ips.h -- driver for the IBM ServeRAID controller */ /* */ /* Written By: Keith Mitchell, IBM Corporation */ /* */ @@ -47,14 +47,13 @@ #ifndef _IPS_H_ #define _IPS_H_ + #include #include #include /* Prototypes */ extern int ips_detect(Scsi_Host_Template *); extern int ips_release(struct Scsi_Host *); - extern int ips_abort(Scsi_Cmnd *); - extern int ips_reset(Scsi_Cmnd *, unsigned int); extern int ips_eh_abort(Scsi_Cmnd *); extern int ips_eh_reset(Scsi_Cmnd *); extern int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *)); @@ -71,7 +70,21 @@ #define IPS_HA(x) ((ips_ha_t *) x->hostdata) #define IPS_COMMAND_ID(ha, scb) (int) (scb - ha->scbs) - + #define IPS_IS_TROMBONE(ha) (((ha->device_id == IPS_COPPERHEAD_DEVICEID) && \ + (ha->revision_id >= IPS_REVID_TROMBONE32) && \ + (ha->revision_id <= IPS_REVID_TROMBONE64)) ? 1 : 0) + #define IPS_IS_CLARINET(ha) (((ha->device_id == IPS_COPPERHEAD_DEVICEID) && \ + (ha->revision_id >= IPS_REVID_CLARINETP1) && \ + (ha->revision_id <= IPS_REVID_CLARINETP3)) ? 1 : 0) + #define IPS_IS_MORPHEUS(ha) (ha->device_id == IPS_MORPHEUS_DEVICEID) + #define IPS_USE_I2O_DELIVER(ha) ((IPS_IS_MORPHEUS(ha) || \ + (IPS_IS_TROMBONE(ha) && \ + (ips_force_i2o))) ? 1 : 0) + #define IPS_USE_I2O_STATUS(ha) (IPS_IS_MORPHEUS(ha)) + #define IPS_USE_MEMIO(ha) ((IPS_IS_MORPHEUS(ha) || \ + ((IPS_IS_TROMBONE(ha) || IPS_IS_CLARINET(ha)) && \ + (ips_force_memio))) ? 1 : 0) + #ifndef VIRT_TO_BUS #define VIRT_TO_BUS(x) (unsigned int)virt_to_bus((void *) x) #endif @@ -79,7 +92,7 @@ #ifndef UDELAY #define UDELAY udelay #endif - + #ifndef MDELAY #define MDELAY mdelay #endif @@ -87,15 +100,15 @@ #ifndef verify_area_20 #define verify_area_20(t,a,sz) (0) /* success */ #endif - + #ifndef PUT_USER #define PUT_USER put_user #endif - + #ifndef __PUT_USER #define __PUT_USER __put_user #endif - + #ifndef GET_USER #define GET_USER get_user #endif @@ -129,6 +142,14 @@ #define IPS_REG_CBSP 0x07 /* CBSP register */ #define IPS_REG_FLAP 0x18 /* Flash address port */ #define IPS_REG_FLDP 0x1C /* Flash data port */ + #define IPS_REG_NDAE 0x38 /* Anaconda 64 NDAE Register */ + #define IPS_REG_I2O_INMSGQ 0x40 /* I2O Inbound Message Queue */ + #define IPS_REG_I2O_OUTMSGQ 0x44 /* I2O Outbound Message Queue */ + #define IPS_REG_I2O_HIR 0x30 /* I2O Interrupt Status */ + #define IPS_REG_I960_IDR 0x20 /* i960 Inbound Doorbell */ + #define IPS_REG_I960_MSG0 0x18 /* i960 Outbound Reg 0 */ + #define IPS_REG_I960_MSG1 0x1C /* i960 Outbound Reg 1 */ + #define IPS_REG_I960_OIMR 0x34 /* i960 Oubound Int Mask Reg */ /* * Adapter register bit equates @@ -144,6 +165,9 @@ #define IPS_BIT_EBM 0x02 /* SCPR Enable Bus Master */ #define IPS_BIT_EI 0x80 /* HISR Enable Interrupts */ #define IPS_BIT_OP 0x01 /* OP bit in CBSP */ + #define IPS_BIT_I2O_OPQI 0x08 /* General Host Interrupt */ + #define IPS_BIT_I960_MSG0I 0x01 /* Message Register 0 Interrupt*/ + #define IPS_BIT_I960_MSG1I 0x02 /* Message Register 1 Interrupt*/ /* * Adapter Command ID Equates @@ -194,13 +218,15 @@ #define IPS_INTR_HAL 2 #define IPS_ADAPTER_ID 0xF #define IPS_VENDORID 0x1014 - #define IPS_DEVICEID 0x002E + #define IPS_COPPERHEAD_DEVICEID 0x002E + #define IPS_MORPHEUS_DEVICEID 0x01BD #define IPS_IOCTL_SIZE 8192 #define IPS_STATUS_SIZE 4 #define IPS_STATUS_Q_SIZE (IPS_MAX_CMDS+1) * IPS_STATUS_SIZE + #define IPS_MEMMAP_SIZE 128 #define IPS_ONE_MSEC 1 #define IPS_ONE_SEC 1000 - + /* * Geometry Settings */ @@ -328,10 +354,12 @@ /* * Scsi_Host Template */ +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) #define IPS { \ next : NULL, \ module : NULL, \ proc_info : NULL, \ + proc_dir : NULL, \ name : NULL, \ detect : ips_detect, \ release : ips_release, \ @@ -343,8 +371,8 @@ eh_device_reset_handler : NULL, \ eh_bus_reset_handler : NULL, \ eh_host_reset_handler : ips_eh_reset, \ - abort : ips_abort, \ - reset : ips_reset, \ + abort : NULL, \ + reset : NULL, \ slave_attach : NULL, \ bios_param : ips_biosparam, \ can_queue : 0, \ @@ -355,7 +383,37 @@ unchecked_isa_dma : 0, \ use_clustering : ENABLE_CLUSTERING, \ use_new_eh_code : 1 \ - } +} +#else + #define IPS { \ + next : NULL, \ + module : NULL, \ + proc_info : NULL, \ + name : NULL, \ + detect : ips_detect, \ + release : ips_release, \ + info : ips_info, \ + command : NULL, \ + queuecommand : ips_queue, \ + eh_strategy_handler : NULL, \ + eh_abort_handler : ips_eh_abort, \ + eh_device_reset_handler : NULL, \ + eh_bus_reset_handler : NULL, \ + eh_host_reset_handler : ips_eh_reset, \ + abort : NULL, \ + reset : NULL, \ + slave_attach : NULL, \ + bios_param : ips_biosparam, \ + can_queue : 0, \ + this_id: -1, \ + sg_tablesize : IPS_MAX_SG, \ + cmd_per_lun: 16, \ + present : 0, \ + unchecked_isa_dma : 0, \ + use_clustering : ENABLE_CLUSTERING, \ + use_new_eh_code : 1 \ +} +#endif /* * IBM PCI Raid Command Formats @@ -523,11 +581,15 @@ typedef struct { u8 reserved2[3]; } IPS_DCDB_TABLE, *PIPS_DCDB_TABLE; -typedef struct { - volatile u8 reserved; - volatile u8 command_id; - volatile u8 basic_status; - volatile u8 extended_status; +typedef union { + struct { + volatile u8 reserved; + volatile u8 command_id; + volatile u8 basic_status; + volatile u8 extended_status; + } fields; + + volatile u32 value; } IPS_STATUS, *PIPS_STATUS; typedef struct { @@ -602,7 +664,7 @@ typedef struct { u8 ucCompression; u8 ucNvramType; u32 ulNvramSize; -} IPS_HARDWARE, *PIPS_HARDWARE; +} IPS_HARDWARE, *PIPS_HARDWARE; typedef struct { u8 ucLogDriveCount; @@ -769,8 +831,15 @@ typedef struct _IPS_INFOSTR { int length; int offset; int pos; + int localpos; } IPS_INFOSTR; +typedef struct { + char *option_name; + int *option_flag; + int option_value; +} IPS_OPTION; + /* * Status Info */ @@ -815,6 +884,24 @@ typedef struct ips_copp_queue { spinlock_t lock; } ips_copp_queue_t; +/* forward decl for host structure */ +struct ips_ha; + +typedef struct { + int (*reset)(struct ips_ha *); + int (*issue)(struct ips_ha *, struct ips_scb *); + int (*isinit)(struct ips_ha *); + int (*isintr)(struct ips_ha *); + int (*init)(struct ips_ha *); + int (*erasebios)(struct ips_ha *); + int (*programbios)(struct ips_ha *, char *, int); + int (*verifybios)(struct ips_ha *, char *, int); + u32 (*statupd)(struct ips_ha *); + void (*statinit)(struct ips_ha *); + void (*intr)(struct ips_ha *); + void (*enableint)(struct ips_ha *); +} ips_hw_func_t; + typedef struct ips_ha { u8 ha_id[IPS_MAX_CHANNELS+1]; u32 dcdb_active[IPS_MAX_CHANNELS]; @@ -849,12 +936,18 @@ typedef struct ips_ha { u16 reset_count; /* number of resets */ u32 last_ffdc; /* last time we sent ffdc info*/ u8 revision_id; /* Revision level */ - - #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) + u16 device_id; /* PCI device ID */ + u8 reserved; + u32 mem_addr; /* Memory mapped address */ + u32 io_len; /* Size of IO Address */ + u32 mem_len; /* Size of memory address */ + char *mem_ptr; /* Memory mapped Ptr */ + char *ioremap_ptr; /* ioremapped memory pointer */ + ips_hw_func_t func; /* hw function pointers */ + struct pci_dev *pcidev; /* PCI device handle */ spinlock_t scb_lock; spinlock_t copp_lock; spinlock_t ips_lock; - #endif } ips_ha_t; typedef void (*ips_scb_callback) (ips_ha_t *, struct ips_scb *); diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c index f407740b904b..87b46476be4c 100644 --- a/drivers/scsi/mac53c94.c +++ b/drivers/scsi/mac53c94.c @@ -47,6 +47,7 @@ struct fsc_state { Scsi_Cmnd *current_req; /* req we're currently working on */ enum fsc_phase phase; /* what we're currently trying to do */ struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */ + void *dma_cmd_space; }; static struct fsc_state *all_53c94s; @@ -113,6 +114,7 @@ mac53c94_detect(Scsi_Host_Template *tp) DBDMA_ALIGN(dma_cmd_space); memset(state->dma_cmds, 0, (host->sg_tablesize + 1) * sizeof(struct dbdma_cmd)); + state->dma_cmd_space = dma_cmd_space; *prev_statep = state; prev_statep = &state->next; @@ -129,6 +131,22 @@ mac53c94_detect(Scsi_Host_Template *tp) return nfscs; } +int +mac53c94_release(struct Scsi_Host *host) +{ + struct fsc_state *fp = (struct fsc_state *) host->hostdata; + + if (fp == 0) + return 0; + if (fp->regs) + iounmap((void *) fp->regs); + if (fp->dma) + iounmap((void *) fp->dma); + kfree(fp->dma_cmd_space); + free_irq(fp->intr, fp); + return 0; +} + int mac53c94_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) { @@ -537,3 +555,7 @@ data_goes_out(Scsi_Cmnd *cmd) return 0; } } + +static Scsi_Host_Template driver_template = SCSI_MAC53C94; + +#include "scsi_module.c" diff --git a/drivers/scsi/mac53c94.h b/drivers/scsi/mac53c94.h index 320e7f0b38a8..171eeb148dd1 100644 --- a/drivers/scsi/mac53c94.h +++ b/drivers/scsi/mac53c94.h @@ -8,6 +8,7 @@ #define _MAC53C94_H int mac53c94_detect(Scsi_Host_Template *); +int mac53c94_release(struct Scsi_Host *); int mac53c94_command(Scsi_Cmnd *); int mac53c94_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int mac53c94_abort(Scsi_Cmnd *); @@ -17,6 +18,7 @@ int mac53c94_reset(Scsi_Cmnd *, unsigned int); proc_name: "53c94", \ name: "53C94", \ detect: mac53c94_detect, \ + release: mac53c94_release, \ command: mac53c94_command, \ queuecommand: mac53c94_queue, \ abort: mac53c94_abort, \ diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index adafbe43e1ba..f80a7c4972bd 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -149,6 +149,8 @@ struct mesh_state { struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */ int clk_freq; struct mesh_target tgts[8]; + void *dma_cmd_space; + struct device_node *ofnode; #ifndef MESH_NEW_STYLE_EH Scsi_Cmnd *completed_q; Scsi_Cmnd *completed_qtail; @@ -262,6 +264,7 @@ mesh_detect(Scsi_Host_Template *tp) panic("no mesh state"); memset(ms, 0, sizeof(*ms)); ms->host = mesh_host; + ms->ofnode = mesh; ms->mesh = (volatile struct mesh_regs *) ioremap(mesh->addrs[0].address, 0x1000); ms->dma = (volatile struct dbdma_regs *) @@ -278,6 +281,7 @@ mesh_detect(Scsi_Host_Template *tp) ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space); memset(ms->dma_cmds, 0, (mesh_host->sg_tablesize + 1) * sizeof(struct dbdma_cmd)); + ms->dma_cmd_space = dma_cmd_space; ms->current_req = 0; for (tgt = 0; tgt < 8; ++tgt) { @@ -323,6 +327,23 @@ mesh_detect(Scsi_Host_Template *tp) return nmeshes; } +int +mesh_release(struct Scsi_Host *host) +{ + struct mesh_state *ms = (struct mesh_state *) host->hostdata; + + if (ms == 0) + return 0; + if (ms->mesh) + iounmap((void *) ms->mesh); + if (ms->dma) + iounmap((void *) ms->dma); + kfree(ms->dma_cmd_space); + free_irq(ms->meshintr, ms); + feature_clear(ms->ofnode, FEATURE_MESH_enable); + return 0; +} + int mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) { @@ -1910,3 +1931,7 @@ static void dumpslog(struct mesh_state *ms) } while (i != ms->log_ix); } #endif /* MESH_DBG */ + +static Scsi_Host_Template driver_template = SCSI_MESH; + +#include "scsi_module.c" diff --git a/drivers/scsi/mesh.h b/drivers/scsi/mesh.h index 7ceeaf1a4c5c..19b2c9fc987d 100644 --- a/drivers/scsi/mesh.h +++ b/drivers/scsi/mesh.h @@ -8,6 +8,7 @@ #define _MESH_H int mesh_detect(Scsi_Host_Template *); +int mesh_release(struct Scsi_Host *); int mesh_command(Scsi_Cmnd *); int mesh_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int mesh_abort(Scsi_Cmnd *); @@ -17,6 +18,7 @@ int mesh_reset(Scsi_Cmnd *, unsigned int); proc_name: "mesh", \ name: "MESH", \ detect: mesh_detect, \ + release: mesh_release, \ command: mesh_command, \ queuecommand: mesh_queue, \ abort: mesh_abort, \ diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c7b9c37b1ea0..2dffa448df3a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1841,8 +1841,8 @@ static int scsi_register_host(Scsi_Host_Template * tpnt) return 1; } /* - * The low-level driver failed to register a driver. We - * can do this now. + * The low-level driver failed to register a driver. + * We can do this now. */ scsi_register(tpnt, 0); } @@ -1888,9 +1888,6 @@ static int scsi_register_host(Scsi_Host_Template * tpnt) } } - printk("scsi : %d host%s.\n", next_scsi_host, - (next_scsi_host == 1) ? "" : "s"); - /* The next step is to call scan_scsis here. This generates the * Scsi_Devices entries */ @@ -1968,7 +1965,7 @@ static int scsi_register_host(Scsi_Host_Template * tpnt) static void scsi_unregister_host(Scsi_Host_Template * tpnt) { int online_status; - int pcount; + int pcount0, pcount; Scsi_Cmnd *SCpnt; Scsi_Device *SDpnt; Scsi_Device *SDpnt1; @@ -2108,6 +2105,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) /* Next we go through and remove the instances of the individual hosts * that were detected */ + pcount0 = next_scsi_host; for (shpnt = scsi_hostlist; shpnt; shpnt = sh1) { sh1 = shpnt->next; if (shpnt->hostt != tpnt) @@ -2143,8 +2141,9 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) if (!scsi_hosts) scsi_resize_dma_pool(); - printk("scsi : %d host%s.\n", next_scsi_host, - (next_scsi_host == 1) ? "" : "s"); + if (pcount0 != next_scsi_host) + printk("scsi : %d host%s left.\n", next_scsi_host, + (next_scsi_host == 1) ? "" : "s"); #if defined(USE_STATIC_SCSI_MEMORY) printk("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n", diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c index deaa0815375c..c14a8eef77e3 100644 --- a/drivers/scsi/scsi_module.c +++ b/drivers/scsi/scsi_module.c @@ -40,7 +40,7 @@ static int __init init_this_scsi_driver(void) return 0; scsi_unregister_module(MODULE_SCSI_HA, &driver_template); - return -1; + return -ENODEV; } static void __exit exit_this_scsi_driver(void) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a46fd0f4e715..04ea9fbf13c6 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -133,6 +133,8 @@ static struct dev_info device_list[] = {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN}, {"CANON", "IPUBJD", "*", BLIST_SPARSELUN}, {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN}, + {"DEC","HSG80","*", BLIST_FORCELUN}, + {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA", "PD-1", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, @@ -142,6 +144,8 @@ static struct dev_info device_list[] = {"DGC", "DISK", "*", BLIST_SPARSELUN}, // Dell PV 650F (no tgt @ LUN 0) {"DELL", "PV530F", "*", BLIST_SPARSELUN}, // Dell PV 530F {"SONY", "TSL", "*", BLIST_FORCELUN}, // DDS3 & DDS4 autoloaders + {"DELL", "PERCRAID", "*", BLIST_FORCELUN}, + {"HP", "NetRAID-4M", "*", BLIST_FORCELUN}, /* * Must be at end of list... diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index 94820f5db8d3..f0e85c70e5e3 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -6,8 +6,6 @@ #include #include -#ifdef CONFIG_MODULES - #include #include #include @@ -93,4 +91,3 @@ EXPORT_SYMBOL(scsi_hosts); EXPORT_SYMBOL(scsi_devicelist); EXPORT_SYMBOL(scsi_device_types); -#endif /* CONFIG_MODULES */ diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 43667ec21dd0..9a9cd5b4d945 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -721,13 +721,14 @@ static int sd_init_onedisk(int i) sd_devname(i, nbuff); /* - * If the device is offline, don't try and read capacity or any of the other - * nicities. + * If the device is offline, don't try and read capacity or any + * of the other niceties. */ - if (rscsi_disks[i].device->online == FALSE) { + if (rscsi_disks[i].device->online == FALSE) return i; - } - /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is + + /* + * We need to retry the READ_CAPACITY because a UNIT_ATTENTION is * considered a fatal error, and many devices report such an error * just after a scsi bus reset. */ @@ -808,7 +809,8 @@ static int sd_init_onedisk(int i) } while(time1); printk("."); } - } while (the_result && spintime && time_after(spintime_value + 100 * HZ, jiffies)); + } while (the_result && spintime && + time_after(spintime_value + 100 * HZ, jiffies)); if (spintime) { if (the_result) printk("not responding...\n"); @@ -837,15 +839,16 @@ static int sd_init_onedisk(int i) /* * The SCSI standard says: * "READ CAPACITY is necessary for self configuring software" - * While not mandatory, support of READ CAPACITY is strongly encouraged. + * While not mandatory, support of READ CAPACITY is strongly + * encouraged. * We used to die if we couldn't successfully do a READ CAPACITY. * But, now we go on about our way. The side effects of this are * - * 1. We can't know block size with certainty. I have said "512 bytes - * is it" as this is most common. + * 1. We can't know block size with certainty. I have said + * "512 bytes is it" as this is most common. * - * 2. Recovery from when some one attempts to read past the end of the - * raw device will be slower. + * 2. Recovery from when someone attempts to read past the + * end of the raw device will be slower. */ if (the_result) { @@ -868,15 +871,15 @@ static int sd_init_onedisk(int i) rscsi_disks[i].capacity = 0x1fffff; sector_size = 512; - /* Set dirty bit for removable devices if not ready - sometimes drives - * will not report this properly. */ + /* Set dirty bit for removable devices if not ready - + * sometimes drives will not report this properly. */ if (rscsi_disks[i].device->removable && SRpnt->sr_sense_buffer[2] == NOT_READY) rscsi_disks[i].device->changed = 1; } else { /* - * FLOPTICAL , if read_capa is ok , drive is assumed to be ready + * FLOPTICAL, if read_capa is ok, drive is assumed to be ready */ rscsi_disks[i].ready = 1; @@ -890,7 +893,8 @@ static int sd_init_onedisk(int i) if (sector_size == 0) { sector_size = 512; - printk("%s : sector size 0 reported, assuming 512.\n", nbuff); + printk("%s : sector size 0 reported, assuming 512.\n", + nbuff); } if (sector_size != 512 && sector_size != 1024 && @@ -925,31 +929,30 @@ static int sd_init_onedisk(int i) * So I have created this table. See ll_rw_blk.c * Jacques Gelinas (Jacques@solucorp.qc.ca) */ - int m, mb; - int sz_quot, sz_rem; + int m; int hard_sector = sector_size; + int sz = rscsi_disks[i].capacity * (hard_sector/256); + /* There are 16 minors allocated for each major device */ for (m = i << 4; m < ((i + 1) << 4); m++) { sd_hardsizes[m] = hard_sector; } - mb = rscsi_disks[i].capacity / 1024 * hard_sector / 1024; - /* sz = div(m/100, 10); this seems to not be in the libr */ - m = (mb + 50) / 100; - sz_quot = m / 10; - sz_rem = m - (10 * sz_quot); - printk("SCSI device %s: hdwr sector= %d bytes." - " Sectors= %d [%d MB] [%d.%1d GB]\n", - nbuff, hard_sector, rscsi_disks[i].capacity, - mb, sz_quot, sz_rem); + + printk("SCSI device %s: " + "%d %d-byte hdwr sectors (%d MB)\n", + nbuff, rscsi_disks[i].capacity, + hard_sector, (sz/2 - sz/1250 + 974)/1950); } + + /* Rescale capacity to 512-byte units */ if (sector_size == 4096) rscsi_disks[i].capacity <<= 3; if (sector_size == 2048) - rscsi_disks[i].capacity <<= 2; /* Change into 512 byte sectors */ + rscsi_disks[i].capacity <<= 2; if (sector_size == 1024) - rscsi_disks[i].capacity <<= 1; /* Change into 512 byte sectors */ + rscsi_disks[i].capacity <<= 1; if (sector_size == 256) - rscsi_disks[i].capacity >>= 1; /* Change into 512 byte sectors */ + rscsi_disks[i].capacity >>= 1; } diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c index 3dc3e347df7d..62af8f2f11ba 100644 --- a/drivers/scsi/sym53c8xx.c +++ b/drivers/scsi/sym53c8xx.c @@ -107,6 +107,7 @@ #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#include #ifdef MODULE #include #endif diff --git a/drivers/sound/softoss.c b/drivers/sound/softoss.c index d478e243dbe8..7288b3b67d7a 100644 --- a/drivers/sound/softoss.c +++ b/drivers/sound/softoss.c @@ -1113,7 +1113,10 @@ static int softsyn_load_patch(int dev, int format, const char *addr, return -ENOMEM; } if(copy_from_user(&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs)) + { + vfree(patch); return -EFAULT; + } if (patch->mode & WAVE_ROM) { diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index fc715db83a25..ae77b1f74cb3 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -267,6 +267,7 @@ static int sound_release(struct inode *inode, struct file *file) DEB(printk("sound_release(dev=%d)\n", dev)); switch (dev & 0x0f) { case SND_DEV_CTL: + dev >>= 4; if (mixer_devs[dev]->owner) __MOD_DEC_USE_COUNT (mixer_devs[dev]->owner); break; diff --git a/drivers/sound/uart401.c b/drivers/sound/uart401.c index 0b7eb3297a0a..f537e2f584a9 100644 --- a/drivers/sound/uart401.c +++ b/drivers/sound/uart401.c @@ -315,8 +315,8 @@ void attach_uart401(struct address_info *hw_config, struct module *owner) { printk(KERN_WARNING "uart401: Failed to allocate memory\n"); kfree(midi_devs[devc->my_dev]); - kfree(devc); sound_unload_mididev(devc->my_dev); + kfree(devc); devc=NULL; return; } diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c index bf1dd38944e3..53452e878ad8 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -19,6 +19,7 @@ * Mike Preston, * * Fixes: + * Marc Boucher, * * More information about the hardware related to this driver can be found * at our website: http://www.quicknet.net @@ -43,6 +44,7 @@ static char ixj_c_revision[] = "$Revision: 3.31 $"; #define IXJDEBUG 0 #define MAXRINGS 5 +#include #include #include @@ -602,7 +604,7 @@ static void ixj_timeout(unsigned long ptr) j->flags.cringing = 0; ixj_ring_off(board); } else { - if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) { + if (jiffies - j->ring_cadence_jif >= (hertz/2)) { if (j->flags.cidring && !j->flags.cidsent) { j->flags.cidsent = 1; ixj_write_cid(board); @@ -1245,7 +1247,7 @@ static int ixj_hookstate(int board) if (!in_interrupt()) { det = jiffies + (hertz / 50); while (time_before(jiffies, det)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } } @@ -1356,7 +1358,7 @@ static int ixj_ring(int board) j->flags.ringing = 0; return 1; } - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); if (signal_pending(current)) break; @@ -1367,7 +1369,7 @@ static int ixj_ring(int board) if (ixj_hookstate(board) & 1) { det = jiffies + (hertz / 100); while (time_before(jiffies, det)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); if (signal_pending(current)) break; @@ -1377,7 +1379,7 @@ static int ixj_ring(int board) return 1; } } - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); if (signal_pending(current)) break; @@ -1796,7 +1798,7 @@ ssize_t ixj_read(struct file * file_p, char *buf, size_t length, loff_t * ppos) j->flags.inread = 1; add_wait_queue(&j->read_q, &wait); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); mb(); while (!j->read_buffer_ready || (j->dtmf_state && j->flags.dtmf_oob)) { @@ -1806,20 +1808,20 @@ ssize_t ixj_read(struct file * file_p, char *buf, size_t length, loff_t * ppos) return -EAGAIN; } if (file_p->f_flags & O_NONBLOCK) { - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->read_q, &wait); j->flags.inread = 0; return -EAGAIN; } if (!ixj_hookstate(NUM(file_p->f_dentry->d_inode->i_rdev))) { - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->read_q, &wait); j->flags.inread = 0; return 0; } interruptible_sleep_on(&j->read_q); if (signal_pending(current)) { - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->read_q, &wait); j->flags.inread = 0; return -EINTR; @@ -1827,7 +1829,7 @@ ssize_t ixj_read(struct file * file_p, char *buf, size_t length, loff_t * ppos) } remove_wait_queue(&j->read_q, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); /* Don't ever copy more than the user asks */ i = copy_to_user(buf, j->read_buffer, min(length, j->read_buffer_size)); j->read_buffer_ready = 0; @@ -1877,7 +1879,7 @@ ssize_t ixj_write(struct file *file_p, const char *buf, size_t count, loff_t * p j->flags.inwrite = 1; add_wait_queue(&j->write_q, &wait); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); mb(); @@ -1888,26 +1890,26 @@ ssize_t ixj_write(struct file *file_p, const char *buf, size_t count, loff_t * p return -EAGAIN; } if (file_p->f_flags & O_NONBLOCK) { - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->write_q, &wait); j->flags.inwrite = 0; return -EAGAIN; } if (!ixj_hookstate(NUM(file_p->f_dentry->d_inode->i_rdev))) { - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->write_q, &wait); j->flags.inwrite = 0; return 0; } interruptible_sleep_on(&j->write_q); if (signal_pending(current)) { - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->write_q, &wait); j->flags.inwrite = 0; return -EINTR; } } - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&j->write_q, &wait); if (j->write_buffer_wp + count >= j->write_buffer_end) j->write_buffer_wp = j->write_buffer; @@ -2305,7 +2307,7 @@ static void ixj_write_cidcw(int board) ixj_play_tone(board, 23); while(j->tone_state) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } @@ -2313,14 +2315,14 @@ static void ixj_write_cidcw(int board) ixj_play_tone(board, 24); while(j->tone_state) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } j->cidcw_wait = jiffies + (200 * hertz / 100000); while(!j->flags.cidcw_ack && time_before(jiffies, j->cidcw_wait)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } @@ -4454,14 +4456,21 @@ static int ixj_build_cadence(int board, IXJ_CADENCE * cp) if (lcp == NULL) return -ENOMEM; if (copy_from_user(lcp, (char *) cp, sizeof(IXJ_CADENCE))) + { + kfree(lcp); return -EFAULT; + } lcep = kmalloc(sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used, GFP_KERNEL); if (lcep == NULL) { kfree(lcp); return -ENOMEM; } if (copy_from_user(lcep, lcp->ce, sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used)) + { + kfree(lcep); + kfree(lcp); return -EFAULT; + } if (j->cadence_t) { kfree(j->cadence_t->ce); kfree(j->cadence_t); @@ -4674,7 +4683,7 @@ int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd, unsign copy_from_user(&j->cid_send, (char *)arg, sizeof(PHONE_CID)); } else { - memcpy(&j->cid_send, 0, sizeof(PHONE_CID)); + memset(&j->cid_send, 0, sizeof(PHONE_CID)); } ixj_write_cidcw(board); break; @@ -4687,7 +4696,7 @@ int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd, unsign copy_from_user(&j->cid_send, (char *)arg, sizeof(PHONE_CID)); } else { - memcpy(&j->cid_send, 0, sizeof(PHONE_CID)); + memset(&j->cid_send, 0, sizeof(PHONE_CID)); } ixj_ring_start(board); break; @@ -5128,7 +5137,7 @@ static int ixj_linetest(int board) daa_set_mode(board, SOP_PU_CONVERSATION); jifwait = jiffies + hertz; while (time_before(jiffies, jifwait)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } daa_int_read(board); @@ -5155,7 +5164,7 @@ static int ixj_linetest(int board) daa_set_mode(board, SOP_PU_CONVERSATION); jifwait = jiffies + hertz; while (time_before(jiffies, jifwait)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } daa_int_read(board); @@ -5349,25 +5358,25 @@ static int ixj_selfprobe(int board) LED_SetState(0x1, board); jif = jiffies + (hertz / 10); while (time_before(jiffies, jif)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } LED_SetState(0x2, board); jif = jiffies + (hertz / 10); while (time_before(jiffies, jif)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } LED_SetState(0x4, board); jif = jiffies + (hertz / 10); while (time_before(jiffies, jif)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } LED_SetState(0x8, board); jif = jiffies + (hertz / 10); while (time_before(jiffies, jif)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } LED_SetState(0x0, board); @@ -5482,8 +5491,9 @@ static int ixj_selfprobe(int board) return 0; } -int ixj_read_proc(char *buf, char **start, off_t offset, int len, int unused) +int ixj_get_status_proc(char *buf) { + int len; int cnt; IXJ *j; len = 0; @@ -5734,8 +5744,9 @@ int ixj_read_proc(char *buf, char **start, off_t offset, int len, int unused) } return len; } -int ixj_read_proc_fsk(char *buf, char **start, off_t offset, int len, int unused) +int ixj_get_status_proc_fsk(char *buf) { + int len; len = 0; if (ixj[2].fskdcnt) { memcpy(buf, &ixj[2].fskdata, (ixj[2].fskdcnt) * 2); @@ -5743,27 +5754,30 @@ int ixj_read_proc_fsk(char *buf, char **start, off_t offset, int len, int unused } return len; } -static struct proc_dir_entry ixj_proc_entry = + +static int ixj_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { - 0, - 3, "ixj", - S_IFREG | S_IRUGO, - 1, 0, 0, - 0, - NULL, - &ixj_read_proc -}; + int len = ixj_get_status_proc(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} -static struct proc_dir_entry ixjfsk_proc_entry = +static int ixj_read_proc_fsk(char *page, char **start, off_t off, + int count, int *eof, void *data) { - 0, - 6, "ixjfsk", - S_IFREG | S_IRUGO, - 1, 0, 0, - 0, - NULL, - &ixj_read_proc_fsk -}; + int len = ixj_get_status_proc_fsk(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} MODULE_DESCRIPTION("Internet Phone/Internet LineJack module - www.quicknet.net"); MODULE_AUTHOR("Ed Okerson "); @@ -6092,8 +6106,8 @@ static void cleanup(void) ixj_detach(dev_list); #endif } - proc_unregister(&proc_root, ixj_proc_entry.low_ino); - proc_unregister(&proc_root, ixjfsk_proc_entry.low_ino); + remove_proc_entry ("ixj", NULL); + remove_proc_entry ("ixjfsk", NULL); } // Typedefs @@ -6315,6 +6329,8 @@ int __init ixj_init(void) pci = pci_find_device(0x15E2, 0x0500, pci); if (!pci) break; + if (pci_enable_device(pci)) + break; { ixj[cnt].DSPbase = pci_resource_start(pci, 0); ixj[cnt].XILINXbase = ixj[cnt].DSPbase + 0x10; @@ -6336,8 +6352,8 @@ int __init ixj_init(void) } #endif printk("%s\n", ixj_c_rcsid); - proc_register(&proc_root, &ixj_proc_entry); - proc_register(&proc_root, &ixjfsk_proc_entry); + create_proc_read_entry ("ixj", 0, NULL, ixj_read_proc, NULL); + create_proc_read_entry ("ixjfsk", 0, NULL, ixj_read_proc_fsk, NULL); ixj_init_timer(); ixj_add_timer(); return probe; diff --git a/drivers/telephony/phonedev.c b/drivers/telephony/phonedev.c index 63586caf4cd4..2e914ef5fa92 100644 --- a/drivers/telephony/phonedev.c +++ b/drivers/telephony/phonedev.c @@ -14,7 +14,6 @@ * phone_register_device now works with unit!=PHONE_UNIT_ANY */ -#include #include #include #include diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index 58e4ffe2b592..885e636bd38f 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -739,7 +739,7 @@ static int proc_submiturb(struct dev_state *ps, void *arg) if (copy_from_user(&uurb, arg, sizeof(uurb))) return -EFAULT; - if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD)) + if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD|USBDEVFS_URB_QUEUE_BULK)) return -EINVAL; if (!uurb.buffer) return -EINVAL; diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 08e84dde4eed..581e11736461 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -76,30 +77,32 @@ static int usb_get_port_status(struct usb_device *dev, int port, void *data) data, sizeof(struct usb_hub_status), HZ); } -/* - * A irq handler returns non-zero to indicate to - * the low-level driver that it wants to be re-activated, - * or zero to say "I'm done". - */ static void hub_irq(struct urb *urb) { struct usb_hub *hub = (struct usb_hub *)urb->context; unsigned long flags; + /* Cause a hub reset after 10 consecutive errors */ if (urb->status) { - if (urb->status != -ENOENT) - dbg("nonzero status in irq %d", urb->status); + if (urb->status == -ENOENT) + return; - return; + dbg("nonzero status in irq %d", urb->status); + + if ((++hub->nerrors < 10) || hub->error) + return; + + hub->error = urb->status; } + hub->nerrors = 0; + /* Something happened, let khubd figure it out */ if (waitqueue_active(&khubd_wait)) { /* Add the hub to the event queue */ spin_lock_irqsave(&hub_event_lock, flags); - if (hub->event_list.next == &hub->event_list) { + if (list_empty(&hub->event_list)) { list_add(&hub->event_list, &hub_event_list); - /* Wake up khubd */ wake_up(&khubd_wait); } spin_unlock_irqrestore(&hub_event_lock, flags); @@ -114,40 +117,43 @@ static void usb_hub_power_on(struct usb_hub *hub) dbg("enabling power on all ports"); for (i = 0; i < hub->nports; i++) usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER); + + /* Wait for power to be enabled */ + wait_ms(hub->descriptor->bPwrOn2PwrGood * 2); } -static int usb_hub_configure(struct usb_hub *hub) +static int usb_hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { struct usb_device *dev = hub->dev; - unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap; - struct usb_hub_descriptor *descriptor; - struct usb_descriptor_header *header; - struct usb_hub_status *hubsts; - int i, ret; + struct usb_hub_status hubstatus; + char portstr[USB_MAXCHILDREN + 1]; + unsigned int pipe; + int i, maxp, ret; + + hub->descriptor = kmalloc(HUB_DESCRIPTOR_MAX_SIZE, GFP_KERNEL); + if (!hub->descriptor) { + err("Unable to kmalloc %d bytes for hub descriptor", HUB_DESCRIPTOR_MAX_SIZE); + return -1; + } /* Request the entire hub descriptor. */ - header = (struct usb_descriptor_header *)buffer; - ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer)); - /* is large enough for a hub with 127 ports; + ret = usb_get_hub_descriptor(dev, hub->descriptor, HUB_DESCRIPTOR_MAX_SIZE); + /* descriptor> is large enough for a hub with 127 ports; * the hub can/will return fewer bytes here. */ if (ret < 0) { err("Unable to get hub descriptor (err = %d)", ret); return -1; } - bitmap = kmalloc(header->bLength, GFP_KERNEL); - if (!bitmap) { - err("Unable to kmalloc %d bytes for bitmap", header->bLength); - return -1; - } - - memcpy (bitmap, buffer, header->bLength); - descriptor = (struct usb_hub_descriptor *)bitmap; - - hub->nports = dev->maxchild = descriptor->bNbrPorts; + hub->nports = dev->maxchild = hub->descriptor->bNbrPorts; info("%d port%s detected", hub->nports, (hub->nports == 1) ? "" : "s"); - switch (descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { + if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) + dbg("part of a compound device"); + else + dbg("standalone hub"); + + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: dbg("ganged power switching"); break; @@ -160,12 +166,7 @@ static int usb_hub_configure(struct usb_hub *hub) break; } - if (descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) - dbg("part of a compound device"); - else - dbg("standalone hub"); - - switch (descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: dbg("global over-current protection"); break; @@ -178,28 +179,52 @@ static int usb_hub_configure(struct usb_hub *hub) break; } - dbg("power on to power good time: %dms", descriptor->bPwrOn2PwrGood * 2); - dbg("hub controller current requirement: %dmA", descriptor->bHubContrCurrent); + dbg("power on to power good time: %dms", hub->descriptor->bPwrOn2PwrGood * 2); + dbg("hub controller current requirement: %dmA", hub->descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) - dbg("port %d is%s removable", i + 1, - bitmap[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) - ? " not" : ""); + portstr[i] = hub->descriptor->bitmap[((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; + portstr[dev->maxchild] = 0; - kfree(bitmap); + dbg("port removable status: %s", portstr); - ret = usb_get_hub_status(dev, buffer); + ret = usb_get_hub_status(dev, &hubstatus); if (ret < 0) { err("Unable to get hub status (err = %d)", ret); return -1; } - hubsts = (struct usb_hub_status *)buffer; + le16_to_cpus(&hubstatus.wHubStatus); + dbg("local power source is %s", - (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); + (hubstatus.wHubStatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); dbg("%sover-current condition exists", - (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no "); + (hubstatus.wHubStatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); + + /* Start the interrupt endpoint */ + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + if (maxp > sizeof(hub->buffer)) + maxp = sizeof(hub->buffer); + + hub->urb = usb_alloc_urb(0); + if (!hub->urb) { + err("couldn't allocate interrupt urb"); + return -1; + } + + FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, + hub, endpoint->bInterval); + ret = usb_submit_urb(hub->urb); + if (ret) { + err("usb_submit_urb failed (%d)", ret); + return -1; + } + + /* Wake up khubd */ + wake_up(&khubd_wait); usb_hub_power_on(hub); @@ -212,8 +237,6 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; unsigned long flags; - unsigned int pipe; - int maxp, ret; interface = &dev->actconfig->interface[i].altsetting[0]; @@ -265,34 +288,11 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) list_add(&hub->hub_list, &hub_list); spin_unlock_irqrestore(&hub_event_lock, flags); - if (usb_hub_configure(hub) >= 0) { - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - if (maxp > sizeof(hub->buffer)) - maxp = sizeof(hub->buffer); - - hub->urb = usb_alloc_urb(0); - if (!hub->urb) { - err("couldn't allocate interrupt urb"); - goto fail; - } - - FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, - hub, endpoint->bInterval); - ret = usb_submit_urb(hub->urb); - if (ret) { - err("usb_submit_urb failed (%d)", ret); - goto fail; - } - - /* Wake up khubd */ - wake_up(&khubd_wait); - } + if (usb_hub_configure(hub, endpoint) >= 0) + return hub; - return hub; + err("hub configuration failed"); -fail: /* free hub, but first clean up its list. */ spin_lock_irqsave(&hub_event_lock, flags); @@ -330,11 +330,16 @@ static void hub_disconnect(struct usb_device *dev, void *ptr) hub->urb = NULL; } + if (hub->descriptor) { + kfree(hub->descriptor); + hub->descriptor = NULL; + } + /* Free the memory */ kfree(hub); } -static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data) +static int hub_ioctl(struct usb_device *hub, unsigned int code, void *user_data) { /* assert ifno == 0 (part of hub spec) */ switch (code) { @@ -343,19 +348,19 @@ static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data unsigned long flags; int i; - spin_lock_irqsave (&hub_event_lock, flags); + spin_lock_irqsave(&hub_event_lock, flags); if (hub->devnum <= 0) info->nports = 0; else { info->nports = hub->maxchild; for (i = 0; i < info->nports; i++) { - if (hub->children [i] == NULL) - info->port [i] = 0; + if (hub->children[i] == NULL) + info->port[i] = 0; else - info->port [i] = hub->children [i]->devnum; + info->port[i] = hub->children[i]->devnum; } } - spin_unlock_irqrestore (&hub_event_lock, flags); + spin_unlock_irqrestore(&hub_event_lock, flags); return info->nports + 1; } @@ -365,121 +370,259 @@ static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data } } -static void usb_hub_port_connect_change(struct usb_device *hub, int port) +static int usb_hub_reset(struct usb_hub *hub) { - struct usb_device *usb; - struct usb_port_status portsts; - unsigned short portstatus, portchange; - int ret, tries; - - wait_ms(100); + struct usb_device *dev = hub->dev; + int i; - ret = usb_get_port_status(hub, port + 1, &portsts); - if (ret < 0) { - err("get_port_status(%d) failed (err = %d)", port + 1, ret); - return; + /* Disconnect any attached devices */ + for (i = 0; i < hub->nports; i++) { + if (dev->children[i]) + usb_disconnect(&dev->children[i]); } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - dbg("portstatus %x, change %x, %s", portstatus, portchange, - portstatus&(1<urb) + usb_unlink_urb(hub->urb); + else + return -1; - /* Clear the connection change status */ - usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); + if (usb_reset_device(dev)) + return -1; - /* Disconnect any existing devices under this port */ - if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && - (!(portstatus & USB_PORT_STAT_ENABLE)))|| (hub->children[port])) { - usb_disconnect(&hub->children[port]); - /* Return now if nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return; + if (usb_submit_urb(hub->urb)) + return -1; + + usb_hub_power_on(hub); + + return 0; +} + +static void usb_hub_disconnect(struct usb_device *dev) +{ + struct usb_device *parent = dev->parent; + int i; + + /* Find the device pointer to disconnect */ + if (parent) { + for (i = 0; i < parent->maxchild; i++) { + if (parent->children[i] == dev) { + usb_disconnect(&parent->children[i]); + return; + } + } } - wait_ms(400); - down(&usb_address0_sem); + err("cannot disconnect hub %d", dev->devnum); +} -#define MAX_TRIES 5 - /* Reset the port */ - for (tries = 0; tries < MAX_TRIES ; tries++) { - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); - wait_ms(200); +#define HUB_RESET_TRIES 5 +#define HUB_PROBE_TRIES 2 +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 +static int usb_hub_port_wait_reset(struct usb_device *hub, int port, + struct usb_device *dev, unsigned int delay) +{ + int delay_time, ret; + struct usb_port_status portsts; + unsigned short portchange, portstatus; + + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { + /* wait to give the device a chance to reset */ + wait_ms(delay); + + /* read and decode port status */ ret = usb_get_port_status(hub, port + 1, &portsts); if (ret < 0) { err("get_port_status(%d) failed (err = %d)", port + 1, ret); - goto out; + return -1; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - dbg("portstatus %x, change %x, %s", portstatus ,portchange, - portstatus&(1<slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + return 0; + } + + /* switch to the long delay after two short delay failures */ + if (delay_time >= 2 * HUB_SHORT_RESET_TIME) + delay = HUB_LONG_RESET_TIME; - wait_ms(200); + dbg("port %d of hub %d not reset yet, waiting %dms", port + 1, + hub->devnum, delay); } - if (tries >= MAX_TRIES) { - err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES); - err("Maybe the USB cable is bad?"); - goto out; + return -1; +} + +static int usb_hub_port_reset(struct usb_device *hub, int port, + struct usb_device *dev, unsigned int delay) +{ + int i; + + /* Reset the port */ + for (i = 0; i < HUB_RESET_TRIES; i++) { + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + + /* return success if the port reset OK */ + if (!usb_hub_port_wait_reset(hub, port, dev, delay)) { + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); + return 0; + } + + dbg("port %d of hub %d not enabled, trying reset again...", + port + 1, hub->devnum); + delay = HUB_LONG_RESET_TIME; } - usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); + err("Cannot enable port %i of hub %d, disabling port.", + port + 1, hub->devnum); + err("Maybe the USB cable is bad?"); + + return -1; +} + +void usb_hub_port_disable(struct usb_device *hub, int port) +{ + int ret; + + ret = usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE); + if (ret) + err("cannot disable port %d of hub %d (err = %d)", + port + 1, hub->devnum, ret); +} - /* Allocate a new device struct for it */ - usb = usb_alloc_dev(hub, hub->bus); - if (!usb) { - err("couldn't allocate usb_device"); - goto out; +static void usb_hub_port_connect_change(struct usb_device *hub, int port, + struct usb_port_status *portsts) +{ + struct usb_device *dev; + unsigned short portstatus, portchange; + unsigned int delay = HUB_SHORT_RESET_TIME; + int i; + char *portstr, *tempstr; + + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + dbg("port %d, portstatus %x, change %x, %s", port + 1, portstatus, + portchange, portstatus & (1 << USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); + + /* Clear the connection change status */ + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); + + /* Disconnect any existing devices under this port */ + if (hub->children[port]) + usb_disconnect(&hub->children[port]); + + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + if (portstatus & USB_PORT_STAT_ENABLE) + usb_hub_port_disable(hub, port); + + return; } - usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + down(&usb_address0_sem); + + tempstr = kmalloc(1024, GFP_KERNEL); + portstr = kmalloc(1024, GFP_KERNEL); + if (portstr) + portstr[0] = 0; - hub->children[port] = usb; + for (i = 0; i < HUB_PROBE_TRIES; i++) { + struct usb_device *pdev, *cdev; - /* Find a new device ID for it */ - usb_connect(usb); + /* Allocate a new device struct */ + dev = usb_alloc_dev(hub, hub->bus); + if (!dev) { + err("couldn't allocate usb_device"); + break; + } - /* Run it through the hoops (find a driver, etc) */ - ret = usb_new_device(usb); - if (ret) { - /* Try resetting the device. Windows does this and it */ - /* gets some devices working correctly */ - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + hub->children[port] = dev; - ret = usb_new_device(usb); - if (ret) { - usb_disconnect(&hub->children[port]); + /* Reset the device */ + if (usb_hub_port_reset(hub, port, dev, delay)) { + usb_free_dev(dev); + break; + } + + /* Find a new device ID for it */ + usb_connect(dev); - /* Woops, disable the port */ - dbg("hub: disabling port %d", port + 1); - usb_clear_port_feature(hub, port + 1, - USB_PORT_FEAT_ENABLE); + /* Create a readable topology string */ + cdev = dev; + pdev = dev->parent; + if (portstr && tempstr) { + while (pdev) { + int port; + + for (port = 0; port < pdev->maxchild; port++) + if (pdev->children[port] == cdev) + break; + + strcpy(tempstr, portstr); + if (!strlen(tempstr)) + sprintf(portstr, "%d", port + 1); + else + sprintf(portstr, "%d/%s", port + 1, tempstr); + + cdev = pdev; + pdev = pdev->parent; + } + } + + if (portstr) + info("USB new device connect on bus%d/%s, assigned device number %d", + dev->bus->busnum, portstr, dev->devnum); + else + info("USB new device connect on bus%d, assigned device number %d", + dev->bus->busnum, dev->devnum); + + if (portstr) + kfree(portstr); + if (tempstr) + kfree(tempstr); + + /* Run it through the hoops (find a driver, etc) */ + if (!usb_new_device(dev)) { + up(&usb_address0_sem); + return; } + + /* Free the configuration if there was an error */ + usb_free_dev(dev); + + /* Switch to a long reset time */ + delay = HUB_LONG_RESET_TIME; } -out: + usb_hub_port_disable(hub, port); up(&usb_address0_sem); } static void usb_hub_events(void) { unsigned long flags; - int i; struct list_head *tmp; struct usb_device *dev; struct usb_hub *hub; struct usb_hub_status hubsts; unsigned short hubstatus, hubchange; + int i, ret; /* * We restart the list everytime to avoid a deadlock with @@ -504,12 +647,26 @@ static void usb_hub_events(void) spin_unlock_irqrestore(&hub_event_lock, flags); + if (hub->error) { + dbg("resetting hub %d for error %d", dev->devnum, hub->error); + + if (usb_hub_reset(hub)) { + err("error resetting hub %d - disconnecting", dev->devnum); + usb_hub_disconnect(dev); + continue; + } + + hub->nerrors = 0; + hub->error = 0; + } + for (i = 0; i < hub->nports; i++) { struct usb_port_status portsts; unsigned short portstatus, portchange; - if (usb_get_port_status(dev, i + 1, &portsts) < 0) { - err("get_port_status failed"); + ret = usb_get_port_status(dev, i + 1, &portsts); + if (ret < 0) { + err("get_port_status failed (err = %d)", ret); continue; } @@ -519,27 +676,27 @@ static void usb_hub_events(void) if (portchange & USB_PORT_STAT_C_CONNECTION) { dbg("port %d connection change", i + 1); - usb_hub_port_connect_change(dev, i); - } - - if (portchange & USB_PORT_STAT_C_ENABLE) { + usb_hub_port_connect_change(dev, i, &portsts); + } else if (portchange & USB_PORT_STAT_C_ENABLE) { dbg("port %d enable change, status %x", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); - // EM interference sometimes causes bad shielded USB devices to - // be shutdown by the hub, this hack enables them again. - // Works at least with mouse driver. + /* + * EM interference sometimes causes bad shielded USB devices to + * be shutdown by the hub, this hack enables them again. + * Works at least with mouse driver. + */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { err("already running port %i disabled by hub (EMI?), re-enabling...", i + 1); - usb_hub_port_connect_change(dev, i); + usb_hub_port_connect_change(dev, i, &portsts); } } - if (portstatus & USB_PORT_STAT_SUSPEND) { + if (portchange & USB_PORT_STAT_C_SUSPEND) { dbg("port %d suspend change", i + 1); - usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { @@ -555,9 +712,9 @@ static void usb_hub_events(void) } /* end for i */ /* deal with hub status changes */ - if (usb_get_hub_status(dev, &hubsts) < 0) { + if (usb_get_hub_status(dev, &hubsts) < 0) err("get_hub_status failed"); - } else { + else { hubstatus = le16_to_cpup(&hubsts.wHubStatus); hubchange = le16_to_cpup(&hubsts.wHubChange); if (hubchange & HUB_CHANGE_LOCAL_POWER) { @@ -566,7 +723,7 @@ static void usb_hub_events(void) } if (hubchange & HUB_CHANGE_OVERCURRENT) { dbg("hub overcurrent change"); - wait_ms(500); //Cool down + wait_ms(500); /* Cool down */ usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); usb_hub_power_on(hub); } @@ -660,7 +817,7 @@ void usb_hub_cleanup(void) } /* - * Hub resources are freed for us by usb_deregister. It + * Hub resources are freed for us by usb_deregister. It calls * usb_driver_purge on every device which in turn calls that * devices disconnect function if it is using this driver. * The hub_disconnect function takes care of releasing the @@ -701,23 +858,23 @@ int usb_reset_device(struct usb_device *dev) down(&usb_address0_sem); /* Send a reset to the device */ - usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET); - - wait_ms(200); - - usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET); + if (usb_hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) { + usb_hub_port_disable(parent, port); + up(&usb_address0_sem); + return(-ENODEV); + } /* Reprogram the Address */ ret = usb_set_address(dev); if (ret < 0) { err("USB device not accepting new address (error=%d)", ret); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); - dev->devnum = -1; + usb_hub_port_disable(parent, port); up(&usb_address0_sem); return ret; } - wait_ms(10); /* Let the SET_ADDRESS settle */ + /* Let the SET_ADDRESS settle */ + wait_ms(10); up(&usb_address0_sem); @@ -768,30 +925,23 @@ int usb_reset_device(struct usb_device *dev) usb_set_maxpacket(dev); return 1; - } else { - ret = usb_set_configuration(dev, - dev->actconfig->bConfigurationValue); - if (ret < 0) { - err("failed to set active configuration (error=%d)", - ret); - return ret; - } + } - for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { - struct usb_interface *intf = - &dev->actconfig->interface[i]; - struct usb_interface_descriptor *as = - &intf->altsetting[intf->act_altsetting]; + ret = usb_set_configuration(dev, dev->actconfig->bConfigurationValue); + if (ret < 0) { + err("failed to set active configuration (error=%d)", ret); + return ret; + } - ret = usb_set_interface(dev, as->bInterfaceNumber, - as->bAlternateSetting); - if (ret < 0) { - err("failed to set active alternate setting for interface %d (error=%d)", i, ret); - return ret; - } - } + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = &dev->actconfig->interface[i]; + struct usb_interface_descriptor *as = &intf->altsetting[intf->act_altsetting]; - return 0; + ret = usb_set_interface(dev, as->bInterfaceNumber, as->bAlternateSetting); + if (ret < 0) { + err("failed to set active alternate setting for interface %d (error=%d)", i, ret); + return ret; + } } return 0; diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h index 1be8422dae7e..626869736116 100644 --- a/drivers/usb/hub.h +++ b/drivers/usb/hub.h @@ -82,47 +82,32 @@ struct usb_hub_descriptor { __u16 wHubCharacteristics; __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length bitmaps that hold max 256 entries, but for now they're ignored */ + __u8 bitmap[0]; } __attribute__ ((packed)); struct usb_device; -typedef enum { - USB_PORT_UNPOWERED = 0, /* Default state */ - USB_PORT_POWERED, /* When we've put power to it */ - USB_PORT_ENABLED, /* When it's been enabled */ - USB_PORT_DISABLED, /* If it's been disabled */ - USB_PORT_ADMINDISABLED, /* Forced down */ -} usb_hub_port_state; - -struct usb_hub_port { - usb_hub_port_state cstate; /* Configuration state */ - - struct usb_device *child; /* Device attached to this port */ - - struct usb_hub *parent; /* Parent hub */ -}; - struct usb_hub { - /* Device structure */ struct usb_device *dev; - /* Interrupt polling pipe */ - struct urb *urb; + struct urb *urb; /* Interrupt polling pipe */ char buffer[USB_MAXCHILDREN / 8]; - /* List of hubs */ + int error; + int nerrors; + struct list_head hub_list; - /* Temporary event list */ struct list_head event_list; /* Number of ports on the hub */ int nports; - struct usb_hub_port ports[0]; /* Dynamically allocated */ + struct usb_hub_descriptor *descriptor; }; #endif diff --git a/drivers/usb/net1080.c b/drivers/usb/net1080.c index ab32f6d5509f..044e72b4a53e 100644 --- a/drivers/usb/net1080.c +++ b/drivers/usb/net1080.c @@ -25,6 +25,7 @@ * *-------------------------------------------------------------------------*/ +#include #include #include #include diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 8f32026f25f7..275ff05b71a4 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.20"; +static const char version[] = "1.25"; #define __NO_VERSION__ @@ -59,11 +59,9 @@ static const char version[] = "1.20"; #define MAX_FRAME_SIZE (640 * 480 * 3) #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 - #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) -#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : ((p) == VIDEO_PALETTE_YUV422 ? 8 : 24)) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \ + ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24)) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -107,6 +105,13 @@ static int cams = 1; /* Prevent apps from timing out if frame is not done in time */ static int retry_sync = 0; +/* Enable compression. This is for experimentation only; compressed images + * still cannot be decoded yet. */ +static int compress = 0; + +/* Display test pattern - doesn't work yet either */ +static int testpat = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure"); MODULE_PARM(debug, "i"); @@ -122,21 +127,28 @@ MODULE_PARM_DESC(i2c_detect_tries, "Number of tries to detect sensor"); MODULE_PARM(aperture, "i"); MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs"); MODULE_PARM(force_rgb, "i"); -MODULE_PARM_DESC(force_rgb, "Read RBG instead of BGR"); +MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR"); MODULE_PARM(buf_timeout, "i"); MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation"); MODULE_PARM(cams, "i"); MODULE_PARM_DESC(cams, "Number of simultaneous cameras"); MODULE_PARM(retry_sync, "i"); MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out"); +MODULE_PARM(compress, "i"); +MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)"); +MODULE_PARM(testpat, "i"); +MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)"); -MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); +MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); MODULE_DESCRIPTION("OV511 USB Camera Driver"); char kernel_version[] = UTS_RELEASE; static struct usb_driver ov511_driver; +/* I know, I know, global variables suck. This is only a temporary hack */ +int output_offset; + /********************************************************************** * List of known OV511-based cameras **********************************************************************/ @@ -645,7 +657,7 @@ static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn) static void ov511_dump_i2c_regs(struct usb_device *dev) { PDEBUG(3, "I2C REGS"); - ov511_dump_i2c_range(dev, 0x00, 0x38); + ov511_dump_i2c_range(dev, 0x00, 0x7C); } #if 0 @@ -673,7 +685,7 @@ static void ov511_dump_regs(struct usb_device *dev) PDEBUG(1, "I2C REGS"); ov511_dump_reg_range(dev, 0x40, 0x49); PDEBUG(1, "SYSTEM CONTROL REGS"); - ov511_dump_reg_range(dev, 0x50, 0x53); + ov511_dump_reg_range(dev, 0x50, 0x55); ov511_dump_reg_range(dev, 0x5e, 0x5f); PDEBUG(1, "OmniCE REGS"); ov511_dump_reg_range(dev, 0x70, 0x79); @@ -797,15 +809,22 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0) return -EIO; #endif - if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) - if(ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0) + if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE + || ov511->sensor == SEN_OV6620) + if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0) return -EIO; - if (ov511->sensor == SEN_OV7610) { - if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) + if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) { + if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) return -EIO; - if(ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0) + if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0) + return -EIO; + + if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0) + return -EIO; + + if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0) return -EIO; } else if ((ov511->sensor == SEN_OV7620) || (ov511->sensor == SEN_OV7620AE)) { @@ -816,7 +835,7 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT), ov511_i2c_read(dev, OV7610_REG_BRT)); - if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) + if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) return -EIO; #endif } @@ -838,16 +857,19 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) if (ov511_stop(dev) < 0) return -EIO; - if((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO; + if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO; p->colour = ret << 8; - if((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO; + if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO; p->contrast = ret << 8; - if((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO; + if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO; p->brightness = ret << 8; - p->hue = 0x8000; + /* This may not be the best way to do it */ + if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO; + p->hue = ret << 8; + p->whiteness = 105 << 8; /* Can we get these from frame[0]? -claudio? */ @@ -860,20 +882,23 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } -/* FIXME: 176x144, 160x140 */ /* LNCNT values fixed by Lawrence Glaister */ static struct mode_list mlist[] = { - /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COMC COML */ - { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, - { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, - { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, - { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, - { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COML */ + { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e }, + { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e }, + { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e }, { 0, 0 } }; @@ -883,7 +908,8 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, { int i; struct usb_device *dev = ov511->dev; - int hwsbase = 0, hwebase = 0; + int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; + int hwscale = 0, vwscale = 0; PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); @@ -895,7 +921,7 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, ov511_reg_write(dev, 0x16, 0x00); if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) { - /* these aren't valid on the OV7620 */ + /* these aren't valid on the OV6620/OV7620 */ ov511_i2c_write(dev, 0x0e, 0x44); } ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20); @@ -907,7 +933,7 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, ov511_reg_write(dev, 0x16, 0x01); if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) { - /* not valid on the OV7620 */ + /* not valid on the OV6620/OV7620 */ ov511_i2c_write(dev, 0x0e, 0x04); } ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00); @@ -923,27 +949,64 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, case SEN_OV7620AE: hwsbase = 0x38; hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + hwsbase = 0x38; + hwebase = 0x39; + vwsbase = 0x03; + vwebase = 0x04; break; case SEN_OV7620: hwsbase = 0x2c; hwebase = 0x2d; + vwsbase = vwebase = 0x05; break; default: - hwsbase = 0; - hwebase = 0; - break; + err("Invalid sensor"); + return -EINVAL; } + /* Bit 5 of COM C register varies with sensor */ + if (ov511->sensor == SEN_OV6620) { + if (width > 176 && height > 144) { /* CIF */ + ov511_i2c_write(dev, 0x14, 0x04); + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + hwsize = 352; + vwsize = 288; + } else { /* QCIF */ + ov511_i2c_write(dev, 0x14, 0x24); + hwsize = 176; + vwsize = 144; + } + } + else { + if (width > 320 && height > 240) { /* VGA */ + ov511_i2c_write(dev, 0x14, 0x04); + hwscale = 2; + vwscale = 1; + hwsize = 640; + vwsize = 480; + } else { /* QVGA */ + ov511_i2c_write(dev, 0x14, 0x24); + hwscale = 1; + hwsize = 320; + vwsize = 240; + } + } + + /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ if (sub_flag) { - ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2)); - ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2)); - ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1)); - ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1)); + ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale)); + ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale)); + ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale)); } else { ov511_i2c_write(dev, 0x17, hwsbase); - ov511_i2c_write(dev, 0x18, hwebase + (640>>2)); - ov511_i2c_write(dev, 0x19, 0x5); - ov511_i2c_write(dev, 0x1a, 5 + (480>>1)); + ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase); + ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale)); } for (i = 0; mlist[i].width; i++) { @@ -981,17 +1044,22 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, #ifdef OV511_GBR422 ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08); #else - ov511_i2c_write(dev, 0x12, mlist[i].common_A); + ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); #endif - ov511_i2c_write(dev, 0x14, mlist[i].common_C); - /* 7620 doesn't have register 0x35, so play it safe */ - if (ov511->sensor != SEN_OV7620) + /* 7620/6620 don't have register 0x35, so play it safe */ + if (ov511->sensor == SEN_OV7610 || + ov511->sensor == SEN_OV7620AE) ov511_i2c_write(dev, 0x35, mlist[i].common_L); break; } + if (compress) { + ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression + ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs + } + if (ov511_restart(ov511->dev) < 0) return -EIO; @@ -1028,6 +1096,16 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, * * To avoid floating point arithmetic, the color conversion * coefficients are scaled into 16.16 fixed-point integers. + * They were determined as follows: + * + * double brightness = 1.0; (0->black; 1->full scale) + * double saturation = 1.0; (0->greyscale; 1->full color) + * double fixScale = brightness * 256 * 256; + * int rvScale = (int)(1.402 * saturation * fixScale); + * int guScale = (int)(-0.344136 * saturation * fixScale); + * int gvScale = (int)(-0.714136 * saturation * fixScale); + * int buScale = (int)(1.772 * saturation * fixScale); + * int yScale = (int)(fixScale); */ /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */ @@ -1037,14 +1115,11 @@ static inline void ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int rowPixels, unsigned char * rgb) { - const double brightness = 1.0; /* 0->black; 1->full scale */ - const double saturation = 1.0; /* 0->greyscale; 1->full color */ - const double fixScale = brightness * 256 * 256; - const int rvScale = (int)(1.402 * saturation * fixScale); - const int guScale = (int)(-0.344136 * saturation * fixScale); - const int gvScale = (int)(-0.714136 * saturation * fixScale); - const int buScale = (int)(1.772 * saturation * fixScale); - const int yScale = (int)(fixScale); + const int rvScale = 91881; + const int guScale = -22553; + const int gvScale = -46801; + const int buScale = 116129; + const int yScale = 65536; int r, g, b; g = guScale * u + gvScale * v; @@ -1104,6 +1179,9 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, #undef OV511_DUMPPIX +/* #define this and OV511_DUMPPIX to disable parsing of UV data */ +#undef OV511_FORCE_MONO + #ifdef OV511_GBR422 static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, @@ -1234,6 +1312,8 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, } } #else + +#ifndef OV511_FORCE_MONO /* Just dump pix data straight out for debug */ int i, j; @@ -1246,95 +1326,124 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, } pOut0 += (iWidth - WDIV) * 3; } +#else + +#if 1 + /* This converts the Y data to "black-and-white" RGB data */ + /* Useful for experimenting with compression */ + int k, l, m; + unsigned char *pIn, *pOut, *pOut1; + + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1++ = *pIn; + *pOut1++ = *pIn; + *pOut1++ = *pIn++; + } + pOut1 += (iWidth - 8) * 3; + } + pOut += 8 * 3; + } +#else + /* This will dump the Y channel data stream as-is */ + int count; + unsigned char *pIn, *pOut; + + pIn = pIn0 + 128; + pOut = pOut0 + output_offset; + for (count = 0; count < 256; count++) { + *pOut++ = *pIn; + *pOut++ = *pIn; + *pOut++ = *pIn++; + output_offset += 3; + } +#endif + +#endif + #endif } #endif +/* This converts YUV420 segments to YUYV */ static void ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) + int iOutY, int iOutUV, int iWidth) { int k, l, m; - unsigned char *pIn; - unsigned char *pOut, *pOut1; + unsigned char *pIn, *pOut, *pOut1; - /* Just copy the Y's if in the first stripe */ - if (!iHalf) { - pIn = pIn0 + 128; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - for (m = 0; m < 8; m++) { - *pOut1++ = (*pIn++) & 0xF0; - } - pOut1 += iWidth - 8; + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1 = (*pIn++); + pOut1 += 2; } - pOut += 8; + pOut1 += (iWidth - 8) * 2; } + pOut += 8 * 2; } - /* Use the first half of VUs to calculate value */ pIn = pIn0; - pOut = pOut0 + iOutUV; - for (l = 0; l < 4; l++) { + pOut = pOut0 + iOutUV + 1; + for (l = 0; l < 8; l++) { for (m=0; m<8; m++) { - unsigned char *p00 = pOut; - unsigned char *p01 = pOut+1; - unsigned char *p10 = pOut+iWidth; - unsigned char *p11 = pOut+iWidth+1; - int v = *(pIn+64) - 128; - int u = *pIn++ - 128; - int uv = ((u >> 4) & 0x0C) + (v >> 6); - - *p00 |= uv; - *p01 |= uv; - *p10 |= uv; - *p11 |= uv; - - pOut += 2; + int v = *(pIn+64); + int u = *pIn++; + + *pOut = u; + *(pOut+2) = v; + *(pOut+iWidth) = u; + *(pOut+iWidth+2) = v; + pOut += 4; } - pOut += (iWidth*2 - 16); + pOut += (iWidth*4 - 32); } +} - /* Just copy the other UV rows */ - for (l = 0; l < 4; l++) { - for (m = 0; m < 8; m++) { - int v = *(pIn + 64) - 128; - int u = (*pIn++) - 128; - int uv = ((u >> 4) & 0x0C) + (v >> 6); - *(pOut) = uv; - pOut += 2; - } - pOut += (iWidth*2 - 16); +static void +ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iWidth, int iHeight) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + unsigned a = iWidth * iHeight; + unsigned w = iWidth / 2; + + pIn = pIn0; + pOut = pOut0 + iOutUV + a; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) *pOut1++ = *pIn++; + pOut += w; } - /* Calculate values if it's the second half */ - if (iHalf) { - pIn = pIn0 + 128; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l=0; l<4; l++) { - for (m=0; m<4; m++) { - int y10 = *(pIn+8); - int y00 = *pIn++; - int y11 = *(pIn+8); - int y01 = *pIn++; - int uv = *pOut1; + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + a + a/4; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) *pOut1++ = *pIn++; + pOut += w; + } - *pOut1 = (y00 & 0xF0) | uv; - *(pOut1+1) = (y01 & 0xF0) | uv; - *(pOut1+iWidth) = (y10 & 0xF0) | uv; - *(pOut1+iWidth+1) = (y11 & 0xF0) | uv; - - pOut1 += 2; - } - pOut1 += (iWidth*2 - 8); - pIn += 8; - } - pOut += 8; + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) + *pOut1++ =*pIn++; + pOut1 += iWidth - 8; } + pOut += 8; } } @@ -1553,6 +1662,8 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) /* Frame start */ PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); + output_offset = 0; + /* Check to see if it's a snapshot frame */ /* FIXME?? Should the snapshot reset go here? Performance? */ if (cdata[8] & 0x02) { @@ -1633,8 +1744,12 @@ check_middle: iY & 1, frame->width); break; case VIDEO_PALETTE_YUV422: - ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, - iY & 1, frame->width); + case VIDEO_PALETTE_YUYV: + ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width); + break; + case VIDEO_PALETTE_YUV420: + ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4, + frame->width, frame->height); break; case VIDEO_PALETTE_YUV422P: ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2, @@ -1674,7 +1789,7 @@ static void ov511_isoc_irq(struct urb *urb) return; if (!ov511->streaming) { - PDEBUG(2, "hmmm... not streaming, but got interrupt"); + PDEBUG(4, "hmmm... not streaming, but got interrupt"); return; } @@ -1794,7 +1909,6 @@ static void ov511_stop_isoc(struct usb_ov511 *ov511) static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) { struct ov511_frame *frame; - int width, height; PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe, framenum); @@ -1810,11 +1924,9 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) return 0; frame = &ov511->frame[framenum]; - width = frame->width; - height = frame->height; - PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, width, - height); + PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, + frame->width, frame->height); frame->grabstate = FRAME_GRABBING; frame->scanstate = STATE_SCANNING; @@ -1824,15 +1936,15 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) ov511->curframe = framenum; /* Make sure it's not too big */ - if (width > DEFAULT_WIDTH) - width = DEFAULT_WIDTH; + if (frame->width > ov511->maxwidth) + frame->width = ov511->maxwidth; - width &= ~7L; /* Multiple of 8 */ + frame->width &= ~7L; /* Multiple of 8 */ - if (height > DEFAULT_HEIGHT) - height = DEFAULT_HEIGHT; + if (frame->height > ov511->maxheight) + frame->height = ov511->maxheight; - width &= ~3L; /* Multiple of 4 */ + frame->height &= ~3L; /* Multiple of 4 */ return 0; } @@ -2006,22 +2118,24 @@ static void ov511_close(struct video_device *dev) PDEBUG(4, "ov511_close"); - down(&ov511->lock); - ov511->user--; + down(&ov511->lock); + ov511->user--; ov511_stop_isoc(ov511); - ov511_dealloc(ov511, 0); + if (ov511->dev) + ov511_dealloc(ov511, 0); + up(&ov511->lock); if (!ov511->dev) { + ov511_dealloc(ov511, 1); video_unregister_device(&ov511->vdev); kfree(ov511); ov511 = NULL; } MOD_DEC_USE_COUNT; - } static int ov511_init_done(struct video_device *dev) @@ -2058,8 +2172,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; b.channels = 1; b.audios = 0; - b.maxwidth = DEFAULT_WIDTH; - b.maxheight = DEFAULT_HEIGHT; + b.maxwidth = ov511->maxwidth; + b.maxheight = ov511->maxheight; b.minwidth = 32; b.minheight = 16; @@ -2126,6 +2240,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if (p.palette != VIDEO_PALETTE_GREY && p.palette != VIDEO_PALETTE_RGB24 && p.palette != VIDEO_PALETTE_YUV422 && + p.palette != VIDEO_PALETTE_YUYV && + p.palette != VIDEO_PALETTE_YUV420 && p.palette != VIDEO_PALETTE_YUV422P) return -EINVAL; @@ -2198,9 +2314,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) return -EINVAL; if (vw.clipcount) return -EINVAL; - if (vw.height != DEFAULT_HEIGHT) + if (vw.height != ov511->maxheight) return -EINVAL; - if (vw.width != DEFAULT_WIDTH) + if (vw.width != ov511->maxwidth) return -EINVAL; #endif @@ -2268,6 +2384,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if (vm.format != VIDEO_PALETTE_RGB24 && vm.format != VIDEO_PALETTE_YUV422 && + vm.format != VIDEO_PALETTE_YUYV && + vm.format != VIDEO_PALETTE_YUV420 && vm.format != VIDEO_PALETTE_YUV422P && vm.format != VIDEO_PALETTE_GREY) return -EINVAL; @@ -2275,7 +2393,7 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if ((vm.frame != 0) && (vm.frame != 1)) return -EINVAL; - if (vm.width > DEFAULT_WIDTH || vm.height > DEFAULT_HEIGHT) + if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) return -EINVAL; if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) @@ -2392,14 +2510,14 @@ redo: return 0; } case VIDIOCKEY: - return 0; + return 0; case VIDIOCCAPTURE: return -EINVAL; case VIDIOCSFBUF: return -EINVAL; case VIDIOCGTUNER: case VIDIOCSTUNER: - return -EINVAL; + return -EINVAL; case VIDIOCGFREQ: case VIDIOCSFREQ: return -EINVAL; @@ -2599,12 +2717,9 @@ static int ov76xx_configure(struct usb_ov511 *ov511) { OV511_I2C_BUS, 0x16, 0x06 }, { OV511_I2C_BUS, 0x28, 0x24 }, { OV511_I2C_BUS, 0x2b, 0xac }, - { OV511_I2C_BUS, 0x05, 0x00 }, - { OV511_I2C_BUS, 0x06, 0x00 }, { OV511_I2C_BUS, 0x12, 0x00 }, { OV511_I2C_BUS, 0x38, 0x81 }, { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ - { OV511_I2C_BUS, 0x05, 0x00 }, { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ { OV511_I2C_BUS, 0x15, 0x01 }, { OV511_I2C_BUS, 0x20, 0x1c }, @@ -2612,7 +2727,6 @@ static int ov76xx_configure(struct usb_ov511 *ov511) { OV511_I2C_BUS, 0x24, 0x10 }, { OV511_I2C_BUS, 0x25, 0x8a }, { OV511_I2C_BUS, 0x27, 0xc2 }, - { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ { OV511_I2C_BUS, 0x2a, 0x04 }, { OV511_I2C_BUS, 0x2c, 0xfe }, { OV511_I2C_BUS, 0x30, 0x71 }, @@ -2634,14 +2748,12 @@ static int ov76xx_configure(struct usb_ov511 *ov511) { OV511_I2C_BUS, 0x2b, 0xac }, { OV511_I2C_BUS, 0x12, 0x00 }, { OV511_I2C_BUS, 0x28, 0x24 }, - { OV511_I2C_BUS, 0x05, 0x00 }, { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ { OV511_I2C_BUS, 0x15, 0x01 }, { OV511_I2C_BUS, 0x23, 0x00 }, { OV511_I2C_BUS, 0x24, 0x10 }, { OV511_I2C_BUS, 0x25, 0x8a }, { OV511_I2C_BUS, 0x27, 0xe2 }, - { OV511_I2C_BUS, 0x29, 0x03 }, { OV511_I2C_BUS, 0x2a, 0x00 }, { OV511_I2C_BUS, 0x2c, 0xfe }, { OV511_I2C_BUS, 0x30, 0x71 }, @@ -2658,24 +2770,23 @@ static int ov76xx_configure(struct usb_ov511 *ov511) PDEBUG (4, "starting configuration"); - if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, - OV7610_I2C_WRITE_ID) < 0) + /* This looks redundant, but is necessary for WebCam 3 */ + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, + OV7610_I2C_WRITE_ID) < 0) return -1; - if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV7610_I2C_READ_ID) < 0) + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, + OV7610_I2C_READ_ID) < 0) return -1; if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) return -1; - + /* Reset the 76xx */ if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; -#if 1 /* Maybe this will fix detection problems? MM */ /* Wait for it to initialize */ schedule_timeout (1 + 150 * HZ / 1000); -#endif for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && @@ -2693,12 +2804,34 @@ static int ov76xx_configure(struct usb_ov511 *ov511) } if (success) { - PDEBUG(1, "I2C synced in %d attempt(s)", i); + PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i); } else { - err("Failed to read sensor ID. You might not have an OV76xx,"); - err("or it may be not responding. Report this to"); - err("mmcclelland@delphi.com"); - return -1; + /* Reset the 76xx */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; + + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + + i = 0; + success = 0; + while (i <= i2c_detect_tries) { + if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + break; + } else { + i++; + } + } + + if ((i == i2c_detect_tries) && (success == 0)) { + err("Failed to read sensor ID. You might not have an OV7610/20,"); + err("or it may be not responding. Report this to"); + err("mwm@i.am"); + return -1; + } else { + PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1); + } } /* Detect sensor if user didn't use override param */ @@ -2736,6 +2869,128 @@ static int ov76xx_configure(struct usb_ov511 *ov511) return -1; } + /* Set sensor-specific vars */ + ov511->maxwidth = 640; + ov511->maxheight = 480; + + if (aperture < 0) { /* go with the default */ + if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1; + } else if (aperture <= 0xf) { /* user overrode default */ + if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0) + return -1; + } else { + err("Invalid setting for aperture; legal value: 0 - 15"); + return -1; + } + + if (autoadjust) { + if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, + ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1; + } else { + if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, + ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1; + ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8); + } + + return 0; +} + +static int ov6xx0_configure(struct usb_ov511 *ov511) +{ + struct usb_device *dev = ov511->dev; + int i, success, rc; + + static struct ov511_regvals aRegvalsNorm6x20[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x03, 0xd0 }, + { OV511_I2C_BUS, 0x05, 0x7f }, + { OV511_I2C_BUS, 0x07, 0xa8 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x10, 0xff }, /* ? */ + { OV511_I2C_BUS, 0x14, 0x04 }, + { OV511_I2C_BUS, 0x16, 0x06 }, /* ? */ + { OV511_I2C_BUS, 0x19, 0x04 }, + { OV511_I2C_BUS, 0x1a, 0x93 }, + { OV511_I2C_BUS, 0x20, 0x28 }, + { OV511_I2C_BUS, 0x27, 0xa2 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2a, 0x04 }, /* 84? */ + { OV511_I2C_BUS, 0x2b, 0xac }, /* a8? */ + { OV511_I2C_BUS, 0x2d, 0x95 }, + { OV511_I2C_BUS, 0x33, 0x28 }, + { OV511_I2C_BUS, 0x34, 0xc7 }, + { OV511_I2C_BUS, 0x38, 0x8b }, + { OV511_I2C_BUS, 0x3c, 0x5c }, + { OV511_I2C_BUS, 0x3d, 0x80 }, + { OV511_I2C_BUS, 0x3f, 0x00 }, + { OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */ + { OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */ + { OV511_I2C_BUS, 0x4d, 0xd2 }, + { OV511_I2C_BUS, 0x4e, 0xc1 }, + { OV511_I2C_BUS, 0x4f, 0x04 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG (4, "starting sensor configuration"); + + /* Reset the 6xx0 */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; + + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + continue; + } + + /* Reset the 6xx0 */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + /* Dummy read to sync I2C */ + if (ov511_i2c_read(dev, 0x00) < 0) return -1; + } + + if (success) { + PDEBUG(1, "I2C synced in %d attempt(s)", i); + } else { + err("Failed to read sensor ID. You might not have an OV6xx0,"); + err("or it may be not responding. Report this to"); + err("mwm@i.am"); + return -1; + } + + /* Detect sensor if user didn't use override param */ + if (sensor == 0) { + rc = ov511_i2c_read(dev, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else { + info("Sensor is an OV6xx0 (version %d)", rc & 3); + ov511->sensor = SEN_OV6620; + } + } else { /* sensor != 0; user overrode detection */ + ov511->sensor = sensor; + info("Sensor set to type %d", ov511->sensor); + } + + /* Set sensor-specific vars */ + ov511->maxwidth = 352; + ov511->maxheight = 288; + + PDEBUG(4, "Writing 6x20 registers"); + if (ov511_write_regvals(dev, aRegvalsNorm6x20)) + return -1; + if (aperture < 0) { /* go with the default */ if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1; } else if (aperture <= 0xf) { /* user overrode default */ @@ -2781,10 +3036,10 @@ static int ov511_configure(struct usb_ov511 *ov511) { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, - { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, /* 0f */ - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x3f }, - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x3f }, - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 }, { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 }, { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 }, { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 }, @@ -2814,11 +3069,52 @@ static int ov511_configure(struct usb_ov511 *ov511) ov511->snap_enabled = snapshot; + /* Test for 76xx */ + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, + OV7610_I2C_WRITE_ID) < 0) + goto error; + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, + OV7610_I2C_READ_ID) < 0) + goto error; + + if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + goto error; + + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + /* Test for 6xx0 */ + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, + OV6xx0_I2C_WRITE_ID) < 0) + goto error; + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, + OV6xx0_I2C_READ_ID) < 0) + goto error; + + if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + goto error; + + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } + + if(ov6xx0_configure(ov511) < 0) { + err("failed to configure OV6xx0"); + goto error; + } + } else { + if(ov76xx_configure(ov511) < 0) { + err("failed to configure OV76xx"); + goto error; + } + } + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].width = DEFAULT_WIDTH; - ov511->frame[i].height = DEFAULT_HEIGHT; + ov511->frame[i].width = ov511->maxwidth; + ov511->frame[i].height = ov511->maxheight; ov511->frame[i].depth = 24; ov511->frame[i].bytes_read = 0; ov511->frame[i].segment = 0; @@ -2826,14 +3122,8 @@ static int ov511_configure(struct usb_ov511 *ov511) ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); } - /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ - - if(ov76xx_configure(ov511) < 0) { - err("failed to configure OV76xx"); - goto error; - } - - if (ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT, + /* Initialize to max width/height, RGB24 */ + if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight, VIDEO_PALETTE_RGB24, 0) < 0) goto error; @@ -2869,10 +3159,12 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) interface = &dev->actconfig->interface[ifnum].altsetting[0]; /* Is it an OV511/OV511+? */ - if (dev->descriptor.idVendor != 0x05a9) + if (dev->descriptor.idVendor != 0x05a9 + && dev->descriptor.idVendor != 0x0813) return NULL; if (dev->descriptor.idProduct != 0x0511 - && dev->descriptor.idProduct != 0xA511) + && dev->descriptor.idProduct != 0xA511 + && dev->descriptor.idProduct != 0x0002) return NULL; /* Checking vendor/product should be enough, but what the hell */ @@ -2903,6 +3195,15 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) info("USB OV511+ camera found"); ov511->bridge = BRG_OV511PLUS; break; + case 0x0002: + if (dev->descriptor.idVendor != 0x0813) + goto error; + info("Intel Play Me2Cam (OV511+) found"); + ov511->bridge = BRG_OV511PLUS; + break; + default: + err("Unknown product ID"); + goto error; } ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID); @@ -2929,7 +3230,7 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) if (clist[i].id == -1) { err("Camera type (%d) not recognized", ov511->customid); - err("Please contact mmcclelland@delphi.com to request"); + err("Please contact mwm@i.am to request"); err("support for your camera."); } @@ -2969,14 +3270,14 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) MOD_INC_USE_COUNT; + PDEBUG(3, ""); + /* We don't want people trying to open up the device */ if (!ov511->user) video_unregister_device(&ov511->vdev); + else + PDEBUG(3, "Device open...deferring video_unregister_device"); - usb_driver_release_interface(&ov511_driver, - &ov511->dev->actconfig->interface[ov511->iface]); - - ov511->dev = NULL; for (n = 0; n < OV511_NUMFRAMES; n++) ov511->frame[n].grabstate = FRAME_ERROR; @@ -3001,15 +3302,17 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) } } + usb_driver_release_interface(&ov511_driver, + &ov511->dev->actconfig->interface[ov511->iface]); + ov511->dev = NULL; + #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) destroy_proc_ov511_cam(ov511); #endif - /* FIXME - is this correct/safe? Should we acquire ov511->lock? */ - ov511_dealloc(ov511, 1); - /* Free the memory */ if (ov511 && !ov511->user) { + ov511_dealloc(ov511, 1); kfree(ov511); ov511 = NULL; } diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h index f4499b2bed8b..a4ef7dacde5b 100644 --- a/drivers/usb/ov511.h +++ b/drivers/usb/ov511.h @@ -183,6 +183,9 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args // FIXME - these can vary between specific models #define OV7610_I2C_WRITE_ID 0x42 #define OV7610_I2C_READ_ID 0x43 +#define OV6xx0_I2C_WRITE_ID 0xC0 +#define OV6xx0_I2C_READ_ID 0xC1 + #define OV511_I2C_CLOCK_PRESCALER 0x03 /* Prototypes */ @@ -203,6 +206,7 @@ enum { SEN_OV7610, SEN_OV7620, SEN_OV7620AE, + SEN_OV6620, }; enum { @@ -254,7 +258,7 @@ struct ov511_frame { int hdrheight; /* Height */ int sub_flag; /* Sub-capture mode for this frame? */ - int format; /* Format for this frame */ + unsigned int format; /* Format for this frame */ int segsize; /* How big is each segment from the camera? */ volatile int grabstate; /* State of grabbing */ @@ -285,6 +289,10 @@ struct usb_ov511 { int desc; unsigned char iface; + /* Determined by sensor type */ + int maxwidth; + int maxheight; + int brightness; int colour; int contrast; @@ -356,7 +364,6 @@ struct mode_list { u8 lndv; /* line divisor */ u8 m420; u8 common_A; - u8 common_C; u8 common_L; }; diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c index d326e58922af..baa69f4fe67e 100644 --- a/drivers/usb/scanner.c +++ b/drivers/usb/scanner.c @@ -199,6 +199,14 @@ * - Fixed HP S20 ID's...again..sigh. Thanks to Ruud * Linders . * + * 0.4.4 + * - Added addtional Mustek ID's (BearPaw 1200, 600 CU, 1200 USB, + * and 1200 UB. Thanks to Henning Meier-Geinitz . + * - Added the Vuego Scan Brisa 340U ID's. Apparently this scanner is + * marketed by Acer Peripherals as a cheap 300 dpi model. Thanks to + * David Gundersen . + * - Added the Epson Expression1600 ID's. Thanks to Karl Heinz + * Kremer . * * TODO * diff --git a/drivers/usb/scanner.h b/drivers/usb/scanner.h index 7662f125e1f4..bf01c808273a 100644 --- a/drivers/usb/scanner.h +++ b/drivers/usb/scanner.h @@ -106,6 +106,7 @@ static const struct scanner_device { /* Acer */ { 0x04a5, 0x2060 }, /* Prisa Acerscan 620U & 640U (!) */ { 0x04a5, 0x2040 }, /* Prisa AcerScan 620U (!) */ + { 0x04a5, 0x2022 }, /* Vuego Scan Brisa 340U */ /* Agfa */ { 0x06bd, 0x0001 }, /* SnapScan 1212U */ { 0x06bd, 0x2061 }, /* Another SnapScan 1212U (?) */ @@ -120,6 +121,7 @@ static const struct scanner_device { { 0x03f0, 0x0105 }, /* 4200C */ { 0x03f0, 0x0102 }, /* PhotoSmart S20 */ { 0x03f0, 0x0401 }, /* 5200C */ + { 0x03f0, 0x0701 }, /* 5300C */ { 0x03f0, 0x0201 }, /* 6200C */ { 0x03f0, 0x0601 }, /* 6300C */ /* iVina */ @@ -134,6 +136,10 @@ static const struct scanner_device { { 0x05da, 0x80ac }, /* ScanMaker V6UL - SpicyU */ /* Mustek */ { 0x055f, 0x0001 }, /* 1200 CU */ + { 0x0400, 0x1000 }, /* BearPaw 1200 */ + { 0x055f, 0x0002 }, /* 600 CU */ + { 0x055f, 0x0003 }, /* 1200 USB */ + { 0x055f, 0x0006 }, /* 1200 UB */ /* Primax/Colorado */ { 0x0461, 0x0300 }, /* G2-300 #1 */ { 0x0461, 0x0380 }, /* G2-600 #1 */ @@ -151,15 +157,19 @@ static const struct scanner_device { { 0x04b8, 0x0101 }, /* Perfection 636U and 636Photo */ { 0x04b8, 0x0103 }, /* Perfection 610 */ { 0x04b8, 0x0104 }, /* Perfection 1200U and 1200Photo */ + { 0x04b8, 0x0107 }, /* Expression 1600 */ /* Umax */ { 0x1606, 0x0010 }, /* Astra 1220U */ { 0x1606, 0x0002 }, /* Astra 1236U */ { 0x1606, 0x0030 }, /* Astra 2000U */ { 0x1606, 0x0230 }, /* Astra 2200U */ /* Visioneer */ - { 0x04a7, 0x0221 }, /* OneTouch 5300 */ - { 0x04a7, 0x0221 }, /* OneTouch 7600 duplicate ID (!) */ - { 0x04a7, 0x0231 }, /* 6100 */ + { 0x04a7, 0x0221 }, /* OneTouch 5300 USB */ + { 0x04a7, 0x0211 }, /* OneTouch 7600 USB */ + { 0x04a7, 0x0231 }, /* 6100 USB */ + { 0x04a7, 0x0311 }, /* 6200 EPP/USB */ + { 0x04a7, 0x0321 }, /* OneTouch 8100 EPP/USB */ + { 0x04a7, 0x0331 }, /* OneTouch 8600 EPP/USB */ }; /* Forward declarations */ diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index 71948e53ae79..6a45764d1787 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -1,6 +1,6 @@ /* Driver for Freecom USB/IDE adaptor * - * $Id: freecom.c,v 1.8 2000/08/29 14:49:15 dlbrown Exp $ + * $Id: freecom.c,v 1.11 2000/09/15 23:06:40 mdharm Exp $ * * Freecom v0.1: * @@ -28,6 +28,7 @@ * (http://www.freecom.de/) */ +#include #include "transport.h" #include "protocol.h" #include "usb.h" @@ -55,7 +56,7 @@ struct freecom_xfer_wrap { __u8 Timeout; /* Timeout in seconds. */ __u32 Count; /* Number of bytes to transfer. */ __u8 Pad[58]; -}; +} __attribute__ ((packed)); struct freecom_ide_out { __u8 Type; /* Type + IDE register. */ @@ -88,6 +89,9 @@ struct freecom_status { * waited, so the data should be immediately available. */ #define FCM_PACKET_INPUT 0x81 +/* Send data to the IDE interface. */ +#define FCM_PACKET_OUTPUT 0x01 + /* Write a value to an ide register. Or the ide register to write after * munging the addres a bit. */ #define FCM_PACKET_IDE_WRITE 0x40 @@ -96,6 +100,61 @@ struct freecom_status { /* All packets (except for status) are 64 bytes long. */ #define FCM_PACKET_LENGTH 64 +/* + * Transfer an entire SCSI command's worth of data payload over the bulk + * pipe. + * + * Note that this uses us_transfer_partial to achieve it's goals -- this + * function simply determines if we're going to use scatter-gather or not, + * and acts appropriately. For now, it also re-interprets the error codes. + */ +static void us_transfer_freecom(Scsi_Cmnd *srb, struct us_data* us, int transfer_amount) +{ + int i; + int result = -1; + struct scatterlist *sg; + unsigned int total_transferred = 0; + + /* was someone foolish enough to request more data than available + * buffer space? */ + if (transfer_amount > srb->request_bufflen) + transfer_amount = srb->request_bufflen; + + /* are we scatter-gathering? */ + if (srb->use_sg) { + + /* loop over all the scatter gather structures and + * make the appropriate requests for each, until done + */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) { + + /* transfer the lesser of the next buffer or the + * remaining data */ + if (transfer_amount - total_transferred >= + sg[i].length) { + result = us_transfer_partial(us, sg[i].address, + sg[i].length); + total_transferred += sg[i].length; + } else + result = us_transfer_partial(us, sg[i].address, + transfer_amount - total_transferred); + + /* if we get an error, end the loop here */ + if (result) + break; + } + } + else + /* no scatter-gather, just make the request */ + result = us_transfer_partial(us, srb->request_buffer, + transfer_amount); + + /* return the result in the data structure itself */ + srb->result = result; +} + + /* Write a value to an ide register. */ static int freecom_ide_write (struct us_data *us, int reg, int value) @@ -231,6 +290,9 @@ freecom_readdata (Scsi_Cmnd *srb, struct us_data *us, result, partial); /* Now transfer all of our blocks. */ + printk (KERN_DEBUG "Start of read\n"); + us_transfer_freecom(srb, us, count); +#if 0 if (srb->use_sg) { US_DEBUGP ("Need to implement scatter-gather\n"); return USB_STOR_TRANSPORT_ERROR; @@ -287,11 +349,115 @@ freecom_readdata (Scsi_Cmnd *srb, struct us_data *us, offset += this_read; } } +#endif printk (KERN_DEBUG "freecom_readdata done!\n"); return USB_STOR_TRANSPORT_GOOD; } +static int +freecom_writedata (Scsi_Cmnd *srb, struct us_data *us, + int ipipe, int opipe, int count) +{ + freecom_udata_t extra = (freecom_udata_t) us->extra; + struct freecom_xfer_wrap *fxfr = + (struct freecom_xfer_wrap *) extra->buffer; + int result, partial; + int offset; + int this_write; + __u8 *buffer = extra->buffer; + + fxfr->Type = FCM_PACKET_OUTPUT | 0x00; + fxfr->Timeout = 0; /* Short timeout for debugging. */ + fxfr->Count = cpu_to_le32 (count); + memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); + + printk (KERN_DEBUG "Write data Freecom! (c=%d)\n", count); + + /* Issue the transfer command. */ + result = usb_stor_bulk_msg (us, fxfr, opipe, + FCM_PACKET_LENGTH, &partial); + if (result != 0) { + US_DEBUGP ("Freecom writedata xpot failure: r=%d, p=%d\n", + result, partial); + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("us_transfer_partial(): transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + + return USB_STOR_TRANSPORT_ERROR; + } + printk (KERN_DEBUG "Done issuing write request: %d %d\n", + result, partial); + + /* Now transfer all of our blocks. */ + printk (KERN_DEBUG "Start of write\n"); + us_transfer_freecom(srb, us, count); +#if 0 + if (srb->use_sg) { + US_DEBUGP ("Need to implement scatter-gather\n"); + return USB_STOR_TRANSPORT_ERROR; + } else { + offset = 0; + + while (offset < count) { +#if 1 + this_write = count - offset; + if (this_write > 64) + this_write = 64; +#else + this_write = 64; +#endif + + printk (KERN_DEBUG "Start of write\n"); + /* Use the given buffer directly, but only if there + * is space for an entire packet. */ + + if (offset + 64 <= srb->request_bufflen) { + result = usb_stor_bulk_msg ( + us, srb->request_buffer+offset, + opipe, this_write, &partial); + printk (KERN_DEBUG "Write111 = %d, %d\n", + result, partial); + pdump (srb->request_buffer+offset, + partial); + } else { + result = usb_stor_bulk_msg ( + us, buffer, + opipe, this_write, &partial); + printk (KERN_DEBUG "Write112 = %d, %d\n", + result, partial); + memcpy (buffer, + srb->request_buffer+offset, + srb->request_bufflen - offset); + pdump (srb->request_buffer+offset, + srb->request_bufflen - offset); + } + + if (result != 0) { + US_DEBUGP ("Freecom writeblock r=%d, p=%d\n", + result, partial); + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("us_transfer_partial(): transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + + return USB_STOR_TRANSPORT_ERROR; + } + + offset += this_write; + } + } +#endif + + printk (KERN_DEBUG "freecom_writedata done!\n"); + return USB_STOR_TRANSPORT_GOOD; +} + /* * Transport for the Freecom USB/IDE adaptor. * @@ -401,6 +567,36 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) if (result != USB_STOR_TRANSPORT_GOOD) return result; + printk (KERN_DEBUG "FCM: Waiting for status\n"); + result = usb_stor_bulk_msg (us, fst, ipipe, + FCM_PACKET_LENGTH, &partial); + pdump ((void *) fst, partial); + if (result == -ENOENT) { + US_DEBUGP ("freecom_transport: transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + if (partial != 4 || result != 0) + return USB_STOR_TRANSPORT_ERROR; + if ((fst->Status & ERR_STAT) != 0) { + printk (KERN_DEBUG "operation failed\n"); + return USB_STOR_TRANSPORT_FAILED; + } + if ((fst->Reason & 3) != 3) { + printk (KERN_DEBUG "Drive seems still hungry\n"); + return USB_STOR_TRANSPORT_FAILED; + } + printk (KERN_DEBUG "Transfer happy\n"); + break; + + case SCSI_DATA_WRITE: + /* Make sure the status indicates that the device wants to + * send us data. */ + /* !!IMPLEMENT!! */ + result = freecom_writedata (srb, us, ipipe, opipe, length); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + +#if 1 printk (KERN_DEBUG "FCM: Waiting for status\n"); result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); @@ -418,9 +614,15 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) printk (KERN_DEBUG "Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } +#endif printk (KERN_DEBUG "Transfer happy\n"); break; + + case SCSI_DATA_NONE: + /* Easy, do nothing. */ + break; + default: US_DEBUGP ("freecom unimplemented direction: %d\n", us->srb->sc_data_direction); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index d312ef6780d8..90a6ddff1430 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,7 +1,7 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.10 2000/08/30 01:22:42 mdharm Exp $ + * $Id: scsiglue.c,v 1.11 2000/09/12 01:18:08 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -124,9 +124,7 @@ static int release(struct Scsi_Host *psh) wake_up(&(us->wqh)); down(&(us->notify)); - /* free the data structure we were using */ - US_DEBUGP("-- freeing URB\n"); - kfree(us->current_urb); + /* remove the pointer to the data structure we were using */ (struct us_data*)psh->hostdata[0] = NULL; /* we always have a successful release */ diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 6fb0db93680c..dc0fd4fe72e5 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,6 +1,6 @@ /* Driver for SCM Microsystems USB-ATAPI cable * - * $Id: shuttle_usbat.c,v 1.7 2000/09/04 02:08:42 groovyjava Exp $ + * $Id: shuttle_usbat.c,v 1.8 2000/09/08 23:20:41 groovyjava Exp $ * * SCM driver v0.2: * @@ -64,7 +64,7 @@ extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe, extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, unsigned int len, unsigned int *act_len); -#define short_pack(b1,b2) ( ((u16)(b1)) | ( ((u16)(b2))<<8 ) ) +#define short_pack(LSB,MSB) ( ((u16)(LSB)) | ( ((u16)(MSB))<<8 ) ) #define LSB_of(s) ((s)&0xFF) #define MSB_of(s) ((s)>>8) @@ -208,7 +208,8 @@ static int usbat_raw_bulk(struct us_data *us, return US_BULK_TRANSFER_SHORT; } - US_DEBUGP("Transfered %d of %d bytes\n", act_len, len); + US_DEBUGP("Transferred %s %d of %d bytes\n", + direction==SCSI_DATA_READ ? "in" : "out", act_len, len); return US_BULK_TRANSFER_GOOD; } @@ -285,9 +286,9 @@ static int usbat_bulk_transport(struct us_data *us, } */ - US_DEBUGP("SCM data %s transfer %d sg buffers %d\n", - ( direction==SCSI_DATA_READ ? "in" : "out"), - len, use_sg); + //US_DEBUGP("SCM data %s transfer %d sg buffers %d\n", + // ( direction==SCSI_DATA_READ ? "in" : "out"), + // len, use_sg); if (!use_sg) result = usbat_raw_bulk(us, direction, data, len); @@ -574,9 +575,9 @@ int usbat_rw_block_test(struct us_data *us, } - US_DEBUGP("Transfer %s %d bytes, sg buffers %d\n", - direction == SCSI_DATA_WRITE ? "out" : "in", - len, use_sg); + //US_DEBUGP("Transfer %s %d bytes, sg buffers %d\n", + // direction == SCSI_DATA_WRITE ? "out" : "in", + // len, use_sg); result = usbat_bulk_transport(us, NULL, 0, direction, content, len, use_sg); @@ -730,6 +731,126 @@ int usbat_write_user_io(struct us_data *us, return result; } +/* + * Squeeze a potentially huge (> 65535 byte) read10 command into + * a little ( <= 65535 byte) ATAPI pipe + */ + +int usbat_handle_read10(struct us_data *us, + unsigned char *registers, + unsigned char *data, + Scsi_Cmnd *srb) { + + int result = USB_STOR_TRANSPORT_GOOD; + unsigned char *buffer; + unsigned int len; + unsigned int sector; + unsigned int amount; + struct scatterlist *sg = NULL; + int sg_segment = 0; + int sg_offset = 0; + + if (srb->request_bufflen < 0x10000) { + + result = usbat_rw_block_test(us, USBAT_ATA, + registers, data, 19, + 0x10, 0x17, 0xFD, 0x30, + SCSI_DATA_READ, + srb->request_buffer, + srb->request_bufflen, srb->use_sg); + + return result; + } + + /* + * Since we're requesting more data than we can handle in + * a single read command (max is 64k-1), we will perform + * multiple reads, but each read must be in multiples of + * a sector. Luckily the sector size is in srb->transfersize + * (see linux/drivers/scsi/sr.c). + */ + + + len = (65535/srb->transfersize) * srb->transfersize; + US_DEBUGP("Max read is %d bytes\n", len); + buffer = kmalloc(len, GFP_KERNEL); + if (buffer == NULL) // bloody hell! + return USB_STOR_TRANSPORT_FAILED; + sector = short_pack(data[7+3], data[7+2]); + sector <<= 16; + sector |= short_pack(data[7+5], data[7+4]); + transferred = 0; + + if (srb->use_sg) { + sg = (struct scatterlist *)srb->request_buffer; + sg_segment = 0; // for keeping track of where we are in + sg_offset = 0; // the scatter/gather list + } + + while (transferred != srb->request_bufflen) { + + if (len > srb->request_bufflen - transferred) + len = srb->request_bufflen - transferred; + + data[3] = len&0xFF; // (cylL) = expected length (L) + data[4] = (len>>8)&0xFF; // (cylH) = expected length (H) + + // Fix up the SCSI command sector and num sectors + + data[7+2] = MSB_of(sector>>16); // SCSI command sector + data[7+3] = LSB_of(sector>>16); + data[7+4] = MSB_of(sector&0xFFFF); + data[7+5] = LSB_of(sector&0xFFFF); + data[7+7] = MSB_of(len / srb->transfersize); // SCSI command + data[7+8] = LSB_of(len / srb->transfersize); // num sectors + + result = usbat_rw_block_test(us, USBAT_ATA, + registers, data, 19, + 0x10, 0x17, 0xFD, 0x30, + SCSI_DATA_READ, + buffer, + len, 0); + + if (result != USB_STOR_TRANSPORT_GOOD) + break; + + // Transfer the received data into the srb buffer + + if (!srb->use_sg) { + memcpy(srb->request_buffer+transferred, buffer, len); + } else { + amount = 0; + while (amount= + sg[sg_segment].length-sg_offset) { + memcpy(sg[sg_segment].address + sg_offset, + buffer + amount, + sg[sg_segment].length - sg_offset); + amount += + sg[sg_segment].length-sg_offset; + sg_segment++; + sg_offset=0; + } else { + memcpy(sg[sg_segment].address + sg_offset, + buffer + amount, + len - amount); + sg_offset += (len - amount); + amount = len; + } + } + } + + // Update the amount transferred and the sector number + + transferred += len; + sector += len / srb->transfersize; + + } // while transferred != srb->request_bufflen + + kfree(buffer); + return result; +} + static int hp_8200e_select_and_test_registers(struct us_data *us) { int result; @@ -915,6 +1036,7 @@ int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us) unsigned int len; int i; char string[64]; + // struct scatterlist *sg; /* This table tells us: X = command not supported @@ -1003,12 +1125,6 @@ int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us) } } */ - if (len > 0xFFFF) { - US_DEBUGP("Error: len = %08X... what do I do now?\n", - len); - return USB_STOR_TRANSPORT_ERROR; - } - // US_DEBUGP("XXXXXXXXXXXXXXXX req_bufflen %d, len %d, bufflen %d\n", // srb->request_bufflen, len, srb->bufflen); @@ -1058,14 +1174,14 @@ int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us) } else if (srb->cmnd[0] == READ_10) { - result = usbat_rw_block_test(us, USBAT_ATA, - registers, data, 19, - 0x10, 0x17, 0xFD, 0x30, - SCSI_DATA_READ, - srb->request_buffer, - len, srb->use_sg); + return usbat_handle_read10(us, registers, data, srb); - return result; + } + + if (len > 0xFFFF) { + US_DEBUGP("Error: len = %08X... what do I do now?\n", + len); + return USB_STOR_TRANSPORT_ERROR; } if ( (result = usbat_multiple_write(us, diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 60cdc821e138..b15d7c5e887a 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,6 +1,6 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: transport.c,v 1.23 2000/09/08 21:20:06 mdharm Exp $ + * $Id: transport.c,v 1.25 2000/09/13 21:48:26 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -48,7 +48,6 @@ #include "usb.h" #include "debug.h" -#include #include #include #include @@ -870,8 +869,12 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us) US_DEBUGP("CBI data stage result is 0x%x\n", srb->result); /* if it was aborted, we need to indicate that */ - if (srb->result == USB_STOR_TRANSPORT_ABORTED) + if (srb->result == USB_STOR_TRANSPORT_ABORTED) { + /* we need to reset the state of this semaphore */ + down(&(us->ip_waitq)); + return USB_STOR_TRANSPORT_ABORTED; + } } /* STATUS STAGE */ @@ -1147,8 +1150,9 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; case US_BULK_STAT_PHASE: - /* phase error */ - usb_stor_Bulk_reset(us); + /* phase error -- note that a transport reset will be + * invoked by the invoke_transport() function + */ return USB_STOR_TRANSPORT_ERROR; } @@ -1177,11 +1181,15 @@ int usb_stor_CB_reset(struct us_data *us) USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, cmd, sizeof(cmd), HZ*5); - if (result < 0) + if (result < 0) { US_DEBUGP("CB[I] soft reset failed %d\n", result); + return FAILED; + } /* long wait for reset */ + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ*6); + set_current_state(TASK_RUNNING); US_DEBUGP("CB_reset: clearing endpoint halt\n"); clear_halt(us->pusb_dev, @@ -1191,7 +1199,7 @@ int usb_stor_CB_reset(struct us_data *us) US_DEBUGP("CB_reset done\n"); /* return a result code based on the result of the control message */ - return result < 0 ? FAILED : SUCCESS; + return SUCCESS; } /* This issues a Bulk-only Reset to the device in question, including @@ -1209,17 +1217,20 @@ int usb_stor_Bulk_reset(struct us_data *us) USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, NULL, 0, HZ*5); - if (result < 0) + if (result < 0) { US_DEBUGP("Bulk soft reset failed %d\n", result); + return FAILED; + } /* long wait for reset */ + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ*6); + set_current_state(TASK_RUNNING); clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out)); - - /* return a result code based on the result of the control message */ - return result < 0 ? FAILED : SUCCESS; + US_DEBUGP("Bulk soft reset completed\n"); + return SUCCESS; } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 4c36c532e97a..bcdd9abeec8f 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,6 +1,6 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: usb.c,v 1.39 2000/09/08 21:20:06 mdharm Exp $ + * $Id: usb.c,v 1.44 2000/09/14 22:56:36 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -43,7 +43,6 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include "usb.h" #include "scsiglue.h" #include "transport.h" @@ -63,6 +62,7 @@ #include "freecom.h" #endif +#include #include #include #include @@ -331,6 +331,12 @@ static int usb_stor_control_thread(void * __us) */ static struct us_unusual_dev us_unusual_dev_list[] = { + { 0x03ee, 0x0000, 0x0000, 0x0245, + "Mitsumi", + "CD-R/RW Drive", + US_SC_8020, US_PR_CBI, NULL, + US_FL_SINGLE_LUN}, + { 0x03f0, 0x0107, 0x0200, 0x0200, "HP", "CD-Writer+", @@ -415,9 +421,9 @@ static struct us_unusual_dev us_unusual_dev_list[] = { US_SC_8020, US_PR_CB, NULL, US_FL_SINGLE_LUN}, - { 0x054c, 0x0010, 0x0210, 0x0210, + { 0x054c, 0x0010, 0x0106, 0x0210, "Sony", - "DSC-S30/S70/505V", + "DSC-S30/S70/505V/F505", US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE }, @@ -507,7 +513,7 @@ static struct us_unusual_dev us_unusual_dev_list[] = { { 0x07ab, 0xfc01, 0x0921, 0x0921, "Freecom", "USB-IDE", - US_SC_8070, US_PR_FREECOM, freecom_init, US_FL_SINGLE_LUN }, + US_SC_QIC, US_PR_FREECOM, freecom_init, US_FL_SINGLE_LUN }, #endif { 0x07af, 0x0005, 0x0100, 0x0100, @@ -725,6 +731,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } /* At this point, we're committed to using the device */ + usb_inc_dev_use(dev); /* clear the GUID and fetch the strings */ GUID_CLEAR(guid); @@ -784,8 +791,14 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) return NULL; - /* Re-Initialize the device if it needs it */ + /* allocate the URB we're going to use */ + ss->current_urb = usb_alloc_urb(0); + if (!ss->current_urb) { + kfree(ss); + return NULL; + } + /* Re-Initialize the device if it needs it */ if (unusual_dev && unusual_dev->initFunction) (unusual_dev->initFunction)(ss); @@ -1003,7 +1016,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) /* Just before we start our control thread, initialize * the device if it needs initialization */ if (unusual_dev && unusual_dev->initFunction) - (unusual_dev->initFunction)(ss); + unusual_dev->initFunction(ss); /* start up our control thread */ ss->pid = kernel_thread(usb_stor_control_thread, ss, @@ -1060,18 +1073,26 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) /* release the IRQ, if we have one */ down(&(ss->irq_urb_sem)); if (ss->irq_urb) { - US_DEBUGP("-- releasing irq handle\n"); + US_DEBUGP("-- releasing irq URB\n"); result = usb_unlink_urb(ss->irq_urb); - ss->irq_urb = NULL; US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); usb_free_urb(ss->irq_urb); + ss->irq_urb = NULL; } up(&(ss->irq_urb_sem)); + /* free up the main URB for this device */ + US_DEBUGP("-- releasing main URB\n"); + result = usb_unlink_urb(ss->current_urb); + US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); + usb_free_urb(ss->current_urb); + ss->current_urb = NULL; + /* mark the device as gone */ + usb_dec_dev_use(ss->pusb_dev); ss->pusb_dev = NULL; - /* lock access to the device data structure */ + /* unlock access to the device data structure */ up(&(ss->dev_semaphore)); } @@ -1132,8 +1153,7 @@ void __exit usb_stor_exit(void) if (us_list->extra) { if (us_list->extra_destructor) - (*us_list->extra_destructor)( - us_list->extra); + us_list->extra_destructor(us_list->extra); kfree(us_list->extra); } kfree (us_list); @@ -1146,5 +1166,5 @@ void __exit usb_stor_exit(void) up(&us_list_semaphore); } -module_init(usb_stor_init) ; -module_exit(usb_stor_exit) ; +module_init(usb_stor_init); +module_exit(usb_stor_exit); diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 9470f9627bf3..0a0d32f690f1 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -493,8 +493,6 @@ struct urb_priv *uhci_alloc_urb_priv(struct urb *urb) urb->hcpriv = urbp; - usb_inc_dev_use(urb->dev); - return urbp; } @@ -556,8 +554,6 @@ static void uhci_destroy_urb_priv(struct urb *urb) urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp); - usb_dec_dev_use(urb->dev); - unlock: spin_unlock_irqrestore(&urb->lock, flags); } @@ -739,8 +735,6 @@ static int uhci_submit_control(struct urb *urb) uhci_add_urb_list(uhci, urb); - usb_inc_dev_use(urb->dev); - return -EINPROGRESS; } @@ -1316,10 +1310,13 @@ static int uhci_submit_urb(struct urb *urb) if (u && !(urb->transfer_flags & USB_QUEUE_BULK)) return -ENXIO; + usb_inc_dev_use(urb->dev); spin_lock_irqsave(&urb->lock, flags); if (!uhci_alloc_urb_priv(urb)) { spin_unlock_irqrestore(&urb->lock, flags); + usb_dec_dev_use(urb->dev); + return -ENOMEM; } @@ -1329,17 +1326,16 @@ static int uhci_submit_urb(struct urb *urb) break; case PIPE_INTERRUPT: if (urb->bandwidth == 0) { /* not yet checked/allocated */ - bustime = usb_check_bandwidth (urb->dev, urb); + bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else { ret = uhci_submit_interrupt(urb); if (ret == -EINPROGRESS) - usb_claim_bandwidth (urb->dev, urb, bustime, 0); + usb_claim_bandwidth(urb->dev, urb, bustime, 0); } - } else { /* bandwidth is already set */ + } else /* bandwidth is already set */ ret = uhci_submit_interrupt(urb); - } break; case PIPE_BULK: ret = uhci_submit_bulk(urb, u); @@ -1350,7 +1346,7 @@ static int uhci_submit_urb(struct urb *urb) ret = -EINVAL; break; } - bustime = usb_check_bandwidth (urb->dev, urb); + bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; break; @@ -1358,10 +1354,9 @@ static int uhci_submit_urb(struct urb *urb) ret = uhci_submit_isochronous(urb); if (ret == -EINPROGRESS) - usb_claim_bandwidth (urb->dev, urb, bustime, 1); - } else { /* bandwidth is already set */ + usb_claim_bandwidth(urb->dev, urb, bustime, 1); + } else /* bandwidth is already set */ ret = uhci_submit_isochronous(urb); - } break; } @@ -1371,8 +1366,10 @@ static int uhci_submit_urb(struct urb *urb) if (ret == -EINPROGRESS) ret = 0; - else + else { uhci_unlink_generic(urb); + usb_dec_dev_use(urb->dev); + } return ret; } @@ -1384,6 +1381,7 @@ static int uhci_submit_urb(struct urb *urb) */ static void uhci_transfer_result(struct urb *urb) { + struct usb_device *dev = urb->dev; struct urb *turb; int proceed = 0, is_ring = 0; int ret = -EINVAL; @@ -1420,22 +1418,23 @@ static void uhci_transfer_result(struct urb *urb) /* Release bandwidth for Interrupt or Isoc. transfers */ /* Spinlock needed ? */ if (urb->bandwidth) - usb_release_bandwidth (urb->dev, urb, 1); + usb_release_bandwidth(urb->dev, urb, 1); uhci_unlink_generic(urb); break; case PIPE_INTERRUPT: /* Interrupts are an exception */ - urb->complete(urb); - if (urb->interval) + if (urb->interval) { + urb->complete(urb); uhci_reset_interrupt(urb); - else { - /* Release bandwidth for Interrupt or Isoc. transfers */ - /* Spinlock needed ? */ - if (urb->bandwidth) - usb_release_bandwidth (urb->dev, urb, 0); - uhci_unlink_generic(urb); + return; } - return; /* <-- Note the return */ + + /* Release bandwidth for Interrupt or Isoc. transfers */ + /* Spinlock needed ? */ + if (urb->bandwidth) + usb_release_bandwidth(urb->dev, urb, 0); + uhci_unlink_generic(urb); + break; } if (urb->next) { @@ -1453,7 +1452,7 @@ static void uhci_transfer_result(struct urb *urb) is_ring = 1; } - if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) { + if (urb->complete && !proceed) { urb->complete(urb); if (!proceed && is_ring) uhci_submit_urb(urb); @@ -1468,9 +1467,12 @@ static void uhci_transfer_result(struct urb *urb) turb = turb->next; } while (turb && turb != urb->next); - if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) + if (urb->complete) urb->complete(urb); } + + /* We decrement the usage count after we're done with everything */ + usb_dec_dev_use(dev); } static int uhci_unlink_generic(struct urb *urb) @@ -1494,6 +1496,8 @@ static int uhci_unlink_generic(struct urb *urb) uhci_destroy_urb_priv(urb); + urb->dev = NULL; + return 0; } @@ -1520,10 +1524,10 @@ static int uhci_unlink_urb(struct urb *urb) if (urb->bandwidth) { switch (usb_pipetype(urb->pipe)) { case PIPE_INTERRUPT: - usb_release_bandwidth (urb->dev, urb, 0); + usb_release_bandwidth(urb->dev, urb, 0); break; case PIPE_ISOCHRONOUS: - usb_release_bandwidth (urb->dev, urb, 1); + usb_release_bandwidth(urb->dev, urb, 1); break; default: break; diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 17cd8d8ef6cc..468cfe3f275e 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1898,8 +1898,6 @@ int usb_new_device(struct usb_device *dev) { int err; - info("USB new device connect, assigned device number %d", dev->devnum); - /* USB v1.1 5.5.3 */ /* We read the first 8 bytes from the device descriptor to get to */ /* the bMaxPacketSize0 field. Then we set the maximum packet size */ diff --git a/fs/Config.in b/fs/Config.in index 183aab8f8974..50d28469de5c 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -100,8 +100,11 @@ if [ "$CONFIG_NET" = "y" ]; then dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET if [ "$CONFIG_SMB_FS" != "n" ]; then - string 'Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "" - fi + bool ' Use a default NLS' CONFIG_SMB_NLS_DEFAULT + if [ "$CONFIG_SMB_NLS_DEFAULT" = "y" ]; then + string ' Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "cp437" + fi + fi if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS source fs/ncpfs/Config.in diff --git a/fs/coda/cache.c b/fs/coda/cache.c index e837db96a990..b1c1c1c94ffa 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -24,168 +24,60 @@ #include #include -/* create a new acl cache entry and enlist it */ -static struct coda_cache *coda_cache_create(struct inode *inode) -{ - struct coda_inode_info *cii = ITOC(inode); - struct coda_sb_info *sbi = coda_sbp(inode->i_sb); - struct coda_cache *cc = NULL; - ENTRY; - - if ( !sbi || !cii ) { - printk("coda_cache_create: NULL sbi or cii!\n"); - return NULL; - } - - CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc)); - - if ( !cc ) { - printk("Out of memory in coda_cache_create!\n"); - return NULL; - } - - coda_load_creds(&cc->cc_cred); - cc->cc_mask = 0; - - INIT_LIST_HEAD(&cc->cc_cclist); - INIT_LIST_HEAD(&cc->cc_cnlist); - list_add(&cc->cc_cclist, &sbi->sbi_cchead); - list_add(&cc->cc_cnlist, &cii->c_cnhead); - - return cc; -} - -/* destroy an acl cache entry */ -static void coda_cache_destroy(struct coda_cache *el) -{ - ENTRY; - if (list_empty(&el->cc_cclist) || list_empty(&el->cc_cnlist)) { - printk("coda_cache_destroy: loose entry!"); - return; - } - list_del(&el->cc_cclist); - list_del(&el->cc_cnlist); - CODA_FREE(el, sizeof(struct coda_cache)); -} - -/* see if there is a match for the current - credentials already */ -static struct coda_cache * coda_cache_find(struct inode *inode) -{ - struct coda_inode_info *cii = ITOC(inode); - struct list_head *le; - struct coda_cache *cc = NULL; - - list_for_each(le, &cii->c_cnhead) - { - /* compare name and creds */ - cc = list_entry(le, struct coda_cache, cc_cnlist); - if ( !coda_cred_ok(&cc->cc_cred) ) - continue; - CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); - return cc; /* cache hit */ - } - return NULL; -} - -/* create or extend an acl cache hit */ +/* replace or extend an acl cache hit */ void coda_cache_enter(struct inode *inode, int mask) { - struct coda_cache *cc; - - cc = coda_cache_find(inode); + struct coda_inode_info *cii = ITOC(inode); + ENTRY; - if (!cc) - cc = coda_cache_create(inode); - if (cc) - cc->cc_mask |= mask; + if ( !coda_cred_ok(&cii->c_cached_cred) ) { + coda_load_creds(&cii->c_cached_cred); + cii->c_cached_perm = mask; + } else + cii->c_cached_perm |= mask; } -/* remove all cached acl matches from an inode */ +/* remove cached acl from an inode */ void coda_cache_clear_inode(struct inode *inode) { - struct list_head *le; - struct coda_inode_info *cii; - struct coda_cache *cc; + struct coda_inode_info *cii = ITOC(inode); ENTRY; - - if ( !inode ) { - CDEBUG(D_CACHE, "coda_cache_clear_inode: NULL inode\n"); - return; - } - cii = ITOC(inode); - - le = cii->c_cnhead.next; - while ( le != &cii->c_cnhead ) { - cc = list_entry(le, struct coda_cache, cc_cnlist); - le = le->next; - coda_cache_destroy(cc); - } + cii->c_cached_perm = 0; } -/* remove all acl caches */ -void coda_cache_clear_all(struct super_block *sb) +/* remove all acl caches for a principal (or all principals when cred == NULL)*/ +void coda_cache_clear_all(struct super_block *sb, struct coda_cred *cred) { - struct list_head *le; - struct coda_cache *cc; - struct coda_sb_info *sbi = coda_sbp(sb); + struct coda_sb_info *sbi; + struct coda_inode_info *cii; + struct list_head *tmp; - if ( !sbi ) { - printk("coda_cache_clear_all: NULL sbi\n"); - return; - } - - le = sbi->sbi_cchead.next; - while ( le != &sbi->sbi_cchead ) { - cc = list_entry(le, struct coda_cache, cc_cclist); - le = le->next; - coda_cache_destroy(cc); - } -} + ENTRY; + sbi = coda_sbp(sb); + if (!sbi) BUG(); -/* remove all acl caches for a principal */ -void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) -{ - struct list_head *le; - struct coda_cache *cc; - struct coda_sb_info *sbi = coda_sbp(sb); + list_for_each(tmp, &sbi->sbi_cihead) + { + cii = list_entry(tmp, struct coda_inode_info, c_cilist); + if ( cii->c_magic != CODA_CNODE_MAGIC ) continue; - if ( !sbi ) { - printk("coda_cache_clear_all: NULL sbi\n"); - return; - } - - le = sbi->sbi_cchead.next; - while ( le != &sbi->sbi_cchead ) { - cc = list_entry(le, struct coda_cache, cc_cclist); - le = le->next; - if ( coda_cred_eq(&cc->cc_cred, cred)) - coda_cache_destroy(cc); + if (!cred || coda_cred_eq(cred, &cii->c_cached_cred)) + cii->c_cached_perm = 0; } } -/* check if the mask has been matched against the acl - already */ +/* check if the mask has been matched against the acl already */ int coda_cache_check(struct inode *inode, int mask) { struct coda_inode_info *cii = ITOC(inode); - struct list_head *le; - struct coda_cache *cc = NULL; + int hit; - list_for_each(le, &cii->c_cnhead) - { - /* compare name and creds */ - cc = list_entry(le, struct coda_cache, cc_cnlist); - if ( (cc->cc_mask & mask) != mask ) - continue; - if ( !coda_cred_ok(&cc->cc_cred) ) - continue; - CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); - return 1; /* cache hit */ - } - CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino ); - return 0; + hit = ((mask & cii->c_cached_perm) == mask) && + coda_cred_ok(&cii->c_cached_cred); + + CDEBUG(D_CACHE, "%s for ino %ld\n", hit ? "HIT" : "MISS", inode->i_ino); + return hit; } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 7c554461271f..5a38471ff889 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -59,7 +59,6 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid, struct coda_vattr * attr) { struct inode *inode; - struct coda_sb_info *sbi= coda_sbp(sb); struct coda_inode_info *cii; ino_t ino = attr->va_fileid; @@ -73,48 +72,25 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid, cii = ITOC(inode); if (cii->c_magic == CODA_CNODE_MAGIC) { /* see if it is the right one (might have an inode collision) */ - if ( !coda_fideq(fid, &cii->c_fid) ) { + if ( !coda_fideq(fid, &cii->c_fid) ) { printk("coda_iget: initialized inode old %s new %s!\n", coda_f2s(&cii->c_fid), coda_f2s2(fid)); iput(inode); return ERR_PTR(-ENOENT); } - /* replace the attributes, type might have changed */ - coda_fill_inode(inode, attr); + /* we will still replace the attributes, type might have changed */ goto out; } /* new, empty inode found... initializing */ /* Initialize the Coda inode info structure */ - memset(cii, 0, (int) sizeof(struct coda_inode_info)); cii->c_magic = CODA_CNODE_MAGIC; cii->c_fid = *fid; - cii->c_flags = 0; cii->c_vnode = inode; - INIT_LIST_HEAD(&(cii->c_cnhead)); - INIT_LIST_HEAD(&(cii->c_volrootlist)); - coda_fill_inode(inode, attr); - - /* check if it is a weird fid (hashed fid != ino), f.i mountpoints - repair object, expanded local-global conflict trees, etc. - */ - if ( coda_f2i(fid) == ino ) - goto out; - - /* check if we expected this weird fid */ - if ( !coda_fid_is_weird(fid) ) { - printk("Coda: unknown weird fid: ino %ld, fid %s." - "Tell Peter.\n", (long)ino, coda_f2s(&cii->c_fid)); - goto out; - } - - /* add the inode to a global list so we can find it back later */ - list_add(&cii->c_volrootlist, &sbi->sbi_volroothead); - CDEBUG(D_CNODE, "Added %ld, %s to volroothead\n", - (long)ino, coda_f2s(&cii->c_fid)); out: + coda_fill_inode(inode, attr); return inode; } @@ -161,22 +137,14 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, struct ViceFid *newfid) { - struct coda_inode_info *cnp; - struct coda_sb_info *sbi= coda_sbp(inode->i_sb); + struct coda_inode_info *cii; - cnp = ITOC(inode); - - if ( ! coda_fideq(&cnp->c_fid, oldfid) ) - printk("What? oldfid != cnp->c_fid. Call 911.\n"); - - cnp->c_fid = *newfid; + cii = ITOC(inode); - list_del(&cnp->c_volrootlist); - INIT_LIST_HEAD(&cnp->c_volrootlist); - if ( coda_fid_is_weird(newfid) ) - list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); + if ( ! coda_fideq(&cii->c_fid, oldfid) ) + printk("What? oldfid != cii->c_fid. Call 911.\n"); - return; + cii->c_fid = *newfid; } @@ -204,17 +172,15 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) CDEBUG(D_INODE, "%s\n", coda_f2s(fid)); + /* weird fids cannot be hashed, have to look for them the hard way */ if ( coda_fid_is_weird(fid) ) { - struct list_head *lh, *le; struct coda_sb_info *sbi = coda_sbp(sb); - le = lh = &sbi->sbi_volroothead; + struct list_head *le; - while ( (le = le->next) != lh ) { - cii = list_entry(le, struct coda_inode_info, - c_volrootlist); - /* paranoia check, should never trigger */ - if ( cii->c_magic != CODA_CNODE_MAGIC ) - printk("coda_fid_to_inode: Bad magic in inode %x.\n", cii->c_magic); + list_for_each(le, &sbi->sbi_cihead) + { + cii = list_entry(le, struct coda_inode_info, c_cilist); + if ( cii->c_magic != CODA_CNODE_MAGIC ) continue; CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n", coda_f2s(&cii->c_fid), cii->c_vnode->i_ino); @@ -240,26 +206,19 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) /* check if this inode is linked to a cnode */ cii = ITOC(inode); - if ( cii->c_magic != CODA_CNODE_MAGIC ) { - CDEBUG(D_INODE, "uninitialized inode. Return.\n"); - goto bad_inode; - } - /* make sure fid is the one we want */ - if ( !coda_fideq(fid, &(cii->c_fid)) ) { + /* make sure this is the one we want */ + if ( cii->c_magic == CODA_CNODE_MAGIC && coda_fideq(fid, &cii->c_fid) ) { + CDEBUG(D_INODE, "found %ld\n", inode->i_ino); + return inode; + } + #if 0 - printk("coda_fid2inode: bad cnode (ino %ld, fid %s)", nr, - coda_f2s(fid)); + printk("coda_fid2inode: bad cnode (ino %ld, fid %s)", nr, coda_f2s(fid)); #endif - goto bad_inode; - } - - CDEBUG(D_INODE, "found %ld\n", inode->i_ino); - return inode; + iput(inode); + return NULL; -bad_inode: - iput(inode); - return NULL; } /* the CONTROL inode is made without asking attributes from Venus */ @@ -271,7 +230,7 @@ int coda_cnode_makectl(struct inode **inode, struct super_block *sb) if ( *inode ) { (*inode)->i_op = &coda_ioctl_inode_operations; (*inode)->i_fop = &coda_ioctl_operations; - (*inode)->i_mode = 00444; + (*inode)->i_mode = 0444; error = 0; } else { error = -ENOMEM; diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 4a7bebb6236c..2a3f544f3d64 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -23,7 +23,6 @@ #include #include #include -#include /* initialize the debugging variables */ int coda_debug = 0; @@ -71,12 +70,6 @@ int coda_isroot(struct inode *i) } } -/* is this a volume root FID */ -int coda_fid_is_volroot(struct ViceFid *fid) -{ - return ( (fid->Vnode == 1) && (fid->Unique == 1 ) ); -} - int coda_fid_is_weird(struct ViceFid *fid) { /* volume roots */ diff --git a/fs/coda/dir.c b/fs/coda/dir.c index a51bfc647ce9..1629be78287c 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -170,12 +170,13 @@ int coda_permission(struct inode *inode, int mask) ENTRY; coda_vfs_stat.permission++; - coda_permission_stat.count++; if ( mask == 0 ) return 0; - if ( coda_access_cache == 1 ) { + if ( coda_access_cache ) { + coda_permission_stat.count++; + if ( coda_cache_check(inode, mask) ) { coda_permission_stat.hit_count++; return 0; @@ -472,6 +473,7 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, const char *new_name = new_dentry->d_name.name; int old_length = old_dentry->d_name.len; int new_length = new_dentry->d_name.len; + int link_adjust = 0; int error; ENTRY; @@ -488,16 +490,16 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, if ( !error ) { if ( new_dentry->d_inode ) { - if ( S_ISDIR(new_dentry->d_inode->i_mode) ) { - old_dir->i_nlink--; - new_dir->i_nlink++; - } - coda_flag_inode(new_dentry->d_inode, C_VATTR); - } + if ( S_ISDIR(new_dentry->d_inode->i_mode) ) + link_adjust = 1; - /* coda_flag_inode(old_dir, C_VATTR); */ - /* coda_flag_inode(new_dir, C_VATTR); */ - old_dir->i_mtime = new_dir->i_mtime = CURRENT_TIME; + coda_dir_changed(old_dir, -link_adjust); + coda_dir_changed(new_dir, link_adjust); + coda_flag_inode(new_dentry->d_inode, C_VATTR); + } else { + coda_flag_inode(old_dir, C_VATTR); + coda_flag_inode(new_dir, C_VATTR); + } } CDEBUG(D_INODE, "result %d\n", error); @@ -578,6 +580,7 @@ int coda_open(struct inode *i, struct file *f) unsigned short flags = f->f_flags & (~O_EXCL); unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_cred *cred; + struct coda_inode_info *cii; lock_kernel(); ENTRY; @@ -617,8 +620,11 @@ int coda_open(struct inode *i, struct file *f) } i->i_mapping = cont_inode->i_mapping; - CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", - error, atomic_read(&i->i_count), i->i_ino); + cii = ITOC(i); + cii->c_contcount++; + + CDEBUG(D_FILE, "result %d, coda i->i_count is %d, cii->contcount is %d for ino %ld\n", + error, atomic_read(&i->i_count), cii->c_contcount, i->i_ino); CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %p\n", cont_inode->i_ino, atomic_read(&cont_inode->i_count), cont_inode->i_op); @@ -634,6 +640,7 @@ int coda_release(struct inode *i, struct file *f) unsigned short flags = (f->f_flags) & (~O_EXCL); unsigned short cflags = coda_flags_to_cflags(flags); struct coda_cred *cred; + struct coda_inode_info *cii; lock_kernel(); ENTRY; @@ -644,11 +651,17 @@ int coda_release(struct inode *i, struct file *f) if (i->i_mapping != &i->i_data) container = (struct inode *)i->i_mapping->host; - CDEBUG(D_FILE, "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n", - i->i_ino, atomic_read(&i->i_count), + cii = ITOC(i); + CDEBUG(D_FILE, "RELEASE coda (ino %ld, ct %d, cc %d) cache (ino %ld, ct %d)\n", + i->i_ino, atomic_read(&i->i_count), cii->c_contcount, (container ? container->i_ino : 0), (container ? atomic_read(&container->i_count) : -99)); + if (--cii->c_contcount == 0 && container) { + i->i_mapping = &i->i_data; + iput(container); + } + error = venus_release(i->i_sb, coda_i2f(i), cflags, cred); f->private_data = NULL; diff --git a/fs/coda/file.c b/fs/coda/file.c index 128b07d44e01..0344c20728aa 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -23,18 +23,21 @@ #include #include #include -#include #include static ssize_t coda_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; + struct inode *container = (struct inode*)inode->i_mapping->host; ssize_t n; + down(&container->i_sem); + n = generic_file_write(file, buf, count, ppos); + inode->i_size = container->i_size; - inode->i_size = ((struct inode*)inode->i_mapping->host)->i_size; + up(&container->i_sem); return n; } @@ -63,7 +66,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) result = file_fsync(NULL, &cont_dentry, datasync); up(&cont_dentry.d_inode->i_sem); - if ( result == 0 ) { + if ( result == 0 && datasync == 0 ) { lock_kernel(); result = venus_fsync(inode->i_sb, coda_i2f(inode)); unlock_kernel(); diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 84191c494283..8c126838c4f0 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,47 @@ struct super_operations coda_super_operations = statfs: coda_statfs, }; +static int get_device_index(struct coda_mount_data *data) +{ + struct file *file; + struct inode *inode; + int idx; + + if(data == NULL) { + printk("coda_read_super: Bad mount data\n"); + return -1; + } + + if(data->version != CODA_MOUNT_VERSION) { + printk("coda_read_super: Bad mount version\n"); + return -1; + } + + file = fget(data->fd); + inode = NULL; + if(file) + inode = file->f_dentry->d_inode; + + if(!inode || !S_ISCHR(inode->i_mode) || + MAJOR(inode->i_rdev) != CODA_PSDEV_MAJOR) { + if(file) + fput(file); + + printk("coda_read_super: Bad file\n"); + return -1; + } + + idx = MINOR(inode->i_rdev); + fput(file); + + if(idx < 0 || idx >= MAX_CODADEVS) { + printk("coda_read_super: Bad minor number\n"); + return -1; + } + + return idx; +} + static struct super_block * coda_read_super(struct super_block *sb, void *data, int silent) { @@ -57,23 +99,41 @@ static struct super_block * coda_read_super(struct super_block *sb, ViceFid fid; kdev_t dev = sb->s_dev; int error; - + int idx; ENTRY; - vc = &coda_upc_comm; - sbi = &coda_super_info; + idx = get_device_index((struct coda_mount_data *) data); - if ( sbi->sbi_sb ) { - printk("Already mounted\n"); + /* Ignore errors in data, for backward compatibility */ + if(idx == -1) + idx = 0; + + printk(KERN_INFO "coda_read_super: device index: %i\n", idx); + + vc = &coda_comms[idx]; + if (!vc->vc_inuse) { + printk("coda_read_super: No pseudo device\n"); EXIT; return NULL; } + if ( vc->vc_sb ) { + printk("coda_read_super: Device already mounted\n"); + EXIT; + return NULL; + } + + sbi = kmalloc(sizeof(struct coda_sb_info), GFP_KERNEL); + if(!sbi) { + EXIT; + return NULL; + } + + vc->vc_sb = sb; + sbi->sbi_sb = sb; - sbi->sbi_psdev = psdev; sbi->sbi_vcomm = vc; - INIT_LIST_HEAD(&(sbi->sbi_cchead)); - INIT_LIST_HEAD(&(sbi->sbi_volroothead)); + INIT_LIST_HEAD(&sbi->sbi_cihead); sb->u.generic_sbp = sbi; sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ @@ -100,7 +160,6 @@ static struct super_block * coda_read_super(struct super_block *sb, printk("coda_read_super: rootinode is %ld dev %d\n", root->i_ino, root->i_dev); - sbi->sbi_root = root; sb->s_root = d_alloc_root(root); EXIT; return sb; @@ -108,9 +167,9 @@ static struct super_block * coda_read_super(struct super_block *sb, error: EXIT; if (sbi) { - sbi->sbi_vcomm = NULL; - sbi->sbi_root = NULL; - sbi->sbi_sb = NULL; + kfree(sbi); + if(vc) + vc->vc_sb = NULL; } if (root) { iput(root); @@ -120,15 +179,16 @@ static struct super_block * coda_read_super(struct super_block *sb, static void coda_put_super(struct super_block *sb) { - struct coda_sb_info *sb_info; + struct coda_sb_info *sbi; ENTRY; - coda_cache_clear_all(sb); - sb_info = coda_sbp(sb); - coda_super_info.sbi_sb = NULL; + sbi = coda_sbp(sb); + sbi->sbi_vcomm->vc_sb = NULL; + list_del_init(&sbi->sbi_cihead); + printk("Coda: Bye bye.\n"); - memset(sb_info, 0, sizeof(* sb_info)); + kfree(sbi); EXIT; } @@ -136,11 +196,20 @@ static void coda_put_super(struct super_block *sb) /* all filling in of inodes postponed until lookup */ static void coda_read_inode(struct inode *inode) { + struct coda_sb_info *sbi = coda_sbp(inode->i_sb); struct coda_inode_info *cii; ENTRY; + + if (!sbi) BUG(); + cii = ITOC(inode); - cii->c_magic = 0; - return; + if (cii->c_magic == CODA_CNODE_MAGIC) { + printk("coda_read_inode: initialized inode"); + return; + } + + memset(cii, 0, sizeof(struct coda_inode_info)); + list_add(&cii->c_cilist, &sbi->sbi_cihead); } static void coda_clear_inode(struct inode *inode) @@ -152,15 +221,13 @@ static void coda_clear_inode(struct inode *inode) CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", inode->i_ino, atomic_read(&inode->i_count)); - if ( inode->i_ino == CTL_INO || cii->c_magic != CODA_CNODE_MAGIC ) - goto out; + if ( cii->c_magic != CODA_CNODE_MAGIC ) + return; - lock_kernel(); + list_del_init(&cii->c_cilist); - if ( !list_empty(&cii->c_volrootlist) ) { - list_del(&cii->c_volrootlist); - INIT_LIST_HEAD(&cii->c_volrootlist); - } + if ( inode->i_ino == CTL_INO ) + goto out; if ( inode->i_mapping != &inode->i_data ) { open_inode = (struct inode *)inode->i_mapping->host; @@ -170,10 +237,8 @@ static void coda_clear_inode(struct inode *inode) iput(open_inode); } - coda_cache_clear_inode(inode); - unlock_kernel(); - CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags); + coda_cache_clear_inode(inode); out: inode->u.coda_i.c_magic = 0; EXIT; @@ -238,8 +303,3 @@ static int coda_statfs(struct super_block *sb, struct statfs *buf) DECLARE_FSTYPE( coda_fs_type, "coda", coda_read_super, 0); -int init_coda_fs(void) -{ - return register_filesystem(&coda_fs_type); -} - diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index 99ead4e51e8a..6deeb927c0ee 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -23,13 +23,12 @@ #include #include #include -#include #include /* pioctl ops */ static int coda_ioctl_permission(struct inode *inode, int mask); static int coda_pioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long user_data); /* exported from this file */ struct inode_operations coda_ioctl_inode_operations = @@ -52,7 +51,7 @@ static int coda_ioctl_permission(struct inode *inode, int mask) } static int coda_pioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long user_data) + unsigned int cmd, unsigned long user_data) { struct nameidata nd; int error; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index c475b73acd7b..486e421855ba 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -46,21 +46,19 @@ #include #include #include -#include #include /* * Coda stuff */ extern struct file_system_type coda_fs_type; -extern int init_coda_fs(void); /* statistics */ int coda_hard = 0; /* allows signals during upcalls */ unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ -struct coda_sb_info coda_super_info; -struct venus_comm coda_upc_comm; + +struct venus_comm coda_comms[MAX_CODADEVS]; /* * Device operations @@ -68,7 +66,7 @@ struct venus_comm coda_upc_comm; static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; unsigned int mask = POLLOUT | POLLWRNORM; poll_wait(file, &vcp->vc_waitq, wait); @@ -101,7 +99,7 @@ static int coda_psdev_ioctl(struct inode * inode, struct file * filp, static ssize_t coda_psdev_write(struct file *file, const char *buf, size_t nbytes, loff_t *off) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; struct upc_req *req = NULL; struct upc_req *tmp; struct list_head *lh; @@ -109,8 +107,6 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, ssize_t retval = 0, count = 0; int error; - if ( !coda_upc_comm.vc_inuse ) - return -EIO; /* Peek at the opcode, uniquefier */ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) return -EFAULT; @@ -123,7 +119,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, union outputArgs *dcbuf; int size = sizeof(*dcbuf); - sb = coda_super_info.sbi_sb; + sb = vcp->vc_sb; if ( !sb ) { CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!\n"); count = nbytes; @@ -221,7 +217,7 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, size_t nbytes, loff_t *off) { DECLARE_WAITQUEUE(wait, current); - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; struct upc_req *req; ssize_t retval = 0, count = 0; @@ -245,7 +241,7 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, schedule(); } - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&vcp->vc_waitq, &wait); if (retval) @@ -285,21 +281,32 @@ out: return (count ? count : retval); } - static int coda_psdev_open(struct inode * inode, struct file * file) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp; + int idx; ENTRY; - - /* first opener, initialize */ + lock_kernel(); + idx = MINOR(inode->i_rdev); + if(idx >= MAX_CODADEVS) + return -ENODEV; + + vcp = &coda_comms[idx]; + if(vcp->vc_inuse) + return -EBUSY; + if (!vcp->vc_inuse++) { - INIT_LIST_HEAD(&vcp->vc_pending); - INIT_LIST_HEAD(&vcp->vc_processing); - vcp->vc_seq = 0; + INIT_LIST_HEAD(&vcp->vc_pending); + INIT_LIST_HEAD(&vcp->vc_processing); + init_waitqueue_head(&vcp->vc_waitq); + vcp->vc_sb = 0; + vcp->vc_seq = 0; } + + file->private_data = vcp; - CDEBUG(D_PSDEV, "inuse: %d\n", vcp->vc_inuse); + CDEBUG(D_PSDEV, "device %i - inuse: %d\n", idx, vcp->vc_inuse); EXIT; unlock_kernel(); @@ -309,7 +316,7 @@ static int coda_psdev_open(struct inode * inode, struct file * file) static int coda_psdev_release(struct inode * inode, struct file * file) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; struct upc_req *req; struct list_head *lh, *next; ENTRY; @@ -369,29 +376,9 @@ static struct file_operations coda_psdev_fops = { release: coda_psdev_release, }; - - -int __init init_coda(void) -{ - int status; - printk(KERN_INFO "Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n"); - - status = init_coda_psdev(); - if ( status ) { - printk("Problem (%d) in init_coda_psdev\n", status); - return status; - } - - status = init_coda_fs(); - if (status) { - printk("coda: failed in init_coda_fs!\n"); - } - return status; -} - static devfs_handle_t devfs_handle = NULL; -int init_coda_psdev(void) +static int init_coda_psdev(void) { if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { @@ -404,9 +391,6 @@ int init_coda_psdev(void) CODA_PSDEV_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &coda_psdev_fops, NULL); - memset(&coda_upc_comm, 0, sizeof(coda_upc_comm)); - memset(&coda_super_info, 0, sizeof(coda_super_info)); - init_waitqueue_head(&coda_upc_comm.vc_waitq); coda_sysctl_init(); @@ -414,36 +398,35 @@ int init_coda_psdev(void) } -#ifdef MODULE - MODULE_AUTHOR("Peter J. Braam "); -int init_module(void) +static int __init init_coda(void) { int status; - printk(KERN_INFO "Coda Kernel/Venus communications (module), v5.0-pre1, braam@cs.cmu.edu.\n"); + printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.9, coda@cs.cmu.edu\n"); + status = init_coda_psdev(); if ( status ) { printk("Problem (%d) in init_coda_psdev\n", status); return status; } - - status = init_coda_fs(); + + status = register_filesystem(&coda_fs_type); if (status) { printk("coda: failed in init_coda_fs!\n"); } return status; } - -void cleanup_module(void) +static void __exit exit_coda(void) { int err; ENTRY; - if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { + err = unregister_filesystem(&coda_fs_type); + if ( err != 0 ) { printk("coda: failed to unregister filesystem\n"); } devfs_unregister (devfs_handle); @@ -451,5 +434,5 @@ void cleanup_module(void) coda_sysctl_clean(); } -#endif - +module_init(init_coda); +module_exit(exit_coda); diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index 252f116640ef..dbdc946f4c53 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -20,7 +20,6 @@ #include #include #include -#include #include static int coda_symlink_filler(struct file *file, struct page *page) diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index cbfff3e5b354..4215c3b41506 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -76,9 +76,9 @@ int coda_upcall_timestamping = 0; /* keep this in sync with coda.h! */ char *coda_upcall_names[] = { "totals ", /* 0 */ - "noop ", /* 1 */ + "- ", /* 1 */ "root ", /* 2 */ - "sync ", /* 3 */ + "open_by_fd ", /* 3 */ "open ", /* 4 */ "close ", /* 5 */ "ioctl ", /* 6 */ @@ -96,7 +96,7 @@ char *coda_upcall_names[] = { "symlink ", /* 18 */ "readlink ", /* 19 */ "fsync ", /* 20 */ - "inactive ", /* 21 */ + "- ", /* 21 */ "vget ", /* 22 */ "signal ", /* 23 */ "replace ", /* 24 */ @@ -104,13 +104,12 @@ char *coda_upcall_names[] = { "purgeuser ", /* 26 */ "zapfile ", /* 27 */ "zapdir ", /* 28 */ - "noop2 ", /* 29 */ + "- ", /* 29 */ "purgefid ", /* 30 */ "open_by_path", /* 31 */ "resolve ", /* 32 */ "reintegrate ", /* 33 */ - "statfs ", /* 34 */ - "make_cinode " /* 35 */ + "statfs " /* 34 */ }; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 416f330ce1c3..6eb6f2e71c20 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -543,7 +543,8 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, goto exit; } - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size, + &outsize, inp); if (error) { printk("coda_pioctl: Venus returns: %d for %s\n", @@ -607,7 +608,8 @@ int venus_statfs(struct super_block *sb, struct statfs *sfs) * */ -static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) +static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp, + struct venus_comm *vcommp) { DECLARE_WAITQUEUE(wait, current); struct timeval begin = { 0, 0 }, end = { 0, 0 }; @@ -625,7 +627,7 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) set_current_state(TASK_UNINTERRUPTIBLE); /* venus died */ - if ( !coda_upc_comm.vc_inuse ) + if ( !vcommp->vc_inuse ) break; /* got a reply */ @@ -645,7 +647,7 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) schedule(); } remove_wait_queue(&vmp->uc_sleep, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (coda_upcall_timestamping && begin.tv_sec != 0) { do_gettimeofday(&end); @@ -685,9 +687,9 @@ static int coda_upcall(struct coda_sb_info *sbi, struct upc_req *req; int error = 0; -ENTRY; + ENTRY; - vcommp = &coda_upc_comm; + vcommp = sbi->sbi_vcomm; if ( !vcommp->vc_inuse ) { printk("No pseudo device in upcall comms at %p\n", vcommp); return -ENXIO; @@ -724,7 +726,7 @@ ENTRY; * ENODEV. */ /* Go to sleep. Wake up on signals only after the timeout. */ - runtime = coda_waitfor_upcall(req); + runtime = coda_waitfor_upcall(req, vcommp); coda_upcall_stats(((union inputArgs *)buffer)->ih.opcode, runtime); CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n", @@ -738,11 +740,6 @@ ENTRY; if (req->uc_flags & REQ_WRITE) { out = (union outputArgs *)req->uc_data; /* here we map positive Venus errors to kernel errors */ - if ( out->oh.result < 0 ) { - printk("Tell Peter: Venus returns negative error %ld, for oc %ld!\n", - out->oh.result, out->oh.opcode); - out->oh.result = EINTR; - } error = -out->oh.result; CDEBUG(D_UPCALL, "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n", @@ -855,7 +852,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_FLUSH : { clstats(CODA_FLUSH); CDEBUG(D_DOWNCALL, "CODA_FLUSH\n"); - coda_cache_clear_all(sb); + coda_cache_clear_all(sb, NULL); shrink_dcache_sb(sb); coda_flag_inode(sb->s_root->d_inode, C_FLUSH); return(0); @@ -869,7 +866,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) return 0; } clstats(CODA_PURGEUSER); - coda_cache_clear_cred(sb, cred); + coda_cache_clear_all(sb, cred); return(0); } diff --git a/fs/file_table.c b/fs/file_table.c index 54538ddf0565..931314661e61 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -98,48 +98,35 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) return 0; } -/* - * Called when retiring the last use of a file pointer. - */ -static void __fput(struct file *filp) +void fput(struct file * file) { - struct dentry * dentry = filp->f_dentry; - struct vfsmount * mnt = filp->f_vfsmnt; + struct dentry * dentry = file->f_dentry; + struct vfsmount * mnt = file->f_vfsmnt; struct inode * inode = dentry->d_inode; - if (filp->f_op && filp->f_op->release) - filp->f_op->release(inode, filp); - fops_put(filp->f_op); - filp->f_dentry = NULL; - filp->f_vfsmnt = NULL; - if (filp->f_mode & FMODE_WRITE) - put_write_access(inode); - dput(dentry); - if (mnt) - mntput(mnt); -} - -static void _fput(struct file *file) -{ - locks_remove_flock(file); - __fput(file); - - file_list_lock(); - list_del(&file->f_list); - list_add(&file->f_list, &free_list); - files_stat.nr_free_files++; - file_list_unlock(); -} - -void fput(struct file * file) -{ - if (atomic_dec_and_test(&file->f_count)) - _fput(file); + if (atomic_dec_and_test(&file->f_count)) { + locks_remove_flock(file); + if (file->f_op && file->f_op->release) + file->f_op->release(inode, file); + fops_put(file->f_op); + file->f_dentry = NULL; + file->f_vfsmnt = NULL; + if (file->f_mode & FMODE_WRITE) + put_write_access(inode); + dput(dentry); + if (mnt) + mntput(mnt); + file_list_lock(); + list_del(&file->f_list); + list_add(&file->f_list, &free_list); + files_stat.nr_free_files++; + file_list_unlock(); + } } struct file * fget(unsigned int fd) { - struct file * file = NULL; + struct file * file; struct files_struct *files = current->files; read_lock(&files->file_lock); diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index b063860ee7eb..0406f72cb88e 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -33,6 +33,13 @@ #include "smb_debug.h" #include "getopt.h" +/* Always pick a default string */ +#ifdef CONFIG_SMB_NLS_REMOTE +#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE +#else +#define SMB_NLS_REMOTE "" +#endif + static void smb_delete_inode(struct inode *); static void smb_put_super(struct super_block *); static int smb_statfs(struct super_block *, struct statfs *); @@ -445,7 +452,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, SMB_NLS_MAXNAMELEN); - strncpy(mnt->codepage.remote_name, CONFIG_SMB_NLS_REMOTE, + strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE, SMB_NLS_MAXNAMELEN); if (ver == SMB_MOUNT_OLDVERSION) { diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c index 167de8f5a75a..1a97daf7cc52 100644 --- a/fs/ufs/ialloc.c +++ b/fs/ufs/ialloc.c @@ -265,9 +265,7 @@ cg_found: inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; - if (test_opt (sb, GRPID)) - inode->i_gid = dir->i_gid; - else if (dir->i_mode & S_ISGID) { + if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h index a3d037e815b1..dc98c596da2a 100644 --- a/include/asm-ppc/prom.h +++ b/include/asm-ppc/prom.h @@ -7,6 +7,8 @@ #ifndef _PPC_PROM_H #define _PPC_PROM_H +#include + typedef void *phandle; typedef void *ihandle; diff --git a/include/linux/byteorder/swab.h b/include/linux/byteorder/swab.h index 813df46c35ef..23f1be7d718b 100644 --- a/include/linux/byteorder/swab.h +++ b/include/linux/byteorder/swab.h @@ -43,33 +43,33 @@ * provide defaults when no architecture-specific optimization is detected */ #ifndef __arch__swab16 -# define __arch__swab16(x) ___swab16(x) +# define __arch__swab16(x) ({ __u16 __tmp = (x) ; ___swab16(__tmp); }) #endif #ifndef __arch__swab32 -# define __arch__swab32(x) ___swab32(x) +# define __arch__swab32(x) ({ __u32 __tmp = (x) ; ___swab32(__tmp); }) #endif #ifndef __arch__swab64 -# define __arch__swab64(x) ___swab64(x) +# define __arch__swab64(x) ({ __u64 __tmp = (x) ; ___swab64(__tmp); }) #endif #ifndef __arch__swab16p -# define __arch__swab16p(x) __swab16(*(x)) +# define __arch__swab16p(x) __arch__swab16(*(x)) #endif #ifndef __arch__swab32p -# define __arch__swab32p(x) __swab32(*(x)) +# define __arch__swab32p(x) __arch__swab32(*(x)) #endif #ifndef __arch__swab64p -# define __arch__swab64p(x) __swab64(*(x)) +# define __arch__swab64p(x) __arch__swab64(*(x)) #endif #ifndef __arch__swab16s -# define __arch__swab16s(x) do { *(x) = __swab16p((x)); } while (0) +# define __arch__swab16s(x) do { *(x) = __arch__swab16p((x)); } while (0) #endif #ifndef __arch__swab32s -# define __arch__swab32s(x) do { *(x) = __swab32p((x)); } while (0) +# define __arch__swab32s(x) do { *(x) = __arch__swab32p((x)); } while (0) #endif #ifndef __arch__swab64s -# define __arch__swab64s(x) do { *(x) = __swab64p((x)); } while (0) +# define __arch__swab64s(x) do { *(x) = __arch__swab64p((x)); } while (0) #endif diff --git a/include/linux/cciss_ioctl.h b/include/linux/cciss_ioctl.h new file mode 100644 index 000000000000..989549b2413f --- /dev/null +++ b/include/linux/cciss_ioctl.h @@ -0,0 +1,186 @@ +#ifndef CCISS_IOCTLH +#define CCISS_IOCTLH + +#include +#include + +#define CCISS_IOC_MAGIC 'B' + + +typedef struct _cciss_pci_info_struct +{ + unsigned char bus; + unsigned char dev_fn; + __u32 board_id; +} cciss_pci_info_struct; + +typedef struct _cciss_coalint_struct +{ + __u32 delay; + __u32 count; +} cciss_coalint_struct; + +typedef char NodeName_type[16]; + +typedef __u32 Heartbeat_type; + +#define CISS_PARSCSIU2 0x0001 +#define CISS_PARCSCIU3 0x0002 +#define CISS_FIBRE1G 0x0100 +#define CISS_FIBRE2G 0x0200 +typedef __u32 BusTypes_type; + +typedef char FirmwareVer_type[4]; +typedef __u32 DriverVer_type; + + +#ifndef CCISS_CMD_H +// This defines are duplicated in cciss_cmd.h in the driver directory + +//general boundary defintions +#define SENSEINFOBYTES 32//note that this value may vary between host implementations + +//Command Status value +#define CMD_SUCCESS 0x0000 +#define CMD_TARGET_STATUS 0x0001 +#define CMD_DATA_UNDERRUN 0x0002 +#define CMD_DATA_OVERRUN 0x0003 +#define CMD_INVALID 0x0004 +#define CMD_PROTOCOL_ERR 0x0005 +#define CMD_HARDWARE_ERR 0x0006 +#define CMD_CONNECTION_LOST 0x0007 +#define CMD_ABORTED 0x0008 +#define CMD_ABORT_FAILED 0x0009 +#define CMD_UNSOLICITED_ABORT 0x000A +#define CMD_TIMEOUT 0x000B +#define CMD_UNABORTABLE 0x000C + +//transfer direction +#define XFER_NONE 0x00 +#define XFER_WRITE 0x01 +#define XFER_READ 0x02 +#define XFER_RSVD 0x03 + +//task attribute +#define ATTR_UNTAGGED 0x00 +#define ATTR_SIMPLE 0x04 +#define ATTR_HEADOFQUEUE 0x05 +#define ATTR_ORDERED 0x06 +#define ATTR_ACA 0x07 + +//cdb type +#define TYPE_CMD 0x00 +#define TYPE_MSG 0x01 + +// Type defs used in the following structs +#define BYTE __u8 +#define WORD __u16 +#define HWORD __u16 +#define DWORD __u32 + +#define CISS_MAX_LUN 16 + +#pragma pack(1) + +//Command List Structure +typedef union _SCSI3Addr_struct { + struct { + BYTE Bus:6; + BYTE Mode:2; // b00 + BYTE Dev; + } PeripDev; + struct { + BYTE DevMSB:6; + BYTE Mode:2; // b01 + BYTE DevLSB; + } LogDev; + struct { + BYTE Targ:6; + BYTE Mode:2; // b10 + BYTE Dev:5; + BYTE Bus:3; + } LogUnit; +} SCSI3Addr_struct; + +typedef struct _PhysDevAddr_struct { + DWORD TargetId:24; + DWORD Bus:6; + DWORD Mode:2; + SCSI3Addr_struct Target[2]; //2 level target device addr +} PhysDevAddr_struct; + +typedef struct _LogDevAddr_struct { + DWORD VolId:30; + DWORD Mode:2; + BYTE reserved[4]; +} LogDevAddr_struct; + +typedef union _LUNAddr_struct { + BYTE LunAddrBytes[8]; + SCSI3Addr_struct SCSI3Lun[4]; + PhysDevAddr_struct PhysDev; + LogDevAddr_struct LogDev; +} LUNAddr_struct; + +typedef struct _RequestBlock_struct { + BYTE CDBLen; + struct { + BYTE Type:3; + BYTE Attribute:3; + BYTE Direction:2; + } Type; + HWORD Timeout; + BYTE CDB[16]; +} RequestBlock_struct; + +typedef union _MoreErrInfo_struct{ + struct { + BYTE Reserved[3]; + BYTE Type; + DWORD ErrorInfo; + }Common_Info; + struct{ + BYTE Reserved[2]; + BYTE offense_size;//size of offending entry + BYTE offense_num; //byte # of offense 0-base + DWORD offense_value; + }Invalid_Cmd; +}MoreErrInfo_struct; +typedef struct _ErrorInfo_struct { + BYTE ScsiStatus; + BYTE SenseLen; + HWORD CommandStatus; + DWORD ResidualCnt; + MoreErrInfo_struct MoreErrInfo; + BYTE SenseInfo[SENSEINFOBYTES]; +} ErrorInfo_struct; + +#pragma pack() +#endif /* CCISS_CMD_H */ + +typedef struct _IOCTL_Command_struct { + LUNAddr_struct LUN_info; + RequestBlock_struct Request; + ErrorInfo_struct error_info; + WORD buf_size; /* size in bytes of the buf */ + BYTE *buf; +} IOCTL_Command_struct; + + +#define CCISS_GETPCIINFO _IOR(CCISS_IOC_MAGIC, 1, cciss_pci_info_struct) + +#define CCISS_GETINTINFO _IOR(CCISS_IOC_MAGIC, 2, cciss_coalint_struct) +#define CCISS_SETINTINFO _IOW(CCISS_IOC_MAGIC, 3, cciss_coalint_struct) + +#define CCISS_GETNODENAME _IOR(CCISS_IOC_MAGIC, 4, NodeName_type) +#define CCISS_SETNODENAME _IOW(CCISS_IOC_MAGIC, 5, NodeName_type) + +#define CCISS_GETHEARTBEAT _IOR(CCISS_IOC_MAGIC, 6, Heartbeat_type) +#define CCISS_GETBUSTYPES _IOR(CCISS_IOC_MAGIC, 7, BusTypes_type) +#define CCISS_GETFIRMVER _IOR(CCISS_IOC_MAGIC, 8, FirmwareVer_type) +#define CCISS_GETDRIVVER _IOR(CCISS_IOC_MAGIC, 9, DriverVer_type) +#define CCISS_REVALIDVOLS _IO(CCISS_IOC_MAGIC, 10) +#define CCISS_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 11, IOCTL_Command_struct) + + +#endif diff --git a/include/linux/coda.h b/include/linux/coda.h index a085586285cd..d9ab68c3541c 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -284,7 +284,7 @@ struct coda_statfs { */ #define CODA_ROOT 2 -#define CODA_SYNC 3 +#define CODA_OPEN_BY_FD 3 #define CODA_OPEN 4 #define CODA_CLOSE 5 #define CODA_IOCTL 6 @@ -298,11 +298,9 @@ struct coda_statfs { #define CODA_RENAME 14 #define CODA_MKDIR 15 #define CODA_RMDIR 16 -#define CODA_READDIR 17 #define CODA_SYMLINK 18 #define CODA_READLINK 19 #define CODA_FSYNC 20 -#define CODA_INACTIVE 21 #define CODA_VGET 22 #define CODA_SIGNAL 23 #define CODA_REPLACE 24 /* DOWNCALL */ @@ -315,12 +313,9 @@ struct coda_statfs { #define CODA_RESOLVE 32 #define CODA_REINTEGRATE 33 #define CODA_STATFS 34 -#define CODA_MAKE_CINODE 35 /* DOWNCALL */ -#define CODA_NCALLS 36 +#define CODA_NCALLS 35 -#define DOWNCALL(opcode) \ - ((opcode >= CODA_REPLACE && opcode <= CODA_PURGEFID) || \ - opcode == CODA_MAKE_CINODE) +#define DOWNCALL(opcode) (opcode >= CODA_REPLACE && opcode <= CODA_PURGEFID) #define VC_MAXDATASIZE 8192 #define VC_MAXMSGSIZE sizeof(union inputArgs)+sizeof(union outputArgs) +\ @@ -328,7 +323,7 @@ struct coda_statfs { #define CIOC_KERNEL_VERSION _IOWR('c', 10, sizeof (int)) -#if 0 +#if 0 #define CODA_KERNEL_VERSION 0 /* don't care about kernel version number */ #define CODA_KERNEL_VERSION 1 /* The old venus 4.6 compatible interface */ #endif @@ -363,9 +358,6 @@ struct coda_root_in { struct coda_in_hdr in; }; -/* coda_sync: */ -/* Nothing needed for coda_sync */ - /* coda_open: */ struct coda_open_in { struct coda_in_hdr ih; @@ -542,20 +534,6 @@ struct coda_rmdir_out { struct coda_out_hdr out; }; -/* coda_readdir: */ -struct coda_readdir_in { - struct coda_in_hdr ih; - ViceFid VFid; - int count; - int offset; -}; - -struct coda_readdir_out { - struct coda_out_hdr oh; - int size; - caddr_t data; /* Place holder for data. */ -}; - /* coda_symlink: NO_OUT */ struct coda_symlink_in { struct coda_in_hdr ih; @@ -592,12 +570,6 @@ struct coda_fsync_out { struct coda_out_hdr out; }; -/* coda_inactive: NO_OUT */ -struct coda_inactive_in { - struct coda_in_hdr ih; - ViceFid VFid; -}; - /* coda_vget: */ struct coda_vget_in { struct coda_in_hdr ih; @@ -651,38 +623,24 @@ struct coda_purgefid_out { ViceFid CodaFid; }; -struct coda_make_cinode_out { +/* coda_replace: */ +/* CODA_REPLACE is a venus->kernel call */ +struct coda_replace_out { /* coda_replace is a venus->kernel call */ struct coda_out_hdr oh; - ViceFid CodaFid; - struct coda_vattr attr; - int fd; + ViceFid NewFid; + ViceFid OldFid; }; -/* coda_rdwr: */ -struct coda_rdwr_in { +/* coda_open_by_fd: */ +struct coda_open_by_fd_in { struct coda_in_hdr ih; - ViceFid VFid; - int rwflag; - int count; - int offset; - int ioflag; - caddr_t data; /* Place holder for data. */ + ViceFid VFid; + int flags; }; -struct coda_rdwr_out { +struct coda_open_by_fd_out { struct coda_out_hdr oh; - int rwflag; - int count; - caddr_t data; /* Place holder for data. */ -}; - - -/* coda_replace: */ -/* CODA_REPLACE is a venus->kernel call */ -struct coda_replace_out { /* coda_replace is a venus->kernel call */ - struct coda_out_hdr oh; - ViceFid NewFid; - ViceFid OldFid; + int fd; }; /* coda_open_by_path: */ @@ -729,13 +687,11 @@ union inputArgs { struct coda_rename_in coda_rename; struct coda_mkdir_in coda_mkdir; struct coda_rmdir_in coda_rmdir; - struct coda_readdir_in coda_readdir; struct coda_symlink_in coda_symlink; struct coda_readlink_in coda_readlink; struct coda_fsync_in coda_fsync; - struct coda_inactive_in coda_inactive; struct coda_vget_in coda_vget; - struct coda_rdwr_in coda_rdwr; + struct coda_open_by_fd_in coda_open_by_fd; struct coda_open_by_path_in coda_open_by_path; struct coda_statfs_in coda_statfs; }; @@ -749,7 +705,6 @@ union outputArgs { struct coda_lookup_out coda_lookup; struct coda_create_out coda_create; struct coda_mkdir_out coda_mkdir; - struct coda_readdir_out coda_readdir; struct coda_readlink_out coda_readlink; struct coda_vget_out coda_vget; struct coda_purgeuser_out coda_purgeuser; @@ -757,9 +712,8 @@ union outputArgs { struct coda_zapdir_out coda_zapdir; struct coda_zapvnode_out coda_zapvnode; struct coda_purgefid_out coda_purgefid; - struct coda_rdwr_out coda_rdwr; struct coda_replace_out coda_replace; - struct coda_make_cinode_out coda_make_cinode; + struct coda_open_by_fd_out coda_open_by_fd; struct coda_open_by_path_out coda_open_by_path; struct coda_statfs_out coda_statfs; }; @@ -805,5 +759,15 @@ struct PioctlData { #define IS_CTL_FID(fidp) ((fidp)->Volume == CTL_VOL &&\ (fidp)->Vnode == CTL_VNO &&\ (fidp)->Unique == CTL_UNI) + +/* Data passed to mount */ + +#define CODA_MOUNT_VERSION 1 + +struct coda_mount_data { + int version; + int fd; /* Opened device */ +}; + #endif diff --git a/include/linux/coda_cache.h b/include/linux/coda_cache.h index fe3b2f40c317..95b593d14eec 100644 --- a/include/linux/coda_cache.h +++ b/include/linux/coda_cache.h @@ -10,22 +10,10 @@ #ifndef _CFSNC_HEADER_ #define _CFSNC_HEADER_ -/* - * Structure for an element in the Coda Credential Cache. - */ - -struct coda_cache { - struct list_head cc_cclist; /* list of all cache entries */ - struct list_head cc_cnlist; /* list of cache entries/cnode */ - int cc_mask; - struct coda_cred cc_cred; -}; - /* credential cache */ void coda_cache_enter(struct inode *inode, int mask); void coda_cache_clear_inode(struct inode *); -void coda_cache_clear_all(struct super_block *sb); -void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred); +void coda_cache_clear_all(struct super_block *sb, struct coda_cred *cred); int coda_cache_check(struct inode *inode, int mask); /* for downcalls and attributes and lookups */ diff --git a/include/linux/coda_fs_i.h b/include/linux/coda_fs_i.h index ac14c8e7fbf8..4a776e87f9d9 100644 --- a/include/linux/coda_fs_i.h +++ b/include/linux/coda_fs_i.h @@ -20,9 +20,12 @@ struct coda_inode_info { struct ViceFid c_fid; /* Coda identifier */ u_short c_flags; /* flags (see below) */ - struct list_head c_cnhead; /* head of cache entries */ struct list_head c_volrootlist; /* list of volroot cnoddes */ - struct inode *c_vnode; /* inode associated with cnode */ + struct list_head c_cilist; /* list of all coda inodes */ + struct inode *c_vnode; /* inode associated with cnode */ + unsigned int c_contcount; /* refcount for container inode */ + struct coda_cred c_cached_cred; /* credentials of cached perms */ + unsigned int c_cached_perm; /* cached access permissions */ int c_magic; /* to verify the data structure */ }; diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h index 0b4057efc202..3374036ce6b1 100644 --- a/include/linux/coda_linux.h +++ b/include/linux/coda_linux.h @@ -40,6 +40,8 @@ int coda_release(struct inode *i, struct file *f); int coda_permission(struct inode *inode, int mask); int coda_revalidate_inode(struct dentry *); int coda_notify_change(struct dentry *, struct iattr *); +int coda_pioctl(struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg); /* global variables */ extern int coda_debug; @@ -103,20 +105,20 @@ void coda_sysctl_clean(void); #define CODA_ALLOC(ptr, cast, size) \ do { \ - if (size < 3000) { \ + if (size < PAGE_SIZE) { \ ptr = (cast)kmalloc((unsigned long) size, GFP_KERNEL); \ - CDEBUG(D_MALLOC, "kmalloced: %lx at %p.\n", (long)size, ptr);\ - } else { \ + CDEBUG(D_MALLOC, "kmalloced: %lx at %p.\n", (long)size, ptr); \ + } else { \ ptr = (cast)vmalloc((unsigned long) size); \ - CDEBUG(D_MALLOC, "vmalloced: %lx at %p .\n", (long)size, ptr);}\ + CDEBUG(D_MALLOC, "vmalloced: %lx at %p .\n", (long)size, ptr);} \ if (ptr == 0) { \ - printk("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \ + printk("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \ } \ - memset( ptr, 0, size ); \ + else memset( ptr, 0, size ); \ } while (0) -#define CODA_FREE(ptr,size) do {if (size < 3000) { kfree((ptr)); CDEBUG(D_MALLOC, "kfreed: %lx at %p.\n", (long) size, ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %lx at %p.\n", (long) size, ptr);} } while (0) +#define CODA_FREE(ptr,size) do {if (size < PAGE_SIZE) { kfree((ptr)); CDEBUG(D_MALLOC, "kfreed: %lx at %p.\n", (long) size, ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %lx at %p.\n", (long) size, ptr);} } while (0) /* inode to cnode access functions */ diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index 73757bb5360b..db03f6af8e42 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -4,19 +4,13 @@ #define CODA_PSDEV_MAJOR 67 #define MAX_CODADEVS 5 /* how many do we allow */ -extern struct venus_comm coda_upc_comm; -extern struct coda_sb_info coda_super_info; #define CODA_SUPER_MAGIC 0x73757245 struct coda_sb_info { - struct inode * sbi_psdev; /* /dev/cfs? Venus/kernel device */ - int sbi_refct; struct venus_comm * sbi_vcomm; - struct inode * sbi_root; struct super_block *sbi_sb; - struct list_head sbi_cchead; - struct list_head sbi_volroothead; + struct list_head sbi_cihead; }; /* communication pending/processing queues */ @@ -26,6 +20,7 @@ struct venus_comm { struct list_head vc_pending; struct list_head vc_processing; int vc_inuse; + struct super_block *vc_sb; }; @@ -35,11 +30,6 @@ static inline struct coda_sb_info *coda_sbp(struct super_block *sb) } - -extern void coda_psdev_detach(int unit); -extern int init_coda_psdev(void); - - /* upcalls */ int venus_rootfid(struct super_block *sb, ViceFid *fidp); int venus_getattr(struct super_block *sb, struct ViceFid *fid, @@ -112,6 +102,7 @@ struct coda_upcallstats { } ; extern struct coda_upcallstats coda_callstats; +extern struct venus_comm coda_comms[]; static inline void clstats(int opcode) { diff --git a/include/linux/major.h b/include/linux/major.h index 7b43e59d0902..89e06bb8a6fe 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -108,6 +108,15 @@ #define SPECIALIX_NORMAL_MAJOR 75 #define SPECIALIX_CALLOUT_MAJOR 76 +#define COMPAQ_CISS_MAJOR 104 +#define COMPAQ_CISS_MAJOR1 105 +#define COMPAQ_CISS_MAJOR2 106 +#define COMPAQ_CISS_MAJOR3 107 +#define COMPAQ_CISS_MAJOR4 108 +#define COMPAQ_CISS_MAJOR5 109 +#define COMPAQ_CISS_MAJOR6 110 +#define COMPAQ_CISS_MAJOR7 111 + #define DASD_MAJOR 94 /* Official assignations from Peter */ #define MDISK_MAJOR 95 /* Official assignations from Peter */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 09a5895545cb..cff4c4fffadb 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -131,6 +131,7 @@ #define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 #define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 #define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 +#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 #define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 #define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 @@ -463,6 +464,7 @@ #define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 #define PCI_DEVICE_ID_APPLE_GC 0x0002 #define PCI_DEVICE_ID_APPLE_HYDRA 0x000e +#define PCI_DEVICE_ID_APPLE_UNINORTH 0x0020 #define PCI_VENDOR_ID_NEXGEN 0x1074 #define PCI_DEVICE_ID_NEXGEN_82C501 0x4e78 diff --git a/include/linux/usb.h b/include/linux/usb.h index e89a8ddd3d53..35495a408fa2 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -345,7 +345,6 @@ typedef int (*usb_device_irq)(int, void *, int, void *); */ #define USB_DISABLE_SPD 0x0001 #define USB_ISO_ASAP 0x0002 -#define USB_URB_EARLY_COMPLETE 0x0004 #define USB_ASYNC_UNLINK 0x0008 #define USB_QUEUE_BULK 0x0010 #define USB_NO_FSBR 0x0020 diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 4ad7577dffc4..487ee821526f 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -80,6 +80,7 @@ struct usbdevfs_connectinfo { #define USBDEVFS_URB_DISABLE_SPD 1 #define USBDEVFS_URB_ISO_ASAP 2 +#define USBDEVFS_URB_QUEUE_BULK 0x10 #define USBDEVFS_URB_TYPE_ISO 0 #define USBDEVFS_URB_TYPE_INTERRUPT 1 diff --git a/include/scsi/scsi_ioctl.h b/include/scsi/scsi_ioctl.h index 6ba0dd542f93..937cadfb917c 100644 --- a/include/scsi/scsi_ioctl.h +++ b/include/scsi/scsi_ioctl.h @@ -32,6 +32,13 @@ typedef struct scsi_idlun { __u32 host_unique_id; } Scsi_Idlun; +/* Fibre Channel WWN, port_id struct */ +typedef struct scsi_fctargaddress +{ + __u32 host_port_id; + unsigned char host_wwn[8]; // include NULL term. +} Scsi_FCTargAddress; + extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg); extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg); extern int scsi_ioctl_send_command(Scsi_Device *dev, diff --git a/net/core/dev.c b/net/core/dev.c index c670dd521f87..2a801868b02b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -295,37 +295,20 @@ int netdev_boot_setup_add(char *name, struct ifmap *map) * netdev_boot_setup_check - check boot time settings * @dev: the netdevice * - * Check boot time settings for the device. If device's name is a - * mask (eg. eth%d) and settings are found then this will allocate - * name for the device. The found settings are set for the device - * to be used later in the device probing. Returns 0 if no settings - * found, 1 if they are. + * Check boot time settings for the device. + * The found settings are set for the device to be used + * later in the device probing. + * Returns 0 if no settings found, 1 if they are. */ int netdev_boot_setup_check(struct net_device *dev) { struct netdev_boot_setup *s; - char buf[IFNAMSIZ + 1]; - int i, mask = 0; - - memset(buf, 0, sizeof(buf)); - strcpy(buf, dev->name); - if (strchr(dev->name, '%')) { - *strchr(buf, '%') = '\0'; - mask = 1; - } + int i; s = dev_boot_setup; for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { if (s[i].name[0] != '\0' && s[i].name[0] != ' ' && - !strncmp(buf, s[i].name, mask ? strlen(buf) : - strlen(s[i].name))) { - if (__dev_get_by_name(s[i].name)) { - if (!mask) - return 0; - continue; - } - memset(dev->name, 0, IFNAMSIZ); - strcpy(dev->name, s[i].name); + !strncmp(dev->name, s[i].name, strlen(s[i].name))) { dev->irq = s[i].map.irq; dev->base_addr = s[i].map.base_addr; dev->mem_start = s[i].map.mem_start; @@ -333,7 +316,6 @@ int netdev_boot_setup_check(struct net_device *dev) return 1; } } - return 0; } @@ -2463,27 +2445,26 @@ int __init net_dev_init(void) dev->iflink = -1; dev_hold(dev); + /* + * Allocate name. If the init() fails + * the name will be reissued correctly. + */ + if (strchr(dev->name, '%')) + dev_alloc_name(dev, dev->name); + /* * Check boot time settings for the device. */ - if (!netdev_boot_setup_check(dev)) { - /* - * No settings found - allocate name. If the init() - * fails the name will be reissued correctly. - */ - if (strchr(dev->name, '%')) - dev_alloc_name(dev, dev->name); - } + netdev_boot_setup_check(dev); if (dev->init && dev->init(dev)) { /* - * It failed to come up. Unhook it. + * It failed to come up. It will be unhooked later. + * dev_alloc_name can now advance to next suitable + * name that is checked next. */ - write_lock_bh(&dev_base_lock); - *dp = dev->next; dev->deadbeaf = 1; - write_unlock_bh(&dev_base_lock); - dev_put(dev); + dp = &dev->next; } else { dp = &dev->next; dev->ifindex = dev_new_index(); @@ -2496,6 +2477,21 @@ int __init net_dev_init(void) } } + /* + * Unhook devices that failed to come up + */ + dp = &dev_base; + while ((dev = *dp) != NULL) { + if (dev->deadbeaf) { + write_lock_bh(&dev_base_lock); + *dp = dev->next; + write_unlock_bh(&dev_base_lock); + dev_put(dev); + } else { + dp = &dev->next; + } + } + #ifdef CONFIG_PROC_FS proc_net_create("dev", 0, dev_get_info); create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL); diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 01211e990fe1..9c1088e768f3 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -21,7 +21,7 @@ struct in_device; #endif /* Send RST reply */ -static void send_reset(struct sk_buff *oldskb) +static void send_reset(struct sk_buff *oldskb, int local) { struct sk_buff *nskb; struct tcphdr *otcph, *tcph; @@ -114,8 +114,9 @@ static void send_reset(struct sk_buff *oldskb) nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, nskb->nh.iph->ihl); - /* Routing */ - if (ip_route_output(&rt, nskb->nh.iph->daddr, nskb->nh.iph->saddr, + /* Routing: if not headed for us, route won't like source */ + if (ip_route_output(&rt, nskb->nh.iph->daddr, + local ? nskb->nh.iph->saddr : 0, RT_TOS(nskb->nh.iph->tos) | RTO_CONN, 0) != 0) goto free_nskb; @@ -188,7 +189,7 @@ static unsigned int reject(struct sk_buff **pskb, } break; case IPT_TCP_RESET: - send_reset(*pskb); + send_reset(*pskb, hooknum == NF_IP_LOCAL_IN); break; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fe512077c4b9..78fffa9c075c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -58,6 +58,7 @@ * J Hadi Salim: ECN support */ +#include #include #include #include -- 2.39.5