## Automatically generated incremental diff ## From: linux-2.5.68-bk7 ## To: linux-2.5.68-bk8 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.5.68-bk7/Documentation/scsi/ChangeLog.megaraid linux-2.5.68-bk8/Documentation/scsi/ChangeLog.megaraid --- linux-2.5.68-bk7/Documentation/scsi/ChangeLog.megaraid 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.68-bk8/Documentation/scsi/ChangeLog.megaraid 2003-04-27 04:36:26.000000000 -0700 @@ -0,0 +1,42 @@ +### Version 2.00.3 +Wed Jan 29 09:13:44 EST 200 - Atul Mukker +i. Change the handshake in ISR while acknowledging interrupts. Write the + valid interrupt pattern 0x10001234 as soon as it is read from the + outdoor register. In existing driver and on certain platform, invalid + command ids were being returned. + + Also, do not wait on status be become 0xFF, since FW can return this + status in certain circumstances. + + Initialize the numstatus field of mailbox to 0xFF so that we can wait + on this wait in next interrupt. Firmware does not change its value + unless there are some status to be posted + +ii. Specify the logical drive number while issuing the RESERVATION_STATUS + +iii. Reduce the default mailbox busy wait time from 300us to 10us. This is + done to avaoid a possible deadlock in FW because of longer bust waits. + +iv. The max outstanding commands are reduced to 126 because that't the + safest value on all FW. + +v. Number of sectors per IO are reduced to 128 (64kb), becuase FW needs + resources in special circumstances like check consistency, rebuilds + etc. + +vi. max_commands is no longer a module parameter because of iv. + +### Version: 2.00.2 +i. Intermediate release with kernel specific code + + +### Version: 2.00.1i +Wed Dec 4 14:34:51 EST 2002 - Atul Mukker +i. Making the older IO based controllers to work with this driver + + +### Version 2.00.1 +Fri Nov 15 10:59:44 EST 2002 - Atul Mukker +i. Release host lock before issuing internal command to reset + reservations in megaraid_reset() and reacquire after internal command + is completed. diff -urN linux-2.5.68-bk7/Documentation/scsi/dc395x.txt linux-2.5.68-bk8/Documentation/scsi/dc395x.txt --- linux-2.5.68-bk7/Documentation/scsi/dc395x.txt 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.68-bk8/Documentation/scsi/dc395x.txt 2003-04-27 04:36:26.000000000 -0700 @@ -0,0 +1,72 @@ +README file for the dc395x SCSI driver +========================================== + +Status +------ +The driver has been tested with CD-R and CD-R/W drives. These should +be safe to use. Testing with hard disks has not been done to any +great degree and caution should be exercised if you want to attempt +to use this driver with hard disks. + +This is a 2.5 only driver. For a 2.4 driver please see the original +driver (which this driver started from) at +http://www.garloff.de/kurt/linux/dc395/ + +Problems, questions and patches should be submitted to the mailing +list. Details on the list, including archives, are available at +http://lists.twibble.org/mailman/listinfo/dc395x/ + +Parameters +---------- +The driver uses the settings from the EEPROM set in the SCSI BIOS +setup. If there is no EEPROM, the driver uses default values. +Both can be overriden by command line parameters (module or kernel +parameters). + +The syntax is as follows: + dc395x = AdapterID, SpeedIdx, DevMode, AdaptMode, Tags, DelayReset + +AdapterID : Host Adapter SCSI ID +SpeedIdx : 0,1,...7 = 20,13.3,10,8,6.7,5.8,5,4 MHz [ 7] +DevMode : Bitmap for Dev Cfg [63] +AdaptMode : Bitmap for Adapter Cfg [47] +Tags : The number of tags is 1<= 1. + +If you set AdapterID to -1, the adapter will use conservative +("safe") default settings instead; more precisely, dc395x=-1 is a +shortcut for dc395x=7,4,9,15,2,10 + +If you specify -2 for a value, it will be ignored. You don't need to +specify all six parameters. + +Copyright +--------- +The driver is free software. It is protected by the GNU General Public +License (GPL). Please read it, before using this driver. It should be +included in your kernel sources and with your distribution. It carries the +filename COPYING. If you don't have it, please ask me to send you one by +email. +Note: The GNU GPL says also something about warranty and liability. +Please be aware the following: While we do my best to provide a working and +reliable driver, there is a chance, that it will kill your valuable data. +We refuse to take any responsibility for that. The driver is provided as-is +and YOU USE IT AT YOUR OWN RESPONSIBILITY. diff -urN linux-2.5.68-bk7/Documentation/scsi/scsi_mid_low_api.txt linux-2.5.68-bk8/Documentation/scsi/scsi_mid_low_api.txt --- linux-2.5.68-bk7/Documentation/scsi/scsi_mid_low_api.txt 2003-04-19 19:50:35.000000000 -0700 +++ linux-2.5.68-bk8/Documentation/scsi/scsi_mid_low_api.txt 2003-04-27 04:36:26.000000000 -0700 @@ -16,13 +16,13 @@ (SPI) controllers based on that company's 7xxx chip series. The aic7xxx LLD can be built into the kernel or loaded as a module. There can only be one aic7xxx LLD running in a Linux system but it may be controlling many -HBAs. These HBAs might be either on PCI daughterboards or built into +HBAs. These HBAs might be either on PCI daughter-boards or built into the motherboard (or both). Like most modern HBAs, each aic7xxx host -has its own PCI device address. [The one-to-one correspondance between +has its own PCI device address. [The one-to-one correspondence between a SCSI host and a PCI device is common but not required (e.g. with ISA or MCA adapters).] -This version of the document roughly matches linux kernel version 2.5.63 . +This version of the document roughly matches linux kernel version 2.5.67 . Documentation ============= @@ -33,14 +33,15 @@ (e.g. aic7xxx.txt). The SCSI mid-level is briefly described in scsi.txt (with a url to a document describing the SCSI subsystem in the lk 2.4 series). Two upper level drivers have documents in that directory: -st.txt (SCSI tape driver) and scsi-generic.txt . +st.txt (SCSI tape driver) and scsi-generic.txt (for the sg driver). Some documentation (or urls) for LLDs may be in the C source code or -in the same directory. For example to find a url about the USB mass -storage driver see the /usr/src/linux/drivers/usb/storage directory. +in the same directory as the C source code. For example to find a url +about the USB mass storage driver see the +/usr/src/linux/drivers/usb/storage directory. The Linux kernel source Documentation/DocBook/scsidrivers.tmpl file -refers to this file. With the appropriate DocBook toolset, this permits +refers to this file. With the appropriate DocBook tool-set, this permits users to generate html, ps and pdf renderings of information within this file (e.g. the interface functions). @@ -49,17 +50,15 @@ Traditionally a LLD for the SCSI subsystem has been at least two files in the drivers/scsi directory. For example, a driver called "xyz" has a header file "xyz.h" and a source file "xyz.c". [Actually there is no good reason -why this couldn't all be in one file.] Some drivers that have been ported -to several operating systems have more than two files. For example the -aic7xxx driver has separate files for generic and OS-specific code -(e.g. FreeBSD and Linux). Such drivers tend to have their own directory -under the drivers/scsi directory. +why this couldn't all be in one file; the header file is superfluous.] Some +drivers that have been ported to several operating systems have more than +two files. For example the aic7xxx driver has separate files for generic +and OS-specific code (e.g. FreeBSD and Linux). Such drivers tend to have +their own directory under the drivers/scsi directory. When a new LLD is being added to Linux, the following files (found in the -drivers/scsi directory) will need some attention: Makefile, Config.help and -Config.in . SCSI documentation is found in the Documentation/scsi directory -of the kernel source tree. It is probably best to study how existing LLDs -are organized. +drivers/scsi directory) will need some attention: Makefile and Kconfig . +It is probably best to study how existing LLDs are organized. As the 2.5 series development kernels evolve, changes are being introduced into this interface. An example of this is driver @@ -78,7 +77,8 @@ a) directly invoking functions supplied by the mid level b) passing a set of function pointers to a registration function supplied by the mid level. The mid level will then invoke these - functions at some point in the future + functions at some point in the future. The LLD will supply + implementations of these functions. c) direct access to instances of well known data structures maintained by the mid level @@ -86,7 +86,7 @@ supplied functions" below. Those functions in group b) are listed in a section entitled "Interface -functions" below. The function pointers are placed in the members of +functions" below. Their function pointers are placed in the members of "struct SHT", an instance of which is passed to scsi_register() [or scsi_register_host() in the passive initialization model]. Those interface functions that are not mandatory and that the LLD does not wish to supply @@ -97,34 +97,42 @@ Those instances in group c) are slowly being removed as they tend to be "racy" especially in a hotplug environment. +All functions defined within a LLD and all data defined at file scope +should be static. For example the slave_alloc() function in a LLD +called "xxx" could be defined as +"static int xxx_slave_alloc(struct scsi_device * sdev) { /* code */ }" + Hotplug initialization model ============================ In this model a LLD controls when SCSI hosts are introduced and removed from the SCSI subsystem. Hosts can be introduced as early as driver initialization and removed as late as driver shutdown. Typically a driver -will respond to a sysfs probe() callback that indicates a HBA is present -(e.g. a PCI device). After confirming it is a device that it wants to -control, it will initialize the HBA and then register a new host with the -SCSI mid level. +will respond to a sysfs probe() callback that indicates a HBA has been +detected. After confirming that the new device is one that the LLD wants +to control, the LLD will initialize the HBA and then register a new host +with the SCSI mid level. Hot unplugging a HBA that controls a disk which is processing SCSI commands on a mounted file system is an ugly situation. Issues with this scenario are still being worked through. The primary concern is the stability of the kernel (specifically the block and SCSI subsystems) since the effected disk can be "cleaned up" the next time it is seen. +In the sysfs model, a remove() callback indicates a HBA has disappeared. During LLD initialization the driver should register itself with the appropriate IO bus on which it expects to find HBA(s) (e.g. the PCI bus). This can probably be done via sysfs. Any driver parameters (especially -those that are writeable after the driver is loaded) could also be -registered with sysfs at this point. At the end of driver initialization -the SCSI mid level is typically not aware of its presence. +those that are writable after the driver is loaded) could also be +registered with sysfs at this point. The SCSI mid level first becomes +aware of a LLD when that LLD registers its first HBA. At some later time, the LLD becomes aware of a HBA and what follows is a typical sequence of calls between the LLD and the mid level. -This example shows 3 devices being found and 1 device not being found: +This example shows the mid level scanning the newly introduced HBA for 3 +scsi devices of which only the first 2 respond: +[HBA PROBE] LLD mid level LLD --- --------- --- scsi_register() --> @@ -136,9 +144,6 @@ slave_alloc() slave_configure() --> scsi_adjust_queue_depth() | - slave_alloc() - slave_configure() --> scsi_adjust_queue_depth() - | slave_alloc() ** slave_destroy() ** @@ -150,21 +155,40 @@ Here is the corresponding sequence when a host (HBA) is being removed: -LLD mid level ---- --------- -scsi_remove_host() -----+ - | - slave_destroy() - slave_destroy() - slave_destroy() -scsi_unregister() --> +[HBA REMOVE] +LLD mid level LLD +--- --------- --- +scsi_remove_host() ---------+ + | + slave_destroy() + slave_destroy() + release() --> scsi_unregister() It is practical for a LLD to keep track of struct Scsi_Host instances (a pointer is returned by scsi_register() ) and struct scsi_device instances (a pointer is passed as the parameter to slave_alloc() and slave_configure() ). Both classes of instances are "owned" by the mid-level. struct scsi_device instances are freed after slave_destroy(). -struct Scsi_Host instances are freed after scsi_unregister(). +struct Scsi_Host instances are freed within scsi_unregister(). + +TODO: +Descriptions, are the following correct? +[DEVICE hotplug] +LLD mid level LLD +--- --------- --- +scsi_add_device() ------+ + | + slave_alloc() + slave_configure() --> scsi_adjust_queue_depth() + +[DEVICE unplug] +LLD mid level LLD +--- --------- --- +scsi_set_device_offline() +scsi_remove_device() -------+ + | + slave_destroy() + Passive initialization model @@ -226,8 +250,7 @@ slave_destroy() release() --> scsi_unregister() -Both slave_destroy() and release() are optional. If they are not supplied -the mid level supplies default actions. +slave_destroy() is optional. The shortcoming of the "passive initialization model" is that host registration and de-registration are (typically) tied to LLD initialization @@ -238,39 +261,97 @@ Conventions =========== -First Linus's thoughts on C coding found in file Documentation/CodingStyle . +First, Linus's thoughts on C coding can be found in the file +Documentation/CodingStyle . -Next there is a movement to "outlaw" typedefs introducing synonyms for +Next, there is a movement to "outlaw" typedefs introducing synonyms for struct tags. Both can be still found in the SCSI subsystem, for example: "typedef struct SHT { ...} Scsi_Host_Template;" in hosts.h . In this -case "struct SHT" is preferred to "Scsi_Host_Template". +case "struct SHT" is preferred to "Scsi_Host_Template". [The poor naming +example was chosen with malevolent intent.] -Also C99 additions are encouraged to the extent they are supported +Also, C99 enhancements are encouraged to the extent they are supported by the relevant gcc compilers. So "//" style comments are encouraged were appropriate as are C99 style structure and array initializers. +Don't go too far, VLAs are not properly supported yet. + +Well written, tested and documented code, need not be re-formatted to +comply with the above conventions. For example, the aic7xxx driver +comes to Linux from FreeBSD and Adaptec's own labs. No doubt FreeBSD +and Adaptec have their own coding conventions. Mid level supplied functions ============================ These functions are supplied by the SCSI mid level for use by LLDs. -The names (i.e. entry points) of these functions are exported -so a LLD that is a module can access them when the SCSI -mid level is built into the kernel. The kernel will arrange for the -SCSI mid level to be loaded and initialized before any LLD +The names (i.e. entry points) of these functions are exported (in +scsi_syms.c) so a LLD that is a module can access them. The kernel will +arrange for the SCSI mid level to be loaded and initialized before any LLD is initialized. The functions below are listed alphabetically and their names all start with "scsi_". +Summary: + scsi_add_device - creates new scsi device (lu) instance + scsi_add_host - perform sysfs registration and SCSI bus scan. + scsi_add_timer - (re-)start timer on a SCSI command. + scsi_adjust_queue_depth - change the queue depth on a SCSI device + scsi_assign_lock - replace default host_lock with given lock + scsi_bios_ptable - return copy of block device's partition table + scsi_block_requests - prevent further commands being queued to given host + scsi_delete_timer - cancel timer on a SCSI command. + scsi_partsize - parse partition table into cylinders, heads + sectors + scsi_register - create and register a scsi host adapter instance. + scsi_register_host - register a low level host driver + scsi_remove_device - detach and remove a SCSI device + scsi_remove_host - detach and remove all SCSI devices owned by host + scsi_report_bus_reset - report scsi _bus_ reset observed + scsi_set_device - place device reference in host structure + scsi_set_device_offline - set device offline then flush its queue + scsi_to_pci_dma_dir - convert SCSI subsystem direction flag to PCI + scsi_to_sbus_dma_dir - convert SCSI subsystem direction flag to SBUS + scsi_track_queue_full - track successive QUEUE_FULL events + scsi_unblock_requests - allow further commands to be queued to given host + scsi_unregister - unregister and free memory used by host instance + scsi_unregister_host - unregister a low level host adapter driver + +Details: + +/** + * scsi_add_device - creates new scsi device (lu) instance + * @shost: pointer to scsi host instance + * @channel: channel number (rarely other than 0) + * @id: target id number + * @lun: logical unit number + * + * Returns pointer to new struct scsi_device instance or + * ERR_PTR(-ENODEV) (or some other bent pointer) if something is + * wrong (e.g. no lu responds at given address) + * + * Notes: This call is usually performed internally during a scsi + * bus scan when a HBA is added (i.e. scsi_add_host()). So it + * should only be called if the HBA becomes aware of a new scsi + * device (lu) after scsi_add_host() has completed. If successful + * this call we lead to slave_alloc() and slave_configure() callbacks + * into the LLD. + * + * Defined in: drivers/scsi/scsi_scan.c + **/ +struct scsi_device * scsi_add_device(struct Scsi_Host *shost, + unsigned int channel, + unsigned int id, unsigned int lun) + + /** * scsi_add_host - perform sysfs registration and SCSI bus scan. * @shost: pointer to scsi host instance - * @dev: pointer to struct device host instance of class type scsi - * (or related) + * @dev: pointer to struct device host instance * * Returns 0 on success, negative errno of failure (e.g. -ENOMEM) * * Notes: Only required in "hotplug initialization model" after a * successful call to scsi_register(). - * Defined in drivers/scsi/hosts.c + * + * Defined in: drivers/scsi/hosts.c **/ int scsi_add_host(struct Scsi_Host *shost, struct device * dev) @@ -283,10 +364,12 @@ * * Returns nothing * - * Notes: All commands issued by upper levels already have a timeout - * associated with them. A LLD can use this function to change + * Notes: Each scsi command has its own timer, and as it is added + * to the queue, we set up the timer. When the command completes, + * we cancel the timer. A LLD can use this function to change * the existing timeout value. - * Defined in drivers/scsi/scsi_error.c + * + * Defined in: drivers/scsi/scsi_error.c **/ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete) (Scsi_Cmnd *)) @@ -295,10 +378,10 @@ /** * scsi_adjust_queue_depth - change the queue depth on a SCSI device * @SDpnt: pointer to SCSI device to change queue depth on - * @tagged: 0 - no tagged queueing - * MSG_SIMPLE_TAG - simple (unordered) tagged queueing - * MSG_ORDERED_TAG - ordered tagged queueing - * @tags Number of tags allowed if tagged queueing enabled, + * @tagged: 0 - no tagged queuing + * MSG_SIMPLE_TAG - simple (unordered) tagged queuing + * MSG_ORDERED_TAG - ordered tagged queuing + * @tags Number of tags allowed if tagged queuing enabled, * or number of commands the LLD can queue up * in non-tagged mode (as per cmd_per_lun). * @@ -309,7 +392,8 @@ * slave_destroy().] Can safely be invoked from interrupt code. Actual * queue depth change may be delayed until the next command is being * processed. - * Defined in drivers/scsi/scsi.c [see source code for more notes] + * + * Defined in: drivers/scsi/scsi.c [see source code for more notes] * **/ void scsi_adjust_queue_depth(struct scsi_device * SDpnt, int tagged, @@ -323,7 +407,7 @@ * * Returns nothing * - * Notes: Defined in drivers/scsi/hosts.h . + * Defined in: drivers/scsi/hosts.h . **/ void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock) @@ -332,10 +416,11 @@ * scsi_bios_ptable - return copy of block device's partition table * @dev: pointer to block device * - * Returns pointer to partition table, or NULL or failure + * Returns pointer to partition table, or NULL for failure * * Notes: Caller owns memory returned (free with kfree() ) - * Defined in drivers/scsi/scsicam.c + * + * Defined in: drivers/scsi/scsicam.c **/ unsigned char *scsi_bios_ptable(struct block_device *dev) @@ -349,7 +434,8 @@ * * Notes: There is no timer nor any other means by which the requests * get unblocked other than the LLD calling scsi_unblock_requests(). - * Defined in drivers/scsi/scsi_lib.c + * + * Defined in: drivers/scsi/scsi_lib.c **/ void scsi_block_requests(struct Scsi_Host * SHpnt) @@ -364,7 +450,8 @@ * Notes: All commands issued by upper levels already have a timeout * associated with them. A LLD can use this function to cancel the * timer. - * Defined in drivers/scsi/scsi_error.c + * + * Defined in: drivers/scsi/scsi_error.c **/ int scsi_delete_timer(Scsi_Cmnd *scmd) @@ -380,10 +467,11 @@ * Returns 0 on success, -1 on failure * * Notes: Caller owns memory returned (free with kfree() ) - * Defined in drivers/scsi/scsicam.c + * + * Defined in: drivers/scsi/scsicam.c **/ int scsi_partsize(unsigned char *buf, unsigned long capacity, - unsigned int *cyls, unsigned int *hds, unsigned int *secs) + unsigned int *cyls, unsigned int *hds, unsigned int *secs) /** @@ -398,7 +486,8 @@ * this host has _not_ yet been done. * The hostdata array (by default zero length) is a per host scratch * area for the LLD. - * Defined in drivers/scsi/hosts.c . + * + * Defined in: drivers/scsi/hosts.c . **/ struct Scsi_Host * scsi_register(struct SHT *, int xtr_bytes) @@ -415,12 +504,30 @@ * function by including the scsi_module.c file. * This function is deprecated, use the "hotplug initialization * model" instead. - * Defined in drivers/scsi/hosts.c . + * + * Defined in: drivers/scsi/hosts.c . **/ int scsi_register_host(Scsi_Host_Template *shost_tp) /** + * scsi_remove_device - detach and remove a SCSI device + * @sdev: a pointer to a scsi device instance + * + * Returns value: 0 on success, -EINVAL if device not attached + * + * Notes: If a LLD becomes aware that a scsi device (lu) has + * been removed but its host is still present then it can request + * the removal of that scsi device. If successful this call will + * lead to the slave_destroy() callback being invoked. sdev is an + * invalid pointer after this call. + * + * Defined in: drivers/scsi/scsi_scan.c . + **/ +int scsi_remove_device(struct scsi_device *sdev) + + +/** * scsi_remove_host - detach and remove all SCSI devices owned by host * @shost: a pointer to a scsi host instance * @@ -429,7 +536,8 @@ * Notes: Should only be invoked if the "hotplug initialization * model" is being used. It should be called _prior_ to * scsi_unregister(). - * Defined in drivers/scsi/hosts.c . + * + * Defined in: drivers/scsi/hosts.c . **/ int scsi_remove_host(struct Scsi_Host *shost) @@ -446,7 +554,8 @@ * mid level itself don't need to call this, but there should be * no harm. The main purpose of this is to make sure that a * CHECK_CONDITION is properly treated. - * Defined in drivers/scsi/scsi_lib.c . + * + * Defined in: drivers/scsi/scsi_lib.c . **/ void scsi_report_bus_reset(struct Scsi_Host * shost, int channel) @@ -458,12 +567,27 @@ * * Returns nothing * - * Notes: Defined in drivers/scsi/hosts.h . + * Defined in: drivers/scsi/hosts.h . **/ void scsi_set_device(struct Scsi_Host * shost, struct device * dev) /** + * scsi_set_device_offline - set device offline then flush its queue + * @sdev: a pointer to a scsi device instance to be set offline + * + * Returns nothing + * + * Notes: Commands that are currently active on the scsi device have + * their timers cancelled and are transferred to the host's + * "eh" list for cancellation. + + * Defined in: drivers/scsi/scsi.c . + **/ +void scsi_set_device_offline(struct scsi_device * sdev) + + +/** * scsi_to_pci_dma_dir - convert SCSI subsystem direction flag to PCI * @scsi_data_direction: SCSI subsystem direction flag * @@ -472,12 +596,26 @@ * PCI_DMA_BIDIRECTIONAL given SCSI_DATA_UNKNOWN * else returns PCI_DMA_NONE * - * Notes: Defined in drivers/scsi/scsi.h . + * Defined in: drivers/scsi/scsi.h . **/ int scsi_to_pci_dma_dir(unsigned char scsi_data_direction) /** + * scsi_to_sbus_dma_dir - convert SCSI subsystem direction flag to SBUS + * @scsi_data_direction: SCSI subsystem direction flag + * + * Returns SBUS_DMA_TODEVICE given SCSI_DATA_WRITE, + * SBUS_DMA_FROMDEVICE given SCSI_DATA_READ + * SBUS_DMA_BIDIRECTIONAL given SCSI_DATA_UNKNOWN + * else returns SBUS_DMA_NONE + * + * Defined in: drivers/scsi/scsi.h . + **/ +int scsi_to_sbus_dma_dir(unsigned char scsi_data_direction) + + +/** * scsi_track_queue_full - track successive QUEUE_FULL events on given * device to determine if and when there is a need * to adjust the queue depth on the device. @@ -492,7 +630,8 @@ * * Notes: LLDs may call this at any time and we will do "The Right * Thing"; interrupt context safe. - * Defined in drivers/scsi/scsi.c . + * + * Defined in: drivers/scsi/scsi.c . **/ int scsi_track_queue_full(Scsi_Device *SDptr, int depth) @@ -504,22 +643,22 @@ * * Returns nothing * - * Notes: Defined in drivers/scsi/scsi_lib.c . + * Defined in: drivers/scsi/scsi_lib.c . **/ void scsi_unblock_requests(struct Scsi_Host * SHpnt) /** - * scsi_unregister - unregister and free host + * scsi_unregister - unregister and free memory used by host instance * @shp: pointer to scsi host instance to unregister. - * N.B. shp points to freed memory on return * * Returns nothing * * Notes: Should only be invoked if the "hotplug initialization * model" is being used. It should be called _after_ - * scsi_remove_host(). - * Defined in drivers/scsi/hosts.c . + * scsi_remove_host(). The shp pointer is invalid after this call. + * + * Defined in: drivers/scsi/hosts.c . **/ void scsi_unregister(struct Scsi_Host * shp) @@ -532,11 +671,13 @@ * * Notes: Should only be invoked if the "passive initialization * model" is being used. Notice this is a _driver_ rather than - * HBA deregistration function. Most older drivers call this - * function by including the scsi_module.c file. - * This function is deprecated, use the "hotplug initialization + * HBA deregistration function. So if there are multiple HBAs + * associated with the given template, they are each removed. Most + * older drivers call this function by including the scsi_module.c + * file. This function is deprecated, use the "hotplug initialization * model" instead. - * Defined in drivers/scsi/hosts.c . + * + * Defined in: drivers/scsi/hosts.c . **/ int scsi_unregister_host(Scsi_Host_Template *shost_tp) @@ -567,6 +708,25 @@ The interface functions are listed below in alphabetical order. +Summary: + bios_param - fetch head, sector, cylinder info for a disk + command - send scsi command to device, wait for reply + detect - detects HBAs this driver wants to control + eh_abort_handler - abort given command + eh_bus_reset_handler - issue SCSI bus reset + eh_device_reset_handler - issue SCSI device reset + eh_host_reset_handler - reset host (host bus adapter) + eh_strategy_handler - driver supplied alternate to scsi_unjam_host() + info - supply information about given host + ioctl - driver can respond to ioctls + proc_info - supports /proc/scsi/{driver_name}/{host_no} + queuecommand - queue scsi command, invoke 'done' on completion + release - release all resources associated with given host + slave_alloc - prior to any commands being sent to a new device + slave_configure - driver fine tuning for given device after attach + slave_destroy - given device is about to be shut down + +Details: /** * bios_param - fetch head, sector, cylinder info for a disk @@ -588,6 +748,8 @@ * if this function is not provided. The params array is * pre-initialized with made up values just in case this function * doesn't output anything. + * + * Defined in: LLD **/ int bios_param(struct scsi_device * sdev, struct block_device *bdev, sector_t capacity, int params[3]); @@ -609,6 +771,8 @@ * * Notes: Drivers tend to be dropping support for this function and * supporting queuecommand() instead. + * + * Defined in: LLD **/ int command(struct scsi_cmnd * scp); @@ -629,6 +793,8 @@ * driver. Upper level drivers (e.g. sd) may not (yet) be present. * For each host found, this method should call scsi_register() * [see hosts.c]. + * + * Defined in: LLD **/ int detect(struct SHT * shtp); @@ -646,42 +812,48 @@ * * Notes: Invoked from scsi_eh thread. No other commands will be * queued on current host during eh. + * + * Defined in: LLD **/ int eh_abort_handler(struct scsi_cmnd * scp); /** - * eh_device_reset_handler - issue SCSI device reset - * @scp: identifies SCSI device to be reset + * eh_bus_reset_handler - issue SCSI bus reset + * @scp: SCSI bus that contains this device should be reset * * Returns SUCCESS if command aborted else FAILED * * Required: no * - * Locks: struct Scsi_Host::host_lock held (with irqsave) on entry + * Locks: struct Scsi_Host::host_lock held (with irqsave) on entry * and assumed to be held on return. * * Notes: Invoked from scsi_eh thread. No other commands will be * queued on current host during eh. + * + * Defined in: LLD **/ - int eh_device_reset_handler(struct scsi_cmnd * scp); + int eh_bus_reset_handler(struct scsi_cmnd * scp); /** - * eh_bus_reset_handler - issue SCSI bus reset - * @scp: SCSI bus that contains this device should be reset + * eh_device_reset_handler - issue SCSI device reset + * @scp: identifies SCSI device to be reset * * Returns SUCCESS if command aborted else FAILED * * Required: no * - * Locks: struct Scsi_Host::host_lock held (with irqsave) on entry + * Locks: struct Scsi_Host::host_lock held (with irqsave) on entry * and assumed to be held on return. * * Notes: Invoked from scsi_eh thread. No other commands will be * queued on current host during eh. + * + * Defined in: LLD **/ - int eh_bus_reset_handler(struct scsi_cmnd * scp); + int eh_device_reset_handler(struct scsi_cmnd * scp); /** @@ -701,6 +873,8 @@ * _device_reset_, _bus_reset_ or this eh handler function are * defined (or they all return FAILED) then the device in question * will be set offline whenever eh is invoked. + * + * Defined in: LLD **/ int eh_host_reset_handler(struct scsi_cmnd * scp); @@ -717,6 +891,8 @@ * * Notes: Invoked from scsi_eh thread. Driver supplied alternate to * scsi_unjam_host() found in scsi_error.c + * + * Defined in: LLD **/ int eh_strategy_handler(struct Scsi_Host * shp); @@ -745,6 +921,8 @@ * each host's "info" (or name) for the driver it is registering. * Also if proc_info() is not supplied, the output of this function * is used instead. + * + * Defined in: LLD **/ const char * info(struct Scsi_Host * shp); @@ -776,6 +954,8 @@ * numbers when this function is not supplied by the driver. * Unfortunately some applications expect -EINVAL and react badly * when -ENOTTY is returned; stick with -EINVAL. + * + * Defined in: LLD **/ int ioctl(struct scsi_device *sdp, int cmd, void *arg); @@ -803,6 +983,8 @@ * Locks: none held * * Notes: Driven from scsi_proc.c which interfaces to proc_fs + * + * Defined in: LLD **/ int proc_info(char * buffer, char ** start, off_t offset, int length, int hostno, int writeto1_read0); @@ -840,6 +1022,8 @@ * 'done' callback is invoked, then the LLD driver should * perform autosense and fill in the struct scsi_cmnd::sense_buffer * array. + * + * Defined in: LLD **/ int queuecommand(struct scsi_cmnd * scp, void (*done)(struct scsi_cmnd *)); @@ -849,16 +1033,20 @@ * release - release all resources associated with given host * @shp: host to be released. * - * Return value ignored. + * Return value ignored (could soon be a function returning void). * - * Required: no + * Required: yes (see notes) * - * Locks: lock_kernel() active on entry and expected to be active - * on return. + * Locks: none held * * Notes: Invoked from mid level's scsi_unregister_host(). - * This function should call scsi_unregister(shp) [found in hosts.c] - * prior to returning. + * LLD's implementation of this function should call + * scsi_unregister(shp) prior to returning. + * If not supplied mid-level [in hosts.c] supplies its own + * implementation (see scsi_host_legacy_release()) which is for old + * ISA adapters so it is best not to use it. + * + * Defined in: LLD **/ int release(struct Scsi_Host * shp); @@ -882,12 +1070,14 @@ * slave_configure() will be called while if a device is not found * slave_destroy() is called. * For more details see the hosts.h file. + * + * Defined in: LLD **/ int slave_alloc(struct scsi_device *sdp); /** - * slave_configure - driver fine tuning for give device just after it + * slave_configure - driver fine tuning for given device just after it * has been first scanned (i.e. it responded to an * INQUIRY) * @sdp: device that has just been attached @@ -908,6 +1098,8 @@ * value on behalf of the given device. If this function is * supplied then its implementation must call * scsi_adjust_queue_depth(). + * + * Defined in: LLD **/ int slave_configure(struct scsi_device *sdp); @@ -930,6 +1122,8 @@ * could be re-attached in the future in which case a new instance * of struct scsi_device would be supplied by future slave_alloc() * and slave_configure() calls.] + * + * Defined in: LLD **/ void slave_destroy(struct scsi_device *sdp); @@ -943,14 +1137,20 @@ typically initialized as a file scope static in a driver's header file. That way members that are not explicitly initialized will be set to 0 or NULL. Member of interest: - name - name of driver (should only use characters that are - permitted in a unix file name) - proc_name - name used in "/proc/scsi//" + name - name of driver (may contain spaces, please limit to + less than 80 characters) + proc_name - name used in "/proc/scsi//" and + by sysfs in one of its "drivers" directories. Hence + "proc_name" should only contain characters acceptable + to a Unix file name. + (*release)() - should be defined by all LLDs as the default (legacy) + implementation is only appropriate for ISA adapters). The structure is defined and commented in hosts.h *** In extreme situations a single driver may have several instances if it controls several different classes of hardware (e.g. the - advansys driver handles both ISA and PCI cards). + advansys driver handles both ISA and PCI cards and has a separate + instance of struct SHT for each). struct Scsi_Host ---------------- @@ -982,24 +1182,29 @@ 0->use bounce buffers if data is in high memory hostt - pointer to driver's struct SHT from which this struct Scsi_Host instance was spawned - host_queue - deceptively named pointer to the start of a double linked - list of struct scsi_device instances that belong to this - host. + sh_list - a double linked list of pointers to all struct Scsi_Host + instances (currently ordered by ascending host_no) + my_devices - a double linked list of pointers to struct scsi_device + instances that belong to this host. + hostdata[0] - area reserved for LLD at end of struct Scsi_Host. Size + is set by the second argument (named 'xtr_bytes') to + scsi_register(). The structure is defined in hosts.h struct scsi_device ------------------ Generally, there is one instance of this structure for each SCSI logical unit on a host. Scsi devices are uniquely identified within a host by bus number, -target id and logical unit number (lun). +cahnnel number, target id and logical unit number (lun). The structure is defined in scsi.h struct scsi_cmnd ---------------- -Instances of this structure convey SCSI commands to the LLD. -Each SCSI device has a pool of struct scsi_cmnd instances whose size -is determined by scsi_adjust_queue_depth() (or struct Scsi_Host::cmd_per_lun). -There will be at least one instance of struct scsi_cmnd for each SCSI device. +Instances of this structure convey SCSI commands to the LLD and responses +back to the mid level. The SCSI mid level will ensure that no more SCSI +commands become queued against the LLD than are indicated by +scsi_adjust_queue_depth() (or struct Scsi_Host::cmd_per_lun). There will +be at least one instance of struct scsi_cmnd available for each SCSI device. The structure is defined in scsi.h @@ -1033,9 +1238,9 @@ Either way, the mid level decides whether the LLD has performed autosense by checking struct scsi_cmnd::sense_buffer[0] . If this -byte has an upper nibble of 7 then autosense is assumed to have taken -place. If it has another value (and this byte is initialized to 0 before -each command) then the mid level will issue a REQUEST SENSE command. +byte has an upper nibble of 7 (or 0xf) then autosense is assumed to have +taken place. If it has another value (and this byte is initialized to 0 +before each command) then the mid level will issue a REQUEST SENSE command. In the presence of queued commands the "nexus" that maintains sense buffer data from the command that failed until a following REQUEST SENSE @@ -1076,4 +1281,4 @@ Douglas Gilbert dgilbert@interlog.com -21st February 2003 +19th April 2003 diff -urN linux-2.5.68-bk7/MAINTAINERS linux-2.5.68-bk8/MAINTAINERS --- linux-2.5.68-bk7/MAINTAINERS 2003-04-27 04:36:21.000000000 -0700 +++ linux-2.5.68-bk8/MAINTAINERS 2003-04-27 04:36:26.000000000 -0700 @@ -505,6 +505,17 @@ L: linux-hams@vger.kernel.org S: Maintained +DC395x SCSI driver +P: Oliver Neukum +M: oliver@neukum.name +P: Ali Akcaagac +M: aliakc@web.de +P: Jamie Lenehan +M: lenehan@twibble.org +W: http://twibble.org/dist/dc395x/ +L: http://lists.twibble.org/mailman/listinfo/dc395x/ +S: Maintained + DC390/AM53C974 SCSI driver P: Kurt Garloff M: garloff@suse.de diff -urN linux-2.5.68-bk7/Makefile linux-2.5.68-bk8/Makefile --- linux-2.5.68-bk7/Makefile 2003-04-27 04:36:21.000000000 -0700 +++ linux-2.5.68-bk8/Makefile 2003-04-27 04:36:26.000000000 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 68 -EXTRAVERSION = -bk7 +EXTRAVERSION = -bk8 # *DOCUMENTATION* # To see a list of typical targets execute "make help" diff -urN linux-2.5.68-bk7/arch/alpha/kernel/core_marvel.c linux-2.5.68-bk8/arch/alpha/kernel/core_marvel.c --- linux-2.5.68-bk7/arch/alpha/kernel/core_marvel.c 2003-04-19 19:48:50.000000000 -0700 +++ linux-2.5.68-bk8/arch/alpha/kernel/core_marvel.c 2003-04-27 04:36:26.000000000 -0700 @@ -452,7 +452,6 @@ /* With multiple PCI busses, we play with I/O as physical addrs. */ ioport_resource.end = ~0UL; - iomem_resource.end = ~0UL; /* PCI DMA Direct Mapping is 1GB at 2GB. */ __direct_map_base = 0x80000000; diff -urN linux-2.5.68-bk7/arch/alpha/kernel/core_mcpcia.c linux-2.5.68-bk8/arch/alpha/kernel/core_mcpcia.c --- linux-2.5.68-bk7/arch/alpha/kernel/core_mcpcia.c 2003-04-19 19:50:02.000000000 -0700 +++ linux-2.5.68-bk8/arch/alpha/kernel/core_mcpcia.c 2003-04-27 04:36:26.000000000 -0700 @@ -407,7 +407,6 @@ { /* With multiple PCI busses, we play with I/O as physical addrs. */ ioport_resource.end = ~0UL; - iomem_resource.end = ~0UL; /* Allocate hose 0. That's the one that all the ISA junk hangs off of, from which we'll be registering stuff here in a bit. diff -urN linux-2.5.68-bk7/arch/alpha/kernel/core_titan.c linux-2.5.68-bk8/arch/alpha/kernel/core_titan.c --- linux-2.5.68-bk7/arch/alpha/kernel/core_titan.c 2003-04-19 19:48:57.000000000 -0700 +++ linux-2.5.68-bk8/arch/alpha/kernel/core_titan.c 2003-04-27 04:36:26.000000000 -0700 @@ -412,7 +412,6 @@ /* With multiple PCI busses, we play with I/O as physical addrs. */ ioport_resource.end = ~0UL; - iomem_resource.end = ~0UL; /* PCI DMA Direct Mapping is 1GB at 2GB. */ __direct_map_base = 0x80000000; diff -urN linux-2.5.68-bk7/arch/alpha/kernel/core_tsunami.c linux-2.5.68-bk8/arch/alpha/kernel/core_tsunami.c --- linux-2.5.68-bk7/arch/alpha/kernel/core_tsunami.c 2003-04-19 19:49:10.000000000 -0700 +++ linux-2.5.68-bk8/arch/alpha/kernel/core_tsunami.c 2003-04-27 04:36:26.000000000 -0700 @@ -390,7 +390,6 @@ #endif /* With multiple PCI busses, we play with I/O as physical addrs. */ ioport_resource.end = ~0UL; - iomem_resource.end = ~0UL; /* Find how many hoses we have, and initialize them. TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10). */ diff -urN linux-2.5.68-bk7/arch/alpha/kernel/core_wildfire.c linux-2.5.68-bk8/arch/alpha/kernel/core_wildfire.c --- linux-2.5.68-bk7/arch/alpha/kernel/core_wildfire.c 2003-04-19 19:48:46.000000000 -0700 +++ linux-2.5.68-bk8/arch/alpha/kernel/core_wildfire.c 2003-04-27 04:36:26.000000000 -0700 @@ -309,7 +309,6 @@ /* With multiple PCI buses, we play with I/O as physical addrs. */ ioport_resource.end = ~0UL; - iomem_resource.end = ~0UL; /* Probe the hardware for info about configuration. */ diff -urN linux-2.5.68-bk7/arch/ia64/sn/io/pci_bus_cvlink.c linux-2.5.68-bk8/arch/ia64/sn/io/pci_bus_cvlink.c --- linux-2.5.68-bk7/arch/ia64/sn/io/pci_bus_cvlink.c 2003-04-19 19:50:35.000000000 -0700 +++ linux-2.5.68-bk8/arch/ia64/sn/io/pci_bus_cvlink.c 2003-04-27 04:36:26.000000000 -0700 @@ -373,12 +373,6 @@ #endif /* - * Set the root start and end for Mem Resource. - */ - iomem_resource.start = 0; - iomem_resource.end = 0xffffffffffffffff; - - /* * Initialize the device vertex in the pci_dev struct. */ pci_for_each_dev(device_dev) { diff -urN linux-2.5.68-bk7/arch/ia64/sn/io/sn2/pci_bus_cvlink.c linux-2.5.68-bk8/arch/ia64/sn/io/sn2/pci_bus_cvlink.c --- linux-2.5.68-bk7/arch/ia64/sn/io/sn2/pci_bus_cvlink.c 2003-04-19 19:48:50.000000000 -0700 +++ linux-2.5.68-bk8/arch/ia64/sn/io/sn2/pci_bus_cvlink.c 2003-04-27 04:36:26.000000000 -0700 @@ -323,12 +323,6 @@ ioport_resource.end = 0xcfffffffffffffff; /* - * Set the root start and end for Mem Resource. - */ - iomem_resource.start = 0; - iomem_resource.end = 0xffffffffffffffff; - - /* * Initialize the device vertex in the pci_dev struct. */ pci_for_each_dev(device_dev) { diff -urN linux-2.5.68-bk7/arch/mips64/sgi-ip27/ip27-pci.c linux-2.5.68-bk8/arch/mips64/sgi-ip27/ip27-pci.c --- linux-2.5.68-bk7/arch/mips64/sgi-ip27/ip27-pci.c 2003-04-19 19:50:34.000000000 -0700 +++ linux-2.5.68-bk8/arch/mips64/sgi-ip27/ip27-pci.c 2003-04-27 04:36:26.000000000 -0700 @@ -149,7 +149,6 @@ int i; ioport_resource.end = ~0UL; - iomem_resource.end = ~0UL; for (i=0; ihw_status_page, 0, PAGE_SIZE); - DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); + DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); I810_WRITE(0x02080, dev_priv->dma_status_page); DRM_DEBUG("Enabled hardware status page\n"); diff -urN linux-2.5.68-bk7/drivers/sbus/char/openprom.c linux-2.5.68-bk8/drivers/sbus/char/openprom.c --- linux-2.5.68-bk7/drivers/sbus/char/openprom.c 2003-04-19 19:50:30.000000000 -0700 +++ linux-2.5.68-bk8/drivers/sbus/char/openprom.c 2003-04-27 04:36:27.000000000 -0700 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff -urN linux-2.5.68-bk7/drivers/scsi/3w-xxxx.c linux-2.5.68-bk8/drivers/scsi/3w-xxxx.c --- linux-2.5.68-bk7/drivers/scsi/3w-xxxx.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/3w-xxxx.c 2003-04-27 04:36:27.000000000 -0700 @@ -225,10 +225,10 @@ /* File operations struct for character device */ static struct file_operations tw_fops = { - owner: THIS_MODULE, - ioctl: tw_chrdev_ioctl, - open: tw_chrdev_open, - release: tw_chrdev_release + .owner = THIS_MODULE, + .ioctl = tw_chrdev_ioctl, + .open = tw_chrdev_open, + .release = tw_chrdev_release }; /* Globals */ diff -urN linux-2.5.68-bk7/drivers/scsi/53c700.c linux-2.5.68-bk8/drivers/scsi/53c700.c --- linux-2.5.68-bk7/drivers/scsi/53c700.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/53c700.c 2003-04-27 04:36:27.000000000 -0700 @@ -133,6 +133,7 @@ #include #include #include +#include #include "scsi.h" #include "hosts.h" diff -urN linux-2.5.68-bk7/drivers/scsi/Kconfig linux-2.5.68-bk8/drivers/scsi/Kconfig --- linux-2.5.68-bk7/drivers/scsi/Kconfig 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/Kconfig 2003-04-27 04:36:27.000000000 -0700 @@ -1390,6 +1390,23 @@ read . The module will be called sym53c416. +config SCSI_DC395x + tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support (EXPERIMENTAL)" + depends on EXPERIMENTAL && PCI && SCSI + ---help--- + This driver supports PCI SCSI host adapters based on the ASIC + TRM-S1040 chip, e.g Tekram DC395(U/UW/F) and DC315(U) variants. + + This driver works, but is still in experimental status. So better + have a bootable disk and a backup in case of emergency. + + Documentation can be found in . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called dc395x. + config SCSI_DC390T tristate "Tekram DC390(T) and Am53/79C974 SCSI support" depends on PCI && SCSI diff -urN linux-2.5.68-bk7/drivers/scsi/Makefile linux-2.5.68-bk8/drivers/scsi/Makefile --- linux-2.5.68-bk7/drivers/scsi/Makefile 2003-04-19 19:50:37.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/Makefile 2003-04-27 04:36:27.000000000 -0700 @@ -88,6 +88,7 @@ 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_DC395x) += dc395x.o obj-$(CONFIG_SCSI_DC390T) += tmscsim.o obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o diff -urN linux-2.5.68-bk7/drivers/scsi/advansys.c linux-2.5.68-bk8/drivers/scsi/advansys.c --- linux-2.5.68-bk7/drivers/scsi/advansys.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/advansys.c 2003-04-27 04:36:27.000000000 -0700 @@ -4395,32 +4395,6 @@ curbuf += cnt; /* - * Display target driver information for each device attached - * to the board. - */ - list_for_each_entry (scd, &shp->my_devices, siblings) - { - if (scd->host == shp) { - cp = boardp->prtbuf; - /* - * Note: If proc_print_scsidevice() writes more than - * ASC_PRTBUF_SIZE bytes, it will overrun 'prtbuf'. - */ - proc_print_scsidevice(scd, cp, &cplen, 0); - ASC_ASSERT(cplen < ASC_PRTBUF_SIZE); - cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen); - totcnt += cnt; - leftlen -= cnt; - if (leftlen == 0) { - ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt); - return totcnt; - } - advoffset += cplen; - curbuf += cnt; - } - } - - /* * Display EEPROM configuration for the board. */ cp = boardp->prtbuf; diff -urN linux-2.5.68-bk7/drivers/scsi/aha152x.c linux-2.5.68-bk8/drivers/scsi/aha152x.c --- linux-2.5.68-bk7/drivers/scsi/aha152x.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/aha152x.c 2003-04-27 04:36:27.000000000 -0700 @@ -1380,8 +1380,6 @@ */ static int setup_expected_interrupts(struct Scsi_Host *shpnt) { - ASSERT_LOCK(&QLOCK,1); - if(CURRENT_SC) { CURRENT_SC->SCp.phase |= 1 << 16; diff -urN linux-2.5.68-bk7/drivers/scsi/aic7xxx_old/aic7xxx.h linux-2.5.68-bk8/drivers/scsi/aic7xxx_old/aic7xxx.h --- linux-2.5.68-bk7/drivers/scsi/aic7xxx_old/aic7xxx.h 2003-04-19 19:48:49.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/aic7xxx_old/aic7xxx.h 2003-04-27 04:36:27.000000000 -0700 @@ -30,26 +30,26 @@ * to do with card config are filled in after the card is detected. */ #define AIC7XXX { \ - proc_info: aic7xxx_proc_info, \ - detect: aic7xxx_detect, \ - release: aic7xxx_release, \ - info: aic7xxx_info, \ - queuecommand: aic7xxx_queue, \ - slave_alloc: aic7xxx_slave_alloc, \ - slave_configure: aic7xxx_slave_configure, \ - slave_destroy: aic7xxx_slave_destroy, \ - bios_param: aic7xxx_biosparam, \ - eh_abort_handler: aic7xxx_abort, \ - eh_device_reset_handler: aic7xxx_bus_device_reset, \ - eh_host_reset_handler: aic7xxx_reset, \ - can_queue: 255, /* max simultaneous cmds */\ - this_id: -1, /* scsi id of host adapter */\ - sg_tablesize: 0, /* max scatter-gather cmds */\ - max_sectors: 2048, /* max physical sectors in 1 cmd */\ - cmd_per_lun: 3, /* cmds per lun (linked cmds) */\ - present: 0, /* number of 7xxx's present */\ - unchecked_isa_dma: 0, /* no memory DMA restrictions */\ - use_clustering: ENABLE_CLUSTERING, \ + .proc_info = aic7xxx_proc_info, \ + .detect = aic7xxx_detect, \ + .release = aic7xxx_release, \ + .info = aic7xxx_info, \ + .queuecommand = aic7xxx_queue, \ + .slave_alloc = aic7xxx_slave_alloc, \ + .slave_configure = aic7xxx_slave_configure, \ + .slave_destroy = aic7xxx_slave_destroy, \ + .bios_param = aic7xxx_biosparam, \ + .eh_abort_handler = aic7xxx_abort, \ + .eh_device_reset_handler = aic7xxx_bus_device_reset, \ + .eh_host_reset_handler = aic7xxx_reset, \ + .can_queue = 255, /* max simultaneous cmds */\ + .this_id = -1, /* scsi id of host adapter */\ + .sg_tablesize = 0, /* max scatter-gather cmds */\ + .max_sectors = 2048, /* max physical sectors in 1 cmd */\ + .cmd_per_lun = 3, /* cmds per lun (linked cmds) */\ + .present = 0, /* number of 7xxx's present */\ + .unchecked_isa_dma = 0, /* no memory DMA restrictions */\ + .use_clustering = ENABLE_CLUSTERING, \ } extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); diff -urN linux-2.5.68-bk7/drivers/scsi/dc395x.c linux-2.5.68-bk8/drivers/scsi/dc395x.c --- linux-2.5.68-bk7/drivers/scsi/dc395x.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.68-bk8/drivers/scsi/dc395x.c 2003-04-27 04:36:27.000000000 -0700 @@ -0,0 +1,6351 @@ +/* + * dc395x.c + * + * Device Driver for Tekram DC395(U/UW/F), DC315(U) + * PCI SCSI Bus Master Host Adapter + * (SCSI chip set used Tekram ASIC TRM-S1040) + * + * Authors: + * C.L. Huang + * Erich Chen + * (C) Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * Kurt Garloff + * (C) 1999-2000 Kurt Garloff + * + * Oliver Neukum + * Ali Akcaagac + * Jamie Lenehan + * (C) 2003 + * + * License: GNU GPL + * + ************************************************************************* + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************ + */ + +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" +#include "dc395x.h" +#include /* needed for scsicam_bios_param */ +#include +#include +#include + +/* Debugging */ +/*#define DC395x_DEBUG_KG */ +/*#define DC395x_DEBUG0*/ +/*#define DC395x_DEBUG1*/ +/*#define DC395x_DEBUGDCB*/ +#define DC395x_DEBUGTRACE +/*#define DC395x_DEBUGTRACEALL*/ +/*#define DC395x_DEBUGPARSE*/ +/*#define DC395x_SGPARANOIA*/ +/*#define DC395x_DEBUGFIFO*/ +/*#define DC395x_DEBUGRECURSION*/ +/*#define DC395x_DEBUGPIO*/ +/*#define DC395x_DEBUGMALLOC*/ + +/* DISable features */ +/*#define DC395x_NO_DISCONNECT*/ +/*#define DC395x_NO_TAGQ*/ +/*#define DC395x_NO_SYNC*/ +/*#define DC395x_NO_WIDE*/ + +#ifdef DC395x_DEBUG0 +# define DEBUG0(x) x +#else +# define DEBUG0(x) +#endif + +#ifdef DC395x_DEBUG1 +# define DEBUG1(x) x +#else +# define DEBUG1(x) +#endif + +#ifdef DC395x_DEBUGDCB +# define DCBDEBUG(x) x +#else +# define DCBDEBUG(x) +#endif + +#ifdef DC395x_DEBUGPARSE +# define PARSEDEBUG(x) x +#else +# define PARSEDEBUG(x) +#endif + +#ifdef DC395x_DEBUGRECURSION +# define DEBUGRECURSION(x) x +#else +# define DEBUGRECURSION(x) +#endif + +#ifdef DC395x_DEBUGPIO +# define DEBUGPIO(x) x +#else +# define DEBUGPIO(x) +#endif + +/* Here comes the joker of all debugging facilities! */ +#ifdef DC395x_DEBUGTRACEALL +# ifndef DC395x_DEBUGTRACE +# define DC395x_DEBUGTRACE +# endif +# define TRACEOUTALL(x...) printk ( x) +#else +# define TRACEOUTALL(x...) do {} while (0) +#endif +#ifdef DC395x_DEBUGTRACE +# define DEBUGTRACEBUFSZ 512 +char DC395x_tracebuf[64]; +char DC395x_traceoverflow[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +# define TRACEPRINTF(x...) \ +do { int ln = sprintf (DC395x_tracebuf, x); \ + if (pSRB->debugpos + ln >= DEBUGTRACEBUFSZ) \ + { pSRB->debugtrace[pSRB->debugpos] = 0; pSRB->debugpos = DEBUGTRACEBUFSZ/5; pSRB->debugtrace[pSRB->debugpos++] = '>'; }; \ + sprintf (pSRB->debugtrace + pSRB->debugpos, "%s", DC395x_tracebuf); \ + pSRB->debugpos += ln - 1; \ + } while (0) +# define TRACEOUT(x...) printk ( x) +#else +# define TRACEPRINTF(x...) do {} while (0) +# define TRACEOUT(x...) do {} while (0) +#endif + + +#ifdef DC395x_DEBUGMALLOC +inline void *dc395x_kmalloc(size_t sz, int fl) +{ + void *ptr = kmalloc(sz, fl); + printk(KERN_DEBUG DC395X_NAME ": Alloc %i bytes @ %p w/ fl %08x\n", + sz, ptr, fl); + return ptr; +} +inline void dc395x_kfree(const void *adr) +{ + printk(KERN_DEBUG DC395X_NAME ": Free mem @ %p\n", adr); + kfree(adr); +} + +# define KMALLOC(sz,fl) dc395x_kmalloc(sz,fl) +# define KFREE(adr) dc395x_kfree(adr) +#else +# define KMALLOC(sz,fl) kmalloc(sz,fl) +# define KFREE(adr) kfree(adr) +#endif + + +#ifndef PCI_VENDOR_ID_TEKRAM +#define PCI_VENDOR_ID_TEKRAM 0x1DE1 /* Vendor ID */ +#endif +#ifndef PCI_DEVICE_ID_TEKRAM_TRMS1040 +#define PCI_DEVICE_ID_TEKRAM_TRMS1040 0x0391 /* Device ID */ +#endif + +static struct pci_device_id dc395x_pci_tbl[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TEKRAM, + .device = PCI_DEVICE_ID_TEKRAM_TRMS1040, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, dc395x_pci_tbl); + + +#define DC395x_LOCK_IO(dev) spin_lock_irqsave(((struct Scsi_Host *)dev)->host_lock, flags) +#define DC395x_UNLOCK_IO(dev) spin_unlock_irqrestore(((struct Scsi_Host *)dev)->host_lock, flags) + +#define DC395x_ACB_INITLOCK(pACB) spin_lock_init(&pACB->smp_lock) +#define DC395x_ACB_LOCK(pACB,acb_flags) if (!pACB->lock_level_count[cpuid]) { spin_lock_irqsave(&pACB->smp_lock,acb_flags); pACB->lock_level_count[cpuid]++; } else { pACB->lock_level_count[cpuid]++; } +#define DC395x_ACB_UNLOCK(pACB,acb_flags) if (--pACB->lock_level_count[cpuid] == 0) { spin_unlock_irqrestore(&pACB->smp_lock,acb_flags); } + +#define DC395x_SMP_IO_LOCK(dev,irq_flags) spin_lock_irqsave(((struct Scsi_Host*)dev)->host_lock,irq_flags) +#define DC395x_SMP_IO_UNLOCK(dev,irq_flags) spin_unlock_irqrestore(((struct Scsi_Host*)dev)->host_lock,irq_flags) +#define DC395x_SCSI_DONE_ACB_LOCK spin_lock(&(pACB->smp_lock)) +#define DC395x_SCSI_DONE_ACB_UNLOCK spin_unlock(&(pACB->smp_lock)) + + +#define DC395x_read8(address) (u8)(inb(pACB->IOPortBase + (address))) +#define DC395x_read8_(address, base) (u8)(inb((USHORT)(base) + (address))) +#define DC395x_read16(address) (u16)(inw(pACB->IOPortBase + (address))) +#define DC395x_read32(address) (u32)(inl(pACB->IOPortBase + (address))) +#define DC395x_write8(address,value) outb((value), pACB->IOPortBase + (address)) +#define DC395x_write8_(address,value,base) outb((value), (USHORT)(base) + (address)) +#define DC395x_write16(address,value) outw((value), pACB->IOPortBase + (address)) +#define DC395x_write32(address,value) outl((value), pACB->IOPortBase + (address)) + + +#define BUS_ADDR(sg) sg_dma_address(&(sg)) +#define CPU_ADDR(sg) (page_address((sg).page)+(sg).offset) +#define PAGE_ADDRESS(sg) page_address((sg)->page) +#define SET_DIR(dir,pcmd) dir = scsi_to_pci_dma_dir((pcmd)->sc_data_direction) + +/* cmd->result */ +#define RES_TARGET 0x000000FF /* Target State */ +#define RES_TARGET_LNX STATUS_MASK /* Only official ... */ +#define RES_ENDMSG 0x0000FF00 /* End Message */ +#define RES_DID 0x00FF0000 /* DID_ codes */ +#define RES_DRV 0xFF000000 /* DRIVER_ codes */ + +#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)) +#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1) + +#define SET_RES_TARGET(who,tgt) { who &= ~RES_TARGET; who |= (int)(tgt); } +#define SET_RES_TARGET_LNX(who,tgt) { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; } +#define SET_RES_MSG(who,msg) { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; } +#define SET_RES_DID(who,did) { who &= ~RES_DID; who |= (int)(did) << 16; } +#define SET_RES_DRV(who,drv) { who &= ~RES_DRV; who |= (int)(drv) << 24; } + +/* +************************************************************************** +*/ +#define NO_IRQ 255 +#define TAG_NONE 255 + +struct SGentry { + u32 address; /* bus! address */ + u32 length; +}; + + +/*----------------------------------------------------------------------- + SCSI Request Block + -----------------------------------------------------------------------*/ +struct ScsiReqBlk { + struct ScsiReqBlk *pNextSRB; + struct DeviceCtlBlk *pSRBDCB; + + /* HW scatter list (up to 64 entries) */ + struct SGentry *SegmentX; + Scsi_Cmnd *pcmd; + + /* Offset 0x20/0x10 */ + unsigned char *virt_addr; /* set by DC395x_update_SGlist */ + + u32 SRBTotalXferLength; + u32 Xferred; /* Backup for the already xferred len */ + + u32 SRBSGBusAddr; /* bus address of DC395x scatterlist */ + + u16 SRBState; + u8 SRBSGCount; + u8 SRBSGIndex; + + /* Offset 0x38/0x24 */ + u8 MsgInBuf[6]; + u8 MsgOutBuf[6]; + + u8 AdaptStatus; + u8 TargetStatus; + u8 MsgCnt; + u8 EndMessage; + + /* Offset 0x48/0x34 */ + u8 *pMsgPtr; + + u8 TagNumber; + u8 SRBStatus; + u8 RetryCnt; + u8 SRBFlag; + + u8 ScsiPhase; + u8 padding; + u16 debugpos; + /* Offset 0x58/0x40 */ +#ifdef DC395x_DEBUGTRACE + char *debugtrace; + /* Offset 0x60/0x44 */ +#endif +}; + + +/*----------------------------------------------------------------------- + Device Control Block + -----------------------------------------------------------------------*/ +struct DeviceCtlBlk { + struct DeviceCtlBlk *pNextDCB; + struct AdapterCtlBlk *pDCBACB; + + struct ScsiReqBlk *pGoingSRB; + struct ScsiReqBlk *pGoingLast; + +/* 0x10: */ + struct ScsiReqBlk *pWaitingSRB; + struct ScsiReqBlk *pWaitLast; + + struct ScsiReqBlk *pActiveSRB; + u32 TagMask; + +/* 0x20: */ + u16 MaxCommand; + u8 AdaptIndex; /* UnitInfo struc start */ + u8 UnitIndex; /* nth Unit on this card */ + + u16 GoingSRBCnt; + u16 WaitSRBCnt; + u8 TargetID; /* SCSI Target ID (SCSI Only) */ + u8 TargetLUN; /* SCSI Log. Unit (SCSI Only) */ + u8 IdentifyMsg; + u8 DevMode; + +/* 0x2c: */ +/* u8 AdpMode;*/ + u8 Inquiry7; /* To store Inquiry flags */ + u8 SyncMode; /* 0:async mode */ + u8 MinNegoPeriod; /* for nego. */ + u8 SyncPeriod; /* for reg. */ + + u8 SyncOffset; /* for reg. and nego.(low nibble) */ + u8 UnitCtrlFlag; + u8 DCBFlag; + u8 DevType; + u8 init_TCQ_flag; + + unsigned long last_derated; /* last time, when features were turned off in abort */ +/* 0x38: */ + /* u8 Reserved2[3]; for dword alignment */ +}; + +/*----------------------------------------------------------------------- + Adapter Control Block + -----------------------------------------------------------------------*/ +struct AdapterCtlBlk { + struct Scsi_Host *pScsiHost; + struct AdapterCtlBlk *pNextACB; + + u16 IOPortBase; + u16 Revxx1; + + struct DeviceCtlBlk *pLinkDCB; + struct DeviceCtlBlk *pLastDCB; + struct DeviceCtlBlk *pDCBRunRobin; + + struct DeviceCtlBlk *pActiveDCB; + + struct ScsiReqBlk *pFreeSRB; + struct ScsiReqBlk *pTmpSRB; + struct timer_list Waiting_Timer; + struct timer_list SelTO_Timer; + + u16 SRBCount; + u16 AdapterIndex; /* nth Adapter this driver */ + + u32 QueryCnt; + Scsi_Cmnd *pQueryHead; + Scsi_Cmnd *pQueryTail; + + u8 msgin123[4]; + + u8 status; + u8 DCBCnt; + u8 sel_timeout; + u8 dummy; + + u8 IRQLevel; + u8 TagMaxNum; + u8 ACBFlag; + u8 Gmode2; + + u8 Config; + u8 LUNchk; + u8 scan_devices; + u8 HostID_Bit; + + u8 DCBmap[DC395x_MAX_SCSI_ID]; + struct DeviceCtlBlk *children[DC395x_MAX_SCSI_ID][32]; + + u32 Cmds; + u32 SelLost; + u32 SelConn; + u32 CmdInQ; + u32 CmdOutOfSRB; + + /*struct DeviceCtlBlk DCB_array[DC395x_MAX_DCB]; *//* +74h, Len=3E8 */ + struct pci_dev *pdev; + + u8 MsgLen; + u8 DeviceCnt; + + struct ScsiReqBlk SRB_array[DC395x_MAX_SRB_CNT]; + struct ScsiReqBlk TmpSRB; +}; + + +/* + * The SEEPROM structure for TRM_S1040 + */ +struct NVRamTarget { + u8 NvmTarCfg0; /* Target configuration byte 0 */ + u8 NvmTarPeriod; /* Target period */ + u8 NvmTarCfg2; /* Target configuration byte 2 */ + u8 NvmTarCfg3; /* Target configuration byte 3 */ +}; + + +struct NvRamType { + u8 NvramSubVendorID[2]; /* 0,1 Sub Vendor ID */ + u8 NvramSubSysID[2]; /* 2,3 Sub System ID */ + u8 NvramSubClass; /* 4 Sub Class */ + u8 NvramVendorID[2]; /* 5,6 Vendor ID */ + u8 NvramDeviceID[2]; /* 7,8 Device ID */ + u8 NvramReserved; /* 9 Reserved */ + struct NVRamTarget NvramTarget[DC395x_MAX_SCSI_ID]; + /** 10,11,12,13 + ** 14,15,16,17 + ** .... + ** .... + ** 70,71,72,73 + */ + u8 NvramScsiId; /* 74 Host Adapter SCSI ID */ + u8 NvramChannelCfg; /* 75 Channel configuration */ + u8 NvramDelayTime; /* 76 Power on delay time */ + u8 NvramMaxTag; /* 77 Maximum tags */ + u8 NvramReserved0; /* 78 */ + u8 NvramBootTarget; /* 79 */ + u8 NvramBootLun; /* 80 */ + u8 NvramReserved1; /* 81 */ + u16 Reserved[22]; /* 82,..125 */ + u16 NvramCheckSum; /* 126,127 */ +}; + +/*------------------------------------------------------------------------------*/ + +void DC395x_DataOutPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +void DC395x_DataInPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_CommandPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_StatusPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_MsgOutPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +void DC395x_MsgInPhase0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_DataOutPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_DataInPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_CommandPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_StatusPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_MsgOutPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_MsgInPhase1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, + u16 * pscsi_status); +static void DC395x_Nop0(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_Nop1(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 * pscsi_status); +static void DC395x_basic_config(struct AdapterCtlBlk *pACB); +static void DC395x_cleanup_after_transfer(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +static void DC395x_ResetSCSIBus(struct AdapterCtlBlk *pACB); +void DC395x_DataIO_transfer(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB, u16 ioDir); +void DC395x_Disconnect(struct AdapterCtlBlk *pACB); +void DC395x_Reselect(struct AdapterCtlBlk *pACB); +u8 DC395x_StartSCSI(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +static void DC395x_BuildSRB(Scsi_Cmnd * pcmd, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +void DC395x_DoingSRB_Done(struct AdapterCtlBlk *pACB, u8 did_code, + Scsi_Cmnd * pcmd, u8 force); +static void DC395x_ScsiRstDetect(struct AdapterCtlBlk *pACB); +static void DC395x_pci_unmap(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +static void DC395x_pci_unmap_sense(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +static inline void DC395x_EnableMsgOut_Abort(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB); +void DC395x_SRBdone(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +static void DC395x_RequestSense(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB); +static inline void DC395x_SetXferRate(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB); +void DC395x_initDCB(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk **ppDCB, u8 target, u8 lun); +int DC395x_shutdown(struct Scsi_Host *host); +static void DC395x_remove_dev(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB); + + +static struct AdapterCtlBlk *DC395x_pACB_start = NULL; +static struct AdapterCtlBlk *DC395x_pACB_current = NULL; +static u16 DC395x_adapterCnt = 0; +static u16 DC395x_CurrSyncOffset = 0; + +DEBUGRECURSION(static char in_driver = 0; + ) +static char DC395x_monitor_next_IRQ = 0; + +/* + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + */ +static void *DC395x_SCSI_phase0[] = { + DC395x_DataOutPhase0, /* phase:0 */ + DC395x_DataInPhase0, /* phase:1 */ + DC395x_CommandPhase0, /* phase:2 */ + DC395x_StatusPhase0, /* phase:3 */ + DC395x_Nop0, /* phase:4 PH_BUS_FREE .. initial phase */ + DC395x_Nop0, /* phase:5 PH_BUS_FREE .. initial phase */ + DC395x_MsgOutPhase0, /* phase:6 */ + DC395x_MsgInPhase0, /* phase:7 */ +}; + +/* + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + */ +static void *DC395x_SCSI_phase1[] = { + DC395x_DataOutPhase1, /* phase:0 */ + DC395x_DataInPhase1, /* phase:1 */ + DC395x_CommandPhase1, /* phase:2 */ + DC395x_StatusPhase1, /* phase:3 */ + DC395x_Nop1, /* phase:4 PH_BUS_FREE .. initial phase */ + DC395x_Nop1, /* phase:5 PH_BUS_FREE .. initial phase */ + DC395x_MsgOutPhase1, /* phase:6 */ + DC395x_MsgInPhase1, /* phase:7 */ +}; + +struct NvRamType dc395x_trm_eepromBuf[DC395x_MAX_ADAPTER_NUM]; +/* + *Fast20: 000 50ns, 20.0 MHz + * 001 75ns, 13.3 MHz + * 010 100ns, 10.0 MHz + * 011 125ns, 8.0 MHz + * 100 150ns, 6.6 MHz + * 101 175ns, 5.7 MHz + * 110 200ns, 5.0 MHz + * 111 250ns, 4.0 MHz + * + *Fast40(LVDS): 000 25ns, 40.0 MHz + * 001 50ns, 20.0 MHz + * 010 75ns, 13.3 MHz + * 011 100ns, 10.0 MHz + * 100 125ns, 8.0 MHz + * 101 150ns, 6.6 MHz + * 110 175ns, 5.7 MHz + * 111 200ns, 5.0 MHz + */ +/*static u8 dc395x_clock_period[] = {12,19,25,31,37,44,50,62};*/ + +/* real period:48ns,76ns,100ns,124ns,148ns,176ns,200ns,248ns */ +static u8 dc395x_clock_period[] = { 12, 18, 25, 31, 37, 43, 50, 62 }; +static u16 dc395x_clock_speed[] = { 200, 133, 100, 80, 67, 58, 50, 40 }; +/* real period:48ns,72ns,100ns,124ns,148ns,172ns,200ns,248ns */ + +/* + * Override defaults on cmdline: + * dc395x_trm = AdaptID, MaxSpeed (Index), DevMode (Bitmapped), AdaptMode (Bitmapped), Tags (log2-1), DelayReset + */ +int dc395x_trm[] = { -2, -2, -2, -2, -2, -2 }; + +#if defined(MODULE) +MODULE_PARM(dc395x_trm, "1-6i"); +MODULE_PARM_DESC(dc395x_trm, + "Host SCSI ID, Speed (0=20MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)"); +#endif + +MODULE_AUTHOR("C.L. Huang / Erich Chen / Kurt Garloff"); +MODULE_DESCRIPTION + ("SCSI host adapter driver for Tekram TRM-S1040 based adapters: Tekram DC395 and DC315 series"); +MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); + +MODULE_LICENSE("GPL"); + +/* Delaying after a reset */ +static char __initdata DC395x_interpd[] = { 1, 3, 5, 10, 16, 30, 60, 120 }; + +/* Convert EEprom value to seconds */ +static void __init DC395x_interpret_delay(struct NvRamType *eeprom) +{ + /*printk (DC395X_NAME ": Debug: Delay: %i\n", eeprom->NvramDelayTime); */ + eeprom->NvramDelayTime = DC395x_interpd[eeprom->NvramDelayTime]; +} + +/* seconds to EEProm value */ +static int __init DC395x_uninterpret_delay(int delay) +{ + u8 idx = 0; + while (idx < 7 && DC395x_interpd[idx] < delay) + idx++; + return idx; +} + + +/* Handle "-1" case */ +static void __init DC395x_check_for_safe_settings(void) +{ + if (dc395x_trm[0] == -1 || dc395x_trm[0] > 15) { /* modules-2.0.0 passes -1 as string */ + dc395x_trm[0] = 7; + dc395x_trm[1] = 4; + dc395x_trm[2] = 0x09; + dc395x_trm[3] = 0x0f; + dc395x_trm[4] = 2; + dc395x_trm[5] = 10; + printk(KERN_INFO DC395X_NAME ": Using safe settings.\n"); + } +} + +/* Defaults, to be overriden by (a) BIOS and (b) Cmnd line (kernel/module) args */ +int __initdata dc395x_def[] = { 7, 1 /* 13.3MHz */ , + NTC_DO_PARITY_CHK | NTC_DO_DISCONNECT | NTC_DO_SYNC_NEGO | + NTC_DO_WIDE_NEGO | NTC_DO_TAG_QUEUEING | NTC_DO_SEND_START, + NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET + /* | NAC_ACTIVE_NEG */ +#ifdef CONFIG_SCSI_MULTI_LUN + | NAC_SCANLUN +#endif + , 3 /* 16 Tags per LUN */ , 1 /* s delay after Reset */ +}; + +/* Copy defaults over set values where missing */ +static void __init DC395x_fill_with_defaults(void) +{ + int i; + PARSEDEBUG(printk + (KERN_INFO DC395X_NAME + ": setup %08x %08x %08x %08x %08x %08x\n", + dc395x_trm[0], dc395x_trm[1], dc395x_trm[2], + dc395x_trm[3], dc395x_trm[4], dc395x_trm[5]); + ) + for (i = 0; i < 6; i++) { + if (dc395x_trm[i] < 0 || dc395x_trm[i] > 255) + dc395x_trm[i] = dc395x_def[i]; + } + /* Sanity checks */ + if (dc395x_trm[0] > 15) + dc395x_trm[0] = 7; + if (dc395x_trm[1] > 7) + dc395x_trm[1] = 4; + if (dc395x_trm[4] > 5) + dc395x_trm[4] = 4; + if (dc395x_trm[5] > 180) + dc395x_trm[5] = 180; +} + + +/* Read the parameters from the command line */ +#if !defined(MODULE) +static int DC395x_trm_setup(char *str) +{ + int i; + int im; + int ints[8]; + (void) get_options(str, ARRAY_SIZE(ints), ints); + im = ints[0]; + if (im > 6) { + printk(KERN_NOTICE DC395X_NAME ": ignore extra params!\n"); + im = 6; + } + for (i = 0; i < im; i++) + dc395x_trm[i] = ints[i + 1]; + + return 1; +} + +__setup(DC395X_NAME "=", DC395x_trm_setup); + +#endif /* !MODULE */ + +/* Overrride BIOS values with the set ones */ +static void __init DC395x_EEprom_Override(struct NvRamType *eeprom) +{ + u8 id; + + /* Adapter Settings */ + if (dc395x_trm[0] != -2) + eeprom->NvramScsiId = (u8) dc395x_trm[0]; /* Adapter ID */ + if (dc395x_trm[3] != -2) + eeprom->NvramChannelCfg = (u8) dc395x_trm[3]; + if (dc395x_trm[5] != -2) + eeprom->NvramDelayTime = DC395x_uninterpret_delay(dc395x_trm[5]); /* Reset delay */ + if (dc395x_trm[4] != -2) + eeprom->NvramMaxTag = (u8) dc395x_trm[4]; /* Tagged Cmds */ + + /* Device Settings */ + for (id = 0; id < DC395x_MAX_SCSI_ID; id++) { + if (dc395x_trm[2] != -2) + eeprom->NvramTarget[id].NvmTarCfg0 = (u8) dc395x_trm[2]; /* Cfg0 */ + if (dc395x_trm[1] != -2) + eeprom->NvramTarget[id].NvmTarPeriod = (u8) dc395x_trm[1]; /* Speed */ + } +} + + +/* + * Queueing philosphy: + * There are a couple of lists: + * - Query: Contains the Scsi Commands not yet turned into SRBs (per ACB) + * (Note: For new EH, it is unecessary!) + * - Waiting: Contains a list of SRBs not yet sent (per DCB) + * - Free: List of free SRB slots + * + * If there are no waiting commands for the DCB, the new one is sent to the bus + * otherwise the oldest one is taken from the Waiting list and the new one is + * queued to the Waiting List + * + * Lists are managed using two pointers and eventually a counter + */ + +/* Nomen est omen ... */ +static inline void +DC395x_freetag(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + if (pSRB->TagNumber < 255) { + pDCB->TagMask &= ~(1 << pSRB->TagNumber); /* free tag mask */ + pSRB->TagNumber = 255; + } +} + + +/* Find cmd in SRB list */ +inline static struct ScsiReqBlk *DC395x_find_cmd(Scsi_Cmnd * pcmd, + struct ScsiReqBlk *start) +{ + struct ScsiReqBlk *psrb = start; + if (!start) + return 0; + do { + if (psrb->pcmd == pcmd) + return psrb; + psrb = psrb->pNextSRB; + } while (psrb && psrb != start); + return 0; +} + + +/* Append to Query List */ +static void +DC395x_Query_append(Scsi_Cmnd * cmd, struct AdapterCtlBlk *pACB) +{ + DEBUG0(printk(DC395X_NAME ": Append cmd %li to Query\n", cmd->pid); + ) + + cmd->host_scribble = NULL; + + if (!pACB->QueryCnt) + pACB->pQueryHead = cmd; + else + pACB->pQueryTail->host_scribble = (void *) cmd; + + pACB->pQueryTail = cmd; + pACB->QueryCnt++; + pACB->CmdOutOfSRB++; +} + + +/* Return next cmd from Query list */ +static Scsi_Cmnd *DC395x_Query_get(struct AdapterCtlBlk *pACB) +{ + Scsi_Cmnd *pcmd; + + pcmd = pACB->pQueryHead; + if (!pcmd) + return pcmd; + DEBUG0(printk(DC395X_NAME ": Get cmd %li from Query\n", pcmd->pid); + ) + pACB->pQueryHead = (void *) pcmd->host_scribble; + pcmd->host_scribble = NULL; + if (!pACB->pQueryHead) + pACB->pQueryTail = NULL; + pACB->QueryCnt--; + return pcmd; +} + + +/* Return next free SRB */ +static __inline__ struct ScsiReqBlk *DC395x_Free_get(struct AdapterCtlBlk + *pACB) +{ + struct ScsiReqBlk *pSRB; + + /*DC395x_Free_integrity (pACB); */ + pSRB = pACB->pFreeSRB; + if (!pSRB) + printk(DC395X_NAME ": Out of Free SRBs :-(\n"); + if (pSRB) { + pACB->pFreeSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + + return pSRB; +} + + +/* Insert SRB oin top of free list */ +static __inline__ void +DC395x_Free_insert(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk(DC395X_NAME ": Free SRB %p\n", pSRB); + ) + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; +} + + +/* Inserts a SRB to the top of the Waiting list */ +static __inline__ void +DC395x_Waiting_insert(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk + (DC395X_NAME ": Insert pSRB %p cmd %li to Waiting\n", pSRB, + pSRB->pcmd->pid); + ) + pSRB->pNextSRB = pDCB->pWaitingSRB; + if (!pDCB->pWaitingSRB) + pDCB->pWaitLast = pSRB; + pDCB->pWaitingSRB = pSRB; + pDCB->WaitSRBCnt++; +} + + +/* Queue SRB to waiting list */ +static __inline__ void +DC395x_Waiting_append(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk + (DC395X_NAME ": Append pSRB %p cmd %li to Waiting\n", pSRB, + pSRB->pcmd->pid); + ) + if (pDCB->pWaitingSRB) + pDCB->pWaitLast->pNextSRB = pSRB; + else + pDCB->pWaitingSRB = pSRB; + + pDCB->pWaitLast = pSRB; + /* No next one in waiting list */ + pSRB->pNextSRB = NULL; + pDCB->WaitSRBCnt++; + /*pDCB->pDCBACB->CmdInQ++; */ +} + + +static __inline__ void +DC395x_Going_append(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk(DC395X_NAME ": Append SRB %p to Going\n", pSRB); + ) + /* Append to the list of Going commands */ + if (pDCB->pGoingSRB) + pDCB->pGoingLast->pNextSRB = pSRB; + else + pDCB->pGoingSRB = pSRB; + + pDCB->pGoingLast = pSRB; + /* No next one in sent list */ + pSRB->pNextSRB = NULL; + pDCB->GoingSRBCnt++; +} + + +/* Find predecessor SRB */ +inline static struct ScsiReqBlk *DC395x_find_SRBpre(struct ScsiReqBlk + *pSRB, + struct ScsiReqBlk + *start) +{ + struct ScsiReqBlk *srb = start; + if (!start) + return 0; + do { + if (srb->pNextSRB == pSRB) + return srb; + srb = srb->pNextSRB; + } while (srb && srb != start); + return 0; +} + + +/* Remove SRB from SRB queue */ +inline static struct ScsiReqBlk *DC395x_rmv_SRB(struct ScsiReqBlk *pSRB, + struct ScsiReqBlk *pre) +{ + if (pre->pNextSRB != pSRB) + pre = DC395x_find_SRBpre(pSRB, pre); + if (!pre) { + printk(DC395X_NAME + ": Internal ERROR: SRB to rmv not found in Q!\n"); + return 0; + } + pre->pNextSRB = pSRB->pNextSRB; + /*pSRB->pNextSRB = 0; */ + return pre; +} + + +/* Remove SRB from Going queue */ +static void +DC395x_Going_remove(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB, + struct ScsiReqBlk *hint) +{ + struct ScsiReqBlk *pre = 0; + DEBUG0(printk(DC395X_NAME ": Remove SRB %p from Going\n", pSRB); + ) + if (!pSRB) + printk(DC395X_NAME ": Going_remove %p!\n", pSRB); + if (pSRB == pDCB->pGoingSRB) + pDCB->pGoingSRB = pSRB->pNextSRB; + else if (hint && hint->pNextSRB == pSRB) + pre = DC395x_rmv_SRB(pSRB, hint); + else + pre = DC395x_rmv_SRB(pSRB, pDCB->pGoingSRB); + if (pSRB == pDCB->pGoingLast) + pDCB->pGoingLast = pre; + pDCB->GoingSRBCnt--; +} + + +/* Remove SRB from Waiting queue */ +static void +DC395x_Waiting_remove(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB, + struct ScsiReqBlk *hint) +{ + struct ScsiReqBlk *pre = 0; + DEBUG0(printk(DC395X_NAME ": Remove SRB %p from Waiting\n", pSRB); + ) + if (!pSRB) + printk(DC395X_NAME ": Waiting_remove %p!\n", pSRB); + if (pSRB == pDCB->pWaitingSRB) + pDCB->pWaitingSRB = pSRB->pNextSRB; + else if (hint && hint->pNextSRB == pSRB) + pre = DC395x_rmv_SRB(pSRB, hint); + else + pre = DC395x_rmv_SRB(pSRB, pDCB->pWaitingSRB); + if (pSRB == pDCB->pWaitLast) + pDCB->pWaitLast = pre; + pDCB->WaitSRBCnt--; +} + + +/* Moves SRB from Going list to the top of Waiting list */ +static void +DC395x_Going_to_Waiting(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + DEBUG0(printk + (KERN_INFO DC395X_NAME + ": Going_to_Waiting (SRB %p) pid = %li\n", pSRB, + pSRB->pcmd->pid); + ) + /* Remove SRB from Going */ + DC395x_Going_remove(pDCB, pSRB, 0); + TRACEPRINTF("GtW *"); + /* Insert on top of Waiting */ + DC395x_Waiting_insert(pDCB, pSRB); + /* Tag Mask must be freed elsewhere ! (KG, 99/06/18) */ +} + + +/* Moves first SRB from Waiting list to Going list */ +static __inline__ void +DC395x_Waiting_to_Going(struct DeviceCtlBlk *pDCB, struct ScsiReqBlk *pSRB) +{ + /* Remove from waiting list */ + DEBUG0(printk + (DC395X_NAME ": Remove SRB %p from head of Waiting\n", + pSRB); + ) + DC395x_Waiting_remove(pDCB, pSRB, 0); + TRACEPRINTF("WtG *"); + DC395x_Going_append(pDCB, pSRB); +} + + +void DC395x_waiting_timed_out(unsigned long ptr); +/* Sets the timer to wake us up */ +static void +DC395x_waiting_timer(struct AdapterCtlBlk *pACB, unsigned long to) +{ + if (timer_pending(&pACB->Waiting_Timer)) + return; + init_timer(&pACB->Waiting_Timer); + pACB->Waiting_Timer.function = DC395x_waiting_timed_out; + pACB->Waiting_Timer.data = (unsigned long) pACB; + if (time_before + (jiffies + to, pACB->pScsiHost->last_reset - HZ / 2)) + pACB->Waiting_Timer.expires = + pACB->pScsiHost->last_reset - HZ / 2 + 1; + else + pACB->Waiting_Timer.expires = jiffies + to + 1; + add_timer(&pACB->Waiting_Timer); +} + + +/* Send the next command from the waiting list to the bus */ +void DC395x_Waiting_process(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *ptr; + struct DeviceCtlBlk *ptr1; + struct ScsiReqBlk *pSRB; + + if ((pACB->pActiveDCB) + || (pACB->ACBFlag & (RESET_DETECT + RESET_DONE + RESET_DEV))) + return; + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + ptr = pACB->pDCBRunRobin; + if (!ptr) { /* This can happen! */ + ptr = pACB->pLinkDCB; + pACB->pDCBRunRobin = ptr; + } + ptr1 = ptr; + if (!ptr1) + return; + do { + /* Make sure, the next another device gets scheduled ... */ + pACB->pDCBRunRobin = ptr1->pNextDCB; + if (!(pSRB = ptr1->pWaitingSRB) + || (ptr1->MaxCommand <= ptr1->GoingSRBCnt)) + ptr1 = ptr1->pNextDCB; + else { + /* Try to send to the bus */ + if (!DC395x_StartSCSI(pACB, ptr1, pSRB)) + DC395x_Waiting_to_Going(ptr1, pSRB); + else + DC395x_waiting_timer(pACB, HZ / 50); + break; + } + } while (ptr1 != ptr); + return; +} + + +/* Wake up waiting queue */ +void DC395x_waiting_timed_out(unsigned long ptr) +{ + unsigned long flags; + struct AdapterCtlBlk *pACB = (struct AdapterCtlBlk *) ptr; +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": Debug: Waiting queue woken up by timer.\n"); +#endif + DC395x_LOCK_IO(pACB->pScsiHost); + DC395x_Waiting_process(pACB); + DC395x_UNLOCK_IO(pACB->pScsiHost); +} + + +/* Get the DCB for a given ID/LUN combination */ +static inline struct DeviceCtlBlk *DC395x_findDCB(struct AdapterCtlBlk + *pACB, u8 id, u8 lun) +{ + return pACB->children[id][lun]; +} + + +/*********************************************************************** + * Function: static void DC395x_SendSRB (struct AdapterCtlBlk* pACB, struct ScsiReqBlk* pSRB) + * + * Purpose: Send SCSI Request Block (pSRB) to adapter (pACB) + * + * DC395x_queue_command + * DC395x_Waiting_process + * + ***********************************************************************/ +static void +DC395x_SendSRB(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB; + + pDCB = pSRB->pSRBDCB; + if ((pDCB->MaxCommand <= pDCB->GoingSRBCnt) || (pACB->pActiveDCB) + || (pACB->ACBFlag & (RESET_DETECT + RESET_DONE + RESET_DEV))) { + DC395x_Waiting_append(pDCB, pSRB); + DC395x_Waiting_process(pACB); + return; + } +#if 0 + if (pDCB->pWaitingSRB) { + DC395x_Waiting_append(pDCB, pSRB); + /* pSRB = GetWaitingSRB(pDCB); *//* non-existent */ + pSRB = pDCB->pWaitingSRB; + /* Remove from waiting list */ + pDCB->pWaitingSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + if (!pDCB->pWaitingSRB) + pDCB->pWaitLast = NULL; + } +#endif + + if (!DC395x_StartSCSI(pACB, pDCB, pSRB)) + DC395x_Going_append(pDCB, pSRB); + else { + DC395x_Waiting_insert(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 50); + } +} + + +/* + ********************************************************************* + * + * Function: static void DC395x_BuildSRB (Scsi_Cmd *pcmd, struct DeviceCtlBlk* pDCB, struct ScsiReqBlk* pSRB) + * + * Purpose: Prepare SRB for being sent to Device DCB w/ command *pcmd + * + ********************************************************************* + */ +static void +DC395x_BuildSRB(Scsi_Cmnd * pcmd, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + int i, max; + struct SGentry *sgp; + struct scatterlist *sl; + u32 request_size; + int dir; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_BuildSRB..............\n "); +#endif + /*memset (pSRB, 0, sizeof (struct ScsiReqBlk)); */ + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + /* Find out about direction */ + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + + if (pcmd->use_sg && dir != PCI_DMA_NONE) { + unsigned int len = 0; + /* TODO: In case usg_sg and the no of segments differ, things + * will probably go wrong. */ + max = pSRB->SRBSGCount = + pci_map_sg(pDCB->pDCBACB->pdev, + (struct scatterlist *) pcmd->request_buffer, + pcmd->use_sg, dir); + sgp = pSRB->SegmentX; + request_size = pcmd->request_bufflen; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": BuildSRB: Bufflen = %d, buffer = %p, use_sg = %d\n", + pcmd->request_bufflen, pcmd->request_buffer, + pcmd->use_sg); + printk(KERN_INFO DC395X_NAME + ": Mapped %i Segments to %i\n", pcmd->use_sg, + pSRB->SRBSGCount); +#endif + sl = (struct scatterlist *) pcmd->request_buffer; + + pSRB->virt_addr = page_address(sl->page); + for (i = 0; i < max; i++) { + u32 busaddr = (u32) sg_dma_address(&sl[i]); + u32 seglen = (u32) sl[i].length; + sgp[i].address = busaddr; + sgp[i].length = seglen; + len += seglen; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": Setting up sgp %d, address = 0x%08x, length = %d, tot len = %d\n", + i, busaddr, seglen, len); +#endif + } + sgp += max - 1; + /* Fixup for last buffer too big as it is allocated on even page boundaries */ + if (len > request_size) { +#if defined(DC395x_DEBUG_KG) || defined (DC395x_SGPARANOIA) + printk(KERN_INFO DC395X_NAME + ": Fixup SG total length: %d->%d, last seg %d->%d\n", + len, request_size, sgp->length, + sgp->length - (len - request_size)); +#endif + sgp->length -= (len - request_size); + len = request_size; + } + /* WIDE padding */ + if (pDCB->SyncPeriod & WIDE_SYNC && len % 2) { + len++; + sgp->length++; + } + pSRB->SRBTotalXferLength = len; /*? */ + /* Hopefully this does not cross a page boundary ... */ + pSRB->SRBSGBusAddr = + pci_map_single(pDCB->pDCBACB->pdev, pSRB->SegmentX, + sizeof(struct SGentry) * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME + ": Map SG descriptor list %p (%05x) to %08x\n", + pSRB->SegmentX, + sizeof(struct SGentry) * DC395x_MAX_SG_LISTENTRY, + pSRB->SRBSGBusAddr); +#endif + } else { + if (pcmd->request_buffer && dir != PCI_DMA_NONE) { + u32 len = pcmd->request_bufflen; /* Actual request size */ + pSRB->SRBSGCount = 1; + pSRB->SegmentX[0].address = + pci_map_single(pDCB->pDCBACB->pdev, + pcmd->request_buffer, len, dir); + /* WIDE padding */ + if (pDCB->SyncPeriod & WIDE_SYNC && len % 2) + len++; + pSRB->SegmentX[0].length = len; + pSRB->SRBTotalXferLength = len; + pSRB->virt_addr = pcmd->request_buffer; + pSRB->SRBSGBusAddr = 0; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": BuildSRB: len = %d, buffer = %p, use_sg = %d, map %08x\n", + len, pcmd->request_buffer, pcmd->use_sg, + pSRB->SegmentX[0].address); +#endif + } else { + pSRB->SRBSGCount = 0; + pSRB->SRBTotalXferLength = 0; + pSRB->SRBSGBusAddr = 0; + pSRB->virt_addr = 0; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": BuildSRB: buflen = %d, buffer = %p, use_sg = %d, NOMAP %08x\n", + pcmd->bufflen, pcmd->request_buffer, + pcmd->use_sg, pSRB->SegmentX[0].address); +#endif + } + } + + pSRB->SRBSGIndex = 0; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->SRBState = 0; + pSRB->RetryCnt = 0; + +#if DC395x_SGPARANOIA + if ((unsigned long) pSRB->debugtrace & (DEBUGTRACEBUFSZ - 1)) { + printk(DC395X_NAME + ": SRB %i (%p): debugtrace %p corrupt!\n", + (pSRB - + pDCB->pDCBACB->SRB_array) / + sizeof(struct ScsiReqBlk), pSRB, pSRB->debugtrace); + } +#endif +#ifdef DC395x_TRACEDEBUG + pSRB->debugpos = 0; + pSRB->debugtrace = 0; +#endif + TRACEPRINTF("pid %li(%li):%02x %02x..(%i-%i) *", pcmd->pid, + jiffies, pcmd->cmnd[0], pcmd->cmnd[1], + pcmd->device->id, pcmd->device->lun); + pSRB->TagNumber = TAG_NONE; + + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + pSRB->EndMessage = 0; + return; +} + + +/* Put cmnd from Query to Waiting list and send next Waiting cmnd */ +static void DC395x_Query_to_Waiting(struct AdapterCtlBlk *pACB) +{ + Scsi_Cmnd *pcmd; + struct ScsiReqBlk *pSRB; + struct DeviceCtlBlk *pDCB; + + if (pACB->ACBFlag & (RESET_DETECT + RESET_DONE + RESET_DEV)) + return; + + while (pACB->QueryCnt) { + pSRB = DC395x_Free_get(pACB); + if (!pSRB) + return; + pcmd = DC395x_Query_get(pACB); + if (!pcmd) { + DC395x_Free_insert(pACB, pSRB); + return; + } /* should not happen */ + pDCB = + DC395x_findDCB(pACB, pcmd->device->id, + pcmd->device->lun); + if (!pDCB) { + DC395x_Free_insert(pACB, pSRB); + printk(KERN_ERR DC395X_NAME + ": Command in queue to non-existing device!\n"); + pcmd->result = + MK_RES(DRIVER_ERROR, DID_ERROR, 0, 0); + /*DC395x_UNLOCK_ACB_NI; */ + pcmd->done(pcmd); + /*DC395x_LOCK_ACB_NI; */ + } + DC395x_BuildSRB(pcmd, pDCB, pSRB); + DC395x_Waiting_append(pDCB, pSRB); + } +} + + +/*********************************************************************** + * Function : static int DC395x_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - callback function called on + * completion, with a pointer to the command descriptor. + * + * Returns : (depending on kernel version) + * 2.0.x: always return 0 + * 2.1.x: old model: (use_new_eh_code == 0): like 2.0.x + * new model: return 0 if successful + * return 1 if command cannot be queued (queue full) + * command will be inserted in midlevel queue then ... + * + ***********************************************************************/ +static int +DC395x_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) cmd->device->host->hostdata; + + + DEBUG0( /* if(pACB->scan_devices) */ + printk(KERN_INFO DC395X_NAME + ": Queue Cmd=%02x,Tgt=%d,LUN=%d (pid=%li)\n", + cmd->cmnd[0], cmd->device->id, + cmd->device->lun, cmd->pid); + ) + + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) + printk(DC395X_NAME + ": %i queue_command () recursion? (pid=%li)\n", + in_driver, cmd->pid);) + + /* Assume BAD_TARGET; will be cleared later */ + cmd->result = DID_BAD_TARGET << 16; + + if ((cmd->device->id >= pACB->pScsiHost->max_id) + || (cmd->device->lun >= pACB->pScsiHost->max_lun) + || (cmd->device->lun >31)) { + /* printk (KERN_INFO DC395X_NAME "Ignore target %d lun %d\n", + cmd->device->id, cmd->device->lun); */ + DEBUGRECURSION(in_driver--; + ) + /*return 1; */ + done(cmd); + return 0; + } + + if (!(pACB->DCBmap[cmd->device->id] & (1 << cmd->device->lun))) { + printk(KERN_INFO DC395X_NAME + ": Ignore target %02x lun %02x\n", cmd->device->id, + cmd->device->lun); + /*return 1; */ + DEBUGRECURSION(in_driver--; + ) + done(cmd); + return 0; + } else { + pDCB = + DC395x_findDCB(pACB, cmd->device->id, + cmd->device->lun); + if (!pDCB) { /* should never happen */ + printk(KERN_ERR DC395X_NAME + ": no DCB failed, target %02x lun %02x\n", + cmd->device->id, cmd->device->lun); + printk(DC395X_NAME + ": No DCB in queuecommand (2)!\n"); + DEBUGRECURSION(in_driver--; + ) + return 1; + } + } + + pACB->Cmds++; + cmd->scsi_done = done; + cmd->result = 0; + + DC395x_Query_to_Waiting(pACB); + + if (pACB->QueryCnt) { + /* Unsent commands ? */ + DEBUG0(printk(DC395X_NAME ": QueryCnt != 0\n"); + ) + DC395x_Query_append(cmd, pACB); + DC395x_Waiting_process(pACB); + } else { + if (pDCB->pWaitingSRB) { + pSRB = DC395x_Free_get(pACB); + DEBUG0(if (!pSRB) + printk(DC395X_NAME + ": No free SRB but Waiting\n"); + else + printk(DC395X_NAME + ": Free SRB w/ Waiting\n");) + if (!pSRB) { + DC395x_Query_append(cmd, pACB); + } else { + DC395x_BuildSRB(cmd, pDCB, pSRB); + DC395x_Waiting_append(pDCB, pSRB); + } + DC395x_Waiting_process(pACB); + } else { + pSRB = DC395x_Free_get(pACB); + DEBUG0(if (!pSRB) + printk(DC395X_NAME + ": No free SRB w/o Waiting\n"); + else + printk(DC395X_NAME + ": Free SRB w/o Waiting\n");) + if (!pSRB) { + DC395x_Query_append(cmd, pACB); + DC395x_Waiting_process(pACB); + } else { + DC395x_BuildSRB(cmd, pDCB, pSRB); + DC395x_SendSRB(pACB, pSRB); + } + } + } + + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + DEBUG1(printk + (KERN_DEBUG " ... command (pid %li) queued successfully.\n", + cmd->pid); + ) + DEBUGRECURSION(in_driver--; + ) + return 0; +} + + +/*********************************************************************** + * Function static int DC395x_slave_alloc() + * + * Purpose: Allocate DCB + ***********************************************************************/ +static int DC395x_slave_alloc(struct scsi_device *sdp) +{ + struct AdapterCtlBlk *pACB; + struct DeviceCtlBlk *dummy; + + pACB = (struct AdapterCtlBlk *) sdp->host->hostdata; + + DC395x_initDCB(pACB, &dummy, sdp->id, sdp->lun); + + return dummy ? 0 : -ENOMEM; +} + + +static void DC395x_slave_destroy(struct scsi_device *sdp) +{ + struct AdapterCtlBlk *ACB; + struct DeviceCtlBlk *DCB; + + ACB = (struct AdapterCtlBlk *) sdp->host->hostdata; + DCB = DC395x_findDCB(ACB, sdp->id, sdp->lun); + + DC395x_remove_dev(ACB, DCB); +} + + +/*********************************************************************** + * Function : static void DC395_updateDCB() + * + * Purpose : Set the configuration dependent DCB parameters + ***********************************************************************/ +void +DC395x_updateDCB(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + /* Prevent disconnection of narrow devices if this_id > 7 */ + if (!(pDCB->DevMode & NTC_DO_WIDE_NEGO) + && pACB->pScsiHost->this_id > 7) + pDCB->DevMode &= ~NTC_DO_DISCONNECT; + + /* TagQ w/o DisCn is impossible */ + if (!(pDCB->DevMode & NTC_DO_DISCONNECT)) + pDCB->DevMode &= ~NTC_DO_TAG_QUEUEING; + pDCB->IdentifyMsg = + IDENTIFY((pDCB->DevMode & NTC_DO_DISCONNECT), pDCB->TargetLUN); + + pDCB->SyncMode &= + EN_TAG_QUEUEING | SYNC_NEGO_DONE | WIDE_NEGO_DONE + /*| EN_ATN_STOP */ ; + if (pDCB->DevMode & NTC_DO_TAG_QUEUEING) { + if (pDCB->SyncMode & EN_TAG_QUEUEING) + pDCB->MaxCommand = pACB->TagMaxNum; + } else { + pDCB->SyncMode &= ~EN_TAG_QUEUEING; + pDCB->MaxCommand = 1; + } + + if (pDCB->DevMode & NTC_DO_SYNC_NEGO) + pDCB->SyncMode |= SYNC_NEGO_ENABLE; + else { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE | SYNC_NEGO_ENABLE); + pDCB->SyncOffset &= ~0x0f; + } + + if (pDCB->DevMode & NTC_DO_WIDE_NEGO + && pACB->Config & HCC_WIDE_CARD) + pDCB->SyncMode |= WIDE_NEGO_ENABLE; + else { + pDCB->SyncMode &= ~(WIDE_NEGO_DONE | WIDE_NEGO_ENABLE); + pDCB->SyncPeriod &= ~WIDE_SYNC; + } + /*if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; */ +} + + +/* + ********************************************************************* + * + * Function : DC395x_bios_param + * Description: Return the disk geometry for the given SCSI device. + ********************************************************************* + */ +static int +DC395x_bios_param(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int *info) +{ +#ifdef CONFIG_SCSI_DC395x_TRMS1040_TRADMAP + int heads, sectors, cylinders; + struct AdapterCtlBlk *pACB; + int size = capacity; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ":DC395x_bios_param..............\n "); +#endif + pACB = (struct AdapterCtlBlk *) sdev->host->hostdata; + heads = 64; + sectors = 32; + cylinders = size / (heads * sectors); + + if ((pACB->Gmode2 & NAC_GREATER_1G) && (cylinders > 1024)) { + heads = 255; + sectors = 63; + cylinders = size / (heads * sectors); + } + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + return 0; +#else + return scsicam_bios_param(bdev, capacity, info); +#endif +} + + +/* + * DC395x register dump + */ +void +DC395x_dumpinfo(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u16 pstat; + struct pci_dev *pdev = pACB->pdev; + pci_read_config_word(pdev, PCI_STATUS, &pstat); + if (!pDCB) + pDCB = pACB->pActiveDCB; + if (!pSRB && pDCB) + pSRB = pDCB->pActiveSRB; + if (pSRB) { + if (!(pSRB->pcmd)) + printk(DC395X_NAME + ": dump: SRB %p: cmd %p OOOPS!\n", pSRB, + pSRB->pcmd); + else + printk(DC395X_NAME + ": dump: SRB %p: cmd %p pid %li: %02x (%02i-%i)\n", + pSRB, pSRB->pcmd, pSRB->pcmd->pid, + pSRB->pcmd->cmnd[0], pSRB->pcmd->device->id, + pSRB->pcmd->device->lun); + printk(" SGList %p Cnt %i Idx %i Len %i\n", + pSRB->SegmentX, pSRB->SRBSGCount, pSRB->SRBSGIndex, + pSRB->SRBTotalXferLength); + printk + (" State %04x Status %02x Phase %02x (%sconn.)\n", + pSRB->SRBState, pSRB->SRBStatus, pSRB->ScsiPhase, + (pACB->pActiveDCB) ? "" : "not"); + TRACEOUT(" %s\n", pSRB->debugtrace); + } + printk(DC395X_NAME ": dump: SCSI block\n"); + printk + (" Status %04x FIFOCnt %02x Signals %02x IRQStat %02x\n", + DC395x_read16(TRM_S1040_SCSI_STATUS), + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read8(TRM_S1040_SCSI_SIGNAL), + DC395x_read8(TRM_S1040_SCSI_INTSTATUS)); + printk + (" Sync %02x Target %02x RSelID %02x SCSICtr %08x\n", + DC395x_read8(TRM_S1040_SCSI_SYNC), + DC395x_read8(TRM_S1040_SCSI_TARGETID), + DC395x_read8(TRM_S1040_SCSI_IDMSG), + DC395x_read32(TRM_S1040_SCSI_COUNTER)); + printk + (" IRQEn %02x Config %04x Cfg2 %02x Cmd %02x SelTO %02x\n", + DC395x_read8(TRM_S1040_SCSI_INTEN), + DC395x_read16(TRM_S1040_SCSI_CONFIG0), + DC395x_read8(TRM_S1040_SCSI_CONFIG2), + DC395x_read8(TRM_S1040_SCSI_COMMAND), + DC395x_read8(TRM_S1040_SCSI_TIMEOUT)); + printk(DC395X_NAME ": dump: DMA block\n"); + printk + (" Cmd %04x FIFOCnt %02x FStat %02x IRQStat %02x IRQEn %02x Cfg %04x\n", + DC395x_read16(TRM_S1040_DMA_COMMAND), + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read8(TRM_S1040_DMA_STATUS), + DC395x_read8(TRM_S1040_DMA_INTEN), + DC395x_read16(TRM_S1040_DMA_CONFIG)); + printk(" TCtr %08x CTCtr %08x Addr %08x%08x\n", + DC395x_read32(TRM_S1040_DMA_XCNT), + DC395x_read32(TRM_S1040_DMA_CXCNT), + DC395x_read32(TRM_S1040_DMA_XHIGHADDR), + DC395x_read32(TRM_S1040_DMA_XLOWADDR)); + printk(DC395X_NAME + ": dump: Misc: GCtrl %02x GStat %02x GTmr %02x\n", + DC395x_read8(TRM_S1040_GEN_CONTROL), + DC395x_read8(TRM_S1040_GEN_STATUS), + DC395x_read8(TRM_S1040_GEN_TIMER)); + printk(DC395X_NAME ": dump: PCI Status %04x\n", pstat); + + +} + + +static inline void DC395x_clrfifo(struct AdapterCtlBlk *pACB, char *txt) +{ +#ifdef DC395x_DEBUGFIFO + u8 lines = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + u8 fifocnt = DC395x_read8(TRM_S1040_SCSI_FIFOCNT); + if (!(fifocnt & 0x40)) + printk(DC395X_NAME + ": Clr FIFO (%i bytes) on phase %02x in %s\n", + fifocnt & 0x3f, lines, txt); +#endif + if (pACB->pActiveDCB && pACB->pActiveDCB->pActiveSRB) { + struct ScsiReqBlk *pSRB = pACB->pActiveDCB->pActiveSRB; + TRACEPRINTF("#*"); + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); +} + + +/* + ******************************************************************** + * + * DC395x_reset DC395x_ScsiRstDetect + * + ******************************************************************** + */ +static void DC395x_ResetDevParam(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *pDCB; + struct DeviceCtlBlk *pDCBTemp; + struct NvRamType *eeprom; + u8 PeriodIndex; + u16 index; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_ResetDevParam..............\n "); +#endif + pDCB = pACB->pLinkDCB; + if (pDCB == NULL) + return; + + pDCBTemp = pDCB; + do { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE + WIDE_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + index = pACB->AdapterIndex; + eeprom = &dc395x_trm_eepromBuf[index]; + + pDCB->DevMode = + eeprom->NvramTarget[pDCB->TargetID].NvmTarCfg0; + /*pDCB->AdpMode = eeprom->NvramChannelCfg; */ + PeriodIndex = + eeprom->NvramTarget[pDCB->TargetID]. + NvmTarPeriod & 0x07; + pDCB->MinNegoPeriod = dc395x_clock_period[PeriodIndex]; + if (!(pDCB->DevMode & NTC_DO_WIDE_NEGO) + || !(pACB->Config & HCC_WIDE_CARD)) + pDCB->SyncMode &= ~WIDE_NEGO_ENABLE; + + pDCB = pDCB->pNextDCB; + } + while (pDCBTemp != pDCB && pDCB != NULL); +} + + +/* + ********************************************************************* + * Function : int DC395x_eh_bus_reset(Scsi_Cmnd *cmd) + * Purpose : perform a hard reset on the SCSI bus + * Inputs : cmd - some command for this host (for fetching hooks) + * Returns : SUCCESS (0x2002) on success, else FAILED (0x2003). + ********************************************************************* + */ +static int DC395x_eh_bus_reset(Scsi_Cmnd * cmd) +{ + struct AdapterCtlBlk *pACB; + /*u32 acb_flags=0; */ + + printk(KERN_INFO DC395X_NAME ": reset requested!\n"); + pACB = (struct AdapterCtlBlk *) cmd->device->host->hostdata; + /* mid level guarantees no recursion */ + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + + /* + * disable interrupt + */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + + DC395x_ResetSCSIBus(pACB); + udelay(500); + + /* We may be in serious trouble. Wait some seconds */ + pACB->pScsiHost->last_reset = + jiffies + 3 * HZ / 2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + + /* + * re-enable interrupt + */ + /* Clear SCSI FIFO */ + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + DC395x_clrfifo(pACB, "reset"); + /* Delete pending IRQ */ + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + DC395x_basic_config(pACB); + + DC395x_ResetDevParam(pACB); + DC395x_DoingSRB_Done(pACB, DID_RESET, cmd, 0); + + pACB->pActiveDCB = NULL; + + pACB->ACBFlag = 0; /* RESET_DETECT, RESET_DONE ,RESET_DEV */ + DC395x_Waiting_process(pACB); + + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + return SUCCESS; +} + + +/* + ********************************************************************* + * Function : int DC395x_eh_abort(Scsi_Cmnd *cmd) + * Purpose : abort an errant SCSI command + * Inputs : cmd - command to be aborted + * Returns : SUCCESS (0x2002) on success, else FAILED (0x2003). + ********************************************************************* + */ +static int DC395x_eh_abort(Scsi_Cmnd * cmd) +{ + /* + * Look into our command queues: If it has not been sent already, + * we remove it and return success. Otherwise fail. + * First check the Query Queues, then the Waiting ones + */ + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) cmd->device->host->hostdata; + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + int cnt = pACB->QueryCnt; + Scsi_Cmnd *pcmd; + Scsi_Cmnd *last = 0; + printk(DC395X_NAME ": DC395x_eh_abort: cmd %p (pid %li, %02i-%i) ", + cmd, cmd->pid, cmd->device->id, cmd->device->lun); + for (pcmd = pACB->pQueryHead; cnt--; + last = pcmd, pcmd = (Scsi_Cmnd *) pcmd->host_scribble) { + if (pcmd == cmd) { + /* unqueue */ + if (last) { + last->host_scribble = pcmd->host_scribble; + if (!pcmd->host_scribble) + pACB->pQueryTail = last; + } else { + pACB->pQueryHead = + (Scsi_Cmnd *) pcmd->host_scribble; + if (!pcmd->host_scribble) + pACB->pQueryTail = 0; + } + printk("found in Query queue :-)\n"); + pACB->QueryCnt--; + cmd->result = DID_ABORT << 16; + return SUCCESS; + } + } + pDCB = DC395x_findDCB(pACB, cmd->device->id, cmd->device->lun); + if (!pDCB) { + printk("no DCB !\n"); + return FAILED; + } + + pSRB = DC395x_find_cmd(cmd, pDCB->pWaitingSRB); + if (pSRB) { + DC395x_Waiting_remove(pDCB, pSRB, 0); + DC395x_pci_unmap_sense(pACB, pSRB); + DC395x_pci_unmap(pACB, pSRB); + DC395x_freetag(pDCB, pSRB); + DC395x_Free_insert(pACB, pSRB); + printk("found in waiting queue :-)\n"); + cmd->result = DID_ABORT << 16; + return SUCCESS; + } + pSRB = DC395x_find_cmd(cmd, pDCB->pGoingSRB); + if (pSRB) + printk("found in going queue :-(\n"); + else + printk("not found!\n"); + return FAILED; +} + + +/* + * TODO (new EH): + * int (*eh_device_reset_handler)(Scsi_Cmnd *); + * int (*eh_host_reset_handler)(Scsi_Cmnd *); + * + * remove Query Queue + * investigate whether/which commands need to be ffed back to mid-layer + * in _eh_reset() + */ + + +/* SDTR */ +static void +DC395x_Build_SDTR(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u8 *ptr = pSRB->MsgOutBuf + pSRB->MsgCnt; + if (pSRB->MsgCnt > 1) { + printk(DC395X_NAME + ": Build_SDTR: MsgOutBuf BUSY (%i: %02x %02x)\n", + pSRB->MsgCnt, pSRB->MsgOutBuf[0], + pSRB->MsgOutBuf[1]); + return; + } + if (!(pDCB->DevMode & NTC_DO_SYNC_NEGO)) { + pDCB->SyncOffset = 0; + pDCB->MinNegoPeriod = 200 >> 2; + } else if (pDCB->SyncOffset == 0) + pDCB->SyncOffset = SYNC_NEGO_OFFSET; + + *ptr++ = MSG_EXTENDED; /* (01h) */ + *ptr++ = 3; /* length */ + *ptr++ = EXTENDED_SDTR; /* (01h) */ + *ptr++ = pDCB->MinNegoPeriod; /* Transfer period (in 4ns) */ + *ptr++ = pDCB->SyncOffset; /* Transfer period (max. REQ/ACK dist) */ + pSRB->MsgCnt += 5; + pSRB->SRBState |= SRB_DO_SYNC_NEGO; + TRACEPRINTF("S *"); +} + + +/* SDTR */ +static void +DC395x_Build_WDTR(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u8 wide = + ((pDCB->DevMode & NTC_DO_WIDE_NEGO) & (pACB-> + Config & HCC_WIDE_CARD)) + ? 1 : 0; + u8 *ptr = pSRB->MsgOutBuf + pSRB->MsgCnt; + if (pSRB->MsgCnt > 1) { + printk(DC395X_NAME + ": Build_WDTR: MsgOutBuf BUSY (%i: %02x %02x)\n", + pSRB->MsgCnt, pSRB->MsgOutBuf[0], + pSRB->MsgOutBuf[1]); + return; + } + *ptr++ = MSG_EXTENDED; /* (01h) */ + *ptr++ = 2; /* length */ + *ptr++ = EXTENDED_WDTR; /* (03h) */ + *ptr++ = wide; + pSRB->MsgCnt += 4; + pSRB->SRBState |= SRB_DO_WIDE_NEGO; + TRACEPRINTF("W *"); +} + + +#if 0 +/* Timer to work around chip flaw: When selecting and the bus is + * busy, we sometimes miss a Selection timeout IRQ */ +void DC395x_selection_timeout_missed(unsigned long ptr); +/* Sets the timer to wake us up */ +static void DC395x_selto_timer(struct AdapterCtlBlk *pACB) +{ + if (timer_pending(&pACB->SelTO_Timer)) + return; + init_timer(&pACB->SelTO_Timer); + pACB->SelTO_Timer.function = DC395x_selection_timeout_missed; + pACB->SelTO_Timer.data = (unsigned long) pACB; + if (time_before + (jiffies + HZ, pACB->pScsiHost->last_reset + HZ / 2)) + pACB->SelTO_Timer.expires = + pACB->pScsiHost->last_reset + HZ / 2 + 1; + else + pACB->SelTO_Timer.expires = jiffies + HZ + 1; + add_timer(&pACB->SelTO_Timer); +} + + +void DC395x_selection_timeout_missed(unsigned long ptr) +{ + unsigned long flags; + struct AdapterCtlBlk *pACB = (struct AdapterCtlBlk *) ptr; + struct ScsiReqBlk *pSRB; + printk(DC395X_NAME ": Debug: Chip forgot to produce SelTO IRQ!\n"); + if (!pACB->pActiveDCB || !pACB->pActiveDCB->pActiveSRB) { + printk(DC395X_NAME ": ... but no cmd pending? Oops!\n"); + return; + } + DC395x_LOCK_IO(pACB->pScsiHost); + pSRB = pACB->pActiveDCB->pActiveSRB; + TRACEPRINTF("N/TO *"); + DC395x_Disconnect(pACB); + DC395x_UNLOCK_IO(pACB->pScsiHost); +} +#endif + + +/* + * scsiio + * DC395x_DoWaitingSRB DC395x_SRBdone + * DC395x_SendSRB DC395x_RequestSense + */ +u8 +DC395x_StartSCSI(struct AdapterCtlBlk * pACB, struct DeviceCtlBlk * pDCB, + struct ScsiReqBlk * pSRB) +{ + u16 s_stat2, return_code; + u8 s_stat, scsicommand, i, identify_message; + u8 *ptr; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_StartSCSI..............\n "); +#endif + pSRB->TagNumber = TAG_NONE; /* pACB->TagMaxNum: had error read in eeprom */ + + s_stat = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + s_stat2 = 0; + s_stat2 = DC395x_read16(TRM_S1040_SCSI_STATUS); + TRACEPRINTF("Start %02x *", s_stat); +#if 1 + if (s_stat & 0x20 /* s_stat2 & 0x02000 */ ) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: StartSCSI: pid %li(%02i-%i): BUSY %02x %04x\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN, + s_stat, s_stat2); +#endif + /* + * Try anyway? + * + * We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection + * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed! + * (This is likely to be a bug in the hardware. Obviously, most people + * only have one initiator per SCSI bus.) + * Instead let this fail and have the timer make sure the command is + * tried again after a short time + */ + TRACEPRINTF("^*"); + /*DC395x_selto_timer (pACB); */ + /*DC395x_monitor_next_IRQ = 1; */ + return 1; + } +#endif + if (pACB->pActiveDCB) { + printk(DC395X_NAME + ": We try to start a SCSI command (%li)!\n", + pSRB->pcmd->pid); + printk(DC395X_NAME + ": While another one (%li) is active!!\n", + (pACB->pActiveDCB->pActiveSRB ? pACB->pActiveDCB-> + pActiveSRB->pcmd->pid : 0)); + TRACEOUT(" %s\n", pSRB->debugtrace); + if (pACB->pActiveDCB->pActiveSRB) + TRACEOUT(" %s\n", + pACB->pActiveDCB->pActiveSRB->debugtrace); + return 1; + } + if (DC395x_read16(TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: StartSCSI failed (busy) for pid %li(%02i-%i)\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); +#endif + TRACEPRINTF("°*"); + return 1; + } + /* Allow starting of SCSI commands half a second before we allow the mid-level + * to queue them again after a reset */ + if (time_before(jiffies, pACB->pScsiHost->last_reset - HZ / 2)) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": We were just reset and don't accept commands yet!\n"); +#endif + return 1; + } + + /* Flush FIFO */ + DC395x_clrfifo(pACB, "Start"); + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + + identify_message = pDCB->IdentifyMsg; + /*DC395x_TRM_write8(TRM_S1040_SCSI_IDMSG, identify_message); */ + /* Don't allow disconnection for AUTO_REQSENSE: Cont.All.Cond.! */ + if (pSRB->SRBFlag & AUTO_REQSENSE) + identify_message &= 0xBF; + + if (((pSRB->pcmd->cmnd[0] == INQUIRY) + || (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) + || (pSRB->SRBFlag & AUTO_REQSENSE)) + && (((pDCB->SyncMode & WIDE_NEGO_ENABLE) + && !(pDCB->SyncMode & WIDE_NEGO_DONE)) + || ((pDCB->SyncMode & SYNC_NEGO_ENABLE) + && !(pDCB->SyncMode & SYNC_NEGO_DONE))) + && (pDCB->TargetLUN == 0)) { + pSRB->MsgOutBuf[0] = identify_message; + pSRB->MsgCnt = 1; + scsicommand = SCMD_SEL_ATNSTOP; + pSRB->SRBState = SRB_MSGOUT; +#ifndef SYNC_FIRST + if (pDCB->SyncMode & WIDE_NEGO_ENABLE + && pDCB->Inquiry7 & SCSI_INQ_WBUS16) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + goto no_cmd; + } +#endif + if (pDCB->SyncMode & SYNC_NEGO_ENABLE + && pDCB->Inquiry7 & SCSI_INQ_SYNC) { + DC395x_Build_SDTR(pACB, pDCB, pSRB); + goto no_cmd; + } + if (pDCB->SyncMode & WIDE_NEGO_ENABLE + && pDCB->Inquiry7 & SCSI_INQ_WBUS16) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + goto no_cmd; + } + pSRB->MsgCnt = 0; + } + /* + ** Send identify message + */ + DC395x_write8(TRM_S1040_SCSI_FIFO, identify_message); + + scsicommand = SCMD_SEL_ATN; + pSRB->SRBState = SRB_START_; +#ifndef DC395x_NO_TAGQ + if ((pDCB->SyncMode & EN_TAG_QUEUEING) + && (identify_message & 0xC0)) { + /* Send Tag message */ + u32 tag_mask = 1; + u8 tag_number = 0; + while (tag_mask & pDCB->TagMask + && tag_number <= pDCB->MaxCommand) { + tag_mask = tag_mask << 1; + tag_number++; + } + if (tag_number >= pDCB->MaxCommand) { + printk(KERN_WARNING DC395X_NAME + ": Start_SCSI: Out of tags for pid %li (%i-%i)\n", + pSRB->pcmd->pid, pSRB->pcmd->device->id, + pSRB->pcmd->device->lun); + pSRB->SRBState = SRB_READY; + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_HWRESELECT); + return 1; + } + /* + ** Send Tag id + */ + DC395x_write8(TRM_S1040_SCSI_FIFO, MSG_SIMPLE_QTAG); + DC395x_write8(TRM_S1040_SCSI_FIFO, tag_number); + pDCB->TagMask |= tag_mask; + pSRB->TagNumber = tag_number; + TRACEPRINTF("Tag %i *", tag_number); + + scsicommand = SCMD_SEL_ATN3; + pSRB->SRBState = SRB_START_; + } +#endif +/*polling:*/ + /* + * Send CDB ..command block ......... + */ +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO DC395X_NAME + ": StartSCSI (pid %li) %02x (%i-%i): Tag %i\n", + pSRB->pcmd->pid, pSRB->pcmd->cmnd[0], + pSRB->pcmd->device->id, pSRB->pcmd->device->lun, + pSRB->TagNumber); +#endif + if (pSRB->SRBFlag & AUTO_REQSENSE) { + DC395x_write8(TRM_S1040_SCSI_FIFO, REQUEST_SENSE); + DC395x_write8(TRM_S1040_SCSI_FIFO, (pDCB->TargetLUN << 5)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, + sizeof(pSRB->pcmd->sense_buffer)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + } else { + ptr = (u8 *) pSRB->pcmd->cmnd; + for (i = 0; i < pSRB->pcmd->cmd_len; i++) + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr++); + } + no_cmd: + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_HWRESELECT | DO_DATALATCH); + if (DC395x_read16(TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) { + /* + * If DC395x_StartSCSI return 1: + * we caught an interrupt (must be reset or reselection ... ) + * : Let's process it first! + */ + DEBUG0(printk + (DC395X_NAME + ": Debug: StartSCSI failed (busy) for pid %li(%02i-%i)!\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); + ) + /*DC395x_clrfifo (pACB, "Start2"); */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); */ + pSRB->SRBState = SRB_READY; + DC395x_freetag(pDCB, pSRB); + pSRB->MsgCnt = 0; + return_code = 1; + /* This IRQ should NOT get lost, as we did not acknowledge it */ + } else { + /* + * If DC395x_StartSCSI returns 0: + * we know that the SCSI processor is free + */ + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + pDCB->pActiveSRB = pSRB; + pACB->pActiveDCB = pDCB; + return_code = 0; + /* it's important for atn stop */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_DATALATCH | DO_HWRESELECT); + /* + ** SCSI command + */ + TRACEPRINTF("%02x *", scsicommand); + DC395x_write8(TRM_S1040_SCSI_COMMAND, scsicommand); + } + return return_code; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_initAdapter + ******************************************************************** + */ + +/** + * dc395x_handle_interrupt - Handle an interrupt that has been confirmed to + * have been triggered for this card. + * + * @pACB: a pointer to the adpter control block + * @scsi_status: the status return when we checked the card + **/ +static void dc395x_handle_interrupt(struct AdapterCtlBlk *pACB, u16 scsi_status) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + u16 phase; + u8 scsi_intstatus; + unsigned long flags; + void (*DC395x_stateV) (struct AdapterCtlBlk *, struct ScsiReqBlk *, + u16 *); + + DC395x_LOCK_IO(pACB->pScsiHost); + + /* This acknowledges the IRQ */ + scsi_intstatus = DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + if ((scsi_status & 0x2007) == 0x2002) + printk(DC395X_NAME ": COP after COP completed? %04x\n", + scsi_status); +#if 1 /*def DC395x_DEBUG0 */ + if (DC395x_monitor_next_IRQ) { + printk(KERN_INFO DC395X_NAME + ": status=%04x intstatus=%02x\n", scsi_status, + scsi_intstatus); + DC395x_monitor_next_IRQ--; + } +#endif + /*DC395x_ACB_LOCK(pACB,acb_flags); */ +#ifdef DC395x_DEBUG_KG + if (scsi_intstatus & INT_SELTIMEOUT) + printk(KERN_INFO DC395X_NAME ": Sel Timeout IRQ\n"); +#endif + /*printk (DC395X_NAME ": DC395x_IRQ: intstatus = %02x ", scsi_intstatus); */ + + if (timer_pending(&pACB->SelTO_Timer)) + del_timer(&pACB->SelTO_Timer); + + if (scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) { + DC395x_Disconnect(pACB); /* bus free interrupt */ + goto out_unlock; + } + if (scsi_intstatus & INT_RESELECTED) { + DC395x_Reselect(pACB); + goto out_unlock; + } + if (scsi_intstatus & INT_SELECT) { + printk(KERN_INFO DC395X_NAME + ": Host does not support target mode!\n"); + goto out_unlock; + } + if (scsi_intstatus & INT_SCSIRESET) { + DC395x_ScsiRstDetect(pACB); + goto out_unlock; + } + if (scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) { + pDCB = pACB->pActiveDCB; + if (!pDCB) { + printk(DC395X_NAME + ": Oops: BusService (%04x %02x) w/o ActiveDCB!\n", + scsi_status, scsi_intstatus); + goto out_unlock; + } + pSRB = pDCB->pActiveSRB; + if (pDCB->DCBFlag & ABORT_DEV_) { +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "MsgOut Abort Device..... "); +#endif + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } + /* + ************************************************************ + * software sequential machine + ************************************************************ + */ + phase = (u16) pSRB->ScsiPhase; + /* + * 62037 or 62137 + * call DC395x_SCSI_phase0[]... "phase entry" + * handle every phase before start transfer + */ + /* DC395x_DataOutPhase0, phase:0 */ + /* DC395x_DataInPhase0, phase:1 */ + /* DC395x_CommandPhase0, phase:2 */ + /* DC395x_StatusPhase0, phase:3 */ + /* DC395x_Nop0, phase:4 PH_BUS_FREE .. initial phase */ + /* DC395x_Nop0, phase:5 PH_BUS_FREE .. initial phase */ + /* DC395x_MsgOutPhase0, phase:6 */ + /* DC395x_MsgInPhase0, phase:7 */ + DC395x_stateV = (void *) DC395x_SCSI_phase0[phase]; + DC395x_stateV(pACB, pSRB, &scsi_status); + /* + *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + * + * if there were any exception occured + * scsi_status will be modify to bus free phase + * new scsi_status transfer out from ... previous DC395x_stateV + * + *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + */ + pSRB->ScsiPhase = scsi_status & PHASEMASK; + phase = (u16) scsi_status & PHASEMASK; + /* + * call DC395x_SCSI_phase1[]... "phase entry" + * handle every phase do transfer + */ + /* DC395x_DataOutPhase1, phase:0 */ + /* DC395x_DataInPhase1, phase:1 */ + /* DC395x_CommandPhase1, phase:2 */ + /* DC395x_StatusPhase1, phase:3 */ + /* DC395x_Nop1, phase:4 PH_BUS_FREE .. initial phase */ + /* DC395x_Nop1, phase:5 PH_BUS_FREE .. initial phase */ + /* DC395x_MsgOutPhase1, phase:6 */ + /* DC395x_MsgInPhase1, phase:7 */ + DC395x_stateV = (void *) DC395x_SCSI_phase1[phase]; + DC395x_stateV(pACB, pSRB, &scsi_status); + } + out_unlock: + DC395x_UNLOCK_IO(pACB->pScsiHost); + return; +} + +/*inline */ +irqreturn_t DC395x_Interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct AdapterCtlBlk *pACB = DC395x_pACB_start; + u16 scsi_status; + u8 dma_status; + irqreturn_t handled = IRQ_NONE; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_Interrupt..............\n "); +#endif + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) + printk(DC395X_NAME ": %i interrupt recursion?\n", + in_driver);) + + /* + * Find which card generated the interrupt. Note that it may have + * been something else that we share the interupt with which + * actually generated it. + * + * We'll check the interupt status register of each card that + * is on the IRQ that was responsible for this interupt. + */ + for (; pACB != NULL; pACB = pACB->pNextACB) { + if (pACB->IRQLevel != (u8) irq) { + /* card is not on the irq that triggered */ + continue; + } + + /* + * Ok, we've found a card on the correct irq, + * let's check if an interupt is pending + */ + scsi_status = DC395x_read16(TRM_S1040_SCSI_STATUS); + dma_status = DC395x_read8(TRM_S1040_DMA_STATUS); + if (scsi_status & SCSIINTERRUPT) { + /* interupt pending - let's process it! */ + dc395x_handle_interrupt(pACB, scsi_status); + handled = IRQ_HANDLED; + } + else if (dma_status & 0x20) { + /* Error from the DMA engine */ + printk(DC395X_NAME ": Interrupt from DMA engine: %02x!\n", + dma_status); +#if 0 + printk(DC395X_NAME ": This means DMA error! Try to handle ...\n"); + if (pACB->pActiveDCB) { + pACB->pActiveDCB-> DCBFlag |= ABORT_DEV_; + if (pACB->pActiveDCB->pActiveSRB) + DC395x_EnableMsgOut_Abort(pACB, pACB->pActiveDCB->pActiveSRB); + } + DC395x_write8(TRM_S1040_DMA_CONTROL, ABORTXFER | CLRXFIFO); +#else + printk(DC395X_NAME ": Ignoring DMA error (probably a bad thing) ...\n"); + pACB = (struct AdapterCtlBlk *)NULL; +#endif + handled = IRQ_HANDLED; + } + } + + DEBUGRECURSION(in_driver--;) + return handled; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgOutPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =6 + ******************************************************************** + */ +static void +DC395x_MsgOutPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_MsgOutPhase0..... "); +#endif + if (pSRB->SRBState & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT)) { + *pscsi_status = PH_BUS_FREE; /*.. initial phase */ + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + pSRB->SRBState &= ~SRB_MSGOUT; + TRACEPRINTF("MOP0 *"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgOutPhase1: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =6 + ******************************************************************** + */ +static void +DC395x_MsgOutPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + u16 i; + u8 *ptr; + struct DeviceCtlBlk *pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_MsgOutPhase1..............\n "); +#endif + TRACEPRINTF("MOP1*"); + pDCB = pACB->pActiveDCB; + DC395x_clrfifo(pACB, "MOP1"); + if (!(pSRB->SRBState & SRB_MSGOUT)) { + pSRB->SRBState |= SRB_MSGOUT; + printk(DC395X_NAME ": Debug: pid %li: MsgOut Phase unexpected.\n", pSRB->pcmd->pid); /* So what ? */ + } + if (!pSRB->MsgCnt) { + DEBUG0(printk + (DC395X_NAME + ": Debug: pid %li: NOP Msg (no output message there).\n", + pSRB->pcmd->pid); + ) + DC395x_write8(TRM_S1040_SCSI_FIFO, MSG_NOP); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); + TRACEPRINTF("\\*"); + TRACEOUT(" %s\n", pSRB->debugtrace); + return; + } + ptr = (u8 *) pSRB->MsgOutBuf; + TRACEPRINTF("(*"); + /*printk (DC395X_NAME ": Send msg: "); DC395x_printMsg (ptr, pSRB->MsgCnt); */ + /*printk (DC395X_NAME ": MsgOut: "); */ + for (i = 0; i < pSRB->MsgCnt; i++) { + TRACEPRINTF("%02x *", *ptr); + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr++); + } + TRACEPRINTF(")*"); + pSRB->MsgCnt = 0; + /*printk ("\n"); */ + if ( /*(pDCB->DCBFlag & ABORT_DEV_) && */ + (pSRB->MsgOutBuf[0] == MSG_ABORT)) + pSRB->SRBState = SRB_ABORT_SENT; + + /*1.25 */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); *//* it's important for atn stop */ + /* + ** SCSI command + */ + /*TRACEPRINTF (".*"); */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_CommandPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =2 + ******************************************************************** + */ +static void +DC395x_CommandPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + TRACEPRINTF("COP0 *"); + /*1.25 */ + /*DC395x_clrfifo (pACB, COP0); */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_CommandPhase1: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =2 + ******************************************************************** + */ +static void +DC395x_CommandPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + struct DeviceCtlBlk *pDCB; + u8 *ptr; + u16 i; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_CommandPhase1..............\n "); +#endif + TRACEPRINTF("COP1*"); + DC395x_clrfifo(pACB, "COP1"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_CLRATN); + if (!(pSRB->SRBFlag & AUTO_REQSENSE)) { + ptr = (u8 *) pSRB->pcmd->cmnd; + for (i = 0; i < pSRB->pcmd->cmd_len; i++) { + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr); + ptr++; + } + } else { + DC395x_write8(TRM_S1040_SCSI_FIFO, REQUEST_SENSE); + pDCB = pACB->pActiveDCB; + /* target id */ + DC395x_write8(TRM_S1040_SCSI_FIFO, (pDCB->TargetLUN << 5)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, + sizeof(pSRB->pcmd->sense_buffer)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + } + pSRB->SRBState |= SRB_COMMAND; + /* it's important for atn stop */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* SCSI command */ + TRACEPRINTF(".*"); + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + + +/* Do sanity checks for S/G list */ +#ifdef DC395x_SGPARANOIA +static inline void DC395x_check_SG(struct ScsiReqBlk *pSRB) +{ + unsigned Length = 0; + unsigned Idx = pSRB->SRBSGIndex; + struct SGentry *psge = pSRB->SegmentX + Idx; + for (; Idx < pSRB->SRBSGCount; psge++, Idx++) + Length += psge->length; + if (Length != pSRB->SRBTotalXferLength) + printk(DC395X_NAME + ": Inconsistent SRB S/G lengths (Tot=%i, Count=%i) !!\n", + pSRB->SRBTotalXferLength, Length); +} +#else +static inline void DC395x_check_SG(struct ScsiReqBlk *pSRB) +{ +} +#endif + + +/* + * Compute the next Scatter Gather list index and adjust its length + * and address if necessary; also compute virt_addr + */ +void DC395x_update_SGlist(struct ScsiReqBlk *pSRB, u32 Left) +{ + struct SGentry *psge; + u32 Xferred = 0; + u8 Idx; + Scsi_Cmnd *pcmd = pSRB->pcmd; + struct scatterlist *sg; + int segment = pcmd->use_sg; + +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": Update SG: Total %i, Left %i\n", + pSRB->SRBTotalXferLength, Left); +#endif + DC395x_check_SG(pSRB); + psge = pSRB->SegmentX + pSRB->SRBSGIndex; + /* data that has already been transferred */ + Xferred = pSRB->SRBTotalXferLength - Left; + if (pSRB->SRBTotalXferLength != Left) { + /*DC395x_check_SG_TX (pSRB, Xferred); */ + /* Remaining */ + pSRB->SRBTotalXferLength = Left; + /* parsing from last time disconnect SGIndex */ + for (Idx = pSRB->SRBSGIndex; Idx < pSRB->SRBSGCount; Idx++) { + /* Complete SG entries done */ + if (Xferred >= psge->length) + Xferred -= psge->length; + /* Partial SG entries done */ + else { + psge->length -= Xferred; /* residue data length */ + psge->address += Xferred; /* residue data pointer */ + pSRB->SRBSGIndex = Idx; + pci_dma_sync_single(pSRB->pSRBDCB-> + pDCBACB->pdev, + pSRB->SRBSGBusAddr, + sizeof(struct SGentry) + * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); + break; + } + psge++; + } + DC395x_check_SG(pSRB); + } + /* We need the corresponding virtual address sg_to_virt */ + /*printk (DC395X_NAME ": sg_to_virt: bus %08x -> virt ", psge->address); */ + if (!segment) { + pSRB->virt_addr += Xferred; + /*printk ("%p\n", pSRB->virt_addr); */ + return; + } + /* We have to walk the scatterlist to find it */ + sg = (struct scatterlist *) pcmd->request_buffer; + while (segment--) { + /*printk ("(%08x)%p ", BUS_ADDR(*sg), PAGE_ADDRESS(sg)); */ + unsigned long mask = + ~((unsigned long) sg->length - 1) & PAGE_MASK; + if ((BUS_ADDR(*sg) & mask) == (psge->address & mask)) { + pSRB->virt_addr = (PAGE_ADDRESS(sg) + + psge->address - + (psge->address & PAGE_MASK)); + /*printk ("%p\n", pSRB->virt_addr); */ + return; + } + ++sg; + } + printk(DC395X_NAME ": sg_to_virt failed!\n"); + pSRB->virt_addr = 0; +} + + +/* + * DC395x_cleanup_after_transfer + * + * Makes sure, DMA and SCSI engine are empty, after the transfer has finished + * KG: Currently called from StatusPhase1 () + * Should probably also be called from other places + * Best might be to call it in DataXXPhase0, if new phase will differ + */ +static void +DC395x_cleanup_after_transfer(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB) +{ + TRACEPRINTF(" Cln*"); + /*DC395x_write8 (TRM_S1040_DMA_STATUS, FORCEDMACOMP); */ + if (DC395x_read16(TRM_S1040_DMA_COMMAND) & 0x0001) { /* read */ + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + DC395x_clrfifo(pACB, "ClnIn"); + + if (!(DC395x_read8(TRM_S1040_DMA_FIFOSTAT) & 0x80)) + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + } else { /* write */ + if (!(DC395x_read8(TRM_S1040_DMA_FIFOSTAT) & 0x80)) + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + DC395x_clrfifo(pACB, "ClnOut"); + + } + /*1.25 */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); +} + + +/* + * Those no of bytes will be transfered w/ PIO through the SCSI FIFO + * Seems to be needed for unknown reasons; could be a hardware bug :-( + */ +#define DC395x_LASTPIO 4 +/* + ******************************************************************** + * scsiio + * DC395x_DataOutPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =0 + ******************************************************************** + */ +void +DC395x_DataOutPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + u16 scsi_status; + u32 dLeftCounter = 0; + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_DataOutPhase0.....\n "); +#endif + TRACEPRINTF("DOP0*"); + pDCB = pSRB->pSRBDCB; + scsi_status = *pscsi_status; + + /* + * KG: We need to drain the buffers before we draw any conclusions! + * This means telling the DMA to push the rest into SCSI, telling + * SCSI to push the rest to the bus. + * However, the device might have been the one to stop us (phase + * change), and the data in transit just needs to be accounted so + * it can be retransmitted.) + */ + /* + * KG: Stop DMA engine pushing more data into the SCSI FIFO + * If we need more data, the DMA SG list will be freshly set up, anyway + */ +#ifdef DC395x_DEBUGPIO + printk(DC395X_NAME + ": DOP0: DMA_FCNT: %02x, DMA_FSTAT: %02x, SCSI_FCNT: %02x, CTR %06x, stat %04x, Tot: %06x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read32(TRM_S1040_SCSI_COUNTER), scsi_status, + pSRB->SRBTotalXferLength); + /*DC395x_dumpinfo(pACB, pDCB, pSRB); */ +#endif + DC395x_write8(TRM_S1040_DMA_CONTROL, STOPDMAXFER | CLRXFIFO); + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsi_status & PARITYERROR) + pSRB->SRBStatus |= PARITY_ERROR; + + /* + * KG: Right, we can't just rely on the SCSI_COUNTER, because this + * is the no of bytes it got from the DMA engine not the no it + * transferred successfully to the device. (And the difference could + * be as much as the FIFO size, I guess ...) + */ + if (!(scsi_status & SCSIXFERDONE)) { + /* + * when data transfer from DMA FIFO to SCSI FIFO + * if there was some data left in SCSI FIFO + */ + dLeftCounter = + (u32) (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & + 0x1F); + if (pDCB->SyncPeriod & WIDE_SYNC) + dLeftCounter <<= 1; + +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: SCSI FIFO contains %i %s in DOP0\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + (pDCB-> + SyncPeriod & WIDE_SYNC) ? "words" : + "bytes"); + printk(DC395X_NAME + ": SCSI FIFOCNT %02x, SCSI CTR %08x\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read32(TRM_S1040_SCSI_COUNTER)); + printk(DC395X_NAME + ": DMA FIFOCNT %04x, FIFOSTAT %02x, DMA CTR %08x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read32(TRM_S1040_DMA_CXCNT)); +#endif + /* + * if WIDE scsi SCSI FIFOCNT unit is word !!! + * so need to *= 2 + */ + } + /* + * calculate all the residue data that not yet tranfered + * SCSI transfer counter + left in SCSI FIFO data + * + * .....TRM_S1040_SCSI_COUNTER (24bits) + * The counter always decrement by one for every SCSI byte transfer. + * .....TRM_S1040_SCSI_FIFOCNT ( 5bits) + * The counter is SCSI FIFO offset counter (in units of bytes or! words) + */ + if (pSRB->SRBTotalXferLength > DC395x_LASTPIO) + dLeftCounter += + DC395x_read32(TRM_S1040_SCSI_COUNTER); + TRACEPRINTF("%06x *", dLeftCounter); + + /* Is this a good idea? */ + /*DC395x_clrfifo (pACB, "DOP1"); */ + /* KG: What is this supposed to be useful for? WIDE padding stuff? */ + if (dLeftCounter == 1 && pDCB->SyncPeriod & WIDE_SYNC + && pSRB->pcmd->request_bufflen % 2) { + dLeftCounter = 0; + printk(DC395X_NAME + ": DOP0: Discard 1 byte. (%02x)\n", + scsi_status); + } + /* + * KG: Oops again. Same thinko as above: The SCSI might have been + * faster than the DMA engine, so that it ran out of data. + * In that case, we have to do just nothing! + * But: Why the interrupt: No phase change. No XFERCNT_2_ZERO. Or? + */ + /* + * KG: This is nonsense: We have been WRITING data to the bus + * If the SCSI engine has no bytes left, how should the DMA engine? + */ + if ((dLeftCounter == + 0) /*|| (scsi_status & SCSIXFERCNT_2_ZERO) ) */ ) { + /* + * int ctr = 6000000; u8 TempDMAstatus; + * do + * { + * TempDMAstatus = DC395x_read8(TRM_S1040_DMA_STATUS); + * } while( !(TempDMAstatus & DMAXFERCOMP) && --ctr); + * if (ctr < 6000000-1) printk (DC395X_NAME ": DMA should be complete ... in DOP1\n"); + * if (!ctr) printk (KERN_ERR DC395X_NAME ": Deadlock in DataOutPhase0 !!\n"); + */ + pSRB->SRBTotalXferLength = 0; + } else { /* Update SG list */ + /* + * if transfer not yet complete + * there were some data residue in SCSI FIFO or + * SCSI transfer counter not empty + */ + long oldXferred = + pSRB->SRBTotalXferLength - dLeftCounter; + const int diff = + (pDCB->SyncPeriod & WIDE_SYNC) ? 2 : 1; + DC395x_update_SGlist(pSRB, dLeftCounter); + /* KG: Most ugly hack! Apparently, this works around a chip bug */ + if ((pSRB->SegmentX[pSRB->SRBSGIndex].length == + diff && pSRB->pcmd->use_sg) + || ((oldXferred & ~PAGE_MASK) == + (PAGE_SIZE - diff)) + ) { + printk(DC395X_NAME + ": Work around chip bug (%i)?\n", + diff); + dLeftCounter = + pSRB->SRBTotalXferLength - diff; + DC395x_update_SGlist(pSRB, dLeftCounter); + /*pSRB->SRBTotalXferLength -= diff; */ + /*pSRB->virt_addr += diff; */ + /*if (pSRB->pcmd->use_sg) */ + /* pSRB->SRBSGIndex++; */ + } + } + } +#if 0 + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + printk(DC395X_NAME + ": DOP0(%li): %i bytes in SCSI FIFO! (Clear!)\n", + pSRB->pcmd->pid, + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f); +#endif + /*DC395x_clrfifo (pACB, "DOP0"); */ + /*DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO | ABORTXFER); */ +#if 1 + if ((*pscsi_status & PHASEMASK) != PH_DATA_OUT) { + /*printk (DC395X_NAME ": Debug: Clean up after Data Out ...\n"); */ + DC395x_cleanup_after_transfer(pACB, pSRB); + } +#endif + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataOutPhase1: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =0 + * 62037 + ******************************************************************** + */ +static void +DC395x_DataOutPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_DataOutPhase1.....\n"); +#endif + /*1.25 */ + TRACEPRINTF("DOP1*"); + DC395x_clrfifo(pACB, "DOP1"); + /* + ** do prepare befor transfer when data out phase + */ + DC395x_DataIO_transfer(pACB, pSRB, XFERDATAOUT); + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataInPhase0: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =1 + ******************************************************************** + */ +void +DC395x_DataInPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + u16 scsi_status; + u32 dLeftCounter = 0; + /*struct DeviceCtlBlk* pDCB = pSRB->pSRBDCB; */ + /*u8 bval; */ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_DataInPhase0..............\n "); +#endif + TRACEPRINTF("DIP0*"); + scsi_status = *pscsi_status; + + /* + * KG: DataIn is much more tricky than DataOut. When the device is finished + * and switches to another phase, the SCSI engine should be finished too. + * But: There might still be bytes left in its FIFO to be fetched by the DMA + * engine and transferred to memory. + * We should wait for the FIFOs to be emptied by that (is there any way to + * enforce this?) and then stop the DMA engine, because it might think, that + * there are more bytes to follow. Yes, the device might disconnect prior to + * having all bytes transferred! + * Also we should make sure that all data from the DMA engine buffer's really + * made its way to the system memory! Some documentation on this would not + * seem to be a bad idea, actually. + */ + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsi_status & PARITYERROR) { + printk(DC395X_NAME + ": Parity Error (pid %li, target %02i-%i)\n", + pSRB->pcmd->pid, pSRB->pcmd->device->id, + pSRB->pcmd->device->lun); + pSRB->SRBStatus |= PARITY_ERROR; + } + /* + * KG: We should wait for the DMA FIFO to be empty ... + * but: it would be better to wait first for the SCSI FIFO and then the + * the DMA FIFO to become empty? How do we know, that the device not already + * sent data to the FIFO in a MsgIn phase, eg.? + */ + if (!(DC395x_read8(TRM_S1040_DMA_FIFOSTAT) & 0x80)) { +#if 0 + int ctr = 6000000; + printk(DC395X_NAME + ": DIP0: Wait for DMA FIFO to flush ...\n"); + /*DC395x_write8 (TRM_S1040_DMA_CONTROL, STOPDMAXFER); */ + /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 7); */ + /*DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_IN); */ + while (! + (DC395x_read16(TRM_S1040_DMA_FIFOSTAT) & + 0x80) && --ctr); + if (ctr < 6000000 - 1) + printk(DC395X_NAME + ": Debug: DIP0: Had to wait for DMA ...\n"); + if (!ctr) + printk(KERN_ERR DC395X_NAME + ": Deadlock in DIP0 waiting for DMA FIFO empty!!\n"); + /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 0); */ +#endif +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": DIP0: DMA_FIFO: %02x %02x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT)); +#endif + } + /* Now: Check remainig data: The SCSI counters should tell us ... */ + dLeftCounter = DC395x_read32(TRM_S1040_SCSI_COUNTER) + + ((DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f) + << ((pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) ? 1 : + 0)); + +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Debug: SCSI FIFO contains %i %s in DIP0\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f, + (pSRB->pSRBDCB-> + SyncPeriod & WIDE_SYNC) ? "words" : "bytes"); + printk(DC395X_NAME ": SCSI FIFOCNT %02x, SCSI CTR %08x\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + DC395x_read32(TRM_S1040_SCSI_COUNTER)); + printk(DC395X_NAME + ": DMA FIFOCNT %02x,%02x DMA CTR %08x\n", + DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read32(TRM_S1040_DMA_CXCNT)); + printk(DC395X_NAME + ": Remaining: TotXfer: %i, SCSI FIFO+Ctr: %i\n", + pSRB->SRBTotalXferLength, dLeftCounter); +#endif +#if DC395x_LASTPIO + /* KG: Less than or equal to 4 bytes can not be transfered via DMA, it seems. */ + if (dLeftCounter + && pSRB->SRBTotalXferLength <= DC395x_LASTPIO) { + /*u32 addr = (pSRB->SegmentX[pSRB->SRBSGIndex].address); */ + /*DC395x_update_SGlist (pSRB, dLeftCounter); */ + DEBUGPIO(printk + (DC395X_NAME + ": DIP0: PIO (%i %s) to %p for remaining %i bytes:", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & + 0x1f, + (pSRB->pSRBDCB-> + SyncPeriod & WIDE_SYNC) ? "words" : + "bytes", pSRB->virt_addr, + pSRB->SRBTotalXferLength); + ) + + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + DC395x_write8(TRM_S1040_SCSI_CONFIG2, + CFG2_WIDEFIFO); + + while (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) != + 0x40) { + u8 byte = + DC395x_read8(TRM_S1040_SCSI_FIFO); + *(pSRB->virt_addr)++ = byte; + DEBUGPIO(printk(" %02x", byte); + ) + pSRB->SRBTotalXferLength--; + dLeftCounter--; + pSRB->SegmentX[pSRB->SRBSGIndex].length--; + if (pSRB->SRBTotalXferLength + && !pSRB->SegmentX[pSRB->SRBSGIndex]. + length) { + DEBUGPIO(printk(" (next segment)"); + ) + pSRB->SRBSGIndex++; + DC395x_update_SGlist(pSRB, + dLeftCounter); + } + } + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) { +#if 1 /* Read the last byte ... */ + if (pSRB->SRBTotalXferLength > 0) { + u8 byte = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + *(pSRB->virt_addr)++ = byte; + pSRB->SRBTotalXferLength--; + DEBUGPIO(printk(" %02x", byte); + ) + } +#endif + DC395x_write8(TRM_S1040_SCSI_CONFIG2, 0); + } + /*printk (" %08x", *(u32*)(bus_to_virt (addr))); */ + /*pSRB->SRBTotalXferLength = 0; */ + DEBUGPIO(printk("\n"); + ) + } +#endif /* DC395x_LASTPIO */ + +#if 0 + /* + * KG: This was in DATAOUT. Does it also belong here? + * Nobody seems to know what counter and fifo_cnt count exactly ... + */ + if (!(scsi_status & SCSIXFERDONE)) { + /* + * when data transfer from DMA FIFO to SCSI FIFO + * if there was some data left in SCSI FIFO + */ + dLeftCounter = + (u32) (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & + 0x1F); + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + dLeftCounter <<= 1; + /* + * if WIDE scsi SCSI FIFOCNT unit is word !!! + * so need to *= 2 + * KG: Seems to be correct ... + */ + } +#endif + /*dLeftCounter += DC395x_read32(TRM_S1040_SCSI_COUNTER); */ +#if 0 + printk(DC395X_NAME + ": DIP0: ctr=%08x, DMA_FIFO=%02x,%02x SCSI_FIFO=%02x\n", + dLeftCounter, DC395x_read8(TRM_S1040_DMA_FIFOCNT), + DC395x_read8(TRM_S1040_DMA_FIFOSTAT), + DC395x_read8(TRM_S1040_SCSI_FIFOCNT)); + printk(DC395X_NAME ": DIP0: DMAStat %02x\n", + DC395x_read8(TRM_S1040_DMA_STATUS)); +#endif + + /* KG: This should not be needed any more! */ + if ((dLeftCounter == 0) + || (scsi_status & SCSIXFERCNT_2_ZERO)) { +#if 0 + int ctr = 6000000; + u8 TempDMAstatus; + do { + TempDMAstatus = + DC395x_read8(TRM_S1040_DMA_STATUS); + } while (!(TempDMAstatus & DMAXFERCOMP) && --ctr); + if (!ctr) + printk(KERN_ERR DC395X_NAME + ": Deadlock in DataInPhase0 waiting for DMA!!\n"); + pSRB->SRBTotalXferLength = 0; +#endif +#if 0 /*def DC395x_DEBUG_KG */ + printk(DC395X_NAME + ": DIP0: DMA not yet ready: %02x: %i -> %i bytes\n", + DC395x_read8(TRM_S1040_DMA_STATUS), + pSRB->SRBTotalXferLength, dLeftCounter); +#endif + pSRB->SRBTotalXferLength = dLeftCounter; + } else { /* phase changed */ + /* + * parsing the case: + * when a transfer not yet complete + * but be disconnected by target + * if transfer not yet complete + * there were some data residue in SCSI FIFO or + * SCSI transfer counter not empty + */ + DC395x_update_SGlist(pSRB, dLeftCounter); + } + } + /* KG: The target may decide to disconnect: Empty FIFO before! */ + if ((*pscsi_status & PHASEMASK) != PH_DATA_IN) { + /*printk (DC395X_NAME ": Debug: Clean up after Data In ...\n"); */ + DC395x_cleanup_after_transfer(pACB, pSRB); + } +#if 0 + /* KG: Make sure, no previous transfers are pending! */ + bval = DC395x_read8(TRM_S1040_SCSI_FIFOCNT); + if (!(bval & 0x40)) { + bval &= 0x1f; + printk(DC395X_NAME + ": DIP0(%li): %i bytes in SCSI FIFO (stat %04x) (left %08x)!!\n", + pSRB->pcmd->pid, bval & 0x1f, scsi_status, + dLeftCounter); + if ((dLeftCounter == 0) + || (scsi_status & SCSIXFERCNT_2_ZERO)) { + printk(DC395X_NAME ": Clear FIFO!\n"); + DC395x_clrfifo(pACB, "DIP0"); + } + } +#endif + /*DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO | ABORTXFER); */ + + /*DC395x_clrfifo (pACB, "DIP0"); */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); */ + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataInPhase1: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =1 + ******************************************************************** + */ +static void +DC395x_DataInPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_DataInPhase1..... "); +#endif + /* FIFO should be cleared, if previous phase was not DataPhase */ + /*DC395x_clrfifo (pACB, "DIP1"); */ + /* Allow data in! */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); */ + TRACEPRINTF("DIP1:*"); + /* + ** do prepare before transfer when data in phase + */ + DC395x_DataIO_transfer(pACB, pSRB, XFERDATAIN); + TRACEPRINTF(".*"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_DataOutPhase1 + * DC395x_DataInPhase1 + ******************************************************************** + */ +void +DC395x_DataIO_transfer(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 ioDir) +{ + u8 bval; + struct DeviceCtlBlk *pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DataIO_transfer %c (pid %li): len = %i, SG: %i/%i\n", + ((ioDir & DMACMD_DIR) ? 'r' : 'w'), pSRB->pcmd->pid, + pSRB->SRBTotalXferLength, pSRB->SRBSGIndex, + pSRB->SRBSGCount); +#endif + TRACEPRINTF("%05x(%i/%i)*", pSRB->SRBTotalXferLength, + pSRB->SRBSGIndex, pSRB->SRBSGCount); + pDCB = pSRB->pSRBDCB; + if (pSRB == pACB->pTmpSRB) { + printk(DC395X_NAME + ": ERROR! Using TmpSRB in DataPhase!\n"); + } + if (pSRB->SRBSGIndex < pSRB->SRBSGCount) { + if (pSRB->SRBTotalXferLength > DC395x_LASTPIO) { + u8 dma_status = DC395x_read8(TRM_S1040_DMA_STATUS); + /* + * KG: What should we do: Use SCSI Cmd 0x90/0x92? + * Maybe, even ABORTXFER would be appropriate + */ + if (dma_status & XFERPENDING) { + printk(DC395X_NAME + ": Xfer pending! Expect trouble!!\n"); + DC395x_dumpinfo(pACB, pDCB, pSRB); + DC395x_write8(TRM_S1040_DMA_CONTROL, + CLRXFIFO); + } + /*DC395x_clrfifo (pACB, "IO"); */ + /* + * load what physical address of Scatter/Gather list table want to be + * transfer + */ + pSRB->SRBState |= SRB_DATA_XFER; + DC395x_write32(TRM_S1040_DMA_XHIGHADDR, 0); + if (pSRB->pcmd->use_sg) { /* with S/G */ + ioDir |= DMACMD_SG; + DC395x_write32(TRM_S1040_DMA_XLOWADDR, + pSRB->SRBSGBusAddr + + sizeof(struct SGentry) * + pSRB->SRBSGIndex); + /* load how many bytes in the Scatter/Gather list table */ + DC395x_write32(TRM_S1040_DMA_XCNT, + ((u32) + (pSRB->SRBSGCount - + pSRB->SRBSGIndex) << 3)); + } else { /* without S/G */ + ioDir &= ~DMACMD_SG; + DC395x_write32(TRM_S1040_DMA_XLOWADDR, + pSRB->SegmentX[0].address); + DC395x_write32(TRM_S1040_DMA_XCNT, + pSRB->SegmentX[0].length); + } + /* load total transfer length (24bits) max value 16Mbyte */ + DC395x_write32(TRM_S1040_SCSI_COUNTER, + pSRB->SRBTotalXferLength); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + if (ioDir & DMACMD_DIR) { /* read */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_DMA_IN); + DC395x_write16(TRM_S1040_DMA_COMMAND, + ioDir); + } else { + DC395x_write16(TRM_S1040_DMA_COMMAND, + ioDir); + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_DMA_OUT); + } + + } +#if DC395x_LASTPIO + else if (pSRB->SRBTotalXferLength > 0) { /* The last four bytes: Do PIO */ + /*DC395x_clrfifo (pACB, "IO"); */ + /* + * load what physical address of Scatter/Gather list table want to be + * transfer + */ + pSRB->SRBState |= SRB_DATA_XFER; + /* load total transfer length (24bits) max value 16Mbyte */ + DC395x_write32(TRM_S1040_SCSI_COUNTER, + pSRB->SRBTotalXferLength); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + if (ioDir & DMACMD_DIR) { /* read */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_FIFO_IN); + } else { /* write */ + int ln = pSRB->SRBTotalXferLength; + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + DC395x_write8 + (TRM_S1040_SCSI_CONFIG2, + CFG2_WIDEFIFO); + DEBUGPIO(printk + (DC395X_NAME + ": DOP1: PIO %i bytes from %p:", + pSRB->SRBTotalXferLength, + pSRB->virt_addr); + ) + while (pSRB->SRBTotalXferLength) { + DEBUGPIO(printk + (" %02x", + (unsigned char) *(pSRB-> + virt_addr)); + ) + DC395x_write8 + (TRM_S1040_SCSI_FIFO, + *(pSRB->virt_addr)++); + pSRB->SRBTotalXferLength--; + pSRB->SegmentX[pSRB->SRBSGIndex]. + length--; + if (pSRB->SRBTotalXferLength + && !pSRB->SegmentX[pSRB-> + SRBSGIndex]. + length) { + DEBUGPIO(printk + (" (next segment)"); + ) + pSRB->SRBSGIndex++; + DC395x_update_SGlist(pSRB, + pSRB-> + SRBTotalXferLength); + } + } + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) { + if (ln % 2) { + DC395x_write8 + (TRM_S1040_SCSI_FIFO, + 0); + DEBUGPIO(printk(" |00"); + ) + } + DC395x_write8 + (TRM_S1040_SCSI_CONFIG2, 0); + } + /*DC395x_write32(TRM_S1040_SCSI_COUNTER, ln); */ + DEBUGPIO(printk("\n"); + ) + DC395x_write8(TRM_S1040_SCSI_COMMAND, + SCMD_FIFO_OUT); + } + } +#endif /* DC395x_LASTPIO */ + else { /* xfer pad */ + + u8 data = 0, data2 = 0; + if (pSRB->SRBSGCount) { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + /* + * KG: despite the fact that we are using 16 bits I/O ops + * the SCSI FIFO is only 8 bits according to the docs + * (we can set bit 1 in 0x8f to serialize FIFO access ...) + */ + if (pDCB->SyncPeriod & WIDE_SYNC) { + DC395x_write32(TRM_S1040_SCSI_COUNTER, 2); + DC395x_write8(TRM_S1040_SCSI_CONFIG2, + CFG2_WIDEFIFO); + if (ioDir & DMACMD_DIR) { /* read */ + data = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + data2 = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + /*printk (DC395X_NAME ": DataIO: Xfer pad: %02x %02x\n", data, data2); */ + } else { + /* Danger, Robinson: If you find KGs scattered over the wide + * disk, the driver or chip is to blame :-( */ + DC395x_write8(TRM_S1040_SCSI_FIFO, + 'K'); + DC395x_write8(TRM_S1040_SCSI_FIFO, + 'G'); + } + DC395x_write8(TRM_S1040_SCSI_CONFIG2, 0); + } else { + DC395x_write32(TRM_S1040_SCSI_COUNTER, 1); + /* Danger, Robinson: If you find a collection of Ks on your disk + * something broke :-( */ + if (ioDir & DMACMD_DIR) { /* read */ + data = + DC395x_read8 + (TRM_S1040_SCSI_FIFO); + /*printk (DC395X_NAME ": DataIO: Xfer pad: %02x\n", data); */ + } else { + DC395x_write8(TRM_S1040_SCSI_FIFO, + 'K'); + } + } + pSRB->SRBState |= SRB_XFERPAD; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + * SCSI command + */ + bval = + (ioDir & DMACMD_DIR) ? SCMD_FIFO_IN : + SCMD_FIFO_OUT; + DC395x_write8(TRM_S1040_SCSI_COMMAND, bval); + } + } + /*DC395x_monitor_next_IRQ = 2; */ + /*printk (" done\n"); */ +} + + +/* + ******************************************************************** + * scsiio + * DC395x_StatusPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =3 + ******************************************************************** + */ +static void +DC395x_StatusPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": StatusPhase0 (pid %li)\n", + pSRB->pcmd->pid); +#endif + TRACEPRINTF("STP0 *"); + pSRB->TargetStatus = DC395x_read8(TRM_S1040_SCSI_FIFO); + pSRB->EndMessage = DC395x_read8(TRM_S1040_SCSI_FIFO); /* get message */ + pSRB->SRBState = SRB_COMPLETED; + *pscsi_status = PH_BUS_FREE; /*.. initial phase */ + /*1.25 */ + /*DC395x_clrfifo (pACB, "STP0"); */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + ** SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_StatusPhase1: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =3 + ******************************************************************** + */ +static void +DC395x_StatusPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": StatusPhase1 (pid=%li)\n", + pSRB->pcmd->pid); +#endif + TRACEPRINTF("STP1 *"); + /* Cleanup is now done at the end of DataXXPhase0 */ + /*DC395x_cleanup_after_transfer (pACB, pSRB); */ + + pSRB->SRBState = SRB_STATUS; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + * SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_COMP); +} + +/* Message handling */ + +#if 0 +/* Print received message */ +static void DC395x_printMsg(u8 * MsgBuf, u32 len) +{ + int i; + printk(" %02x", MsgBuf[0]); + for (i = 1; i < len; i++) + printk(" %02x", MsgBuf[i]); + printk("\n"); +} +#endif + +/* Check if the message is complete */ +static inline u8 DC395x_MsgIn_complete(u8 * msgbuf, u32 len) +{ + if (*msgbuf == EXTENDED_MESSAGE) { + if (len < 2) + return 0; + if (len < msgbuf[1] + 2) + return 0; + } else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) /* two byte messages */ + if (len < 2) + return 0; + return 1; +} + +#define DC395x_ENABLE_MSGOUT \ + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_SETATN); \ + pSRB->SRBState |= SRB_MSGOUT + + +/* reject_msg */ +static inline void +DC395x_MsgIn_reject(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + pSRB->MsgOutBuf[0] = MESSAGE_REJECT; + pSRB->MsgCnt = 1; + DC395x_ENABLE_MSGOUT; + pSRB->SRBState &= ~SRB_MSGIN; + pSRB->SRBState |= SRB_MSGOUT; + printk(KERN_INFO DC395X_NAME + ": Reject message %02x from %02i-%i\n", pSRB->MsgInBuf[0], + pSRB->pSRBDCB->TargetID, pSRB->pSRBDCB->TargetLUN); + TRACEPRINTF("\\*"); +} + + +/* abort command */ +static inline void +DC395x_EnableMsgOut_Abort(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB) +{ + pSRB->MsgOutBuf[0] = ABORT; + pSRB->MsgCnt = 1; + DC395x_ENABLE_MSGOUT; + pSRB->SRBState &= ~SRB_MSGIN; + pSRB->SRBState |= SRB_MSGOUT; + /* + if (pSRB->pSRBDCB) + pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_; + */ + TRACEPRINTF("#*"); +} + + +static struct ScsiReqBlk *DC395x_MsgIn_QTag(struct AdapterCtlBlk *pACB, + struct DeviceCtlBlk *pDCB, + u8 tag) +{ + struct ScsiReqBlk *lastSRB = pDCB->pGoingLast; + struct ScsiReqBlk *pSRB = pDCB->pGoingSRB; +#ifdef DC395x_DEBUG0 + printk(DC395X_NAME ": QTag Msg (SRB %p): %i ", pSRB, tag); +#endif + if (!(pDCB->TagMask & (1 << tag))) + printk(DC395X_NAME + ": MsgIn_QTag: TagMask (%08x) does not reserve tag %i!\n", + pDCB->TagMask, tag); + + if (!pSRB) + goto mingx0; + while (pSRB) { + if (pSRB->TagNumber == tag) + break; + if (pSRB == lastSRB) + goto mingx0; + pSRB = pSRB->pNextSRB; + } +#ifdef DC395x_DEBUG0 + printk("pid %li (%i-%i)\n", pSRB->pcmd->pid, + pSRB->pSRBDCB->TargetID, pSRB->pSRBDCB->TargetLUN); +#endif + if (pDCB->DCBFlag & ABORT_DEV_) { + /*pSRB->SRBState = SRB_ABORT_SENT; */ + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } + + if (!(pSRB->SRBState & SRB_DISCONNECT)) + goto mingx0; + + /* Tag found */ + TRACEPRINTF("[%s]*", pDCB->pActiveSRB->debugtrace); + TRACEPRINTF("RTag*"); + /* Just for debugging ... */ + lastSRB = pSRB; + pSRB = pDCB->pActiveSRB; + TRACEPRINTF("Found.*"); + pSRB = lastSRB; + + memcpy(pSRB->MsgInBuf, pDCB->pActiveSRB->MsgInBuf, pACB->MsgLen); + pSRB->SRBState |= pDCB->pActiveSRB->SRBState; + pSRB->SRBState |= SRB_DATA_XFER; + pDCB->pActiveSRB = pSRB; + /* How can we make the DORS happy? */ + return pSRB; + + mingx0: + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + pSRB->MsgCnt = 1; + DC395x_ENABLE_MSGOUT; + TRACEPRINTF("?*"); + printk(DC395X_NAME ": Unknown tag received: %i: abort !!\n", tag); + return pSRB; +} + + +/* Reprogram registers */ +static inline void +DC395x_reprog(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + DC395x_SetXferRate(pACB, pDCB); +} + + +/* set async transfer mode */ +static void +DC395x_MsgIn_set_async(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + printk(DC395X_NAME ": Target %02i: No sync transfers\n", + pDCB->TargetID); + TRACEPRINTF("!S *"); + pDCB->SyncMode &= ~(SYNC_NEGO_ENABLE); + pDCB->SyncMode |= SYNC_NEGO_DONE; + /*pDCB->SyncPeriod &= 0; */ + pDCB->SyncOffset = 0; + pDCB->MinNegoPeriod = 200 >> 2; /* 200ns <=> 5 MHz */ + pSRB->SRBState &= ~SRB_DO_SYNC_NEGO; + DC395x_reprog(pACB, pDCB); + if ((pDCB->SyncMode & WIDE_NEGO_ENABLE) + && !(pDCB->SyncMode & WIDE_NEGO_DONE)) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk + (DC395X_NAME ": SDTR(rej): Try WDTR anyway ...\n"); + ) + } +} + + +/* set sync transfer mode */ +static void +DC395x_MsgIn_set_sync(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + u8 bval; + int fact; + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + /*u8 oldsyncperiod = pDCB->SyncPeriod; */ + /*u8 oldsyncoffset = pDCB->SyncOffset; */ + +#ifdef DC395x_DEBUG1 + printk(KERN_INFO DC395X_NAME + ": Target %02i: Sync: %ins (%02i.%01i MHz) Offset %i\n", + pDCB->TargetID, pSRB->MsgInBuf[3] << 2, + (250 / pSRB->MsgInBuf[3]), + ((250 % pSRB->MsgInBuf[3]) * 10) / pSRB->MsgInBuf[3], + pSRB->MsgInBuf[4]); +#endif + + if (pSRB->MsgInBuf[4] > 15) + pSRB->MsgInBuf[4] = 15; + if (!(pDCB->DevMode & NTC_DO_SYNC_NEGO)) + pDCB->SyncOffset = 0; + else if (pDCB->SyncOffset == 0) + pDCB->SyncOffset = pSRB->MsgInBuf[4]; + if (pSRB->MsgInBuf[4] > pDCB->SyncOffset) + pSRB->MsgInBuf[4] = pDCB->SyncOffset; + else + pDCB->SyncOffset = pSRB->MsgInBuf[4]; + bval = 0; + while (bval < 7 && (pSRB->MsgInBuf[3] > dc395x_clock_period[bval] + || pDCB->MinNegoPeriod > + dc395x_clock_period[bval])) + bval++; + if (pSRB->MsgInBuf[3] < dc395x_clock_period[bval]) + printk(KERN_INFO DC395X_NAME + ": Increase sync nego period to %ins\n", + dc395x_clock_period[bval] << 2); + pSRB->MsgInBuf[3] = dc395x_clock_period[bval]; + pDCB->SyncPeriod &= 0xf0; + pDCB->SyncPeriod |= ALT_SYNC | bval; + pDCB->MinNegoPeriod = pSRB->MsgInBuf[3]; + + if (pDCB->SyncPeriod & WIDE_SYNC) + fact = 500; + else + fact = 250; + + printk(KERN_INFO DC395X_NAME + ": Target %02i: %s Sync: %ins Offset %i (%02i.%01i MB/s)\n", + pDCB->TargetID, (fact == 500) ? "Wide16" : "", + pDCB->MinNegoPeriod << 2, pDCB->SyncOffset, + (fact / pDCB->MinNegoPeriod), + ((fact % pDCB->MinNegoPeriod) * 10 + + pDCB->MinNegoPeriod / 2) / pDCB->MinNegoPeriod); + + TRACEPRINTF("S%i *", pDCB->MinNegoPeriod << 2); + if (!(pSRB->SRBState & SRB_DO_SYNC_NEGO)) { + /* Reply with corrected SDTR Message */ + printk(DC395X_NAME ": .. answer w/ %ins %i\n", + pSRB->MsgInBuf[3] << 2, pSRB->MsgInBuf[4]); + + memcpy(pSRB->MsgOutBuf, pSRB->MsgInBuf, 5); + pSRB->MsgCnt = 5; + DC395x_ENABLE_MSGOUT; + pDCB->SyncMode |= SYNC_NEGO_DONE; + } else { + if ((pDCB->SyncMode & WIDE_NEGO_ENABLE) + && !(pDCB->SyncMode & WIDE_NEGO_DONE)) { + DC395x_Build_WDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk + (DC395X_NAME ": SDTR: Also try WDTR ...\n"); + ) + } + } + pSRB->SRBState &= ~SRB_DO_SYNC_NEGO; + pDCB->SyncMode |= SYNC_NEGO_DONE | SYNC_NEGO_ENABLE; + + DC395x_reprog(pACB, pDCB); +} + + +static inline void +DC395x_MsgIn_set_nowide(struct AdapterCtlBlk *pACB, + struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": WDTR got rejected from target %02i\n", + pDCB->TargetID); +#endif + TRACEPRINTF("!W *"); + pDCB->SyncPeriod &= ~WIDE_SYNC; + pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE); + pDCB->SyncMode |= WIDE_NEGO_DONE; + pSRB->SRBState &= ~SRB_DO_WIDE_NEGO; + DC395x_reprog(pACB, pDCB); + if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) + && !(pDCB->SyncMode & SYNC_NEGO_DONE)) { + DC395x_Build_SDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk + (DC395X_NAME ": WDTR(rej): Try SDTR anyway ...\n"); + ) + } +} + +static void +DC395x_MsgIn_set_wide(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + struct DeviceCtlBlk *pDCB = pSRB->pSRBDCB; + u8 wide = (pDCB->DevMode & NTC_DO_WIDE_NEGO + && pACB->Config & HCC_WIDE_CARD) ? 1 : 0; + if (pSRB->MsgInBuf[3] > wide) + pSRB->MsgInBuf[3] = wide; + /* Completed */ + if (!(pSRB->SRBState & SRB_DO_WIDE_NEGO)) { + printk(DC395X_NAME + ": Target %02i initiates Wide Nego ...\n", + pDCB->TargetID); + memcpy(pSRB->MsgOutBuf, pSRB->MsgInBuf, 4); + pSRB->MsgCnt = 4; + pSRB->SRBState |= SRB_DO_WIDE_NEGO; + DC395x_ENABLE_MSGOUT; + } + + pDCB->SyncMode |= (WIDE_NEGO_ENABLE | WIDE_NEGO_DONE); + if (pSRB->MsgInBuf[3] > 0) + pDCB->SyncPeriod |= WIDE_SYNC; + else + pDCB->SyncPeriod &= ~WIDE_SYNC; + pSRB->SRBState &= ~SRB_DO_WIDE_NEGO; + TRACEPRINTF("W%i *", (pDCB->SyncPeriod & WIDE_SYNC ? 1 : 0)); + /*pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); */ +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Wide transfers (%i bit) negotiated with target %02i\n", + (8 << pSRB->MsgInBuf[3]), pDCB->TargetID); +#endif + DC395x_reprog(pACB, pDCB); + if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) + && !(pDCB->SyncMode & SYNC_NEGO_DONE)) { + DC395x_Build_SDTR(pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk(DC395X_NAME ": WDTR: Also try SDTR ...\n"); + ) + } +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgInPhase0: one of DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * if phase =7 + * + * extended message codes: + * + * code description + * + * 02h Reserved + * 00h MODIFY DATA POINTER + * 01h SYNCHRONOUS DATA TRANSFER REQUEST + * 03h WIDE DATA TRANSFER REQUEST + * 04h - 7Fh Reserved + * 80h - FFh Vendor specific + * + ******************************************************************** + */ +void +DC395x_MsgInPhase0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + struct DeviceCtlBlk *pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_MsgInPhase0..............\n "); +#endif + TRACEPRINTF("MIP0*"); + pDCB = pACB->pActiveDCB; + + pSRB->MsgInBuf[pACB->MsgLen++] = DC395x_read8(TRM_S1040_SCSI_FIFO); + if (DC395x_MsgIn_complete(pSRB->MsgInBuf, pACB->MsgLen)) { + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[0]); + /*printk (KERN_INFO DC395X_NAME ": MsgIn:"); */ + /*DC395x_printMsg (pSRB->MsgInBuf, pACB->MsgLen); */ + + /* Now eval the msg */ + switch (pSRB->MsgInBuf[0]) { + case DISCONNECT: + pSRB->SRBState = SRB_DISCONNECT; + break; + + case SIMPLE_QUEUE_TAG: + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[1]); + pSRB = + DC395x_MsgIn_QTag(pACB, pDCB, + pSRB->MsgInBuf[1]); + break; + + case MESSAGE_REJECT: + DC395x_write16(TRM_S1040_SCSI_CONTROL, + DO_CLRATN | DO_DATALATCH); + /* A sync nego message was rejected ! */ + if (pSRB->SRBState & SRB_DO_SYNC_NEGO) { + DC395x_MsgIn_set_async(pACB, pSRB); + break; + } + /* A wide nego message was rejected ! */ + if (pSRB->SRBState & SRB_DO_WIDE_NEGO) { + DC395x_MsgIn_set_nowide(pACB, pSRB); + break; + } + DC395x_EnableMsgOut_Abort(pACB, pSRB); + /*pSRB->SRBState |= SRB_ABORT_SENT */ + break; + + case EXTENDED_MESSAGE: + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[2]); + /* SDTR */ + if (pSRB->MsgInBuf[1] == 3 + && pSRB->MsgInBuf[2] == EXTENDED_SDTR) { + DC395x_MsgIn_set_sync(pACB, pSRB); + break; + } + /* WDTR */ + if (pSRB->MsgInBuf[1] == 2 && pSRB->MsgInBuf[2] == EXTENDED_WDTR && pSRB->MsgInBuf[3] <= 2) { /* sanity check ... */ + DC395x_MsgIn_set_wide(pACB, pSRB); + break; + } + DC395x_MsgIn_reject(pACB, pSRB); + break; + + /* Discard wide residual */ + case MSG_IGNOREWIDE: + DEBUG0(printk + (DC395X_NAME ": Ignore Wide Residual!\n"); + ) + /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 1); */ + /*DC395x_read8 (TRM_S1040_SCSI_FIFO); */ + break; + + /* nothing has to be done */ + case COMMAND_COMPLETE: + break; + + /* + * SAVE POINTER may be ignored as we have the struct ScsiReqBlk* associated with the + * scsi command. Thanks, Gérard, for pointing it out. + */ + case SAVE_POINTERS: +#ifdef DC395x_DEBUG0 + printk(DC395X_NAME + ": SAVE POINTER message received (pid %li: rem.%i) ... ignore :-(\n", + pSRB->pcmd->pid, pSRB->SRBTotalXferLength); +#endif + /*pSRB->Saved_Ptr = pSRB->TotalXferredLen; */ + break; + /* The device might want to restart transfer with a RESTORE */ + case RESTORE_POINTERS: + printk(DC395X_NAME + ": RESTORE POINTER message received ... ignore :-(\n"); + /*dc395x_restore_ptr (pACB, pSRB); */ + break; + case ABORT: + printk(DC395X_NAME + ": ABORT msg received (pid %li %02i-%i)\n", + pSRB->pcmd->pid, pDCB->TargetID, + pDCB->TargetLUN); + pDCB->DCBFlag |= ABORT_DEV_; + DC395x_EnableMsgOut_Abort(pACB, pSRB); + break; + /* reject unknown messages */ + default: + if (pSRB->MsgInBuf[0] & IDENTIFY_BASE) { + printk(DC395X_NAME + ": Identify Message received?\n"); + /*TRACEOUT (" %s\n", pSRB->debugtrace); */ + pSRB->MsgCnt = 1; + pSRB->MsgOutBuf[0] = pDCB->IdentifyMsg; + DC395x_ENABLE_MSGOUT; + pSRB->SRBState |= SRB_MSGOUT; + /*break; */ + } + DC395x_MsgIn_reject(pACB, pSRB); + TRACEOUT(" %s\n", pSRB->debugtrace); + } + TRACEPRINTF(".*"); + + /* Clear counter and MsgIn state */ + pSRB->SRBState &= ~SRB_MSGIN; + pACB->MsgLen = 0; + } + + /*1.25 */ + if ((*pscsi_status & PHASEMASK) != PH_MSG_IN) +#if 0 + DC395x_clrfifo(pACB, "MIP0_"); +#else + TRACEPRINTF("N/Cln *"); +#endif + *pscsi_status = PH_BUS_FREE; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important ... you know! */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgInPhase1: one of DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =7 + ******************************************************************** + */ +static void +DC395x_MsgInPhase1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_MsgInPhase1..............\n "); +#endif + TRACEPRINTF("MIP1 *"); + DC395x_clrfifo(pACB, "MIP1"); + DC395x_write32(TRM_S1040_SCSI_COUNTER, 1); + if (!(pSRB->SRBState & SRB_MSGIN)) { + pSRB->SRBState &= ~SRB_DISCONNECT; + pSRB->SRBState |= SRB_MSGIN; + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* + * SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Nop0: one of DC395x_SCSI_phase1[] ,DC395x_SCSI_phase0[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =4 ..PH_BUS_FREE + ******************************************************************** + */ +static void +DC395x_Nop0(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + /*TRACEPRINTF("NOP0 *"); */ +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Nop1: one of DC395x_SCSI_phase0[] ,DC395x_SCSI_phase1[] vectors + * DC395x_stateV = (void *)DC395x_SCSI_phase0[phase] + * DC395x_stateV = (void *)DC395x_SCSI_phase1[phase] + * if phase =5 + ******************************************************************** + */ +static void +DC395x_Nop1(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB, + u16 * pscsi_status) +{ + /*TRACEPRINTF("NOP1 *"); */ +} + + +/* + ******************************************************************** + * scsiio + * DC395x_MsgInPhase0 + ******************************************************************** + */ +static void +DC395x_SetXferRate(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + u8 bval; + u16 cnt, i; + struct DeviceCtlBlk *pDCBTemp; + + /* + ** set all lun device's period , offset + */ + if (!(pDCB->IdentifyMsg & 0x07)) { + if (pACB->scan_devices) + DC395x_CurrSyncOffset = pDCB->SyncOffset; + else { + pDCBTemp = pACB->pLinkDCB; + cnt = pACB->DCBCnt; + bval = pDCB->TargetID; + for (i = 0; i < cnt; i++) { + if (pDCBTemp->TargetID == bval) { + pDCBTemp->SyncPeriod = + pDCB->SyncPeriod; + pDCBTemp->SyncOffset = + pDCB->SyncOffset; + pDCBTemp->SyncMode = + pDCB->SyncMode; + pDCBTemp->MinNegoPeriod = + pDCB->MinNegoPeriod; + } + pDCBTemp = pDCBTemp->pNextDCB; + } + } + } + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Interrupt + ******************************************************************** + */ +void DC395x_Disconnect(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": Disconnect (pid=%li)\n", + pACB->pActiveDCB->pActiveSRB->pcmd->pid); +#endif + pDCB = pACB->pActiveDCB; + if (!pDCB) { + printk(KERN_ERR DC395X_NAME + ": Disc: Exception Disconnect pDCB=NULL !!\n "); + udelay(500); + /* Suspend queue for a while */ + pACB->pScsiHost->last_reset = + jiffies + HZ / 2 + + HZ * + dc395x_trm_eepromBuf[pACB->AdapterIndex]. + NvramDelayTime; + DC395x_clrfifo(pACB, "DiscEx"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + return; + } + pSRB = pDCB->pActiveSRB; + pACB->pActiveDCB = 0; + TRACEPRINTF("DISC *"); + + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + DC395x_clrfifo(pACB, "Disc"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + if (pSRB->SRBState & SRB_UNEXPECT_RESEL) { + printk(KERN_ERR DC395X_NAME + ": Disc: Unexpected Reselection (%i-%i)\n", + pDCB->TargetID, pDCB->TargetLUN); + pSRB->SRBState = 0; + DC395x_Waiting_process(pACB); + } else if (pSRB->SRBState & SRB_ABORT_SENT) { + /*Scsi_Cmnd* pcmd = pSRB->pcmd; */ + pDCB->DCBFlag &= ~ABORT_DEV_; + pACB->pScsiHost->last_reset = jiffies + HZ / 2 + 1; + printk(KERN_ERR DC395X_NAME ": Disc: SRB_ABORT_SENT!\n"); + DC395x_DoingSRB_Done(pACB, DID_ABORT, pSRB->pcmd, 1); + DC395x_Query_to_Waiting(pACB); + DC395x_Waiting_process(pACB); + } else { + if ((pSRB->SRBState & (SRB_START_ + SRB_MSGOUT)) + || !(pSRB-> + SRBState & (SRB_DISCONNECT + SRB_COMPLETED))) { + /* + * Selection time out + * SRB_START_ || SRB_MSGOUT || (!SRB_DISCONNECT && !SRB_COMPLETED) + */ + /* Unexp. Disc / Sel Timeout */ + if (pSRB->SRBState != SRB_START_ + && pSRB->SRBState != SRB_MSGOUT) { + pSRB->SRBState = SRB_READY; + printk(DC395X_NAME + ": Unexpected Disconnection (pid %li)!\n", + pSRB->pcmd->pid); + pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; + TRACEPRINTF("UnExpD *"); + TRACEOUT("%s\n", pSRB->debugtrace); + goto disc1; + } else { + /* Normal selection timeout */ + TRACEPRINTF("SlTO *"); +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Disc: SelTO (pid=%li) for dev %02i-%i\n", + pSRB->pcmd->pid, pDCB->TargetID, + pDCB->TargetLUN); +#endif + if (pSRB->RetryCnt++ > DC395x_MAX_RETRIES + || pACB->scan_devices) { + pSRB->TargetStatus = + SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } + DC395x_freetag(pDCB, pSRB); + DC395x_Going_to_Waiting(pDCB, pSRB); +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME ": Retry pid %li ...\n", + pSRB->pcmd->pid); +#endif + DC395x_waiting_timer(pACB, HZ / 20); + } + } else if (pSRB->SRBState & SRB_DISCONNECT) { + u8 bval = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + /* + * SRB_DISCONNECT (This is what we expect!) + */ + /* printk (DC395X_NAME ": DoWaitingSRB (pid=%li)\n", pSRB->pcmd->pid); */ + TRACEPRINTF("+*"); + if (bval & 0x40) { + DEBUG0(printk + (DC395X_NAME + ": Debug: DISC: SCSI bus stat %02x: ACK set! Other controllers?\n", + bval); + ) + /* It could come from another initiator, therefore don't do much ! */ + TRACEPRINTF("ACK(%02x) *", bval); + /*DC395x_dumpinfo (pACB, pDCB, pSRB); */ + /*TRACEOUT (" %s\n", pSRB->debugtrace); */ + /*pDCB->DCBFlag |= ABORT_DEV_; */ + /*DC395x_EnableMsgOut_Abort (pACB, pSRB); */ + /*DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_CLRFIFO | DO_CLRATN | DO_HWRESELECT); */ + } else + DC395x_Waiting_process(pACB); + } else if (pSRB->SRBState & SRB_COMPLETED) { + disc1: + /* + ** SRB_COMPLETED + */ + DC395x_freetag(pDCB, pSRB); + pDCB->pActiveSRB = 0; + pSRB->SRBState = SRB_FREE; + /*printk (DC395X_NAME ": done (pid=%li)\n", pSRB->pcmd->pid); */ + DC395x_SRBdone(pACB, pDCB, pSRB); + } + } + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Reselect + ******************************************************************** + */ +void DC395x_Reselect(struct AdapterCtlBlk *pACB) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB = 0; + u16 RselTarLunId; + u8 id, lun; + u8 arblostflag = 0; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_Reselect..............\n "); +#endif + + DC395x_clrfifo(pACB, "Resel"); + /*DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); */ + /* Read Reselected Target ID and LUN */ + RselTarLunId = DC395x_read16(TRM_S1040_SCSI_TARGETID); + pDCB = pACB->pActiveDCB; + if (pDCB) { /* Arbitration lost but Reselection win */ + pSRB = pDCB->pActiveSRB; + if (!pSRB) { + printk(DC395X_NAME + ": Arb lost Resel won, but pActiveSRB == 0!\n"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + return; + } + /* Why the if ? */ + if (!(pACB->scan_devices)) { +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Arb lost but Resel win pid %li (%02i-%i) Rsel %04x Stat %04x\n", + pSRB->pcmd->pid, pDCB->TargetID, + pDCB->TargetLUN, RselTarLunId, + DC395x_read16(TRM_S1040_SCSI_STATUS)); +#endif + TRACEPRINTF("ArbLResel!*"); + /*TRACEOUT (" %s\n", pSRB->debugtrace); */ + arblostflag = 1; + /*pSRB->SRBState |= SRB_DISCONNECT; */ + + pSRB->SRBState = SRB_READY; + DC395x_freetag(pDCB, pSRB); + DC395x_Going_to_Waiting(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 20); + + /* return; */ + } + } + /* Read Reselected Target Id and LUN */ + if (!(RselTarLunId & (IDENTIFY_BASE << 8))) + printk(DC395X_NAME + ": Resel expects identify msg! Got %04x!\n", + RselTarLunId); + id = RselTarLunId & 0xff; + lun = (RselTarLunId >> 8) & 7; + pDCB = DC395x_findDCB(pACB, id, lun); + if (!pDCB) { + printk(KERN_ERR DC395X_NAME + ": Reselect from non existing device (%02i-%i)\n", + id, lun); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + return; + } + + pACB->pActiveDCB = pDCB; + + if (!(pDCB->DevMode & NTC_DO_DISCONNECT)) + printk(DC395X_NAME + ": Reselection in spite of forbidden disconnection? (%02i-%i)\n", + pDCB->TargetID, pDCB->TargetLUN); + + if ((pDCB->SyncMode & EN_TAG_QUEUEING) /*&& !arblostflag */ ) { + struct ScsiReqBlk *oldSRB = pSRB; + pSRB = pACB->pTmpSRB; +#ifdef DC395x_DEBUGTRACE + pSRB->debugpos = 0; + pSRB->debugtrace[0] = 0; +#endif + pDCB->pActiveSRB = pSRB; + if (oldSRB) + TRACEPRINTF("ArbLResel(%li):*", oldSRB->pcmd->pid); + /*if (arblostflag) printk (DC395X_NAME ": Reselect: Wait for Tag ... \n"); */ + } else { + /* There can be only one! */ + pSRB = pDCB->pActiveSRB; + if (pSRB) + TRACEPRINTF("RSel *"); + if (!pSRB || !(pSRB->SRBState & SRB_DISCONNECT)) { + /* + * abort command + */ + printk(DC395X_NAME + ": Reselected w/o disconnected cmds from %02i-%i?\n", + pDCB->TargetID, pDCB->TargetLUN); + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } else { + if (pDCB->DCBFlag & ABORT_DEV_) { + /*pSRB->SRBState = SRB_ABORT_SENT; */ + DC395x_EnableMsgOut_Abort(pACB, pSRB); + } else + pSRB->SRBState = SRB_DATA_XFER; + + } + /*if (arblostflag) TRACEOUT (" %s\n", pSRB->debugtrace); */ + } + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + /* + *********************************************** + ** Program HA ID, target ID, period and offset + *********************************************** + */ + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); /* host ID */ + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); /* target ID */ + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); /* offset */ + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); /* sync period, wide */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ + /* SCSI command */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* Dynamic device handling */ + +/* Remove dev (and DCB) */ +static void +DC395x_remove_dev(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + struct DeviceCtlBlk *pPrevDCB = pACB->pLinkDCB; + + if (pDCB->GoingSRBCnt > 1) { + DCBDEBUG(printk + (KERN_INFO DC395X_NAME + ": Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n", + pDCB->TargetID, pDCB->TargetLUN, (int) pDCB, + pDCB->GoingSRBCnt); + ) + return; + } + pACB->DCBmap[pDCB->TargetID] &= ~(1 << pDCB->TargetLUN); + pACB->children[pDCB->TargetID][pDCB->TargetLUN] = NULL; + + /* The first one */ + if (pDCB == pACB->pLinkDCB) { + /* The last one */ + if (pACB->pLastDCB == pDCB) { + pDCB->pNextDCB = 0; + pACB->pLastDCB = 0; + } + pACB->pLinkDCB = pDCB->pNextDCB; + } else { + while (pPrevDCB->pNextDCB != pDCB) + pPrevDCB = pPrevDCB->pNextDCB; + pPrevDCB->pNextDCB = pDCB->pNextDCB; + if (pDCB == pACB->pLastDCB) + pACB->pLastDCB = pPrevDCB; + } + + DCBDEBUG(printk + (KERN_INFO DC395X_NAME + ": Driver about to free DCB (ID %i, LUN %i): %p\n", + pDCB->TargetID, pDCB->TargetLUN, pDCB); + ) + if (pDCB == pACB->pActiveDCB) + pACB->pActiveDCB = 0; + if (pDCB == pACB->pLinkDCB) + pACB->pLinkDCB = pDCB->pNextDCB; + if (pDCB == pACB->pDCBRunRobin) + pACB->pDCBRunRobin = pDCB->pNextDCB; + pACB->DCBCnt--; + KFREE(pDCB); + /* pACB->DeviceCnt--; */ +} + + +static inline u8 DC395x_tagq_blacklist(char *name) +{ +#ifndef DC395x_NO_TAGQ +#if 0 + u8 i; + for (i = 0; i < BADDEVCNT; i++) + if (memcmp(name, DC395x_baddevname1[i], 28) == 0) + return 1; +#endif + return 0; +#else + return 1; +#endif +} + + +static void +DC395x_disc_tagq_set(struct DeviceCtlBlk *pDCB, struct ScsiInqData *ptr) +{ + /* Check for SCSI format (ANSI and Response data format) */ + if ((ptr->Vers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2) { + if ((ptr->Flags & SCSI_INQ_CMDQUEUE) + && (pDCB->DevMode & NTC_DO_TAG_QUEUEING) && + /*(pDCB->DevMode & NTC_DO_DISCONNECT) */ + /* ((pDCB->DevType == TYPE_DISK) + || (pDCB->DevType == TYPE_MOD)) && */ + !DC395x_tagq_blacklist(((char *) ptr) + 8)) { + if (pDCB->MaxCommand == 1) + pDCB->MaxCommand = + pDCB->pDCBACB->TagMaxNum; + pDCB->SyncMode |= EN_TAG_QUEUEING; + /*pDCB->TagMask = 0; */ + } else + pDCB->MaxCommand = 1; + } +} + + +static void +DC395x_add_dev(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiInqData *ptr) +{ + u8 bval1 = ptr->DevType & SCSI_DEVTYPE; + pDCB->DevType = bval1; + /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */ + DC395x_disc_tagq_set(pDCB, ptr); +} + + +/* + ******************************************************************** + * unmap mapped pci regions from SRB + ******************************************************************** + */ +static void +DC395x_pci_unmap(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + int dir; + Scsi_Cmnd *pcmd = pSRB->pcmd; + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + if (pcmd->use_sg && dir != PCI_DMA_NONE) { + /* unmap DC395x SG list */ +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME + ": Unmap SG descriptor list %08x (%05x)\n", + pSRB->SRBSGBusAddr, + sizeof(struct SGentry) * DC395x_MAX_SG_LISTENTRY); +#endif + pci_unmap_single(pACB->pdev, pSRB->SRBSGBusAddr, + sizeof(struct SGentry) * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Unmap %i SG segments from %p\n", + pcmd->use_sg, pcmd->request_buffer); +#endif + /* unmap the sg segments */ + pci_unmap_sg(pACB->pdev, + (struct scatterlist *) pcmd->request_buffer, + pcmd->use_sg, dir); + } else if (pcmd->request_buffer && dir != PCI_DMA_NONE) { +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Unmap buffer at %08x (%05x)\n", + pSRB->SegmentX[0].address, pcmd->request_bufflen); +#endif + pci_unmap_single(pACB->pdev, pSRB->SegmentX[0].address, + pcmd->request_bufflen, dir); + } +} + + +/* + ******************************************************************** + * unmap mapped pci sense buffer from SRB + ******************************************************************** + */ +static void +DC395x_pci_unmap_sense(struct AdapterCtlBlk *pACB, struct ScsiReqBlk *pSRB) +{ + if (!(pSRB->SRBFlag & AUTO_REQSENSE)) + return; + /* Unmap sense buffer */ +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Unmap sense buffer from %08x (%05x)\n", + pSRB->SegmentX[0].address, sizeof(pcmd->sense_buffer)); +#endif + pci_unmap_single(pACB->pdev, pSRB->SegmentX[0].address, + pSRB->SegmentX[0].length, PCI_DMA_FROMDEVICE); + /* Restore SG stuff */ + /*printk ("Auto_ReqSense finished: Restore Counters ...\n"); */ + pSRB->SRBTotalXferLength = pSRB->Xferred; + pSRB->SegmentX[0].address = + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].address; + pSRB->SegmentX[0].length = + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].length; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Disconnected + * Complete execution of a SCSI command + * Signal completion to the generic SCSI driver + ******************************************************************** + */ +void +DC395x_SRBdone(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + u8 tempcnt, status; + Scsi_Cmnd *pcmd; + struct ScsiInqData *ptr; + /*u32 drv_flags=0; */ + int dir; + + pcmd = pSRB->pcmd; + TRACEPRINTF("DONE *"); + + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + ptr = (struct ScsiInqData *) (pcmd->request_buffer); + if (pcmd->use_sg) + ptr = + (struct ScsiInqData *) CPU_ADDR(*(struct scatterlist *) + ptr); +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO DC395X_NAME + ": SRBdone SG=%i (%i/%i), req_buf = %p, adr = %p\n", + pcmd->use_sg, pSRB->SRBSGIndex, pSRB->SRBSGCount, + pcmd->request_buffer, ptr); +#endif +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO DC395X_NAME + ": SRBdone (pid %li, target %02i-%i): ", pSRB->pcmd->pid, + pSRB->pcmd->device->id, pSRB->pcmd->device->lun); +#endif + status = pSRB->TargetStatus; + if (pSRB->SRBFlag & AUTO_REQSENSE) { +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "AUTO_REQSENSE1..............\n "); +#endif + DC395x_pci_unmap_sense(pACB, pSRB); + /* + ** target status.......................... + */ + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = CHECK_CONDITION << 1; +#ifdef DC395x_DEBUG_KG + switch (pcmd->sense_buffer[2] & 0x0f) { + case NOT_READY: + printk + ("\nDC395x: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case UNIT_ATTENTION: + printk + ("\nDC395x: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case ILLEGAL_REQUEST: + printk + ("\nDC395x: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case MEDIUM_ERROR: + printk + ("\nDC395x: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + case HARDWARE_ERROR: + printk + ("\nDC395x: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, + pDCB->TargetLUN, status, pACB->scan_devices); + break; + } + if (pcmd->sense_buffer[7] >= 6) + printk + ("\nDC395x: Sense=%02x, ASC=%02x, ASCQ=%02x (%08x %08x) ", + pcmd->sense_buffer[2], pcmd->sense_buffer[12], + pcmd->sense_buffer[13], + *((unsigned int *) (pcmd->sense_buffer + 3)), + *((unsigned int *) (pcmd->sense_buffer + 8))); + else + printk + ("\nDC395x: Sense=%02x, No ASC/ASCQ (%08x) ", + pcmd->sense_buffer[2], + *((unsigned int *) (pcmd->sense_buffer + 3))); +#endif + + if (status == (CHECK_CONDITION << 1)) { + pcmd->result = DID_BAD_TARGET << 16; + goto ckc_e; + } +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "AUTO_REQSENSE2..............\n "); +#endif + + if ((pSRB->SRBTotalXferLength) + && (pSRB->SRBTotalXferLength >= pcmd->underflow)) + pcmd->result = + MK_RES_LNX(DRIVER_SENSE, DID_OK, + pSRB->EndMessage, CHECK_CONDITION); + /*SET_RES_DID(pcmd->result,DID_OK) */ + else + pcmd->result = + MK_RES_LNX(DRIVER_SENSE, DID_OK, + pSRB->EndMessage, CHECK_CONDITION); + + goto ckc_e; + } + +/*************************************************************/ + if (status) { + /* + * target status.......................... + */ + if (status_byte(status) == CHECK_CONDITION) { + DC395x_RequestSense(pACB, pDCB, pSRB); + return; + } else if (status_byte(status) == QUEUE_FULL) { + tempcnt = (u8) pDCB->GoingSRBCnt; + printk + ("\nDC395x: QUEUE_FULL for dev %02i-%i with %i cmnds\n", + pDCB->TargetID, pDCB->TargetLUN, tempcnt); + if (tempcnt > 1) + tempcnt--; + pDCB->MaxCommand = tempcnt; + DC395x_freetag(pDCB, pSRB); + DC395x_Going_to_Waiting(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 20); + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + return; + } else if (status == SCSI_STAT_SEL_TIMEOUT) { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + pcmd->result = DID_NO_CONNECT << 16; + } else { + pSRB->AdaptStatus = 0; + SET_RES_DID(pcmd->result, DID_ERROR); + SET_RES_MSG(pcmd->result, pSRB->EndMessage); + SET_RES_TARGET(pcmd->result, status); + + } + } else { + /* + ** process initiator status.......................... + */ + status = pSRB->AdaptStatus; + if (status & H_OVER_UNDER_RUN) { + pSRB->TargetStatus = 0; + SET_RES_DID(pcmd->result, DID_OK); + SET_RES_MSG(pcmd->result, pSRB->EndMessage); + } else if (pSRB->SRBStatus & PARITY_ERROR) { + SET_RES_DID(pcmd->result, DID_PARITY); + SET_RES_MSG(pcmd->result, pSRB->EndMessage); + } else { /* No error */ + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + SET_RES_DID(pcmd->result, DID_OK); + } + } + + if (dir != PCI_DMA_NONE) { + if (pcmd->use_sg) + pci_dma_sync_sg(pACB->pdev, + (struct scatterlist *) pcmd-> + request_buffer, pcmd->use_sg, dir); + else if (pcmd->request_buffer) + pci_dma_sync_single(pACB->pdev, + pSRB->SegmentX[0].address, + pcmd->request_bufflen, dir); + } + + if ((pcmd->result & RES_DID) == 0 && pcmd->cmnd[0] == INQUIRY + && pcmd->cmnd[2] == 0 && pcmd->request_bufflen >= 8 + && dir != PCI_DMA_NONE && ptr && (ptr->Vers & 0x07) >= 2) + pDCB->Inquiry7 = ptr->Flags; +/* Check Error Conditions */ + ckc_e: + + /*if( pSRB->pcmd->cmnd[0] == INQUIRY && */ + /* (host_byte(pcmd->result) == DID_OK || status_byte(pcmd->result) & CHECK_CONDITION) ) */ + if (pcmd->cmnd[0] == INQUIRY && (pcmd->result == (DID_OK << 16) + || status_byte(pcmd-> + result) & + CHECK_CONDITION)) { + + if (!pDCB->init_TCQ_flag) { + DC395x_add_dev(pACB, pDCB, ptr); + pDCB->init_TCQ_flag = 1; + } + + } + + + /* Here is the info for Doug Gilbert's sg3 ... */ + pcmd->resid = pSRB->SRBTotalXferLength; + /* This may be interpreted by sb. or not ... */ + pcmd->SCp.this_residual = pSRB->SRBTotalXferLength; + pcmd->SCp.buffers_residual = 0; +#ifdef DC395x_DEBUG_KG + if (pSRB->SRBTotalXferLength) + printk + ("\nDC395x: pid %li: %02x (%02i-%i): Missed %i bytes\n", + pcmd->pid, pcmd->cmnd[0], pcmd->device->id, + pcmd->device->lun, pSRB->SRBTotalXferLength); +#endif + + DC395x_Going_remove(pDCB, pSRB, 0); + /* Add to free list */ + if (pSRB == pACB->pTmpSRB) + printk("\nDC395x: ERROR! Completed Cmnd with TmpSRB!\n"); + else + DC395x_Free_insert(pACB, pSRB); + + DEBUG0(printk + (KERN_DEBUG DC395X_NAME ": SRBdone: done pid %li\n", + pcmd->pid); + ) +#ifdef DC395x_DEBUG_KG + printk(" 0x%08x\n", pcmd->result); +#endif + TRACEPRINTF("%08x(%li)*", pcmd->result, jiffies); + DC395x_pci_unmap(pACB, pSRB); + /*DC395x_UNLOCK_ACB_NI; */ + pcmd->scsi_done(pcmd); + /*DC395x_LOCK_ACB_NI; */ + TRACEOUTALL(KERN_INFO " %s\n", pSRB->debugtrace); + + DC395x_Query_to_Waiting(pACB); + DC395x_Waiting_process(pACB); + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_reset + * abort all cmds in our queues + ******************************************************************** + */ +void +DC395x_DoingSRB_Done(struct AdapterCtlBlk *pACB, u8 did_flag, + Scsi_Cmnd * cmd, u8 force) +{ + struct DeviceCtlBlk *pDCB; + struct ScsiReqBlk *pSRB; + struct ScsiReqBlk *pSRBTemp; + u16 cnt; + Scsi_Cmnd *pcmd; + + pDCB = pACB->pLinkDCB; + if (!pDCB) + return; + printk(KERN_INFO DC395X_NAME ": DC395x_DoingSRB_Done: pids "); + do { + /* As the ML may queue cmnds again, cache old values */ + struct ScsiReqBlk *pWaitingSRB = pDCB->pWaitingSRB; + /*struct ScsiReqBlk* pWaitLast = pDCB->pWaitLast; */ + u16 WaitSRBCnt = pDCB->WaitSRBCnt; + /* Going queue */ + cnt = pDCB->GoingSRBCnt; + pSRB = pDCB->pGoingSRB; + while (cnt--) { + int result; + int dir; + pSRBTemp = pSRB->pNextSRB; + pcmd = pSRB->pcmd; + dir = scsi_to_pci_dma_dir(pcmd->sc_data_direction); + result = MK_RES(0, did_flag, 0, 0); + /*result = MK_RES(0,DID_RESET,0,0); */ + TRACEPRINTF("Reset(%li):%08x*", jiffies, result); + printk(" (G)"); +#if 1 /*ndef DC395x_DEBUGTRACE */ + printk("%li(%02i-%i) ", pcmd->pid, + pcmd->device->id, pcmd->device->lun); +#endif + TRACEOUT("%s\n", pSRB->debugtrace); + pDCB->pGoingSRB = pSRBTemp; + pDCB->GoingSRBCnt--; + if (!pSRBTemp) + pDCB->pGoingLast = NULL; + DC395x_freetag(pDCB, pSRB); + DC395x_Free_insert(pACB, pSRB); + pcmd->result = result; + DC395x_pci_unmap_sense(pACB, pSRB); + DC395x_pci_unmap(pACB, pSRB); + if (force) { + /* For new EH, we normally don't need to give commands back, + * as they all complete or all time out */ + /* do we need the aic7xxx hack and conditionally decrease retry ? */ + /*DC395x_SCSI_DONE_ACB_UNLOCK; */ + pcmd->scsi_done(pcmd); + /*DC395x_SCSI_DONE_ACB_LOCK; */ + } + pSRB = pSRBTemp; + } + if (pDCB->pGoingSRB) + printk(DC395X_NAME + ": How could the ML send cmnds to the Going queue? (%02i-%i)!!\n", + pDCB->TargetID, pDCB->TargetLUN); + if (pDCB->TagMask) + printk(DC395X_NAME + ": TagMask for %02i-%i should be empty, is %08x!\n", + pDCB->TargetID, pDCB->TargetLUN, + pDCB->TagMask); + /*pDCB->GoingSRBCnt = 0;; */ + /*pDCB->pGoingSRB = NULL; pDCB->pGoingLast = NULL; */ + + /* Waiting queue */ + cnt = WaitSRBCnt; + pSRB = pWaitingSRB; + while (cnt--) { + int result; + pSRBTemp = pSRB->pNextSRB; + pcmd = pSRB->pcmd; + result = MK_RES(0, did_flag, 0, 0); + TRACEPRINTF("Reset(%li):%08x*", jiffies, result); + printk(" (W)"); +#if 1 /*ndef DC395x_DEBUGTRACE */ + printk("%li(%i-%i)", pcmd->pid, pcmd->device->id, + pcmd->device->lun); +#endif + TRACEOUT("%s\n", pSRB->debugtrace); + pDCB->pWaitingSRB = pSRBTemp; + pDCB->WaitSRBCnt--; + if (!pSRBTemp) + pDCB->pWaitLast = NULL; + DC395x_Free_insert(pACB, pSRB); + + pcmd->result = result; + DC395x_pci_unmap_sense(pACB, pSRB); + DC395x_pci_unmap(pACB, pSRB); + if (force) { + /* For new EH, we normally don't need to give commands back, + * as they all complete or all time out */ + /* do we need the aic7xxx hack and conditionally decrease retry ? */ + /*DC395x_SCSI_DONE_ACB_UNLOCK; */ + pcmd->scsi_done(pcmd); + /*DC395x_SCSI_DONE_ACB_LOCK; */ + pSRB = pSRBTemp; + } + } + if (pDCB->WaitSRBCnt) + printk + ("\nDC395x: Debug: ML queued %i cmnds again to %02i-%i\n", + pDCB->WaitSRBCnt, pDCB->TargetID, + pDCB->TargetLUN); + /* The ML could have queued the cmnds again! */ + /*pDCB->WaitSRBCnt = 0;; */ + /*pDCB->pWaitingSRB = NULL; pDCB->pWaitLast = NULL; */ + pDCB->DCBFlag &= ~ABORT_DEV_; + pDCB = pDCB->pNextDCB; + } + while (pDCB != pACB->pLinkDCB && pDCB); + printk("\n"); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_shutdown DC395x_reset + ******************************************************************** + */ +static void DC395x_ResetSCSIBus(struct AdapterCtlBlk *pACB) +{ + /*u32 drv_flags=0; */ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": DC395x_ResetSCSIBus..............\n "); +#endif + + /*DC395x_DRV_LOCK(drv_flags); */ + pACB->ACBFlag |= RESET_DEV; /* RESET_DETECT, RESET_DONE, RESET_DEV */ + + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); + while (!(DC395x_read8(TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET)); + + /*DC395x_DRV_UNLOCK(drv_flags); */ + return; +} + + +/* Set basic config */ +static void DC395x_basic_config(struct AdapterCtlBlk *pACB) +{ + u8 bval; + u16 wval; + DC395x_write8(TRM_S1040_SCSI_TIMEOUT, pACB->sel_timeout); + if (pACB->Config & HCC_PARITY) + bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK; + else + bval = PHASELATCH | INITIATOR | BLOCKRST; + + DC395x_write8(TRM_S1040_SCSI_CONFIG0, bval); + + /* program configuration 1: Act_Neg (+ Act_Neg_Enh? + Fast_Filter? + DataDis?) */ + DC395x_write8(TRM_S1040_SCSI_CONFIG1, 0x03); /* was 0x13: default */ + /* program Host ID */ + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); + /* set ansynchronous transfer */ + DC395x_write8(TRM_S1040_SCSI_OFFSET, 0x00); + /* Turn LED control off */ + wval = DC395x_read16(TRM_S1040_GEN_CONTROL) & 0x7F; + DC395x_write16(TRM_S1040_GEN_CONTROL, wval); + /* DMA config */ + wval = DC395x_read16(TRM_S1040_DMA_CONFIG) & ~DMA_FIFO_CTRL; + wval |= + DMA_FIFO_HALF_HALF | DMA_ENHANCE /*| DMA_MEM_MULTI_READ */ ; + /*printk (KERN_INFO DC395X_NAME "DMA_Config: %04x\n", wval); */ + DC395x_write16(TRM_S1040_DMA_CONFIG, wval); + /* Clear pending interrupt status */ + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + /* Enable SCSI interrupt */ + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x7F); + DC395x_write8(TRM_S1040_DMA_INTEN, EN_SCSIINTR | EN_DMAXFERERROR + /*| EN_DMAXFERABORT | EN_DMAXFERCOMP | EN_FORCEDMACOMP */ + ); +} + + +/* + ******************************************************************** + * scsiio + * DC395x_Interrupt + ******************************************************************** + */ +static void DC395x_ScsiRstDetect(struct AdapterCtlBlk *pACB) +{ + printk(KERN_INFO DC395X_NAME ": DC395x_ScsiRstDetect\n"); + /* delay half a second */ + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + /*DC395x_write8(TRM_S1040_DMA_CONTROL,STOPDMAXFER); */ + udelay(500); + /* Maybe we locked up the bus? Then lets wait even longer ... */ + pACB->pScsiHost->last_reset = + jiffies + 5 * HZ / 2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + + DC395x_clrfifo(pACB, "RstDet"); + DC395x_basic_config(pACB); + /*1.25 */ + /*DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); */ + + if (pACB->ACBFlag & RESET_DEV) { /* RESET_DETECT, RESET_DONE, RESET_DEV */ + pACB->ACBFlag |= RESET_DONE; + } else { + pACB->ACBFlag |= RESET_DETECT; + DC395x_ResetDevParam(pACB); + DC395x_DoingSRB_Done(pACB, DID_RESET, 0, 1); + /*DC395x_RecoverSRB( pACB ); */ + pACB->pActiveDCB = NULL; + pACB->ACBFlag = 0; + DC395x_Waiting_process(pACB); + } + + return; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_SRBdone + ******************************************************************** + */ +static void +DC395x_RequestSense(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB, + struct ScsiReqBlk *pSRB) +{ + Scsi_Cmnd *pcmd; + + pcmd = pSRB->pcmd; +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO DC395X_NAME + ": DC395x_RequestSense for pid %li, target %02i-%i\n", + pcmd->pid, pcmd->device->id, pcmd->device->lun); +#endif + TRACEPRINTF("RqSn*"); + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + /* KG: Can this prevent crap sense data ? */ + memset(pcmd->sense_buffer, 0, sizeof(pcmd->sense_buffer)); + + /* Save some data */ + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].address = + pSRB->SegmentX[0].address; + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY - 1].length = + pSRB->SegmentX[0].length; + pSRB->Xferred = pSRB->SRBTotalXferLength; + /* pSRB->SegmentX : a one entry of S/G list table */ + pSRB->SRBTotalXferLength = sizeof(pcmd->sense_buffer); + pSRB->SegmentX[0].length = sizeof(pcmd->sense_buffer); + /* Map sense buffer */ + pSRB->SegmentX[0].address = + pci_map_single(pACB->pdev, pcmd->sense_buffer, + sizeof(pcmd->sense_buffer), PCI_DMA_FROMDEVICE); +#ifdef DC395x_SGPARANOIA + printk(DC395X_NAME ": Map sense buffer at %p (%05x) to %08x\n", + pcmd->sense_buffer, sizeof(pcmd->sense_buffer), + pSRB->SegmentX[0].address); +#endif + pSRB->SRBSGCount = 1; + pSRB->SRBSGIndex = 0; + + if (DC395x_StartSCSI(pACB, pDCB, pSRB)) { /* Should only happen, if sb. else grabs the bus */ + printk(DC395X_NAME + ": Request Sense failed for pid %li (%02i-%i)!\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); + TRACEPRINTF("?*"); + DC395x_Going_to_Waiting(pDCB, pSRB); + DC395x_waiting_timer(pACB, HZ / 100); + } + TRACEPRINTF(".*"); +} + + +/* + ********************************************************************* + * DC395x_queue_command + * + * Function : void DC395x_initDCB + * Purpose : initialize the internal structures for a given DCB + * Inputs : cmd - pointer to this scsi cmd request block structure + ********************************************************************* + */ +void +DC395x_initDCB(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk **ppDCB, + u8 target, u8 lun) +{ + struct NvRamType *eeprom; + u8 PeriodIndex; + u16 index; + struct DeviceCtlBlk *pDCB; + struct DeviceCtlBlk *pDCB2; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": DC395x_initDCB..............\n "); +#endif + pDCB = KMALLOC(sizeof(struct DeviceCtlBlk), GFP_ATOMIC); + /*pDCB = DC395x_findDCB (pACB, target, lun); */ + *ppDCB = pDCB; + pDCB2 = 0; + if (!pDCB) + return; + + if (pACB->DCBCnt == 0) { + pACB->pLinkDCB = pDCB; + pACB->pDCBRunRobin = pDCB; + } else { + pACB->pLastDCB->pNextDCB = pDCB; + } + + pACB->DCBCnt++; + pDCB->pNextDCB = pACB->pLinkDCB; + pACB->pLastDCB = pDCB; + + /* $$$$$$$ */ + pDCB->pDCBACB = pACB; + pDCB->TargetID = target; + pDCB->TargetLUN = lun; + /* $$$$$$$ */ + pDCB->pWaitingSRB = NULL; + pDCB->pGoingSRB = NULL; + pDCB->GoingSRBCnt = 0; + pDCB->WaitSRBCnt = 0; + pDCB->pActiveSRB = NULL; + /* $$$$$$$ */ + pDCB->TagMask = 0; + pDCB->DCBFlag = 0; + pDCB->MaxCommand = 1; + pDCB->AdaptIndex = pACB->AdapterIndex; + /* $$$$$$$ */ + index = pACB->AdapterIndex; + eeprom = &dc395x_trm_eepromBuf[index]; + pDCB->DevMode = eeprom->NvramTarget[target].NvmTarCfg0; + /*pDCB->AdpMode = eeprom->NvramChannelCfg; */ + pDCB->Inquiry7 = 0; + pDCB->SyncMode = 0; + pDCB->last_derated = pACB->pScsiHost->last_reset - 2; + /* $$$$$$$ */ + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + PeriodIndex = eeprom->NvramTarget[target].NvmTarPeriod & 0x07; + pDCB->MinNegoPeriod = dc395x_clock_period[PeriodIndex]; + +#ifndef DC395x_NO_WIDE + if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) + && (pACB->Config & HCC_WIDE_CARD)) + pDCB->SyncMode |= WIDE_NEGO_ENABLE; +#endif +#ifndef DC395x_NO_SYNC + if (pDCB->DevMode & NTC_DO_SYNC_NEGO) + if (!(lun) || DC395x_CurrSyncOffset) + pDCB->SyncMode |= SYNC_NEGO_ENABLE; +#endif + /* $$$$$$$ */ +#ifndef DC395x_NO_DISCONNECT + pDCB->IdentifyMsg = + IDENTIFY(pDCB->DevMode & NTC_DO_DISCONNECT, lun); +#else + pDCB->IdentifyMsg = IDENTIFY(0, lun); +#endif + /* $$$$$$$ */ + if (pDCB->TargetLUN != 0) { + /* Copy settings */ + struct DeviceCtlBlk *prevDCB = pACB->pLinkDCB; + while (prevDCB->TargetID != pDCB->TargetID) + prevDCB = prevDCB->pNextDCB; +#ifdef DC395x_DEBUG_KG + printk(DC395X_NAME + ": Copy settings from %02i-%02i to %02i-%02i\n", + prevDCB->TargetID, prevDCB->TargetLUN, + pDCB->TargetID, pDCB->TargetLUN); +#endif + pDCB->SyncMode = prevDCB->SyncMode; + pDCB->SyncPeriod = prevDCB->SyncPeriod; + pDCB->MinNegoPeriod = prevDCB->MinNegoPeriod; + pDCB->SyncOffset = prevDCB->SyncOffset; + pDCB->Inquiry7 = prevDCB->Inquiry7; + }; + + pACB->DCBmap[target] |= (1 << lun); + pACB->children[target][lun] = pDCB; +} + + +/* Dynamically allocated memory handling */ + +#ifdef DC395x_DEBUGTRACE +/* Memory for trace buffers */ +void DC395x_free_tracebufs(struct AdapterCtlBlk *pACB, int SRBIdx) +{ + int srbidx; + const unsigned bufs_per_page = PAGE_SIZE / DEBUGTRACEBUFSZ; + for (srbidx = 0; srbidx < SRBIdx; srbidx += bufs_per_page) { + /*printk (DC395X_NAME ": Free tracebuf %p (for %i)\n", */ + /* pACB->SRB_array[srbidx].debugtrace, srbidx); */ + KFREE(pACB->SRB_array[srbidx].debugtrace); + } +} + + +int DC395x_alloc_tracebufs(struct AdapterCtlBlk *pACB) +{ + const unsigned mem_needed = + (DC395x_MAX_SRB_CNT + 1) * DEBUGTRACEBUFSZ; + int pages = (mem_needed + (PAGE_SIZE - 1)) / PAGE_SIZE; + const unsigned bufs_per_page = PAGE_SIZE / DEBUGTRACEBUFSZ; + int SRBIdx = 0; + unsigned i = 0; + unsigned char *ptr; + /*printk (DC395X_NAME ": Alloc %i pages for tracebufs\n", pages); */ + while (pages--) { + ptr = KMALLOC(PAGE_SIZE, GFP_KERNEL); + if (!ptr) { + DC395x_free_tracebufs(pACB, SRBIdx); + return 1; + } + /*printk (DC395X_NAME ": Alloc %li bytes at %p for tracebuf %i\n", */ + /* PAGE_SIZE, ptr, SRBIdx); */ + i = 0; + while (i < bufs_per_page && SRBIdx < DC395x_MAX_SRB_CNT) + pACB->SRB_array[SRBIdx++].debugtrace = + ptr + (i++ * DEBUGTRACEBUFSZ); + } + if (i < bufs_per_page) { + pACB->TmpSRB.debugtrace = ptr + (i * DEBUGTRACEBUFSZ); + pACB->TmpSRB.debugtrace[0] = 0; + } else + printk(DC395X_NAME + ": No space for tmpSRB tracebuf reserved?!\n"); + return 0; +} +#endif + + +/* Free SG tables */ +void DC395x_free_SG_tables(struct AdapterCtlBlk *pACB, int SRBIdx) +{ + int srbidx; + const unsigned SRBs_per_page = + PAGE_SIZE / (DC395x_MAX_SG_LISTENTRY * sizeof(struct SGentry)); + for (srbidx = 0; srbidx < SRBIdx; srbidx += SRBs_per_page) { + /*printk (DC395X_NAME ": Free SG segs %p (for %i)\n", */ + /* pACB->SRB_array[srbidx].SegmentX, srbidx); */ + KFREE(pACB->SRB_array[srbidx].SegmentX); + } +} + + +/* + * Allocate SG tables; as we have to pci_map them, an SG list (struct SGentry*) + * should never cross a page boundary */ +int DC395x_alloc_SG_tables(struct AdapterCtlBlk *pACB) +{ + const unsigned mem_needed = + (DC395x_MAX_SRB_CNT + + 1) * DC395x_MAX_SG_LISTENTRY * sizeof(struct SGentry); + int pages = (mem_needed + (PAGE_SIZE - 1)) / PAGE_SIZE; + const unsigned SRBs_per_page = + PAGE_SIZE / (DC395x_MAX_SG_LISTENTRY * sizeof(struct SGentry)); + int SRBIdx = 0; + unsigned i = 0; + struct SGentry *ptr; + /*printk (DC395X_NAME ": Alloc %i pages for SG tables\n", pages); */ + while (pages--) { + ptr = (struct SGentry *) KMALLOC(PAGE_SIZE, GFP_KERNEL); + if (!ptr) { + DC395x_free_SG_tables(pACB, SRBIdx); + return 1; + } + /*printk (DC395X_NAME ": Alloc %li bytes at %p for SG segments %i\n", */ + /* PAGE_SIZE, ptr, SRBIdx); */ + i = 0; + while (i < SRBs_per_page && SRBIdx < DC395x_MAX_SRB_CNT) + pACB->SRB_array[SRBIdx++].SegmentX = + ptr + (i++ * DC395x_MAX_SG_LISTENTRY); + } + if (i < SRBs_per_page) + pACB->TmpSRB.SegmentX = + ptr + (i * DC395x_MAX_SG_LISTENTRY); + else + printk(DC395X_NAME + ": No space for tmpSRB SG table reserved?!\n"); + return 0; +} + + +/* + ******************************************************************** + * scsiio + * DC395x_initACB + ******************************************************************** + */ +void __init DC395x_linkSRB(struct AdapterCtlBlk *pACB) +{ + int i; + + for (i = 0; i < pACB->SRBCount - 1; i++) + pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i + 1]; + pACB->SRB_array[i].pNextSRB = NULL; + /*DC395x_Free_integrity (pACB); */ +} + + +/* + *********************************************************************** + * DC395x_init + * + * Function : static void DC395x_initACB + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure + *********************************************************************** + */ +int __init +DC395x_initACB(struct Scsi_Host *host, u32 io_port, u8 irq, u16 index) +{ + struct NvRamType *eeprom; + struct AdapterCtlBlk *pACB; + u16 i; + + eeprom = &dc395x_trm_eepromBuf[index]; + host->max_cmd_len = 24; + host->can_queue = DC395x_MAX_CMD_QUEUE; + host->cmd_per_lun = DC395x_MAX_CMD_PER_LUN; + host->this_id = (int) eeprom->NvramScsiId; + host->io_port = io_port; + host->n_io_port = 0x80; + host->dma_channel = -1; + host->unique_id = io_port; + host->irq = irq; + host->last_reset = jiffies; + + pACB = (struct AdapterCtlBlk *) host->hostdata; + + host->max_id = 16; + if (host->max_id - 1 == eeprom->NvramScsiId) + host->max_id--; +#ifdef CONFIG_SCSI_MULTI_LUN + if (eeprom->NvramChannelCfg & NAC_SCANLUN) + host->max_lun = 8; + else + host->max_lun = 1; +#else + host->max_lun = 1; +#endif + /* + ******************************** + */ + pACB->pScsiHost = host; + pACB->IOPortBase = (u16) io_port; + pACB->pLinkDCB = NULL; + pACB->pDCBRunRobin = NULL; + pACB->pActiveDCB = NULL; + pACB->SRBCount = DC395x_MAX_SRB_CNT; + pACB->AdapterIndex = index; + pACB->status = 0; + pACB->pScsiHost->this_id = eeprom->NvramScsiId; + pACB->HostID_Bit = (1 << pACB->pScsiHost->this_id); + /*pACB->pScsiHost->this_lun = 0; */ + pACB->DCBCnt = 0; + pACB->DeviceCnt = 0; + pACB->IRQLevel = irq; + pACB->TagMaxNum = 1 << eeprom->NvramMaxTag; + if (pACB->TagMaxNum > 30) + pACB->TagMaxNum = 30; + pACB->ACBFlag = 0; /* RESET_DETECT, RESET_DONE, RESET_DEV */ + pACB->scan_devices = 1; + pACB->MsgLen = 0; + pACB->Gmode2 = eeprom->NvramChannelCfg; + if (eeprom->NvramChannelCfg & NAC_SCANLUN) + pACB->LUNchk = 1; + /* + * link all device's SRB Q of this adapter + */ + if (DC395x_alloc_SG_tables(pACB)) { + printk(DC395X_NAME ": SG table allocation failed!\n"); + return 1; + } +#ifdef DC395x_DEBUGTRACE + if (DC395x_alloc_tracebufs(pACB)) { + printk(DC395X_NAME + ": SG trace buffer allocation failed!\n"); + DC395x_free_SG_tables(pACB, DC395x_MAX_SRB_CNT); + return 1; + } +#endif + DC395x_linkSRB(pACB); + pACB->pFreeSRB = pACB->SRB_array; + /* + * temp SRB for Q tag used or abort command used + */ + pACB->pTmpSRB = &pACB->TmpSRB; + pACB->TmpSRB.pSRBDCB = 0; + pACB->TmpSRB.pNextSRB = 0; + init_timer(&pACB->Waiting_Timer); + + for (i = 0; i < DC395x_MAX_SCSI_ID; i++) + pACB->DCBmap[i] = 0; +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME + ": pACB = %p, pDCBmap = %p, pSRB_array = %p\n", pACB, + pACB->DCBmap, pACB->SRB_array); + printk(KERN_INFO DC395X_NAME + ": ACB size= %04lx, DCB size= %04lx, SRB size= %04lx\n", + sizeof(struct AdapterCtlBlk), sizeof(struct DeviceCtlBlk), + sizeof(struct ScsiReqBlk)); +#endif + return 0; +} + + +/*=========================================================================== + Init + ===========================================================================*/ +/* + * Intialise the SCSI chip control registers + * + * @param host This hosts adapter strcuture + * @param io_port The base I/O port + * @param irq IRQ + * @param index Card number?? (for multiple cards?) + */ +static int __init +DC395x_initAdapter(struct Scsi_Host *host, u32 io_port, u8 irq, u16 index) +{ + struct NvRamType *eeprom; + struct AdapterCtlBlk *pACB; + struct AdapterCtlBlk *pTempACB; + u16 used_irq = 0; + + eeprom = &dc395x_trm_eepromBuf[index]; + pTempACB = DC395x_pACB_start; + if (pTempACB != NULL) { + for (; (pTempACB != (struct AdapterCtlBlk *) -1);) { + if (pTempACB->IRQLevel == irq) { + used_irq = 1; + break; + } else + pTempACB = pTempACB->pNextACB; + } + } + + if (!request_region(io_port, host->n_io_port, DC395X_NAME)) { + printk(KERN_ERR DC395X_NAME + ": Failed to reserve IO region 0x%x\n", io_port); + return -1; + } + if (!used_irq) { + if (request_irq + (irq, DC395x_Interrupt, SA_SHIRQ, DC395X_NAME, + (void *) host->hostdata)) { + printk(KERN_INFO DC395X_NAME + ": Failed to register IRQ!\n"); + return -1; + } + } + + pACB = (struct AdapterCtlBlk *) host->hostdata; + pACB->IOPortBase = io_port; + + /* selection timeout = 250 ms */ + pACB->sel_timeout = DC395x_SEL_TIMEOUT; + + /* Mask all the interrupt */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x00); + + /* Reset SCSI module */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + + /* Reset PCI/DMA module */ + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + udelay(20); + + /* program configuration 0 */ + pACB->Config = HCC_AUTOTERM | HCC_PARITY; + if (DC395x_read8(TRM_S1040_GEN_STATUS) & WIDESCSI) + pACB->Config |= HCC_WIDE_CARD; + + if (eeprom->NvramChannelCfg & NAC_POWERON_SCSI_RESET) + pACB->Config |= HCC_SCSI_RESET; + + if (pACB->Config & HCC_SCSI_RESET) { + printk(KERN_INFO DC395X_NAME + ": Performing initial SCSI bus reset\n"); + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); + + /*while (!( DC395x_read8(TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET )); */ + /*spin_unlock_irq (&io_request_lock); */ + udelay(500); + + pACB->pScsiHost->last_reset = + jiffies + HZ / 2 + + HZ * + dc395x_trm_eepromBuf[pACB->AdapterIndex]. + NvramDelayTime; + + /*spin_lock_irq (&io_request_lock); */ + } + DC395x_basic_config(pACB); + return 0; +} + + +/* + * eeprom - wait 30 us + * + * Waits for 30us (using the chip by the looks of it..) + * + * @param io_port - base I/O address + */ +static void __init TRM_S1040_wait_30us(u16 io_port) +{ + /* ScsiPortStallExecution(30); wait 30 us */ + outb(5, io_port + TRM_S1040_GEN_TIMER); + while (!(inb(io_port + TRM_S1040_GEN_STATUS) & GTIMEOUT)) + /* nothing */ ; + return; +} + + +/* + * eeprom - write command and address to chip + * + * Write the specified command and address + * + * @param io_port - base I/O address + * @param cmd - SB + op code (command) to send + * @param addr - address to send + */ +static void __init TRM_S1040_write_cmd(u16 io_port, u8 cmd, u8 addr) +{ + int i; + u8 send_data; + + /* program SB + OP code */ + for (i = 0; i < 3; i++, cmd <<= 1) { + send_data = NVR_SELECT; + if (cmd & 0x04) /* Start from bit 2 */ + send_data |= NVR_BITOUT; + + outb(send_data, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb((send_data | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + } + + /* send address */ + for (i = 0; i < 7; i++, addr <<= 1) { + send_data = NVR_SELECT; + if (addr & 0x40) /* Start from bit 6 */ + send_data |= NVR_BITOUT; + + outb(send_data, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb((send_data | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + } + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); +} + + +/* + * eeprom - store a single byte in the SEEPROM + * + * Called from write all to write a single byte into the SSEEPROM + * Which is done one bit at a time. + * + * @param io_port - base I/O address + * @param addr - offset into EEPROM + * @param byte - bytes to write + */ +static void __init TRM_S1040_set_data(u16 io_port, u8 addr, u8 byte) +{ + int i; + u8 send_data; + + /* Send write command & address */ + TRM_S1040_write_cmd(io_port, 0x05, addr); + + /* Write data */ + for (i = 0; i < 8; i++, byte <<= 1) { + send_data = NVR_SELECT; + if (byte & 0x80) /* Start from bit 7 */ + send_data |= NVR_BITOUT; + + outb(send_data, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb((send_data | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + } + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* Disable chip select */ + outb(0, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* Wait for write ready */ + while (1) { + outb((NVR_SELECT | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + if (inb(io_port + TRM_S1040_GEN_NVRAM) & NVR_BITIN) + break; + } + + /* Disable chip select */ + outb(0, io_port + TRM_S1040_GEN_NVRAM); +} + + +/* + * eeprom - write 128 bytes to the SEEPROM + * + * Write the supplied 128 bytes to the chips SEEPROM + * + * @param eeprom - the data to write + * @param io_port - the base io port + */ +static void __init +TRM_S1040_write_all(struct NvRamType *eeprom, u16 io_port) +{ + u8 *b_eeprom = (u8 *) eeprom; + u8 addr; + + /* Enable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) | EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); + + /* write enable */ + TRM_S1040_write_cmd(io_port, 0x04, 0xFF); + outb(0, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* write */ + for (addr = 0; addr < 128; addr++, b_eeprom++) { + TRM_S1040_set_data(io_port, addr, *b_eeprom); + } + + /* write disable */ + TRM_S1040_write_cmd(io_port, 0x04, 0x00); + outb(0, io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + + /* Disable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) & ~EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); +} + + +/* + * eeprom - get a single byte from the SEEPROM + * + * Called from read all to read a single byte into the SSEEPROM + * Which is done one bit at a time. + * + * @param io_port - base I/O address + * @param addr - offset into SEEPROM + * @return - the byte read + */ +static u8 __init TRM_S1040_get_data(u16 io_port, u8 addr) +{ + int i; + u8 read_byte; + u8 result = 0; + + /* Send read command & address */ + TRM_S1040_write_cmd(io_port, 0x06, addr); + + /* read data */ + for (i = 0; i < 8; i++) { + outb((NVR_SELECT | NVR_CLOCK), + io_port + TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(io_port); + outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM); + + /* Get data bit while falling edge */ + read_byte = inb(io_port + TRM_S1040_GEN_NVRAM); + result <<= 1; + if (read_byte & NVR_BITIN) + result |= 1; + + TRM_S1040_wait_30us(io_port); + } + + /* Disable chip select */ + outb(0, io_port + TRM_S1040_GEN_NVRAM); + return result; +} + + +/* + * eeprom - read_all + * + * Read the 128 bytes from the SEEPROM. + * + * @param eeprom - where to store the data + * @param io_port - the base io port + */ +static void __init +TRM_S1040_read_all(struct NvRamType *eeprom, u16 io_port) +{ + u8 *b_eeprom = (u8 *) eeprom; + u8 addr; + + /* Enable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) | EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); + + /* read details */ + for (addr = 0; addr < 128; addr++, b_eeprom++) { + *b_eeprom = TRM_S1040_get_data(io_port, addr); + } + + /* Disable SEEPROM */ + outb((inb(io_port + TRM_S1040_GEN_CONTROL) & ~EN_EEPROM), + io_port + TRM_S1040_GEN_CONTROL); +} + + + +/* + * eeprom - get and check contents + * + * Read seeprom 128 bytes into the memory provider in eeprom. + * Checks the checksum and if it's not correct it uses a set of default + * values. + * + * @param eeprom - caller allocated strcuture to read the eeprom data into + * @param io_port - io port to read from + */ +static void __init +DC395x_check_eeprom(struct NvRamType *eeprom, u16 io_port) +{ + u16 *w_eeprom = (u16 *) eeprom; + u16 w_addr; + u16 cksum; + u32 d_addr; + u32 *d_eeprom; + + TRM_S1040_read_all(eeprom, io_port); /* read eeprom */ + + cksum = 0; + for (w_addr = 0, w_eeprom = (u16 *) eeprom; w_addr < 64; + w_addr++, w_eeprom++) + cksum += *w_eeprom; + if (cksum != 0x1234) { + /* + * Checksum is wrong. + * Load a set of defaults into the eeprom buffer + */ + printk(KERN_WARNING DC395X_NAME + ": EEProm checksum error: using default values and options.\n"); + eeprom->NvramSubVendorID[0] = (u8) PCI_VENDOR_ID_TEKRAM; + eeprom->NvramSubVendorID[1] = + (u8) (PCI_VENDOR_ID_TEKRAM >> 8); + eeprom->NvramSubSysID[0] = + (u8) PCI_DEVICE_ID_TEKRAM_TRMS1040; + eeprom->NvramSubSysID[1] = + (u8) (PCI_DEVICE_ID_TEKRAM_TRMS1040 >> 8); + eeprom->NvramSubClass = 0x00; + eeprom->NvramVendorID[0] = (u8) PCI_VENDOR_ID_TEKRAM; + eeprom->NvramVendorID[1] = + (u8) (PCI_VENDOR_ID_TEKRAM >> 8); + eeprom->NvramDeviceID[0] = + (u8) PCI_DEVICE_ID_TEKRAM_TRMS1040; + eeprom->NvramDeviceID[1] = + (u8) (PCI_DEVICE_ID_TEKRAM_TRMS1040 >> 8); + eeprom->NvramReserved = 0x00; + + for (d_addr = 0, d_eeprom = (u32 *) eeprom->NvramTarget; + d_addr < 16; d_addr++, d_eeprom++) + *d_eeprom = 0x00000077; /* NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 */ + + *d_eeprom++ = 0x04000F07; /* NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId */ + *d_eeprom++ = 0x00000015; /* NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0 */ + for (d_addr = 0; d_addr < 12; d_addr++, d_eeprom++) + *d_eeprom = 0x00; + + /* Now load defaults (maybe set by boot/module params) */ + DC395x_check_for_safe_settings(); + DC395x_fill_with_defaults(); + DC395x_EEprom_Override(eeprom); + + eeprom->NvramCheckSum = 0x00; + for (w_addr = 0, cksum = 0, w_eeprom = (u16 *) eeprom; + w_addr < 63; w_addr++, w_eeprom++) + cksum += *w_eeprom; + + *w_eeprom = 0x1234 - cksum; + TRM_S1040_write_all(eeprom, io_port); + eeprom->NvramDelayTime = dc395x_trm[5]; + } else { + DC395x_check_for_safe_settings(); + DC395x_interpret_delay(eeprom); + DC395x_EEprom_Override(eeprom); + } +} + + +/* + * adapter - print connection and terminiation config + * + * @param pACB - adapter control block + */ +static void __init DC395x_print_config(struct AdapterCtlBlk *pACB) +{ + u8 bval; + + bval = DC395x_read8(TRM_S1040_GEN_STATUS); + printk(KERN_INFO DC395X_NAME "%c: Connectors: ", + ((bval & WIDESCSI) ? 'W' : ' ')); + if (!(bval & CON5068)) + printk("ext%s ", !(bval & EXT68HIGH) ? "68" : "50"); + if (!(bval & CON68)) + printk("int68%s ", !(bval & INT68HIGH) ? "" : "(50)"); + if (!(bval & CON50)) + printk("int50 "); + if ((bval & (CON5068 | CON50 | CON68)) == + 0 /*(CON5068 | CON50 | CON68) */ ) + printk(" Oops! (All 3?) "); + bval = DC395x_read8(TRM_S1040_GEN_CONTROL); + printk(" Termination: "); + if (bval & DIS_TERM) + printk("Disabled\n"); + else { + if (bval & AUTOTERM) + printk("Auto "); + if (bval & LOW8TERM) + printk("Low "); + if (bval & UP8TERM) + printk("High "); + printk("\n"); + } +} + + +/* + ********************************************************************* + * DC395x_detect + * + * Function : static int DC395x_init (struct Scsi_Host *host) + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure/ + * Preconditions : when this function is called, the chip_type + * field of the pACB structure MUST have been set. + ********************************************************************* + */ +static struct Scsi_Host *__init +DC395x_init(Scsi_Host_Template * host_template, u32 io_port, u8 irq, + u16 index) +{ + struct Scsi_Host *host; + struct AdapterCtlBlk *pACB; + + /* + * Read the eeprom contents info the buffer we supply. Use + * defaults is eeprom checksum is wrong. + */ + DC395x_check_eeprom(&dc395x_trm_eepromBuf[index], (u16) io_port); + + /*$$$$$$$$$$$ MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK $$$$$$$$$$$$ */ + host = scsi_register(host_template, sizeof(struct AdapterCtlBlk)); + if (!host) { + printk(KERN_INFO DC395X_NAME + " : pSH scsi_register ERROR\n"); + return 0; + } + printk(KERN_INFO DC395X_NAME + ": Used settings: AdapterID=%02i, Speed=%i(%02i.%01iMHz), DevMode=0x%02x\n", + dc395x_trm_eepromBuf[index].NvramScsiId, + dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarPeriod, + dc395x_clock_speed[dc395x_trm_eepromBuf[index]. + NvramTarget[0].NvmTarPeriod] / 10, + dc395x_clock_speed[dc395x_trm_eepromBuf[index]. + NvramTarget[0].NvmTarPeriod] % 10, + dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarCfg0); + printk(KERN_INFO DC395X_NAME + ": AdaptMode=0x%02x, Tags=%i(%02i), DelayReset=%is\n", + dc395x_trm_eepromBuf[index].NvramChannelCfg, + dc395x_trm_eepromBuf[index].NvramMaxTag, + 1 << dc395x_trm_eepromBuf[index].NvramMaxTag, + dc395x_trm_eepromBuf[index].NvramDelayTime); + + pACB = (struct AdapterCtlBlk *) host->hostdata; + /*DC395x_ACB_INITLOCK(pACB); */ + /*DC395x_ACB_LOCK(pACB,acb_flags); */ + /*$$$$$$$$ INITIAL ADAPTER CONTROL BLOCK $$$$$$$$$$$$ */ + if (DC395x_initACB(host, io_port, irq, index)) { + scsi_unregister(host); + /*DC395x_ACB_UNLOCK(pACB,acb_flags); */ + return 0; + } + DC395x_print_config(pACB); + /*$$$$$$$$$$$$$$$$$ INITIAL ADAPTER $$$$$$$$$$$$$$$$$ */ + if (!DC395x_initAdapter(host, io_port, irq, index)) { + if (!DC395x_pACB_start) { + DC395x_pACB_start = pACB; + DC395x_pACB_current = pACB; + pACB->pNextACB = (struct AdapterCtlBlk *) -1; + } else { + DC395x_pACB_current->pNextACB = pACB; + DC395x_pACB_current = pACB; + pACB->pNextACB = (struct AdapterCtlBlk *) -1; + } + /*DC395x_ACB_UNLOCK(pACB,acb_flags); */ + return host; + } else { + printk(KERN_INFO DC395X_NAME + ": DC395x_initAdapter initial ERROR\n"); + scsi_unregister(host); + /*DC395x_ACB_UNLOCK(pACB,acb_flags); */ + return 0; + } +} + + +/* + * DC395x_detect + * + * Detect TRM-S1040 cards, acquire resources and initialise the card. + * Argument is a pointer to the host driver's scsi_hosts entry. + * + * Returns the number of adapters found. + * + * This function is called during system initialization and must not + * call SCSI mid-level functions including scsi_malloc() and + * scsi_free(). + */ +static int __init DC395x_detect(Scsi_Host_Template * host_template) +{ + struct pci_dev *pdev = NULL; + unsigned int io_port; + u8 irq; + DC395x_pACB_start = NULL; + + /* without PCI we cannot do anything */ + if (pci_present() == 0) { + printk(KERN_INFO DC395X_NAME ": PCI not present\n"); + return 0; + } + printk(KERN_INFO DC395X_NAME ": %s %s\n", DC395X_BANNER, + DC395X_VERSION); + + while ((pdev = + pci_find_device(PCI_VENDOR_ID_TEKRAM, + PCI_DEVICE_ID_TEKRAM_TRMS1040, pdev))) { + struct Scsi_Host *scsi_host; + if (pci_enable_device(pdev)) + continue; + + io_port = + pci_resource_start(pdev, 0) & PCI_BASE_ADDRESS_IO_MASK; + irq = pdev->irq; +#ifdef DC395x_DEBUG0 + printk(KERN_INFO DC395X_NAME ": IO_PORT=%04x,IRQ=%x\n", + (unsigned int) io_port, irq); +#endif + if ((scsi_host = + DC395x_init(host_template, io_port, irq, + DC395x_adapterCnt))) { + pci_set_master(pdev); + ((struct AdapterCtlBlk *) (scsi_host->hostdata))-> + pdev = pdev; + /*DC395x_set_pci_cfg(pdev); */ + DC395x_adapterCnt++; + } + } + + if (DC395x_adapterCnt) { + host_template->proc_name = DC395X_NAME; + } + printk(KERN_INFO DC395X_NAME ": %s: %i adapters found\n", + DC395X_BANNER, DC395x_adapterCnt); + + return DC395x_adapterCnt; +} + + +/* + * Functions: DC395x_inquiry(), DC395x_inquiry_done() + * + * Purpose: When changing speed etc., we have to issue an INQUIRY + * command to make sure, we agree upon the nego parameters + * with the device + */ +static void DC395x_inquiry_done(Scsi_Cmnd * cmd) +{ + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) cmd->device->host->hostdata; + struct DeviceCtlBlk *pDCB = + DC395x_findDCB(pACB, cmd->device->id, cmd->device->lun); +#ifdef DC395x_DEBUGTRACE + struct ScsiReqBlk *pSRB = pACB->pFreeSRB; +#endif + printk(KERN_INFO DC395X_NAME + ": INQUIRY (%02i-%i) returned %08x: %02x %02x %02x %02x ...\n", + cmd->device->id, cmd->device->lun, cmd->result, + ((u8 *) cmd->request_buffer)[0], + ((u8 *) cmd->request_buffer)[1], + ((u8 *) cmd->request_buffer)[2], + ((u8 *) cmd->request_buffer)[3]); + /*TRACEOUT ("%s\n", pSRB->debugtrace); */ + if (cmd->result) { + printk(DC395X_NAME ": Unsetting Wide, Sync and TagQ!\n"); + if (pDCB) { + TRACEOUT("%s\n", pSRB->debugtrace); + pDCB->DevMode &= + ~(NTC_DO_SYNC_NEGO | NTC_DO_WIDE_NEGO | + NTC_DO_TAG_QUEUEING); + DC395x_updateDCB(pACB, pDCB); + } + } + if (pDCB) { + if (!(pDCB->SyncMode & SYNC_NEGO_DONE)) { + pDCB->SyncOffset = 0; /*pDCB->SyncMode &= ~SYNC_NEGO_ENABLE; */ + } + if (!(pDCB->SyncMode & WIDE_NEGO_DONE)) { + pDCB->SyncPeriod &= ~WIDE_SYNC; + pDCB->SyncMode &= ~WIDE_NEGO_ENABLE; + } + } else { + printk(DC395X_NAME + ": ERROR! No DCB existent for %02i-%i ?\n", + cmd->device->id, cmd->device->lun); + } + kfree(cmd->buffer); + kfree(cmd); +} + + +/* + * Perform INQUIRY + */ +void DC395x_inquiry(struct AdapterCtlBlk *pACB, struct DeviceCtlBlk *pDCB) +{ + char *buffer; + Scsi_Cmnd *cmd; + cmd = KMALLOC(sizeof(Scsi_Cmnd), GFP_ATOMIC); + if (!cmd) { + printk(DC395X_NAME ": kmalloc failed in inquiry!\n"); + return; + } + buffer = kmalloc(256, GFP_ATOMIC); + if (!buffer) { + kfree(cmd); + printk(DC395X_NAME ": kmalloc failed in inquiry!\n"); + return; + } + + memset(cmd, 0, sizeof(Scsi_Cmnd)); + cmd->cmnd[0] = INQUIRY; + cmd->cmnd[1] = (pDCB->TargetLUN << 5) & 0xe0; + cmd->cmnd[4] = 0xff; + + cmd->cmd_len = 6; + cmd->old_cmd_len = 6; + cmd->device->host = pACB->pScsiHost; + cmd->device->id = pDCB->TargetID; + cmd->device->lun = pDCB->TargetLUN; + cmd->serial_number = 1; + cmd->pid = 395; + cmd->bufflen = 128; + cmd->buffer = buffer; + cmd->request_bufflen = 128; + cmd->request_buffer = &buffer[128]; + cmd->done = DC395x_inquiry_done; + cmd->scsi_done = DC395x_inquiry_done; + cmd->timeout_per_command = HZ; + +#if 0 + /* XXX */ + cmd->request.rq_status = RQ_SCSI_BUSY; +#endif + + pDCB->SyncMode &= ~SYNC_NEGO_DONE; + pDCB->SyncMode |= SYNC_NEGO_ENABLE; + pDCB->SyncMode &= ~WIDE_NEGO_DONE; + pDCB->SyncMode |= WIDE_NEGO_ENABLE; + printk(KERN_INFO DC395X_NAME + ": Queue INQUIRY command to dev %02i-%i\n", pDCB->TargetID, + pDCB->TargetLUN); + DC395x_queue_command(cmd, DC395x_inquiry_done); +} + +#undef SEARCH +#undef YESNO +#undef SCANF + + +/* + ****************************************************************** + * Function: DC395x_proc_info(char* buffer, char **start, + * off_t offset, int length, int hostno, int inout) + * Purpose: return SCSI Adapter/Device Info + * Input: + * buffer: Pointer to a buffer where to write info + * start : + * offset: + * hostno: Host adapter index + * inout : Read (=0) or set(!=0) info + * Output: + * buffer: contains info length + * + * return value: length of info in buffer + * + ****************************************************************** + */ + +/* KG: proc_info taken from driver aha152x.c */ + +#undef SPRINTF +#define SPRINTF(args...) pos += sprintf(pos, args) + +#define YESNO(YN) \ + if (YN) SPRINTF(" Yes ");\ + else SPRINTF(" No ") + +static int +DC395x_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + int dev, spd, spd1; + char *pos = buffer; + struct Scsi_Host *shpnt = NULL; + struct AdapterCtlBlk *pACB; + struct DeviceCtlBlk *pDCB; + unsigned long flags; + Scsi_Cmnd *pcmd; + + /* Scsi_Cmnd *ptr; */ + + pACB = DC395x_pACB_start; + + while (pACB != (struct AdapterCtlBlk *) -1) { + shpnt = pACB->pScsiHost; + if (shpnt->host_no == hostno) + break; + pACB = pACB->pNextACB; + } + if (pACB == (struct AdapterCtlBlk *) -1) + return -ESRCH; + + if (!shpnt) + return -ESRCH; + + if (inout) /* Has data been written to the file ? */ + return -EPERM; + + SPRINTF(DC395X_BANNER " PCI SCSI Host Adapter\n"); + SPRINTF(" Driver Version " DC395X_VERSION "\n"); + + DC395x_LOCK_IO(pACB->pScsiHost); + + SPRINTF("SCSI Host Nr %i, ", shpnt->host_no); + SPRINTF("DC395U/UW/F DC315/U %s Adapter Nr %i\n", + (pACB->Config & HCC_WIDE_CARD) ? "Wide" : "", + pACB->AdapterIndex); + SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase); + SPRINTF("IRQLevel 0x%02x, ", pACB->IRQLevel); + SPRINTF(" SelTimeout %ims\n", (1638 * pACB->sel_timeout) / 1000); + + SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun); + SPRINTF("AdapterID %i\n", shpnt->this_id); + + SPRINTF("TagMaxNum %i, Status %i", pACB->TagMaxNum, pACB->status); + /*SPRINTF(", DMA_Status %i\n", DC395x_read8(TRM_S1040_DMA_STATUS)); */ + SPRINTF(", FilterCfg 0x%02x", + DC395x_read8(TRM_S1040_SCSI_CONFIG1)); + SPRINTF(", DelayReset %is\n", + dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime); + /*SPRINTF("\n"); */ + + SPRINTF("Nr of attached devices: %i, Nr of DCBs: %i\n", + pACB->DeviceCnt, pACB->DCBCnt); + SPRINTF + ("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n", + pACB->DCBmap[0], pACB->DCBmap[1], pACB->DCBmap[2], + pACB->DCBmap[3], pACB->DCBmap[4], pACB->DCBmap[5], + pACB->DCBmap[6], pACB->DCBmap[7]); + SPRINTF + (" %02x %02x %02x %02x %02x %02x %02x %02x\n", + pACB->DCBmap[8], pACB->DCBmap[9], pACB->DCBmap[10], + pACB->DCBmap[11], pACB->DCBmap[12], pACB->DCBmap[13], + pACB->DCBmap[14], pACB->DCBmap[15]); + + SPRINTF + ("Un ID LUN Prty Sync Wide DsCn SndS TagQ NegoPeriod SyncFreq SyncOffs MaxCmd\n"); + + pDCB = pACB->pLinkDCB; + for (dev = 0; dev < pACB->DCBCnt; dev++) { + int NegoPeriod; + SPRINTF("%02i %02i %02i ", dev, pDCB->TargetID, + pDCB->TargetLUN); + YESNO(pDCB->DevMode & NTC_DO_PARITY_CHK); + YESNO(pDCB->SyncOffset); + YESNO(pDCB->SyncPeriod & WIDE_SYNC); + YESNO(pDCB->DevMode & NTC_DO_DISCONNECT); + YESNO(pDCB->DevMode & NTC_DO_SEND_START); + YESNO(pDCB->SyncMode & EN_TAG_QUEUEING); + NegoPeriod = + dc395x_clock_period[pDCB->SyncPeriod & 0x07] << 2; + if (pDCB->SyncOffset) + SPRINTF(" %03i ns ", NegoPeriod); + else + SPRINTF(" (%03i ns)", (pDCB->MinNegoPeriod << 2)); + + if (pDCB->SyncOffset & 0x0f) { + spd = 1000 / (NegoPeriod); + spd1 = 1000 % (NegoPeriod); + spd1 = (spd1 * 10 + NegoPeriod / 2) / (NegoPeriod); + SPRINTF(" %2i.%1i M %02i ", spd, spd1, + (pDCB->SyncOffset & 0x0f)); + } else + SPRINTF(" "); + + /* Add more info ... */ + SPRINTF(" %02i\n", pDCB->MaxCommand); + pDCB = pDCB->pNextDCB; + } + + SPRINTF("Commands in Queues: Query: %i:", pACB->QueryCnt); + for (pcmd = pACB->pQueryHead; pcmd; + pcmd = (Scsi_Cmnd *) pcmd->host_scribble) + SPRINTF(" %li", pcmd->pid); + if (timer_pending(&pACB->Waiting_Timer)) + SPRINTF("Waiting queue timer running\n"); + else + SPRINTF("\n"); + pDCB = pACB->pLinkDCB; + + for (dev = 0; dev < pACB->DCBCnt; dev++) { + struct ScsiReqBlk *pSRB; + if (pDCB->WaitSRBCnt) + SPRINTF("DCB (%02i-%i): Waiting: %i:", + pDCB->TargetID, pDCB->TargetLUN, + pDCB->WaitSRBCnt); + for (pSRB = pDCB->pWaitingSRB; pSRB; pSRB = pSRB->pNextSRB) + SPRINTF(" %li", pSRB->pcmd->pid); + if (pDCB->GoingSRBCnt) + SPRINTF("\nDCB (%02i-%i): Going : %i:", + pDCB->TargetID, pDCB->TargetLUN, + pDCB->GoingSRBCnt); + for (pSRB = pDCB->pGoingSRB; pSRB; pSRB = pSRB->pNextSRB) +#ifdef DC395x_DEBUGTRACE + SPRINTF("\n %s", pSRB->debugtrace); +#else + SPRINTF(" %li", pSRB->pcmd->pid); +#endif + if (pDCB->WaitSRBCnt || pDCB->GoingSRBCnt) + SPRINTF("\n"); + pDCB = pDCB->pNextDCB; + } + +#ifdef DC395x_DEBUGDCB + SPRINTF("DCB list for ACB %p:\n", pACB); + pDCB = pACB->pLinkDCB; + SPRINTF("%p", pDCB); + for (dev = 0; dev < pACB->DCBCnt; dev++, pDCB = pDCB->pNextDCB) + SPRINTF("->%p", pDCB->pNextDCB); + SPRINTF("\n"); +#endif + + *start = buffer + offset; + DC395x_UNLOCK_IO(pACB->pScsiHost); + + if (pos - buffer < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + else + return length; +} + + +/* + * Function : int DC395x_shutdown (struct Scsi_Host *host) + * Purpose : does a clean (we hope) shutdown of the SCSI chip. + * Use prior to dumping core, unloading the driver, etc. + * Returns : 0 on success + */ +int DC395x_shutdown(struct Scsi_Host *host) +{ + struct AdapterCtlBlk *pACB; + pACB = (struct AdapterCtlBlk *) (host->hostdata); + + /* pACB->soft_reset(host); */ + + /* disable interrupt */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0); + if (timer_pending(&pACB->Waiting_Timer)) + del_timer(&pACB->Waiting_Timer); + if (timer_pending(&pACB->SelTO_Timer)) + del_timer(&pACB->SelTO_Timer); + + if (1 || pACB->Config & HCC_SCSI_RESET) + DC395x_ResetSCSIBus(pACB); + + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); +#ifdef DC395x_DEBUGTRACE + DC395x_free_tracebufs(pACB, DC395x_MAX_SRB_CNT); +#endif + DC395x_free_SG_tables(pACB, DC395x_MAX_SRB_CNT); + return 0; +} + + +/* + * Free all DCBs + */ +void DC395x_freeDCBs(struct Scsi_Host *host) +{ + struct DeviceCtlBlk *pDCB; + struct DeviceCtlBlk *nDCB; + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) (host->hostdata); + + DCBDEBUG(printk + (KERN_INFO DC395X_NAME ": Free %i DCBs\n", pACB->DCBCnt); + ) + pDCB = pACB->pLinkDCB; + if (pDCB) { + do { + nDCB = pDCB->pNextDCB; + DCBDEBUG(printk + (KERN_INFO DC395X_NAME + ": Free DCB (ID %i, LUN %i): %p\n", + pDCB->TargetID, pDCB->TargetLUN, pDCB); + ) + DC395x_remove_dev(pACB, pDCB); /* includes a KFREE(pDCB); */ + printk("."); + pDCB = nDCB; + } while (pDCB && pACB->pLinkDCB); + } +} + + +/* + * Release method + * + * Called when we are to shutdown the controller and release all of + * it's resources. + */ +static int DC395x_release(struct Scsi_Host *host) +{ + struct AdapterCtlBlk *pACB = + (struct AdapterCtlBlk *) (host->hostdata); + unsigned long flags; + + printk(DC395X_NAME ": release"); + + DC395x_LOCK_IO(pACB->pScsiHost); + DC395x_shutdown(host); + DC395x_freeDCBs(host); + + if (host->irq != NO_IRQ) { + /* + * Find the IRQ to release. XXX Why didn't we just store the + * appropriate IRQ details when we request_irq it? + */ + int irq_count; + for (irq_count = 0, pACB = DC395x_pACB_start; + pACB != (struct AdapterCtlBlk *) -1; + pACB = pACB->pNextACB) { + if (pACB->IRQLevel == host->irq) + ++irq_count; + } + if (irq_count == 1) + free_irq(host->irq, DC395x_pACB_start); + } + release_region(host->io_port, host->n_io_port); + + DC395x_UNLOCK_IO(pACB->pScsiHost); + + return 1; +} + + +/* + * SCSI host template + */ +static Scsi_Host_Template driver_template = { + .proc_name = DC395X_NAME, + .proc_info = DC395x_proc_info, + .name = DC395X_BANNER " " DC395X_VERSION, + .detect = DC395x_detect, + .release = DC395x_release, + .queuecommand = DC395x_queue_command, + .bios_param = DC395x_bios_param, + .slave_alloc = DC395x_slave_alloc, + .slave_destroy = DC395x_slave_destroy, + .can_queue = DC395x_MAX_CAN_QUEUE, + .this_id = 7, + .sg_tablesize = DC395x_MAX_SG_TABLESIZE, + .cmd_per_lun = DC395x_MAX_CMD_PER_LUN, + .eh_abort_handler = DC395x_eh_abort, + .eh_bus_reset_handler = DC395x_eh_bus_reset, + .unchecked_isa_dma = 0, + .use_clustering = DISABLE_CLUSTERING, +}; +/* + * The following code deals with registering the above scsi host + * template with the higher level scsi code and results in the detect + * method from the template being called during initialisation. + */ +#include "scsi_module.c" diff -urN linux-2.5.68-bk7/drivers/scsi/dc395x.h linux-2.5.68-bk8/drivers/scsi/dc395x.h --- linux-2.5.68-bk7/drivers/scsi/dc395x.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.68-bk8/drivers/scsi/dc395x.h 2003-04-27 04:36:28.000000000 -0700 @@ -0,0 +1,659 @@ +/************************************************************************/ +/* */ +/* dc395x.h */ +/* */ +/* Device Driver for Tekram DC395(U/UW/F), DC315(U) */ +/* PCI SCSI Bus Master Host Adapter */ +/* (SCSI chip set used Tekram ASIC TRM-S1040) */ +/* */ +/************************************************************************/ + +#ifndef DC395x_H +#define DC395x_H + +/************************************************************************/ +/* */ +/* Name, Banner and Version */ +/* */ +/************************************************************************/ +#define DC395X_NAME "dc395x" +#define DC395X_BANNER "Tekram DC395(U/UW/F), DC315(U) - ASIC TRM-S1040" +#define DC395X_VERSION "v2.02, 2003/04/20" + +/************************************************************************/ +/* */ +/* Initial values */ +/* */ +/************************************************************************/ +#define DC395x_MAX_CMD_QUEUE 32 +/* #define DC395x_MAX_QTAGS 32 */ +#define DC395x_MAX_QTAGS 16 +#define DC395x_MAX_ADAPTER_NUM 4 +#define DC395x_MAX_SCSI_ID 16 +#define DC395x_MAX_CMD_PER_LUN DC395x_MAX_QTAGS +#define DC395x_MAX_SG_TABLESIZE 64 /* HW limitation */ +#define DC395x_MAX_SG_LISTENTRY 64 /* Must be equal or lower to previous */ + /* item */ +#define DC395x_MAX_SRB_CNT 63 +/* #define DC395x_MAX_CAN_QUEUE 7 * DC395x_MAX_QTAGS */ +#define DC395x_MAX_CAN_QUEUE DC395x_MAX_SRB_CNT +#define DC395x_END_SCAN 2 +#define DC395x_SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ +#define DC395x_MAX_RETRIES 3 + +#if 0 +#define SYNC_FIRST +#endif + +#define NORM_REC_LVL 0 + +/************************************************************************/ +/* */ +/* Various definitions */ +/* */ +/************************************************************************/ +#define BIT31 0x80000000 +#define BIT30 0x40000000 +#define BIT29 0x20000000 +#define BIT28 0x10000000 +#define BIT27 0x08000000 +#define BIT26 0x04000000 +#define BIT25 0x02000000 +#define BIT24 0x01000000 +#define BIT23 0x00800000 +#define BIT22 0x00400000 +#define BIT21 0x00200000 +#define BIT20 0x00100000 +#define BIT19 0x00080000 +#define BIT18 0x00040000 +#define BIT17 0x00020000 +#define BIT16 0x00010000 +#define BIT15 0x00008000 +#define BIT14 0x00004000 +#define BIT13 0x00002000 +#define BIT12 0x00001000 +#define BIT11 0x00000800 +#define BIT10 0x00000400 +#define BIT9 0x00000200 +#define BIT8 0x00000100 +#define BIT7 0x00000080 +#define BIT6 0x00000040 +#define BIT5 0x00000020 +#define BIT4 0x00000010 +#define BIT3 0x00000008 +#define BIT2 0x00000004 +#define BIT1 0x00000002 +#define BIT0 0x00000001 + +/* UnitCtrlFlag */ +#define UNIT_ALLOCATED BIT0 +#define UNIT_INFO_CHANGED BIT1 +#define FORMATING_MEDIA BIT2 +#define UNIT_RETRY BIT3 + +/* UnitFlags */ +#define DASD_SUPPORT BIT0 +#define SCSI_SUPPORT BIT1 +#define ASPI_SUPPORT BIT2 + +/* SRBState machine definition */ +#define SRB_FREE 0x0000 +#define SRB_WAIT 0x0001 +#define SRB_READY 0x0002 +#define SRB_MSGOUT 0x0004 /* arbitration+msg_out 1st byte */ +#define SRB_MSGIN 0x0008 +#define SRB_EXTEND_MSGIN 0x0010 +#define SRB_COMMAND 0x0020 +#define SRB_START_ 0x0040 /* arbitration+msg_out+command_out */ +#define SRB_DISCONNECT 0x0080 +#define SRB_DATA_XFER 0x0100 +#define SRB_XFERPAD 0x0200 +#define SRB_STATUS 0x0400 +#define SRB_COMPLETED 0x0800 +#define SRB_ABORT_SENT 0x1000 +#define SRB_DO_SYNC_NEGO 0x2000 +#define SRB_DO_WIDE_NEGO 0x4000 +#define SRB_UNEXPECT_RESEL 0x8000 + +/************************************************************************/ +/* */ +/* ACB Config */ +/* */ +/************************************************************************/ +#define HCC_WIDE_CARD 0x20 +#define HCC_SCSI_RESET 0x10 +#define HCC_PARITY 0x08 +#define HCC_AUTOTERM 0x04 +#define HCC_LOW8TERM 0x02 +#define HCC_UP8TERM 0x01 + +/* ACBFlag */ +#define RESET_DEV BIT0 +#define RESET_DETECT BIT1 +#define RESET_DONE BIT2 + +/* DCBFlag */ +#define ABORT_DEV_ BIT0 + +/* SRBstatus */ +#define SRB_OK BIT0 +#define ABORTION BIT1 +#define OVER_RUN BIT2 +#define UNDER_RUN BIT3 +#define PARITY_ERROR BIT4 +#define SRB_ERROR BIT5 + +/* SRBFlag */ +#define DATAOUT BIT7 +#define DATAIN BIT6 +#define RESIDUAL_VALID BIT5 +#define ENABLE_TIMER BIT4 +#define RESET_DEV0 BIT2 +#define ABORT_DEV BIT1 +#define AUTO_REQSENSE BIT0 + +/* Adapter status */ +#define H_STATUS_GOOD 0 +#define H_SEL_TIMEOUT 0x11 +#define H_OVER_UNDER_RUN 0x12 +#define H_UNEXP_BUS_FREE 0x13 +#define H_TARGET_PHASE_F 0x14 +#define H_INVALID_CCB_OP 0x16 +#define H_LINK_CCB_BAD 0x17 +#define H_BAD_TARGET_DIR 0x18 +#define H_DUPLICATE_CCB 0x19 +#define H_BAD_CCB_OR_SG 0x1A +#define H_ABORT 0x0FF + +/* SCSI BUS Status byte codes */ +#define SCSI_STAT_GOOD 0x0 /* Good status */ +#define SCSI_STAT_CHECKCOND 0x02 /* SCSI Check Condition */ +#define SCSI_STAT_CONDMET 0x04 /* Condition Met */ +#define SCSI_STAT_BUSY 0x08 /* Target busy status */ +#define SCSI_STAT_INTER 0x10 /* Intermediate status */ +#define SCSI_STAT_INTERCONDMET 0x14 /* Intermediate condition met */ +#define SCSI_STAT_RESCONFLICT 0x18 /* Reservation conflict */ +#define SCSI_STAT_CMDTERM 0x22 /* Command Terminated */ +#define SCSI_STAT_QUEUEFULL 0x28 /* Queue Full */ +#define SCSI_STAT_UNEXP_BUS_F 0xFD /* Unexpect Bus Free */ +#define SCSI_STAT_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */ +#define SCSI_STAT_SEL_TIMEOUT 0xFF /* Selection Time out */ + +/* Sync_Mode */ +#define SYNC_WIDE_TAG_ATNT_DISABLE 0 +#define SYNC_NEGO_ENABLE BIT0 +#define SYNC_NEGO_DONE BIT1 +#define WIDE_NEGO_ENABLE BIT2 +#define WIDE_NEGO_DONE BIT3 +#define WIDE_NEGO_STATE BIT4 +#define EN_TAG_QUEUEING BIT5 +#define EN_ATN_STOP BIT6 + +#define SYNC_NEGO_OFFSET 15 + +/* SCSI MSG BYTE */ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_PTR 0x02 +#define MSG_RESTORE_PTR 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_INITIATOR_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT_ 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINK_CMD_COMPL 0x0A +#define MSG_LINK_CMD_COMPL_FLG 0x0B +#define MSG_BUS_RESET 0x0C +#define MSG_ABORT_TAG 0x0D +#define MSG_SIMPLE_QTAG 0x20 +#define MSG_HEAD_QTAG 0x21 +#define MSG_ORDER_QTAG 0x22 +#define MSG_IGNOREWIDE 0x23 +#define MSG_IDENTIFY 0x80 +#define MSG_HOST_ID 0xC0 + +/* SCSI STATUS BYTE */ +#define STATUS_GOOD 0x00 +#define CHECK_CONDITION_ 0x02 +#define STATUS_BUSY 0x08 +#define STATUS_INTERMEDIATE 0x10 +#define RESERVE_CONFLICT 0x18 + +/* cmd->result */ +#define STATUS_MASK_ 0xFF +#define MSG_MASK 0xFF00 +#define RETURN_MASK 0xFF0000 + +/************************************************************************/ +/* */ +/* Inquiry Data format */ +/* */ +/************************************************************************/ +struct ScsiInqData +{ /* INQ */ + u8 DevType; /* Periph Qualifier & Periph Dev Type */ + u8 RMB_TypeMod; /* rem media bit & Dev Type Modifier */ + u8 Vers; /* ISO, ECMA, & ANSI versions */ + u8 RDF; /* AEN, TRMIOP, & response data format */ + u8 AddLen; /* length of additional data */ + u8 Res1; /* reserved */ + u8 Res2; /* reserved */ + u8 Flags; /* RelADr, Wbus32, Wbus16, Sync, etc. */ + u8 VendorID[8]; /* Vendor Identification */ + u8 ProductID[16]; /* Product Identification */ + u8 ProductRev[4]; /* Product Revision */ +}; + + /* Inquiry byte 0 masks */ +#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */ +#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */ + /* Inquiry byte 1 mask */ +#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */ + /* Peripheral Device Type definitions */ + /* See include/scsi/scsi.h */ +#define TYPE_NODEV SCSI_DEVTYPE /* Unknown or no device type */ +#ifndef TYPE_PRINTER /* */ +# define TYPE_PRINTER 0x02 /* Printer device */ +#endif /* */ +#ifndef TYPE_COMM /* */ +# define TYPE_COMM 0x09 /* Communications device */ +#endif + +/************************************************************************/ +/* */ +/* Inquiry flag definitions (Inq data byte 7) */ +/* */ +/************************************************************************/ +#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing */ +#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */ +#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */ +#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */ +#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */ +#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */ +#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */ + +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 + +/************************************************************************/ +/* */ +/* The PCI configuration register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_ID 0x00 /* Vendor and Device ID */ +#define TRM_S1040_COMMAND 0x04 /* PCI command register */ +#define TRM_S1040_IOBASE 0x10 /* I/O Space base address */ +#define TRM_S1040_ROMBASE 0x30 /* Expansion ROM Base Address */ +#define TRM_S1040_INTLINE 0x3C /* Interrupt line */ + +/************************************************************************/ +/* */ +/* The SCSI register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_SCSI_STATUS 0x80 /* SCSI Status (R) */ +#define COMMANDPHASEDONE 0x2000 /* SCSI command phase done */ +#define SCSIXFERDONE 0x0800 /* SCSI SCSI transfer done */ +#define SCSIXFERCNT_2_ZERO 0x0100 /* SCSI SCSI transfer count to zero */ +#define SCSIINTERRUPT 0x0080 /* SCSI interrupt pending */ +#define COMMANDABORT 0x0040 /* SCSI command abort */ +#define SEQUENCERACTIVE 0x0020 /* SCSI sequencer active */ +#define PHASEMISMATCH 0x0010 /* SCSI phase mismatch */ +#define PARITYERROR 0x0008 /* SCSI parity error */ + +#define PHASEMASK 0x0007 /* Phase MSG/CD/IO */ +#define PH_DATA_OUT 0x00 /* Data out phase */ +#define PH_DATA_IN 0x01 /* Data in phase */ +#define PH_COMMAND 0x02 /* Command phase */ +#define PH_STATUS 0x03 /* Status phase */ +#define PH_BUS_FREE 0x05 /* Invalid phase used as bus free */ +#define PH_MSG_OUT 0x06 /* Message out phase */ +#define PH_MSG_IN 0x07 /* Message in phase */ + +#define TRM_S1040_SCSI_CONTROL 0x80 /* SCSI Control (W) */ +#define DO_CLRATN 0x0400 /* Clear ATN */ +#define DO_SETATN 0x0200 /* Set ATN */ +#define DO_CMDABORT 0x0100 /* Abort SCSI command */ +#define DO_RSTMODULE 0x0010 /* Reset SCSI chip */ +#define DO_RSTSCSI 0x0008 /* Reset SCSI bus */ +#define DO_CLRFIFO 0x0004 /* Clear SCSI transfer FIFO */ +#define DO_DATALATCH 0x0002 /* Enable SCSI bus data input (latched) */ +/* #define DO_DATALATCH 0x0000 */ /* KG: DISable SCSI bus data latch */ +#define DO_HWRESELECT 0x0001 /* Enable hardware reselection */ + +#define TRM_S1040_SCSI_FIFOCNT 0x82 /* SCSI FIFO Counter 5bits(R) */ +#define TRM_S1040_SCSI_SIGNAL 0x83 /* SCSI low level signal (R/W) */ + +#define TRM_S1040_SCSI_INTSTATUS 0x84 /* SCSI Interrupt Status (R) */ +#define INT_SCAM 0x80 /* SCAM selection interrupt */ +#define INT_SELECT 0x40 /* Selection interrupt */ +#define INT_SELTIMEOUT 0x20 /* Selection timeout interrupt */ +#define INT_DISCONNECT 0x10 /* Bus disconnected interrupt */ +#define INT_RESELECTED 0x08 /* Reselected interrupt */ +#define INT_SCSIRESET 0x04 /* SCSI reset detected interrupt */ +#define INT_BUSSERVICE 0x02 /* Bus service interrupt */ +#define INT_CMDDONE 0x01 /* SCSI command done interrupt */ + +#define TRM_S1040_SCSI_OFFSET 0x84 /* SCSI Offset Count (W) */ + +/************************************************************************/ +/* */ +/* Bit Name Definition */ +/* --------- ------------- ---------------------------- */ +/* 07-05 0 RSVD Reversed. Always 0. */ +/* 04 0 OFFSET4 Reversed for LVDS. Always 0. */ +/* 03-00 0 OFFSET[03:00] Offset number from 0 to 15 */ +/* */ +/************************************************************************/ + +#define TRM_S1040_SCSI_SYNC 0x85 /* SCSI Synchronous Control (R/W) */ +#define LVDS_SYNC 0x20 /* Enable LVDS synchronous */ +#define WIDE_SYNC 0x10 /* Enable WIDE synchronous */ +#define ALT_SYNC 0x08 /* Enable Fast-20 alternate synchronous */ + +/************************************************************************/ +/* */ +/* SYNCM 7 6 5 4 3 2 1 0 */ +/* Name RSVD RSVD LVDS WIDE ALTPERD PERIOD2 PERIOD1 PERIOD0 */ +/* Default 0 0 0 0 0 0 0 0 */ +/* */ +/* Bit Name Definition */ +/* --------- ------------- --------------------------- */ +/* 07-06 0 RSVD Reversed. Always read 0 */ +/* 05 0 LVDS Reversed. Always read 0 */ +/* 04 0 WIDE/WSCSI Enable wide (16-bits) SCSI */ +/* transfer. */ +/* 03 0 ALTPERD/ALTPD Alternate (Sync./Period) mode. */ +/* */ +/* @@ When this bit is set, */ +/* the synchronous period bits 2:0 */ +/* in the Synchronous Mode register */ +/* are used to transfer data */ +/* at the Fast-20 rate. */ +/* @@ When this bit is unset, */ +/* the synchronous period bits 2:0 */ +/* in the Synchronous Mode Register */ +/* are used to transfer data */ +/* at the Fast-10 rate (or Fast-40 w/ LVDS). */ +/* */ +/* 02-00 0 PERIOD[2:0]/ Synchronous SCSI Transfer Rate. */ +/* SXPD[02:00] These 3 bits specify */ +/* the Synchronous SCSI Transfer */ +/* Rate for Fast-20 and Fast-10. */ +/* These bits are also reset */ +/* by a SCSI Bus reset. */ +/* */ +/* For Fast-10 bit ALTPD = 0 and LVDS = 0 */ +/* and bit2,bit1,bit0 is defined as follows : */ +/* */ +/* 000 100ns, 10.0 MHz */ +/* 001 150ns, 6.6 MHz */ +/* 010 200ns, 5.0 MHz */ +/* 011 250ns, 4.0 MHz */ +/* 100 300ns, 3.3 MHz */ +/* 101 350ns, 2.8 MHz */ +/* 110 400ns, 2.5 MHz */ +/* 111 450ns, 2.2 MHz */ +/* */ +/* For Fast-20 bit ALTPD = 1 and LVDS = 0 */ +/* and bit2,bit1,bit0 is defined as follows : */ +/* */ +/* 000 50ns, 20.0 MHz */ +/* 001 75ns, 13.3 MHz */ +/* 010 100ns, 10.0 MHz */ +/* 011 125ns, 8.0 MHz */ +/* 100 150ns, 6.6 MHz */ +/* 101 175ns, 5.7 MHz */ +/* 110 200ns, 5.0 MHz */ +/* 111 250ns, 4.0 MHz KG: Maybe 225ns, 4.4 MHz */ +/* */ +/* For Fast-40 bit ALTPD = 0 and LVDS = 1 */ +/* and bit2,bit1,bit0 is defined as follows : */ +/* */ +/* 000 25ns, 40.0 MHz */ +/* 001 50ns, 20.0 MHz */ +/* 010 75ns, 13.3 MHz */ +/* 011 100ns, 10.0 MHz */ +/* 100 125ns, 8.0 MHz */ +/* 101 150ns, 6.6 MHz */ +/* 110 175ns, 5.7 MHz */ +/* 111 200ns, 5.0 MHz */ +/* */ +/************************************************************************/ + +#define TRM_S1040_SCSI_TARGETID 0x86 /* SCSI Target ID (R/W) */ +#define TRM_S1040_SCSI_IDMSG 0x87 /* SCSI Identify Message (R) */ +#define TRM_S1040_SCSI_HOSTID 0x87 /* SCSI Host ID (W) */ +#define TRM_S1040_SCSI_COUNTER 0x88 /* SCSI Transfer Counter 24bits(R/W) */ + +#define TRM_S1040_SCSI_INTEN 0x8C /* SCSI Interrupt Enable (R/W) */ +#define EN_SCAM 0x80 /* Enable SCAM selection interrupt */ +#define EN_SELECT 0x40 /* Enable selection interrupt */ +#define EN_SELTIMEOUT 0x20 /* Enable selection timeout interrupt */ +#define EN_DISCONNECT 0x10 /* Enable bus disconnected interrupt */ +#define EN_RESELECTED 0x08 /* Enable reselected interrupt */ +#define EN_SCSIRESET 0x04 /* Enable SCSI reset detected interrupt */ +#define EN_BUSSERVICE 0x02 /* Enable bus service interrupt */ +#define EN_CMDDONE 0x01 /* Enable SCSI command done interrupt */ + +#define TRM_S1040_SCSI_CONFIG0 0x8D /* SCSI Configuration 0 (R/W) */ +#define PHASELATCH 0x40 /* Enable phase latch */ +#define INITIATOR 0x20 /* Enable initiator mode */ +#define PARITYCHECK 0x10 /* Enable parity check */ +#define BLOCKRST 0x01 /* Disable SCSI reset1 */ + +#define TRM_S1040_SCSI_CONFIG1 0x8E /* SCSI Configuration 1 (R/W) */ +#define ACTIVE_NEGPLUS 0x10 /* Enhance active negation */ +#define FILTER_DISABLE 0x08 /* Disable SCSI data filter */ +#define FAST_FILTER 0x04 /* ? */ +#define ACTIVE_NEG 0x02 /* Enable active negation */ + +#define TRM_S1040_SCSI_CONFIG2 0x8F /* SCSI Configuration 2 (R/W) */ +#define CFG2_WIDEFIFO 0x02 /* */ + +#define TRM_S1040_SCSI_COMMAND 0x90 /* SCSI Command (R/W) */ +#define SCMD_COMP 0x12 /* Command complete */ +#define SCMD_SEL_ATN 0x60 /* Selection with ATN */ +#define SCMD_SEL_ATN3 0x64 /* Selection with ATN3 */ +#define SCMD_SEL_ATNSTOP 0xB8 /* Selection with ATN and Stop */ +#define SCMD_FIFO_OUT 0xC0 /* SCSI FIFO transfer out */ +#define SCMD_DMA_OUT 0xC1 /* SCSI DMA transfer out */ +#define SCMD_FIFO_IN 0xC2 /* SCSI FIFO transfer in */ +#define SCMD_DMA_IN 0xC3 /* SCSI DMA transfer in */ +#define SCMD_MSGACCEPT 0xD8 /* Message accept */ + +/************************************************************************/ +/* */ +/* Code Command Description */ +/* ---- ---------------------------------------- */ +/* 02 Enable reselection with FIFO */ +/* 40 Select without ATN with FIFO */ +/* 60 Select with ATN with FIFO */ +/* 64 Select with ATN3 with FIFO */ +/* A0 Select with ATN and stop with FIFO */ +/* C0 Transfer information out with FIFO */ +/* C1 Transfer information out with DMA */ +/* C2 Transfer information in with FIFO */ +/* C3 Transfer information in with DMA */ +/* 12 Initiator command complete with FIFO */ +/* 50 Initiator transfer information out sequence without ATN */ +/* with FIFO */ +/* 70 Initiator transfer information out sequence with ATN */ +/* with FIFO */ +/* 74 Initiator transfer information out sequence with ATN3 */ +/* with FIFO */ +/* 52 Initiator transfer information in sequence without ATN */ +/* with FIFO */ +/* 72 Initiator transfer information in sequence with ATN */ +/* with FIFO */ +/* 76 Initiator transfer information in sequence with ATN3 */ +/* with FIFO */ +/* 90 Initiator transfer information out command complete */ +/* with FIFO */ +/* 92 Initiator transfer information in command complete */ +/* with FIFO */ +/* D2 Enable selection */ +/* 08 Reselection */ +/* 48 Disconnect command with FIFO */ +/* 88 Terminate command with FIFO */ +/* C8 Target command complete with FIFO */ +/* 18 SCAM Arbitration/ Selection */ +/* 5A Enable reselection */ +/* 98 Select without ATN with FIFO */ +/* B8 Select with ATN with FIFO */ +/* D8 Message Accepted */ +/* 58 NOP */ +/* */ +/************************************************************************/ + +#define TRM_S1040_SCSI_TIMEOUT 0x91 /* SCSI Time Out Value (R/W) */ +#define TRM_S1040_SCSI_FIFO 0x98 /* SCSI FIFO (R/W) */ + +#define TRM_S1040_SCSI_TCR0 0x9C /* SCSI Target Control 0 (R/W) */ +#define TCR0_WIDE_NEGO_DONE 0x8000 /* Wide nego done */ +#define TCR0_SYNC_NEGO_DONE 0x4000 /* Synchronous nego done */ +#define TCR0_ENABLE_LVDS 0x2000 /* Enable LVDS synchronous */ +#define TCR0_ENABLE_WIDE 0x1000 /* Enable WIDE synchronous */ +#define TCR0_ENABLE_ALT 0x0800 /* Enable alternate synchronous */ +#define TCR0_PERIOD_MASK 0x0700 /* Transfer rate */ + +#define TCR0_DO_WIDE_NEGO 0x0080 /* Do wide NEGO */ +#define TCR0_DO_SYNC_NEGO 0x0040 /* Do sync NEGO */ +#define TCR0_DISCONNECT_EN 0x0020 /* Disconnection enable */ +#define TCR0_OFFSET_MASK 0x001F /* Offset number */ + +#define TRM_S1040_SCSI_TCR1 0x9E /* SCSI Target Control 1 (R/W) */ +#define MAXTAG_MASK 0x7F00 /* Maximum tags (127) */ +#define NON_TAG_BUSY 0x0080 /* Non tag command active */ +#define ACTTAG_MASK 0x007F /* Active tags */ + +/************************************************************************/ +/* */ +/* The DMA register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_DMA_COMMAND 0xA0 /* DMA Command (R/W) */ +#define DMACMD_SG 0x02 /* Enable HW S/G support */ +#define DMACMD_DIR 0x01 /* 1 = read from SCSI write to Host */ +#define XFERDATAIN_SG 0x0103 /* Transfer data in w/ SG */ +#define XFERDATAOUT_SG 0x0102 /* Transfer data out w/ SG */ +#define XFERDATAIN 0x0101 /* Transfer data in w/o SG */ +#define XFERDATAOUT 0x0100 /* Transfer data out w/o SG */ + +#define TRM_S1040_DMA_FIFOCNT 0xA1 /* DMA FIFO Counter (R) */ + +#define TRM_S1040_DMA_CONTROL 0xA1 /* DMA Control (W) */ +#define DMARESETMODULE 0x10 /* Reset PCI/DMA module */ +#define STOPDMAXFER 0x08 /* Stop DMA transfer */ +#define ABORTXFER 0x04 /* Abort DMA transfer */ +#define CLRXFIFO 0x02 /* Clear DMA transfer FIFO */ +#define STARTDMAXFER 0x01 /* Start DMA transfer */ + +#define TRM_S1040_DMA_FIFOSTAT 0xA2 /* DMA FIFO Status (R) */ + +#define TRM_S1040_DMA_STATUS 0xA3 /* DMA Interrupt Status (R/W) */ +#define XFERPENDING 0x80 /* Transfer pending */ +#define SCSIBUSY 0x40 /* SCSI busy */ +#define GLOBALINT 0x20 /* DMA_INTEN bit 0-4 set */ +#define FORCEDMACOMP 0x10 /* Force DMA transfer complete */ +#define DMAXFERERROR 0x08 /* DMA transfer error */ +#define DMAXFERABORT 0x04 /* DMA transfer abort */ +#define DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define SCSICOMP 0x01 /* SCSI complete interrupt */ + +#define TRM_S1040_DMA_INTEN 0xA4 /* DMA Interrupt Enable (R/W) */ +#define EN_FORCEDMACOMP 0x10 /* Force DMA transfer complete */ +#define EN_DMAXFERERROR 0x08 /* DMA transfer error */ +#define EN_DMAXFERABORT 0x04 /* DMA transfer abort */ +#define EN_DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define EN_SCSIINTR 0x01 /* Enable SCSI complete interrupt */ + +#define TRM_S1040_DMA_CONFIG 0xA6 /* DMA Configuration (R/W) */ +#define DMA_ENHANCE 0x8000 /* Enable DMA enhance feature (SG?) */ +#define DMA_PCI_DUAL_ADDR 0x4000 /* */ +#define DMA_CFG_RES 0x2000 /* Always 1 */ +#define DMA_AUTO_CLR_FIFO 0x1000 /* DISable DMA auto clear FIFO */ +#define DMA_MEM_MULTI_READ 0x0800 /* */ +#define DMA_MEM_WRITE_INVAL 0x0400 /* Memory write and invalidate */ +#define DMA_FIFO_CTRL 0x0300 /* Control FIFO operation with DMA */ +#define DMA_FIFO_HALF_HALF 0x0200 /* Keep half filled on both read/write */ + +#define TRM_S1040_DMA_XCNT 0xA8 /* DMA Transfer Counter (R/W), 24bits */ +#define TRM_S1040_DMA_CXCNT 0xAC /* DMA Current Transfer Counter (R) */ +#define TRM_S1040_DMA_XLOWADDR 0xB0 /* DMA Transfer Physical Low Address */ +#define TRM_S1040_DMA_XHIGHADDR 0xB4 /* DMA Transfer Physical High Address */ + +/************************************************************************/ +/* */ +/* The general register offset for TRM_S1040 */ +/* */ +/************************************************************************/ +#define TRM_S1040_GEN_CONTROL 0xD4 /* Global Control */ +#define CTRL_LED 0x80 /* Control onboard LED */ +#define EN_EEPROM 0x10 /* Enable EEPROM programming */ +#define DIS_TERM 0x08 /* Disable onboard termination */ +#define AUTOTERM 0x04 /* Enable Auto SCSI terminator */ +#define LOW8TERM 0x02 /* Enable Lower 8 bit SCSI terminator */ +#define UP8TERM 0x01 /* Enable Upper 8 bit SCSI terminator */ + +#define TRM_S1040_GEN_STATUS 0xD5 /* Global Status */ +#define GTIMEOUT 0x80 /* Global timer reach 0 */ +#define EXT68HIGH 0x40 /* Higher 8 bit connected externally */ +#define INT68HIGH 0x20 /* Higher 8 bit connected internally */ +#define CON5068 0x10 /* External 50/68 pin connected (low) */ +#define CON68 0x08 /* Internal 68 pin connected (low) */ +#define CON50 0x04 /* Internal 50 pin connected (low!) */ +#define WIDESCSI 0x02 /* Wide SCSI card */ +#define STATUS_LOAD_DEFAULT 0x01 /* */ + +#define TRM_S1040_GEN_NVRAM 0xD6 /* Serial NON-VOLATILE RAM port */ +#define NVR_BITOUT 0x08 /* Serial data out */ +#define NVR_BITIN 0x04 /* Serial data in */ +#define NVR_CLOCK 0x02 /* Serial clock */ +#define NVR_SELECT 0x01 /* Serial select */ + +#define TRM_S1040_GEN_EDATA 0xD7 /* Parallel EEPROM data port */ +#define TRM_S1040_GEN_EADDRESS 0xD8 /* Parallel EEPROM address */ +#define TRM_S1040_GEN_TIMER 0xDB /* Global timer */ + +/************************************************************************/ +/* */ +/* NvmTarCfg0: Target configuration byte 0 :..pDCB->DevMode */ +/* */ +/************************************************************************/ +#define NTC_DO_WIDE_NEGO 0x20 /* Wide negotiate */ +#define NTC_DO_TAG_QUEUEING 0x10 /* Enable SCSI tag queuing */ +#define NTC_DO_SEND_START 0x08 /* Send start command SPINUP */ +#define NTC_DO_DISCONNECT 0x04 /* Enable SCSI disconnect */ +#define NTC_DO_SYNC_NEGO 0x02 /* Sync negotiation */ +#define NTC_DO_PARITY_CHK 0x01 /* (it sould define at NAC) */ + /* Parity check enable */ + +/************************************************************************/ +/* */ +/* Nvram Initiater bits definition */ +/* */ +/************************************************************************/ +#if 0 +#define MORE2_DRV BIT0 +#define GREATER_1G BIT1 +#define RST_SCSI_BUS BIT2 +#define ACTIVE_NEGATION BIT3 +#define NO_SEEK BIT4 +#define LUN_CHECK BIT5 +#endif + +/************************************************************************/ +/* */ +/* Nvram Adapter Cfg bits definition */ +/* */ +/************************************************************************/ +#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */ +#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */ +#define NAC_GREATER_1G 0x02 /* > 1G support enable */ +#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */ +/* #define NAC_DO_PARITY_CHK 0x08 */ /* Parity check enable */ + +#endif diff -urN linux-2.5.68-bk7/drivers/scsi/eata_pio.c linux-2.5.68-bk8/drivers/scsi/eata_pio.c --- linux-2.5.68-bk7/drivers/scsi/eata_pio.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/eata_pio.c 2003-04-27 04:36:28.000000000 -0700 @@ -155,24 +155,6 @@ if (pos > offset + length) goto stop_output; - size = sprintf(buffer+len,"Attached devices: %s\n", - (!list_empty(&shost->my_devices))?"":"none"); - len += size; - pos = begin + len; - - list_for_each_entry(sdev, &shost->my_devices, siblings) { - proc_print_scsidevice(sdev, buffer, &size, len); - len += size; - pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; - } - if (pos > offset + length) - goto stop_output; - } - stop_output: DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len)); *start=buffer+(offset-begin); /* Start of wanted data */ diff -urN linux-2.5.68-bk7/drivers/scsi/gdth.c linux-2.5.68-bk8/drivers/scsi/gdth.c --- linux-2.5.68-bk7/drivers/scsi/gdth.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/gdth.c 2003-04-27 04:36:28.000000000 -0700 @@ -743,9 +743,9 @@ #ifdef GDTH_IOCTL_CHRDEV /* ioctl interface */ static struct file_operations gdth_fops = { - ioctl:gdth_ioctl, - open:gdth_open, - release:gdth_close, + .ioctl = gdth_ioctl, + .open = gdth_open, + .release = gdth_close, }; #endif @@ -3661,9 +3661,9 @@ else if (scp->SCp.Status == GDTH_MAP_SINGLE) pci_unmap_single(ha->pdev,scp->SCp.dma_handle, scp->request_bufflen,scp->SCp.Message); - if (scp->SCp.buffer) - pci_unmap_single(ha->pdev,(dma_addr_t)scp->SCp.buffer, - 16,PCI_DMA_FROMDEVICE); + if (scp->SCp.buffer) + pci_unmap_single(ha->pdev,(dma_addr_t)(u32)scp->SCp.buffer, + 16,PCI_DMA_FROMDEVICE); #endif if (ha->status == S_OK) { scp->SCp.Status = S_OK; @@ -4215,6 +4215,9 @@ /* scanning for controllers, at first: ISA controller */ for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) { + + dma_addr_t scratch_dma_handle; + if (gdth_ctr_count >= MAXHA) break; if (gdth_search_isa(isa_bios)) { /* controller found */ @@ -4271,7 +4274,8 @@ #if LINUX_VERSION_CODE >= 0x020400 ha->pdev = NULL; ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH, - &ha->scratch_phys); + &scratch_dma_handle); + ha->scratch_phys = (ulong32)scratch_dma_handle; #else ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA); if (ha->pscratch) @@ -4338,6 +4342,9 @@ /* scanning for EISA controllers */ for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) { + + dma_addr_t scratch_dma_handle; + if (gdth_ctr_count >= MAXHA) break; if (gdth_search_eisa(eisa_slot)) { /* controller found */ @@ -4381,7 +4388,8 @@ #if LINUX_VERSION_CODE >= 0x020400 ha->pdev = NULL; ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH, - &ha->scratch_phys); + &scratch_dma_handle); + ha->scratch_phys = (ulong32) scratch_dma_handle; ha->ccb_phys = pci_map_single(ha->pdev,ha->pccb, sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL); @@ -4464,6 +4472,9 @@ printk("GDT: Found %d PCI Storage RAID Controllers\n",cnt); gdth_sort_pci(pcistr,cnt); for (ctr = 0; ctr < cnt; ++ctr) { + + dma_addr_t scratch_dma_handle; + if (gdth_ctr_count >= MAXHA) break; shp = scsi_register(shtp,sizeof(gdth_ext_str)); @@ -4505,7 +4516,8 @@ ha->ccb_phys = 0L; #if LINUX_VERSION_CODE >= 0x020400 ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH, - &ha->scratch_phys); + &scratch_dma_handle); + ha->scratch_phys = (ulong32)scratch_dma_handle; #else ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA); if (ha->pscratch) @@ -4881,90 +4893,156 @@ return 0; } -static int gdth_ioctl(struct inode *inode, struct file *filep, - unsigned int cmd, unsigned long arg) +static int ioc_event(unsigned long arg) { - gdth_ha_str *ha; -#if LINUX_VERSION_CODE >= 0x020503 - Scsi_Request *srp; - Scsi_Cmnd *scp; - Scsi_Device *sdev; -#elif LINUX_VERSION_CODE >= 0x020322 - Scsi_Cmnd *scp; - Scsi_Device *sdev; -#else - Scsi_Cmnd scp; - Scsi_Device sdev; -#endif - ulong flags; - char cmnd[MAX_COMMAND_SIZE]; - memset(cmnd, 0xff, 12); - - TRACE(("gdth_ioctl() cmd 0x%x\n", cmd)); - - switch (cmd) { - case GDTIOCTL_CTRCNT: - { - int cnt = gdth_ctr_count; - put_user(cnt, (int *)arg); - break; - } + gdth_ioctl_event evt; + gdth_ha_str *ha; + ulong flags; - case GDTIOCTL_DRVERS: - { - int ver = (GDTH_VERSION<<8) | GDTH_SUBVERSION; - put_user(ver, (int *)arg); - break; - } - - case GDTIOCTL_OSVERS: - { - gdth_ioctl_osvers osv; + if (copy_from_user(&evt, (char *)arg, sizeof(gdth_ioctl_event)) || + evt.ionode >= gdth_ctr_count) + return -EFAULT; + ha = HADATA(gdth_ctr_tab[evt.ionode]); - osv.version = (unchar)(LINUX_VERSION_CODE >> 16); - osv.subversion = (unchar)(LINUX_VERSION_CODE >> 8); - osv.revision = (ushort)(LINUX_VERSION_CODE & 0xff); - copy_to_user((char *)arg, &osv, sizeof(gdth_ioctl_osvers)); - break; - } + if (evt.erase == 0xff) { + if (evt.event.event_source == ES_TEST) + evt.event.event_data.size=sizeof(evt.event.event_data.eu.test); + else if (evt.event.event_source == ES_DRIVER) + evt.event.event_data.size=sizeof(evt.event.event_data.eu.driver); + else if (evt.event.event_source == ES_SYNC) + evt.event.event_data.size=sizeof(evt.event.event_data.eu.sync); + else + evt.event.event_data.size=sizeof(evt.event.event_data.eu.async); + GDTH_LOCK_HA(ha, flags); + gdth_store_event(ha, evt.event.event_source, evt.event.event_idx, + &evt.event.event_data); + GDTH_UNLOCK_HA(ha, flags); + } else if (evt.erase == 0xfe) { + gdth_clear_events(); + } else if (evt.erase == 0) { + evt.handle = gdth_read_event(ha, evt.handle, &evt.event); + } else { + gdth_readapp_event(ha, evt.erase, &evt.event); + } + if (copy_to_user((char *)arg, &evt, sizeof(gdth_ioctl_event))) + return -EFAULT; + return 0; +} - case GDTIOCTL_CTRTYPE: - { - gdth_ioctl_ctrtype ctrt; - - if (copy_from_user(&ctrt, (char *)arg, sizeof(gdth_ioctl_ctrtype)) || - ctrt.ionode >= gdth_ctr_count) +static int ioc_lockdrv(unsigned long arg) +{ + gdth_ioctl_lockdrv ldrv; + unchar i, j; + ulong flags; + gdth_ha_str *ha; + + if (copy_from_user(&ldrv, (char *)arg, sizeof(gdth_ioctl_lockdrv)) || + ldrv.ionode >= gdth_ctr_count) return -EFAULT; - ha = HADATA(gdth_ctr_tab[ctrt.ionode]); - if (ha->type == GDT_ISA || ha->type == GDT_EISA) { - ctrt.type = (unchar)((ha->stype>>20) - 0x10); - } else { - if (ha->type != GDT_PCIMPR) { - ctrt.type = (unchar)((ha->stype<<4) + 6); + ha = HADATA(gdth_ctr_tab[ldrv.ionode]); + + for (i = 0; i < ldrv.drive_cnt && i < MAX_HDRIVES; ++i) { + j = ldrv.drives[i]; + if (j >= MAX_HDRIVES || !ha->hdr[j].present) + continue; + if (ldrv.lock) { + GDTH_LOCK_HA(ha, flags); + ha->hdr[j].lock = 1; + GDTH_UNLOCK_HA(ha, flags); + gdth_wait_completion(ldrv.ionode, ha->bus_cnt, j); + gdth_stop_timeout(ldrv.ionode, ha->bus_cnt, j); } else { - ctrt.type = - (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe); - if (ha->stype >= 0x300) - ctrt.ext_type = 0x6000 | ha->subdevice_id; - else - ctrt.ext_type = 0x6000 | ha->stype; + GDTH_LOCK_HA(ha, flags); + ha->hdr[j].lock = 0; + GDTH_UNLOCK_HA(ha, flags); + gdth_start_timeout(ldrv.ionode, ha->bus_cnt, j); + gdth_next(ldrv.ionode); } - ctrt.device_id = ha->stype; - ctrt.sub_device_id = ha->subdevice_id; - } - ctrt.info = ha->brd_phys; - ctrt.oem_id = ha->oem_id; - if (copy_to_user((char *)arg, &ctrt, sizeof(gdth_ioctl_ctrtype))) + } + return 0; +} + +static int ioc_resetdrv(unsigned long arg, char *cmnd) +{ + gdth_ioctl_reset res; + gdth_cmd_str cmd; + int hanum; + gdth_ha_str *ha; +#if LINUX_VERSION_CODE >= 0x020503 + Scsi_Request *srp; + Scsi_Device *sdev; +#elif LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else + Scsi_Cmnd scp; + Scsi_Device sdev; +#endif + + if (copy_from_user(&res, (char *)arg, sizeof(gdth_ioctl_reset)) || + res.ionode >= gdth_ctr_count || res.number >= MAX_HDRIVES) return -EFAULT; - break; - } - - case GDTIOCTL_GENERAL: - { + hanum = res.ionode; + ha = HADATA(gdth_ctr_tab[hanum]); + + if (!ha->hdr[res.number].present) + return 0; + cmd.Service = CACHESERVICE; + cmd.OpCode = GDT_CLUST_RESET; + cmd.u.cache.DeviceNo = res.number; +#if LINUX_VERSION_CODE >= 0x020503 + sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); + srp = scsi_allocate_request(sdev); + if (!srp) + return -ENOMEM; + srp->sr_cmd_len = 12; + srp->sr_use_sg = 0; + gdth_do_req(srp, &cmd, cmnd, 30); + res.status = (ushort)srp->sr_command->SCp.Status; + scsi_release_request(srp); + scsi_free_host_dev(sdev); +#elif LINUX_VERSION_CODE >= 0x020322 + sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); + scp = scsi_allocate_device(sdev, 1, FALSE); + if (!scp) + return -ENOMEM; + scp->cmd_len = 12; + scp->use_sg = 0; + gdth_do_cmd(scp, &cmd, cmnd, 30); + res.status = (ushort)scp->SCp.Status; + scsi_release_command(scp); + scsi_free_host_dev(sdev); +#else + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = scp.host = gdth_ctr_tab[hanum]; + sdev.id = scp.target = sdev.host->this_id; + scp.device = &sdev; + gdth_do_cmd(&scp, &cmd, cmnd, 30); + res.status = (ushort)scp.SCp.Status; +#endif + if (copy_to_user((char *)arg, &res, sizeof(gdth_ioctl_reset))) + return -EFAULT; + return 0; +} + +static int ioc_general(unsigned long arg, char *cmnd) +{ gdth_ioctl_general gen; char *buf = NULL; ulong32 paddr; int hanum; + gdth_ha_str *ha; +#if LINUX_VERSION_CODE >= 0x020503 + Scsi_Request *srp; + Scsi_Device *sdev; +#elif LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else + Scsi_Cmnd scp; + Scsi_Device sdev; +#endif if (copy_from_user(&gen, (char *)arg, sizeof(gdth_ioctl_general)) || gen.ionode >= gdth_ctr_count) @@ -5059,116 +5137,115 @@ return -EFAULT; } gdth_ioctl_free(hanum, gen.data_len+gen.sense_len, buf, paddr); - break; - } + return 0; +} - case GDTIOCTL_EVENT: - { - gdth_ioctl_event evt; +static int ioc_hdrlist(unsigned long arg, char *cmnd) +{ + gdth_ioctl_rescan rsc; + gdth_cmd_str cmd; gdth_ha_str *ha; - ulong flags; - - if (copy_from_user(&evt, (char *)arg, sizeof(gdth_ioctl_event)) || - evt.ionode >= gdth_ctr_count) + unchar i; + int hanum; +#if LINUX_VERSION_CODE >= 0x020503 + Scsi_Request *srp; + Scsi_Device *sdev; +#elif LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else + Scsi_Cmnd scp; + Scsi_Device sdev; +#endif + + if (copy_from_user(&rsc, (char *)arg, sizeof(gdth_ioctl_rescan)) || + rsc.ionode >= gdth_ctr_count) return -EFAULT; - ha = HADATA(gdth_ctr_tab[evt.ionode]); + hanum = rsc.ionode; + ha = HADATA(gdth_ctr_tab[hanum]); + +#if LINUX_VERSION_CODE >= 0x020503 + sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); + srp = scsi_allocate_request(sdev); + if (!srp) + return -ENOMEM; + srp->sr_cmd_len = 12; + srp->sr_use_sg = 0; +#elif LINUX_VERSION_CODE >= 0x020322 + sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); + scp = scsi_allocate_device(sdev, 1, FALSE); + if (!scp) + return -ENOMEM; + scp->cmd_len = 12; + scp->use_sg = 0; +#else + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = scp.host = gdth_ctr_tab[hanum]; + sdev.id = scp.target = sdev.host->this_id; + scp.device = &sdev; +#endif - if (evt.erase == 0xff) { - if (evt.event.event_source == ES_TEST) - evt.event.event_data.size=sizeof(evt.event.event_data.eu.test); - else if (evt.event.event_source == ES_DRIVER) - evt.event.event_data.size=sizeof(evt.event.event_data.eu.driver); - else if (evt.event.event_source == ES_SYNC) - evt.event.event_data.size=sizeof(evt.event.event_data.eu.sync); - else - evt.event.event_data.size=sizeof(evt.event.event_data.eu.async); - GDTH_LOCK_HA(ha, flags); - gdth_store_event(ha, evt.event.event_source, evt.event.event_idx, - &evt.event.event_data); - GDTH_UNLOCK_HA(ha, flags); - } else if (evt.erase == 0xfe) { - gdth_clear_events(); - } else if (evt.erase == 0) { - evt.handle = gdth_read_event(ha, evt.handle, &evt.event); - } else { - gdth_readapp_event(ha, evt.erase, &evt.event); - } - if (copy_to_user((char *)arg, &evt, sizeof(gdth_ioctl_event))) - return -EFAULT; - break; - } - - case GDTIOCTL_LOCKDRV: - { - gdth_ioctl_lockdrv ldrv; - unchar i, j; - - if (copy_from_user(&ldrv, (char *)arg, sizeof(gdth_ioctl_lockdrv)) || - ldrv.ionode >= gdth_ctr_count) - return -EFAULT; - ha = HADATA(gdth_ctr_tab[ldrv.ionode]); - - for (i = 0; i < ldrv.drive_cnt && i < MAX_HDRIVES; ++i) { - j = ldrv.drives[i]; - if (j >= MAX_HDRIVES || !ha->hdr[j].present) + for (i = 0; i < MAX_HDRIVES; ++i) { + if (!ha->hdr[i].present) { + rsc.hdr_list[i].bus = 0xff; continue; - if (ldrv.lock) { - GDTH_LOCK_HA(ha, flags); - ha->hdr[j].lock = 1; - GDTH_UNLOCK_HA(ha, flags); - gdth_wait_completion(ldrv.ionode, ha->bus_cnt, j); - gdth_stop_timeout(ldrv.ionode, ha->bus_cnt, j); - } else { - GDTH_LOCK_HA(ha, flags); - ha->hdr[j].lock = 0; - GDTH_UNLOCK_HA(ha, flags); - gdth_start_timeout(ldrv.ionode, ha->bus_cnt, j); - gdth_next(ldrv.ionode); + } + rsc.hdr_list[i].bus = ha->virt_bus; + rsc.hdr_list[i].target = i; + rsc.hdr_list[i].lun = 0; + rsc.hdr_list[i].cluster_type = ha->hdr[i].cluster_type; + if (ha->hdr[i].cluster_type & CLUSTER_DRIVE) { + cmd.Service = CACHESERVICE; + cmd.OpCode = GDT_CLUST_INFO; + cmd.u.cache.DeviceNo = i; +#if LINUX_VERSION_CODE >= 0x020503 + gdth_do_req(srp, &cmd, cmnd, 30); + if (srp->sr_command->SCp.Status == S_OK) + rsc.hdr_list[i].cluster_type = srp->sr_command->SCp.Message; +#elif LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &cmd, cmnd, 30); + if (scp->SCp.Status == S_OK) + rsc.hdr_list[i].cluster_type = scp->SCp.Message; +#else + gdth_do_cmd(&scp, &cmd, cmnd, 30); + if (scp.SCp.Status == S_OK) + rsc.hdr_list[i].cluster_type = scp.SCp.Message; +#endif } } - break; - } - - case GDTIOCTL_LOCKCHN: - { - gdth_ioctl_lockchn lchn; - unchar i, j; - - if (copy_from_user(&lchn, (char *)arg, sizeof(gdth_ioctl_lockchn)) || - lchn.ionode >= gdth_ctr_count) +#if LINUX_VERSION_CODE >= 0x020503 + scsi_release_request(srp); + scsi_free_host_dev(sdev); +#elif LINUX_VERSION_CODE >= 0x020322 + scsi_release_command(scp); + scsi_free_host_dev(sdev); +#endif + + if (copy_to_user((char *)arg, &rsc, sizeof(gdth_ioctl_rescan))) return -EFAULT; - ha = HADATA(gdth_ctr_tab[lchn.ionode]); - - i = lchn.channel; - if (i < ha->bus_cnt) { - if (lchn.lock) { - GDTH_LOCK_HA(ha, flags); - ha->raw[i].lock = 1; - GDTH_UNLOCK_HA(ha, flags); - for (j = 0; j < ha->tid_cnt; ++j) { - gdth_wait_completion(lchn.ionode, i, j); - gdth_stop_timeout(lchn.ionode, i, j); - } - } else { - GDTH_LOCK_HA(ha, flags); - ha->raw[i].lock = 0; - GDTH_UNLOCK_HA(ha, flags); - for (j = 0; j < ha->tid_cnt; ++j) { - gdth_start_timeout(lchn.ionode, i, j); - gdth_next(lchn.ionode); - } - } - } - break; - } + return 0; +} - case GDTIOCTL_RESCAN: - { +static int ioc_rescan(unsigned long arg, char *cmnd) +{ gdth_ioctl_rescan rsc; gdth_cmd_str cmd; ushort i, status, hdr_cnt; ulong32 info; int hanum, cyls, hds, secs; + ulong flags; + gdth_ha_str *ha; +#if LINUX_VERSION_CODE >= 0x020503 + Scsi_Request *srp; + Scsi_Device *sdev; +#elif LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else + Scsi_Cmnd scp; + Scsi_Device sdev; +#endif if (copy_from_user(&rsc, (char *)arg, sizeof(gdth_ioctl_rescan)) || rsc.ionode >= gdth_ctr_count) @@ -5333,85 +5410,134 @@ if (copy_to_user((char *)arg, &rsc, sizeof(gdth_ioctl_rescan))) return -EFAULT; - break; - } + return 0; +} - case GDTIOCTL_HDRLIST: - { - gdth_ioctl_rescan rsc; - gdth_cmd_str cmd; - gdth_ha_str *ha; - unchar i; - int hanum; - - if (copy_from_user(&rsc, (char *)arg, sizeof(gdth_ioctl_rescan)) || - rsc.ionode >= gdth_ctr_count) - return -EFAULT; - hanum = rsc.ionode; - ha = HADATA(gdth_ctr_tab[hanum]); - +static int gdth_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + gdth_ha_str *ha; #if LINUX_VERSION_CODE >= 0x020503 - sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); - srp = scsi_allocate_request(sdev); - if (!srp) - return -ENOMEM; - srp->sr_cmd_len = 12; - srp->sr_use_sg = 0; + Scsi_Cmnd *scp; + Scsi_Device *sdev; #elif LINUX_VERSION_CODE >= 0x020322 - sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); - scp = scsi_allocate_device(sdev, 1, FALSE); - if (!scp) - return -ENOMEM; - scp->cmd_len = 12; - scp->use_sg = 0; + Scsi_Cmnd *scp; + Scsi_Device *sdev; #else - memset(&sdev,0,sizeof(Scsi_Device)); - memset(&scp, 0,sizeof(Scsi_Cmnd)); - sdev.host = scp.host = gdth_ctr_tab[hanum]; - sdev.id = scp.target = sdev.host->this_id; - scp.device = &sdev; + Scsi_Cmnd scp; + Scsi_Device sdev; #endif + ulong flags; + char cmnd[MAX_COMMAND_SIZE]; - for (i = 0; i < MAX_HDRIVES; ++i) { - if (!ha->hdr[i].present) { - rsc.hdr_list[i].bus = 0xff; - continue; - } - rsc.hdr_list[i].bus = ha->virt_bus; - rsc.hdr_list[i].target = i; - rsc.hdr_list[i].lun = 0; - rsc.hdr_list[i].cluster_type = ha->hdr[i].cluster_type; - if (ha->hdr[i].cluster_type & CLUSTER_DRIVE) { - cmd.Service = CACHESERVICE; - cmd.OpCode = GDT_CLUST_INFO; - cmd.u.cache.DeviceNo = i; -#if LINUX_VERSION_CODE >= 0x020503 - gdth_do_req(srp, &cmd, cmnd, 30); - if (srp->sr_command->SCp.Status == S_OK) - rsc.hdr_list[i].cluster_type = srp->sr_command->SCp.Message; -#elif LINUX_VERSION_CODE >= 0x020322 - gdth_do_cmd(scp, &cmd, cmnd, 30); - if (scp->SCp.Status == S_OK) - rsc.hdr_list[i].cluster_type = scp->SCp.Message; -#else - gdth_do_cmd(&scp, &cmd, cmnd, 30); - if (scp.SCp.Status == S_OK) - rsc.hdr_list[i].cluster_type = scp.SCp.Message; -#endif - } - } -#if LINUX_VERSION_CODE >= 0x020503 - scsi_release_request(srp); - scsi_free_host_dev(sdev); -#elif LINUX_VERSION_CODE >= 0x020322 - scsi_release_command(scp); - scsi_free_host_dev(sdev); -#endif + memset(cmnd, 0xff, 12); + + TRACE(("gdth_ioctl() cmd 0x%x\n", cmd)); - if (copy_to_user((char *)arg, &rsc, sizeof(gdth_ioctl_rescan))) + switch (cmd) { + case GDTIOCTL_CTRCNT: + { + int cnt = gdth_ctr_count; + put_user(cnt, (int *)arg); + break; + } + + case GDTIOCTL_DRVERS: + { + int ver = (GDTH_VERSION<<8) | GDTH_SUBVERSION; + put_user(ver, (int *)arg); + break; + } + + case GDTIOCTL_OSVERS: + { + gdth_ioctl_osvers osv; + + osv.version = (unchar)(LINUX_VERSION_CODE >> 16); + osv.subversion = (unchar)(LINUX_VERSION_CODE >> 8); + osv.revision = (ushort)(LINUX_VERSION_CODE & 0xff); + copy_to_user((char *)arg, &osv, sizeof(gdth_ioctl_osvers)); + break; + } + + case GDTIOCTL_CTRTYPE: + { + gdth_ioctl_ctrtype ctrt; + + if (copy_from_user(&ctrt, (char *)arg, sizeof(gdth_ioctl_ctrtype)) || + ctrt.ionode >= gdth_ctr_count) + return -EFAULT; + ha = HADATA(gdth_ctr_tab[ctrt.ionode]); + if (ha->type == GDT_ISA || ha->type == GDT_EISA) { + ctrt.type = (unchar)((ha->stype>>20) - 0x10); + } else { + if (ha->type != GDT_PCIMPR) { + ctrt.type = (unchar)((ha->stype<<4) + 6); + } else { + ctrt.type = + (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe); + if (ha->stype >= 0x300) + ctrt.ext_type = 0x6000 | ha->subdevice_id; + else + ctrt.ext_type = 0x6000 | ha->stype; + } + ctrt.device_id = ha->stype; + ctrt.sub_device_id = ha->subdevice_id; + } + ctrt.info = ha->brd_phys; + ctrt.oem_id = ha->oem_id; + if (copy_to_user((char *)arg, &ctrt, sizeof(gdth_ioctl_ctrtype))) return -EFAULT; break; } + + case GDTIOCTL_GENERAL: + return ioc_general(arg, cmnd); + + case GDTIOCTL_EVENT: + return ioc_event(arg); + + case GDTIOCTL_LOCKDRV: + return ioc_lockdrv(arg); + + case GDTIOCTL_LOCKCHN: + { + gdth_ioctl_lockchn lchn; + unchar i, j; + + if (copy_from_user(&lchn, (char *)arg, sizeof(gdth_ioctl_lockchn)) || + lchn.ionode >= gdth_ctr_count) + return -EFAULT; + ha = HADATA(gdth_ctr_tab[lchn.ionode]); + + i = lchn.channel; + if (i < ha->bus_cnt) { + if (lchn.lock) { + GDTH_LOCK_HA(ha, flags); + ha->raw[i].lock = 1; + GDTH_UNLOCK_HA(ha, flags); + for (j = 0; j < ha->tid_cnt; ++j) { + gdth_wait_completion(lchn.ionode, i, j); + gdth_stop_timeout(lchn.ionode, i, j); + } + } else { + GDTH_LOCK_HA(ha, flags); + ha->raw[i].lock = 0; + GDTH_UNLOCK_HA(ha, flags); + for (j = 0; j < ha->tid_cnt; ++j) { + gdth_start_timeout(lchn.ionode, i, j); + gdth_next(lchn.ionode); + } + } + } + break; + } + + case GDTIOCTL_RESCAN: + return ioc_rescan(arg, cmnd); + + case GDTIOCTL_HDRLIST: + return ioc_hdrlist(arg, cmnd); case GDTIOCTL_RESET_BUS: { @@ -5470,57 +5596,7 @@ } case GDTIOCTL_RESET_DRV: - { - gdth_ioctl_reset res; - gdth_cmd_str cmd; - int hanum; - - if (copy_from_user(&res, (char *)arg, sizeof(gdth_ioctl_reset)) || - res.ionode >= gdth_ctr_count || res.number >= MAX_HDRIVES) - return -EFAULT; - hanum = res.ionode; - ha = HADATA(gdth_ctr_tab[hanum]); - - if (!ha->hdr[res.number].present) - return 0; - cmd.Service = CACHESERVICE; - cmd.OpCode = GDT_CLUST_RESET; - cmd.u.cache.DeviceNo = res.number; -#if LINUX_VERSION_CODE >= 0x020503 - sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); - srp = scsi_allocate_request(sdev); - if (!srp) - return -ENOMEM; - srp->sr_cmd_len = 12; - srp->sr_use_sg = 0; - gdth_do_req(srp, &cmd, cmnd, 30); - res.status = (ushort)srp->sr_command->SCp.Status; - scsi_release_request(srp); - scsi_free_host_dev(sdev); -#elif LINUX_VERSION_CODE >= 0x020322 - sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); - scp = scsi_allocate_device(sdev, 1, FALSE); - if (!scp) - return -ENOMEM; - scp->cmd_len = 12; - scp->use_sg = 0; - gdth_do_cmd(scp, &cmd, cmnd, 30); - res.status = (ushort)scp->SCp.Status; - scsi_release_command(scp); - scsi_free_host_dev(sdev); -#else - memset(&sdev,0,sizeof(Scsi_Device)); - memset(&scp, 0,sizeof(Scsi_Cmnd)); - sdev.host = scp.host = gdth_ctr_tab[hanum]; - sdev.id = scp.target = sdev.host->this_id; - scp.device = &sdev; - gdth_do_cmd(&scp, &cmd, cmnd, 30); - res.status = (ushort)scp.SCp.Status; -#endif - if (copy_to_user((char *)arg, &res, sizeof(gdth_ioctl_reset))) - return -EFAULT; - break; - } + return ioc_resetdrv(arg, cmnd); default: break; diff -urN linux-2.5.68-bk7/drivers/scsi/gdth_proc.c linux-2.5.68-bk8/drivers/scsi/gdth_proc.c --- linux-2.5.68-bk7/drivers/scsi/gdth_proc.c 2003-04-19 19:49:26.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/gdth_proc.c 2003-04-27 04:36:28.000000000 -0700 @@ -1471,7 +1471,10 @@ ret_val = NULL; } else { #if LINUX_VERSION_CODE >= 0x020400 - ret_val = pci_alloc_consistent(ha->pdev, size, paddr); + dma_addr_t dma_addr; + + ret_val = pci_alloc_consistent(ha->pdev, size, &dma_addr); + *paddr = (ulong32)dma_addr; #else ret_val = scsi_init_malloc(size, GFP_ATOMIC | GFP_DMA); if (ret_val) diff -urN linux-2.5.68-bk7/drivers/scsi/megaraid.c linux-2.5.68-bk8/drivers/scsi/megaraid.c --- linux-2.5.68-bk7/drivers/scsi/megaraid.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/megaraid.c 2003-04-27 04:36:28.000000000 -0700 @@ -1,5100 +1,5385 @@ -/*=================================================================== - * - * Linux MegaRAID device driver - * - * Copyright 2001 American Megatrends Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Version : v1.18 (Oct 11, 2001) - * - * Description: Linux device driver for LSI Logic MegaRAID controller - * - * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490 - * 493. - * History: - * - * Version 0.90: - * Original source contributed by Dell; integrated it into the kernel and - * cleaned up some things. Added support for 438/466 controllers. - * Version 0.91: - * Aligned mailbox area on 16-byte boundary. - * Added schedule() at the end to properly clean up. - * Made improvements for conformity to linux driver standards. - * - * Version 0.92: - * Added support for 2.1 kernels. - * Reads from pci_dev struct, so it's not dependent on pcibios. - * Added some missing virt_to_bus() translations. - * Added support for SMP. - * Changed global cli()'s to spinlocks for 2.1, and simulated - * spinlocks for 2.0. - * Removed setting of SA_INTERRUPT flag when requesting Irq. - * - * Version 0.92ac: - * Small changes to the comments/formatting. Plus a couple of - * added notes. Returned to the authors. No actual code changes - * save printk levels. - * 8 Oct 98 Alan Cox - * - * Merged with 2.1.131 source tree. - * 12 Dec 98 K. Baranowski - * - * Version 0.93: - * Added support for vendor specific ioctl commands (M_RD_IOCTL_CMD+xxh) - * Changed some fields in MEGARAID struct to better values. - * Added signature check for Rp controllers under 2.0 kernels - * Changed busy-wait loop to be time-based - * Fixed SMP race condition in isr - * Added kfree (sgList) on release - * Added #include linux/version.h to megaraid.h for hosts.h - * Changed max_id to represent max logical drives instead of targets. - * - * Version 0.94: - * Got rid of some excess locking/unlocking - * Fixed slight memory corruption problem while memcpy'ing into mailbox - * Changed logical drives to be reported as luns rather than targets - * Changed max_id to 16 since it is now max targets/chan again. - * Improved ioctl interface for upcoming megamgr - * - * Version 0.95: - * Fixed problem of queueing multiple commands to adapter; - * still has some strange problems on some setups, so still - * defaults to single. To enable parallel commands change - * #define MULTI_IO in megaraid.h - * Changed kmalloc allocation to be done in beginning. - * Got rid of C++ style comments - * - * Version 0.96: - * 762 fully supported. - * - * Version 0.97: - * Changed megaraid_command to use wait_queue. - * - * Version 1.00: - * Checks to see if an irq occurred while in isr, and runs through - * routine again. - * Copies mailbox to temp area before processing in isr - * Added barrier() in busy wait to fix volatility bug - * Uses separate list for freed Scbs, keeps track of cmd state - * Put spinlocks around entire queue function for now... - * Full multi-io commands working stablely without previous problems - * Added skipXX LILO option for Madrona motherboard support - * - * Version 1.01: - * Fixed bug in mega_cmd_done() for megamgr control commands, - * the host_byte in the result code from the scsi request to - * scsi midlayer is set to DID_BAD_TARGET when adapter's - * returned codes are 0xF0 and 0xF4. - * - * Version 1.02: - * Fixed the tape drive bug by extending the adapter timeout value - * for passthrough command to 60 seconds in mega_build_cmd(). - * - * Version 1.03: - * Fixed Madrona support. - * Changed the adapter timeout value from 60 sec in 1.02 to 10 min - * for bigger and slower tape drive. - * Added driver version printout at driver loadup time - * - * Version 1.04 - * Added code for 40 ld FW support. - * Added new ioctl command 0x81 to support NEW_READ/WRITE_CONFIG with - * data area greater than 4 KB, which is the upper bound for data - * tranfer through scsi_ioctl interface. - * The additional 32 bit field for 64bit address in the newly defined - * mailbox64 structure is set to 0 at this point. - * - * Version 1.05 - * Changed the queing implementation for handling SCBs and completed - * commands. - * Added spinlocks in the interrupt service routine to enable the driver - * function in the SMP environment. - * Fixed the problem of unnecessary aborts in the abort entry point, which - * also enables the driver to handle large amount of I/O requests for - * long duration of time. - * Version 1.06 - * Intel Release - * Version 1.07 - * Removed the usage of uaccess.h file for kernel versions less than - * 2.0.36, as this file is not present in those versions. - * - * Version 108 - * Modified mega_ioctl so that 40LD megamanager would run - * Made some changes for 2.3.XX compilation , esp wait structures - * Code merge between 1.05 and 1.06 . - * Bug fixed problem with ioctl interface for concurrency between - * 8ld and 40ld firwmare - * Removed the flawed semaphore logic for handling new config command - * Added support for building own scatter / gather list for big user - * mode buffers - * Added /proc file system support ,so that information is available in - * human readable format - * - * Version 1a08 - * Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag - * - * Version 1b08 - * Include file changes. - * Version 1b08b - * Change PCI ID value for the 471 card, use #defines when searching - * for megaraid cards. - * - * Version 1.10 - * - * I) Changes made to make following ioctl commands work in 0x81 interface - * a)DCMD_DELETE_LOGDRV - * b)DCMD_GET_DISK_CONFIG - * c)DCMD_DELETE_DRIVEGROUP - * d)NC_SUBOP_ENQUIRY3 - * e)DCMD_CHANGE_LDNO - * f)DCMD_CHANGE_LOOPID - * g)DCMD_FC_READ_NVRAM_CONFIG - * h)DCMD_WRITE_CONFIG - * II) Added mega_build_kernel_sg function - * III)Firmware flashing option added - * - * Version 1.10a - * - * I)Dell updates included in the source code. - * Note: This change is not tested due to the unavailability of IA64 kernel - * and it is in the #ifdef DELL_MODIFICATION macro which is not defined - * - * Version 1.10b - * - * I)In M_RD_IOCTL_CMD_NEW command the wrong way of copying the data - * to the user address corrected - * - * Version 1.10c - * - * I) DCMD_GET_DISK_CONFIG opcode updated for the firmware changes. - * - * Version 1.11 - * I) Version number changed from 1.10c to 1.11 - * II) DCMD_WRITE_CONFIG(0x0D) command in the driver changed from - * scatter/gather list mode to direct pointer mode.. - * Fixed bug of undesirably detecting HP onboard controllers which - * are disabled. - * - * Version 1.12 (Sep 21, 2000) - * - * I. Changes have been made for Dynamic DMA mapping in IA64 platform. - * To enable all these changes define M_RD_DYNAMIC_DMA_SUPPORT in megaraid.h - * II. Got rid of windows mode comments - * III. Removed unwanted code segments - * IV. Fixed bug of HP onboard controller information (commented with - * MEGA_HP_FIX) - * - * Version 1a12 - * I. reboot notifier and new ioctl changes ported from 1c09 - * - * Version 1b12 - * I. Changes in new ioctl interface routines ( Nov 06, 2000 ) - * - * Version 1c12 - * I. Changes in new ioctl interface routines ( Nov 07, 2000 ) - * - * Version 1d12 - * I. Compilation error under kernel 2.4.0 for 32-bit machine in mega_ioctl - * - * Version 1e12, 1f12 - * 1. Fixes for pci_map_single, pci_alloc_consistent along with mailbox - * alignment - * - * Version 1.13beta - * Added Support for Full 64bit address space support. If firmware - * supports 64bit, it goes to 64 bit mode even on x86 32bit - * systems. Data Corruption Issues while running on test9 kernel - * on IA64 systems. This issue not seen on test11 on x86 system - * - * Version 1.13c - * 1. Resolved Memory Leak when using M_RD_IOCTL_CMD interface - * 2. Resolved Queuing problem when MailBox Blocks - * 3. Added unregister_reboot_notifier support - * - * Version 1.13d - * Experimental changes in interfacing with the controller in ISR - * - * Version 1.13e - * Fixed Broken 2.2.XX compilation changes + misc changes - * - * Version 1.13f to 1.13i - * misc changes + code clean up - * Cleaned up the ioctl code and added set_mbox_xfer_addr() - * Support for START_DEV (6) - * - * Version 1.13j - * Moved some code to megaraid.h file, replaced some hard coded values - * with respective macros. Changed some functions to static - * - * Version 1.13k - * Only some idendation correction to 1.13j - * - * Version 1.13l , 1.13m, 1.13n, 1.13o - * Minor Identation changes + misc changes - * - * Version 1.13q - * Paded the new uioctl_t MIMD structure for maintaining alignment - * and size across 32 / 64 bit platforms - * Changed the way MIMD IOCTL interface used virt_to_bus() to use pci - * memory location - * - * Version 1.13r - * 2.4.xx SCSI Changes. - * - * Version 1.13s - * Stats counter fixes - * Temporary fix for some 64 bit firmwares in 2.4.XX kernels - * - * Version 1.13t - * Support for 64bit version of READ/WRITE/VIEW DISK CONFIG - * - * Version 1.14 - * Did away with MEGADEV_IOCTL flag. It is now standard part of driver - * without need for a special #define flag - * Disabled old scsi ioctl path for kernel versions > 2.3.xx. This is due - * to the nature in which the new scsi code queues a new scsi command to - * controller during SCSI IO Completion - * Driver now checks for sub-system vendor id before taking ownership of - * the controller - * - * Version 1.14a - * Added Host re-ordering - * - * Version 1.14b - * Corrected some issue which caused the older cards not to work - * - * Version 1.14c - * IOCTL changes for not handling the non-64bit firmwares under 2.4.XX - * kernel - * - * Version 1.14d - * Fixed Various MIMD Synchronization Issues - * - * Version 1.14e - * Fixed the error handling during card initialization - * - * Version 1.14f - * Multiple invocations of mimd phase I ioctl stalls the cpu. Replaced - * spinlock with semaphore(mutex) - * - * Version 1.14g - * Fixed running out of scbs issues while running MIMD apps under heavy IO - * - * Version 1.14g-ac - 02/03/01 - * Reformatted to Linux format so I could compare to old one and cross - * check bug fixes - * Re fixed the assorted missing 'static' cases - * Removed some unneeded version checks - * Cleaned up some of the VERSION checks in the code - * Left 2.0 support but removed 2.1.x support. - * Collected much of the compat glue into one spot - * - * Version 1.14g-ac2 - 22/03/01 - * Fixed a non obvious dereference after free in the driver unload path - * - * Version 1.14i - * changes for making 32bit application run on IA64 - * - * Version 1.14j - * Tue Mar 13 14:27:54 EST 2001 - AM - * Changes made in the driver to be able to run applications if the - * system has memory >4GB. - * - * - * Version 1.14k - * Thu Mar 15 18:38:11 EST 2001 - AM - * - * Firmware version check removed if subsysid==0x1111 and - * subsysvid==0x1111, since it's not yet initialized. - * - * changes made to correctly calculate the base in mega_findCard. - * - * Driver informational messages now appear on the console as well as - * with dmesg - * - * Older ioctl interface is returned failure on newer(2.4.xx) kernels. - * - * Inclusion of "modversions.h" is still a debatable question. It is - * included anyway with this release. - * - * Version 1.14l - * Mon Mar 19 17:39:46 EST 2001 - AM - * - * Assorted changes to remove compilation error in 1.14k when compiled - * with kernel < 2.4.0 - * - * Version 1.14m - * Tue Mar 27 12:09:22 EST 2001 - AM - * - * Added support for extended CDBs ( > 10 bytes ) and OBDR ( One Button - * Disaster Recovery ) feature. - * - * - * Version 1.14n - * Tue Apr 10 14:28:13 EDT 2001 - AM - * - * "modeversions.h" is no longer included in the code. - * 2.4.xx style mutex initialization used for older kernels also - * - * Version 1.14o - * Wed Apr 18 17:47:26 EDT 2001 - PJ - * - * Before returning status for 'inquiry', we first check if request buffer - * is SG list, and then return appropriate status - * - * Version 1.14p - * Wed Apr 25 13:44:48 EDT 2001 - PJ - * - * SCSI result made appropriate in case of check conditions for extended - * passthru commands - * - * Do not support lun >7 for physically accessed devices - * - * - * Version 1.15 - * Thu Apr 19 09:38:38 EDT 2001 - AM - * - * 1.14l rollover to 1.15 - merged with main trunk after 1.15d - * - * Version 1.15b - * Wed May 16 20:10:01 EDT 2001 - AM - * - * "modeversions.h" is no longer included in the code. - * 2.4.xx style mutex initialization used for older kernels also - * Brought in-sync with Alan's changes in 2.4.4 - * Note: 1.15a is on OBDR branch(main trunk), and is not merged with yet. - * - * Version 1.15c - * Mon May 21 23:10:42 EDT 2001 - AM - * - * ioctl interface uses 2.4.x conforming pci dma calls - * similar calls used for older kernels - * - * Version 1.15d - * Wed May 30 17:30:41 EDT 2001 - AM - * - * NULL is not a valid first argument for pci_alloc_consistent() on - * IA64(2.4.3-2.10.1). Code shuffling done in ioctl interface to get - * "pci_dev" before making calls to pci interface routines. - * - * Version 1.16pre - * Fri Jun 1 19:40:48 EDT 2001 - AM - * - * 1.14p and 1.15d merged - * ROMB support added - * - * Version 1.16-pre1 - * Mon Jun 4 15:01:01 EDT 2001 - AM - * - * Non-ROMB firmware do no DMA support 0xA9 command. Value 0xFF - * (all channels are raid ) is chosen for those firmware. - * - * Version 1.16-pre2 - * Mon Jun 11 18:15:31 EDT 2001 - AM - * - * Changes for boot from any logical drive - * - * Version 1.16 - * Tue Jun 26 18:07:02 EDT 2001 - AM - * - * branched at 1.14p - * - * Check added for HP 1M/2M controllers if having firmware H.01.07 or - * H.01.08. If found, disable 64 bit support since these firmware have - * limitations for 64 bit addressing - * - * - * Version 1.17 - * Thu Jul 12 11:14:09 EDT 2001 - AM - * - * 1.16pre2 and 1.16 merged. - * - * init_MUTEX and init_MUTEX_LOCKED are defined in 2.2.19. Pre-processor - * statements are added for them - * - * Linus's 2.4.7pre3 kernel introduces a new field 'max_sectors' in Scsi_Host - * structure, to improve IO performance. - * - * - * Version 1.17a - * Fri Jul 13 18:44:01 EDT 2001 - AM - * - * Starting from kernel 2.4.x, LUN is not < 8 - following SCSI-III. So to have - * our current formula working to calculate logical drive number, return - * failure for LUN > 7 - * - * - * Version 1.17b - * Mon Jul 30 19:24:02 EDT 2001 - AM - * - * Added support for random deletion of logical drives - * - * Version 1.17c - * Tue Sep 25 09:37:49 EDT 2001 - Atul Mukker +/* * - * With single and dual channel controllers, some virtaul channels are - * displayed negative. + * Linux MegaRAID device driver * - * Version 1.17a-ac - * Mon Aug 6 14:59:29 BST 2001 - "Michael Johnson" + * Copyright © 2002 LSI Logic Corporation. * - * Make the HP print formatting and check for buggy firmware runtime not - * ifdef dependent. + * 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. * + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * - fixes + * - speed-ups (list handling fixes, issued_list, optimizations.) + * - lots of cleanups. * - * Version 1.17d - * Thu Oct 11 10:48:45 EDT 2001 - Atul Mukker + * Version : v2.00.3 (Feb 19, 2003) - Atul Mukker * - * Driver 1.17c oops when loaded without controller. + * Description: Linux device driver for LSI Logic MegaRAID controller * - * Special case for "use_sg == 1" removed while building the scatter gather - * list. + * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490, 493 + * 518, 520, 531, 532 * - * Version 1.18 - * Thu Oct 11 15:02:53 EDT 2001 - Atul Mukker + * This driver is supported by LSI Logic, with assistance from Red Hat, Dell, + * and others. Please send updates to the public mailing list + * linux-megaraid-devel@dell.com, and subscribe to and read archives of this + * list at http://lists.us.dell.com/. * - * References to AMI have been changed to LSI Logic. + * For history of changes, see ChangeLog.megaraid. * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" + +#include "megaraid.h" + +MODULE_AUTHOR ("LSI Logic Corporation"); +MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); +MODULE_LICENSE ("GPL"); + +static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN; +MODULE_PARM(max_cmd_per_lun, "i"); +MODULE_PARM_DESC(max_cmd_per_lun, "Maximum number of commands which can be issued to a single LUN (default=DEF_CMD_PER_LUN=63)"); + +static unsigned short int max_sectors_per_io = MAX_SECTORS_PER_IO; +MODULE_PARM(max_sectors_per_io, "h"); +MODULE_PARM_DESC(max_sectors_per_io, "Maximum number of sectors per I/O request (default=MAX_SECTORS_PER_IO=128)"); + + +static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT; +MODULE_PARM(max_mbox_busy_wait, "h"); +MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)"); + +#define RDINDOOR(adapter) readl((adapter)->base + 0x20) +#define RDOUTDOOR(adapter) readl((adapter)->base + 0x2C) +#define WRINDOOR(adapter,value) writel(value, (adapter)->base + 0x20) +#define WROUTDOOR(adapter,value) writel(value, (adapter)->base + 0x2C) + +/* + * Global variables + */ + +static int hba_count; +static adapter_t *hba_soft_state[MAX_CONTROLLERS]; +static struct proc_dir_entry *mega_proc_dir_entry; + +static struct notifier_block mega_notifier = { + .notifier_call = megaraid_reboot_notify +}; + +/* For controller re-ordering */ +static struct mega_hbas mega_hbas[MAX_CONTROLLERS]; + +/* + * The File Operations structure for the serial/ioctl interface of the driver + */ +static struct file_operations megadev_fops = { + .owner = THIS_MODULE, + .ioctl = megadev_ioctl, + .open = megadev_open, +}; + +/* + * Array to structures for storing the information about the controllers. This + * information is sent to the user level applications, when they do an ioctl + * for this information. + */ +static struct mcontroller mcontroller[MAX_CONTROLLERS]; + +/* The current driver version */ +static u32 driver_ver = 0x02000000; + +/* major number used by the device for character interface */ +static int major; + +#define IS_RAID_CH(hba, ch) (((hba)->mega_ch_class >> (ch)) & 0x01) + + +/* + * Debug variable to print some diagnostic messages + */ +static int trace_level; + +/* + * megaraid_validate_parms() * - * BUGS: - * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that - * fails to detect the controller as a pci device on the system. + * Validate that any module parms passed in + * have proper values. + */ +static void +megaraid_validate_parms(void) +{ + if( (max_cmd_per_lun <= 0) || (max_cmd_per_lun > MAX_CMD_PER_LUN) ) + max_cmd_per_lun = MAX_CMD_PER_LUN; + if( max_mbox_busy_wait > MBOX_BUSY_WAIT ) + max_mbox_busy_wait = MBOX_BUSY_WAIT; +} + + +/** + * megaraid_detect() + * @host_template - Our soft state maintained by mid-layer * - * Timeout period for upper scsi layer, i.e. SD_TIMEOUT in - * /drivers/scsi/sd.c, is too short for this controller. SD_TIMEOUT - * value must be increased to (30 * HZ) otherwise false timeouts - * will occur in the upper layer. + * the detect entry point for the mid-layer. + * We scan the PCI bus for our controllers and start them. * - * Never set skip_id. The existing PCI code the megaraid uses fails - * to properly check the vendor subid in some cases. Setting this then - * makes it steal other i960's and crashes some boxes + * Note: PCI_DEVICE_ID_PERC4_DI below represents the PERC4/Di class of + * products. All of them share the same vendor id, device id, and subsystem + * vendor id but different subsystem ids. As of now, driver does not use the + * subsystem id. + */ +static int +megaraid_detect(Scsi_Host_Template *host_template) +{ + int i; + u16 dev_sw_table[] = { /* Table of all supported + vendor/device ids */ + + PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DISCOVERY, + PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_PERC4_DI, + PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_PERC4_QC_VERDE, + PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID, + PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID2, + PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_AMI_MEGARAID3 }; + + host_template->proc_name = "megaraid"; + + printk(KERN_NOTICE "megaraid: " MEGARAID_VERSION); + + megaraid_validate_parms(); + + memset(mega_hbas, 0, sizeof (mega_hbas)); + + hba_count = 0; + + /* + * Scan PCI bus for our all devices. + */ + for( i = 0; i < sizeof(dev_sw_table)/sizeof(u16); i += 2 ) { + + mega_find_card(host_template, dev_sw_table[i], + dev_sw_table[i+1]); + } + + if(hba_count) { + /* + * re-order hosts so that one with bootable logical drive + * comes first + */ +#ifdef CONFIG_PROC_FS + mega_proc_dir_entry = proc_mkdir("megaraid", &proc_root); + + if(!mega_proc_dir_entry) { + printk(KERN_WARNING + "megaraid: failed to create megaraid root\n"); + } + else { + for(i = 0; i < hba_count; i++) { + mega_create_proc_entry(i, mega_proc_dir_entry); + } + } +#endif + + /* + * Register the driver as a character device, for applications + * to access it for ioctls. + * First argument (major) to register_chrdev implies a dynamic + * major number allocation. + */ + major = register_chrdev(0, "megadev", &megadev_fops); + + /* + * Register the Shutdown Notification hook in kernel + */ + if(register_reboot_notifier(&mega_notifier)) { + printk(KERN_WARNING + "MegaRAID Shutdown routine not registered!!\n"); + } + + } + + return hba_count; +} + + + +/** + * mega_find_card() - find and start this controller + * @host_template - Our soft state maintained by mid-layer + * @pci_vendor - pci vendor id for this controller + * @pci_device - pci device id for this controller * - * Far too many ifdefs for versions. + * Scans the PCI bus for this vendor and device id combination, setup the + * resources, and register ourselves as a SCSI HBA driver, and setup all + * parameters for our soft state. * - *===================================================================*/ + * This routine also checks for some buggy firmware and ajust the flags + * accordingly. + */ +static void +mega_find_card(Scsi_Host_Template *host_template, u16 pci_vendor, + u16 pci_device) +{ + struct Scsi_Host *host = NULL; + adapter_t *adapter = NULL; + u32 magic64; + unsigned long mega_baseport; + u16 subsysid, subsysvid; + u8 pci_bus; + u8 pci_dev_func; + u8 irq; + struct pci_dev *pdev = NULL; + u8 did_ioremap_f = 0; + u8 did_req_region_f = 0; + u8 did_scsi_reg_f = 0; + u8 alloc_int_buf_f = 0; + u8 alloc_scb_f = 0; + u8 got_irq_f = 0; + u8 did_setup_mbox_f = 0; + unsigned long tbase; + unsigned long flag = 0; + int i, j; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + while((pdev = pci_find_device(pci_vendor, pci_device, pdev))) { -#include -#include -#include /* for kmalloc() */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ -#include -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */ -#include -#else -#include -#endif -#endif + if(pci_enable_device (pdev)) continue; + + pci_bus = pdev->bus->number; + pci_dev_func = pdev->devfn; + + /* + * For these vendor and device ids, signature offsets are not + * valid and 64 bit is implicit + */ + if( (pci_vendor == PCI_VENDOR_ID_DELL && + pci_device == PCI_DEVICE_ID_PERC4_DI) || + (pci_vendor == PCI_VENDOR_ID_LSI_LOGIC && + pci_device == PCI_DEVICE_ID_PERC4_QC_VERDE) ) { + + flag |= BOARD_64BIT; + } + else { + pci_read_config_dword(pdev, PCI_CONF_AMISIG64, + &magic64); + + if (magic64 == HBA_SIGNATURE_64BIT) + flag |= BOARD_64BIT; + } + + subsysvid = pdev->subsystem_vendor; + subsysid = pdev->subsystem_device; + + /* + * If we do not find the valid subsys vendor id, refuse to + * load the driver. This is part of PCI200X compliance + * We load the driver if subsysvid is 0. + */ + if( subsysvid && (subsysvid != AMI_SUBSYS_VID) && + (subsysvid != DELL_SUBSYS_VID) && + (subsysvid != HP_SUBSYS_VID) && + (subsysvid != LSI_SUBSYS_VID) ) continue; + + + printk(KERN_NOTICE "megaraid: found 0x%4.04x:0x%4.04x:bus %d:", + pci_vendor, pci_device, pci_bus); + + printk("slot %d:func %d\n", + PCI_SLOT(pci_dev_func), PCI_FUNC(pci_dev_func)); + + /* Read the base port and IRQ from PCI */ + mega_baseport = pci_resource_start(pdev, 0); + irq = pdev->irq; + + tbase = mega_baseport; + + if( pci_resource_flags(pdev, 0) & IORESOURCE_MEM ) { + + if (!request_mem_region(mega_baseport, 128, + "MegaRAID: LSI Logic Corporation.")) { + printk(KERN_WARNING + "megaraid: mem region busy!\n"); + continue; + } + + mega_baseport = + (unsigned long)ioremap(mega_baseport, 128); + + if( !mega_baseport ) { + printk(KERN_WARNING + "megaraid: could not map hba memory\n"); + + release_mem_region(tbase, 128); + + continue; + } + + flag |= BOARD_MEMMAP; + + did_ioremap_f = 1; + } + else { + mega_baseport += 0x10; + + if( !request_region(mega_baseport, 16, "megaraid") ) + goto fail_attach; + + flag |= BOARD_IOMAP; + + did_req_region_f = 1; + } + + /* Initialize SCSI Host structure */ + host = scsi_register(host_template, sizeof(adapter_t)); + + if(!host) goto fail_attach; + + did_scsi_reg_f = 1; + + scsi_set_device(host, &pdev->dev); + + adapter = (adapter_t *)host->hostdata; + memset(adapter, 0, sizeof(adapter_t)); + + printk(KERN_NOTICE + "scsi%d:Found MegaRAID controller at 0x%lx, IRQ:%d\n", + host->host_no, mega_baseport, irq); + + adapter->base = mega_baseport; + + /* Copy resource info into structure */ + INIT_LIST_HEAD(&adapter->free_list); + INIT_LIST_HEAD(&adapter->pending_list); + INIT_LIST_HEAD(&adapter->completed_list); + + adapter->flag = flag; + spin_lock_init(&adapter->lock); + scsi_assign_lock(host, &adapter->lock); + + host->cmd_per_lun = max_cmd_per_lun; + host->max_sectors = max_sectors_per_io; + + adapter->dev = pdev; + adapter->host = host; + + adapter->host->irq = irq; + + if( flag & BOARD_MEMMAP ) { + adapter->host->base = tbase; + } + else { + adapter->host->io_port = tbase; + adapter->host->n_io_port = 16; + } + + adapter->host->unique_id = (pci_bus << 8) | pci_dev_func; + + /* + * Allocate buffer to issue internal commands. + */ + adapter->mega_buffer = pci_alloc_consistent(adapter->dev, + MEGA_BUFFER_SIZE, &adapter->buf_dma_handle); + + if( !adapter->mega_buffer ) { + printk(KERN_WARNING "megaraid: out of RAM.\n"); + goto fail_attach; + } + alloc_int_buf_f = 1; + + adapter->scb_list = kmalloc(sizeof(scb_t)*MAX_COMMANDS, + GFP_KERNEL); + + if(!adapter->scb_list) { + printk(KERN_WARNING "megaraid: out of RAM.\n"); + goto fail_attach; + } + + alloc_scb_f = 1; + + /* Request our IRQ */ + if( adapter->flag & BOARD_MEMMAP ) { + if(request_irq(irq, megaraid_isr_memmapped, SA_SHIRQ, + "megaraid", adapter)) { + printk(KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", + irq); + goto fail_attach; + } + } + else { + if(request_irq(irq, megaraid_isr_iomapped, SA_SHIRQ, + "megaraid", adapter)) { + printk(KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", + irq); + goto fail_attach; + } + } + got_irq_f = 1; + + if( mega_setup_mailbox(adapter) != 0 ) + goto fail_attach; + + did_setup_mbox_f = 1; + + if( mega_query_adapter(adapter) != 0 ) + goto fail_attach; + + /* + * Have checks for some buggy f/w + */ + if((subsysid == 0x1111) && (subsysvid == 0x1111)) { + /* + * Which firmware + */ + if (!strcmp(adapter->fw_version, "3.00") || + !strcmp(adapter->fw_version, "3.01")) { + + printk( KERN_WARNING + "megaraid: Your card is a Dell PERC " + "2/SC RAID controller with " + "firmware\nmegaraid: 3.00 or 3.01. " + "This driver is known to have " + "corruption issues\nmegaraid: with " + "those firmware versions on this " + "specific card. In order\nmegaraid: " + "to protect your data, please upgrade " + "your firmware to version\nmegaraid: " + "3.10 or later, available from the " + "Dell Technical Support web\n" + "megaraid: site at\nhttp://support." + "dell.com/us/en/filelib/download/" + "index.asp?fileid=2940\n" + ); + } + } + + /* + * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with + * firmware H.01.07, H.01.08, and H.01.09 disable 64 bit + * support, since this firmware cannot handle 64 bit + * addressing + */ + + if((subsysvid == HP_SUBSYS_VID) && + ((subsysid == 0x60E7)||(subsysid == 0x60E8))) { + + /* + * which firmware + */ + if( !strcmp(adapter->fw_version, "H01.07") || + !strcmp(adapter->fw_version, "H01.08") || + !strcmp(adapter->fw_version, "H01.09") ) { + + printk(KERN_WARNING + "megaraid: Firmware H.01.07, " + "H.01.08, and H.01.09 on 1M/2M " + "controllers\n" + "megaraid: do not support 64 bit " + "addressing.\nmegaraid: DISABLING " + "64 bit support.\n"); + adapter->flag &= ~BOARD_64BIT; + } + } + + + if(mega_is_bios_enabled(adapter)) { + mega_hbas[hba_count].is_bios_enabled = 1; + } + mega_hbas[hba_count].hostdata_addr = adapter; + + /* + * Find out which channel is raid and which is scsi. This is + * for ROMB support. + */ + mega_enum_raid_scsi(adapter); + + /* + * Find out if a logical drive is set as the boot drive. If + * there is one, will make that as the first logical drive. + * ROMB: Do we have to boot from a physical drive. Then all + * the physical drives would appear before the logical disks. + * Else, all the physical drives would be exported to the mid + * layer after logical drives. + */ + mega_get_boot_drv(adapter); + + if( ! adapter->boot_pdrv_enabled ) { + for( i = 0; i < NVIRT_CHAN; i++ ) + adapter->logdrv_chan[i] = 1; + + for( i = NVIRT_CHAN; ilogdrv_chan[i] = 0; + + adapter->mega_ch_class <<= NVIRT_CHAN; + } + else { + j = adapter->product_info.nchannels; + for( i = 0; i < j; i++ ) + adapter->logdrv_chan[i] = 0; + + for( i = j; i < NVIRT_CHAN + j; i++ ) + adapter->logdrv_chan[i] = 1; + } + + + /* + * Do we support random deletion and addition of logical + * drives + */ + adapter->read_ldidmap = 0; /* set it after first logdrv + delete cmd */ + adapter->support_random_del = mega_support_random_del(adapter); + + /* Initialize SCBs */ + if(mega_init_scb(adapter)) { + goto fail_attach; + } + + /* + * Reset the pending commands counter + */ + atomic_set(&adapter->pend_cmds, 0); + + /* + * Reset the adapter quiescent flag + */ + atomic_set(&adapter->quiescent, 0); + + hba_soft_state[hba_count] = adapter; + + /* + * Fill in the structure which needs to be passed back to the + * application when it does an ioctl() for controller related + * information. + */ + i = hba_count; + + mcontroller[i].base = mega_baseport; + mcontroller[i].irq = irq; + mcontroller[i].numldrv = adapter->numldrv; + mcontroller[i].pcibus = pci_bus; + mcontroller[i].pcidev = pci_device; + mcontroller[i].pcifun = PCI_FUNC (pci_dev_func); + mcontroller[i].pciid = -1; + mcontroller[i].pcivendor = pci_vendor; + mcontroller[i].pcislot = PCI_SLOT (pci_dev_func); + mcontroller[i].uid = (pci_bus << 8) | pci_dev_func; + + + /* Set the Mode of addressing to 64 bit if we can */ + if((adapter->flag & BOARD_64BIT)&&(sizeof(dma_addr_t) == 8)) { + pci_set_dma_mask(pdev, 0xffffffffffffffff); + adapter->has_64bit_addr = 1; + } + else { + pci_set_dma_mask(pdev, 0xffffffff); + adapter->has_64bit_addr = 0; + } + + init_MUTEX(&adapter->int_mtx); + init_waitqueue_head(&adapter->int_waitq); + + adapter->this_id = DEFAULT_INITIATOR_ID; + adapter->host->this_id = DEFAULT_INITIATOR_ID; + +#if MEGA_HAVE_CLUSTERING + /* + * Is cluster support enabled on this controller + * Note: In a cluster the HBAs ( the initiators ) will have + * different target IDs and we cannot assume it to be 7. Call + * to mega_support_cluster() will get the target ids also if + * the cluster support is available + */ + adapter->has_cluster = mega_support_cluster(adapter); + + if( adapter->has_cluster ) { + printk(KERN_NOTICE + "megaraid: Cluster driver, initiator id:%d\n", + adapter->this_id); + } +#endif + + hba_count++; + continue; + +fail_attach: + if( did_setup_mbox_f ) { + pci_free_consistent(adapter->dev, sizeof(mbox64_t), + (void *)adapter->una_mbox64, + adapter->una_mbox64_dma); + } + + if( got_irq_f ) { + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); + } + + if( alloc_scb_f ) { + kfree(adapter->scb_list); + } + + if( alloc_int_buf_f ) { + pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE, + (void *)adapter->mega_buffer, + adapter->buf_dma_handle); + } + + if( did_scsi_reg_f ) scsi_unregister(host); + + if( did_ioremap_f ) { + iounmap((void *)mega_baseport); + release_mem_region(tbase, 128); + } + + if( did_req_region_f ) + release_region(mega_baseport, 16); + } + + return; +} + + +/** + * mega_setup_mailbox() + * @adapter - pointer to our soft state + * + * Allocates a 8 byte aligned memory for the handshake mailbox. + */ +static int +mega_setup_mailbox(adapter_t *adapter) +{ + unsigned long align; + + adapter->una_mbox64 = pci_alloc_consistent(adapter->dev, + sizeof(mbox64_t), &adapter->una_mbox64_dma); + + if( !adapter->una_mbox64 ) return -1; + + adapter->mbox = &adapter->una_mbox64->mbox; + + adapter->mbox = (mbox_t *)((((unsigned long) adapter->mbox) + 15) & + (~0UL ^ 0xFUL)); + + adapter->mbox64 = (mbox64_t *)(((unsigned long)adapter->mbox) - 8); + + align = ((void *)adapter->mbox) - ((void *)&adapter->una_mbox64->mbox); + + adapter->mbox_dma = adapter->una_mbox64_dma + 8 + align; + + /* + * Register the mailbox if the controller is an io-mapped controller + */ + if( adapter->flag & BOARD_IOMAP ) { + + outb_p(adapter->mbox_dma & 0xFF, + adapter->host->io_port + MBOX_PORT0); + + outb_p((adapter->mbox_dma >> 8) & 0xFF, + adapter->host->io_port + MBOX_PORT1); + + outb_p((adapter->mbox_dma >> 16) & 0xFF, + adapter->host->io_port + MBOX_PORT2); + + outb_p((adapter->mbox_dma >> 24) & 0xFF, + adapter->host->io_port + MBOX_PORT3); + + outb_p(ENABLE_MBOX_BYTE, + adapter->host->io_port + ENABLE_MBOX_REGION); + + irq_ack(adapter); + + irq_enable(adapter); + } + + return 0; +} + + +/* + * mega_query_adapter() + * @adapter - pointer to our soft state + * + * Issue the adapter inquiry commands to the controller and find out + * information and parameter about the devices attached + */ +static int +mega_query_adapter(adapter_t *adapter) +{ + dma_addr_t prod_info_dma_handle; + mega_inquiry3 *inquiry3; + u8 raw_mbox[16]; + mbox_t *mbox; + int retval; + + /* Initialize adapter inquiry mailbox */ + + mbox = (mbox_t *)raw_mbox; + + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); + memset(mbox, 0, 16); + + /* + * Try to issue Inquiry3 command + * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and + * update enquiry3 structure + */ + mbox->xferaddr = (u32)adapter->buf_dma_handle; + + inquiry3 = (mega_inquiry3 *)adapter->mega_buffer; + + raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ + raw_mbox[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ + raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ + + /* Issue a blocking command to the card */ + if ((retval = issue_scb_block(adapter, raw_mbox))) { + /* the adapter does not support 40ld */ + + mraid_ext_inquiry *ext_inq; + mraid_inquiry *inq; + dma_addr_t dma_handle; + + ext_inq = pci_alloc_consistent(adapter->dev, + sizeof(mraid_ext_inquiry), &dma_handle); + + if( ext_inq == NULL ) return -1; + + inq = &ext_inq->raid_inq; + + mbox->xferaddr = (u32)dma_handle; + + /*issue old 0x04 command to adapter */ + mbox->cmd = MEGA_MBOXCMD_ADPEXTINQ; + + issue_scb_block(adapter, raw_mbox); + + /* + * update Enquiry3 and ProductInfo structures with + * mraid_inquiry structure + */ + mega_8_to_40ld(inq, inquiry3, + (mega_product_info *)&adapter->product_info); + + pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry), + ext_inq, dma_handle); + + } else { /*adapter supports 40ld */ + adapter->flag |= BOARD_40LD; + + /* + * get product_info, which is static information and will be + * unchanged + */ + prod_info_dma_handle = pci_map_single(adapter->dev, (void *) + &adapter->product_info, + sizeof(mega_product_info), PCI_DMA_FROMDEVICE); + + mbox->xferaddr = prod_info_dma_handle; + + raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ + raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ + + if ((retval = issue_scb_block(adapter, raw_mbox))) + printk(KERN_WARNING + "megaraid: Product_info cmd failed with error: %d\n", + retval); + + pci_dma_sync_single(adapter->dev, prod_info_dma_handle, + sizeof(mega_product_info), + PCI_DMA_FROMDEVICE); + + pci_unmap_single(adapter->dev, prod_info_dma_handle, + sizeof(mega_product_info), PCI_DMA_FROMDEVICE); + } + + + /* + * kernel scans the channels from 0 to <= max_channel + */ + adapter->host->max_channel = + adapter->product_info.nchannels + NVIRT_CHAN -1; + + adapter->host->max_id = 16; /* max targets per channel */ + + adapter->host->max_lun = 7; /* Upto 7 luns for non disk devices */ + + adapter->host->cmd_per_lun = max_cmd_per_lun; + + adapter->numldrv = inquiry3->num_ldrv; + + adapter->max_cmds = adapter->product_info.max_commands; + + if(adapter->max_cmds > MAX_COMMANDS) + adapter->max_cmds = MAX_COMMANDS; + + adapter->host->can_queue = adapter->max_cmds - 1; + + /* + * Get the maximum number of scatter-gather elements supported by this + * firmware + */ + mega_get_max_sgl(adapter); + + adapter->host->sg_tablesize = adapter->sglen; + + + /* use HP firmware and bios version encoding */ + if (adapter->product_info.subsysvid == HP_SUBSYS_VID) { + sprintf (adapter->fw_version, "%c%d%d.%d%d", + adapter->product_info.fw_version[2], + adapter->product_info.fw_version[1] >> 8, + adapter->product_info.fw_version[1] & 0x0f, + adapter->product_info.fw_version[0] >> 8, + adapter->product_info.fw_version[0] & 0x0f); + sprintf (adapter->bios_version, "%c%d%d.%d%d", + adapter->product_info.bios_version[2], + adapter->product_info.bios_version[1] >> 8, + adapter->product_info.bios_version[1] & 0x0f, + adapter->product_info.bios_version[0] >> 8, + adapter->product_info.bios_version[0] & 0x0f); + } else { + memcpy(adapter->fw_version, + (char *)adapter->product_info.fw_version, 4); + adapter->fw_version[4] = 0; + + memcpy(adapter->bios_version, + (char *)adapter->product_info.bios_version, 4); + + adapter->bios_version[4] = 0; + } + + printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n", + adapter->fw_version, adapter->bios_version, adapter->numldrv); + + /* + * Do we support extended (>10 bytes) cdbs + */ + adapter->support_ext_cdb = mega_support_ext_cdb(adapter); + if (adapter->support_ext_cdb) + printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); + + + return 0; +} + + +/* + * megaraid_queue() + * @scmd - Issue this scsi command + * @done - the callback hook into the scsi mid-layer + * + * The command queuing entry point for the mid-layer. + */ +static int +megaraid_queue(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *)) +{ + adapter_t *adapter; + scb_t *scb; + int busy=0; + + adapter = (adapter_t *)scmd->device->host->hostdata; + + scmd->scsi_done = done; + + + /* + * Allocate and build a SCB request + * busy flag will be set if mega_build_cmd() command could not + * allocate scb. We will return non-zero status in that case. + * NOTE: scb can be null even though certain commands completed + * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, we would + * return 0 in that case. + */ + + scb = mega_build_cmd(adapter, scmd, &busy); + + if(scb) { + scb->state |= SCB_PENDQ; + list_add_tail(&scb->list, &adapter->pending_list); + + /* + * Check if the HBA is in quiescent state, e.g., during a + * delete logical drive opertion. If it is, don't run + * the pending_list. + */ + if(atomic_read(&adapter->quiescent) == 0) { + mega_runpendq(adapter); + } + return 0; + } + + return busy; +} + + +/** + * mega_build_cmd() + * @adapter - pointer to our soft state + * @cmd - Prepare using this scsi command + * @busy - busy flag if no resources + * + * Prepares a command and scatter gather list for the controller. This routine + * also finds out if the commands is intended for a logical drive or a + * physical device and prepares the controller command accordingly. + * + * We also re-order the logical drives and physical devices based on their + * boot settings. + */ +static scb_t * +mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy) +{ + mega_ext_passthru *epthru; + mega_passthru *pthru; + scb_t *scb; + mbox_t *mbox; + long seg; + char islogical; + int max_ldrv_num; + int channel = 0; + int target = 0; + int ldrv_num = 0; /* logical drive number */ + + + /* + * filter the internal and ioctl commands + */ + if((cmd->cmnd[0] == MEGA_INTERNAL_CMD)) { + return cmd->buffer; + } + + + /* + * We know what channels our logical drives are on - mega_find_card() + */ + islogical = adapter->logdrv_chan[cmd->device->channel]; + + /* + * The theory: If physical drive is chosen for boot, all the physical + * devices are exported before the logical drives, otherwise physical + * devices are pushed after logical drives, in which case - Kernel sees + * the physical devices on virtual channel which is obviously converted + * to actual channel on the HBA. + */ + if( adapter->boot_pdrv_enabled ) { + if( islogical ) { + /* logical channel */ + channel = cmd->device->channel - + adapter->product_info.nchannels; + } + else { + /* this is physical channel */ + channel = cmd->device->channel; + target = cmd->device->id; + + /* + * boot from a physical disk, that disk needs to be + * exposed first IF both the channels are SCSI, then + * booting from the second channel is not allowed. + */ + if( target == 0 ) { + target = adapter->boot_pdrv_tgt; + } + else if( target == adapter->boot_pdrv_tgt ) { + target = 0; + } + } + } + else { + if( islogical ) { + /* this is the logical channel */ + channel = cmd->device->channel; + } + else { + /* physical channel */ + channel = cmd->device->channel - NVIRT_CHAN; + target = cmd->device->id; + } + } + + + if(islogical) { + + /* have just LUN 0 for each target on virtual channels */ + if (cmd->device->lun) { + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + + ldrv_num = mega_get_ldrv_num(adapter, cmd, channel); + + + max_ldrv_num = (adapter->flag & BOARD_40LD) ? + MAX_LOGICAL_DRIVES_40LD : MAX_LOGICAL_DRIVES_8LD; + + /* + * max_ldrv_num increases by 0x80 if some logical drive was + * deleted. + */ + if(adapter->read_ldidmap) + max_ldrv_num += 0x80; + + if(ldrv_num > max_ldrv_num ) { + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + + } + else { + if( cmd->device->lun > 7) { + /* + * Do not support lun >7 for physically accessed + * devices + */ + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + } + + /* + * + * Logical drive commands + * + */ + if(islogical) { + switch (cmd->cmnd[0]) { + case TEST_UNIT_READY: + memset(cmd->request_buffer, 0, cmd->request_bufflen); + +#if MEGA_HAVE_CLUSTERING + /* + * Do we support clustering and is the support enabled + * If no, return success always + */ + if( !adapter->has_cluster ) { + cmd->result = (DID_OK << 16); + cmd->scsi_done(cmd); + return NULL; + } + + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + + scb->raw_mbox[0] = MEGA_CLUSTER_CMD; + scb->raw_mbox[2] = MEGA_RESERVATION_STATUS; + scb->raw_mbox[3] = ldrv_num; + + scb->dma_direction = PCI_DMA_NONE; + + return scb; +#else + cmd->result = (DID_OK << 16); + cmd->scsi_done(cmd); + return NULL; +#endif + + case MODE_SENSE: + memset(cmd->request_buffer, 0, cmd->cmnd[4]); + cmd->result = (DID_OK << 16); + cmd->scsi_done(cmd); + return NULL; + + case READ_CAPACITY: + case INQUIRY: + + if(!(adapter->flag & (1L << cmd->device->channel))) { + + printk(KERN_NOTICE + "scsi%d: scanning scsi channel %d ", + adapter->host->host_no, + cmd->device->channel); + printk("for logical drives.\n"); + + adapter->flag |= (1L << cmd->device->channel); + } + + /* Allocate a SCB and initialize passthru */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + pthru = scb->pthru; + + mbox = (mbox_t *)scb->raw_mbox; + memset(mbox, 0, sizeof(scb->raw_mbox)); + memset(pthru, 0, sizeof(mega_passthru)); + + pthru->timeout = 0; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 1; + pthru->logdrv = ldrv_num; + pthru->cdblen = cmd->cmd_len; + memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len); + + if( adapter->has_64bit_addr ) { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU64; + } + else { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + } + + scb->dma_direction = PCI_DMA_FROMDEVICE; + + pthru->numsgelements = mega_build_sglist(adapter, scb, + &pthru->dataxferaddr, &pthru->dataxferlen); + + mbox->xferaddr = scb->pthru_dma_addr; + + return scb; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + + /* Allocate a SCB and initialize mailbox */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + mbox = (mbox_t *)scb->raw_mbox; + + memset(mbox, 0, sizeof(scb->raw_mbox)); + mbox->logdrv = ldrv_num; + + /* + * A little hack: 2nd bit is zero for all scsi read + * commands and is set for all scsi write commands + */ + if( adapter->has_64bit_addr ) { + mbox->cmd = (*cmd->cmnd & 0x02) ? + MEGA_MBOXCMD_LWRITE64: + MEGA_MBOXCMD_LREAD64 ; + } + else { + mbox->cmd = (*cmd->cmnd & 0x02) ? + MEGA_MBOXCMD_LWRITE: + MEGA_MBOXCMD_LREAD ; + } + + /* + * 6-byte READ(0x08) or WRITE(0x0A) cdb + */ + if( cmd->cmd_len == 6 ) { + mbox->numsectors = (u32) cmd->cmnd[4]; + mbox->lba = + ((u32)cmd->cmnd[1] << 16) | + ((u32)cmd->cmnd[2] << 8) | + (u32)cmd->cmnd[3]; + + mbox->lba &= 0x1FFFFF; + +#if MEGA_HAVE_STATS + /* + * Take modulo 0x80, since the logical drive + * number increases by 0x80 when a logical + * drive was deleted + */ + if (*cmd->cmnd == READ_6) { + adapter->nreads[ldrv_num%0x80]++; + adapter->nreadblocks[ldrv_num%0x80] += + mbox->numsectors; + } else { + adapter->nwrites[ldrv_num%0x80]++; + adapter->nwriteblocks[ldrv_num%0x80] += + mbox->numsectors; + } +#endif + } + + /* + * 10-byte READ(0x28) or WRITE(0x2A) cdb + */ + if( cmd->cmd_len == 10 ) { + mbox->numsectors = + (u32)cmd->cmnd[8] | + ((u32)cmd->cmnd[7] << 8); + mbox->lba = + ((u32)cmd->cmnd[2] << 24) | + ((u32)cmd->cmnd[3] << 16) | + ((u32)cmd->cmnd[4] << 8) | + (u32)cmd->cmnd[5]; + +#if MEGA_HAVE_STATS + if (*cmd->cmnd == READ_10) { + adapter->nreads[ldrv_num%0x80]++; + adapter->nreadblocks[ldrv_num%0x80] += + mbox->numsectors; + } else { + adapter->nwrites[ldrv_num%0x80]++; + adapter->nwriteblocks[ldrv_num%0x80] += + mbox->numsectors; + } +#endif + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + if( cmd->cmd_len == 12 ) { + mbox->lba = + ((u32)cmd->cmnd[2] << 24) | + ((u32)cmd->cmnd[3] << 16) | + ((u32)cmd->cmnd[4] << 8) | + (u32)cmd->cmnd[5]; + + mbox->numsectors = + ((u32)cmd->cmnd[6] << 24) | + ((u32)cmd->cmnd[7] << 16) | + ((u32)cmd->cmnd[8] << 8) | + (u32)cmd->cmnd[9]; + +#if MEGA_HAVE_STATS + if (*cmd->cmnd == READ_12) { + adapter->nreads[ldrv_num%0x80]++; + adapter->nreadblocks[ldrv_num%0x80] += + mbox->numsectors; + } else { + adapter->nwrites[ldrv_num%0x80]++; + adapter->nwriteblocks[ldrv_num%0x80] += + mbox->numsectors; + } +#endif + } + + /* + * If it is a read command + */ + if( (*cmd->cmnd & 0x0F) == 0x08 ) { + scb->dma_direction = PCI_DMA_FROMDEVICE; + } + else { + scb->dma_direction = PCI_DMA_TODEVICE; + } + + /* Calculate Scatter-Gather info */ + mbox->numsgelements = mega_build_sglist(adapter, scb, + (u32 *)&mbox->xferaddr, (u32 *)&seg); + + return scb; + +#if MEGA_HAVE_CLUSTERING + case RESERVE: /* Fall through */ + case RELEASE: + + /* + * Do we support clustering and is the support enabled + */ + if( ! adapter->has_cluster ) { + + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + + /* Allocate a SCB and initialize mailbox */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + + scb->raw_mbox[0] = MEGA_CLUSTER_CMD; + scb->raw_mbox[2] = ( *cmd->cmnd == RESERVE ) ? + MEGA_RESERVE_LD : MEGA_RELEASE_LD; + + scb->raw_mbox[3] = ldrv_num; + + scb->dma_direction = PCI_DMA_NONE; + + return scb; +#endif + + default: + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + } + + /* + * Passthru drive commands + */ + else { + /* Allocate a SCB and initialize passthru */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + + mbox = (mbox_t *)scb->raw_mbox; + memset(mbox, 0, sizeof(scb->raw_mbox)); + + if( adapter->support_ext_cdb ) { + + epthru = mega_prepare_extpassthru(adapter, scb, cmd, + channel, target); + + mbox->cmd = MEGA_MBOXCMD_EXTPTHRU; + + mbox->xferaddr = scb->epthru_dma_addr; + + } + else { + + pthru = mega_prepare_passthru(adapter, scb, cmd, + channel, target); + + /* Initialize mailbox */ + if( adapter->has_64bit_addr ) { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU64; + } + else { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + } + + mbox->xferaddr = scb->pthru_dma_addr; + + } + return scb; + } + return NULL; +} + + +/** + * mega_prepare_passthru() + * @adapter - pointer to our soft state + * @scb - our scsi control block + * @cmd - scsi command from the mid-layer + * @channel - actual channel on the controller + * @target - actual id on the controller. + * + * prepare a command for the scsi physical devices. + */ +static mega_passthru * +mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd, + int channel, int target) +{ + mega_passthru *pthru; + + pthru = scb->pthru; + memset(pthru, 0, sizeof (mega_passthru)); + + /* 0=6sec/1=60sec/2=10min/3=3hrs */ + pthru->timeout = 2; + + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 0; + + pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel; + + pthru->target = (adapter->flag & BOARD_40LD) ? + (channel << 4) | target : target; + + pthru->cdblen = cmd->cmd_len; + pthru->logdrv = cmd->device->lun; + + memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len); + + /* Not sure about the direction */ + scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + + /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ + switch (cmd->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + if(!(adapter->flag & (1L << cmd->device->channel))) { + + printk(KERN_NOTICE + "scsi%d: scanning scsi channel %d [P%d] ", + adapter->host->host_no, + cmd->device->channel, channel); + printk("for physical devices.\n"); + + adapter->flag |= (1L << cmd->device->channel); + } + /* Fall through */ + default: + pthru->numsgelements = mega_build_sglist(adapter, scb, + &pthru->dataxferaddr, &pthru->dataxferlen); + break; + } + return pthru; +} + + +/** + * mega_prepare_extpassthru() + * @adapter - pointer to our soft state + * @scb - our scsi control block + * @cmd - scsi command from the mid-layer + * @channel - actual channel on the controller + * @target - actual id on the controller. + * + * prepare a command for the scsi physical devices. This rountine prepares + * commands for devices which can take extended CDBs (>10 bytes) + */ +static mega_ext_passthru * +mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd, + int channel, int target) +{ + mega_ext_passthru *epthru; + + epthru = scb->epthru; + memset(epthru, 0, sizeof(mega_ext_passthru)); + + /* 0=6sec/1=60sec/2=10min/3=3hrs */ + epthru->timeout = 2; + + epthru->ars = 1; + epthru->reqsenselen = 14; + epthru->islogical = 0; + + epthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel; + epthru->target = (adapter->flag & BOARD_40LD) ? + (channel << 4) | target : target; + + epthru->cdblen = cmd->cmd_len; + epthru->logdrv = cmd->device->lun; + + memcpy(epthru->cdb, cmd->cmnd, cmd->cmd_len); + + /* Not sure about the direction */ + scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + + switch(cmd->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + if(!(adapter->flag & (1L << cmd->device->channel))) { + + printk(KERN_NOTICE + "scsi%d: scanning scsi channel %d [P%d] ", + adapter->host->host_no, + cmd->device->channel, channel); + printk("for physical devices.\n"); + + adapter->flag |= (1L << cmd->device->channel); + } + /* Fall through */ + default: + epthru->numsgelements = mega_build_sglist(adapter, scb, + &epthru->dataxferaddr, &epthru->dataxferlen); + break; + } + + return epthru; +} + + +/** + * mega_allocate_scb() + * @adapter - pointer to our soft state + * @cmd - scsi command from the mid-layer + * + * Allocate a SCB structure. This is the central structure for controller + * commands. + */ +static inline scb_t * +mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd) +{ + struct list_head *head = &adapter->free_list; + scb_t *scb; + + /* Unlink command from Free List */ + if( !list_empty(head) ) { + + scb = list_entry(head->next, scb_t, list); + + list_del_init(head->next); + + scb->state = SCB_ACTIVE; + scb->cmd = cmd; + scb->dma_type = MEGA_DMA_TYPE_NONE; + + return scb; + } + + return NULL; +} + + +/** + * mega_runpendq() + * @adapter - pointer to our soft state + * + * Runs through the list of pending requests. + */ +static inline void +mega_runpendq(adapter_t *adapter) +{ + if(!list_empty(&adapter->pending_list)) + __mega_runpendq(adapter); +} + +static void +__mega_runpendq(adapter_t *adapter) +{ + scb_t *scb; + struct list_head *pos, *next; + + /* Issue any pending commands to the card */ + list_for_each_safe(pos, next, &adapter->pending_list) { + + scb = list_entry(pos, scb_t, list); + + if( !(scb->state & SCB_ISSUED) ) { + + if( issue_scb(adapter, scb) != 0 ) + return; + } + } + + return; +} + + +/** + * issue_scb() + * @adapter - pointer to our soft state + * @scb - scsi control block + * + * Post a command to the card if the mailbox is available, otherwise return + * busy. We also take the scb from the pending list if the mailbox is + * available. + */ +static inline int +issue_scb(adapter_t *adapter, scb_t *scb) +{ + volatile mbox64_t *mbox64 = adapter->mbox64; + volatile mbox_t *mbox = adapter->mbox; + unsigned int i = 0; + + if(unlikely(mbox->busy)) { + do { + udelay(1); + i++; + } while( mbox->busy && (i < max_mbox_busy_wait) ); + + if(mbox->busy) return -1; + } + + /* Copy mailbox data into host structure */ + memcpy((char *)mbox, (char *)scb->raw_mbox, 16); + + mbox->cmdid = scb->idx; /* Set cmdid */ + mbox->busy = 1; /* Set busy */ + + + /* + * Increment the pending queue counter + */ + atomic_inc(&adapter->pend_cmds); + + switch (mbox->cmd) { + case MEGA_MBOXCMD_LREAD64: + case MEGA_MBOXCMD_LWRITE64: + case MEGA_MBOXCMD_PASSTHRU64: + case MEGA_MBOXCMD_EXTPTHRU: + mbox64->xfer_segment_lo = mbox->xferaddr; + mbox64->xfer_segment_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + break; + default: + mbox64->xfer_segment_lo = 0; + mbox64->xfer_segment_hi = 0; + } + + /* + * post the command + */ + scb->state |= SCB_ISSUED; + + if( likely(adapter->flag & BOARD_MEMMAP) ) { + mbox->poll = 0; + mbox->ack = 0; + WRINDOOR(adapter, adapter->mbox_dma | 0x1); + } + else { + irq_enable(adapter); + issue_command(adapter); + } + + return 0; +} + + +/** + * issue_scb_block() + * @adapter - pointer to our soft state + * @raw_mbox - the mailbox + * + * Issue a scb in synchronous and non-interrupt mode + */ +static int +issue_scb_block(adapter_t *adapter, u_char *raw_mbox) +{ + volatile mbox64_t *mbox64 = adapter->mbox64; + volatile mbox_t *mbox = adapter->mbox; + u8 byte; + + raw_mbox[0x1] = 0xFE; /* Set cmdid */ + raw_mbox[0xF] = 1; /* Set busy */ + + /* Wait until mailbox is free */ + if(mega_busywait_mbox (adapter)) + goto bug_blocked_mailbox; + + /* Copy mailbox data into host structure */ + memcpy((char *) mbox, raw_mbox, 16); + + switch (raw_mbox[0]) { + case MEGA_MBOXCMD_LREAD64: + case MEGA_MBOXCMD_LWRITE64: + case MEGA_MBOXCMD_PASSTHRU64: + case MEGA_MBOXCMD_EXTPTHRU: + mbox64->xfer_segment_lo = mbox->xferaddr; + mbox64->xfer_segment_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + break; + default: + mbox64->xfer_segment_lo = 0; + mbox64->xfer_segment_hi = 0; + } + + if( likely(adapter->flag & BOARD_MEMMAP) ) { + mbox->poll = 0; + mbox->ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + WRINDOOR(adapter, adapter->mbox_dma | 0x1); + + while((volatile u8)mbox->numstatus == 0xFF) + cpu_relax(); + + mbox->numstatus = 0xFF; + + while( (volatile u8)mbox->poll != 0x77 ) + cpu_relax(); + + mbox->poll = 0; + mbox->ack = 0x77; + + WRINDOOR(adapter, adapter->mbox_dma | 0x2); + + while(RDINDOOR(adapter) & 0x2) + cpu_relax(); + } + else { + irq_disable(adapter); + issue_command(adapter); + + while (!((byte = irq_state(adapter)) & INTR_VALID)) + cpu_relax(); + + set_irq_state(adapter, byte); + irq_enable(adapter); + irq_ack(adapter); + } + + return mbox->status; + +bug_blocked_mailbox: + printk(KERN_WARNING "megaraid: Blocked mailbox......!!\n"); + udelay (1000); + return -1; +} + + +/** + * megaraid_isr_iomapped() + * @irq - irq + * @devp - pointer to our soft state + * @regs - unused + * + * Interrupt service routine for io-mapped controllers. + * Find out if our device is interrupting. If yes, acknowledge the interrupt + * and service the completed commands. + */ +static irqreturn_t +megaraid_isr_iomapped(int irq, void *devp, struct pt_regs *regs) +{ + adapter_t *adapter = devp; + unsigned long flags; + u8 status; + u8 nstatus; + u8 completed[MAX_FIRMWARE_STATUS]; + u8 byte; + int handled = 0; + + + /* + * loop till F/W has more commands for us to complete. + */ + spin_lock_irqsave(&adapter->lock, flags); + + do { + /* Check if a valid interrupt is pending */ + byte = irq_state(adapter); + if( (byte & VALID_INTR_BYTE) == 0 ) { + /* + * No more pending commands + */ + goto out_unlock; + } + set_irq_state(adapter, byte); + + while((nstatus = (volatile u8)adapter->mbox->numstatus) + == 0xFF) + cpu_relax(); + adapter->mbox->numstatus = 0xFF; + + status = adapter->mbox->status; + + /* + * decrement the pending queue counter + */ + atomic_sub(nstatus, &adapter->pend_cmds); + + memcpy(completed, (void *)adapter->mbox->completed, nstatus); + + /* Acknowledge interrupt */ + irq_ack(adapter); + + mega_cmd_done(adapter, completed, nstatus, status); + + mega_rundoneq(adapter); + + handled = 1; + + /* Loop through any pending requests */ + if(atomic_read(&adapter->quiescent) == 0) { + mega_runpendq(adapter); + } + + } while(1); + + out_unlock: + + spin_unlock_irqrestore(&adapter->lock, flags); + + return IRQ_RETVAL(handled); +} + + +/** + * megaraid_isr_memmapped() + * @irq - irq + * @devp - pointer to our soft state + * @regs - unused + * + * Interrupt service routine for memory-mapped controllers. + * Find out if our device is interrupting. If yes, acknowledge the interrupt + * and service the completed commands. + */ +static irqreturn_t +megaraid_isr_memmapped(int irq, void *devp, struct pt_regs *regs) +{ + adapter_t *adapter = devp; + unsigned long flags; + u8 status; + u32 dword = 0; + u8 nstatus; + u8 completed[MAX_FIRMWARE_STATUS]; + int handled = 0; + + + /* + * loop till F/W has more commands for us to complete. + */ + spin_lock_irqsave(&adapter->lock, flags); + + do { + /* Check if a valid interrupt is pending */ + dword = RDOUTDOOR(adapter); + if(dword != 0x10001234) { + /* + * No more pending commands + */ + goto out_unlock; + } + WROUTDOOR(adapter, 0x10001234); -#include -#include + while((nstatus = (volatile u8)adapter->mbox->numstatus) + == 0xFF) { + cpu_relax(); + } + adapter->mbox->numstatus = 0xFF; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */ -#include -#endif + status = adapter->mbox->status; -/* - * These header files are required for Shutdown Notification routines - */ -#include -#include -#include -#include "scsi.h" -#include "hosts.h" -#include + /* + * decrement the pending queue counter + */ + atomic_sub(nstatus, &adapter->pend_cmds); -#include "megaraid.h" + memcpy(completed, (void *)adapter->mbox->completed, nstatus); -/* - *================================================================ - * #Defines - *================================================================ - */ + /* Acknowledge interrupt */ + WRINDOOR(adapter, 0x2); -#define MAX_SERBUF 160 -#define COM_BASE 0x2f8 + handled = 1; -static ulong RDINDOOR (mega_host_config * megaCfg) -{ - return readl (megaCfg->base + 0x20); -} + while( RDINDOOR(adapter) & 0x02 ) cpu_relax(); -static void WRINDOOR (mega_host_config * megaCfg, ulong value) -{ - writel (value, megaCfg->base + 0x20); -} + mega_cmd_done(adapter, completed, nstatus, status); -static ulong RDOUTDOOR (mega_host_config * megaCfg) -{ - return readl (megaCfg->base + 0x2C); -} + mega_rundoneq(adapter); -static void WROUTDOOR (mega_host_config * megaCfg, ulong value) -{ - writel (value, megaCfg->base + 0x2C); -} + /* Loop through any pending requests */ + if(atomic_read(&adapter->quiescent) == 0) { + mega_runpendq(adapter); + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ -#include -#define cpuid smp_processor_id() -#endif + } while(1); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) -#define scsi_set_pci_device(x,y) -#endif + out_unlock: -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ + spin_unlock_irqrestore(&adapter->lock, flags); -/* - * Linux 2.4 and higher + return IRQ_RETVAL(handled); +} +/** + * mega_cmd_done() + * @adapter - pointer to our soft state + * @completed - array of ids of completed commands + * @nstatus - number of completed commands + * @status - status of the last command completed * - * No driver private lock - * Use the io_request_lock not cli/sti - * queue task is a simple api without irq forms + * Complete the comamnds and call the scsi mid-layer callback hooks. */ +static inline void +mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status) +{ + mega_ext_passthru *epthru = NULL; + struct scatterlist *sgl; + Scsi_Cmnd *cmd = NULL; + mega_passthru *pthru = NULL; + mbox_t *mbox = NULL; + u8 c; + scb_t *scb; + int islogical; + int cmdid; + int i; -MODULE_AUTHOR ("LSI Logic Corporation"); -MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); -MODULE_LICENSE ("GPL"); + /* + * for all the commands completed, call the mid-layer callback routine + * and free the scb. + */ + for( i = 0; i < nstatus; i++ ) { -#define DRIVER_LOCK_T -#define DRIVER_LOCK_INIT(p) -#define DRIVER_LOCK(p) -#define DRIVER_UNLOCK(p) -#define IO_LOCK_T unsigned long io_flags = 0 -#define IO_LOCK(host) spin_lock_irqsave(host->host_lock,io_flags) -#define IO_UNLOCK(host) spin_unlock_irqrestore(host->host_lock,io_flags) -#define IO_LOCK_IRQ(host) spin_lock_irq(host->host_lock) -#define IO_UNLOCK_IRQ(host) spin_unlock_irq(host->host_lock) + cmdid = completed[i]; -#define queue_task_irq(a,b) queue_task(a,b) -#define queue_task_irq_off(a,b) queue_task(a,b) + if( cmdid == CMDID_INT_CMDS ) { /* internal command */ + scb = &adapter->int_scb; + cmd = scb->cmd; + mbox = (mbox_t *)scb->raw_mbox; -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ + /* + * Internal command interface do not fire the extended + * passthru or 64-bit passthru + */ + pthru = scb->pthru; -/* - * Linux 2.2 and higher - * - * No driver private lock - * Use the io_request_lock not cli/sti - * No pci region api - * queue_task is now a single simple API - */ + } + else { + scb = &adapter->scb_list[cmdid]; -static char kernel_version[] = UTS_RELEASE; -MODULE_AUTHOR ("LSI Logic Corporation"); -MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); + /* + * Make sure f/w has completed a valid command + */ + if( !(scb->state & SCB_ISSUED) || scb->cmd == NULL ) { + printk(KERN_CRIT + "megaraid: invalid command "); + printk("Id %d, scb->state:%x, scsi cmd:%p\n", + cmdid, scb->state, scb->cmd); -#define DRIVER_LOCK_T -#define DRIVER_LOCK_INIT(p) -#define DRIVER_LOCK(p) -#define DRIVER_UNLOCK(p) -#define IO_LOCK_T unsigned long io_flags = 0 -#define IO_LOCK(host) spin_lock_irqsave(host->host_lock,io_flags); -#define IO_UNLOCK(host) spin_unlock_irqrestore(host->host_lock,io_flags); - -#define pci_free_consistent(a,b,c,d) -#define pci_unmap_single(a,b,c,d) -#define pci_enable_device(x) (0) -#define queue_task_irq(a,b) queue_task(a,b) -#define queue_task_irq_off(a,b) queue_task(a,b) - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ -#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) -#define init_MUTEX(x) (*(x)=MUTEX) -#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL -#endif + continue; + } + /* + * Was a abort issued for this command + */ + if( scb->state & SCB_ABORT ) { -#else + printk(KERN_WARNING + "megaraid: aborted cmd %lx[%x] complete.\n", + scb->cmd->serial_number, scb->idx); -/* - * Linux 2.0 macros. Here we have to provide some of our own - * functionality. We also only work little endian 32bit. - * Again no pci_alloc/free api - * IO_LOCK/IO_LOCK_T were never used in 2.0 so now are empty - */ - -#define cpuid 0 -#define DRIVER_LOCK_T long cpu_flags; -#define DRIVER_LOCK_INIT(p) -#define DRIVER_LOCK(p) \ - save_flags(cpu_flags); \ - cli(); -#define DRIVER_UNLOCK(p) \ - restore_flags(cpu_flags); -#define IO_LOCK_T -#define IO_LOCK(p) -#define IO_UNLOCK(p) -#define le32_to_cpu(x) (x) -#define cpu_to_le32(x) (x) + scb->cmd->result = (DID_ABORT << 16); -#define pci_free_consistent(a,b,c,d) -#define pci_unmap_single(a,b,c,d) + list_add_tail(SCSI_LIST(scb->cmd), + &adapter->completed_list); -#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) -#define init_MUTEX(x) (*(x)=MUTEX) + mega_free_scb(adapter, scb); -#define pci_enable_device(x) (0) + continue; + } -/* - * 2.0 lacks spinlocks, iounmap/ioremap - */ + /* + * Was a reset issued for this command + */ + if( scb->state & SCB_RESET ) { -#define ioremap vremap -#define iounmap vfree + printk(KERN_WARNING + "megaraid: reset cmd %lx[%x] complete.\n", + scb->cmd->serial_number, scb->idx); - /* simulate spin locks */ -typedef struct { - volatile char lock; -} spinlock_t; + scb->cmd->result = (DID_RESET << 16); -#define spin_lock_init(x) { (x)->lock = 0;} -#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\ - (x)->lock=1; save_flags(flags);\ - cli();} -#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);} + list_add_tail(SCSI_LIST(scb->cmd), + &adapter->completed_list); -#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL + mega_free_scb (adapter, scb); -#endif + continue; + } + cmd = scb->cmd; + pthru = scb->pthru; + epthru = scb->epthru; + mbox = (mbox_t *)scb->raw_mbox; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ -#define dma_alloc_consistent pci_alloc_consistent -#define dma_free_consistent pci_free_consistent -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ -typedef unsigned long dma_addr_t; -#endif -void *dma_alloc_consistent(void *, size_t, dma_addr_t *); -void dma_free_consistent(void *, size_t, void *, dma_addr_t); -int mega_get_order(int); -int pow_2(int); -#endif +#if MEGA_HAVE_STATS + { -/* set SERDEBUG to 1 to enable serial debugging */ -#define SERDEBUG 0 -#if SERDEBUG -static void ser_init (void); -static void ser_puts (char *str); -static void ser_putc (char c); -static int ser_printk (const char *fmt, ...); -#endif + int logdrv = mbox->logdrv; -#ifdef CONFIG_PROC_FS -#define COPY_BACK if (offset > megaCfg->procidx) { \ - *eof = TRUE; \ - megaCfg->procidx = 0; \ - megaCfg->procbuf[0] = 0; \ - return 0;} \ - if ((count + offset) > megaCfg->procidx) { \ - count = megaCfg->procidx - offset; \ - *eof = TRUE; } \ - memcpy(page, &megaCfg->procbuf[offset], count); \ - megaCfg->procidx = 0; \ - megaCfg->procbuf[0] = 0; -#endif + islogical = adapter->logdrv_chan[cmd->channel]; + /* + * Maintain an error counter for the logical drive. + * Some application like SNMP agent need such + * statistics + */ + if( status && islogical && (cmd->cmnd[0] == READ_6 || + cmd->cmnd[0] == READ_10 || + cmd->cmnd[0] == READ_12)) { + /* + * Logical drive number increases by 0x80 when + * a logical drive is deleted + */ + adapter->rd_errors[logdrv%0x80]++; + } -/* - * ================================================================ - * Global variables - *================================================================ - */ + if( status && islogical && (cmd->cmnd[0] == WRITE_6 || + cmd->cmnd[0] == WRITE_10 || + cmd->cmnd[0] == WRITE_12)) { + /* + * Logical drive number increases by 0x80 when + * a logical drive is deleted + */ + adapter->wr_errors[logdrv%0x80]++; + } -/* Use "megaraid=skipXX" as LILO option to prohibit driver from scanning - XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE - processor id cannot be scanned */ - -static char *megaraid; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) /* 0x20100 */ -#ifdef MODULE -MODULE_PARM (megaraid, "s"); -#endif + } #endif -static int skip_id = -1; -static int numCtlrs = 0; -static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = { 0 }; -static struct proc_dir_entry *mega_proc_dir_entry; + } -#if DEBUG -static u32 maxCmdTime = 0; -#endif + /* + * Do not return the presence of hard disk on the channel so, + * inquiry sent, and returned data==hard disk or removable + * hard disk and not logical, request should return failure! - + * PJ + */ + islogical = adapter->logdrv_chan[cmd->device->channel]; + if( cmd->cmnd[0] == INQUIRY && !islogical ) { -static mega_scb *pLastScb = NULL; -static struct notifier_block mega_notifier = { - megaraid_reboot_notify, - NULL, - 0 -}; + if( cmd->use_sg ) { + sgl = (struct scatterlist *) + cmd->request_buffer; + + if( sgl->page ) { + c = *(unsigned char *) + page_address((&sgl[0])->page) + + (&sgl[0])->offset; + } + else { + printk(KERN_WARNING + "megaraid: invalid sg.\n"); + c = 0; + } + } + else { + c = *(u8 *)cmd->request_buffer; + } -/* For controller re-ordering */ -struct mega_hbas mega_hbas[MAX_CONTROLLERS]; + if(IS_RAID_CH(adapter, cmd->device->channel) && + ((c & 0x1F ) == TYPE_DISK)) { + status = 0xF0; + } + } -/* - * The File Operations structure for the serial/ioctl interface of the driver - */ -/* For controller re-ordering */ + /* clear result; otherwise, success returns corrupt value */ + cmd->result = 0; -static struct file_operations megadev_fops = { - .owner = THIS_MODULE, - .ioctl = megadev_ioctl_entry, -}; + /* Convert MegaRAID status to Linux error code */ + switch (status) { + case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */ + cmd->result |= (DID_OK << 16); + break; -/* - * Array to structures for storing the information about the controllers. This - * information is sent to the user level applications, when they do an ioctl - * for this information. - */ -static struct mcontroller mcontroller[MAX_CONTROLLERS]; + case 0x02: /* ERROR_ABORTED, i.e. + SCSI_STATUS_CHECK_CONDITION */ -/* The current driver version */ -static u32 driver_ver = 114; + /* set sense_buffer and result fields */ + if( mbox->cmd == MEGA_MBOXCMD_PASSTHRU || + mbox->cmd == MEGA_MBOXCMD_PASSTHRU64 ) { + + memcpy(cmd->sense_buffer, pthru->reqsensearea, + 14); + + cmd->result = (DRIVER_SENSE << 24) | + (DID_OK << 16) | + (CHECK_CONDITION << 1); + } + else { + if (mbox->cmd == MEGA_MBOXCMD_EXTPTHRU) { -/* major number used by the device for character interface */ -static int major; + memcpy(cmd->sense_buffer, + epthru->reqsensearea, 14); -static struct semaphore mimd_ioctl_sem; -static struct semaphore mimd_entry_mtx; + cmd->result = (DRIVER_SENSE << 24) | + (DID_OK << 16) | + (CHECK_CONDITION << 1); + } else { + cmd->sense_buffer[0] = 0x70; + cmd->sense_buffer[2] = ABORTED_COMMAND; + cmd->result |= (CHECK_CONDITION << 1); + } + } + break; -#if SERDEBUG -volatile static spinlock_t serial_lock; -#endif + case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. + SCSI_STATUS_BUSY */ + cmd->result |= (DID_BUS_BUSY << 16) | status; + break; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x20300 */ -static struct proc_dir_entry proc_scsi_megaraid = { - PROC_SCSI_MEGARAID, 8, "megaraid", - S_IFDIR | S_IRUGO | S_IXUGO, 2 -}; -#endif + default: +#if MEGA_HAVE_CLUSTERING + /* + * If TEST_UNIT_READY fails, we know + * MEGA_RESERVATION_STATUS failed + */ + if( cmd->cmnd[0] == TEST_UNIT_READY ) { + cmd->result |= (DID_ERROR << 16) | + (RESERVATION_CONFLICT << 1); + } + else + /* + * Error code returned is 1 if Reserve or Release + * failed or the input parameter is invalid + */ + if( status == 1 && + (cmd->cmnd[0] == RESERVE || + cmd->cmnd[0] == RELEASE) ) { -#ifdef CONFIG_PROC_FS -extern struct proc_dir_entry proc_root; + cmd->result |= (DID_ERROR << 16) | + (RESERVATION_CONFLICT << 1); + } + else #endif + cmd->result |= (DID_BAD_TARGET << 16)|status; + } -static char mega_ch_class; /* channels are raid or scsi */ -#define IS_RAID_CH(ch) ( (mega_ch_class >> (ch)) & 0x01 ) - -#if SERDEBUG -static char strbuf[MAX_SERBUF + 1]; + /* + * Only free SCBs for the commands coming down from the + * mid-layer, not for which were issued internally + * + * For internal command, restore the status returned by the + * firmware so that user can interpret it. + */ + if( cmdid == CMDID_INT_CMDS ) { /* internal command */ + cmd->result = status; -static void ser_init (void) -{ - unsigned port = COM_BASE; + /* + * Remove the internal command from the pending list + */ + list_del_init(&scb->list); + scb->state = SCB_FREE; + } + else { + mega_free_scb(adapter, scb); + } - outb (0x80, port + 3); - outb (0, port + 1); - /* 9600 Baud, if 19200: outb(6,port) */ - outb (12, port); - outb (3, port + 3); - outb (0, port + 1); + /* Add Scsi_Command to end of completed queue */ + list_add_tail(SCSI_LIST(cmd), &adapter->completed_list); + } } -static void ser_puts (char *str) + +/* + * mega_runpendq() + * + * Run through the list of completed requests and finish it + */ +static void +mega_rundoneq (adapter_t *adapter) { - char *ptr; + Scsi_Cmnd *cmd; + struct list_head *pos; - ser_init (); - for (ptr = str; *ptr; ++ptr) - ser_putc (*ptr); -} + list_for_each(pos, &adapter->completed_list) { -static void ser_putc (char c) -{ - unsigned port = COM_BASE; + Scsi_Pointer* spos = (Scsi_Pointer *)pos; - while ((inb (port + 5) & 0x20) == 0) ; - outb (c, port); - if (c == 0x0a) { - while ((inb (port + 5) & 0x20) == 0) ; - outb (0x0d, port); + cmd = list_entry(spos, Scsi_Cmnd, SCp); + cmd->scsi_done(cmd); } + + INIT_LIST_HEAD(&adapter->completed_list); } -static int ser_printk (const char *fmt, ...) + +/* + * Free a SCB structure + * Note: We assume the scsi commands associated with this scb is not free yet. + */ +static void +mega_free_scb(adapter_t *adapter, scb_t *scb) { - va_list args; - int i; - long flags; + switch( scb->dma_type ) { - spin_lock_irqsave (&serial_lock, flags); - va_start (args, fmt); - i = vsprintf (strbuf, fmt, args); - ser_puts (strbuf); - va_end (args); - spin_unlock_irqrestore (&serial_lock, flags); + case MEGA_DMA_TYPE_NONE: + break; - return i; -} + case MEGA_BULK_DATA: + pci_unmap_page(adapter->dev, scb->dma_h_bulkdata, + scb->cmd->request_bufflen, scb->dma_direction); + + if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { + pci_dma_sync_single(adapter->dev, + scb->dma_h_bulkdata, + scb->cmd->request_bufflen, + PCI_DMA_FROMDEVICE); + } -#define TRACE(a) { ser_printk a;} + break; -#else -#define TRACE(A) -#endif + case MEGA_SGLIST: + pci_unmap_sg(adapter->dev, scb->cmd->request_buffer, + scb->cmd->use_sg, scb->dma_direction); + + if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { + pci_dma_sync_sg(adapter->dev, + scb->cmd->request_buffer, + scb->cmd->use_sg, PCI_DMA_FROMDEVICE); + } -#define TRACE1(a) + break; -static void callDone (Scsi_Cmnd * SCpnt) -{ - if (SCpnt->result) { - TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n", - SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->device->channel, - SCpnt->device->id, SCpnt->device->lun, SCpnt->result)); + default: + break; } - SCpnt->scsi_done (SCpnt); + + /* + * Remove from the pending list + */ + list_del_init(&scb->list); + + /* Link the scb back into free list */ + scb->state = SCB_FREE; + scb->cmd = NULL; + + list_add(&scb->list, &adapter->free_list); } -/*------------------------------------------------------------------------- - * - * Local functions - * - *-------------------------------------------------------------------------*/ -/*======================= - * Free a SCB structure - *======================= +/* + * Wait until the controller's mailbox is available */ -static void mega_freeSCB (mega_host_config * megaCfg, mega_scb * pScb) +static inline int +mega_busywait_mbox (adapter_t *adapter) { + if (adapter->mbox->busy) + return __mega_busywait_mbox(adapter); + return 0; +} - mega_scb *pScbtmp; +static int +__mega_busywait_mbox (adapter_t *adapter) +{ + volatile mbox_t *mbox = adapter->mbox; + long counter; - if ((pScb == NULL) || (pScb->idx >= 0xFE)) { - return; + for (counter = 0; counter < 10000; counter++) { + if (!mbox->busy) + return 0; + udelay(100); yield(); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - switch (pScb->dma_type) { - case M_RD_DMA_TYPE_NONE: - break; - case M_RD_PTHRU_WITH_BULK_DATA: - pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, - pScb->pthru->dataxferlen, - pScb->dma_direction); - break; - case M_RD_EPTHRU_WITH_BULK_DATA: - pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, - pScb->epthru->dataxferlen, - pScb->dma_direction); - break; - case M_RD_PTHRU_WITH_SGLIST: - { - int count; - for (count = 0; count < pScb->sglist_count; count++) { - pci_unmap_single (megaCfg->dev, - pScb->dma_h_sglist[count], - pScb->sgList[count].length, - pScb->dma_direction); + return -1; /* give up after 1 second */ +} - } - break; - } - case M_RD_BULK_DATA_ONLY: - pci_unmap_single (megaCfg->dev, - pScb->dma_h_bulkdata, - pScb->iDataSize, pScb->dma_direction); +/* + * Copies data to SGLIST + * Note: For 64 bit cards, we need a minimum of one SG element for read/write + */ +static int +mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len) +{ + struct scatterlist *sgl; + struct page *page; + unsigned long offset; + Scsi_Cmnd *cmd; + int sgcnt; + int idx; - break; - case M_RD_SGLIST_ONLY: - pci_unmap_sg (megaCfg->dev, - pScb->SCpnt->request_buffer, - pScb->SCpnt->use_sg, pScb->dma_direction); - break; - default: - break; - } -#endif + cmd = scb->cmd; - /* Unlink from pending queue */ - if (pScb == megaCfg->qPendingH) { + /* Scatter-gather not used */ + if( !cmd->use_sg ) { - if (megaCfg->qPendingH == megaCfg->qPendingT) - megaCfg->qPendingH = megaCfg->qPendingT = NULL; - else - megaCfg->qPendingH = megaCfg->qPendingH->next; + page = virt_to_page(cmd->request_buffer); - megaCfg->qPcnt--; + offset = ((unsigned long)cmd->request_buffer & ~PAGE_MASK); - } else { - for (pScbtmp = megaCfg->qPendingH; pScbtmp; - pScbtmp = pScbtmp->next) { + scb->dma_h_bulkdata = pci_map_page(adapter->dev, + page, offset, + cmd->request_bufflen, + scb->dma_direction); + scb->dma_type = MEGA_BULK_DATA; - if (pScbtmp->next == pScb) { + /* + * We need to handle special 64-bit commands that need a + * minimum of 1 SG + */ + if( adapter->has_64bit_addr ) { + scb->sgl64[0].address = scb->dma_h_bulkdata; + scb->sgl64[0].length = cmd->request_bufflen; + *buf = (u32)scb->sgl_dma_addr; + *len = (u32)cmd->request_bufflen; + return 1; + } + else { + *buf = (u32)scb->dma_h_bulkdata; + *len = (u32)cmd->request_bufflen; + } - pScbtmp->next = pScb->next; + if( scb->dma_direction == PCI_DMA_TODEVICE ) { + pci_dma_sync_single(adapter->dev, + scb->dma_h_bulkdata, + cmd->request_bufflen, + PCI_DMA_TODEVICE); + } - if (pScb == megaCfg->qPendingT) { - megaCfg->qPendingT = pScbtmp; - } + return 0; + } - megaCfg->qPcnt--; - break; - } + sgl = (struct scatterlist *)cmd->request_buffer; + + /* + * Copy Scatter-Gather list info into controller structure. + * + * The number of sg elements returned must not exceed our limit + */ + sgcnt = pci_map_sg(adapter->dev, sgl, cmd->use_sg, + scb->dma_direction); + + scb->dma_type = MEGA_SGLIST; + + if( sgcnt > adapter->sglen ) BUG(); + + for( idx = 0; idx < sgcnt; idx++, sgl++ ) { + + if( adapter->has_64bit_addr ) { + scb->sgl64[idx].address = sg_dma_address(sgl); + scb->sgl64[idx].length = sg_dma_len(sgl); + } + else { + scb->sgl[idx].address = sg_dma_address(sgl); + scb->sgl[idx].length = sg_dma_len(sgl); } } - /* Link back into free list */ - pScb->state = SCB_FREE; - pScb->SCpnt = NULL; + /* Reset pointer and length fields */ + *buf = scb->sgl_dma_addr; - if (megaCfg->qFreeH == (mega_scb *) NULL) { - megaCfg->qFreeH = megaCfg->qFreeT = pScb; - } else { - megaCfg->qFreeT->next = pScb; - megaCfg->qFreeT = pScb; - } + /* + * For passthru command, dataxferlen must be set, even for commands + * with a sg list + */ + *len = (u32)cmd->request_bufflen; - megaCfg->qFreeT->next = NULL; - megaCfg->qFcnt++; + if( scb->dma_direction == PCI_DMA_TODEVICE ) { + pci_dma_sync_sg(adapter->dev, sgl, cmd->use_sg, + PCI_DMA_TODEVICE); + } + /* Return count of SG requests */ + return sgcnt; } -/*=========================== - * Allocate a SCB structure - *=========================== + +/* + * mega_8_to_40ld() + * + * takes all info in AdapterInquiry structure and puts it into ProductInfo and + * Enquiry3 structures for later use */ -static mega_scb *mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +static void +mega_8_to_40ld(mraid_inquiry *inquiry, mega_inquiry3 *enquiry3, + mega_product_info *product_info) { - mega_scb *pScb; + int i; - /* Unlink command from Free List */ - if ((pScb = megaCfg->qFreeH) != NULL) { - megaCfg->qFreeH = pScb->next; - megaCfg->qFcnt--; - - pScb->isrcount = jiffies; - pScb->next = NULL; - pScb->state = SCB_ACTIVE; - pScb->SCpnt = SCpnt; + product_info->max_commands = inquiry->adapter_info.max_commands; + enquiry3->rebuild_rate = inquiry->adapter_info.rebuild_rate; + product_info->nchannels = inquiry->adapter_info.nchannels; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pScb->dma_type = M_RD_DMA_TYPE_NONE; -#endif + for (i = 0; i < 4; i++) { + product_info->fw_version[i] = + inquiry->adapter_info.fw_version[i]; - return pScb; + product_info->bios_version[i] = + inquiry->adapter_info.bios_version[i]; } + enquiry3->cache_flush_interval = + inquiry->adapter_info.cache_flush_interval; - printk (KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n"); - - return NULL; -} - -/* Run through the list of completed requests and finish it */ -static void mega_rundoneq (mega_host_config * megaCfg) -{ - Scsi_Cmnd *SCpnt; + product_info->dram_size = inquiry->adapter_info.dram_size; - while ((SCpnt = megaCfg->qCompletedH) != NULL) { - megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; - megaCfg->qCcnt--; + enquiry3->num_ldrv = inquiry->logdrv_info.num_ldrv; - SCpnt->host_scribble = (unsigned char *) NULL; /* XC : sep 14 */ - /* Callback */ - callDone (SCpnt); + for (i = 0; i < MAX_LOGICAL_DRIVES_8LD; i++) { + enquiry3->ldrv_size[i] = inquiry->logdrv_info.ldrv_size[i]; + enquiry3->ldrv_prop[i] = inquiry->logdrv_info.ldrv_prop[i]; + enquiry3->ldrv_state[i] = inquiry->logdrv_info.ldrv_state[i]; } - megaCfg->qCompletedH = megaCfg->qCompletedT = NULL; + for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) + enquiry3->pdrv_state[i] = inquiry->pdrv_info.pdrv_state[i]; } + /* - * Runs through the list of pending requests - * Assumes that mega_lock spin_lock has been acquired. + * megaraid_proc_info() + * + * Returns data to be displayed in /proc/scsi/megaraid/X */ -static int mega_runpendq (mega_host_config * megaCfg) +static int +megaraid_proc_info(char *buffer, char **start, off_t offset, int length, + int host_no, int inout) { - mega_scb *pScb; - int rc; - - /* Issue any pending commands to the card */ - for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { - if (pScb->state == SCB_ACTIVE) { - if ((rc = - megaIssueCmd (megaCfg, pScb->mboxData, pScb, 1)) == -1) - return rc; - } - } + *start = buffer; return 0; } -/* Add command to the list of completed requests */ -static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status) +/* + * Release the controller's resources + */ +static int +megaraid_release(struct Scsi_Host *host) { - int islogical; - Scsi_Cmnd *SCpnt; - mega_passthru *pthru; - mega_ext_passthru *epthru; - mega_mailbox *mbox; - struct scatterlist *sgList; - u8 c; + adapter_t *adapter; + mbox_t *mbox; + u_char raw_mbox[16]; + char buf[12] = { 0 }; - if (pScb == NULL) { - TRACE (("NULL pScb in mega_cmd_done!")); - printk(KERN_CRIT "NULL pScb in mega_cmd_done!"); - } + adapter = (adapter_t *)host->hostdata; + mbox = (mbox_t *)raw_mbox; - SCpnt = pScb->SCpnt; + printk(KERN_NOTICE "megaraid: being unloaded..."); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = pScb->pthru; - epthru = pScb->epthru; -#else - pthru = &pScb->pthru; - epthru = &pScb->epthru; -#endif + /* Flush adapter cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_ADAPTER; - mbox = (mega_mailbox *) & pScb->mboxData; + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); - if (SCpnt == NULL) { - TRACE (("NULL SCpnt in mega_cmd_done!")); - TRACE (("pScb->idx = ", pScb->idx)); - TRACE (("pScb->state = ", pScb->state)); - TRACE (("pScb->state = ", pScb->state)); - panic(KERN_ERR "megaraid:Problem...!\n"); - } + /* Issue a blocking (interrupts disabled) command to the card */ + issue_scb_block(adapter, raw_mbox); - islogical = ( (SCpnt->device->channel >= megaCfg->productInfo.SCSIChanPresent) && - (SCpnt->device->channel <= megaCfg->host->max_channel) ); -#if 0 - islogical = (SCpnt->device->channel == megaCfg->host->max_channel); -#endif + /* Flush disks cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_SYSTEM; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Special Case to handle PassThrough->XferAddrress > 4GB */ - switch (SCpnt->cmnd[0]) { - case INQUIRY: - case READ_CAPACITY: - memcpy (SCpnt->request_buffer, - pScb->bounce_buffer, SCpnt->request_bufflen); - break; - } -#endif + /* Issue a blocking (interrupts disabled) command to the card */ + issue_scb_block(adapter, raw_mbox); - mega_freeSCB (megaCfg, pScb); - /* - * Do not return the presence of hard disk on the channel so, inquiry - * sent, and returned data==hard disk or removable hard disk and not - * logical, request should return failure! - PJ - */ -#if 0 - if (SCpnt->cmnd[0] == INQUIRY && ((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && !islogical) { - status = 0xF0; + /* Free our resources */ + if( adapter->flag & BOARD_MEMMAP ) { + iounmap((void *)adapter->base); + release_mem_region(adapter->host->base, 128); } -#endif - - if (SCpnt->cmnd[0] == INQUIRY && !islogical) { - if ( SCpnt->use_sg ) { - sgList = (struct scatterlist *)SCpnt->request_buffer; - memcpy(&c, cpu_to_le32(sg_dma_address(&sgList[0])), 0x1); - } else { - memcpy(&c, SCpnt->request_buffer, 0x1); - } -#if 0 - if( (c & 0x1F ) == TYPE_DISK ) { - status = 0xF0; - } -#endif - if( IS_RAID_CH(SCpnt->device->channel) && ((c & 0x1F ) == TYPE_DISK) ) { - status = 0xF0; - } + else { + release_region(adapter->base, 16); } + mega_free_sgl(adapter); - /* clear result; otherwise, success returns corrupt value */ - SCpnt->result = 0; +#ifdef CONFIG_PROC_FS + if( adapter->controller_proc_dir_entry ) { + remove_proc_entry("stat", adapter->controller_proc_dir_entry); + remove_proc_entry("config", + adapter->controller_proc_dir_entry); + remove_proc_entry("mailbox", + adapter->controller_proc_dir_entry); +#if MEGA_HAVE_ENH_PROC + remove_proc_entry("rebuild-rate", + adapter->controller_proc_dir_entry); + remove_proc_entry("battery-status", + adapter->controller_proc_dir_entry); + + remove_proc_entry("diskdrives-ch0", + adapter->controller_proc_dir_entry); + remove_proc_entry("diskdrives-ch1", + adapter->controller_proc_dir_entry); + remove_proc_entry("diskdrives-ch2", + adapter->controller_proc_dir_entry); + remove_proc_entry("diskdrives-ch3", + adapter->controller_proc_dir_entry); + + remove_proc_entry("raiddrives-0-9", + adapter->controller_proc_dir_entry); + remove_proc_entry("raiddrives-10-19", + adapter->controller_proc_dir_entry); + remove_proc_entry("raiddrives-20-29", + adapter->controller_proc_dir_entry); + remove_proc_entry("raiddrives-30-39", + adapter->controller_proc_dir_entry); +#endif + + sprintf(buf, "hba%d", adapter->host->host_no); + remove_proc_entry(buf, mega_proc_dir_entry); + } +#endif - if ((SCpnt->cmnd[0] & M_RD_IOCTL_CMD)) { /* i.e. ioctl cmd such as M_RD_IOCTL_CMD, M_RD_IOCTL_CMD_NEW of megamgr */ - switch (status) { - case 2: - case 0xF0: - case 0xF4: - SCpnt->result = (DID_BAD_TARGET << 16) | status; - break; - default: - SCpnt->result |= status; - } /*end of switch */ - } else { - /* Convert MegaRAID status to Linux error code */ - switch (status) { - case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */ - SCpnt->result |= (DID_OK << 16); - break; + pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE, + adapter->mega_buffer, adapter->buf_dma_handle); + kfree(adapter->scb_list); + pci_free_consistent(adapter->dev, sizeof(mbox64_t), + (void *)adapter->una_mbox64, adapter->una_mbox64_dma); - case 0x02: /* ERROR_ABORTED, i.e. SCSI_STATUS_CHECK_CONDITION */ + hba_count--; - /*set sense_buffer and result fields */ - if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU) { - memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14); - } else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) { - SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION < 1); - memcpy( - SCpnt->sense_buffer, - epthru->reqsensearea, 14 - ); - SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION < 1); - /*SCpnt->result = - (DRIVER_SENSE << 24) | - (DID_ERROR << 16) | status;*/ - } else { - SCpnt->sense_buffer[0] = 0x70; - SCpnt->sense_buffer[2] = ABORTED_COMMAND; - SCpnt->result |= (CHECK_CONDITION << 1); - } - break; + if( hba_count == 0 ) { - case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. SCSI_STATUS_BUSY */ - SCpnt->result |= (DID_BUS_BUSY << 16) | status; - break; + /* + * Unregister the character device interface to the driver. + */ + unregister_chrdev(major, "megadev"); - default: - SCpnt->result |= (DID_BAD_TARGET << 16) | status; - break; + unregister_reboot_notifier(&mega_notifier); + +#ifdef CONFIG_PROC_FS + if( adapter->controller_proc_dir_entry ) { + remove_proc_entry ("megaraid", &proc_root); } - } +#endif - /* Add Scsi_Command to end of completed queue */ - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; } - megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; - megaCfg->qCcnt++; -} + /* + * Release the controller memory. A word of warning this frees + * hostdata and that includes adapter-> so be careful what you + * dereference beyond this point + */ + scsi_unregister(host); -/*------------------------------------------------------------------- - * - * Build a SCB from a Scsi_Cmnd - * - * Returns a SCB pointer, or NULL - * If NULL is returned, the scsi_done function MUST have been called - * - *-------------------------------------------------------------------*/ -static mega_scb *mega_build_cmd (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) -{ - mega_scb *pScb; - mega_mailbox *mbox; - mega_passthru *pthru; - mega_ext_passthru *epthru; - long seg; - char islogical; - int lun = SCpnt->device->lun; - int max_lun; - - if ((SCpnt->cmnd[0] == MEGADEVIOC)) - return megadev_doioctl (megaCfg, SCpnt); - - if ((SCpnt->cmnd[0] == M_RD_IOCTL_CMD) - || (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW)) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - return mega_ioctl (megaCfg, SCpnt); /* Handle IOCTL command */ -#else - { - printk(KERN_WARNING "megaraid ioctl: older interface - " - "not supported.\n"); - return NULL; - } -#endif + printk("ok.\n"); - islogical = ( (SCpnt->device->channel >= megaCfg->productInfo.SCSIChanPresent) && - (SCpnt->device->channel <= megaCfg->host->max_channel) ); -#if 0 - islogical = (IS_RAID_CH(SCpnt->device->channel) && /* virtual ch is raid - AM */ - (SCpnt->device->channel == megaCfg->host->max_channel)); -#endif + return 0; +} - if ( ! megaCfg->support_ext_cdb ) { - if (!islogical && lun != 0) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } - } +static inline void +mega_free_sgl(adapter_t *adapter) +{ + scb_t *scb; + int i; - if (!islogical && SCpnt->device->id == skip_id) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } + for(i = 0; i < adapter->max_cmds; i++) { - if (islogical) { + scb = &adapter->scb_list[i]; - /* have just LUN 0 for each target on virtual channels */ - if( SCpnt->device->lun != 0 ) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } + if( scb->sgl64 ) { + pci_free_consistent(adapter->dev, + sizeof(mega_sgl64) * adapter->sglen, + scb->sgl64, + scb->sgl_dma_addr); - lun = mega_get_lun(megaCfg, SCpnt); + scb->sgl64 = NULL; + } - max_lun = (megaCfg->flag & BOARD_40LD) ? - FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; + if( scb->pthru ) { + pci_free_consistent(adapter->dev, sizeof(mega_passthru), + scb->pthru, scb->pthru_dma_addr); - /* - * max_lun increases by 0x80 if some logical drive was deleted. - */ - if(megaCfg->read_ldidmap) { - max_lun += 0x80; + scb->pthru = NULL; } - if( lun > max_lun ) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } + if( scb->epthru ) { + pci_free_consistent(adapter->dev, + sizeof(mega_ext_passthru), + scb->epthru, scb->epthru_dma_addr); - /* - * If we have a logical drive with boot enabled, project it first - */ - if( megaCfg->boot_ldrv_enabled ) { - if( lun == 0 ) { - lun = megaCfg->boot_ldrv; - } - else { - if( lun <= megaCfg->boot_ldrv ) { - lun--; - } - } - } - } else { - if ( lun > 7) { - /* Do not support lun >7 for physically accessed devices */ - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; + scb->epthru = NULL; } + } - /*----------------------------------------------------- - * - * Logical drive commands - * - *-----------------------------------------------------*/ - if (islogical) { - switch (SCpnt->cmnd[0]) { - case TEST_UNIT_READY: - memset (SCpnt->request_buffer, 0, SCpnt->request_bufflen); - SCpnt->result = (DID_OK << 16); - callDone (SCpnt); - return NULL; +} - case MODE_SENSE: - memset (SCpnt->request_buffer, 0, SCpnt->cmnd[4]); - SCpnt->result = (DID_OK << 16); - callDone (SCpnt); - return NULL; - case READ_CAPACITY: - case INQUIRY: - /* Allocate a SCB and initialize passthru */ - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = pScb->pthru; -#else - pthru = &pScb->pthru; -#endif +/* + * Get information about the card/driver + */ +const char * +megaraid_info(struct Scsi_Host *host) +{ + static char buffer[512]; + adapter_t *adapter; - mbox = (mega_mailbox *) & pScb->mboxData; - memset (mbox, 0, sizeof (pScb->mboxData)); - memset (pthru, 0, sizeof (mega_passthru)); - pthru->timeout = 0; - pthru->ars = 1; - pthru->reqsenselen = 14; - pthru->islogical = 1; - pthru->logdrv = lun; - pthru->cdblen = SCpnt->cmd_len; + adapter = (adapter_t *)host->hostdata; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /*Not sure about the direction */ - pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; - pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; + sprintf (buffer, + "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", + adapter->fw_version, adapter->product_info.max_commands, + adapter->host->max_id, adapter->host->max_channel, + adapter->host->max_lun); + return buffer; +} -#if 0 -/* Normal Code w/o the need for bounce buffer */ - pScb->dma_h_bulkdata - = pci_map_single (megaCfg->dev, - SCpnt->request_buffer, - SCpnt->request_bufflen, - pScb->dma_direction); +volatile static int internal_done_flag = 0; +volatile static int internal_done_errcode = 0; - pthru->dataxferaddr = pScb->dma_h_bulkdata; -#else -/* Special Code to use bounce buffer for READ_CAPA/INQ */ - pthru->dataxferaddr = pScb->dma_bounce_buffer; - pScb->dma_type = M_RD_DMA_TYPE_NONE; -#endif +static DECLARE_WAIT_QUEUE_HEAD (internal_wait); -#else - pthru->dataxferaddr = - virt_to_bus (SCpnt->request_buffer); -#endif +static void internal_done (Scsi_Cmnd *cmd) +{ + internal_done_errcode = cmd->result; + internal_done_flag++; + wake_up (&internal_wait); +} - pthru->dataxferlen = SCpnt->request_bufflen; - memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); +/* shouldn't be used, but included for completeness */ - /* Initialize mailbox area */ - mbox->cmd = MEGA_MBOXCMD_PASSTHRU; +static int +megaraid_command (Scsi_Cmnd *cmd) +{ + internal_done_flag = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mbox->xferaddr = pScb->dma_passthruhandle64; - TRACE1 (("M_RD_PTHRU_WITH_BULK_DATA Enabled \n")); -#else - mbox->xferaddr = virt_to_bus (pthru); -#endif - return pScb; + /* Queue command, and wait until it has completed */ + megaraid_queue (cmd, internal_done); - case READ_6: - case WRITE_6: - case READ_10: - case WRITE_10: - /* Allocate a SCB and initialize mailbox */ - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - mbox = (mega_mailbox *) & pScb->mboxData; + while (!internal_done_flag) + interruptible_sleep_on (&internal_wait); - memset (mbox, 0, sizeof (pScb->mboxData)); - mbox->logdrv = lun; + return internal_done_errcode; +} - if (megaCfg->flag & BOARD_64BIT) { - mbox->cmd = (*SCpnt->cmnd == READ_6 - || *SCpnt->cmnd == - READ_10) ? MEGA_MBOXCMD_LREAD64 : - MEGA_MBOXCMD_LWRITE64; - } else { - mbox->cmd = (*SCpnt->cmnd == READ_6 - || *SCpnt->cmnd == - READ_10) ? MEGA_MBOXCMD_LREAD : - MEGA_MBOXCMD_LWRITE; - } - - /* 6-byte */ - if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) { - mbox->numsectors = (u32) SCpnt->cmnd[4]; - mbox->lba = - ((u32) SCpnt->cmnd[1] << 16) | - ((u32) SCpnt->cmnd[2] << 8) | - (u32) SCpnt->cmnd[3]; - mbox->lba &= 0x1FFFFF; - if (*SCpnt->cmnd == READ_6) { - megaCfg->nReads[(int) lun]++; - megaCfg->nReadBlocks[(int) lun] += - mbox->numsectors; - } else { - megaCfg->nWrites[(int) lun]++; - megaCfg->nWriteBlocks[(int) lun] += - mbox->numsectors; - } - } +/* + * Abort a previous SCSI request. Only commands on the pending list can be + * aborted. All the commands issued to the F/W must complete. + */ +static int +megaraid_abort(Scsi_Cmnd *cmd) +{ + adapter_t *adapter; + int rval; - /* 10-byte */ - if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) { - mbox->numsectors = - (u32) SCpnt->cmnd[8] | - ((u32) SCpnt->cmnd[7] << 8); - mbox->lba = - ((u32) SCpnt->cmnd[2] << 24) | - ((u32) SCpnt->cmnd[3] << 16) | - ((u32) SCpnt->cmnd[4] << 8) | - (u32) SCpnt->cmnd[5]; - - if (*SCpnt->cmnd == READ_10) { - megaCfg->nReads[(int) lun]++; - megaCfg->nReadBlocks[(int) lun] += - mbox->numsectors; - } else { - megaCfg->nWrites[(int) lun]++; - megaCfg->nWriteBlocks[(int) lun] += - mbox->numsectors; - } - } + adapter = (adapter_t *)cmd->device->host->hostdata; - /* 12-byte */ - if (*SCpnt->cmnd == READ_12 || *SCpnt->cmnd == WRITE_12) { - mbox->lba = - ((u32) SCpnt->cmnd[2] << 24) | - ((u32) SCpnt->cmnd[3] << 16) | - ((u32) SCpnt->cmnd[4] << 8) | - (u32) SCpnt->cmnd[5]; + rval = megaraid_abort_and_reset(adapter, cmd, SCB_ABORT); - mbox->numsectors = - ((u32) SCpnt->cmnd[6] << 24) | - ((u32) SCpnt->cmnd[7] << 16) | - ((u32) SCpnt->cmnd[8] << 8) | - (u32) SCpnt->cmnd[9]; - - if (*SCpnt->cmnd == READ_12) { - megaCfg->nReads[(int) lun]++; - megaCfg->nReadBlocks[(int) lun] += - mbox->numsectors; - } else { - megaCfg->nWrites[(int) lun]++; - megaCfg->nWriteBlocks[(int) lun] += - mbox->numsectors; - } - } + /* + * This is required here to complete any completed requests + * to be communicated over to the mid layer. + */ + mega_rundoneq(adapter); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10 - || *SCpnt->cmnd == READ_12) { - pScb->dma_direction = PCI_DMA_FROMDEVICE; - } else { /*WRITE_6 or WRITE_10 */ - pScb->dma_direction = PCI_DMA_TODEVICE; - } -#endif + return rval; +} - /* Calculate Scatter-Gather info */ - mbox->numsgelements = mega_build_sglist (megaCfg, pScb, - (u32 *)&mbox->xferaddr, (u32 *)&seg); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pScb->iDataSize = seg; +static int +megaraid_reset(Scsi_Cmnd *cmd) +{ + adapter_t *adapter; + megacmd_t mc; + int rval; + + adapter = (adapter_t *)cmd->device->host->hostdata; - if (mbox->numsgelements) { - pScb->dma_type = M_RD_SGLIST_ONLY; - TRACE1 (("M_RD_SGLIST_ONLY Enabled \n")); - } else { - pScb->dma_type = M_RD_BULK_DATA_ONLY; - TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); - } -#endif +#if MEGA_HAVE_CLUSTERING + mc.cmd = MEGA_CLUSTER_CMD; + mc.opcode = MEGA_RESET_RESERVATIONS; - return pScb; - default: - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } + spin_unlock_irq(&adapter->lock); + if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) != 0 ) { + printk(KERN_WARNING + "megaraid: reservation reset failed.\n"); } - /*----------------------------------------------------- - * - * Passthru drive commands - * - *-----------------------------------------------------*/ else { - /* Allocate a SCB and initialize passthru */ - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } + printk(KERN_INFO "megaraid: reservation reset.\n"); + } + spin_lock_irq(&adapter->lock); +#endif - mbox = (mega_mailbox *) pScb->mboxData; - memset (mbox, 0, sizeof (pScb->mboxData)); + rval = megaraid_abort_and_reset(adapter, cmd, SCB_RESET); - if ( megaCfg->support_ext_cdb && SCpnt->cmd_len > 10 ) { - epthru = mega_prepare_extpassthru(megaCfg, pScb, SCpnt); - mbox->cmd = MEGA_MBOXCMD_EXTPASSTHRU; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mbox->xferaddr = pScb->dma_ext_passthruhandle64; - - if(epthru->numsgelements) { - pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; - } else { - pScb->dma_type = M_RD_EPTHRU_WITH_BULK_DATA; - } -#else - mbox->xferaddr = virt_to_bus(epthru); -#endif - } - else { - pthru = mega_prepare_passthru(megaCfg, pScb, SCpnt); + /* + * This is required here to complete any completed requests + * to be communicated over to the mid layer. + */ + mega_rundoneq(adapter); - /* Initialize mailbox */ - mbox->cmd = MEGA_MBOXCMD_PASSTHRU; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mbox->xferaddr = pScb->dma_passthruhandle64; - - if (pthru->numsgelements) { - pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; - } else { - pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; - } -#else - mbox->xferaddr = virt_to_bus(pthru); -#endif - } - return pScb; - } - return NULL; + return rval; } + + +/** + * megaraid_abort_and_reset() + * @adapter - megaraid soft state + * @cmd - scsi command to be aborted or reset + * @aor - abort or reset flag + * + * Try to locate the scsi command in the pending queue. If found and is not + * issued to the controller, abort/reset it. Otherwise return failure + */ static int -mega_get_lun(mega_host_config *this_hba, Scsi_Cmnd *sc) +megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) { - int tgt; - int lun; - int virt_chan; + struct list_head *pos, *next; + scb_t *scb; - tgt = sc->device->id; - - if ( tgt > 7 ) tgt--; /* we do not get inquires for tgt 7 */ + printk(KERN_WARNING "megaraid: %s-%lx cmd=%x \n", + (aor == SCB_ABORT)? "ABORTING":"RESET", cmd->serial_number, + cmd->cmnd[0], cmd->device->channel, + cmd->device->id, cmd->device->lun); - virt_chan = sc->device->channel - this_hba->productInfo.SCSIChanPresent; - lun = (virt_chan * 15) + tgt; + if(list_empty(&adapter->pending_list)) + return FALSE; - /* - * If "delete logical drive" feature is enabled on this controller. - * Do only if at least one delete logical drive operation was done. - * - * Also, after logical drive deletion, instead of logical drive number, - * the value returned should be 0x80+logical drive id. - * - * These is valid only for IO commands. - */ + list_for_each_safe(pos, next, &adapter->pending_list) { - if( this_hba->support_random_del && this_hba->read_ldidmap ) { - switch(sc->cmnd[0]) { - case READ_6: /* fall through */ - case WRITE_6: /* fall through */ - case READ_10: /* fall through */ - case WRITE_10: - lun += 0x80; - } - } + scb = list_entry(pos, scb_t, list); - return lun; -} + if (scb->cmd == cmd) { /* Found command */ + scb->state |= aor; -static mega_passthru * -mega_prepare_passthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc) -{ - mega_passthru *pthru; + /* + * Check if this command has firmare owenership. If + * yes, we cannot reset this command. Whenever, f/w + * completes this command, we will return appropriate + * status from ISR. + */ + if( scb->state & SCB_ISSUED ) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = scb->pthru; -#else - pthru = &scb->pthru; -#endif - memset (pthru, 0, sizeof (mega_passthru)); + printk(KERN_WARNING + "megaraid: %s-%lx[%x], fw owner.\n", + (aor==SCB_ABORT) ? "ABORTING":"RESET", + cmd->serial_number, scb->idx); - /* set adapter timeout value to 10 min. for tape drive */ - /* 0=6sec/1=60sec/2=10min/3=3hrs */ - pthru->timeout = 2; - pthru->ars = 1; - pthru->reqsenselen = 14; - pthru->islogical = 0; - pthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : sc->device->channel; - pthru->target = (megacfg->flag & BOARD_40LD) ? - (sc->device->channel << 4) | sc->device->id : sc->device->id; - pthru->cdblen = sc->cmd_len; - pthru->logdrv = sc->device->lun; + return FALSE; + } + else { + + /* + * Not yet issued! Remove from the pending + * list + */ + printk(KERN_WARNING + "megaraid: %s-%lx[%x], driver owner.\n", + (aor==SCB_ABORT) ? "ABORTING":"RESET", + cmd->serial_number, scb->idx); + + mega_free_scb(adapter, scb); - memcpy (pthru->cdb, sc->cmnd, sc->cmd_len); + if( aor == SCB_ABORT ) { + cmd->result = (DID_ABORT << 16); + } + else { + cmd->result = (DID_RESET << 16); + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Not sure about the direction */ - scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + list_add_tail(SCSI_LIST(cmd), + &adapter->completed_list); - /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ - switch (sc->cmnd[0]) { - case INQUIRY: - case READ_CAPACITY: - pthru->numsgelements = 0; - pthru->dataxferaddr = scb->dma_bounce_buffer; - pthru->dataxferlen = sc->request_bufflen; - break; - default: - pthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&pthru->dataxferaddr, - (u32 *)&pthru->dataxferlen - ); - break; + return TRUE; + } + } } -#else - pthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&pthru->dataxferaddr, - (u32 *)&pthru->dataxferlen - ); -#endif - return pthru; + + return FALSE; } -static mega_ext_passthru * -mega_prepare_extpassthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc) -{ - mega_ext_passthru *epthru; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - epthru = scb->epthru; -#else - epthru = &scb->epthru; -#endif - memset(epthru, 0, sizeof(mega_ext_passthru)); +#ifdef CONFIG_PROC_FS +/* Following code handles /proc fs */ - /* set adapter timeout value to 10 min. for tape drive */ - /* 0=6sec/1=60sec/2=10min/3=3hrs */ - epthru->timeout = 2; - epthru->ars = 1; - epthru->reqsenselen = 14; - epthru->islogical = 0; - epthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : sc->device->channel; - epthru->target = (megacfg->flag & BOARD_40LD) ? - (sc->device->channel << 4) | sc->device->id : sc->device->id; - epthru->cdblen = sc->cmd_len; - epthru->logdrv = sc->device->lun; +#define CREATE_READ_PROC(string, func) create_proc_read_entry(string, \ + S_IRUSR | S_IFREG, \ + controller_proc_dir_entry, \ + func, adapter) + +/** + * mega_create_proc_entry() + * @index - index in soft state array + * @parent - parent node for this /proc entry + * + * Creates /proc entries for our controllers. + */ +static void +mega_create_proc_entry(int index, struct proc_dir_entry *parent) +{ + struct proc_dir_entry *controller_proc_dir_entry = NULL; + u8 string[64] = { 0 }; + adapter_t *adapter = hba_soft_state[index]; - memcpy(epthru->cdb, sc->cmnd, sc->cmd_len); + sprintf(string, "hba%d", adapter->host->host_no); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Not sure about the direction */ - scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + controller_proc_dir_entry = + adapter->controller_proc_dir_entry = proc_mkdir(string, parent); - /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ - switch (sc->cmnd[0]) { - case INQUIRY: - case READ_CAPACITY: - epthru->numsgelements = 0; - epthru->dataxferaddr = scb->dma_bounce_buffer; - epthru->dataxferlen = sc->request_bufflen; - break; - default: - epthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&epthru->dataxferaddr, - (u32 *)&epthru->dataxferlen - ); - break; + if(!controller_proc_dir_entry) { + printk(KERN_WARNING "\nmegaraid: proc_mkdir failed\n"); + return; } -#else - epthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&epthru->dataxferaddr, - (u32 *)&epthru->dataxferlen - ); + adapter->proc_read = CREATE_READ_PROC("config", proc_read_config); + adapter->proc_stat = CREATE_READ_PROC("stat", proc_read_stat); + adapter->proc_mbox = CREATE_READ_PROC("mailbox", proc_read_mbox); +#if MEGA_HAVE_ENH_PROC + adapter->proc_rr = CREATE_READ_PROC("rebuild-rate", proc_rebuild_rate); + adapter->proc_battery = CREATE_READ_PROC("battery-status", + proc_battery); + + /* + * Display each physical drive on its channel + */ + adapter->proc_pdrvstat[0] = CREATE_READ_PROC("diskdrives-ch0", + proc_pdrv_ch0); + adapter->proc_pdrvstat[1] = CREATE_READ_PROC("diskdrives-ch1", + proc_pdrv_ch1); + adapter->proc_pdrvstat[2] = CREATE_READ_PROC("diskdrives-ch2", + proc_pdrv_ch2); + adapter->proc_pdrvstat[3] = CREATE_READ_PROC("diskdrives-ch3", + proc_pdrv_ch3); + + /* + * Display a set of up to 10 logical drive through each of following + * /proc entries + */ + adapter->proc_rdrvstat[0] = CREATE_READ_PROC("raiddrives-0-9", + proc_rdrv_10); + adapter->proc_rdrvstat[1] = CREATE_READ_PROC("raiddrives-10-19", + proc_rdrv_20); + adapter->proc_rdrvstat[2] = CREATE_READ_PROC("raiddrives-20-29", + proc_rdrv_30); + adapter->proc_rdrvstat[3] = CREATE_READ_PROC("raiddrives-30-39", + proc_rdrv_40); #endif - return epthru; } -/* Handle Driver Level IOCTLs - * Return value of 0 indicates this function could not handle , so continue - * processing -*/ -static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +/** + * proc_read_config() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display configuration information about the controller. + */ +static int +proc_read_config(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - unsigned char *data = (unsigned char *) SCpnt->request_buffer; - mega_driver_info driver_info; - /* If this is not our command don't do anything */ - if (SCpnt->cmnd[0] != M_RD_DRIVER_IOCTL_INTERFACE) - return 0; + adapter_t *adapter = (adapter_t *)data; + int len = 0; - switch (SCpnt->cmnd[1]) { - case GET_DRIVER_INFO: - if (SCpnt->request_bufflen < sizeof (driver_info)) { - SCpnt->result = DID_BAD_TARGET << 16; - callDone (SCpnt); - return 1; - } + len += sprintf(page+len, "%s", MEGARAID_VERSION); - driver_info.size = sizeof (driver_info) - sizeof (int); - driver_info.version = MEGARAID_IOCTL_VERSION; - memcpy (data, &driver_info, sizeof (driver_info)); - break; - default: - SCpnt->result = DID_BAD_TARGET << 16; - } + if(adapter->product_info.product_name[0]) + len += sprintf(page+len, "%s\n", + adapter->product_info.product_name); - callDone (SCpnt); - return 1; -} + len += sprintf(page+len, "Controller Type: "); -static inline void set_mbox_xfer_addr (mega_host_config * megaCfg, mega_scb * pScb, - mega_ioctl_mbox * mbox, u32 direction) -{ + if( adapter->flag & BOARD_MEMMAP ) { + len += sprintf(page+len, + "438/466/467/471/493/518/520/531/532\n"); + } + else { + len += sprintf(page+len, + "418/428/434\n"); + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - switch (direction) { - case TO_DEVICE: - pScb->dma_direction = PCI_DMA_TODEVICE; - break; - case FROM_DEVICE: - pScb->dma_direction = PCI_DMA_FROMDEVICE; - break; - case FROMTO_DEVICE: - pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; - break; + if(adapter->flag & BOARD_40LD) { + len += sprintf(page+len, + "Controller Supports 40 Logical Drives\n"); } - pScb->dma_h_bulkdata - = pci_map_single (megaCfg->dev, - pScb->buff_ptr, - pScb->iDataSize, pScb->dma_direction); - mbox->xferaddr = pScb->dma_h_bulkdata; - pScb->dma_type = M_RD_BULK_DATA_ONLY; - TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); -#else - mbox->xferaddr = virt_to_bus (pScb->buff_ptr); -#endif -} + if(adapter->flag & BOARD_64BIT) { + len += sprintf(page+len, + "Controller capable of 64-bit memory addressing\n"); + } + if( adapter->has_64bit_addr ) { + len += sprintf(page+len, + "Controller using 64-bit memory addressing\n"); + } + else { + len += sprintf(page+len, + "Controller is not using 64-bit memory addressing\n"); + } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + len += sprintf(page+len, "Base = %08lx, Irq = %d, ", adapter->base, + adapter->host->irq); -/*-------------------------------------------------------------------- - * build RAID commands for controller, passed down through ioctl() - *--------------------------------------------------------------------*/ -static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) -{ - mega_scb *pScb; - mega_ioctl_mbox *mbox; - mega_mailbox *mailbox; - mega_passthru *pthru; - u8 *mboxdata; - long seg, i = 0; - unsigned char *data = (unsigned char *) SCpnt->request_buffer; - - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - pthru = &pScb->pthru; - - mboxdata = (u8 *) & pScb->mboxData; - mbox = (mega_ioctl_mbox *) & pScb->mboxData; - mailbox = (mega_mailbox *) & pScb->mboxData; - memset (mailbox, 0, sizeof (pScb->mboxData)); - - if (data[0] == 0x03) { /* passthrough command */ - unsigned char cdblen = data[2]; - memset (pthru, 0, sizeof (mega_passthru)); - pthru->islogical = (data[cdblen + 3] & 0x80) ? 1 : 0; - pthru->timeout = data[cdblen + 3] & 0x07; - pthru->reqsenselen = 14; - pthru->ars = (data[cdblen + 3] & 0x08) ? 1 : 0; - pthru->logdrv = data[cdblen + 4]; - pthru->channel = data[cdblen + 5]; - pthru->target = data[cdblen + 6]; - pthru->cdblen = cdblen; - memcpy (pthru->cdb, &data[3], cdblen); - - mailbox->cmd = MEGA_MBOXCMD_PASSTHRU; - - - pthru->numsgelements = mega_build_sglist (megaCfg, pScb, - (u32 *) & pthru-> - dataxferaddr, - (u32 *) & pthru-> - dataxferlen); - - mailbox->xferaddr = virt_to_bus (pthru); - - for (i = 0; i < (SCpnt->request_bufflen - cdblen - 7); i++) { - data[i] = data[i + cdblen + 7]; - } - return pScb; - } - /* else normal (nonpassthru) command */ - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /*0x020024 */ - /* - *usage of the function copy from user is used in case of data more than - *4KB.This is used only with adapters which supports more than 8 logical - * drives.This feature is disabled on kernels earlier or same as 2.0.36 - * as the uaccess.h file is not available with those kernels. - */ - - if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - /* use external data area for large xfers */ - /* If cmnd[0] is set to M_RD_IOCTL_CMD_NEW then * - * cmnd[4..7] = external user buffer * - * cmnd[8..11] = length of buffer * - * */ - char *user_area = (char *)*((u32*)&SCpnt->cmnd[4]); - u32 xfer_size = *((u32 *) & SCpnt->cmnd[8]); - switch (data[0]) { - case FW_FIRE_WRITE: - case FW_FIRE_FLASH: - if ((ulong) user_area & (PAGE_SIZE - 1)) { - printk - ("megaraid:user address not aligned on 4K boundary.Error.\n"); - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - break; - default: - break; - } + len += sprintf(page+len, "Logical Drives = %d, Channels = %d\n", + adapter->numldrv, adapter->product_info.nchannels); + + len += sprintf(page+len, "Version =%s:%s, DRAM = %dMb\n", + adapter->fw_version, adapter->bios_version, + adapter->product_info.dram_size); + + len += sprintf(page+len, + "Controller Queue Depth = %d, Driver Queue Depth = %d\n", + adapter->product_info.max_commands, adapter->max_cmds); + + len += sprintf(page+len, "support_ext_cdb = %d\n", + adapter->support_ext_cdb); + len += sprintf(page+len, "support_random_del = %d\n", + adapter->support_random_del); + len += sprintf(page+len, "boot_ldrv_enabled = %d\n", + adapter->boot_ldrv_enabled); + len += sprintf(page+len, "boot_ldrv = %d\n", + adapter->boot_ldrv); + len += sprintf(page+len, "boot_pdrv_enabled = %d\n", + adapter->boot_pdrv_enabled); + len += sprintf(page+len, "boot_pdrv_ch = %d\n", + adapter->boot_pdrv_ch); + len += sprintf(page+len, "boot_pdrv_tgt = %d\n", + adapter->boot_pdrv_tgt); + len += sprintf(page+len, "quiescent = %d\n", + atomic_read(&adapter->quiescent)); + len += sprintf(page+len, "has_cluster = %d\n", + adapter->has_cluster); + + len += sprintf(page+len, "\nModule Parameters:\n"); + len += sprintf(page+len, "max_cmd_per_lun = %d\n", + max_cmd_per_lun); + len += sprintf(page+len, "max_sectors_per_io = %d\n", + max_sectors_per_io); + + *eof = 1; + + return len; +} - if (!(pScb->buff_ptr = kmalloc (xfer_size, GFP_KERNEL))) { - printk - ("megaraid: Insufficient mem for M_RD_IOCTL_CMD_NEW.\n"); - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - copy_from_user (pScb->buff_ptr, user_area, xfer_size); - pScb->iDataSize = xfer_size; - switch (data[0]) { - case DCMD_FC_CMD: - switch (data[1]) { - case DCMD_FC_READ_NVRAM_CONFIG: - case DCMD_GET_DISK_CONFIG: - { - if ((ulong) pScb-> - buff_ptr & (PAGE_SIZE - 1)) { - printk - ("megaraid:user address not sufficient Error.\n"); - SCpnt->result = - (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - - /*building SG list */ - mega_build_kernel_sg (pScb->buff_ptr, - xfer_size, - pScb, mbox); - break; - } - default: - break; - } /*switch (data[1]) */ - break; - } +/** + * proc_read_stat() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Diaplay statistical information about the I/O activity. + */ +static int +proc_read_stat(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter; + int len; + int i; - } -#endif + i = 0; /* avoid compilation warnings */ + len = 0; + adapter = (adapter_t *)data; - mbox->cmd = data[0]; - mbox->channel = data[1]; - mbox->param = data[2]; - mbox->pad[0] = data[3]; - mbox->logdrv = data[4]; - - if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - switch (data[0]) { - case FW_FIRE_WRITE: - mbox->cmd = FW_FIRE_WRITE; - mbox->channel = data[1]; /* Current Block Number */ - set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); - mbox->numsgelements = 0; - break; - case FW_FIRE_FLASH: - mbox->cmd = FW_FIRE_FLASH; - mbox->channel = data[1] | 0x80; /* Origin */ - set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); - mbox->numsgelements = 0; - break; - case DCMD_FC_CMD: - *(mboxdata + 0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD */ - *(mboxdata + 2) = data[1]; /*sub command */ - switch (data[1]) { - case DCMD_FC_READ_NVRAM_CONFIG: - case DCMD_FC_READ_NVRAM_CONFIG_64: - /* number of elements in SG list */ - *(mboxdata + 3) = mbox->numsgelements; - if (megaCfg->flag & BOARD_64BIT) - *(mboxdata + 2) = - DCMD_FC_READ_NVRAM_CONFIG_64; - break; - case DCMD_WRITE_CONFIG: - case DCMD_WRITE_CONFIG_64: - if (megaCfg->flag & BOARD_64BIT) - *(mboxdata + 2) = DCMD_WRITE_CONFIG_64; - set_mbox_xfer_addr (megaCfg, pScb, mbox, - TO_DEVICE); - mbox->numsgelements = 0; - break; - case DCMD_GET_DISK_CONFIG: - case DCMD_GET_DISK_CONFIG_64: - if (megaCfg->flag & BOARD_64BIT) - *(mboxdata + 2) = - DCMD_GET_DISK_CONFIG_64; - *(mboxdata + 3) = data[2]; /*number of elements in SG list */ - /*nr of elements in SG list */ - *(mboxdata + 4) = mbox->numsgelements; - break; - case DCMD_DELETE_LOGDRV: - case DCMD_DELETE_DRIVEGROUP: - case NC_SUBOP_ENQUIRY3: - *(mboxdata + 3) = data[2]; - set_mbox_xfer_addr (megaCfg, pScb, mbox, - FROMTO_DEVICE); - mbox->numsgelements = 0; - break; - case DCMD_CHANGE_LDNO: - case DCMD_CHANGE_LOOPID: - *(mboxdata + 3) = data[2]; - *(mboxdata + 4) = data[3]; - set_mbox_xfer_addr (megaCfg, pScb, mbox, - TO_DEVICE); - mbox->numsgelements = 0; - break; - default: - set_mbox_xfer_addr (megaCfg, pScb, mbox, - FROMTO_DEVICE); - mbox->numsgelements = 0; - break; - } /*switch */ - break; - default: - set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE); - mbox->numsgelements = 0; - break; - } - } else { + len = sprintf(page, "Statistical Information for this controller\n"); + len += sprintf(page+len, "pend_cmds = %d\n", + atomic_read(&adapter->pend_cmds)); +#if MEGA_HAVE_STATS + for(i = 0; i < adapter->numldrv; i++) { + len += sprintf(page+len, "Logical Drive %d:\n", i); - mbox->numsgelements = mega_build_sglist (megaCfg, pScb, - (u32 *) & mbox-> - xferaddr, - (u32 *) & seg); - - /* Handling some of the fw special commands */ - switch (data[0]) { - case 6: /* START_DEV */ - mbox->xferaddr = *((u32 *) & data[i + 6]); - break; - default: - break; - } + len += sprintf(page+len, + "\tReads Issued = %lu, Writes Issued = %lu\n", + adapter->nreads[i], adapter->nwrites[i]); - for (i = 0; i < (SCpnt->request_bufflen - 6); i++) { - data[i] = data[i + 6]; - } + len += sprintf(page+len, + "\tSectors Read = %lu, Sectors Written = %lu\n", + adapter->nreadblocks[i], adapter->nwriteblocks[i]); + + len += sprintf(page+len, + "\tRead errors = %lu, Write errors = %lu\n\n", + adapter->rd_errors[i], adapter->wr_errors[i]); } +#else + len += sprintf(page+len, + "IO and error counters not compiled in driver.\n"); +#endif - return (pScb); + *eof = 1; + + return len; } -static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, mega_ioctl_mbox * mbox) +/** + * proc_read_mbox() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display mailbox information for the last command issued. This information + * is good for debugging. + */ +static int +proc_read_mbox(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - ulong i, buffer_area, len, end, end_page, x, idx = 0; - buffer_area = (ulong) barea; - i = buffer_area; - end = buffer_area + xfersize; - end_page = (end) & ~(PAGE_SIZE - 1); - - do { - len = PAGE_SIZE - (i % PAGE_SIZE); - x = pScb->sgList[idx].address = - virt_to_bus ((volatile void *) i); - pScb->sgList[idx].length = len; - i += len; - idx++; - } while (i < end_page); - - if ((end - i) < 0) { - printk ("megaraid:Error in user address\n"); - } - - if (end - i) { - pScb->sgList[idx].address = virt_to_bus ((volatile void *) i); - pScb->sgList[idx].length = end - i; - idx++; - } - mbox->xferaddr = virt_to_bus (pScb->sgList); - mbox->numsgelements = idx; + adapter_t *adapter = (adapter_t *)data; + volatile mbox_t *mbox = adapter->mbox; + int len = 0; + + len = sprintf(page, "Contents of Mail Box Structure\n"); + len += sprintf(page+len, " Fw Command = 0x%02x\n", mbox->cmd); + len += sprintf(page+len, " Cmd Sequence = 0x%02x\n", mbox->cmdid); + len += sprintf(page+len, " No of Sectors= %04d\n", mbox->numsectors); + len += sprintf(page+len, " LBA = 0x%02x\n", mbox->lba); + len += sprintf(page+len, " DTA = 0x%08x\n", mbox->xferaddr); + len += sprintf(page+len, " Logical Drive= 0x%02x\n", mbox->logdrv); + len += sprintf(page+len, " No of SG Elmt= 0x%02x\n", + mbox->numsgelements); + len += sprintf(page+len, " Busy = %01x\n", mbox->busy); + len += sprintf(page+len, " Status = 0x%02x\n", mbox->status); + + *eof = 1; + + return len; } -#endif -#if DEBUG -static unsigned int cum_time = 0; -static unsigned int cum_time_cnt = 0; - -static void showMbox (mega_scb * pScb) +/** + * proc_rebuild_rate() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display current rebuild rate + */ +static int +proc_rebuild_rate(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - mega_mailbox *mbox; + adapter_t *adapter = (adapter_t *)data; + dma_addr_t dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + int len = 0; - if (pScb == NULL) - return; + if( make_local_pdev(adapter, &pdev) != 0 ) { + *eof = 1; + return len; + } - mbox = (mega_mailbox *) pScb->mboxData; - printk ("%u cmd:%x id:%x #scts:%x lba:%x addr:%x logdrv:%x #sg:%x\n", - pScb->SCpnt->pid, - mbox->cmd, mbox->cmdid, mbox->numsectors, - mbox->lba, mbox->xferaddr, mbox->logdrv, mbox->numsgelements); -} + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + free_local_pdev(pdev); + *eof = 1; + return len; + } -#endif + if( mega_adapinq(adapter, dma_handle) != 0 ) { -/*-------------------------------------------------------------------- - * Interrupt service routine - *--------------------------------------------------------------------*/ -static irqreturn_t megaraid_isr (int irq, void *devp, struct pt_regs *regs) -{ - IO_LOCK_T; - mega_host_config * megaCfg; - u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE]; - u32 dword = 0; - mega_mailbox *mbox; - mega_scb *pScb; - u_char qCnt, qStatus; - u_char completed[MAX_FIRMWARE_STATUS]; - Scsi_Cmnd *SCpnt; - int handled = 0; + len = sprintf(page, "Adapter inquiry failed.\n"); - megaCfg = (mega_host_config *) devp; - mbox = (mega_mailbox *) tmpBox; + printk(KERN_WARNING "megaraid: inquiry failed.\n"); - if (megaCfg->host->irq == irq) { - handled = 1; - if (megaCfg->flag & IN_ISR) { - TRACE (("ISR called reentrantly!!\n")); - printk ("ISR called reentrantly!!\n"); - } - megaCfg->flag |= IN_ISR; + mega_free_inquiry(inquiry, dma_handle, pdev); - if (mega_busyWaitMbox (megaCfg)) { - printk (KERN_WARNING "Error: mailbox busy in isr!\n"); - } + free_local_pdev(pdev); - /* Check if a valid interrupt is pending */ - if (megaCfg->flag & BOARD_QUARTZ) { - dword = RDOUTDOOR (megaCfg); - if (dword != 0x10001234) { - /* Spurious interrupt */ - megaCfg->flag &= ~IN_ISR; - goto out; - } - } else { - byte = READ_PORT (megaCfg->host->io_port, INTR_PORT); - if ((byte & VALID_INTR_BYTE) == 0) { - /* Spurious interrupt */ - megaCfg->flag &= ~IN_ISR; - goto out; - } - WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); - } + *eof = 1; - for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++) - completed[idx] = 0; + return len; + } - IO_LOCK(megaCfg->host); + if( adapter->flag & BOARD_40LD ) { + len = sprintf(page, "Rebuild Rate: [%d%%]\n", + ((mega_inquiry3 *)inquiry)->rebuild_rate); + } + else { + len = sprintf(page, "Rebuild Rate: [%d%%]\n", + ((mraid_ext_inquiry *) + inquiry)->raid_inq.adapter_info.rebuild_rate); + } - megaCfg->nInterrupts++; - qCnt = 0xff; - while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ; - qStatus = 0xff; - while ((qStatus = megaCfg->mbox->status) == 0xFF) ; + mega_free_inquiry(inquiry, dma_handle, pdev); - /* Get list of completed requests */ - for (idx = 0; idx < qCnt; idx++) { - while ((sIdx = megaCfg->mbox->completed[idx]) == 0xFF) { - printk ("p"); - } - completed[idx] = sIdx; - sIdx = 0xFF; - } + free_local_pdev(pdev); - if (megaCfg->flag & BOARD_QUARTZ) { - WROUTDOOR (megaCfg, dword); - /* Acknowledge interrupt */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* In this case mbox contains physical address */ -#if 0 - WRINDOOR (megaCfg, megaCfg->adjdmahandle64 | 0x2); -#else - WRINDOOR (megaCfg, 0x2); -#endif + *eof = 1; -#else + return len; +} -#if 0 - WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); -#else - WRINDOOR (megaCfg, 0x2); -#endif -#endif +/** + * proc_battery() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the battery module on the controller. + */ +static int +proc_battery(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + dma_addr_t dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + u8 battery_status = 0; + char str[256]; + int len = 0; -#if 0 - while (RDINDOOR (megaCfg) & 0x02) ; -#endif - } else { - CLEAR_INTR (megaCfg->host->io_port); - } + if( make_local_pdev(adapter, &pdev) != 0 ) { + *eof = 1; + return len; + } -#if DEBUG - if (qCnt >= MAX_FIRMWARE_STATUS) { - printk ("megaraid_isr: cmplt=%d ", qCnt); - } -#endif + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + free_local_pdev(pdev); + *eof = 1; + return len; + } - for (idx = 0; idx < qCnt; idx++) { - sIdx = completed[idx]; - if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) { - pScb = &megaCfg->scbList[sIdx - 1]; - - /* ASSERT(pScb->state == SCB_ISSUED); */ - -#if DEBUG - if (((jiffies) - pScb->isrcount) > maxCmdTime) { - maxCmdTime = (jiffies) - pScb->isrcount; - printk - ("megaraid_isr : cmd time = %u\n", - maxCmdTime); - } -#endif - /* - * Assuming that the scsi command, for which - * an abort request was received earlier, has - * completed. - */ - if (pScb->state == SCB_ABORTED) { - SCpnt = pScb->SCpnt; - } - if (pScb->state == SCB_RESET) { - SCpnt = pScb->SCpnt; - mega_freeSCB (megaCfg, pScb); - SCpnt->result = (DID_RESET << 16); - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = - megaCfg->qCompletedT = - SCpnt; - } else { - megaCfg->qCompletedT-> - host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = - (unsigned char *) NULL; - megaCfg->qCcnt++; - continue; - } + if( mega_adapinq(adapter, dma_handle) != 0 ) { - /* We don't want the ISR routine to touch M_RD_IOCTL_CMD_NEW commands, so - * don't mark them as complete, instead we pop their semaphore so - * that the queue routine can finish them off - */ - if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - /* save the status byte for the queue routine to use */ - pScb->SCpnt->result = qStatus; - up (&pScb->ioctl_sem); - } else { - /* Mark command as completed */ - mega_cmd_done (megaCfg, pScb, qStatus); - } - } else { - printk - ("megaraid: wrong cmd id completed from firmware:id=%x\n", - sIdx); - } - } + len = sprintf(page, "Adapter inquiry failed.\n"); - mega_rundoneq (megaCfg); + printk(KERN_WARNING "megaraid: inquiry failed.\n"); - megaCfg->flag &= ~IN_ISR; - /* Loop through any pending requests */ - mega_runpendq (megaCfg); - IO_UNLOCK(megaCfg->host); + mega_free_inquiry(inquiry, dma_handle, pdev); - } -out: - return IRQ_RETVAL(handled); -} + free_local_pdev(pdev); -/*==================================================*/ -/* Wait until the controller's mailbox is available */ -/*==================================================*/ + *eof = 1; -static int mega_busyWaitMbox (mega_host_config * megaCfg) -{ - mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; - long counter; + return len; + } - for (counter = 0; counter < 10000; counter++) { - if (!mbox->busy) { - return 0; - } - udelay (100); - barrier (); + if( adapter->flag & BOARD_40LD ) { + battery_status = ((mega_inquiry3 *)inquiry)->battery_status; + } + else { + battery_status = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.adapter_info.battery_status; } - return -1; /* give up after 1 second */ -} -/*===================================================== - * Post a command to the card - * - * Arguments: - * mega_host_config *megaCfg - Controller structure - * u_char *mboxData - Mailbox area, 16 bytes - * mega_scb *pScb - SCB posting (or NULL if N/A) - * int intr - if 1, interrupt, 0 is blocking - * Return Value: (added on 7/26 for 40ld/64bit) - * -1: the command was not actually issued out - * other cases: - * intr==0, return ScsiStatus, i.e. mbox->status - * intr==1, return 0 - *===================================================== - */ -static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, - mega_scb * pScb, int intr) -{ - volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; + /* + * Decode the battery status + */ + sprintf(str, "Battery Status:[%d]", battery_status); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - volatile mega_mailbox64 *mbox64 = (mega_mailbox64 *) megaCfg->mbox64; -#endif + if(battery_status == MEGA_BATT_CHARGE_DONE) + strcat(str, " Charge Done"); + + if(battery_status & MEGA_BATT_MODULE_MISSING) + strcat(str, " Module Missing"); + + if(battery_status & MEGA_BATT_LOW_VOLTAGE) + strcat(str, " Low Voltage"); + + if(battery_status & MEGA_BATT_TEMP_HIGH) + strcat(str, " Temperature High"); + + if(battery_status & MEGA_BATT_PACK_MISSING) + strcat(str, " Pack Missing"); + + if(battery_status & MEGA_BATT_CHARGE_INPROG) + strcat(str, " Charge In-progress"); + + if(battery_status & MEGA_BATT_CHARGE_FAIL) + strcat(str, " Charge Fail"); + + if(battery_status & MEGA_BATT_CYCLES_EXCEEDED) + strcat(str, " Cycles Exceeded"); - u_char byte; + len = sprintf(page, "%s\n", str); -#ifdef __LP64__ - u64 phys_mbox; -#else - u32 phys_mbox; -#endif - u8 retval = -1; - mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE); /* Set cmdid */ - mboxData[0xF] = 1; /* Set busy */ + mega_free_inquiry(inquiry, dma_handle, pdev); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* In this case mbox contains physical address */ - phys_mbox = megaCfg->adjdmahandle64; -#else - phys_mbox = virt_to_bus (megaCfg->mbox); -#endif + free_local_pdev(pdev); -#if DEBUG - ShowMbox (pScb); -#endif + *eof = 1; - /* Wait until mailbox is free */ - if (mega_busyWaitMbox (megaCfg)) { - printk ("Blocked mailbox......!!\n"); - udelay (1000); + return len; +} -#if DEBUG - showMbox (pLastScb); -#endif - /* Abort command */ - if (pScb == NULL) { - TRACE (("NULL pScb in megaIssue\n")); - printk ("NULL pScb in megaIssue\n"); - } - mega_cmd_done (megaCfg, pScb, 0x08); - return -1; - } +/** + * proc_pdrv_ch0() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 0. + */ +static int +proc_pdrv_ch0(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; - pLastScb = pScb; + *eof = 1; - /* Copy mailbox data into host structure */ - megaCfg->mbox64->xferSegment_lo = 0; - megaCfg->mbox64->xferSegment_hi = 0; + return (proc_pdrv(adapter, page, 0)); +} - memcpy ((char *) mbox, mboxData, 16); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - switch (mboxData[0]) { - case MEGA_MBOXCMD_LREAD64: - case MEGA_MBOXCMD_LWRITE64: - mbox64->xferSegment_lo = mbox->xferaddr; - mbox64->xferSegment_hi = 0; - mbox->xferaddr = 0xFFFFFFFF; - break; - } -#endif +/** + * proc_pdrv_ch1() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 1. + */ +static int +proc_pdrv_ch1(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; - /* Kick IO */ - if (intr) { - /* Issue interrupt (non-blocking) command */ - if (megaCfg->flag & BOARD_QUARTZ) { - mbox->mraid_poll = 0; - mbox->mraid_ack = 0; - - WRINDOOR (megaCfg, phys_mbox | 0x1); - } else { - ENABLE_INTR (megaCfg->host->io_port); - ISSUE_COMMAND (megaCfg->host->io_port); - } - pScb->state = SCB_ISSUED; - - retval = 0; - } else { /* Issue non-ISR (blocking) command */ - disable_irq (megaCfg->host->irq); - if (megaCfg->flag & BOARD_QUARTZ) { - mbox->mraid_poll = 0; - mbox->mraid_ack = 0; - mbox->numstatus = 0xFF; - mbox->status = 0xFF; - WRINDOOR (megaCfg, phys_mbox | 0x1); - - while (mbox->numstatus == 0xFF) ; - while (mbox->status == 0xFF) ; - while (mbox->mraid_poll != 0x77) ; - mbox->mraid_poll = 0; - mbox->mraid_ack = 0x77; - - /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); - WROUTDOOR (megaCfg, cmdDone); */ - - if (pScb) { - mega_cmd_done (megaCfg, pScb, mbox->status); - } - - WRINDOOR (megaCfg, phys_mbox | 0x2); - while (RDINDOOR (megaCfg) & 0x2) ; - - } else { - DISABLE_INTR (megaCfg->host->io_port); - ISSUE_COMMAND (megaCfg->host->io_port); - - while (! - ((byte = - READ_PORT (megaCfg->host->io_port, - INTR_PORT)) & INTR_VALID)) ; - WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); - - ENABLE_INTR (megaCfg->host->io_port); - CLEAR_INTR (megaCfg->host->io_port); - - if (pScb) { - mega_cmd_done (megaCfg, pScb, mbox->status); - } else { - TRACE (("Error: NULL pScb!\n")); - } - } - enable_irq (megaCfg->host->irq); - retval = mbox->status; - } -#if DEBUG - while (mega_busyWaitMbox (megaCfg)) { - printk(KERN_ERR "Blocked mailbox on exit......!\n"); - udelay (1000); - } -#endif + *eof = 1; - return retval; + return (proc_pdrv(adapter, page, 1)); } -/*------------------------------------------------------------------- - * Copies data to SGLIST - *-------------------------------------------------------------------*/ -/* Note: - For 64 bit cards, we need a minimum of one SG element for read/write -*/ +/** + * proc_pdrv_ch2() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 2. + */ static int -mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, - u32 * buffer, u32 * length) +proc_pdrv_ch2(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - struct scatterlist *sgList; - int idx; + adapter_t *adapter = (adapter_t *)data; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - int sgcnt; -#endif + *eof = 1; - mega_mailbox *mbox = NULL; + return (proc_pdrv(adapter, page, 2)); +} - mbox = (mega_mailbox *) scb->mboxData; - /* Scatter-gather not used */ - if (scb->SCpnt->use_sg == 0) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, - scb->SCpnt->request_buffer, - scb->SCpnt->request_bufflen, - scb->dma_direction); - /* We need to handle special commands like READ64, WRITE64 - as they need a minimum of 1 SG irrespective of actually SG - */ - if ((megaCfg->flag & BOARD_64BIT) && - ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || - (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { - scb->sg64List[0].address = scb->dma_h_bulkdata; - scb->sg64List[0].length = scb->SCpnt->request_bufflen; - *buffer = scb->dma_sghandle64; - *length = 0; - scb->sglist_count = 1; - return 1; - } else { - *buffer = scb->dma_h_bulkdata; - *length = (u32) scb->SCpnt->request_bufflen; - } -#else - *buffer = virt_to_bus (scb->SCpnt->request_buffer); - *length = (u32) scb->SCpnt->request_bufflen; -#endif - return 0; - } +/** + * proc_pdrv_ch3() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 3. + */ +static int +proc_pdrv_ch3(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; - sgList = (struct scatterlist *) scb->SCpnt->request_buffer; -#if 0 - if (scb->SCpnt->use_sg == 1) { + *eof = 1; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, - sgList[0].address, - sgList[0].length, scb->dma_direction); - - if ((megaCfg->flag & BOARD_64BIT) && - ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || - (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { - scb->sg64List[0].address = scb->dma_h_bulkdata; - scb->sg64List[0].length = scb->SCpnt->request_bufflen; - *buffer = scb->dma_sghandle64; - *length = 0; - scb->sglist_count = 1; - return 1; - } else { - *buffer = scb->dma_h_bulkdata; - *length = (u32) sgList[0].length; - } -#else - *buffer = virt_to_bus (sgList[0].address); - *length = (u32) sgList[0].length; -#endif + return (proc_pdrv(adapter, page, 3)); +} + + +/** + * proc_pdrv() + * @page - buffer to write the data in + * @adapter - pointer to our soft state + * + * Display information about the physical drives. + */ +static int +proc_pdrv(adapter_t *adapter, char *page, int channel) +{ + dma_addr_t dma_handle; + char *scsi_inq; + dma_addr_t scsi_inq_dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + u8 *pdrv_state; + u8 state; + int tgt; + int max_channels; + int len = 0; + char str[80]; + int i; - return 0; + if( make_local_pdev(adapter, &pdev) != 0 ) { + return len; } -#endif - /* Copy Scatter-Gather list info into controller structure */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - sgcnt = pci_map_sg (megaCfg->dev, - sgList, scb->SCpnt->use_sg, scb->dma_direction); - - /* Determine the validity of the new count */ - if (sgcnt == 0) - printk ("pci_map_sg returned zero!!! "); - - for (idx = 0; idx < sgcnt; idx++, sgList++) { - - if ((megaCfg->flag & BOARD_64BIT) && - ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || - (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { - scb->sg64List[idx].address = sg_dma_address (sgList); - scb->sg64List[idx].length = sg_dma_len (sgList); - } else { - scb->sgList[idx].address = sg_dma_address (sgList); - scb->sgList[idx].length = sg_dma_len (sgList); - } + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + free_local_pdev(pdev); + return len; } -#else - for (idx = 0; idx < scb->SCpnt->use_sg; idx++) { - scb->sgList[idx].address = virt_to_bus (sgList[idx].address); - scb->sgList[idx].length = (u32) sgList[idx].length; - } -#endif + if( mega_adapinq(adapter, dma_handle) != 0 ) { - /* Reset pointer and length fields */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - *buffer = scb->dma_sghandle64; - scb->sglist_count = scb->SCpnt->use_sg; -#else - *buffer = virt_to_bus (scb->sgList); -#endif - *length = 0; + len = sprintf(page, "Adapter inquiry failed.\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Return count of SG requests */ - return sgcnt; -#else - /* Return count of SG requests */ - return scb->SCpnt->use_sg; -#endif -} + printk(KERN_WARNING "megaraid: inquiry failed.\n"); -/*-------------------------------------------------------------------- - * Initializes the address of the controller's mailbox register - * The mailbox register is used to issue commands to the card. - * Format of the mailbox area: - * 00 01 command - * 01 01 command id - * 02 02 # of sectors - * 04 04 logical bus address - * 08 04 physical buffer address - * 0C 01 logical drive # - * 0D 01 length of scatter/gather list - * 0E 01 reserved - * 0F 01 mailbox busy - * 10 01 numstatus byte - * 11 01 status byte - *--------------------------------------------------------------------*/ -static int -mega_register_mailbox (mega_host_config * megaCfg, u32 paddr) -{ - /* align on 16-byte boundary */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megaCfg->mbox = &megaCfg->mailbox64ptr->mailbox; -#else - megaCfg->mbox = &megaCfg->mailbox64.mailbox; -#endif + mega_free_inquiry(inquiry, dma_handle, pdev); -#ifdef __LP64__ - megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ((u64) (-1) ^ 0x0F)); - megaCfg->adjdmahandle64 = (megaCfg->dma_handle64 + 16) & ((u64) (-1) ^ 0x0F); - megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - sizeof (u64)); - paddr = (paddr + 4 + 16) & ((u64) (-1) ^ 0x0F); -#else - megaCfg->mbox - = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0); + free_local_pdev(pdev); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megaCfg->adjdmahandle64 = ((megaCfg->dma_handle64 + 16) & 0xFFFFFFF0); -#endif + return len; + } - megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - 8); - paddr = (paddr + 4 + 16) & 0xFFFFFFF0; -#endif - /* Register mailbox area with the firmware */ - if (!(megaCfg->flag & BOARD_QUARTZ)) { - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF); - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1, - (paddr >> 8) & 0xFF); - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2, - (paddr >> 16) & 0xFF); - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT3, - (paddr >> 24) & 0xFF); - WRITE_PORT (megaCfg->host->io_port, ENABLE_MBOX_REGION, - ENABLE_MBOX_BYTE); + scsi_inq = pci_alloc_consistent(pdev, 256, &scsi_inq_dma_handle); - CLEAR_INTR (megaCfg->host->io_port); - ENABLE_INTR (megaCfg->host->io_port); - } - return 0; -} + if( scsi_inq == NULL ) { + len = sprintf(page, "memory not available for scsi inq.\n"); -/*--------------------------------------------------------------------------- - * mega_Convert8ldTo40ld() -- takes all info in AdapterInquiry structure and - * puts it into ProductInfo and Enquiry3 structures for later use - *---------------------------------------------------------------------------*/ -static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, - mega_Enquiry3 * enquiry3, - megaRaidProductInfo * productInfo) -{ - int i; + mega_free_inquiry(inquiry, dma_handle, pdev); - productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds; - enquiry3->rbldRate = inquiry->AdpInfo.RbldRate; - productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent; + free_local_pdev(pdev); - for (i = 0; i < 4; i++) { - productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i]; - productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i]; + return len; } - enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval; - productInfo->DramSize = inquiry->AdpInfo.DramSize; - - enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv; - for (i = 0; i < MAX_LOGICAL_DRIVES; i++) { - enquiry3->lDrvSize[i] = inquiry->LogdrvInfo.LDrvSize[i]; - enquiry3->lDrvProp[i] = inquiry->LogdrvInfo.LDrvProp[i]; - enquiry3->lDrvState[i] - = inquiry->LogdrvInfo.LDrvState[i]; + if( adapter->flag & BOARD_40LD ) { + pdrv_state = ((mega_inquiry3 *)inquiry)->pdrv_state; } - - for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) { - enquiry3->pDrvState[i] - = inquiry->PhysdrvInfo.PDrvState[i]; + else { + pdrv_state = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.pdrv_info.pdrv_state; } -} -/*------------------------------------------------------------------- - * Issue an adapter info query to the controller - *-------------------------------------------------------------------*/ -static int mega_i_query_adapter (mega_host_config * megaCfg) -{ - mega_Enquiry3 *enquiry3Pnt; - mega_mailbox *mbox; - u_char mboxData[16]; + max_channels = adapter->product_info.nchannels; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t raid_inq_dma_handle = 0, prod_info_dma_handle = 0, enquiry3_dma_handle = 0; -#endif - u8 retval; + if( channel >= max_channels ) return 0; - /* Initialize adapter inquiry mailbox */ + for( tgt = 0; tgt <= MAX_TARGET; tgt++ ) { - mbox = (mega_mailbox *) mboxData; + i = channel*16 + tgt; - memset ((void *) megaCfg->mega_buffer, 0, - sizeof (megaCfg->mega_buffer)); - memset (mbox, 0, 16); + state = *(pdrv_state + i); -/* - * Try to issue Enquiry3 command - * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and - * update enquiry3 structure - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - enquiry3_dma_handle = pci_map_single (megaCfg->dev, - (void *) megaCfg->mega_buffer, - (2 * 1024L), PCI_DMA_FROMDEVICE); + switch( state & 0x0F ) { - mbox->xferaddr = enquiry3_dma_handle; -#else - /*Taken care */ - mbox->xferaddr = virt_to_bus ((void *) megaCfg->mega_buffer); -#endif + case PDRV_ONLINE: + sprintf(str, + "Channel:%2d Id:%2d State: Online", + channel, tgt); + break; - /* Initialize mailbox databuffer addr */ - enquiry3Pnt = (mega_Enquiry3 *) megaCfg->mega_buffer; - /* point mega_Enguiry3 to the data buf */ - - mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ - mboxData[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ - mboxData[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ + case PDRV_FAILED: + sprintf(str, + "Channel:%2d Id:%2d State: Failed", + channel, tgt); + break; - /* Issue a blocking command to the card */ - if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) { /* the adapter does not support 40ld */ - mega_RAIDINQ adapterInquiryData; - mega_RAIDINQ *adapterInquiryPnt = &adapterInquiryData; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - raid_inq_dma_handle = pci_map_single (megaCfg->dev, - (void *) adapterInquiryPnt, - sizeof (mega_RAIDINQ), - PCI_DMA_FROMDEVICE); - mbox->xferaddr = raid_inq_dma_handle; -#else - /*taken care */ - mbox->xferaddr = virt_to_bus ((void *) adapterInquiryPnt); -#endif + case PDRV_RBLD: + sprintf(str, + "Channel:%2d Id:%2d State: Rebuild", + channel, tgt); + break; - mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; /*issue old 0x05 command to adapter */ - /* Issue a blocking command to the card */ ; - retval = megaIssueCmd (megaCfg, mboxData, NULL, 0); - - pci_unmap_single (megaCfg->dev, - raid_inq_dma_handle, - sizeof (mega_RAIDINQ), PCI_DMA_FROMDEVICE); - - /*update Enquiry3 and ProductInfo structures with mega_RAIDINQ structure*/ - mega_Convert8ldTo40ld (adapterInquiryPnt, - enquiry3Pnt, - (megaRaidProductInfo *) & megaCfg-> - productInfo); - - } else { /* adapter supports 40ld */ - megaCfg->flag |= BOARD_40LD; - - pci_unmap_single (megaCfg->dev, - enquiry3_dma_handle, - (2 * 1024L), PCI_DMA_FROMDEVICE); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -/*get productInfo, which is static information and will be unchanged*/ - prod_info_dma_handle - = pci_map_single (megaCfg->dev, - (void *) &megaCfg->productInfo, - sizeof (megaRaidProductInfo), - PCI_DMA_FROMDEVICE); - mbox->xferaddr = prod_info_dma_handle; -#else - /*taken care */ - mbox->xferaddr = virt_to_bus ((void *) &megaCfg->productInfo); -#endif + case PDRV_HOTSPARE: + sprintf(str, + "Channel:%2d Id:%2d State: Hot spare", + channel, tgt); + break; + + default: + sprintf(str, + "Channel:%2d Id:%2d State: Un-configured", + channel, tgt); + break; - mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ - mboxData[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ + } - if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) - printk ("megaraid: Product_info cmd failed with error: %d\n", - retval); + /* + * This interface displays inquiries for disk drives + * only. Inquries for logical drives and non-disk + * devices are available through /proc/scsi/scsi + */ + memset(scsi_inq, 0, 256); + if( mega_internal_dev_inquiry(adapter, channel, tgt, + scsi_inq_dma_handle) || + (scsi_inq[0] & 0x1F) != TYPE_DISK ) { + continue; + } - pci_unmap_single (megaCfg->dev, - prod_info_dma_handle, - sizeof (megaRaidProductInfo), - PCI_DMA_FROMDEVICE); + /* + * Check for overflow. We print less than 240 + * characters for inquiry + */ + if( (len + 240) >= PAGE_SIZE ) break; + + len += sprintf(page+len, "%s.\n", str); + + len += mega_print_inquiry(page+len, scsi_inq); } - /* - * kernel scans the channels from 0 to <= max_channel - */ - megaCfg->host->max_channel = - megaCfg->productInfo.SCSIChanPresent + NVIRT_CHAN -1; + pci_free_consistent(pdev, 256, scsi_inq, scsi_inq_dma_handle); - megaCfg->host->max_id = 16; /* max targets per channel */ - /*(megaCfg->flag & BOARD_40LD)?FC_MAX_TARGETS_PER_CHANNEL:MAX_TARGET+1; */ -#if 0 - megaCfg->host->max_lun = /* max lun */ - (megaCfg->flag & BOARD_40LD) ? - FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; -#endif - megaCfg->host->max_lun = 7; /* Upto 7 luns for non disk devices */ + mega_free_inquiry(inquiry, dma_handle, pdev); - megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN; + free_local_pdev(pdev); - megaCfg->numldrv = enquiry3Pnt->numLDrv; - megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds; - if (megaCfg->max_cmds > MAX_COMMANDS) - megaCfg->max_cmds = MAX_COMMANDS - 1; + return len; +} - megaCfg->host->can_queue = megaCfg->max_cmds - 1; -#if 0 - if (megaCfg->host->can_queue >= MAX_COMMANDS) { - megaCfg->host->can_queue = MAX_COMMANDS - 16; +/* + * Display scsi inquiry + */ +static int +mega_print_inquiry(char *page, char *scsi_inq) +{ + int len = 0; + int i; + + len = sprintf(page, " Vendor: "); + for( i = 8; i < 16; i++ ) { + len += sprintf(page+len, "%c", scsi_inq[i]); } -#endif - /* use HP firmware and bios version encoding */ - if (megaCfg->productInfo.subSystemVendorID == HP_SUBSYS_ID) { - sprintf (megaCfg->fwVer, "%c%d%d.%d%d", - megaCfg->productInfo.FwVer[2], - megaCfg->productInfo.FwVer[1] >> 8, - megaCfg->productInfo.FwVer[1] & 0x0f, - megaCfg->productInfo.FwVer[0] >> 8, - megaCfg->productInfo.FwVer[0] & 0x0f); - sprintf (megaCfg->biosVer, "%c%d%d.%d%d", - megaCfg->productInfo.BiosVer[2], - megaCfg->productInfo.BiosVer[1] >> 8, - megaCfg->productInfo.BiosVer[1] & 0x0f, - megaCfg->productInfo.BiosVer[0] >> 8, - megaCfg->productInfo.BiosVer[0] & 0x0f); - } else { - memcpy (megaCfg->fwVer, (char *) megaCfg->productInfo.FwVer, 4); - megaCfg->fwVer[4] = 0; + len += sprintf(page+len, " Model: "); - memcpy (megaCfg->biosVer, (char *) megaCfg->productInfo.BiosVer, 4); - megaCfg->biosVer[4] = 0; + for( i = 16; i < 32; i++ ) { + len += sprintf(page+len, "%c", scsi_inq[i]); } - megaCfg->support_ext_cdb = mega_support_ext_cdb(megaCfg); - printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR, - megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv); + len += sprintf(page+len, " Rev: "); - if ( megaCfg->support_ext_cdb ) { - printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); + for( i = 32; i < 36; i++ ) { + len += sprintf(page+len, "%c", scsi_inq[i]); } - /* - * I hope that I can unmap here, reason DMA transaction is not required any more - * after this - */ + len += sprintf(page+len, "\n"); - return 0; + i = scsi_inq[0] & 0x1f; + + len += sprintf(page+len, " Type: %s ", + i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : + "Unknown "); + + len += sprintf(page+len, + " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07); + + if( (scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1 ) + len += sprintf(page+len, " CCS\n"); + else + len += sprintf(page+len, "\n"); + + return len; } -/*------------------------------------------------------------------------- + +/** + * proc_rdrv_10() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state * - * Driver interface functions + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_10(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + + return (proc_rdrv(adapter, page, 0, 9)); +} + + +/** + * proc_rdrv_20() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state * - *-------------------------------------------------------------------------*/ + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_20(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + + return (proc_rdrv(adapter, page, 10, 19)); +} -/*---------------------------------------------------------- - * Returns data to be displayed in /proc/scsi/megaraid/X - *----------------------------------------------------------*/ -int megaraid_proc_info (char *buffer, char **start, off_t offset, - int length, int host_no, int inout) +/** + * proc_rdrv_30() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_30(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - *start = buffer; - return 0; + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + + return (proc_rdrv(adapter, page, 20, 29)); } -static int mega_findCard (Scsi_Host_Template * pHostTmpl, - u16 pciVendor, u16 pciDev, long flag) + +/** + * proc_rdrv_40() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_40(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - mega_host_config *megaCfg = NULL; - struct Scsi_Host *host = NULL; - u_char pciBus, pciDevFun, megaIrq; - - u16 magic; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - u32 magic64; -#endif + adapter_t *adapter = (adapter_t *)data; - int i; + *eof = 1; -#ifdef __LP64__ - u64 megaBase; -#else - u32 megaBase; -#endif + return (proc_rdrv(adapter, page, 30, 39)); +} - u16 pciIdx = 0; - u16 numFound = 0; - u16 subsysid, subsysvid; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - while (!pcibios_find_device - (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) { -#else -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ - struct pci_dev *pdev = NULL; -#else - struct pci_dev *pdev = pci_devices; -#endif +/** + * proc_rdrv() + * @page - buffer to write the data in + * @adapter - pointer to our soft state + * @start - starting logical drive to display + * @end - ending logical drive to display + * + * We do not print the inquiry information since its already available through + * /proc/scsi/scsi interface + */ +static int +proc_rdrv(adapter_t *adapter, char *page, int start, int end ) +{ + dma_addr_t dma_handle; + logdrv_param *lparam; + megacmd_t mc; + char *disk_array; + dma_addr_t disk_array_dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + u8 *rdrv_state; + int num_ldrv; + u32 array_sz; + int len = 0; + int i; - while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) { - if(pci_enable_device (pdev)) - continue; - pciBus = pdev->bus->number; - pciDevFun = pdev->devfn; -#endif - if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { - pci_read_config_word (pdev, PCI_CONF_AMISIG, &magic); - if ((magic != AMI_SIGNATURE) - && (magic != AMI_SIGNATURE_471)) { - pciIdx++; - continue; /* not an AMI board */ - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pci_read_config_dword (pdev, PCI_CONF_AMISIG64, &magic64); + if( make_local_pdev(adapter, &pdev) != 0 ) { + return len; + } - if (magic64 == AMI_64BIT_SIGNATURE) - flag |= BOARD_64BIT; -#endif - } + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + free_local_pdev(pdev); + return len; + } - /* Hmmm...Should we not make this more modularized so that in future we don't add - for each firmware */ + if( mega_adapinq(adapter, dma_handle) != 0 ) { - if (flag & BOARD_QUARTZ) { - /* Check to see if this is a Dell PERC RAID controller model 466 */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - pcibios_read_config_word (pciBus, pciDevFun, - PCI_SUBSYSTEM_VENDOR_ID, - &subsysvid); - pcibios_read_config_word (pciBus, pciDevFun, - PCI_SUBSYSTEM_ID, &subsysid); -#else - pci_read_config_word (pdev, - PCI_SUBSYSTEM_VENDOR_ID, - &subsysvid); - pci_read_config_word (pdev, - PCI_SUBSYSTEM_ID, &subsysid); -#endif + len = sprintf(page, "Adapter inquiry failed.\n"); -#if 0 - /* - * This routine is called with well know values and we - * should not be getting what we have not asked. - * Also, the check is not right. It should have been for - * pci_vendor_id not subsysvid - AM - */ + printk(KERN_WARNING "megaraid: inquiry failed.\n"); - /* If we don't detect this valid subsystem vendor id's - we refuse to load the driver - PART of PC200X compliance - */ + mega_free_inquiry(inquiry, dma_handle, pdev); - if ((subsysvid != AMI_SUBSYS_ID) - && (subsysvid != DELL_SUBSYS_ID) - && (subsysvid != HP_SUBSYS_ID)) - continue; -#endif - } + free_local_pdev(pdev); - printk (KERN_NOTICE - "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n", - pciVendor, pciDev, pciIdx, pciBus, PCI_SLOT (pciDevFun), - PCI_FUNC (pciDevFun)); - /* Read the base port and IRQ from PCI */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - pcibios_read_config_dword (pciBus, pciDevFun, - PCI_BASE_ADDRESS_0, - (u_int *) & megaBase); - pcibios_read_config_byte (pciBus, pciDevFun, - PCI_INTERRUPT_LINE, &megaIrq); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ - megaBase = pdev->base_address[0]; - megaIrq = pdev->irq; -#else + return len; + } - megaBase = pci_resource_start (pdev, 0); - megaIrq = pdev->irq; -#endif + memset(&mc, 0, sizeof(megacmd_t)); - pciIdx++; + if( adapter->flag & BOARD_40LD ) { + array_sz = sizeof(disk_array_40ld); - if (flag & BOARD_QUARTZ) { - megaBase &= PCI_BASE_ADDRESS_MEM_MASK; - megaBase = (long) ioremap (megaBase, 128); - if (!megaBase) - continue; - } else { - megaBase &= PCI_BASE_ADDRESS_IO_MASK; - megaBase += 0x10; - } + rdrv_state = ((mega_inquiry3 *)inquiry)->ldrv_state; - /* Initialize SCSI Host structure */ - host = scsi_register (pHostTmpl, sizeof (mega_host_config)); - if (!host) - goto err_unmap; + num_ldrv = ((mega_inquiry3 *)inquiry)->num_ldrv; + } + else { + array_sz = sizeof(disk_array_8ld); - /* - * Comment the following initialization if you know 'max_sectors' is - * not defined for this kernel. - * This field was introduced in Linus's kernel 2.4.7pre3 and it - * greatly increases the IO performance - AM - */ - host->max_sectors = 1024; + rdrv_state = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.logdrv_info.ldrv_state; - scsi_set_device(host, &pdev->dev); - megaCfg = (mega_host_config *) host->hostdata; - memset (megaCfg, 0, sizeof (mega_host_config)); + num_ldrv = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.logdrv_info.num_ldrv; + } - printk (KERN_NOTICE "scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d" - M_RD_CRLFSTR, host->host_no, (u_int) megaBase, megaIrq); + disk_array = pci_alloc_consistent(pdev, array_sz, + &disk_array_dma_handle); - if (flag & BOARD_64BIT) - printk (KERN_NOTICE "scsi%d : Enabling 64 bit support\n", - host->host_no); + if( disk_array == NULL ) { + len = sprintf(page, "memory not available.\n"); - /* Copy resource info into structure */ - megaCfg->qCompletedH = NULL; - megaCfg->qCompletedT = NULL; - megaCfg->qPendingH = NULL; - megaCfg->qPendingT = NULL; - megaCfg->qFreeH = NULL; - megaCfg->qFreeT = NULL; - megaCfg->qFcnt = 0; - megaCfg->qPcnt = 0; - megaCfg->qCcnt = 0; - megaCfg->lock_free = SPIN_LOCK_UNLOCKED; - megaCfg->lock_pend = SPIN_LOCK_UNLOCKED; - megaCfg->lock_scsicmd = SPIN_LOCK_UNLOCKED; - megaCfg->flag = flag; - megaCfg->int_qh = NULL; - megaCfg->int_qt = NULL; - megaCfg->int_qlen = 0; + mega_free_inquiry(inquiry, dma_handle, pdev); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megaCfg->dev = pdev; -#endif - megaCfg->host = host; - megaCfg->base = megaBase; - megaCfg->host->irq = megaIrq; - megaCfg->host->io_port = megaBase; - megaCfg->host->n_io_port = 16; - megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; - megaCtlrs[numCtlrs] = megaCfg; - - if (!(flag & BOARD_QUARTZ)) { - /* Request our IO Range */ - if (!request_region(megaBase, 16, "megaraid")) { - printk(KERN_WARNING "megaraid: Couldn't register I/O range!\n"); - goto err_unregister; - } - } + free_local_pdev(pdev); - /* Request our IRQ */ - if (request_irq (megaIrq, megaraid_isr, SA_SHIRQ, - "megaraid", megaCfg)) { - printk (KERN_WARNING - "megaraid: Couldn't register IRQ %d!\n", - megaIrq); - goto err_release; - } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* - * unmap while releasing the driver, Is it required to be - * PCI_DMA_BIDIRECTIONAL - */ - - megaCfg->mailbox64ptr - = pci_alloc_consistent (megaCfg->dev, - sizeof (mega_mailbox64), - &(megaCfg->dma_handle64)); + return len; + } - mega_register_mailbox (megaCfg, megaCfg->dma_handle64); -#else - mega_register_mailbox (megaCfg, - virt_to_bus ((void *) &megaCfg-> - mailbox64)); -#endif + mc.xferaddr = (u32)disk_array_dma_handle; - mega_i_query_adapter (megaCfg); + if( adapter->flag & BOARD_40LD ) { + mc.cmd = FC_NEW_CONFIG; + mc.opcode = OP_DCMD_READ_CONFIG; - if ((subsysid == 0x1111) && (subsysvid == 0x1111)) { + if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) ) { - /* - * Which firmware - */ - if( strcmp(megaCfg->fwVer, "3.00") == 0 || - strcmp(megaCfg->fwVer, "3.01") == 0 ) { + len = sprintf(page, "40LD read config failed.\n"); - printk( KERN_WARNING - "megaraid: Your card is a Dell PERC 2/SC RAID controller " - "with firmware\nmegaraid: 3.00 or 3.01. This driver is " - "known to have corruption issues\nmegaraid: with those " - "firmware versions on this specific card. In order\n" - "megaraid: to protect your data, please upgrade your " - "firmware to version\nmegaraid: 3.10 or later, available " - "from the Dell Technical Support web\nmegaraid: site at\n" - "http://support.dell.com/us/en/filelib/download/" - "index.asp?fileid=2940\n" - ); - } + mega_free_inquiry(inquiry, dma_handle, pdev); + + pci_free_consistent(pdev, array_sz, disk_array, + disk_array_dma_handle); + + free_local_pdev(pdev); + + return len; } - /* - * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with - * firmware H.01.07 or H.01.08, disable 64 bit support, - * since this firmware cannot handle 64 bit addressing - */ + } + else { + mc.cmd = NEW_READ_CONFIG_8LD; - if( (subsysvid == HP_SUBSYS_ID) && - ((subsysid == 0x60E7)||(subsysid == 0x60E8)) ) { + if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) ) { - /* - * which firmware - */ - if( strcmp(megaCfg->fwVer, "H01.07") == 0 || - strcmp(megaCfg->fwVer, "H01.08") == 0 ) { - printk(KERN_WARNING - "megaraid: Firmware H.01.07 or H.01.08 on 1M/2M " - "controllers\nmegaraid: do not support 64 bit " - "addressing.\n" - "megaraid: DISABLING 64 bit support.\n"); - megaCfg->flag &= ~BOARD_64BIT; + mc.cmd = READ_CONFIG_8LD; + + if( mega_internal_command(adapter, LOCK_INT, &mc, + NULL) ){ + + len = sprintf(page, + "8LD read config failed.\n"); + + mega_free_inquiry(inquiry, dma_handle, pdev); + + pci_free_consistent(pdev, array_sz, + disk_array, + disk_array_dma_handle); + + free_local_pdev(pdev); + + return len; } } + } - if (mega_is_bios_enabled (megaCfg)) { - mega_hbas[numCtlrs].is_bios_enabled = 1; - } + for( i = start; i < ( (end+1 < num_ldrv) ? end+1 : num_ldrv ); i++ ) { - /* - * Find out which channel is raid and which is scsi - */ - mega_enum_raid_scsi(megaCfg); - for( i = 0; i < megaCfg->productInfo.SCSIChanPresent; i++ ) { - if(IS_RAID_CH(i)) - printk(KERN_NOTICE"megaraid: channel[%d] is raid.\n", i+1); - else - printk(KERN_NOTICE"megaraid: channel[%d] is scsi.\n", i+1); + if( adapter->flag & BOARD_40LD ) { + lparam = + &((disk_array_40ld *)disk_array)->ldrv[i].lparam; + } + else { + lparam = + &((disk_array_8ld *)disk_array)->ldrv[i].lparam; } /* - * Find out if a logical drive is set as the boot drive. If there is - * one, will make that as the first logical drive. + * Check for overflow. We print less than 240 characters for + * information about each logical drive. */ - mega_get_boot_ldrv(megaCfg); + if( (len + 240) >= PAGE_SIZE ) break; - mega_hbas[numCtlrs].hostdata_addr = megaCfg; + len += sprintf(page+len, "Logical drive:%2d:, ", i); - /* - * Do we support random deletion and addition of logical drives - */ - megaCfg->read_ldidmap = 0; /* set it after first logdrv delete cmd */ - megaCfg->support_random_del = mega_support_random_del(megaCfg); + switch( rdrv_state[i] & 0x0F ) { + case RDRV_OFFLINE: + len += sprintf(page+len, "state: offline"); + break; - /* Initialize SCBs */ - if (mega_init_scb (megaCfg)) { - pci_free_consistent (megaCfg->dev, - sizeof (mega_mailbox64), - (void *) megaCfg->mailbox64ptr, - megaCfg->dma_handle64); - scsi_unregister (host); - continue; + case RDRV_DEGRADED: + len += sprintf(page+len, "state: degraded"); + break; + + case RDRV_OPTIMAL: + len += sprintf(page+len, "state: optimal"); + break; + + case RDRV_DELETED: + len += sprintf(page+len, "state: deleted"); + break; + + default: + len += sprintf(page+len, "state: unknown"); + break; } /* - * Fill in the structure which needs to be passed back to the - * application when it does an ioctl() for controller related - * information. + * Check if check consistency or initialization is going on + * for this logical drive. */ + if( (rdrv_state[i] & 0xF0) == 0x20 ) { + len += sprintf(page+len, + ", check-consistency in progress"); + } + else if( (rdrv_state[i] & 0xF0) == 0x10 ) { + len += sprintf(page+len, + ", initialization in progress"); + } + + len += sprintf(page+len, "\n"); - i = numCtlrs; - numCtlrs++; - - mcontroller[i].base = megaBase; - mcontroller[i].irq = megaIrq; - mcontroller[i].numldrv = megaCfg->numldrv; - mcontroller[i].pcibus = pciBus; - mcontroller[i].pcidev = pciDev; - mcontroller[i].pcifun = PCI_FUNC (pciDevFun); - mcontroller[i].pciid = pciIdx; - mcontroller[i].pcivendor = pciVendor; - mcontroller[i].pcislot = PCI_SLOT (pciDevFun); - mcontroller[i].uid = (pciBus << 8) | pciDevFun; - - numFound++; - - /* Set the Mode of addressing to 64 bit */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if ((megaCfg->flag & BOARD_64BIT) && BITS_PER_LONG == 64) -#ifdef __LP64__ - pdev->dma_mask = 0xffffffffffffffff; -#else - pdev->dma_mask = 0xffffffff; -#endif -#endif - continue; - err_release: - if (flag & BOARD_QUARTZ) - release_region (megaBase, 16); - err_unregister: - scsi_unregister (host); - err_unmap: - if (flag & BOARD_QUARTZ) - iounmap ((void *) megaBase); - } - return numFound; -} - -/*--------------------------------------------------------- - * Detects if a megaraid controller exists in this system - *---------------------------------------------------------*/ + len += sprintf(page+len, "Span depth:%3d, ", + lparam->span_depth); -int megaraid_detect (Scsi_Host_Template * pHostTmpl) -{ - int ctlridx = 0, count = 0; + len += sprintf(page+len, "RAID level:%3d, ", + lparam->level); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ - pHostTmpl->proc_dir = &proc_scsi_megaraid; -#else - pHostTmpl->proc_name = "megaraid"; -#endif + len += sprintf(page+len, "Stripe size:%3d, ", + lparam->stripe_sz ? lparam->stripe_sz/2: 128); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - if (!pcibios_present ()) { - printk (KERN_WARNING "megaraid: PCI bios not present." - M_RD_CRLFSTR); - return 0; - } -#endif - skip_id = -1; - if (megaraid && !strncmp (megaraid, "skip", strlen ("skip"))) { - if (megaraid[4] != '\0') { - skip_id = megaraid[4] - '0'; - if (megaraid[5] != '\0') { - skip_id = (skip_id * 10) + (megaraid[5] - '0'); - } - } - skip_id = (skip_id > 15) ? -1 : skip_id; - } + len += sprintf(page+len, "Row size:%3d\n", + lparam->row_size); - printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION); - memset (mega_hbas, 0, sizeof (mega_hbas)); + len += sprintf(page+len, "Read Policy: "); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, - PCI_DEVICE_ID_AMI_MEGARAID, 0); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, - PCI_DEVICE_ID_AMI_MEGARAID2, 0); - count += mega_findCard (pHostTmpl, 0x8086, - PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, - PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); + switch(lparam->read_ahead) { + case NO_READ_AHEAD: + len += sprintf(page+len, "No read ahead, "); + break; -#ifdef CONFIG_PROC_FS - if (count) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ - mega_proc_dir_entry = proc_mkdir ("megaraid", &proc_root); -#else - mega_proc_dir_entry = create_proc_entry ("megaraid", - S_IFDIR | S_IRUGO | - S_IXUGO, &proc_root); -#endif - if (!mega_proc_dir_entry) - printk ("megaraid: failed to create megaraid root\n"); - else - for (ctlridx = 0; ctlridx < count; ctlridx++) - mega_create_proc_entry (ctlridx, - mega_proc_dir_entry); - } -#endif + case READ_AHEAD: + len += sprintf(page+len, "Read ahead, "); + break; - /* - * Register the driver as a character device, for applications to access - * it for ioctls. - * Ideally, this should go in the init_module() routine, but since it is - * hidden in the file "scsi_module.c" ( included in the end ), we define - * it here - * First argument (major) to register_chrdev implies a dynamic major - * number allocation. - */ - if (count) { - major = register_chrdev (0, "megadev", &megadev_fops); + case ADAP_READ_AHEAD: + len += sprintf(page+len, "Adaptive, "); + break; - /* - * Register the Shutdown Notification hook in kernel - */ - if (register_reboot_notifier (&mega_notifier)) { - printk ("MegaRAID Shutdown routine not registered!!\n"); } - init_MUTEX (&mimd_entry_mtx); - } - - return count; -} - -/*--------------------------------------------------------------------- - * Release the controller's resources - *---------------------------------------------------------------------*/ -int megaraid_release (struct Scsi_Host *pSHost) -{ - mega_host_config *megaCfg; - mega_mailbox *mbox; - u_char mboxData[16]; - int i; + len += sprintf(page+len, "Write Policy: "); - megaCfg = (mega_host_config *) pSHost->hostdata; - mbox = (mega_mailbox *) mboxData; + switch(lparam->write_mode) { - /* Flush cache to disk */ - memset (mbox, 0, 16); - mboxData[0] = 0xA; + case WRMODE_WRITE_THRU: + len += sprintf(page+len, "Write thru, "); + break; - free_irq (megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise - extra interrupt is generated */ + case WRMODE_WRITE_BACK: + len += sprintf(page+len, "Write back, "); + break; + } - /* Issue a blocking (interrupts disabled) command to the card */ - megaIssueCmd (megaCfg, mboxData, NULL, 0); + len += sprintf(page+len, "Cache Policy: "); - /* Free our resources */ - if (megaCfg->flag & BOARD_QUARTZ) { - iounmap ((void *) megaCfg->base); - } else { - release_region (megaCfg->host->io_port, 16); - } + switch(lparam->direct_io) { - mega_freeSgList (megaCfg); - pci_free_consistent (megaCfg->dev, - sizeof (mega_mailbox64), - (void *) megaCfg->mailbox64ptr, - megaCfg->dma_handle64); + case CACHED_IO: + len += sprintf(page+len, "Cached IO\n\n"); + break; -#ifdef CONFIG_PROC_FS - if (megaCfg->controller_proc_dir_entry) { - remove_proc_entry ("stat", megaCfg->controller_proc_dir_entry); - remove_proc_entry ("status", - megaCfg->controller_proc_dir_entry); - remove_proc_entry ("config", - megaCfg->controller_proc_dir_entry); - remove_proc_entry ("mailbox", - megaCfg->controller_proc_dir_entry); - for (i = 0; i < numCtlrs; i++) { - char buf[12] = { 0 }; - sprintf (buf, "%d", i); - remove_proc_entry (buf, mega_proc_dir_entry); + case DIRECT_IO: + len += sprintf(page+len, "Direct IO\n\n"); + break; } - remove_proc_entry ("megaraid", &proc_root); } -#endif - /* - * Release the controller memory. A word of warning this frees - * hostdata and that includes megaCfg-> so be careful what you - * dereference beyond this point - */ - - scsi_unregister (pSHost); + mega_free_inquiry(inquiry, dma_handle, pdev); - /* - * Unregister the character device interface to the driver. Ideally this - * should have been done in cleanup_module routine. Since this is hidden - * in file "scsi_module.c", we do it here. - * major is the major number of the character device returned by call to - * register_chrdev() routine. - */ + pci_free_consistent(pdev, array_sz, disk_array, + disk_array_dma_handle); - unregister_chrdev (major, "megadev"); - unregister_reboot_notifier (&mega_notifier); + free_local_pdev(pdev); - return 0; + return len; } -static int mega_is_bios_enabled (mega_host_config * megacfg) +#endif + + +/** + * megaraid_biosparam() + * + * Return the disk geometry for a particular disk + */ +static int +megaraid_biosparam(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int geom[]) { - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int ret; + adapter_t *adapter; + unsigned char *bh; + int heads; + int sectors; + int cylinders; + int rval; + + /* Get pointer to host config structure */ + adapter = (adapter_t *)sdev->host->hostdata; - mboxpnt = (mega_mailbox *) mbox; + if (IS_RAID_CH(adapter, sdev->channel)) { + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = (ulong)capacity / (heads * sectors); - memset (mbox, 0, sizeof (mbox)); - memset ((void *) megacfg->mega_buffer, - 0, sizeof (megacfg->mega_buffer)); + /* + * Handle extended translation size for logical drives + * > 1Gb + */ + if ((ulong)capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = (ulong)capacity / (heads * sectors); + } - /* - * issue command to find out if the BIOS is enabled for this controller - */ - mbox[0] = IS_BIOS_ENABLED; - mbox[2] = GET_BIOS; + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + } + else { + bh = scsi_bios_ptable(bdev); + + if( bh ) { + rval = scsi_partsize(bh, capacity, + &geom[2], &geom[0], &geom[1]); + kfree(bh); + if( rval != -1 ) + return rval; + } + + printk(KERN_INFO + "megaraid: invalid partition on this disk on channel %d\n", + sdev->channel); + + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = (ulong)capacity / (heads * sectors); - mboxpnt->xferaddr = virt_to_bus ((void *) megacfg->mega_buffer); + /* Handle extended translation size for logical drives > 1Gb */ + if ((ulong)capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = (ulong)capacity / (heads * sectors); + } - ret = megaIssueCmd (megacfg, mbox, NULL, 0); + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + } - return (*(char *) megacfg->mega_buffer); + return 0; } -/* - * Find out what channels are RAID/SCSI +/** + * megaraid_reboot_notify() + * @this - unused + * @code - shutdown code + * @unused - unused + * + * This routine will be called when the use has done a forced shutdown on the + * system. Flush the Adapter and disks cache. */ -void -mega_enum_raid_scsi(mega_host_config *megacfg) +static int +megaraid_reboot_notify (struct notifier_block *this, unsigned long code, + void *unused) { - mega_mailbox *mboxp; - unsigned char mbox[16]; - int i; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t dma_handle; -#endif + adapter_t *adapter; + struct Scsi_Host *host; + u8 raw_mbox[16]; + mbox_t *mbox; + int i,j; - mboxp = (mega_mailbox *)mbox; - - memset(mbox, 0, sizeof(mbox)); /* - * issue command to find out what channels are raid/scsi + * Flush the controller's cache irrespective of the codes coming down. + * SYS_DOWN, SYS_HALT, SYS_RESTART, SYS_POWER_OFF */ - mbox[0] = CHNL_CLASS; - mbox[2] = GET_CHNL_CLASS; + for( i = 0; i < hba_count; i++ ) { + printk(KERN_INFO "megaraid: flushing adapter %d..", i); + host = hba_soft_state[i]->host; + + adapter = (adapter_t *)host->hostdata; + mbox = (mbox_t *)raw_mbox; + + /* Flush adapter cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_ADAPTER; - memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, - (2 * 1024L), PCI_DMA_FROMDEVICE); + /* + * Issue a blocking (interrupts disabled) command to + * the card + */ + issue_scb_block(adapter, raw_mbox); - mboxp->xferaddr = dma_handle; -#else - mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); -#endif + /* Flush disks cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_SYSTEM; - /* - * Non-ROMB firware fail this command, so all channels - * must be shown RAID - */ - if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { - mega_ch_class = *((char *)megacfg->mega_buffer); + issue_scb_block(adapter, raw_mbox); + + printk("Done.\n"); - for( i = 0; i < NVIRT_CHAN; i++ ) { - /* logical drives channel is RAID */ - mega_ch_class |= (0x01 << (megacfg->productInfo.SCSIChanPresent+i)); + if( atomic_read(&adapter->pend_cmds) > 0 ) { + printk(KERN_WARNING "megaraid: pending commands!!\n"); } } - else { - mega_ch_class = 0xFF; - } + /* + * Have a delibrate delay to make sure all the caches are + * actually flushed. + */ + printk("megaraid: cache flush delay: "); + for( j = 10; j >= 0; j-- ) { + printk("[%d] ", j); + mdelay(1000); + } + printk("\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pci_unmap_single(megacfg->dev, dma_handle, - (2 * 1024L), PCI_DMA_FROMDEVICE); -#endif - + return NOTIFY_DONE; } - -/* - * get the boot logical drive number if enabled +/** + * mega_init_scb() + * @adapter - pointer to our soft state + * + * Allocate memory for the various pointers in the scb structures: + * scatter-gather list pointer, passthru and extended passthru structure + * pointers. */ -void -mega_get_boot_ldrv(mega_host_config *megacfg) +static int +mega_init_scb(adapter_t *adapter) { - mega_mailbox *mboxp; - unsigned char mbox[16]; - struct private_bios_data *prv_bios_data; - u16 cksum = 0; - char *cksum_p; - int i; + scb_t *scb; + int i; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t dma_handle; -#endif + for( i = 0; i < adapter->max_cmds; i++ ) { - mboxp = (mega_mailbox *)mbox; + scb = &adapter->scb_list[i]; - memset(mbox, 0, sizeof(mbox)); + scb->sgl64 = NULL; + scb->sgl = NULL; + scb->pthru = NULL; + scb->epthru = NULL; + } - mbox[0] = BIOS_PVT_DATA; - mbox[2] = GET_BIOS_PVT_DATA; + for( i = 0; i < adapter->max_cmds; i++ ) { - memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); + scb = &adapter->scb_list[i]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, - (2 * 1024L), PCI_DMA_FROMDEVICE); + scb->idx = i; - mboxp->xferaddr = dma_handle; -#else - mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); -#endif + scb->sgl64 = pci_alloc_consistent(adapter->dev, + sizeof(mega_sgl64) * adapter->sglen, + &scb->sgl_dma_addr); - megacfg->boot_ldrv_enabled = 0; - megacfg->boot_ldrv = 0; - if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { + scb->sgl = (mega_sglist *)scb->sgl64; - prv_bios_data = (struct private_bios_data *)megacfg->mega_buffer; + if( !scb->sgl ) { + printk(KERN_WARNING "RAID: Can't allocate sglist.\n"); + mega_free_sgl(adapter); + return -1; + } - cksum = 0; - cksum_p = (char *)prv_bios_data; - for( i = 0; i < 14; i++ ) { - cksum += (u16)(*cksum_p++); + scb->pthru = pci_alloc_consistent(adapter->dev, + sizeof(mega_passthru), + &scb->pthru_dma_addr); + + if( !scb->pthru ) { + printk(KERN_WARNING "RAID: Can't allocate passthru.\n"); + mega_free_sgl(adapter); + return -1; } - if( prv_bios_data->cksum == (u16)(0-cksum) ) { - megacfg->boot_ldrv_enabled = 1; - megacfg->boot_ldrv = prv_bios_data->boot_ldrv; + scb->epthru = pci_alloc_consistent(adapter->dev, + sizeof(mega_ext_passthru), + &scb->epthru_dma_addr); + + if( !scb->epthru ) { + printk(KERN_WARNING + "Can't allocate extended passthru.\n"); + mega_free_sgl(adapter); + return -1; } - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pci_unmap_single(megacfg->dev, dma_handle, - (2 * 1024L), PCI_DMA_FROMDEVICE); -#endif + scb->dma_type = MEGA_DMA_TYPE_NONE; + + /* + * Link to free list + * lock not required since we are loading the driver, so no + * commands possible right now. + */ + scb->state = SCB_FREE; + scb->cmd = NULL; + list_add(&scb->list, &adapter->free_list); + } + + return 0; } -static inline void mega_freeSgList (mega_host_config * megaCfg) +/** + * megadev_open() + * @inode - unused + * @filep - unused + * + * Routines for the character/ioctl interface to the driver. Find out if this + * is a valid open. If yes, increment the module use count so that it cannot + * be unloaded. + */ +static int +megadev_open (struct inode *inode, struct file *filep) { - int i; + /* + * Only allow superuser to access private ioctl interface + */ + if( !capable(CAP_SYS_ADMIN) ) return -EACCES; - for (i = 0; i < megaCfg->max_cmds; i++) { - if (megaCfg->scbList[i].sgList) - pci_free_consistent (megaCfg->dev, - sizeof (mega_64sglist) * - MAX_SGLIST, - megaCfg->scbList[i].sgList, - megaCfg->scbList[i]. - dma_sghandle64); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x020400 */ - kfree (megaCfg->scbList[i].sgList); /* free sgList */ -#endif - } + return 0; } -/*---------------------------------------------- - * Get information about the card/driver - *----------------------------------------------*/ -const char *megaraid_info (struct Scsi_Host *pSHost) -{ - static char buffer[512]; - mega_host_config *megaCfg; - - megaCfg = (mega_host_config *) pSHost->hostdata; - sprintf (buffer, - "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", - megaCfg->fwVer, megaCfg->productInfo.MaxConcCmds, - megaCfg->host->max_id, megaCfg->host->max_channel, - megaCfg->host->max_lun); - return buffer; -} +/** + * megadev_ioctl() + * @inode - Our device inode + * @filep - unused + * @cmd - ioctl command + * @arg - user buffer + * + * ioctl entry point for our private ioctl interface. We move the data in from + * the user space, prepare the command (if necessary, convert the old MIMD + * ioctl to new ioctl command), and issue a synchronous command to the + * controller. + */ +static int +megadev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + adapter_t *adapter; + nitioctl_t uioc; + int adapno; + int rval; + mega_passthru *upthru; /* user address for passthru */ + mega_passthru *pthru; /* copy user passthru here */ + dma_addr_t pthru_dma_hndl; + void *data = NULL; /* data to be transferred */ + dma_addr_t data_dma_hndl; /* dma handle for data xfer area */ + megacmd_t mc; + megastat_t *ustats; + int num_ldrv; + u32 uxferaddr = 0; + struct pci_dev *pdev; -/*----------------------------------------------------------------- - * Perform a SCSI command - * Mailbox area: - * 00 01 command - * 01 01 command id - * 02 02 # of sectors - * 04 04 logical bus address - * 08 04 physical buffer address - * 0C 01 logical drive # - * 0D 01 length of scatter/gather list - * 0E 01 reserved - * 0F 01 mailbox busy - * 10 01 numstatus byte - * 11 01 status byte - *-----------------------------------------------------------------*/ -int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) -{ - DRIVER_LOCK_T mega_host_config * megaCfg; - mega_scb *pScb; - char *user_area = NULL; - - megaCfg = (mega_host_config *) SCpnt->device->host->hostdata; - DRIVER_LOCK (megaCfg); - - if (!(megaCfg->flag & (1L << SCpnt->device->channel))) { - if (SCpnt->device->channel < megaCfg->productInfo.SCSIChanPresent) - printk ( KERN_NOTICE - "scsi%d: scanning channel %d for devices.\n", - megaCfg->host->host_no, SCpnt->device->channel); - else - printk ( KERN_NOTICE - "scsi%d: scanning virtual channel %d for logical drives.\n", - megaCfg->host->host_no, - SCpnt->device->channel-megaCfg->productInfo.SCSIChanPresent+1); + ustats = NULL; /* avoid compilation warnings */ + num_ldrv = 0; - megaCfg->flag |= (1L << SCpnt->device->channel); + /* + * Make sure only USCSICMD are issued through this interface. + * MIMD application would still fire different command. + */ + if( (_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD) ) { + return -EINVAL; } - SCpnt->scsi_done = pktComp; + /* + * Check and convert a possible MIMD command to NIT command. + * mega_m_to_n() copies the data from the user space, so we do not + * have to do it here. + * NOTE: We will need some user address to copyout the data, therefore + * the inteface layer will also provide us with the required user + * addresses. + */ + memset(&uioc, 0, sizeof(nitioctl_t)); + if( (rval = mega_m_to_n( (void *)arg, &uioc)) != 0 ) + return rval; + - if (mega_driver_ioctl (megaCfg, SCpnt)) - return 0; + switch( uioc.opcode ) { - /* If driver in abort or reset.. cancel this command */ - if (megaCfg->flag & IN_ABORT) { - SCpnt->result = (DID_ABORT << 16); - /* Add Scsi_Command to end of completed queue */ - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; - megaCfg->qCcnt++; + case GET_DRIVER_VER: + if( put_user(driver_ver, (u32 *)uioc.uioc_uaddr) ) + return (-EFAULT); - DRIVER_UNLOCK (megaCfg); - return 0; - } else if (megaCfg->flag & IN_RESET) { - SCpnt->result = (DID_RESET << 16); - /* Add Scsi_Command to end of completed queue */ - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; - megaCfg->qCcnt++; + break; - DRIVER_UNLOCK (megaCfg); - return 0; - } + case GET_N_ADAP: + if( put_user(hba_count, (u32 *)uioc.uioc_uaddr) ) + return (-EFAULT); - megaCfg->flag |= IN_QUEUE; - /* Allocate and build a SCB request */ - if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) { - - /* - * Check if the HBA is in quiescent state, e.g., during a delete - * logical drive opertion. If it is, queue the commands in the - * internal queue until the delete operation is complete. - */ - if( ! megaCfg->quiescent ) { - /* Add SCB to the head of the pending queue */ - if (megaCfg->qPendingH == NULL) { - megaCfg->qPendingH = megaCfg->qPendingT = pScb; - } else { - megaCfg->qPendingT->next = pScb; - megaCfg->qPendingT = pScb; - } - megaCfg->qPendingT->next = NULL; - megaCfg->qPcnt++; - - if (mega_runpendq (megaCfg) == -1) { - DRIVER_UNLOCK (megaCfg); - return 0; - } - } - else { - /* Add SCB to the internal queue */ - if (megaCfg->int_qh == NULL) { - megaCfg->int_qh = megaCfg->int_qt = pScb; - } else { - megaCfg->int_qt->next = pScb; - megaCfg->int_qt = pScb; - } - megaCfg->int_qt->next = NULL; - megaCfg->int_qlen++; - } - - if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - init_MUTEX_LOCKED (&pScb->ioctl_sem); - IO_UNLOCK_IRQ(megaCfg->host); - down (&pScb->ioctl_sem); - user_area = (char *)*((u32*)&pScb->SCpnt->cmnd[4]); - if (copy_to_user - (user_area, pScb->buff_ptr, pScb->iDataSize)) { - printk - ("megaraid: Error copying ioctl return value to user buffer.\n"); - pScb->SCpnt->result = (DID_ERROR << 16); - } - IO_LOCK_IRQ(megaCfg->host); - DRIVER_LOCK (megaCfg); - kfree (pScb->buff_ptr); - pScb->buff_ptr = NULL; - mega_cmd_done (megaCfg, pScb, pScb->SCpnt->result); - mega_rundoneq (megaCfg); - mega_runpendq (megaCfg); - DRIVER_UNLOCK (megaCfg); - } + /* + * Shucks. MIMD interface returns a positive value for number + * of adapters. TODO: Change it to return 0 when there is no + * applicatio using mimd interface. + */ + return hba_count; - megaCfg->flag &= ~IN_QUEUE; + case GET_ADAP_INFO: - } + /* + * Which adapter + */ + if( (adapno = GETADAP(uioc.adapno)) >= hba_count ) + return (-ENODEV); - DRIVER_UNLOCK (megaCfg); - return 0; -} + if( copy_to_user(uioc.uioc_uaddr, mcontroller+adapno, + sizeof(struct mcontroller)) ) + return (-EFAULT); + break; -/*---------------------------------------------------------------------- - * Issue a blocking command to the controller - *----------------------------------------------------------------------*/ -volatile static int internal_done_flag = 0; -volatile static int internal_done_errcode = 0; +#if MEGA_HAVE_STATS -static DECLARE_WAIT_QUEUE_HEAD (internal_wait); + case GET_STATS: + /* + * Which adapter + */ + if( (adapno = GETADAP(uioc.adapno)) >= hba_count ) + return (-ENODEV); -static void internal_done (Scsi_Cmnd * SCpnt) -{ - internal_done_errcode = SCpnt->result; - internal_done_flag++; - wake_up (&internal_wait); -} + adapter = hba_soft_state[adapno]; -/* shouldn't be used, but included for completeness */ + ustats = (megastat_t *)uioc.uioc_uaddr; -int megaraid_command (Scsi_Cmnd * SCpnt) -{ - internal_done_flag = 0; + if( copy_from_user(&num_ldrv, &ustats->num_ldrv, sizeof(int)) ) + return (-EFAULT); - /* Queue command, and wait until it has completed */ - megaraid_queue (SCpnt, internal_done); + /* + * Check for the validity of the logical drive number + */ + if( num_ldrv >= MAX_LOGICAL_DRIVES_40LD ) return -EINVAL; - while (!internal_done_flag) { - interruptible_sleep_on (&internal_wait); - } + if( copy_to_user(ustats->nreads, adapter->nreads, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->nreadblocks, adapter->nreadblocks, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->nwrites, adapter->nwrites, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->nwriteblocks, adapter->nwriteblocks, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->rd_errors, adapter->rd_errors, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->wr_errors, adapter->wr_errors, + num_ldrv*sizeof(u32)) ) + return -EFAULT; - return internal_done_errcode; -} + return 0; -/*--------------------------------------------------------------------- - * Abort a previous SCSI request - *---------------------------------------------------------------------*/ -int megaraid_abort (Scsi_Cmnd * SCpnt) -{ - mega_host_config *megaCfg; - int rc; /*, idx; */ - mega_scb *pScb; - - rc = SCSI_ABORT_NOT_RUNNING; - - megaCfg = (mega_host_config *) SCpnt->device->host->hostdata; - - megaCfg->flag |= IN_ABORT; - - for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { - if (pScb->SCpnt == SCpnt) { - /* Found an aborting command */ -#if DEBUG - showMbox (pScb); #endif + case MBOX_CMD: - /* - * If the command is queued to be issued to the firmware, abort the scsi cmd, - * If the command is already aborted in a previous call to the _abort entry - * point, return SCSI_ABORT_SNOOZE, suggesting a reset. - * If the command is issued to the firmware, which might complete after - * some time, we will mark the scb as aborted, and return to the mid layer, - * that abort could not be done. - * In the ISR, when this command actually completes, we will perform a normal - * completion. - * - * Oct 27, 1999 - */ + /* + * Which adapter + */ + if( (adapno = GETADAP(uioc.adapno)) >= hba_count ) + return (-ENODEV); - switch (pScb->state) { - case SCB_ABORTED: /* Already aborted */ - rc = SCSI_ABORT_SNOOZE; - break; - case SCB_ISSUED: /* Waiting on ISR result */ - rc = SCSI_ABORT_NOT_RUNNING; - pScb->state = SCB_ABORTED; - break; - case SCB_ACTIVE: /* still on the pending queue */ - mega_freeSCB (megaCfg, pScb); - SCpnt->result = (DID_ABORT << 16); - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = - megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = - (unsigned char *) NULL; - megaCfg->qCcnt++; - rc = SCSI_ABORT_SUCCESS; - break; - default: - printk - ("megaraid_abort: unknown command state!!\n"); - rc = SCSI_ABORT_NOT_RUNNING; - break; + adapter = hba_soft_state[adapno]; + + /* + * Deletion of logical drive is a special case. The adapter + * should be quiescent before this command is issued. + */ + if( uioc.uioc_rmbox[0] == FC_DEL_LOGDRV && + uioc.uioc_rmbox[2] == OP_DEL_LOGDRV ) { + + /* + * Do we support this feature + */ + if( !adapter->support_random_del ) { + printk(KERN_WARNING "megaraid: logdrv "); + printk("delete on non-supporting F/W.\n"); + + return (-EINVAL); } - break; - } - } - megaCfg->flag &= ~IN_ABORT; + rval = mega_del_logdrv( adapter, uioc.uioc_rmbox[3] ); -#if DEBUG - if (megaCfg->flag & IN_QUEUE) - printk ("ma:flag is in queue\n"); - if (megaCfg->qCompletedH == NULL) - printk ("ma:qchead == null\n"); -#endif + if( rval == 0 ) { + memset(&mc, 0, sizeof(megacmd_t)); - /* - * This is required here to complete any completed requests to be communicated - * over to the mid layer. - * Calling just mega_rundoneq() did not work. - */ - if (megaCfg->qCompletedH) { - SCpnt = megaCfg->qCompletedH; - megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; - megaCfg->qCcnt--; + mc.status = rval; - SCpnt->host_scribble = (unsigned char *) NULL; - /* Callback */ - callDone (SCpnt); - } - mega_rundoneq (megaCfg); + rval = mega_n_to_m((void *)arg, &mc); + } - return rc; -} + return rval; + } + /* + * This interface only support the regular passthru commands. + * Reject extended passthru and 64-bit passthru + */ + if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU64 || + uioc.uioc_rmbox[0] == MEGA_MBOXCMD_EXTPTHRU ) { -/*--------------------------------------------------------------------- - * Reset a previous SCSI request - *---------------------------------------------------------------------*/ + printk(KERN_WARNING "megaraid: rejected passthru.\n"); -int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags) -{ - mega_host_config *megaCfg; - int idx; - int rc; - mega_scb *pScb; + return (-EINVAL); + } + + /* + * For all internal commands, the buffer must be allocated in + * <4GB address range + */ + if( make_local_pdev(adapter, &pdev) != 0 ) + return -EIO; + + /* Is it a passthru command or a DCMD */ + if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU ) { + /* Passthru commands */ + + pthru = pci_alloc_consistent(pdev, + sizeof(mega_passthru), + &pthru_dma_hndl); + + if( pthru == NULL ) { + free_local_pdev(pdev); + return (-ENOMEM); + } - rc = SCSI_RESET_NOT_RUNNING; - megaCfg = (mega_host_config *) SCpnt->device->host->hostdata; + /* + * The user passthru structure + */ + upthru = (mega_passthru *)MBOX(uioc)->xferaddr; - megaCfg->flag |= IN_RESET; + /* + * Copy in the user passthru here. + */ + if( copy_from_user(pthru, (char *)upthru, + sizeof(mega_passthru)) ) { - printk - ("megaraid_RESET: %.08lx cmd=%.02x , flag = %x\n", - SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->device->channel, - SCpnt->device->id, SCpnt->device->lun, rstflags); + pci_free_consistent(pdev, + sizeof(mega_passthru), pthru, + pthru_dma_hndl); - TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n", - SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->device->channel, - SCpnt->device->id, SCpnt->device->lun)); + free_local_pdev(pdev); - /* - * Walk list of SCBs for any that are still outstanding - */ - for (idx = 0; idx < megaCfg->max_cmds; idx++) { - if (megaCfg->scbList[idx].state != SCB_FREE) { - SCpnt = megaCfg->scbList[idx].SCpnt; - pScb = &megaCfg->scbList[idx]; - if (SCpnt != NULL) { - pScb->state = SCB_RESET; - break; + return (-EFAULT); } - } - } - megaCfg->flag &= ~IN_RESET; + /* + * Is there a data transfer + */ + if( pthru->dataxferlen ) { + data = pci_alloc_consistent(pdev, + pthru->dataxferlen, + &data_dma_hndl); + + if( data == NULL ) { + pci_free_consistent(pdev, + sizeof(mega_passthru), + pthru, + pthru_dma_hndl); - mega_rundoneq (megaCfg); - return rc; -} + free_local_pdev(pdev); -#ifdef CONFIG_PROC_FS -/* Following code handles /proc fs */ -static int proc_printf (mega_host_config * megaCfg, const char *fmt, ...) -{ - va_list args; - int i; + return (-ENOMEM); + } - if (megaCfg->procidx > PROCBUFSIZE) - return 0; + /* + * Save the user address and point the kernel + * address at just allocated memory + */ + uxferaddr = pthru->dataxferaddr; + pthru->dataxferaddr = data_dma_hndl; + } - va_start (args, fmt); - i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args); - va_end (args); - megaCfg->procidx += i; - return i; -} + /* + * Is data coming down-stream + */ + if( pthru->dataxferlen && (uioc.flags & UIOC_WR) ) { + /* + * Get the user data + */ + if( copy_from_user(data, (char *)uxferaddr, + pthru->dataxferlen) ) { + rval = (-EFAULT); + goto freemem_and_return; + } + } -static int proc_read_config (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ + memset(&mc, 0, sizeof(megacmd_t)); + + mc.cmd = MEGA_MBOXCMD_PASSTHRU; + mc.xferaddr = (u32)pthru_dma_hndl; - mega_host_config *megaCfg = (mega_host_config *) data; + /* + * Issue the command + */ + mega_internal_command(adapter, LOCK_INT, &mc, pthru); - *start = page; + rval = mega_n_to_m((void *)arg, &mc); - if (megaCfg->productInfo.ProductName[0] != 0) - proc_printf (megaCfg, "%s\n", megaCfg->productInfo.ProductName); + if( rval ) goto freemem_and_return; - proc_printf (megaCfg, "Controller Type: "); - if (megaCfg->flag & BOARD_QUARTZ) - proc_printf (megaCfg, "438/466/467/471/493\n"); - else - proc_printf (megaCfg, "418/428/434\n"); + /* + * Is data going up-stream + */ + if( pthru->dataxferlen && (uioc.flags & UIOC_RD) ) { + if( copy_to_user((char *)uxferaddr, data, + pthru->dataxferlen) ) { + rval = (-EFAULT); + } + } + + /* + * Send the request sense data also, irrespective of + * whether the user has asked for it or not. + */ + copy_to_user(upthru->reqsensearea, + pthru->reqsensearea, 14); - if (megaCfg->flag & BOARD_40LD) - proc_printf (megaCfg, - "Controller Supports 40 Logical Drives\n"); +freemem_and_return: + if( pthru->dataxferlen ) { + pci_free_consistent(pdev, + pthru->dataxferlen, data, + data_dma_hndl); + } - if (megaCfg->flag & BOARD_64BIT) - proc_printf (megaCfg, - "Controller / Driver uses 64 bit memory addressing\n"); + pci_free_consistent(pdev, sizeof(mega_passthru), + pthru, pthru_dma_hndl); - proc_printf (megaCfg, "Base = %08x, Irq = %d, ", megaCfg->base, - megaCfg->host->irq); + free_local_pdev(pdev); - proc_printf (megaCfg, "Logical Drives = %d, Channels = %d\n", - megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent); + return rval; + } + else { + /* DCMD commands */ - proc_printf (megaCfg, "Version =%s:%s, DRAM = %dMb\n", - megaCfg->fwVer, megaCfg->biosVer, - megaCfg->productInfo.DramSize); + /* + * Is there a data transfer + */ + if( uioc.xferlen ) { + data = pci_alloc_consistent(pdev, + uioc.xferlen, &data_dma_hndl); + + if( data == NULL ) { + free_local_pdev(pdev); + return (-ENOMEM); + } - proc_printf (megaCfg, - "Controller Queue Depth = %d, Driver Queue Depth = %d\n", - megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds); - COPY_BACK; - return count; -} + uxferaddr = MBOX(uioc)->xferaddr; + } -static int proc_read_stat (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ - int i; - mega_host_config *megaCfg = (mega_host_config *) data; + /* + * Is data coming down-stream + */ + if( uioc.xferlen && (uioc.flags & UIOC_WR) ) { + /* + * Get the user data + */ + if( copy_from_user(data, (char *)uxferaddr, + uioc.xferlen) ) { - *start = page; + pci_free_consistent(pdev, + uioc.xferlen, + data, data_dma_hndl); - proc_printf (megaCfg, "Statistical Information for this controller\n"); - proc_printf (megaCfg, "Interrupts Collected = %lu\n", - megaCfg->nInterrupts); + free_local_pdev(pdev); - for (i = 0; i < megaCfg->numldrv; i++) { - proc_printf (megaCfg, "Logical Drive %d:\n", i); + return (-EFAULT); + } + } - proc_printf (megaCfg, - "\tReads Issued = %lu, Writes Issued = %lu\n", - megaCfg->nReads[i], megaCfg->nWrites[i]); + memcpy(&mc, MBOX(uioc), sizeof(megacmd_t)); - proc_printf (megaCfg, - "\tSectors Read = %lu, Sectors Written = %lu\n\n", - megaCfg->nReadBlocks[i], megaCfg->nWriteBlocks[i]); + mc.xferaddr = (u32)data_dma_hndl; - } + /* + * Issue the command + */ + mega_internal_command(adapter, LOCK_INT, &mc, NULL); - COPY_BACK; - return count; -} + rval = mega_n_to_m((void *)arg, &mc); -static int proc_read_status (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ - mega_host_config *megaCfg = (mega_host_config *) data; - *start = page; + if( rval ) { + if( uioc.xferlen ) { + pci_free_consistent(pdev, + uioc.xferlen, data, + data_dma_hndl); + } - proc_printf (megaCfg, "TBD\n"); - COPY_BACK; - return count; -} + free_local_pdev(pdev); -static int proc_read_mbox (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ + return rval; + } - mega_host_config *megaCfg = (mega_host_config *) data; - volatile mega_mailbox *mbox = megaCfg->mbox; + /* + * Is data going up-stream + */ + if( uioc.xferlen && (uioc.flags & UIOC_RD) ) { + if( copy_to_user((char *)uxferaddr, data, + uioc.xferlen) ) { - *start = page; + rval = (-EFAULT); + } + } - proc_printf (megaCfg, "Contents of Mail Box Structure\n"); - proc_printf (megaCfg, " Fw Command = 0x%02x\n", mbox->cmd); - proc_printf (megaCfg, " Cmd Sequence = 0x%02x\n", mbox->cmdid); - proc_printf (megaCfg, " No of Sectors= %04d\n", mbox->numsectors); - proc_printf (megaCfg, " LBA = 0x%02x\n", mbox->lba); - proc_printf (megaCfg, " DTA = 0x%08x\n", mbox->xferaddr); - proc_printf (megaCfg, " Logical Drive= 0x%02x\n", mbox->logdrv); - proc_printf (megaCfg, " No of SG Elmt= 0x%02x\n", mbox->numsgelements); - proc_printf (megaCfg, " Busy = %01x\n", mbox->busy); - proc_printf (megaCfg, " Status = 0x%02x\n", mbox->status); + if( uioc.xferlen ) { + pci_free_consistent(pdev, + uioc.xferlen, data, + data_dma_hndl); + } - /* proc_printf(megaCfg, "Dump of MailBox\n"); - for (i = 0; i < 16; i++) - proc_printf(megaCfg, "%02x ",*(mbox + i)); + free_local_pdev(pdev); - proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus); + return rval; + } - for (i = 0; i < 46; i++) { - proc_printf(megaCfg,"%02d ",*(mbox + 16 + i)); - if (i%16) - proc_printf(megaCfg,"\n"); + default: + return (-EINVAL); } - if (!mbox->numsgelements) { - dta = phys_to_virt(mbox->xferaddr); - for (i = 0; i < mbox->numsgelements; i++) - if (dta) { - proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i)); proc_printf(megaCfg,"Length = %08x\n", - (ulong)*(dta + i + 4)); - } - }*/ - COPY_BACK; - return count; + return 0; } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ -#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \ - S_IRUSR | S_IFREG,\ - controller_proc_dir_entry,\ - fxn, megaCfg) -#else -#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg) +/** + * mega_m_to_n() + * @arg - user address + * @uioc - new ioctl structure + * + * A thin layer to convert older mimd interface ioctl structure to NIT ioctl + * structure + * + * Converts the older mimd ioctl structure to newer NIT structure + */ +static int +mega_m_to_n(void *arg, nitioctl_t *uioc) +{ + struct uioctl_t uioc_mimd; + char signature[8] = {0}; + u8 opcode; + u8 subopcode; -static struct proc_dir_entry * -create_proc_read_entry (const char *string, - int mode, - struct proc_dir_entry *parent, - read_proc_t * fxn, mega_host_config * megaCfg) -{ - struct proc_dir_entry *temp = NULL; - - temp = kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL); - if (!temp) - return NULL; - memset (temp, 0, sizeof (struct proc_dir_entry)); - - if ((temp->name = kmalloc (strlen (string) + 1, GFP_KERNEL)) == NULL) { - kfree (temp); - return NULL; - } - - strcpy ((char *) temp->name, string); - temp->namelen = strlen (string); - temp->mode = mode; /*S_IFREG | S_IRUSR */ ; - temp->data = (void *) megaCfg; - temp->read_proc = fxn; - proc_register (parent, temp); - return temp; -} -#endif -static void mega_create_proc_entry (int index, struct proc_dir_entry *parent) -{ - u_char string[64] = { 0 }; - mega_host_config *megaCfg = megaCtlrs[index]; - struct proc_dir_entry *controller_proc_dir_entry = NULL; + /* + * check is the application conforms to NIT. We do not have to do much + * in that case. + * We exploit the fact that the signature is stored in the very + * begining of the structure. + */ - sprintf (string, "%d", index); + if( copy_from_user(signature, (char *)arg, 7) ) + return (-EFAULT); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ - controller_proc_dir_entry = - megaCfg->controller_proc_dir_entry = proc_mkdir (string, parent); -#else - controller_proc_dir_entry = - megaCfg->controller_proc_dir_entry = - create_proc_entry (string, S_IFDIR | S_IRUGO | S_IXUGO, parent); -#endif + if( memcmp(signature, "MEGANIT", 7) == 0 ) { - if (!controller_proc_dir_entry) - printk ("\nmegaraid: proc_mkdir failed\n"); - else { - megaCfg->proc_read = - CREATE_READ_PROC ("config", proc_read_config); - megaCfg->proc_status = - CREATE_READ_PROC ("status", proc_read_status); - megaCfg->proc_stat = CREATE_READ_PROC ("stat", proc_read_stat); - megaCfg->proc_mbox = - CREATE_READ_PROC ("mailbox", proc_read_mbox); + /* + * NOTE NOTE: The nit ioctl is still under flux because of + * change of mailbox definition, in HPE. No applications yet + * use this interface and let's not have applications use this + * interface till the new specifitions are in place. + */ + return -EINVAL; +#if 0 + if( copy_from_user(uioc, (char *)arg, sizeof(nitioctl_t)) ) + return (-EFAULT); + return 0; +#endif } -} -#endif /* CONFIG_PROC_FS */ + /* + * Else assume we have mimd uioctl_t as arg. Convert to nitioctl_t + * + * Get the user ioctl structure + */ + if( copy_from_user(&uioc_mimd, (char *)arg, sizeof(struct uioctl_t)) ) + return (-EFAULT); -/*------------------------------------------------------------- - * Return the disk geometry for a particular disk - * Input: - * Disk *disk - Disk geometry - * struct block_device *dev - Device node - * int *geom - Returns geometry fields - * geom[0] = heads - * geom[1] = sectors - * geom[2] = cylinders - *-------------------------------------------------------------*/ -int megaraid_biosparam (struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int *geom) -{ - int heads, sectors, cylinders; - mega_host_config *megaCfg; - /* Get pointer to host config structure */ - megaCfg = (mega_host_config *) sdev->host->hostdata; + /* + * Get the opcode and subopcode for the commands + */ + opcode = uioc_mimd.ui.fcs.opcode; + subopcode = uioc_mimd.ui.fcs.subopcode; - if( IS_RAID_CH(sdev->channel)) { - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; - cylinders = (unsigned long)capacity >> 11; + switch (opcode) { + case 0x82: - /* Handle extended translation size for logical drives > 1Gb */ - if (capacity >= 0x200000) { - heads = 255; - sectors = 63; - cylinders = (unsigned long)capacity / (heads * sectors); - } + switch (subopcode) { - /* return result */ - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } - else { - if( mega_partsize(bdev, capacity, geom) == 0 ) return 0; + case MEGAIOC_QDRVRVER: /* Query driver version */ + uioc->opcode = GET_DRIVER_VER; + uioc->uioc_uaddr = uioc_mimd.data; + break; - printk(KERN_WARNING - "megaraid: invalid partition on this disk on channel %d\n", - sdev->channel); + case MEGAIOC_QNADAP: /* Get # of adapters */ + uioc->opcode = GET_N_ADAP; + uioc->uioc_uaddr = uioc_mimd.data; + break; - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; - cylinders = capacity >> 11; + case MEGAIOC_QADAPINFO: /* Get adapter information */ + uioc->opcode = GET_ADAP_INFO; + uioc->adapno = uioc_mimd.ui.fcs.adapno; + uioc->uioc_uaddr = uioc_mimd.data; + break; - /* Handle extended translation size for logical drives > 1Gb */ - if (capacity >= 0x200000) { - heads = 255; - sectors = 63; - cylinders = (unsigned long)capacity / (heads * sectors); + default: + return(-EINVAL); } - /* return result */ - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } + break; - return 0; -} -/* - * Function : static int mega_partsize(Disk * disk, struct block_device *bdev, int *geom) - * - * Purpose : to determine the BIOS mapping used to create the partition - * table, storing the results (cyls, hds, and secs) in geom - * - * Note: Code is picked from scsicam.h - * - * Returns : -1 on failure, 0 on success. - */ -static int -mega_partsize(struct block_device *bdev, sector_t capacity, int *geom) -{ - struct partition *p, *largest = NULL; - int i, largest_cyl; - int heads, cyls, sectors; - unsigned char *buf; + case 0x81: - if (!(buf = scsi_bios_ptable(bdev))) - return -1; + uioc->opcode = MBOX_CMD; + uioc->adapno = uioc_mimd.ui.fcs.adapno; - if( *(unsigned short *)(buf + 64) == 0xAA55 ) { + memcpy(uioc->uioc_rmbox, uioc_mimd.mbox, 18); - for( largest_cyl = -1, p = (struct partition *)buf, - i = 0; i < 4; ++i, ++p) { + uioc->xferlen = uioc_mimd.ui.fcs.length; - if (!p->sys_ind) continue; + if( uioc_mimd.outlen ) uioc->flags = UIOC_RD; + if( uioc_mimd.inlen ) uioc->flags |= UIOC_WR; - cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2); + break; - if(cyls >= largest_cyl) { - largest_cyl = cyls; - largest = p; - } - } - } + case 0x80: - if (largest) { - heads = largest->end_head + 1; - sectors = largest->end_sector & 0x3f; + uioc->opcode = MBOX_CMD; + uioc->adapno = uioc_mimd.ui.fcs.adapno; - if (heads == 0 || sectors == 0) { - kfree(buf); - return -1; - } + memcpy(uioc->uioc_rmbox, uioc_mimd.mbox, 18); - cyls = (unsigned long)capacity/(heads * sectors); + /* + * Choose the xferlen bigger of input and output data + */ + uioc->xferlen = uioc_mimd.outlen > uioc_mimd.inlen ? + uioc_mimd.outlen : uioc_mimd.inlen; - geom[0] = heads; - geom[1] = sectors; - geom[2] = cyls; + if( uioc_mimd.outlen ) uioc->flags = UIOC_RD; + if( uioc_mimd.inlen ) uioc->flags |= UIOC_WR; + + break; + + default: + return (-EINVAL); - kfree(buf); - return 0; } - kfree(buf); - return -1; + return 0; } - /* - * This routine will be called when the use has done a forced shutdown on the - * system. Flush the Adapter cache, that's the most we can do. + * mega_n_to_m() + * @arg - user address + * @mc - mailbox command + * + * Updates the status information to the application, depending on application + * conforms to older mimd ioctl interface or newer NIT ioctl interface */ -static int megaraid_reboot_notify (struct notifier_block *this, unsigned long code, - void *unused) +static int +mega_n_to_m(void *arg, megacmd_t *mc) { - struct Scsi_Host *pSHost; - mega_host_config *megaCfg; - mega_mailbox *mbox; - u_char mboxData[16]; - int i; - - if (code == SYS_DOWN || code == SYS_HALT) { - for (i = 0; i < numCtlrs; i++) { - pSHost = megaCtlrs[i]->host; - - megaCfg = (mega_host_config *) pSHost->hostdata; - mbox = (mega_mailbox *) mboxData; - - /* Flush cache to disk */ - memset (mbox, 0, 16); - mboxData[0] = 0xA; + nitioctl_t *uiocp; + megacmd_t *umc; + mega_passthru *upthru; + struct uioctl_t *uioc_mimd; + char signature[8] = {0}; - /* - * Free irq, otherwise extra interrupt is generated - */ - free_irq (megaCfg->host->irq, megaCfg); + /* + * check is the application conforms to NIT. + */ + if( copy_from_user(signature, (char *)arg, 7) ) + return -EFAULT; - /* - * Issue a blocking (interrupts disabled) command to - * the card - */ - megaIssueCmd (megaCfg, mboxData, NULL, 0); - } - } - return NOTIFY_DONE; -} + if( memcmp(signature, "MEGANIT", 7) == 0 ) { -static int mega_init_scb (mega_host_config * megacfg) -{ - int idx; + uiocp = (nitioctl_t *)arg; -#if DEBUG - if (megacfg->max_cmds >= MAX_COMMANDS) { - printk ("megaraid:ctlr max cmds = %x : MAX_CMDS = %x", - megacfg->max_cmds, MAX_COMMANDS); - } -#endif + if( put_user(mc->status, (u8 *)&MBOX_P(uiocp)->status) ) + return (-EFAULT); - for (idx = megacfg->max_cmds - 1; idx >= 0; idx--) { + if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) { - megacfg->scbList[idx].idx = idx; + umc = MBOX_P(uiocp); - /* - * ISR will make this flag zero to indicate the command has been - * completed. This is only for user ioctl calls. Rest of the driver - * and the mid-layer operations are not connected with this flag. - */ + upthru = (mega_passthru *)umc->xferaddr; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megacfg->scbList[idx].sgList = - pci_alloc_consistent (megacfg->dev, - sizeof (mega_64sglist) * MAX_SGLIST, - &(megacfg->scbList[idx]. - dma_sghandle64)); + if( put_user(mc->status, (u8 *)&upthru->scsistatus) ) + return (-EFAULT); + } + } + else { + uioc_mimd = (struct uioctl_t *)arg; - megacfg->scbList[idx].sg64List = - (mega_64sglist *) megacfg->scbList[idx].sgList; -#else - megacfg->scbList[idx].sgList = kmalloc (sizeof (mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA); -#endif + if( put_user(mc->status, (u8 *)&uioc_mimd->mbox[17]) ) + return (-EFAULT); - if (megacfg->scbList[idx].sgList == NULL) { - printk (KERN_WARNING - "Can't allocate sglist for id %d\n", idx); - mega_freeSgList (megacfg); - return -1; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megacfg->scbList[idx].pthru = pci_alloc_consistent (megacfg->dev, - sizeof (mega_passthru), - &(megacfg->scbList[idx]. - dma_passthruhandle64)); - - if (megacfg->scbList[idx].pthru == NULL) { - printk (KERN_WARNING - "Can't allocate passthru for id %d\n", idx); - } - - megacfg->scbList[idx].epthru = - pci_alloc_consistent( - megacfg->dev, sizeof(mega_ext_passthru), - &(megacfg->scbList[idx].dma_ext_passthruhandle64) - ); - - if (megacfg->scbList[idx].epthru == NULL) { - printk (KERN_WARNING - "Can't allocate extended passthru for id %d\n", idx); - } - /* - * Allocate a 256 Byte Bounce Buffer for handling INQ/RD_CAPA - */ - megacfg->scbList[idx].bounce_buffer = pci_alloc_consistent (megacfg->dev, - 256, - &(megacfg->scbList[idx]. - dma_bounce_buffer)); - - if (!megacfg->scbList[idx].bounce_buffer) - printk - ("megaraid: allocation for bounce buffer failed\n"); + if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) { - megacfg->scbList[idx].dma_type = M_RD_DMA_TYPE_NONE; -#endif + umc = (megacmd_t *)uioc_mimd->mbox; - if (idx < MAX_COMMANDS) { - /* - * Link to free list - * lock not required since we are loading the driver, so no - * commands possible right now. - */ - enq_scb_freelist (megacfg, &megacfg->scbList[idx], - NO_LOCK, INTR_ENB); + upthru = (mega_passthru *)umc->xferaddr; + if( put_user(mc->status, (u8 *)&upthru->scsistatus) ) + return (-EFAULT); } } return 0; } + /* - * Enqueues a SCB + * MEGARAID 'FW' commands. */ -static void enq_scb_freelist (mega_host_config * megacfg, mega_scb * scb, int lock, - int intr) -{ - if (lock == INTERNAL_LOCK || intr == INTR_DIS) { - if (intr == INTR_DIS) - spin_lock_irq (&megacfg->lock_free); - else - spin_lock (&megacfg->lock_free); - } +/** + * mega_is_bios_enabled() + * @adapter - pointer to our soft state + * + * issue command to find out if the BIOS is enabled for this controller + */ +static int +mega_is_bios_enabled(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; + int ret; - scb->state = SCB_FREE; - scb->SCpnt = NULL; + mbox = (mbox_t *)raw_mbox; - if (megacfg->qFreeH == (mega_scb *) NULL) { - megacfg->qFreeH = megacfg->qFreeT = scb; - } else { - megacfg->qFreeT->next = scb; - megacfg->qFreeT = scb; - } + memset(mbox, 0, sizeof(mbox)); - megacfg->qFreeT->next = NULL; - megacfg->qFcnt++; + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - if (lock == INTERNAL_LOCK || intr == INTR_DIS) { - if (intr == INTR_DIS) - spin_unlock_irq (&megacfg->lock_free); - else - spin_unlock (&megacfg->lock_free); - } -} + mbox->xferaddr = (u32)adapter->buf_dma_handle; -static int megadev_ioctl_entry (struct inode *inode, struct file *filep, - unsigned int cmd, unsigned long arg) -{ - int ret = -1; + raw_mbox[0] = IS_BIOS_ENABLED; + raw_mbox[2] = GET_BIOS; - /* - * We do not allow parallel ioctls to the driver as of now. - */ - down (&mimd_entry_mtx); - ret = megadev_ioctl (inode, filep, cmd, arg); - up (&mimd_entry_mtx); - return ret; + ret = issue_scb_block(adapter, raw_mbox); + return *(char *)adapter->mega_buffer; } -static int megadev_ioctl (struct inode *inode, struct file *filep, - unsigned int cmd, unsigned long arg) + +/** + * mega_enum_raid_scsi() + * @adapter - pointer to our soft state + * + * Find out what channels are RAID/SCSI. This information is used to + * differentiate the virtual channels and physical channels and to support + * ROMB feature and non-disk devices. + */ +static void +mega_enum_raid_scsi(adapter_t *adapter) { - int adapno; - u32 inlen; - struct uioctl_t ioc; - char *kvaddr = NULL; - int nadap = numCtlrs; - u8 opcode; - u32 outlen; - int ret; - u8 subopcode; - Scsi_Cmnd *scsicmd; - struct Scsi_Host *shpnt; - char *uaddr; - struct uioctl_t *uioc; - dma_addr_t dma_addr; - u32 length; - mega_host_config *megacfg = NULL; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ - struct pci_dev pdev; - struct pci_dev *pdevp = &pdev; -#else - char *pdevp = NULL; -#endif - IO_LOCK_T; + unsigned char raw_mbox[16]; + mbox_t *mbox; + int i; - if (!inode) - return -EINVAL; + mbox = (mbox_t *)raw_mbox; - if (_IOC_TYPE (cmd) != MEGAIOC_MAGIC) - return (-EINVAL); + memset(mbox, 0, sizeof(mbox)); /* - * Get the user ioctl structure + * issue command to find out what channels are raid/scsi */ - ret = verify_area (VERIFY_WRITE, (char *) arg, sizeof (struct uioctl_t)); + raw_mbox[0] = CHNL_CLASS; + raw_mbox[2] = GET_CHNL_CLASS; - if (ret) - return ret; + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - if(copy_from_user (&ioc, (char *) arg, sizeof (struct uioctl_t))) - return -EFAULT; + mbox->xferaddr = (u32)adapter->buf_dma_handle; /* - * The first call the applications should make is to find out the - * number of controllers in the system. The next logical call should - * be for getting the list of controllers in the system as detected - * by the driver. + * Non-ROMB firware fail this command, so all channels + * must be shown RAID */ + adapter->mega_ch_class = 0xFF; - /* - * Get the opcode and subopcode for the commands - */ - opcode = ioc.ui.fcs.opcode; - subopcode = ioc.ui.fcs.subopcode; + if(!issue_scb_block(adapter, raw_mbox)) { + adapter->mega_ch_class = *((char *)adapter->mega_buffer); - switch (opcode) { - case M_RD_DRIVER_IOCTL_INTERFACE: - switch (subopcode) { - case MEGAIOC_QDRVRVER: /* Query driver version */ - put_user (driver_ver, (u32 *) ioc.data); - return 0; + } - case MEGAIOC_QNADAP: /* Get # of adapters */ - put_user (nadap, (int *) ioc.data); - return nadap; + for( i = 0; i < adapter->product_info.nchannels; i++ ) { + if( (adapter->mega_ch_class >> i) & 0x01 ) { + printk(KERN_INFO "megaraid: channel[%d] is raid.\n", + i); + } + else { + printk(KERN_INFO "megaraid: channel[%d] is scsi.\n", + i); + } + } - case MEGAIOC_QADAPINFO: /* Get adapter information */ - /* - * which adapter? - */ - adapno = ioc.ui.fcs.adapno; + return; +} - /* - * The adapter numbers do not start with 0, at least in - * the user space. This is just to make sure, 0 is not the - * default value which will refer to adapter 1. So the - * user needs to make use of macros MKADAP() and GETADAP() - * (See megaraid.h) while making ioctl() call. - */ - adapno = GETADAP (adapno); - if (adapno >= numCtlrs) - return (-ENODEV); +/** + * mega_get_boot_drv() + * @adapter - pointer to our soft state + * + * Find out which device is the boot device. Note, any logical drive or any + * phyical device (e.g., a CDROM) can be designated as a boot device. + */ +static void +mega_get_boot_drv(adapter_t *adapter) +{ + struct private_bios_data *prv_bios_data; + unsigned char raw_mbox[16]; + mbox_t *mbox; + u16 cksum = 0; + u8 *cksum_p; + u8 boot_pdrv; + int i; - ret = verify_area (VERIFY_WRITE, - ioc.data, - sizeof (struct mcontroller)); - if (ret) - return ret; + mbox = (mbox_t *)raw_mbox; - /* - * Copy struct mcontroller to user area - */ - copy_to_user (ioc.data, - mcontroller + adapno, - sizeof (struct mcontroller)); - return 0; + memset(mbox, 0, sizeof(raw_mbox)); - default: - return (-EINVAL); + raw_mbox[0] = BIOS_PVT_DATA; + raw_mbox[2] = GET_BIOS_PVT_DATA; - } /* inner switch */ - break; + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - case M_RD_IOCTL_CMD_NEW: + mbox->xferaddr = (u32)adapter->buf_dma_handle; - /* - * Deletion of logical drives is only handled in 0x80 commands - */ - if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { - return -EINVAL; - } + adapter->boot_ldrv_enabled = 0; + adapter->boot_ldrv = 0; - /* which adapter? */ - adapno = ioc.ui.fcs.adapno; + adapter->boot_pdrv_enabled = 0; + adapter->boot_pdrv_ch = 0; + adapter->boot_pdrv_tgt = 0; - /* See comment above: MEGAIOC_QADAPINFO */ - adapno = GETADAP(adapno); + if(issue_scb_block(adapter, raw_mbox) == 0) { + prv_bios_data = + (struct private_bios_data *)adapter->mega_buffer; - if (adapno >= numCtlrs) - return(-ENODEV); + cksum = 0; + cksum_p = (char *)prv_bios_data; + for (i = 0; i < 14; i++ ) { + cksum += (u16)(*cksum_p++); + } - length = ioc.ui.fcs.length; + if (prv_bios_data->cksum == (u16)(0-cksum) ) { - /* Check for zero length buffer or very large buffers */ - if( !length || length > 32*1024 ) - return -EINVAL; + /* + * If MSB is set, a physical drive is set as boot + * device + */ + if( prv_bios_data->boot_drv & 0x80 ) { + adapter->boot_pdrv_enabled = 1; + boot_pdrv = prv_bios_data->boot_drv & 0x7F; + adapter->boot_pdrv_ch = boot_pdrv / 16; + adapter->boot_pdrv_tgt = boot_pdrv % 16; + } + else { + adapter->boot_ldrv_enabled = 1; + adapter->boot_ldrv = prv_bios_data->boot_drv; + } + } + } - /* save the user address */ - uaddr = ioc.ui.fcs.buffer; +} - /* - * For M_RD_IOCTL_CMD_NEW commands, the fields outlen and inlen of - * uioctl_t structure are treated as flags. If outlen is 1, the - * data is transferred from the device and if inlen is 1, the data - * is transferred to the device. - */ - outlen = ioc.outlen; - inlen = ioc.inlen; +/** + * mega_support_random_del() + * @adapter - pointer to our soft state + * + * Find out if this controller supports random deletion and addition of + * logical drives + */ +static int +mega_support_random_del(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; + int rval; - if(outlen) { - ret = verify_area(VERIFY_WRITE, (char *)ioc.ui.fcs.buffer, length); - if (ret) return ret; - } - if(inlen) { - ret = verify_area(VERIFY_READ, (char *) ioc.ui.fcs.buffer, length); - if (ret) return ret; - } + mbox = (mbox_t *)raw_mbox; - /* - * Find this host - */ - shpnt = megaCtlrs[adapno]->host; - if(shpnt == NULL) return -ENODEV; + memset(mbox, 0, sizeof(mbox)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA); -#else - scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd), - GFP_ATOMIC | GFP_DMA); -#endif - if(scsicmd == NULL) return -ENOMEM; + /* + * issue command + */ + raw_mbox[0] = FC_DEL_LOGDRV; + raw_mbox[2] = OP_SUP_DEL_LOGDRV; - memset(scsicmd, 0, sizeof(Scsi_Cmnd)); - scsicmd->device->host = shpnt; + rval = issue_scb_block(adapter, raw_mbox); - if( outlen || inlen ) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pdevp = &pdev; - memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev)); - pdevp->dma_mask = 0xffffffff; -#else - pdevp = NULL; -#endif - kvaddr = dma_alloc_consistent(pdevp, length, &dma_addr); + return !rval; +} - if( kvaddr == NULL ) { - printk(KERN_WARNING "megaraid:allocation failed\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ - kfree(scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif - return -ENOMEM; - } - ioc.ui.fcs.buffer = kvaddr; +/** + * mega_support_ext_cdb() + * @adapter - pointer to our soft state + * + * Find out if this firmware support cdblen > 10 + */ +static int +mega_support_ext_cdb(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; + int rval; - if (inlen) { - /* copyin the user data */ - copy_from_user(kvaddr, (char *)uaddr, length ); - } - } + mbox = (mbox_t *)raw_mbox; - scsicmd->cmnd[0] = MEGADEVIOC; - scsicmd->request_buffer = (void *)&ioc; + memset(mbox, 0, sizeof (mbox)); + /* + * issue command to find out if controller supports extended CDBs. + */ + raw_mbox[0] = 0xA4; + raw_mbox[2] = 0x16; - init_MUTEX_LOCKED(&mimd_ioctl_sem); + rval = issue_scb_block(adapter, raw_mbox); - IO_LOCK(shpnt); - megaraid_queue(scsicmd, megadev_ioctl_done); + return !rval; +} - IO_UNLOCK(shpnt); - down(&mimd_ioctl_sem); +/** + * mega_del_logdrv() + * @adapter - pointer to our soft state + * @logdrv - logical drive to be deleted + * + * Delete the specified logical drive. It is the responsibility of the user + * app to let the OS know about this operation. + */ +static int +mega_del_logdrv(adapter_t *adapter, int logdrv) +{ + DECLARE_WAIT_QUEUE_HEAD(wq); + unsigned long flags; + scb_t *scb; + int rval; - if( !scsicmd->result && outlen ) { - copy_to_user(uaddr, kvaddr, length); - } + /* + * Stop sending commands to the controller, queue them internally. + * When deletion is complete, ISR will flush the queue. + */ + atomic_set(&adapter->quiescent, 1); - /* - * copyout the result - */ - uioc = (struct uioctl_t *)arg; + /* + * Wait till all the issued commands are complete and there are no + * commands in the pending queue + */ + while( atomic_read(&adapter->pend_cmds) > 0 || + !list_empty(&adapter->pending_list) ) { - if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) { - put_user( scsicmd->result, &uioc->pthru.scsistatus ); - } else { - put_user(1, &uioc->mbox[16]); /* numstatus */ - /* status */ - put_user (scsicmd->result, &uioc->mbox[17]); - } + sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */ + } - if (kvaddr) { - dma_free_consistent(pdevp, length, kvaddr, dma_addr); - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ - kfree (scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif + rval = mega_do_del_logdrv(adapter, logdrv); - /* restore the user address */ - ioc.ui.fcs.buffer = uaddr; + spin_lock_irqsave(&adapter->lock, flags); - return ret; + /* + * If delete operation was successful, add 0x80 to the logical drive + * ids for commands in the pending queue. + */ + if (adapter->read_ldidmap) { + struct list_head *pos; + list_for_each(pos, &adapter->pending_list) { + scb = list_entry(pos, scb_t, list); + if (scb->pthru->logdrv < 0x80 ) + scb->pthru->logdrv += 0x80; + } + } - case M_RD_IOCTL_CMD: - /* which adapter? */ - adapno = ioc.ui.fcs.adapno; + atomic_set(&adapter->quiescent, 0); - /* See comment above: MEGAIOC_QADAPINFO */ - adapno = GETADAP (adapno); + mega_runpendq(adapter); - if (adapno >= numCtlrs) - return (-ENODEV); + spin_unlock_irqrestore(&adapter->lock, flags); - /* save the user address */ - uaddr = ioc.data; - outlen = ioc.outlen; - inlen = ioc.inlen; + return rval; +} - if ((outlen >= IOCTL_MAX_DATALEN) || (inlen >= IOCTL_MAX_DATALEN)) - return (-EINVAL); - if (outlen) { - ret = verify_area (VERIFY_WRITE, ioc.data, outlen); - if (ret) return ret; - } - if (inlen) { - ret = verify_area (VERIFY_READ, ioc.data, inlen); - if (ret) return ret; - } +static int +mega_do_del_logdrv(adapter_t *adapter, int logdrv) +{ + megacmd_t mc; + int rval; - /* - * Find this host - */ - shpnt = megaCtlrs[adapno]->host; - if(shpnt == NULL) return -ENODEV; + memset( &mc, 0, sizeof(megacmd_t)); - /* - * ioctls for deleting logical drives is a special case, so check - * for it first - */ - if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { + mc.cmd = FC_DEL_LOGDRV; + mc.opcode = OP_DEL_LOGDRV; + mc.subopcode = logdrv; - if( !megacfg->support_random_del ) { - printk("megaraid: logdrv delete on non supporting f/w.\n"); - return -EINVAL; - } + rval = mega_internal_command(adapter, LOCK_INT, &mc, NULL); - uioc = (struct uioctl_t *)arg; + /* log this event */ + if(rval) { + printk(KERN_WARNING "megaraid: Delete LD-%d failed.", logdrv); + return rval; + } - ret = mega_del_logdrv(megacfg, ioc.mbox[3]); + /* + * After deleting first logical drive, the logical drives must be + * addressed by adding 0x80 to the logical drive id. + */ + adapter->read_ldidmap = 1; - put_user(1, &uioc->mbox[16]); /* numstatus */ - put_user(ret, &uioc->mbox[17]); /* status */ + return rval; +} - /* if deletion failed, let the user know by failing ioctl */ - return ret; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA); -#else - scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd), - GFP_ATOMIC | GFP_DMA); -#endif - if(scsicmd == NULL) return -ENOMEM; +/** + * mega_get_max_sgl() + * @adapter - pointer to our soft state + * + * Find out the maximum number of scatter-gather elements supported by this + * version of the firmware + */ +static void +mega_get_max_sgl(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; - memset(scsicmd, 0, sizeof(Scsi_Cmnd)); - scsicmd->device->host = shpnt; + mbox = (mbox_t *)raw_mbox; - if (outlen || inlen) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pdevp = &pdev; - memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev)); - pdevp->dma_mask = 0xffffffff; -#else - pdevp = NULL; -#endif - /* - * Allocate a page of kernel space. - */ - kvaddr = dma_alloc_consistent(pdevp, PAGE_SIZE, &dma_addr); + memset(mbox, 0, sizeof(raw_mbox)); - if( kvaddr == NULL ) { - printk (KERN_WARNING "megaraid:allocation failed\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ - kfree(scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif - return -ENOMEM; - } + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - ioc.data = kvaddr; + mbox->xferaddr = (u32)adapter->buf_dma_handle; - if (inlen) { - if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { - /* copyin the user data */ - copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen); - } else { - copy_from_user (kvaddr, uaddr, inlen); - } - } - } + raw_mbox[0] = MAIN_MISC_OPCODE; + raw_mbox[2] = GET_MAX_SG_SUPPORT; - scsicmd->cmnd[0] = MEGADEVIOC; - scsicmd->request_buffer = (void *) &ioc; - init_MUTEX_LOCKED (&mimd_ioctl_sem); + if( issue_scb_block(adapter, raw_mbox) ) { + /* + * f/w does not support this command. Choose the default value + */ + adapter->sglen = MIN_SGLIST; + } + else { + adapter->sglen = *((char *)adapter->mega_buffer); + + /* + * Make sure this is not more than the resources we are + * planning to allocate + */ + if ( adapter->sglen > MAX_SGLIST ) + adapter->sglen = MAX_SGLIST; + } - IO_LOCK(shpnt); - megaraid_queue (scsicmd, megadev_ioctl_done); + return; +} - IO_UNLOCK(shpnt); - down (&mimd_ioctl_sem); - if (!scsicmd->result && outlen) { - if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { - copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen); - } else { - copy_to_user (uaddr, kvaddr, outlen); - } - } +/** + * mega_support_cluster() + * @adapter - pointer to our soft state + * + * Find out if this firmware support cluster calls. + */ +static int +mega_support_cluster(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; - /* - * copyout the result - */ - uioc = (struct uioctl_t *) arg; + mbox = (mbox_t *)raw_mbox; - if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { - put_user (scsicmd->result, &uioc->pthru.scsistatus); - } else { - put_user (1, &uioc->mbox[16]); /* numstatus */ - put_user (scsicmd->result, &uioc->mbox[17]); /* status */ - } + memset(mbox, 0, sizeof(raw_mbox)); - if (kvaddr) { - dma_free_consistent(pdevp, PAGE_SIZE, kvaddr, dma_addr ); - } + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - kfree (scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif + mbox->xferaddr = (u32)adapter->buf_dma_handle; - /* restore user pointer */ - ioc.data = uaddr; + /* + * Try to get the initiator id. This command will succeed iff the + * clustering is available on this HBA. + */ + raw_mbox[0] = MEGA_GET_TARGET_ID; - return ret; + if( issue_scb_block(adapter, raw_mbox) == 0 ) { - default: - return (-EINVAL); + /* + * Cluster support available. Get the initiator target id. + * Tell our id to mid-layer too. + */ + adapter->this_id = *(u32 *)adapter->mega_buffer; + adapter->host->this_id = adapter->this_id; - }/* Outer switch */ + return 1; + } return 0; } -static void -megadev_ioctl_done(Scsi_Cmnd *sc) -{ - up (&mimd_ioctl_sem); -} -static mega_scb * -megadev_doioctl (mega_host_config * megacfg, Scsi_Cmnd * sc) -{ - u8 cmd; - struct uioctl_t *ioc = NULL; - mega_mailbox *mbox = NULL; - mega_ioctl_mbox *mboxioc = NULL; - struct mbox_passthru *mboxpthru = NULL; - mega_scb *scb = NULL; - mega_passthru *pthru = NULL; - if ((scb = mega_allocateSCB (megacfg, sc)) == NULL) { - sc->result = (DID_ERROR << 16); - callDone (sc); - return NULL; - } +/** + * mega_get_ldrv_num() + * @adapter - pointer to our soft state + * @cmd - scsi mid layer command + * @channel - channel on the controller + * + * Calculate the logical drive number based on the information in scsi command + * and the channel number. + */ +static inline int +mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel) +{ + int tgt; + int ldrv_num; - ioc = (struct uioctl_t *) sc->request_buffer; + tgt = cmd->device->id; + + if ( tgt > adapter->this_id ) + tgt--; /* we do not get inquires for initiator id */ - memcpy (scb->mboxData, ioc->mbox, sizeof (scb->mboxData)); + ldrv_num = (channel * 15) + tgt; - /* The generic mailbox */ - mbox = (mega_mailbox *) ioc->mbox; /* - * Get the user command + * If we have a logical drive with boot enabled, project it first */ - cmd = ioc->mbox[0]; - - switch (cmd) { - case MEGA_MBOXCMD_PASSTHRU: - /* - * prepare the SCB with information from the user ioctl structure - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = scb->pthru; -#else - pthru = &scb->pthru; -#endif - memcpy (pthru, &ioc->pthru, sizeof (mega_passthru)); - mboxpthru = (struct mbox_passthru *) scb->mboxData; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if (megacfg->flag & BOARD_64BIT) { - /* This is just a sample with one element - * This if executes onlu on 2.4 kernels - */ - mboxpthru->dataxferaddr = scb->dma_passthruhandle64; - scb->sg64List[0].address = - pci_map_single (megacfg->dev, - ioc->data, - 4096, PCI_DMA_BIDIRECTIONAL); - scb->sg64List[0].length = 4096; // TODO: Check this - pthru->dataxferaddr = scb->dma_sghandle64; - pthru->numsgelements = 1; - mboxpthru->cmd = 0xC3; - } else { - mboxpthru->dataxferaddr = scb->dma_passthruhandle64; - pthru->dataxferaddr = - pci_map_single (megacfg->dev, - ioc->data, - 4096, PCI_DMA_BIDIRECTIONAL); - pthru->numsgelements = 0; + if( adapter->boot_ldrv_enabled ) { + if( ldrv_num == 0 ) { + ldrv_num = adapter->boot_ldrv; } - -#else - { - mboxpthru->dataxferaddr = virt_to_bus (&scb->pthru); - pthru->dataxferaddr = virt_to_bus (ioc->data); - pthru->numsgelements = 0; + else { + if( ldrv_num <= adapter->boot_ldrv ) { + ldrv_num--; + } } -#endif - - pthru->reqsenselen = 14; - break; + } - default: /* Normal command */ - mboxioc = (mega_ioctl_mbox *) scb->mboxData; + /* + * If "delete logical drive" feature is enabled on this controller. + * Do only if at least one delete logical drive operation was done. + * + * Also, after logical drive deletion, instead of logical drive number, + * the value returned should be 0x80+logical drive id. + * + * These is valid only for IO commands. + */ - if (ioc->ui.fcs.opcode == M_RD_IOCTL_CMD_NEW) { - scb->buff_ptr = ioc->ui.fcs.buffer; - scb->iDataSize = ioc->ui.fcs.length; - } else { - scb->buff_ptr = ioc->data; - scb->iDataSize = 4096; // TODO:check it + if (adapter->support_random_del && adapter->read_ldidmap ) + switch (cmd->cmnd[0]) { + case READ_6: /* fall through */ + case WRITE_6: /* fall through */ + case READ_10: /* fall through */ + case WRITE_10: + ldrv_num += 0x80; } - set_mbox_xfer_addr (megacfg, scb, mboxioc, FROMTO_DEVICE); - mboxioc->numsgelements = 0; - break; - } - - return scb; + return ldrv_num; } + +/** + * mega_adapinq() + * @adapter - pointer to our soft state + * @dma_handle - DMA address of the buffer + * + * Issue internal comamnds while interrupts are available. + * We only issue direct mailbox commands from within the driver. ioctl() + * interface using these routines can issue passthru commands. + */ static int -mega_support_ext_cdb(mega_host_config *this_hba) +mega_adapinq(adapter_t *adapter, dma_addr_t dma_handle) { - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int ret; + megacmd_t mc; - mboxpnt = (mega_mailbox *) mbox; + memset(&mc, 0, sizeof(megacmd_t)); - memset(mbox, 0, sizeof (mbox)); - /* - * issue command to find out if controller supports extended CDBs. - */ - mbox[0] = 0xA4; - mbox[2] = 0x16; + if( adapter->flag & BOARD_40LD ) { + mc.cmd = FC_NEW_CONFIG; + mc.opcode = NC_SUBOP_ENQUIRY3; + mc.subopcode = ENQ3_GET_SOLICITED_FULL; + } + else { + mc.cmd = MEGA_MBOXCMD_ADPEXTINQ; + } + + mc.xferaddr = (u32)dma_handle; - ret = megaIssueCmd(this_hba, mbox, NULL, 0); + if ( mega_internal_command(adapter, LOCK_INT, &mc, NULL) != 0 ) { + return -1; + } - return !ret; + return 0; } -/* - * Find out if this controller supports random deletion and addition of - * logical drives +/** + * mega_allocate_inquiry() + * @dma_handle - handle returned for dma address + * @pdev - handle to pci device + * + * allocates memory for inquiry structure */ -static int -mega_support_random_del(mega_host_config *this_hba) +static inline caddr_t +mega_allocate_inquiry(dma_addr_t *dma_handle, struct pci_dev *pdev) { - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int ret; - - mboxpnt = (mega_mailbox *)mbox; - - memset(mbox, 0, sizeof(mbox)); - - /* - * issue command - */ - mbox[0] = FC_DEL_LOGDRV; - mbox[2] = OP_SUP_DEL_LOGDRV; + return pci_alloc_consistent(pdev, sizeof(mega_inquiry3), dma_handle); +} - ret = megaIssueCmd(this_hba, mbox, NULL, 0); - return !ret; +static inline void +mega_free_inquiry(caddr_t inquiry, dma_addr_t dma_handle, struct pci_dev *pdev) +{ + pci_free_consistent(pdev, sizeof(mega_inquiry3), inquiry, dma_handle); } + +/** mega_internal_dev_inquiry() + * @adapter - pointer to our soft state + * @ch - channel for this device + * @tgt - ID of this device + * @buf_dma_handle - DMA address of the buffer + * + * Issue the scsi inquiry for the specified device. + */ static int -mega_del_logdrv(mega_host_config *this_hba, int logdrv) +mega_internal_dev_inquiry(adapter_t *adapter, u8 ch, u8 tgt, + dma_addr_t buf_dma_handle) { + mega_passthru *pthru; + dma_addr_t pthru_dma_handle; + megacmd_t mc; int rval; - IO_LOCK_T; - DECLARE_WAIT_QUEUE_HEAD(wq); - mega_scb *scbp; + struct pci_dev *pdev; + /* - * Stop sending commands to the controller, queue them internally. - * When deletion is complete, ISR will flush the queue. + * For all internal commands, the buffer must be allocated in <4GB + * address range */ - IO_LOCK(this_hba->host); - this_hba->quiescent = 1; - IO_UNLOCK(this_hba->host); + if( make_local_pdev(adapter, &pdev) != 0 ) return -1; - while( this_hba->qPcnt ) { - sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */ - } - rval = mega_do_del_logdrv(this_hba, logdrv); + pthru = pci_alloc_consistent(pdev, sizeof(mega_passthru), + &pthru_dma_handle); - IO_LOCK(this_hba->host); - /* - * Attach the internal queue to the pending queue - */ - if( this_hba->qPendingH == NULL ) { - /* - * If pending queue head is null, make internal queue as - * pending queue - */ - this_hba->qPendingH = this_hba->int_qh; - this_hba->qPendingT = this_hba->int_qt; - this_hba->qPcnt = this_hba->int_qlen; + if( pthru == NULL ) { + free_local_pdev(pdev); + return -1; } - else { - /* - * Append pending queue to internal queue - */ - if( this_hba->int_qt ) { - this_hba->int_qt->next = this_hba->qPendingH; - this_hba->qPendingH = this_hba->int_qh; - this_hba->qPcnt += this_hba->int_qlen; - } - } + pthru->timeout = 2; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 0; - this_hba->int_qh = this_hba->int_qt = NULL; - this_hba->int_qlen = 0; + pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : ch; - /* - * If delete operation was successful, add 0x80 to the logical drive - * ids for commands in the pending queue. - */ - if( this_hba->read_ldidmap) { - for( scbp = this_hba->qPendingH; scbp; scbp = scbp->next ) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if( scbp->pthru->logdrv < 0x80 ) - scbp->pthru->logdrv += 0x80; -#else - if( scbp->pthru.logdrv < 0x80 ) - scbp->pthru.logdrv += 0x80; -#endif - } - } - this_hba->quiescent = 0; + pthru->target = (adapter->flag & BOARD_40LD) ? (ch << 4)|tgt : tgt; + + pthru->cdblen = 6; - IO_UNLOCK(this_hba->host); + pthru->cdb[0] = INQUIRY; + pthru->cdb[1] = 0; + pthru->cdb[2] = 0; + pthru->cdb[3] = 0; + pthru->cdb[4] = 255; + pthru->cdb[5] = 0; + + + pthru->dataxferaddr = (u32)buf_dma_handle; + pthru->dataxferlen = 256; + + memset(&mc, 0, sizeof(megacmd_t)); + + mc.cmd = MEGA_MBOXCMD_PASSTHRU; + mc.xferaddr = (u32)pthru_dma_handle; + + rval = mega_internal_command(adapter, LOCK_INT, &mc, pthru); + + pci_free_consistent(pdev, sizeof(mega_passthru), pthru, + pthru_dma_handle); + + free_local_pdev(pdev); return rval; } +/** + * mega_internal_command() + * @adapter - pointer to our soft state + * @ls - the scope of the exclusion lock. + * @mc - the mailbox command + * @pthru - Passthru structure for DCDB commands + * + * Issue the internal commands in interrupt mode. + * The last argument is the address of the passthru structure if the command + * to be fired is a passthru command + * + * lockscope specifies whether the caller has already acquired the lock. Of + * course, the caller must know which lock we are talking about. + * + * Note: parameter 'pthru' is null for non-passthru commands. + */ static int -mega_do_del_logdrv(mega_host_config *this_hba, int logdrv) +mega_internal_command(adapter_t *adapter, lockscope_t ls, megacmd_t *mc, + mega_passthru *pthru ) { - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int rval; + Scsi_Cmnd *scmd; + struct scsi_device *sdev; + unsigned long flags = 0; + scb_t *scb; + int rval; - mboxpnt = (mega_mailbox *)mbox; + /* + * The internal commands share one command id and hence are + * serialized. This is so because we want to reserve maximum number of + * available command ids for the I/O commands. + */ + down(&adapter->int_mtx); - memset(mbox, 0, sizeof(mbox)); + scb = &adapter->int_scb; + memset(scb, 0, sizeof(scb_t)); - mbox[0] = FC_DEL_LOGDRV; - mbox[2] = OP_DEL_LOGDRV; - mbox[3] = logdrv; + scmd = &adapter->int_scmd; + memset(scmd, 0, sizeof(Scsi_Cmnd)); - rval = megaIssueCmd(this_hba, mbox, NULL, 0); + sdev = kmalloc(sizeof(struct scsi_device), GFP_KERNEL); + memset(sdev, 0, sizeof(struct scsi_device)); + scmd->device = sdev; - /* log this event */ - if( rval != 0 ) { - printk("megaraid: Attempt to delete logical drive %d failed.", - logdrv); - return rval; - } + scmd->device->host = adapter->host; + scmd->buffer = (void *)scb; + scmd->cmnd[0] = MEGA_INTERNAL_CMD; + + scb->state |= SCB_ACTIVE; + scb->cmd = scmd; - printk("megaraid: logical drive %d deleted.\n", logdrv); + memcpy(scb->raw_mbox, mc, sizeof(megacmd_t)); /* - * After deleting first logical drive, the logical drives must be - * addressed by adding 0x80 to the logical drive id. + * Is it a passthru command */ - this_hba->read_ldidmap = 1; + if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) { - return rval; -} + scb->pthru = pthru; + } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) -void * -dma_alloc_consistent(void *dev, size_t size, dma_addr_t *dma_addr) -{ - void *_tv; - int npages; - int order = 0; + scb->idx = CMDID_INT_CMDS; + + scmd->state = 0; /* - * How many pages application needs + * Get the lock only if the caller has not acquired it already */ - npages = size / PAGE_SIZE; + if( ls == LOCK_INT ) spin_lock_irqsave(&adapter->lock, flags); + + megaraid_queue(scmd, mega_internal_done); - /* Do we need one more page */ - if(size % PAGE_SIZE) - npages++; + if( ls == LOCK_INT ) spin_unlock_irqrestore(&adapter->lock, flags); - order = mega_get_order(npages); + /* + * Wait till this command finishes. Do not use + * wait_event_interruptible(). It causes panic if CTRL-C is hit when + * dumping e.g., physical disk information through /proc interface. + */ +#if 0 + wait_event_interruptible(adapter->int_waitq, scmd->state); +#endif + wait_event(adapter->int_waitq, scmd->state); - _tv = (void *)__get_free_pages(GFP_DMA, order); + rval = scmd->result; + mc->status = scmd->result; + kfree(sdev); - if( _tv != NULL ) { - memset(_tv, 0, size); - *(dma_addr) = virt_to_bus(_tv); + /* + * Print a debug message for all failed commands. Applications can use + * this information. + */ + if( scmd->result && trace_level ) { + printk("megaraid: cmd [%x, %x, %x] status:[%x]\n", + mc->cmd, mc->opcode, mc->subopcode, scmd->result); } - return _tv; + up(&adapter->int_mtx); + + return rval; } -/* - * int mega_get_order(int) + +/** + * mega_internal_done() + * @scmd - internal scsi command * - * returns the order to be used as 2nd argument to __get_free_pages() - which - * return pages equal to pow(2, order) - AM + * Callback routine for internal commands. */ -int -mega_get_order(int n) +static void +mega_internal_done(Scsi_Cmnd *scmd) { - int i = 0; + adapter_t *adapter; - while( pow_2(i++) < n ) - ; /* null statement */ + adapter = (adapter_t *)scmd->device->host->hostdata; - return i-1; -} + scmd->state = 1; /* thread waiting for its command to complete */ -/* - * int pow_2(int) - * - * calculates pow(2, i) - */ -int -pow_2(int i) -{ - unsigned int v = 1; - - while(i--) - v <<= 1; + /* + * See comment in mega_internal_command() routine for + * wait_event_interruptible() + */ +#if 0 + wake_up_interruptible(&adapter->int_waitq); +#endif + wake_up(&adapter->int_waitq); - return v; } -void -dma_free_consistent(void *dev, size_t size, void *vaddr, dma_addr_t dma_addr) + +static inline int +make_local_pdev(adapter_t *adapter, struct pci_dev **pdev) { - int npages; - int order = 0; + *pdev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); - npages = size / PAGE_SIZE; + if( *pdev == NULL ) return -1; - if(size % PAGE_SIZE) - npages++; + memcpy(*pdev, adapter->dev, sizeof(struct pci_dev)); - if (npages == 1) - order = 0; - else if (npages == 2) - order = 1; - else if (npages <= 4) - order = 2; - else - order = 3; + if( pci_set_dma_mask(*pdev, 0xffffffff) != 0 ) { + kfree(*pdev); + return -1; + } - free_pages((unsigned long)vaddr, order); + return 0; +} +static inline void +free_local_pdev(struct pci_dev *pdev) +{ + kfree(pdev); } -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -static -#endif /* LINUX VERSION 2.4.XX */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) || defined(MODULE) -Scsi_Host_Template driver_template = MEGARAID; + +static Scsi_Host_Template driver_template = MEGARAID; #include "scsi_module.c" -#endif /* LINUX VERSION 2.4.XX || MODULE */ -/* vi: set ts=4: */ +/* vi: set ts=8 sw=8 tw=78: */ diff -urN linux-2.5.68-bk7/drivers/scsi/megaraid.h linux-2.5.68-bk8/drivers/scsi/megaraid.h --- linux-2.5.68-bk7/drivers/scsi/megaraid.h 2003-04-19 19:49:52.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/megaraid.h 2003-04-27 04:36:28.000000000 -0700 @@ -1,131 +1,67 @@ #ifndef __MEGARAID_H__ #define __MEGARAID_H__ -#ifndef LINUX_VERSION_CODE #include -#endif - -/* - * For state flag. Do not use LSB(8 bits) which are - * reserved for storing info about channels. - */ -#define IN_ISR 0x80000000L -#define IN_ABORT 0x40000000L -#define IN_RESET 0x20000000L -#define IN_QUEUE 0x10000000L -#define BOARD_QUARTZ 0x08000000L -#define BOARD_40LD 0x04000000L -#define BOARD_64BIT 0x02000000L - -#define SCB_FREE 0x0 -#define SCB_ACTIVE 0x1 -#define SCB_WAITQ 0x2 -#define SCB_ISSUED 0x3 -#define SCB_COMPLETE 0x4 -#define SCB_ABORTED 0x5 -#define SCB_RESET 0x6 - -#define M_RD_CRLFSTR "\n" -#define M_RD_IOCTL_CMD 0x80 -#define M_RD_IOCTL_CMD_NEW 0x81 -#define M_RD_DRIVER_IOCTL_INTERFACE 0x82 - -#define MEGARAID_VERSION "v1.18 (Release Date: Thu Oct 11 15:02:53 EDT 2001)\n" - -#define MEGARAID_IOCTL_VERSION 114 - -/* Methods */ -#define GET_DRIVER_INFO 0x1 - -#define MEGA_CMD_TIMEOUT 10 - -/* Feel free to fiddle with these.. max values are: - SGLIST 0..26 - COMMANDS 0..253 - CMDPERLUN 0..63 -*/ - -#define MAX_SGLIST 0x1A -#define MAX_COMMANDS 127 -#define MAX_CMD_PER_LUN 63 -#define MAX_FIRMWARE_STATUS 46 - -#define MAX_LOGICAL_DRIVES 8 -#define MAX_CHANNEL 5 -#define MAX_TARGET 15 -#define MAX_PHYSICAL_DRIVES MAX_CHANNEL*MAX_TARGET +#include -#define INQUIRY_DATA_SIZE 0x24 -#define MAX_CDB_LEN 0x0A -#define MAX_REQ_SENSE_LEN 0x20 -#define INTR_VALID 0x40 +#define MEGARAID_VERSION \ + "v2.00.3 (Release Date: Wed Feb 19 08:51:30 EST 2003)\n" -/* Direction Macros for MBOX Data direction */ -#define TO_DEVICE 0x0 -#define FROM_DEVICE 0x1 -#define FROMTO_DEVICE 0x2 - -/* Mailbox commands */ -#define MEGA_MBOXCMD_LREAD 0x01 -#define MEGA_MBOXCMD_LWRITE 0x02 -#define MEGA_MBOXCMD_LREAD64 0xA7 -#define MEGA_MBOXCMD_LWRITE64 0xA8 -#define MEGA_MBOXCMD_PASSTHRU 0x03 -#define MEGA_MBOXCMD_EXTPASSTHRU 0xE3 -#define MEGA_MBOXCMD_ADAPTERINQ 0x05 - - -/* Offsets into Mailbox */ -#define COMMAND_PORT 0x00 -#define COMMAND_ID_PORT 0x01 -#define SG_LIST_PORT0 0x08 -#define SG_LIST_PORT1 0x09 -#define SG_LIST_PORT2 0x0a -#define SG_LIST_PORT3 0x0b -#define SG_ELEMENT_PORT 0x0d -#define NO_FIRED_PORT 0x0f +/* + * Driver features - change the values to enable or disable features in the + * driver. + */ -/* I/O Port offsets */ -#define I_CMD_PORT 0x00 -#define I_ACK_PORT 0x00 -#define I_TOGGLE_PORT 0x01 -#define INTR_PORT 0x0a +/* + * Comand coalescing - This feature allows the driver to be able to combine + * two or more commands and issue as one command in order to boost I/O + * performance. Useful if the nature of the I/O is sequential. It is not very + * useful for random natured I/Os. + */ +#define MEGA_HAVE_COALESCING 0 -#define MAILBOX_SIZE (sizeof(mega_mailbox)-16) -#define MBOX_BUSY_PORT 0x00 -#define MBOX_PORT0 0x04 -#define MBOX_PORT1 0x05 -#define MBOX_PORT2 0x06 -#define MBOX_PORT3 0x07 -#define ENABLE_MBOX_REGION 0x0B +/* + * Clustering support - Set this flag if you are planning to use the + * clustering services provided by the megaraid controllers and planning to + * setup a cluster + */ +#define MEGA_HAVE_CLUSTERING 1 -/* I/O Port Values */ -#define ISSUE_BYTE 0x10 -#define ACK_BYTE 0x08 -#define ENABLE_INTR_BYTE 0xc0 -#define DISABLE_INTR_BYTE 0x00 -#define VALID_INTR_BYTE 0x40 -#define MBOX_BUSY_BYTE 0x10 -#define ENABLE_MBOX_BYTE 0x00 +/* + * Driver statistics - Set this flag if you are interested in statics about + * number of I/O completed on each logical drive and how many interrupts + * generated. If enabled, this information is available through /proc + * interface and through the private ioctl. Setting this flag has a + * performance penalty. + */ +#define MEGA_HAVE_STATS 0 -/* Setup some port macros here */ -#define WRITE_MAILBOX(base,offset,value) *(base+offset)=value -#define READ_MAILBOX(base,offset) *(base+offset) +/* + * Enhanced /proc interface - This feature will allow you to have a more + * detailed /proc interface for megaraid driver. E.g., a real time update of + * the status of the logical drives, battery status, physical drives etc. + */ +#define MEGA_HAVE_ENH_PROC 1 -#define WRITE_PORT(base,offset,value) outb_p(value,base+offset) -#define READ_PORT(base,offset) inb_p(base+offset) +#define MAX_DEV_TYPE 32 -#define ISSUE_COMMAND(base) WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE) -#define CLEAR_INTR(base) WRITE_PORT(base,I_ACK_PORT,ACK_BYTE) -#define ENABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE) -#define DISABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE) +#ifndef PCI_VENDOR_ID_LSI_LOGIC +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#endif -/* Define AMI's PCI codes */ #ifndef PCI_VENDOR_ID_AMI #define PCI_VENDOR_ID_AMI 0x101E #endif +#ifndef PCI_VENDOR_ID_DELL +#define PCI_VENDOR_ID_DELL 0x1028 +#endif + +#ifndef PCI_VENDOR_ID_INTEL +#define PCI_VENDOR_ID_INTEL 0x8086 +#endif + #ifndef PCI_DEVICE_ID_AMI_MEGARAID #define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 #endif @@ -138,418 +74,110 @@ #define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 #endif -/* Special Adapter Commands */ -#define FW_FIRE_WRITE 0x2C -#define FW_FIRE_FLASH 0x2D - -#define FC_NEW_CONFIG 0xA1 -#define DCMD_FC_CMD 0xA1 -#define DCMD_FC_PROCEED 0x02 -#define DCMD_DELETE_LOGDRV 0x03 -#define DCMD_FC_READ_NVRAM_CONFIG 0x04 -#define DCMD_FC_READ_NVRAM_CONFIG_64 0xC0 -#define DCMD_FC_READ_FINAL_CONFIG 0x05 -#define DCMD_GET_DISK_CONFIG 0x06 -#define DCMD_GET_DISK_CONFIG_64 0xC2 -#define DCMD_CHANGE_LDNO 0x07 -#define DCMD_COMPACT_CONFIG 0x08 -#define DCMD_DELETE_DRIVEGROUP 0x09 -#define DCMD_GET_LOOPID_INFO 0x0A -#define DCMD_CHANGE_LOOPID 0x0B -#define DCMD_GET_NUM_SCSI_CHANS 0x0C -#define DCMD_WRITE_CONFIG 0x0D -#define DCMD_WRITE_CONFIG_64 0xC1 - -#define NC_SUBOP_PRODUCT_INFO 0x0E -#define NC_SUBOP_ENQUIRY3 0x0F -#define ENQ3_GET_SOLICITED_NOTIFY_ONLY 0x01 -#define ENQ3_GET_SOLICITED_FULL 0x02 -#define ENQ3_GET_UNSOLICITED 0x03 - -#define PCI_CONF_BASE_ADDR_OFFSET 0x10 -#define PCI_CONF_IRQ_OFFSET 0x3c -#define PCI_CONF_AMISIG 0xa0 -#define PCI_CONF_AMISIG64 0xa4 - -/* Sub-System Vendor ID sorted on alphabetical order*/ -#define AMI_SUBSYS_ID 0x101E -#define DELL_SUBSYS_ID 0x1028 -#define HP_SUBSYS_ID 0x103C - -#define AMI_SIGNATURE 0x3344 -#define AMI_SIGNATURE_471 0xCCCC -#define AMI_64BIT_SIGNATURE 0x0299 - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /*0x20100 */ -#define MEGARAID \ - { NULL, /* Next */\ - NULL, /* Usage Count Pointer */\ - NULL, /* proc Directory Entry */\ - megaraid_proc_info, /* proc Info Function */\ - "MegaRAID", /* Driver Name */\ - megaraid_detect, /* Detect Host Adapter */\ - megaraid_release, /* Release Host Adapter */\ - megaraid_info, /* Driver Info Function */\ - megaraid_command, /* Command Function */\ - megaraid_queue, /* Queue Command Function */\ - megaraid_abort, /* Abort Command Function */\ - megaraid_reset, /* Reset Command Function */\ - NULL, /* Slave Attach Function */\ - megaraid_biosparam, /* Disk BIOS Parameters */\ - MAX_COMMANDS, /* # of cmds that can be\ - outstanding at any time */\ - 7, /* HBA Target ID */\ - MAX_SGLIST, /* Scatter/Gather Table Size */\ - MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\ - 0, /* Present */\ - 0, /* Default Unchecked ISA DMA */\ - ENABLE_CLUSTERING } /* Enable Clustering */ -#else -#define MEGARAID \ - {\ - .name = "MegaRAID", /* Driver Name */\ - .proc_info = megaraid_proc_info, /* /proc driver info */\ - .detect = megaraid_detect, /* Detect Host Adapter */\ - .release = megaraid_release, /* Release Host Adapter */\ - .info = megaraid_info, /* Driver Info Function */\ - .command = megaraid_command, /* Command Function */\ - .queuecommand = megaraid_queue, /* Queue Command Function */\ - .bios_param = megaraid_biosparam, /* Disk BIOS Parameters */\ - .can_queue = MAX_COMMANDS, /* Can Queue */\ - .this_id = 7, /* HBA Target ID */\ - .sg_tablesize = MAX_SGLIST, /* Scatter/Gather Table Size */\ - .cmd_per_lun = MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\ - .present = 0, /* Present */\ - .unchecked_isa_dma = 0, /* Default Unchecked ISA DMA */\ - .use_clustering = ENABLE_CLUSTERING, /* Enable Clustering */\ - .highmem_io = 1, \ - } -#endif - -/*********************************************************************** - * Structure Declarations for the Firmware supporting 40 Logical Drives - * and 256 Physical Drives. - ***********************************************************************/ - -#define FC_MAX_LOGICAL_DRIVES 40 -#define FC_MAX_LOG_DEVICES FC_MAX_LOGICAL_DRIVES -#define FC_MAX_SPAN_DEPTH 8 -#define FC_MAX_ROW_SIZE 32 - -#define FC_MAX_CHANNELS 16 -#define FC_MAX_TARGETS_PER_CHANNEL 16 -#define FC_MAX_PHYSICAL_DEVICES 256 - -/******************************************** - * PRODUCT_INFO - ********************************************/ - -#define SIG_40LOG_32STR_8SPN 0x00282008 - -/* - * Utilities declare this strcture size as 1024 bytes. So more fields can - * be added in future. - */ - -struct MRaidProductInfo { - u32 DataSize; /* current size in bytes (not including resvd) */ - u32 ConfigSignature; - /* Current value is 0x00282008 - * 0x28=MAX_LOGICAL_DRIVES, - * 0x20=Number of stripes and - * 0x08=Number of spans */ - u8 FwVer[16]; /* printable ASCI string */ - u8 BiosVer[16]; /* printable ASCI string */ - u8 ProductName[80]; /* printable ASCI string */ - - u8 MaxConcCmds; /* Max. concurrent commands supported */ - u8 SCSIChanPresent; /* Number of SCSI Channels detected */ - u8 FCLoopPresent; /* Number of Fibre Loops detected */ - u8 memType; /* EDO, FPM, SDRAM etc */ - - u32 signature; - u16 DramSize; /* In terms of MB */ - u16 subSystemID; - - u16 subSystemVendorID; - u8 numNotifyCounters; - u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */ -} __attribute__ ((packed)); -typedef struct MRaidProductInfo megaRaidProductInfo; +#define PCI_DEVICE_ID_DISCOVERY 0x000E +#define PCI_DEVICE_ID_PERC4_DI 0x000F +#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407 + +/* Sub-System Vendor IDs */ +#define AMI_SUBSYS_VID 0x101E +#define DELL_SUBSYS_VID 0x1028 +#define HP_SUBSYS_VID 0x103C +#define LSI_SUBSYS_VID 0x1000 + +#define HBA_SIGNATURE 0x3344 +#define HBA_SIGNATURE_471 0xCCCC +#define HBA_SIGNATURE_64BIT 0x0299 + +#define MBOX_BUSY_WAIT 10 /* wait for up to 10 usec for + mailbox to be free */ +#define DEFAULT_INITIATOR_ID 7 + +#define MAX_SGLIST 64 /* max supported in f/w */ +#define MIN_SGLIST 26 /* guaranteed to support these many */ +#define MAX_COMMANDS 126 +#define CMDID_INT_CMDS MAX_COMMANDS+1 /* make sure CMDID_INT_CMDS + is less than max commands + supported by any f/w */ + +#define MAX_CDB_LEN 10 +#define MAX_EXT_CDB_LEN 16 /* we support cdb length up to 16 */ + +#define DEF_CMD_PER_LUN 63 +#define MAX_CMD_PER_LUN MAX_COMMANDS +#define MAX_FIRMWARE_STATUS 46 +#define MAX_XFER_PER_CMD (64*1024) +#define MAX_SECTORS_PER_IO 128 + +#define MAX_LOGICAL_DRIVES_40LD 40 +#define FC_MAX_PHYSICAL_DEVICES 256 +#define MAX_LOGICAL_DRIVES_8LD 8 +#define MAX_CHANNELS 5 +#define MAX_TARGET 15 +#define MAX_PHYSICAL_DRIVES MAX_CHANNELS*MAX_TARGET +#define MAX_ROW_SIZE_40LD 32 +#define MAX_ROW_SIZE_8LD 8 +#define MAX_SPAN_DEPTH 8 + +#define NVIRT_CHAN 4 /* # of virtual channels to represent + up to 60 logical drives */ + +#define MEGARAID \ +{ \ + .name = "MegaRAID", \ + .proc_info = megaraid_proc_info, \ + .detect = megaraid_detect, \ + .release = megaraid_release, \ + .info = megaraid_info, \ + .command = megaraid_command, \ + .queuecommand = megaraid_queue, \ + .bios_param = megaraid_biosparam, \ + .max_sectors = MAX_SECTORS_PER_IO, \ + .can_queue = MAX_COMMANDS, \ + .this_id = DEFAULT_INITIATOR_ID, \ + .sg_tablesize = MAX_SGLIST, \ + .cmd_per_lun = DEF_CMD_PER_LUN, \ + .present = 0, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING, \ + .eh_abort_handler = megaraid_abort, \ + .eh_device_reset_handler = megaraid_reset, \ + .eh_bus_reset_handler = megaraid_reset, \ + .eh_host_reset_handler = megaraid_reset, \ + .highmem_io = 1, \ +} -/******************************************** - * Standard ENQUIRY - ********************************************/ -struct FC_ADP_INFO { - u8 MaxConcCmds; /* Max. concurrent commands supported. */ - u8 RbldRate; /* Rebuild Rate. Varies from 0%-100% */ - u8 MaxTargPerChan; /* Max. Targets supported per chan. */ - u8 ChanPresent; /* No. of Chans present on this adapter. */ - u8 FwVer[4]; /* Firmware version. */ - u16 AgeOfFlash; /* No. of times FW has been downloaded. */ - u8 ChipSetValue; /* Contents of 0xC0000832 */ - u8 DramSize; /* In terms of MB */ - u8 CacheFlushInterval; /* In terms of Seconds */ - u8 BiosVersion[4]; - u8 BoardType; - u8 sense_alert; - u8 write_config_count; /* Increase with evry configuration change */ - u8 drive_inserted_count;/* Increase with every drive inserted */ - u8 inserted_drive; /* Channel: Id of inserted drive */ - u8 battery_status; - /* - BIT 0 : battery module missing - BIT 1 : VBAD - BIT 2 : temp high - BIT 3 : battery pack missing - BIT 4,5 : 00 - charge complete - 01 - fast charge in prog - 10 - fast charge fail - 11 - undefined - BIt 6 : counter > 1000 - Bit 7 : undefined - */ - u8 dec_fault_bus_info; /* was resvd */ -} __attribute__ ((packed)); - -struct FC_LDRV_INFO { - u8 NumLDrv; /* No. of Log. Drvs configured. */ - u8 recon_state[FC_MAX_LOGICAL_DRIVES / 8]; - /* bit field for State of reconstruct */ - u16 LDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; - /* bit field Status of Long Operations. */ - - u32 LDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv. */ - u8 LDrvProp[FC_MAX_LOGICAL_DRIVES]; - u8 LDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives. */ -} __attribute__ ((packed)); - -#define PREVSTAT_MASK 0xf0 -#define CURRSTAT_MASK 0x0f - -struct FC_PDRV_INFO { - u8 PDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys Drvs. */ -} __attribute__ ((packed)); - -struct FC_AdapterInq { - struct FC_ADP_INFO AdpInfo; - struct FC_LDRV_INFO LogdrvInfo; - struct FC_PDRV_INFO PhysdrvInfo; -} __attribute__ ((packed)); -typedef struct FC_AdapterInq mega_RAIDINQ_FC; -/******************************************** - * NOTIFICATION - ********************************************/ - -#define MAX_NOTIFY_SIZE 0x80 -#define CUR_NOTIFY_SIZE sizeof(struct MegaRAID_Notify) - -/* - * Utilities declare this strcture size as ?? bytes. So more fields can - * be added in future. - */ -struct MegaRAID_Notify { - u32 globalCounter; /* Any change increments this counter */ +typedef struct { + /* 0x0 */ u8 cmd; + /* 0x1 */ u8 cmdid; + /* 0x2 */ u16 numsectors; + /* 0x4 */ u32 lba; + /* 0x8 */ u32 xferaddr; + /* 0xC */ u8 logdrv; + /* 0xD */ u8 numsgelements; + /* 0xE */ u8 resvd; + /* 0xF */ volatile u8 busy; + /* 0x10 */ volatile u8 numstatus; + /* 0x11 */ volatile u8 status; + /* 0x12 */ volatile u8 completed[MAX_FIRMWARE_STATUS]; + volatile u8 poll; + volatile u8 ack; +} __attribute__ ((packed)) mbox_t; - u8 paramCounter; /* Indicates any params changed */ - u8 paramId; /* Param modified - defined below */ - u16 paramVal; /* New val of last param modified */ - - u8 writeConfigCounter; /* write config occurred */ - u8 writeConfigRsvd[3]; - - u8 ldrvOpCounter; /* Indicates ldrv op started/completed */ - u8 ldrvOpId; /* ldrv num */ - u8 ldrvOpCmd; /* ldrv operation - defined below */ - u8 ldrvOpStatus; /* status of the operation */ - - u8 ldrvStateCounter; /* Indicates change of ldrv state */ - u8 ldrvStateId; /* ldrv num */ - u8 ldrvStateNew; /* New state */ - u8 ldrvStateOld; /* old state */ - - u8 pdrvStateCounter; /* Indicates change of ldrv state */ - u8 pdrvStateId; /* pdrv id */ - u8 pdrvStateNew; /* New state */ - u8 pdrvStateOld; /* old state */ - - u8 pdrvFmtCounter; /* Indicates pdrv format started/over */ - u8 pdrvFmtId; /* pdrv id */ - u8 pdrvFmtVal; /* format started/over */ - u8 pdrvFmtRsvd; - - u8 targXferCounter; /* Indicates SCSI-2 Xfer rate change */ - u8 targXferId; /* pdrv Id */ - u8 targXferVal; /* new Xfer params of last pdrv */ - u8 targXferRsvd; - - u8 fcLoopIdChgCounter; /* Indicates loopid changed */ - u8 fcLoopIdPdrvId; /* pdrv id */ - u8 fcLoopId0; /* loopid on fc loop 0 */ - u8 fcLoopId1; /* loopid on fc loop 1 */ - - u8 fcLoopStateCounter; /* Indicates loop state changed */ - u8 fcLoopState0; /* state of fc loop 0 */ - u8 fcLoopState1; /* state of fc loop 1 */ - u8 fcLoopStateRsvd; -} __attribute__ ((packed)); +typedef struct { + u32 xfer_segment_lo; + u32 xfer_segment_hi; + mbox_t mbox; +} __attribute__ ((packed)) mbox64_t; -/******************************************** - * PARAM IDs in Notify struct - ********************************************/ -#define PARAM_RBLD_RATE 0x01 - /*-------------------------------------- - * Param val = - * byte 0: new rbld rate - *--------------------------------------*/ -#define PARAM_CACHE_FLUSH_INTERVAL 0x02 - /*-------------------------------------- - * Param val = - * byte 0: new cache flush interval - *--------------------------------------*/ -#define PARAM_SENSE_ALERT 0x03 - /*-------------------------------------- - * Param val = - * byte 0: last pdrv id causing chkcond - *--------------------------------------*/ -#define PARAM_DRIVE_INSERTED 0x04 - /*-------------------------------------- - * Param val = - * byte 0: last pdrv id inserted - *--------------------------------------*/ -#define PARAM_BATTERY_STATUS 0x05 - /*-------------------------------------- - * Param val = - * byte 0: battery status - *--------------------------------------*/ - -/******************************************** - * Ldrv operation cmd in Notify struct - ********************************************/ -#define LDRV_CMD_CHKCONSISTANCY 0x01 -#define LDRV_CMD_INITIALIZE 0x02 -#define LDRV_CMD_RECONSTRUCTION 0x03 - -/******************************************** - * Ldrv operation status in Notify struct - ********************************************/ -#define LDRV_OP_SUCCESS 0x00 -#define LDRV_OP_FAILED 0x01 -#define LDRV_OP_ABORTED 0x02 -#define LDRV_OP_CORRECTED 0x03 -#define LDRV_OP_STARTED 0x04 - -/******************************************** - * Raid Logical drive states. - ********************************************/ -#define RDRV_OFFLINE 0 -#define RDRV_DEGRADED 1 -#define RDRV_OPTIMAL 2 -#define RDRV_DELETED 3 -/******************************************* - * Physical drive states. - *******************************************/ -#define PDRV_UNCNF 0 -#define PDRV_ONLINE 3 -#define PDRV_FAILED 4 -#define PDRV_RBLD 5 - -/******************************************* - * Formal val in Notify struct - *******************************************/ -#define PDRV_FMT_START 0x01 -#define PDRV_FMT_OVER 0x02 - -/******************************************** - * FC Loop State in Notify Struct - ********************************************/ -#define ENQ_FCLOOP_FAILED 0 -#define ENQ_FCLOOP_ACTIVE 1 -#define ENQ_FCLOOP_TRANSIENT 2 - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -#define M_RD_DMA_TYPE_NONE 0xFFFF -#define M_RD_PTHRU_WITH_BULK_DATA 0x0001 -#define M_RD_PTHRU_WITH_SGLIST 0x0002 -#define M_RD_BULK_DATA_ONLY 0x0004 -#define M_RD_SGLIST_ONLY 0x0008 -#define M_RD_EPTHRU_WITH_BULK_DATA 0x0010 -#endif -/******************************************** - * ENQUIRY3 - ********************************************/ /* - * Utilities declare this strcture size as 1024 bytes. So more fields can - * be added in future. + * Passthru definitions */ -struct MegaRAID_Enquiry3 { - u32 dataSize; /* current size in bytes (not including resvd) */ - - struct MegaRAID_Notify notify; - - u8 notifyRsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; - - u8 rbldRate; /* Rebuild rate (0% - 100%) */ - u8 cacheFlushInterval; /* In terms of Seconds */ - u8 senseAlert; - u8 driveInsertedCount; /* drive insertion count */ - - u8 batteryStatus; - u8 numLDrv; /* No. of Log Drives configured */ - u8 reconState[FC_MAX_LOGICAL_DRIVES / 8]; /* State of reconstruct */ - u16 lDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; /* log. Drv Status */ - - u32 lDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv */ - u8 lDrvProp[FC_MAX_LOGICAL_DRIVES]; - u8 lDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives */ - u8 pDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys. Drvs. */ - u16 physDrvFormat[FC_MAX_PHYSICAL_DEVICES / 16]; - - u8 targXfer[80]; /* phys device transfer rate */ - u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */ -} __attribute__ ((packed)); -typedef struct MegaRAID_Enquiry3 mega_Enquiry3; - -/* Structures */ -typedef struct _mega_ADP_INFO { - u8 MaxConcCmds; - u8 RbldRate; - u8 MaxTargPerChan; - u8 ChanPresent; - u8 FwVer[4]; - u16 AgeOfFlash; - u8 ChipSetValue; - u8 DramSize; - u8 CacheFlushInterval; - u8 BiosVer[4]; - u8 resvd[7]; -} mega_ADP_INFO; - -typedef struct _mega_LDRV_INFO { - u8 NumLDrv; - u8 resvd[3]; - u32 LDrvSize[MAX_LOGICAL_DRIVES]; - u8 LDrvProp[MAX_LOGICAL_DRIVES]; - u8 LDrvState[MAX_LOGICAL_DRIVES]; -} mega_LDRV_INFO; - -typedef struct _mega_PDRV_INFO { - u8 PDrvState[MAX_PHYSICAL_DRIVES]; - u8 resvd; -} mega_PDRV_INFO; - -/* RAID inquiry: Mailbox command 0x5*/ -typedef struct _mega_RAIDINQ { - mega_ADP_INFO AdpInfo; - mega_LDRV_INFO LogdrvInfo; - mega_PDRV_INFO PhysdrvInfo; -} mega_RAIDINQ; +#define MAX_REQ_SENSE_LEN 0x20 -/* Passthrough command: Mailbox command 0x3*/ -typedef struct mega_passthru { +typedef struct { u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */ u8 ars:1; u8 reserved:3; @@ -567,7 +195,8 @@ u8 scsistatus; u32 dataxferaddr; u32 dataxferlen; -} mega_passthru; +} __attribute__ ((packed)) mega_passthru; + /* * Extended passthru: support CDB > 10 bytes @@ -579,228 +208,318 @@ u8 cd_rom:1; u8 rsvd2:1; u8 islogical:1; - u8 logdrv; /* if islogical == 1 */ u8 channel; /* if islogical == 0 */ u8 target; /* if islogical == 0 */ - u8 queuetag; /* unused */ u8 queueaction; /* unused */ - u8 cdblen; u8 rsvd3; - u8 cdb[16]; - + u8 cdb[MAX_EXT_CDB_LEN]; u8 numsgelements; u8 status; u8 reqsenselen; u8 reqsensearea[MAX_REQ_SENSE_LEN]; u8 rsvd4; - u32 dataxferaddr; u32 dataxferlen; -}mega_ext_passthru; - -struct _mega_mailbox { - /* 0x0 */ u8 cmd; - /* 0x1 */ u8 cmdid; - /* 0x2 */ u16 numsectors; - /* 0x4 */ u32 lba; - /* 0x8 */ u32 xferaddr; - /* 0xC */ u8 logdrv; - /* 0xD */ u8 numsgelements; - /* 0xE */ u8 resvd; - /* 0xF */ u8 busy; - /* 0x10 */ u8 numstatus; - /* 0x11 */ u8 status; - /* 0x12 */ u8 completed[46]; - volatile u8 mraid_poll; - volatile u8 mraid_ack; - u8 pad[16]; /* for alignment purposes */ -} __attribute__ ((packed)); -typedef struct _mega_mailbox mega_mailbox; +} __attribute__ ((packed)) mega_ext_passthru; typedef struct { - u32 xferSegment_lo; - u32 xferSegment_hi; - mega_mailbox mailbox; -} mega_mailbox64; - -typedef struct _mega_ioctl_mbox { - /* 0x0 */ u8 cmd; - /* 0x1 */ u8 cmdid; - /* 0x2 */ u8 channel; - /* 0x3 */ u8 param; - /* 0x4 */ u8 pad[4]; - /* 0x8 */ u32 xferaddr; - /* 0xC */ u8 logdrv; - /* 0xD */ u8 numsgelements; - /* 0xE */ u8 resvd; - /* 0xF */ u8 busy; - /* 0x10 */ u8 numstatus; - /* 0x11 */ u8 status; - /* 0x12 */ u8 completed[46]; - u8 mraid_poll; - u8 mraid_ack; - u8 malign[16]; -} mega_ioctl_mbox; - -typedef struct _mega_64sglist32 { u64 address; u32 length; -} __attribute__ ((packed)) mega_64sglist; +} __attribute__ ((packed)) mega_sgl64; -typedef struct _mega_sglist { +typedef struct { u32 address; u32 length; -} mega_sglist; +} __attribute__ ((packed)) mega_sglist; + /* Queued command data */ -typedef struct _mega_scb mega_scb; +typedef struct { + int idx; + u32 state; + struct list_head list; + u8 raw_mbox[66]; + u32 dma_type; + u32 dma_direction; + + Scsi_Cmnd *cmd; + dma_addr_t dma_h_bulkdata; + dma_addr_t dma_h_sgdata; + + mega_sglist *sgl; + mega_sgl64 *sgl64; + dma_addr_t sgl_dma_addr; + + mega_passthru *pthru; + dma_addr_t pthru_dma_addr; + mega_ext_passthru *epthru; + dma_addr_t epthru_dma_addr; +} scb_t; -struct _mega_scb { - int idx; - u32 state; - u32 isrcount; - u8 mboxData[16]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - u32 dma_type; - dma_addr_t dma_h_bulkdata; /*Dma handle for bulk data transfter */ - u32 dma_direction; /*Dma direction */ - dma_addr_t dma_h_sgdata; /*Dma handle for the sglist structure */ - dma_addr_t dma_h_sglist[MAX_SGLIST]; /*Dma handle for all SGL elements */ - u8 sglist_count; - dma_addr_t dma_sghandle64; - dma_addr_t dma_passthruhandle64; - dma_addr_t dma_ext_passthruhandle64; - dma_addr_t dma_bounce_buffer; - u8 *bounce_buffer; -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mega_passthru *pthru; - mega_ext_passthru *epthru; -#else - mega_passthru pthru; - mega_ext_passthru epthru; -#endif +/* + * Flags to follow the scb as it transitions between various stages + */ +#define SCB_FREE 0x0000 /* on the free list */ +#define SCB_ACTIVE 0x0001 /* off the free list */ +#define SCB_PENDQ 0x0002 /* on the pending queue */ +#define SCB_ISSUED 0x0004 /* issued - owner f/w */ +#define SCB_ABORT 0x0008 /* Got an abort for this one */ +#define SCB_RESET 0x0010 /* Got a reset for this one */ - Scsi_Cmnd *SCpnt; - mega_sglist *sgList; - mega_64sglist *sg64List; - struct semaphore ioctl_sem; - void *buff_ptr; - u32 iDataSize; - mega_scb *next; -}; +/* + * Utilities declare this strcture size as 1024 bytes. So more fields can + * be added in future. + */ +typedef struct { + u32 data_size; /* current size in bytes (not including resvd) */ -/* internal locking by the queue manipulting routines */ -#define INTERNAL_LOCK 0 -/* external locking by the queue manipulting routines */ -#define EXTERNAL_LOCK 1 -#define NO_LOCK 2 -#define INTR_ENB 0 /* do not disable interrupt while manipulating */ -#define INTR_DIS 1 /* disable interrupt while manipulating */ + u32 config_signature; + /* Current value is 0x00282008 + * 0x28=MAX_LOGICAL_DRIVES, + * 0x20=Number of stripes and + * 0x08=Number of spans */ + + u8 fw_version[16]; /* printable ASCI string */ + u8 bios_version[16]; /* printable ASCI string */ + u8 product_name[80]; /* printable ASCI string */ + + u8 max_commands; /* Max. concurrent commands supported */ + u8 nchannels; /* Number of SCSI Channels detected */ + u8 fc_loop_present; /* Number of Fibre Loops detected */ + u8 mem_type; /* EDO, FPM, SDRAM etc */ + + u32 signature; + u16 dram_size; /* In terms of MB */ + u16 subsysid; + + u16 subsysvid; + u8 notify_counters; + u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */ +} __attribute__ ((packed)) mega_product_info; + +struct notify { + u32 global_counter; /* Any change increments this counter */ + + u8 param_counter; /* Indicates any params changed */ + u8 param_id; /* Param modified - defined below */ + u16 param_val; /* New val of last param modified */ + + u8 write_config_counter; /* write config occurred */ + u8 write_config_rsvd[3]; + + u8 ldrv_op_counter; /* Indicates ldrv op started/completed */ + u8 ldrv_opid; /* ldrv num */ + u8 ldrv_opcmd; /* ldrv operation - defined below */ + u8 ldrv_opstatus; /* status of the operation */ + + u8 ldrv_state_counter; /* Indicates change of ldrv state */ + u8 ldrv_state_id; /* ldrv num */ + u8 ldrv_state_new; /* New state */ + u8 ldrv_state_old; /* old state */ + + u8 pdrv_state_counter; /* Indicates change of ldrv state */ + u8 pdrv_state_id; /* pdrv id */ + u8 pdrv_state_new; /* New state */ + u8 pdrv_state_old; /* old state */ + + u8 pdrv_fmt_counter; /* Indicates pdrv format started/over */ + u8 pdrv_fmt_id; /* pdrv id */ + u8 pdrv_fmt_val; /* format started/over */ + u8 pdrv_fmt_rsvd; + + u8 targ_xfer_counter; /* Indicates SCSI-2 Xfer rate change */ + u8 targ_xfer_id; /* pdrv Id */ + u8 targ_xfer_val; /* new Xfer params of last pdrv */ + u8 targ_xfer_rsvd; + + u8 fcloop_id_chg_counter; /* Indicates loopid changed */ + u8 fcloopid_pdrvid; /* pdrv id */ + u8 fcloop_id0; /* loopid on fc loop 0 */ + u8 fcloop_id1; /* loopid on fc loop 1 */ + + u8 fcloop_state_counter; /* Indicates loop state changed */ + u8 fcloop_state0; /* state of fc loop 0 */ + u8 fcloop_state1; /* state of fc loop 1 */ + u8 fcloop_state_rsvd; +} __attribute__ ((packed)); -/* Per-controller data */ -typedef struct _mega_host_config { - u8 numldrv; - u32 flag; +#define MAX_NOTIFY_SIZE 0x80 +#define CUR_NOTIFY_SIZE sizeof(struct notify) -#ifdef __LP64__ - u64 base; -#else - u32 base; -#endif +typedef struct { + u32 data_size; /* current size in bytes (not including resvd) */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t dma_handle64, adjdmahandle64; - struct pci_dev *dev; -#endif + struct notify notify; - mega_scb *qFreeH; - mega_scb *qFreeT; - spinlock_t lock_free; + u8 notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; - mega_scb *qPendingH; - mega_scb *qPendingT; - spinlock_t lock_pend; + u8 rebuild_rate; /* Rebuild rate (0% - 100%) */ + u8 cache_flush_interval; /* In terms of Seconds */ + u8 sense_alert; + u8 drive_insert_count; /* drive insertion count */ + + u8 battery_status; + u8 num_ldrv; /* No. of Log Drives configured */ + u8 recon_state[MAX_LOGICAL_DRIVES_40LD / 8]; /* State of + reconstruct */ + u16 ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8]; /* logdrv + Status */ + + u32 ldrv_size[MAX_LOGICAL_DRIVES_40LD];/* Size of each log drv */ + u8 ldrv_prop[MAX_LOGICAL_DRIVES_40LD]; + u8 ldrv_state[MAX_LOGICAL_DRIVES_40LD];/* State of log drives */ + u8 pdrv_state[FC_MAX_PHYSICAL_DEVICES];/* State of phys drvs. */ + u16 pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16]; + + u8 targ_xfer[80]; /* phys device transfer rate */ + u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */ +} __attribute__ ((packed)) mega_inquiry3; - Scsi_Cmnd *qCompletedH; - Scsi_Cmnd *qCompletedT; - spinlock_t lock_scsicmd; - u32 qFcnt; - u32 qPcnt; - u32 qCcnt; +/* Structures */ +typedef struct { + u8 max_commands; /* Max concurrent commands supported */ + u8 rebuild_rate; /* Rebuild rate - 0% thru 100% */ + u8 max_targ_per_chan; /* Max targ per channel */ + u8 nchannels; /* Number of channels on HBA */ + u8 fw_version[4]; /* Firmware version */ + u16 age_of_flash; /* Number of times FW has been flashed */ + u8 chip_set_value; /* Contents of 0xC0000832 */ + u8 dram_size; /* In MB */ + u8 cache_flush_interval; /* in seconds */ + u8 bios_version[4]; + u8 board_type; + u8 sense_alert; + u8 write_config_count; /* Increase with every configuration + change */ + u8 drive_inserted_count; /* Increase with every drive inserted + */ + u8 inserted_drive; /* Channel:Id of inserted drive */ + u8 battery_status; /* + * BIT 0: battery module missing + * BIT 1: VBAD + * BIT 2: temprature high + * BIT 3: battery pack missing + * BIT 4,5: + * 00 - charge complete + * 01 - fast charge in progress + * 10 - fast charge fail + * 11 - undefined + * Bit 6: counter > 1000 + * Bit 7: Undefined + */ + u8 dec_fault_bus_info; +} __attribute__ ((packed)) mega_adp_info; - unsigned long nReads[FC_MAX_LOGICAL_DRIVES]; - unsigned long nReadBlocks[FC_MAX_LOGICAL_DRIVES]; - unsigned long nWrites[FC_MAX_LOGICAL_DRIVES]; - unsigned long nWriteBlocks[FC_MAX_LOGICAL_DRIVES]; - unsigned long nInterrupts; - /* Host adapter parameters */ - u8 fwVer[7]; - u8 biosVer[7]; - struct Scsi_Host *host; +typedef struct { + u8 num_ldrv; /* Number of logical drives configured */ + u8 rsvd[3]; + u32 ldrv_size[MAX_LOGICAL_DRIVES_8LD]; + u8 ldrv_prop[MAX_LOGICAL_DRIVES_8LD]; + u8 ldrv_state[MAX_LOGICAL_DRIVES_8LD]; +} __attribute__ ((packed)) mega_ldrv_info; + +typedef struct { + u8 pdrv_state[MAX_PHYSICAL_DRIVES]; + u8 rsvd; +} __attribute__ ((packed)) mega_pdrv_info; + +/* RAID inquiry: Mailbox command 0x05*/ +typedef struct { + mega_adp_info adapter_info; + mega_ldrv_info logdrv_info; + mega_pdrv_info pdrv_info; +} __attribute__ ((packed)) mraid_inquiry; + + +/* RAID extended inquiry: Mailbox command 0x04*/ +typedef struct { + mraid_inquiry raid_inq; + u16 phys_drv_format[MAX_CHANNELS]; + u8 stack_attn; + u8 modem_status; + u8 rsvd[2]; +} __attribute__ ((packed)) mraid_ext_inquiry; + + +typedef struct { + u8 channel; + u8 target; +}__attribute__ ((packed)) adp_device; + +typedef struct { + u32 start_blk; /* starting block */ + u32 num_blks; /* # of blocks */ + adp_device device[MAX_ROW_SIZE_40LD]; +}__attribute__ ((packed)) adp_span_40ld; + +typedef struct { + u32 start_blk; /* starting block */ + u32 num_blks; /* # of blocks */ + adp_device device[MAX_ROW_SIZE_8LD]; +}__attribute__ ((packed)) adp_span_8ld; + +typedef struct { + u8 span_depth; /* Total # of spans */ + u8 level; /* RAID level */ + u8 read_ahead; /* read ahead, no read ahead, adaptive read + ahead */ + u8 stripe_sz; /* Encoded stripe size */ + u8 status; /* Status of the logical drive */ + u8 write_mode; /* write mode, write_through/write_back */ + u8 direct_io; /* direct io or through cache */ + u8 row_size; /* Number of stripes in a row */ +} __attribute__ ((packed)) logdrv_param; + +typedef struct { + logdrv_param lparam; + adp_span_40ld span[MAX_SPAN_DEPTH]; +}__attribute__ ((packed)) logdrv_40ld; + +typedef struct { + logdrv_param lparam; + adp_span_8ld span[MAX_SPAN_DEPTH]; +}__attribute__ ((packed)) logdrv_8ld; + +typedef struct { + u8 type; /* Type of the device */ + u8 cur_status; /* current status of the device */ + u8 tag_depth; /* Level of tagging */ + u8 sync_neg; /* sync negotiation - ENABLE or DISBALE */ + u32 size; /* configurable size in terms of 512 byte + blocks */ +}__attribute__ ((packed)) phys_drv; - volatile mega_mailbox64 *mbox64; /* ptr to beginning of 64-bit mailbox */ - volatile mega_mailbox *mbox; /* ptr to beginning of standard mailbox */ +typedef struct { + u8 nlog_drives; /* number of logical drives */ + u8 resvd[3]; + logdrv_40ld ldrv[MAX_LOGICAL_DRIVES_40LD]; + phys_drv pdrv[MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_40ld; + +typedef struct { + u8 nlog_drives; /* number of logical drives */ + u8 resvd[3]; + logdrv_8ld ldrv[MAX_LOGICAL_DRIVES_8LD]; + phys_drv pdrv[MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_8ld; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -/* ptr to beginning of standard mailbox */ - volatile mega_mailbox64 *mailbox64ptr; -#else - volatile mega_mailbox64 mailbox64; -#endif - - volatile u8 mega_buffer[2 * 1024L]; - volatile megaRaidProductInfo productInfo; - - u8 max_cmds; - mega_scb scbList[MAX_COMMANDS]; - -#define PROCBUFSIZE 4096 - char procbuf[PROCBUFSIZE]; - int procidx; - struct proc_dir_entry *controller_proc_dir_entry; - struct proc_dir_entry *proc_read, *proc_stat, *proc_status, *proc_mbox; - int support_ext_cdb; - int boot_ldrv_enabled; - int boot_ldrv; - - int support_random_del; /* Do we support random deletion of logdrvs */ - int read_ldidmap; /* set after logical drive deltion. The logical - drive number must be read from the map */ - int quiescent; /* a stage reached when delete logical drive needs to - be done. Stop sending requests to the hba till - delete operation is completed */ - - mega_scb *int_qh; /* commands are queued in the internal queue */ - mega_scb *int_qt; /* while the hba is quiescent */ - int int_qlen; -} mega_host_config; - -typedef struct _driver_info { - int size; - ulong version; -} mega_driver_info; /* * User ioctl structure. * This structure will be used for Traditional Method ioctl interface - * commands (M_RD_IOCTL_CMD),Alternate Buffer Method (M_RD_IOCTL_CMD_NEW) - * ioctl commands and the Driver ioctls(M_RD_DRIVER_IOCTL_INTERFACE). - * The Driver ioctl interface handles the commands at - * the driver level, without being sent to the card. + * commands (0x80),Alternate Buffer Method (0x81) ioctl commands and the + * Driver ioctls. + * The Driver ioctl interface handles the commands at the driver level, + * without being sent to the card. */ -#define MEGADEVIOC 0x84 - /* system call imposed limit. Change accordingly */ #define IOCTL_MAX_DATALEN 4096 -#pragma pack(1) struct uioctl_t { u32 inlen; u32 outlen; @@ -818,8 +537,8 @@ u8 *buffer; #endif u32 length; - } fcs; - } ui; + } __attribute__ ((packed)) fcs; + } __attribute__ ((packed)) ui; u8 mbox[18]; /* 16 bytes + 2 status bytes */ mega_passthru pthru; #if BITS_PER_LONG == 32 @@ -829,8 +548,7 @@ #if BITS_PER_LONG == 64 char *data; #endif -}; -#pragma pack() +} __attribute__ ((packed)); /* * struct mcontroller is used to pass information about the controllers in the @@ -854,25 +572,26 @@ u32 uid; }; -struct mbox_passthru { - u8 cmd; - u8 cmdid; - u16 pad1; - u32 pad2; - u32 dataxferaddr; - u8 pad3; - u8 pad4; - u8 rsvd; - u8 mboxbusy; - u8 nstatus; - u8 status; -}; +/* + * mailbox structure used for internal commands + */ +typedef struct { + u8 cmd; + u8 cmdid; + u8 opcode; + u8 subopcode; + u32 lba; + u32 xferaddr; + u8 logdrv; + u8 rsvd[3]; + u8 numstatus; + u8 status; +} __attribute__ ((packed)) megacmd_t; /* - * Defines for Driver IOCTL interface, Op-code:M_RD_DRIVER_IOCTL_INTERFACE + * Defines for Driver IOCTL interface */ #define MEGAIOC_MAGIC 'm' -#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0) /* Mega IOCTL command */ #define MEGAIOC_QNADAP 'm' /* Query # of adapters */ #define MEGAIOC_QDRVRVER 'e' /* Query driver version */ @@ -880,126 +599,508 @@ #define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) ) #define GETADAP(mkadap) ( (mkadap) ^ MEGAIOC_MAGIC << 8 ) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ -extern struct proc_dir_entry proc_scsi_megaraid; -#endif +/* + * Definition for the new ioctl interface (NIT) + */ -/* For Host Re-Ordering */ -#define MAX_CONTROLLERS 32 +/* + * Vendor specific Group-7 commands + */ +#define VENDOR_SPECIFIC_COMMANDS 0xE0 +#define MEGA_INTERNAL_CMD VENDOR_SPECIFIC_COMMANDS + 0x01 -struct mega_hbas { - int is_bios_enabled; - mega_host_config *hostdata_addr; -}; +/* + * The ioctl command. No other command shall be used for this interface + */ +#define USCSICMD VENDOR_SPECIFIC_COMMANDS + +/* + * Data direction flags + */ +#define UIOC_RD 0x00001 +#define UIOC_WR 0x00002 -#define IS_BIOS_ENABLED 0x62 -#define GET_BIOS 0x01 -#define CHNL_CLASS 0xA9 -#define GET_CHNL_CLASS 0x00 -#define SET_CHNL_CLASS 0x01 -#define CH_RAID 0x01 -#define CH_SCSI 0x00 +/* + * ioctl opcodes + */ +#define MBOX_CMD 0x00000 /* DCMD or passthru command */ +#define GET_DRIVER_VER 0x10000 /* Get driver version */ +#define GET_N_ADAP 0x20000 /* Get number of adapters */ +#define GET_ADAP_INFO 0x30000 /* Get information about a adapter */ +#define GET_CAP 0x40000 /* Get ioctl capabilities */ +#define GET_STATS 0x50000 /* Get statistics, including error info */ -#define BIOS_PVT_DATA 0x40 -#define GET_BIOS_PVT_DATA 0x00 +/* + * The ioctl structure. + * MBOX macro converts a nitioctl_t structure to megacmd_t pointer and + * MBOX_P macro converts a nitioctl_t pointer to megacmd_t pointer. + */ +typedef struct { + char signature[8]; /* Must contain "MEGANIT" */ + u32 opcode; /* opcode for the command */ + u32 adapno; /* adapter number */ + union { + u8 __raw_mbox[18]; + caddr_t __uaddr; /* xferaddr for non-mbox cmds */ + }__ua; + +#define uioc_rmbox __ua.__raw_mbox +#define MBOX(uioc) ((megacmd_t *)&((uioc).__ua.__raw_mbox[0])) +#define MBOX_P(uioc) ((megacmd_t *)&((uioc)->__ua.__raw_mbox[0])) +#define uioc_uaddr __ua.__uaddr + + u32 xferlen; /* xferlen for DCMD and non-mbox + commands */ + u32 flags; /* data direction flags */ +}nitioctl_t; + + +/* + * I/O statistics for some applications like SNMP agent. The caller must + * provide the number of logical drives for which status should be reported. + */ +typedef struct { + int num_ldrv; /* Number for logical drives for which the + status should be reported. */ + u32 nreads[MAX_LOGICAL_DRIVES_40LD]; /* number of reads for + each logical drive */ + u32 nreadblocks[MAX_LOGICAL_DRIVES_40LD]; /* number of blocks + read for each logical + drive */ + u32 nwrites[MAX_LOGICAL_DRIVES_40LD]; /* number of writes + for each logical + drive */ + u32 nwriteblocks[MAX_LOGICAL_DRIVES_40LD]; /* number of blocks + writes for each + logical drive */ + u32 rd_errors[MAX_LOGICAL_DRIVES_40LD]; /* number of read + errors for each + logical drive */ + u32 wr_errors[MAX_LOGICAL_DRIVES_40LD]; /* number of write + errors for each + logical drive */ +}megastat_t; + -#pragma pack(1) struct private_bios_data { - u8 geometry:4; /* - * bits 0-3 - BIOS geometry - * 0x0001 - 1GB - * 0x0010 - 2GB - * 0x1000 - 8GB - * Others values are invalid - */ - u8 unused:4; /* bits 4-7 are unused */ - u8 boot_ldrv; /* - * logical drive set as boot drive - * 0..7 - for 8LD cards - * 0..39 - for 40LD cards + u8 geometry:4; /* + * bits 0-3 - BIOS geometry + * 0x0001 - 1GB + * 0x0010 - 2GB + * 0x1000 - 8GB + * Others values are invalid */ - u8 rsvd[12]; - u16 cksum; /* 0-(sum of first 13 bytes of this structure) */ -}; -#pragma pack() + u8 unused:4; /* bits 4-7 are unused */ + u8 boot_drv; /* + * logical drive set as boot drive + * 0..7 - for 8LD cards + * 0..39 - for 40LD cards + */ + u8 rsvd[12]; + u16 cksum; /* 0-(sum of first 13 bytes of this structure) */ +} __attribute__ ((packed)); + + + + +/* + * Mailbox and firmware commands and subopcodes used in this driver. + */ -#define NVIRT_CHAN 4 /* # of virtual channels to represent 60 logical - drives */ +#define MEGA_MBOXCMD_LREAD 0x01 +#define MEGA_MBOXCMD_LWRITE 0x02 +#define MEGA_MBOXCMD_PASSTHRU 0x03 +#define MEGA_MBOXCMD_ADPEXTINQ 0x04 +#define MEGA_MBOXCMD_ADAPTERINQ 0x05 +#define MEGA_MBOXCMD_LREAD64 0xA7 +#define MEGA_MBOXCMD_LWRITE64 0xA8 +#define MEGA_MBOXCMD_PASSTHRU64 0xC3 +#define MEGA_MBOXCMD_EXTPTHRU 0xE3 + +#define MAIN_MISC_OPCODE 0xA4 /* f/w misc opcode */ +#define GET_MAX_SG_SUPPORT 0x01 /* get max sg len supported by f/w */ + +#define FC_NEW_CONFIG 0xA1 +#define NC_SUBOP_PRODUCT_INFO 0x0E +#define NC_SUBOP_ENQUIRY3 0x0F +#define ENQ3_GET_SOLICITED_FULL 0x02 +#define OP_DCMD_READ_CONFIG 0x04 +#define NEW_READ_CONFIG_8LD 0x67 +#define READ_CONFIG_8LD 0x07 +#define FLUSH_ADAPTER 0x0A +#define FLUSH_SYSTEM 0xFE /* * Command for random deletion of logical drives */ #define FC_DEL_LOGDRV 0xA4 /* f/w command */ #define OP_SUP_DEL_LOGDRV 0x2A /* is feature supported */ -#define OP_GET_LDID_MAP 0x18 /* get logdrv id and logdrv number map */ +#define OP_GET_LDID_MAP 0x18 /* get ldid and logdrv number map */ #define OP_DEL_LOGDRV 0x1C /* delete logical drive */ -/*================================================================ - * - * Function prototypes - * - *================================================================ +/* + * BIOS commands + */ +#define IS_BIOS_ENABLED 0x62 +#define GET_BIOS 0x01 +#define CHNL_CLASS 0xA9 +#define GET_CHNL_CLASS 0x00 +#define SET_CHNL_CLASS 0x01 +#define CH_RAID 0x01 +#define CH_SCSI 0x00 +#define BIOS_PVT_DATA 0x40 +#define GET_BIOS_PVT_DATA 0x00 + + +/* + * Commands to support clustering */ +#define MEGA_GET_TARGET_ID 0x7D +#define MEGA_CLUSTER_OP 0x70 +#define MEGA_GET_CLUSTER_MODE 0x02 +#define MEGA_CLUSTER_CMD 0x6E +#define MEGA_RESERVE_LD 0x01 +#define MEGA_RELEASE_LD 0x02 +#define MEGA_RESET_RESERVATIONS 0x03 +#define MEGA_RESERVATION_STATUS 0x04 +#define MEGA_RESERVE_PD 0x05 +#define MEGA_RELEASE_PD 0x06 + + +/* + * Module battery status + */ +#define MEGA_BATT_MODULE_MISSING 0x01 +#define MEGA_BATT_LOW_VOLTAGE 0x02 +#define MEGA_BATT_TEMP_HIGH 0x04 +#define MEGA_BATT_PACK_MISSING 0x08 +#define MEGA_BATT_CHARGE_MASK 0x30 +#define MEGA_BATT_CHARGE_DONE 0x00 +#define MEGA_BATT_CHARGE_INPROG 0x10 +#define MEGA_BATT_CHARGE_FAIL 0x20 +#define MEGA_BATT_CYCLES_EXCEEDED 0x40 + +/* + * Physical drive states. + */ +#define PDRV_UNCNF 0 +#define PDRV_ONLINE 3 +#define PDRV_FAILED 4 +#define PDRV_RBLD 5 +#define PDRV_HOTSPARE 6 + + +/* + * Raid logical drive states. + */ +#define RDRV_OFFLINE 0 +#define RDRV_DEGRADED 1 +#define RDRV_OPTIMAL 2 +#define RDRV_DELETED 3 + +/* + * Read, write and cache policies + */ +#define NO_READ_AHEAD 0 +#define READ_AHEAD 1 +#define ADAP_READ_AHEAD 2 +#define WRMODE_WRITE_THRU 0 +#define WRMODE_WRITE_BACK 1 +#define CACHED_IO 0 +#define DIRECT_IO 1 + + +#define SCSI_LIST(scp) ((struct list_head *)(&(scp)->SCp)) + +/* + * Each controller's soft state + */ +typedef struct { + int this_id; /* our id, may set to different than 7 if + clustering is available */ + u32 flag; + + unsigned long base; + + /* mbox64 with mbox not aligned on 16-byte boundry */ + mbox64_t *una_mbox64; + dma_addr_t una_mbox64_dma; + + volatile mbox64_t *mbox64;/* ptr to 64-bit mailbox */ + volatile mbox_t *mbox; /* ptr to standard mailbox */ + dma_addr_t mbox_dma; + + struct pci_dev *dev; + + struct list_head free_list; + struct list_head pending_list; + struct list_head completed_list; + + struct Scsi_Host *host; + +#define MEGA_BUFFER_SIZE (2*1024) + u8 *mega_buffer; + dma_addr_t buf_dma_handle; + + mega_product_info product_info; + + u8 max_cmds; + scb_t *scb_list; + + atomic_t pend_cmds; /* maintain a counter for pending + commands in firmware */ + +#if MEGA_HAVE_STATS + u32 nreads[MAX_LOGICAL_DRIVES_40LD]; + u32 nreadblocks[MAX_LOGICAL_DRIVES_40LD]; + u32 nwrites[MAX_LOGICAL_DRIVES_40LD]; + u32 nwriteblocks[MAX_LOGICAL_DRIVES_40LD]; + u32 rd_errors[MAX_LOGICAL_DRIVES_40LD]; + u32 wr_errors[MAX_LOGICAL_DRIVES_40LD]; +#endif + + /* Host adapter parameters */ + u8 numldrv; + u8 fw_version[7]; + u8 bios_version[7]; + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *controller_proc_dir_entry; + struct proc_dir_entry *proc_read; + struct proc_dir_entry *proc_stat; + struct proc_dir_entry *proc_mbox; + +#if MEGA_HAVE_ENH_PROC + struct proc_dir_entry *proc_rr; + struct proc_dir_entry *proc_battery; +#define MAX_PROC_CHANNELS 4 + struct proc_dir_entry *proc_pdrvstat[MAX_PROC_CHANNELS]; + struct proc_dir_entry *proc_rdrvstat[MAX_PROC_CHANNELS]; +#endif + +#endif + + int has_64bit_addr; /* are we using 64-bit addressing */ + int support_ext_cdb; + int boot_ldrv_enabled; + int boot_ldrv; + int boot_pdrv_enabled; /* boot from physical drive */ + int boot_pdrv_ch; /* boot physical drive channel */ + int boot_pdrv_tgt; /* boot physical drive target */ + + + int support_random_del; /* Do we support random deletion of + logdrvs */ + int read_ldidmap; /* set after logical drive deltion. The + logical drive number must be read from the + map */ + atomic_t quiescent; /* a stage reached when delete logical + drive needs to be done. Stop + sending requests to the hba till + delete operation is completed */ + spinlock_t lock; + + u8 logdrv_chan[MAX_CHANNELS+NVIRT_CHAN]; /* logical drive are on + what channels. */ + int mega_ch_class; + + u8 sglen; /* f/w supported scatter-gather list length */ + + scb_t int_scb; + Scsi_Cmnd int_scmd; + struct semaphore int_mtx; /* To synchronize the internal + commands */ + wait_queue_head_t int_waitq; /* wait queue for internal + cmds */ + + int has_cluster; /* cluster support on this HBA */ +}adapter_t; + + +struct mega_hbas { + int is_bios_enabled; + adapter_t *hostdata_addr; +}; + + +/* + * For state flag. Do not use LSB(8 bits) which are + * reserved for storing info about channels. + */ +#define IN_ABORT 0x80000000L +#define IN_RESET 0x40000000L +#define BOARD_MEMMAP 0x20000000L +#define BOARD_IOMAP 0x10000000L +#define BOARD_40LD 0x08000000L +#define BOARD_64BIT 0x04000000L + +#define INTR_VALID 0x40 + +#define PCI_CONF_AMISIG 0xa0 +#define PCI_CONF_AMISIG64 0xa4 + + +#define MEGA_DMA_TYPE_NONE 0xFFFF +#define MEGA_BULK_DATA 0x0001 +#define MEGA_SGLIST 0x0002 + +/* + * lockscope definitions, callers can specify the lock scope with this data + * type. LOCK_INT would mean the caller has not acquired the lock before + * making the call and LOCK_EXT would mean otherwise. + */ +typedef enum { LOCK_INT, LOCK_EXT } lockscope_t; + +/* + * Parameters for the io-mapped controllers + */ + +/* I/O Port offsets */ +#define CMD_PORT 0x00 +#define ACK_PORT 0x00 +#define TOGGLE_PORT 0x01 +#define INTR_PORT 0x0a + +#define MBOX_BUSY_PORT 0x00 +#define MBOX_PORT0 0x04 +#define MBOX_PORT1 0x05 +#define MBOX_PORT2 0x06 +#define MBOX_PORT3 0x07 +#define ENABLE_MBOX_REGION 0x0B + +/* I/O Port Values */ +#define ISSUE_BYTE 0x10 +#define ACK_BYTE 0x08 +#define ENABLE_INTR_BYTE 0xc0 +#define DISABLE_INTR_BYTE 0x00 +#define VALID_INTR_BYTE 0x40 +#define MBOX_BUSY_BYTE 0x10 +#define ENABLE_MBOX_BYTE 0x00 + + +/* Setup some port macros here */ +#define issue_command(adapter) \ + outb_p(ISSUE_BYTE, (adapter)->base + CMD_PORT) + +#define irq_state(adapter) inb_p((adapter)->base + INTR_PORT) + +#define set_irq_state(adapter, value) \ + outb_p((value), (adapter)->base + INTR_PORT) + +#define irq_ack(adapter) \ + outb_p(ACK_BYTE, (adapter)->base + ACK_PORT) + +#define irq_enable(adapter) \ + outb_p(ENABLE_INTR_BYTE, (adapter)->base + TOGGLE_PORT) + +#define irq_disable(adapter) \ + outb_p(DISABLE_INTR_BYTE, (adapter)->base + TOGGLE_PORT) + + +/* + * This is our SYSDEP area. All kernel specific detail should be placed here - + * as much as possible + */ + +/* + * End of SYSDEP area + */ + const char *megaraid_info (struct Scsi_Host *); -int megaraid_detect (Scsi_Host_Template *); -int megaraid_release (struct Scsi_Host *); -int megaraid_command (Scsi_Cmnd *); -int megaraid_abort (Scsi_Cmnd *); -int megaraid_reset (Scsi_Cmnd *, unsigned int); -int megaraid_queue (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -int megaraid_biosparam (struct scsi_device *, struct block_device *, - sector_t, int *); -int megaraid_proc_info (char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, - mega_scb * scb, int intr); -static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, - u32 * buffer, u32 * length); -static int mega_busyWaitMbox (mega_host_config *); -static int mega_runpendq (mega_host_config *); -static void mega_rundoneq (mega_host_config *); -static void mega_cmd_done (mega_host_config *, mega_scb *, int); -static inline void mega_freeSgList (mega_host_config * megaCfg); -static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, - mega_Enquiry3 * enquiry3, - megaRaidProductInfo * productInfo); + +static int megaraid_detect(Scsi_Host_Template *); +static void mega_find_card(Scsi_Host_Template *, u16, u16); +static int mega_query_adapter(adapter_t *); +static inline int issue_scb(adapter_t *, scb_t *); +static int mega_setup_mailbox(adapter_t *); + +static int megaraid_queue (Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); +static scb_t * mega_build_cmd(adapter_t *, Scsi_Cmnd *, int *); +static inline scb_t *mega_allocate_scb(adapter_t *, Scsi_Cmnd *); +static void __mega_runpendq(adapter_t *); +static inline void mega_runpendq(adapter_t *); +static int issue_scb_block(adapter_t *, u_char *); + +static irqreturn_t megaraid_isr_memmapped(int, void *, struct pt_regs *); +static irqreturn_t megaraid_isr_iomapped(int, void *, struct pt_regs *); + +static void mega_free_scb(adapter_t *, scb_t *); + +static int megaraid_release (struct Scsi_Host *); +static int megaraid_command (Scsi_Cmnd *); +static int megaraid_abort(Scsi_Cmnd *); +static int megaraid_reset(Scsi_Cmnd *); +static int megaraid_abort_and_reset(adapter_t *, Scsi_Cmnd *, int); +static int megaraid_biosparam(struct scsi_device *, struct block_device *, + sector_t, int []); +static int megaraid_proc_info (char *, char **, off_t, int, int, int); +static int mega_print_inquiry(char *, char *); + +static int mega_build_sglist (adapter_t *adapter, scb_t *scb, + u32 *buffer, u32 *length); +static inline int mega_busywait_mbox (adapter_t *); +static int __mega_busywait_mbox (adapter_t *); +static void mega_rundoneq (adapter_t *); +static inline void mega_cmd_done(adapter_t *, u8 [], int, int); +static inline void mega_free_sgl (adapter_t *adapter); +static void mega_8_to_40ld (mraid_inquiry *inquiry, + mega_inquiry3 *enquiry3, mega_product_info *); static int megaraid_reboot_notify (struct notifier_block *, unsigned long, void *); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt); -static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, - mega_ioctl_mbox * mbox); -#endif - -static int megadev_ioctl_entry (struct inode *, struct file *, - unsigned int, unsigned long); -static int megadev_ioctl (struct inode *, struct file *, - unsigned int, unsigned long); -static mega_scb *megadev_doioctl (mega_host_config *, Scsi_Cmnd *); -static void megadev_ioctl_done (Scsi_Cmnd *); -static int mega_init_scb (mega_host_config *); -static void enq_scb_freelist (mega_host_config *, mega_scb *, - int lock, int intr); - -static int mega_is_bios_enabled (mega_host_config *); - -static void mega_create_proc_entry (int index, struct proc_dir_entry *); -static int mega_support_ext_cdb(mega_host_config *); -static mega_passthru* mega_prepare_passthru(mega_host_config *, mega_scb *, - Scsi_Cmnd *); -static mega_ext_passthru* mega_prepare_extpassthru(mega_host_config *, - mega_scb *, Scsi_Cmnd *); -static void mega_enum_raid_scsi(mega_host_config *); -static int mega_partsize(struct block_device *, sector_t, int *); -static void mega_get_boot_ldrv(mega_host_config *); -static int mega_get_lun(mega_host_config *, Scsi_Cmnd *); -static int mega_support_random_del(mega_host_config *); -static int mega_del_logdrv(mega_host_config *, int); -static int mega_do_del_logdrv(mega_host_config *, int); +static int megadev_open (struct inode *, struct file *); +static int megadev_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); +static int mega_m_to_n(void *, nitioctl_t *); +static int mega_n_to_m(void *, megacmd_t *); + +static int mega_init_scb (adapter_t *); + +static int mega_is_bios_enabled (adapter_t *); + +#ifdef CONFIG_PROC_FS +static void mega_create_proc_entry(int, struct proc_dir_entry *); +static int proc_read_config(char *, char **, off_t, int, int *, void *); +static int proc_read_stat(char *, char **, off_t, int, int *, void *); +static int proc_read_mbox(char *, char **, off_t, int, int *, void *); +static int proc_rebuild_rate(char *, char **, off_t, int, int *, void *); +static int proc_battery(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch0(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch1(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch2(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch3(char *, char **, off_t, int, int *, void *); +static int proc_pdrv(adapter_t *, char *, int); +static int proc_rdrv_10(char *, char **, off_t, int, int *, void *); +static int proc_rdrv_20(char *, char **, off_t, int, int *, void *); +static int proc_rdrv_30(char *, char **, off_t, int, int *, void *); +static int proc_rdrv_40(char *, char **, off_t, int, int *, void *); +static int proc_rdrv(adapter_t *, char *, int, int); +#endif +static int mega_adapinq(adapter_t *, dma_addr_t); +static int mega_internal_dev_inquiry(adapter_t *, u8, u8, dma_addr_t); +static inline caddr_t mega_allocate_inquiry(dma_addr_t *, struct pci_dev *); +static inline void mega_free_inquiry(caddr_t, dma_addr_t, struct pci_dev *); +static inline int make_local_pdev(adapter_t *, struct pci_dev **); +static inline void free_local_pdev(struct pci_dev *); + +static int mega_support_ext_cdb(adapter_t *); +static mega_passthru* mega_prepare_passthru(adapter_t *, scb_t *, + Scsi_Cmnd *, int, int); +static mega_ext_passthru* mega_prepare_extpassthru(adapter_t *, + scb_t *, Scsi_Cmnd *, int, int); +static void mega_enum_raid_scsi(adapter_t *); +static void mega_get_boot_drv(adapter_t *); +static inline int mega_get_ldrv_num(adapter_t *, Scsi_Cmnd *, int); +static int mega_support_random_del(adapter_t *); +static int mega_del_logdrv(adapter_t *, int); +static int mega_do_del_logdrv(adapter_t *, int); +static void mega_get_max_sgl(adapter_t *); +static int mega_internal_command(adapter_t *, lockscope_t, megacmd_t *, + mega_passthru *); +static void mega_internal_done(Scsi_Cmnd *); +static int mega_support_cluster(adapter_t *); #endif -/* vi: set ts=4: */ +/* vi: set ts=8 sw=8 tw=78: */ diff -urN linux-2.5.68-bk7/drivers/scsi/ncr53c8xx.c linux-2.5.68-bk8/drivers/scsi/ncr53c8xx.c --- linux-2.5.68-bk7/drivers/scsi/ncr53c8xx.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/ncr53c8xx.c 2003-04-27 04:36:28.000000000 -0700 @@ -397,7 +397,7 @@ #define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f)) -static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs); static void ncr53c8xx_timeout(unsigned long np); static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int func); @@ -4882,7 +4882,7 @@ * Return immediately if reset is in progress. */ if (np->settle_time) { - return SCSI_RESET_PUNT; + return FAILED; } /* * Start the reset process. @@ -4928,7 +4928,7 @@ ncr_queue_done_cmd(np, cmd); } - return SCSI_RESET_SUCCESS; + return SUCCESS; } /*========================================================== @@ -8774,7 +8774,7 @@ ** routine for each host that uses this IRQ. */ -static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs) { unsigned long flags; ncb_p np = (ncb_p) dev_id; @@ -8799,6 +8799,7 @@ ncr_flush_done_cmds(done_list); NCR_UNLOCK_SCSI_DONE(done_list->device->host, flags); } + return IRQ_HANDLED; } /* @@ -8828,59 +8829,24 @@ ** Linux entry point of reset() function */ -#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS -int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags) -#else -int ncr53c8xx_reset(Scsi_Cmnd *cmd) -#endif +int ncr53c8xx_bus_reset(Scsi_Cmnd *cmd) { ncb_p np = ((struct host_data *) cmd->device->host->hostdata)->ncb; int sts; unsigned long flags; Scsi_Cmnd *done_list; -#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS - printk("ncr53c8xx_reset: pid=%lu reset_flags=%x serial_number=%ld serial_number_at_timeout=%ld\n", - cmd->pid, reset_flags, cmd->serial_number, cmd->serial_number_at_timeout); -#else - printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid); -#endif - NCR_LOCK_NCB(np, flags); /* - * We have to just ignore reset requests in some situations. - */ -#if defined SCSI_RESET_NOT_RUNNING - if (cmd->serial_number != cmd->serial_number_at_timeout) { - sts = SCSI_RESET_NOT_RUNNING; - goto out; - } -#endif - /* * If the mid-level driver told us reset is synchronous, it seems * that we must call the done() callback for the involved command, * even if this command was not queued to the low-level driver, - * before returning SCSI_RESET_SUCCESS. + * before returning SUCCESS. */ -#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS - sts = ncr_reset_bus(np, cmd, - (reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS); -#else - sts = ncr_reset_bus(np, cmd, 0); -#endif - - /* - * Since we always reset the controller, when we return success, - * we add this information to the return code. - */ -#if defined SCSI_RESET_HOST_RESET - if (sts == SCSI_RESET_SUCCESS) - sts |= SCSI_RESET_HOST_RESET; -#endif + sts = ncr_reset_bus(np, cmd, 1); -out: done_list = np->done_list; np->done_list = 0; NCR_UNLOCK_NCB(np, flags); @@ -9512,6 +9478,8 @@ .release = zalon7xx_release, .info = ncr53c8xx_info, .queuecommand = ncr53c8xx_queue_command, + .slave_configure = ncr53c8xx_slave_configure, + .eh_bus_reset_handler = ncr53c8xx_bus_reset, .can_queue = SCSI_NCR_CAN_QUEUE, .this_id = 7, .sg_tablesize = SCSI_NCR_SG_TABLESIZE, diff -urN linux-2.5.68-bk7/drivers/scsi/ncr53c8xx.h linux-2.5.68-bk8/drivers/scsi/ncr53c8xx.h --- linux-2.5.68-bk7/drivers/scsi/ncr53c8xx.h 2003-04-19 19:49:10.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/ncr53c8xx.h 2003-04-27 04:36:28.000000000 -0700 @@ -63,35 +63,19 @@ int ncr53c8xx_release(struct Scsi_Host *); -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,75) - #define NCR53C8XX { .name = "ncr53c8xx", \ .detect = ncr53c8xx_detect, \ .release = ncr53c8xx_release, \ .info = ncr53c8xx_info, \ .queuecommand = ncr53c8xx_queue_command,\ .slave_configure = ncr53c8xx_slave_configure,\ - .abort = ncr53c8xx_abort, \ - .reset = ncr53c8xx_reset, \ + .eh_bus_reset_handler = ncr53c8xx_bus_reset, \ .can_queue = SCSI_NCR_CAN_QUEUE, \ .this_id = 7, \ .sg_tablesize = SCSI_NCR_SG_TABLESIZE, \ .cmd_per_lun = SCSI_NCR_CMD_PER_LUN, \ .use_clustering = DISABLE_CLUSTERING} -#else - -#define NCR53C8XX { NULL, NULL, NULL, NULL, \ - NULL, ncr53c8xx_detect, \ - ncr53c8xx_release, ncr53c8xx_info, NULL, \ - ncr53c8xx_queue_command,ncr53c8xx_abort, \ - ncr53c8xx_reset, NULL, scsicam_bios_param, \ - SCSI_NCR_CAN_QUEUE, 7, \ - SCSI_NCR_SG_TABLESIZE, SCSI_NCR_CMD_PER_LUN, \ - 0, 0, DISABLE_CLUSTERING} - -#endif /* LINUX_VERSION_CODE */ - #endif /* defined(HOSTS_C) || defined(MODULE) */ #endif /* NCR53C8XX_H */ diff -urN linux-2.5.68-bk7/drivers/scsi/ppa.c linux-2.5.68-bk8/drivers/scsi/ppa.c --- linux-2.5.68-bk7/drivers/scsi/ppa.c 2003-04-19 19:49:59.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/ppa.c 2003-04-27 04:36:28.000000000 -0700 @@ -219,15 +219,12 @@ printk(" supported by the imm (ZIP Plus) driver. If the\n"); printk(" cable is marked with \"AutoDetect\", this is what has\n"); printk(" happened.\n"); - spin_lock_irq(hreg->host_lock); return 0; } try_again = 1; goto retry_entry; - } else { - spin_lock_irq(hreg->host_lock); + } else return 1; /* return number of hosts detected */ - } } /* This is to give the ppa driver a way to modify the timings (and other diff -urN linux-2.5.68-bk7/drivers/scsi/psi240i.c linux-2.5.68-bk8/drivers/scsi/psi240i.c --- linux-2.5.68-bk7/drivers/scsi/psi240i.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/psi240i.c 2003-04-27 04:36:28.000000000 -0700 @@ -54,7 +54,7 @@ #define DEB(x) #endif -#define MAXBOARDS 2 /* Increase this and the sizes of the arrays below, if you need more. */ +#define MAXBOARDS 6 /* Increase this and the sizes of the arrays below, if you need more. */ #define PORT_DATA 0 #define PORT_ERROR 1 @@ -393,8 +393,8 @@ int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB - PADAPTER240I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure - POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + PADAPTER240I padapter = HOSTDATA (SCpnt->device->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device [SCpnt->device->id];// Pointer to device information UCHAR rc; // command return code SCpnt->scsi_done = done; @@ -569,25 +569,25 @@ int count = 0; int unit; int z; - USHORT port; + USHORT port, port_range = 16; CHIP_CONFIG_N chipConfig; CHIP_DEVICE_N chipDevice[8]; struct Scsi_Host *pshost; - ULONG flags; - for ( board = 0; board < 6; board++ ) // scan for I/O ports + for ( board = 0; board < MAXBOARDS; board++ ) // scan for I/O ports { + pshost = NULL; port = portAddr[board]; // get base address to test - if ( check_region (port, 16) ) // test for I/O addresses available - continue; // nope - if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us + if ( !request_region (port, port_range, "psi240i") ) continue; + if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us + goto host_init_failure; outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup EEPROM/RAM access outw (0, port + REG_ADDRESS); // setup EEPROM address zero if ( inb_p (port) != 0x55 ) // test 1st byte - continue; // nope + goto host_init_failure; // nope if ( inb_p (port + 1) != 0xAA ) // test 2nd byte - continue; // nope + goto host_init_failure; // nope // at this point our board is found and can be accessed. Now we need to initialize // our informatation and register with the kernel. @@ -598,20 +598,11 @@ ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port); if ( !chipConfig.numDrives ) // if no devices on this board - continue; + goto host_init_failure; pshost = scsi_register (tpnt, sizeof(ADAPTER240I)); if(pshost == NULL) - continue; - - save_flags (flags); - cli (); - if ( request_irq (chipConfig.irq, do_Irq_Handler, 0, "psi240i", pshost) ) - { - printk ("Unable to allocate IRQ for PSI-240I controller.\n"); - restore_flags (flags); - goto unregister; - } + goto host_init_failure; PsiHost[chipConfig.irq - 10] = pshost; pshost->unique_id = port; @@ -645,14 +636,22 @@ DEB (printk ("\n blocks = %lX", HOSTDATA(pshost)->device[unit].blocks)); } - restore_flags (flags); - printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x IRQ = %d\n", port, chipConfig.irq); - printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); - count++; - continue; + if ( request_irq (chipConfig.irq, do_Irq_Handler, 0, "psi240i", pshost) == 0 ) + { + printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x IRQ = %d\n", port, chipConfig.irq); + printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); + count++; + continue; + } + + printk ("Unable to allocate IRQ for PSI-240I controller.\n"); + +host_init_failure: + + release_region (port, port_range); + if (pshost) + scsi_unregister (pshost); -unregister:; - scsi_unregister (pshost); } return count; } diff -urN linux-2.5.68-bk7/drivers/scsi/scsi.c linux-2.5.68-bk8/drivers/scsi/scsi.c --- linux-2.5.68-bk7/drivers/scsi/scsi.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi.c 2003-04-27 04:36:28.000000000 -0700 @@ -38,39 +38,24 @@ #include #include - +#include #include #include #include #include -#include -#include -#include -#include -#include +#include #include #include -#include #include #include - -#define __KERNEL_SYSCALLS__ - #include #include - -#include -#include -#include -#include +#include +#include #include "scsi.h" #include "hosts.h" -#ifdef CONFIG_KMOD -#include -#endif - /* * Definitions and constants. @@ -87,18 +72,16 @@ * vendor unique and we will depend upon the command length being * supplied correctly in cmd_len. */ -#define CDB_SIZE(SCpnt) ((((SCpnt->cmnd[0] >> 5) & 7) < 6) ? \ - COMMAND_SIZE(SCpnt->cmnd[0]) : SCpnt->cmd_len) +#define CDB_SIZE(cmd) (((((cmd)->cmnd[0] >> 5) & 7) < 6) ? \ + COMMAND_SIZE((cmd)->cmnd[0]) : (cmd)->cmd_len) /* * Data declarations. */ unsigned long scsi_pid; -Scsi_Cmnd *last_cmnd; +struct scsi_cmnd *last_cmnd; static unsigned long serial_number; -static struct list_head done_q[NR_CPUS] __cacheline_aligned; - /* * List of all highlevel drivers. */ @@ -111,8 +94,7 @@ */ unsigned int scsi_logging_level; -const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = -{ +const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = { "Direct-Access ", "Sequential-Access", "Printer ", @@ -137,14 +119,12 @@ /* * Function prototypes. */ -extern void scsi_times_out(Scsi_Cmnd * SCpnt); +extern void scsi_times_out(struct scsi_cmnd *cmd); -#ifdef MODULE MODULE_PARM(scsi_logging_level, "i"); MODULE_PARM_DESC(scsi_logging_level, "SCSI logging level; should be zero or nonzero"); -#else - +#ifndef MODULE static int __init scsi_logging_setup(char *str) { int tmp; @@ -158,9 +138,7 @@ return 0; } } - __setup("scsi_logging=", scsi_logging_setup); - #endif /* @@ -178,30 +156,26 @@ * to track the difference between a command and a * request. A request is a pending item in the queue that * has not yet reached the top of the queue. + * + * XXX(hch): Need to add a gfp_mask argument. */ - -Scsi_Request *scsi_allocate_request(Scsi_Device * device) +struct scsi_request *scsi_allocate_request(struct scsi_device *sdev) { - Scsi_Request *SRpnt = NULL; - const int offset = ALIGN(sizeof(Scsi_Request), 4); - const int size = offset + sizeof(struct request); - - if (!device) - panic("No device passed to scsi_allocate_request().\n"); + const int offset = ALIGN(sizeof(struct scsi_request), 4); + const int size = offset + sizeof(struct request); + struct scsi_request *sreq; - SRpnt = (Scsi_Request *) kmalloc(size, GFP_ATOMIC); - if( SRpnt == NULL ) - { - return NULL; - } - memset(SRpnt, 0, size); - SRpnt->sr_request = (struct request *)(((char *)SRpnt) + offset); - SRpnt->sr_device = device; - SRpnt->sr_host = device->host; - SRpnt->sr_magic = SCSI_REQ_MAGIC; - SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; + sreq = kmalloc(size, GFP_ATOMIC); + if (likely(sreq != NULL)) { + memset(sreq, 0, size); + sreq->sr_request = (struct request *)(((char *)sreq) + offset); + sreq->sr_device = sdev; + sreq->sr_host = sdev->host; + sreq->sr_magic = SCSI_REQ_MAGIC; + sreq->sr_data_direction = SCSI_DATA_UNKNOWN; + } - return SRpnt; + return sreq; } /* @@ -209,27 +183,21 @@ * * Purpose: Release a request descriptor. * - * Arguments: device - device for which we want a request + * Arguments: sreq - request to release * * Lock status: No locks assumed to be held. This function is SMP-safe. - * - * Returns: Pointer to request block. - * - * Notes: With the new queueing code, it becomes important - * to track the difference between a command and a - * request. A request is a pending item in the queue that - * has not yet reached the top of the queue. We still need - * to free a request when we are done with it, of course. */ -void scsi_release_request(Scsi_Request * req) +void scsi_release_request(struct scsi_request *sreq) { - if( req->sr_command != NULL ) - { - scsi_put_command(req->sr_command); - req->sr_command = NULL; + if (likely(sreq->sr_command != NULL)) { + struct request_queue *q = sreq->sr_device->request_queue; + + scsi_put_command(sreq->sr_command); + sreq->sr_command = NULL; + scsi_queue_next_request(q, NULL); } - kfree(req); + kfree(sreq); } struct scsi_host_cmd_pool { @@ -423,42 +391,31 @@ * * Purpose: Dispatch a command to the low-level driver. * - * Arguments: SCpnt - command block we are dispatching. + * Arguments: cmd - command block we are dispatching. * * Notes: */ -int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) +int scsi_dispatch_cmd(struct scsi_cmnd *cmd) { -#ifdef DEBUG_DELAY - unsigned long clock; -#endif - struct Scsi_Host *host; - int rtn = 0; + struct Scsi_Host *host = cmd->device->host; unsigned long flags = 0; unsigned long timeout; - -#if DEBUG - unsigned long *ret = 0; -#ifdef __mips__ - __asm__ __volatile__("move\t%0,$31":"=r"(ret)); -#else - ret = __builtin_return_address(0); -#endif -#endif - - host = SCpnt->device->host; + int rtn = 1; /* Assign a unique nonzero serial_number. */ + /* XXX(hch): this is racy */ if (++serial_number == 0) serial_number = 1; - SCpnt->serial_number = serial_number; - SCpnt->pid = scsi_pid++; + cmd->serial_number = serial_number; + cmd->pid = scsi_pid++; + /* * If SCSI-2 or lower, store the LUN value in cmnd. */ - if (SCpnt->device->scsi_level <= SCSI_2) - SCpnt->cmnd[1] = (SCpnt->cmnd[1] & 0x1f) | - (SCpnt->device->lun << 5 & 0xe0); + if (cmd->device->scsi_level <= SCSI_2) { + cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) | + (cmd->device->lun << 5 & 0xe0); + } /* * We will wait MIN_RESET_DELAY clock ticks after the last reset so @@ -482,65 +439,63 @@ host->resetting = 0; } - scsi_add_timer(SCpnt, SCpnt->timeout_per_command, scsi_times_out); + scsi_add_timer(cmd, cmd->timeout_per_command, scsi_times_out); /* - * We will use a queued command if possible, otherwise we will emulate the - * queuing and calling of completion function ourselves. + * We will use a queued command if possible, otherwise we will + * emulate the queuing and calling of completion function ourselves. */ - SCSI_LOG_MLQUEUE(3, printk("scsi_dispatch_cmnd (host = %d, channel = %d, target = %d, " - "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n", - SCpnt->device->host->host_no, SCpnt->device->channel, SCpnt->device->id, SCpnt->cmnd, - SCpnt->buffer, SCpnt->bufflen, SCpnt->done)); + SCSI_LOG_MLQUEUE(3, printk("scsi_dispatch_cmnd (host = %d, " + "channel = %d, target = %d, command = %p, " + "buffer = %p, \nbufflen = %d, done = %p)\n", + host->host_no, cmd->device->channel, + cmd->device->id, cmd->cmnd, cmd->buffer, + cmd->bufflen, cmd->done)); + + cmd->state = SCSI_STATE_QUEUED; + cmd->owner = SCSI_OWNER_LOWLEVEL; + + /* + * Before we queue this command, check if the command + * length exceeds what the host adapter can handle. + */ + if (CDB_SIZE(cmd) > cmd->device->host->max_cmd_len) { + SCSI_LOG_MLQUEUE(3, + printk("queuecommand : command too long.\n")); + cmd->result = (DID_ABORT << 16); + + spin_lock_irqsave(host->host_lock, flags); + scsi_done(cmd); + spin_unlock_irqrestore(host->host_lock, flags); + goto out; + } - SCpnt->state = SCSI_STATE_QUEUED; - SCpnt->owner = SCSI_OWNER_LOWLEVEL; if (host->can_queue) { SCSI_LOG_MLQUEUE(3, printk("queuecommand : routine at %p\n", host->hostt->queuecommand)); - /* - * Before we queue this command, check if the command - * length exceeds what the host adapter can handle. - */ - if (CDB_SIZE(SCpnt) <= SCpnt->device->host->max_cmd_len) { - spin_lock_irqsave(host->host_lock, flags); - rtn = host->hostt->queuecommand(SCpnt, scsi_done); - spin_unlock_irqrestore(host->host_lock, flags); - if (rtn != 0) { - scsi_queue_insert(SCpnt, rtn == SCSI_MLQUEUE_DEVICE_BUSY ? rtn : SCSI_MLQUEUE_HOST_BUSY); - SCSI_LOG_MLQUEUE(3, - printk("queuecommand : request rejected\n")); - } - } else { + + spin_lock_irqsave(host->host_lock, flags); + rtn = host->hostt->queuecommand(cmd, scsi_done); + spin_unlock_irqrestore(host->host_lock, flags); + if (rtn) { + scsi_queue_insert(cmd, + (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ? + rtn : SCSI_MLQUEUE_HOST_BUSY); SCSI_LOG_MLQUEUE(3, - printk("queuecommand : command too long.\n")); - SCpnt->result = (DID_ABORT << 16); - spin_lock_irqsave(host->host_lock, flags); - scsi_done(SCpnt); - spin_unlock_irqrestore(host->host_lock, flags); - rtn = 1; + printk("queuecommand : request rejected\n")); } } else { - int temp; + SCSI_LOG_MLQUEUE(3, printk("command() : routine at %p\n", + host->hostt->command)); - SCSI_LOG_MLQUEUE(3, printk("command() : routine at %p\n", host->hostt->command)); - spin_lock_irqsave(host->host_lock, flags); - temp = host->hostt->command(SCpnt); - SCpnt->result = temp; -#ifdef DEBUG_DELAY - spin_unlock_irqrestore(host->host_lock, flags); - clock = jiffies + 4 * HZ; - while (time_before(jiffies, clock)) { - barrier(); - cpu_relax(); - } - printk("done(host = %d, result = %04x) : routine at %p\n", - host->host_no, temp, host->hostt->command); - spin_lock_irqsave(host->host_lock, flags); -#endif - scsi_done(SCpnt); - spin_unlock_irqrestore(host->host_lock, flags); + spin_lock_irqsave(host->host_lock, flags); + cmd->result = host->hostt->command(cmd); + scsi_done(cmd); + spin_unlock_irqrestore(host->host_lock, flags); + rtn = 0; } + + out: SCSI_LOG_MLQUEUE(3, printk("leaving scsi_dispatch_cmnd()\n")); return rtn; } @@ -549,10 +504,10 @@ * Function: scsi_init_cmd_from_req * * Purpose: Queue a SCSI command - * Purpose: Initialize a Scsi_Cmnd from a Scsi_Request + * Purpose: Initialize a struct scsi_cmnd from a struct scsi_request * - * Arguments: SCpnt - command descriptor. - * SRpnt - Request from the queue. + * Arguments: cmd - command descriptor. + * sreq - Request from the queue. * * Lock status: None needed. * @@ -569,65 +524,61 @@ * The request structure is generally used by ioctls and character * devices. */ -void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt) +void scsi_init_cmd_from_req(struct scsi_cmnd *cmd, struct scsi_request *sreq) { - struct Scsi_Host *host = SCpnt->device->host; + sreq->sr_command = cmd; - SCpnt->owner = SCSI_OWNER_MIDLEVEL; - SRpnt->sr_command = SCpnt; + cmd->owner = SCSI_OWNER_MIDLEVEL; + cmd->cmd_len = sreq->sr_cmd_len; + cmd->use_sg = sreq->sr_use_sg; - if (!host) { - panic("Invalid or not present host.\n"); - } - - SCpnt->cmd_len = SRpnt->sr_cmd_len; - SCpnt->use_sg = SRpnt->sr_use_sg; - - SCpnt->request = SRpnt->sr_request; - memcpy((void *) SCpnt->data_cmnd, (const void *) SRpnt->sr_cmnd, - sizeof(SCpnt->data_cmnd)); - SCpnt->reset_chain = NULL; - SCpnt->serial_number = 0; - SCpnt->serial_number_at_timeout = 0; - SCpnt->bufflen = SRpnt->sr_bufflen; - SCpnt->buffer = SRpnt->sr_buffer; - SCpnt->flags = 0; - SCpnt->retries = 0; - SCpnt->allowed = SRpnt->sr_allowed; - SCpnt->done = SRpnt->sr_done; - SCpnt->timeout_per_command = SRpnt->sr_timeout_per_command; - - SCpnt->sc_data_direction = SRpnt->sr_data_direction; - - SCpnt->sglist_len = SRpnt->sr_sglist_len; - SCpnt->underflow = SRpnt->sr_underflow; - - SCpnt->sc_request = SRpnt; + cmd->request = sreq->sr_request; + memcpy(cmd->data_cmnd, sreq->sr_cmnd, sizeof(cmd->data_cmnd)); + cmd->reset_chain = NULL; + cmd->serial_number = 0; + cmd->serial_number_at_timeout = 0; + cmd->bufflen = sreq->sr_bufflen; + cmd->buffer = sreq->sr_buffer; + cmd->flags = 0; + cmd->retries = 0; + cmd->allowed = sreq->sr_allowed; + cmd->done = sreq->sr_done; + cmd->timeout_per_command = sreq->sr_timeout_per_command; + cmd->sc_data_direction = sreq->sr_data_direction; + cmd->sglist_len = sreq->sr_sglist_len; + cmd->underflow = sreq->sr_underflow; + cmd->sc_request = sreq; + memcpy(cmd->cmnd, sreq->sr_cmnd, sizeof(sreq->sr_cmnd)); - memcpy((void *) SCpnt->cmnd, (const void *) SRpnt->sr_cmnd, - sizeof(SCpnt->cmnd)); - /* Zero the sense buffer. Some host adapters automatically request + /* + * Zero the sense buffer. Some host adapters automatically request * sense on error. 0 is not a valid sense code. */ - memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); - SCpnt->request_buffer = SRpnt->sr_buffer; - SCpnt->request_bufflen = SRpnt->sr_bufflen; - SCpnt->old_use_sg = SCpnt->use_sg; - if (SCpnt->cmd_len == 0) - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->old_cmd_len = SCpnt->cmd_len; - SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; - SCpnt->old_underflow = SCpnt->underflow; - - /* Start the timer ticking. */ - - SCpnt->internal_timeout = NORMAL_TIMEOUT; - SCpnt->abort_reason = 0; - SCpnt->result = 0; + memset(cmd->sense_buffer, 0, sizeof(sreq->sr_sense_buffer)); + cmd->request_buffer = sreq->sr_buffer; + cmd->request_bufflen = sreq->sr_bufflen; + cmd->old_use_sg = cmd->use_sg; + if (cmd->cmd_len == 0) + cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); + cmd->old_cmd_len = cmd->cmd_len; + cmd->sc_old_data_direction = cmd->sc_data_direction; + cmd->old_underflow = cmd->underflow; + + /* + * Start the timer ticking. + */ + cmd->internal_timeout = NORMAL_TIMEOUT; + cmd->abort_reason = 0; + cmd->result = 0; SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_init_cmd_from_req()\n")); } +/* + * Per-CPU I/O completion queue. + */ +static struct list_head done_q[NR_CPUS] __cacheline_aligned; + /** * scsi_done - Enqueue the finished SCSI command into the done queue. * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives @@ -643,9 +594,8 @@ */ void scsi_done(struct scsi_cmnd *cmd) { - int cpu; unsigned long flags; - struct list_head *pdone_q; + int cpu; /* * We don't have to worry about this one timing out any more. @@ -658,7 +608,9 @@ if (!scsi_delete_timer(cmd)) return; - /* Set the serial numbers back to zero */ + /* + * Set the serial numbers back to zero + */ cmd->serial_number = 0; cmd->serial_number_at_timeout = 0; cmd->state = SCSI_STATE_BHQUEUE; @@ -670,12 +622,9 @@ * and need no spinlock. */ local_irq_save(flags); - cpu = smp_processor_id(); - pdone_q = &done_q[cpu]; - list_add_tail(&cmd->eh_entry, pdone_q); + list_add_tail(&cmd->eh_entry, &done_q[cpu]); cpu_raise_softirq(cpu, SCSI_SOFTIRQ); - local_irq_restore(flags); } @@ -763,17 +712,15 @@ /* * Dump the sense information too. */ - if ((status_byte(cmd->result)&CHECK_CONDITION) != 0) { + if (status_byte(cmd->result) & CHECK_CONDITION) SCSI_LOG_MLCOMPLETE(3, print_sense("bh", cmd)); - } - if (!scsi_eh_scmd_add(cmd, 0)) { - /* - * We only get here if the error - * recovery thread has died. - */ + /* + * We only fail here if the error recovery thread + * has died. + */ + if (!scsi_eh_scmd_add(cmd, 0)) scsi_finish_command(cmd); - } } } } @@ -788,20 +735,20 @@ * level drivers should not become re-entrant as a result of * this. */ -int scsi_retry_command(Scsi_Cmnd * SCpnt) +int scsi_retry_command(struct scsi_cmnd *cmd) { /* * Restore the SCSI command state. */ - scsi_setup_cmd_retry(SCpnt); + scsi_setup_cmd_retry(cmd); /* * Zero the sense information from the last time we tried * this command. */ - memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - return scsi_dispatch_cmd(SCpnt); + return scsi_dispatch_cmd(cmd); } /* @@ -811,68 +758,66 @@ * request, waking processes that are waiting on results, * etc. */ -void scsi_finish_command(Scsi_Cmnd * SCpnt) +void scsi_finish_command(struct scsi_cmnd *cmd) { - struct Scsi_Host *host; - Scsi_Device *device; - Scsi_Request * SRpnt; + struct scsi_device *sdev = cmd->device; + struct Scsi_Host *shost = sdev->host; + struct scsi_request *sreq; unsigned long flags; - host = SCpnt->device->host; - device = SCpnt->device; + scsi_host_busy_dec_and_test(shost, sdev); - /* - * We need to protect the decrement, as otherwise a race condition - * would exist. Fiddling with SCpnt isn't a problem as the - * design only allows a single SCpnt to be active in only - * one execution context, but the device and host structures are - * shared. - */ - scsi_host_busy_dec_and_test(host, device); - spin_lock_irqsave(SCpnt->device->request_queue->queue_lock, flags); - SCpnt->device->device_busy--; - spin_unlock_irqrestore(SCpnt->device->request_queue->queue_lock, flags); + /* + * XXX(hch): We really want a nice helper for this.. + */ + spin_lock_irqsave(&sdev->sdev_lock, flags); + sdev->device_busy--; + spin_unlock_irqrestore(&sdev->sdev_lock, flags); /* * Clear the flags which say that the device/host is no longer * capable of accepting new commands. These are set in scsi_queue.c * for both the queue full condition on a device, and for a * host full condition on the host. + * + * XXX(hch): What about locking? */ - host->host_blocked = 0; - device->device_blocked = 0; + shost->host_blocked = 0; + sdev->device_blocked = 0; /* * If we have valid sense information, then some kind of recovery * must have taken place. Make a note of this. */ - if (SCSI_SENSE_VALID(SCpnt)) { - SCpnt->result |= (DRIVER_SENSE << 24); - } - SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion for device %d %x\n", - SCpnt->device->id, SCpnt->result)); + if (SCSI_SENSE_VALID(cmd)) + cmd->result |= (DRIVER_SENSE << 24); + + SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion " + "for device %d %x\n", sdev->id, cmd->result)); - SCpnt->owner = SCSI_OWNER_HIGHLEVEL; - SCpnt->state = SCSI_STATE_FINISHED; + cmd->owner = SCSI_OWNER_HIGHLEVEL; + cmd->state = SCSI_STATE_FINISHED; + + /* + * We can get here with use_sg=0, causing a panic in the upper level + */ + cmd->use_sg = cmd->old_use_sg; - /* We can get here with use_sg=0, causing a panic in the upper level (DB) */ - SCpnt->use_sg = SCpnt->old_use_sg; - - /* - * If there is an associated request structure, copy the data over before we call the - * completion function. - */ - SRpnt = SCpnt->sc_request; - if( SRpnt != NULL ) { - SRpnt->sr_result = SRpnt->sr_command->result; - if( SRpnt->sr_result != 0 ) { - memcpy(SRpnt->sr_sense_buffer, - SRpnt->sr_command->sense_buffer, - sizeof(SRpnt->sr_sense_buffer)); + /* + * If there is an associated request structure, copy the data over + * before we call the completion function. + */ + sreq = cmd->sc_request; + if (sreq) { + sreq->sr_result = sreq->sr_command->result; + if (sreq->sr_result) { + memcpy(sreq->sr_sense_buffer, + sreq->sr_command->sense_buffer, + sizeof(sreq->sr_sense_buffer)); } } - SCpnt->done(SCpnt); + cmd->done(cmd); } /* @@ -881,7 +826,7 @@ * Purpose: Allow low level drivers to tell us to change the queue depth * on a specific SCSI device * - * Arguments: SDpnt - SCSI Device in question + * Arguments: sdev - SCSI Device in question * tagged - Do we use tagged queueing (non-0) or do we treat * this device as an untagged device (0) * tags - Number of tags allowed if tagged queueing enabled, @@ -896,8 +841,10 @@ * the right thing depending on whether or not the device is * currently active and whether or not it even has the * command blocks built yet. + * + * XXX(hch): What exactly is device_request_lock trying to protect? */ -void scsi_adjust_queue_depth(Scsi_Device *SDpnt, int tagged, int tags) +void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) { static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED; unsigned long flags; @@ -905,35 +852,35 @@ /* * refuse to set tagged depth to an unworkable size */ - if(tags <= 0) + if (tags <= 0) return; /* * Limit max queue depth on a single lun to 256 for now. Remember, * we allocate a struct scsi_command for each of these and keep it * around forever. Too deep of a depth just wastes memory. */ - if(tags > 256) + if (tags > 256) return; spin_lock_irqsave(&device_request_lock, flags); - SDpnt->queue_depth = tags; - switch(tagged) { + sdev->queue_depth = tags; + switch (tagged) { case MSG_ORDERED_TAG: - SDpnt->ordered_tags = 1; - SDpnt->simple_tags = 1; + sdev->ordered_tags = 1; + sdev->simple_tags = 1; break; case MSG_SIMPLE_TAG: - SDpnt->ordered_tags = 0; - SDpnt->simple_tags = 1; + sdev->ordered_tags = 0; + sdev->simple_tags = 1; break; default: printk(KERN_WARNING "(scsi%d:%d:%d:%d) " "scsi_adjust_queue_depth, bad queue type, " - "disabled\n", SDpnt->host->host_no, - SDpnt->channel, SDpnt->id, SDpnt->lun); + "disabled\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); case 0: - SDpnt->ordered_tags = SDpnt->simple_tags = 0; - SDpnt->queue_depth = tags; + sdev->ordered_tags = sdev->simple_tags = 0; + sdev->queue_depth = tags; break; } spin_unlock_irqrestore(&device_request_lock, flags); @@ -946,7 +893,7 @@ * specific SCSI device to determine if and when there is a * need to adjust the queue depth on the device. * - * Arguments: SDpnt - SCSI Device in question + * Arguments: sdev - SCSI Device in question * depth - Current number of outstanding SCSI commands on * this device, not counting the one returned as * QUEUE_FULL. @@ -961,33 +908,32 @@ * Notes: Low level drivers may call this at any time and we will do * "The Right Thing." We are interrupt context safe. */ -int scsi_track_queue_full(Scsi_Device *SDptr, int depth) +int scsi_track_queue_full(struct scsi_device *sdev, int depth) { - if((jiffies >> 4) != SDptr->last_queue_full_time) { - SDptr->last_queue_full_time = (jiffies >> 4); - if(SDptr->last_queue_full_depth == depth) - SDptr->last_queue_full_count++; - else { - SDptr->last_queue_full_count = 1; - SDptr->last_queue_full_depth = depth; - } - if(SDptr->last_queue_full_count > 10) { - if(SDptr->last_queue_full_depth < 8) { - /* Drop back to untagged */ - scsi_adjust_queue_depth(SDptr, 0 /* untagged */, - SDptr->host->cmd_per_lun); - return -1; - } - if(SDptr->ordered_tags) - scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, - depth); - else - scsi_adjust_queue_depth(SDptr, MSG_SIMPLE_TAG, - depth); - return depth; - } + if ((jiffies >> 4) == sdev->last_queue_full_time) + return 0; + + sdev->last_queue_full_time = (jiffies >> 4); + if (sdev->last_queue_full_depth != depth) { + sdev->last_queue_full_count = 1; + sdev->last_queue_full_depth = depth; + } else { + sdev->last_queue_full_count++; } - return 0; + + if (sdev->last_queue_full_count <= 10) + return 0; + if (sdev->last_queue_full_depth < 8) { + /* Drop back to untagged */ + scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + return -1; + } + + if (sdev->ordered_tags) + scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth); + else + scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth); + return depth; } @@ -1306,7 +1252,7 @@ struct list_head *lh, *lh_sf; unsigned long flags; - sdev->online = FALSE; + sdev->online = 0; spin_lock_irqsave(&sdev->list_lock, flags); list_for_each_entry(scmd, &sdev->cmd_list, list) { @@ -1316,9 +1262,8 @@ * that the command has already timed out or * finished. */ - if (!scsi_delete_timer(scmd)) { + if (!scsi_delete_timer(scmd)) continue; - } list_add_tail(&scmd->eh_entry, &active_list); } } @@ -1366,10 +1311,8 @@ { sdev->attached--; } + /* - * This entry point should be called by a loadable module if it is trying - * add a high level scsi driver to the system. - * * This entry point is called from the upper level module's module_init() * routine. That implies that when this function is called, the * scsi_mod module is locked down because of upper module layering and @@ -1384,7 +1327,7 @@ */ int scsi_register_device(struct Scsi_Device_Template *tpnt) { - Scsi_Device *SDpnt; + struct scsi_device *sdev; struct Scsi_Host *shpnt; #ifdef CONFIG_KMOD @@ -1403,15 +1346,15 @@ for (shpnt = scsi_host_get_next(NULL); shpnt; shpnt = scsi_host_get_next(shpnt)) - list_for_each_entry (SDpnt, &shpnt->my_devices, siblings) - (*tpnt->attach) (SDpnt); + list_for_each_entry(sdev, &shpnt->my_devices, siblings) + (*tpnt->attach)(sdev); return 0; } int scsi_unregister_device(struct Scsi_Device_Template *tpnt) { - Scsi_Device *SDpnt; + struct scsi_device *sdev; struct Scsi_Host *shpnt; /* @@ -1419,8 +1362,8 @@ */ for (shpnt = scsi_host_get_next(NULL); shpnt; shpnt = scsi_host_get_next(shpnt)) { - list_for_each_entry(SDpnt, &shpnt->my_devices, siblings) - (*tpnt->detach) (SDpnt); + list_for_each_entry(sdev, &shpnt->my_devices, siblings) + (*tpnt->detach)(sdev); } /* @@ -1447,8 +1390,7 @@ MODULE_LICENSE("GPL"); #ifndef MODULE - -int __init setup_scsi_dev_flags(char *str) +static int __init setup_scsi_dev_flags(char *str) { scsi_dev_flags = str; return 1; @@ -1470,7 +1412,6 @@ } } __setup("scsi_default_dev_flags=", setup_scsi_default_dev_flags); - #endif static int __init init_scsi(void) diff -urN linux-2.5.68-bk7/drivers/scsi/scsi.h linux-2.5.68-bk8/drivers/scsi/scsi.h --- linux-2.5.68-bk7/drivers/scsi/scsi.h 2003-04-19 19:48:56.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi.h 2003-04-27 04:36:28.000000000 -0700 @@ -87,21 +87,6 @@ #endif /* - * Used for debugging the new queueing code. We want to make sure - * that the lock state is consistent with design. Only do this in - * the user space simulator. - */ -#define ASSERT_LOCK(_LOCK, _COUNT) - -#if defined(CONFIG_SMP) && defined(CONFIG_USER_DEBUG) -#undef ASSERT_LOCK -#define ASSERT_LOCK(_LOCK,_COUNT) \ - { if( (_LOCK)->lock != _COUNT ) \ - panic("Lock count inconsistent %s %d\n", __FILE__, __LINE__); \ - } -#endif - -/* * Use these to separate status msg and our bytes * * These are set by: @@ -470,13 +455,9 @@ extern void scsi_proc_host_add(struct Scsi_Host *); extern void scsi_proc_host_rm(struct Scsi_Host *); - -extern struct proc_dir_entry *proc_scsi; -extern void proc_print_scsidevice(Scsi_Device * sdev, char *buffer, int *size, int len); #else static inline int scsi_init_procfs(void) { return 0; } static inline void scsi_exit_procfs(void) { ; } -static inline void proc_print_scsidevice(Scsi_Device * sdev, char *buffer, int *size, int len) { ; } static inline void scsi_proc_host_add(struct Scsi_Host *); static inline void scsi_proc_host_rm(struct Scsi_Host *); diff -urN linux-2.5.68-bk7/drivers/scsi/scsi_error.c linux-2.5.68-bk8/drivers/scsi/scsi_error.c --- linux-2.5.68-bk7/drivers/scsi/scsi_error.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi_error.c 2003-04-27 04:36:28.000000000 -0700 @@ -1334,10 +1334,6 @@ { struct scsi_request *sreq = scmd->sc_request; - scmd->sc_request = NULL; - sreq->sr_command = NULL; - - scsi_put_command(scmd); scsi_release_request(sreq); } diff -urN linux-2.5.68-bk7/drivers/scsi/scsi_lib.c linux-2.5.68-bk8/drivers/scsi/scsi_lib.c --- linux-2.5.68-bk7/drivers/scsi/scsi_lib.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi_lib.c 2003-04-27 04:36:28.000000000 -0700 @@ -733,7 +733,7 @@ if (good_sectors >= 0) { SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d sectors done.\n", req->nr_sectors, good_sectors)); - SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n ", cmd->use_sg)); + SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg)); if (clear_errors) req->errors = 0; diff -urN linux-2.5.68-bk7/drivers/scsi/scsi_proc.c linux-2.5.68-bk8/drivers/scsi/scsi_proc.c --- linux-2.5.68-bk7/drivers/scsi/scsi_proc.c 2003-04-19 19:48:50.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi_proc.c 2003-04-27 04:36:28.000000000 -0700 @@ -149,8 +149,8 @@ remove_proc_entry(shost->hostt->proc_name, proc_scsi); } -/* XXX: this shouldn't really be exposed to drivers. */ -void proc_print_scsidevice(Scsi_Device * sdev, char *buffer, int *size, int len) +static void proc_print_scsidevice(struct scsi_device* sdev, char *buffer, + int *size, int len) { int x, y = *size; @@ -195,7 +195,6 @@ *size = y; return; } -EXPORT_SYMBOL(proc_print_scsidevice); /* * proc_scsi_dev_info_read: dump the scsi_dev_info_list via diff -urN linux-2.5.68-bk7/drivers/scsi/scsi_scan.c linux-2.5.68-bk8/drivers/scsi/scsi_scan.c --- linux-2.5.68-bk7/drivers/scsi/scsi_scan.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi_scan.c 2003-04-27 04:36:28.000000000 -0700 @@ -376,15 +376,15 @@ } /** - * scsi_alloc_sdev - allocate and setup a Scsi_Device + * scsi_alloc_sdev - allocate and setup a scsi_Device * * Description: - * Allocate, initialize for io, and return a pointer to a Scsi_Device. - * Stores the @shost, @channel, @id, and @lun in the Scsi_Device, and - * adds Scsi_Device to the appropriate list. + * Allocate, initialize for io, and return a pointer to a scsi_Device. + * Stores the @shost, @channel, @id, and @lun in the scsi_Device, and + * adds scsi_Device to the appropriate list. * * Return value: - * Scsi_Device pointer, or NULL on failure. + * scsi_Device pointer, or NULL on failure. **/ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, uint channel, uint id, uint lun) @@ -465,9 +465,7 @@ return sdev; out_free_queue: - if (sdev->request_queue) - scsi_free_queue(sdev->request_queue); - + scsi_free_queue(sdev->request_queue); out_free_dev: kfree(sdev); out: @@ -476,8 +474,8 @@ } /** - * scsi_free_sdev - cleanup and free a Scsi_Device - * @sdev: cleanup and free this Scsi_Device + * scsi_free_sdev - cleanup and free a scsi_device + * @sdev: cleanup and free this scsi_device * * Description: * Undo the actions in scsi_alloc_sdev, including removing @sdev from @@ -499,8 +497,7 @@ spin_lock_irqsave(sdev->host->host_lock, flags); list_del(&sdev->starved_entry); if (sdev->single_lun) { - sdev->sdev_target->starget_refcnt--; - if (sdev->sdev_target->starget_refcnt == 0) + if (--sdev->sdev_target->starget_refcnt == 0) kfree(sdev->sdev_target); } spin_unlock_irqrestore(sdev->host->host_lock, flags); @@ -509,481 +506,6 @@ } /** - * scsi_get_evpd_page - get a list of supported vpd pages - * @sdev: Scsi_Device to send an INQUIRY VPD - * @sreq: Scsi_Request associated with @sdev - * - * Description: - * Get SCSI INQUIRY Vital Product Data page 0 - a list of supported - * VPD pages. - * - * Return: - * A pointer to data containing the results on success, else NULL. - **/ -unsigned char *scsi_get_evpd_page(Scsi_Device *sdev, Scsi_Request *sreq) -{ - unsigned char *evpd_page; - unsigned char scsi_cmd[MAX_COMMAND_SIZE]; - int max_lgth = 255; - -retry: - evpd_page = kmalloc(max_lgth, GFP_ATOMIC | - (sdev->host->unchecked_isa_dma) ? - GFP_DMA : 0); - if (!evpd_page) { - printk(KERN_WARNING "scsi scan: Allocation failure identifying" - " host %d channel %d id %d lun %d, device might be" - " improperly identified.\n", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - return NULL; - } - - memset(scsi_cmd, 0, MAX_COMMAND_SIZE); - scsi_cmd[0] = INQUIRY; - scsi_cmd[1] = 0x01; - scsi_cmd[4] = max_lgth; - sreq->sr_cmd_len = 0; - sreq->sr_sense_buffer[0] = 0; - sreq->sr_sense_buffer[2] = 0; - sreq->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(sreq, (void *) scsi_cmd, (void *) evpd_page, - max_lgth, SCSI_TIMEOUT+4*HZ, 3); - - if (sreq->sr_result) { - kfree(evpd_page); - return NULL; - } - - /* - * check to see if response was truncated - */ - if (evpd_page[3] > max_lgth) { - max_lgth = evpd_page[3] + 4; - kfree(evpd_page); - goto retry; - } - - /* - * Some ill behaved devices return the standard inquiry here - * rather than the evpd data, snoop the data to verify. - */ - if (evpd_page[3] > 16) { - /* - * If the vendor id appears in the evpd page assume the - * page is invalid. - */ - if (!strncmp(&evpd_page[8], sdev->vendor, 8)) { - kfree(evpd_page); - return NULL; - } - } - return evpd_page; -} - - -/* - * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the - * SCSI Primary Commands specification for details. - * - * XXX The following defines should be in scsi.h - */ - -/* - * id type values of id descriptors. These are assumed to fit in 4 bits, - * else the code using hex_str[id_type] needs modification. - */ -#define SCSI_ID_VENDOR_SPECIFIC 0 -#define SCSI_ID_T10_VENDOR 1 -#define SCSI_ID_EUI_64 2 -#define SCSI_ID_NAA 3 - -/* - * Supported NAA values. These fit in 4 bits, so the don't care value - * cannot conflict with real values. - * - */ -#define SCSI_ID_NAA_DONT_CARE 0xff -#define SCSI_ID_NAA_IEEE_REG 5 -#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6 - -/* - * Supported Code Set values. - */ -#define SCSI_ID_BINARY 1 -#define SCSI_ID_ASCII 2 - -/* - * Use a priority based list of id, naa, and binary/ascii for the - * identifier descriptor in VPD page 0x83. - * - * Brute force search for a match starting with the first value in - * id_search_list. This is not a performance issue, since there - * is normally one or some small number of descriptors. - */ -struct scsi_id_search_values { - int id_type; - int naa_type; - int code_set; -}; - -static const struct scsi_id_search_values id_search_list[] = { - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, - /* - * Devices already exist using NAA values that are now marked - * reserved. These should not conflict with other values, or it is - * a bug in the device. As long as we find the IEEE extended one - * first, we really don't care what other ones are used. Using - * don't care here means that a device that returns multiple - * non-IEEE descriptors in a random order will get different - * names. - */ - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, -}; - -/** - * scsi_check_fill_deviceid - check the id and if OK fill it - * @sdev: device to use for error messages - * @id_page: id descriptor for INQUIRY VPD DEVICE ID, page 0x83 - * @name: store the id in name (of size DEVICE_NAME_SIZE > 26) - * @id_search: store if the id_page matches these values - * - * Description: - * Check if @id_page matches the @id_search, if so store an id (uid) - * into name, that is all zero on entrance. - * - * Return: - * 0: Success - * 1: No match - **/ -static int scsi_check_fill_deviceid(Scsi_Device *sdev, char *id_page, - char *name, const struct scsi_id_search_values *id_search) -{ - static const char hex_str[]="0123456789abcdef"; - int i, j; - - /* - * ASSOCIATION must be with the device (value 0) - */ - if ((id_page[1] & 0x30) != 0) - return 1; - - if ((id_page[1] & 0x0f) != id_search->id_type) - return 1; - /* - * Possibly check NAA sub-type. - */ - if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && - (id_search->naa_type != (id_page[4] & 0xf0) >> 4)) { - return 1; - } - - /* - * Check for matching code set - ASCII or BINARY. - */ - if ((id_page[0] & 0x0f) != id_search->code_set) - return 1; - - /* - * All OK - store ID - */ - name[0] = hex_str[id_search->id_type]; - - /* - * Prepend the vendor and model before the id, since the id - * might not be unique across all vendors and models. - * The same code is used below, with a different size. - */ - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) { - strncat(name, sdev->vendor, 8); - strncat(name, sdev->model, 16); - } - - i = 4; - j = strlen(name); - if ((id_page[0] & 0x0f) == SCSI_ID_ASCII) { - /* - * ASCII descriptor. - */ - while (i < 4 + id_page[3] && j < DEVICE_NAME_SIZE-1) - name[j++] = id_page[i++]; - } else { - /* - * Binary descriptor, convert to ASCII, using two bytes of - * ASCII for each byte in the id_page. - */ - while (i < 4 + id_page[3] && j < DEVICE_NAME_SIZE-2) { - name[j++] = hex_str[(id_page[i] & 0xf0) >> 4]; - name[j++] = hex_str[id_page[i] & 0x0f]; - i++; - } - } - return 0; -} - -/** - * scsi_get_deviceid - get a device id using INQUIRY VPD page 0x83 - * @sdev: get the identifer of this device - * @sreq: Scsi_Requeset associated with @sdev - * - * Description: - * Try to get an id (serial number) for device @sdev using a SCSI - * Vital Product Data page 0x83 (device id). - * - * Return: - * 0: Failure - * 1: Success - **/ -static int scsi_get_deviceid(Scsi_Device *sdev, Scsi_Request *sreq) -{ - unsigned char *id_page; - unsigned char scsi_cmd[MAX_COMMAND_SIZE]; - int id_idx, scnt, ret; - int max_lgth = 255; - -retry: - id_page = kmalloc(max_lgth, GFP_ATOMIC | - (sdev->host->unchecked_isa_dma) ? - GFP_DMA : 0); - if (!id_page) { - printk(KERN_WARNING "scsi scan: Allocation failure identifying" - " host %d channel %d id %d lun %d, device might be" - " improperly identified.\n", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - return 0; - } - - memset(scsi_cmd, 0, MAX_COMMAND_SIZE); - scsi_cmd[0] = INQUIRY; - scsi_cmd[1] = 0x01; - scsi_cmd[2] = 0x83; - scsi_cmd[4] = max_lgth; - sreq->sr_cmd_len = 0; - sreq->sr_sense_buffer[0] = 0; - sreq->sr_sense_buffer[2] = 0; - sreq->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(sreq, (void *) scsi_cmd, (void *) id_page, - max_lgth, SCSI_TIMEOUT+4*HZ, 3); - if (sreq->sr_result) { - ret = 0; - goto leave; - } - - /* - * check to see if response was truncated - */ - if (id_page[3] > max_lgth) { - max_lgth = id_page[3] + 4; - kfree(id_page); - goto retry; - } - - /* - * Search for a match in the prioritized id_search_list. - */ - for (id_idx = 0; id_idx < ARRAY_SIZE(id_search_list); id_idx++) { - /* - * Examine each descriptor returned. There is normally only - * one or a small number of descriptors. - */ - for (scnt = 4; scnt <= id_page[3] + 3; - scnt += id_page[scnt + 3] + 4) { - if ((scsi_check_fill_deviceid(sdev, &id_page[scnt], - sdev->sdev_driverfs_dev.name, - &id_search_list[id_idx])) == 0) { - SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO - "scsi scan: host %d channel %d id %d lun %d" - " used id desc %d/%d/%d\n", - sdev->host->host_no, sdev->channel, - sdev->id, sdev->lun, - id_search_list[id_idx].id_type, - id_search_list[id_idx].naa_type, - id_search_list[id_idx].code_set)); - ret = 1; - goto leave; - } else { - SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO - "scsi scan: host %d channel %d id %d lun %d" - " no match/error id desc %d/%d/%d\n", - sdev->host->host_no, sdev->channel, - sdev->id, sdev->lun, - id_search_list[id_idx].id_type, - id_search_list[id_idx].naa_type, - id_search_list[id_idx].code_set)); - } - /* - * scsi_check_fill_deviceid can fill the first - * byte of name with a non-zero value, reset it. - */ - sdev->sdev_driverfs_dev.name[0] = '\0'; - } - } - ret = 0; - - leave: - kfree(id_page); - return ret; -} - -/** - * scsi_get_serialnumber - get a serial number using INQUIRY page 0x80 - * @sdev: get the serial number of this device - * @sreq: Scsi_Requeset associated with @sdev - * - * Description: - * Send a SCSI INQUIRY page 0x80 to @sdev to get a serial number. - * - * Return: - * 0: Failure - * 1: Success - **/ -int scsi_get_serialnumber(Scsi_Device *sdev, Scsi_Request *sreq) -{ - unsigned char *serialnumber_page; - unsigned char scsi_cmd[MAX_COMMAND_SIZE]; - const int max_lgth = 255; - int len; - - serialnumber_page = kmalloc(max_lgth, GFP_ATOMIC | - (sdev->host->unchecked_isa_dma) ? GFP_DMA : 0); - if (!serialnumber_page) { - printk(KERN_WARNING "scsi scan: Allocation failure identifying" - " host %d channel %d id %d lun %d, device might be" - " improperly identified.\n", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - return 0; - } - - memset(scsi_cmd, 0, MAX_COMMAND_SIZE); - scsi_cmd[0] = INQUIRY; - scsi_cmd[1] = 0x01; - scsi_cmd[2] = 0x80; - scsi_cmd[4] = max_lgth; - sreq->sr_cmd_len = 0; - sreq->sr_sense_buffer[0] = 0; - sreq->sr_sense_buffer[2] = 0; - sreq->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(sreq, (void *) scsi_cmd, (void *) serialnumber_page, - max_lgth, SCSI_TIMEOUT+4*HZ, 3); - - if (sreq->sr_result) - goto leave; - - /* - * a check to see if response was truncated is superfluous, - * since serialnumber_page[3] cannot be larger than 255 - */ - - sdev->sdev_driverfs_dev.name[0] = SCSI_UID_SER_NUM; - strncat(sdev->sdev_driverfs_dev.name, sdev->vendor, 8); - strncat(sdev->sdev_driverfs_dev.name, sdev->model, 16); - len = serialnumber_page[3]; - if (len > DEVICE_NAME_SIZE-26) - len = DEVICE_NAME_SIZE-26; - strncat(sdev->sdev_driverfs_dev.name, &serialnumber_page[4], len); - kfree(serialnumber_page); - return 1; - leave: - memset(sdev->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); - kfree(serialnumber_page); - return 0; -} - -/** - * scsi_get_default_name - get a default name - * @sdev: get a default name for this device - * - * Description: - * Set the name of @sdev (of size DEVICE_NAME_SIZE > 29) to the - * concatenation of the vendor, model, and revision found in @sdev. - * - * Return: - * 1: Success - **/ -int scsi_get_default_name(Scsi_Device *sdev) -{ - sdev->sdev_driverfs_dev.name[0] = SCSI_UID_UNKNOWN; - strncpy(&sdev->sdev_driverfs_dev.name[1], sdev->vendor, 8); - strncat(sdev->sdev_driverfs_dev.name, sdev->model, 16); - strncat(sdev->sdev_driverfs_dev.name, sdev->rev, 4); - return 1; -} - -/** - * scsi_load_identifier: - * @sdev: get an identifier (name) of this device - * @sreq: Scsi_Requeset associated with @sdev - * - * Description: - * Determine what INQUIRY pages are supported by @sdev, and try the - * different pages until we get an identifier, or no other pages are - * left. Start with page 0x83 (device id) and then try page 0x80 - * (serial number). If neither of these pages gets an id, use the - * default naming convention. - * - * The first character of sdev_driverfs_dev.name is SCSI_UID_SER_NUM - * (S) if we used page 0x80, SCSI_UID_UNKNOWN (Z) if we used the - * default name, otherwise it starts with the page 0x83 id type - * (see the SCSI Primary Commands specification for details). - * - * Notes: - * If a device returns the same serial number for different LUNs or - * even for different LUNs on different devices, special handling must - * be added to get an id, or a new black list flag must be added (so - * we use the default name, or add a way to prefix the id/name with - * SCSI_UID_UNKNOWN - and change the define to something meaningful - * like SCSI_UID_NOT_UNIQUE). Complete user level scanning would be - * nice for such devices, so we do not need device specific code in - * the kernel. - **/ -static void scsi_load_identifier(Scsi_Device *sdev, Scsi_Request *sreq) -{ - unsigned char *evpd_page = NULL; - int cnt; - - memset(sdev->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); - evpd_page = scsi_get_evpd_page(sdev, sreq); - if (evpd_page == NULL) { - /* - * try to obtain serial number anyway - */ - (void)scsi_get_serialnumber(sdev, sreq); - } else { - /* - * XXX search high to low, since the pages are lowest to - * highest - page 0x83 will be after page 0x80. - */ - for (cnt = 4; cnt <= evpd_page[3] + 3; cnt++) - if (evpd_page[cnt] == 0x83) - if (scsi_get_deviceid(sdev, sreq)) - goto leave; - - for (cnt = 4; cnt <= evpd_page[3] + 3; cnt++) - if (evpd_page[cnt] == 0x80) - if (scsi_get_serialnumber(sdev, sreq)) - goto leave; - - if (sdev->sdev_driverfs_dev.name[0] == 0) - scsi_get_default_name(sdev); - - } -leave: - if (evpd_page) kfree(evpd_page); - SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: host %d channel %d" - " id %d lun %d name/id: '%s'\n", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun, sdev->sdev_driverfs_dev.name)); -} - -/** * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY * @sreq: used to send the INQUIRY * @inq_result: area to store the INQUIRY result @@ -1004,6 +526,8 @@ unsigned char scsi_cmd[MAX_COMMAND_SIZE]; int possible_inq_resp_len; + *bflags = 0; + repeat_inquiry: SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d" " channel %d id %d lun %d\n", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun)); @@ -1043,8 +567,7 @@ * bit fields in Scsi_Device, so bflags need not be passed as an * argument. */ - BUG_ON(bflags == NULL); - *bflags = scsi_get_device_flags(&inq_result[8], &inq_result[16]); + *bflags |= scsi_get_device_flags(&inq_result[8], &inq_result[16]); possible_inq_resp_len = (unsigned char) inq_result[4] + 5; if (BLIST_INQUIRY_36 & *bflags) @@ -1070,8 +593,17 @@ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY" " %s with code 0x%x\n", sreq->sr_result ? "failed" : "successful", sreq->sr_result)); - if (sreq->sr_result) - return; + if (sreq->sr_result) { + /* if the longer inquiry has failed, flag the device + * as only accepting 36 byte inquiries and retry the + * 36 byte inquiry */ + printk(KERN_INFO "scsi scan: %d byte inquiry failed" + " with code %d. Consider BLIST_INQUIRY_36 for" + " this device\n", possible_inq_resp_len, + sreq->sr_result); + *bflags = BLIST_INQUIRY_36; + goto repeat_inquiry; + } /* * The INQUIRY can change, this means the length can change. @@ -1119,11 +651,29 @@ return; } +static void scsi_set_name(struct scsi_device *sdev, char *inq_result) +{ + int i; + char type[72]; + + i = inq_result[0] & 0x1f; + if (i < MAX_SCSI_DEVICE_CODE) + strcpy(type, scsi_device_types[i]); + else + strcpy(type, "Unknown"); + + i = strlen(type) - 1; + while (i >= 0 && type[i] == ' ') + type[i--] = '\0'; + + snprintf(sdev->sdev_driverfs_dev.name, DEVICE_NAME_SIZE, "SCSI %s", + type); +} + /** * scsi_add_lun - allocate and fully initialze a Scsi_Device * @sdevscan: holds information to be stored in the new Scsi_Device * @sdevnew: store the address of the newly allocated Scsi_Device - * @sreq: scsi request used when getting an identifier * @inq_result: holds the result of a previous INQUIRY to the LUN * @bflags: black/white list flag * @@ -1137,8 +687,7 @@ * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized **/ -static int scsi_add_lun(Scsi_Device *sdev, Scsi_Request *sreq, - char *inq_result, int *bflags) +static int scsi_add_lun(Scsi_Device *sdev, char *inq_result, int *bflags) { struct scsi_device *sdev_sibling; struct scsi_target *starget; @@ -1197,6 +746,8 @@ sdev->random = (sdev->type == TYPE_TAPE) ? 0 : 1; + scsi_set_name(sdev, inq_result); + print_inquiry(inq_result); /* @@ -1233,15 +784,6 @@ if (inq_result[7] & 0x10) sdev->sdtr = 1; - /* - * XXX maybe move the identifier and driverfs/devfs setup to a new - * function, and call them after this function is called. - * - * scsi_load_identifier is the only reason sreq is needed in this - * function. - */ - scsi_load_identifier(sdev, sreq); - scsi_device_register(sdev); sprintf(sdev->devfs_name, "scsi/host%d/bus%d/target%d/lun%d", @@ -1369,7 +911,7 @@ goto out_free_result; } - res = scsi_add_lun(sdev, sreq, result, &bflags); + res = scsi_add_lun(sdev, result, &bflags); if (res == SCSI_SCAN_LUN_PRESENT) { if (bflags & BLIST_KEY) { sdev->lockable = 0; @@ -1482,25 +1024,25 @@ #ifdef CONFIG_SCSI_REPORT_LUNS /** - * scsilun_to_int: convert a ScsiLun to an int - * @scsilun: ScsiLun to be converted. + * scsilun_to_int: convert a scsi_lun to an int + * @scsilun: struct scsi_lun to be converted. * * Description: - * Convert @scsilun from a ScsiLun to a four byte host byte-ordered + * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered * integer, and return the result. The caller must check for * truncation before using this function. * * Notes: - * The ScsiLun is assumed to be four levels, with each level + * The struct scsi_lun is assumed to be four levels, with each level * effectively containing a SCSI byte-ordered (big endian) short; the * addressing bits of each level are ignored (the highest two bits). * For a description of the LUN format, post SCSI-3 see the SCSI * Architecture Model, for SCSI-3 see the SCSI Controller Commands. * - * Given a ScsiLun of: 0a 04 0b 03 00 00 00 00, this function returns + * Given a struct scsi_lun of: 0a 04 0b 03 00 00 00 00, this function returns * the integer: 0x0b030a04 **/ -static int scsilun_to_int(ScsiLun *scsilun) +static int scsilun_to_int(struct scsi_lun *scsilun) { int i; unsigned int lun; @@ -1511,7 +1053,6 @@ scsilun->scsi_lun[i + 1]) << (i * 8)); return lun; } -#endif /** * scsi_report_lun_scan - Scan using SCSI REPORT LUN results @@ -1528,18 +1069,16 @@ * 0: scan completed (or no memory, so further scanning is futile) * 1: no report lun scan, or not configured **/ -static int scsi_report_lun_scan(Scsi_Device *sdev, int bflags) +static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags) { -#ifdef CONFIG_SCSI_REPORT_LUNS - char devname[64]; unsigned char scsi_cmd[MAX_COMMAND_SIZE]; unsigned int length; unsigned int lun; unsigned int num_luns; unsigned int retries; - ScsiLun *fcp_cur_lun, *lun_data; - Scsi_Request *sreq; + struct scsi_lun *lunp, *lun_data; + struct scsi_request *sreq; char *data; /* @@ -1551,15 +1090,14 @@ return 0; sreq = scsi_allocate_request(sdev); - if (sreq == NULL) { - printk(ALLOC_FAILURE_MSG, __FUNCTION__); - return 0; - } + if (!sreq) + goto out; + + sprintf(devname, "host %d channel %d id %d", + sdev->host->host_no, sdev->channel, sdev->id); - sprintf(devname, "host %d channel %d id %d", sdev->host->host_no, - sdev->channel, sdev->id); /* - * Allocate enough to hold the header (the same size as one ScsiLun) + * Allocate enough to hold the header (the same size as one scsi_lun) * plus the max number of luns we are requesting. * * Reallocating and trying again (with the exact amount we need) @@ -1568,24 +1106,19 @@ * kmalloc - we don't want a kmalloc() failure of a huge value to * prevent us from finding any LUNs on this target. */ - length = (max_scsi_report_luns + 1) * sizeof(ScsiLun); - lun_data = (ScsiLun *) kmalloc(length, GFP_ATOMIC | - (sdev->host->unchecked_isa_dma ? - GFP_DMA : 0)); - if (lun_data == NULL) { - printk(ALLOC_FAILURE_MSG, __FUNCTION__); - scsi_release_request(sreq); - /* - * We are out of memory, don't try scanning any further. - */ - return 0; - } + length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun); + lun_data = kmalloc(length, GFP_ATOMIC | + (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); + if (!lun_data) + goto out_release_request; scsi_cmd[0] = REPORT_LUNS; + /* * bytes 1 - 5: reserved, set to zero. */ memset(&scsi_cmd[1], 0, 5); + /* * bytes 6 - 9: length of the command. */ @@ -1609,19 +1142,18 @@ * should come through as a check condition, and will not generate * a retry. */ - retries = 0; - while (retries++ < 3) { + for (retries = 0; retries < 3; retries++) { SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending" " REPORT LUNS to %s (try %d)\n", devname, retries)); - scsi_wait_req(sreq, (void *) scsi_cmd, (void *) lun_data, - length, SCSI_TIMEOUT + 4 * HZ, 3); + scsi_wait_req(sreq, scsi_cmd, lun_data, length, + SCSI_TIMEOUT + 4*HZ, 3); SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS" " %s (try %d) result 0x%x\n", sreq->sr_result ? "failed" : "successful", retries, sreq->sr_result)); - if (sreq->sr_result == 0 - || sreq->sr_sense_buffer[2] != UNIT_ATTENTION) + if (sreq->sr_result == 0 || + sreq->sr_sense_buffer[2] != UNIT_ATTENTION) break; } @@ -1629,7 +1161,7 @@ /* * The device probably does not support a REPORT LUN command */ - kfree((char *) lun_data); + kfree(lun_data); scsi_release_request(sreq); return 1; } @@ -1641,31 +1173,32 @@ data = (char *) lun_data->scsi_lun; length = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3] << 0)); - if ((length / sizeof(ScsiLun)) > max_scsi_report_luns) { + + num_luns = (length / sizeof(struct scsi_lun)); + if (num_luns > max_scsi_report_luns) { printk(KERN_WARNING "scsi: On %s only %d (max_scsi_report_luns)" " of %d luns reported, try increasing" " max_scsi_report_luns.\n", devname, - max_scsi_report_luns, length / sizeof(ScsiLun)); + max_scsi_report_luns, num_luns); num_luns = max_scsi_report_luns; - } else - num_luns = (length / sizeof(ScsiLun)); + } SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of" " host %d channel %d id %d\n", sdev->host->host_no, sdev->channel, sdev->id)); + /* * Scan the luns in lun_data. The entry at offset 0 is really * the header, so start at 1 and go up to and including num_luns. */ - for (fcp_cur_lun = &lun_data[1]; - fcp_cur_lun <= &lun_data[num_luns]; fcp_cur_lun++) { - lun = scsilun_to_int(fcp_cur_lun); + for (lunp = &lun_data[1]; lunp <= &lun_data[num_luns]; lunp++) { + lun = scsilun_to_int(lunp); + /* - * Check if the unused part of fcp_cur_lun is non-zero, - * and so does not fit in lun. + * Check if the unused part of lunp is non-zero, and so + * does not fit in lun. */ - if (memcmp(&fcp_cur_lun->scsi_lun[sizeof(lun)], - "\0\0\0\0", 4) != 0) { + if (memcmp(&lunp->scsi_lun[sizeof(lun)], "\0\0\0\0", 4)) { int i; /* @@ -1674,8 +1207,8 @@ * integer LUN value. */ printk(KERN_WARNING "scsi: %s lun 0x", devname); - data = (char *) fcp_cur_lun->scsi_lun; - for (i = 0; i < sizeof(ScsiLun); i++) + data = (char *)lunp->scsi_lun; + for (i = 0; i < sizeof(struct scsi_lun); i++) printk("%02x", data[i]); printk(" has a LUN larger than currently supported.\n"); } else if (lun == 0) { @@ -1703,15 +1236,22 @@ } } - kfree((char *) lun_data); + kfree(lun_data); return 0; + out_release_request: + scsi_release_request(sreq); + out: + /* + * We are out of memory, don't try scanning any further. + */ + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + return 0; +} #else - return 1; +# define scsi_report_lun_scan(sdev, blags) (1) #endif /* CONFIG_SCSI_REPORT_LUNS */ -} - struct scsi_device *scsi_add_device(struct Scsi_Host *shost, uint channel, uint id, uint lun) { diff -urN linux-2.5.68-bk7/drivers/scsi/scsi_syms.c linux-2.5.68-bk8/drivers/scsi/scsi_syms.c --- linux-2.5.68-bk7/drivers/scsi/scsi_syms.c 2003-04-19 19:49:30.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/scsi_syms.c 2003-04-27 04:36:28.000000000 -0700 @@ -92,7 +92,6 @@ /* * These are here only while I debug the rest of the scsi stuff. */ -EXPORT_SYMBOL(scsi_host_get_next); EXPORT_SYMBOL(scsi_host_hn_get); EXPORT_SYMBOL(scsi_host_put); EXPORT_SYMBOL(scsi_device_types); diff -urN linux-2.5.68-bk7/drivers/scsi/sg.c linux-2.5.68-bk8/drivers/scsi/sg.c --- linux-2.5.68-bk7/drivers/scsi/sg.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/sg.c 2003-04-27 04:36:28.000000000 -0700 @@ -2773,6 +2773,10 @@ size : begin + len - offset; \ } while(0) +/* this should _really_ be private to the scsi midlayer. But + /proc/scsi/sg is an established name, so.. */ +extern struct proc_dir_entry *proc_scsi; + static int sg_proc_init() { diff -urN linux-2.5.68-bk7/drivers/scsi/sym53c8xx_2/sym53c8xx.h linux-2.5.68-bk8/drivers/scsi/sym53c8xx_2/sym53c8xx.h --- linux-2.5.68-bk7/drivers/scsi/sym53c8xx_2/sym53c8xx.h 2003-04-19 19:51:17.000000000 -0700 +++ linux-2.5.68-bk8/drivers/scsi/sym53c8xx_2/sym53c8xx.h 2003-04-27 04:36:28.000000000 -0700 @@ -106,22 +106,22 @@ #include #define SYM53C8XX { \ - name: "sym53c8xx", \ - detect: sym53c8xx_detect, \ - release: sym53c8xx_release, \ - info: sym53c8xx_info, \ - queuecommand: sym53c8xx_queue_command, \ - slave_configure: sym53c8xx_slave_configure, \ - eh_abort_handler: sym53c8xx_eh_abort_handler, \ - eh_device_reset_handler:sym53c8xx_eh_device_reset_handler, \ - eh_bus_reset_handler: sym53c8xx_eh_bus_reset_handler, \ - eh_host_reset_handler: sym53c8xx_eh_host_reset_handler, \ - can_queue: 0, \ - this_id: 7, \ - sg_tablesize: 0, \ - cmd_per_lun: 0, \ - use_clustering: DISABLE_CLUSTERING, \ - highmem_io: 1} + .name = "sym53c8xx", \ + .detect = sym53c8xx_detect, \ + .release = sym53c8xx_release, \ + .info = sym53c8xx_info, \ + .queuecommand = sym53c8xx_queue_command, \ + .slave_configure = sym53c8xx_slave_configure, \ + .eh_abort_handler = sym53c8xx_eh_abort_handler, \ + .eh_device_reset_handler = sym53c8xx_eh_device_reset_handler,\ + .eh_bus_reset_handler = sym53c8xx_eh_bus_reset_handler, \ + .eh_host_reset_handler = sym53c8xx_eh_host_reset_handler, \ + .can_queue = 0, \ + .this_id = 7, \ + .sg_tablesize = 0, \ + .cmd_per_lun = 0, \ + .use_clustering = DISABLE_CLUSTERING, \ + .highmem_io = 1} #endif /* defined(HOSTS_C) || defined(MODULE) */ diff -urN linux-2.5.68-bk7/fs/cifs/CHANGES linux-2.5.68-bk8/fs/cifs/CHANGES --- linux-2.5.68-bk7/fs/cifs/CHANGES 2003-04-19 19:50:45.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/CHANGES 2003-04-27 04:36:28.000000000 -0700 @@ -1,3 +1,33 @@ +Version 0.75 +------------ +Fix delete of readonly file to Windows servers. Reflect +presence or absence of read only dos attribute in mode +bits for servers that do not support CIFS Unix extensions. +Fix shortened results on readdir of large directories to +servers supporting CIFS Unix extensions (caused by +incorrect resume key). + +Version 0.74 +------------ +Fix truncate bug (set file size) that could cause hangs e.g. running fsx + +Version 0.73 +------------ +unload nls if mount fails. + +Version 0.72 +------------ +Add resume key support to search (readdir) code to workaround +Windows bug. Add /proc/fs/cifs/LookupCacheEnable which +allows disabling caching of attribute information for +lookups. + +Version 0.71 +------------ +Add more oplock handling (distributed caching code). Remove +dead code. Remove excessive stack space utilization from +symlink routines. + Version 0.70 ------------ Fix oops in get dfs referral (triggered when null path sent in to diff -urN linux-2.5.68-bk7/fs/cifs/cifs_debug.c linux-2.5.68-bk8/fs/cifs/cifs_debug.c --- linux-2.5.68-bk7/fs/cifs/cifs_debug.c 2003-04-19 19:49:26.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/cifs_debug.c 2003-04-27 04:36:28.000000000 -0700 @@ -185,6 +185,8 @@ static write_proc_t cifsFYI_write; static read_proc_t oplockEnabled_read; static write_proc_t oplockEnabled_write; +static read_proc_t lookupFlag_read; +static write_proc_t lookupFlag_write; static read_proc_t traceSMB_read; static write_proc_t traceSMB_write; static read_proc_t multiuser_mount_read; @@ -244,6 +246,12 @@ pde->write_proc = extended_security_write; pde = + create_proc_read_entry("LookupCacheEnable", 0, proc_fs_cifs, + lookupFlag_read, 0); + if (pde) + pde->write_proc = lookupFlag_write; + + pde = create_proc_read_entry("NTLMV2Enabled", 0, proc_fs_cifs, ntlmv2_enabled_read, 0); if (pde) @@ -354,6 +362,44 @@ } static int +lookupFlag_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = sprintf(page, "%d\n", lookupCacheEnabled); + + len -= off; + *start = page + off; + + if (len > count) + len = count; + else + *eof = 1; + + if (len < 0) + len = 0; + + return len; +} +static int +lookupFlag_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char c; + int rc; + + rc = get_user(c, buffer); + if (rc) + return rc; + if (c == '0' || c == 'n' || c == 'N') + lookupCacheEnabled = 0; + else if (c == '1' || c == 'y' || c == 'Y') + lookupCacheEnabled = 1; + + return count; +} +static int traceSMB_read(char *page, char **start, off_t off, int count, int *eof, void *data) { diff -urN linux-2.5.68-bk7/fs/cifs/cifsfs.c linux-2.5.68-bk8/fs/cifs/cifsfs.c --- linux-2.5.68-bk7/fs/cifs/cifsfs.c 2003-04-19 19:49:56.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/cifsfs.c 2003-04-27 04:36:28.000000000 -0700 @@ -48,11 +48,13 @@ int cifsERROR = 1; int traceSMB = 0; unsigned int oplockEnabled = 0; +unsigned int lookupCacheEnabled = 1; unsigned int multiuser_mount = 0; unsigned int extended_security = 0; unsigned int ntlmv2_support = 0; unsigned int sign_CIFS_PDUs = 0; unsigned int CIFSMaximumBufferSize = CIFS_MAX_MSGSIZE; +struct task_struct * oplockThread = NULL; extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, char *); @@ -107,6 +109,8 @@ iput(inode); out_mount_failed: + if(cifs_sb->local_nls) + unload_nls(cifs_sb->local_nls); if(cifs_sb) kfree(cifs_sb); return -EINVAL; @@ -175,6 +179,7 @@ static kmem_cache_t *cifs_inode_cachep; kmem_cache_t *cifs_req_cachep; kmem_cache_t *cifs_mid_cachep; +kmem_cache_t *cifs_oplock_cachep; static struct inode * cifs_alloc_inode(struct super_block *sb) @@ -390,10 +395,17 @@ cifs_init_mids(void) { cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids", - sizeof (struct mid_q_entry), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + sizeof (struct mid_q_entry), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); if (cifs_mid_cachep == NULL) return -ENOMEM; + cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs", + sizeof (struct oplock_q_entry), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (cifs_oplock_cachep == NULL) { + kmem_cache_destroy(cifs_mid_cachep); + return -ENOMEM; + } return 0; } @@ -404,6 +416,49 @@ if (kmem_cache_destroy(cifs_mid_cachep)) printk(KERN_WARNING "cifs_destroy_mids: error not all structures were freed\n"); + if (kmem_cache_destroy(cifs_oplock_cachep)) + printk(KERN_WARNING + "error not all oplock structures were freed\n");} + +static int cifs_oplock_thread(void * dummyarg) +{ + struct list_head * tmp; + struct oplock_q_entry * oplock_item; + struct file * pfile; + struct cifsTconInfo *pTcon; + int rc; + + daemonize("cifsoplockd"); + allow_signal(SIGKILL); + + oplockThread = current; + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100*HZ); + /* BB add missing code */ + cFYI(1,("oplock thread woken up - flush inode")); /* BB remove */ + write_lock(&GlobalMid_Lock); + list_for_each(tmp, &GlobalOplock_Q) { + oplock_item = list_entry(tmp, struct + oplock_q_entry, + qhead); + if(oplock_item) { + pTcon = oplock_item->tcon; + pfile = oplock_item->file_to_flush; + cFYI(1,("process item on queue"));/* BB remove */ + DeleteOplockQEntry(oplock_item); + write_unlock(&GlobalMid_Lock); + rc = filemap_fdatawrite(pfile->f_dentry->d_inode->i_mapping); + cFYI(1,("Oplock flush file %p rc %d",pfile,rc)); + /* send oplock break */ + write_lock(&GlobalMid_Lock); + } else + break; + cFYI(1,("next time through list")); /* BB remove */ + } + write_unlock(&GlobalMid_Lock); + cFYI(1,("next time through while loop")); /* BB remove */ + } } static int __init @@ -416,7 +471,7 @@ INIT_LIST_HEAD(&GlobalServerList); /* BB not implemented yet */ INIT_LIST_HEAD(&GlobalSMBSessionList); INIT_LIST_HEAD(&GlobalTreeConnectionList); - + INIT_LIST_HEAD(&GlobalOplock_Q); /* * Initialize Global counters */ @@ -437,9 +492,11 @@ rc = cifs_init_request_bufs(); if (!rc) { rc = register_filesystem(&cifs_fs_type); - if (!rc) - return rc; /* Success */ - else + if (!rc) { + kernel_thread(cifs_oplock_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_VM); + return rc; /* Success */ + } else cifs_destroy_request_bufs(); } cifs_destroy_mids(); @@ -459,10 +516,12 @@ #if CONFIG_PROC_FS cifs_proc_clean(); #endif - unregister_filesystem(&cifs_fs_type); + unregister_filesystem(&cifs_fs_type); cifs_destroy_inodecache(); cifs_destroy_mids(); cifs_destroy_request_bufs(); + if(oplockThread) + send_sig(SIGKILL, oplockThread, 1); } MODULE_AUTHOR("Steve French "); diff -urN linux-2.5.68-bk7/fs/cifs/cifsglob.h linux-2.5.68-bk8/fs/cifs/cifsglob.h --- linux-2.5.68-bk7/fs/cifs/cifsglob.h 2003-04-19 19:48:54.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/cifsglob.h 2003-04-27 04:36:28.000000000 -0700 @@ -199,14 +199,18 @@ struct cifsFileInfo { struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head flist; /* next fid (file instance) for this inode */ - unsigned int uid; /* allows you to find which FileInfo structure */ + unsigned int uid; /* allows finding which FileInfo structure */ __u32 pid; /* process id who opened file */ __u16 netfid; /* file id from remote */ /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ - struct file * pfile; /* needed for writepage */ + struct file * pfile; /* needed for writepage */ int endOfSearch:1; /* we have reached end of search */ int closePend:1; /* file is marked to close */ + int emptyDir:1; + char * search_resume_name; + unsigned int resume_name_length; + __u32 resume_key; }; /* @@ -221,7 +225,8 @@ atomic_t inUse; /* num concurrent users (local openers cifs) of file*/ unsigned long time; /* jiffies of last update/check of inode */ int clientCanCacheRead:1; /* read oplock */ - int clientCanCacheAll:1; /* read and writebehind oplock */ + int clientCanCacheAll:1; /* read and writebehind oplock */ + int oplockPending:1; struct inode vfs_inode; }; @@ -251,14 +256,20 @@ int midState; /* wish this were enum but can not pass to wait_event */ }; +struct oplock_q_entry { + struct list_head qhead; + struct file * file_to_flush; + struct cifsTconInfo * tcon; +}; + #define MID_FREE 0 #define MID_REQUEST_ALLOCATED 1 #define MID_REQUEST_SUBMITTED 2 #define MID_RESPONSE_RECEIVED 4 -struct servers_not_supported { /* @z4a */ - struct servers_not_supported *next1; /* @z4a */ - char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* @z4a */ +struct servers_not_supported { /* @z4a */ + struct servers_not_supported *next1; /* @z4a */ + char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* @z4a */ /* Server Names in SMB protocol are 15 chars + X'20' */ /* in 16th byte... @z4a */ }; @@ -301,6 +312,8 @@ GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ +GLOBAL_EXTERN struct list_head GlobalOplock_Q; + /* * Global transaction id (XID) information */ @@ -327,6 +340,7 @@ have the uid/password or Kerberos credential or equivalent for current user */ GLOBAL_EXTERN unsigned int oplockEnabled; +GLOBAL_EXTERN unsigned int lookupCacheEnabled; GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent with more secure ntlmssp2 challenge/resp */ GLOBAL_EXTERN unsigned int ntlmv2_support; /* better optional password hash */ diff -urN linux-2.5.68-bk7/fs/cifs/cifspdu.h linux-2.5.68-bk8/fs/cifs/cifspdu.h --- linux-2.5.68-bk7/fs/cifs/cifspdu.h 2003-04-19 19:50:44.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/cifspdu.h 2003-04-27 04:36:28.000000000 -0700 @@ -38,6 +38,7 @@ #define SMB_COM_WRITE_ANDX 0x2F #define SMB_COM_TRANSACTION2 0x32 #define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 #define SMB_COM_TREE_DISCONNECT 0x71 #define SMB_COM_NEGOTIATE 0x72 #define SMB_COM_SESSION_SETUP_ANDX 0x73 @@ -584,7 +585,7 @@ typedef struct smb_com_close_req { struct smb_hdr hdr; /* wct = 3 */ - __u16 FileID; /* target file attributes */ + __u16 FileID; __u32 LastWriteTime; /* should be zero */ __u16 ByteCount; /* 0 */ } CLOSE_REQ; @@ -594,6 +595,12 @@ __u16 ByteCount; /* bct = 0 */ } CLOSE_RSP; +typedef struct smb_com_findclose_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} FINDCLOSE_REQ; + /* OpenFlags */ #define REQ_OPLOCK 0x00000002 #define REQ_BATCHOPLOCK 0x00000004 @@ -1152,7 +1159,7 @@ __u16 InformationLevel; __u32 ResumeKey; __u16 SearchFlags; - char ResumeFileName[1]; /* will be null string actually since we set bit 3 - resume from previous ending place */ + char ResumeFileName[1]; } TRANSACTION2_FNEXT_REQ; typedef struct smb_com_transaction2_fnext_rsp { @@ -1509,7 +1516,7 @@ __u32 ExtFileAttributes; __u32 FileNameLength; char FileName[1]; -} FILE_DIRECTORY_INFO; /* level 257 FF response data area */ +} FILE_DIRECTORY_INFO; /* level 257 FF response data area */ struct gea { unsigned char cbName; diff -urN linux-2.5.68-bk7/fs/cifs/cifsproto.h linux-2.5.68-bk8/fs/cifs/cifsproto.h --- linux-2.5.68-bk7/fs/cifs/cifsproto.h 2003-04-19 19:48:49.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/cifsproto.h 2003-04-27 04:36:28.000000000 -0700 @@ -57,6 +57,8 @@ const struct cifsTconInfo *, int /* length of fixed section (word count) in two byte units */ ); +struct oplock_q_entry * AllocOplockQEntry(struct file *,struct cifsTconInfo *); +void DeleteOplockQEntry(struct oplock_q_entry *); extern struct timespec cifs_NTtimeToUnix(u64 /* utc nanoseconds since 1601 */ ); extern u64 cifs_UnixTimeToNT(struct timespec); extern void RevUcode_to_Ucode(char *revUnicode, char *UnicodeName); @@ -104,9 +106,13 @@ extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms, - const __u16 searchHandle, const __u32 resumeKey, + const __u16 searchHandle, char * resume_name, + int name_length, __u32 resume_key, int *UnicodeFlag, int *pUnixFlag); +extern int CIFSFindClose(const int, struct cifsTconInfo *tcon, + const __u16 search_handle); + extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO * findData, diff -urN linux-2.5.68-bk7/fs/cifs/cifssmb.c linux-2.5.68-bk8/fs/cifs/cifssmb.c --- linux-2.5.68-bk7/fs/cifs/cifssmb.c 2003-04-19 19:48:55.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/cifssmb.c 2003-04-27 04:36:28.000000000 -0700 @@ -1449,7 +1449,7 @@ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); pSMB->SearchCount = cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO)); /* should this be shrunk even more ? */ - pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END); + pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); /* test for Unix extensions */ if (tcon->ses->capabilities & CAP_UNIX) { @@ -1496,9 +1496,9 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, - FILE_DIRECTORY_INFO * findData, - T2_FNEXT_RSP_PARMS * findParms, const __u16 searchHandle, - __u32 resumeKey, int *pUnicodeFlag, int *pUnixFlag) + FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms, + const __u16 searchHandle, char * resume_file_name, int name_len, + __u32 resume_key, int *pUnicodeFlag, int *pUnixFlag) { /* level 257 SMB_ */ TRANSACTION2_FNEXT_REQ *pSMB = NULL; @@ -1508,6 +1508,9 @@ int bytes_returned; cFYI(1, ("In FindNext")); + if(resume_file_name == NULL) { + return -EIO; + } rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) @@ -1530,9 +1533,6 @@ pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); - pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount); - pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->SearchHandle = searchHandle; /* always kept as le */ findParms->SearchCount = 0; /* set to zero in case of error */ pSMB->SearchCount = @@ -1546,10 +1546,19 @@ cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO); *pUnixFlag = FALSE; } - pSMB->ResumeKey = resumeKey; /* always kept as le */ + pSMB->ResumeKey = resume_key; pSMB->SearchFlags = - cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | - CIFS_SEARCH_CONTINUE_FROM_LAST); + cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); + /* BB add check to make sure we do not cross end of smb */ + if(name_len < CIFS_MAX_MSGSIZE) { + memcpy(pSMB->ResumeFileName, resume_file_name, name_len); + pSMB->ByteCount += name_len; + } + pSMB->TotalParameterCount += name_len; + pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount); + pSMB->ParameterCount = pSMB->TotalParameterCount; + /* BB improve error handling here */ pSMB->hdr.smb_buf_length += pSMB->ByteCount; pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); @@ -1586,6 +1595,33 @@ } int +CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle) +{ + int rc = 0; + FINDCLOSE_REQ *pSMB = NULL; + CLOSE_RSP *pSMBr = NULL; + int bytes_returned; + cFYI(1, ("In CIFSSMBFindClose")); + + rc = smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->FileID = searchHandle; + pSMB->ByteCount = 0; + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cERROR(1, ("Send error in FindClose = %d", rc)); + } + if (pSMB) + buf_release(pSMB); + + return rc; +} + +int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, unsigned char **targetUNCs, @@ -1984,21 +2020,21 @@ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount; - if(SetAllocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { + if(SetAllocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); - } + } parm_data = (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + @@ -2035,10 +2071,10 @@ struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; char *data_offset; - struct file_end_of_file_info *parm_data; + struct file_end_of_file_info *parm_data; int rc = 0; int bytes_returned = 0; - __u32 tmp; + __u32 tmp; cFYI(1, ("SetFileSize (via SetFileInfo)")); @@ -2079,25 +2115,25 @@ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset); pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset); parm_data = - (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + - pSMB->DataOffset); - parm_data->FileSize = size; - pSMB->Fid = fid; - if(SetAllocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { + (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + + pSMB->DataOffset); + parm_data->FileSize = size; + pSMB->Fid = fid; + if(SetAllocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); - } + } pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += pSMB->ByteCount; pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); diff -urN linux-2.5.68-bk7/fs/cifs/connect.c linux-2.5.68-bk8/fs/cifs/connect.c --- linux-2.5.68-bk7/fs/cifs/connect.c 2003-04-19 19:50:48.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/connect.c 2003-04-27 04:36:28.000000000 -0700 @@ -850,8 +850,7 @@ init_waitqueue_head(&srvTcp->response_q); INIT_LIST_HEAD(&srvTcp->pending_mid_q); srvTcp->tcpStatus = CifsGood; - kernel_thread((void *) (void *) - cifs_demultiplex_thread, srvTcp, + kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp, CLONE_FS | CLONE_FILES | CLONE_VM); } } diff -urN linux-2.5.68-bk7/fs/cifs/file.c linux-2.5.68-bk8/fs/cifs/file.c --- linux-2.5.68-bk7/fs/cifs/file.c 2003-04-19 19:48:46.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/file.c 2003-04-27 04:36:28.000000000 -0700 @@ -152,28 +152,31 @@ { int rc = 0; struct cifsFileInfo *open_file = NULL; + struct file * file = NULL; struct list_head *tmp; /* list all files open on tree connection */ list_for_each(tmp, &pTcon->openFileList) { open_file = list_entry(tmp,struct cifsFileInfo, flist); - if(open_file) - if(open_file->pfile) { - if(open_file->pfile->private_data) { - kfree(open_file->pfile->private_data); - } - rc = cifs_open(open_file->pfile->f_dentry->d_inode, - open_file->pfile); + if(open_file) { + if(open_file->search_resume_name) { + kfree(open_file->search_resume_name); + } + file = open_file->pfile; + kfree(open_file); + if(file) { + file->private_data = NULL; + rc = cifs_open(file->f_dentry->d_inode,file); if(rc) { cFYI(1,("reconnecting file %s failed with %d", - open_file->pfile->f_dentry->d_name.name,rc)); + file->f_dentry->d_name.name,rc)); } else { cFYI(1,("reconnection of %s succeeded", - open_file->pfile->f_dentry->d_name.name)); + file->f_dentry->d_name.name)); } - } + } + } } - return rc; } @@ -196,6 +199,8 @@ list_del(&pSMBFile->flist); list_del(&pSMBFile->tlist); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); + if(pSMBFile->search_resume_name) + kfree(pSMBFile->search_resume_name); kfree(file->private_data); file->private_data = NULL; } else @@ -874,6 +879,9 @@ } else { *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; + if(pfindData->ExtFileAttributes & ATTR_READONLY) + tmp_inode->i_mode &= ~(S_IWUGO); + }/* could add code here - to validate if device or weird share type? */ /* can not fill in nlink here as in qpathinfo version and Unx search */ @@ -1055,6 +1063,7 @@ int xid, i; int Unicode = FALSE; int UnixSearch = FALSE; + unsigned int bufsize; __u16 searchHandle; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -1065,14 +1074,19 @@ T2_FFIRST_RSP_PARMS findParms; T2_FNEXT_RSP_PARMS findNextParms; FILE_DIRECTORY_INFO *pfindData; + FILE_DIRECTORY_INFO *lastFindData; FILE_UNIX_INFO *pfindDataUnix; xid = GetXid(); cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; - data = kmalloc(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE, - GFP_KERNEL); + bufsize = pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; + if(bufsize > CIFS_MAX_MSGSIZE) { + FreeXid(xid); + return -EIO; + } + data = kmalloc(bufsize, GFP_KERNEL); pfindData = (FILE_DIRECTORY_INFO *) data; full_path = build_wildcard_path_from_dentry(file->f_dentry); @@ -1081,8 +1095,7 @@ switch ((int) file->f_pos) { case 0: - if (filldir - (direntry, ".", 1, file->f_pos, + if (filldir(direntry, ".", 1, file->f_pos, file->f_dentry->d_inode->i_ino, DT_DIR) < 0) { cERROR(1, ("Filldir for current dir failed ")); break; @@ -1090,8 +1103,7 @@ file->f_pos++; /* fallthrough */ case 1: - if (filldir - (direntry, "..", 2, file->f_pos, + if (filldir(direntry, "..", 2, file->f_pos, file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { cERROR(1, ("Filldir for parent dir failed ")); break; @@ -1099,44 +1111,107 @@ file->f_pos++; /* fallthrough */ case 2: - /* do not reallocate search handle if rewind */ - if(file->private_data == NULL) { - rc = CIFSFindFirst(xid, pTcon, full_path, pfindData, - &findParms, cifs_sb->local_nls, - &Unicode, &UnixSearch); - cFYI(1, - ("Count: %d End: %d ", findParms.SearchCount, - findParms.EndofSearch)); - } else { - cFYI(1,("Search rewinding on %s",full_path)); - goto readdir_rewind; + if (file->private_data != NULL) { + cifsFile = + (struct cifsFileInfo *) file->private_data; + if (cifsFile->endOfSearch) { + if(cifsFile->emptyDir) { + cFYI(1, ("End of search, empty dir")); + rc = 0; + break; + } + } else + CIFSFindClose(xid, pTcon, cifsFile->netfid); + if(cifsFile->search_resume_name) { + kfree(cifsFile->search_resume_name); + cifsFile->search_resume_name = NULL; + } } - + rc = CIFSFindFirst(xid, pTcon, full_path, pfindData, + &findParms, cifs_sb->local_nls, + &Unicode, &UnixSearch); + cFYI(1, ("Count: %d End: %d ", findParms.SearchCount, + findParms.EndofSearch)); + if (rc == 0) { searchHandle = findParms.SearchHandle; if(file->private_data == NULL) file->private_data = kmalloc(sizeof(struct cifsFileInfo), - GFP_KERNEL); - else { - /* BB close search handle */ - cFYI(1,("Search rewinding on %s",full_path)); - } + GFP_KERNEL); if (file->private_data) { memset(file->private_data, 0, sizeof (struct cifsFileInfo)); cifsFile = (struct cifsFileInfo *) file->private_data; cifsFile->netfid = searchHandle; + } else { + rc = -ENOMEM; + break; } renew_parental_timestamps(file->f_dentry); - + lastFindData = + (FILE_DIRECTORY_INFO *) ((char *) pfindData + + le32_to_cpu(findParms.LastNameOffset)); + if((char *)lastFindData > (char *)pfindData + bufsize) { + cFYI(1,("last search entry past end of packet")); + rc = -EIO; + break; + } + /* Offset of resume key same for levels 257 and 514 */ + cifsFile->resume_key = lastFindData->FileIndex; + if(UnixSearch == FALSE) { + cifsFile->resume_name_length = + le32_to_cpu(lastFindData->FileNameLength); + if(cifsFile->resume_name_length > bufsize - 64) { + cFYI(1,("Illegal resume file name length %d", + cifsFile->resume_name_length)); + rc = -ENOMEM; + break; + } + cifsFile->search_resume_name = + kmalloc(cifsFile->resume_name_length, GFP_KERNEL); + cFYI(1,("Last file: %s with name %d bytes long", + lastFindData->FileName, + cifsFile->resume_name_length)); + memcpy(cifsFile->search_resume_name, + lastFindData->FileName, + cifsFile->resume_name_length); + } else { + pfindDataUnix = (FILE_UNIX_INFO *)lastFindData; + if (Unicode == TRUE) { + for(i=0;(pfindDataUnix->FileName[i] + | pfindDataUnix->FileName[i+1]); + i+=2) { + if(i > bufsize-64) + break; + } + cifsFile->resume_name_length = i + 2; + } else { + cifsFile->resume_name_length = + strnlen(pfindDataUnix->FileName, + bufsize-63); + } + if(cifsFile->resume_name_length > bufsize - 64) { + cFYI(1,("Illegal resume file name length %d", + cifsFile->resume_name_length)); + rc = -ENOMEM; + break; + } + cifsFile->search_resume_name = + kmalloc(cifsFile->resume_name_length, GFP_KERNEL); + cFYI(1,("Last file: %s with name %d bytes long", + pfindDataUnix->FileName, + cifsFile->resume_name_length)); + memcpy(cifsFile->search_resume_name, + pfindDataUnix->FileName, + cifsFile->resume_name_length); + } for (i = 2; i < findParms.SearchCount + 2; i++) { if (UnixSearch == FALSE) { pfindData->FileNameLength = - le32_to_cpu(pfindData-> - FileNameLength); + le32_to_cpu(pfindData->FileNameLength); if (Unicode == TRUE) pfindData->FileNameLength = cifs_strfromUCS_le @@ -1165,18 +1240,17 @@ (FILE_UNIX_INFO *) pfindData; if (Unicode == TRUE) qstring.len = - cifs_strfromUCS_le - (pfindDataUnix-> - FileName, (wchar_t *) - pfindDataUnix-> - FileName, - MAX_PATHCONF, - cifs_sb->local_nls); + cifs_strfromUCS_le + (pfindDataUnix->FileName, + (wchar_t *) + pfindDataUnix->FileName, + MAX_PATHCONF, + cifs_sb->local_nls); else qstring.len = - strnlen(pfindDataUnix-> - FileName, - MAX_PATHCONF); + strnlen(pfindDataUnix-> + FileName, + MAX_PATHCONF); if (((qstring.len != 1) || (pfindDataUnix-> FileName[0] != '.')) @@ -1193,21 +1267,26 @@ file->f_pos++; } } - pfindData = (FILE_DIRECTORY_INFO *) ((char *) pfindData + le32_to_cpu(pfindData->NextEntryOffset)); /* works also for Unix find struct since this is the first field of both */ + /* works also for Unix ff struct since first field of both */ + pfindData = + (FILE_DIRECTORY_INFO *) ((char *) pfindData + + le32_to_cpu(pfindData->NextEntryOffset)); /* BB also should check to make sure that pointer is not beyond the end of the SMB */ + /* if(pfindData > lastFindData) rc = -EIO; break; */ } /* end for loop */ if ((findParms.EndofSearch != 0) && cifsFile) { cifsFile->endOfSearch = TRUE; + if(findParms.SearchCount == 2) + cifsFile->emptyDir = TRUE; } } else { if (cifsFile) cifsFile->endOfSearch = TRUE; - rc = 0; /* unless parent directory disappeared - do not return error here (eg Access Denied or no more files) */ + /* unless parent directory gone do not return error */ + rc = 0; } break; -readdir_rewind: default: - /* BB rewrite eventually to better handle rewind */ if (file->private_data == NULL) { rc = -EBADF; cFYI(1, @@ -1222,43 +1301,96 @@ } searchHandle = cifsFile->netfid; rc = CIFSFindNext(xid, pTcon, pfindData, - &findNextParms, searchHandle, 0, - &Unicode, &UnixSearch); - cFYI(1, - ("Count: %d End: %d ", + &findNextParms, searchHandle, + cifsFile->search_resume_name, + cifsFile->resume_name_length, + cifsFile->resume_key, + &Unicode, &UnixSearch); + cFYI(1,("Count: %d End: %d ", findNextParms.SearchCount, findNextParms.EndofSearch)); if ((rc == 0) && (findNextParms.SearchCount != 0)) { + /* BB save off resume key, key name and name length */ + lastFindData = + (FILE_DIRECTORY_INFO *) ((char *) pfindData + + le32_to_cpu(findNextParms.LastNameOffset)); + if((char *)lastFindData > (char *)pfindData + bufsize) { + cFYI(1,("last search entry past end of packet")); + rc = -EIO; + break; + } + /* Offset of resume key same for levels 257 and 514 */ + cifsFile->resume_key = lastFindData->FileIndex; + + if(UnixSearch == FALSE) { + cifsFile->resume_name_length = + le32_to_cpu(lastFindData->FileNameLength); + if(cifsFile->resume_name_length > bufsize - 64) { + cFYI(1,("Illegal resume file name length %d", + cifsFile->resume_name_length)); + rc = -ENOMEM; + break; + } + cifsFile->search_resume_name = + kmalloc(cifsFile->resume_name_length, GFP_KERNEL); + cFYI(1,("Last file: %s with name %d bytes long", + lastFindData->FileName, + cifsFile->resume_name_length)); + memcpy(cifsFile->search_resume_name, + lastFindData->FileName, + cifsFile->resume_name_length); + } else { + pfindDataUnix = (FILE_UNIX_INFO *)lastFindData; + if (Unicode == TRUE) { + for(i=0;(pfindDataUnix->FileName[i] + | pfindDataUnix->FileName[i+1]); + i+=2) { + if(i > bufsize-64) + break; + } + cifsFile->resume_name_length = i + 2; + } else { + cifsFile->resume_name_length = + strnlen(pfindDataUnix-> + FileName, + MAX_PATHCONF); + } + if(cifsFile->resume_name_length > bufsize - 64) { + cFYI(1,("Illegal resume file name length %d", + cifsFile->resume_name_length)); + rc = -ENOMEM; + break; + } + cifsFile->search_resume_name = + kmalloc(cifsFile->resume_name_length, GFP_KERNEL); + cFYI(1,("fnext last file: %s with name %d bytes long", + lastFindData->FileName, + cifsFile->resume_name_length)); + memcpy(cifsFile->search_resume_name, + lastFindData->FileName, + cifsFile->resume_name_length); + } + for (i = 0; i < findNextParms.SearchCount; i++) { pfindData->FileNameLength = le32_to_cpu(pfindData-> FileNameLength); if (UnixSearch == FALSE) { if (Unicode == TRUE) - pfindData-> - FileNameLength - = - cifs_strfromUCS_le - (pfindData-> - FileName, - (wchar_t *) - pfindData-> - FileName, - (pfindData-> - FileNameLength) - / 2, - cifs_sb-> - local_nls); - qstring.len = - pfindData->FileNameLength; + pfindData->FileNameLength = + cifs_strfromUCS_le + (pfindData->FileName, + (wchar_t *) + pfindData->FileName, + (pfindData->FileNameLength)/ 2, + cifs_sb->local_nls); + qstring.len = + pfindData->FileNameLength; if (((qstring.len != 1) - || (pfindData-> - FileName[0] != '.')) + || (pfindData->FileName[0] != '.')) && ((qstring.len != 2) - || (pfindData-> - FileName[0] != '.') - || (pfindData-> - FileName[1] != + || (pfindData->FileName[0] != '.') + || (pfindData->FileName[1] != '.'))) { cifs_filldir (&qstring, @@ -1273,22 +1405,18 @@ pfindData; if (Unicode == TRUE) qstring.len = - cifs_strfromUCS_le - (pfindDataUnix-> - FileName, - (wchar_t *) - pfindDataUnix-> - FileName, - MAX_PATHCONF, - cifs_sb-> - local_nls); + cifs_strfromUCS_le + (pfindDataUnix->FileName, + (wchar_t *) + pfindDataUnix->FileName, + MAX_PATHCONF, + cifs_sb->local_nls); else qstring.len = - strnlen - (pfindDataUnix-> - FileName, - MAX_PATHCONF); - + strnlen + (pfindDataUnix-> + FileName, + MAX_PATHCONF); if (((qstring.len != 1) || (pfindDataUnix-> FileName[0] != '.')) @@ -1308,8 +1436,7 @@ } pfindData = (FILE_DIRECTORY_INFO *) ((char *) pfindData + le32_to_cpu(pfindData->NextEntryOffset)); /* works also for Unix find struct since this is the first field of both */ /* BB also should check to make sure that pointer is not beyond the end of the SMB */ - - } /* end for loop */ + } /* end for loop */ if (findNextParms.EndofSearch != 0) { cifsFile->endOfSearch = TRUE; } @@ -1318,8 +1445,7 @@ rc = 0; /* unless parent directory disappeared - do not return error here (eg Access Denied or no more files) */ } } - } /* end switch */ - + } /* end switch */ if (data) kfree(data); if (full_path) @@ -1335,7 +1461,8 @@ .writepage = cifs_writepage, .prepare_write = simple_prepare_write, .commit_write = cifs_commit_write, - .sync_page = cifs_sync_page, + .sync_page = cifs_sync_page, + /*.direct_IO = */ }; struct address_space_operations cifs_addr_ops_writethrough = { @@ -1345,6 +1472,7 @@ .prepare_write = simple_prepare_write, .commit_write = cifs_commit_write, .sync_page = cifs_sync_page, + /*.direct_IO = */ }; struct address_space_operations cifs_addr_ops_nocache = { @@ -1354,5 +1482,6 @@ .prepare_write = simple_prepare_write, .commit_write = cifs_commit_write, .sync_page = cifs_sync_page, + /*.direct_IO = */ }; diff -urN linux-2.5.68-bk7/fs/cifs/inode.c linux-2.5.68-bk8/fs/cifs/inode.c --- linux-2.5.68-bk7/fs/cifs/inode.c 2003-04-19 19:48:49.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/inode.c 2003-04-27 04:36:28.000000000 -0700 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" @@ -232,8 +233,6 @@ cifs_NTtimeToUnix(le64_to_cpu(findData.LastWriteTime)); inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(findData.ChangeTime)); -/* inode->i_mode = S_IRWXUGO; *//* 777 perms */ - /* should we treat the dos attribute of read-only as read-only mode bit e.g. 555 */ inode->i_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP); /* 2767 perms indicate mandatory locking - will override for dirs later */ cFYI(0, (" Attributes came in as 0x%x ", findData.Attributes)); @@ -246,6 +245,9 @@ inode->i_mode |= S_IFDIR; } else { inode->i_mode |= S_IFREG; + /* treat the dos attribute of read-only as read-only mode e.g. 555 */ + if(cifsInfo->cifsAttrs & ATTR_READONLY) + inode->i_mode &= ~(S_IWUGO); /* BB add code here - validate if device or weird share or device type? */ } inode->i_size = le64_to_cpu(findData.EndOfFile); @@ -303,6 +305,7 @@ struct cifsTconInfo *pTcon; char *full_path = NULL; struct cifsInodeInfo *cifsInode; + FILE_BASIC_INFO * pinfo_buf; cFYI(1, (" cifs_unlink, inode = 0x%p with ", inode)); @@ -329,6 +332,22 @@ /* BB In the future chain close with the NTCreateX to narrow window */ direntry->d_inode->i_nlink--; } + } else if (rc == -EACCES) { + /* try only if r/o attribute set in local lookup data? */ + pinfo_buf = (FILE_BASIC_INFO *)kmalloc(sizeof(FILE_BASIC_INFO),GFP_KERNEL); + if(pinfo_buf) { + memset(pinfo_buf,0,sizeof(FILE_BASIC_INFO)); + /* ATTRS set to normal clears r/o bit */ + pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL); + rc = CIFSSMBSetTimes(xid, pTcon, full_path, pinfo_buf, + cifs_sb->local_nls); + kfree(pinfo_buf); + } + if(rc==0) { + rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls); + if (!rc) + direntry->d_inode->i_nlink--; + } } cifsInode = CIFS_I(direntry->d_inode); cifsInode->time = 0; /* will force revalidate to get info when needed */ @@ -377,15 +396,14 @@ direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); direntry->d_inode->i_nlink = 2; - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) - CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, - 0xFFFFFFFFFFFFFFFF, - 0xFFFFFFFFFFFFFFFF, - cifs_sb->local_nls); - else { /* BB to be implemented via Windows secrty descriptors*/ - /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ - } - + if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) + CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, + 0xFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, + cifs_sb->local_nls); + else { /* BB to be implemented via Windows secrty descriptors*/ + /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ + } } if (full_path) kfree(full_path); @@ -450,10 +468,10 @@ cifs_sb_source = CIFS_SB(source_inode->i_sb); pTcon = cifs_sb_source->tcon; - if (pTcon != cifs_sb_target->tcon) { + if (pTcon != cifs_sb_target->tcon) { + FreeXid(xid); return -EXDEV; /* BB actually could be allowed if same server, but different share. Might eventually add support for this */ - FreeXid(xid); } fromName = build_path_from_dentry(source_direntry); @@ -471,7 +489,7 @@ if (toName) kfree(toName); - FreeXid(xid); + FreeXid(xid); return rc; } @@ -500,15 +518,15 @@ if (time_before(jiffies, cifsInode->time + HZ)) { if((S_ISREG(direntry->d_inode->i_mode) == 0) || - (direntry->d_inode->i_nlink == 1)) { - if (full_path) - kfree(full_path); - FreeXid(xid); - return rc; - } else { - cFYI(1,("Have to revalidate file due to hardlinks")); - } - + (direntry->d_inode->i_nlink == 1) || + (lookupCacheEnabled == 0)) { + if (full_path) + kfree(full_path); + FreeXid(xid); + return rc; + } else { + cFYI(1,("Have to revalidate file due to hardlinks")); + } } if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) @@ -587,6 +605,28 @@ return; } +static int cifs_trunc_page(struct address_space *mapping, loff_t from) +{ + pgoff_t index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_CACHE_SIZE-1); + struct page *page; + char *kaddr; + int rc = 0; + + page = grab_cache_page(mapping, index); + if (!page) + return -ENOMEM; + + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + set_page_dirty(page); + unlock_page(page); + page_cache_release(page); + return rc; +} + int cifs_setattr(struct dentry *direntry, struct iattr *attrs) { @@ -639,7 +679,8 @@ /* CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, TRUE, cifs_sb->local_nls);*/ if (rc == 0) { rc = vmtruncate(direntry->d_inode, attrs->ia_size); - nobh_truncate_page(direntry->d_inode->i_mapping, direntry->d_inode->i_size); + cifs_trunc_page(direntry->d_inode->i_mapping, direntry->d_inode->i_size); + /* cFYI(1,("truncate_page to 0x%lx \n",direntry->d_inode->i_size)); */ } } @@ -653,17 +694,29 @@ gid = attrs->ia_gid; /* entry->gid = cpu_to_le16(attr->ia_gid); */ } + + time_buf.Attributes = 0; if (attrs->ia_valid & ATTR_MODE) { cFYI(1, (" CIFS - Mode changed to 0x%x", attrs->ia_mode)); mode = attrs->ia_mode; - /* entry->mode = cpu_to_le16(attr->ia_mode); */ + /* entry->mode = cpu_to_le16(attr->ia_mode); */ } if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID))) rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid, cifs_sb->local_nls); - else { /* BB to be implemented - via Windows security descriptors */ + else if (attrs->ia_valid & ATTR_MODE) { + if((mode & S_IWUGO) == 0) /* not writeable */ { + if((cifsInode->cifsAttrs & ATTR_READONLY) == 0) + time_buf.Attributes = + cpu_to_le32(cifsInode->cifsAttrs | ATTR_READONLY); + } else if((mode & S_IWUGO) == S_IWUGO) { + if(cifsInode->cifsAttrs & ATTR_READONLY) + time_buf.Attributes = + cpu_to_le32(cifsInode->cifsAttrs & (~ATTR_READONLY)); + } + /* BB to be implemented - via Windows security descriptors or streams */ /* CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,uid,gid,cifs_sb->local_nls);*/ } @@ -689,14 +742,12 @@ } else time_buf.ChangeTime = 0; - if (set_time) { - /* BB handle errors better if one attribute not set - (such as size) but time setting works */ + if (set_time | time_buf.Attributes) { + /* BB what if setting one attribute fails + (such as size) but time setting works */ time_buf.CreationTime = 0; /* do not change */ - time_buf.Attributes = 0; /* BB is this ignored by server? - or do I have to query and reset anyway BB */ rc = CIFSSMBSetTimes(xid, pTcon, full_path, &time_buf, - cifs_sb->local_nls); + cifs_sb->local_nls); } /* do not need local check to inode_check_ok since the server does that */ diff -urN linux-2.5.68-bk7/fs/cifs/link.c linux-2.5.68-bk8/fs/cifs/link.c --- linux-2.5.68-bk7/fs/cifs/link.c 2003-04-19 19:48:57.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/link.c 2003-04-27 04:36:28.000000000 -0700 @@ -82,7 +82,7 @@ int rc = -EACCES; int xid; char *full_path = NULL; - char target_path[257]; + char * target_path; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -91,7 +91,11 @@ cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - + target_path = kmalloc(PATH_MAX, GFP_KERNEL); + if(target_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } /* can not call the following line due to EFAULT in vfs_readlink which is presumably expecting a user space buffer */ /* length = cifs_readlink(direntry,target_path, sizeof(target_path) - 1); */ @@ -99,7 +103,7 @@ if (pTcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, target_path, - sizeof (target_path) - 1, + PATH_MAX-1, cifs_sb->local_nls); else { /* rc = CIFSSMBQueryReparseLinkInfo */ @@ -109,11 +113,13 @@ /* BB Should we be using page symlink ops here? */ if (rc == 0) { - target_path[256] = 0; + target_path[PATH_MAX-1] = 0; rc = vfs_follow_link(nd, target_path); } /* else EACCESS */ + if (target_path) + kfree(target_path); if (full_path) kfree(full_path); FreeXid(xid); @@ -180,7 +186,8 @@ struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; - char tmpbuffer[256]; + char * tmpbuffer; + int len; __u16 fid; xid = GetXid(); @@ -190,20 +197,28 @@ cFYI(1, ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d", full_path, inode, pBuffer, buflen)); - + if(buflen > PATH_MAX) + len = PATH_MAX; + else + len = buflen; + tmpbuffer = kmalloc(len,GFP_KERNEL); + if(tmpbuffer == NULL) { + FreeXid(xid); + return -ENOMEM; + } /* BB add read reparse point symlink code and Unix extensions symlink code here BB */ if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, - tmpbuffer, - sizeof (tmpbuffer) - 1, - cifs_sb->local_nls); + tmpbuffer, + len - 1, + cifs_sb->local_nls); else { rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, OPEN_REPARSE_POINT,&fid, &oplock, cifs_sb->local_nls); if(!rc) { rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path, tmpbuffer, - sizeof(tmpbuffer) - 1, + len - 1, fid, cifs_sb->local_nls); if(CIFSSMBClose(xid, pTcon, fid)) { @@ -216,15 +231,15 @@ /* BB Should we be using page ops here? */ /* BB null terminate returned string in pBuffer? BB */ - if (buflen > sizeof (tmpbuffer)) - buflen = sizeof (tmpbuffer); if (rc == 0) { - rc = vfs_readlink(direntry, pBuffer, buflen, tmpbuffer); + rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer); cFYI(1, ("vfs_readlink called from cifs_readlink returned %d", rc)); } + if (tmpbuffer) + kfree(tmpbuffer); if (full_path) kfree(full_path); FreeXid(xid); diff -urN linux-2.5.68-bk7/fs/cifs/misc.c linux-2.5.68-bk8/fs/cifs/misc.c --- linux-2.5.68-bk7/fs/cifs/misc.c 2003-04-19 19:51:12.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/misc.c 2003-04-27 04:36:28.000000000 -0700 @@ -27,6 +27,7 @@ #include "cifs_debug.h" extern kmem_cache_t *cifs_req_cachep; +extern struct task_struct * oplockThread; __u16 GlobalMid; /* multiplex id - rotating counter */ @@ -336,6 +337,7 @@ list_for_each(tmp1,&tcon->openFileList){ netfile = list_entry(tmp1,struct cifsFileInfo,tlist); if(pSMB->Fid == netfile->netfid) { + struct cifsInodeInfo *pCifsInode; /* BB Add following logic: 2) look up inode from tcon->openFileList->file->f_dentry->d_inode 3) flush dirty pages and cached byte range locks and mark inode @@ -345,6 +347,15 @@ 6) send oplock break response to server */ read_unlock(&GlobalSMBSeslock); cFYI(1,("Matching file id, processing oplock break")); + pCifsInode = + CIFS_I(netfile->pfile->f_dentry->d_inode); + pCifsInode->clientCanCacheAll = FALSE; + if(pSMB->OplockLevel == 0) + pCifsInode->clientCanCacheRead = FALSE; + pCifsInode->oplockPending = TRUE; + AllocOplockQEntry(netfile->pfile, tcon); + cFYI(1,("about to wake up oplock thd")); + wake_up_process(oplockThread); return TRUE; } } diff -urN linux-2.5.68-bk7/fs/cifs/transport.c linux-2.5.68-bk8/fs/cifs/transport.c --- linux-2.5.68-bk7/fs/cifs/transport.c 2003-04-19 19:48:57.000000000 -0700 +++ linux-2.5.68-bk8/fs/cifs/transport.c 2003-04-27 04:36:28.000000000 -0700 @@ -32,6 +32,7 @@ #include "cifs_debug.h" extern kmem_cache_t *cifs_mid_cachep; +extern kmem_cache_t *cifs_oplock_cachep; struct mid_q_entry * AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) @@ -95,6 +96,39 @@ kmem_cache_free(cifs_mid_cachep, midEntry); } +struct oplock_q_entry * +AllocOplockQEntry(struct file * file, struct cifsTconInfo * tcon) +{ + struct oplock_q_entry *temp; + if ((file == NULL) || (tcon == NULL)) { + cERROR(1, ("Null parms passed to AllocOplockQEntry")); + return NULL; + } + temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep, + SLAB_KERNEL); + if (temp == NULL) + return temp; + else { + temp->file_to_flush = file; + temp->tcon = tcon; + write_lock(&GlobalMid_Lock); + list_add_tail(&temp->qhead, &GlobalOplock_Q); + write_unlock(&GlobalMid_Lock); + } + return temp; + +} + +void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry) +{ + /* BB add spinlock to protect midq for each session BB */ + write_lock(&GlobalMid_Lock); + /* should we check if list empty first? */ + list_del(&oplockEntry->qhead); + write_unlock(&GlobalMid_Lock); + kmem_cache_free(cifs_oplock_cachep, oplockEntry); +} + int smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, unsigned int smb_buf_length, struct sockaddr *sin) diff -urN linux-2.5.68-bk7/include/linux/list.h linux-2.5.68-bk8/include/linux/list.h --- linux-2.5.68-bk7/include/linux/list.h 2003-04-19 19:49:56.000000000 -0700 +++ linux-2.5.68-bk8/include/linux/list.h 2003-04-27 04:36:28.000000000 -0700 @@ -320,6 +320,21 @@ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) +/** + * list_for_each_entry_rcu - iterate over rcu list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_rcu(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is diff -urN linux-2.5.68-bk7/include/net/if_inet6.h linux-2.5.68-bk8/include/net/if_inet6.h --- linux-2.5.68-bk7/include/net/if_inet6.h 2003-04-19 19:51:12.000000000 -0700 +++ linux-2.5.68-bk8/include/net/if_inet6.h 2003-04-27 04:36:28.000000000 -0700 @@ -15,6 +15,8 @@ #ifndef _NET_IF_INET6_H #define _NET_IF_INET6_H +#include + #define IF_RA_RCVD 0x20 #define IF_RS_SENT 0x10 @@ -152,6 +154,11 @@ void *sysctl; }; +struct ipv6_devstat { + struct proc_dir_entry *proc_dir_entry; + DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6); +}; + struct inet6_dev { struct net_device *dev; @@ -185,6 +192,7 @@ struct neigh_parms *nd_parms; struct inet6_dev *next; struct ipv6_devconf cnf; + struct ipv6_devstat stats; }; extern struct ipv6_devconf ipv6_devconf; diff -urN linux-2.5.68-bk7/include/net/ipv6.h linux-2.5.68-bk8/include/net/ipv6.h --- linux-2.5.68-bk7/include/net/ipv6.h 2003-04-19 19:50:39.000000000 -0700 +++ linux-2.5.68-bk8/include/net/ipv6.h 2003-04-27 04:36:28.000000000 -0700 @@ -106,24 +106,48 @@ /* sysctls */ extern int sysctl_ipv6_bindv6only; +/* MIBs */ DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics); #define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field) #define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field) #define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field) DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics); -#define ICMP6_INC_STATS(field) SNMP_INC_STATS(icmpv6_statistics, field) -#define ICMP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmpv6_statistics, field) -#define ICMP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmpv6_statistics, field) -#define ICMP6_STATS_PTR_BH(field) \ - (& \ - ((per_cpu_ptr(icmpv6_statistics[0], smp_processor_id()))-> \ - field)) +#define ICMP6_INC_STATS(idev, field) ({ \ + struct inet6_dev *_idev = (idev); \ + if (likely(_idev != NULL)) \ + SNMP_INC_STATS(idev->stats.icmpv6, field); \ + SNMP_INC_STATS(icmpv6_statistics, field); \ +}) +#define ICMP6_INC_STATS_BH(idev, field) ({ \ + struct inet6_dev *_idev = (idev); \ + if (likely(_idev != NULL)) \ + SNMP_INC_STATS_BH((_idev)->stats.icmpv6, field); \ + SNMP_INC_STATS_BH(icmpv6_statistics, field); \ +}) +#define ICMP6_INC_STATS_USER(idev, field) ({ \ + struct inet6_dev *_idev = (idev); \ + if (likely(_idev != NULL)) \ + SNMP_INC_STATS_USER(_idev->stats.icmpv6, field); \ + SNMP_INC_STATS_USER(icmpv6_statistics, field); \ +}) +#define ICMP6_INC_STATS_OFFSET_BH(idev, field, offset) ({ \ + struct inet6_dev *_idev = idev; \ + __typeof__(offset) _offset = (offset); \ + if (likely(_idev != NULL)) \ + SNMP_INC_STATS_OFFSET_BH(_idev->stats.icmpv6, field, _offset); \ + SNMP_INC_STATS_OFFSET_BH(icmpv6_statistics, field, _offset); \ +}) DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6); #define UDP6_INC_STATS(field) SNMP_INC_STATS(udp_stats_in6, field) #define UDP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field) #define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field) extern atomic_t inet6_sock_nr; +int snmp6_register_dev(struct inet6_dev *idev); +int snmp6_unregister_dev(struct inet6_dev *idev); +int snmp6_mib_init(void *ptr[2], size_t mibsize); +void snmp6_mib_free(void *ptr[2]); + struct ip6_ra_chain { struct ip6_ra_chain *next; diff -urN linux-2.5.68-bk7/include/net/snmp.h linux-2.5.68-bk8/include/net/snmp.h --- linux-2.5.68-bk7/include/net/snmp.h 2003-04-19 19:49:10.000000000 -0700 +++ linux-2.5.68-bk8/include/net/snmp.h 2003-04-27 04:36:28.000000000 -0700 @@ -304,6 +304,8 @@ #define SNMP_INC_STATS_BH(mib, field) \ (per_cpu_ptr(mib[0], smp_processor_id())->field++) +#define SNMP_INC_STATS_OFFSET_BH(mib, field, offset) \ + ((*((&per_cpu_ptr(mib[0], smp_processor_id())->field) + (offset)))++) #define SNMP_INC_STATS_USER(mib, field) \ (per_cpu_ptr(mib[1], smp_processor_id())->field++) #define SNMP_INC_STATS(mib, field) \ diff -urN linux-2.5.68-bk7/init/Makefile linux-2.5.68-bk8/init/Makefile --- linux-2.5.68-bk7/init/Makefile 2003-04-19 19:49:11.000000000 -0700 +++ linux-2.5.68-bk8/init/Makefile 2003-04-27 04:36:28.000000000 -0700 @@ -6,6 +6,7 @@ mounts-y := do_mounts.o mounts-$(CONFIG_DEVFS_FS) += do_mounts_devfs.o mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o +mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o # files to be removed upon make clean diff -urN linux-2.5.68-bk7/init/do_mounts_initrd.c linux-2.5.68-bk8/init/do_mounts_initrd.c --- linux-2.5.68-bk7/init/do_mounts_initrd.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.68-bk8/init/do_mounts_initrd.c 2003-04-27 04:36:28.000000000 -0700 @@ -0,0 +1,115 @@ + +#include +#include +#include +#include +#include +#include + +#include "do_mounts.h" + +unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */ +static int __initdata old_fd, root_fd; +static int __initdata mount_initrd = 1; + +static int __init no_initrd(char *str) +{ + mount_initrd = 0; + return 1; +} + +__setup("noinitrd", no_initrd); + +static int __init do_linuxrc(void * shell) +{ + static char *argv[] = { "linuxrc", NULL, }; + extern char * envp_init[]; + + close(old_fd);close(root_fd); + close(0);close(1);close(2); + setsid(); + (void) open("/dev/console",O_RDWR,0); + (void) dup(0); + (void) dup(0); + return execve(shell, argv, envp_init); +} + +static void __init handle_initrd(void) +{ + int error; + int i, pid; + + real_root_dev = ROOT_DEV; + create_dev("/dev/root.old", Root_RAM0, NULL); + /* mount initrd on rootfs' /root */ + mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); + sys_mkdir("/old", 0700); + root_fd = open("/", 0, 0); + old_fd = open("/old", 0, 0); + /* move initrd over / and chdir/chroot in initrd root */ + sys_chdir("/root"); + sys_mount(".", "/", NULL, MS_MOVE, NULL); + sys_chroot("."); + mount_devfs_fs (); + + pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); + if (pid > 0) { + while (pid != waitpid(-1, &i, 0)) + yield(); + } + + /* move initrd to rootfs' /old */ + sys_fchdir(old_fd); + sys_mount("/", ".", NULL, MS_MOVE, NULL); + /* switch root and cwd back to / of rootfs */ + sys_fchdir(root_fd); + sys_chroot("."); + close(old_fd); + close(root_fd); + umount_devfs("/old/dev"); + + if (real_root_dev == Root_RAM0) { + sys_chdir("/old"); + return; + } + + ROOT_DEV = real_root_dev; + mount_root(); + + printk(KERN_NOTICE "Trying to move old root to /initrd ... "); + error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); + if (!error) + printk("okay\n"); + else { + int fd = open("/dev/root.old", O_RDWR, 0); + printk("failed\n"); + printk(KERN_NOTICE "Unmounting old root\n"); + sys_umount("/old", MNT_DETACH); + printk(KERN_NOTICE "Trying to free ramdisk memory ... "); + if (fd < 0) { + error = fd; + } else { + error = sys_ioctl(fd, BLKFLSBUF, 0); + close(fd); + } + printk(!error ? "okay\n" : "failed\n"); + } +} + +int __init initrd_load(void) +{ + if (!mount_initrd) + return 0; + + create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, 0), NULL); + create_dev("/dev/initrd", MKDEV(RAMDISK_MAJOR, INITRD_MINOR), NULL); + /* Load the initrd data into /dev/ram0. Execute it as initrd unless + * /dev/ram0 is supposed to be our actual root device, in + * that case the ram disk is just set up here, and gets + * mounted in the normal path. */ + if (rd_load_image("/dev/initrd") && ROOT_DEV != Root_RAM0) { + handle_initrd(); + return 1; + } + return 0; +} diff -urN linux-2.5.68-bk7/init/do_mounts_rd.c linux-2.5.68-bk8/init/do_mounts_rd.c --- linux-2.5.68-bk7/init/do_mounts_rd.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/init/do_mounts_rd.c 2003-04-27 04:36:28.000000000 -0700 @@ -248,115 +248,6 @@ return rd_load_image("/dev/root"); } -#ifdef CONFIG_BLK_DEV_INITRD - -unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */ -static int __initdata old_fd, root_fd; -static int __initdata mount_initrd = 1; - -static int __init no_initrd(char *str) -{ - mount_initrd = 0; - return 1; -} - -__setup("noinitrd", no_initrd); - -static int __init do_linuxrc(void * shell) -{ - static char *argv[] = { "linuxrc", NULL, }; - extern char * envp_init[]; - - close(old_fd);close(root_fd); - close(0);close(1);close(2); - setsid(); - (void) open("/dev/console",O_RDWR,0); - (void) dup(0); - (void) dup(0); - return execve(shell, argv, envp_init); -} - -static void __init handle_initrd(void) -{ - int error; - int i, pid; - - real_root_dev = ROOT_DEV; - create_dev("/dev/root.old", Root_RAM0, NULL); - /* mount initrd on rootfs' /root */ - mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); - sys_mkdir("/old", 0700); - root_fd = open("/", 0, 0); - old_fd = open("/old", 0, 0); - /* move initrd over / and chdir/chroot in initrd root */ - sys_chdir("/root"); - sys_mount(".", "/", NULL, MS_MOVE, NULL); - sys_chroot("."); - mount_devfs_fs (); - - pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); - if (pid > 0) { - while (pid != waitpid(-1, &i, 0)) - yield(); - } - - /* move initrd to rootfs' /old */ - sys_fchdir(old_fd); - sys_mount("/", ".", NULL, MS_MOVE, NULL); - /* switch root and cwd back to / of rootfs */ - sys_fchdir(root_fd); - sys_chroot("."); - close(old_fd); - close(root_fd); - umount_devfs("/old/dev"); - - if (real_root_dev == Root_RAM0) { - sys_chdir("/old"); - return; - } - - ROOT_DEV = real_root_dev; - mount_root(); - - printk(KERN_NOTICE "Trying to move old root to /initrd ... "); - error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); - if (!error) - printk("okay\n"); - else { - int fd = open("/dev/root.old", O_RDWR, 0); - printk("failed\n"); - printk(KERN_NOTICE "Unmounting old root\n"); - sys_umount("/old", MNT_DETACH); - printk(KERN_NOTICE "Trying to free ramdisk memory ... "); - if (fd < 0) { - error = fd; - } else { - error = sys_ioctl(fd, BLKFLSBUF, 0); - close(fd); - } - printk(!error ? "okay\n" : "failed\n"); - } -} - -int __init initrd_load(void) -{ - if (!mount_initrd) - return 0; - - create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, 0), NULL); - create_dev("/dev/initrd", MKDEV(RAMDISK_MAJOR, INITRD_MINOR), NULL); - /* Load the initrd data into /dev/ram0. Execute it as initrd unless - * /dev/ram0 is supposed to be our actual root device, in - * that case the ram disk is just set up here, and gets - * mounted in the normal path. */ - if (rd_load_image("/dev/initrd") && ROOT_DEV != Root_RAM0) { - handle_initrd(); - return 1; - } - return 0; -} -#endif - #ifdef BUILD_CRAMDISK /* diff -urN linux-2.5.68-bk7/kernel/resource.c linux-2.5.68-bk8/kernel/resource.c --- linux-2.5.68-bk7/kernel/resource.c 2003-04-19 19:50:07.000000000 -0700 +++ linux-2.5.68-bk8/kernel/resource.c 2003-04-27 04:36:28.000000000 -0700 @@ -21,7 +21,7 @@ struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO }; -struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM }; +struct resource iomem_resource = { "PCI mem", 0UL, ~0UL, IORESOURCE_MEM }; static rwlock_t resource_lock = RW_LOCK_UNLOCKED; diff -urN linux-2.5.68-bk7/net/bridge/br_device.c linux-2.5.68-bk8/net/bridge/br_device.c --- linux-2.5.68-bk7/net/bridge/br_device.c 2003-04-19 19:48:46.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_device.c 2003-04-27 04:36:28.000000000 -0700 @@ -74,27 +74,20 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { - struct net_bridge *br; int ret; - br = dev->priv; - read_lock(&br->lock); + rcu_read_lock(); ret = __br_dev_xmit(skb, dev); - read_unlock(&br->lock); + rcu_read_unlock(); return ret; } static int br_dev_open(struct net_device *dev) { - struct net_bridge *br; - netif_start_queue(dev); - br = dev->priv; - write_lock(&br->lock); - br_stp_enable_bridge(br); - write_unlock(&br->lock); + br_stp_enable_bridge(dev->priv); return 0; } @@ -105,12 +98,7 @@ static int br_dev_stop(struct net_device *dev) { - struct net_bridge *br; - - br = dev->priv; - write_lock(&br->lock); - br_stp_disable_bridge(br); - write_unlock(&br->lock); + br_stp_disable_bridge(dev->priv); netif_stop_queue(dev); diff -urN linux-2.5.68-bk7/net/bridge/br_forward.c linux-2.5.68-bk8/net/bridge/br_forward.c --- linux-2.5.68-bk7/net/bridge/br_forward.c 2003-04-19 19:48:57.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_forward.c 2003-04-27 04:36:28.000000000 -0700 @@ -21,7 +21,8 @@ #include #include "br_private.h" -static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb) +static inline int should_deliver(const struct net_bridge_port *p, + const struct sk_buff *skb) { if (skb->dev == p->dev || p->state != BR_STATE_FORWARDING) @@ -52,7 +53,7 @@ return 0; } -static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb) +static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { skb->dev = to->dev; #ifdef CONFIG_NETFILTER_DEBUG @@ -62,7 +63,7 @@ br_forward_finish); } -static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) +static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) { struct net_device *indev; @@ -73,8 +74,8 @@ br_forward_finish); } -/* called under bridge lock */ -void br_deliver(struct net_bridge_port *to, struct sk_buff *skb) +/* called with rcu_read_lock */ +void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { if (should_deliver(to, skb)) { __br_deliver(to, skb); @@ -84,8 +85,8 @@ kfree_skb(skb); } -/* called under bridge lock */ -void br_forward(struct net_bridge_port *to, struct sk_buff *skb) +/* called with rcu_read_lock */ +void br_forward(const struct net_bridge_port *to, struct sk_buff *skb) { if (should_deliver(to, skb)) { __br_forward(to, skb); @@ -97,7 +98,8 @@ /* called under bridge lock */ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, - void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb)) + void (*__packet_hook)(const struct net_bridge_port *p, + struct sk_buff *skb)) { struct net_bridge_port *p; struct net_bridge_port *prev; @@ -115,8 +117,7 @@ prev = NULL; - p = br->port_list; - while (p != NULL) { + list_for_each_entry_rcu(p, &br->port_list, list) { if (should_deliver(p, skb)) { if (prev != NULL) { struct sk_buff *skb2; @@ -132,8 +133,6 @@ prev = p; } - - p = p->next; } if (prev != NULL) { @@ -144,7 +143,8 @@ kfree_skb(skb); } -/* called under bridge lock */ + +/* called with rcu_read_lock */ void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone) { br_flood(br, skb, clone, __br_deliver); diff -urN linux-2.5.68-bk7/net/bridge/br_if.c linux-2.5.68-bk8/net/bridge/br_if.c --- linux-2.5.68-bk7/net/bridge/br_if.c 2003-04-19 19:51:13.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_if.c 2003-04-27 04:36:28.000000000 -0700 @@ -18,8 +18,8 @@ #include #include #include +#include #include -#include #include #include #include "br_private.h" @@ -38,45 +38,39 @@ return 100; } -/* called under BR_NETPROTO_LOCK and bridge lock */ -static int __br_del_if(struct net_bridge *br, struct net_device *dev) +static void destroy_nbp(void *arg) { - struct net_bridge_port *p; - struct net_bridge_port **pptr; + struct net_bridge_port *p = arg; + dev_put(p->dev); + kfree(p); +} - if ((p = dev->br_port) == NULL) - return -EINVAL; +/* called under bridge lock */ +static void del_nbp(struct net_bridge_port *p) +{ + struct net_device *dev = p->dev; br_stp_disable_port(p); dev_set_promiscuity(dev, -1); dev->br_port = NULL; - pptr = &br->port_list; - while (*pptr != NULL) { - if (*pptr == p) { - *pptr = p->next; - break; - } + list_del_rcu(&p->list); - pptr = &((*pptr)->next); - } + br_fdb_delete_by_port(p->br, p); - br_fdb_delete_by_port(br, p); - kfree(p); - dev_put(dev); - - return 0; + call_rcu(&p->rcu, destroy_nbp, p); } static void del_ifs(struct net_bridge *br) { - br_write_lock_bh(BR_NETPROTO_LOCK); - write_lock(&br->lock); - while (br->port_list != NULL) - __br_del_if(br, br->port_list->dev); - write_unlock(&br->lock); - br_write_unlock_bh(BR_NETPROTO_LOCK); + struct list_head *p, *n; + + spin_lock_bh(&br->lock); + list_for_each_safe(p, n, &br->port_list) { + del_nbp(list_entry(p, struct net_bridge_port, list)); + } + spin_unlock_bh(&br->lock); } static struct net_bridge *new_nb(const char *name) @@ -98,7 +92,8 @@ ether_setup(dev); br_dev_setup(dev); - br->lock = RW_LOCK_UNLOCKED; + br->lock = SPIN_LOCK_UNLOCKED; + INIT_LIST_HEAD(&br->port_list); br->hash_lock = RW_LOCK_UNLOCKED; br->bridge_id.prio[0] = 0x80; @@ -155,8 +150,7 @@ br_init_port(p); p->state = BR_STATE_DISABLED; - p->next = br->port_list; - br->port_list = p; + list_add_rcu(&p->list, &br->port_list); return p; } @@ -218,9 +212,9 @@ return -ELOOP; dev_hold(dev); - write_lock_bh(&br->lock); + spin_lock_bh(&br->lock); if ((p = new_nbp(br, dev)) == NULL) { - write_unlock_bh(&br->lock); + spin_unlock_bh(&br->lock); dev_put(dev); return -EXFULL; } @@ -231,21 +225,24 @@ br_fdb_insert(br, p, dev->dev_addr, 1); if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP)) br_stp_enable_port(p); - write_unlock_bh(&br->lock); + spin_unlock_bh(&br->lock); return 0; } int br_del_if(struct net_bridge *br, struct net_device *dev) { - int retval; + struct net_bridge_port *p; + int retval = 0; - br_write_lock_bh(BR_NETPROTO_LOCK); - write_lock(&br->lock); - retval = __br_del_if(br, dev); - br_stp_recalculate_bridge_id(br); - write_unlock(&br->lock); - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_lock_bh(&br->lock); + if ((p = dev->br_port) == NULL || p->br != br) + retval = -EINVAL; + else { + del_nbp(p); + br_stp_recalculate_bridge_id(br); + } + spin_unlock_bh(&br->lock); return retval; } @@ -269,13 +266,11 @@ { struct net_bridge_port *p; - read_lock(&br->lock); - p = br->port_list; - while (p != NULL) { + rcu_read_lock(); + list_for_each_entry_rcu(p, &br->port_list, list) { ifindices[p->port_no] = p->dev->ifindex; - p = p->next; } - read_unlock(&br->lock); + rcu_read_unlock(); } diff -urN linux-2.5.68-bk7/net/bridge/br_input.c linux-2.5.68-bk8/net/bridge/br_input.c --- linux-2.5.68-bk7/net/bridge/br_input.c 2003-04-19 19:48:56.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_input.c 2003-04-27 04:36:28.000000000 -0700 @@ -59,15 +59,16 @@ dest = skb->mac.ethernet->h_dest; + rcu_read_lock(); p = skb->dev->br_port; - if (p == NULL) - goto err_nolock; + smp_read_barrier_depends(); - br = p->br; - read_lock(&br->lock); - if (skb->dev->br_port == NULL) - goto err; + if (p == NULL || p->state == BR_STATE_DISABLED) { + kfree(skb); + goto out; + } + br = p->br; passedup = 0; if (br->dev.flags & IFF_PROMISC) { struct sk_buff *skb2; @@ -105,35 +106,20 @@ br_flood_forward(br, skb, 0); out: - read_unlock(&br->lock); - return 0; - -err: - read_unlock(&br->lock); -err_nolock: - kfree_skb(skb); + rcu_read_unlock(); return 0; } int br_handle_frame(struct sk_buff *skb) { - struct net_bridge *br; unsigned char *dest; struct net_bridge_port *p; dest = skb->mac.ethernet->h_dest; + rcu_read_lock(); p = skb->dev->br_port; - if (p == NULL) - goto err_nolock; - - br = p->br; - read_lock(&br->lock); - if (skb->dev->br_port == NULL) - goto err; - - if (!(br->dev.flags & IFF_UP) || - p->state == BR_STATE_DISABLED) + if (p == NULL || p->state == BR_STATE_DISABLED) goto err; if (skb->mac.ethernet->h_source[0] & 1) @@ -141,39 +127,30 @@ if (p->state == BR_STATE_LEARNING || p->state == BR_STATE_FORWARDING) - br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); + br_fdb_insert(p->br, p, skb->mac.ethernet->h_source, 0); - if (br->stp_enabled && + if (p->br->stp_enabled && !memcmp(dest, bridge_ula, 5) && - !(dest[5] & 0xF0)) - goto handle_special_frame; + !(dest[5] & 0xF0)) { + if (!dest[5]) + br_stp_handle_bpdu(skb); + goto err; + } if (p->state == BR_STATE_FORWARDING) { if (br_should_route_hook && br_should_route_hook(&skb)) { - read_unlock(&br->lock); + rcu_read_unlock(); return -1; } NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish); - read_unlock(&br->lock); + rcu_read_unlock(); return 0; } err: - read_unlock(&br->lock); -err_nolock: - kfree_skb(skb); - return 0; - -handle_special_frame: - if (!dest[5]) { - br_stp_handle_bpdu(skb); - read_unlock(&br->lock); - return 0; - } - + rcu_read_unlock(); kfree_skb(skb); - read_unlock(&br->lock); return 0; } diff -urN linux-2.5.68-bk7/net/bridge/br_ioctl.c linux-2.5.68-bk8/net/bridge/br_ioctl.c --- linux-2.5.68-bk7/net/bridge/br_ioctl.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_ioctl.c 2003-04-27 04:36:28.000000000 -0700 @@ -68,8 +68,8 @@ { struct __bridge_info b; - read_lock(&br->lock); memset(&b, 0, sizeof(struct __bridge_info)); + rcu_read_lock(); memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.bridge_id, &br->bridge_id, 8); b.root_path_cost = br->root_path_cost; @@ -89,7 +89,7 @@ b.tcn_timer_value = timer_residue(&br->tcn_timer); b.topology_change_timer_value = timer_residue(&br->topology_change_timer); b.gc_timer_value = timer_residue(&br->gc_timer); - read_unlock(&br->lock); + rcu_read_unlock(); if (copy_to_user((void *)arg0, &b, sizeof(b))) return -EFAULT; @@ -116,27 +116,27 @@ } case BRCTL_SET_BRIDGE_FORWARD_DELAY: - write_lock(&br->lock); + spin_lock_bh(&br->lock); br->bridge_forward_delay = user_to_ticks(arg0); if (br_is_root_bridge(br)) br->forward_delay = br->bridge_forward_delay; - write_unlock(&br->lock); + spin_unlock_bh(&br->lock); return 0; case BRCTL_SET_BRIDGE_HELLO_TIME: - write_lock(&br->lock); + spin_lock_bh(&br->lock); br->bridge_hello_time = user_to_ticks(arg0); if (br_is_root_bridge(br)) br->hello_time = br->bridge_hello_time; - write_unlock(&br->lock); + spin_unlock_bh(&br->lock); return 0; case BRCTL_SET_BRIDGE_MAX_AGE: - write_lock(&br->lock); + spin_lock_bh(&br->lock); br->bridge_max_age = user_to_ticks(arg0); if (br_is_root_bridge(br)) br->max_age = br->bridge_max_age; - write_unlock(&br->lock); + spin_unlock_bh(&br->lock); return 0; case BRCTL_SET_AGEING_TIME: @@ -152,9 +152,9 @@ struct __port_info p; struct net_bridge_port *pt; - read_lock(&br->lock); + rcu_read_lock(); if ((pt = br_get_port(br, arg1)) == NULL) { - read_unlock(&br->lock); + rcu_read_unlock(); return -EINVAL; } @@ -172,7 +172,7 @@ p.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer); p.hold_timer_value = timer_residue(&pt->hold_timer); - read_unlock(&br->lock); + rcu_read_unlock(); if (copy_to_user((void *)arg0, &p, sizeof(p))) return -EFAULT; @@ -185,9 +185,9 @@ return 0; case BRCTL_SET_BRIDGE_PRIORITY: - write_lock(&br->lock); + spin_lock_bh(&br->lock); br_stp_set_bridge_priority(br, arg0); - write_unlock(&br->lock); + spin_unlock_bh(&br->lock); return 0; case BRCTL_SET_PORT_PRIORITY: @@ -195,12 +195,12 @@ struct net_bridge_port *p; int ret = 0; - write_lock(&br->lock); + spin_lock_bh(&br->lock); if ((p = br_get_port(br, arg0)) == NULL) ret = -EINVAL; else br_stp_set_port_priority(p, arg1); - write_unlock(&br->lock); + spin_unlock_bh(&br->lock); return ret; } @@ -209,12 +209,12 @@ struct net_bridge_port *p; int ret = 0; - write_lock(&br->lock); + spin_lock_bh(&br->lock); if ((p = br_get_port(br, arg0)) == NULL) ret = -EINVAL; else br_stp_set_path_cost(p, arg1); - write_unlock(&br->lock); + spin_unlock_bh(&br->lock); return ret; } diff -urN linux-2.5.68-bk7/net/bridge/br_notify.c linux-2.5.68-bk8/net/bridge/br_notify.c --- linux-2.5.68-bk7/net/bridge/br_notify.c 2003-04-19 19:48:50.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_notify.c 2003-04-27 04:36:28.000000000 -0700 @@ -41,10 +41,10 @@ switch (event) { case NETDEV_CHANGEADDR: - write_lock_bh(&br->lock); + spin_lock_bh(&br->lock); br_fdb_changeaddr(p, dev->dev_addr); br_stp_recalculate_bridge_id(br); - write_unlock_bh(&br->lock); + spin_unlock_bh(&br->lock); break; case NETDEV_GOING_DOWN: @@ -53,17 +53,17 @@ case NETDEV_DOWN: if (br->dev.flags & IFF_UP) { - write_lock_bh(&br->lock); + spin_lock_bh(&br->lock); br_stp_disable_port(p); - write_unlock_bh(&br->lock); + spin_unlock_bh(&br->lock); } break; case NETDEV_UP: if (!(br->dev.flags & IFF_UP)) { - write_lock_bh(&br->lock); + spin_lock_bh(&br->lock); br_stp_enable_port(p); - write_unlock_bh(&br->lock); + spin_unlock_bh(&br->lock); } break; diff -urN linux-2.5.68-bk7/net/bridge/br_private.h linux-2.5.68-bk8/net/bridge/br_private.h --- linux-2.5.68-bk7/net/bridge/br_private.h 2003-04-19 19:48:55.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_private.h 2003-04-27 04:36:28.000000000 -0700 @@ -55,9 +55,9 @@ struct net_bridge_port { - struct net_bridge_port *next; struct net_bridge *br; struct net_device *dev; + struct list_head list; int port_no; /* STP */ @@ -75,12 +75,14 @@ struct br_timer forward_delay_timer; struct br_timer hold_timer; struct br_timer message_age_timer; + + struct rcu_head rcu; }; struct net_bridge { - rwlock_t lock; - struct net_bridge_port *port_list; + spinlock_t lock; + struct list_head port_list; struct net_device dev; struct net_device_stats statistics; rwlock_t hash_lock; @@ -137,10 +139,10 @@ int is_local); /* br_forward.c */ -extern void br_deliver(struct net_bridge_port *to, +extern void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); extern int br_dev_queue_push_xmit(struct sk_buff *skb); -extern void br_forward(struct net_bridge_port *to, +extern void br_forward(const struct net_bridge_port *to, struct sk_buff *skb); extern int br_forward_finish(struct sk_buff *skb); extern void br_flood_deliver(struct net_bridge *br, diff -urN linux-2.5.68-bk7/net/bridge/br_stp.c linux-2.5.68-bk8/net/bridge/br_stp.c --- linux-2.5.68-bk7/net/bridge/br_stp.c 2003-04-19 19:48:55.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_stp.c 2003-04-27 04:36:28.000000000 -0700 @@ -22,7 +22,7 @@ -/* called under ioctl_lock or bridge lock */ +/* called under bridge lock */ int br_is_root_bridge(struct net_bridge *br) { return !memcmp(&br->bridge_id, &br->designated_root, 8); @@ -35,17 +35,14 @@ (p->designated_port == p->port_id); } -/* called under ioctl_lock or bridge lock */ +/* called under bridge lock */ struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no) { struct net_bridge_port *p; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->port_no == port_no) return p; - - p = p->next; } return NULL; @@ -109,12 +106,10 @@ root_port = 0; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (br_should_become_root_port(p, root_port)) root_port = p->port_no; - p = p->next; } br->root_port = root_port; @@ -241,13 +236,11 @@ { struct net_bridge_port *p; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED && br_should_become_designated_port(p)) br_become_designated_port(p); - p = p->next; } } @@ -313,13 +306,10 @@ { struct net_bridge_port *p; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED && br_is_designated_port(p)) br_transmit_config(p); - - p = p->next; } } @@ -391,8 +381,7 @@ { struct net_bridge_port *p; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED) { if (p->port_no == br->root_port) { p->config_pending = 0; @@ -407,8 +396,6 @@ br_make_blocking(p); } } - - p = p->next; } } @@ -419,18 +406,13 @@ br_transmit_config(p); } -/* lock-safe */ +/* called under bridge lock */ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) { struct net_bridge *br; int was_root; - if (p->state == BR_STATE_DISABLED) - return; - br = p->br; - read_lock(&br->lock); - was_root = br_is_root_bridge(br); if (br_supersedes_port_info(p, bpdu)) { br_record_config_information(p, bpdu); @@ -455,21 +437,16 @@ } else if (br_is_designated_port(p)) { br_reply(p); } - - read_unlock(&br->lock); } -/* lock-safe */ +/* called under bridge lock */ void br_received_tcn_bpdu(struct net_bridge_port *p) { - read_lock(&p->br->lock); - if (p->state != BR_STATE_DISABLED && - br_is_designated_port(p)) { + if (br_is_designated_port(p)) { printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n", p->br->dev.name, p->port_no, p->dev->name); br_topology_change_detection(p->br); br_topology_change_acknowledge(p); } - read_unlock(&p->br->lock); } diff -urN linux-2.5.68-bk7/net/bridge/br_stp_bpdu.c linux-2.5.68-bk8/net/bridge/br_stp_bpdu.c --- linux-2.5.68-bk7/net/bridge/br_stp_bpdu.c 2003-04-19 19:50:36.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_stp_bpdu.c 2003-04-27 04:36:28.000000000 -0700 @@ -132,18 +132,23 @@ static unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; -/* called under bridge lock */ +/* NO locks */ void br_stp_handle_bpdu(struct sk_buff *skb) { unsigned char *buf; struct net_bridge_port *p; + struct net_bridge *br; buf = skb->mac.raw + 14; p = skb->dev->br_port; - if (!p->br->stp_enabled || memcmp(buf, header, 6)) { - kfree_skb(skb); - return; - } + br = p->br; + + spin_lock_bh(&br->lock); + if (p->state == BR_STATE_DISABLED + || !(br->dev.flags & IFF_UP) + || !br->stp_enabled + || memcmp(buf, header, 6)) + goto out; if (buf[6] == BPDU_TYPE_CONFIG) { struct br_config_bpdu bpdu; @@ -178,16 +183,14 @@ bpdu.hello_time = br_get_ticks(buf+34); bpdu.forward_delay = br_get_ticks(buf+36); - kfree_skb(skb); br_received_config_bpdu(p, &bpdu); - return; + goto out; } if (buf[6] == BPDU_TYPE_TCN) { br_received_tcn_bpdu(p); - kfree_skb(skb); - return; + goto out; } - - kfree_skb(skb); + out: + spin_unlock_bh(&br->lock); } diff -urN linux-2.5.68-bk7/net/bridge/br_stp_if.c linux-2.5.68-bk8/net/bridge/br_stp_if.c --- linux-2.5.68-bk7/net/bridge/br_stp_if.c 2003-04-19 19:50:45.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_stp_if.c 2003-04-27 04:36:28.000000000 -0700 @@ -44,6 +44,7 @@ struct net_bridge_port *p; struct timer_list *timer = &br->tick; + spin_lock_bh(&br->lock); init_timer(timer); timer->data = (unsigned long) br; timer->function = br_tick; @@ -53,22 +54,21 @@ br_timer_set(&br->hello_timer, jiffies); br_config_bpdu_generation(br); - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->dev->flags & IFF_UP) br_stp_enable_port(p); - - p = p->next; } br_timer_set(&br->gc_timer, jiffies); + spin_unlock_bh(&br->lock); } -/* called under bridge lock */ +/* NO locks held */ void br_stp_disable_bridge(struct net_bridge *br) { struct net_bridge_port *p; + spin_lock_bh(&br->lock); br->topology_change = 0; br->topology_change_detected = 0; br_timer_clear(&br->hello_timer); @@ -77,13 +77,11 @@ br_timer_clear(&br->gc_timer); br_fdb_cleanup(br); - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED) br_stp_disable_port(p); - - p = p->next; } + spin_unlock_bh(&br->lock); del_timer_sync(&br->tick); } @@ -133,15 +131,13 @@ memcpy(br->bridge_id.addr, addr, ETH_ALEN); memcpy(br->dev.dev_addr, addr, ETH_ALEN); - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) memcpy(p->designated_bridge.addr, addr, ETH_ALEN); if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) memcpy(p->designated_root.addr, addr, ETH_ALEN); - p = p->next; } br_configuration_update(br); @@ -160,13 +156,11 @@ addr = br_mac_zero; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (addr == br_mac_zero || memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) addr = p->dev->dev_addr; - p = p->next; } if (memcmp(br->bridge_id.addr, addr, ETH_ALEN)) @@ -181,15 +175,13 @@ wasroot = br_is_root_bridge(br); - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED && br_is_designated_port(p)) { p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; p->designated_bridge.prio[1] = newprio & 0xFF; } - p = p->next; } br->bridge_id.prio[0] = (newprio >> 8) & 0xFF; diff -urN linux-2.5.68-bk7/net/bridge/br_stp_timer.c linux-2.5.68-bk8/net/bridge/br_stp_timer.c --- linux-2.5.68-bk7/net/bridge/br_stp_timer.c 2003-04-19 19:49:34.000000000 -0700 +++ linux-2.5.68-bk8/net/bridge/br_stp_timer.c 2003-04-27 04:36:28.000000000 -0700 @@ -32,13 +32,10 @@ { struct net_bridge_port *p; - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED && !memcmp(&p->designated_bridge, &br->bridge_id, 8)) return 1; - - p = p->next; } return 0; @@ -162,12 +159,9 @@ br_topology_change_timer_expired(br); } - p = br->port_list; - while (p != NULL) { + list_for_each_entry(p, &br->port_list, list) { if (p->state != BR_STATE_DISABLED) br_check_port_timers(p); - - p = p->next; } } @@ -175,10 +169,10 @@ { struct net_bridge *br = (struct net_bridge *)__data; - read_lock(&br->lock); - br_check_timers(br); - read_unlock(&br->lock); - + if (spin_trylock_bh(&br->lock)) { + br_check_timers(br); + spin_unlock_bh(&br->lock); + } br->tick.expires = jiffies + 1; add_timer(&br->tick); } diff -urN linux-2.5.68-bk7/net/ipv6/addrconf.c linux-2.5.68-bk8/net/ipv6/addrconf.c --- linux-2.5.68-bk7/net/ipv6/addrconf.c 2003-04-19 19:51:08.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/addrconf.c 2003-04-27 04:36:29.000000000 -0700 @@ -300,6 +300,7 @@ printk("Freeing alive inet6 device %p\n", idev); return; } + snmp6_unregister_dev(idev); inet6_dev_count--; kfree(idev); } @@ -332,6 +333,15 @@ /* We refer to the device */ dev_hold(dev); + if (snmp6_register_dev(ndev) < 0) { + ADBG((KERN_WARNING + "%s(): cannot create /proc/net/dev_snmp6/%s\n", + __FUNCTION__, dev->name)); + neigh_parms_release(&nd_tbl, ndev->nd_parms); + in6_dev_finish_destroy(ndev); + return NULL; + } + #ifdef CONFIG_IPV6_PRIVACY get_random_bytes(ndev->rndid, sizeof(ndev->rndid)); get_random_bytes(ndev->entropy, sizeof(ndev->entropy)); diff -urN linux-2.5.68-bk7/net/ipv6/af_inet6.c linux-2.5.68-bk8/net/ipv6/af_inet6.c --- linux-2.5.68-bk7/net/ipv6/af_inet6.c 2003-04-19 19:49:56.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/af_inet6.c 2003-04-27 04:36:29.000000000 -0700 @@ -631,79 +631,72 @@ inet_unregister_protosw(p); } -static int __init init_ipv6_mibs(void) +int +snmp6_mib_init(void *ptr[2], size_t mibsize) { int i; - - ipv6_statistics[0] = kmalloc_percpu(sizeof (struct ipv6_mib), - GFP_KERNEL); - if (!ipv6_statistics[0]) - goto err_ip_mib0; - ipv6_statistics[1] = kmalloc_percpu(sizeof (struct ipv6_mib), - GFP_KERNEL); - if (!ipv6_statistics[1]) - goto err_ip_mib1; - - icmpv6_statistics[0] = kmalloc_percpu(sizeof (struct icmpv6_mib), - GFP_KERNEL); - if (!icmpv6_statistics[0]) - goto err_icmp_mib0; - icmpv6_statistics[1] = kmalloc_percpu(sizeof (struct icmpv6_mib), - GFP_KERNEL); - if (!icmpv6_statistics[1]) - goto err_icmp_mib1; - - udp_stats_in6[0] = kmalloc_percpu(sizeof (struct udp_mib), - GFP_KERNEL); - if (!udp_stats_in6[0]) - goto err_udp_mib0; - udp_stats_in6[1] = kmalloc_percpu(sizeof (struct udp_mib), - GFP_KERNEL); - if (!udp_stats_in6[1]) - goto err_udp_mib1; - /* Zero all percpu versions of the mibs */ + if (ptr == NULL) + return -EINVAL; + + ptr[0] = kmalloc_percpu(mibsize, GFP_KERNEL); + if (!ptr[0]) + goto err0; + + ptr[1] = kmalloc_percpu(mibsize, GFP_KERNEL); + if (!ptr[1]) + goto err1; + + /* Zero percpu version of the mibs */ for (i = 0; i < NR_CPUS; i++) { if (cpu_possible(i)) { - memset(per_cpu_ptr(ipv6_statistics[0], i), 0, - sizeof (struct ipv6_mib)); - memset(per_cpu_ptr(ipv6_statistics[1], i), 0, - sizeof (struct ipv6_mib)); - memset(per_cpu_ptr(icmpv6_statistics[0], i), 0, - sizeof (struct icmpv6_mib)); - memset(per_cpu_ptr(icmpv6_statistics[1], i), 0, - sizeof (struct icmpv6_mib)); - memset(per_cpu_ptr(udp_stats_in6[0], i), 0, - sizeof (struct udp_mib)); - memset(per_cpu_ptr(udp_stats_in6[1], i), 0, - sizeof (struct udp_mib)); + memset(per_cpu_ptr(ptr[0], i), 0, mibsize); + memset(per_cpu_ptr(ptr[1], i), 0, mibsize); } } return 0; -err_udp_mib1: - kfree_percpu(udp_stats_in6[0]); -err_udp_mib0: - kfree_percpu(icmpv6_statistics[1]); -err_icmp_mib1: - kfree_percpu(icmpv6_statistics[0]); -err_icmp_mib0: - kfree_percpu(ipv6_statistics[1]); -err_ip_mib1: - kfree_percpu(ipv6_statistics[0]); -err_ip_mib0: +err1: + kfree_percpu(ptr[0]); + ptr[0] = NULL; +err0: + return -ENOMEM; +} + +void +snmp6_mib_free(void *ptr[2]) +{ + if (ptr == NULL) + return; + kfree_percpu(ptr[0]); + kfree_percpu(ptr[1]); + ptr[0] = ptr[1] = NULL; +} + +static int __init init_ipv6_mibs(void) +{ + if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipv6_mib)) < 0) + goto err_ip_mib; + if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib)) < 0) + goto err_icmp_mib; + if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib)) < 0) + goto err_udp_mib; + return 0; + +err_udp_mib: + snmp6_mib_free((void **)icmpv6_statistics); +err_icmp_mib: + snmp6_mib_free((void **)ipv6_statistics); +err_ip_mib: return -ENOMEM; } static void cleanup_ipv6_mibs(void) { - kfree_percpu(ipv6_statistics[0]); - kfree_percpu(ipv6_statistics[1]); - kfree_percpu(icmpv6_statistics[0]); - kfree_percpu(icmpv6_statistics[1]); - kfree_percpu(udp_stats_in6[0]); - kfree_percpu(udp_stats_in6[1]); + snmp6_mib_free((void **)ipv6_statistics); + snmp6_mib_free((void **)icmpv6_statistics); + snmp6_mib_free((void **)udp_stats_in6); } extern int ipv6_misc_proc_init(void); @@ -819,6 +812,7 @@ #ifdef CONFIG_PROC_FS proc_anycast6_fail: proc_net_remove("snmp6"); + proc_net_remove("dev_snmp6"); proc_net_remove("sockstat6"); proc_misc6_fail: proc_net_remove("udp6"); @@ -854,6 +848,7 @@ proc_net_remove("tcp6"); proc_net_remove("udp6"); proc_net_remove("sockstat6"); + proc_net_remove("dev_snmp6"); proc_net_remove("snmp6"); proc_net_remove("anycast6"); #endif diff -urN linux-2.5.68-bk7/net/ipv6/icmp.c linux-2.5.68-bk8/net/ipv6/icmp.c --- linux-2.5.68-bk7/net/ipv6/icmp.c 2003-04-19 19:48:57.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/icmp.c 2003-04-27 04:36:29.000000000 -0700 @@ -26,6 +26,8 @@ * yoshfuji : ensure to sent parameter problem for * fragments. * YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit. + * Randy Dunlap and + * YOSHIFUJI Hideaki @USAGI: Per-interface statistics support */ #include @@ -247,6 +249,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, struct net_device *dev) { + struct inet6_dev *idev; struct ipv6hdr *hdr = skb->nh.ipv6h; struct sock *sk = icmpv6_socket->sk; struct in6_addr *saddr = NULL; @@ -351,11 +354,16 @@ msg.len = len; + idev = in6_dev_get(skb->dev); + ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, MSG_DONTWAIT); if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) - ICMP6_STATS_PTR_BH(Icmp6OutDestUnreachs) [type-ICMPV6_DEST_UNREACH]++; - ICMP6_INC_STATS_BH(Icmp6OutMsgs); + ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH); + ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs); + + if (likely(idev != NULL)) + in6_dev_put(idev); out: icmpv6_xmit_unlock(); } @@ -363,6 +371,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) { struct sock *sk = icmpv6_socket->sk; + struct inet6_dev *idev; struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; struct in6_addr *saddr; struct icmpv6_msg msg; @@ -394,14 +403,19 @@ fl.fl_icmp_type = ICMPV6_ECHO_REPLY; fl.fl_icmp_code = 0; + idev = in6_dev_get(skb->dev); + icmpv6_xmit_lock(); ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, MSG_DONTWAIT); - ICMP6_INC_STATS_BH(Icmp6OutEchoReplies); - ICMP6_INC_STATS_BH(Icmp6OutMsgs); + ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies); + ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs); icmpv6_xmit_unlock(); + + if (likely(idev != NULL)) + in6_dev_put(idev); } static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info) @@ -464,12 +478,13 @@ { struct sk_buff *skb = *pskb; struct net_device *dev = skb->dev; + struct inet6_dev *idev = __in6_dev_get(dev); struct in6_addr *saddr, *daddr; struct ipv6hdr *orig_hdr; struct icmp6hdr *hdr; int type; - ICMP6_INC_STATS_BH(Icmp6InMsgs); + ICMP6_INC_STATS_BH(idev, Icmp6InMsgs); saddr = &skb->nh.ipv6h->saddr; daddr = &skb->nh.ipv6h->daddr; @@ -517,9 +532,9 @@ type = hdr->icmp6_type; if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) - ICMP6_STATS_PTR_BH(Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++; + ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InDestUnreachs, type - ICMPV6_DEST_UNREACH); else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT) - ICMP6_STATS_PTR_BH(Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++; + ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InEchos, type - ICMPV6_ECHO_REQUEST); switch (type) { case ICMPV6_ECHO_REQUEST: @@ -597,7 +612,7 @@ return 0; discard_it: - ICMP6_INC_STATS_BH(Icmp6InErrors); + ICMP6_INC_STATS_BH(idev, Icmp6InErrors); kfree_skb(skb); return 0; } diff -urN linux-2.5.68-bk7/net/ipv6/ipv6_syms.c linux-2.5.68-bk8/net/ipv6/ipv6_syms.c --- linux-2.5.68-bk7/net/ipv6/ipv6_syms.c 2003-04-19 19:50:30.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/ipv6_syms.c 2003-04-27 04:36:29.000000000 -0700 @@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(in6addr_any); EXPORT_SYMBOL(in6addr_loopback); +EXPORT_SYMBOL(in6_dev_finish_destroy); EXPORT_SYMBOL(xfrm6_rcv); EXPORT_SYMBOL(xfrm6_clear_mutable_options); diff -urN linux-2.5.68-bk7/net/ipv6/mcast.c linux-2.5.68-bk8/net/ipv6/mcast.c --- linux-2.5.68-bk7/net/ipv6/mcast.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/mcast.c 2003-04-27 04:36:29.000000000 -0700 @@ -1253,6 +1253,7 @@ struct ipv6hdr *pip6 = skb->nh.ipv6h; struct mld2_report *pmr = (struct mld2_report *)skb->h.raw; int payload_len, mldlen; + struct inet6_dev *idev = in6_dev_get(skb->dev); payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h - sizeof(struct ipv6hdr); @@ -1262,7 +1263,9 @@ pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); dev_queue_xmit(skb); - ICMP6_INC_STATS(Icmp6OutMsgs); + ICMP6_INC_STATS(idev,Icmp6OutMsgs); + if (likely(idev != NULL)) + in6_dev_put(idev); } static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel) @@ -1520,6 +1523,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) { struct sock *sk = igmp6_socket->sk; + struct inet6_dev *idev; struct sk_buff *skb; struct icmp6hdr *hdr; struct in6_addr *snd_addr; @@ -1577,12 +1581,17 @@ IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); + idev = in6_dev_get(skb->dev); + dev_queue_xmit(skb); if (type == ICMPV6_MGM_REDUCTION) - ICMP6_INC_STATS(Icmp6OutGroupMembReductions); + ICMP6_INC_STATS(idev, Icmp6OutGroupMembReductions); else - ICMP6_INC_STATS(Icmp6OutGroupMembResponses); - ICMP6_INC_STATS(Icmp6OutMsgs); + ICMP6_INC_STATS(idev, Icmp6OutGroupMembResponses); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + + if (likely(idev != NULL)) + in6_dev_put(idev); return; out: diff -urN linux-2.5.68-bk7/net/ipv6/ndisc.c linux-2.5.68-bk8/net/ipv6/ndisc.c --- linux-2.5.68-bk7/net/ipv6/ndisc.c 2003-04-27 04:36:23.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/ndisc.c 2003-04-27 04:36:29.000000000 -0700 @@ -415,6 +415,7 @@ { static struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; + struct inet6_dev *idev; struct flowi fl; struct rt6_info *rt = NULL; struct dst_entry* dst; @@ -497,10 +498,14 @@ dst_clone(dst); skb->dst = dst; + idev = in6_dev_get(dst->dev); dst_output(skb); - ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements); - ICMP6_INC_STATS(Icmp6OutMsgs); + ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + + if (likely(idev != NULL)) + in6_dev_put(idev); } void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, @@ -510,6 +515,7 @@ struct flowi fl; struct rt6_info *rt = NULL; struct dst_entry* dst; + struct inet6_dev *idev; struct sock *sk = ndisc_socket->sk; struct sk_buff *skb; struct nd_msg *msg; @@ -576,10 +582,14 @@ /* send it! */ dst_clone(dst); skb->dst = dst; + idev = in6_dev_get(dst->dev); dst_output(skb); - ICMP6_INC_STATS(Icmp6OutNeighborSolicits); - ICMP6_INC_STATS(Icmp6OutMsgs); + ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + + if (likely(idev != NULL)) + in6_dev_put(idev); } void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, @@ -588,6 +598,7 @@ struct flowi fl; struct rt6_info *rt = NULL; struct dst_entry* dst; + struct inet6_dev *idev; struct sock *sk = ndisc_socket->sk; struct sk_buff *skb; struct icmp6hdr *hdr; @@ -644,10 +655,14 @@ /* send it! */ dst_clone(dst); skb->dst = dst; + idev = in6_dev_get(dst->dev); dst_output(skb); - ICMP6_INC_STATS(Icmp6OutRouterSolicits); - ICMP6_INC_STATS(Icmp6OutMsgs); + ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + + if (likely(idev != NULL)) + in6_dev_put(idev); } @@ -1271,6 +1286,7 @@ struct net_device *dev; struct rt6_info *rt; struct dst_entry *dst; + struct inet6_dev *idev; struct flowi fl; u8 *opt; int rd_len; @@ -1379,10 +1395,14 @@ csum_partial((u8 *) icmph, len, 0)); skb->dst = dst; + idev = in6_dev_get(dst->dev); dst_output(skb); - ICMP6_INC_STATS(Icmp6OutRedirects); - ICMP6_INC_STATS(Icmp6OutMsgs); + ICMP6_INC_STATS(idev, Icmp6OutRedirects); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + + if (likely(idev != NULL)) + in6_dev_put(idev); } static void pndisc_redo(struct sk_buff *skb) diff -urN linux-2.5.68-bk7/net/ipv6/proc.c linux-2.5.68-bk8/net/ipv6/proc.c --- linux-2.5.68-bk7/net/ipv6/proc.c 2003-04-19 19:49:11.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/proc.c 2003-04-27 04:36:29.000000000 -0700 @@ -10,12 +10,14 @@ * Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $ * * Authors: David S. Miller (davem@caip.rutgers.edu) + * YOSHIFUJI Hideaki * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include #include #include #include @@ -28,6 +30,10 @@ #include #include +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_net_devsnmp6; +#endif + static int fold_prot_inuse(struct proto *proto) { int res = 0; @@ -53,14 +59,16 @@ } -static struct snmp6_item +struct snmp6_item { char *name; - void **mib; int offset; -} snmp6_list[] = { +}; +#define SNMP6_SENTINEL { .name = NULL, .offset = 0 } + +static struct snmp6_item snmp6_ipv6_list[] = { /* ipv6 mib according to draft-ietf-ipngwg-ipv6-mib-04 */ -#define SNMP6_GEN(x) { #x , (void **)ipv6_statistics, offsetof(struct ipv6_mib, x) } +#define SNMP6_GEN(x) { .name = #x , .offset = offsetof(struct ipv6_mib, x) } SNMP6_GEN(Ip6InReceives), SNMP6_GEN(Ip6InHdrErrors), SNMP6_GEN(Ip6InTooBigErrors), @@ -84,6 +92,10 @@ SNMP6_GEN(Ip6InMcastPkts), SNMP6_GEN(Ip6OutMcastPkts), #undef SNMP6_GEN + SNMP6_SENTINEL +}; + +static struct snmp6_item snmp6_icmp6_list[] = { /* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02 Exceptions: {In|Out}AdminProhibs are removed, because I see @@ -94,7 +106,7 @@ OutRouterAdvertisements too. OutGroupMembQueries too. */ -#define SNMP6_GEN(x) { #x , (void **)icmpv6_statistics, offsetof(struct icmpv6_mib, x) } +#define SNMP6_GEN(x) { .name = #x , .offset = offsetof(struct icmpv6_mib, x) } SNMP6_GEN(Icmp6InMsgs), SNMP6_GEN(Icmp6InErrors), SNMP6_GEN(Icmp6InDestUnreachs), @@ -124,12 +136,17 @@ SNMP6_GEN(Icmp6OutGroupMembResponses), SNMP6_GEN(Icmp6OutGroupMembReductions), #undef SNMP6_GEN -#define SNMP6_GEN(x) { "Udp6" #x , (void **)udp_stats_in6, offsetof(struct udp_mib, Udp##x) } + SNMP6_SENTINEL +}; + +static struct snmp6_item snmp6_udp6_list[] = { +#define SNMP6_GEN(x) { .name = "Udp6" #x , .offset = offsetof(struct udp_mib, Udp##x) } SNMP6_GEN(InDatagrams), SNMP6_GEN(NoPorts), SNMP6_GEN(InErrors), - SNMP6_GEN(OutDatagrams) + SNMP6_GEN(OutDatagrams), #undef SNMP6_GEN + SNMP6_SENTINEL }; static unsigned long @@ -151,18 +168,30 @@ return res; } -static int snmp6_seq_show(struct seq_file *seq, void *v) +static inline void +snmp6_seq_show_item(struct seq_file *seq, void **mib, struct snmp6_item *itemlist) { int i; + for (i=0; itemlist[i].name; i++) + seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, + fold_field(mib, itemlist[i].offset)); +} - for (i=0; iprivate; + if (idev) { + seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); + snmp6_seq_show_item(seq, (void **)idev->stats.icmpv6, snmp6_icmp6_list); + } else { + snmp6_seq_show_item(seq, (void **)ipv6_statistics, snmp6_ipv6_list); + snmp6_seq_show_item(seq, (void **)icmpv6_statistics, snmp6_icmp6_list); + snmp6_seq_show_item(seq, (void **)udp_stats_in6, snmp6_udp6_list); + } return 0; } - static int sockstat6_seq_open(struct inode *inode, struct file *file) { return single_open(file, sockstat6_seq_show, NULL); @@ -177,7 +206,7 @@ static int snmp6_seq_open(struct inode *inode, struct file *file) { - return single_open(file, snmp6_seq_show, NULL); + return single_open(file, snmp6_seq_show, PDE(inode)->data); } static struct file_operations snmp6_seq_fops = { @@ -187,6 +216,57 @@ .release = single_release, }; +int snmp6_register_dev(struct inet6_dev *idev) +{ + int err = -ENOMEM; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *p; +#endif + + if (!idev || !idev->dev) + return -EINVAL; + + if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib)) < 0) + goto err_icmp; + +#ifdef CONFIG_PROC_FS + if (!proc_net_devsnmp6) { + err = -ENOENT; + goto err_proc; + } + p = create_proc_entry(idev->dev->name, S_IRUGO, proc_net_devsnmp6); + if (!p) + goto err_proc; + p->data = idev; + p->proc_fops = &snmp6_seq_fops; + + idev->stats.proc_dir_entry = p; +#endif + return 0; + +#ifdef CONFIG_PROC_FS +err_proc: + snmp6_mib_free((void **)idev->stats.icmpv6); +#endif +err_icmp: + return err; +} + +int snmp6_unregister_dev(struct inet6_dev *idev) +{ +#ifdef CONFIG_PROC_FS + if (!proc_net_devsnmp6) + return -ENOENT; + if (!idev || !idev->stats.proc_dir_entry) + return -EINVAL; + remove_proc_entry(idev->stats.proc_dir_entry->name, + proc_net_devsnmp6); +#endif + snmp6_mib_free((void **)idev->stats.icmpv6); + + return 0; +} + int __init ipv6_misc_proc_init(void) { int rc = 0; @@ -197,6 +277,9 @@ goto proc_snmp6_fail; else p->proc_fops = &snmp6_seq_fops; + proc_net_devsnmp6 = proc_mkdir("dev_snmp6", proc_net); + if (!proc_net_devsnmp6) + goto proc_dev_snmp6_fail; p = create_proc_entry("sockstat6", S_IRUGO, proc_net); if (!p) goto proc_sockstat6_fail; @@ -206,6 +289,8 @@ return rc; proc_sockstat6_fail: + remove_proc_entry("dev_snmp6", proc_net); +proc_dev_snmp6_fail: remove_proc_entry("snmp6", proc_net); proc_snmp6_fail: rc = -ENOMEM; diff -urN linux-2.5.68-bk7/net/ipv6/tcp_ipv6.c linux-2.5.68-bk8/net/ipv6/tcp_ipv6.c --- linux-2.5.68-bk7/net/ipv6/tcp_ipv6.c 2003-04-19 19:49:56.000000000 -0700 +++ linux-2.5.68-bk8/net/ipv6/tcp_ipv6.c 2003-04-27 04:36:29.000000000 -0700 @@ -751,7 +751,7 @@ sk = tcp_v6_lookup(&hdr->daddr, th->dest, &hdr->saddr, th->source, skb->dev->ifindex); if (sk == NULL) { - ICMP6_INC_STATS_BH(Icmp6InErrors); + ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), Icmp6InErrors); return; } diff -urN linux-2.5.68-bk7/net/sctp/ipv6.c linux-2.5.68-bk8/net/sctp/ipv6.c --- linux-2.5.68-bk7/net/sctp/ipv6.c 2003-04-19 19:50:01.000000000 -0700 +++ linux-2.5.68-bk8/net/sctp/ipv6.c 2003-04-27 04:36:29.000000000 -0700 @@ -92,6 +92,7 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, int type, int code, int offset, __u32 info) { + struct inet6_dev *idev; struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); struct sock *sk; @@ -102,6 +103,8 @@ char *saveip, *savesctp; int err; + idev = in6_dev_get(skb->dev); + /* Fix up skb to look at the embedded net header. */ saveip = skb->nh.raw; savesctp = skb->h.raw; @@ -112,8 +115,8 @@ skb->nh.raw = saveip; skb->h.raw = savesctp; if (!sk) { - ICMP6_INC_STATS_BH(Icmp6InErrors); - return; + ICMP6_INC_STATS_BH(idev, Icmp6InErrors); + goto out; } /* Warning: The sock lock is held. Remember to call @@ -139,6 +142,9 @@ out_unlock: sctp_err_finish(sk, ep, asoc); +out: + if (likely(idev != NULL)) + in6_dev_put(idev); } /* Based on tcp_v6_xmit() in tcp_ipv6.c. */