📄 fdomain.c
字号:
/* fdomain.c -- Future Domain TMC-1660/TMC-1680 driver * Created: Sun May 3 18:53:19 1992 by faith * Revised: Wed Dec 9 21:34:53 1992 by root * Author: Rickard E. Faith, faith@cs.unc.edu * Copyright 1992 Rickard E. Faith * * $Log$ * 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. * WARNING: THIS IS A BETA VERSION! * USE AT YOUR OWN RISK! * BACKUP YOUR SYSTEM BEFORE USING! * I would like to thank Maxtor, whose *free* 206 page manual on the LXT * drives was very helpful: "LXT SCSI Products: Specifications and OEM * Technical Manual (Revision B/September 1991)" * I wish that I could thank Future Domain for the necessary documentation, * but I can't. I used the $25 "TMC-1800 SCSI Chip Specification" document * (FDC-1800T), which documents the *chip* and not the board. Without it, * I would have been totally lost, but it would have been nice to have some * example source. (The DOS BIOS source cost $250 and the UN*X driver * source was $750 [both required a non-disclosure agreement]. Ever wonder * why there are no freely available Future Domain drivers?) * Thanks to: Todd Carrico (todd@wutc.wustl.edu), Dan Poirier * (poirier@cs.unc.edu ), Ken Corey (kenc@sol.acs.unt.edu), C. de Bruin * (bruin@dutiba.tudelft.nl) and Sakari Aaltonen (sakaria@vipunen.hit.fi) * for alpha testing. Also thanks to Drew Eckhardt (drew@cs.colorado.edu) * and Eric Youngdale (eric@tantalus.nrl.navy.mil) for answering questions, * and to Doug Hoffman (hoffman@cs.unc.edu) for lending me SCSI devices to * make the driver more robust. */#include <linux/sched.h>#include <asm/io.h>#include "../blk.h"#include "scsi.h"#include "hosts.h"#include "fdomain.h"#include <asm/system.h>#include <linux/errno.h>#define VERSION "3.2" /* Change with each revision *//* START OF USER DEFINABLE OPTIONS */#define DEBUG 1 /* Enable debugging output */#define ENABLE_PARITY 1 /* Enable SCSI Parity */#define QUEUE 1 /* Enable command queueing */#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */#define DO_DETECT 0 /* Do device detection here (see scsi.c) */#define RESELECTION 0 /* Support RESELECTION PHASE (NOT stable) *//* 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 0 /* Debug fdomain_16x0_detect() */#define DEBUG_MESSAGES 0 /* Debug MESSAGE IN PHASE */#define DEBUG_ABORT 1 /* Debug abort() routine */#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#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#endifstatic int port_base = 0;static void *bios_base = NULL;static int interrupt_level = 0;static int Data_Mode_Cntl_port;static int FIFO_Data_Count_port;static int Interrupt_Cntl_port;static int Interrupt_Mask_port;static int Read_FIFO_port;static int Read_SCSI_Data_port;static int SCSI_Cntl_port;static int SCSI_Data_NoACK_port;static int SCSI_Status_port;static int TMC_Cntl_port;static int TMC_Status_port;static int Write_FIFO_port;static int Write_SCSI_Data_port;static int this_host = 0;static int can_queue = QUEUE;static volatile int in_command = 0;static volatile int in_interrupt_code = 0;static Scsi_Cmnd *current_SC = NULL;enum { non_queueing = 0x01, in_arbitration = 0x02, in_selection = 0x04, in_other = 0x08, disconnect = 0x10, aborted = 0x20, sent_ident = 0x40, };extern void fdomain_16x0_intr( int unused );enum in_port_type { Read_SCSI_Data = 0, SCSI_Status = 1, TMC_Status = 2, LSB_ID_Code = 5, MSB_ID_Code = 6, Read_Loopback = 7, SCSI_Data_NoACK = 8, Interrupt_Mask = 9, Option_Select = 10, Read_FIFO = 12, FIFO_Data_Count = 14 };enum out_port_type { Write_SCSI_Data = 0, SCSI_Cntl = 1, Interrupt_Cntl = 2, Data_Mode_Cntl = 3, TMC_Cntl = 4, Write_Loopback = 7, Write_FIFO = 12 };static void *addresses[] = { (void *)0xc8000, (void *)0xca000, (void *)0xce000, (void *)0xde000 };#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned )) static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };/* READ THIS BEFORE YOU ADD A SIGNATURE! READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME! READ EVERY WORD, ESPECIALLY THE WORD *NOT* This driver works *ONLY* for Future Domain cards using the TMC-1800 chip. This includes models TMC-1660 and TMC-1680 *ONLY*. The following BIOS signatures have been tried with this driver. These signatures are for boards which do *NOT* work with this driver (but the first one should work with the Seagate driver): FUTURE DOMAIN COPR. (C) 1986-1989 V6.0A7/28/90 FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90 FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90 */struct signature { char *signature; int sig_offset; int sig_length;} signatures[] = { { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0 7/28/89", 5, 50 }, { "FUTURE DOMAIN CORP. (C) 1986-1990 1800", 5, 37 }, /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE */};#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))/* These functions are based on include/asm/io.h */inline static unsigned short inw( unsigned short port ){ unsigned short _v; __asm__ volatile ("inw %1,%0" :"=a" (_v):"d" ((unsigned short) port)); return _v;}inline static void outw( unsigned short value, unsigned short port ){ __asm__ volatile ("outw %0,%1" ::"a" ((unsigned short) value), "d" ((unsigned short) port));}/* These defines are copied from kernel/blk_drv/hd.c */#define insw( buf, count, port ) \ __asm__ volatile \ ( "cld;rep;insw"::"d" (port),"D" (buf),"c" (count):"cx","di" )#define outsw( buf, count, port ) \ __asm__ volatile \ ("cld;rep;outsw"::"d" (port),"S" (buf),"c" (count):"cx","si")static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */{ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */ while (jiffies < the_time);}inline static void fdomain_make_bus_idle( void ){ outb( 0, SCSI_Cntl_port ); outb( 0, Data_Mode_Cntl_port ); outb( 1 | PARITY_MASK, TMC_Cntl_port );}static int fdomain_is_valid_port( int port ){ int options;#if DEBUG_DETECT printk( " (%x%x),", inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );#endif /* The MCA ID is a unique id for each MCA compatible board. We are using ISA boards, but Future Domain provides the MCA ID anyway. We can use this ID to ensure that this is a Future Domain TMC-1660/TMC-1680. */ if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */ if (inb( port + LSB_ID_Code ) != 0x27) return 0; if (inb( port + MSB_ID_Code ) != 0x61) return 0; } else { /* test for 0xe960 id */ if (inb( port + MSB_ID_Code ) != 0x60) return 0; } /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the bios_base matches these ports. If someone was unlucky enough to have purchased more than one Future Domain board, then they will have to modify this code, as we only detect one board here. [The one with the lowest bios_base.] */ options = inb( port + Option_Select );#if DEBUG_DETECT printk( " Options = %x,", options );#endif if (addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0; interrupt_level = ints[ (options & 0x0e) >> 1 ]; return 1;}static int fdomain_test_loopback( void ){ int i; int result; for (i = 0; i < 255; i++) { outb( i, port_base + Write_Loopback ); result = inb( port_base + Read_Loopback ); if (i != result) return 1; } return 0;}int fdomain_16x0_detect( int hostnum ){ int i, j; int flag; struct sigaction sa; int retcode;#if DO_DETECT const int buflen = 255; 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];#endif#if DEBUG_DETECT printk( "SCSI: fdomain_16x0_detect()," );#endif for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {#if DEBUG_DETECT printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );#endif for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) { if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset), signatures[j].signature, signatures[j].sig_length )) { bios_base = addresses[i]; } } } if (!bios_base) {#if DEBUG_DETECT printk( " FAILED: NO BIOS\n" );#endif return 0; } /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM. Assuming the ROM is enabled (otherwise we wouldn't have been able to read the ROM signature :-), then the ROM sets up the RAM area with some magic numbers, such as a list of port base addresses and a list of the disk "geometry" reported to DOS (this geometry has nothing to do with physical geometry). */ port_base = *((char *)bios_base + 0x1fcc) + (*((char *)bios_base + 0x1fcd) << 8); #if DEBUG_DETECT printk( " %x,", port_base );#endif for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) { if (port_base == ports[i]) ++flag; } if (flag) flag = fdomain_is_valid_port( port_base ); if (!flag) { /* Cannot get port base from BIOS RAM */ /* This is a bad sign. It usually means that someone patched the BIOS signature list (the signatures variable) to contain a BIOS signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */ #if DEBUG_DETECT printk( " RAM FAILED, " );#endif /* Anyway, the alternative to finding the address in the RAM is to just search through every possible port address for one that is attached to the Future Domain card. Don't panic, though, about reading all these random port addresses--there are rumors that the Future Domain BIOS does something very similar. */ for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) { port_base = ports[i];#if DEBUG_DETECT printk( " %x,", port_base );#endif flag = fdomain_is_valid_port( port_base ); } } if (!flag) {#if DEBUG_DETECT printk( " FAILED: NO PORT\n" );#endif return 0; /* Cannot find valid set of ports */ }#if DEBUG_DETECT printk( "\n" ); printk( "SCSI: bios_base = %x, port_base = %x, interrupt_level = %d\n", (unsigned)bios_base, port_base, interrupt_level );#endif if (interrupt_level) { printk( "Future Domain: BIOS at %x; port base at %x; using IRQ %d\n", (unsigned)bios_base, port_base, interrupt_level ); } else { printk( "Future Domain: BIOS at %x; port base at %x; *NO* IRQ\n", (unsigned)bios_base, port_base ); } Data_Mode_Cntl_port = port_base + Data_Mode_Cntl; FIFO_Data_Count_port = port_base + FIFO_Data_Count; Interrupt_Cntl_port = port_base + Interrupt_Cntl; Interrupt_Mask_port = port_base + Interrupt_Mask; Read_FIFO_port = port_base + Read_FIFO; Read_SCSI_Data_port = port_base + Read_SCSI_Data; SCSI_Cntl_port = port_base + SCSI_Cntl; SCSI_Data_NoACK_port = port_base + SCSI_Data_NoACK; SCSI_Status_port = port_base + SCSI_Status; TMC_Cntl_port = port_base + TMC_Cntl; TMC_Status_port = port_base + TMC_Status; Write_FIFO_port = port_base + Write_FIFO; Write_SCSI_Data_port = port_base + Write_SCSI_Data; fdomain_16x0_reset(); if (fdomain_test_loopback()) {#if DEBUG_DETECT printk( "SCSI: LOOPBACK TEST FAILED, FAILING DETECT!\n" );#endif return 0; }#if DO_DETECT /* These routines are here because of the way the SCSI bus behaves after a reset. This appropriate behavior was not handled correctly by the higher level SCSI routines when I first wrote this driver. Now, however, correct scan routines are part of scsi.c and these routines are no longer needed. However, this code is still good for debugging. */ SCinit.request_buffer = SCinit.buffer = buf; SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1; SCinit.use_sg = 0; SCinit.lun = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -