diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /drivers/scsi/BusLogic.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'drivers/scsi/BusLogic.c')
-rw-r--r-- | drivers/scsi/BusLogic.c | 3468 |
1 files changed, 3468 insertions, 0 deletions
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c new file mode 100644 index 000000000..57b9b2a02 --- /dev/null +++ b/drivers/scsi/BusLogic.c @@ -0,0 +1,3468 @@ +/* + + Linux Driver for BusLogic MultiMaster SCSI Host Adapters + + Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com> + + This program is free software; you may redistribute and/or modify it under + the terms of the GNU General Public License Version 2 as published by the + Free Software Foundation, provided that none of the source code or runtime + copyright notices are removed or modified. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for complete details. + + The author respectfully requests that any modifications to this software be + sent directly to him for evaluation and testing. + + Special thanks to Wayne Yen and Alex Win of BusLogic, whose advice has been + invaluable, to David Gentzel, for writing the original Linux BusLogic driver, + and to Paul Gortmaker, for being such a dedicated test site. + +*/ + + +#define BusLogic_DriverVersion "2.0.6" +#define BusLogic_DriverDate "1 December 1996" + + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/kernel_stat.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/system.h> +#include "scsi.h" +#include "hosts.h" +#include "sd.h" +#include "BusLogic.h" + + +/* + BusLogic_CommandLineEntryCount is a count of the number of "BusLogic=" + entries provided on the Linux Kernel Command Line. +*/ + +static int + BusLogic_CommandLineEntryCount = 0; + + +/* + BusLogic_CommandLineEntries is an array of Command Line Entry structures + representing the "BusLogic=" entries provided on the Linux Kernel Command + Line. +*/ + +static BusLogic_CommandLineEntry_T + BusLogic_CommandLineEntries[BusLogic_MaxHostAdapters]; + + +/* + BusLogic_ProbeOptions is a bit mask of Probe Options to be applied + across all Host Adapters. +*/ + +static int + BusLogic_ProbeOptions = 0; + + +/* + BusLogic_GlobalOptions is a bit mask of Global Options to be applied + across all Host Adapters. +*/ + +static int + BusLogic_GlobalOptions = 0; + + +/* + BusLogic_RegisteredHostAdapters is a linked list of all the registered + BusLogic Host Adapters. +*/ + +static BusLogic_HostAdapter_T + *BusLogic_RegisteredHostAdapters = NULL; + + +/* + BusLogic_StandardAddresses is the list of standard ISA I/O Addresses at + which BusLogic Host Adapters may potentially be found. +*/ + +static unsigned int + BusLogic_IO_StandardAddresses[] = + { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 }; + + +/* + BusLogic_IO_AddressProbeList is the list of I/O Addresses to be probed for + potential BusLogic Host Adapters. It is initialized by interrogating the + PCI Configuration Space on PCI machines as well as from the list of + standard BusLogic I/O Addresses. +*/ + +static unsigned int + BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] = { 0 }; + + +/* + BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using + a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared + interrupts. +*/ + +static int + BusLogic_IRQ_UsageCount[NR_IRQS] = { 0 }; + + +/* + BusLogic_CommandFailureReason holds a string identifying the reason why a + call to BusLogic_Command failed. It is only non-NULL when BusLogic_Command + returns a failure code. +*/ + +static char + *BusLogic_CommandFailureReason; + + +/* + BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry. +*/ + +static struct proc_dir_entry + BusLogic_ProcDirectoryEntry = + { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + + +/* + BusLogic_AnnounceDriver announces the Driver Version and Date, Author's + Name, Copyright Notice, and Contact Address. +*/ + +static void BusLogic_AnnounceDriver(void) +{ + static boolean DriverAnnouncementPrinted = false; + if (DriverAnnouncementPrinted) return; + printk("scsi: ***** BusLogic SCSI Driver Version " + BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n"); + printk("scsi: Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>\n"); + DriverAnnouncementPrinted = true; +} + + +/* + BusLogic_DriverInfo returns the Controller Name to identify this SCSI Driver + and Host Adapter. +*/ + +const char *BusLogic_DriverInfo(SCSI_Host_T *Host) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Host->hostdata; + return HostAdapter->ControllerName; +} + + +/* + BusLogic_RegisterHostAdapter adds Host Adapter to the list of registered + BusLogic Host Adapters. +*/ + +static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + HostAdapter->Next = NULL; + if (BusLogic_RegisteredHostAdapters != NULL) + { + BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters; + BusLogic_HostAdapter_T *NextHostAdapter; + while ((NextHostAdapter = LastHostAdapter->Next) != NULL) + LastHostAdapter = NextHostAdapter; + LastHostAdapter->Next = HostAdapter; + } + else BusLogic_RegisteredHostAdapters = HostAdapter; +} + + +/* + BusLogic_UnregisterHostAdapter removes Host Adapter from the list of + registered BusLogic Host Adapters. +*/ + +static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + if (BusLogic_RegisteredHostAdapters != HostAdapter) + { + BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters; + while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter) + LastHostAdapter = LastHostAdapter->Next; + if (LastHostAdapter != NULL) + LastHostAdapter->Next = HostAdapter->Next; + } + else BusLogic_RegisteredHostAdapters = HostAdapter->Next; + HostAdapter->Next = NULL; +} + + +/* + BusLogic_CreateMailboxes allocates the Outgoing and Incoming Mailboxes for + Host Adapter. +*/ + +static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter) +{ + HostAdapter->FirstOutgoingMailbox = + (BusLogic_OutgoingMailbox_T *) + scsi_init_malloc(HostAdapter->MailboxCount + * (sizeof(BusLogic_OutgoingMailbox_T) + + sizeof(BusLogic_IncomingMailbox_T)), + (HostAdapter->BounceBuffersRequired + ? GFP_ATOMIC | GFP_DMA + : GFP_ATOMIC)); + if (HostAdapter->FirstOutgoingMailbox == NULL) + { + printk("scsi%d: UNABLE TO ALLOCATE MAILBOXES - DETACHING\n", + HostAdapter->HostNumber); + return false; + } + HostAdapter->LastOutgoingMailbox = + HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1; + HostAdapter->FirstIncomingMailbox = + (BusLogic_IncomingMailbox_T *) (HostAdapter->LastOutgoingMailbox + 1); + HostAdapter->LastIncomingMailbox = + HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1; + return true; +} + + +/* + BusLogic_DestroyMailboxes deallocates the Outgoing and Incoming Mailboxes + for Host Adapter. +*/ + +static void BusLogic_DestroyMailboxes(BusLogic_HostAdapter_T *HostAdapter) +{ + if (HostAdapter->FirstOutgoingMailbox == NULL) return; + scsi_init_free((char *) HostAdapter->FirstOutgoingMailbox, + HostAdapter->MailboxCount + * (sizeof(BusLogic_OutgoingMailbox_T) + + sizeof(BusLogic_IncomingMailbox_T))); +} + + +/* + BusLogic_CreateCCBs allocates the initial Command Control Blocks (CCBs) + for Host Adapter. +*/ + +static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter) +{ + int i; + for (i = 0; i < HostAdapter->InitialCCBs; i++) + { + BusLogic_CCB_T *CCB = (BusLogic_CCB_T *) + scsi_init_malloc(sizeof(BusLogic_CCB_T), + (HostAdapter->BounceBuffersRequired + ? GFP_ATOMIC | GFP_DMA + : GFP_ATOMIC)); + if (CCB == NULL) + { + printk("scsi%d: UNABLE TO ALLOCATE CCB %d - DETACHING\n", + HostAdapter->HostNumber, i); + return false; + } + memset(CCB, 0, sizeof(BusLogic_CCB_T)); + CCB->HostAdapter = HostAdapter; + CCB->Status = BusLogic_CCB_Free; + CCB->Next = HostAdapter->Free_CCBs; + CCB->NextAll = HostAdapter->All_CCBs; + HostAdapter->Free_CCBs = CCB; + HostAdapter->All_CCBs = CCB; + } + return true; +} + + +/* + BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter. +*/ + +static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter) +{ + BusLogic_CCB_T *NextCCB = HostAdapter->All_CCBs, *CCB; + HostAdapter->All_CCBs = NULL; + HostAdapter->Free_CCBs = NULL; + while ((CCB = NextCCB) != NULL) + { + NextCCB = CCB->NextAll; + scsi_init_free((char *) CCB, sizeof(BusLogic_CCB_T)); + } +} + + +/* + BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list, + allocating more memory from the Kernel if necessary. The Host Adapter's + Lock should have already been acquired by the caller. +*/ + +static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter) +{ + static unsigned long SerialNumber = 0; + BusLogic_CCB_T *CCB; + int Allocated; + CCB = HostAdapter->Free_CCBs; + if (CCB != NULL) + { + CCB->SerialNumber = ++SerialNumber; + HostAdapter->Free_CCBs = CCB->Next; + CCB->Next = NULL; + return CCB; + } + for (Allocated = 0; Allocated < HostAdapter->IncrementalCCBs; Allocated++) + { + CCB = (BusLogic_CCB_T *) + scsi_init_malloc(sizeof(BusLogic_CCB_T), + (HostAdapter->BounceBuffersRequired + ? GFP_ATOMIC | GFP_DMA + : GFP_ATOMIC)); + if (CCB == NULL) break; + memset(CCB, 0, sizeof(BusLogic_CCB_T)); + CCB->HostAdapter = HostAdapter; + CCB->Status = BusLogic_CCB_Free; + CCB->Next = HostAdapter->Free_CCBs; + CCB->NextAll = HostAdapter->All_CCBs; + HostAdapter->Free_CCBs = CCB; + HostAdapter->All_CCBs = CCB; + } + CCB = HostAdapter->Free_CCBs; + if (CCB == NULL) + { + printk("scsi%d: Failed to allocate additional CCBs\n", + HostAdapter->HostNumber); + return NULL; + } + printk("scsi%d: Allocated %d additional CCBs\n", + HostAdapter->HostNumber, Allocated); + CCB->SerialNumber = ++SerialNumber; + HostAdapter->Free_CCBs = CCB->Next; + CCB->Next = NULL; + return CCB; +} + + +/* + BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's + free list. The Host Adapter's Lock should have already been acquired by the + caller. +*/ + +static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB) +{ + BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter; + CCB->Command = NULL; + CCB->Status = BusLogic_CCB_Free; + CCB->Next = HostAdapter->Free_CCBs; + HostAdapter->Free_CCBs = CCB; +} + + +/* + BusLogic_Command sends the command OperationCode to HostAdapter, optionally + providing ParameterLength bytes of ParameterData and receiving at most + ReplyLength bytes of ReplyData; any excess reply data is received but + discarded. + + On success, this function returns the number of reply bytes read from + the Host Adapter (including any discarded data); on failure, it returns + -1 if the command was invalid, or -2 if a timeout occurred. + + This function is only called during controller detection and initialization, + so performance and latency are not critical, and exclusive access to the Host + Adapter hardware is assumed. Once the controller and driver are initialized, + the only Host Adapter command that is issued is the single byte Execute + Mailbox Command operation code, which does not require waiting for the Host + Adapter Ready bit to be set in the Status Register. +*/ + +static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter, + BusLogic_OperationCode_T OperationCode, + void *ParameterData, + int ParameterLength, + void *ReplyData, + int ReplyLength) +{ + unsigned char *ParameterPointer = (unsigned char *) ParameterData; + unsigned char *ReplyPointer = (unsigned char *) ReplyData; + unsigned char StatusRegister = 0, InterruptRegister; + int ReplyBytes = 0, TimeoutCounter; + /* + Clear out the Reply Data if provided. + */ + if (ReplyLength > 0) + memset(ReplyData, 0, ReplyLength); + /* + Wait for the Host Adapter Ready bit to be set and the Command/Parameter + Register Busy bit to be reset in the Status Register. + */ + TimeoutCounter = loops_per_sec >> 3; + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if ((StatusRegister & BusLogic_HostAdapterReady) && + !(StatusRegister & BusLogic_CommandParameterRegisterBusy)) + break; + } + BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready"; + if (TimeoutCounter < 0) return -2; + /* + Write the OperationCode to the Command/Parameter Register. + */ + HostAdapter->HostAdapterCommandCompleted = false; + BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode); + /* + Write any additional Parameter Bytes. + */ + TimeoutCounter = 10000; + while (ParameterLength > 0 && --TimeoutCounter >= 0) + { + /* + Wait 100 microseconds to give the Host Adapter enough time to determine + whether the last value written to the Command/Parameter Register was + valid or not. If the Command Complete bit is set in the Interrupt + Register, then the Command Invalid bit in the Status Register will be + reset if the Operation Code or Parameter was valid and the command + has completed, or set if the Operation Code or Parameter was invalid. + If the Data In Register Ready bit is set in the Status Register, then + the Operation Code was valid, and data is waiting to be read back + from the Host Adapter. Otherwise, wait for the Command/Parameter + Register Busy bit in the Status Register to be reset. + */ + udelay(100); + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (InterruptRegister & BusLogic_CommandComplete) break; + if (HostAdapter->HostAdapterCommandCompleted) break; + if (StatusRegister & BusLogic_DataInRegisterReady) break; + if (StatusRegister & BusLogic_CommandParameterRegisterBusy) continue; + BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++); + ParameterLength--; + } + BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance"; + if (TimeoutCounter < 0) return -2; + /* + The Modify I/O Address command does not cause a Command Complete Interrupt. + */ + if (OperationCode == BusLogic_ModifyIOAddress) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + BusLogic_CommandFailureReason = "Modify I/O Address Invalid"; + if (StatusRegister & BusLogic_CommandInvalid) return -1; + BusLogic_CommandFailureReason = NULL; + return 0; + } + /* + Select an appropriate timeout value for awaiting command completion. + */ + switch (OperationCode) + { + case BusLogic_InquireInstalledDevicesID0to7: + case BusLogic_InquireInstalledDevicesID8to15: + case BusLogic_InquireTargetDevices: + /* Approximately 60 seconds. */ + TimeoutCounter = loops_per_sec << 2; + break; + default: + /* Approximately 1 second. */ + TimeoutCounter = loops_per_sec >> 4; + break; + } + /* + Receive any Reply Bytes, waiting for either the Command Complete bit to + be set in the Interrupt Register, or for the Interrupt Handler to set the + Host Adapter Command Completed bit in the Host Adapter structure. + */ + while (--TimeoutCounter >= 0) + { + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (InterruptRegister & BusLogic_CommandComplete) break; + if (HostAdapter->HostAdapterCommandCompleted) break; + if (StatusRegister & BusLogic_DataInRegisterReady) + if (++ReplyBytes <= ReplyLength) + *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter); + else BusLogic_ReadDataInRegister(HostAdapter); + if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && + (StatusRegister & BusLogic_HostAdapterReady)) break; + } + BusLogic_CommandFailureReason = "Timeout waiting for Command Complete"; + if (TimeoutCounter < 0) return -2; + /* + If testing Command Complete Interrupts, wait a short while in case the + loop immediately above terminated due to the Command Complete bit being + set in the Interrupt Register, but the interrupt hasn't actually been + processed yet. Otherwise, acknowledging the interrupt here could prevent + the interrupt test from succeeding. + */ + if (OperationCode == BusLogic_TestCommandCompleteInterrupt) + udelay(10000); + /* + Clear any pending Command Complete Interrupt. + */ + BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset); + if (BusLogic_GlobalOptions & BusLogic_TraceConfiguration) + if (OperationCode != BusLogic_TestCommandCompleteInterrupt) + { + int i; + printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", + OperationCode, StatusRegister, ReplyLength, ReplyBytes); + if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes; + for (i = 0; i < ReplyLength; i++) + printk(" %02X", ((unsigned char *) ReplyData)[i]); + printk("\n"); + } + /* + Process Command Invalid conditions. + */ + if (StatusRegister & BusLogic_CommandInvalid) + { + /* + Some early BusLogic Host Adapters may not recover properly from + a Command Invalid condition, so if this appears to be the case, + a Soft Reset is issued to the Host Adapter. Potentially invalid + commands are never attempted after Mailbox Initialization is + performed, so there should be no Host Adapter state lost by a + Soft Reset in response to a Command Invalid condition. + */ + udelay(1000); + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (StatusRegister != (BusLogic_HostAdapterReady | + BusLogic_InitializationRequired)) + { + BusLogic_WriteControlRegister(HostAdapter, BusLogic_SoftReset); + udelay(1000); + } + BusLogic_CommandFailureReason = "Command Invalid"; + return -1; + } + /* + Handle Excess Parameters Supplied conditions. + */ + BusLogic_CommandFailureReason = "Excess Parameters Supplied"; + if (ParameterLength > 0) return -1; + /* + Indicate the command completed successfully. + */ + BusLogic_CommandFailureReason = NULL; + return ReplyBytes; +} + + +/* + BusLogic_InitializeAddressProbeList initializes the list of I/O Addresses + to be probed for potential BusLogic SCSI Host Adapters by interrogating the + PCI Configuration Space on PCI machines as well as from the list of standard + BusLogic I/O Addresses. +*/ + +static void BusLogic_InitializeAddressProbeList(void) +{ + int ProbeAddressCount = 0, StandardAddressIndex = 0; + /* + If BusLogic_Setup has provided an I/O Address probe list, do not override + the Kernel Command Line specifications. + */ + if (BusLogic_IO_AddressProbeList[0] != 0) return; +#ifdef CONFIG_PCI + /* + Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters. + */ + if (pcibios_present()) + { + unsigned int BusDeviceFunction[BusLogic_IO_MaxProbeAddresses]; + unsigned short Index = 0, VendorID, DeviceID; + boolean NonIncreasingScanningOrder = false; + unsigned char Bus, DeviceFunction; + unsigned int BaseAddress0; + while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++, + &Bus, &DeviceFunction) == 0) + if (pcibios_read_config_word(Bus, DeviceFunction, + PCI_VENDOR_ID, &VendorID) == 0 && + VendorID == PCI_VENDOR_ID_BUSLOGIC && + pcibios_read_config_word(Bus, DeviceFunction, + PCI_DEVICE_ID, &DeviceID) == 0 && + (DeviceID == PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER || + DeviceID == PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC) && + pcibios_read_config_dword(Bus, DeviceFunction, + PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 && + (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) == + PCI_BASE_ADDRESS_SPACE_IO) + { + BusLogic_IO_AddressProbeList[ProbeAddressCount] = + BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; + BusDeviceFunction[ProbeAddressCount] = (Bus << 8) | DeviceFunction; + if (ProbeAddressCount > 0 && + BusDeviceFunction[ProbeAddressCount] < + BusDeviceFunction[ProbeAddressCount-1]) + NonIncreasingScanningOrder = true; + ProbeAddressCount++; + } + /* + If there are multiple BusLogic PCI SCSI Host Adapters present and if + they are enumerated by the PCI BIOS in an order other than by strictly + increasing Bus Number and Device Number, then interrogate the setting + of the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option. + If it is ON, and if the first enumeratedBusLogic Host Adapter is a + BT-948/958/958D, then sort the PCI Host Adapter I/O Addresses by + increasing Bus Number and Device Number so that the Host Adapters are + recognized in the same order by the Linux kernel as by the Host + Adapter's BIOS. + */ + if (ProbeAddressCount > 1 && NonIncreasingScanningOrder && + !(BusLogic_ProbeOptions & BusLogic_NoSortPCI)) + { + BusLogic_HostAdapter_T HostAdapterPrototype; + BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype; + BusLogic_FetchHostAdapterLocalRAMRequest_T + FetchHostAdapterLocalRAMRequest; + BusLogic_AutoSCSIByte45_T AutoSCSIByte45; + BusLogic_BoardID_T BoardID; + HostAdapter->IO_Address = BusLogic_IO_AddressProbeList[0]; + FetchHostAdapterLocalRAMRequest.ByteOffset = + BusLogic_AutoSCSI_BaseOffset + 45; + FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte45); + AutoSCSIByte45.ForceBusDeviceScanningOrder = false; + BusLogic_Command(HostAdapter, + BusLogic_FetchHostAdapterLocalRAM, + &FetchHostAdapterLocalRAMRequest, + sizeof(FetchHostAdapterLocalRAMRequest), + &AutoSCSIByte45, sizeof(AutoSCSIByte45)); + BoardID.FirmwareVersion1stDigit = '\0'; + BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, + NULL, 0, &BoardID, sizeof(BoardID)); + if (BoardID.FirmwareVersion1stDigit == '5' && + AutoSCSIByte45.ForceBusDeviceScanningOrder) + { + /* + Sort the I/O Addresses such that the corresponding + PCI devices are in ascending order by Bus Number and + Device Number. + */ + int LastInterchange = ProbeAddressCount-1, Bound, j; + while (LastInterchange > 0) + { + Bound = LastInterchange; + LastInterchange = 0; + for (j = 0; j < Bound; j++) + if (BusDeviceFunction[j] > BusDeviceFunction[j+1]) + { + unsigned int Temp; + Temp = BusDeviceFunction[j]; + BusDeviceFunction[j] = BusDeviceFunction[j+1]; + BusDeviceFunction[j+1] = Temp; + Temp = BusLogic_IO_AddressProbeList[j]; + BusLogic_IO_AddressProbeList[j] = + BusLogic_IO_AddressProbeList[j+1]; + BusLogic_IO_AddressProbeList[j+1] = Temp; + LastInterchange = j; + } + } + } + } + } +#endif + /* + Append the list of standard BusLogic ISA I/O Addresses. + */ + if (!(BusLogic_ProbeOptions & BusLogic_NoProbeISA)) + while (ProbeAddressCount < BusLogic_IO_MaxProbeAddresses && + BusLogic_IO_StandardAddresses[StandardAddressIndex] > 0) + BusLogic_IO_AddressProbeList[ProbeAddressCount++] = + BusLogic_IO_StandardAddresses[StandardAddressIndex++]; + BusLogic_IO_AddressProbeList[ProbeAddressCount] = 0; +} + + +/* + BusLogic_Failure prints a standardized error message, and then returns false. +*/ + +static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter, + char *ErrorMessage) +{ + BusLogic_AnnounceDriver(); + printk("While configuring BusLogic Host Adapter at I/O Address 0x%X:\n", + HostAdapter->IO_Address); + printk("%s FAILED - DETACHING\n", ErrorMessage); + if (BusLogic_CommandFailureReason != NULL) + printk("ADDITIONAL FAILURE INFO - %s\n", BusLogic_CommandFailureReason); + return false; +} + + +/* + BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter. +*/ + +static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + boolean TraceProbe = (BusLogic_GlobalOptions & BusLogic_TraceProbe); + unsigned char StatusRegister, GeometryRegister; + /* + Read the Status Register to test if there is an I/O port that responds. A + nonexistent I/O port will return 0xFF, in which case there is definitely no + BusLogic Host Adapter at this base I/O Address. + */ + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (TraceProbe) + printk("BusLogic_Probe(0x%X): Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (StatusRegister == 0xFF) return false; + /* + Read the undocumented BusLogic Geometry Register to test if there is an I/O + port that responds. Adaptec Host Adapters do not implement the Geometry + Register, so this test helps serve to avoid incorrectly recognizing an + Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C + series does respond to the Geometry Register I/O port, but it will be + rejected later when the Inquire Extended Setup Information command is + issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a + BusLogic clone that implements the same interface as earlier BusLogic + controllers, including the undocumented commands, and is therefore + supported by this driver. However, the AMI FastDisk always returns 0x00 + upon reading the Geometry Register, so the extended translation option + should always be left disabled on the AMI FastDisk. + */ + GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter); + if (TraceProbe) + printk("BusLogic_Probe(0x%X): Geometry 0x%02X\n", + HostAdapter->IO_Address, GeometryRegister); + if (GeometryRegister == 0xFF) return false; + /* + Indicate the Host Adapter Probe completed successfully. + */ + return true; +} + + +/* + BusLogic_HardResetHostAdapter issues a Hard Reset to the Host Adapter, + and waits for Host Adapter Diagnostics to complete. +*/ + +static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T + *HostAdapter) +{ + boolean TraceHardReset = (BusLogic_GlobalOptions & BusLogic_TraceHardReset); + int TimeoutCounter = loops_per_sec; + unsigned char StatusRegister = 0; + /* + Issue a Hard Reset Command to the Host Adapter. The Host Adapter should + respond by setting Diagnostic Active in the Status Register. + */ + BusLogic_WriteControlRegister(HostAdapter, BusLogic_HardReset); + /* + Wait until Diagnostic Active is set in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if ((StatusRegister & BusLogic_DiagnosticActive)) break; + } + if (TraceHardReset) + printk("BusLogic_HardReset(0x%X): Diagnostic Active, Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (TimeoutCounter < 0) return false; + /* + Wait 100 microseconds to allow completion of any initial diagnostic + activity which might leave the contents of the Status Register + unpredictable. + */ + udelay(100); + /* + Wait until Diagnostic Active is reset in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (!(StatusRegister & BusLogic_DiagnosticActive)) break; + } + if (TraceHardReset) + printk("BusLogic_HardReset(0x%X): Diagnostic Completed, Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (TimeoutCounter < 0) return false; + /* + Wait until at least one of the Diagnostic Failure, Host Adapter Ready, + or Data In Register Ready bits is set in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (StatusRegister & (BusLogic_DiagnosticFailure | + BusLogic_HostAdapterReady | + BusLogic_DataInRegisterReady)) + break; + } + if (TraceHardReset) + printk("BusLogic_HardReset(0x%X): Host Adapter Ready, Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (TimeoutCounter < 0) return false; + /* + If Diagnostic Failure is set or Host Adapter Ready is reset, then an + error occurred during the Host Adapter diagnostics. If Data In Register + Ready is set, then there is an Error Code available. + */ + if ((StatusRegister & BusLogic_DiagnosticFailure) || + !(StatusRegister & BusLogic_HostAdapterReady)) + { + BusLogic_CommandFailureReason = NULL; + BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS"); + printk("HOST ADAPTER STATUS REGISTER = %02X\n", StatusRegister); + if (StatusRegister & BusLogic_DataInRegisterReady) + { + unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter); + printk("HOST ADAPTER ERROR CODE = %d\n", ErrorCode); + } + return false; + } + /* + Indicate the Host Adapter Hard Reset completed successfully. + */ + return true; +} + + +/* + BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic + Host Adapter. +*/ + +static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation; + BusLogic_RequestedReplyLength_T RequestedReplyLength; + unsigned long ProcessorFlags; + int Result; + /* + Issue the Inquire Extended Setup Information command. Only genuine + BusLogic Host Adapters and true clones support this command. Adaptec 1542C + series Host Adapters that respond to the Geometry Register I/O port will + fail this command. Interrupts must be disabled around the call to + BusLogic_Command since a Command Complete interrupt could occur if the IRQ + Channel was previously enabled for another BusLogic Host Adapter sharing + the same IRQ Channel. + */ + save_flags(ProcessorFlags); + cli(); + RequestedReplyLength = sizeof(ExtendedSetupInformation); + Result = BusLogic_Command(HostAdapter, + BusLogic_InquireExtendedSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &ExtendedSetupInformation, + sizeof(ExtendedSetupInformation)); + restore_flags(ProcessorFlags); + if (BusLogic_GlobalOptions & BusLogic_TraceProbe) + printk("BusLogic_Check(0x%X): Result %d\n", + HostAdapter->IO_Address, Result); + return (Result == sizeof(ExtendedSetupInformation)); +} + + +/* + BusLogic_ReadHostAdapterConfiguration reads the Configuration Information + from Host Adapter. +*/ + +static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T + *HostAdapter) +{ + BusLogic_BoardID_T BoardID; + BusLogic_Configuration_T Configuration; + BusLogic_SetupInformation_T SetupInformation; + BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation; + BusLogic_ControllerModelNumber_T ControllerModelNumber; + BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit; + BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter; + BusLogic_GenericIOPortInformation_T GenericIOPortInformation; + BusLogic_FetchHostAdapterLocalRAMRequest_T FetchHostAdapterLocalRAMRequest; + BusLogic_AutoSCSIByte15_T AutoSCSIByte15; + BusLogic_RequestedReplyLength_T RequestedReplyLength; + unsigned char GeometryRegister, *TargetPointer, Character; + unsigned short AllTargetsMask, DisconnectPermitted; + unsigned short TaggedQueuingPermitted, TaggedQueuingPermittedDefault; + boolean CommonErrorRecovery; + int TargetID, i; + /* + Issue the Inquire Board ID command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, + &BoardID, sizeof(BoardID)) != sizeof(BoardID)) + return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID"); + /* + Issue the Inquire Configuration command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0, + &Configuration, sizeof(Configuration)) + != sizeof(Configuration)) + return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION"); + /* + Issue the Inquire Setup Information command. + */ + RequestedReplyLength = sizeof(SetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &SetupInformation, sizeof(SetupInformation)) + != sizeof(SetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + /* + Issue the Inquire Extended Setup Information command. + */ + RequestedReplyLength = sizeof(ExtendedSetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &ExtendedSetupInformation, + sizeof(ExtendedSetupInformation)) + != sizeof(ExtendedSetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION"); + /* + Issue the Inquire Controller Model Number command. + */ + if (ExtendedSetupInformation.BusType == 'A' && + BoardID.FirmwareVersion1stDigit == '2') + /* BusLogic BT-542B ISA 2.xx */ + strcpy(ControllerModelNumber, "542B"); + else if (ExtendedSetupInformation.BusType == 'E' && + BoardID.FirmwareVersion1stDigit == '0') + /* AMI FastDisk EISA Series 441 0.x */ + strcpy(ControllerModelNumber, "747A"); + else + { + RequestedReplyLength = sizeof(ControllerModelNumber); + if (BusLogic_Command(HostAdapter, BusLogic_InquireControllerModelNumber, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &ControllerModelNumber, + sizeof(ControllerModelNumber)) + != sizeof(ControllerModelNumber)) + return BusLogic_Failure(HostAdapter, "INQUIRE CONTROLLER MODEL NUMBER"); + } + /* + Issue the Inquire Firmware Version 3rd Digit command. + */ + FirmwareVersion3rdDigit = '\0'; + if (BoardID.FirmwareVersion1stDigit > '0') + if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit, + NULL, 0, &FirmwareVersion3rdDigit, + sizeof(FirmwareVersion3rdDigit)) + != sizeof(FirmwareVersion3rdDigit)) + return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT"); + /* + BusLogic Host Adapters can be identified by their model number and + the major version number of their firmware as follows: + + 5.xx BusLogic "W" Series Host Adapters: + BT-948/958/958D + 4.xx BusLogic "C" Series Host Adapters: + BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF + 3.xx BusLogic "S" Series Host Adapters: + BT-747S/747D/757S/757D/445S/545S/542D + BT-542B/742A (revision H) + 2.xx BusLogic "A" Series Host Adapters: + BT-542B/742A (revision G and below) + 0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter + */ + /* + Save the Model Name and Controller Name in the Host Adapter structure. + */ + TargetPointer = HostAdapter->ModelName; + *TargetPointer++ = 'B'; + *TargetPointer++ = 'T'; + *TargetPointer++ = '-'; + for (i = 0; i < sizeof(ControllerModelNumber); i++) + { + Character = ControllerModelNumber[i]; + if (Character == ' ' || Character == '\0') break; + *TargetPointer++ = Character; + } + *TargetPointer++ = '\0'; + strcpy(HostAdapter->ControllerName, "BusLogic "); + strcat(HostAdapter->ControllerName, HostAdapter->ModelName); + strcpy(HostAdapter->InterruptLabel, HostAdapter->ControllerName); + /* + Save the Firmware Version in the Host Adapter structure. + */ + TargetPointer = HostAdapter->FirmwareVersion; + *TargetPointer++ = BoardID.FirmwareVersion1stDigit; + *TargetPointer++ = '.'; + *TargetPointer++ = BoardID.FirmwareVersion2ndDigit; + if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0') + *TargetPointer++ = FirmwareVersion3rdDigit; + *TargetPointer = '\0'; + /* + Issue the Inquire Firmware Version Letter command. + */ + if (strcmp(HostAdapter->FirmwareVersion, "3.3") >= 0) + { + if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter, + NULL, 0, &FirmwareVersionLetter, + sizeof(FirmwareVersionLetter)) + != sizeof(FirmwareVersionLetter)) + return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER"); + if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0') + *TargetPointer++ = FirmwareVersionLetter; + *TargetPointer = '\0'; + } + /* + Issue the Inquire Generic I/O Port Information command to read the + IRQ Channel from all PCI Host Adapters, and the Termination Information + from "W" Series Host Adapters. + */ + if (HostAdapter->ModelName[3] == '9' && + strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0) + { + if (BusLogic_Command(HostAdapter, + BusLogic_InquireGenericIOPortInformation, + NULL, 0, &GenericIOPortInformation, + sizeof(GenericIOPortInformation)) + != sizeof(GenericIOPortInformation)) + return BusLogic_Failure(HostAdapter, + "INQUIRE GENERIC I/O PORT INFORMATION"); + /* + Save the IRQ Channel in the Host Adapter structure. + */ + HostAdapter->IRQ_Channel = GenericIOPortInformation.PCIAssignedIRQChannel; + /* + Save the Termination Information in the Host Adapter structure. + */ + if (HostAdapter->FirmwareVersion[0] == '5' && + GenericIOPortInformation.Valid) + { + HostAdapter->TerminationInfoValid = true; + HostAdapter->LowByteTerminated = + GenericIOPortInformation.LowByteTerminated; + HostAdapter->HighByteTerminated = + GenericIOPortInformation.HighByteTerminated; + } + } + /* + Issue the Fetch Host Adapter Local RAM command to read the Termination + Information from the AutoSCSI area of "C" Series Host Adapters. + */ + if (HostAdapter->FirmwareVersion[0] == '4') + { + FetchHostAdapterLocalRAMRequest.ByteOffset = + BusLogic_AutoSCSI_BaseOffset + 15; + FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte15); + if (BusLogic_Command(HostAdapter, + BusLogic_FetchHostAdapterLocalRAM, + &FetchHostAdapterLocalRAMRequest, + sizeof(FetchHostAdapterLocalRAMRequest), + &AutoSCSIByte15, sizeof(AutoSCSIByte15)) + != sizeof(AutoSCSIByte15)) + return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM"); + /* + Save the Termination Information in the Host Adapter structure. + */ + HostAdapter->TerminationInfoValid = true; + HostAdapter->LowByteTerminated = AutoSCSIByte15.LowByteTerminated; + HostAdapter->HighByteTerminated = AutoSCSIByte15.HighByteTerminated; + } + /* + Determine the IRQ Channel and save it in the Host Adapter structure. + */ + if (HostAdapter->IRQ_Channel == 0) + { + if (Configuration.IRQ_Channel9) + HostAdapter->IRQ_Channel = 9; + else if (Configuration.IRQ_Channel10) + HostAdapter->IRQ_Channel = 10; + else if (Configuration.IRQ_Channel11) + HostAdapter->IRQ_Channel = 11; + else if (Configuration.IRQ_Channel12) + HostAdapter->IRQ_Channel = 12; + else if (Configuration.IRQ_Channel14) + HostAdapter->IRQ_Channel = 14; + else if (Configuration.IRQ_Channel15) + HostAdapter->IRQ_Channel = 15; + } + /* + Save the Host Adapter SCSI ID in the Host Adapter structure. + */ + HostAdapter->SCSI_ID = Configuration.HostAdapterID; + /* + Save the Synchronous Initiation flag and SCSI Parity Checking flag + in the Host Adapter structure. + */ + HostAdapter->SynchronousInitiation = + SetupInformation.SynchronousInitiationEnabled; + HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled; + /* + Determine the Bus Type and save it in the Host Adapter structure, + and determine and save the DMA Channel for ISA Host Adapters. + */ + switch (HostAdapter->ModelName[3]) + { + case '4': + HostAdapter->BusType = BusLogic_VESA_Bus; + break; + case '5': + HostAdapter->BusType = BusLogic_ISA_Bus; + if (Configuration.DMA_Channel5) + HostAdapter->DMA_Channel = 5; + else if (Configuration.DMA_Channel6) + HostAdapter->DMA_Channel = 6; + else if (Configuration.DMA_Channel7) + HostAdapter->DMA_Channel = 7; + break; + case '6': + HostAdapter->BusType = BusLogic_MCA_Bus; + break; + case '7': + HostAdapter->BusType = BusLogic_EISA_Bus; + break; + case '9': + HostAdapter->BusType = BusLogic_PCI_Bus; + break; + } + /* + Determine whether Extended Translation is enabled and save it in + the Host Adapter structure. + */ + GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter); + if (GeometryRegister & BusLogic_ExtendedTranslationEnabled) + HostAdapter->ExtendedTranslation = true; + /* + Save the Disconnect/Reconnect Permitted flag bits in the Host Adapter + structure. The Disconnect Permitted information is only valid on "W" and + "C" Series controllers, but Disconnect/Reconnect is always permitted on "S" + and "A" Series controllers. + */ + if (HostAdapter->FirmwareVersion[0] >= '4') + HostAdapter->DisconnectPermitted = + (SetupInformation.DisconnectPermittedID8to15 << 8) + | SetupInformation.DisconnectPermittedID0to7; + else HostAdapter->DisconnectPermitted = 0xFF; + /* + Save the Scatter Gather Limits, Level Sensitive Interrupts flag, Wide + SCSI flag, Differential SCSI flag, Automatic Configuration flag, and + Ultra SCSI flag in the Host Adapter structure. + */ + HostAdapter->HostAdapterScatterGatherLimit = + ExtendedSetupInformation.ScatterGatherLimit; + HostAdapter->DriverScatterGatherLimit = + HostAdapter->HostAdapterScatterGatherLimit; + if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit) + HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit; + if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupts) + HostAdapter->LevelSensitiveInterrupts = true; + HostAdapter->HostWideSCSI = ExtendedSetupInformation.HostWideSCSI; + HostAdapter->HostDifferentialSCSI = + ExtendedSetupInformation.HostDifferentialSCSI; + HostAdapter->HostAutomaticConfiguration = + ExtendedSetupInformation.HostAutomaticConfiguration; + HostAdapter->HostUltraSCSI = ExtendedSetupInformation.HostUltraSCSI; + /* + Determine whether 64 LUN Format CCBs are supported and save the information + in the Host Adapter structure. + */ + if (HostAdapter->FirmwareVersion[0] == '5' || + (HostAdapter->FirmwareVersion[0] == '4' && HostAdapter->HostWideSCSI)) + HostAdapter->Host64LUNSupport = true; + /* + Determine the Host Adapter BIOS Address if the BIOS is enabled and + save it in the Host Adapter structure. The BIOS is disabled if the + BIOS_Address is 0. + */ + HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12; + /* + ISA Host Adapters require Bounce Buffers if there is more than 16MB memory. + */ + if (HostAdapter->BusType == BusLogic_ISA_Bus && + high_memory > (void *) MAX_DMA_ADDRESS) + HostAdapter->BounceBuffersRequired = true; + /* + BusLogic BT-445S Host Adapters prior to controller revision E have a + hardware bug whereby when the BIOS is enabled, transfers to/from the same + address range the BIOS occupies modulo 16MB are handled incorrectly. Only + properly functioning BT-445S controllers have firmware version 3.37, so we + require that ISA Bounce Buffers be used for the buggy BT-445S models if + there is more than 16MB memory. + */ + if (HostAdapter->BIOS_Address > 0 && + strcmp(HostAdapter->ModelName, "BT-445S") == 0 && + strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 && + high_memory > (void *) MAX_DMA_ADDRESS) + HostAdapter->BounceBuffersRequired = true; + /* + Determine the maximum number of Target IDs and Logical Units supported by + this driver for Wide and Narrow Host Adapters. + */ + HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8); + HostAdapter->MaxLogicalUnits = (HostAdapter->Host64LUNSupport ? 64 : 8); + /* + Select appropriate values for the Mailbox Count, Initial CCBs, and + Incremental CCBs variables based on whether or not Strict Round Robin Mode + is supported. If Strict Round Robin Mode is supported, then there is no + performance degradation in using the maximum possible number of Outgoing + and Incoming Mailboxes and allowing the Tagged and Untagged Queue Depths to + determine the actual utilization. If Strict Round Robin Mode is not + supported, then the Host Adapter must scan all the Outgoing Mailboxes + whenever an Outgoing Mailbox entry is made, which can cause a substantial + performance penalty. The Host Adapters actually have room to store the + following number of CCBs internally; that is, they can internally queue and + manage this many active commands on the SCSI bus simultaneously. + Performance measurements demonstrate that the Mailbox Count should be set + to the maximum possible, rather than the internal CCB capacity, as it is + more efficient to have the queued commands waiting in Outgoing Mailboxes if + necessary than to block the process in the higher levels of the SCSI + Subsystem. + + 192 BT-948/958/958D + 100 BT-946C/956C/956CD/747C/757C/757CD/445C + 50 BT-545C/540CF + 30 BT-747S/747D/757S/757D/445S/545S/542D/542B/742A + */ + if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0) + { + HostAdapter->StrictRoundRobinModeSupport = true; + HostAdapter->MailboxCount = 255; + HostAdapter->InitialCCBs = 64; + HostAdapter->IncrementalCCBs = 32; + } + else + { + HostAdapter->StrictRoundRobinModeSupport = false; + HostAdapter->MailboxCount = 32; + HostAdapter->InitialCCBs = 32; + HostAdapter->IncrementalCCBs = 4; + } + if (HostAdapter->FirmwareVersion[0] == '5') + HostAdapter->TotalQueueDepth = 192; + else if (HostAdapter->FirmwareVersion[0] == '4') + HostAdapter->TotalQueueDepth = + (HostAdapter->BusType != BusLogic_ISA_Bus ? 100 : 50); + else HostAdapter->TotalQueueDepth = 30; + /* + Select an appropriate value for the Tagged Queue Depth either from a + Command Line Entry, or based on whether this Host Adapter requires that + ISA Bounce Buffers be used. The Tagged Queue Depth is left at 0 for + automatic determination in BusLogic_SelectQueueDepths. Initialize the + Untagged Queue Depth. + */ + if (HostAdapter->CommandLineEntry != NULL && + HostAdapter->CommandLineEntry->TaggedQueueDepth > 0) + HostAdapter->TaggedQueueDepth = + HostAdapter->CommandLineEntry->TaggedQueueDepth; + else if (HostAdapter->BounceBuffersRequired) + HostAdapter->TaggedQueueDepth = BusLogic_TaggedQueueDepth_BB; + else HostAdapter->TaggedQueueDepth = 0; + HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth; + if (HostAdapter->UntaggedQueueDepth > HostAdapter->TaggedQueueDepth && + HostAdapter->TaggedQueueDepth > 0) + HostAdapter->UntaggedQueueDepth = HostAdapter->TaggedQueueDepth; + /* + Select an appropriate value for Bus Settle Time either from a Command + Line Entry, or from BusLogic_DefaultBusSettleTime. + */ + if (HostAdapter->CommandLineEntry != NULL && + HostAdapter->CommandLineEntry->BusSettleTime > 0) + HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime; + else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime; + /* + Select an appropriate value for Local Options from a Command Line Entry. + */ + if (HostAdapter->CommandLineEntry != NULL) + HostAdapter->LocalOptions = HostAdapter->CommandLineEntry->LocalOptions; + /* + Select appropriate values for the Error Recovery Strategy array either from + a Command Line Entry, or using BusLogic_ErrorRecovery_Default. + */ + if (HostAdapter->CommandLineEntry != NULL) + memcpy(HostAdapter->ErrorRecoveryStrategy, + HostAdapter->CommandLineEntry->ErrorRecoveryStrategy, + sizeof(HostAdapter->ErrorRecoveryStrategy)); + else memset(HostAdapter->ErrorRecoveryStrategy, + BusLogic_ErrorRecovery_Default, + sizeof(HostAdapter->ErrorRecoveryStrategy)); + /* + Tagged Queuing support is available and operates properly on all "W" Series + controllers, on "C" Series controllers with firmware version 4.22 and + above, and on "S" Series controllers with firmware version 3.35 and above. + Tagged Queuing is disabled by default when the Tagged Queue Depth is 1 + since queuing multiple commands is not possible. + */ + TaggedQueuingPermittedDefault = 0; + if (HostAdapter->TaggedQueueDepth != 1) + switch (HostAdapter->FirmwareVersion[0]) + { + case '5': + TaggedQueuingPermittedDefault = 0xFFFF; + break; + case '4': + if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0) + TaggedQueuingPermittedDefault = 0xFFFF; + break; + case '3': + if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0) + TaggedQueuingPermittedDefault = 0xFFFF; + break; + } + /* + Tagged Queuing is only useful if Disconnect/Reconnect is permitted. + Therefore, mask the Tagged Queuing Permitted Default bits with the + Disconnect/Reconnect Permitted bits. + */ + TaggedQueuingPermittedDefault &= HostAdapter->DisconnectPermitted; + /* + Combine the default Tagged Queuing Permitted bits with any Command + Line Entry Tagged Queuing specification. + */ + if (HostAdapter->CommandLineEntry != NULL) + HostAdapter->TaggedQueuingPermitted = + (HostAdapter->CommandLineEntry->TaggedQueuingPermitted & + HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) | + (TaggedQueuingPermittedDefault & + ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask); + else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault; + /* + Announce the Host Adapter Configuration. + */ + printk("scsi%d: Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n", + HostAdapter->HostNumber, HostAdapter->ModelName, + BusLogic_BusNames[HostAdapter->BusType], + (HostAdapter->HostWideSCSI ? " Wide" : ""), + (HostAdapter->HostDifferentialSCSI ? " Differential" : ""), + (HostAdapter->HostUltraSCSI ? " Ultra" : "")); + printk("scsi%d: Firmware Version: %s, I/O Address: 0x%X, " + "IRQ Channel: %d/%s\n", + HostAdapter->HostNumber, HostAdapter->FirmwareVersion, + HostAdapter->IO_Address, HostAdapter->IRQ_Channel, + (HostAdapter->LevelSensitiveInterrupts ? "Level" : "Edge")); + printk("scsi%d: DMA Channel: ", HostAdapter->HostNumber); + if (HostAdapter->DMA_Channel > 0) + printk("%d, ", HostAdapter->DMA_Channel); + else printk("None, "); + if (HostAdapter->BIOS_Address > 0) + printk("BIOS Address: 0x%X, ", HostAdapter->BIOS_Address); + else printk("BIOS Address: None, "); + printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID); + printk("scsi%d: Scatter/Gather Limit: %d of %d segments, " + "Parity Checking: %s\n", HostAdapter->HostNumber, + HostAdapter->DriverScatterGatherLimit, + HostAdapter->HostAdapterScatterGatherLimit, + (HostAdapter->ParityChecking ? "Enabled" : "Disabled")); + printk("scsi%d: Synchronous Initiation: %s, " + "Extended Disk Translation: %s\n", HostAdapter->HostNumber, + (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled"), + (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled")); + AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1; + DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask; + printk("scsi%d: Disconnect/Reconnect: ", HostAdapter->HostNumber); + if (DisconnectPermitted == 0) + printk("Disabled"); + else if (DisconnectPermitted == AllTargetsMask) + printk("Enabled"); + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + printk("%c", (DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N'); + printk(", Tagged Queuing: "); + TaggedQueuingPermitted = + HostAdapter->TaggedQueuingPermitted & AllTargetsMask; + if (TaggedQueuingPermitted == 0) + printk("Disabled"); + else if (TaggedQueuingPermitted == AllTargetsMask) + printk("Enabled"); + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N'); + printk("\n"); + printk("scsi%d: Total Queue Depth: %d, Mailboxes: %d, Initial CCBs: %d\n", + HostAdapter->HostNumber, HostAdapter->TotalQueueDepth, + HostAdapter->MailboxCount, HostAdapter->InitialCCBs); + printk("scsi%d: Tagged Queue Depth: ", HostAdapter->HostNumber); + if (HostAdapter->TaggedQueueDepth > 0) + printk("%d", HostAdapter->TaggedQueueDepth); + else printk("Automatic"); + printk(", Untagged Queue Depth: %d\n", HostAdapter->UntaggedQueueDepth); + if (HostAdapter->TerminationInfoValid) + if (HostAdapter->HostWideSCSI) + printk("scsi%d: Host Adapter SCSI Bus Termination (Low/High): %s/%s\n", + HostAdapter->HostNumber, + (HostAdapter->LowByteTerminated ? "Enabled" : "Disabled"), + (HostAdapter->HighByteTerminated ? "Enabled" : "Disabled")); + else printk("scsi%d: Host Adapter SCSI Bus Termination: %s\n", + HostAdapter->HostNumber, + (HostAdapter->LowByteTerminated ? "Enabled" : "Disabled")); + CommonErrorRecovery = true; + for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->ErrorRecoveryStrategy[TargetID] != + HostAdapter->ErrorRecoveryStrategy[0]) + { + CommonErrorRecovery = false; + break; + } + printk("scsi%d: Error Recovery Strategy: ", HostAdapter->HostNumber); + if (CommonErrorRecovery) + printk("%s", BusLogic_ErrorRecoveryStrategyNames[ + HostAdapter->ErrorRecoveryStrategy[0]]); + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + printk("%s", BusLogic_ErrorRecoveryStrategyLetters[ + HostAdapter->ErrorRecoveryStrategy[TargetID]]); + printk("\n"); + /* + Indicate reading the Host Adapter Configuration completed successfully. + */ + return true; +} + + +/* + BusLogic_AcquireResources acquires the system resources necessary to use + Host Adapter. +*/ + +static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter) +{ + if (HostAdapter->IRQ_Channel == 0) + { + printk("scsi%d: NO INTERRUPT CHANNEL ASSIGNED - DETACHING\n", + HostAdapter->HostNumber); + return false; + } + /* + Acquire exclusive or shared access to the IRQ Channel. A usage count is + maintained so that PCI, EISA, or MCA shared interrupts can be supported. + */ + if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel]++ == 0) + { + if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, + SA_INTERRUPT | SA_SHIRQ, + HostAdapter->InterruptLabel, NULL) < 0) + { + BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel]--; + printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", + HostAdapter->HostNumber, HostAdapter->IRQ_Channel); + return false; + } + } + else + { + BusLogic_HostAdapter_T *FirstHostAdapter = + BusLogic_RegisteredHostAdapters; + while (FirstHostAdapter != NULL) + { + if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel) + { + if (strlen(FirstHostAdapter->InterruptLabel) + 11 + < sizeof(FirstHostAdapter->InterruptLabel)) + { + strcat(FirstHostAdapter->InterruptLabel, " + "); + strcat(FirstHostAdapter->InterruptLabel, + HostAdapter->ModelName); + } + break; + } + FirstHostAdapter = FirstHostAdapter->Next; + } + } + HostAdapter->IRQ_ChannelAcquired = true; + /* + Acquire exclusive access to the DMA Channel. + */ + if (HostAdapter->DMA_Channel > 0) + { + if (request_dma(HostAdapter->DMA_Channel, + HostAdapter->ControllerName) < 0) + { + printk("scsi%d: UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n", + HostAdapter->HostNumber, HostAdapter->DMA_Channel); + return false; + } + set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE); + enable_dma(HostAdapter->DMA_Channel); + HostAdapter->DMA_ChannelAcquired = true; + } + /* + Indicate the System Resource Acquisition completed successfully, + */ + return true; +} + + +/* + BusLogic_ReleaseResources releases any system resources previously acquired + by BusLogic_AcquireResources. +*/ + +static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter) +{ + /* + Release exclusive or shared access to the IRQ Channel. + */ + if (HostAdapter->IRQ_ChannelAcquired) + if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel] == 0) + free_irq(HostAdapter->IRQ_Channel, NULL); + /* + Release exclusive access to the DMA Channel. + */ + if (HostAdapter->DMA_ChannelAcquired) + free_dma(HostAdapter->DMA_Channel); +} + + +/* + BusLogic_TestInterrupts tests for proper functioning of the Host Adapter + Interrupt Register and that interrupts generated by the Host Adapter are + getting through to the Interrupt Handler. A large proportion of initial + problems with installing PCI Host Adapters are due to configuration problems + where either the Host Adapter or Motherboard is configured incorrectly, and + interrupts do not get through as a result. +*/ + +static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter) +{ + unsigned int InitialInterruptCount, FinalInterruptCount; + int TestCount = 5, i; + InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel]; + /* + Issue the Test Command Complete Interrupt commands. + */ + for (i = 0; i < TestCount; i++) + BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt, + NULL, 0, NULL, 0); + /* + Verify that BusLogic_InterruptHandler was called at least TestCount times. + Shared IRQ Channels could cause more than TestCount interrupts to occur, + but there should never be fewer than TestCount. + */ + FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel]; + if (FinalInterruptCount < InitialInterruptCount + TestCount) + { + BusLogic_Failure(HostAdapter, "HOST ADAPTER INTERRUPT TEST"); + printk("\n\ +Interrupts are not getting through from the Host Adapter to the BusLogic\n\ +Driver Interrupt Handler. The most likely cause is that either the Host\n\ +Adapter or Motherboard is configured incorrectly. Please check the Host\n\ +Adapter configuration with AutoSCSI or by examining any dip switch and\n\ +jumper settings on the Host Adapter, and verify that no other device is\n\ +attempting to use the same IRQ Channel. For PCI Host Adapters, it may also\n\ +be necessary to investigate and manually set the PCI interrupt assignments\n\ +and edge/level interrupt type selection in the BIOS Setup Program or with\n\ +Motherboard jumpers.\n\n"); + return false; + } + /* + Indicate the Host Adapter Interrupt Test completed successfully. + */ + return true; +} + + +/* + BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only + function called during SCSI Host Adapter detection which modifies the state + of the Host Adapter from its initial power on or hard reset state. +*/ + +static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T + *HostAdapter) +{ + BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest; + BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest; + BusLogic_SetCCBFormatRequest_T SetCCBFormatRequest; + BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest; + int TargetID; + /* + Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active, + Command Successful Flag, Active Command Count, and Total Command Count + for each Target Device. + */ + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL; + memset(HostAdapter->TaggedQueuingActive, false, + sizeof(HostAdapter->TaggedQueuingActive)); + memset(HostAdapter->CommandSuccessfulFlag, false, + sizeof(HostAdapter->CommandSuccessfulFlag)); + memset(HostAdapter->ActiveCommandCount, 0, + sizeof(HostAdapter->ActiveCommandCount)); + memset(HostAdapter->TotalCommandCount, 0, + sizeof(HostAdapter->TotalCommandCount)); + /* + Initialize the Outgoing and Incoming Mailbox structures. + */ + memset(HostAdapter->FirstOutgoingMailbox, 0, + HostAdapter->MailboxCount * sizeof(BusLogic_OutgoingMailbox_T)); + memset(HostAdapter->FirstIncomingMailbox, 0, + HostAdapter->MailboxCount * sizeof(BusLogic_IncomingMailbox_T)); + /* + Initialize the pointers to the Next Mailboxes. + */ + HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + /* + Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes. + */ + ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount; + ExtendedMailboxRequest.BaseMailboxAddress = + Virtual_to_Bus(HostAdapter->FirstOutgoingMailbox); + if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox, + &ExtendedMailboxRequest, + sizeof(ExtendedMailboxRequest), NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION"); + /* + Enable Strict Round Robin Mode if supported by the Host Adapter. In + Strict Round Robin Mode, the Host Adapter only looks at the next Outgoing + Mailbox for each new command, rather than scanning through all the + Outgoing Mailboxes to find any that have new commands in them. Strict + Round Robin Mode is significantly more efficient. + */ + if (HostAdapter->StrictRoundRobinModeSupport) + { + RoundRobinModeRequest = BusLogic_StrictRoundRobinMode; + if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode, + &RoundRobinModeRequest, + sizeof(RoundRobinModeRequest), NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE"); + } + /* + For Host Adapters that support 64 LUN Format CCBs, issue the Set CCB Format + command to allow 64 Logical Units per Target Device. + */ + if (HostAdapter->Host64LUNSupport) + { + SetCCBFormatRequest = BusLogic_64LUNFormatCCB; + if (BusLogic_Command(HostAdapter, BusLogic_SetCCBFormat, + &SetCCBFormatRequest, sizeof(SetCCBFormatRequest), + NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "SET CCB FORMAT"); + } + /* + For PCI Host Adapters being accessed through the PCI compliant I/O + Address, disable the ISA compatible I/O Address to avoid detecting the + same Host Adapter at both I/O Addresses. + */ + if (HostAdapter->BusType == BusLogic_PCI_Bus) + { + int Index; + for (Index = 0; BusLogic_IO_StandardAddresses[Index] > 0; Index++) + if (HostAdapter->IO_Address == BusLogic_IO_StandardAddresses[Index]) + break; + if (BusLogic_IO_StandardAddresses[Index] == 0) + { + ModifyIOAddressRequest = BusLogic_ModifyIO_Disable; + if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress, + &ModifyIOAddressRequest, + sizeof(ModifyIOAddressRequest), NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "MODIFY I/O ADDRESS"); + } + } + /* + Announce Successful Initialization. + */ + printk("scsi%d: *** %s Initialized Successfully ***\n", + HostAdapter->HostNumber, HostAdapter->ControllerName); + /* + Indicate the Host Adapter Initialization completed successfully. + */ + return true; +} + + +/* + BusLogic_TargetDeviceInquiry inquires about the Target Devices accessible + through Host Adapter and reports on the results. +*/ + +static boolean BusLogic_TargetDeviceInquiry(BusLogic_HostAdapter_T + *HostAdapter) +{ + BusLogic_InstalledDevices_T InstalledDevices; + BusLogic_InstalledDevices8_T InstalledDevicesID0to7; + BusLogic_SetupInformation_T SetupInformation; + BusLogic_SynchronousPeriod_T SynchronousPeriod; + BusLogic_RequestedReplyLength_T RequestedReplyLength; + int TargetDevicesFound = 0, TargetID; + /* + Wait a few seconds between the Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get + confused if they receive SCSI Commands too soon after a SCSI Bus Reset. + */ + BusLogic_Delay(HostAdapter->BusSettleTime); + /* + Inhibit the Target Devices Inquiry if requested. + */ + if (HostAdapter->LocalOptions & BusLogic_InhibitTargetInquiry) + { + printk("scsi%d: Target Device Inquiry Inhibited\n", + HostAdapter->HostNumber); + return true; + } + /* + Issue the Inquire Target Devices command for controllers with firmware + version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command + for older controllers. This is necessary to force Synchronous Transfer + Negotiation so that the Inquire Setup Information and Inquire Synchronous + Period commands will return valid data. The Inquire Target Devices command + is preferable to Inquire Installed Devices ID 0 to 7 since it only probes + Logical Unit 0 of each Target Device. + */ + if (strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0) + { + if (BusLogic_Command(HostAdapter, BusLogic_InquireTargetDevices, NULL, 0, + &InstalledDevices, sizeof(InstalledDevices)) + != sizeof(InstalledDevices)) + return BusLogic_Failure(HostAdapter, "INQUIRE TARGET DEVICES"); + } + else + { + if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7, + NULL, 0, &InstalledDevicesID0to7, + sizeof(InstalledDevicesID0to7)) + != sizeof(InstalledDevicesID0to7)) + return BusLogic_Failure(HostAdapter, + "INQUIRE INSTALLED DEVICES ID 0 TO 7"); + InstalledDevices = 0; + for (TargetID = 0; TargetID < 8; TargetID++) + if (InstalledDevicesID0to7[TargetID] != 0) + InstalledDevices |= (1 << TargetID); + } + /* + Issue the Inquire Setup Information command. + */ + RequestedReplyLength = sizeof(SetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &SetupInformation, sizeof(SetupInformation)) + != sizeof(SetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + /* + Issue the Inquire Synchronous Period command. + */ + if (HostAdapter->FirmwareVersion[0] >= '3') + { + RequestedReplyLength = sizeof(SynchronousPeriod); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &SynchronousPeriod, sizeof(SynchronousPeriod)) + != sizeof(SynchronousPeriod)) + return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD"); + } + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0) + SynchronousPeriod[TargetID] = + 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID] + .TransferPeriod; + else SynchronousPeriod[TargetID] = 0; + /* + Save the Installed Devices, Synchronous Values, and Synchronous Period + information in the Host Adapter structure. + */ + HostAdapter->InstalledDevices = InstalledDevices; + memcpy(HostAdapter->SynchronousValues, + SetupInformation.SynchronousValuesID0to7, + sizeof(BusLogic_SynchronousValues8_T)); + if (HostAdapter->HostWideSCSI) + memcpy(&HostAdapter->SynchronousValues[8], + SetupInformation.SynchronousValuesID8to15, + sizeof(BusLogic_SynchronousValues8_T)); + memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod, + sizeof(BusLogic_SynchronousPeriod_T)); + /* + Report on the Target Devices found. + */ + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->InstalledDevices & (1 << TargetID)) + { + int SynchronousPeriod = HostAdapter->SynchronousPeriod[TargetID]; + if (SynchronousPeriod > 10) + { + int SynchronousTransferRate = 100000000 / SynchronousPeriod; + int RoundedSynchronousTransferRate = + (SynchronousTransferRate + 5000) / 10000; + printk("scsi%d: Target %d: Synchronous at " + "%d.%02d mega-transfers/second, offset %d\n", + HostAdapter->HostNumber, TargetID, + RoundedSynchronousTransferRate / 100, + RoundedSynchronousTransferRate % 100, + HostAdapter->SynchronousValues[TargetID].Offset); + } + else if (SynchronousPeriod > 0) + { + int SynchronousTransferRate = 100000000 / SynchronousPeriod; + int RoundedSynchronousTransferRate = + (SynchronousTransferRate + 50000) / 100000; + printk("scsi%d: Target %d: Synchronous at " + "%d.%01d mega-transfers/second, offset %d\n", + HostAdapter->HostNumber, TargetID, + RoundedSynchronousTransferRate / 10, + RoundedSynchronousTransferRate % 10, + HostAdapter->SynchronousValues[TargetID].Offset); + } + else printk("scsi%d: Target %d: Asynchronous\n", + HostAdapter->HostNumber, TargetID); + TargetDevicesFound++; + } + if (TargetDevicesFound == 0) + printk("scsi%d: No Target Devices Found\n", HostAdapter->HostNumber); + /* + Indicate the Target Device Inquiry completed successfully. + */ + return true; +} + + +/* + BusLogic_InitializeHostStructure initializes the fields in the SCSI Host + structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the + SCSI Host structure are intentionally left uninitialized, as this driver + handles acquisition and release of these resources explicitly, as well as + ensuring exclusive access to the Host Adapter hardware and data structures + through explicit acquisition and release of the Host Adapter's Lock. +*/ + +static void BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T + *HostAdapter, + SCSI_Host_T *Host) +{ + Host->max_id = HostAdapter->MaxTargetDevices; + Host->max_lun = HostAdapter->MaxLogicalUnits; + Host->max_channel = 0; + Host->unique_id = HostAdapter->IO_Address; + Host->this_id = HostAdapter->SCSI_ID; + Host->can_queue = HostAdapter->MailboxCount; + Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit; + Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired; + Host->cmd_per_lun = HostAdapter->UntaggedQueueDepth; +} + + +/* + BusLogic_SelectQueueDepths selects Queue Depths for each Target Device + based on the Host Adapter's Total Queue Depth and the number, type, speed, + and capabilities of the Target Devices. +*/ + +static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host, + SCSI_Device_T *DeviceList) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Host->hostdata; + int TaggedQueueDepth = HostAdapter->TaggedQueueDepth; + int UntaggedQueueDepth = HostAdapter->UntaggedQueueDepth; + int TaggedDeviceCount = 0, UntaggedDeviceCount = 0; + SCSI_Device_T *Device; + for (Device = DeviceList; Device != NULL; Device = Device->next) + if (Device->host == Host) + { + if (Device->tagged_supported && + (HostAdapter->TaggedQueuingPermitted & (1 << Device->id))) + TaggedDeviceCount++; + else UntaggedDeviceCount++; + } + if (TaggedQueueDepth == 0 && TaggedDeviceCount > 0) + { + TaggedQueueDepth = + 1 + ((HostAdapter->TotalQueueDepth + - UntaggedDeviceCount * UntaggedQueueDepth) + / TaggedDeviceCount); + if (TaggedQueueDepth > BusLogic_PreferredTaggedQueueDepth) + TaggedQueueDepth = BusLogic_PreferredTaggedQueueDepth; + } + for (Device = DeviceList; Device != NULL; Device = Device->next) + if (Device->host == Host) + { + if (Device->tagged_supported && + (HostAdapter->TaggedQueuingPermitted & (1 << Device->id))) + Device->queue_depth = TaggedQueueDepth; + else Device->queue_depth = UntaggedQueueDepth; + if (BusLogic_GlobalOptions & BusLogic_TraceQueueDepths) + printk("scsi%d: Setting Queue Depth for Target %d to %d\n", + HostAdapter->HostNumber, Device->id, Device->queue_depth); + } +} + + +/* + BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard + I/O Addresses where they may be located, initializing, registering, and + reporting the configuration of each BusLogic Host Adapter it finds. It + returns the number of BusLogic Host Adapters successfully initialized and + registered. +*/ + +int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate) +{ + int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0; + int AddressProbeIndex = 0; + if (BusLogic_ProbeOptions & BusLogic_NoProbe) return 0; + BusLogic_InitializeAddressProbeList(); + while (BusLogic_IO_AddressProbeList[AddressProbeIndex] > 0) + { + BusLogic_HostAdapter_T HostAdapterPrototype; + BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype; + SCSI_Host_T *Host; + memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T)); + HostAdapter->IO_Address = + BusLogic_IO_AddressProbeList[AddressProbeIndex++]; + /* + Initialize the Command Line Entry field if an explicit I/O Address + was specified. + */ + if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount && + BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == + HostAdapter->IO_Address) + HostAdapter->CommandLineEntry = + &BusLogic_CommandLineEntries[CommandLineEntryIndex++]; + /* + Check whether the I/O Address range is already in use. + */ + if (check_region(HostAdapter->IO_Address, BusLogic_IO_PortCount) < 0) + continue; + /* + Probe the Host Adapter. If unsuccessful, abort further initialization. + */ + if (!BusLogic_ProbeHostAdapter(HostAdapter)) continue; + /* + Hard Reset the Host Adapter. If unsuccessful, abort further + initialization. + */ + if (!BusLogic_HardResetHostAdapter(HostAdapter)) continue; + /* + Check the Host Adapter. If unsuccessful, abort further initialization. + */ + if (!BusLogic_CheckHostAdapter(HostAdapter)) continue; + /* + Initialize the Command Line Entry field if an explicit I/O Address + was not specified. + */ + if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount && + BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == 0) + HostAdapter->CommandLineEntry = + &BusLogic_CommandLineEntries[CommandLineEntryIndex++]; + /* + Announce the Driver Version and Date, Author's Name, Copyright Notice, + and Contact Address. + */ + BusLogic_AnnounceDriver(); + /* + Register usage of the I/O Address range. From this point onward, any + failure will be assumed to be due to a problem with the Host Adapter, + rather than due to having mistakenly identified this port as belonging + to a BusLogic Host Adapter. The I/O Address range will not be + released, thereby preventing it from being incorrectly identified as + any other type of Host Adapter. + */ + request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount, + "BusLogic"); + /* + Register the SCSI Host structure. + */ + HostTemplate->proc_dir = &BusLogic_ProcDirectoryEntry; + Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T)); + HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata; + memcpy(HostAdapter, &HostAdapterPrototype, + sizeof(BusLogic_HostAdapter_T)); + HostAdapter->SCSI_Host = Host; + HostAdapter->HostNumber = Host->host_no; + Host->select_queue_depths = BusLogic_SelectQueueDepths; + /* + Add Host Adapter to the end of the list of registered BusLogic + Host Adapters. In order for Command Complete Interrupts to be + properly dismissed by BusLogic_InterruptHandler, the Host Adapter + must be registered. This must be done before the IRQ Channel is + acquired, and in a shared IRQ Channel environment, must be done + before any Command Complete Interrupts occur, since the IRQ Channel + may have already been acquired by a previous BusLogic Host Adapter. + */ + BusLogic_RegisterHostAdapter(HostAdapter); + /* + Read the Host Adapter Configuration, Acquire the System Resources + necessary to use Host Adapter and initialize the fields in the SCSI + Host structure, then Test Interrupts, Create the Mailboxes and CCBs, + Initialize the Host Adapter, and finally perform Target Device Inquiry. + */ + if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) && + BusLogic_AcquireResources(HostAdapter) && + BusLogic_TestInterrupts(HostAdapter) && + BusLogic_CreateMailboxes(HostAdapter) && + BusLogic_CreateCCBs(HostAdapter) && + BusLogic_InitializeHostAdapter(HostAdapter) && + BusLogic_TargetDeviceInquiry(HostAdapter)) + { + /* + Initialization has been completed successfully. Release and + re-register usage of the I/O Address range so that the Model + Name of the Host Adapter will appear, and initialize the SCSI + Host structure. + */ + release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount); + request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount, + HostAdapter->ControllerName); + BusLogic_InitializeHostStructure(HostAdapter, Host); + BusLogicHostAdapterCount++; + } + else + { + /* + An error occurred during Host Adapter Configuration Querying, + Resource Acquisition, Interrupt Testing, CCB Creation, Host Adapter + Initialization, or Target Device Inquiry, so remove Host Adapter + from the list of registered BusLogic Host Adapters, destroy the + CCBs and Mailboxes, Release the System Resources, and Unregister + the SCSI Host. + */ + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_DestroyMailboxes(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + BusLogic_UnregisterHostAdapter(HostAdapter); + scsi_unregister(Host); + } + } + return BusLogicHostAdapterCount; +} + + +/* + BusLogic_ReleaseHostAdapter releases all resources previously acquired to + support a specific Host Adapter, including the I/O Address range, and + unregisters the BusLogic Host Adapter. +*/ + +int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Host->hostdata; + /* + Destroy the CCBs and Mailboxes, and release any system resources acquired + to support Host Adapter. + */ + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_DestroyMailboxes(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + /* + Release usage of the I/O Address range. + */ + release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount); + /* + Remove Host Adapter from the list of registered BusLogic Host Adapters. + */ + BusLogic_UnregisterHostAdapter(HostAdapter); + return 0; +} + + +/* + BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from + the Host Adapter Status and Target Device Status. +*/ + +static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T + HostAdapterStatus, + BusLogic_TargetDeviceStatus_T + TargetDeviceStatus) +{ + int HostStatus; + switch (HostAdapterStatus) + { + case BusLogic_CommandCompletedNormally: + case BusLogic_LinkedCommandCompleted: + case BusLogic_LinkedCommandCompletedWithFlag: + HostStatus = DID_OK; + break; + case BusLogic_SCSISelectionTimeout: + HostStatus = DID_TIME_OUT; + break; + case BusLogic_InvalidOutgoingMailboxActionCode: + case BusLogic_InvalidCommandOperationCode: + case BusLogic_InvalidCommandParameter: + printk("BusLogic: BusLogic Driver Protocol Error 0x%02X\n", + HostAdapterStatus); + case BusLogic_DataOverUnderRun: + case BusLogic_UnexpectedBusFree: + case BusLogic_LinkedCCBhasInvalidLUN: + case BusLogic_AutoRequestSenseFailed: + case BusLogic_TaggedQueuingMessageRejected: + case BusLogic_UnsupportedMessageReceived: + case BusLogic_HostAdapterHardwareFailed: + case BusLogic_TargetDeviceReconnectedImproperly: + case BusLogic_AbortQueueGenerated: + case BusLogic_HostAdapterSoftwareError: + case BusLogic_HostAdapterHardwareTimeoutError: + case BusLogic_SCSIParityErrorDetected: + HostStatus = DID_ERROR; + break; + case BusLogic_InvalidBusPhaseRequested: + case BusLogic_TargetFailedResponseToATN: + case BusLogic_HostAdapterAssertedRST: + case BusLogic_OtherDeviceAssertedRST: + case BusLogic_HostAdapterAssertedBusDeviceReset: + HostStatus = DID_RESET; + break; + default: + printk("BusLogic: Unknown Host Adapter Status 0x%02X\n", + HostAdapterStatus); + HostStatus = DID_ERROR; + break; + } + return (HostStatus << 16) | TargetDeviceStatus; +} + + +/* + BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host + Adapters. To simplify handling shared IRQ Channels, all installed BusLogic + Host Adapters are scanned whenever any one of them signals a hardware + interrupt. +*/ + +static void BusLogic_InterruptHandler(int IRQ_Channel, + void *DeviceIdentifier, + Registers_T *InterruptRegisters) +{ + BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL; + BusLogic_HostAdapter_T *HostAdapter; + int HostAdapterResetRequestedCount = 0; + BusLogic_Lock_T Lock; + /* + Iterate over the installed BusLogic Host Adapters accepting any Incoming + Mailbox entries and saving the completed CCBs for processing. This + interrupt handler is installed as a fast interrupt, so interrupts are + disabled when the interrupt handler is entered. + */ + for (HostAdapter = BusLogic_RegisteredHostAdapters; + HostAdapter != NULL; + HostAdapter = HostAdapter->Next) + { + unsigned char InterruptRegister; + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock); + /* + Read the Host Adapter Interrupt Register. + */ + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + if (InterruptRegister & BusLogic_InterruptValid) + { + /* + Acknowledge the interrupt and reset the Host Adapter + Interrupt Register. + */ + BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset); + /* + Process valid SCSI Reset State and Incoming Mailbox Loaded + Interrupts. Command Complete Interrupts are noted, and + Outgoing Mailbox Available Interrupts are ignored, as they + are never enabled. + */ + if (InterruptRegister & BusLogic_SCSIResetState) + { + HostAdapter->HostAdapterResetRequested = true; + HostAdapterResetRequestedCount++; + } + else if (InterruptRegister & BusLogic_IncomingMailboxLoaded) + { + /* + Scan through the Incoming Mailboxes in Strict Round Robin + fashion, saving any completed CCBs for further processing. + It is essential that for each CCB and SCSI Command issued, + command completion processing is performed exactly once. + Therefore, only Incoming Mailboxes with completion code + Command Completed Without Error, Command Completed With + Error, or Command Aborted At Host Request are saved for + completion processing. When an Incoming Mailbox has a + completion code of Aborted Command Not Found, the CCB had + already completed or been aborted before the current Abort + request was processed, and so completion processing has + already occurred and no further action should be taken. + */ + BusLogic_IncomingMailbox_T *NextIncomingMailbox = + HostAdapter->NextIncomingMailbox; + BusLogic_CompletionCode_T MailboxCompletionCode; + while ((MailboxCompletionCode = + NextIncomingMailbox->CompletionCode) != + BusLogic_IncomingMailboxFree) + { + BusLogic_CCB_T *CCB = + (BusLogic_CCB_T *) Bus_to_Virtual(NextIncomingMailbox->CCB); + if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound) + if (CCB->Status == BusLogic_CCB_Active || + CCB->Status == BusLogic_CCB_Reset) + { + /* + Mark this CCB as completed and add it to the end + of the list of completed CCBs. + */ + CCB->Status = BusLogic_CCB_Completed; + CCB->MailboxCompletionCode = MailboxCompletionCode; + CCB->Next = NULL; + if (FirstCompletedCCB == NULL) + { + FirstCompletedCCB = CCB; + LastCompletedCCB = CCB; + } + else + { + LastCompletedCCB->Next = CCB; + LastCompletedCCB = CCB; + } + HostAdapter->ActiveCommandCount[CCB->TargetID]--; + } + else + { + /* + If a CCB ever appears in an Incoming Mailbox and + is not marked as status Active or Reset, then there + is most likely a bug in the Host Adapter firmware. + */ + printk("scsi%d: Illegal CCB #%ld status %d in " + "Incoming Mailbox\n", HostAdapter->HostNumber, + CCB->SerialNumber, CCB->Status); + } + else printk("scsi%d: Aborted CCB #%ld to Target %d " + "Not Found\n", HostAdapter->HostNumber, + CCB->SerialNumber, CCB->TargetID); + NextIncomingMailbox->CompletionCode = + BusLogic_IncomingMailboxFree; + if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox) + NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + } + HostAdapter->NextIncomingMailbox = NextIncomingMailbox; + } + else if (InterruptRegister & BusLogic_CommandComplete) + HostAdapter->HostAdapterCommandCompleted = true; + } + /* + Release exclusive access to Host Adapter. + */ + BusLogic_ReleaseHostAdapterLockID(HostAdapter, &Lock); + } + /* + Iterate over the completed CCBs setting the SCSI Command Result Codes, + deallocating the CCBs, and calling the Completion Routines. + */ + while (FirstCompletedCCB != NULL) + { + BusLogic_CCB_T *CCB = FirstCompletedCCB; + SCSI_Command_T *Command = CCB->Command; + FirstCompletedCCB = FirstCompletedCCB->Next; + HostAdapter = CCB->HostAdapter; + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock); + /* + Process the Completed CCB. + */ + if (CCB->Opcode == BusLogic_BusDeviceReset) + { + int TargetID = CCB->TargetID; + printk("scsi%d: Bus Device Reset CCB #%ld to Target %d Completed\n", + HostAdapter->HostNumber, CCB->SerialNumber, TargetID); + HostAdapter->TotalCommandCount[TargetID] = 0; + HostAdapter->TaggedQueuingActive[TargetID] = false; + /* + Place CCB back on the Host Adapter's free list. + */ + BusLogic_DeallocateCCB(CCB); + /* + Bus Device Reset CCBs have the Command field non-NULL only when a + Bus Device Reset was requested for a Command that did not have a + currently active CCB in the Host Adapter (i.e., a Synchronous + Bus Device Reset), and hence would not have its Completion Routine + called otherwise. + */ + while (Command != NULL) + { + SCSI_Command_T *NextCommand = Command->reset_chain; + Command->reset_chain = NULL; + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + Command = NextCommand; + } + /* + Iterate over the CCBs for this Host Adapter performing completion + processing for any CCBs marked as Reset for this Target. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Reset && CCB->TargetID == TargetID) + { + Command = CCB->Command; + BusLogic_DeallocateCCB(CCB); + HostAdapter->ActiveCommandCount[TargetID]--; + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + } + HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL; + } + else + { + /* + Translate the Mailbox Completion Code, Host Adapter Status, and + Target Device Status into a SCSI Subsystem Result Code. + */ + switch (CCB->MailboxCompletionCode) + { + case BusLogic_IncomingMailboxFree: + case BusLogic_AbortedCommandNotFound: + printk("scsi%d: CCB #%ld to Target %d Impossible State\n", + HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID); + break; + case BusLogic_CommandCompletedWithoutError: + HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true; + Command->result = DID_OK << 16; + break; + case BusLogic_CommandAbortedAtHostRequest: + printk("scsi%d: CCB #%ld to Target %d Aborted\n", + HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID); + Command->result = DID_ABORT << 16; + break; + case BusLogic_CommandCompletedWithError: + Command->result = + BusLogic_ComputeResultCode(CCB->HostAdapterStatus, + CCB->TargetDeviceStatus); + if (BusLogic_GlobalOptions & BusLogic_TraceErrors) + if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout) + { + int i; + printk("scsi%d: CCB #%ld Target %d: Result %X " + "Host Adapter Status %02X Target Status %02X\n", + HostAdapter->HostNumber, CCB->SerialNumber, + CCB->TargetID, Command->result, + CCB->HostAdapterStatus, CCB->TargetDeviceStatus); + printk("scsi%d: CDB ", HostAdapter->HostNumber); + for (i = 0; i < CCB->CDB_Length; i++) + printk(" %02X", CCB->CDB[i]); + printk("\n"); + printk("scsi%d: Sense ", HostAdapter->HostNumber); + for (i = 0; i < CCB->SenseDataLength; i++) + printk(" %02X", Command->sense_buffer[i]); + printk("\n"); + } + break; + } + /* + Place CCB back on the Host Adapter's free list. + */ + BusLogic_DeallocateCCB(CCB); + /* + Call the SCSI Command Completion Routine. + */ + Command->scsi_done(Command); + } + /* + Release exclusive access to Host Adapter. + */ + BusLogic_ReleaseHostAdapterLockID(HostAdapter, &Lock); + } + /* + Iterate over the Host Adapters performing any requested Host Adapter Resets. + */ + if (HostAdapterResetRequestedCount == 0) return; + for (HostAdapter = BusLogic_RegisteredHostAdapters; + HostAdapter != NULL; + HostAdapter = HostAdapter->Next) + if (HostAdapter->HostAdapterResetRequested) + { + BusLogic_ResetHostAdapter(HostAdapter, NULL, 0); + HostAdapter->HostAdapterResetRequested = false; + scsi_mark_host_reset(HostAdapter->SCSI_Host); + } +} + + +/* + BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing + Mailbox for execution by Host Adapter. The Host Adapter's Lock should have + already been acquired by the caller. +*/ + +static boolean BusLogic_WriteOutgoingMailbox(BusLogic_HostAdapter_T + *HostAdapter, + BusLogic_ActionCode_T ActionCode, + BusLogic_CCB_T *CCB) +{ + BusLogic_OutgoingMailbox_T *NextOutgoingMailbox; + NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox; + if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree) + { + CCB->Status = BusLogic_CCB_Active; + /* + The CCB field must be written before the Action Code field since + the Host Adapter is operating asynchronously and the locking code + does not protect against simultaneous access by the Host Adapter. + */ + NextOutgoingMailbox->CCB = Virtual_to_Bus(CCB); + NextOutgoingMailbox->ActionCode = ActionCode; + BusLogic_StartMailboxCommand(HostAdapter); + if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox) + NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox; + if (ActionCode == BusLogic_MailboxStartCommand) + HostAdapter->ActiveCommandCount[CCB->TargetID]++; + return true; + } + return false; +} + + +/* + BusLogic_QueueCommand creates a CCB for Command and places it into an + Outgoing Mailbox for execution by the associated Host Adapter. +*/ + +int BusLogic_QueueCommand(SCSI_Command_T *Command, + void (*CompletionRoutine)(SCSI_Command_T *)) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Command->host->hostdata; + unsigned char *CDB = Command->cmnd; + int CDB_Length = Command->cmd_len; + int TargetID = Command->target; + int LogicalUnit = Command->lun; + void *BufferPointer = Command->request_buffer; + int BufferLength = Command->request_bufflen; + int SegmentCount = Command->use_sg; + BusLogic_Lock_T Lock; + BusLogic_CCB_T *CCB; + /* + SCSI REQUEST_SENSE commands will be executed automatically by the Host + Adapter for any errors, so they should not be executed explicitly unless + the Sense Data is zero indicating that no error occurred. + */ + if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0) + { + Command->result = DID_OK << 16; + CompletionRoutine(Command); + return 0; + } + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + /* + Allocate a CCB from the Host Adapter's free list. In the unlikely event + that there are none available and memory allocation fails, wait 1 second + and try again. If that fails, the Host Adapter is probably hung so we + signal an error as a Host Adapter Hard Reset should be initiated soon. + */ + CCB = BusLogic_AllocateCCB(HostAdapter); + if (CCB == NULL) + { + BusLogic_Delay(1); + CCB = BusLogic_AllocateCCB(HostAdapter); + if (CCB == NULL) + { + Command->result = DID_ERROR << 16; + CompletionRoutine(Command); + goto Done; + } + } + /* + Initialize the fields in the BusLogic Command Control Block (CCB). + */ + if (SegmentCount == 0) + { + CCB->Opcode = BusLogic_InitiatorCCB; + CCB->DataLength = BufferLength; + CCB->DataPointer = Virtual_to_Bus(BufferPointer); + } + else + { + SCSI_ScatterList_T *ScatterList = (SCSI_ScatterList_T *) BufferPointer; + int Segment; + CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather; + CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T); + CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList); + for (Segment = 0; Segment < SegmentCount; Segment++) + { + CCB->ScatterGatherList[Segment].SegmentByteCount = + ScatterList[Segment].length; + CCB->ScatterGatherList[Segment].SegmentDataPointer = + Virtual_to_Bus(ScatterList[Segment].address); + } + } + switch (CDB[0]) + { + case READ_6: + case READ_10: + CCB->DataDirection = BusLogic_DataInLengthChecked; + break; + case WRITE_6: + case WRITE_10: + CCB->DataDirection = BusLogic_DataOutLengthChecked; + break; + default: + CCB->DataDirection = BusLogic_UncheckedDataTransfer; + break; + } + CCB->CDB_Length = CDB_Length; + CCB->SenseDataLength = sizeof(Command->sense_buffer); + CCB->HostAdapterStatus = 0; + CCB->TargetDeviceStatus = 0; + CCB->TargetID = TargetID; + CCB->LogicalUnit = LogicalUnit; + /* + For Host Adapters that support it, 64 LUN Format CCBs are used to allow + 64 Logical Units per Target, and this requires setting the overloaded + TagEnable field to Logical Unit bit 5. + */ + if (HostAdapter->Host64LUNSupport) + { + CCB->TagEnable = LogicalUnit >> 5; + CCB->TagEnable64LUN = false; + } + else CCB->TagEnable = false; + /* + BusLogic recommends that after a Reset the first couple of commands that + are sent to a Target Device be sent in a non Tagged Queue fashion so that + the Host Adapter and Target Device can establish Synchronous and Wide + Transfer before Queue Tag messages can interfere with the Synchronous and + Wide Negotiation messages. By waiting to enable Tagged Queuing until after + the first BusLogic_MaxTaggedQueueDepth commands have been queued, it is + assured that after a Reset any pending commands are requeued before Tagged + Queuing is enabled and that the Tagged Queuing message will not occur while + the partition table is being printed. In addition, some devices do not + properly handle the transition from non-tagged to tagged commands, so it is + necessary to wait until there are no pending commands for a target device + before queuing tagged commands. + */ + if (HostAdapter->TotalCommandCount[TargetID]++ >= + BusLogic_MaxTaggedQueueDepth && + !HostAdapter->TaggedQueuingActive[TargetID] && + HostAdapter->ActiveCommandCount[TargetID] == 0 && + (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) && + Command->device->tagged_supported) + { + HostAdapter->TaggedQueuingActive[TargetID] = true; + printk("scsi%d: Tagged Queuing now active for Target %d\n", + HostAdapter->HostNumber, TargetID); + } + if (HostAdapter->TaggedQueuingActive[TargetID]) + { + BusLogic_QueueTag_T QueueTag = BusLogic_SimpleQueueTag; + /* + When using Tagged Queuing with Simple Queue Tags, it appears that disk + drive controllers do not guarantee that a queued command will not + remain in a disconnected state indefinitely if commands that read or + write nearer the head position continue to arrive without interruption. + Therefore, for each Target Device this driver keeps track of the last + time either the queue was empty or an Ordered Queue Tag was issued. If + more than 5 seconds (one third of the 15 second disk timeout) have + elapsed since this last sequence point, this command will be issued + with an Ordered Queue Tag rather than a Simple Queue Tag, which forces + the Target Device to complete all previously queued commands before + this command may be executed. + */ + if (HostAdapter->ActiveCommandCount[TargetID] == 0) + HostAdapter->LastSequencePoint[TargetID] = jiffies; + else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 5*HZ) + { + HostAdapter->LastSequencePoint[TargetID] = jiffies; + QueueTag = BusLogic_OrderedQueueTag; + } + if (HostAdapter->Host64LUNSupport) + { + CCB->TagEnable64LUN = true; + CCB->QueueTag64LUN = QueueTag; + } + else + { + CCB->TagEnable = true; + CCB->QueueTag = QueueTag; + } + } + memcpy(CCB->CDB, CDB, CDB_Length); + CCB->SenseDataPointer = Virtual_to_Bus(&Command->sense_buffer); + CCB->Command = Command; + Command->scsi_done = CompletionRoutine; + /* + Place the CCB in an Outgoing Mailbox. The higher levels of the SCSI + Subsystem should not attempt to queue more commands than can be placed in + Outgoing Mailboxes, so there should always be one free. In the unlikely + event that there are none available, wait 1 second and try again. If + that fails, the Host Adapter is probably hung so we signal an error as + a Host Adapter Hard Reset should be initiated soon. + */ + if (!BusLogic_WriteOutgoingMailbox(HostAdapter, + BusLogic_MailboxStartCommand, CCB)) + { + printk("scsi%d: cannot write Outgoing Mailbox - Pausing for 1 second\n", + HostAdapter->HostNumber); + BusLogic_Delay(1); + if (!BusLogic_WriteOutgoingMailbox(HostAdapter, + BusLogic_MailboxStartCommand, CCB)) + { + printk("scsi%d: still cannot write Outgoing Mailbox - " + "Host Adapter Dead?\n", HostAdapter->HostNumber); + BusLogic_DeallocateCCB(CCB); + Command->result = DID_ERROR << 16; + Command->scsi_done(Command); + } + } + /* + Release exclusive access to Host Adapter. + */ +Done: + BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + return 0; +} + + +/* + BusLogic_AbortCommand aborts Command if possible. +*/ + +int BusLogic_AbortCommand(SCSI_Command_T *Command) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Command->host->hostdata; + int TargetID = Command->target; + BusLogic_Lock_T Lock; + BusLogic_CCB_T *CCB; + int Result; + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + /* + If this Command has already completed, then no Abort is necessary. + */ + if (Command->serial_number != Command->serial_number_at_timeout) + { + printk("scsi%d: Unable to Abort Command to Target %d - " + "Already Completed\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_ABORT_NOT_RUNNING; + goto Done; + } + /* + Attempt to find an Active CCB for this Command. If no Active CCB for this + Command is found, then no Abort is necessary. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Command == Command) break; + if (CCB == NULL) + { + printk("scsi%d: Unable to Abort Command to Target %d - No CCB Found\n", + HostAdapter->HostNumber, TargetID); + Result = SCSI_ABORT_NOT_RUNNING; + goto Done; + } + else if (CCB->Status == BusLogic_CCB_Completed) + { + printk("scsi%d: Unable to Abort Command to Target %d - CCB Completed\n", + HostAdapter->HostNumber, TargetID); + Result = SCSI_ABORT_NOT_RUNNING; + goto Done; + } + else if (CCB->Status == BusLogic_CCB_Reset) + { + printk("scsi%d: Unable to Abort Command to Target %d - CCB Reset\n", + HostAdapter->HostNumber, TargetID); + Result = SCSI_ABORT_NOT_RUNNING; + goto Done; + } + /* + Attempt to Abort this CCB. Firmware versions prior to 5.xx do not generate + Abort Tag messages, but only generate the non-tagged Abort message. Since + non-tagged commands are not sent by the Host Adapter until the queue of + outstanding tagged commands has completed, and the Abort message is treated + as a non-tagged command, it is effectively impossible to abort commands + when Tagged Queuing is active. Firmware version 5.xx does generate Abort + Tag messages, so it is possible to abort commands when Tagged Queuing is + active. + */ + if (HostAdapter->TaggedQueuingActive[TargetID] && + HostAdapter->FirmwareVersion[0] < '5') + { + printk("scsi%d: Unable to Abort CCB #%ld to Target %d - " + "Abort Tag Not Supported\n", HostAdapter->HostNumber, + CCB->SerialNumber, TargetID); + Result = SCSI_ABORT_SNOOZE; + } + else if (BusLogic_WriteOutgoingMailbox(HostAdapter, + BusLogic_MailboxAbortCommand, CCB)) + { + printk("scsi%d: Aborting CCB #%ld to Target %d\n", + HostAdapter->HostNumber, CCB->SerialNumber, TargetID); + Result = SCSI_ABORT_PENDING; + } + else + { + printk("scsi%d: Unable to Abort CCB #%ld to Target %d - " + "No Outgoing Mailboxes\n", HostAdapter->HostNumber, + CCB->SerialNumber, TargetID); + Result = SCSI_ABORT_BUSY; + } + /* + Release exclusive access to Host Adapter. + */ +Done: + BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + return Result; +} + + +/* + BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all + currently executing SCSI Commands as having been Reset. +*/ + +static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter, + SCSI_Command_T *Command, + unsigned int ResetFlags) +{ + BusLogic_Lock_T Lock; + BusLogic_CCB_T *CCB; + int TargetID, Result; + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + /* + If this is an Asynchronous Reset and this Command has already completed, + then no Reset is necessary. + */ + if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) + { + TargetID = Command->target; + if (Command->serial_number != Command->serial_number_at_timeout) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "Already Completed or Reset\n", + HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_NOT_RUNNING; + goto Done; + } + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Command == Command) break; + if (CCB == NULL) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "No CCB Found\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_NOT_RUNNING; + goto Done; + } + else if (CCB->Status == BusLogic_CCB_Completed) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "CCB Completed\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_NOT_RUNNING; + goto Done; + } + else if (CCB->Status == BusLogic_CCB_Reset && + HostAdapter->BusDeviceResetPendingCCB[TargetID] == NULL) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "Reset Pending\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_PENDING; + goto Done; + } + } + if (Command == NULL) + printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n", + HostAdapter->HostNumber, HostAdapter->ControllerName); + else printk("scsi%d: Resetting %s due to Target %d\n", + HostAdapter->HostNumber, HostAdapter->ControllerName, + Command->target); + /* + Attempt to Reset and Reinitialize the Host Adapter. + */ + if (!(BusLogic_HardResetHostAdapter(HostAdapter) && + BusLogic_InitializeHostAdapter(HostAdapter))) + { + printk("scsi%d: Resetting %s Failed\n", + HostAdapter->HostNumber, HostAdapter->ControllerName); + Result = SCSI_RESET_ERROR; + goto Done; + } + /* + Mark all currently executing CCBs as having been Reset. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Active) + CCB->Status = BusLogic_CCB_Reset; + /* + Wait a few seconds between the Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get + confused if they receive SCSI Commands too soon after a SCSI Bus Reset. + Note that a timer interrupt may occur here, but all active CCBs have + already been marked Reset and so a reentrant call will return Pending. + */ + BusLogic_Delay(HostAdapter->BusSettleTime); + /* + If this is a Synchronous Reset, perform completion processing for + the Command being Reset. + */ + if (ResetFlags & SCSI_RESET_SYNCHRONOUS) + { + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + } + /* + Perform completion processing for all CCBs marked as Reset. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Reset) + { + Command = CCB->Command; + BusLogic_DeallocateCCB(CCB); + while (Command != NULL) + { + SCSI_Command_T *NextCommand = Command->reset_chain; + Command->reset_chain = NULL; + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + Command = NextCommand; + } + } + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->LastResetTime[TargetID] = jiffies; + Result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET; + /* + Release exclusive access to Host Adapter. + */ +Done: + BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + return Result; +} + + +/* + BusLogic_SendBusDeviceReset sends a Bus Device Reset to the Target + Device associated with Command. +*/ + +static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter, + SCSI_Command_T *Command, + unsigned int ResetFlags) +{ + int TargetID = Command->target; + BusLogic_Lock_T Lock; + BusLogic_CCB_T *CCB; + int Result = -1; + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + /* + If this is an Asynchronous Reset and this Command has already completed, + then no Reset is necessary. + */ + if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) + { + if (Command->serial_number != Command->serial_number_at_timeout) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "Already Completed\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_NOT_RUNNING; + goto Done; + } + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Command == Command) break; + if (CCB == NULL) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "No CCB Found\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_NOT_RUNNING; + goto Done; + } + else if (CCB->Status == BusLogic_CCB_Completed) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "CCB Completed\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_NOT_RUNNING; + goto Done; + } + else if (CCB->Status == BusLogic_CCB_Reset) + { + printk("scsi%d: Unable to Reset Command to Target %d - " + "Reset Pending\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_PENDING; + goto Done; + } + else if (HostAdapter->BusDeviceResetPendingCCB[TargetID] != NULL) + { + printk("scsi%d: Bus Device Reset already pending to Target %d\n", + HostAdapter->HostNumber, TargetID); + goto Done; + } + } + /* + If this is a Synchronous Reset and a Bus Device Reset is already pending + for this Target Device, do not send a second one. Add this Command to + the list of Commands for which completion processing must be performed + when the Bus Device Reset CCB completes. + */ + if (ResetFlags & SCSI_RESET_SYNCHRONOUS) + if ((CCB = HostAdapter->BusDeviceResetPendingCCB[TargetID]) != NULL) + { + Command->reset_chain = CCB->Command; + CCB->Command = Command; + printk("scsi%d: Unable to Reset Command to Target %d - " + "Reset Pending\n", HostAdapter->HostNumber, TargetID); + Result = SCSI_RESET_PENDING; + goto Done; + } + /* + Firmware versions prior to 5.xx treat a Bus Device Reset as a non-tagged + command. Since non-tagged commands are not sent by the Host Adapter until + the queue of outstanding tagged commands has completed, it is effectively + impossible to send a Bus Device Reset while there are tagged commands + outstanding. Therefore, in that case a full Host Adapter Hard Reset and + SCSI Bus Reset must be done. + */ + if (HostAdapter->TaggedQueuingActive[TargetID] && + HostAdapter->ActiveCommandCount[TargetID] > 0 && + HostAdapter->FirmwareVersion[0] < '5') + goto Done; + /* + Allocate a CCB from the Host Adapter's free list. In the unlikely event + that there are none available and memory allocation fails, attempt a full + Host Adapter Hard Reset and SCSI Bus Reset. + */ + CCB = BusLogic_AllocateCCB(HostAdapter); + if (CCB == NULL) goto Done; + printk("scsi%d: Sending Bus Device Reset CCB #%ld to Target %d\n", + HostAdapter->HostNumber, CCB->SerialNumber, TargetID); + CCB->Opcode = BusLogic_BusDeviceReset; + CCB->TargetID = TargetID; + /* + For Synchronous Resets, arrange for the interrupt handler to perform + completion processing for the Command being Reset. + */ + if (ResetFlags & SCSI_RESET_SYNCHRONOUS) + { + Command->reset_chain = NULL; + CCB->Command = Command; + } + /* + Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB. + If sending a Bus Device Reset is impossible, attempt a full Host + Adapter Hard Reset and SCSI Bus Reset. + */ + if (!(BusLogic_WriteOutgoingMailbox(HostAdapter, + BusLogic_MailboxStartCommand, CCB))) + { + printk("scsi%d: cannot write Outgoing Mailbox for Bus Device Reset\n", + HostAdapter->HostNumber); + BusLogic_DeallocateCCB(CCB); + goto Done; + } + /* + If there is a currently executing CCB in the Host Adapter for this Command + (i.e. this is an Asynchronous Reset), then an Incoming Mailbox entry may be + made with a completion code of BusLogic_HostAdapterAssertedBusDeviceReset. + If there is no active CCB for this Command (i.e. this is a Synchronous + Reset), then the Bus Device Reset CCB's Command field will have been set + to the Command so that the interrupt for the completion of the Bus Device + Reset can call the Completion Routine for the Command. On successful + execution of a Bus Device Reset, older firmware versions did return the + pending CCBs with the appropriate completion code, but more recent firmware + versions only return the Bus Device Reset CCB itself. This driver handles + both cases by marking all the currently executing CCBs to this Target + Device as Reset. When the Bus Device Reset CCB is processed by the + interrupt handler, any remaining CCBs marked as Reset will have completion + processing performed. + */ + HostAdapter->BusDeviceResetPendingCCB[TargetID] = CCB; + HostAdapter->LastResetTime[TargetID] = jiffies; + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Active && CCB->TargetID == TargetID) + CCB->Status = BusLogic_CCB_Reset; + Result = SCSI_RESET_PENDING; + /* + If a Bus Device Reset was not possible for some reason, force a full + Host Adapter Hard Reset and SCSI Bus Reset. + */ +Done: + if (Result < 0) + Result = BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags); + /* + Release exclusive access to Host Adapter. + */ + BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + return Result; +} + + +/* + BusLogic_ResetCommand takes appropriate action to reset Command. +*/ + +int BusLogic_ResetCommand(SCSI_Command_T *Command, unsigned int ResetFlags) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Command->host->hostdata; + int TargetID = Command->target; + int ErrorRecoveryStrategy = HostAdapter->ErrorRecoveryStrategy[TargetID]; + /* + Disable Tagged Queuing if it is active for this Target Device and if + it has been less than 10 minutes since the last reset occurred, or since + the system was initialized if no prior resets have occurred. + */ + if (HostAdapter->TaggedQueuingActive[TargetID] && + jiffies - HostAdapter->LastResetTime[TargetID] < 10*60*HZ) + { + HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID); + HostAdapter->TaggedQueuingActive[TargetID] = false; + printk("scsi%d: Tagged Queuing now disabled for Target %d\n", + HostAdapter->HostNumber, TargetID); + } + if (ErrorRecoveryStrategy == BusLogic_ErrorRecovery_Default) + if (ResetFlags & SCSI_RESET_SUGGEST_HOST_RESET) + ErrorRecoveryStrategy = BusLogic_ErrorRecovery_HardReset; + else if (ResetFlags & SCSI_RESET_SUGGEST_BUS_RESET) + ErrorRecoveryStrategy = BusLogic_ErrorRecovery_HardReset; + else ErrorRecoveryStrategy = BusLogic_ErrorRecovery_BusDeviceReset; + switch (ErrorRecoveryStrategy) + { + case BusLogic_ErrorRecovery_HardReset: + return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags); + case BusLogic_ErrorRecovery_BusDeviceReset: + /* + The Bus Device Reset Error Recovery Strategy only graduates to a Hard + Reset when no commands have completed successfully since the last Bus + Device Reset and it has been at least 100 milliseconds. This prevents + a sequence of commands that all timeout together from immediately + forcing a Hard Reset before the Bus Device Reset has had a chance to + clear the error condition. + */ + if (HostAdapter->CommandSuccessfulFlag[TargetID] || + jiffies - HostAdapter->LastResetTime[TargetID] < HZ/10) + { + HostAdapter->CommandSuccessfulFlag[TargetID] = false; + return BusLogic_SendBusDeviceReset(HostAdapter, Command, ResetFlags); + } + else return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags); + } + printk("scsi%d: Error Recovery for Target %d Suppressed\n", + HostAdapter->HostNumber, TargetID); + return SCSI_RESET_PUNT; +} + + +/* + BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk + Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and + the appropriate number of cylinders so as not to exceed drive capacity. In + order for disks equal to or larger than 1 GB to be addressable by the BIOS + without exceeding the BIOS limitation of 1024 cylinders, Extended Translation + may be enabled in AutoSCSI on "W" and "C" Series controllers or by a dip + switch setting on older controllers. With Extended Translation enabled, + drives between 1 GB inclusive and 2 GB exclusive are given a disk geometry of + 128 heads and 32 sectors, and drives above 2 GB inclusive are given a disk + geometry of 255 heads and 63 sectors. However, if the BIOS detects that the + Extended Translation setting does not match the geometry in the partition + table, then the translation inferred from the partition table will be used by + the BIOS, and a warning may be displayed. +*/ + +int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device, + int *Parameters) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Disk->device->host->hostdata; + BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters; + struct buffer_head *BufferHead; + if (HostAdapter->ExtendedTranslation && + Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */) + if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */) + { + DiskParameters->Heads = 255; + DiskParameters->Sectors = 63; + } + else + { + DiskParameters->Heads = 128; + DiskParameters->Sectors = 32; + } + else + { + DiskParameters->Heads = 64; + DiskParameters->Sectors = 32; + } + DiskParameters->Cylinders = + Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors); + /* + Attempt to read the first 1024 bytes from the disk device. + */ + BufferHead = bread(MKDEV(MAJOR(Device), MINOR(Device) & ~0x0F), 0, 1024); + if (BufferHead == NULL) return 0; + /* + If the boot sector partition table flag is valid, search for a partition + table entry whose end_head matches one of the standard BusLogic geometry + translations (64/32, 128/32, or 255/63). + */ + if (*(unsigned short *) (BufferHead->b_data + 0x1FE) == 0xAA55) + { + struct partition *PartitionEntry = + (struct partition *) (BufferHead->b_data + 0x1BE); + int SavedCylinders = DiskParameters->Cylinders, PartitionNumber; + for (PartitionNumber = 0; PartitionNumber < 4; PartitionNumber++) + { + if (PartitionEntry->end_head == 64-1) + { + DiskParameters->Heads = 64; + DiskParameters->Sectors = 32; + break; + } + else if (PartitionEntry->end_head == 128-1) + { + DiskParameters->Heads = 128; + DiskParameters->Sectors = 32; + break; + } + else if (PartitionEntry->end_head == 255-1) + { + DiskParameters->Heads = 255; + DiskParameters->Sectors = 63; + break; + } + PartitionEntry++; + } + DiskParameters->Cylinders = + Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors); + if (SavedCylinders != DiskParameters->Cylinders) + printk("scsi%d: Warning: Extended Translation Setting " + "(> 1GB Switch) does not match\n" + "scsi%d: Partition Table - Adopting %d/%d Geometry " + "from Partition Table\n", + HostAdapter->HostNumber, HostAdapter->HostNumber, + DiskParameters->Heads, DiskParameters->Sectors); + } + brelse(BufferHead); + return 0; +} + + +/* + BusLogic_Setup handles processing of Kernel Command Line Arguments. + + For the BusLogic driver, a Kernel command line entry comprises the driver + identifier "BusLogic=" optionally followed by a comma-separated sequence of + integers and then optionally followed by a comma-separated sequence of + strings. Each command line entry applies to one BusLogic Host Adapter. + Multiple command line entries may be used in systems which contain multiple + BusLogic Host Adapters. + + The first integer specified is the I/O Address at which the Host Adapter is + located. If unspecified, it defaults to 0 which means to apply this entry to + the first BusLogic Host Adapter found during the default probe sequence. If + any I/O Address parameters are provided on the command line, then the default + probe sequence is omitted. + + The second integer specified is the Tagged Queue Depth to use for Target + Devices that support Tagged Queuing. The Queue Depth is the number of SCSI + commands that are allowed to be concurrently presented for execution. If + unspecified, it defaults to 0 which means to use a value determined + automatically based on the Host Adapter's Total Queue Depth and the number, + type, speed, and capabilities of the detected Target Devices. For Host + Adapters that require ISA Bounce Buffers, the Tagged Queue Depth is + automatically set to BusLogic_TaggedQueueDepth_BB to avoid excessive + preallocation of DMA Bounce Buffer memory. Target Devices that do not + support Tagged Queuing use a Queue Depth of BusLogic_UntaggedQueueDepth. + + The third integer specified is the Bus Settle Time in seconds. This is + the amount of time to wait between a Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI Commands. If unspecified, it defaults + to 0 which means to use the value of BusLogic_DefaultBusSettleTime. + + The fourth integer specified is the Local Options. If unspecified, it + defaults to 0. Note that Local Options are only applied to a specific Host + Adapter. + + The fifth integer specified is the Global Options. If unspecified, it + defaults to 0. Note that Global Options are applied across all Host + Adapters. + + The string options are used to provide control over Tagged Queuing, Error + Recovery, and Host Adapter Probing. + + The Tagged Queuing specification begins with "TQ:" and allows for explicitly + specifying whether Tagged Queuing is permitted on Target Devices that support + it. The following specification options are available: + + TQ:Default Tagged Queuing will be permitted based on the firmware + version of the BusLogic Host Adapter and based on + whether the Tagged Queue Depth value allows queuing + multiple commands. + + TQ:Enable Tagged Queuing will be enabled for all Target Devices + on this Host Adapter overriding any limitation that + would otherwise be imposed based on the Host Adapter + firmware version. + + TQ:Disable Tagged Queuing will be disabled for all Target Devices + on this Host Adapter. + + TQ:<Per-Target-Spec> Tagged Queuing will be controlled individually for each + Target Device. <Per-Target-Spec> is a sequence of "Y", + "N", and "X" characters. "Y" enabled Tagged Queuing, + "N" disables Tagged Queuing, and "X" accepts the + default based on the firmware version. The first + character refers to Target Device 0, the second to + Target Device 1, and so on; if the sequence of "Y", + "N", and "X" characters does not cover all the Target + Devices, unspecified characters are assumed to be "X". + + Note that explicitly requesting Tagged Queuing may lead to problems; this + facility is provided primarily to allow disabling Tagged Queuing on Target + Devices that do not implement it correctly. + + The Error Recovery Strategy specification begins with "ER:" and allows for + explicitly specifying the Error Recovery action to be performed when + ResetCommand is called due to a SCSI Command failing to complete + successfully. The following specification options are available: + + ER:Default Error Recovery will select between the Hard Reset and + Bus Device Reset options based on the recommendation + of the SCSI Subsystem. + + ER:HardReset Error Recovery will initiate a Host Adapter Hard Reset + which also causes a SCSI Bus Reset. + + ER:BusDeviceReset Error Recovery will send a Bus Device Reset message to + the individual Target Device causing the error. If + Error Recovery is again initiated for this Target + Device and no SCSI Command to this Target Device has + completed successfully since the Bus Device Reset + message was sent, then a Hard Reset will be attempted. + + ER:None Error Recovery will be suppressed. This option should + only be selected if a SCSI Bus Reset or Bus Device + Reset will cause the Target Device to fail completely + and unrecoverably. + + ER:<Per-Target-Spec> Error Recovery will be controlled individually for each + Target Device. <Per-Target-Spec> is a sequence of "D", + "H", "B", and "N" characters. "D" selects Default, "H" + selects Hard Reset, "B" selects Bus Device Reset, and + "N" selects None. The first character refers to Target + Device 0, the second to Target Device 1, and so on; if + the sequence of "D", "H", "B", and "N" characters does + not cover all the possible Target Devices, unspecified + characters are assumed to be "D". + + The Host Adapter Probing specification comprises the following strings: + + NoProbe No probing of any kind is to be performed, and hence + no BusLogic Host Adapters will be detected. + + NoProbeISA No probing of the standard ISA I/O Addresses will + be done, and hence only PCI Host Adapters will be + detected. + + NoSortPCI PCI Host Adapters will be enumerated in the order + provided by the PCI BIOS, ignoring any setting of + the AutoSCSI "Use Bus And Device # For PCI Scanning + Seq." option. +*/ + +void BusLogic_Setup(char *Strings, int *Integers) +{ + BusLogic_CommandLineEntry_T *CommandLineEntry = + &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++]; + static int ProbeListIndex = 0; + int IntegerCount = Integers[0]; + int TargetID, i; + CommandLineEntry->IO_Address = 0; + CommandLineEntry->TaggedQueueDepth = 0; + CommandLineEntry->BusSettleTime = 0; + CommandLineEntry->LocalOptions = 0; + CommandLineEntry->TaggedQueuingPermitted = 0; + CommandLineEntry->TaggedQueuingPermittedMask = 0; + memset(CommandLineEntry->ErrorRecoveryStrategy, + BusLogic_ErrorRecovery_Default, + sizeof(CommandLineEntry->ErrorRecoveryStrategy)); + if (IntegerCount > 5) + printk("BusLogic: Unexpected Command Line Integers ignored\n"); + if (IntegerCount >= 1) + { + unsigned int IO_Address = Integers[1]; + if (IO_Address > 0) + { + for (i = 0; ; i++) + if (BusLogic_IO_StandardAddresses[i] == 0) + { + printk("BusLogic: Invalid Command Line Entry " + "(illegal I/O Address 0x%X)\n", IO_Address); + return; + } + else if (i < ProbeListIndex && + IO_Address == BusLogic_IO_AddressProbeList[i]) + { + printk("BusLogic: Invalid Command Line Entry " + "(duplicate I/O Address 0x%X)\n", IO_Address); + return; + } + else if (IO_Address >= 0x400 || + IO_Address == BusLogic_IO_StandardAddresses[i]) break; + BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address; + BusLogic_IO_AddressProbeList[ProbeListIndex] = 0; + } + CommandLineEntry->IO_Address = IO_Address; + } + if (IntegerCount >= 2) + { + unsigned short TaggedQueueDepth = Integers[2]; + if (TaggedQueueDepth > BusLogic_MaxTaggedQueueDepth) + { + printk("BusLogic: Invalid Command Line Entry " + "(illegal Tagged Queue Depth %d)\n", TaggedQueueDepth); + return; + } + CommandLineEntry->TaggedQueueDepth = TaggedQueueDepth; + } + if (IntegerCount >= 3) + CommandLineEntry->BusSettleTime = Integers[3]; + if (IntegerCount >= 4) + CommandLineEntry->LocalOptions = Integers[4]; + if (IntegerCount >= 5) + BusLogic_GlobalOptions |= Integers[5]; + if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 || + BusLogic_CommandLineEntryCount == ProbeListIndex)) + { + printk("BusLogic: Invalid Command Line Entry " + "(all or no I/O Addresses must be specified)\n"); + return; + } + if (Strings == NULL) return; + while (*Strings != '\0') + { + if (strncmp(Strings, "TQ:", 3) == 0) + { + Strings += 3; + if (strncmp(Strings, "Default", 7) == 0) + Strings += 7; + else if (strncmp(Strings, "Enable", 6) == 0) + { + Strings += 6; + CommandLineEntry->TaggedQueuingPermitted = 0xFFFF; + CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF; + } + else if (strncmp(Strings, "Disable", 7) == 0) + { + Strings += 7; + CommandLineEntry->TaggedQueuingPermitted = 0x0000; + CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF; + } + else + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + switch (*Strings++) + { + case 'Y': + CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID; + CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID; + break; + case 'N': + CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID; + break; + case 'X': + break; + default: + Strings--; + TargetID = BusLogic_MaxTargetDevices; + break; + } + } + else if (strncmp(Strings, "ER:", 3) == 0) + { + Strings += 3; + if (strncmp(Strings, "Default", 7) == 0) + Strings += 7; + else if (strncmp(Strings, "HardReset", 9) == 0) + { + Strings += 9; + memset(CommandLineEntry->ErrorRecoveryStrategy, + BusLogic_ErrorRecovery_HardReset, + sizeof(CommandLineEntry->ErrorRecoveryStrategy)); + } + else if (strncmp(Strings, "BusDeviceReset", 14) == 0) + { + Strings += 14; + memset(CommandLineEntry->ErrorRecoveryStrategy, + BusLogic_ErrorRecovery_BusDeviceReset, + sizeof(CommandLineEntry->ErrorRecoveryStrategy)); + } + else if (strncmp(Strings, "None", 4) == 0) + { + Strings += 4; + memset(CommandLineEntry->ErrorRecoveryStrategy, + BusLogic_ErrorRecovery_None, + sizeof(CommandLineEntry->ErrorRecoveryStrategy)); + } + else + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + switch (*Strings++) + { + case 'D': + CommandLineEntry->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_Default; + break; + case 'H': + CommandLineEntry->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_HardReset; + break; + case 'B': + CommandLineEntry->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_BusDeviceReset; + break; + case 'N': + CommandLineEntry->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_None; + break; + default: + Strings--; + TargetID = BusLogic_MaxTargetDevices; + break; + } + } + else if (strcmp(Strings, "NoProbe") == 0 || + strcmp(Strings, "noprobe") == 0) + { + Strings += 7; + BusLogic_ProbeOptions |= BusLogic_NoProbe; + } + else if (strncmp(Strings, "NoProbeISA", 10) == 0) + { + Strings += 10; + BusLogic_ProbeOptions |= BusLogic_NoProbeISA; + } + else if (strncmp(Strings, "NoSortPCI", 9) == 0) + { + Strings += 9; + BusLogic_ProbeOptions |= BusLogic_NoSortPCI; + } + else + { + printk("BusLogic: Unexpected Command Line String '%s' ignored\n", + Strings); + break; + } + if (*Strings == ',') Strings++; + } +} + + +/* + Include Module support if requested. +*/ + +#ifdef MODULE + +SCSI_Host_Template_T driver_template = BUSLOGIC; + +#include "scsi_module.c" + +#endif |