📄 fdomain.c
字号:
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;
chip = tmc1800;
} else { /* test for 0xe960 id */
if (inb( port + MSB_ID_Code ) != 0x60) return 0;
chip = tmc18c50;
}
/* 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 + Configuration1 );
#if DEBUG_DETECT
printk( " Options = %x\n", options );
#endif
/* Check for board with lowest bios_base. */
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 = 0;
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( "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_major = signatures[j].major_bios_version;
bios_minor = signatures[j].minor_bios_version;
bios_base = addresses[i];
}
}
}
if (!bios_base) {
#if DEBUG_DETECT
printk( " FAILED: NO BIOS\n" );
#endif
return 0;
}
if (bios_major == 2) {
/* 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. It
also means that we don't have a Version 2.0 BIOS :-)
*/
#if DEBUG_DETECT
if (bios_major != 2) 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.
Do not, however, check ports which the kernel knows are being used
by another driver.
*/
for (i = 0; !flag && i < PORT_COUNT; i++) {
port_base = ports[i];
if (check_region( port_base, 0x10 )) {
#if DEBUG_DETECT
printf( " (%x inuse),", port_base );
#endif
continue;
}
#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 */
}
print_banner();
SCSI_Mode_Cntl_port = port_base + SCSI_Mode_Cntl;
FIFO_Data_Count_port = port_base + FIFO_Data_Count;
Interrupt_Cntl_port = port_base + Interrupt_Cntl;
Interrupt_Status_port = port_base + Interrupt_Status;
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( NULL );
if (fdomain_test_loopback()) {
#if DEBUG_DETECT
printk( "Future Domain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
#endif
return 0;
}
this_host = hostnum;
/* Log IRQ with kernel */
if (!interrupt_level) {
panic( "Future Domain: *NO* interrupt level selected!\n" );
} else {
/* Register the IRQ with the kernel */
sa.sa_handler = fdomain_16x0_intr;
sa.sa_flags = SA_INTERRUPT;
sa.sa_mask = 0;
sa.sa_restorer = NULL;
retcode = irqaction( interrupt_level, &sa );
if (retcode < 0) {
if (retcode == -EINVAL) {
printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
printk( " This shouldn't happen!\n" );
printk( " Send mail to faith@cs.unc.edu\n" );
} else if (retcode == -EBUSY) {
printk( "Future Domain: IRQ %d is already in use!\n",
interrupt_level );
printk( " Please use another IRQ!\n" );
} else {
printk( "Future Domain: Error getting IRQ %d\n", interrupt_level );
printk( " This shouldn't happen!\n" );
printk( " Send mail to faith@cs.unc.edu\n" );
}
panic( "Future Domain: Driver requires interruptions\n" );
} else {
printk( "Future Domain: IRQ %d requested from kernel\n",
interrupt_level );
}
}
/* Log I/O ports with kernel */
snarf_region( port_base, 0x10 );
if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
adapter_mask = 0x80;
scsi_hosts[this_host].this_id = 7;
}
#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;
printk( "Future Domain detection routine scanning for devices:\n" );
for (i = 0; i < 8; i++) {
SCinit.target = i;
if (i == scsi_hosts[this_host].this_id) /* Skip host adapter */
continue;
memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
retcode = fdomain_16x0_command(&SCinit);
if (!retcode) {
memcpy(SCinit.cmnd, do_inquiry, sizeof(do_inquiry));
retcode = fdomain_16x0_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 = fdomain_16x0_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)",
((capacity + 5L) / 10L), size );
} else {
memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
retcode = fdomain_16x0_command(&SCinit);
}
printk ("\n" );
} else {
memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
retcode = fdomain_16x0_command(&SCinit);
}
}
}
#endif
return 1;
}
const char *fdomain_16x0_info(void)
{
static char buffer[80];
char *pt;
strcpy( buffer, "Future Domain: TMC-16x0 SCSI driver, version" );
if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
strcat( buffer, strchr( VERSION, ':' ) + 1 );
pt = strrchr( buffer, '$') - 1;
if (!pt) /* Stripped RCS Revision string? */
pt = buffer + strlen( buffer ) - 1;
if (*pt != ' ')
++pt;
*pt++ = '\n';
*pt = '\0';
} else { /* Assume VERSION is a number */
strcat( buffer, " " VERSION "\n" );
}
return buffer;
}
#if 0
static int fdomain_arbitrate( void )
{
int status = 0;
unsigned long timeout;
#if EVERY_ACCESS
printk( "fdomain_arbitrate()\n" );
#endif
outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
outb( adapter_mask, port_base + SCSI_Data_NoACK ); /* Set our id bit */
outb( 0x04 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
timeout = jiffies + 50; /* 500 mS */
while (jiffies < timeout) {
status = inb( TMC_Status_port ); /* Read adapter status */
if (status & 0x02) /* Arbitration complete */
return 0;
}
/* Make bus idle */
fdomain_make_bus_idle();
#if EVERY_ACCESS
printk( "Arbitration failed, status = %x\n", status );
#endif
#if ERRORS_ONLY
printk( "Future Domain: Arbitration failed, status = %x", status );
#endif
return 1;
}
#endif
static int fdomain_select( 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 = jiffies + 25; /* 250mS */
while (jiffies < timeout) {
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;
}
}
/* Make bus idle */
fdomain_make_bus_idle();
#if EVERY_ACCESS
if (!target) printk( "Selection failed\n" );
#endif
#if ERRORS_ONLY
if (!target) printk( "Future Domain: Selection failed" );
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -