Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
- Version 2.2.10 for Linux 2.2.18
- Version 2.4.10 for Linux 2.4.1
+ Version 2.2.11 for Linux 2.2.19
+ Version 2.4.11 for Linux 2.4.12
PRODUCTION RELEASE
- 1 February 2001
+ 11 October 2001
Leonard N. Zubkoff
Dandelion Digital
controllers. Mylex Corporation is located at 34551 Ardenwood Blvd., Fremont,
California 94555, USA and can be reached at 510.796.6100 or on the World Wide
Web at http://www.mylex.com. Mylex Technical Support can be reached by
-electronic mail at support@mylex.com, by voice at 510.608.2400, or by FAX at
+electronic mail at mylexsup@us.ibm.com, by voice at 510.608.2400, or by FAX at
510.745.7715. Contact information for offices in Europe and Japan is available
on their Web site.
100MHz Intel i960RM RISC Processor
16MB/32MB/64MB ECC SDRAM Memory
-AcceleRAID 160
+AcceleRAID 160 (AcceleRAID 170LP)
1 Wide Ultra-160 LVD SCSI channel
100MHz Intel i960RS RISC Processor
Built in 16M ECC SDRAM Memory
Intel i960 RISC Processor
2MB/4MB/8MB/16MB/32MB DRAM Memory
+DAC960P 1/2/3 Wide Fast SCSI-2 Channels
+ Intel i960 RISC Processor
+ 2MB/4MB/8MB/16MB/32MB DRAM Memory
+
For the eXtremeRAID 2000/3000 and AcceleRAID 352/170/160, firmware version
6.00-01 or above is required.
For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required.
-For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above
-is required.
-
-Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers
-were delivered with version 2.xx firmware. Version 2.xx firmware is not
-supported by this driver and no support is envisioned. Contact Mylex RAID
-Technical Support to inquire about upgrading controllers with version 2.xx
-firmware to version 3.51-0-04. Upgrading to version 3.xx firmware requires
-installation of higher capacity Flash ROM chips, and not all DAC960PD and
-DAC960PL controllers can be upgraded.
+For the DAC960PU, DAC960PD, DAC960PL, and DAC960P, either firmware version
+3.51-0-04 or above is required (for dual Flash ROM controllers), or firmware
+version 2.73-0-00 or above is required (for single Flash ROM controllers)
Please note that not all SCSI disk drives are suitable for use with DAC960
controllers, and only particular firmware versions of any given model may
actually function correctly. Similarly, not all motherboards have a BIOS that
properly initializes the AcceleRAID 250, AcceleRAID 200, AcceleRAID 150,
DAC960PJ, and DAC960PG because the Intel i960RD/RP is a multi-function device.
-If in doubt, contact Mylex RAID Technical Support (support@mylex.com) to verify
-compatibility. Mylex makes available a hard disk compatibility list at
+If in doubt, contact Mylex RAID Technical Support (mylexsup@us.ibm.com) to
+verify compatibility. Mylex makes available a hard disk compatibility list at
http://www.mylex.com/support/hdcomp/hd-lists.html.
DRIVER INSTALLATION
-This distribution was prepared for Linux kernel version 2.2.18 or 2.4.1.
+This distribution was prepared for Linux kernel version 2.2.19 or 2.4.12.
To install the DAC960 RAID driver, you may use the following commands,
replacing "/usr/src" with wherever you keep your Linux kernel source tree:
cd /usr/src
- tar -xvzf DAC960-2.2.10.tar.gz (or DAC960-2.4.10.tar.gz)
+ tar -xvzf DAC960-2.2.11.tar.gz (or DAC960-2.4.11.tar.gz)
mv README.DAC960 linux/Documentation
mv DAC960.[ch] linux/drivers/block
patch -p0 < DAC960.patch (if DAC960.patch is included)
The "make-online" command changes the physical drive <channel>:<target-id>
from status DEAD to status ONLINE. In cases where multiple physical drives
- have been killed simultaneously, this command may be used to bring them
- back online, after which a consistency check is advisable.
+ have been killed simultaneously, this command may be used to bring all but
+ one of them back online, after which a rebuild to the final drive is
+ necessary.
Warning: make-online should only be used on a dead physical drive that is
- an active part of a drive group, never on a standby drive.
+ an active part of a drive group, never on a standby drive. The command
+ should never be used on a dead drive that is part of a critical logical
+ drive; rebuild should be used if only a single drive is dead.
make-standby <channel>:<target-id>
Release Notes For Linux Kernel 2.2 and higher.
These notes are for the drivers which have already been integrated into the
-kernel and have been tested on Linux kernels 2.0, 2.2, and 2.3.
+kernel and have been tested on Linux kernels 2.0, 2.2, 2.3, and 2.4.
-Version: 1.2.9
-Date: 04/12/2000
-Author: Andrew Manison <amanison@america.net>
+Version: 1.2.11
+Date: 10/21/2001
+Historical Author: Andrew Manison <amanison@america.net>
+Primary Author: Doug McNash <dougm@computone.com>
Testing: larryg@computone.com
Support: support@computone.com
-Fixes and Updates: Doug McNash <dougm@computone.com>
-Proc Filesystem and Kernel Integration: Mike Warfield <mhw@wittsend.com>
-
+Fixes and Updates: Mike Warfield <mhw@wittsend.com>
This file assumes that you are using the Computone drivers which are
integrated into the kernel sources. For updating the drivers or installing
before or after drivers installation.
Note the hardware address from the Computone ISA cards installed into
- the system. These are required for editing ip2.h or editing
+ the system. These are required for editing ip2.c or editing
/etc/modules.conf, or for specification on the modprobe
command line.
Select (m) module for CONFIG_COMPUTONE under character
devices. CONFIG_PCI and CONFIG_MODULES also may need to be set.
c) Set address on ISA cards then:
- edit /usr/src/linux/drivers/char/ip2/ip2.h if needed
+ edit /usr/src/linux/drivers/char/ip2.c if needed
or
edit /etc/modules.conf if needed (module).
or both to match this setting.
Select (y) kernel for CONFIG_COMPUTONE under character
devices. CONFIG_PCI may need to be set if you have PCI bus.
c) Set address on ISA cards then:
- edit /usr/src/linux/drivers/char/ip2/ip2.h
+ edit /usr/src/linux/drivers/char/ip2.c
+ (Optional - may be specified on kernel command line now)
d) Run "make dep"
e) Run "make zImage" or whatever target you prefer.
f) mv /usr/src/linux/arch/i386/boot/zImage to /boot.
h) Reboot using this kernel
i) run ip2mkdev (either the script below or the binary version)
+Kernel command line options:
+
+When compiling the driver into the kernel, io and irq may be
+compiled into the driver by editing ip2.c and setting the values for
+io and irq in the appropriate array. An alternative is to specify
+a command line parameter to the kernel at boot up.
+
+ ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
+
+Note that this order is very different from the specifications for the
+modload parameters which have separate IRQ and IO specifiers.
+
+The io port also selects PCI (1) and EISA (2) boards.
+
+ io=0 No board
+ io=1 PCI board
+ io=2 EISA board
+ else ISA board io address
+
+You only need to specify the boards which are present.
+
+ Examples:
+
+ 2 PCI boards:
+
+ ip2=1,0,1,0
+
+ 1 ISA board at 0x310 irq 5:
+
+ ip2=0x310,5
+
+This can be added to and "append" option in lilo.conf similar to this:
+
+ append="ip2=1,0,1,0"
+
3. INSTALLATION
The driver can be installed as a module (recommended) or built into the
kernel. This is selected as for other drivers through the `make config`
command from the root of the Linux source tree. If the driver is built
-into the kernel you will need to edit the file ip2.h to match the boards
+into the kernel you will need to edit the file ip2.c to match the boards
you are installing. See that file for instructions. If the driver is
installed as a module the configuration can also be specified on the
modprobe command line as follows:
where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11,
12,15) and addr1-4 are the base addresses for up to four controllers. If
-the irqs are not specified the driver uses the default in ip2/ip2.h (which
+the irqs are not specified the driver uses the default in ip2.c (which
selects polled mode). If no base addresses are specified the defaults in
-ip2.h are used. If you are autoloading the driver module with kerneld or
-kmod the base addresses and interrupt number must also be set in ip2/ip2.h
+ip2.c are used. If you are autoloading the driver module with kerneld or
+kmod the base addresses and interrupt number must also be set in ip2.c
and recompile or just insert and options line in /etc/modules.conf or both.
The options line is equivalent to the command line and takes precidence over
-what is in ip2.h.
+what is in ip2.c.
/etc/modules.conf sample:
options ip2 io=1,0x328 irq=1,10
alias char-major-72 ip2
alias char-major-73 ip2
-equivelant ip2.h:
-static ip2config_t ip2config =
-{
- {1,10,0,0},
- {
- 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */
- 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */
- 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */
- 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */
- }
-};
+The equivalent in ip2.c:
+
+static int io[IP2_MAX_BOARDS]= { 1, 0x328, 0, 0 };
+static int irq[IP2_MAX_BOARDS] = { 1, 10, -1, -1 };
+
+The equivalent for the kernel command line (in lilo.conf):
+
+ append="ip2=1,1,0x328,10"
Note: Both io and irq should be updated to reflect YOUR system. An "io"
- address of "1/2" indicates a PCI/EISA card in the board table. The
- PCI or EISA irq will be assigned automatically.
+ address of 1 or 2 indicates a PCI or EISA card in the board table. The PCI or EISA irq will be assigned automatically.
Specifying an invalid or in-use irq will default the driver into
running in polled mode for that card. If all irq entries are 0 then
S: Supported
COMPUTONE INTELLIPORT MULTIPORT CARD
-P: Doug McNash
P: Michael H. Warfield
-M: Doug McNash <dmcnash@computone.com>
M: Michael H. Warfield <mhw@wittsend.com>
W: http://www.computone.com/
W: http://www.wittsend.com/computone.html
VERSION = 2
PATCHLEVEL = 2
SUBLEVEL = 20
-EXTRAVERSION = pre11
+EXTRAVERSION = pre12
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
*/
-#define DAC960_DriverVersion "2.2.10"
-#define DAC960_DriverDate "1 February 2001"
+#define DAC960_DriverVersion "2.2.11"
+#define DAC960_DriverDate "11 October 2001"
#include <linux/version.h>
*/
static NotifierBlock_T
- DAC960_NotifierBlock = { DAC960_Finalize, NULL, 0 };
+ DAC960_NotifierBlock = { DAC960_Notifier, NULL, 0 };
/*
/*
DAC960_AllocateCommand allocates a Command structure from Controller's
- free list.
+ free list. During driver initialization, a special initialization command
+ has been placed on the free list to guarantee that command allocation can
+ never fail.
*/
static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T
}
+/*
+ DAC960_P_QueueCommand queues Command for DAC960 P Series Controllers.
+*/
+
+static void DAC960_P_QueueCommand(DAC960_Command_T *Command)
+{
+ DAC960_Controller_T *Controller = Command->Controller;
+ void *ControllerBaseAddress = Controller->BaseAddress;
+ DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+ CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+ switch (CommandMailbox->Common.CommandOpcode)
+ {
+ case DAC960_V1_Enquiry:
+ CommandMailbox->Common.CommandOpcode = DAC960_V1_Enquiry_Old;
+ break;
+ case DAC960_V1_GetDeviceState:
+ CommandMailbox->Common.CommandOpcode = DAC960_V1_GetDeviceState_Old;
+ break;
+ case DAC960_V1_Read:
+ CommandMailbox->Common.CommandOpcode = DAC960_V1_Read_Old;
+ DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ case DAC960_V1_Write:
+ CommandMailbox->Common.CommandOpcode = DAC960_V1_Write_Old;
+ DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ case DAC960_V1_ReadWithScatterGather:
+ CommandMailbox->Common.CommandOpcode =
+ DAC960_V1_ReadWithScatterGather_Old;
+ DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ case DAC960_V1_WriteWithScatterGather:
+ CommandMailbox->Common.CommandOpcode =
+ DAC960_V1_WriteWithScatterGather_Old;
+ DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ default:
+ break;
+ }
+ while (DAC960_PD_MailboxFullP(ControllerBaseAddress))
+ udelay(1);
+ DAC960_PD_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+ DAC960_PD_NewCommand(ControllerBaseAddress);
+}
+
+
/*
DAC960_ExecuteCommand executes Command and waits for completion.
*/
}
+/*
+ DAC960_V1_ExecuteTypeB executes a DAC960 V1 Firmware Controller Type 3B
+ Command and waits for completion. It returns true on success and false
+ on failure.
+*/
+
+static boolean DAC960_V1_ExecuteType3B(DAC960_Controller_T *Controller,
+ DAC960_V1_CommandOpcode_T CommandOpcode,
+ unsigned char CommandOpcode2,
+ void *DataPointer)
+{
+ DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+ DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+ DAC960_V1_CommandStatus_T CommandStatus;
+ DAC960_V1_ClearCommand(Command);
+ Command->CommandType = DAC960_ImmediateCommand;
+ CommandMailbox->Type3B.CommandOpcode = CommandOpcode;
+ CommandMailbox->Type3B.CommandOpcode2 = CommandOpcode2;
+ CommandMailbox->Type3B.BusAddress = Virtual_to_Bus32(DataPointer);
+ DAC960_ExecuteCommand(Command);
+ CommandStatus = Command->V1.CommandStatus;
+ DAC960_DeallocateCommand(Command);
+ return (CommandStatus == DAC960_V1_NormalCompletion);
+}
+
+
/*
DAC960_V1_ExecuteType3D executes a DAC960 V1 Firmware Controller Type 3D
Command and waits for completion. It returns true on success and false
DAC1164P 5.06 and above
DAC960PTL/PRL/PJ/PG 4.06 and above
DAC960PU/PD/PL 3.51 and above
+ DAC960PU/PD/PL/P 2.73 and above
*/
+ if (Enquiry2.FirmwareID.MajorVersion == 0)
+ {
+ Enquiry2.FirmwareID.MajorVersion =
+ Controller->V1.Enquiry.MajorFirmwareVersion;
+ Enquiry2.FirmwareID.MinorVersion =
+ Controller->V1.Enquiry.MinorFirmwareVersion;
+ Enquiry2.FirmwareID.FirmwareType = '0';
+ Enquiry2.FirmwareID.TurnID = 0;
+ }
sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d",
Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion,
Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID);
(Controller->FirmwareVersion[0] == '4' &&
strcmp(Controller->FirmwareVersion, "4.06") >= 0) ||
(Controller->FirmwareVersion[0] == '3' &&
- strcmp(Controller->FirmwareVersion, "3.51") >= 0)))
+ strcmp(Controller->FirmwareVersion, "3.51") >= 0) ||
+ (Controller->FirmwareVersion[0] == '2' &&
+ strcmp(Controller->FirmwareVersion, "2.73") >= 0)))
{
DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
DAC960_Error("Firmware Version = '%s'\n", Controller,
default:
return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
}
+ /*
+ Initialize the Background Initialization Status.
+ */
+ if ((Controller->FirmwareVersion[0] == '4' &&
+ strcmp(Controller->FirmwareVersion, "4.08") >= 0) ||
+ (Controller->FirmwareVersion[0] == '5' &&
+ strcmp(Controller->FirmwareVersion, "5.08") >= 0))
+ {
+ Controller->V1.BackgroundInitializationStatusSupported = true;
+ DAC960_V1_ExecuteType3B(Controller,
+ DAC960_V1_BackgroundInitializationControl, 0x20,
+ &Controller->
+ V1.LastBackgroundInitializationStatus);
+ }
/*
Initialize the Logical Drive Initially Accessible flag.
*/
DeviceState->DeviceType == DAC960_V1_DiskType)
{
if (Controller->V1.DeviceResetCount[Channel][TargetID] > 0)
- DAC960_Info(" Disk Status: %s, %d blocks, %d resets\n",
+ DAC960_Info(" Disk Status: %s, %u blocks, %d resets\n",
Controller,
(DeviceState->DeviceState == DAC960_V1_Device_Dead
? "Dead"
DeviceState->DiskSize,
Controller->V1.DeviceResetCount[Channel][TargetID]);
else
- DAC960_Info(" Disk Status: %s, %d blocks\n", Controller,
+ DAC960_Info(" Disk Status: %s, %u blocks\n", Controller,
(DeviceState->DeviceState == DAC960_V1_Device_Dead
? "Dead"
: DeviceState->DeviceState
{
DAC960_V1_LogicalDriveInformation_T *LogicalDriveInformation =
&Controller->V1.LogicalDriveInformation[LogicalDriveNumber];
- DAC960_Info(" /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n",
+ DAC960_Info(" /dev/rd/c%dd%d: RAID-%d, %s, %u blocks, %s\n",
Controller, Controller->ControllerNumber, LogicalDriveNumber,
LogicalDriveInformation->RAIDLevel,
(LogicalDriveInformation->LogicalDriveState
if (PhysicalDeviceInfo->PhysicalDeviceState ==
DAC960_V2_Device_Unconfigured)
continue;
- DAC960_Info(" Disk Status: %s, %d blocks\n", Controller,
+ DAC960_Info(" Disk Status: %s, %u blocks\n", Controller,
(PhysicalDeviceInfo->PhysicalDeviceState
== DAC960_V2_Device_Online
? "Online"
: PhysicalDeviceInfo->PhysicalDeviceState
- == DAC960_V2_Device_WriteOnly
- ? "Write-Only"
+ == DAC960_V2_Device_Rebuild
+ ? "Rebuild"
: PhysicalDeviceInfo->PhysicalDeviceState
- == DAC960_V2_Device_Dead
- ? "Dead" : "Standby"),
- PhysicalDeviceInfo
- ->ConfigurableDeviceSizeIn512ByteBlocksOrMB);
+ == DAC960_V2_Device_Missing
+ ? "Missing"
+ : PhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Critical
+ ? "Critical"
+ : PhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Dead
+ ? "Dead"
+ : PhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_SuspectedDead
+ ? "Suspected-Dead"
+ : PhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_CommandedOffline
+ ? "Commanded-Offline"
+ : PhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Standby
+ ? "Standby" : "Unknown"),
+ PhysicalDeviceInfo->ConfigurableDeviceSize);
if (PhysicalDeviceInfo->ParityErrors == 0 &&
PhysicalDeviceInfo->SoftErrors == 0 &&
PhysicalDeviceInfo->HardErrors == 0 &&
"-", "-", "-", "-" };
unsigned char *GeometryTranslation;
if (LogicalDeviceInfo == NULL) continue;
- switch(LogicalDeviceInfo->DriveGeometry)
+ switch (LogicalDeviceInfo->DriveGeometry)
{
case DAC960_V2_Geometry_128_32:
GeometryTranslation = "128/32";
Controller, LogicalDeviceInfo->DriveGeometry);
break;
}
- DAC960_Info(" /dev/rd/c%dd%d: RAID-%d, %s, %d blocks\n",
+ DAC960_Info(" /dev/rd/c%dd%d: RAID-%d, %s, %u blocks\n",
Controller, Controller->ControllerNumber, LogicalDriveNumber,
LogicalDeviceInfo->RAIDLevel,
(LogicalDeviceInfo->LogicalDeviceState
: LogicalDeviceInfo->LogicalDeviceState
== DAC960_V2_LogicalDevice_Critical
? "Critical" : "Offline"),
- LogicalDeviceInfo->ConfigurableDeviceSizeIn512ByteBlocksOrMB);
+ LogicalDeviceInfo->ConfigurableDeviceSize);
DAC960_Info(" Logical Device %s, BIOS Geometry: %s\n",
Controller,
(LogicalDeviceInfo->LogicalDeviceControl
blk_dev[MajorNumber].request_fn =
RequestFunctions[Controller->ControllerNumber];
/*
- Initialize the Disk Partitions array, Partition Sizes array, Block Sizes
- array, Max Sectors per Request array, and Max Segments per Request array.
+ Initialize the Disk Partitions array, Partition Sizes array, Max Sectors
+ per Request array, and Max Segments per Request array.
*/
for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++)
{
- Controller->BlockSizes[MinorNumber] = BLOCK_SIZE;
Controller->MaxSectorsPerRequest[MinorNumber] =
Controller->MaxBlocksPerCommand;
Controller->MaxSegmentsPerRequest[MinorNumber] =
Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives;
- Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo;
- Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount;
+ Controller->GenericDiskInfo.init = DAC960_ComputeGenericDiskInfo;
+ Controller->GenericDiskInfo.nr_real = DAC960_MaxLogicalDrives;
Controller->GenericDiskInfo.real_devices = Controller;
Controller->GenericDiskInfo.next = NULL;
/*
/*
- DAC960_GenericDiskInit is the Generic Disk Information Initialization
- Function for the DAC960 Driver.
+ DAC960_ComputeGenericDiskInfo computes the values for the Generic Disk
+ Information Partition Sector Counts and Block Sizes.
*/
-static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
+static void DAC960_ComputeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
{
DAC960_Controller_T *Controller =
(DAC960_Controller_T *) GenericDiskInfo->real_devices;
- int LogicalDriveNumber;
+ int LogicalDriveNumber, i;
+ for (LogicalDriveNumber = 0;
+ LogicalDriveNumber < DAC960_MaxLogicalDrives;
+ LogicalDriveNumber++)
+ {
+ int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber, 0);
+ if (Controller->FirmwareType == DAC960_V1_Controller)
+ {
+ if (LogicalDriveNumber < Controller->LogicalDriveCount)
+ GenericDiskInfo->part[MinorNumber].nr_sects =
+ Controller->V1.LogicalDriveInformation
+ [LogicalDriveNumber].LogicalDriveSize;
+ else GenericDiskInfo->part[MinorNumber].nr_sects = 0;
+ }
+ else
+ {
+ DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
+ Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
+ if (LogicalDeviceInfo != NULL)
+ GenericDiskInfo->part[MinorNumber].nr_sects =
+ LogicalDeviceInfo->ConfigurableDeviceSize;
+ else GenericDiskInfo->part[MinorNumber].nr_sects = 0;
+ }
+ for (i = 0; i < DAC960_MaxPartitions; i++)
+ if (GenericDiskInfo->part[MinorNumber].nr_sects > 0)
+ Controller->BlockSizes[MinorNumber + i] = BLOCK_SIZE;
+ else Controller->BlockSizes[MinorNumber + i] = 0;
+ }
+}
+
+
+/*
+ DAC960_RegisterDisk registers the DAC960 Logical Disk Device for Logical
+ Drive Number if it exists.
+*/
+
+static void DAC960_RegisterDisk(DAC960_Controller_T *Controller,
+ int LogicalDriveNumber)
+{
if (Controller->FirmwareType == DAC960_V1_Controller)
- for (LogicalDriveNumber = 0;
- LogicalDriveNumber < Controller->LogicalDriveCount;
- LogicalDriveNumber++)
- GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)]
- .nr_sects =
- Controller->V1.LogicalDriveInformation
- [LogicalDriveNumber].LogicalDriveSize;
+ {
+ if (LogicalDriveNumber > Controller->LogicalDriveCount - 1) return;
+ resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+ }
else
- for (LogicalDriveNumber = 0;
- LogicalDriveNumber < DAC960_MaxLogicalDrives;
- LogicalDriveNumber++)
- {
- DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
- Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
- if (LogicalDeviceInfo != NULL)
- GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)]
- .nr_sects =
- LogicalDeviceInfo->ConfigurableDeviceSizeIn512ByteBlocksOrMB;
- }
+ {
+ DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
+ Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
+ if (LogicalDeviceInfo == NULL) return;
+ resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+ }
}
InterruptHandler = DAC960_PD_InterruptHandler;
MemoryWindowSize = DAC960_PD_RegisterWindowSize;
break;
+ case DAC960_P_Controller:
+ VendorID = PCI_VENDOR_ID_MYLEX;
+ DeviceID = PCI_DEVICE_ID_MYLEX_DAC960_P;
+ FirmwareType = DAC960_V1_Controller;
+ InterruptHandler = DAC960_P_InterruptHandler;
+ MemoryWindowSize = DAC960_PD_RegisterWindowSize;
+ break;
}
while ((PCI_Device = pci_find_device(VendorID, DeviceID, PCI_Device)) != NULL)
{
IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
break;
+ case DAC960_P_Controller:
+ IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+ PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+ break;
}
if (DAC960_ControllerCount == DAC960_MaxControllers)
{
Controller->QueueReadWriteCommand =
DAC960_V1_QueueReadWriteCommand;
break;
+ case DAC960_P_Controller:
+ request_region(Controller->IO_Address, 0x80,
+ Controller->FullModelName);
+ DAC960_PD_DisableInterrupts(BaseAddress);
+ DAC960_PD_AcknowledgeStatus(BaseAddress);
+ udelay(1000);
+ while (DAC960_PD_InitializationInProgressP(BaseAddress))
+ {
+ if (DAC960_PD_ReadErrorStatus(BaseAddress, &ErrorStatus,
+ &Parameter0, &Parameter1) &&
+ DAC960_ReportErrorStatus(Controller, ErrorStatus,
+ Parameter0, Parameter1))
+ goto Failure;
+ udelay(10);
+ }
+ DAC960_PD_EnableInterrupts(Controller->BaseAddress);
+ Controller->QueueCommand = DAC960_P_QueueCommand;
+ Controller->ReadControllerConfiguration =
+ DAC960_V1_ReadControllerConfiguration;
+ Controller->ReadDeviceConfiguration =
+ DAC960_V1_ReadDeviceConfiguration;
+ Controller->ReportDeviceConfiguration =
+ DAC960_V1_ReportDeviceConfiguration;
+ Controller->QueueReadWriteCommand =
+ DAC960_V1_QueueReadWriteCommand;
+ break;
}
/*
Acquire shared access to the IRQ Channel.
DAC960_Initialize initializes the DAC960 Driver.
*/
-void DAC960_Initialize(void)
+int DAC960_Initialize(void)
{
int ControllerNumber;
DAC960_DetectControllers(DAC960_BA_Controller);
DAC960_DetectControllers(DAC960_LA_Controller);
DAC960_DetectControllers(DAC960_PG_Controller);
DAC960_DetectControllers(DAC960_PD_Controller);
+ DAC960_DetectControllers(DAC960_P_Controller);
DAC960_SortControllers();
- if (DAC960_ActiveControllerCount == 0) return;
+ if (DAC960_ActiveControllerCount == 0) return -ENODEV;
for (ControllerNumber = 0;
ControllerNumber < DAC960_ControllerCount;
ControllerNumber++)
- if (DAC960_Controllers[ControllerNumber] != NULL)
- DAC960_InitializeController(DAC960_Controllers[ControllerNumber]);
+ {
+ DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+ int LogicalDriveNumber;
+ if (Controller == NULL) continue;
+ DAC960_InitializeController(Controller);
+ DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+#ifdef MODULE
+ for (LogicalDriveNumber = 0;
+ LogicalDriveNumber < DAC960_MaxLogicalDrives;
+ LogicalDriveNumber++)
+ DAC960_RegisterDisk(Controller, LogicalDriveNumber);
+#endif
+ }
DAC960_CreateProcEntries();
register_reboot_notifier(&DAC960_NotifierBlock);
+ return 0;
}
DAC960_Finalize finalizes the DAC960 Driver.
*/
-static int DAC960_Finalize(NotifierBlock_T *NotifierBlock,
- unsigned long Event,
- void *Buffer)
+static void DAC960_Finalize(void)
{
int ControllerNumber;
- if (!(Event == SYS_RESTART || Event == SYS_HALT || Event == SYS_POWER_OFF))
- return NOTIFY_DONE;
- if (DAC960_ActiveControllerCount == 0) return NOTIFY_OK;
+ if (DAC960_ActiveControllerCount == 0) return;
for (ControllerNumber = 0;
ControllerNumber < DAC960_ControllerCount;
ControllerNumber++)
DAC960_FinalizeController(DAC960_Controllers[ControllerNumber]);
DAC960_DestroyProcEntries();
unregister_reboot_notifier(&DAC960_NotifierBlock);
+}
+
+
+/*
+ DAC960_Notifier is the notifier for the DAC960 Driver.
+*/
+
+static int DAC960_Notifier(NotifierBlock_T *NotifierBlock,
+ unsigned long Event,
+ void *Buffer)
+{
+ if (!(Event == SYS_RESTART || Event == SYS_HALT || Event == SYS_POWER_OFF))
+ return NOTIFY_DONE;
+ DAC960_Finalize();
return NOTIFY_OK;
}
char *LastDataEndPointer = NULL;
int SegmentNumber = 0;
if (Command->CommandType == DAC960_ReadCommand)
- CommandMailbox->Type5.CommandOpcode =
- DAC960_V1_ReadWithOldScatterGather;
+ CommandMailbox->Type5.CommandOpcode = DAC960_V1_ReadWithScatterGather;
else
- CommandMailbox->Type5.CommandOpcode =
- DAC960_V1_WriteWithOldScatterGather;
+ CommandMailbox->Type5.CommandOpcode = DAC960_V1_WriteWithScatterGather;
CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
Controller, Command->V1.CommandStatus, CommandName);
break;
}
- DAC960_Error(" /dev/rd/c%dd%d: absolute blocks %d..%d\n",
+ DAC960_Error(" /dev/rd/c%dd%d: absolute blocks %u..%u\n",
Controller, Controller->ControllerNumber,
Command->LogicalDriveNumber, Command->BlockNumber,
Command->BlockNumber + Command->BlockCount - 1);
if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0)
- DAC960_Error(" /dev/rd/c%dd%dp%d: relative blocks %d..%d\n",
+ DAC960_Error(" /dev/rd/c%dd%dp%d: relative blocks %u..%u\n",
Controller, Controller->ControllerNumber,
Command->LogicalDriveNumber,
DAC960_PartitionNumber(Command->BufferHeader->b_rdev),
LogicalDriveNumber,
Controller->ControllerNumber,
LogicalDriveNumber);
+ Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
+ DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
}
if (NewEnquiry->NumberOfLogicalDrives < Controller->LogicalDriveCount)
{
LogicalDriveNumber,
Controller->ControllerNumber,
LogicalDriveNumber);
+ Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
+ DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
}
- Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
if (NewEnquiry->StatusFlags.DeferredWriteError !=
OldEnquiry->StatusFlags.DeferredWriteError)
DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller,
Controller->V1.NeedErrorTableInformation = true;
Controller->V1.NeedDeviceStateInformation = true;
Controller->V1.StartDeviceStateScan = true;
+ Controller->V1.NeedBackgroundInitializationStatus =
+ Controller->V1.BackgroundInitializationStatusSupported;
Controller->SecondaryMonitoringTime = jiffies;
}
if (NewEnquiry->RebuildFlag == DAC960_V1_StandbyRebuildInProgress ||
AdditionalSenseCodeQualifier == 0x02))))
{
DAC960_Critical("Physical Device %d:%d Error Log: "
- "Sense Key = %d, ASC = %02X, ASCQ = %02X\n",
+ "Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
Controller,
EventLogEntry->Channel,
EventLogEntry->TargetID,
Controller->EphemeralProgressMessage = false;
}
}
+ else if (CommandOpcode == DAC960_V1_BackgroundInitializationControl)
+ {
+ unsigned int LogicalDriveNumber =
+ Controller->V1.BackgroundInitializationStatus.LogicalDriveNumber;
+ unsigned int LogicalDriveSize =
+ Controller->V1.BackgroundInitializationStatus.LogicalDriveSize;
+ unsigned int BlocksCompleted =
+ Controller->V1.BackgroundInitializationStatus.BlocksCompleted;
+ switch (CommandStatus)
+ {
+ case DAC960_V1_NormalCompletion:
+ switch (Controller->V1.BackgroundInitializationStatus.Status)
+ {
+ case DAC960_V1_BackgroundInitializationInvalid:
+ break;
+ case DAC960_V1_BackgroundInitializationStarted:
+ DAC960_Progress("Background Initialization Started\n",
+ Controller);
+ break;
+ case DAC960_V1_BackgroundInitializationInProgress:
+ if (BlocksCompleted ==
+ Controller->V1.LastBackgroundInitializationStatus
+ .BlocksCompleted &&
+ LogicalDriveNumber ==
+ Controller->V1.LastBackgroundInitializationStatus
+ .LogicalDriveNumber)
+ break;
+ Controller->EphemeralProgressMessage = true;
+ DAC960_Progress("Background Initialization in Progress: "
+ "Logical Drive %d (/dev/rd/c%dd%d) "
+ "%d%% completed\n",
+ Controller, LogicalDriveNumber,
+ Controller->ControllerNumber,
+ LogicalDriveNumber,
+ (100 * (BlocksCompleted >> 7))
+ / (LogicalDriveSize >> 7));
+ Controller->EphemeralProgressMessage = false;
+ break;
+ case DAC960_V1_BackgroundInitializationSuspended:
+ DAC960_Progress("Background Initialization Suspended\n",
+ Controller);
+ break;
+ case DAC960_V1_BackgroundInitializationCancelled:
+ DAC960_Progress("Background Initialization Cancelled\n",
+ Controller);
+ break;
+ }
+ memcpy(&Controller->V1.LastBackgroundInitializationStatus,
+ &Controller->V1.BackgroundInitializationStatus,
+ sizeof(DAC960_V1_BackgroundInitializationStatus_T));
+ break;
+ case DAC960_V1_BackgroundInitSuccessful:
+ if (Controller->V1.BackgroundInitializationStatus.Status ==
+ DAC960_V1_BackgroundInitializationInProgress)
+ DAC960_Progress("Background Initialization "
+ "Completed Successfully\n", Controller);
+ Controller->V1.BackgroundInitializationStatus.Status =
+ DAC960_V1_BackgroundInitializationInvalid;
+ break;
+ case DAC960_V1_BackgroundInitAborted:
+ if (Controller->V1.BackgroundInitializationStatus.Status ==
+ DAC960_V1_BackgroundInitializationInProgress)
+ DAC960_Progress("Background Initialization Aborted\n",
+ Controller);
+ Controller->V1.BackgroundInitializationStatus.Status =
+ DAC960_V1_BackgroundInitializationInvalid;
+ break;
+ case DAC960_V1_NoBackgroundInitInProgress:
+ break;
+ }
+ }
}
if (CommandType == DAC960_MonitoringCommand)
{
DAC960_QueueCommand(Command);
return;
}
+ if (Controller->V1.NeedBackgroundInitializationStatus)
+ {
+ Controller->V1.NeedBackgroundInitializationStatus = false;
+ Command->V1.CommandMailbox.Type3B.CommandOpcode =
+ DAC960_V1_BackgroundInitializationControl;
+ Command->V1.CommandMailbox.Type3B.CommandOpcode2 = 0x20;
+ Command->V1.CommandMailbox.Type3B.BusAddress =
+ Virtual_to_Bus32(&Controller->V1.BackgroundInitializationStatus);
+ DAC960_QueueCommand(Command);
+ return;
+ }
Controller->MonitoringTimerCount++;
Controller->MonitoringTimer.expires =
jiffies + DAC960_MonitoringTimerInterval;
}
DAC960_Error("Error Condition %s on %s:\n", Controller,
SenseErrors[Command->V2.RequestSense.SenseKey], CommandName);
- DAC960_Error(" /dev/rd/c%dd%d: absolute blocks %d..%d\n",
+ DAC960_Error(" /dev/rd/c%dd%d: absolute blocks %u..%u\n",
Controller, Controller->ControllerNumber,
Command->LogicalDriveNumber, Command->BlockNumber,
Command->BlockNumber + Command->BlockCount - 1);
if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0)
- DAC960_Error(" /dev/rd/c%dd%dp%d: relative blocks %d..%d\n",
+ DAC960_Error(" /dev/rd/c%dd%dp%d: relative blocks %u..%u\n",
Controller, Controller->ControllerNumber,
Command->LogicalDriveNumber,
DAC960_PartitionNumber(Command->BufferHeader->b_rdev),
{ 0x000B, "P Rebuild Failed due to Logical Drive Failure" },
{ 0x000C, "S Offline" },
{ 0x000D, "P Found" },
- { 0x000E, "P Gone" },
+ { 0x000E, "P Removed" },
{ 0x000F, "P Unconfigured" },
{ 0x0010, "P Expand Capacity Started" },
{ 0x0011, "P Expand Capacity Completed" },
{ 0x0012, "P Expand Capacity Failed" },
+ { 0x0013, "P Command Timed Out" },
+ { 0x0014, "P Command Aborted" },
+ { 0x0015, "P Command Retried" },
{ 0x0016, "P Parity Error" },
{ 0x0017, "P Soft Error" },
{ 0x0018, "P Miscellaneous Error" },
{ 0x0031, "P Failed because BDT Write Operation Failed" },
{ 0x0039, "P Missing at Startup" },
{ 0x003A, "P Start Rebuild Failed due to Physical Drive Too Small" },
+ { 0x003C, "P Temporarily Offline Device Automatically Made Online" },
+ { 0x003D, "P Standby Rebuild Started" },
/* Logical Device Events (0x0080 - 0x00FF) */
{ 0x0080, "M Consistency Check Started" },
{ 0x0081, "M Consistency Check Completed" },
{ 0x0092, "M Initialization Cancelled" },
{ 0x0093, "M Initialization Failed" },
{ 0x0094, "L Found" },
- { 0x0095, "L Gone" },
+ { 0x0095, "L Deleted" },
{ 0x0096, "M Expand Capacity Started" },
{ 0x0097, "M Expand Capacity Completed" },
{ 0x0098, "M Expand Capacity Failed" },
{ 0x009C, "L Bad Data Block Found" },
{ 0x009E, "L Read of Data Block in BDT" },
{ 0x009F, "L Write Back Data for Disk Block Lost" },
+ { 0x00A0, "L Temporarily Offline RAID-5/3 Drive Made Online" },
+ { 0x00A1, "L Temporarily Offline RAID-6/1/0/7 Drive Made Online" },
+ { 0x00A2, "L Standby Rebuild Started" },
/* Fault Management Events (0x0100 - 0x017F) */
{ 0x0140, "E Fan %d Failed" },
{ 0x0141, "E Fan %d OK" },
{ 0x0143, "E Power Supply %d Failed" },
{ 0x0144, "E Power Supply %d OK" },
{ 0x0145, "E Power Supply %d Not Present" },
- { 0x0146, "E Temperature Sensor %d Failed" },
- { 0x0147, "E Temperature Sensor %d Critical" },
- { 0x0148, "E Temperature Sensor %d OK" },
+ { 0x0146, "E Temperature Sensor %d Temperature Exceeds Safe Limit" },
+ { 0x0147, "E Temperature Sensor %d Temperature Exceeds Working Limit" },
+ { 0x0148, "E Temperature Sensor %d Temperature Normal" },
{ 0x0149, "E Temperature Sensor %d Not Present" },
- { 0x014A, "E Unit %d Access Critical" },
- { 0x014B, "E Unit %d Access OK" },
- { 0x014C, "E Unit %d Access Offline" },
+ { 0x014A, "E Enclosure Management Unit %d Access Critical" },
+ { 0x014B, "E Enclosure Management Unit %d Access OK" },
+ { 0x014C, "E Enclosure Management Unit %d Access Offline" },
/* Controller Events (0x0180 - 0x01FF) */
{ 0x0181, "C Cache Write Back Error" },
{ 0x0188, "C Battery Backup Unit Found" },
{ 0x0189, "C Battery Backup Unit Charge Level Low" },
{ 0x018A, "C Battery Backup Unit Charge Level OK" },
{ 0x0193, "C Installation Aborted" },
- { 0x0195, "C Mirror Race Recovery In Progress" },
- { 0x0196, "C Mirror Race on Critical Drive" },
- { 0x019E, "C Memory Soft ECC Error" },
- { 0x019F, "C Memory Hard ECC Error" },
+ { 0x0195, "C Battery Backup Unit Physically Removed" },
+ { 0x0196, "C Memory Error During Warm Boot" },
+ { 0x019E, "C Memory Soft ECC Error Corrected" },
+ { 0x019F, "C Memory Hard ECC Error Corrected" },
{ 0x01A2, "C Battery Backup Unit Failed" },
+ { 0x01AB, "C Mirror Race Recovery Failed" },
+ { 0x01AC, "C Mirror Race on Critical Drive" },
+ /* Controller Internal Processor Events */
+ { 0x0380, "C Internal Controller Hung" },
+ { 0x0381, "C Internal Controller Firmware Breakpoint" },
+ { 0x0390, "C Internal Controller i960 Processor Specific Error" },
+ { 0x03A0, "C Internal Controller StrongARM Processor Specific Error" },
{ 0, "" } };
int EventListIndex = 0, EventCode;
unsigned char EventType, *EventMessage;
DAC960_Critical("Physical Device %d:%d %s\n", Controller,
Event->Channel, Event->TargetID, EventMessage);
DAC960_Critical("Physical Device %d:%d Request Sense: "
- "Sense Key = %d, ASC = %02X, ASCQ = %02X\n",
+ "Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
Controller,
Event->Channel,
Event->TargetID,
{
if (NewPhysicalDeviceInfo->PhysicalDeviceState !=
PhysicalDeviceInfo->PhysicalDeviceState)
- DAC960_Critical("Physical Device %d:%d is now %s\n", Controller,
- NewPhysicalDeviceInfo->Channel,
- NewPhysicalDeviceInfo->TargetID,
- (NewPhysicalDeviceInfo->PhysicalDeviceState
- == DAC960_V2_Device_Unconfigured
- ? "UNCONFIGURED"
- : NewPhysicalDeviceInfo->PhysicalDeviceState
- == DAC960_V2_Device_Online
- ? "ONLINE"
- : NewPhysicalDeviceInfo->PhysicalDeviceState
- == DAC960_V2_Device_WriteOnly
- ? "WRITE-ONLY"
- : NewPhysicalDeviceInfo
- ->PhysicalDeviceState
- == DAC960_V2_Device_Dead
- ? "DEAD" : "STANDBY"));
+ DAC960_Critical(
+ "Physical Device %d:%d is now %s\n", Controller,
+ NewPhysicalDeviceInfo->Channel,
+ NewPhysicalDeviceInfo->TargetID,
+ (NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Online
+ ? "ONLINE"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Rebuild
+ ? "REBUILD"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Missing
+ ? "MISSING"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Critical
+ ? "CRITICAL"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Dead
+ ? "DEAD"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_SuspectedDead
+ ? "SUSPECTED-DEAD"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_CommandedOffline
+ ? "COMMANDED-OFFLINE"
+ : NewPhysicalDeviceInfo->PhysicalDeviceState
+ == DAC960_V2_Device_Standby
+ ? "STANDBY" : "UNKNOWN"));
if ((NewPhysicalDeviceInfo->ParityErrors !=
PhysicalDeviceInfo->ParityErrors) ||
(NewPhysicalDeviceInfo->SoftErrors !=
(LogicalDeviceInfo != NULL
? "" : " - Allocation Failed"));
if (LogicalDeviceInfo != NULL)
- memset(LogicalDeviceInfo, 0,
- sizeof(DAC960_V2_LogicalDeviceInfo_T));
+ {
+ memset(LogicalDeviceInfo, 0,
+ sizeof(DAC960_V2_LogicalDeviceInfo_T));
+ DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ }
}
if (LogicalDeviceInfo != NULL)
{
unsigned long LogicalDeviceSize =
- NewLogicalDeviceInfo->ConfigurableDeviceSizeIn512ByteBlocksOrMB;
+ NewLogicalDeviceInfo->ConfigurableDeviceSize;
if (NewLogicalDeviceInfo->LogicalDeviceState !=
LogicalDeviceInfo->LogicalDeviceState)
DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
kfree(LogicalDeviceInfo);
Controller->LogicalDriveInitiallyAccessible
[LogicalDriveNumber] = false;
+ DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
}
Controller->V2.NeedLogicalDeviceInformation = false;
}
}
+/*
+ DAC960_P_InterruptHandler handles hardware interrupts from DAC960 P Series
+ Controllers.
+*/
+
+static void DAC960_P_InterruptHandler(int IRQ_Channel,
+ void *DeviceIdentifier,
+ Registers_T *InterruptRegisters)
+{
+ DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier;
+ void *ControllerBaseAddress = Controller->BaseAddress;
+ ProcessorFlags_T ProcessorFlags;
+ /*
+ Acquire exclusive access to Controller.
+ */
+ DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags);
+ /*
+ Process Hardware Interrupts for Controller.
+ */
+ while (DAC960_PD_StatusAvailableP(ControllerBaseAddress))
+ {
+ DAC960_V1_CommandIdentifier_T CommandIdentifier =
+ DAC960_PD_ReadStatusCommandIdentifier(ControllerBaseAddress);
+ DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+ DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+ DAC960_V1_CommandOpcode_T CommandOpcode =
+ CommandMailbox->Common.CommandOpcode;
+ Command->V1.CommandStatus =
+ DAC960_PD_ReadStatusRegister(ControllerBaseAddress);
+ DAC960_PD_AcknowledgeInterrupt(ControllerBaseAddress);
+ DAC960_PD_AcknowledgeStatus(ControllerBaseAddress);
+ switch (CommandOpcode)
+ {
+ case DAC960_V1_Enquiry_Old:
+ Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Enquiry;
+ DAC960_P_To_PD_TranslateEnquiry(
+ Bus32_to_Virtual(CommandMailbox->Type3.BusAddress));
+ break;
+ case DAC960_V1_GetDeviceState_Old:
+ Command->V1.CommandMailbox.Common.CommandOpcode =
+ DAC960_V1_GetDeviceState;
+ DAC960_P_To_PD_TranslateDeviceState(
+ Bus32_to_Virtual(CommandMailbox->Type3.BusAddress));
+ break;
+ case DAC960_V1_Read_Old:
+ Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Read;
+ DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ case DAC960_V1_Write_Old:
+ Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Write;
+ DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ case DAC960_V1_ReadWithScatterGather_Old:
+ Command->V1.CommandMailbox.Common.CommandOpcode =
+ DAC960_V1_ReadWithScatterGather;
+ DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ case DAC960_V1_WriteWithScatterGather_Old:
+ Command->V1.CommandMailbox.Common.CommandOpcode =
+ DAC960_V1_WriteWithScatterGather;
+ DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+ break;
+ default:
+ break;
+ }
+ DAC960_V1_ProcessCompletedCommand(Command);
+ }
+ /*
+ Attempt to remove additional I/O Requests from the Controller's
+ I/O Request Queue and queue them to the Controller.
+ */
+ while (DAC960_ProcessRequest(Controller, false)) ;
+ /*
+ Release exclusive access to Controller.
+ */
+ DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags);
+}
+
+
/*
DAC960_V1_QueueMonitoringCommand queues a Monitoring Command to DAC960 V1
Firmware Controllers.
Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
if (LogicalDeviceInfo == NULL) continue;
if (!LogicalDeviceInfo->LogicalDeviceControl
- .LogicalDeviceInitialized &&
- Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 0)
+ .LogicalDeviceInitialized)
{
ForceMonitoringCommand = true;
break;
if (!Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber])
{
Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber] = true;
- DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo);
- resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+ DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_RegisterDisk(Controller, LogicalDriveNumber);
}
if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0)
return -ENXIO;
Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
if (LogicalDeviceInfo == NULL)
return -EINVAL;
- switch(LogicalDeviceInfo->DriveGeometry)
+ switch (LogicalDeviceInfo->DriveGeometry)
{
case DAC960_V2_Geometry_128_32:
Geometry.heads = 128;
return -EINVAL;
}
Geometry.cylinders =
- LogicalDeviceInfo->ConfigurableDeviceSizeIn512ByteBlocksOrMB
+ LogicalDeviceInfo->ConfigurableDeviceSize
/ (Geometry.heads * Geometry.sectors);
}
Geometry.start =
sizeof(DiskGeometry_T)) ? -EFAULT : 0);
case BLKGETSIZE:
/* Get Device Size. */
- if ((long *) Argument == NULL) return -EINVAL;
+ if ((unsigned long *) Argument == NULL) return -EINVAL;
return put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)]
.nr_sects,
- (long *) Argument);
+ (unsigned long *) Argument);
case BLKRAGET:
/* Get Read-Ahead. */
if ((int *) Argument == NULL) return -EINVAL;
*/
set_blocksize(Device, BLOCK_SIZE);
}
- resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+ DAC960_RegisterDisk(Controller, LogicalDriveNumber);
return 0;
}
return -EINVAL;
== DAC960_V2_NormalCompletion
? "Cancelled" : "Not Cancelled"));
}
+ else if (strcmp(UserCommand, "perform-discovery") == 0)
+ {
+ CommandMailbox->Common.IOCTL_Opcode = DAC960_V2_StartDiscovery;
+ DAC960_ExecuteCommand(Command);
+ DAC960_UserCritical("Discovery %s\n", Controller,
+ (Command->V2.CommandStatus
+ == DAC960_V2_NormalCompletion
+ ? "Initiated" : "Not Initiated"));
+ if (Command->V2.CommandStatus == DAC960_V2_NormalCompletion)
+ {
+ CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
+ CommandMailbox->ControllerInfo.CommandControlBits
+ .DataTransferControllerToHost = true;
+ CommandMailbox->ControllerInfo.CommandControlBits
+ .NoAutoRequestSense = true;
+ CommandMailbox->ControllerInfo.DataTransferSize =
+ sizeof(DAC960_V2_ControllerInfo_T);
+ CommandMailbox->ControllerInfo.ControllerNumber = 0;
+ CommandMailbox->ControllerInfo.IOCTL_Opcode =
+ DAC960_V2_GetControllerInfo;
+ CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+ .ScatterGatherSegments[0]
+ .SegmentDataPointer =
+ Virtual_to_Bus64(&Controller->V2.NewControllerInformation);
+ CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+ .ScatterGatherSegments[0]
+ .SegmentByteCount =
+ CommandMailbox->ControllerInfo.DataTransferSize;
+ DAC960_ExecuteCommand(Command);
+ while (Controller->V2.NewControllerInformation.PhysicalScanActive)
+ {
+ DAC960_ExecuteCommand(Command);
+ sleep_on_timeout(&Controller->CommandWaitQueue, HZ);
+ }
+ DAC960_UserCritical("Discovery Completed\n", Controller);
+ }
+ }
else if (strcmp(UserCommand, "suppress-enclosure-messages") == 0)
Controller->SuppressEnclosureMessages = true;
else DAC960_UserCritical("Illegal User Command: '%s'\n",
/*
- DAC960_CreateProcEntries creates the /proc/rd/... entries for the DAC960
- Driver.
+ DAC960_CreateProcEntries creates the /proc/rd/... entries for the
+ DAC960 Driver.
*/
static void DAC960_CreateProcEntries(void)
/*
- DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the DAC960
- Driver.
+ DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the
+ DAC960 Driver.
*/
static void DAC960_DestroyProcEntries(void)
int init_module(void)
{
- int ControllerNumber, LogicalDriveNumber;
- DAC960_Initialize();
- if (DAC960_ActiveControllerCount == 0) return -1;
- for (ControllerNumber = 0;
- ControllerNumber < DAC960_ControllerCount;
- ControllerNumber++)
- {
- DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
- if (Controller == NULL) continue;
- DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo);
- for (LogicalDriveNumber = 0;
- LogicalDriveNumber < Controller->LogicalDriveCount;
- LogicalDriveNumber++)
- resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
- }
- return 0;
+ return DAC960_Initialize();
}
void cleanup_module(void)
{
- DAC960_Finalize(&DAC960_NotifierBlock, SYS_RESTART, NULL);
+ DAC960_Finalize();
}
DAC960_V1_ReadExtendedWithScatterGather = 0xB3,
DAC960_V1_WriteExtendedWithScatterGather = 0xB4,
DAC960_V1_Read = 0x36,
- DAC960_V1_ReadWithOldScatterGather = 0xB6,
+ DAC960_V1_ReadWithScatterGather = 0xB6,
DAC960_V1_Write = 0x37,
- DAC960_V1_WriteWithOldScatterGather = 0xB7,
+ DAC960_V1_WriteWithScatterGather = 0xB7,
DAC960_V1_DCDB = 0x04,
DAC960_V1_DCDBWithScatterGather = 0x84,
DAC960_V1_Flush = 0x0A,
DAC960_V1_RunDiagnostic = 0x32,
/* Subsystem Service Commands */
DAC960_V1_GetSubsystemData = 0x70,
- DAC960_V1_SetSubsystemParameters = 0x71
+ DAC960_V1_SetSubsystemParameters = 0x71,
+ /* Version 2.xx Firmware Commands */
+ DAC960_V1_Enquiry_Old = 0x05,
+ DAC960_V1_GetDeviceState_Old = 0x14,
+ DAC960_V1_Read_Old = 0x02,
+ DAC960_V1_Write_Old = 0x03,
+ DAC960_V1_ReadWithScatterGather_Old = 0x82,
+ DAC960_V1_WriteWithScatterGather_Old = 0x83
}
__attribute__ ((packed))
DAC960_V1_CommandOpcode_T;
#define DAC960_V1_RebuildFailed_NewDriveFailed 0x0004 /* Consistency */
#define DAC960_V1_RebuildSuccessful 0x0100 /* Consistency */
#define DAC960_V1_RebuildSuccessfullyTerminated 0x0107 /* Consistency */
+#define DAC960_V1_BackgroundInitSuccessful 0x0100 /* Consistency */
+#define DAC960_V1_BackgroundInitAborted 0x0005 /* Consistency */
+#define DAC960_V1_NoBackgroundInitInProgress 0x0105 /* Consistency */
#define DAC960_V1_AddCapacityInProgress 0x0004 /* Consistency */
#define DAC960_V1_AddCapacityFailedOrSuspended 0x00F4 /* Consistency */
#define DAC960_V1_Config2ChecksumError 0x0002 /* Configuration */
/*
Define the DAC960 V1 Firmware Get Device State Command reply structure.
+ The structure is padded by 2 bytes for compatibility with Version 2.xx
+ Firmware.
*/
typedef struct DAC960_V1_DeviceState
unsigned char SynchronousOffset:5; /* Byte 5 Bits 0-4 */
unsigned char :3; /* Byte 5 Bits 5-7 */
unsigned int DiskSize __attribute__ ((packed)); /* Bytes 6-9 */
+ unsigned short :16; /* Bytes 10-11 */
}
DAC960_V1_DeviceState_T;
DAC960_V1_RebuildProgress_T;
+/*
+ Define the DAC960 V1 Firmware Background Initialization Status Command
+ reply structure.
+*/
+
+typedef struct DAC960_V1_BackgroundInitializationStatus
+{
+ unsigned int LogicalDriveSize; /* Bytes 0-3 */
+ unsigned int BlocksCompleted; /* Bytes 4-7 */
+ unsigned char Reserved1[12]; /* Bytes 8-19 */
+ unsigned int LogicalDriveNumber; /* Bytes 20-23 */
+ unsigned char RAIDLevel; /* Byte 24 */
+ enum {
+ DAC960_V1_BackgroundInitializationInvalid = 0x00,
+ DAC960_V1_BackgroundInitializationStarted = 0x02,
+ DAC960_V1_BackgroundInitializationInProgress = 0x04,
+ DAC960_V1_BackgroundInitializationSuspended = 0x05,
+ DAC960_V1_BackgroundInitializationCancelled = 0x06
+ } __attribute__ ((packed)) Status; /* Byte 25 */
+ unsigned char Reserved2[6]; /* Bytes 26-31 */
+}
+DAC960_V1_BackgroundInitializationStatus_T;
+
+
/*
Define the DAC960 V1 Firmware Error Table Entry structure.
*/
DAC960_BusAddress32_T BusAddress; /* Bytes 8-11 */
unsigned char Dummy2[4]; /* Bytes 12-15 */
} __attribute__ ((packed)) Type3;
+ struct {
+ DAC960_V1_CommandOpcode_T CommandOpcode; /* Byte 0 */
+ DAC960_V1_CommandIdentifier_T CommandIdentifier; /* Byte 1 */
+ unsigned char CommandOpcode2; /* Byte 2 */
+ unsigned char Dummy1[5]; /* Bytes 3-7 */
+ DAC960_BusAddress32_T BusAddress; /* Bytes 8-11 */
+ unsigned char Dummy2[4]; /* Bytes 12-15 */
+ } __attribute__ ((packed)) Type3B;
struct {
DAC960_V1_CommandOpcode_T CommandOpcode; /* Byte 0 */
DAC960_V1_CommandIdentifier_T CommandIdentifier; /* Byte 1 */
DAC960_V2_GetPhysicalDeviceInfoValid = 0x05,
DAC960_V2_GetHealthStatus = 0x11,
DAC960_V2_GetEvent = 0x15,
+ DAC960_V2_StartDiscovery = 0x81,
DAC960_V2_SetDeviceState = 0x82,
DAC960_V2_RebuildDeviceStart = 0x88,
DAC960_V2_RebuildDeviceStop = 0x89,
#define DAC960_V2_NormalCompletion 0x00
#define DAC960_V2_AbormalCompletion 0x02
+#define DAC960_V2_DeviceBusy 0x08
#define DAC960_V2_DeviceNonresponsive 0x0E
+#define DAC960_V2_DeviceNonresponsive2 0x0F
+#define DAC960_V2_DeviceRevervationConflict 0x18
typedef unsigned char DAC960_V2_CommandStatus_T;
DAC960_V2_EXR2000P = 0x1C,
DAC960_V2_EXR3000P = 0x1D,
DAC960_V2_AcceleRAID352 = 0x1E,
- DAC960_V2_AcceleRAID351 = 0x1F,
+ DAC960_V2_AcceleRAID170 = 0x1F,
+ DAC960_V2_AcceleRAID160 = 0x20,
DAC960_V2_DAC960S = 0x60,
DAC960_V2_DAC960SU = 0x61,
DAC960_V2_DAC960SX = 0x62,
unsigned char :8; /* Byte 3 */
unsigned short BusInterfaceSpeedMHz; /* Bytes 4-5 */
unsigned char BusWidthBits; /* Byte 6 */
- unsigned char Reserved1[9]; /* Bytes 7-15 */
+ unsigned char FlashCodeTypeOrProductID; /* Byte 7 */
+ unsigned char NumberOfHostPortsPresent; /* Byte 8 */
+ unsigned char Reserved1[7]; /* Bytes 9-15 */
unsigned char BusInterfaceName[16]; /* Bytes 16-31 */
unsigned char ControllerName[16]; /* Bytes 32-47 */
unsigned char Reserved2[16]; /* Bytes 48-63 */
unsigned char HardwareManufacturingMonth; /* Byte 85 */
unsigned char HardwareManufacturingYearHigh2Digits; /* Byte 86 */
unsigned char HardwareManufacturingYearLow2Digits; /* Byte 87 */
- unsigned char MaximumNumberOfPDDperXLDD; /* Byte 88 */
- unsigned char MaximumNumberOfILDDperXLDD; /* Byte 89 */
+ unsigned char MaximumNumberOfPDDperXLD; /* Byte 88 */
+ unsigned char MaximumNumberOfILDperXLD; /* Byte 89 */
unsigned short NonvolatileMemorySizeKB; /* Bytes 90-91 */
- unsigned char MaximumNumberOfXLDD; /* Byte 92 */
+ unsigned char MaximumNumberOfXLD; /* Byte 92 */
unsigned int :24; /* Bytes 93-95 */
/* Unique Information per Controller */
unsigned char ControllerSerialNumber[16]; /* Bytes 96-111 */
unsigned char Reserved3[16]; /* Bytes 112-127 */
/* Vendor Information */
unsigned int :24; /* Bytes 128-130 */
- unsigned char OEM_Information; /* Byte 131 */
+ unsigned char OEM_Code; /* Byte 131 */
unsigned char VendorName[16]; /* Bytes 132-147 */
/* Other Physical/Controller/Operation Information */
boolean BBU_Present:1; /* Byte 148 Bit 0 */
unsigned short PhysicalDeviceHostCommandAbortsDone; /* Bytes 370-371 */
unsigned short PhysicalDevicePredictedFailuresDetected; /* Bytes 372-373 */
unsigned short PhysicalDeviceHostCommandsFailed; /* Bytes 374-375 */
- unsigned char Reserved9[8]; /* Bytes 376-383 */
+ unsigned short PhysicalDeviceHardErrors; /* Bytes 376-377 */
+ unsigned char Reserved9[6]; /* Bytes 378-383 */
/* Error Counters on Logical Devices */
unsigned short LogicalDeviceSoftErrors; /* Bytes 384-385 */
unsigned short LogicalDeviceCommandsFailed; /* Bytes 386-387 */
unsigned short LogicalDeviceHostCommandAbortsDone; /* Bytes 388-389 */
unsigned short :16; /* Bytes 390-391 */
+ /* Error Counters on Controller */
unsigned short ControllerMemoryErrors; /* Bytes 392-393 */
unsigned short ControllerHostCommandAbortsDone; /* Bytes 394-395 */
unsigned int :32; /* Bytes 396-399 */
unsigned short RebuildsActive; /* Bytes 408-409 */
unsigned short OnlineExpansionsActive; /* Bytes 410-411 */
unsigned short PatrolActivitiesActive; /* Bytes 412-413 */
- unsigned char LongOperationStatus; /* Byte 414 */
- unsigned char :8; /* Byte 415 */
+ unsigned short :16; /* Bytes 414-415 */
/* Flash ROM Information */
unsigned char FlashType; /* Byte 416 */
unsigned char :8; /* Byte 417 */
unsigned short NumberOfConfigurationGroups; /* Bytes 474-475 */
boolean InstallationAbortStatus:1; /* Byte 476 Bit 0 */
boolean MaintenanceModeStatus:1; /* Byte 476 Bit 1 */
- unsigned int :6; /* Byte 476 Bits 2-7 */
- unsigned int :24; /* Bytes 477-479 */
+ unsigned int :24; /* Bytes 476-479 */
unsigned char Reserved10[32]; /* Bytes 480-511 */
unsigned char Reserved11[512]; /* Bytes 512-1023 */
}
DAC960_V2_Geometry_Reserved1 = 0x2,
DAC960_V2_Geometry_Reserved2 = 0x3
} __attribute__ ((packed)) DriveGeometry:2; /* Byte 14 Bits 5-6 */
- unsigned char :1; /* Byte 14 Bit 7 */
+ boolean SuperReadAheadEnabled:1; /* Byte 14 Bit 7 */
unsigned char :8; /* Byte 15 */
/* Error Counters */
unsigned short SoftErrors; /* Bytes 16-17 */
/* Device Size Information */
unsigned short :16; /* Bytes 32-33 */
unsigned short DeviceBlockSizeInBytes; /* Bytes 34-35 */
- unsigned int OriginalDeviceSizeIn512ByteBlocksOrMB; /* Bytes 36-39 */
- unsigned int ConfigurableDeviceSizeIn512ByteBlocksOrMB; /* Bytes 40-43 */
+ unsigned int OriginalDeviceSize; /* Bytes 36-39 */
+ unsigned int ConfigurableDeviceSize; /* Bytes 40-43 */
unsigned int :32; /* Bytes 44-47 */
unsigned char LogicalDeviceName[32]; /* Bytes 48-79 */
unsigned char SCSI_InquiryData[36]; /* Bytes 80-115 */
{
DAC960_V2_Device_Unconfigured = 0x00,
DAC960_V2_Device_Online = 0x01,
- DAC960_V2_Device_WriteOnly = 0x03,
+ DAC960_V2_Device_Rebuild = 0x03,
+ DAC960_V2_Device_Missing = 0x04,
+ DAC960_V2_Device_Critical = 0x05,
DAC960_V2_Device_Dead = 0x08,
+ DAC960_V2_Device_SuspectedDead = 0x0C,
+ DAC960_V2_Device_CommandedOffline = 0x10,
DAC960_V2_Device_Standby = 0x21,
DAC960_V2_Device_InvalidState = 0xFF
}
unsigned char LogicalUnit; /* Byte 3 */
/* Configuration Status Bits */
boolean PhysicalDeviceFaultTolerant:1; /* Byte 4 Bit 0 */
- boolean :1; /* Byte 4 Bit 1 */
+ boolean PhysicalDeviceConnected:1; /* Byte 4 Bit 1 */
boolean PhysicalDeviceLocalToController:1; /* Byte 4 Bit 2 */
unsigned char :5; /* Byte 4 Bits 3-7 */
/* Multiple Host/Controller Status Bits */
unsigned int :32; /* Bytes 44-47 */
unsigned short :16; /* Bytes 48-49 */
unsigned short DeviceBlockSizeInBytes; /* Bytes 50-51 */
- unsigned int OriginalDeviceSizeIn512ByteBlocksOrMB; /* Bytes 52-55 */
- unsigned int ConfigurableDeviceSizeIn512ByteBlocksOrMB; /* Bytes 56-59 */
+ unsigned int OriginalDeviceSize; /* Bytes 52-55 */
+ unsigned int ConfigurableDeviceSize; /* Bytes 56-59 */
unsigned int :32; /* Bytes 60-63 */
unsigned char PhysicalDeviceName[16]; /* Bytes 64-79 */
unsigned char Reserved1[16]; /* Bytes 80-95 */
unsigned char Reserved2[32]; /* Bytes 96-127 */
unsigned char SCSI_InquiryData[36]; /* Bytes 128-163 */
- unsigned char Reserved3[12]; /* Bytes 164-175 */
- unsigned char Reserved4[16]; /* Bytes 176-191 */
+ unsigned char Reserved3[20]; /* Bytes 164-183 */
+ unsigned char Reserved4[8]; /* Bytes 184-191 */
DAC960_ByteCount64_T LastReadBlockNumber; /* Bytes 192-199 */
DAC960_ByteCount64_T LastWrittenBlockNumber; /* Bytes 200-207 */
DAC960_ByteCount64_T ConsistencyCheckBlockNumber; /* Bytes 208-215 */
DAC960_V2_RAID_Channel = 0x03,
DAC960_V2_Physical_Controller = 0x04,
DAC960_V2_RAID_Controller = 0x05,
- DAC960_V2_Configuration_Group = 0x10
+ DAC960_V2_Configuration_Group = 0x10,
+ DAC960_V2_Enclosure = 0x11
}
__attribute__ ((packed))
DAC960_V2_OperationDevice_T;
DAC960_V2_CommandIdentifier_T CommandIdentifier; /* Bytes 0-1 */
DAC960_V2_CommandOpcode_T CommandOpcode; /* Byte 2 */
DAC960_V2_CommandControlBits_T CommandControlBits; /* Byte 3 */
- DAC960_ByteCount32_T DataTransferSize:24; /* Bytes 4-6 */
- unsigned char DataTransferPageNumber; /* Byte 7 */
+ DAC960_ByteCount32_T DataTransferSize; /* Bytes 4-7 */
DAC960_BusAddress64_T RequestSenseBusAddress; /* Bytes 8-15 */
DAC960_V2_PhysicalDevice_T PhysicalDevice; /* Bytes 16-18 */
DAC960_V2_CommandTimeout_T CommandTimeout; /* Byte 19 */
DAC960_V2_CommandIdentifier_T CommandIdentifier; /* Bytes 0-1 */
DAC960_V2_CommandOpcode_T CommandOpcode; /* Byte 2 */
DAC960_V2_CommandControlBits_T CommandControlBits; /* Byte 3 */
- DAC960_ByteCount32_T DataTransferSize:24; /* Bytes 4-6 */
- unsigned char DataTransferPageNumber; /* Byte 7 */
+ DAC960_ByteCount32_T DataTransferSize; /* Bytes 4-7 */
DAC960_BusAddress64_T RequestSenseBusAddress; /* Bytes 8-15 */
DAC960_V2_PhysicalDevice_T PhysicalDevice; /* Bytes 16-18 */
DAC960_V2_CommandTimeout_T CommandTimeout; /* Byte 19 */
DataTransferMemoryAddress; /* Bytes 32-63 */
} DeviceOperation;
}
-__attribute__ ((packed))
DAC960_V2_CommandMailbox_T;
DAC960_LP_Controller = 2, /* AcceleRAID 352 */
DAC960_LA_Controller = 3, /* DAC1164P */
DAC960_PG_Controller = 4, /* DAC960PTL/PJ/PG */
- DAC960_PD_Controller = 5 /* DAC960PU/PD/PL */
+ DAC960_PD_Controller = 5, /* DAC960PU/PD/PL/P */
+ DAC960_P_Controller = 6 /* DAC960PU/PD/PL/P */
}
DAC960_HardwareType_T;
unsigned short DeviceStateChannel;
unsigned short DeviceStateTargetID;
boolean DualModeMemoryMailboxInterface;
+ boolean BackgroundInitializationStatusSupported;
boolean SAFTE_EnclosureManagementEnabled;
boolean NeedLogicalDriveInformation;
boolean NeedErrorTableInformation;
boolean NeedDeviceSerialNumberInformation;
boolean NeedRebuildProgress;
boolean NeedConsistencyCheckProgress;
+ boolean NeedBackgroundInitializationStatus;
boolean StartDeviceStateScan;
boolean RebuildProgressFirst;
boolean RebuildFlagPending;
DAC960_V1_CommandStatus_T PendingRebuildStatus;
DAC960_V1_LogicalDriveInformationArray_T LogicalDriveInformation;
DAC960_V1_LogicalDriveInformationArray_T NewLogicalDriveInformation;
+ DAC960_V1_BackgroundInitializationStatus_T
+ BackgroundInitializationStatus;
+ DAC960_V1_BackgroundInitializationStatus_T
+ LastBackgroundInitializationStatus;
DAC960_V1_DeviceState_T
DeviceState[DAC960_V1_MaxChannels][DAC960_V1_MaxTargets];
DAC960_V1_DeviceState_T NewDeviceState;
return true;
}
+static inline void DAC960_P_To_PD_TranslateEnquiry(void *Enquiry)
+{
+ memcpy(Enquiry + 132, Enquiry + 36, 64);
+ memset(Enquiry + 36, 0, 96);
+}
+
+static inline void DAC960_P_To_PD_TranslateDeviceState(void *DeviceState)
+{
+ memcpy(DeviceState + 2, DeviceState + 3, 1);
+ memcpy(DeviceState + 4, DeviceState + 5, 2);
+ memcpy(DeviceState + 6, DeviceState + 8, 4);
+}
+
+static inline
+void DAC960_PD_To_P_TranslateReadWriteCommand(DAC960_V1_CommandMailbox_T
+ *CommandMailbox)
+{
+ int LogicalDriveNumber = CommandMailbox->Type5.LD.LogicalDriveNumber;
+ CommandMailbox->Bytes[3] &= 0x7;
+ CommandMailbox->Bytes[3] |= CommandMailbox->Bytes[7] << 6;
+ CommandMailbox->Bytes[7] = LogicalDriveNumber;
+}
+
+static inline
+void DAC960_P_To_PD_TranslateReadWriteCommand(DAC960_V1_CommandMailbox_T
+ *CommandMailbox)
+{
+ int LogicalDriveNumber = CommandMailbox->Bytes[7];
+ CommandMailbox->Bytes[7] = CommandMailbox->Bytes[3] >> 6;
+ CommandMailbox->Bytes[3] &= 0x7;
+ CommandMailbox->Bytes[3] |= LogicalDriveNumber << 3;
+}
+
/*
Define prototypes for the forward referenced DAC960 Driver Internal Functions.
*/
static void DAC960_FinalizeController(DAC960_Controller_T *);
-static int DAC960_Finalize(NotifierBlock_T *, unsigned long, void *);
+static int DAC960_Notifier(NotifierBlock_T *, unsigned long, void *);
static void DAC960_V1_QueueReadWriteCommand(DAC960_Command_T *);
static void DAC960_V2_QueueReadWriteCommand(DAC960_Command_T *);
static void DAC960_RequestFunction0(void);
static void DAC960_LA_InterruptHandler(int, void *, Registers_T *);
static void DAC960_PG_InterruptHandler(int, void *, Registers_T *);
static void DAC960_PD_InterruptHandler(int, void *, Registers_T *);
+static void DAC960_P_InterruptHandler(int, void *, Registers_T *);
static void DAC960_V1_QueueMonitoringCommand(DAC960_Command_T *);
static void DAC960_V2_QueueMonitoringCommand(DAC960_Command_T *);
static void DAC960_MonitoringTimerFunction(unsigned long);
static int DAC960_Release(Inode_T *, File_T *);
static int DAC960_IOCTL(Inode_T *, File_T *, unsigned int, unsigned long);
static int DAC960_UserIOCTL(Inode_T *, File_T *, unsigned int, unsigned long);
-static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *);
+static void DAC960_ComputeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo);
static void DAC960_Message(DAC960_MessageLevel_T, unsigned char *,
DAC960_Controller_T *, ...);
static void DAC960_CreateProcEntries(void);
-
Computone Intelliport II/Plus Multiport Serial Driver
-----------------------------------------------------
-Release Notes For Linux Kernel 2.2
-These notes have been tested on Linux kernels 2.0 and 2.2.
-
-Please refer to Documentation/computone.txt for information on the driver
-that is included with the kernel sources.
-
-
-Version: 1.2.9
-Date: 04/12/2000
-Fixes and Updates: Doug McNash
-Historical Author: Andrew Manison
-Kernel Integration: Mike Warfield <mhw@wittsend.com>
-
-1. INTRODUCTION
-
-This driver supports the entire family of Intelliport II/Plus controllers
-with the exception of the MicroChannel controllers.
-
-This driver was developed on the v2.0.x Linux source tree and has been
-tested up to v2.2.14; it will probably not work with earlier v1.X kernels,
-and has not yet been tested on the v2.1.x tree. The most likely problems
-will be in patching the kernel sources to support the driver. For this
-reason there are 2 different patch files for 2.0.XX and 2.2.XX kernels.
-Make sure you use the right one!
-Note that a version (1.2.5) is included in the 2.2.12+ kernels so this
-will not be a new install but and upgrade.
-
-
-2. QUICK INSTALLATION
-
-Hardware - If you have an ISA card, find a free interrupt and io port.
- List those in use with `cat /proc/interrupts` and
- `cat /proc/ioports`. Set the card dip switches to that free
- address. You may need to configure your BIOS to reserve the
- irq for the ISA card. PCI and EISA parameters are set
- automagically and need only be set to nonzero values.
- Insert card into computer with the power off before or after
- driver installation.
-
-Software - New Installation
-
-Module installation:
-
-a) Obtain driver-kernel patch file
-b) Copy to the linux source tree root, Run ip2build (if not patch)
-c) Determine free irq/address to use if any (configure BIOS if need be)
-d) Run "make config" or "make menuconfig" or "make xconfig"
- Select (m) module for CONFIG_COMPUTONE under character
- devices. CONFIG_PCI and CONFIG_MODULES also may need to be set.
-e) Set address on ISA cards then:
- edit /usr/src/linux/drivers/char/ip2/ip2.h if needed
- or
- edit /etc/conf.modules (or /etc/modules.conf) if needed (module).
- or both to match this setting.
-f) Run "make dep"
-g) Run "make modules"
-h) Run "make modules_install"
-i) Run "/sbin/depmod -a"
-i) install driver using `modprobe ip2 <options>` (options listed below)
-j) run mkip2dev
-
-
-Kernel installation:
-
-a) Obtain driver-kernel patch file
-b) Copy to the linux source tree root, Run ip2build (if not patch)
-c) Determine free irq/address to use if any (configure BIOS if need be)
-d) Run "make config" or "make menuconfig" or "make xconfig"
- Select (y) kernel for CONFIG_COMPUTONE under character
- devices. CONFIG_PCI may need to be set if you have PCI bus.
-e) Set address on ISA cards then:
- edit /usr/src/linux/drivers/char/ip2/ip2.h
-f) Run "make dep"
-g) Run "make zImage" or whatever target you prefer.
-h) mv /usr/src/linux/arch/i386/boot/zImage to /boot.
-i) add new config for this kernel into /etc/lilo.conf, run "lilo"
-j) reboot using this kernel
-k) make and run ip2/mkip2dev
-
-Software - Upgrades
-
-a) Install new sources in proper location, usually /usr/src/linux/drivers/char
-b) Follow steps above to create new kernel or modules
-
-3. INSTALLATION
-
-Previously, the driver sources were packaged with a set of patch files
-to update the character drivers' makefile and configuration file, and other
-kernel source files. A build script (ip2build) was included which applies
-the patches if needed, and build any utilities needed.
-What you receive may be a single patch file in conventional kernel
-patch format build script. That form can also be applied by
-running patch -p1 < ThePatchFile. Otherwise the drivers source may be
-a tar file, then untar and run ip2build if a new installation.
-
-The driver can be installed as a module (recommended) or built into the
-kernel. This is selected as for other drivers through the `make config`
-command from the root of the Linux source tree. If the driver is built
-into the kernel you will need to edit the file ip2.h to match the boards
-you are installing. See that file for instructions. If the driver is
-installed as a module the configuration can also be specified on the
-modprobe command line as follows:
-
- modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4
-
-where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11,
-12,15) and addr1-4 are the base addresses for up to four controllers. If
-the irqs are not specified the driver uses the default in ip2/ip2.h (which
-selects polled mode). The io addresses are set to io=1 for PCI cards,i
-io=2 for EISA cards or io=[some valid ISA address] for ISA cards. If no
-base addresses are specified the defaults in ip2.h are used. If you are
-autoloading the driver module with kerneld or kmod the base addresses and
-interrupt number must also be set in ip2/ip2.h and recompile or just insert
-an options line in /etc/modules.conf or both. The command line takes
-precidence over the options line which takes precidence over the defaults
-in ip2.h.
-
-command line sample:
-
- modprobe ip2 io=1,0x328 irq=1,10
-
-/etc/modules.conf sample:
-
- options ip2 io=1,0x328 irq=1,10
- alias char-major-71 ip2
- alias char-major-72 ip2
- alias char-major-73 ip2
-
-the equivelant ip2.h:
-
-static ip2config_t ip2config =
-{
- {1,10,0,0},
- {
- 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */
- 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */
- 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */
- 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */
- }
-};
-
-Specifying an invalid or in-use ISA irq will default the driver into
-running in polled mode for that card. If all irq entries are 0 then
-all cards will operate in polled mode. Note that the PCI will be
-assigned it's irq by the BIOS and may not match what you specify.
-It must be non-zero otherwise it will be polled.
-
-Tarball Install:
-
-The whole tarfile should be untarred in the /usr/src/linux/drivers/char/
-directory. Most files required for the driver are placed in the ip2
-subdirectory. Then execute the script (for a new install only)
-
- ip2build
-
-which will patch the files.
-
-Kernel Patch Install:
-
- cd to the Linux source root, run patch -p1 < ThePatchFile.
-
-Now return to the root directory of the Linux
-source tree and run make config or make menuconfig. You will be prompted
-for the Computone drivers, either as a module or part of the kernel.
-If you have a PCI card you many need to select PCI bios support (CONFIG_PCI)
-if not enabled already. Ditto for CONFIG_MODULES if you use modules.
-
-If you select the driver as part of the kernel run :
-
- make depend
- make bzImage(,zlilo or whatever you do to create a bootable kernel)
-
-If you selected a module run :
-
- make modules && make modules_install
-
-The utility ip2mkdev creates all the device nodes required by the driver.
-For a device to be created it must be configured in the driver and the
-board must be installed. Only devices corresponding to real IntelliPort II
-ports are created. With multiple boards and expansion boxes this will
-leave gaps in the sequence of device names. ip2mkdev uses Linux tty naming
-conventions: ttyF0 - ttyF255 for normal devices, and cuf0 - cuf255 for
-callout devices. Note that the callout devices are going away in the
-future and that is what the warning messages are trying to tell you.
-
-4. USING THE DRIVERS
-
-As noted above, the driver implements the ports in accordance with Linux
-conventions, and the devices should be interchangeable with the standard
-serial devices. (This is a key point for problem reporting: please make
-sure that what you are trying do works on the ttySx/cuax ports first; then
-tell us what went wrong with the ip2 ports!)
-
-Higher speeds can be obtained using the setserial utility which remaps
-38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed.
-Intelliport II installations using the PowerPort expansion module can
-use the custom speed setting to select the highest speeds: 153,600 bps,
-230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for
-custom baud rate configuration is fixed at 921,600 for cards/expantion
-modules with ST654's and 115200 for those with Cirrus CD1400's. This
-corresponds to the maximum bit rates those chips are capable.
-For example if the baud base is 921600 and the baud divisor is 18 then
-the custom rate is 921600/18 = 51200 bps. See the setserial man page for
-complete details. Of course, if stty accepts the higher rates now you can
-use that as well as the standard ioctls().
-
-5. NOTES
-
-This is a release version of the driver, but it is impossible to test it
-in all configurations of Linux. If there is any anomalous behaviour that
-does not match the standard serial port's behaviour please let us know.
-
-Some installations report that characters fail to echo immediatly at a
-terminal if the kernel/modules are compiled with the CONFIG_M386 and
-the card is run in polling mode on a pentium class machine. Compiling
-with a more appropriate processor flag or running on interrupt would be
-the fix as well as the wise thing to do.
+Release Notes For Linux Kernel 2.2 and higher
+This file is now deprecated and will be removed at some point.
+Please refer to the file Documentation/computone.txt instead.
-Author: dougm@computone.com
-Testing: larryg@computone.com
-Support: support@computone.com
+Michael H. Warfield 08/12/2001
int
ip2_loadmain(int *, int *, unsigned char *, int ); // ref into ip2main.c
+/* Note: Add compiled in defaults to these arrays, not to the structure
+ in ip2/ip2.h any longer. That structure WILL get overridden
+ by these values, or command line values, or insmod values!!! =mhw=
+*/
+static int io[IP2_MAX_BOARDS]= { 0, 0, 0, 0 };
+static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 };
+static int poll_only = 0;
+
#ifdef MODULE
-
#include <linux/autoconf.h>
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
# define MODVERSIONS
# include <linux/modversions.h>
#endif
-static int io[IP2_MAX_BOARDS]= { 0,};
-static int irq[IP2_MAX_BOARDS] = { 0,};
-
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
MODULE_AUTHOR("Doug McNash");
MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards");
MODULE_PARM(io,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i");
MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards");
+ MODULE_PARM(poll_only,"1i");
+ MODULE_PARM_DESC(poll_only,"Do not use card interrupts");
# endif /* LINUX_VERSION */
MOD_INC_USE_COUNT; // hold till done
+ if( poll_only ) {
+ /* Hard lock the interrupts to zero */
+ irq[0] = irq[1] = irq[2] = irq[3] = 0;
+ }
+
rc = ip2_loadmain(io,irq,(unsigned char *)fip_firm,sizeof(fip_firm));
// The call to lock and load main, create dep
int
ip2_init(void)
{
- // call to this is int tty_io.c so we need this
+ // call to this is in tty_io.c so we need this
return 0;
}
# define NULL ((void *) 0)
#endif
+/******************************************************************************
+ * ip2_setup:
+ * str: kernel command line string
+ *
+ * Can't autoprobe the boards so user must specify configuration on
+ * kernel command line. Sane people build it modular but the others
+ * come here.
+ *
+ * Alternating pairs of io,irq for up to 4 boards.
+ * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
+ *
+ * io=0 => No board
+ * io=1 => PCI
+ * io=2 => EISA
+ * else => ISA I/O address
+ *
+ * irq=0 or invalid for ISA will revert to polling mode
+ *
+ * Any value = -1, do not overwrite compiled in value.
+ *
+ ******************************************************************************/
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,13)
+void __init ip2_setup(char *str)
+{
+ int ints[10]; /* 4 boards, 2 parameters + 2 */
+ int i, j;
+
+ str = get_options (str, ARRAY_SIZE(ints), ints);
+#else
+void __init ip2_setup(char *str, int *ints)
+{
+ int i, j;
+#endif
+
+ for( i = 0, j = 1; i < 4; i++ ) {
+ if( j > ints[0] ) {
+ break;
+ }
+ if( ints[j] >= 0 ) {
+ io[i] = ints[j];
+ }
+ j++;
+ if( j > ints[0] ) {
+ break;
+ }
+ if( ints[j] >= 0 ) {
+ irq[i] = ints[j];
+ }
+ j++;
+ }
+}
+
int
ip2_init(void) {
- return ip2_loadmain(NULL,NULL,(unsigned char *)fip_firm,sizeof(fip_firm));
+ return ip2_loadmain(io,irq,(unsigned char *)fip_firm,sizeof(fip_firm));
}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13))
+__setup("ip2=", ip2_setup);
+__initcall(ip2_init);
+#endif
+
#endif /* !MODULE */
pB->i2eStartMail = iiGetMail(pB);
+ // Throw it away and clear the mailbox structure element
+ pB->i2eStartMail = NO_MAIL_HERE;
+
// Everything is ok now, return with good status/
pB->i2eValid = I2E_MAGIC;
static void
ii2DelayTimer(unsigned int mseconds)
{
+ wait_queue_t wait;
+
+ init_waitqueue_entry(&wait, current);
+
init_timer ( pDelayTimer );
+ add_wait_queue(&pDelayWait, &wait);
+
+ set_current_state( TASK_INTERRUPTIBLE );
+
pDelayTimer->expires = jiffies + ( mseconds + 9 ) / 10;
pDelayTimer->function = ii2DelayWakeup;
pDelayTimer->data = 0;
add_timer ( pDelayTimer );
- interruptible_sleep_on ( &pDelayWait );
+
+ schedule();
+
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pDelayWait, &wait);
+
del_timer ( pDelayTimer );
}
spinlock_t read_fifo_spinlock;
spinlock_t write_fifo_spinlock;
+// For queuing interupt bottom half handlers. /\/\|=mhw=|\/\/
+ struct tq_struct tqueue_interrupt;
+
+ struct timer_list SendPendingTimer; // Used by iiSendPending
+ unsigned int SendPendingRetry;
+
#ifdef CONFIG_DEVFS_FS
/* Device handles into devfs */
devfs_handle_t devfs_ipl_handle;
pB->i2eWaitingForEmptyFifo |=
(pB->i2eOutMailWaiting & MB_OUT_STUFFED);
pB->i2eOutMailWaiting = 0;
+ pB->SendPendingRetry = 0;
+ } else {
+/* The only time we hit this area is when "iiTrySendMail" has
+ failed. That only occurs when the outbound mailbox is
+ still busy with the last message. We take a short breather
+ to let the board catch up with itself and then try again.
+ 16 Retries is the limit - then we got a borked board.
+ /\/\|=mhw=|\/\/ */
+
+ if( ++pB->SendPendingRetry < 16 ) {
+
+ init_timer( &(pB->SendPendingTimer) );
+ pB->SendPendingTimer.expires = jiffies + 1;
+ pB->SendPendingTimer.function = (void*)(unsigned long)iiSendPendingMail;
+ pB->SendPendingTimer.data = (unsigned long)pB;
+ add_timer( &(pB->SendPendingTimer) );
+ } else {
+ printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" );
+ }
}
}
}
pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0;
pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0;
+ pB->SendPendingRetry = 0;
+
memset ( pCh, 0, sizeof (i2ChanStr) * nChannels );
for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf);
pCh->ClosingDelay = 5*HZ/10;
pCh->ClosingWaitTime = 30*HZ;
-#ifdef USE_IQ
// Initialize task queue objects
pCh->tqueue_input.routine = (void(*)(void*)) do_input;
pCh->tqueue_input.data = pCh;
pCh->tqueue_status.routine = (void(*)(void*)) do_status;
pCh->tqueue_status.data = pCh;
-#endif
pCh->trace = ip2trace;
static void
i2DrainOutput(i2ChanStrPtr pCh, int timeout)
{
+ wait_queue_t wait;
i2eBordStrPtr pB;
#ifdef IP2DEBUG_TRACE
}
i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ );
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&(pCh->pBookmarkWait), &wait);
+ set_current_state( TASK_INTERRUPTIBLE );
+
serviceOutgoingFifo( pB );
- interruptible_sleep_on( &(pCh->pBookmarkWait) );
+ schedule(); // Now we take our interruptible sleep on
+
+ // Clean up the queue
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&(pCh->pBookmarkWait), &wait);
// if expires == 0 then timer poped, then do not need to del_timer
if ((timeout > 0) && pCh->BookmarkTimer.expires &&
unsigned long flags;
- inmail = iiGetMail(pB);
+ /* This should be atomic because of the way we are called... */
+ if (NO_MAIL_HERE == ( inmail = pB->i2eStartMail ) ) {
+ inmail = iiGetMail(pB);
+ }
+ pB->i2eStartMail = NO_MAIL_HERE;
#ifdef IP2DEBUG_TRACE
ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail );
* /etc/modules.conf or /etc/conf.modules and load with modprobe, kerneld or
* kmod, the kernel module loader
*/
+
+ /* This structure is NOW always initialized when the driver is initialized.
+ * Compiled in defaults MUST be added to the io and irq arrays in
+ * ip2.c. Those values are configurable from insmod parameters in the
+ * case of modules or from command line parameters (ip2=io,irq) when
+ * compiled in.
+ */
+
static ip2config_t ip2config =
{
{0,0,0,0}, // irqs
{ // Addresses
+ /* Do NOT set compile time defaults HERE! Use the arrays in
+ ip2.c! These WILL be overwritten! =mhw= */
0x0000, // Board 0, ttyF0 - ttyF63
0x0000, // Board 1, ttyF64 - ttyF127
0x0000, // Board 2, ttyF128 - ttyF191
//
// Done:
//
+// 1.2.11 /\/\|=mhw=|\/\/
+// Clean up potential NULL pointer dereferences
+// Clean up devfs registration
+// Add kernel command line parsing for io and irq
+// Compile defaults for io and irq are now set in ip2.c not ip2/ip2.h!
+// Reworked poll_only hack for explicit parameter setting
+// You must now EXPLICITLY set poll_only = 1 or set all irqs to 0
+// Merged ip2_loadmain and old_ip2_init
+// Converted all instances of interruptible_sleep_on into queue calls
+// Most of these had no race conditions but better to clean up now
+//
+// 1.2.10 /\/\|=mhw=|\/\/
+// Fixed the bottom half interrupt handler and enabled USE_IQI
+// to split the interrupt handler into a formal top-half / bottom-half
+// Fixed timing window on high speed processors that queued messages to
+// the outbound mail fifo faster than the board could handle.
+//
// 1.2.9
// Four box EX was barfing on >128k kmalloc, made structure smaller by
// reducing output buffer size
/* String constants to identify ourselves */
static char *pcName = "Computone IntelliPort Plus multiport driver";
-static char *pcVersion = "1.2.9";
+static char *pcVersion = "1.2.11";
/* String constants for port names */
static char *pcDriver_name = "ip2";
void cleanup_module(void);
#endif
-int old_ip2_init(void);
-
/* Private (static) functions */
static int ip2_open(PTTY, struct file *);
static void ip2_close(PTTY, struct file *);
static void ip2_hangup(PTTY);
static void set_irq(int, int);
+static void ip2_interrupt_bh(i2eBordStrPtr pB);
static void ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs);
static void ip2_poll(unsigned long arg);
static inline void service_all_boards(void);
/* NULL, NULL 2.2 */
};
-static long irq_counter = 0;
-static long bh_counter = 0;
+static unsigned long irq_counter = 0;
+static unsigned long bh_counter = 0;
// Use immediate queue to service interrupts
-//#define USE_IQI // PCI&2.2 needs work
+#define USE_IQI
//#define USE_IQ // PCI&2.2 needs work
/* The timer_list entry for our poll routine. If interrupt operation is not
static char rirqs[IP2_MAX_BOARDS] = {0,};
static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0};
-/******************************************************************************/
-/* Initialisation Section */
-/******************************************************************************/
-int
-ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
-{
- int i;
- /* process command line arguments to modprobe or insmod i.e. iop & irqp */
- /* otherwise ip2config is initialized by what's in ip2/ip2.h */
- /* command line trumps initialization in ip2.h */
- /* first two args are null if builtin to kernel */
- if ((irqp != NULL) || (iop != NULL)) {
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if (irqp && irqp[i]) {
- ip2config.irq[i] = irqp[i];
- }
- if (iop && iop[i]) {
- ip2config.addr[i] = iop[i];
- }
- }
- }
- Fip_firmware = firmware;
- Fip_firmware_size = firmsize;
- return old_ip2_init();
-}
-
// Some functions to keep track of what irq's we have
__initfunc(static int
rirqs[iindx++] = irq;
}
+#ifdef MODULE
__initfunc( static int
clear_requested_irq( char irq ))
{
}
return 0;
}
+#endif
__initfunc( static int
have_requested_irq( char irq ))
#ifdef IP2DEBUG_INIT
printk (KERN_DEBUG "Loading module ...\n" );
#endif
- //was return old_ip2_init();
return 0;
}
#endif /* MODULE */
#endif /* MODULE */
/******************************************************************************/
-/* Function: old_ip2_init() */
+/* Function: ip2_loadmain() */
/* Parameters: irq, io from command line of insmod et. al. */
+/* pointer to fip firmware and firmware size for boards */
/* Returns: Success (0) */
/* */
/* Description: */
#define IP2_SA_FLAGS 0
__initfunc( int
-old_ip2_init(void))
+ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) )
{
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_handle = NULL;
ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 );
#endif
+ /* process command line arguments to modprobe or
+ insmod i.e. iop & irqp */
+ /* irqp and iop should ALWAYS be specified now... But we check
+ them individually just to be sure, anyways... */
+ for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+ if (iop) {
+ ip2config.addr[i] = iop[i];
+ if (irqp) {
+ if( irqp[i] >= 0 ) {
+ ip2config.irq[i] = irqp[i];
+ } else {
+ ip2config.irq[i] = 0;
+ }
+ // This is a little bit of a hack. If poll_only=1 on command
+ // line back in ip2.c OR all IRQs on all specified boards are
+ // explicitly set to 0, then drop to poll only mode and override
+ // PCI or EISA interrupts. This superceeds the old hack of
+ // triggering if all interrupts were zero (like da default).
+ // Still a hack but less prone to random acts of terrorism.
+ //
+ // What we really should do, now that the IRQ default is set
+ // to -1, is to use 0 as a hard coded, do not probe.
+ //
+ // /\/\|=mhw=|\/\/
+ poll_only |= irqp[i];
+ }
+ }
+ }
+ poll_only = !poll_only;
+
+ Fip_firmware = firmware;
+ Fip_firmware_size = firmsize;
+
/* Announce our presence */
printk( KERN_INFO "%s version %s\n", pcName, pcVersion );
}
loaded++;
- /* if all irq config is zero we shall poll_only */
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- poll_only |= ip2config.irq[i];
- }
- poll_only = !poll_only;
-
/* Initialise the iiEllis subsystem. */
iiEllisInit();
pcibios_read_config_byte(pci_bus, pci_devfn,
PCI_INTERRUPT_LINE, &pci_irq);
- if (!is_valid_irq(pci_irq)) {
- printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
- pci_irq = 0;
- }
+// If the PCI BIOS assigned it, lets try and use it. If we
+// can't acquire it or it screws up, deal with it then.
+
+// if (!is_valid_irq(pci_irq)) {
+// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
+// pci_irq = 0;
+// }
ip2config.irq[i] = pci_irq;
} else { // ann error
ip2config.addr[i] = 0;
status =
pci_read_config_byte(pci_dev_i, PCI_INTERRUPT_LINE, &pci_irq);
- if (!is_valid_irq(pci_irq)) {
- printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
- pci_irq = 0;
- }
+// If the PCI BIOS assigned it, lets try and use it. If we
+// can't acquire it or it screws up, deal with it then.
+
+// if (!is_valid_irq(pci_irq)) {
+// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
+// pci_irq = 0;
+// }
ip2config.irq[i] = pci_irq;
} else { // ann error
ip2config.addr[i] = 0;
}
#ifdef CONFIG_DEVFS_FS
- sprintf( name, "ipl%d", i );
- i2BoardPtrTable[i]->devfs_ipl_handle =
- devfs_register (devfs_handle, name, 0,
- DEVFS_FL_NONE,
- IP2_IPL_MAJOR, 4 * i,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR,
- 0, 0, &ip2_ipl, NULL);
-
- sprintf( name, "stat%d", i );
- i2BoardPtrTable[i]->devfs_stat_handle =
- devfs_register (devfs_handle, name, 0,
- DEVFS_FL_NONE,
- IP2_IPL_MAJOR, 4 * i + 1,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR,
- 0, 0, &ip2_ipl, NULL);
-
- for ( box = 0; box < ABS_MAX_BOXES; ++box )
- {
- for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
+ if ( NULL != ( pB = i2BoardPtrTable[i] ) ) {
+ sprintf( name, "ipl%d", i );
+ pB->devfs_ipl_handle =
+ devfs_register (devfs_handle, name, 0,
+ DEVFS_FL_NONE,
+ IP2_IPL_MAJOR, 4 * i,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR,
+ 0, 0, &ip2_ipl, NULL);
+
+ sprintf( name, "stat%d", i );
+ pB->devfs_stat_handle =
+ devfs_register (devfs_handle, name, 0,
+ DEVFS_FL_NONE,
+ IP2_IPL_MAJOR, 4 * i + 1,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR,
+ 0, 0, &ip2_ipl, NULL);
+
+ for ( box = 0; box < ABS_MAX_BOXES; ++box )
{
- if ( pB->i2eChannelMap[box] & (1 << j) )
+ for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
{
- tty_register_devfs(&ip2_tty_driver,
- 0, j + ABS_BIGGEST_BOX *
+ if ( pB->i2eChannelMap[box] & (1 << j) )
+ {
+ tty_register_devfs(&ip2_tty_driver,
+ 0, j + ABS_BIGGEST_BOX *
(box+i*ABS_MAX_BOXES));
- tty_register_devfs(&ip2_callout_driver,
- 0, j + ABS_BIGGEST_BOX *
+ tty_register_devfs(&ip2_callout_driver,
+ 0, j + ABS_BIGGEST_BOX *
(box+i*ABS_MAX_BOXES));
+ }
}
}
}
#endif
if (poll_only) {
- ip2config.irq[i] = CIR_POLL;
+// Poll only forces driver to only use polling and
+// to ignore the probed PCI or EISA interrupts.
+ ip2config.irq[i] = CIR_POLL;
}
if ( ip2config.irq[i] == CIR_POLL ) {
retry:
if ( !iiInitialize ( pB ) ) {
printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n",
pB->i2eBase, pB->i2eError );
- kfree ( pB );
- i2BoardPtrTable[boardnum] = NULL;
- return;
+ goto err_initialize;
}
- printk(KERN_INFO "Board %d: addr=0x%x irq=%d ", boardnum + 1,
+ printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1,
ip2config.addr[boardnum], ip2config.irq[boardnum] );
if (0 != ( rc = check_region( ip2config.addr[boardnum], 8))) {
- i2BoardPtrTable[boardnum] = NULL;
- printk(KERN_ERR "bad addr=0x%x rc = %d\n",
+ printk(KERN_ERR "IP2: bad addr=0x%x rc = %d\n",
ip2config.addr[boardnum], rc );
- return;
+ goto err_initialize;
}
request_region( ip2config.addr[boardnum], 8, pcName );
if ( iiDownloadAll ( pB, (loadHdrStrPtr)Fip_firmware, 1, Fip_firmware_size )
!= II_DOWN_GOOD ) {
- printk ( KERN_ERR "IP2:failed to download loadware " );
+ printk ( KERN_ERR "IP2: failed to download loadware\n" );
+ goto err_release_region;
} else {
- printk ( KERN_INFO "fv=%d.%d.%d lv=%d.%d.%d\n",
+ printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n",
pB->i2ePom.e.porVersion,
pB->i2ePom.e.porRevision,
pB->i2ePom.e.porSubRev, pB->i2eLVersion,
switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) {
default:
- printk( KERN_ERR "IP2: Unknown board type, ID = %x",
+ printk( KERN_ERR "IP2: Unknown board type, ID = %x\n",
pB->i2ePom.e.porID );
nports = 0;
- goto ex_exit;
+ goto err_release_region;
break;
case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */
- printk ( KERN_INFO "ISA-4" );
+ printk ( KERN_INFO "IP2: ISA-4\n" );
nports = 4;
break;
case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */
- printk ( KERN_INFO "ISA-8 std" );
+ printk ( KERN_INFO "IP2: ISA-8 std\n" );
nports = 8;
break;
case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */
- printk ( KERN_INFO "ISA-8 RJ11" );
+ printk ( KERN_INFO "IP2: ISA-8 RJ11\n" );
nports = 8;
break;
}
DevTableMem[boardnum] = pCh =
kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL );
+ if ( !pCh ) {
+ printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
+ goto err_release_region;
+ }
if ( !i2InitChannels( pB, nports, pCh ) ) {
- printk(KERN_ERR "i2InitChannels failed: %d\n",pB->i2eError);
+ printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
+ kfree ( pCh );
+ goto err_release_region;
}
pB->i2eChannelPtr = &DevTable[portnum];
pB->i2eChannelCnt = ABS_MOST_PORTS;
}
}
}
- printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit",
+ printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n",
nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 );
}
goto ex_exit;
- break;
}
DevTableMem[boardnum] = pCh =
kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL );
+ if ( !pCh ) {
+ printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
+ goto err_release_region;
+ }
pB->i2eChannelPtr = pCh;
pB->i2eChannelCnt = nports;
- i2InitChannels ( pB, pB->i2eChannelCnt, pCh );
+ if ( !i2InitChannels( pB, nports, pCh ) ) {
+ printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
+ kfree ( pCh );
+ goto err_release_region;
+ }
pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum];
for( i = 0; i < pB->i2eChannelCnt; ++i ) {
pCh++;
}
ex_exit:
- printk ( KERN_INFO "\n" );
+ pB->tqueue_interrupt.routine = (void(*)(void*)) ip2_interrupt_bh;
+ pB->tqueue_interrupt.data = pB;
+
+ return;
+
+err_release_region:
+ release_region(ip2config.addr[boardnum], 8);
+err_initialize:
+ kfree ( pB );
+ i2BoardPtrTable[boardnum] = NULL;
+ return;
}
/******************************************************************************/
}
-#ifdef USE_IQI
-static struct tq_struct
-senior_service =
-{ // it's the death that worse than fate
- NULL,
- 0,
- (void(*)(void*)) service_all_boards,
- NULL, //later - board address XXX
-};
-#endif
+/******************************************************************************/
+/* Function: ip2_interrupt_bh(pB) */
+/* Parameters: pB - pointer to the board structure */
+/* Returns: Nothing */
+/* */
+/* Description: */
+/* Service the board in a bottom half interrupt handler and then */
+/* reenable the board's interrupts if it has an IRQ number */
+/* */
+/******************************************************************************/
+static void
+ip2_interrupt_bh(i2eBordStrPtr pB)
+{
+// pB better well be set or we have a problem! We can only get
+// here from the IMMEDIATE queue. Here, we process the boards.
+// Checking pB doesn't cost much and it saves us from the sanity checkers.
+
+ bh_counter++;
+
+ if ( pB ) {
+ i2ServiceBoard( pB );
+ if( pB->i2eUsingIrq ) {
+// Re-enable his interrupts
+ iiEnableMailIrq(pB);
+ }
+ }
+}
+
/******************************************************************************/
/* Function: ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) */
/* */
/* Description: */
/* */
+/* Our task here is simply to identify each board which needs servicing. */
+/* If we are queuing then, queue it to be serviced, and disable its irq */
+/* mask otherwise process the board directly. */
+/* */
+/* We could queue by IRQ but that just complicates things on both ends */
+/* with very little gain in performance (how many instructions does */
+/* it take to iterate on the immediate queue). */
+/* */
/* */
/******************************************************************************/
static void
ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq );
#endif
-#ifdef USE_IQI
-
- queue_task(&senior_service, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
-
-#else
/* Service just the boards on the list using this irq */
for( i = 0; i < i2nBoards; ++i ) {
pB = i2BoardPtrTable[i];
+
+// Only process those boards which match our IRQ.
+// IRQ = 0 for polled boards, we won't poll "IRQ" boards
+
if ( pB && (pB->i2eUsingIrq == irq) ) {
+#ifdef USE_IQI
+
+ if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) {
+// Disable his interrupt (will be enabled when serviced)
+// This is mostly to protect from reentrancy.
+ iiDisableMailIrq(pB);
+
+// Park the board on the immediate queue for processing.
+ queue_task(&pB->tqueue_interrupt, &tq_immediate);
+
+// Make sure the immediate queue is flagged to fire.
+ mark_bh(IMMEDIATE_BH);
+ }
+#else
+// We are using immediate servicing here. This sucks and can
+// cause all sorts of havoc with ppp and others. The failsafe
+// check on iiSendPendingMail could also throw a hairball.
i2ServiceBoard( pB );
+#endif /* USE_IQI */
}
}
-#endif /* USE_IQI */
-
++irq_counter;
#ifdef IP2DEBUG_TRACE
#endif
TimerOn = 0; // it's the truth but not checked in service
- bh_counter++;
-
-#ifdef USE_IQI
-
- queue_task(&senior_service, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
-
-#else
- // Just polled boards, service_all might be better
+ // Just polled boards, IRQ = 0 will hit all non-interrupt boards.
+ // It will NOT poll boards handled by hard interrupts.
+ // The issue of queued BH interrups is handled in ip2_interrupt().
ip2_interrupt(0, NULL, NULL);
-#endif /* USE_IQI */
-
PollTimer.expires = POLL_TIMEOUT;
add_timer( &PollTimer );
TimerOn = 1;
static int
ip2_open( PTTY tty, struct file *pFile )
{
+ wait_queue_t wait;
int rc = 0;
int do_clocal = 0;
i2ChanStrPtr pCh = DevTable[MINOR(tty->device)];
* 1. If the port is in the middle of closing wait for the completion
* and then return the appropriate error.
*/
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pCh->close_wait, &wait);
+ set_current_state( TASK_INTERRUPTIBLE );
+
if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
if ( pCh->flags & ASYNC_CLOSING ) {
- interruptible_sleep_on( &pCh->close_wait);
+ schedule();
}
if ( tty_hung_up_p(pFile) ) {
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->close_wait, &wait);
return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS;
}
}
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->close_wait, &wait);
+
/*
* 2. If this is a callout device, make sure the normal port is not in
* use, and that someone else doesn't have the callout device locked.
#endif
++pCh->wopen;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pCh->open_wait, &wait);
+
for(;;) {
if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE)) {
i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
pCh->dataSetOut |= (I2_DTR | I2_RTS);
+ set_current_state( TASK_INTERRUPTIBLE );
serviceOutgoingFifo( pCh->pMyBord );
}
if ( tty_hung_up_p(pFile) ) {
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->dss_now_wait, &wait);
return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS;
}
if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) &&
rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
break;
}
- interruptible_sleep_on(&pCh->open_wait);
+ schedule();
}
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->dss_now_wait, &wait);
+
--pCh->wopen; //why count?
#ifdef IP2DEBUG_TRACE
ip2trace (CHANN, ITRC_OPEN, 4, 0 );
{
i2ChanStrPtr pCh = tty->driver_data;
+ if( !pCh ) {
+ return;
+ }
+
#ifdef IP2DEBUG_TRACE
ip2trace (CHANN, ITRC_HANGUP, ITRC_ENTER, 0 );
#endif
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP);
i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
- if ( !tty || (tty->termios->c_cflag & HUPCL) ) {
+ if ( (tty->termios->c_cflag & HUPCL) ) {
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN);
pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
static int
ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg )
{
+ wait_queue_t wait;
i2ChanStrPtr pCh = DevTable[MINOR(tty->device)];
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
ip2trace (CHANN, ITRC_IOCTL, 8, 1, rc );
#endif
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DSS_NOW);
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pCh->dss_now_wait, &wait);
+ set_current_state( TASK_INTERRUPTIBLE );
+
serviceOutgoingFifo( pCh->pMyBord );
- interruptible_sleep_on(&pCh->dss_now_wait);
+
+ schedule();
+
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->dss_now_wait, &wait);
+
if (signal_pending(current)) {
return -EINTR;
}
restore_flags(flags);
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4,
CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP);
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pCh->delta_msr_wait, &wait);
+ set_current_state( TASK_INTERRUPTIBLE );
+
serviceOutgoingFifo( pCh->pMyBord );
for(;;) {
#ifdef IP2DEBUG_TRACE
ip2trace (CHANN, ITRC_IOCTL, 10, 0 );
#endif
- interruptible_sleep_on(&pCh->delta_msr_wait);
+ schedule();
#ifdef IP2DEBUG_TRACE
ip2trace (CHANN, ITRC_IOCTL, 11, 0 );
#endif
}
cprev = cnow;
}
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->delta_msr_wait, &wait);
+
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 3,
CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
if ( ! (pCh->flags & ASYNC_CHECK_CD)) {
/*
- dmfe.c: Version 1.28 01/18/2000
+ dmfe.c: Version 1.36 04/20/2001
- A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
+ A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast
+ ethernet driver for Linux.
Copyright (C) 1997 Sten Wang
This program is free software; you can redistribute it and/or
Compiler command:
- "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall
+ "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/include -Wall
-Wstrict-prototypes -O6 -c dmfe.c"
OR
- "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall
- -Wstrict-prototypes -O6 -c dmfe.c"
+ "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -I/usr/src/linux/include
+ -Wall -Wstrict-prototypes -O6 -c dmfe.c"
+ OR
+ "gcc -D__SMP__ -DMODULE -DMODVERSIONS -D__KERNEL__
+ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O6 -c dmfe.c"
+
+ Note: "O" of -O6 is a capital 'o', not a '0' (zero)
+
The following steps teach you how to active DM9102 board:
1. Used the upper compiler command to compile dmfe.c
"insmod dmfe mode=1" ;;Force 100M Half Duplex
"insmod dmfe mode=4" ;;Force 10M Full Duplex
"insmod dmfe mode=5" ;;Force 100M Full Duplex
+ "insmod dmfe mode=100" ;;Force 1M HomeRun(DM9801)
+ "insmod dmfe mode=200" ;;Force 1M LongRun(DM9802)
3. config a dm9102 network interface
"ifconfig eth0 172.22.3.18"
- ^^^^^^^^^^^ your IP address
+ ^^^^^^^^^^^ your IP address
4. active the IP routing table
"route add -net 172.22.3.0 eth0"
5. Well done. Your DM9102 adapter actived now.
+
+ DAVICOM Web-Site: http://www.davicom.com.tw
+
Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
- Date: 10/28,1998
+ Date: 03/08/2001
(C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
-
+
Cleaned up for kernel merge by Alan Cox (alan@redhat.com)
-
+
*/
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif /* */
#include <linux/module.h>
#include <linux/kernel.h>
#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */
#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */
#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */
+#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */
#define DMFE_SUCC 0
#define DM9102_IO_SIZE 0x80
#define DM9102A_IO_SIZE 0x100
-#define TX_FREE_DESC_CNT 0xc /* Tx packet count */
#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
-#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */
-#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT
+#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */
+#define TX_FREE_DESC_CNT (TX_DESC_CNT-2) /* Max TX packet count */
+#define TX_WAKE_DESC_CNT (TX_DESC_CNT-3) /* TX wakeup count */
+#define DESC_ALL_CNT (TX_DESC_CNT+RX_DESC_CNT)
#define TX_BUF_ALLOC 0x600
#define RX_ALLOC_SIZE 0x620
#define DM910X_RESET 1
-#define CR6_DEFAULT 0x00280000 /* SF, HD */
-#define CR7_DEFAULT 0x1a2cd
+#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */
+#define CR6_DEFAULT 0x00080000 /* HD */
+#define CR7_DEFAULT 0x180c1
#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
#define MAX_PACKET_SIZE 1514
#define DMFE_MAX_MULTICAST 14
-#define RX_MAX_TRAFFIC 0x14000
+#define RX_COPY_SIZE 100
#define MAX_CHECK_PACKET 0x8000
+#define DM9801_NOISE_FLOOR 8
+#define DM9802_NOISE_FLOOR 5
#define DMFE_10MHF 0
#define DMFE_100MHF 1
#define DMFE_10MFD 4
#define DMFE_100MFD 5
#define DMFE_AUTO 8
+#define DMFE_1M_HPNA 0x10
+#define DMFE_1M_LONG 0x20
-#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT ((HZ*3)/2) /* tx packet time-out time 1.5 s" */
+#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */
+#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */
+#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */
+#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */
+#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */
+#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */
-#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
+#define DMFE_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT (HZ * 1.5) /* tx packet time-out time 1.5 s" */
+#define DMFE_TX_KICK (HZ * 0.5) /* tx packet Kick-out time 0.5 s" */
-#define DELAY_5US udelay(5) /* udelay scale 1 usec */
+#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk(KERN_ERR "dmfe: %s %x\n", msg, vaule)
-#define DELAY_1US udelay(1) /* udelay scale 1 usec */
+#define DELAY_5US udelay(5) /* udelay scale 1 usec */
-#define SHOW_MEDIA_TYPE(mode) printk(KERN_WARNING "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+#define DELAY_1US udelay(1) /* udelay scale 1 usec */
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
/* CR9 definition: SROM/MII */
#define CR9_SROM_READ 0x4800
#define PHY_DATA_0 0x00000
#define MDCLKH 0x10000
+#define PHY_POWER_DOWN 0x800
+
+#define SROM_V41_CODE 0x14
+
#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
u32 next_rx_desc;
u32 reserved;
};
-
struct dmfe_board_info {
u32 chip_id; /* Chip vendor/Device ID */
- u32 chip_revesion; /* Chip revesion */
+ u32 chip_revision; /* Chip revision */
struct device *next_dev; /* next device */
struct pci_dev *net_dev; /* PCI device */
- unsigned long ioaddr; /* I/O base address */
+ u32 ioaddr; /* I/O base address */
u32 cr0_data;
u32 cr5_data;
u32 cr6_data;
u32 cr7_data;
u32 cr15_data;
-
+
/* descriptor pointer */
unsigned char *buf_pool_ptr; /* Tx buffer pool memory */
unsigned char *buf_pool_start; /* Tx buffer pool align dword */
u32 tx_queue_cnt; /* wait to send packet count */
u32 rx_avail_cnt; /* available rx descriptor count */
u32 interval_rx_cnt; /* rx packet count a callback time */
-
- u16 phy_id2; /* Phyxcer ID2 */
-
+ u16 HPNA_command; /* For HPNA register 16 */
+ u16 HPNA_timer; /* For HPNA remote device check */
+ u16 dbug_cnt;
+ u16 NIC_capability; /* NIC media capability */
+ u16 PHY_reg4; /* Saved Phyxcer register 4 value */
+ u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */
+ u8 chip_type; /* Keep DM9102A chip type */
u8 media_mode; /* user specify media mode */
u8 op_mode; /* real work media mode */
u8 phy_addr;
u8 link_failed; /* Ever link failed */
u8 wait_reset; /* Hardware failed, need to reset */
- u8 in_reset_state; /* Now driver in reset routine */
- u8 rx_error_cnt; /* received abnormal case count */
u8 dm910x_chk_mode; /* Operating mode check */
+ u8 first_in_callback; /* Flag to record state */
struct timer_list timer;
struct enet_statistics stats; /* statistic counter */
+/* Driver defined statistic counter */
+ unsigned long tx_fifo_underrun;
+ unsigned long tx_loss_carrier;
+ unsigned long tx_no_carrier;
+ unsigned long tx_late_collision;
+ unsigned long tx_excessive_collision;
+ unsigned long tx_jabber_timeout;
+ unsigned long reset_count;
+ unsigned long reset_cr8;
+ unsigned long reset_fatal;
+ unsigned long reset_TXtimeout;
unsigned char srom[128];
};
/* Global variable declaration ----------------------------- */
static int dmfe_debug = 0;
-static unsigned char dmfe_media_mode = 8;
+static unsigned char dmfe_media_mode = DMFE_AUTO;
static struct device *dmfe_root_dev = NULL; /* First device */
static u32 dmfe_cr6_user_set = 0;
static unsigned char mode = 8;
static u8 chkmode = 1;
+static u8 HPNA_mode = 0; /* Default: Low Power/High Speed */
+static u8 HPNA_rx_cmd = 0; /* Default: Disable Rx remote command */
+static u8 HPNA_tx_cmd = 0; /* Default: Don't issue remote command */
+static u8 HPNA_NoiseFloor = 0; /* Default: HPNA NoiseFloor */
+static u8 SF_mode = 0; /* Special Function: 1:VLAN, 2:RX Flow Control
+ 4: TX pause packet */
+
static unsigned long CrcTable[256] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
};
/* function declaration ------------------------------------- */
+int dmfe_reg_board(struct device *);
int dmfe_probe(struct device *);
static int dmfe_open(struct device *);
static int dmfe_start_xmit(struct sk_buff *, struct device *);
static void phy_write(u32, u8, u8, u16, u32);
static void phy_write_1bit(u32, u32);
static u16 phy_read_1bit(u32);
-static void dmfe_sense_speed(struct dmfe_board_info *);
+static u8 dmfe_sense_speed(struct dmfe_board_info *);
static void dmfe_process_mode(struct dmfe_board_info *);
static void dmfe_timer(unsigned long);
static void dmfe_rx_packet(struct device *, struct dmfe_board_info *);
+static void dmfe_free_tx_pkt(struct device *, struct dmfe_board_info *);
static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *);
static void dmfe_dynamic_reset(struct device *);
static void dmfe_free_rxbuffer(struct dmfe_board_info *);
static void dmfe_init_dm910x(struct device *);
static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
+static void dmfe_parse_srom(struct dmfe_board_info *);
+static void dmfe_program_DM9801(struct dmfe_board_info *, int);
+static void dmfe_program_DM9802(struct dmfe_board_info *);
+static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info *);
+static void dmfe_set_phyxcer(struct dmfe_board_info *);
/* DM910X network baord routine ---------------------------- */
/*
- Search DM910X board ,allocate space and register it
+ Search DM910X board, allocate space and register it
*/
+int dmfe_reg_board(struct device *dev) /* For Kernel 2.2X */
+{
+ return dmfe_probe(dev);
+}
+
int dmfe_probe(struct device *dev)
{
- unsigned long pci_iobase;
+ u32 pci_iobase;
u16 dm9102_count = 0;
u8 pci_irqline;
static int index = 0; /* For multiple call */
return -ENODEV;
index = 0;
- while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
- {
- u32 pci_id;
- u32 dev_rev;
+ while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev))) {
+ u32 pci_id, dev_rev, pci_pmr;
u8 pci_cmd;
index++;
if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
continue;
-
- if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
+ if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID) && (pci_id != PCI_DM9009_ID))
continue;
/* read PCI IO base address and IRQ to check */
- pci_iobase = net_dev->base_address[0];
- pci_irqline = net_dev->irq;
- pci_iobase &= ~0x7f; /* mask off bit0~6 */
+ pci_read_config_dword(net_dev, PCI_BASE_ADDRESS_0, &pci_iobase);
+ pci_read_config_byte(net_dev, PCI_INTERRUPT_LINE, &pci_irqline);
+ pci_read_config_byte(net_dev, PCI_COMMAND, &pci_cmd);
+ pci_read_config_dword(net_dev, 8, &dev_rev); /* Read Chip revision */
+ pci_read_config_dword(net_dev, 0x50, &pci_pmr); /* Read PMR */
/* Enable Master/IO access, Disable memory access */
- pci_read_config_byte(net_dev, PCI_COMMAND, &pci_cmd);
pci_cmd |= PCI_COMMAND_IO + PCI_COMMAND_MASTER;
pci_cmd &= ~PCI_COMMAND_MEMORY;
- pci_write_config_byte(net_dev, PCI_COMMAND, pci_cmd);
- /* Set Latency Timer 80h */
+ /* Write back PCI command & Let latency timer = 0x80 */
+ pci_write_config_byte(net_dev, PCI_COMMAND, pci_cmd);
pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
- /* Read Chip revesion */
- pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev);
-
/* IO range check */
- if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev)))
+ pci_iobase &= ~0x7f; /* mask off bit0~6 */
+ if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
+ printk(KERN_ERR "dmfe: I/O conflict : IO=%x Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
continue;
+ }
/* Interrupt check */
- if (pci_irqline == 0) {
+ if ((pci_irqline == 0xff) || (pci_irqline == 0)) {
printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline);
continue;
}
db->chip_id = pci_id; /* keep Chip vandor/Device ID */
db->ioaddr = pci_iobase;
- db->chip_revesion = dev_rev;
+ db->chip_revision = dev_rev;
db->net_dev = net_dev;
dev->do_ioctl = &dmfe_do_ioctl;
request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name);
+ printk("dmfe: INDEX=%d ID=%x NAME=%s IO=%x IRQ=%d\n", index, pci_id, dev->name, pci_iobase, pci_irqline);
+
+ /* Check Chip type decision */
+ pci_pmr &= 0x070000; /* Leave PMR revision */
+ if ((pci_pmr == 0x010000) && (dev_rev == 0x02000031))
+ db->chip_type = 1; /* DM9102A E3 */
+ else
+ db->chip_type = 0;
/* read 64 word srom data */
for (i = 0; i < 64; i++)
dev = 0; /* NULL device */
}
+ if (!dm9102_count)
+ printk(KERN_ERR "dmfe: Can't find DM910X board or resource error\n");
return dm9102_count ? 0 : -ENODEV;
}
db->tx_packet_cnt = 0;
db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
- db->link_failed = 0;
+ db->link_failed = 1;
db->wait_reset = 0;
- db->in_reset_state = 0;
- db->rx_error_cnt = 0;
-
- if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) {
- //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */
- //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */
- db->cr0_data = 0xc00000; /* TX/RX desc burst mode */
+ db->first_in_callback = 0;
+ db->NIC_capability = 0xf; /* All capability */
+ db->PHY_reg4 = 0x1e0;
+
+ /* CR6 operation mode decision */
+ if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revision >= 0x02000030)) {
+ db->cr6_data |= DMFE_TXTH_256;
+ db->cr0_data = CR0_DEFAULT; /* TX/RX desc burst mode */
db->dm910x_chk_mode = 4; /* Enter the normal mode */
} else {
+ db->cr6_data |= CR6_SFT; /* Store & Forward mode */
db->cr0_data = 0;
db->dm910x_chk_mode = 1; /* Enter the check mode */
}
- /* Initilize DM910X board */
+ /* Initialize DM910X board */
dmfe_init_dm910x(dev);
/* Active System Interface */
/* set and active a timer process */
init_timer(&db->timer);
- db->timer.expires = DMFE_TIMER_WUT;
+ db->timer.expires = DMFE_TIMER_WUT + HZ * 2;
db->timer.data = (unsigned long) dev;
db->timer.function = &dmfe_timer;
add_timer(&db->timer);
return 0;
}
-/* Initilize DM910X board
+/* Initialize DM910X board
Reset DM910X board
- Initilize TX/Rx descriptor chain structure
+ Initialize TX/Rx descriptor chain structure
Send the set-up frame
Enable Tx/Rx machine
*/
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
- /* Reset DM910x board : need 32 PCI clock to complete */
+ /* Reset DM910x MAC controller */
outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
- DELAY_5US;
+ udelay(100);
outl(db->cr0_data, ioaddr + DCR0);
+ DELAY_5US;
+ /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+ db->phy_addr = 1;
+
+ /* Parser SROM and media mode */
+ dmfe_parse_srom(db);
+ db->media_mode = dmfe_media_mode;
+
+ /* RESET Phyxcer Chip by GPR port bit 7 */
outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
- outl(0x80, ioaddr + DCR12); /* RESET DM9102 phyxcer */
+ if (db->chip_id == PCI_DM9009_ID) {
+ outl(0x80, ioaddr + DCR12); /* Issue RESET signal */
+ mdelay(300); /* Delay 300 ms */
+ }
outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
- /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
- db->phy_addr = 1;
+ /* Process Phyxcer Media Mode */
+ if (!(db->media_mode & 0x10)) /* Force 1M mode */
+ dmfe_set_phyxcer(db);
/* Media Mode Check */
- db->media_mode = dmfe_media_mode;
- if (db->media_mode & DMFE_AUTO)
- dmfe_sense_speed(db);
- else
- db->op_mode = db->media_mode;
- dmfe_process_mode(db);
+ /* Don't need now. We did Restart Auto-Negotiation in dmfe_set_phyxcer()
+ and it should not complete now. We check media mode and program in
+ dmfe_timer() later */
+ if (!(db->media_mode & DMFE_AUTO))
+ db->op_mode = db->media_mode; /* Force Mode */
/* Initiliaze Transmit/Receive decriptor and CR3/4 */
dmfe_descriptor_init(db, ioaddr);
send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
/* Init CR5/CR7, interrupt active bit */
- outl(0xffffffff, ioaddr + DCR5); /* clear all CR5 status */
db->cr7_data = CR7_DEFAULT;
outl(db->cr7_data, ioaddr + DCR7);
/* Init CR15, Tx jabber and Rx watchdog timer */
- db->cr15_data = CR15_DEFAULT;
outl(db->cr15_data, ioaddr + DCR15);
/* Enable DM910X Tx/Rx function */
- db->cr6_data |= CR6_RXSC | CR6_TXSC;
+ db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000;
update_cr6(db->cr6_data, ioaddr);
-
}
DMFE_DBUG(0, "dmfe_start_xmit", 0);
/* Resource flag check */
- if (dev->tbusy == 1) /* Resource Busy */
+ if (test_and_set_bit(0, (void *) &dev->tbusy))
return 1;
/* Too large packet check */
if (skb->len > MAX_PACKET_SIZE) {
- printk(KERN_WARNING "%s: big packet, size=%d\n", dev->name, (u16) skb->len);
+ printk(KERN_ERR "dmfe: A big packet, size=%d\n", (u16) skb->len);
dev_kfree_skb(skb);
return 0;
}
+
/* No Tx resource check, it never happen normally */
- if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) {
- dev->tbusy = 1;
- return 1;
- }
+ if (db->tx_queue_cnt >= TX_FREE_DESC_CNT)
+ return -EBUSY;
+
+ /* Disable all interrupt */
+ outl(0, dev->base_addr + DCR7);
+
/* transmit this packet */
txptr = db->tx_insert_ptr;
memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
/* Transmit Packet Process */
- if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
+ if ((!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT)) {
txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
db->tx_packet_cnt++; /* Ready to send count */
outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
}
/* Tx resource check */
- if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) {
- dev->tbusy = 1;
+ if (db->tx_queue_cnt < TX_FREE_DESC_CNT) {
+ dev->tbusy = 0;
}
+
/* free this SKB */
dev_kfree_skb(skb);
+
+ /* Restore CR7 to enable interrupt */
+ outl(db->cr7_data, dev->base_addr + DCR7);
return 0;
}
+
/*
Stop the interface.
The interface is stopped when it is brought.
DMFE_DBUG(0, "dmfe_stop", 0);
/* disable system */
- dev->start = 0; /* interface disable */
- dev->tbusy = 1; /* can't transmit */
+ dev->start = 0; /* interface disable */
+ dev->tbusy = 1; /* can't transmit */
- /* Reset & stop DM910X board */
- outl(DM910X_RESET, ioaddr + DCR0);
- DELAY_5US;
/* deleted timer */
del_timer(&db->timer);
+ /* Reset & stop DM910X board */
+ outl(DM910X_RESET, ioaddr + DCR0);
+ DELAY_5US;
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id);
+
/* free interrupt */
free_irq(dev->irq, dev);
MOD_DEC_USE_COUNT;
+#if 0
+ /* show statistic counter */
+ printk("<DM9XS>: FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", db->tx_fifo_underrun, db->tx_excessive_collision, db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, db->tx_jabber_timeout, db->reset_count, db->reset_cr8, db->reset_fatal, db->reset_TXtimeout);
+
+#endif /* */
return 0;
}
+
/*
- DM9102 insterrupt handler
+ DM9102 interrupt handler
receive the packet to upper layer, free the transmitted packet
*/
-
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct device *dev = dev_id;
- struct tx_desc *txptr;
- struct dmfe_board_info *db;
- u32 ioaddr;
+ struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
+ u32 ioaddr = dev->base_addr;
+ DMFE_DBUG(0, "dmfe_interrupt()", 0);
if (!dev) {
DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
return;
}
- if (dev->interrupt) {
- DMFE_DBUG(1, "dmfe_interrupt() re-entry ", 0);
- return;
- }
- /* A real interrupt coming */
- dev->interrupt = 1; /* Lock interrupt */
- db = (struct dmfe_board_info *) dev->priv;
- ioaddr = dev->base_addr;
-
- DMFE_DBUG(0, "dmfe_interrupt()", 0);
-/* Disable all interrupt in CR7 to solve the interrupt edge problem */
- outl(0, ioaddr + DCR7);
-
-/* Got DM910X status */
+ /* Got DM910X status */
db->cr5_data = inl(ioaddr + DCR5);
outl(db->cr5_data, ioaddr + DCR5);
- /* printk("CR5=%x\n", db->cr5_data); */
+ if (!(db->cr5_data & 0xc1))
+ return;
+
+ /* Disable MAC interrupt in CR7 to solve the interrupt edge problem */
+ outl(0, ioaddr + DCR7);
/* Check system status */
if (db->cr5_data & 0x2000) {
/* system bus error happen */
DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data);
dev->tbusy = 1;
+ db->reset_fatal++;
db->wait_reset = 1; /* Need to RESET */
- outl(0, ioaddr + DCR7); /* disable all interrupt */
- dev->interrupt = 0; /* unlock interrupt */
return;
}
+
+ /* Received the coming packet */
+ if ((db->cr5_data & 0x40) && db->rx_avail_cnt)
+ dmfe_rx_packet(dev, db);
+
+ /* reallocated rx descriptor buffer */
+ if (db->rx_avail_cnt < RX_DESC_CNT)
+ allocated_rx_buffer(db);
+
/* Free the transmitted descriptor */
+ // if ( db->cr5_data & 0x01)
+ dmfe_free_tx_pkt(dev, db);
+
+ // mark_bh(NET_BH); /* Active upper layer */
+
+ /* Mode Check */
+ if (db->dm910x_chk_mode & 0x2) {
+ db->dm910x_chk_mode = 0x4;
+ db->cr6_data |= 0x100;
+ update_cr6(db->cr6_data, db->ioaddr);
+ }
+
+ /* Restore CR7 to enable MAC interrupt mask */
+ outl(db->cr7_data, ioaddr + DCR7);
+}
+
+
+/*
+ * Free TX resource after TX complete
+ */
+static void dmfe_free_tx_pkt(struct device *dev, struct dmfe_board_info *db)
+{
+ struct tx_desc *txptr;
+ u32 ioaddr = dev->base_addr;
txptr = db->tx_remove_ptr;
while (db->tx_packet_cnt) {
- /* printk("tdes0=%x\n", txptr->tdes0); */
+ /* printk("<DMFE>: tdes0=%x\n", txptr->tdes0); */
if (txptr->tdes0 & 0x80000000)
break;
/* A packet sent completed */
+ db->tx_packet_cnt--;
db->stats.tx_packets++;
/* Transmit statistic counter */
if (txptr->tdes0 != 0x7fffffff) {
- /* printk("tdes0=%x\n", txptr->tdes0); */
+ /* printk("<DMFE>: tdes0=%x\n", txptr->tdes0); */
db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
+
db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
- if (txptr->tdes0 & TDES0_ERR_MASK)
+
+ if (txptr->tdes0 & TDES0_ERR_MASK) {
db->stats.tx_errors++;
+ if (txptr->tdes0 & 0x0002) { /* UnderRun */
+ db->tx_fifo_underrun++;
+ if (!(db->cr6_data & CR6_SFT)) {
+ db->cr6_data = db->cr6_data | CR6_SFT;
+ update_cr6(db->cr6_data, db->ioaddr);
+ }
+ }
+ if (txptr->tdes0 & 0x0100)
+ db->tx_excessive_collision++;
+ if (txptr->tdes0 & 0x0200)
+ db->tx_late_collision++;
+ if (txptr->tdes0 & 0x0400)
+ db->tx_no_carrier++;
+ if (txptr->tdes0 & 0x0800)
+ db->tx_loss_carrier++;
+ if (txptr->tdes0 & 0x4000)
+ db->tx_jabber_timeout++;
+ }
}
txptr = (struct tx_desc *) txptr->next_tx_desc;
- db->tx_packet_cnt--;
- } /* End of while */
+ } /* End of while */
/* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
/* Send the Tx packet in queue */
if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
- db->tx_packet_cnt++; /* Ready to send count */
- outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */
- dev->trans_start = jiffies; /* saved the time stamp */
+ txptr->tdes0 = 0x80000000; /* Set owner bit */
+ db->tx_packet_cnt++; /* Ready to send */
db->tx_queue_cnt--;
+ outl(0x1, ioaddr + DCR1); /* Issue Tx polling */
+ dev->trans_start = jiffies; /* saved time stamp */
}
- /* Resource available check */
- if (dev->tbusy && (db->tx_queue_cnt < TX_FREE_DESC_CNT)) {
- dev->tbusy = 0; /* free a resource */
- mark_bh(NET_BH); /* active bottom half */
- }
- /* Received the coming packet */
- if (db->rx_avail_cnt)
- dmfe_rx_packet(dev, db);
-
- /* reallocated rx descriptor buffer */
- if (db->rx_avail_cnt < RX_DESC_CNT)
- allocated_rx_buffer(db);
- /* Mode Check */
- if (db->dm910x_chk_mode & 0x2) {
- db->dm910x_chk_mode = 0x4;
- db->cr6_data |= 0x100;
- update_cr6(db->cr6_data, db->ioaddr);
- }
- dev->interrupt = 0; /* release interrupt lock */
+ /* Resource available check */
+ if (db->tx_queue_cnt < TX_WAKE_DESC_CNT) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ } /* Active upper layer */
- /* Restore CR7 to enable interrupt mask */
- if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
- db->cr7_data = 0x1a28d;
- else
- db->cr7_data = 0x1a2cd;
- outl(db->cr7_data, ioaddr + DCR7);
}
/*
/* reused this SKB */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
- /* db->rx_error_cnt++; */
} else {
/* A packet with First/Last flag */
rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4; /* skip CRC */
if (rxptr->rdes0 & 0x80)
db->stats.rx_length_errors++;
}
- if (!(rxptr->rdes0 & 0x8000) ||
- ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
+ if (!(rxptr->rdes0 & 0x8000) || ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
skb = (struct sk_buff *) rxptr->rx_skb_ptr;
/* Received Packet CRC check need or not */
db->dm910x_chk_mode = 3;
} else {
/* A good packet coming, send to upper layer */
- skb->dev = dev;
- skb_put(skb, rxlen);
+ /* Shorst packet used new SKB */
+ if ((rxlen < RX_COPY_SIZE) && ((skb = dev_alloc_skb(rxlen + 2)) != NULL)) {
+ /* Rx packet size less than COPY_SIZE, allocated a rxlen SKB */
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16byte align */
+ memcpy(skb_put(skb, rxlen), ((struct sk_buff *) rxptr->rx_skb_ptr)->tail, rxlen);
+ dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+ } else {
+ /* Pass this Rx buffer to upper layer */
+ skb->dev = dev;
+ skb_put(skb, rxlen);
+ }
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); /* Send to upper layer */
dev->last_rx = jiffies;
DMFE_DBUG(0, "dmfe_timer()", 0);
- /* Do reset now */
- if (db->in_reset_state)
- return;
+ /* Media mode process when Link OK before enter this route */
+ if (db->first_in_callback == 0) {
+ db->first_in_callback = 1;
+ if (db->chip_type && (db->chip_id == PCI_DM9102_ID)) {
+ db->cr6_data &= ~0x40000;
+ update_cr6(db->cr6_data, db->ioaddr);
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id);
+ db->cr6_data |= 0x40000;
+ update_cr6(db->cr6_data, db->ioaddr);
+ db->timer.expires = DMFE_TIMER_WUT + HZ * 2;
+ add_timer(&db->timer);
+ return;
+ }
+ }
/* Operating Mode Check */
if ((db->dm910x_chk_mode & 0x1) && (db->stats.rx_packets > MAX_CHECK_PACKET)) {
/* Dynamic reset DM910X : system error or transmit time-out */
tmp_cr8 = inl(db->ioaddr + DCR8);
if ((db->interval_rx_cnt == 0) && (tmp_cr8)) {
+ db->reset_cr8++;
db->wait_reset = 1;
- /* printk("CR8 %x, Interval Rx %x\n", tmp_cr8, db->interval_rx_cnt); */
}
- /* Receiving Traffic check */
- if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
- db->cr7_data = 0x1a28d;
- else
- db->cr7_data = 0x1a2cd;
- outl(db->cr7_data, db->ioaddr + DCR7);
-
db->interval_rx_cnt = 0;
- if (db->wait_reset | (db->tx_packet_cnt &&
- ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
- /*
- printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
- */
+ /* TX polling kick monitor */
+ if (db->tx_packet_cnt && ((jiffies - dev->trans_start) > DMFE_TX_KICK)) {
+ outl(0x1, dev->base_addr + DCR1); /* Tx polling again */
+
+ /* TX Timeout */
+ if (db->tx_packet_cnt && ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) {
+ db->reset_TXtimeout++;
+ db->wait_reset = 1;
+ }
+ }
+ if (db->wait_reset) {
DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
+ db->reset_count++;
dmfe_dynamic_reset(dev);
+ db->first_in_callback = 0;
db->timer.expires = DMFE_TIMER_WUT;
add_timer(&db->timer);
return;
}
- db->rx_error_cnt = 0; /* Clear previos counter */
/* Link status check, Dynamic media type change */
if (db->chip_id == PCI_DM9132_ID)
else
tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */
- if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) ||
- ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) {
+ if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revision == 0x02000030)) ||
+ ((db->chip_id == PCI_DM9132_ID) && (db->chip_revision == 0x02000010))) {
/* DM9102A Chip */
if (tmp_cr12 & 2)
tmp_cr12 = 0x0; /* Link failed */
/* Link Failed */
DMFE_DBUG(0, "Link Failed", tmp_cr12);
db->link_failed = 1;
- phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* reset Phy */
- /* 10/100M link failed, used 1M Home-Net */
- db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */
- db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */
- update_cr6(db->cr6_data, db->ioaddr);
+ /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */
+ /* AUTO or force 1M Homerun/Longrun don't need */
+ if (!(db->media_mode & 0x38))
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id);
+
+ /* In AUTO mode, if INT phyxcer link failed, select EXT device */
+ if (db->media_mode & DMFE_AUTO) {
+ /* 10/100M link failed, used 1M Home-Net */
+ db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */
+ db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */
+ update_cr6(db->cr6_data, db->ioaddr);
+ }
- /* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */
- db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
- if (db->phy_id2 == 0xb900)
- phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id);
} else if ((tmp_cr12 & 0x3) && db->link_failed) {
DMFE_DBUG(0, "Link link OK", tmp_cr12);
db->link_failed = 0;
- /* CR6 bit18=0, select 10/100M */
- db->cr6_data &= ~0x00040000;
- update_cr6(db->cr6_data, db->ioaddr);
-
/* Auto Sense Speed */
if (db->media_mode & DMFE_AUTO)
- dmfe_sense_speed(db);
+ if (dmfe_sense_speed(db))
+ db->link_failed = 1;
dmfe_process_mode(db);
- update_cr6(db->cr6_data, db->ioaddr);
+
/* SHOW_MEDIA_TYPE(db->op_mode); */
}
- /* reallocated rx descriptor buffer */
- if (db->rx_avail_cnt < RX_DESC_CNT)
- allocated_rx_buffer(db);
+
+ /* HPNA remote command check */
+ if (db->HPNA_command & 0xf00) {
+ db->HPNA_timer--;
+ if (!db->HPNA_timer)
+ dmfe_HPNA_remote_cmd_chk(db);
+ }
/* Timer active again */
db->timer.expires = DMFE_TIMER_WUT;
DMFE_DBUG(0, "dmfe_dynamic_reset()", 0);
- /* Enter dynamic reset route */
- db->in_reset_state = 1;
-
/* Disable upper layer interface */
dev->tbusy = 1; /* transmit packet disable */
dev->start = 0; /* interface not ready */
- db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */
+ /* Stop MAC controller */
+ db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */
update_cr6(db->cr6_data, dev->base_addr);
+ outl(0, dev->base_addr + DCR7);
+ outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5);
/* Free Rx Allocate buffer */
dmfe_free_rxbuffer(db);
db->tx_packet_cnt = 0;
db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
- db->link_failed = 0;
+ db->link_failed = 1;
db->wait_reset = 0;
- db->rx_error_cnt = 0;
/* Re-initilize DM910X board */
dmfe_init_dm910x(dev);
dev->tbusy = 0; /* Can transmit packet */
dev->start = 1; /* interface ready */
- /* Leave dynamic reser route */
- db->in_reset_state = 0;
}
/*
- free all allocated rx buffer
+ Free all allocated rx buffer
*/
static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
{
}
/*
- Initialize transmit/Receive descriptor
+ Initialize transmit/Receive descriptor
Using Chain structure, and allocated Tx/Rx buffer
*/
static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
outl(cr6_tmp, ioaddr + DCR6);
DELAY_5US;
outl(cr6_data, ioaddr + DCR6);
- cr6_tmp = inl(ioaddr + DCR6);
- /* printk("CR6 update %x ", cr6_tmp); */
+ DELAY_5US;
}
/* Send a setup frame for DM9132
rxptr = db->rx_insert_ptr;
while (db->rx_avail_cnt < RX_DESC_CNT) {
- if ((skb = alloc_skb(RX_ALLOC_SIZE, GFP_ATOMIC)) == NULL)
+ if ((skb = dev_alloc_skb(RX_ALLOC_SIZE)) == NULL)
break;
rxptr->rx_skb_ptr = (u32) skb;
rxptr->rdes2 = virt_to_bus(skb->tail);
/*
Auto sense the media mode
*/
-static void dmfe_sense_speed(struct dmfe_board_info *db)
+static u8 dmfe_sense_speed(struct dmfe_board_info *db)
{
- int i;
+ u8 ErrFlag = 0;
u16 phy_mode;
- for (i = 1000; i; i--) {
- DELAY_5US;
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
- if ((phy_mode & 0x24) == 0x24)
- break;
- }
-
- if (i) {
+ /* CR6 bit18=0, select 10/100M */
+ update_cr6((db->cr6_data & ~0x40000), db->ioaddr);
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
+ if ((phy_mode & 0x24) == 0x24) {
if (db->chip_id == PCI_DM9132_ID) /* DM9132 */
phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000;
else /* DM9102/DM9102A */
default:
db->op_mode = DMFE_10MHF;
DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode);
+ ErrFlag = 1;
break;
}
} else {
db->op_mode = DMFE_10MHF;
DMFE_DBUG(0, "Link Failed :", phy_mode);
+ ErrFlag = 1;
+ }
+ return ErrFlag;
+}
+
+
+/*
+ Set 10/100 phyxcer capability
+ AUTO mode : phyxcer register4 is NIC capability
+ Force mode: phyxcer register4 is the force media
+ */
+static void dmfe_set_phyxcer(struct dmfe_board_info *db)
+{
+ u16 phy_reg;
+
+ /* Select 10/100M phyxcer */
+ db->cr6_data &= ~0x40000;
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* DM9009 Chip: Phyxcer reg18 bit12=0 */
+ if (db->chip_id == PCI_DM9009_ID) {
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 18, db->chip_id) & ~0x1000;
+ phy_write(db->ioaddr, db->phy_addr, 18, phy_reg, db->chip_id);
}
+
+ /* Phyxcer capability setting */
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0;
+ if (!(db->media_mode & DMFE_AUTO)) { /* AUTO/Force Mode Check */
+ /* Force Mode */
+ switch (db->media_mode) {
+ case DMFE_10MHF:
+ phy_reg |= 0x20;
+ break;
+ case DMFE_10MFD:
+ phy_reg |= 0x40;
+ break;
+ case DMFE_100MHF:
+ phy_reg |= 0x80;
+ break;
+ case DMFE_100MFD:
+ phy_reg |= 0x100;
+ break;
+ }
+ if (db->chip_id == PCI_DM9009_ID)
+ phy_reg &= 0x61;
+ }
+
+ /* Write new capability to Phyxcer Reg4 */
+ if (!(phy_reg & 0x01e0)) {
+ phy_reg |= db->PHY_reg4;
+ db->media_mode |= DMFE_AUTO;
+ }
+ phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
+
+ /* Restart Auto-Negotiation */
+ if (db->chip_type && (db->chip_id == PCI_DM9102_ID))
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x1800, db->chip_id);
+ if (!db->chip_type)
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id);
}
+
/*
Process op-mode
AUTO mode : PHY controller in Auto-negotiation Mode
u16 phy_reg;
/* Full Duplex Mode Check */
- db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */
if (db->op_mode & 0x4)
- db->cr6_data |= CR6_FDM;
-
- if (!(db->media_mode & DMFE_AUTO)) { /* Force Mode Check */
- /* User force the media type */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id);
- /* printk("Nway phy_reg5 %x ",phy_reg); */
- if (phy_reg & 0x1) {
- /* parter own the N-Way capability */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0;
- switch (db->op_mode) {
- case DMFE_10MHF:
- phy_reg |= 0x20;
- break;
- case DMFE_10MFD:
- phy_reg |= 0x40;
- break;
- case DMFE_100MHF:
- phy_reg |= 0x80;
- break;
- case DMFE_100MFD:
- phy_reg |= 0x100;
- break;
- }
- phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
- } else {
- /* parter without the N-Way capability */
+ db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */
+ else
+ db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */
+
+ /* Transceiver Selection */
+ if (db->op_mode & 0x10) /* 1M HomePNA */
+ db->cr6_data |= 0x40000; /* External MII select */
+ else
+ db->cr6_data &= ~0x40000; /* Internal 10/100 transciver */
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* 10/100M phyxcer force mode need */
+ if (!(db->media_mode & 0x18)) { /* Skip for AUTO mode or HomePNA */
+ /* Forece Mode */
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id);
+ if (!(phy_reg & 0x1)) {
+
+ /* parter without N-Way capability */
+ phy_reg = 0x0;
switch (db->op_mode) {
case DMFE_10MHF:
phy_reg = 0x0;
break;
}
phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
+ if (db->chip_type && (db->chip_id == PCI_DM9102_ID))
+ mdelay(20);
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
}
}
}
return phy_data;
}
+
/*
- Calculate the CRC valude of the Rx packet
+ Calculate the CRC value of the Rx packet
flag = 1 : return the reverse CRC (for the received packet CRC)
- 0 : return the normal CRC (for Hash Table index)
+ 0 : return the normal CRC (for Hash Table index)
*/
unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
{
return Crc;
}
+
+/*
+ Parser SROM and media mode
+ */
+static void dmfe_parse_srom(struct dmfe_board_info *db)
+{
+ char *srom = db->srom;
+ int dmfe_mode, tmp_reg;
+ DMFE_DBUG(0, "dmfe_parse_srom() ", 0);
+
+ /* Init CR15 */
+ db->cr15_data = CR15_DEFAULT;
+
+ /* Check SROM Version */
+ if (((int) srom[18] & 0xff) == SROM_V41_CODE) {
+ /* SROM V4.01 */
+
+ /* Get NIC support media mode */
+ db->NIC_capability = *(u16 *) (&srom[34]);
+ db->PHY_reg4 = 0;
+ for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) {
+ switch (db->NIC_capability & tmp_reg) {
+ case 0x1:
+ db->PHY_reg4 |= 0x0020;
+ break;
+ case 0x2:
+ db->PHY_reg4 |= 0x0040;
+ break;
+ case 0x4:
+ db->PHY_reg4 |= 0x0080;
+ break;
+ case 0x8:
+ db->PHY_reg4 |= 0x0100;
+ break;
+ }
+ }
+
+ /* Media Mode Force or not check */
+ dmfe_mode = *((int *) &srom[34]) & *((int *) &srom[36]);
+ switch (dmfe_mode) {
+ case 0x1:
+ dmfe_media_mode = DMFE_10MHF;
+ break; /* Select 10MHF */
+ case 0x4:
+ dmfe_media_mode = DMFE_100MHF;
+ break; /* Select 100MHF */
+ case 0x2:
+ dmfe_media_mode = DMFE_10MFD;
+ break; /* Select 10MFD */
+ case 0x8:
+ dmfe_media_mode = DMFE_100MFD;
+ break; /* Select 100MFD */
+ case 0x100:
+ case 0x200:
+ dmfe_media_mode = DMFE_1M_HPNA;
+ break; /* Select HomePNA */
+ }
+
+ /* Special Function setting */
+ /* VLAN function */
+ if ((SF_mode & 0x1) || (srom[43] & 0x80))
+ db->cr15_data |= 0x40;
+
+ /* Flow Control */
+ if ((SF_mode & 0x2) || (srom[40] & 0x1))
+ db->cr15_data |= 0x400;
+
+ /* TX pause packet */
+ if ((SF_mode & 0x4) || (srom[40] & 0xe))
+ db->cr15_data |= 0x9800;
+ }
+
+ /* Parse HPNA parameter */
+ db->HPNA_command = 1;
+
+ /* Accept remote command or not */
+ if (HPNA_rx_cmd == 0)
+ db->HPNA_command |= 0x8000;
+
+ /* Issue remote command & operation mode */
+ if (HPNA_tx_cmd == 1)
+ switch (HPNA_mode) { /* Issue Remote Command */
+ case 0:
+ db->HPNA_command |= 0x0904;
+ break;
+ case 1:
+ db->HPNA_command |= 0x0a00;
+ break;
+ case 2:
+ db->HPNA_command |= 0x0506;
+ break;
+ case 3:
+ db->HPNA_command |= 0x0602;
+ break;
+ }
+ else
+ switch (HPNA_mode) { /* Don't Issue */
+ case 0:
+ db->HPNA_command |= 0x0004;
+ break;
+ case 1:
+ db->HPNA_command |= 0x0000;
+ break;
+ case 2:
+ db->HPNA_command |= 0x0006;
+ break;
+ case 3:
+ db->HPNA_command |= 0x0002;
+ break;
+ }
+
+ /* Check DM9801 or DM9802 present or not */
+ db->HPNA_present = 0;
+ update_cr6(db->cr6_data | 0x40000, db->ioaddr);
+ tmp_reg = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
+ if ((tmp_reg & 0xfff0) == 0xb900) {
+ /* DM9801 or DM9802 present */
+ db->HPNA_timer = 8; /* Check remote device status after 8 second */
+ if (phy_read(db->ioaddr, db->phy_addr, 31, db->chip_id) == 0x4404) {
+ /* DM9801 HomeRun */
+ db->HPNA_present = 1;
+ dmfe_program_DM9801(db, tmp_reg);
+ } else {
+ /* DM9802 LongRun */
+ db->HPNA_present = 2;
+ dmfe_program_DM9802(db);
+ }
+ }
+}
+
+
+/*
+ Description:
+ Init HomeRun DM9801
+ */
+static void dmfe_program_DM9801(struct dmfe_board_info *db, int HPNA_rev)
+{
+ uint reg17, reg25;
+ if (!HPNA_NoiseFloor)
+ HPNA_NoiseFloor = DM9801_NOISE_FLOOR;
+ switch (HPNA_rev) {
+ case 0xb900: /* DM9801 E3 */
+ db->HPNA_command |= 0x1000;
+ reg25 = phy_read(db->ioaddr, db->phy_addr, 24, db->chip_id);
+ reg25 = ((reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000;
+ reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id);
+ break;
+ case 0xb901: /* DM9801 E4 */
+ reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id);
+ reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor;
+ reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id);
+ reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3;
+ break;
+ case 0xb902: /* DM9801 E5 */
+ case 0xb903: /* DM9801 E6 */
+ default:
+ db->HPNA_command |= 0x1000;
+ reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id);
+ reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5;
+ reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id);
+ reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor;
+ break;
+ }
+ phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ phy_write(db->ioaddr, db->phy_addr, 17, reg17, db->chip_id);
+ phy_write(db->ioaddr, db->phy_addr, 25, reg25, db->chip_id);
+}
+
+
+/* Description:
+ Init HomeRun DM9802
+ */
+static void dmfe_program_DM9802(struct dmfe_board_info *db)
+{
+ uint phy_reg;
+ if (!HPNA_NoiseFloor)
+ HPNA_NoiseFloor = DM9802_NOISE_FLOOR;
+ phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id);
+ phy_reg = (phy_reg & 0xff00) + HPNA_NoiseFloor;
+ phy_write(db->ioaddr, db->phy_addr, 25, phy_reg, db->chip_id);
+}
+
+
+/* Description:
+ Check remote HPNA power and speed status. If not correct,
+ issue command again.
+ */
+static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info *db)
+{
+ uint phy_reg;
+
+ /* Got remote device status */
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0x60;
+ switch (phy_reg) {
+ case 0x00:
+ phy_reg = 0x0a00;
+ break; /* LP/LS */
+ case 0x20:
+ phy_reg = 0x0900;
+ break; /* LP/HS */
+ case 0x40:
+ phy_reg = 0x0600;
+ break; /* HP/LS */
+ case 0x60:
+ phy_reg = 0x0500;
+ break; /* HP/HS */
+ }
+
+ /* Check remote device status match our setting ot not */
+ if (phy_reg != (db->HPNA_command & 0x0f00)) {
+ phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ db->HPNA_timer = 8;
+ } else
+ db->HPNA_timer = 600; /* Match, every 10 minutes, check */
+}
+
+
#ifdef MODULE
MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw");
MODULE_PARM(mode, "i");
MODULE_PARM(cr6set, "i");
MODULE_PARM(chkmode, "i");
+MODULE_PARM(HPNA_mode, "i");
+MODULE_PARM(HPNA_rx_cmd, "i");
+MODULE_PARM(HPNA_tx_cmd, "i");
+MODULE_PARM(HPNA_NoiseFloor, "i");
+MODULE_PARM(SF_mode, "i");
-/* Description:
+/* Description:
when user used insmod to add module, system invoked init_module()
to initilize and register.
*/
dmfe_cr6_user_set = cr6set;
switch (mode) {
- case 0:
- case 1:
- case 4:
- case 5:
+ case DMFE_10MHF:
+ case DMFE_100MHF:
+ case DMFE_10MFD:
+ case DMFE_100MFD:
+ case DMFE_1M_HPNA:
dmfe_media_mode = mode;
break;
default:
- dmfe_media_mode = 8;
+ dmfe_media_mode = DMFE_AUTO;
break;
}
+ if (HPNA_mode > 4)
+ HPNA_mode = 0; /* Default: LP/HS */
+ if (HPNA_rx_cmd > 1)
+ HPNA_rx_cmd = 0; /* Default: Ignored remote command */
+ if (HPNA_tx_cmd > 1)
+ HPNA_tx_cmd = 0; /* Default: Don't issue remote command */
+ if (HPNA_NoiseFloor > 15)
+ HPNA_NoiseFloor = 0;
return dmfe_probe(0); /* search board and register */
}
-/* Description:
+
+/* Description:
when user used rmmod to delete module, system invoked clean_module()
to un-register device.
*/
next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
unregister_netdev(dmfe_root_dev);
db = dmfe_root_dev->priv;
- release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion));
+ release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revision));
kfree(db); /* free board information */
kfree(dmfe_root_dev); /* free device structure */
dmfe_root_dev = next_dev;
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * $Log: falc.h,v $
+ * $Log: falc-lh.h,v $
+ * Revision 3.1 2001/06/15 12:41:10 regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1 2001/06/13 20:24:36 daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
* Revision 1.1 2000/05/15 ivan
* Included DJA bits for the LIM2 register.
*
* 2 of the License, or (at your option) any later version.
*
* $Log: hd64572.h,v $
+ * Revision 3.1 2001/06/15 12:41:10 regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1 2001/06/13 20:24:31 daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
* Revision 1.0 2000/01/25 ivan
* Initial version.
*
#define USE_PCI_CLOCK
static char rcsid[] =
-"$Revision: 3.1.0.6 $$Date: 2001/03/02 $";
+"Revision: 3.4.2 Date: 2001/10/11 ";
/*
* pc300.c Cyclades-PC300(tm) Driver.
* 2 of the License, or (at your option) any later version.
*
* $Log: pc300.c,v $
+ * Revision 3.2 to 3.12 2001/10/11 20:26:04 daniela
+ * Fixes for noisy lines: return the size of bad frames in
+ * dma_get_rx_frame_size, so that the Rx buffer descriptors can be cleaned by
+ * dma_buf_read (called in cpc_net_rx); improved Rx statistics; created
+ * rx_dma_start routine.
+ * Changed file revision to the package revision, changed T1/E1 master clock
+ * configuration, reviewed boot messages and default configuration.
+ * Included new configuration parameters (line code, CRC calculation and clock)
+ * Changed the header of message trace to include the device name. New format:
+ * "hdlcX[R/T]: ".
+ *
+ * Revision 3.1 2001/06/15 12:41:10 regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1 2001/06/13 20:24:25 daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
+ * Revision 3.1.0.7 2001/06/08 daniela
+ * Did some changes in the DMA programming implementation to avoid the
+ * occurrence of a SCA-II bug when CDA is accessed during a DMA transfer.
+ *
* Revision 3.1.0.6 2001/03/02 daniela
* Changed SIOCGPC300CONF ioctl, to give hw information to pc300util.
*
#undef PC300_DEBUG_INTR
#undef PC300_DEBUG_TX
#undef PC300_DEBUG_RX
+#undef PC300_DEBUG_OTHER
/* Hardware configuration options.
* These are arrays of configuration options used by verification routines.
while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
rcvd += cpc_readw(&ptdescr->len);
first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1);
- if (status & DST_EOM)
+ if ((status & DST_EOM) || (first_bd == card->chan[ch].rx_last_bd)) {
+ /* Return the size of a good frame or incomplete bad frame
+ * (dma_buf_read will clean the buffer descriptors in this case). */
return (rcvd);
+ }
ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next));
}
return (-1);
ptdescr = (pcsca_bd_t *)(card->hw.rambase +
RX_BD_ADDR(ch, chan->rx_first_bd));
while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
- if (status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) {
+ nchar = cpc_readw(&ptdescr->len);
+ if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) ||
+ (nchar > BD_DEF_LEN)) {
+ if (nchar > BD_DEF_LEN) status |= DST_RBIT;
rcvd = -status;
/* Discard remaining descriptors used by the bad frame */
while(chan->rx_first_bd != chan->rx_last_bd) {
}
break;
}
- if ((nchar = cpc_readw(&ptdescr->len)) != 0) {
- memcpy_fromio(skb_put(skb, nchar),
- (void *)(card->hw.rambase +
- cpc_readl(&ptdescr->ptbuf)),
- nchar);
+ if (nchar != 0) {
+ if (skb) {
+ memcpy_fromio(skb_put(skb, nchar),
+ (void *)(card->hw.rambase +
+ cpc_readl(&ptdescr->ptbuf)),
+ nchar);
+ }
rcvd += nchar;
}
cpc_writeb(&ptdescr->status, 0);
cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit);
}
+void
+rx_dma_start(pc300_t *card, int ch)
+{
+ uclong scabase = card->hw.scabase;
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+
+ /* Start DMA */
+ cpc_writel(scabase + DRX_REG(CDAL, ch),
+ RX_BD_ADDR(ch, chan->rx_first_bd));
+ if (cpc_readl(scabase + DRX_REG(CDAL,ch)) !=
+ RX_BD_ADDR(ch, chan->rx_first_bd)) {
+ cpc_writel(scabase + DRX_REG(CDAL, ch),
+ RX_BD_ADDR(ch, chan->rx_first_bd));
+ }
+ cpc_writel(scabase + DRX_REG(EDAL, ch),
+ RX_BD_ADDR(ch, chan->rx_last_bd));
+ cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN);
+ cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+ if (!(cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) {
+ cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+ }
+}
+
/*************************/
/*** FALC Routines ***/
/*************************/
cpc_writeb(falcbase + F_REG(SIC1, ch), SIC1_XBS0);
/* Clock mode */
- if (conf->clkrate) { /* Master mode */
+ if (conf->clktype == PC300_CLOCK_INT) { /* Master mode */
cpc_writeb(falcbase + F_REG(LIM0, ch),
cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS);
} else { /* Slave mode */
cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_PMOD);
/* Clock mode */
- if (conf->clkrate) { /* Master mode */
+ if (conf->clktype == PC300_CLOCK_INT) { /* Master mode */
cpc_writeb(falcbase + F_REG(LIM0, ch),
cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS);
} else { /* Slave mode */
/**********************************/
/*** Net Interface Routines ***/
/**********************************/
+
static void
cpc_trace (struct device *dev, struct sk_buff *skb_main, char rx_tx)
{
struct sk_buff *skb;
- if ((skb = dev_alloc_skb(3 + skb_main->len)) == NULL) {
+ if ((skb = dev_alloc_skb(10 + skb_main->len)) == NULL) {
printk("%s: out of memory\n", dev->name);
return;
}
- skb_put (skb, 3 + skb_main->len);
+ skb_put (skb, 10 + skb_main->len);
skb->dev = dev;
skb->protocol = htons(ETH_P_CUST);
skb->mac.raw = skb->data;
skb->pkt_type = PACKET_HOST;
- skb->len = 3 + skb_main->len;
+ skb->len = 10 + skb_main->len;
- skb->data[0] = rx_tx;
- skb->data[1] = ':';
- skb->data[2] = ' ';
- memcpy(&skb->data[3], skb_main->data, skb_main->len);
+ memcpy(&skb->data[0], dev->name, 5);
+ skb->data[5] = '[';
+ skb->data[6] = rx_tx;
+ skb->data[7] = ']';
+ skb->data[8] = ':';
+ skb->data[9] = ' ';
+ memcpy(&skb->data[10], skb_main->data, skb_main->len);
netif_rx(skb);
}
while (1) {
if ((rxb = dma_get_rx_frame_size(card, ch)) == -1)
return;
-
- skb = dev_alloc_skb(rxb);
- if (skb == NULL) {
- printk("%s: Memory squeeze!!\n", dev->name);
- return;
+
+ if (rxb > (dev->mtu + 40)) {
+ printk("%s : MTU exceeded %d\n", dev->name, rxb);
+ skb = NULL;
+ } else {
+ skb = dev_alloc_skb(rxb);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze!!\n", dev->name);
+ return;
+ }
+ skb->dev = dev;
}
- skb->dev = dev;
- if((rxb = dma_buf_read(card, ch, skb)) <= 0) {
+ if(((rxb = dma_buf_read(card, ch, skb)) <= 0) || (skb == NULL)) {
+#ifdef PC300_DEBUG_RX
+ printk("%s: rxb = %x\n", dev->name, rxb);
+#endif
+ if ((skb == NULL) && (rxb >= 0)) {
+ /* rxb > dev->mtu */
+ stats->rx_errors++;
+ stats->rx_length_errors++;
+ continue;
+ }
+
if (rxb < 0) { /* Invalid frame */
rxb = -rxb;
if (rxb & DST_OVR) {
stats->rx_frame_errors++;
}
}
- dev_kfree_skb(skb);
+ if (skb) {
+ dev_kfree_skb(skb);
+ }
continue;
}
cpc_writeb(scabase + DSR_RX(ch), drx_stat | DSR_DWE);
#ifdef PC300_DEBUG_INTR
- printk("sca_intr: RX intr (st=0x%08lx, dsr=0x%02x)\n",
- status, drx_stat);
+ printk("sca_intr: RX intr chan[%d] (st=0x%08lx, dsr=0x%02x)\n",
+ ch, status, drx_stat);
#endif
if (status & IR0_DRX(IR0_DMIA, ch)) {
if (drx_stat & DSR_BOF) {
+ if ((cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) {
+ rx_dma_stop(card, ch);
+ }
+ cpc_net_rx(hdlc);
+ /* Discard invalid frames */
+ hdlc->stats.rx_errors++;
+ hdlc->stats.rx_over_errors++;
chan->rx_first_bd = 0;
chan->rx_last_bd = N_DMA_RX_BUF - 1;
- cpc_writel(scabase + DRX_REG(CDAL, ch),
- RX_BD_ADDR(ch, chan->rx_first_bd));
- cpc_writel(scabase + DRX_REG(EDAL, ch),
- RX_BD_ADDR(ch, chan->rx_last_bd));
- cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN);
- cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+ rx_dma_start(card, ch);
}
}
if (status & IR0_DRX(IR0_DMIB, ch)) {
cpc_writeb(scabase + DSR_TX(ch), dtx_stat | DSR_DWE);
#ifdef PC300_DEBUG_INTR
- printk("sca_intr: TX intr (st=0x%08lx, dsr=0x%02x)\n",
- status, dtx_stat);
+ printk("sca_intr: TX intr chan[%d] (st=0x%08lx, dsr=0x%02x)\n",
+ ch, status, dtx_stat);
#endif
if (status & IR0_DTX(IR0_EFT, ch)) {
if (dtx_stat & DSR_UDRF) {
cpc_writeb(scabase + M_REG(ST1, ch), st1);
#ifdef PC300_DEBUG_INTR
- printk("sca_intr: MSCI intr (st=0x%08lx, st1=0x%02x)\n",
- status, st1);
+ printk("sca_intr: MSCI intr chan[%d] (st=0x%08lx, st1=0x%02x)\n"
+ ,ch, status, st1);
#endif
if (st1 & ST1_CDCD) { /* DCD changed */
if (cpc_readb(scabase + M_REG(ST3, ch)) & ST3_DCD) {
return; /* spurious intr */
}
+ if (card->hw.rambase == 0) {
+ printk("cpc_intr: spurious intr2 %d\n", irq);
+ return; /* spurious intr */
+ }
+
switch (card->hw.type) {
case PC300_RSV:
case PC300_X21:
uclong plxbase = card->hw.plxbase;
int ch = chan->channel;
uclong clkrate = chan->conf.clkrate;
+ uclong clktype = chan->conf.clktype;
ucchar loopback = (conf->loopback ? MD2_LOOP_MIR : MD2_F_DUPLEX);
+ ucshort encoding = chan->conf.encoding;
+ ucshort parity = chan->conf.parity;
int tmc, br;
+ ucchar md0, md2;
/* Reset the channel */
cpc_writeb(scabase + M_REG(CMD, ch), CMD_CH_RST);
/* Configure the SCA registers */
- cpc_writeb(scabase + M_REG(MD0, ch),
- (MD0_CRC_CCITT|MD0_CRCC0|MD0_BIT_SYNC));
+ switch (parity) {
+ case PC300_PARITY_NONE:
+ md0 = MD0_BIT_SYNC;
+ break;
+ case PC300_PARITY_CRC16_PR0:
+ md0 = MD0_CRC16_0|MD0_CRCC0|MD0_BIT_SYNC;
+ break;
+ case PC300_PARITY_CRC16_PR1:
+ md0 = MD0_CRC16_1|MD0_CRCC0|MD0_BIT_SYNC;
+ break;
+ case PC300_PARITY_CRC32_PR1_CCITT:
+ md0 = MD0_CRC32|MD0_CRCC0|MD0_BIT_SYNC;
+ break;
+ case PC300_PARITY_CRC16_PR1_CCITT:
+ default:
+ md0 = MD0_CRC_CCITT|MD0_CRCC0|MD0_BIT_SYNC;
+ break;
+ }
+ switch (encoding) {
+ case PC300_ENCODING_NRZI:
+ md2 = loopback|MD2_ADPLL_X8|MD2_NRZI;
+ break;
+ case PC300_ENCODING_FM_MARK: /* FM1 */
+ md2 = loopback|MD2_ADPLL_X8|MD2_FM|MD2_FM1;
+ break;
+ case PC300_ENCODING_FM_SPACE: /* FM0 */
+ md2 = loopback|MD2_ADPLL_X8|MD2_FM|MD2_FM0;
+ break;
+ case PC300_ENCODING_MANCHESTER: /* It's not working... */
+ md2 = loopback|MD2_ADPLL_X8|MD2_FM|MD2_MANCH;
+ break;
+ case PC300_ENCODING_NRZ:
+ default:
+ md2 = loopback|MD2_ADPLL_X8|MD2_NRZ;
+ break;
+ }
+
+ cpc_writeb(scabase + M_REG(MD0, ch), md0);
cpc_writeb(scabase + M_REG(MD1, ch), 0);
- cpc_writeb(scabase + M_REG(MD2, ch), (loopback|MD2_ADPLL_X8|MD2_NRZ));
+ cpc_writeb(scabase + M_REG(MD2, ch), md2);
cpc_writeb(scabase + M_REG(IDL, ch), 0x7e);
cpc_writeb(scabase + M_REG(CTL, ch), CTL_URSKP|CTL_IDLC);
switch(card->hw.type) {
case PC300_RSV:
case PC300_X21:
- if (clkrate) {
+ if (clktype == PC300_CLOCK_INT || clktype == PC300_CLOCK_TXINT) {
/* Calculate the clkrate parameters */
tmc = clock_rate_calc(clkrate, card->hw.clock, &br);
cpc_writeb(scabase + M_REG(TMCT, ch), tmc);
cpc_writeb(scabase + M_REG(TXS, ch), (TXS_DTRXC|TXS_IBRG|br));
- cpc_writeb(scabase + M_REG(TMCR, ch), tmc);
- cpc_writeb(scabase + M_REG(RXS, ch), (RXS_IBRG|br));
+ if (clktype == PC300_CLOCK_INT) {
+ cpc_writeb(scabase + M_REG(TMCR, ch), tmc);
+ cpc_writeb(scabase + M_REG(RXS, ch), (RXS_IBRG|br));
+ } else {
+ cpc_writeb(scabase + M_REG(TMCR, ch), 1);
+ cpc_writeb(scabase + M_REG(RXS, ch), 0);
+ }
if (card->hw.type == PC300_X21) {
cpc_writeb(scabase + M_REG(GPO, ch), 1);
cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1|EXS_RES1);
}
} else {
cpc_writeb(scabase + M_REG(TMCT, ch), 1);
- cpc_writeb(scabase + M_REG(TXS, ch), TXS_DTRXC);
+ if (clktype == PC300_CLOCK_EXT) {
+ cpc_writeb(scabase + M_REG(TXS, ch), TXS_DTRXC);
+ } else {
+ cpc_writeb(scabase + M_REG(TXS, ch), TXS_DTRXC|TXS_RCLK);
+ }
cpc_writeb(scabase + M_REG(TMCR, ch), 1);
cpc_writeb(scabase + M_REG(RXS, ch), 0);
if (card->hw.type == PC300_X21) {
IR0_M(IR0_RXINTA, ch) |
IR0_DRX(IR0_EFT|IR0_DMIA|IR0_DMIB, ch) |
IR0_DTX(IR0_EFT|IR0_DMIA|IR0_DMIB, ch));
- cpc_writel(scabase + M_REG(IE0, ch),
- cpc_readl(scabase + M_REG(IE0, ch)) | IE0_RXINTA);
- cpc_writel(scabase + M_REG(IE1, ch),
- cpc_readl(scabase + M_REG(IE1, ch)) | IE1_CDCD);
+ cpc_writeb(scabase + M_REG(IE0, ch),
+ cpc_readb(scabase + M_REG(IE0, ch)) | IE0_RXINTA);
+ cpc_writeb(scabase + M_REG(IE1, ch),
+ cpc_readb(scabase + M_REG(IE1, ch)) | IE1_CDCD);
return 0;
}
cpc_writeb(scabase + DIR_RX(ch), (DIR_EOM | DIR_BOF));
/* Start DMA */
- cpc_writel(scabase + DRX_REG(CDAL, ch), RX_BD_ADDR(ch, chan->rx_first_bd));
- cpc_writel(scabase + DRX_REG(EDAL, ch), RX_BD_ADDR(ch, chan->rx_last_bd));
- cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN);
- cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+ rx_dma_start(card, ch);
return 0;
}
int ch = chan->channel;
cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_CH_RST);
+ rx_dma_stop(card, ch);
+ tx_dma_stop(card, ch);
+
if (card->hw.type == PC300_TE) {
memset(pfalc, 0, sizeof(falc_t));
cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2,
pc300dev_t *d = (pc300dev_t *)dev->priv;
int err = -1;
+#ifdef PC300_DEBUG_OTHER
+ printk("pc300: cpc_open");
+#endif
+
err = cpc_opench(d);
if (err)
return err;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
+
MOD_INC_USE_COUNT;
return 0;
}
pc300_t *card = (pc300_t *)chan->card;
uclong flags;
+#ifdef PC300_DEBUG_OTHER
+ printk("pc300: cpc_close");
+#endif
+
CPC_LOCK(card, flags);
switch(hdlc->mode & ~MODE_SOFT) {
#ifdef CONFIG_PC300_X25
tmp = strchr(rcsvers, ' '); *tmp++ = '\0';
rcsdate = strchr(tmp, ' '); rcsdate++;
tmp = strrchr(rcsdate, ' '); *tmp = '\0';
- printk("Cyclades-PC300 driver %s %s\n", rcsvers, rcsdate);
- printk(" built %s %s\n", __DATE__, __TIME__);
+ printk(KERN_INFO "Cyclades-PC300 driver %s %s (built %s %s)\n",
+ rcsvers, rcsdate, __DATE__, __TIME__);
} /* show_version */
__initfunc(int
chan->card = card;
chan->channel = j;
- chan->conf.clkrate = 64000;
+ chan->conf.clkrate = 0;
+ chan->conf.clktype = PC300_CLOCK_EXT;
chan->conf.loopback = 0;
+ chan->conf.encoding = PC300_ENCODING_NRZ;
+ chan->conf.parity = PC300_PARITY_CRC16_PR1_CCITT;
switch(card->hw.type) {
case PC300_TE:
chan->conf.media = LINE_T1;
case PC300_RSV:
default:
- chan->conf.media = LINE_RS232;
+ chan->conf.media = LINE_V35;
break;
}
chan->tx_first_bd = 0;
if(register_hdlc_device(hdlc) == 0) {
dev->priv = d; /* We need 'priv', hdlc doesn't */
- printk("%s: PC300/", hdlc->name);
+ printk("%s: Cyclades-PC300/", hdlc->name);
switch(card->hw.type) {
case PC300_TE:
printk("TE ");
pc300_t *card = &cpc_card[i];
if (card->hw.rambase != 0) {
+ /* Disable interrupts on the PCI bridge */
+ cpc_writew(card->hw.plxbase+0x4c,
+ cpc_readw(card->hw.plxbase+0x4c) & ~(0x0040));
+
for(j = 0 ; j < card->hw.nchan ; j++) {
unregister_hdlc_device(card->chan[j].d.hdlc);
}
* 2 of the License, or (at your option) any later version.
*
* $Log: pc300.h,v $
+ * Revision 3.2 to 3.6 2001/09/28 13:16:03 daniela
+ * Included kernel version.
+ * New configuration parameters (line code, CRC calculation and clock).
+ * Increased DEF_MTU and TX_QUEUE_LEN.
+ *
+ * Revision 3.1 2001/06/15 12:41:10 regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1 2001/06/13 20:24:38 daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
* Revision 2.5 2001/03/02 daniela
* Created struct pc300conf, to provide the hardware information to pc300util.
*
typedef unsigned char ucchar; /* 8 bits, unsigned */
#endif /* CY_TYPES */
+#define PC300_KERNEL "2.2.x" /* Kernel supported by this driver */
+
#define PC300_DEVNAME "hdlc" /* Dev. name base (for hdlc0, hdlc1, etc.) */
#define PC300_MAXINDEX 100 /* Max dev. name index (the '0' in hdlc0) */
typedef struct pc300chconf {
ucchar media; /* HW media (RS232, V.35, etc.) */
uclong proto; /* Protocol (PPP, X.25, etc.) */
+ uclong clktype; /* Clock type (ext, int, txint, txfromrx) */
uclong clkrate; /* Clock rate (in bps, 0 = ext. clock) */
ucchar loopback; /* Loopback mode */
ucchar monitor; /* Monitor mode (0 = off, !0 = on) */
+ ucshort encoding; /* NRZ, NRZI, FM0, FM1 (FMi - only RSV/X.21) */
+ ucshort parity; /* CRC calculation */
/* TE-specific parameters */
ucchar lcode; /* Line Code (AMI, B8ZS, etc.) */
SIOCSPC300TRACE,
SIOCSPC300LOOPBACK,
SIOCSPC300PATTERNTEST,
+ SIOCGPC300HARDWARE,
};
/* Loopback types - PC300/TE boards */
#define PC300_X21 0x02
#define PC300_TE 0x03
+#define PC300_CLOCK_EXT 0 /* External TX and RX clock - DTE */
+#define PC300_CLOCK_INT 1 /* Internal TX and RX clock - DCE */
+#define PC300_CLOCK_TXINT 2 /* Internal TX and external RX clock */
+#define PC300_CLOCK_TXFROMRX 3 /* TX clock derived from external RX clock */
+
+#define PC300_ENCODING_NRZ 0x0000
+#define PC300_ENCODING_NRZI 0x0001
+#define PC300_ENCODING_FM_MARK 0x0002
+#define PC300_ENCODING_FM_SPACE 0x0003
+#define PC300_ENCODING_MANCHESTER 0x0004
+
+#define PC300_PARITY_NONE 0x0000
+#define PC300_PARITY_CRC16_PR0 0x0001
+#define PC300_PARITY_CRC16_PR1 0x0002
+#define PC300_PARITY_CRC16_PR0_CCITT 0x0003
+#define PC300_PARITY_CRC16_PR1_CCITT 0x0004
+#define PC300_PARITY_CRC32_PR1_CCITT 0x0005
+
#define PC300_LC_AMI 0x01
#define PC300_LC_B8ZS 0x02
#define PC300_LC_NRZ 0x03
#define PC300_RX_SENS_LH 0x02
#define PC300_TX_TIMEOUT (2*HZ)
-#define PC300_TX_QUEUE_LEN 10
-#define PC300_DEF_MTU 1500
+#define PC300_TX_QUEUE_LEN 100
+#define PC300_DEF_MTU 1600
#ifdef __KERNEL__
/* Function Prototypes */
struct open_request **syn_wait_last;
int syn_backlog; /* Backlog of received SYNs */
+
+ unsigned long last_synq_overflow;
};
#ifdef CONFIG_ADBMOUSE
extern void adb_mouse_setup(char *str, int *ints);
#endif
+#ifdef CONFIG_COMPUTONE
+extern void ip2_setup(char *str, int *ints);
+#endif
#ifdef CONFIG_WDT
extern void wdt_setup(char *str, int *ints);
#endif
#ifdef CONFIG_SOUNDMODEM
{ "soundmodem=", sm_setup },
#endif
+#ifdef CONFIG_COMPUTONE
+ { "ip2=", ip2_setup };
+#endif
#ifdef CONFIG_WDT
{ "wdt=", wdt_setup },
#endif
extern int sysctl_tcp_syncookies;
-static unsigned long tcp_lastsynq_overflow;
-
/*
* This table has to be sorted and terminated with (__u16)-1.
* XXX generate a better table.
int mssind;
const __u16 mss = *mssp;
- tcp_lastsynq_overflow = jiffies;
+
+ sk->tp_pinfo.af_tcp.last_synq_overflow = jiffies;
+
/* XXX sort msstab[] by probability? Binary search? */
for (mssind = 0; mss > msstab[mssind+1]; mssind++)
;
* Check if a ack sequence number is a valid syncookie.
* Return the decoded mss if it is, or 0 if not.
*/
-static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
+static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
{
__u32 seq;
__u32 mssind;
- if ((jiffies - tcp_lastsynq_overflow) > TCP_TIMEOUT_INIT)
- return 0;
-
seq = ntohl(skb->h.th->seq)-1;
mssind = check_tcp_syn_cookie(cookie,
skb->nh.iph->saddr, skb->nh.iph->daddr,
return sk;
if (!skb->h.th->ack)
return sk;
+ if (time_after(jiffies, sk->tp_pinfo.af_tcp.last_synq_overflow + TCP_TIMEOUT_INIT))
+ return 0;
mss = cookie_check(skb, cookie);
if (mss == 0) {