ultrastor.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,205 行 · 第 1/3 页
C
1,205 行
in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1); /* Only upper nibble is significant for Product ID 1 */ if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {#if (ULTRASTOR_DEBUG & UD_DETECT)# ifdef PORT_OVERRIDE printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte);# else printk("US14F: detect: no adapter at port %03X\n", config.port_address);# endif#endif#ifdef PORT_OVERRIDE goto out_release_port;#else release_region(config.port_address, 0x0c); continue;#endif } version_byte = in_byte;#ifndef PORT_OVERRIDE break; } if (i == ARRAY_SIZE(ultrastor_ports_14f)) {# if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: no port address found!\n");# endif /* all ports probed already released - we can just go straight out */ return FALSE; }#endif#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: adapter found at port address %03X\n", config.port_address);#endif /* Set local doorbell mask to disallow bus reset unless ultrastor_bus_reset is true. */ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address)); /* All above tests passed, must be the right thing. Get some useful info. */ /* Register the I/O space that we use */ *(char *)&config_1 = inb(CONFIG(config.port_address + 0)); *(char *)&config_2 = inb(CONFIG(config.port_address + 1)); config.bios_segment = bios_segment_table[config_1.bios_segment]; config.doorbell_address = config.port_address; config.ogm_address = config.port_address + 0x8; config.icm_address = config.port_address + 0xC; config.interrupt = interrupt_table_14f[config_1.interrupt]; config.ha_scsi_id = config_2.ha_scsi_id; config.heads = mapping_table[config_2.mapping_mode].heads; config.sectors = mapping_table[config_2.mapping_mode].sectors; config.bios_drive_number = config_2.bios_drive_number; config.subversion = (version_byte & 0x0F); if (config.subversion == U34F) config.dma_channel = 0; else config.dma_channel = dma_channel_table_14f[config_1.dma_channel]; if (!config.bios_segment) {#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: not detected.\n");#endif goto out_release_port; } /* Final consistency check, verify previous info. */ if (config.subversion != U34F) if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: consistency check failed\n");#endif goto out_release_port; } /* If we were TRULY paranoid, we could issue a host adapter inquiry command here and verify the data returned. But frankly, I'm exhausted! */ /* Finally! Now I'm satisfied... */#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: detect succeeded\n" " Port address: %03X\n" " BIOS segment: %05X\n" " Interrupt: %u\n" " DMA channel: %u\n" " H/A SCSI ID: %u\n" " Subversion: %u\n", config.port_address, config.bios_segment, config.interrupt, config.dma_channel, config.ha_scsi_id, config.subversion);#endif tpnt->this_id = config.ha_scsi_id; tpnt->unchecked_isa_dma = (config.subversion != U34F);#if ULTRASTOR_MAX_CMDS > 1 config.mscp_free = ~0;#endif /* * Brrr, &config.mscp[0].SCint->host) it is something magical.... * XXX and FIXME */ if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", &config.mscp[0].SCint->device->host)) { printk("Unable to allocate IRQ%u for UltraStor controller.\n", config.interrupt); goto out_release_port; } if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) { printk("Unable to allocate DMA channel %u for UltraStor controller.\n", config.dma_channel); free_irq(config.interrupt, NULL); goto out_release_port; } tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG; printk("UltraStor driver version" VERSION ". Using %d SG lists.\n", ULTRASTOR_14F_MAX_SG); return TRUE;out_release_port: release_region(config.port_address, 0x0c); return FALSE;}static int ultrastor_24f_detect(Scsi_Host_Template * tpnt){ int i; struct Scsi_Host * shpnt = NULL;#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US24F: detect");#endif /* probe each EISA slot at slot address C80 */ for (i = 1; i < 15; i++) { unsigned char config_1, config_2; unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT; if (inb(addr) != US24F_PRODUCT_ID_0 && inb(addr+1) != US24F_PRODUCT_ID_1 && inb(addr+2) != US24F_PRODUCT_ID_2) continue; config.revision = inb(addr+3); config.slot = i; if (! (inb(addr+4) & 1)) {#if (ULTRASTOR_DEBUG & UD_DETECT) printk("U24F: found disabled card in slot %u\n", i);#endif continue; }#if (ULTRASTOR_DEBUG & UD_DETECT) printk("U24F: found card in slot %u\n", i);#endif config_1 = inb(addr + 5); config.bios_segment = bios_segment_table[config_1 & 7]; switch(config_1 >> 4) { case 1: config.interrupt = 15; break; case 2: config.interrupt = 14; break; case 4: config.interrupt = 11; break; case 8: config.interrupt = 10; break; default: printk("U24F: invalid IRQ\n"); return FALSE; } /* BIOS addr set */ /* base port set */ config.port_address = addr; config.doorbell_address = addr + 12; config.ogm_address = addr + 0x17; config.icm_address = addr + 0x1C; config_2 = inb(addr + 7); config.ha_scsi_id = config_2 & 7; config.heads = mapping_table[(config_2 >> 3) & 3].heads; config.sectors = mapping_table[(config_2 >> 3) & 3].sectors;#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US24F: detect: detect succeeded\n" " Port address: %03X\n" " BIOS segment: %05X\n" " Interrupt: %u\n" " H/A SCSI ID: %u\n", config.port_address, config.bios_segment, config.interrupt, config.ha_scsi_id);#endif tpnt->this_id = config.ha_scsi_id; tpnt->unchecked_isa_dma = 0; tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG; shpnt = scsi_register(tpnt, 0); if (!shpnt) { printk(KERN_WARNING "(ultrastor:) Could not register scsi device. Aborting registration.\n"); free_irq(config.interrupt, do_ultrastor_interrupt); return FALSE; } if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", shpnt)) { printk("Unable to allocate IRQ%u for UltraStor controller.\n", config.interrupt); return FALSE; } shpnt->irq = config.interrupt; shpnt->dma_channel = config.dma_channel; shpnt->io_port = config.port_address;#if ULTRASTOR_MAX_CMDS > 1 config.mscp_free = ~0;#endif /* Mark ICM and OGM free */ outb(0, addr + 0x16); outb(0, addr + 0x1B); /* Set local doorbell mask to disallow bus reset unless ultrastor_bus_reset is true. */ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12)); outb(0x02, SYS_DOORBELL_MASK(addr+12)); printk("UltraStor driver version " VERSION ". Using %d SG lists.\n", tpnt->sg_tablesize); return TRUE; } return FALSE;}static int ultrastor_detect(Scsi_Host_Template * tpnt){ tpnt->proc_name = "ultrastor"; return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt);}static int ultrastor_release(struct Scsi_Host *shost){ if (shost->irq) free_irq(shost->irq, NULL); if (shost->dma_channel != 0xff) free_dma(shost->dma_channel); if (shost->io_port && shost->n_io_port) release_region(shost->io_port, shost->n_io_port); scsi_unregister(shost); return 0;}static const char *ultrastor_info(struct Scsi_Host * shpnt){ static char buf[64]; if (config.slot) sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u", config.slot, config.interrupt); else if (config.subversion) sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u", config.port_address, (int)config.bios_segment, config.interrupt); else sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u", config.port_address, (int)config.bios_segment, config.interrupt, config.dma_channel); return buf;}static inline void build_sg_list(struct mscp *mscp, Scsi_Cmnd *SCpnt){ struct scatterlist *sl; long transfer_length = 0; int i, max; sl = (struct scatterlist *) SCpnt->request_buffer; max = SCpnt->use_sg; for (i = 0; i < max; i++) { mscp->sglist[i].address = isa_page_to_bus(sl[i].page) + sl[i].offset; mscp->sglist[i].num_bytes = sl[i].length; transfer_length += sl[i].length; } mscp->number_of_sg_list = max; mscp->transfer_data = isa_virt_to_bus(mscp->sglist); /* ??? May not be necessary. Docs are unclear as to whether transfer length field is ignored or whether it should be set to the total number of bytes of the transfer. */ mscp->transfer_data_length = transfer_length;}static int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){ struct mscp *my_mscp;#if ULTRASTOR_MAX_CMDS > 1 int mscp_index;#endif unsigned int status; /* Next test is for debugging; "can't happen" */ if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0) panic("ultrastor_queuecommand: no free MSCP\n"); mscp_index = find_and_clear_bit_16(&config.mscp_free); /* Has the command been aborted? */ if (xchgb(0xff, &config.aborted[mscp_index]) != 0) { status = DID_ABORT << 16; goto aborted; } my_mscp = &config.mscp[mscp_index]; *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3); /* Tape drives don't work properly if the cache is used. The SCSI READ command for a tape doesn't have a block offset, and the adapter incorrectly assumes that all reads from the tape read the same blocks. Results will depend on read buffer size and other disk activity. ??? Which other device types should never use the cache? */ my_mscp->ca = SCpnt->device->type != TYPE_TAPE; my_mscp->target_id = SCpnt->device->id; my_mscp->ch_no = 0; my_mscp->lun = SCpnt->device->lun; if (SCpnt->use_sg) { /* Set scatter/gather flag in SCSI command packet */ my_mscp->sg = TRUE; build_sg_list(my_mscp, SCpnt); } else { /* Unset scatter/gather flag in SCSI command packet */ my_mscp->sg = FALSE; my_mscp->transfer_data = isa_virt_to_bus(SCpnt->request_buffer); my_mscp->transfer_data_length = SCpnt->request_bufflen; } my_mscp->command_link = 0; /*???*/ my_mscp->scsi_command_link_id = 0; /*???*/ my_mscp->length_of_sense_byte = sizeof SCpnt->sense_buffer; my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len; memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs); my_mscp->adapter_status = 0; my_mscp->target_status = 0; my_mscp->sense_data = isa_virt_to_bus(&SCpnt->sense_buffer); my_mscp->done = done; my_mscp->SCint = SCpnt; SCpnt->host_scribble = (unsigned char *)my_mscp; /* Find free OGM slot. On 24F, look for OGM status byte == 0. On 14F and 34F, wait for local interrupt pending flag to clear. FIXME: now we are using new_eh we should punt here and let the midlayer sort it out */retry: if (config.slot) while (inb(config.ogm_address - 1) != 0 && config.aborted[mscp_index] == 0xff) barrier(); /* else??? */ while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) && config.aborted[mscp_index] == 0xff) barrier(); /* To avoid race conditions, keep the code to write to the adapter atomic. This simplifies the abort code. Right now the scsi mid layer has the host_lock already held */ if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) goto retry; status = xchgb(0, &config.aborted[mscp_index]); if (status != 0xff) {#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) printk("USx4F: queuecommand: aborted\n");#if ULTRASTOR_MAX_CMDS > 1 log_ultrastor_abort(&config, mscp_index);#endif#endif status <<= 16; aborted: set_bit(mscp_index, &config.mscp_free); /* If the driver queues commands, call the done proc here. Otherwise return an error. */#if ULTRASTOR_MAX_CMDS > 1 SCpnt->result = status; done(SCpnt); return 0;#else return status;#endif } /* Store pointer in OGM address bytes */ outl(isa_virt_to_bus(my_mscp), config.ogm_address);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?