?? 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 0static 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;}#endifstatic 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 + -