2005-04-16 15:20:36 -07:00
/* 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 / 2 s
* 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 / 18 C50 / 18 C30 , 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 0213 9 , 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 8 K cache ( host adapters based on the
18 C30 chip have a 2 k 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/init.h>
# include <linux/interrupt.h>
# include <linux/blkdev.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 <scsi/scsicam.h>
# include <linux/mca-legacy.h>
# include <asm/io.h>
# include <asm/system.h>
# include "scsi.h"
# include <scsi/scsi_host.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 */
/* 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_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_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 irqreturn_t 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 interrupts [ ] = { 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 ;
static int __init fd_mcs_setup ( char * str )
{
static int done_setup = 0 ;
int ints [ 3 ] ;
get_options ( str , 3 , ints ) ;
if ( done_setup + + | | ints [ 0 ] < 1 | | ints [ 0 ] > 2 | | ints [ 1 ] < 1 | | ints [ 1 ] > 16 ) {
printk ( " fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE \n " ) ;
return 0 ;
}
user_fifo_count = ints [ 0 ] > = 1 ? ints [ 1 ] : 0 ;
user_fifo_size = ints [ 0 ] > = 2 ? ints [ 2 ] : 0 ;
return 1 ;
}
__setup ( " fd_mcs= " , fd_mcs_setup ) ;
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%lX \n " , 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 {
mdelay ( 10 ) ;
} while ( - - amount ) ;
}
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 ) ;
}
static 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 ( KERN_INFO " scsi <fd_mcs>: %s at slot %d \n " , 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 = interrupts [ ( pos2 > > 1 ) & 0x07 ] ;
}
if ( irq ) {
/* claim the slot */
mca_set_adapter_name ( slot - 1 , fd_mcs_adapters [ loop ] . name ) ;
/* check irq/region */
if ( request_irq ( irq , fd_mcs_intr , SA_SHIRQ , " fd_mcs " , hosts ) ) {
printk ( KERN_ERR " fd_mcs: interrupt is not available, skipping... \n " ) ;
continue ;
}
/* request I/O region */
if ( request_region ( port , 0x10 , " fd_mcs " ) ) {
printk ( KERN_ERR " fd_mcs: I/O region is already in use, skipping... \n " ) ;
continue ;
}
/* register */
if ( ! ( shpnt = scsi_register ( tpnt , sizeof ( struct fd_hostdata ) ) ) ) {
printk ( KERN_ERR " fd_mcs: scsi_register() failed \n " ) ;
release_region ( port , 0x10 ) ;
free_irq ( irq , hosts ) ;
continue ;
}
/* 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 ;
/* FIXME: Do we need to keep this bit of code inside NOT_USED around at all? */
# ifdef NOT_USED
/* *************************************************** */
/* Try to toggle 32-bit mode. This only
works on an 18 c30 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%x \n " , ( chip = = tmc18c30 ) ? " tmc18c30 " : " tmc18c50 " , FIFO_Size ) ;
}
}
/* That should have worked, but appears to
have problems . Let ' s assume it is an
18 c30 if the RAM is disabled . */
if ( inb ( port + Configuration2 ) & 0x02 ) {
chip = tmc18c30 ;
FIFO_Size = 0x800 ; /* 2k FIFO */
printk ( " SECOND: chip=%s, fifo_size=0x%x \n " , ( 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 ( found = = FD_MAX_HOSTS ) {
printk ( " fd_mcs: detecting reached max=%d host adapters. \n " , FD_MAX_HOSTS ) ;
break ;
}
}
return found ;
}
static 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
*/
static int fd_mcs_proc_info ( struct Scsi_Host * shpnt , char * buffer , char * * start , off_t offset , int length , int inout )
{
int len = 0 ;
if ( inout )
return ( - ENOSYS ) ;
* start = buffer + offset ;
len + = sprintf ( buffer + len , " Future Domain MCS-600/700 Driver %s \n " , DRIVER_VERSION ) ;
len + = sprintf ( buffer + len , " HOST #%d: %s \n " , shpnt - > host_no , adapter_name ) ;
len + = sprintf ( buffer + len , " FIFO Size=0x%x, FIFO Count=%d \n " , FIFO_Size , FIFO_COUNT ) ;
len + = sprintf ( buffer + len , " DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d \n \n " , 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 250 mS ) */
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 failed \n " ) ;
# endif
# if ERRORS_ONLY
if ( ! target ) {
static int flag = 0 ;
if ( ! flag ) /* Skip first failure for all chips. */
+ + flag ;
else
printk ( " fd_mcs: Selection failed \n " ) ;
}
# 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 command \n " ) ;
}
# if DEBUG_RACE
in_interrupt_flag = 0 ;
# endif
}
/* only my_done needs to be protected */
static irqreturn_t 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 IRQ_NONE ;
}
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, ignoring \n " ) ;
# endif
/* return IRQ_HANDLED; */
}
# 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 ( shpnt - > host_lock , flags ) ;
my_done ( shpnt , DID_BUS_BUSY < < 16 ) ;
spin_unlock_irqrestore ( shpnt - > host_lock , flags ) ;
return IRQ_HANDLED ;
}
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 - > device - > id ) , 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 IRQ_HANDLED ;
} 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 - > device - > id ) ) {
# if EVERY_ACCESS
printk ( " SFAIL " ) ;
# endif
spin_lock_irqsave ( shpnt - > host_lock , flags ) ;
my_done ( shpnt , DID_NO_CONNECT < < 16 ) ;
spin_unlock_irqrestore ( shpnt - > host_lock , flags ) ;
return IRQ_HANDLED ;
} 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 IRQ_HANDLED ;
}
/* 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 = %x \n " , current_SC - > device - > id , 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 = %x \n " , 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 3 a DATA OUT
INQUIRY 12
LOG SELECT 4 c DATA OUT
LOG SENSE 4 d
MODE SELECT ( 6 ) 15 DATA OUT
MODE SELECT ( 10 ) 55 DATA OUT
MODE SENSE ( 6 ) 1 a
MODE SENSE ( 10 ) 5 a
READ BUFFER 3 c
RECEIVE DIAGNOSTIC RESULTS 1 c
REQUEST SENSE 03
SEND DIAGNOSTIC 1 d DATA OUT
TEST UNIT READY 00
WRITE BUFFER 3 b 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 1 e
READ ( 6 ) / RECEIVE 08
READ ( 10 ) 3 c
READ CAPACITY 25
READ DEFECT DATA ( 10 ) 37
READ LONG 3 e
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 ) 0 b
SEEK ( 10 ) 2 b
SET LIMITS ( 10 ) 33
START STOP UNIT 1 b
SYNCHRONIZE CACHE 35
VERIFY ( 10 ) 2f
WRITE ( 6 ) / PRINT / SEND 0 a DATA OUT
WRITE ( 10 ) / SEND 2 a DATA OUT
WRITE AND VERIFY ( 10 ) 2 e 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 1 b
LOCATE 2 b
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 0 b is SEEK ( 6 ) * * * * *
SLEW AND PRINT 0 b DATA OUT - - same as seek
STOP PRINT 1 b
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 4 b
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 1 b
SET WINDOW 24 DATA OUT
p . 391 : Commands for optical memory devices ( not listed )
ERASE ( 10 ) 2 c
ERASE ( 12 ) ac
MEDIUM SCAN 38 DATA OUT
READ DEFECT DATA ( 12 ) b7
READ GENERATION 29
READ UPDATED BLOCK 2 d
UPDATE BLOCK 3 d DATA OUT
p . 419 : Commands for medium changer devices ( not listed )
EXCHANGE MEDIUM 46
INITIALIZE ELEMENT STATUS 07
MOVE MEDIUM a5
POSITION TO ELEMENT 2 b
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 = page_address ( current_SC - > SCp . buffer - > page ) + current_SC - > SCp . buffer - > offset ;
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 = page_address ( current_SC - > SCp . buffer - > page ) + current_SC - > SCp . buffer - > offset ;
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 = %x \n " , key , code , qualifier ) ;
}
}
# endif
# if EVERY_ACCESS
printk ( " BEFORE MY_DONE. . . " ) ;
# endif
spin_lock_irqsave ( shpnt - > host_lock , flags ) ;
my_done ( shpnt , ( current_SC - > SCp . Status & 0xff )
| ( ( current_SC - > SCp . Message & 0xff ) < < 8 ) | ( DID_OK < < 16 ) ) ;
spin_unlock_irqrestore ( shpnt - > host_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 IRQ_HANDLED ;
}
static 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 ;
}
static int fd_mcs_queue ( Scsi_Cmnd * SCpnt , void ( * done ) ( Scsi_Cmnd * ) )
{
struct Scsi_Host * shpnt = SCpnt - > device - > 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 = %u \n " , 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 = page_address ( current_SC - > SCp . buffer - > page ) + current_SC - > SCp . buffer - > offset ;
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 ;
}
# 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 information \n " ) ;
}
printk ( " %s \n " , 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 = %u \n " , SCpnt - > SCp . phase , SCpnt - > device - > id , * ( unsigned char * ) SCpnt - > cmnd , SCpnt - > use_sg , SCpnt - > request_bufflen ) ;
printk ( " sent_command = %d, have_data_in = %d, timeout = %d \n " , SCpnt - > SCp . sent_command , SCpnt - > SCp . have_data_in , SCpnt - > timeout ) ;
# if DEBUG_RACE
printk ( " in_interrupt_flag = %d \n " , 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%04x \n " , irr , isr ) ;
printk ( " SCSI Status = 0x%02x \n " , 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%02x \n " , inb ( shpnt - > io_port + FIFO_Status ) ) ;
printk ( " Int. Condition = 0x%02x \n " , inb ( shpnt - > io_port + Interrupt_Cond ) ) ;
}
printk ( " Configuration 1 = 0x%02x \n " , inb ( shpnt - > io_port + Configuration1 ) ) ;
if ( chip = = tmc18c50 | | chip = = tmc18c30 )
printk ( " Configuration 2 = 0x%02x \n " , inb ( shpnt - > io_port + Configuration2 ) ) ;
}
# endif
static int fd_mcs_abort ( Scsi_Cmnd * SCpnt )
{
struct Scsi_Host * shpnt = SCpnt - > device - > host ;
unsigned long flags ;
# if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
printk ( " fd_mcs: abort " ) ;
# endif
spin_lock_irqsave ( shpnt - > host_lock , flags ) ;
if ( ! in_command ) {
# if EVERY_ACCESS || ERRORS_ONLY
printk ( " (not in command) \n " ) ;
# endif
spin_unlock_irqrestore ( shpnt - > host_lock , flags ) ;
return FAILED ;
} 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 ;
/* Aborts are not done well. . . */
my_done ( shpnt , DID_ABORT < < 16 ) ;
spin_unlock_irqrestore ( shpnt - > host_lock , flags ) ;
return SUCCESS ;
}
static int fd_mcs_bus_reset ( Scsi_Cmnd * SCpnt ) {
struct Scsi_Host * shpnt = SCpnt - > device - > host ;
2005-05-28 07:56:31 -04:00
unsigned long flags ;
2005-04-16 15:20:36 -07:00
# if DEBUG_RESET
static int called_once = 0 ;
# endif
# if ERRORS_ONLY
if ( SCpnt )
printk ( " fd_mcs: SCSI Bus Reset \n " ) ;
# endif
# if DEBUG_RESET
if ( called_once )
fd_mcs_print_info ( current_SC ) ;
called_once = 1 ;
# endif
2005-05-28 07:56:31 -04:00
spin_lock_irqsave ( shpnt - > host_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ) ;
2005-05-28 07:56:31 -04:00
spin_unlock_irqrestore ( shpnt - > host_lock , flags ) ;
2005-04-16 15:20:36 -07:00
/* 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 SUCCESS ;
}
# include <scsi/scsi_ioctl.h>
static int fd_mcs_biosparam ( struct scsi_device * disk , struct block_device * bdev ,
sector_t capacity , int * info_array )
{
unsigned char * p = scsi_bios_ptable ( bdev ) ;
int size = capacity ;
/* BIOS >= 3.4 for MCA cards */
/* This algorithm was provided by Future Domain (much thanks!). */
if ( p & & p [ 65 ] = = 0xaa & & p [ 64 ] = = 0x55 /* Partition table valid */
& & p [ 4 ] ) { /* Partition type */
/* The partition table layout is as follows:
Start : 0x1b3 h
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 ] = p [ 5 ] + 1 ; /* heads */
info_array [ 1 ] = p [ 6 ] & 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.85 GB ( where 1 GB = 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 ] ) ;
kfree ( p ) ;
return 0 ;
}
static Scsi_Host_Template driver_template = {
. proc_name = " fd_mcs " ,
. proc_info = fd_mcs_proc_info ,
. detect = fd_mcs_detect ,
. release = fd_mcs_release ,
. info = fd_mcs_info ,
. queuecommand = fd_mcs_queue ,
. eh_abort_handler = fd_mcs_abort ,
. eh_bus_reset_handler = fd_mcs_bus_reset ,
. bios_param = fd_mcs_biosparam ,
. can_queue = 1 ,
. this_id = 7 ,
. sg_tablesize = 64 ,
. cmd_per_lun = 1 ,
. use_clustering = DISABLE_CLUSTERING ,
} ;
# include "scsi_module.c"