rendered paste body[root@proxy ftsrc]# cat fasttrak.c
/*********************************************************************
* fasttrak.c
*
* Copyright (C) 1999-2002 Promise Technology, Inc.
* May be copied or modified under the terms of the
* GNU Lesser General Public License
*
*********************************************************************/
#include <stdarg.h>
// Ryan Mod
#include <linux/autoconf.h>
#include <asm/io.h>
// Ryan Mod
// #include <asm/irq.h>
#include <asm/byteorder.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/pci_ids.h>
#include <linux/pci_regs.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_device.h>
#include <asm/delay.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/types.h>
#include <linux/hdreg.h>
// #include <linux/delay.h>
// #include <sched.h>
// #include <linux/blk.h>
// #include <linux/tqueue.h>
// #include <linux/wrapper.h>
// #include <linux/module.h>
#include "global.h"
#include "fasttrak.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#define KERNEL_VERSION_24x
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
MODULE_LICENSE("GPL"); //Actually LGPL
#endif
unchar bResetController;
struct timer_list submit_timer ; // for submit_timer
struct timer_list error_timer_ch0 ; // for error_handler
struct timer_list error_timer_ch1 ; // for error_handler
struct timer_list error_timer_ch2 ; // for error_handler
struct timer_list error_timer_ch3 ; // for error_handler
struct timer_list Init_Timer;
#ifdef KERNEL_VERSION_24x
static DECLARE_WAIT_QUEUE_HEAD(Wait24);
#else
static struct wait_queue *Wait22=NULL;
#endif
ulong InitTime = 5000 ; // 5 sec
unchar *ENGINE ;
unchar *P_BVIR ;
ScsiEngReq_p CurScsiReq;
extern void ft_handler_wrapper(int irq, void *dev_id, struct pt_regs *regs);
extern void submit_timer_handler(ulong data);
// Support Controller of Device ID
// -----------------------------------------------
struct proc_dir_entry proc_scsi_fasttrak = {
#ifdef KERNEL_VERSION_24x
0, // PROC_SCSI_FASTTRAK,
#else
PROC_SCSI_NOT_PRESENT, // PROC_SCSI_FASTTRAK,
#endif
8, // length of "FastTrak"
"FastTrak", // name of directory entry
S_IFDIR | S_IRUGO | S_IXUGO,
2,
};
/*********************************************************************
* Function: unchar InitGDS( void )
* Description: Initial Global Data Structure
* ft_adapter[FT_Max_Ada]
* ft_channel[FT_Max_Ch]
* ft_drive[FT_Max_Dev]
********************************************************************/
unchar InitGDS( void ) {
unchar uc;
// initialize ft_adapter[ada]
for( uc=0 ; uc < FT_Max_Ada ; uc++ ) {
memset(&ft_adapter[uc],0,sizeof(ft_adapter_t));
ft_adapter[uc].id = uc ;
ft_adapter[uc].pchannel[0] = (ft_channel_t *)&ft_channel[uc*2+0];
ft_adapter[uc].pchannel[1] = &ft_channel[uc*2+1];
spin_lock_init( &ft_adapter[uc].ft_lock );
}
// initialize ft_channel[ch]
for( uc=0 ; uc < FT_Max_Ch ; uc++ ) {
memset(&ft_channel[uc],0,sizeof(ft_channel_t));
ft_channel[uc].id = uc ;
ft_channel[uc].padapter = &ft_adapter[uc/2];
ft_channel[uc].pdrive[0] = &ft_drive[uc*2+0];
ft_channel[uc].pdrive[1] = &ft_drive[uc*2+1];
}
// initialize ft_drive[dev]
for( uc=0 ; uc < FT_Max_Dev ; uc++ ) {
memset(&ft_drive[uc],0,sizeof(ft_drive_t));
ft_drive[uc].id = uc ;
ft_drive[uc].pchannel = &ft_channel[uc/2];
ft_drive[uc].select = uc%2 ? 0xB0 : 0xA0 ;
}
// By Bennett, Initial FastCheck event variable.
EventPtr = 0;
EventIoctlPtr = 0;
return(0);
}
/*********************************************************************
* Function:
* int TX4ImprovePerformance(void)
*
* Description:
* To improve TX4 performance by fixing conflict between Intel chip
* on TX4 with Intel ChipSets as bellow ,
* (1) Intel ChipSet Device ID : 0x2440 0x244C 0x2410 0x2420
* solution: PCI configuration offset 0xFF bit 2 , set to 1
* (2) Intel ChipSet Device ID = 0x2418 0x2428 0x244E 0x2448
* solution: PCI configuration offset 0x50 bit 2 , set to 1
*********************************************************************/
void TX4ImprovePerformance(void) {
ushort TX4_compatible_1[5] = { 0x2440, 0x244C, 0x2410, 0x2420, 0 } ;
ushort TX4_compatible_2[5] = { 0x2418, 0x2428, 0x244E, 0x2448, 0 } ;
unchar class ;
unchar value ;
#ifdef KERNEL_VERSION_24x
struct pci_dev *intel_chip_pci_dev = NULL ;
#else
struct pci_dev *intel_chip_pci_dev = pci_devices ;
#endif
class = 0 ;
while( TX4_compatible_1[class] ) {
while( (intel_chip_pci_dev = pci_find_device(PCI_VENDOR_ID_INTEL,TX4_compatible_1[class],intel_chip_pci_dev)) ) {
DEB(printk("TX4ImprovePerformance : TX4_compatible_1 : find[%x] %x:%x \n",class,PCI_VENDOR_ID_INTEL,TX4_compatible_1[class]););
pci_read_config_byte(intel_chip_pci_dev,0xFF,&value);
pci_write_config_byte( intel_chip_pci_dev,0xFF,value|(1<<2) );
}
class++;
} // End of while( TX4_compatible_1[class] )
class = 0 ;
while( TX4_compatible_2[class] ) {
while( (intel_chip_pci_dev = pci_find_device(PCI_VENDOR_ID_INTEL,TX4_compatible_2[class],intel_chip_pci_dev)) ) {
DEB(printk("TX4ImprovePerformance : TX4_compatible_2 : find[%x] %x:%x \n",class,PCI_VENDOR_ID_INTEL,TX4_compatible_2[class]););
pci_read_config_byte(intel_chip_pci_dev,0x50,&value);
pci_write_config_byte( intel_chip_pci_dev,0x50,value|(1<<2) );
}
class++;
} // End of while( TX4_compatible_2[class] )
return;
}
// -----------------------------------------------
unchar init_ioports( unchar ada )
{
unchar uc;
ft_adapter_t *adapter = &ft_adapter[ada] ;
struct pci_dev *ft_pci_dev = adapter->ft_pci_dev;
ushort deviceid= ft_pci_dev->device;
ushort command ;
uint base[6];
for( uc = 0 ; uc < 5 ; uc++ ) {
pci_read_config_dword(ft_pci_dev,PCI_BASE_ADDRESS_0+4*uc,&base[uc]);
base[uc] = base[uc] & PCI_BASE_ADDRESS_IO_MASK ;
if(uc%2) base[uc] += 2 ;
// DEB(printk("FastTrak : io_base[%d]:%#x\n",i,temp_p->io_base[i]));
}
adapter->pchannel[0]->base = base[0] ;
adapter->pchannel[0]->ctrl = base[1] ;
adapter->pchannel[1]->base = base[2] ;
adapter->pchannel[1]->ctrl = base[3] ;
adapter->pchannel[0]->bm = base[4] ;
adapter->pchannel[1]->bm = base[4] + 8 ;
pci_read_config_word(ft_pci_dev,PCI_COMMAND,&command);
command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
pci_write_config_word(ft_pci_dev,PCI_COMMAND,command);
pci_read_config_dword(ft_pci_dev,0x30,&adapter->rom_base);
if( ! ( adapter->pchannel[0]->bm &= 0xFFF0 ) )
return(2);
for( uc=0 ; uc < ChPAda ; uc++ ) { // ch per adapter : 2
if( ! check_region(adapter->pchannel[uc]->base,8) )
request_region(adapter->pchannel[uc]->base,8,"FT(base)");
if( ! check_region(adapter->pchannel[uc]->ctrl,1) )
request_region(adapter->pchannel[uc]->ctrl,1,"FT(ctrl)");
if( ! check_region(adapter->pchannel[uc]->bm,8) )
request_region(adapter->pchannel[uc]->bm,8,"FT(BM)");
}
// fix-me : if different adapters use same irq - not error
if( (ada == 1) && (ft_adapter[0].ft_pci_dev->irq == ft_pci_dev->irq) )
DEB(printk("Two FT adapters request same irq number\n"););
else {
if( request_irq(ft_pci_dev->irq,ft_handler_wrapper,
SA_INTERRUPT | SA_SHIRQ,"FastTrak",&ft_adapter[0])) {
printk("FastTrak : adapter%d request_irq() error.\n",ada);
return(2);
}
}
if( DID27x(deviceid) )
return(0);
// Allow byte alignment for ... , such as PRD Table ...
outb( 1 , adapter->pchannel[0]->bm + 0x1A );
outb( 1 , adapter->pchannel[0]->bm + 0x1B );
// Select External Clock
if( (deviceid == DID265) || (deviceid == DID267) ) {
unchar ctrl_init_sta, uata_clock;
ushort sys_ctrl_reg_1F_port = adapter->pchannel[0]->bm + 0x1F ;
ushort sys_ctrl_reg_1C_port = adapter->pchannel[0]->bm + 0x1C ;
unchar sys_ctrl_reg_1F_data,sys_ctrl_reg_1C_data, outdata;
pci_read_config_byte(ft_pci_dev,0x50,&ctrl_init_sta);
uata_clock = ( ctrl_init_sta & 0x40 ) << 1 ;
sys_ctrl_reg_1F_data = inb(sys_ctrl_reg_1F_port);
sys_ctrl_reg_1C_data = inb(sys_ctrl_reg_1C_port);
outdata = ( sys_ctrl_reg_1F_data & 0x3F ) | uata_clock ;
outb( outdata | 1 , sys_ctrl_reg_1F_port ); // burst
outb( sys_ctrl_reg_1C_data | 0x03 , sys_ctrl_reg_1C_port );
DEB(
printk("sys_ctrl_reg_1F_port = %p\n",sys_ctrl_reg_1F_port);
printk("ctrl_init_sta = %#x\n",ctrl_init_sta);
printk("uata_clock = %#x\n",uata_clock);
printk("sys_ctrl_reg_1F_data = %#x\n",sys_ctrl_reg_1F_data);
printk("outdata = %#x\n",outdata);
);
}
return(0);
}
// -------------------------------------------------------------------
int input_data(unchar dev,void *buffer,unsigned wcount)
{
ushort *ptr=(ushort *)buffer;
while(wcount--) {
*ptr++=inw(ft_drive[dev].pchannel->base);
}
return(0);
}
/*********************************************************************
* Function:
* unchar identify_device(unchar dev,void *id)
*
* Return:
* 0 success
* 1 select time out
* 2 identify time out
* #3 request_irq() fail
*********************************************************************/
unchar identify_device(unchar dev,void *id)
{
ulong flags;
unchar error;
ft_channel_t *p = ft_drive[dev].pchannel ;
ushort base = p->base ;
ushort ctrl = p->ctrl ;
ft_drive_t *drive=&ft_drive[dev];
DebFtFun(printk("DebFtFun : identify_device(dev=%d).\n",dev););
if( Check1F7(dev,3*1000,0,BSY) )
return(1); // drive[] busy
outb( drive->select , base+6 ) ;
outb( drive->select , base+6 ) ; // for 277
if( Check1F7(dev,3,DRDY|SEEK,BSY|DF) )
return(1); // drive[] not exist
save_flags(flags);
cli();
// free_irq(p->padapter->irq,"FT_IRQ"); // for some special HDDs
outb( 2 , ctrl ) ; // disable nIEN
outb( drive->select , base+6 ) ;
outb( drive->select , base+6 ) ; // for 277
outb( 0xEC , base+7 ) ;
error = Check1F7(dev,10*1000,DRDY|SEEK|DRQ,BSY|DF) ;
inb(base+7) ;
if( error ) error = 2 ; // drive[] not exist
else {
struct ft_driveid *p_id = (struct ft_driveid *)id;
input_data(dev,id,512/2);
drive->present = 1 ;
drive->LBA_48bit = 0 ;
if( (p_id->cmd_set[1] & (1<<10)) && (p_id->cfs[2] & (1<<10)) )
drive->LBA_48bit = 1 ;
outb( 0 , ctrl ) ; // Enable nIEN
}
// if(request_irq(p->padapter->ft_pci_dev->irq,ft_handler_wrapper,SA_INTERRUPT|SA_SHIRQ,
// "FastTrak","FT_IRQ")) {
// error = 3 ;
// printk("FastTrak : request_irq() error in identify.\n");
// }
restore_flags(flags);
DEB(printk("in identify:dev/error=%d/%#x\n",dev,error););
return(error);
}
// -------------------------------------------------------------------
int set_feature(unchar dev,unchar *pio_mode,unchar *dma_mode,unchar *udma_mode)
{
ft_drive_t *drive = &ft_drive[dev];
struct ft_driveid *id = drive->identify;
struct pci_dev *ft_pci_dev = drive->pchannel->padapter->ft_pci_dev;
ushort deviceid= ft_pci_dev->device ;
ushort base = drive->pchannel->base;
ushort bm = drive->pchannel->bm;
ushort IndexReg= bm + 1 ;
ushort DataReg = bm + 3 ;
ushort cycle_time, mode, Pin40;
unchar xfermode=0, IORDYp;
ulong flags;
unchar ret, error;
if( Check1F7(dev,3*1000,0,BSY) )
return(1); // drive[] busy
// --- UDMA & DMA transfer mode
if( id->capability & 1) {
if( (id->field_valid & 2) && (mode=(id->dma_mword & 0x000F)) ) {
xfermode = 0;
mode=mode>>1;
while(mode) { // --- MDMA transfer mode
xfermode++;
mode=mode>>1;
}
*dma_mode = xfermode ;
xfermode = 0x20 | *dma_mode ;
}
if( (id->field_valid & 4) && (mode=(id->dma_ultra & 0x00FF)) ) {
xfermode = 0;
mode=mode>>1;
while(mode) { // --- UDMA transfer mode
xfermode++;
mode=mode>>1;
}
if ( drive->downmode && (drive->xfermode & 0x40) ) {
xfermode = (drive->xfermode & 0x07) - 1 ; // e.g. ECC
drive->downmode = 0 ;
}
#ifndef Linux_Intel_0140
else if( (deviceid == DID262) && (xfermode > 4) )
xfermode = 4 ; // PDC20262 only supports to UDMA4
else if( DID26x(deviceid) && (xfermode > 5) )
xfermode = 5 ; // PDC20265/267 only support to UDMA5
else if( (deviceid == DID270) && (xfermode > 5) )
xfermode = 5 ; // PDC20270 only support to UDMA5
#else // modify for Intel v2.30.0140.1
else if( (deviceid == DID267) && (xfermode > 5) )
xfermode = 5 ; // PDC20267 only support to UDMA5
else if( (deviceid == DID277) && (xfermode > 5) )
xfermode = 5 ; // PDC20277 support UDMA5 instead of UDMA6
#endif
*udma_mode = xfermode ;
xfermode = 0x40 | *udma_mode ;
}
}
// Get PIO transfer mode & Check Cable Status (80/40pins)
if( DID27x(deviceid) ) {
outb( (( drive->select == 0xA0 ) ? 0x13 : 0x1B) , IndexReg );
IORDYp = inb( DataReg ) & ( 1<<2 ) ;
outb( 0x0B , IndexReg );
Pin40 = inb( DataReg ) & ( 1<<3 ) ;
}
else { // PIO Mode on PDC20262/265/267
unchar C_REG , offset;
ushort State ; // Controller Initial State
offset = ( dev % 4 < 2 ) ? 0x62 : 0x6A ;
pci_read_config_byte(ft_pci_dev, offset, &C_REG);
IORDYp = C_REG & ( 1<<6 ) ;
pci_read_config_word(ft_pci_dev, 0x50, &State);
Pin40 = ( dev % 4 >= 2 ) ? State & (1<<11) : State & (1<<10) ;
}
if( IORDYp ) cycle_time=id->eide_pio_iordy;
else cycle_time=id->eide_pio;
if (cycle_time > 383) *pio_mode=0;
else if(cycle_time > 240) *pio_mode=1;
else if(cycle_time > 180) *pio_mode=2;
else if(cycle_time > 120) *pio_mode=3;
else *pio_mode=4;
if( *udma_mode == 0xFF && *dma_mode == 0xFF )
xfermode = 0x08 | *pio_mode ;
if( (*udma_mode != 0xFF) && (*udma_mode > 2) && Pin40 ) {
printk("Warning: PROMISE IDE%d ",drive->pchannel->id+1);
printk("needs 80-pin cable for UDMA%d .\n",*udma_mode);
*udma_mode = 2;
xfermode = 0x40 | *udma_mode ;
printk("Warning: Drive%d Set Feature To UDMA2 \n",dev+1) ;
}
if( Check1F7(dev,3*1000,0,BSY) )
return(2); // drive[] busy
save_flags(flags);
cli();
outb( 0x03, base+1 );
outb( xfermode, base+2 );
outb( drive->select, base+6 );
outb( drive->select, base+6 ); // for 277
outb( 0xEF, base+7 );
DEB(printk("xfermode=%#x,select=%#x\n",xfermode,drive->select););
error = Check1F7(dev,3*1000,0,BSY) ;
// Setting tHOLD bit to 0 if using UDMA mode 2
if( DID27x(deviceid) && (*udma_mode == 2) ) {
outb( 0x10 , IndexReg );
outb( inb(DataReg)&(~(1<<7)) , DataReg );
}
restore_flags(flags);
if( error ) {
printk("FastTrak : PROMISE Set_Feature time out.\n");
return(3);
}
ft_drive[dev].xfermode = xfermode ;
// get new inquiry data after set feature for data structure
if( (ret=identify_device(dev,ft_drive[dev].identify)) ) {
printk("FastTrak: SF Identify(DEV%d) Error(%d)\n",dev+1,ret);
return(4);
}
return(0);
}
// -------------------------------------------------------------------
int speed_27x(unchar dev, unchar pio_mode, unchar dma_mode, unchar udma_mode)
{
ft_drive_t *drive = &ft_drive[dev];
ushort IndexReg = drive->pchannel->bm + 1 ;
ushort DataReg = drive->pchannel->bm + 3 ;
unchar DataValPIO[5][3] = {{0xFB,0x2B,0xAC},{0x46,0x29,0xA4},{0x23,0x26,0x64},{0x27,0x0D,0x35},{0x23,0x09,0x25}} ;
unchar DataValDMA[3][2] = {{0xDF,0x5F},{0x6B,0x27},{0x69,0x25}} ;
unchar DataValUDMA[7][3] = {{0x4A,0x0F,0xD5},{0x3A,0x0A,0xD0},{0x2A,0x07,0xCD},{0x1A,0x05,0xCD},{0x1A,0x03,0xCD},{0x1A,0x02,0xCB},{0x1A,0x01,0xCB}} ;
unchar IndexVal = (drive->id % 2) ? 0x14 : 0x0C ;
unchar ui;
// PIO mode
for( ui=0 ; ui<2 ; ui++ ) {
outb( IndexVal+ui , IndexReg );
outb( DataValPIO[pio_mode][ui] , DataReg );
// printk("PIO - %#x/%#x - %#x/%#x\n",IndexReg,IndexVal+ui,DataReg,DataValPIO[pio_mode][ui]);
}
outb( IndexVal+7 , IndexReg );
outb( DataValPIO[pio_mode][2] , DataReg );
// printk("PIO - %#x/%#x - %#x/%#x\n",IndexReg,IndexVal+7,DataReg,DataValPIO[pio_mode][2]);
// MDMA mode
for( ui=0 ; ui<2 ; ui++ ) {
outb( IndexVal+2+ui , IndexReg );
outb( DataValDMA[dma_mode][ui] , DataReg );
// printk("DMA - %#x/%#x - %#x/%#x\n",IndexReg,IndexVal+2+ui,DataReg,DataValDMA[dma_mode][ui]);
}
// UDMA mode
for( ui=0 ; ui<3 ; ui++ ) {
outb( IndexVal+4+ui , IndexReg );
outb( DataValUDMA[udma_mode][ui] , DataReg );
// printk("UDMA - %#x/%#x - %#x/%#x\n",IndexReg,IndexVal+4+ui,DataReg,DataValUDMA[udma_mode][ui]);
}
return(0);
}
// -------------------------------------------------------------------
int speed_26x(unchar dev, unchar pio_mode, unchar dma_mode, unchar udma_mode)
{
ft_drive_t *drive = &ft_drive[dev];
struct pci_dev *ft_pci_dev = drive->pchannel->padapter->ft_pci_dev;
unchar PA=0,PB=0,MB=0,MC=0;
unchar A_REG,B_REG,C_REG;
unchar offset;
switch(pio_mode) {
case 0: PA=0x09; PB=0x13; break;
case 1: PA=0x05; PB=0x0C; break;
case 2: PA=0x03; PB=0x08; break;
case 3: PA=0x02; PB=0x06; break;
case 4: PA=0x01; PB=0x04; break;
}
switch(dma_mode) {
case 0: MB=0x07; MC=0x0F; break;
case 1: MB=0x03; MC=0x04; break;
case 2: MB=0x03; MC=0x03; break;
}
switch(udma_mode) {
case 0: MB=0x03; MC=0x03; break;
case 1: MB=0x02; MC=0x02; break;
case 2: MB=0x01; MC=0x01; break;
case 3: MB=0x02; MC=0x02; break;
case 4:
case 5: MB=0x01; MC=0x01; break;
}
offset = ( dev % 4 ) * 4 ;
if( drive->select == 0xA0 )
A_REG = ( pio_mode >= 3 ) ? 0xF0|PA : 0xD0|PA ; // master drive
else A_REG = ( pio_mode >= 3 ) ? 0x70|PA : 0x50|PA ; // slave drive
B_REG=MB<<5|PB;
C_REG=MC;
pci_write_config_byte(ft_pci_dev, 0x60+offset, A_REG);
pci_write_config_byte(ft_pci_dev, 0x61+offset, B_REG);
pci_write_config_byte(ft_pci_dev, 0x62+offset, C_REG);
DEB(printk("bus=%#x,fn=%#x,offset=%d",bus,fn,offset));
DEB(printk("Register:A/B/C=%#x/%#x/%#x\n",A_REG,B_REG,C_REG));
return(0);
}
// -------------------------------------------------------------------
int set_speed(unchar dev, unchar pio, unchar dma, unchar udma)
{
ushort deviceid = ft_drive[dev].pchannel->padapter->deviceid ;
DebFtFun(printk("%#x/%#x/%#x\n",pio,dma,udma););
if( DID26x(deviceid) )
speed_26x( dev,pio,dma,udma );
else if( DID27x(deviceid) ) {
if( deviceid != DID270 )
speed_27x( dev,pio,dma,udma );
}
return(0);
}
/*********************************************************************
* Function:
* void FreeResource(char force, unchar *str) ;
*
* Description:
* Free Memory Resource and print message if need .
********************************************************************/
void FreeResource(char force, unchar *str) {
free_pages((ulong)ENGINE,5);
free_pages((ulong)P_BVIR,1);
if( force ) printk(str);
else DEB(printk(str););
return;
}
/*********************************************************************
* Function:
* int fasttrak_proc_info(char *, char **, off_t, int, int, int)
*
* Description:
* in user space , user can view "/proc/scsi/FastTrak/x"
* to run fasttrak_proc_info() in kernel space.
* PS:
* the rebuilding information reported under the condition of
* one MIRROR array , not support two MIRROR array concurrently.
*********************************************************************/
int fasttrak_proc_info(char *buffer, char **start, off_t offset,
int length, int hostno, int func)
{
ft_proc_t info;
static char StopFlag = 0 ;
if( StopFlag-- ) return(0);
info.buffer = buffer;
info.length = length;
info.offset = offset;
info.position = 0;
if (start) {
*start = buffer;
}
GetProcInfo(&info);
StopFlag = 1;
if (info.position > info.offset)
return (info.position - info.offset);
else
return 0;
}
/*********************************************************************
* Function:
* int fasttrak_ioctl(Scsi_Device *device,int cmd,void *arg)
*
* Description:
* Ioctl Interface: commands list
* . 0x3801: Read FastTrak Status (ft_info_t)
* . 0x3802: Read Identify Drive x Data (ft_driveid)
* . 0x3803: Read Swap Box Information (ft_swapbox_t)
* . 0x3804: Set Disable Mirroring Synchronous Flag
* . 0x3805: Control Swap Box power (on/off - 1/0)
*********************************************************************/
static int fasttrak_ioctl(Scsi_Device *device,int cmd,void *arg)
{
uint ui ;
unchar *args;
if( !arg ) {
DEB(printk("fasttrak_ioctl: Unable to handle NULL pointer.\n"););
//return(0x10);
return -EINVAL ;
}
args = (unchar *) arg ;
switch( cmd ) {
// monitor FastTrak Adapter/Array/Drive Status (arg="")
case 0x3801 : {
memset(args,0,sizeof(ft_info_t));
RepArrInfo(args);
return( 0 );
}
case 0x3802 : { // Read Identify Data (arg="0")
unchar *tmp, error;
error = 0 ;
if( !(args[0] >= '0' && args[0] <= FT_Max_Dev + '0') )
error = 1 ;
else if( !ft_drive[args[0]-'1'].present )
error = 1 ;
if( error ) {
strcpy(args,"fasttrak_ioctl: Drive not exist.\n");
return(-1);
}
tmp = (unchar *)ft_drive[args[0]-'1'].identify ;
ui = 0 ;
do {
args[ui] = tmp[ui];
} while( ++ui < 512 ) ;
args[ui] = 0 ;
return( 0 );
}
case 0x3803 : { // Get Swap Box Information
unchar *ptr, ret;
//memset(&ft_box,0,sizeof(ft_swapbox_t)*FT_Max_Dev);
//structure can't not be reset, fix in 1.02.0.25
if( (ret=GetAllSwapInfo()) ) {
sprintf(args,"IOCTL: System is busy.");
return(ret);
}
ptr = (unchar *) &ft_box ;
ui = 0 ;
while( ui < sizeof(ft_swapbox_t)*FT_Max_Dev ) {
args[ui] = ptr[ui] ;
ui++ ;
}
return(0);
}
case 0x3804 : // Enable(1)/Disable(0) Rebuild Option with args[0]
return( Ioctl3804(args) ) ;
case 0x3805 : {
unchar dev,fun;
ulong flags;
ushort base2;
dev = args[0] ; // user count drive from '1'
fun = args[1] ; // fun : on/off - '1'/'0'
if( (dev < '1') || (dev > '0'+FT_Max_Dev) )
return(-1);
if( (fun!='0') && (fun!='1') )
return(-2);
dev -= '1';
fun -= '0';
base2 = ft_drive[dev].pchannel->base + 2 ;
if( ft_drive[dev].lbusy )
return(-3); // device busy
save_flags(flags);
cli();
SWAP_STATE(0,dev);
if( fun ) // power on
outb( 0x12,base2 ); // Green LED
else // power off
outb( 0x1,base2 ); // RED LED
SWAP_STATE(1,dev);
restore_flags(flags);
return(0);
}
case 0x3806 : {
static int event = 0;
snmp_log_t *p;
memset(args,0,sizeof(snmp_log_t));
p = (snmp_log_t *)args;
if ( !snmp_log_index )
return(-1);
while (event < snmp_log_index) {
p[event].sequence = snmp_log[event].sequence ;
p[event].eventid = snmp_log[event].eventid;
p[event].arrayid = snmp_log[event].arrayid;
p[event].diskid = snmp_log[event].diskid;
event += 1;
}
p[event].sequence = p[event].eventid = 0 ;
p[event].arrayid = p[event].diskid = 0 ;
event = snmp_log_index = 0;
memset(snmp_log,0,sizeof(snmp_log_t)*50);
return(0);
}
#ifdef Linux_Fujitsu_0060
case 0x3807 : // RAID1 Synchronization
return( Ioctl3807(args) ) ;
case 0x3808 :
return( Ioctl3808(args) ) ; //Disk Write Cache
case 0x3809 :
return( Ioctl3809(args) ) ; //Flush Cache
#endif
case 0x380A :
case 0x380B :
case 0x380C : {
extern int ATIoctl(int cmd , unchar *args);
DEB(printk("ATIoctl cmd %#x.\n",cmd););
return(ATIoctl(cmd,args));
}
case 0x380E : // FastCheck event ioctl ( add by hank & bennett )
return( Ioctl380E( args ) );
case 0x380F : // BIOS flash ioctl ( add by hank & bennett )
return( Ioctl380F( device , args ) );
default :
printk("fasttrak_ioctl not support the fun (%#x).\n",cmd);
return(-1);
}
}
/*********************************************************************
* Function:
* init_handler
*
* Description:
* Waitting bInitRAID() terminates.
*********************************************************************/
void init_handler(ulong wait)
{
if(blConfigDoneFlag!=TRUE) {
if( wait ) {
if( (wait!=InitTime) && !(wait%1000) )
printk("Waiting initial ...\n");
init_timer(&Init_Timer);
Init_Timer.expires = jiffies + 5; // 5*10 ms
Init_Timer.data = wait-50 ; // 50 ms
Init_Timer.function = init_handler;
add_timer(&Init_Timer);
}
return;
}
#ifdef KERNEL_VERSION_24x
wake_up(&Wait24);
#else
module_wake_up(&Wait22);
#endif
return;
}
/*********************************************************************
* Function:
* FindAdapters24()
*
* Description: for Linux Kernel 2.2.x & 2.4.x
* 1. find controller
* 2. find FastTrak adapter ( ClassCode=0x0104 )
*********************************************************************/
int FindAdaptersNumber( void ) {
unchar class ;
unchar adapters ;
#ifdef KERNEL_VERSION_24x
struct pci_dev *ft_pci_dev = NULL ;
#else
struct pci_dev *ft_pci_dev = pci_devices ;
#endif
class = adapters = 0 ;
while( FT_DEVID[class] ) {
while( (ft_pci_dev = pci_find_device(PCI_VENDOR_ID_PROMISE,FT_DEVID[class],ft_pci_dev)) ) {
if( (ft_pci_dev->class)>>8 == 0x0104 ) {
ft_adapter_t *pada = &ft_adapter[adapters];
if( adapters >= FT_Max_Ada ) {
printk("FastTrak: The Driver only supports one/two ");
printk("FastTrak adapter(s).\n");
printk("Others do not be supported:\n");
printk(" -- FastTrak%s not been enabled.\n",
(FT_DEVID[class] == DID262) ? "66" : "100" );
return(adapters);
}
pada->present = 1;
pada->ft_pci_dev= ft_pci_dev;
pada->deviceid = ft_pci_dev->device;
pada->irq = ft_pci_dev->irq;
adapters ++;
}
}
class++;
} // End of while( FT_DEVID[class] )
return(adapters);
}
/*********************************************************************
* Function:
* FindAdapters()
*
* Description:
* 1. find controller
* 2. find FastTrak adapter ( ClassCode=0x0104 )
* 3. request ioports and irq , and then fill out structure
*********************************************************************/
int FindAdapters( void )
{
unchar uc ;
unchar adapters ;
unsigned int ldata;
unsigned short fwport;
if (!pci_present()) { // return non-zero on detection
DEB(printk("FastTrak : PCI card is not present. \n"));
return 0;
}
if( !(adapters = FindAdaptersNumber()) )
return(0);
for( uc=0 ; uc < FT_Max_Ada ; uc++ ) {
if( ft_adapter[uc].present ) {
if( init_ioports(uc) ) {
printk("FastTrak: init_ioports(%d) error.\n",uc);
return(0);
}
else {
// origin by Bennett
// read base address #5
pci_read_config_dword( ft_adapter[uc].ft_pci_dev , PCI_BASE_ADDRESS_5 , &ldata );
flash_pmem[uc] = (unsigned char *)ldata;
//printk( "Hank: BA#5(flash) = %8.8X\n" , (unsigned int)flash_pmem[uc] );
pci_read_config_word( ft_adapter[uc].ft_pci_dev , PCI_BASE_ADDRESS_4 , &fwport );
fwport &= 0xFFFC;
index1[uc] = fwport + 0x09;
//printk( "Hank: index1 = %4.4X\n" , index1[uc] );
data1[uc] = fwport + 0x0B;
//printk( "Hank: data1 = %4.4X\n" , data1[uc] );
fwport += 0x14; // for flash memory write
flash_port[uc] = fwport;
//printk( "Hank: Flash Write port = %4.4X\n" , fwport );
}
}
}
return(adapters);
}
/*********************************************************************
* Function:
* do_fasttrak_detect
*
* Description:
* Try to detect , initialize and register an FastTrak adapter.
* Return non-zero number on detection.
* The driver only support one FastTrak adapter now.
*********************************************************************/
int do_fasttrak_detect( struct SHT *template ) {
unchar dev, result ;
unchar adapters ;
struct Scsi_Host *shpnt ;
ft_adapter_t *ft_ada0;
/*
* Initial Global Data Structure :
* ft_adapter[FT_Max_Ada]
* ft_channel[FT_Max_Ch]
* ft_drive[FT_Max_Dev]
*/
if( InitGDS() ) {
printk("FastTrak: Initial Global Data Structure fail.\n");
return(0);
}
InitForSpecificOEM() ;
/*
* Find Adapter :
* 1. find controller
* 2. find FastTrak adapter ( ClassCode=0x0104 )
* 3. request ioports and irq , and then fill out structure
*/
adapters = FindAdapters();
DEB(printk("FastTrak : total %d FastTrak adapter(s).\n",adapters));
if( !adapters ) return(0);
TX4ImprovePerformance() ;
/*
* Register Array :
* register arrays to OS not drives to OS .otherwise ,
* the driver will return(0) on above code.
* FastTrak all arrays(0-3) are handled by shpnt(SCSI HOST),
* if there are many FastTrak adapters, they can be
* define zero device in order to tell user there are
* two/three/... FastTrak adapters exist.But they do
* not handle any FastTrak array.
*/
shpnt = scsi_register(template,sizeof(ft_adapter_t));
shpnt -> max_channel = 0 ; // channel: primary
shpnt -> max_id = 4 ; // dev : array[0-3]
shpnt -> max_lun = 1 ; // one device
if( !shpnt->hostdata ) {
printk("FastTrak: Bad scsi hostdata for card.\n");
// scsi_unregister(shpnt); release .....
return(0);
}
ft_ada0 = (ft_adapter_t *) shpnt->hostdata ;
memcpy(ft_ada0,&ft_adapter[0],sizeof(ft_adapter_t));
if( adapters >= FT_Max_Ada ) {
struct Scsi_Host *shpnt2 ;
shpnt2 = scsi_register(template,sizeof(ft_adapter_t));
shpnt2 -> max_channel = 0 ; // channel: primary
shpnt2 -> max_id = 0 ; // dev : array[0-3]
shpnt2 -> max_lun = 0 ; // one device
}
/*
* Find Drives :
* 1. identify to know which drive exist.
* 2. if drive exist , and then set feature and speed.
* 3. in set_feature() , identify drive once more ,
* in order to record new information to structure.
*/
for( dev = 0 ; dev < FT_Max_Dev ; dev++ ) {
unchar pio, dma, udma;
ft_drive_t *drive = &ft_drive[dev];
ft_channel_t *channel = drive->pchannel ;
if(!channel->padapter->present)
break; // Adapter does not exist
drive->identify = kmalloc(512,GFP_ATOMIC);
result = identify_device(dev,drive->identify) ;
if( result==1 || result==2 ) continue; // drive not exist
// if( result == 3 ) return(0); // irq error
DEB(printk("FastTrak : Drive[%d] Exist.\n",dev+1););
DEB(printk("FastTrak : Drive[%d] Exist.\n",dev+1););
pio = dma = udma = 0xFF;
if( set_feature(dev,&pio,&dma,&udma) ) {
printk("FastTrak: set_feature fail.\n");
return(0);
}
DEB(printk("Feature(%d) P/D/U = %#x/%#x/%#x\n",dev,pio,dma,udma););
set_speed(dev,pio,dma,udma);
}
{ ///////////// Set AT
extern char SetATConf(void) ;
SetATConf();
}
init_timer(&submit_timer);
submit_timer.expires = jiffies + 200;
submit_timer.data = 0;
submit_timer.function = submit_timer_handler;
add_timer(&submit_timer);
/*
* Initialize RAID ENGINE :
* 1. allocate memory for RAID ENGINE arguments
* 2. start initialize RAID ENGINE : bInitRAID
*/
ENGINE = (unchar *) __get_free_pages(GFP_ATOMIC,5); // 5(15B00 bytes)
P_BVIR = (unchar *) __get_free_pages(GFP_ATOMIC,1); // 1(1D38 bytes)
memset(ENGINE, 0, PAGE_SIZE << 5);
memset(P_BVIR, 0, PAGE_SIZE << 1);
if( (!ENGINE) || (!P_BVIR) ) {
printk("FastTrak : System does not allocate ");
printk("enough memory to FastTrak !!\n");
return(0);
}
bInitRAID(bPOWER_UP,virt_to_bus(ENGINE),ENGINE,0X15B00,P_BVIR);
init_timer(&Init_Timer);
Init_Timer.expires = jiffies + 5 ; //1*HZ;
Init_Timer.data = InitTime ;
Init_Timer.function = init_handler;
add_timer(&Init_Timer);
#ifdef KERNEL_VERSION_24x
// sleep_on(&Wait24);
interruptible_sleep_on(&Wait24);
#else
module_interruptible_sleep_on(&Wait22);
#endif
if( !InitTime ) {
FreeResource(1,"FastTrak: bInitRAID Fail.\n");
return(0);
}
/*
* Fill out data structure after RAID initialize
* 1. define ft_drive[dev].arr
* 2. define ft_drive[dev].strm
*/
if( DefDriveStruct() ) {
FreeResource(1,"FastTrak: Define Drive Structure Error.\n");
return(0);
}
/*
* Initialize SwapBox/PTI LED :
* Drive Fail - red light
* Normal - green light
* Critical - orange light
*/
InitLED();
/* get swap box info */
for( dev=0 ; dev < FT_Max_Dev ; dev+=2 )
GetSwapInfo(dev); //General rule for proc info
CurScsiReq = kmalloc(sizeof(*CurScsiReq),GFP_ATOMIC);
InitQueueBuffer();
return(adapters);
}
/*********************************************************************
* Function:
* fasttrak_detect
*
* Description:
* Try to detect , initialize and register an FastTrak adapter.
* Return non-zero number on detection.
* The driver only support one FastTrak adapter now.
*********************************************************************/
int fasttrak_detect( struct SHT *template )
{
unchar adapters ;
char name[20];
extern int ProductName(char *name) ;
ProductName(name);
printk("PROMISE %s Series Linux Driver ",name);
printk(VERSION " " VERDATE"\n");
// spin_unlock_irq(&io_request_lock);
DebScsiFun(printk("DebScsiFun : fasttrak_detect(). \n"););
template -> proc_dir = &proc_scsi_fasttrak;
template -> proc_info = fasttrak_proc_info;
adapters = do_fasttrak_detect( template );
// spin_lock_irq(&io_request_lock);
return(adapters);
}
/***********************************************************
* Function:
* fasttrak_queuecommand
*
* Description:
* cmd - SCSI command.
* fn - function called on completion.
***********************************************************/
extern int do_queuecommand(ScsiEngReq_p p);
int fasttrak_queuecommand(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
{
ushort us;
int ret;
ulong my_flags;
ft_adapter_t *ft_ada0 = (ft_adapter_t *) cmd->host->hostdata ;
DebScsiFun(printk("DebScsiFun : fasttrak_queuecommand()\n"););
if( !ft_ada0 ) ; // only avoid warning on UP compiling
spin_lock_irqsave(&ft_ada0->ft_lock,my_flags);
memset(CurScsiReq,0,sizeof(*CurScsiReq));
CurScsiReq->cmd = (void *)cmd ;
CurScsiReq->fn = (void *)fn ;
CurScsiReq->target = cmd->target ;
CurScsiReq->use_sg = cmd->use_sg ;
CurScsiReq->buffer = (unchar *)cmd->buffer ;
CurScsiReq->sense = (unchar *)cmd->sense_buffer ;
for( us=0 ; us < 12 ; us ++ )
CurScsiReq->cmnd[us] = cmd->cmnd[us] ;
if(cmd->use_sg) {
struct scatterlist *sgpnt;
sgpnt = (struct scatterlist *) cmd->request_buffer;
for( us=0 ; us < cmd->use_sg ; us++ ) {
CurScsiReq->WStartAddr[us] = virt_to_bus(sgpnt[us].address) ;
CurScsiReq->WCount[us] = sgpnt[us].length ;
}
CurScsiReq->WCount[us] |= (1<<31); // EOT=1
}
ret = do_queuecommand( CurScsiReq ) ;
spin_unlock_irqrestore(&ft_ada0->ft_lock,my_flags);
return( ret ) ;
}
/***********************************************************
* Function:
* EndRequest(unchar id,int result)
*
* Description:
* End SCSI request and return to SCSI high/middle level
***********************************************************/
char EndRequest(void *cmnd,void *fun,int result)
{
Scsi_Cmnd *cmd;
*cmd = (Scsi_Cmnd *)cmnd ;
void (*fn)(Scsi_Cmnd *) = (void *)fun ;
cmd->result = result ;
cmd->scsi_done = fn ;
fn(cmd);
DEB(printk("End SCSI request .\n"));
return(0);
}
/***********************************************************
* Function:
* fasttrak_abort
*
* Description:
* cmd - SCSI command.
***********************************************************/
int fasttrak_abort(Scsi_Cmnd *cmd)
{
// This is called when a scsi cmnd timeout at the os level
// Returning SCSI_ABORT_ERROR will cause the os to call our
// reset routine. This is how miniport handles timeouts.
printk("fasttrak_abort: returning SCSI_ABORT_ERROR\n");
return SCSI_ABORT_ERROR;
}
int fasttrak_abort_eh(Scsi_Cmnd *cmd)
{
printk("fasttrak_abort_eh: returning SCSI_ABORT_ERROR\n");
return SCSI_ABORT_ERROR; // 0x05
}
/***********************************************************
* Function:
* fasttrak_reset
*
* Description:
* cmd - SCSI command.
* reset_flags - reset flags.
***********************************************************/
extern int reset_controller(void);
int fasttrak_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
{
printk("fasttrak_reset: returning SCSI_RESET_SUCCESS\n");
reset_controller();
return SCSI_RESET_SUCCESS;
}
int fasttrak_reset_eh(Scsi_Cmnd *cmd)
{
printk("fasttrak_reset_eh: returning SCSI_RESET_SUCCESS\n");
reset_controller();
return SCSI_RESET_SUCCESS; // 0x02
}
/***********************************************************
* Function:
* fasttrak_bios_param
*
* Description:
* This function returns unit geometry in cylinders/heads/sectors
***********************************************************/
int fasttrak_bios_param(Disk *disk, kdev_t dev, int Geometry[])
{
Geometry[0]=64; // heads
Geometry[1]=32; // sectors
Geometry[2]=disk->capacity / (Geometry[0]*Geometry[1]); // cylinders
if (disk->capacity > 0x200000) { // 1GB
Geometry[0]=255; // heads
Geometry[1]=63; // sectors
Geometry[2]=disk->capacity/(Geometry[0]*Geometry[1]); // cylinders
// capacity larger than 526GB(C/H/S = 65535/255/63)
if (Geometry[2] > 65535) {
Geometry[2] = 65535;
Geometry[1] = disk->capacity / (Geometry[2]*Geometry[0]);
}
}
return 0;
}
// -----------------------------------------------
ulong wV2P(unchar *p_b,unchar *p_wCount)
{
ulong addr;
DebEngFun(printk("DebEngFun : wV2P(0x%-lx)\n",(ulong)p_b););
addr = virt_to_bus(p_b);
return((ulong)cpu_to_le32(addr));
}
// -----------------------------------------------
unchar *p_P2V(ulong wAddr,ushort hCount)
{
DebEngFun(printk("DebEngFun : p_P2V(0x%-lx)\n",wAddr););
return((unchar *)bus_to_virt(wAddr));
}
// -----------------------------------------------
#ifdef MODULE
Scsi_Host_Template driver_template = FASTTRAK;
#include <linux/module.h>
int init_module(void) {
driver_template.module = &__this_module;
scsi_register_module(MODULE_SCSI_HA, &driver_template);
if (driver_template.present) {
return 0;
}
free_pages((ulong)ENGINE,5);
free_pages((ulong)P_BVIR,1);
scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
return -1;
}
void cleanup_module(void) {
unchar ch;
uint irq0, irq1;
del_timer(&submit_timer);
del_timer(&error_timer_ch0);
del_timer(&error_timer_ch1);
del_timer(&error_timer_ch2);
del_timer(&error_timer_ch3);
irq0 = ft_adapter[0].ft_pci_dev->irq ;
free_irq(irq0,&ft_adapter[0]) ;
if(ft_adapter[1].present) {
irq1 = ft_adapter[1].ft_pci_dev->irq ;
if(irq0 != irq1)
free_irq(irq1,&ft_adapter[0]) ;
}
for( ch=0 ; ch < FT_Max_Ch ; ch++ ) {
if(ft_channel[ch].padapter->present) {
release_region(ft_channel[ch].base,8);
release_region(ft_channel[ch].ctrl,1);
release_region(ft_channel[ch].bm,8);
}
}
free_pages((ulong)ENGINE,5);
free_pages((ulong)P_BVIR,1);
driver_template.proc_dir = &proc_scsi_fasttrak;
scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
driver_template.proc_dir = &proc_scsi_fasttrak;
return;
}
#endif
[root@proxy ftsrc]#