patch-1.3.42 linux/drivers/scsi/BusLogic.c
Next file: linux/drivers/scsi/BusLogic.h
Previous file: linux/drivers/scsi/53c7,8xx.c
Back to the patch index
Back to the overall index
- Lines: 2578
- Date:
Mon Nov 13 21:57:50 1995
- Orig file:
v1.3.41/linux/drivers/scsi/BusLogic.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.41/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
@@ -0,0 +1,2577 @@
+/*
+
+ Linux Driver for BusLogic 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 all modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Alex T. Win of BusLogic, whose advice has been invaluable,
+ and to David B. Gentzel, for writing the original Linux BusLogic driver.
+
+*/
+
+
+#define BusLogic_DriverVersion "1.3.0"
+#define BusLogic_DriverDate "13 November 1995"
+
+
+#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_TracingOptions is a bit mask of Tracing Options to be applied
+ across all Host Adapters.
+*/
+
+static int
+ BusLogic_TracingOptions = 0;
+
+
+/*
+ BusLogic_RegisteredHostAdapters is a linked list of all the registered
+ BusLogic Host Adapters.
+*/
+
+static BusLogic_HostAdapter_T
+ *BusLogic_RegisteredHostAdapters = NULL;
+
+
+/*
+ BusLogic_Standard_IO_Addresses is the list of standard I/O Addresses at which
+ BusLogic Host Adapters may potentially be found.
+*/
+
+static unsigned short
+ 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 short
+ 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. Only IRQ Channels 9, 10, 11, 12, 14, and 15 are supported by
+ BusLogic Host Adapters.
+*/
+
+static short
+ BusLogic_IRQ_UsageCount[7] = { 0 };
+
+
+/*
+ BusLogic_CommandFailureReason holds a string identifying the reason why a
+ call to BusLogic_Command failed. It is only valid 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 Board 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->BoardName;
+}
+
+
+/*
+ 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 DestinationIndex = 0, SourceIndex = 0;
+ /*
+ If BusLogic_Setup has been called, 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 short Index = 0, VendorID;
+ unsigned char Bus, DeviceAndFunction;
+ unsigned int BaseAddress0;
+ while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++,
+ &Bus, &DeviceAndFunction) == 0)
+ if (pcibios_read_config_word(Bus, DeviceAndFunction,
+ PCI_VENDOR_ID, &VendorID) == 0 &&
+ VendorID == PCI_VENDOR_ID_BUSLOGIC &&
+ pcibios_read_config_dword(Bus, DeviceAndFunction,
+ PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+ (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) ==
+ PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ BusLogic_IO_AddressProbeList[DestinationIndex++] =
+ BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ }
+#endif
+ /*
+ Append the list of standard BusLogic I/O Addresses.
+ */
+ while (DestinationIndex < BusLogic_IO_MaxProbeAddresses &&
+ BusLogic_IO_StandardAddresses[SourceIndex] > 0)
+ BusLogic_IO_AddressProbeList[DestinationIndex++] =
+ BusLogic_IO_StandardAddresses[SourceIndex++];
+ BusLogic_IO_AddressProbeList[DestinationIndex] = 0;
+}
+
+
+/*
+ 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_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 < BusLogic_InitialCCBs; i++)
+ {
+ BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+ scsi_init_malloc(sizeof(BusLogic_CCB_T), GFP_ATOMIC | GFP_DMA);
+ 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.
+*/
+
+static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
+{
+ static unsigned int SerialNumber = 0;
+ BusLogic_CCB_T *CCB;
+ BusLogic_LockHostAdapter(HostAdapter);
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB != NULL)
+ {
+ CCB->SerialNumber = SerialNumber++;
+ HostAdapter->Free_CCBs = CCB->Next;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return CCB;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ CCB = (BusLogic_CCB_T *) scsi_init_malloc(sizeof(BusLogic_CCB_T),
+ GFP_ATOMIC | GFP_DMA);
+ if (CCB == NULL)
+ {
+ printk("scsi%d: Failed to allocate an additional CCB\n",
+ HostAdapter->HostNumber);
+ return NULL;
+ }
+ printk("scsi%d: Allocated an additional CCB\n", HostAdapter->HostNumber);
+ memset(CCB, 0, sizeof(BusLogic_CCB_T));
+ CCB->HostAdapter = HostAdapter;
+ CCB->Status = BusLogic_CCB_Free;
+ BusLogic_LockHostAdapter(HostAdapter);
+ CCB->SerialNumber = SerialNumber++;
+ CCB->NextAll = HostAdapter->All_CCBs;
+ HostAdapter->All_CCBs = CCB;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return CCB;
+}
+
+
+/*
+ BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+ free list.
+*/
+
+static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
+{
+ BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+ BusLogic_LockHostAdapter(HostAdapter);
+ CCB->Command = NULL;
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->SerialNumber = 0;
+ CCB->Next = HostAdapter->Free_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+}
+
+
+/*
+ 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 board detection and initialization, so
+ performance and latency are not critical, and exclusive access to the Host
+ Adapter hardware is assumed. Once the board and driver are initialized, the
+ only Host Adapter command that is issued is the single byte Start Mailbox
+ Scan command, 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;
+ long TimeoutCounter;
+ int ReplyBytes = 0;
+ /*
+ Clear out the Reply Data if provided.
+ */
+ if (ReplyLength > 0)
+ memset(ReplyData, 0, ReplyLength);
+ /*
+ Select an appropriate timeout value.
+ */
+ switch (OperationCode)
+ {
+ case BusLogic_InquireInstalledDevicesID0to7:
+ case BusLogic_InquireInstalledDevicesID8to15:
+ /* Approximately 60 seconds. */
+ TimeoutCounter = loops_per_sec << 2;
+ break;
+ default:
+ /* Approximately 1 second. */
+ TimeoutCounter = loops_per_sec >> 4;
+ break;
+ }
+ /*
+ Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+ Register Busy bit to be reset in the Status Register.
+ */
+ 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.
+ */
+ BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+ /*
+ Write any additional Parameter Bytes.
+ */
+ HostAdapter->HostAdapterCommandCompleted = false;
+ while (--ParameterLength >= 0)
+ {
+ InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+ if (InterruptRegister & BusLogic_CommandComplete) break;
+ if (HostAdapter->HostAdapterCommandCompleted) break;
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (!(StatusRegister & BusLogic_CommandParameterRegisterBusy)) break;
+ }
+ BusLogic_CommandFailureReason =
+ "Timeout waiting for Parameter Acceptance";
+ if (TimeoutCounter < 0) return -2;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+ }
+ BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+ if (ParameterLength >= 0) return -1;
+ /*
+ 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";
+ return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : 0);
+ }
+ /*
+ 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
+ HostAdapterCommandCompleted bit in the Host Adapter structure.
+ */
+ HostAdapter->HostAdapterCommandCompleted = false;
+ 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);
+ }
+ BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+ if (TimeoutCounter < 0) return -2;
+ /*
+ Clear any pending Command Complete Interrupt, unless this is a
+ Test Command Complete Interrupt command.
+ */
+ if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+ BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+ if (BusLogic_TracingOptions & BusLogic_TraceConfiguration)
+ if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+ {
+ int i;
+ printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
+ OperationCode, StatusRegister, ReplyLength, ReplyBytes);
+ for (i = 0; i < ReplyBytes; i++)
+ printk(" %02X", ((unsigned char *) ReplyData)[i]);
+ printk("\n");
+ }
+ /*
+ Return count of Reply Bytes, or -1 if the command was invalid.
+ */
+ BusLogic_CommandFailureReason = "Command Invalid";
+ return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : ReplyBytes);
+}
+
+
+/*
+ BusLogic_Failure prints a standardized error message for tests that are
+ executed before the SCSI Host is registered, 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_TracingOptions & 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
+ boards, 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_TracingOptions & BusLogic_TraceHardReset);
+ long TimeoutCounter = loops_per_sec >> 2;
+ 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 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 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_TracingOptions & 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_ModelAndRevision_T ModelAndRevision;
+ BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
+ BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
+ 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 Board Model and Revision command.
+ */
+ RequestedReplyLength = sizeof(ModelAndRevision);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelAndRevision,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &ModelAndRevision, sizeof(ModelAndRevision))
+ != sizeof(ModelAndRevision))
+ return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL AND REVISION");
+ /*
+ Issue the Inquire Firmware Version 3rd Digit command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
+ NULL, 0, &FirmwareVersion3rdDigit,
+ sizeof(FirmwareVersion3rdDigit))
+ != sizeof(FirmwareVersion3rdDigit))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+ /*
+ Issue the Inquire Firmware Version Letter command.
+ */
+ FirmwareVersionLetter = '\0';
+ if (BoardID.FirmwareVersion1stDigit >= '3')
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
+ NULL, 0, &FirmwareVersionLetter,
+ sizeof(FirmwareVersionLetter))
+ != sizeof(FirmwareVersionLetter))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+ /*
+ BusLogic Host Adapters can be identified by their model number and
+ the major version number of their firmware as follows:
+
+ 4.xx BusLogic "C" Series Host Adapters:
+ 946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+ 3.xx BusLogic "S" Series Host Adapters:
+ 747S/747D/757S/757D/445S/545S/542D
+ 542B/742A (revision H)
+ 2.xx BusLogic "A" Series Host Adapters:
+ 542B/742A (revision G and below)
+ 0.xx AMI FastDisk VLB BusLogic Clone Host Adapter
+ */
+ /*
+ Save the Model Name and Board Name in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->ModelName;
+ for (i = 0; i < sizeof(ModelAndRevision.Model); i++)
+ {
+ Character = ModelAndRevision.Model[i];
+ if (Character == ' ' || Character == '\0') break;
+ *TargetPointer++ = Character;
+ }
+ *TargetPointer++ = '\0';
+ strcpy(HostAdapter->BoardName, "BusLogic ");
+ strcat(HostAdapter->BoardName, HostAdapter->ModelName);
+ strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName);
+ /*
+ 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;
+ if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+ *TargetPointer++ = FirmwareVersionLetter;
+ *TargetPointer++ = '\0';
+ /*
+ Determine the IRQ Channel and save it in the Host Adapter structure.
+ */
+ 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;
+ /*
+ Determine the DMA Channel and save it in the Host Adapter structure.
+ */
+ 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;
+ /*
+ 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,
+ overriding the DMA Channel if it is inappropriate for the bus type.
+ */
+ if (ExtendedSetupInformation.BusType == 'A')
+ HostAdapter->BusType = BusLogic_ISA_Bus;
+ else
+ switch (HostAdapter->ModelName[0])
+ {
+ case '4':
+ HostAdapter->BusType = BusLogic_VESA_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ case '5':
+ HostAdapter->BusType = BusLogic_ISA_Bus;
+ break;
+ case '6':
+ HostAdapter->BusType = BusLogic_MCA_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ case '7':
+ HostAdapter->BusType = BusLogic_EISA_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ case '9':
+ HostAdapter->BusType = BusLogic_PCI_Bus;
+ HostAdapter->DMA_Channel = 0;
+ 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 "C"
+ Series boards, but Disconnect/Reconnect is always permitted on "S" and
+ "A" Series boards.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '4')
+ HostAdapter->DisconnectPermitted =
+ (SetupInformation.DisconnectPermittedID8to15 << 8)
+ | SetupInformation.DisconnectPermittedID0to7;
+ else HostAdapter->DisconnectPermitted = 0xFF;
+ /*
+ Save the Scatter Gather Limits, Level Triggered Interrupts flag,
+ Wide SCSI flag, and Differential 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.LevelTriggeredInterrupts)
+ HostAdapter->LevelTriggeredInterrupts = true;
+ if (ExtendedSetupInformation.HostWideSCSI)
+ {
+ HostAdapter->HostWideSCSI = true;
+ HostAdapter->MaxTargetIDs = 16;
+ HostAdapter->MaxLogicalUnits = 64;
+ }
+ else
+ {
+ HostAdapter->HostWideSCSI = false;
+ HostAdapter->MaxTargetIDs = 8;
+ HostAdapter->MaxLogicalUnits = 8;
+ }
+ HostAdapter->HostDifferentialSCSI =
+ ExtendedSetupInformation.HostDifferentialSCSI;
+ /*
+ 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;
+ /*
+ Select an appropriate value for Concurrency (Commands per Logical Unit)
+ either from a Command Line Entry, or based on whether this is an ISA
+ or non-ISA Host Adapter.
+ */
+ if (HostAdapter->CommandLineEntry != NULL &&
+ HostAdapter->CommandLineEntry->Concurrency > 0)
+ HostAdapter->Concurrency = HostAdapter->CommandLineEntry->Concurrency;
+ else if (HostAdapter->BusType == BusLogic_ISA_Bus)
+ HostAdapter->Concurrency = BusLogic_Concurrency_ISA;
+ else HostAdapter->Concurrency = BusLogic_Concurrency;
+ /*
+ 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 appropriate values for the Error Recovery Option array either from
+ a Command Line Entry, or using BusLogic_ErrorRecoveryDefault.
+ */
+ if (HostAdapter->CommandLineEntry != NULL)
+ memcpy(HostAdapter->ErrorRecoveryOption,
+ HostAdapter->CommandLineEntry->ErrorRecoveryOption,
+ sizeof(HostAdapter->ErrorRecoveryOption));
+ else memset(HostAdapter->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryDefault,
+ sizeof(HostAdapter->ErrorRecoveryOption));
+ /*
+ Tagged Queuing support is available and operates properly only on "C"
+ Series boards with firmware version 4.22 and above and on "S" Series
+ boards with firmware version 3.35 and above. Tagged Queuing is disabled
+ by default when the Concurrency value is 1 since queuing multiple commands
+ is not possible.
+ */
+ TaggedQueuingPermittedDefault = 0;
+ if (HostAdapter->Concurrency > 1)
+ switch (HostAdapter->FirmwareVersion[0])
+ {
+ case '4':
+ if (HostAdapter->FirmwareVersion[2] > '2' ||
+ (HostAdapter->FirmwareVersion[2] == '2' &&
+ HostAdapter->FirmwareVersion[3] >= '2'))
+ TaggedQueuingPermittedDefault = 0xFFFF;
+ break;
+ case '3':
+ if (HostAdapter->FirmwareVersion[2] > '3' ||
+ (HostAdapter->FirmwareVersion[2] == '3' &&
+ HostAdapter->FirmwareVersion[3] >= '5'))
+ TaggedQueuingPermittedDefault = 0xFFFF;
+ break;
+ }
+ /*
+ Combine the default Tagged Queuing permission based on the Host Adapter
+ firmware version and Concurrency 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 SCSI Host Adapter\n",
+ HostAdapter->HostNumber, HostAdapter->ModelName,
+ BusLogic_BusNames[HostAdapter->BusType],
+ (HostAdapter->HostWideSCSI ? " Wide" : ""),
+ (HostAdapter->HostDifferentialSCSI ? " Differential" : ""));
+ 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->LevelTriggeredInterrupts ? "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%lX, ", 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 segments, "
+ "Synchronous Initiation: %s\n", HostAdapter->HostNumber,
+ HostAdapter->HostAdapterScatterGatherLimit,
+ (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled"));
+ printk("scsi%d: SCSI Parity Checking: %s, "
+ "Extended Disk Translation: %s\n", HostAdapter->HostNumber,
+ (HostAdapter->ParityChecking ? "Enabled" : "Disabled"),
+ (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled"));
+ AllTargetsMask = (1 << HostAdapter->MaxTargetIDs) - 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->MaxTargetIDs; 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->MaxTargetIDs; TargetID++)
+ printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ printk("\n");
+ CommonErrorRecovery = true;
+ for (TargetID = 1; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ if (HostAdapter->ErrorRecoveryOption[TargetID] !=
+ HostAdapter->ErrorRecoveryOption[0])
+ {
+ CommonErrorRecovery = false;
+ break;
+ }
+ printk("scsi%d: Error Recovery: ", HostAdapter->HostNumber);
+ if (CommonErrorRecovery)
+ printk("%s", BusLogic_ErrorRecoveryOptions[
+ HostAdapter->ErrorRecoveryOption[0]]);
+ else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ printk("%s", BusLogic_ErrorRecoveryOptions2[
+ HostAdapter->ErrorRecoveryOption[TargetID]]);
+ printk(", Mailboxes: %d, Initial CCBs: %d\n",
+ BusLogic_MailboxCount, BusLogic_InitialCCBs);
+ printk("scsi%d: Driver Scatter/Gather Limit: %d segments, "
+ "Concurrency: %d\n", HostAdapter->HostNumber,
+ HostAdapter->DriverScatterGatherLimit, HostAdapter->Concurrency);
+ /*
+ Indicate reading the Host Adapter Configuration completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_AcquireResources acquires the system resources necessary to use Host
+ Adapter, and 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 locking.
+*/
+
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Host_T *Host)
+{
+ /*
+ 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 - 9]++ == 0)
+ {
+ if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
+ SA_INTERRUPT, HostAdapter->InterruptLabel) < 0)
+ {
+ BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]--;
+ 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) + 8
+ < 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->BoardName) < 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;
+ }
+ /*
+ Initialize necessary fields in the SCSI Host structure.
+ */
+ Host->max_id = HostAdapter->MaxTargetIDs;
+ Host->max_lun = HostAdapter->MaxLogicalUnits;
+ Host->max_channel = 0;
+ Host->this_id = HostAdapter->SCSI_ID;
+ Host->can_queue = BusLogic_MailboxCount;
+ Host->cmd_per_lun = HostAdapter->Concurrency;
+ Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
+ Host->unchecked_isa_dma = (HostAdapter->BusType == BusLogic_ISA_Bus);
+ /*
+ BusLogic 445S Host Adapters prior to board revision D 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. Since 16KB out of
+ each 16MB after the first is such a small amount of memory, this memory
+ can be marked as reserved without a significant loss of performance; this
+ is a much cheaper solution than requiring that ISA bounce buffers be used.
+ */
+ if (HostAdapter->BIOS_Address > 0 &&
+ strcmp(HostAdapter->ModelName, "445S") == 0)
+ {
+ Host->forbidden_addr = HostAdapter->BIOS_Address;
+ Host->forbidden_size = 16*1024;
+ }
+ /*
+ 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 - 9] == 0)
+ free_irq(HostAdapter->IRQ_Channel);
+ /*
+ 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)
+ {
+ printk("scsi%d: HOST ADAPTER INTERRUPT TEST FAILED - DETACHING\n",
+ HostAdapter->HostNumber);
+ printk("scsi%d: Interrupts are not getting through "
+ "from the Host Adapter to the\n", HostAdapter->HostNumber);
+ printk("scsi%d: BusLogic Driver Interrupt Handler. "
+ "The most likely cause is that\n", HostAdapter->HostNumber);
+ printk("scsi%d: either the Host Adapter or Motherboard "
+ "is configured incorrectly.\n", HostAdapter->HostNumber);
+ printk("scsi%d: Please check the Host Adapter configuration "
+ "with AutoSCSI or by\n", HostAdapter->HostNumber);
+ printk("scsi%d: examining any dip switch and jumper settings "
+ "on the Host Adapter, and\n", HostAdapter->HostNumber);
+ printk("scsi%d: verify that no other device is attempting to "
+ "use the same IRQ Channel.\n", HostAdapter->HostNumber);
+ printk("scsi%d: For PCI Host Adapters, it may also be necessary "
+ "to investigate and\n", HostAdapter->HostNumber);
+ printk("scsi%d: manually set the PCI interrupt assignments "
+ "and edge/level interrupt\n", HostAdapter->HostNumber);
+ printk("scsi%d: type selection in the BIOS Setup Program or "
+ "with Motherboard jumpers.\n", HostAdapter->HostNumber);
+ 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_WideModeCCBRequest_T WideModeCCBRequest;
+ BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+ /*
+ Initialize Read/Write Operation Count and Command Successful Flag
+ for each Target.
+ */
+ memset(HostAdapter->ReadWriteOperationCount, 0,
+ sizeof(HostAdapter->ReadWriteOperationCount));
+ memset(HostAdapter->CommandSuccessfulFlag, false,
+ sizeof(HostAdapter->CommandSuccessfulFlag));
+ /*
+ Initialize the Outgoing and Incoming Mailbox structures.
+ */
+ memset(HostAdapter->OutgoingMailboxes, 0,
+ sizeof(HostAdapter->OutgoingMailboxes));
+ memset(HostAdapter->IncomingMailboxes, 0,
+ sizeof(HostAdapter->IncomingMailboxes));
+ /*
+ Initialize the pointers to the First, Last, and Next Mailboxes.
+ */
+ HostAdapter->FirstOutgoingMailbox = &HostAdapter->OutgoingMailboxes[0];
+ HostAdapter->LastOutgoingMailbox =
+ &HostAdapter->OutgoingMailboxes[BusLogic_MailboxCount-1];
+ HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->FirstIncomingMailbox = &HostAdapter->IncomingMailboxes[0];
+ HostAdapter->LastIncomingMailbox =
+ &HostAdapter->IncomingMailboxes[BusLogic_MailboxCount-1];
+ HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ /*
+ Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+ */
+ ExtendedMailboxRequest.MailboxCount = BusLogic_MailboxCount;
+ ExtendedMailboxRequest.BaseMailboxAddress = HostAdapter->OutgoingMailboxes;
+ if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
+ &ExtendedMailboxRequest,
+ sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
+ {
+ printk("scsi%d: MAILBOX INITIALIZATION FAILED - DETACHING\n",
+ HostAdapter->HostNumber);
+ return false;
+ }
+ /*
+ 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. BusLogic indicates
+ that Strict Round Robin Mode is significantly more efficient.
+ */
+ RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+ BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
+ &RoundRobinModeRequest,
+ sizeof(RoundRobinModeRequest), NULL, 0);
+ /*
+ For Wide SCSI Host Adapters, issue the Enable Wide Mode CCB command to
+ allow more than 8 Logical Units per Target to be supported.
+ */
+ if (HostAdapter->HostWideSCSI)
+ {
+ WideModeCCBRequest = BusLogic_WideModeCCB;
+ if (BusLogic_Command(HostAdapter, BusLogic_EnableWideModeCCB,
+ &WideModeCCBRequest,
+ sizeof(WideModeCCBRequest), NULL, 0) < 0)
+ {
+ printk("scsi%d: ENABLE WIDE MODE CCB FAILED - DETACHING\n",
+ HostAdapter->HostNumber);
+ return false;
+ }
+ }
+ /*
+ 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)
+ {
+ printk("scsi%d: MODIFY I/O ADDRESS FAILED - DETACHING\n",
+ HostAdapter->HostNumber);
+ return false;
+ }
+ }
+ }
+ /*
+ Announce Successful Initialization.
+ */
+ printk("scsi%d: *** %s Initialized Successfully ***\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName);
+ /*
+ Indicate the Host Adapter Initialization completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_InquireTargetDevices inquires about the Target Devices accessible
+ through Host Adapter and reports on the results.
+*/
+
+static boolean BusLogic_InquireTargetDevices(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_InstalledDevices8_T InstalledDevicesID0to7;
+ BusLogic_InstalledDevices8_T InstalledDevicesID8to15;
+ 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);
+ /*
+ Issue the Inquire Installed Devices ID 0 to 7 command, and for Wide SCSI
+ Host Adapters the Inquire Installed Devices ID 8 to 15 command. This is
+ necessary to force Synchronous Transfer Negotiation so that the Inquire
+ Setup Information and Inquire Synchronous Period commands will return
+ valid data.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7,
+ NULL, 0, &InstalledDevicesID0to7,
+ sizeof(InstalledDevicesID0to7))
+ != sizeof(InstalledDevicesID0to7))
+ return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+ if (HostAdapter->HostWideSCSI)
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID8to15,
+ NULL, 0, &InstalledDevicesID8to15,
+ sizeof(InstalledDevicesID8to15))
+ != sizeof(InstalledDevicesID8to15))
+ return BusLogic_Failure(HostAdapter,
+ "INQUIRE INSTALLED DEVICES ID 8 TO 15");
+ /*
+ 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.
+ */
+ RequestedReplyLength = sizeof(SynchronousPeriod);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
+ &RequestedReplyLength, 1,
+ &SynchronousPeriod, sizeof(SynchronousPeriod))
+ != sizeof(SynchronousPeriod))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+ /*
+ Save the Installed Devices, Synchronous Values, and Synchronous Period
+ information in the Host Adapter structure.
+ */
+ memcpy(HostAdapter->InstalledDevices, InstalledDevicesID0to7,
+ sizeof(BusLogic_InstalledDevices8_T));
+ memcpy(HostAdapter->SynchronousValues,
+ SetupInformation.SynchronousValuesID0to7,
+ sizeof(BusLogic_SynchronousValues8_T));
+ if (HostAdapter->HostWideSCSI)
+ {
+ memcpy(&HostAdapter->InstalledDevices[8], InstalledDevicesID8to15,
+ sizeof(BusLogic_InstalledDevices8_T));
+ memcpy(&HostAdapter->SynchronousValues[8],
+ SetupInformation.SynchronousValuesID8to15,
+ sizeof(BusLogic_SynchronousValues8_T));
+ }
+ memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod,
+ sizeof(BusLogic_SynchronousPeriod_T));
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ if (HostAdapter->InstalledDevices[TargetID] != 0)
+ {
+ 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/sec, 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/sec, 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_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;
+ 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;
+ /*
+ 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 CCBs, and finally
+ Initialize the Host Adapter.
+ */
+ if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+ BusLogic_AcquireResources(HostAdapter, Host) &&
+ BusLogic_TestInterrupts(HostAdapter) &&
+ BusLogic_CreateCCBs(HostAdapter) &&
+ BusLogic_InitializeHostAdapter(HostAdapter) &&
+ BusLogic_InquireTargetDevices(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.
+ */
+ release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+ request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+ HostAdapter->BoardName);
+ BusLogicHostAdapterCount++;
+ }
+ else
+ {
+ /*
+ An error occurred during Host Adapter Configuration Querying,
+ Resource Acquisition, Interrupt Testing, CCB Creation, or Host
+ Adapter Initialization, so remove Host Adapter from the list of
+ registered BusLogic Host Adapters, destroy the CCBs, Release
+ the System Resources, and Unregister the SCSI Host.
+ */
+ BusLogic_DestroyCCBs(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 release any system resources acquired to use
+ Host Adapter.
+ */
+ BusLogic_DestroyCCBs(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,
+ Registers_T *InterruptRegisters)
+{
+ BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL;
+ BusLogic_HostAdapter_T *HostAdapter;
+ int HostAdapterResetPendingCount = 0;
+ /*
+ Iterate over the installed BusLogic Host Adapters accepting any Incoming
+ Mailbox entries and saving the completed CCBs for processing. This
+ interrupt handler is installed with SA_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_LockHostAdapterID(HostAdapter);
+ /*
+ 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->HostAdapterResetPending = true;
+ HostAdapterResetPendingCount++;
+ }
+ 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,
+ completion processing is performed exactly once. Therefore,
+ only Incoming Mailbox entries 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 entry 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;
+ while (NextIncomingMailbox->CompletionCode !=
+ BusLogic_IncomingMailboxFree)
+ {
+ BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB;
+ BusLogic_CompletionCode_T MailboxCompletionCode =
+ NextIncomingMailbox->CompletionCode;
+ if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
+ {
+ /*
+ 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;
+ }
+ }
+ else printk("scsi%d: Aborted CCB #%d Not Found\n",
+ HostAdapter->HostNumber, CCB->SerialNumber);
+ 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_UnlockHostAdapterID(HostAdapter);
+ }
+ /*
+ Enable interrupts while the completed CCBs are processed.
+ */
+ sti();
+ /*
+ Iterate over the Host Adapters performing any pending Host Adapter Resets.
+ */
+ if (HostAdapterResetPendingCount > 0)
+ for (HostAdapter = BusLogic_RegisteredHostAdapters;
+ HostAdapter != NULL;
+ HostAdapter = HostAdapter->Next)
+ if (HostAdapter->HostAdapterResetPending)
+ {
+ BusLogic_ResetHostAdapter(HostAdapter, NULL);
+ HostAdapter->HostAdapterResetPending = false;
+ scsi_mark_host_bus_reset(HostAdapter->SCSI_Host);
+ }
+ /*
+ 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;
+ /*
+ Bus Device Reset CCBs have the Command field non-NULL only when a Bus
+ Device Reset was requested for a command that was not currently active
+ in the Host Adapter, and hence would not have its Completion Routine
+ called otherwise.
+ */
+ if (CCB->Opcode == BusLogic_SCSIBusDeviceReset)
+ {
+ printk("scsi%d: Bus Device Reset CCB #%d to Target %d Completed\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ if (Command != NULL) Command->result = DID_RESET << 16;
+ }
+ 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 #%d Impossible State\n",
+ HostAdapter->HostNumber, CCB->SerialNumber);
+ break;
+ case BusLogic_CommandCompletedWithoutError:
+ HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true;
+ Command->result = DID_OK << 16;
+ break;
+ case BusLogic_CommandAbortedAtHostRequest:
+ printk("scsi%d: CCB #%d Aborted\n",
+ HostAdapter->HostNumber, CCB->SerialNumber);
+ Command->result = DID_ABORT << 16;
+ break;
+ case BusLogic_CommandCompletedWithError:
+ Command->result =
+ BusLogic_ComputeResultCode(CCB->HostAdapterStatus,
+ CCB->TargetDeviceStatus);
+ if (BusLogic_TracingOptions & BusLogic_TraceErrors)
+ if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
+ {
+ int i;
+ printk("scsi%d: CCB #%d 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", (*CCB->SenseDataPointer)[i]);
+ printk("\n");
+ }
+ break;
+ }
+ /*
+ Place CCB back on the Host Adapter's free list.
+ */
+ BusLogic_DeallocateCCB(CCB);
+ /*
+ Call the SCSI Command Completion Routine if appropriate.
+ */
+ if (Command != NULL) Command->scsi_done(Command);
+ }
+}
+
+
+/*
+ BusLogic_WriteOutgoingMailboxEntry writes an Outgoing Mailbox entry
+ for Host Adapter with Action Code and CCB.
+*/
+
+static boolean BusLogic_WriteOutgoingMailboxEntry(BusLogic_HostAdapter_T
+ *HostAdapter,
+ BusLogic_ActionCode_T
+ ActionCode,
+ BusLogic_CCB_T *CCB)
+{
+ BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+ boolean Result = false;
+ BusLogic_LockHostAdapter(HostAdapter);
+ NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
+ if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree)
+ {
+ NextOutgoingMailbox->ActionCode = ActionCode;
+ NextOutgoingMailbox->CCB = CCB;
+ CCB->Status = BusLogic_CCB_Active;
+ BusLogic_StartMailboxScan(HostAdapter);
+ if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
+ NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+ Result = true;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return Result;
+}
+
+
+/*
+ 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;
+ unsigned char CDB_Length = Command->cmd_len;
+ unsigned char TargetID = Command->target;
+ unsigned char LogicalUnit = Command->lun;
+ void *BufferPointer = Command->request_buffer;
+ int BufferLength = Command->request_bufflen;
+ int SegmentCount = Command->use_sg;
+ BusLogic_CCB_T *CCB;
+ long EnableTQ;
+ /*
+ 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;
+ }
+ /*
+ Allocate a CCB from the Host Adapter's free list, aborting the command
+ with an error if there are none available and memory allocation fails.
+ */
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL)
+ {
+ Command->result = DID_ERROR << 16;
+ CompletionRoutine(Command);
+ return 0;
+ }
+ /*
+ Initialize the fields in the BusLogic Command Control Block (CCB).
+ */
+ if (SegmentCount == 0)
+ {
+ CCB->Opcode = BusLogic_InitiatorCCB;
+ CCB->DataLength = BufferLength;
+ CCB->DataPointer = 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 = CCB->ScatterGatherList;
+ for (Segment = 0; Segment < SegmentCount; Segment++)
+ {
+ CCB->ScatterGatherList[Segment].SegmentByteCount =
+ ScatterList[Segment].length;
+ CCB->ScatterGatherList[Segment].SegmentDataPointer =
+ ScatterList[Segment].address;
+ }
+ }
+ switch (CDB[0])
+ {
+ case READ_6:
+ case READ_10:
+ CCB->DataDirection = BusLogic_DataInLengthChecked;
+ HostAdapter->ReadWriteOperationCount[TargetID]++;
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ CCB->DataDirection = BusLogic_DataOutLengthChecked;
+ HostAdapter->ReadWriteOperationCount[TargetID]++;
+ break;
+ default:
+ CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+ break;
+ }
+ CCB->CDB_Length = CDB_Length;
+ CCB->SenseDataLength = sizeof(Command->sense_buffer);
+ CCB->TargetID = TargetID;
+ CCB->LogicalUnit = LogicalUnit;
+ /*
+ For Wide SCSI Host Adapters, Wide Mode CCBs are used to support more than
+ 8 Logical Units per Target, and this requires setting the overloaded
+ TagEnable field to Logical Unit bit 5.
+ */
+ if (HostAdapter->HostWideSCSI)
+ {
+ CCB->TagEnable = LogicalUnit >> 5;
+ CCB->WideModeTagEnable = false;
+ }
+ else CCB->TagEnable = false;
+ /*
+ BusLogic recommends that after a Reset the first couple of commands that
+ are sent to a Target be sent in a non Tagged Queue fashion so that the Host
+ Adapter and Target can establish Synchronous Transfer before Queue Tag
+ messages can interfere with the Synchronous Negotiation message.
+ */
+ if ((HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
+ Command->device->tagged_supported &&
+ (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 5) >= 0)
+ {
+ if (EnableTQ == 0)
+ printk("scsi%d: Tagged Queuing now active for Target %d\n",
+ HostAdapter->HostNumber, TargetID);
+ if (HostAdapter->HostWideSCSI)
+ {
+ CCB->WideModeTagEnable = true;
+ CCB->WideModeQueueTag = BusLogic_SimpleQueueTag;
+ }
+ else
+ {
+ CCB->TagEnable = true;
+ CCB->QueueTag = BusLogic_SimpleQueueTag;
+ }
+ }
+ memcpy(CCB->CDB, CDB, CDB_Length);
+ CCB->SenseDataPointer = (SCSI_SenseData_T *) &Command->sense_buffer;
+ CCB->Command = Command;
+ Command->scsi_done = CompletionRoutine;
+ /*
+ Place the CCB in an Outgoing Mailbox, aborting the command with an
+ error if there are none available.
+ */
+ if (!(BusLogic_WriteOutgoingMailboxEntry(
+ HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+ {
+ printk("scsi%d: cannot write Outgoing Mailbox Entry\n",
+ HostAdapter->HostNumber);
+ BusLogic_DeallocateCCB(CCB);
+ Command->result = DID_ERROR << 16;
+ CompletionRoutine(Command);
+ }
+ 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;
+ unsigned char InterruptRegister;
+ BusLogic_CCB_T *CCB;
+ int Result;
+ /*
+ If the Host Adapter has posted an interrupt but the Interrupt Handler
+ has not been called for some reason (i.e. the interrupt was lost), try
+ calling the Interrupt Handler directly to process the commands that
+ have been completed.
+ */
+ InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+ if (InterruptRegister & BusLogic_InterruptValid)
+ {
+ unsigned long ProcessorFlags;
+ printk("scsi%d: Recovering Lost Interrupt for IRQ Channel %d\n",
+ HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+ save_flags(ProcessorFlags);
+ cli();
+ BusLogic_InterruptHandler(HostAdapter->IRQ_Channel, NULL);
+ restore_flags(ProcessorFlags);
+ return SCSI_ABORT_SNOOZE;
+ }
+ /*
+ Find the CCB to be aborted and determine how to proceed.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ Result = SCSI_ABORT_NOT_RUNNING;
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Command == Command)
+ {
+ if (CCB->Status == BusLogic_CCB_Active)
+ if ((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable &&
+ CCB->WideModeQueueTag != BusLogic_SimpleQueueTag) ||
+ (!HostAdapter->HostWideSCSI && CCB->TagEnable &&
+ CCB->QueueTag != BusLogic_SimpleQueueTag))
+ {
+ /*
+ CCBs using Tagged Queuing with other than Simple Queue Tag
+ should not be aborted.
+ */
+ Result = SCSI_ABORT_BUSY;
+ }
+ else
+ {
+ /*
+ Attempt to abort the CCB.
+ */
+ if (BusLogic_WriteOutgoingMailboxEntry(
+ HostAdapter, BusLogic_MailboxAbortCommand, CCB))
+ {
+ printk("scsi%d: Aborting CCB #%d\n",
+ HostAdapter->HostNumber, CCB->SerialNumber);
+ Result = SCSI_ABORT_PENDING;
+ }
+ else Result = SCSI_ABORT_BUSY;
+ }
+ break;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return Result;
+}
+
+
+/*
+ BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+ currently executing SCSI commands as having been reset, as well as
+ the specified Command if non-NULL.
+*/
+
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Command_T *Command)
+{
+ BusLogic_CCB_T *CCB;
+ if (Command == NULL)
+ printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName);
+ else printk("scsi%d: Resetting %s due to Target %d\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName, Command->target);
+ /*
+ Attempt to Reset and Reinitialize the Host Adapter.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ if (!(BusLogic_HardResetHostAdapter(HostAdapter) &&
+ BusLogic_InitializeHostAdapter(HostAdapter)))
+ {
+ printk("scsi%d: Resetting %s Failed\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName);
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return SCSI_RESET_ERROR;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ /*
+ 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);
+ /*
+ 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)
+ {
+ SCSI_Command_T *ActiveCommand = CCB->Command;
+ if (ActiveCommand == Command) Command = NULL;
+ BusLogic_DeallocateCCB(CCB);
+ if (ActiveCommand != NULL)
+ {
+ ActiveCommand->result = DID_RESET << 16;
+ ActiveCommand->scsi_done(ActiveCommand);
+ }
+ }
+ if (Command != NULL)
+ {
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ }
+ return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+}
+
+
+/*
+ BusLogic_BusDeviceReset sends a Bus Device Reset to the Target
+ associated with Command.
+*/
+
+static int BusLogic_BusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Command_T *Command)
+{
+ BusLogic_CCB_T *CCB = BusLogic_AllocateCCB(HostAdapter), *XCCB;
+ unsigned char TargetID = Command->target;
+ /*
+ If sending a Bus Device Reset is impossible, attempt a full Host
+ Adapter Hard Reset and SCSI Bus Reset.
+ */
+ if (CCB == NULL)
+ return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ printk("scsi%d: Sending Bus Device Reset CCB #%d to Target %d\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
+ CCB->Opcode = BusLogic_SCSIBusDeviceReset;
+ CCB->TargetID = TargetID;
+ CCB->Command = Command;
+ /*
+ If there is a currently executing CCB in the Host Adapter for this Command,
+ then an Incoming Mailbox entry will be made with a completion code of
+ BusLogic_HostAdapterAssertedBusDeviceReset. Otherwise, the CCB Command
+ field will be left pointing to the Command so that the interrupt for the
+ completion of the Bus Device Reset can call the Completion Routine for the
+ Command.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
+ if (XCCB->Status == BusLogic_CCB_Active && XCCB->Command == Command)
+ {
+ CCB->Command = NULL;
+ break;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ /*
+ 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_WriteOutgoingMailboxEntry(
+ HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+ {
+ printk("scsi%d: cannot write Outgoing Mailbox Entry for "
+ "Bus Device Reset\n", HostAdapter->HostNumber);
+ BusLogic_DeallocateCCB(CCB);
+ return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ }
+ HostAdapter->ReadWriteOperationCount[TargetID] = 0;
+ return SCSI_RESET_PENDING;
+}
+
+
+/*
+ BusLogic_ResetCommand takes appropriate action to reset Command.
+*/
+
+int BusLogic_ResetCommand(SCSI_Command_T *Command)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ unsigned char TargetID = Command->target;
+ unsigned char ErrorRecoveryOption =
+ HostAdapter->ErrorRecoveryOption[TargetID];
+ if (ErrorRecoveryOption == BusLogic_ErrorRecoveryDefault)
+ if (Command->host->suggest_bus_reset)
+ ErrorRecoveryOption = BusLogic_ErrorRecoveryHardReset;
+ else ErrorRecoveryOption = BusLogic_ErrorRecoveryBusDeviceReset;
+ switch (ErrorRecoveryOption)
+ {
+ case BusLogic_ErrorRecoveryHardReset:
+ return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ case BusLogic_ErrorRecoveryBusDeviceReset:
+ if (HostAdapter->CommandSuccessfulFlag[TargetID])
+ {
+ HostAdapter->CommandSuccessfulFlag[TargetID] = false;
+ return BusLogic_BusDeviceReset(HostAdapter, Command);
+ }
+ else return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ }
+ printk("scsi%d: Error Recovery Suppressed\n", HostAdapter->HostNumber);
+ 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 "C" Series boards or by a dip switch setting
+ on older boards. 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 between 2 GB inclusive and 8 GB exclusive are given a
+ disk geometry of 255 heads and 63 sectors. On "C" Series boards the firmware
+ can be queried for the precise translation in effect for each drive
+ individually, but there is really no need to do so since we know the total
+ capacity of the drive and whether Extended Translation is enabled, hence we
+ can deduce the BIOS disk geometry that must be in effect.
+*/
+
+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;
+ 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);
+ 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 number of Concurrent Commands per Logical
+ Unit to allow for Target Devices on the Host Adapter. If unspecified, it
+ defaults to 0 which means to use the value of BusLogic_Concurrency for
+ non-ISA Host Adapters, or BusLogic_Concurrency_ISA for ISA Host Adapters.
+
+ 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 Tracing Options. If unspecified, it
+ defaults to 0 which means that no special tracing information is to be
+ printed. Note that Tracing Options are applied across all Host Adapters.
+
+ The string options are used to provide control over Tagged Queuing and Error
+ Recovery. If both Tagged Queuing and Error Recovery strings are provided, the
+ Tagged Queuing specification string must come first.
+
+ 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 Concurrency 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 0, the second to Target 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 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
+ 0, the second to Target 1, and so on; if the sequence
+ of "D", "H", "B", and "N" characters does not cover all
+ the Target Devices, unspecified characters are assumed
+ to be "D".
+*/
+
+void BusLogic_Setup(char *Strings, int *Integers)
+{
+ BusLogic_CommandLineEntry_T *CommandLineEntry =
+ &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++];
+ static int ProbeListIndex = 0;
+ int IntegerCount = Integers[0], TargetID, i;
+ CommandLineEntry->IO_Address = 0;
+ CommandLineEntry->Concurrency = 0;
+ CommandLineEntry->BusSettleTime = 0;
+ CommandLineEntry->TaggedQueuingPermitted = 0;
+ CommandLineEntry->TaggedQueuingPermittedMask = 0;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryDefault,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ if (IntegerCount > 4)
+ printk("BusLogic: Unexpected Command Line Integers ignored\n");
+ if (IntegerCount >= 1)
+ {
+ unsigned short 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 == 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 Concurrency = Integers[2];
+ if (Concurrency > BusLogic_MailboxCount)
+ {
+ printk("BusLogic: Invalid Command Line Entry "
+ "(illegal Concurrency %d)\n", Concurrency);
+ return;
+ }
+ CommandLineEntry->Concurrency = Concurrency;
+ }
+ if (IntegerCount >= 3)
+ CommandLineEntry->BusSettleTime = Integers[3];
+ if (IntegerCount >= 4)
+ BusLogic_TracingOptions |= Integers[4];
+ 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;
+ 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_MaxTargetIDs; 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_MaxTargetIDs;
+ break;
+ }
+ }
+ if (*Strings == ',') Strings++;
+ 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->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryHardReset,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ }
+ else if (strncmp(Strings, "BusDeviceReset", 14) == 0)
+ {
+ Strings += 14;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryBusDeviceReset,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ }
+ else if (strncmp(Strings, "None", 4) == 0)
+ {
+ Strings += 4;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryNone,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ }
+ else
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+ switch (*Strings++)
+ {
+ case 'D':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryDefault;
+ break;
+ case 'H':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryHardReset;
+ break;
+ case 'B':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryBusDeviceReset;
+ break;
+ case 'N':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryNone;
+ break;
+ default:
+ Strings--;
+ TargetID = BusLogic_MaxTargetIDs;
+ break;
+ }
+ }
+ if (*Strings != '\0')
+ printk("BusLogic: Unexpected Command Line String '%s' ignored\n", Strings);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this