fd_mcs.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:43k
- /* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver
- *
- * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net)
- *
- * This driver is cloned from fdomain.* to specifically support
- * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s
- * also equipped with IBM Fast SCSI Adapter/A which is an OEM
- * of MCS 700.
- *
- * This driver also supports Reply SB16/SCSI card (the SCSI part).
- *
- * What makes this driver different is that this driver is MCA only
- * and it supports multiple adapters in the same system, IRQ
- * sharing, some driver statistics, and maps highest SCSI id to sda.
- * All cards are auto-detected.
- *
- * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4
- *
- * LILO command-line options:
- * fd_mcs=<FIFO_COUNT>[,<FIFO_SIZE>]
- *
- * ********************************************************
- * Please see Copyrights/Comments in fdomain.* for credits.
- * Following is from fdomain.c for acknowledgement:
- *
- * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Wed Oct 2 11:10:55 1996 by r.faith@ieee.org
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
- *
- * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- **************************************************************************
- NOTES ON USER DEFINABLE OPTIONS:
- DEBUG: This turns on the printing of various debug information.
- ENABLE_PARITY: This turns on SCSI parity checking. With the current
- driver, all attached devices must support SCSI parity. If none of your
- devices support parity, then you can probably get the driver to work by
- turning this option off. I have no way of testing this, however, and it
- would appear that no one ever uses this option.
- FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
- 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
- the SCSI device, an interrupt will be raised. Therefore, this could be as
- low as 0, or as high as 16. Note, however, that values which are too high
- or too low seem to prevent any interrupts from occurring, and thereby lock
- up the machine. I have found that 2 is a good number, but throughput may
- be increased by changing this value to values which are close to 2.
- Please let me know if you try any different values.
- [*****Now a runtime option*****]
- RESELECTION: This is no longer an option, since I gave up trying to
- implement it in version 4.x of this driver. It did not improve
- performance at all and made the driver unstable (because I never found one
- of the two race conditions which were introduced by the multiple
- outstanding command code). The instability seems a very high price to pay
- just so that you don't have to wait for the tape to rewind. If you want
- this feature implemented, send me patches. I'll be happy to send a copy
- of my (broken) driver to anyone who would like to see a copy.
- **************************************************************************/
- #include <linux/module.h>
- #include <linux/sched.h>
- #include <linux/blk.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/ioport.h>
- #include <linux/proc_fs.h>
- #include <linux/delay.h>
- #include <linux/mca.h>
- #include <linux/spinlock.h>
- #include <asm/io.h>
- #include <asm/system.h>
- #include "scsi.h"
- #include "hosts.h"
- #include "fd_mcs.h"
- #define DRIVER_VERSION "v0.2 by ZP Gu<zpg@castle.net>"
-
- /* START OF USER DEFINABLE OPTIONS */
- #define DEBUG 0 /* Enable debugging output */
- #define ENABLE_PARITY 1 /* Enable SCSI Parity */
- #define DO_DETECT 0 /* Do device detection here (see scsi.c) */
- /* END OF USER DEFINABLE OPTIONS */
- #if DEBUG
- #define EVERY_ACCESS 0 /* Write a line on every scsi access */
- #define ERRORS_ONLY 1 /* Only write a line if there is an error */
- #define DEBUG_DETECT 1 /* Debug fd_mcs_detect() */
- #define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
- #define DEBUG_ABORT 1 /* Debug abort() routine */
- #define DEBUG_RESET 1 /* Debug reset() routine */
- #define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
- #else
- #define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
- #define ERRORS_ONLY 0
- #define DEBUG_DETECT 0
- #define DEBUG_MESSAGES 0
- #define DEBUG_ABORT 0
- #define DEBUG_RESET 0
- #define DEBUG_RACE 0
- #endif
- /* Errors are reported on the line, so we don't need to report them again */
- #if EVERY_ACCESS
- #undef ERRORS_ONLY
- #define ERRORS_ONLY 0
- #endif
- #if ENABLE_PARITY
- #define PARITY_MASK 0x08
- #else
- #define PARITY_MASK 0x00
- #endif
- enum chip_type {
- unknown = 0x00,
- tmc1800 = 0x01,
- tmc18c50 = 0x02,
- tmc18c30 = 0x03,
- };
- enum {
- in_arbitration = 0x02,
- in_selection = 0x04,
- in_other = 0x08,
- disconnect = 0x10,
- aborted = 0x20,
- sent_ident = 0x40,
- };
- enum in_port_type {
- Read_SCSI_Data = 0,
- SCSI_Status = 1,
- TMC_Status = 2,
- FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
- Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
- LSB_ID_Code = 5,
- MSB_ID_Code = 6,
- Read_Loopback = 7,
- SCSI_Data_NoACK = 8,
- Interrupt_Status = 9,
- Configuration1 = 10,
- Configuration2 = 11, /* tmc18c50/tmc18c30 only */
- Read_FIFO = 12,
- FIFO_Data_Count = 14
- };
- enum out_port_type {
- Write_SCSI_Data = 0,
- SCSI_Cntl = 1,
- Interrupt_Cntl = 2,
- SCSI_Mode_Cntl = 3,
- TMC_Cntl = 4,
- Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
- Write_Loopback = 7,
- IO_Control = 11, /* tmc18c30 only */
- Write_FIFO = 12
- };
- struct fd_hostdata {
- unsigned long _bios_base;
- int _bios_major;
- int _bios_minor;
- volatile int _in_command;
- Scsi_Cmnd *_current_SC;
- enum chip_type _chip;
- int _adapter_mask;
- int _fifo_count; /* Number of 512 byte blocks before INTR */
- char _adapter_name[64];
- #if DEBUG_RACE
- volatile int _in_interrupt_flag;
- #endif
- int _SCSI_Mode_Cntl_port;
- int _FIFO_Data_Count_port;
- int _Interrupt_Cntl_port;
- int _Interrupt_Status_port;
- int _Interrupt_Cond_port;
- int _Read_FIFO_port;
- int _Read_SCSI_Data_port;
- int _SCSI_Cntl_port;
- int _SCSI_Data_NoACK_port;
- int _SCSI_Status_port;
- int _TMC_Cntl_port;
- int _TMC_Status_port;
- int _Write_FIFO_port;
- int _Write_SCSI_Data_port;
- int _FIFO_Size; /* = 0x2000; 8k FIFO for
- pre-tmc18c30 chips */
- /* simple stats */
- int _Bytes_Read;
- int _Bytes_Written;
- int _INTR_Processed;
- };
- #define FD_MAX_HOSTS 3 /* enough? */
- #define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata)
- #define bios_base (HOSTDATA(shpnt)->_bios_base)
- #define bios_major (HOSTDATA(shpnt)->_bios_major)
- #define bios_minor (HOSTDATA(shpnt)->_bios_minor)
- #define in_command (HOSTDATA(shpnt)->_in_command)
- #define current_SC (HOSTDATA(shpnt)->_current_SC)
- #define chip (HOSTDATA(shpnt)->_chip)
- #define adapter_mask (HOSTDATA(shpnt)->_adapter_mask)
- #define FIFO_COUNT (HOSTDATA(shpnt)->_fifo_count)
- #define adapter_name (HOSTDATA(shpnt)->_adapter_name)
- #if DEBUG_RACE
- #define in_interrupt_flag (HOSTDATA(shpnt)->_in_interrupt_flag)
- #endif
- #define SCSI_Mode_Cntl_port (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port)
- #define FIFO_Data_Count_port (HOSTDATA(shpnt)->_FIFO_Data_Count_port)
- #define Interrupt_Cntl_port (HOSTDATA(shpnt)->_Interrupt_Cntl_port)
- #define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port)
- #define Interrupt_Cond_port (HOSTDATA(shpnt)->_Interrupt_Cond_port)
- #define Read_FIFO_port (HOSTDATA(shpnt)->_Read_FIFO_port)
- #define Read_SCSI_Data_port (HOSTDATA(shpnt)->_Read_SCSI_Data_port)
- #define SCSI_Cntl_port (HOSTDATA(shpnt)->_SCSI_Cntl_port)
- #define SCSI_Data_NoACK_port (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port)
- #define SCSI_Status_port (HOSTDATA(shpnt)->_SCSI_Status_port)
- #define TMC_Cntl_port (HOSTDATA(shpnt)->_TMC_Cntl_port)
- #define TMC_Status_port (HOSTDATA(shpnt)->_TMC_Status_port)
- #define Write_FIFO_port (HOSTDATA(shpnt)->_Write_FIFO_port)
- #define Write_SCSI_Data_port (HOSTDATA(shpnt)->_Write_SCSI_Data_port)
- #define FIFO_Size (HOSTDATA(shpnt)->_FIFO_Size)
- #define Bytes_Read (HOSTDATA(shpnt)->_Bytes_Read)
- #define Bytes_Written (HOSTDATA(shpnt)->_Bytes_Written)
- #define INTR_Processed (HOSTDATA(shpnt)->_INTR_Processed)
- struct fd_mcs_adapters_struct {
- char* name;
- int id;
- enum chip_type fd_chip;
- int fifo_size;
- int fifo_count;
- };
- #define REPLY_ID 0x5137
- static struct fd_mcs_adapters_struct fd_mcs_adapters[] = {
- { "Future Domain SCSI Adapter MCS-700(18C50)",
- 0x60e9,
- tmc18c50,
- 0x2000,
- 4 },
- { "Future Domain SCSI Adapter MCS-600/700(TMC-1800)",
- 0x6127,
- tmc1800,
- 0x2000,
- 4 },
- { "Reply Sound Blaster/SCSI Adapter",
- REPLY_ID,
- tmc18c30,
- 0x800,
- 2 },
- };
- #define FD_BRDS sizeof(fd_mcs_adapters)/sizeof(struct fd_mcs_adapters_struct)
- static void fd_mcs_intr( int irq, void *dev_id, struct pt_regs * regs );
- static unsigned long addresses[] = {0xc8000, 0xca000, 0xce000, 0xde000};
- static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
- static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
- /* host information */
- static int found = 0;
- static struct Scsi_Host *hosts[FD_MAX_HOSTS+1] = { NULL };
- static int user_fifo_count = 0;
- static int user_fifo_size = 0;
- void fd_mcs_setup( char *str, int *ints )
- {
- static int done_setup = 0;
- if (done_setup++ || ints[0] < 1 || ints[0] > 2 ||
- ints[1] < 1 || ints[1] > 16) {
- printk( "fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZEn" );
- }
- user_fifo_count = ints[0] >= 1 ? ints[1] : 0;
- user_fifo_size = ints[0] >= 2 ? ints[2] : 0;
- }
- static void print_banner( struct Scsi_Host *shpnt )
- {
- printk( "scsi%d <fd_mcs>: ", shpnt->host_no);
- if (bios_base) {
- printk( "BIOS at 0x%lX", bios_base);
- } else {
- printk( "No BIOS");
- }
- printk( ", HostID %d, %s Chip, IRQ %d, IO 0x%lXn",
- shpnt->this_id,
- chip == tmc18c50 ? "TMC-18C50"
- : (chip == tmc18c30 ? "TMC-18C30" :
- (chip == tmc1800 ? "TMC-1800" : "Unknown")),
- shpnt->irq,
- shpnt->io_port );
- }
- static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
- {
- do {
- udelay(10*1000);
- } while (--amount);
- }
- inline static void fd_mcs_make_bus_idle( struct Scsi_Host *shpnt )
- {
- outb( 0, SCSI_Cntl_port );
- outb( 0, SCSI_Mode_Cntl_port );
- if (chip == tmc18c50 || chip == tmc18c30)
- outb( 0x21 | PARITY_MASK, TMC_Cntl_port ); /* Clear forced intr. */
- else
- outb( 0x01 | PARITY_MASK, TMC_Cntl_port );
- }
- int fd_mcs_detect( Scsi_Host_Template *tpnt )
- {
- int loop;
- struct Scsi_Host *shpnt;
- /* get id, port, bios, irq */
- int slot;
- u_char pos2, pos3, pos4;
- int id, port, irq;
- unsigned long bios;
- /* if not MCA machine, return */
- if (!MCA_bus)
- return 0;
- /* changeable? */
- id = 7;
- for( loop = 0; loop < FD_BRDS; loop++ ) {
- slot = 0;
- while ( MCA_NOTFOUND !=
- (slot = mca_find_adapter(fd_mcs_adapters[loop].id,
- slot)) ) {
- /* if we get this far, an adapter has been detected and is
- enabled */
- printk("scsi <fd_mcs>: %s at slot %dn",
- fd_mcs_adapters[loop].name, slot + 1 );
- pos2 = mca_read_stored_pos( slot, 2 );
- pos3 = mca_read_stored_pos( slot, 3 );
- pos4 = mca_read_stored_pos( slot, 4);
- /* ready for next probe */
- slot++;
- if (fd_mcs_adapters[loop].id == REPLY_ID) { /* reply card */
- static int reply_irq[] = {10, 11, 14, 15};
- bios = 0; /* no bios */
- if (pos2 & 0x2)
- port = ports[pos4 & 0x3];
- else
- continue;
- /* can't really disable it, same as irq=10 */
- irq = reply_irq[((pos4 >> 2) & 0x1) + 2*((pos4 >> 4) & 0x1)];
- } else {
- bios = addresses[pos2 >> 6];
- port = ports[(pos2 >> 4) & 0x03];
- irq = ints[(pos2 >> 1) & 0x07];
- }
- if (irq) {
- /* claim the slot */
- mca_set_adapter_name( slot-1, fd_mcs_adapters[loop].name );
- /* check irq/region */
- if (check_region(port, 0x10) ||
- request_irq(irq, fd_mcs_intr,
- SA_SHIRQ, "fd_mcs", hosts)) {
- printk( "fd_mcs: check_region() || request_irq() failed, Skip itn");
- continue;
- }
- /* register */
- if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) {
- printk( "fd_mcs: scsi_register() failedn");
- continue;
- }
- /* request I/O region */
- request_region( port, 0x10, "fd_mcs" );
- /* save name */
- strcpy(adapter_name, fd_mcs_adapters[loop].name);
- /* chip/fifo */
- chip = fd_mcs_adapters[loop].fd_chip;
- /* use boot time value if available */
- FIFO_COUNT =
- user_fifo_count?user_fifo_count:fd_mcs_adapters[loop].fifo_count;
- FIFO_Size =
- user_fifo_size?user_fifo_size:fd_mcs_adapters[loop].fifo_size;
- #ifdef NOT_USED
- /* *************************************************** */
- /* Try to toggle 32-bit mode. This only
- works on an 18c30 chip. (User reports
- say this works, so we should switch to
- it in the near future.) */
- outb( 0x80, port + IO_Control );
- if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
- outb( 0x00, port + IO_Control );
- if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
- chip = tmc18c30;
- FIFO_Size = 0x800; /* 2k FIFO */
- printk("FIRST: chip=%s, fifo_size=0x%xn",
- (chip == tmc18c30)?"tmc18c30":"tmc18c50", FIFO_Size);
- }
- }
- /* That should have worked, but appears to
- have problems. Let's assume it is an
- 18c30 if the RAM is disabled. */
- if (inb( port + Configuration2 ) & 0x02) {
- chip = tmc18c30;
- FIFO_Size = 0x800; /* 2k FIFO */
- printk("SECOND: chip=%s, fifo_size=0x%xn",
- (chip == tmc18c30)?"tmc18c30":"tmc18c50", FIFO_Size);
- }
- /* *************************************************** */
- #endif
- /* IBM/ANSI scsi scan ordering */
- /* Stick this back in when the scsi.c changes are there */
- shpnt->reverse_ordering = 1;
-
- /* saving info */
- hosts[found++] = shpnt;
- shpnt->this_id = id;
- shpnt->irq = irq;
- shpnt->io_port = port;
- shpnt->n_io_port = 0x10;
- /* save */
- bios_base = bios;
- adapter_mask = (1 << id);
- /* save more */
- SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl;
- FIFO_Data_Count_port = port + FIFO_Data_Count;
- Interrupt_Cntl_port = port + Interrupt_Cntl;
- Interrupt_Status_port = port + Interrupt_Status;
- Interrupt_Cond_port = port + Interrupt_Cond;
- Read_FIFO_port = port + Read_FIFO;
- Read_SCSI_Data_port = port + Read_SCSI_Data;
- SCSI_Cntl_port = port + SCSI_Cntl;
- SCSI_Data_NoACK_port = port + SCSI_Data_NoACK;
- SCSI_Status_port = port + SCSI_Status;
- TMC_Cntl_port = port + TMC_Cntl;
- TMC_Status_port = port + TMC_Status;
- Write_FIFO_port = port + Write_FIFO;
- Write_SCSI_Data_port = port + Write_SCSI_Data;
- Bytes_Read = 0;
- Bytes_Written = 0;
- INTR_Processed = 0;
- /* say something */
- print_banner( shpnt );
- /* reset */
- outb( 1, SCSI_Cntl_port );
- do_pause( 2 );
- outb( 0, SCSI_Cntl_port );
- do_pause( 115 );
- outb( 0, SCSI_Mode_Cntl_port );
- outb( PARITY_MASK, TMC_Cntl_port );
- /* done reset */
- #if DO_DETECT
- /* scan devices attached */
- {
- const int buflen = 255;
- int i, j, retcode;
- Scsi_Cmnd SCinit;
- unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
- unsigned char do_request_sense[] = { REQUEST_SENSE,
- 0, 0, 0, buflen, 0 };
- unsigned char do_read_capacity[] = { READ_CAPACITY,
- 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- unsigned char buf[buflen];
- SCinit.request_buffer = SCinit.buffer = buf;
- SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1;
- SCinit.use_sg = 0;
- SCinit.lun = 0;
- SCinit.host = shpnt;
- printk( "fd_mcs: detection routine scanning for devices:n" );
- for (i = 0; i < 8; i++) {
- if (i == shpnt->this_id) /* Skip host adapter */
- continue;
- SCinit.target = i;
- memcpy(SCinit.cmnd, do_request_sense,
- sizeof(do_request_sense));
- retcode = fd_mcs_command(&SCinit);
- if (!retcode) {
- memcpy(SCinit.cmnd, do_inquiry, sizeof(do_inquiry));
- retcode = fd_mcs_command(&SCinit);
- if (!retcode) {
- printk( " SCSI ID %d: ", i );
- for (j = 8; j < (buf[4] < 32 ? buf[4] : 32); j++)
- printk( "%c", buf[j] >= 20 ? buf[j] : ' ' );
- memcpy(SCinit.cmnd, do_read_capacity,
- sizeof(do_read_capacity));
- retcode = fd_mcs_command(&SCinit);
- if (!retcode) {
- unsigned long blocks, size, capacity;
-
- blocks = (buf[0] << 24) | (buf[1] << 16)
- | (buf[2] << 8) | buf[3];
- size = (buf[4] << 24) | (buf[5] << 16) |
- (buf[6] << 8) | buf[7];
- capacity = +( +(blocks / 1024L) * +(size * 10L)) / 1024L;
-
- printk( "%lu MB (%lu byte blocks)n",
- ((capacity + 5L) / 10L), size );
- }
- }
- }
- }
- }
- #endif
- }
- }
- if (found == FD_MAX_HOSTS) {
- printk( "fd_mcs: detecting reached max=%d host adapters.n",
- FD_MAX_HOSTS);
- break;
- }
- }
- return found;
- }
- const char *fd_mcs_info(struct Scsi_Host *shpnt)
- {
- return adapter_name;
- }
- static int TOTAL_INTR = 0;
- /*
- * inout : decides on the direction of the dataflow and the meaning of the
- * variables
- * buffer: If inout==FALSE data is being written to it else read from it
- * *start: If inout==FALSE start of the valid data in the buffer
- * offset: If inout==FALSE offset from the beginning of the imaginary file
- * from which we start writing into the buffer
- * length: If inout==FALSE max number of bytes to be written into the buffer
- * else number of bytes in the buffer
- */
- int fd_mcs_proc_info( char *buffer, char **start, off_t offset,
- int length, int hostno, int inout )
- {
- struct Scsi_Host *shpnt;
- int len = 0;
- int i;
- if (inout)
- return(-ENOSYS);
- *start = buffer + offset;
- for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++);
- shpnt = hosts[i];
- if (!shpnt) {
- return(-ENOENT);
- } else {
- len += sprintf(buffer+len, "Future Domain MCS-600/700 Driver %sn",
- DRIVER_VERSION);
- len += sprintf(buffer+len, "HOST #%d: %sn",
- hostno, adapter_name);
- len += sprintf(buffer+len, "FIFO Size=0x%x, FIFO Count=%dn",
- FIFO_Size, FIFO_COUNT);
- len += sprintf(buffer+len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%dnn",
- TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written);
- }
- if ((len -= offset) <= 0)
- return 0;
- if (len > length)
- len = length;
- return len;
- }
-
- static int fd_mcs_select(struct Scsi_Host *shpnt, int target )
- {
- int status;
- unsigned long timeout;
- outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
- outb( adapter_mask | (1 << target), SCSI_Data_NoACK_port );
- /* Stop arbitration and enable parity */
- outb( PARITY_MASK, TMC_Cntl_port );
- timeout = 350; /* 350mS -- because of timeouts
- (was 250mS) */
- do {
- status = inb( SCSI_Status_port ); /* Read adapter status */
- if (status & 1) { /* Busy asserted */
- /* Enable SCSI Bus (on error, should make bus idle with 0) */
- outb( 0x80, SCSI_Cntl_port );
- return 0;
- }
- udelay(1000); /* wait one msec */
- } while (--timeout);
- /* Make bus idle */
- fd_mcs_make_bus_idle(shpnt);
- #if EVERY_ACCESS
- if (!target) printk( "Selection failedn" );
- #endif
- #if ERRORS_ONLY
- if (!target) {
- static int flag = 0;
- if (!flag) /* Skip first failure for all chips. */
- ++flag;
- else
- printk( "fd_mcs: Selection failedn" );
- }
- #endif
- return 1;
- }
- static void my_done( struct Scsi_Host *shpnt, int error )
- {
- if (in_command) {
- in_command = 0;
- outb( 0x00, Interrupt_Cntl_port );
- fd_mcs_make_bus_idle(shpnt);
- current_SC->result = error;
- current_SC->scsi_done( current_SC );
- } else {
- panic( "fd_mcs: my_done() called outside of commandn" );
- }
- #if DEBUG_RACE
- in_interrupt_flag = 0;
- #endif
- }
- /* only my_done needs to be protected */
- static void fd_mcs_intr( int irq, void *dev_id, struct pt_regs * regs )
- {
- unsigned long flags;
- int status;
- int done = 0;
- unsigned data_count, tmp_count;
- int i = 0;
- struct Scsi_Host *shpnt;
- TOTAL_INTR++;
- /* search for one adapter-response on shared interrupt */
- while ((shpnt = hosts[i++])) {
- if ((inb(TMC_Status_port)) & 1)
- break;
- }
-
- /* return if some other device on this IRQ caused the interrupt */
- if (!shpnt) {
- return;
- }
- INTR_Processed++;
- outb( 0x00, Interrupt_Cntl_port );
- /* Abort calls my_done, so we do nothing here. */
- if (current_SC->SCp.phase & aborted) {
- #if DEBUG_ABORT
- printk( "Interrupt after abort, ignoringn" );
- #endif
- /* return; */
- }
- #if DEBUG_RACE
- ++in_interrupt_flag;
- #endif
- if (current_SC->SCp.phase & in_arbitration) {
- status = inb( TMC_Status_port ); /* Read adapter status */
- if (!(status & 0x02)) {
- #if EVERY_ACCESS
- printk( " AFAIL " );
- #endif
- spin_lock_irqsave(&io_request_lock, flags);
- my_done( shpnt, DID_BUS_BUSY << 16 );
- spin_unlock_irqrestore(&io_request_lock, flags);
- return;
- }
- current_SC->SCp.phase = in_selection;
-
- outb( 0x40 | FIFO_COUNT, Interrupt_Cntl_port );
- outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
- outb( adapter_mask | (1 << current_SC->target), SCSI_Data_NoACK_port );
-
- /* Stop arbitration and enable parity */
- outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
- #if DEBUG_RACE
- in_interrupt_flag = 0;
- #endif
- return;
- } else if (current_SC->SCp.phase & in_selection) {
- status = inb( SCSI_Status_port );
- if (!(status & 0x01)) {
- /* Try again, for slow devices */
- if (fd_mcs_select(shpnt, current_SC->target )) {
- #if EVERY_ACCESS
- printk( " SFAIL " );
- #endif
- spin_lock_irqsave(&io_request_lock, flags);
- my_done( shpnt, DID_NO_CONNECT << 16 );
- spin_unlock_irqrestore(&io_request_lock, flags);
- return;
- } else {
- #if EVERY_ACCESS
- printk( " AltSel " );
- #endif
- /* Stop arbitration and enable parity */
- outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
- }
- }
- current_SC->SCp.phase = in_other;
- outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
- outb( 0x80, SCSI_Cntl_port );
- #if DEBUG_RACE
- in_interrupt_flag = 0;
- #endif
- return;
- }
-
- /* current_SC->SCp.phase == in_other: this is the body of the routine */
-
- status = inb( SCSI_Status_port );
-
- if (status & 0x10) { /* REQ */
-
- switch (status & 0x0e) {
-
- case 0x08: /* COMMAND OUT */
- outb( current_SC->cmnd[current_SC->SCp.sent_command++],
- Write_SCSI_Data_port );
- #if EVERY_ACCESS
- printk( "CMD = %x,",
- current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
- #endif
- break;
- case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = -1;
- outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
- }
- break;
- case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = 1;
- outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
- }
- break;
- case 0x0c: /* STATUS IN */
- current_SC->SCp.Status = inb( Read_SCSI_Data_port );
- #if EVERY_ACCESS
- printk( "Status = %x, ", current_SC->SCp.Status );
- #endif
- #if ERRORS_ONLY
- if (current_SC->SCp.Status
- && current_SC->SCp.Status != 2
- && current_SC->SCp.Status != 8) {
- printk( "ERROR fd_mcs: target = %d, command = %x, status = %xn",
- current_SC->target,
- current_SC->cmnd[0],
- current_SC->SCp.Status );
- }
- #endif
- break;
- case 0x0a: /* MESSAGE OUT */
- outb( MESSAGE_REJECT, Write_SCSI_Data_port ); /* Reject */
- break;
- case 0x0e: /* MESSAGE IN */
- current_SC->SCp.Message = inb( Read_SCSI_Data_port );
- #if EVERY_ACCESS
- printk( "Message = %x, ", current_SC->SCp.Message );
- #endif
- if (!current_SC->SCp.Message) ++done;
- #if DEBUG_MESSAGES || EVERY_ACCESS
- if (current_SC->SCp.Message) {
- printk( "fd_mcs: message = %xn", current_SC->SCp.Message );
- }
- #endif
- break;
- }
- }
- if (chip == tmc1800
- && !current_SC->SCp.have_data_in
- && (current_SC->SCp.sent_command
- >= current_SC->cmd_len)) {
- /* We have to get the FIFO direction
- correct, so I've made a table based
- on the SCSI Standard of which commands
- appear to require a DATA OUT phase.
- */
- /*
- p. 94: Command for all device types
- CHANGE DEFINITION 40 DATA OUT
- COMPARE 39 DATA OUT
- COPY 18 DATA OUT
- COPY AND VERIFY 3a DATA OUT
- INQUIRY 12
- LOG SELECT 4c DATA OUT
- LOG SENSE 4d
- MODE SELECT (6) 15 DATA OUT
- MODE SELECT (10) 55 DATA OUT
- MODE SENSE (6) 1a
- MODE SENSE (10) 5a
- READ BUFFER 3c
- RECEIVE DIAGNOSTIC RESULTS 1c
- REQUEST SENSE 03
- SEND DIAGNOSTIC 1d DATA OUT
- TEST UNIT READY 00
- WRITE BUFFER 3b DATA OUT
- p.178: Commands for direct-access devices (not listed on p. 94)
- FORMAT UNIT 04 DATA OUT
- LOCK-UNLOCK CACHE 36
- PRE-FETCH 34
- PREVENT-ALLOW MEDIUM REMOVAL 1e
- READ (6)/RECEIVE 08
- READ (10) 3c
- READ CAPACITY 25
- READ DEFECT DATA (10) 37
- READ LONG 3e
- REASSIGN BLOCKS 07 DATA OUT
- RELEASE 17
- RESERVE 16 DATA OUT
- REZERO UNIT/REWIND 01
- SEARCH DATA EQUAL (10) 31 DATA OUT
- SEARCH DATA HIGH (10) 30 DATA OUT
- SEARCH DATA LOW (10) 32 DATA OUT
- SEEK (6) 0b
- SEEK (10) 2b
- SET LIMITS (10) 33
- START STOP UNIT 1b
- SYNCHRONIZE CACHE 35
- VERIFY (10) 2f
- WRITE (6)/PRINT/SEND 0a DATA OUT
- WRITE (10)/SEND 2a DATA OUT
- WRITE AND VERIFY (10) 2e DATA OUT
- WRITE LONG 3f DATA OUT
- WRITE SAME 41 DATA OUT ?
- p. 261: Commands for sequential-access devices (not previously listed)
- ERASE 19
- LOAD UNLOAD 1b
- LOCATE 2b
- READ BLOCK LIMITS 05
- READ POSITION 34
- READ REVERSE 0f
- RECOVER BUFFERED DATA 14
- SPACE 11
- WRITE FILEMARKS 10 ?
- p. 298: Commands for printer devices (not previously listed)
- ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
- SLEW AND PRINT 0b DATA OUT -- same as seek
- STOP PRINT 1b
- SYNCHRONIZE BUFFER 10
- p. 315: Commands for processor devices (not previously listed)
-
- p. 321: Commands for write-once devices (not previously listed)
- MEDIUM SCAN 38
- READ (12) a8
- SEARCH DATA EQUAL (12) b1 DATA OUT
- SEARCH DATA HIGH (12) b0 DATA OUT
- SEARCH DATA LOW (12) b2 DATA OUT
- SET LIMITS (12) b3
- VERIFY (12) af
- WRITE (12) aa DATA OUT
- WRITE AND VERIFY (12) ae DATA OUT
- p. 332: Commands for CD-ROM devices (not previously listed)
- PAUSE/RESUME 4b
- PLAY AUDIO (10) 45
- PLAY AUDIO (12) a5
- PLAY AUDIO MSF 47
- PLAY TRACK RELATIVE (10) 49
- PLAY TRACK RELATIVE (12) a9
- READ HEADER 44
- READ SUB-CHANNEL 42
- READ TOC 43
- p. 370: Commands for scanner devices (not previously listed)
- GET DATA BUFFER STATUS 34
- GET WINDOW 25
- OBJECT POSITION 31
- SCAN 1b
- SET WINDOW 24 DATA OUT
- p. 391: Commands for optical memory devices (not listed)
- ERASE (10) 2c
- ERASE (12) ac
- MEDIUM SCAN 38 DATA OUT
- READ DEFECT DATA (12) b7
- READ GENERATION 29
- READ UPDATED BLOCK 2d
- UPDATE BLOCK 3d DATA OUT
- p. 419: Commands for medium changer devices (not listed)
- EXCHANGE MEDIUM 46
- INITIALIZE ELEMENT STATUS 07
- MOVE MEDIUM a5
- POSITION TO ELEMENT 2b
- READ ELEMENT STATUS b8
- REQUEST VOL. ELEMENT ADDRESS b5
- SEND VOLUME TAG b6 DATA OUT
- p. 454: Commands for communications devices (not listed previously)
- GET MESSAGE (6) 08
- GET MESSAGE (10) 28
- GET MESSAGE (12) a8
- */
-
- switch (current_SC->cmnd[0]) {
- case CHANGE_DEFINITION: case COMPARE: case COPY:
- case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
- case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
- case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
- case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
- case WRITE_6: case WRITE_10: case WRITE_VERIFY:
- case 0x3f: case 0x41:
- case 0xb1: case 0xb0: case 0xb2:
- case 0xaa: case 0xae:
- case 0x24:
- case 0x38: case 0x3d:
- case 0xb6:
-
- case 0xea: /* alternate number for WRITE LONG */
-
- current_SC->SCp.have_data_in = -1;
- outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
- break;
- case 0x00:
- default:
-
- current_SC->SCp.have_data_in = 1;
- outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
- break;
- }
- }
- if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
- while ( (data_count = FIFO_Size - inw( FIFO_Data_Count_port )) > 512 ) {
- #if EVERY_ACCESS
- printk( "DC=%d, ", data_count ) ;
- #endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count > 0) {
- #if EVERY_ACCESS
- printk( "%d OUT, ", data_count );
- #endif
- if (data_count == 1) {
- Bytes_Written++;
- outb( *current_SC->SCp.ptr++, Write_FIFO_port );
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1;
- tmp_count = data_count << 1;
- outsw( Write_FIFO_port, current_SC->SCp.ptr, data_count );
- current_SC->SCp.ptr += tmp_count;
- Bytes_Written += tmp_count;
- current_SC->SCp.this_residual -= tmp_count;
- }
- }
- if (!current_SC->SCp.this_residual) {
- if (current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = current_SC->SCp.buffer->address;
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- } else
- break;
- }
- }
- } else if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
- while ((data_count = inw( FIFO_Data_Count_port )) > 0) {
- #if EVERY_ACCESS
- printk( "DC=%d, ", data_count );
- #endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count) {
- #if EVERY_ACCESS
- printk( "%d IN, ", data_count );
- #endif
- if (data_count == 1) {
- Bytes_Read++;
- *current_SC->SCp.ptr++ = inb( Read_FIFO_port );
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1; /* Number of words */
- tmp_count = data_count << 1;
- insw( Read_FIFO_port, current_SC->SCp.ptr, data_count );
- current_SC->SCp.ptr += tmp_count;
- Bytes_Read += tmp_count;
- current_SC->SCp.this_residual -= tmp_count;
- }
- }
- if (!current_SC->SCp.this_residual
- && current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = current_SC->SCp.buffer->address;
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- }
- }
- }
-
- if (done) {
- #if EVERY_ACCESS
- printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
- #endif
- #if ERRORS_ONLY
- if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
- if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) {
- unsigned char key;
- unsigned char code;
- unsigned char qualifier;
- key = (unsigned char)(*((char *)current_SC->request_buffer + 2))
- & 0x0f;
- code = (unsigned char)(*((char *)current_SC->request_buffer + 12));
- qualifier = (unsigned char)(*((char *)current_SC->request_buffer
- + 13));
- if (key != UNIT_ATTENTION
- && !(key == NOT_READY
- && code == 0x04
- && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
- && !(key == ILLEGAL_REQUEST && (code == 0x25
- || code == 0x24
- || !code)))
-
- printk( "fd_mcs: REQUEST SENSE "
- "Key = %x, Code = %x, Qualifier = %xn",
- key, code, qualifier );
- }
- }
- #endif
- #if EVERY_ACCESS
- printk( "BEFORE MY_DONE. . ." );
- #endif
- spin_lock_irqsave(&io_request_lock, flags);
- my_done( shpnt,
- (current_SC->SCp.Status & 0xff)
- | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
- spin_unlock_irqrestore(&io_request_lock, flags);
- #if EVERY_ACCESS
- printk( "RETURNING.n" );
- #endif
-
- } else {
- if (current_SC->SCp.phase & disconnect) {
- outb( 0xd0 | FIFO_COUNT, Interrupt_Cntl_port );
- outb( 0x00, SCSI_Cntl_port );
- } else {
- outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
- }
- }
- #if DEBUG_RACE
- in_interrupt_flag = 0;
- #endif
- return;
- }
- int fd_mcs_release(struct Scsi_Host *shpnt)
- {
- int i, this_host, irq_usage;
- release_region(shpnt->io_port, shpnt->n_io_port);
- this_host = -1;
- irq_usage = 0;
- for (i = 0; i < found; i++) {
- if (shpnt == hosts[i])
- this_host = i;
- if (shpnt->irq == hosts[i]->irq)
- irq_usage++;
- }
- /* only for the last one */
- if (1 == irq_usage)
- free_irq(shpnt->irq, hosts);
- found--;
- for (i = this_host; i < found; i++)
- hosts[i] = hosts[i+1];
- hosts[found] = NULL;
- return 0;
- }
- int fd_mcs_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
- {
- struct Scsi_Host *shpnt = SCpnt->host;
- if (in_command) {
- panic( "fd_mcs: fd_mcs_queue() NOT REENTRANT!n" );
- }
- #if EVERY_ACCESS
- printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %un",
- SCpnt->target,
- *(unsigned char *)SCpnt->cmnd,
- SCpnt->use_sg,
- SCpnt->request_bufflen );
- #endif
- fd_mcs_make_bus_idle(shpnt);
- SCpnt->scsi_done = done; /* Save this for the done function */
- current_SC = SCpnt;
- /* Initialize static data */
- if (current_SC->use_sg) {
- current_SC->SCp.buffer =
- (struct scatterlist *)current_SC->request_buffer;
- current_SC->SCp.ptr = current_SC->SCp.buffer->address;
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
- } else {
- current_SC->SCp.ptr = (char *)current_SC->request_buffer;
- current_SC->SCp.this_residual = current_SC->request_bufflen;
- current_SC->SCp.buffer = NULL;
- current_SC->SCp.buffers_residual = 0;
- }
-
-
- current_SC->SCp.Status = 0;
- current_SC->SCp.Message = 0;
- current_SC->SCp.have_data_in = 0;
- current_SC->SCp.sent_command = 0;
- current_SC->SCp.phase = in_arbitration;
- /* Start arbitration */
- outb( 0x00, Interrupt_Cntl_port );
- outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
- outb( adapter_mask, SCSI_Data_NoACK_port ); /* Set our id bit */
- in_command = 1;
- outb( 0x20, Interrupt_Cntl_port );
- outb( 0x14 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
- return 0;
- }
- static void internal_done( Scsi_Cmnd *SCpnt )
- {
- /* flag it done */
- SCpnt->host_scribble = (unsigned char *)1;
- }
- int fd_mcs_command( Scsi_Cmnd *SCpnt )
- {
- fd_mcs_queue( SCpnt, internal_done );
- /* host_scribble is used for status here */
- SCpnt->host_scribble = NULL;
- while (!SCpnt->host_scribble)
- barrier();
- return SCpnt->result;
- }
- #if DEBUG_ABORT || DEBUG_RESET
- static void fd_mcs_print_info( Scsi_Cmnd *SCpnt )
- {
- unsigned int imr;
- unsigned int irr;
- unsigned int isr;
- struct Scsi_Host *shpnt = SCpnt->host;
- if (!SCpnt || !SCpnt->host) {
- printk( "fd_mcs: cannot provide detailed informationn" );
- }
-
- printk( "%sn", fd_mcs_info( SCpnt->host ) );
- print_banner( SCpnt->host );
- switch (SCpnt->SCp.phase) {
- case in_arbitration: printk( "arbitration " ); break;
- case in_selection: printk( "selection " ); break;
- case in_other: printk( "other " ); break;
- default: printk( "unknown " ); break;
- }
- printk( "(%d), target = %d cmnd = 0x%02x pieces = %d size = %un",
- SCpnt->SCp.phase,
- SCpnt->target,
- *(unsigned char *)SCpnt->cmnd,
- SCpnt->use_sg,
- SCpnt->request_bufflen );
- printk( "sent_command = %d, have_data_in = %d, timeout = %dn",
- SCpnt->SCp.sent_command,
- SCpnt->SCp.have_data_in,
- SCpnt->timeout );
- #if DEBUG_RACE
- printk( "in_interrupt_flag = %dn", in_interrupt_flag );
- #endif
- imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
- outb( 0x0a, 0xa0 );
- irr = inb( 0xa0 ) << 8;
- outb( 0x0a, 0x20 );
- irr += inb( 0x20 );
- outb( 0x0b, 0xa0 );
- isr = inb( 0xa0 ) << 8;
- outb( 0x0b, 0x20 );
- isr += inb( 0x20 );
- /* Print out interesting information */
- printk( "IMR = 0x%04x", imr );
- if (imr & (1 << shpnt->irq))
- printk( " (masked)" );
- printk( ", IRR = 0x%04x, ISR = 0x%04xn", irr, isr );
- printk( "SCSI Status = 0x%02xn", inb( SCSI_Status_port ) );
- printk( "TMC Status = 0x%02x", inb( TMC_Status_port ) );
- if (inb( TMC_Status_port ) & 1)
- printk( " (interrupt)" );
- printk( "n" );
- printk( "Interrupt Status = 0x%02x", inb( Interrupt_Status_port ) );
- if (inb( Interrupt_Status_port ) & 0x08)
- printk( " (enabled)" );
- printk( "n" );
- if (chip == tmc18c50 || chip == tmc18c30) {
- printk( "FIFO Status = 0x%02xn", inb( shpnt->io_port + FIFO_Status ) );
- printk( "Int. Condition = 0x%02xn",
- inb( shpnt->io_port + Interrupt_Cond ) );
- }
- printk( "Configuration 1 = 0x%02xn", inb( shpnt->io_port + Configuration1 ) );
- if (chip == tmc18c50 || chip == tmc18c30)
- printk( "Configuration 2 = 0x%02xn",
- inb( shpnt->io_port + Configuration2 ) );
- }
- #endif
- int fd_mcs_abort( Scsi_Cmnd *SCpnt)
- {
- struct Scsi_Host *shpnt = SCpnt->host;
- unsigned long flags;
- #if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
- printk( "fd_mcs: abort " );
- #endif
- save_flags( flags );
- cli();
- if (!in_command) {
- #if EVERY_ACCESS || ERRORS_ONLY
- printk( " (not in command)n" );
- #endif
- restore_flags( flags );
- return SCSI_ABORT_NOT_RUNNING;
- } else printk( "n" );
- #if DEBUG_ABORT
- fd_mcs_print_info( SCpnt );
- #endif
- fd_mcs_make_bus_idle(shpnt);
- current_SC->SCp.phase |= aborted;
- current_SC->result = DID_ABORT << 16;
- restore_flags( flags );
-
- /* Aborts are not done well. . . */
- spin_lock_irqsave(&io_request_lock, flags);
- my_done( shpnt, DID_ABORT << 16 );
- spin_unlock_irqrestore(&io_request_lock, flags);
- return SCSI_ABORT_SUCCESS;
- }
- int fd_mcs_reset( Scsi_Cmnd *SCpnt, unsigned int reset_flags )
- {
- struct Scsi_Host *shpnt = SCpnt->host;
- #if DEBUG_RESET
- static int called_once = 0;
- #endif
- #if ERRORS_ONLY
- if (SCpnt) printk( "fd_mcs: SCSI Bus Resetn" );
- #endif
- #if DEBUG_RESET
- if (called_once) fd_mcs_print_info( current_SC );
- called_once = 1;
- #endif
-
- outb( 1, SCSI_Cntl_port );
- do_pause( 2 );
- outb( 0, SCSI_Cntl_port );
- do_pause( 115 );
- outb( 0, SCSI_Mode_Cntl_port );
- outb( PARITY_MASK, TMC_Cntl_port );
- /* Unless this is the very first call (i.e., SCPnt == NULL), everything
- is probably hosed at this point. We will, however, try to keep
- things going by informing the high-level code that we need help. */
- return SCSI_RESET_WAKEUP;
- }
- #include "sd.h"
- #include <scsi/scsi_ioctl.h>
- int fd_mcs_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
- {
- int drive;
- unsigned char buf[512 + sizeof( int ) * 2];
- int size = disk->capacity;
- int *sizes = (int *)buf;
- unsigned char *data = (unsigned char *)(sizes + 2);
- unsigned char do_read[] = { READ_6, 0, 0, 0, 1, 0 };
- int retcode;
- /* BIOS >= 3.4 for MCA cards */
- drive = MINOR(dev) / 16;
- /* This algorithm was provided by Future Domain (much thanks!). */
- sizes[0] = 0; /* zero bytes out */
- sizes[1] = 512; /* one sector in */
- memcpy( data, do_read, sizeof( do_read ) );
- retcode = kernel_scsi_ioctl( disk->device,
- SCSI_IOCTL_SEND_COMMAND,
- (void *)buf );
- if (!retcode /* SCSI command ok */
- && data[511] == 0xaa && data[510] == 0x55 /* Partition table valid */
- && data[0x1c2]) { /* Partition type */
- /* The partition table layout is as follows:
- Start: 0x1b3h
- Offset: 0 = partition status
- 1 = starting head
- 2 = starting sector and cylinder (word, encoded)
- 4 = partition type
- 5 = ending head
- 6 = ending sector and cylinder (word, encoded)
- 8 = starting absolute sector (double word)
- c = number of sectors (double word)
- Signature: 0x1fe = 0x55aa
- So, this algorithm assumes:
- 1) the first partition table is in use,
- 2) the data in the first entry is correct, and
- 3) partitions never divide cylinders
- Note that (1) may be FALSE for NetBSD (and other BSD flavors),
- as well as for Linux. Note also, that Linux doesn't pay any
- attention to the fields that are used by this algorithm -- it
- only uses the absolute sector data. Recent versions of Linux's
- fdisk(1) will fill this data in correctly, and forthcoming
- versions will check for consistency.
- Checking for a non-zero partition type is not part of the
- Future Domain algorithm, but it seemed to be a reasonable thing
- to do, especially in the Linux and BSD worlds. */
- info_array[0] = data[0x1c3] + 1; /* heads */
- info_array[1] = data[0x1c4] & 0x3f; /* sectors */
- } else {
- /* Note that this new method guarantees that there will always be
- less than 1024 cylinders on a platter. This is good for drives
- up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
- if ((unsigned int)size >= 0x7e0000U) {
- info_array[0] = 0xff; /* heads = 255 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else if ((unsigned int)size >= 0x200000U) {
- info_array[0] = 0x80; /* heads = 128 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else {
- info_array[0] = 0x40; /* heads = 64 */
- info_array[1] = 0x20; /* sectors = 32 */
- }
- }
- /* For both methods, compute the cylinders */
- info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
-
- return 0;
- }
- /* Eventually this will go into an include file, but this will be later */
- static Scsi_Host_Template driver_template = FD_MCS;
- #include "scsi_module.c"