fdomain.c
来自「GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目」· C语言 代码 · 共 1,993 行 · 第 1/5 页
C
1,993 行
{ int error; unsigned char pci_bus, pci_dev_fn; /* PCI bus & device function */ unsigned char pci_irq; /* PCI interrupt line */ unsigned int pci_base; /* PCI I/O base address */ unsigned short pci_vendor, pci_device; /* PCI vendor & device IDs */ /* If the PCI BIOS doesn't exist, use the old-style detection routines. Otherwise, get the I/O base address and interrupt from the PCI config registers. */ if (!pcibios_present()) return fdomain_pci_nobios_detect( irq, iobase );#if DEBUG_DETECT /* Tell how to print a list of the known PCI devices from bios32 and list vendor and device IDs being used if in debug mode. */ printk( "\nINFO: cat /proc/pci to see list of PCI devices from bios32\n" ); printk( "\nTMC-3260 detect:" " Using PCI Vendor ID: 0x%x, PCI Device ID: 0x%x\n", PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70 );#endif /* We will have to change this if more than 1 PCI bus is present and the FD scsi host is not on the first bus (i.e., a PCI to PCI bridge, which is not supported by bios32 right now anyway). This should probably be done by a call to pcibios_find_device but I can't get it to work... Also the device ID reported from the PCI config registers does not match the device ID quoted in the tech manual or available from offset 5 from the I/O base address. It should be 0x60E9, but it is 0x0 if read from the PCI config registers. I guess the FD folks neglected to write it to the PCI registers... This loop is necessary to get the device function (at least until someone can get pcibios_find_device to work, I cannot but 53c7,8xx.c uses it...). */ pci_bus = 0; for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++) { pcibios_read_config_word( pci_bus, pci_dev_fn, PCI_VENDOR_ID, &pci_vendor ); if (pci_vendor == PCI_VENDOR_ID_FD) { pcibios_read_config_word( pci_bus, pci_dev_fn, PCI_DEVICE_ID, &pci_device ); if (pci_device == PCI_DEVICE_ID_FD_36C70) { /* Break out once we have the correct device. If other FD PCI devices are added to this driver we will need to add an or of the other PCI_DEVICE_ID_FD_XXXXX's here. */ break; } else { /* If we can't find an FD scsi card we give up. */ return 0; } } } #if DEBUG_DETECT printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n", pci_bus, (pci_dev_fn & 0xf8) >> 3, pci_dev_fn & 7 );#endif /* We now have the appropriate device function for the FD board so we just read the PCI config info from the registers. */ if ((error = pcibios_read_config_dword( pci_bus, pci_dev_fn, PCI_BASE_ADDRESS_0, &pci_base )) || (error = pcibios_read_config_byte( pci_bus, pci_dev_fn, PCI_INTERRUPT_LINE, &pci_irq ))) { printk ( "PCI ERROR: Future Domain 36C70 not initializing" " due to error reading configuration space\n" ); return 0; } else {#if DEBUG_DETECT printk( "TMC-3260 PCI: IRQ = %u, I/O base = 0x%lx\n", pci_irq, pci_base );#endif /* Now we have the I/O base address and interrupt from the PCI configuration registers. Unfortunately it seems that the I/O base address is off by one on my card so I mask it with 0xfff8. This must be some kind of goof in the FD code that does the autoconfig and writes to the PCI registers (or maybe I just don't understand something). If they fix it in later versions of the card or BIOS we may have to adjust the address based on the signature or something... */ *irq = pci_irq; *iobase = (pci_base & 0xfff8);#if DEBUG_DETECT printk( "TMC-3260 fix: Masking I/O base address with 0xff00.\n" ); printk( "TMC-3260: IRQ = %d, I/O base = 0x%x\n", *irq, *iobase );#endif if (!fdomain_is_valid_port( *iobase )) return 0; return 1; } return 0;}#endifint fdomain_16x0_detect( Scsi_Host_Template *tpnt ){ int i, j; int retcode; struct Scsi_Host *shpnt;#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 tpnt->proc_dir = &proc_scsi_fdomain; if (setup_called) {#if DEBUG_DETECT printk( "no BIOS, using port_base = 0x%x, irq = %d\n", port_base, interrupt_level );#endif if (!fdomain_is_valid_port( port_base )) { printk( "fdomain: cannot locate chip at port base 0x%x\n", port_base ); printk( "fdomain: bad LILO parameters?\n" ); return 0; } } else { int flag = 0; 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; PCI_bus = (signatures[j].flag == 1); Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0; bios_base = addresses[i]; } } } if (!bios_base) {#if DEBUG_DETECT printk( " FAILED: NO BIOS\n" );#endif return 0; } if (!PCI_bus) { flag = fdomain_isa_detect( &interrupt_level, &port_base ); } else {#ifdef CONFIG_PCI flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );#else flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );#endif } if (!flag) {#if DEBUG_DETECT printk( " FAILED: NO PORT\n" );#endif#ifdef CONFIG_PCI printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" ); printk( "Send mail to mckinley@msupa.pa.msu.edu.\n" );#endif return 0; /* Cannot find valid set of ports */ } } 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, 0 ); if (fdomain_test_loopback()) {#if DEBUG_DETECT printk( "fdomain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );#endif if (setup_called) { printk( "fdomain: loopback test failed at port base 0x%x\n", port_base ); printk( "fdomain: bad LILO parameters?\n" ); } return 0; } if (this_id) { tpnt->this_id = (this_id & 0x07); adapter_mask = (1 << tpnt->this_id); } else { if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) { tpnt->this_id = 7; adapter_mask = 0x80; } else { tpnt->this_id = 6; adapter_mask = 0x40; } } /* Print out a banner here in case we can't get resources. */ shpnt = scsi_register( tpnt, 0 ); shpnt->irq = interrupt_level; shpnt->io_port = port_base; shpnt->n_io_port = 0x10; print_banner( shpnt ); /* Log IRQ with kernel */ if (!interrupt_level) { panic( "fdomain: *NO* interrupt level selected!\n" ); } else { /* Register the IRQ with the kernel */ retcode = request_irq( interrupt_level, fdomain_16x0_intr, SA_INTERRUPT, "fdomain", NULL); if (retcode < 0) { if (retcode == -EINVAL) { printk( "fdomain: 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( "fdomain: IRQ %d is already in use!\n", interrupt_level ); printk( " Please use another IRQ!\n" ); } else { printk( "fdomain: Error getting IRQ %d\n", interrupt_level ); printk( " This shouldn't happen!\n" ); printk( " Send mail to faith@cs.unc.edu\n" ); } panic( "fdomain: Driver requires interruptions\n" ); } } /* Log I/O ports with kernel */ request_region( port_base, 0x10, "fdomain" );#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( "fdomain: detection routine scanning for devices:\n" ); for (i = 0; i < 8; i++) { SCinit.target = i; if (i == tpnt->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; /* Maximum of one adapter will be detected. */}const char *fdomain_16x0_info( struct Scsi_Host *ignore ){ 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 = '\0'; } else { /* Assume VERSION is a number */ strcat( buffer, " " VERSION ); } return buffer;} /* First pass at /proc information routine. *//* * 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 */int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset, int length, int hostno, int inout ){ const char *info = fdomain_16x0_info( NULL ); int len; int pos; int begin; if (inout) return(-ENOSYS); begin = 0; strcpy( buffer, info ); strcat( buffer, "\n" ); pos = len = strlen( buffer ); if(pos < offset) { len = 0; begin = pos; } *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); if(len > length) len = length; return(len);} #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 */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?