📄 ultrastor.c
字号:
/* * ultrastor.c Copyright (C) 1992 David B. Gentzel * Low-level SCSI driver for UltraStor 14F * by David B. Gentzel, Whitfield Software Services, Carnegie, PA * (gentzel@nova.enet.dec.com) * Thanks to UltraStor for providing the necessary documentation *//* * NOTES: * The UltraStor 14F is an intelligent, high performance ISA SCSI-2 host * adapter. It is essentially an ISA version of the UltraStor 24F EISA * adapter. It supports first-party DMA, command queueing, and * scatter/gather I/O. It can also emulate the standard AT MFM/RLL/IDE * interface for use with OS's which don't support SCSI. * * This driver may also work (with some small changes) with the UltraStor * 24F. I have no way of confirming this... * * Places flagged with a triple question-mark are things which are either * unfinished, questionable, or wrong. *//* * CAVEATS: ??? * This driver is VERY stupid. It takes no advantage of much of the power * of the UltraStor controller. I hope to go back and beat it into shape, * but PLEASE, anyone else who would like to, please make improvements! * * By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature * of the mid-level SCSI driver. Once I'm satisfied that the queueing * version is as stable as the non-queueing version, I'll eliminate this * option. */#include <linux/stddef.h>#include <linux/string.h>#include <linux/sched.h>#include <linux/kernel.h>#include <asm/io.h>#include <asm/system.h>#include <asm/dma.h>#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */#include "../blk.h"#include "scsi.h"#include "hosts.h"#include "ultrastor.h"#define VERSION "1.1 alpha"#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])#define BIT(n) (1ul << (n))#define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8)))/* Simply using "unsigned long" in these structures won't work as it causes alignment. Perhaps the "aligned" attribute may be used in GCC 2.0 to get around this, but for now I use this hack. */typedef struct { unsigned char bytes[4];} Longword;/* Used to fetch the configuration info from the config i/o registers. We then store (in a friendlier format) in config. */struct config_1 { unsigned char bios_segment: 3; unsigned char reserved: 1; unsigned char interrupt: 2; unsigned char dma_channel: 2;};struct config_2 { unsigned char ha_scsi_id: 3; unsigned char mapping_mode: 2; unsigned char bios_drive_number: 1; unsigned char tfr_port: 2;};/* Used to store configuration info read from config i/o registers. Most of this is not used yet, but might as well save it. */struct config { unsigned short port_address; const void *bios_segment; unsigned char interrupt: 4; unsigned char dma_channel: 3; unsigned char ha_scsi_id: 3; unsigned char heads: 6; unsigned char sectors: 6; unsigned char bios_drive_number: 1;};/* MailBox SCSI Command Packet. Basic command structure for communicating with controller. */struct mscp { unsigned char opcode: 3; /* type of command */ unsigned char xdir: 2; /* data transfer direction */ unsigned char dcn: 1; /* disable disconnect */ unsigned char ca: 1; /* use cache (if available) */ unsigned char sg: 1; /* scatter/gather operation */ unsigned char target_id: 3; /* target SCSI id */ unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */ unsigned char lun: 3; /* logical unit number */ Longword transfer_data; /* transfer data pointer */ Longword transfer_data_length; /* length in bytes */ Longword command_link; /* for linking command chains */ unsigned char scsi_command_link_id; /* identifies command in chain */ unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */ unsigned char length_of_sense_byte; unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */ unsigned char scsi_cdbs[12]; /* SCSI commands */ unsigned char adapter_status; /* non-zero indicates HA error */ unsigned char target_status; /* non-zero indicates target error */ Longword sense_data;};/* Allowed BIOS base addresses for 14f (NULL indicates reserved) */static const void *const bios_segment_table[8] = { NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000, (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,};/* Allowed IRQs for 14f */static const unsigned char interrupt_table[4] = { 15, 14, 11, 10 };/* Allowed DMA channels for 14f (0 indicates reserved) */static const unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };/* Head/sector mappings allowed by 14f */static const struct { unsigned char heads; unsigned char sectors;} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };/* Config info */static struct config config;/* Our index in the host adapter array maintained by higher-level driver */static int host_number;/* PORT_ADDRESS is first port address used for i/o of messages. */#ifdef PORT_OVERRIDE# define PORT_ADDRESS PORT_OVERRIDE#else# define PORT_ADDRESS (config.port_address)#endifstatic volatile int aborted = 0;/* A probe of address 0x310 screws up NE2000 cards */#ifndef PORT_OVERRIDEstatic const unsigned short ultrastor_ports[] = { 0x330, 0x340, /* 0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,};#endifstatic void ultrastor_interrupt(int cpl);static void (*ultrastor_done)(Scsi_Cmnd *) = 0;static Scsi_Cmnd *SCint = NULL;static const struct { const char *signature; size_t offset; size_t length;} signatures[] = { { "SBIOS 1.01 COPYRIGHT (C) UltraStor Corporation,1990-1992.", 0x10, 57 },};int ultrastor_14f_detect(int hostnum){ size_t i; unsigned char in_byte; struct config_1 config_1; struct config_2 config_2;#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: called\n");#endif#ifndef PORT_OVERRIDE PORT_ADDRESS = 0; for (i = 0; i < ARRAY_SIZE(ultrastor_ports); i++) { PORT_ADDRESS = ultrastor_ports[i];#endif#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: testing port address %03X\n", PORT_ADDRESS);#endif in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 0)); if (in_byte != US14F_PRODUCT_ID_0) {#if (ULTRASTOR_DEBUG & UD_DETECT)# ifdef PORT_OVERRIDE printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte);# else printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS);# endif#endif#ifdef PORT_OVERRIDE return FALSE;#else continue;#endif } in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 1)); /* Only upper nibble is defined 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", PORT_ADDRESS);# endif#endif#ifdef PORT_OVERRIDE return FALSE;#else continue;#endif }#ifndef PORT_OVERRIDE break; } if (i == ARRAY_SIZE(ultrastor_ports)) {# if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: no port address found!\n");# endif return FALSE; }#endif#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: adapter found at port address %03X\n", PORT_ADDRESS);#endif /* All above tests passed, must be the right thing. Get some useful info. */ *(char *)&config_1 = inb(CONFIG(PORT_ADDRESS + 0)); *(char *)&config_2 = inb(CONFIG(PORT_ADDRESS + 1)); config.bios_segment = bios_segment_table[config_1.bios_segment]; config.interrupt = interrupt_table[config_1.interrupt]; config.dma_channel = dma_channel_table[config_1.dma_channel]; 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; /* To verify this card, we simply look for the UltraStor SCSI from the BIOS version notice. */ if (config.bios_segment != NULL) { int found = 0; for (i = 0; !found && i < ARRAY_SIZE(signatures); i++) if (memcmp((char *)config.bios_segment + signatures[i].offset, signatures[i].signature, signatures[i].length)) found = 1; if (!found) config.bios_segment = NULL; } if (!config.bios_segment) {#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: not detected.\n");#endif return FALSE; } /* Final consistancy check, verify previous info. */ if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {#if (ULTRASTOR_DEBUG & UD_DETECT) printk("US14F: detect: consistancy check failed\n");#endif return FALSE; } /* 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", PORT_ADDRESS, config.bios_segment, config.interrupt, config.dma_channel, config.ha_scsi_id);#endif host_number = hostnum; scsi_hosts[hostnum].this_id = config.ha_scsi_id;#ifndef NO_QUEUEING if (request_irq(config.interrupt, ultrastor_interrupt)) { printk("Unable to allocate IRQ%u for UltraStor controller.\n", config.interrupt); return FALSE; }#endif if (request_dma(config.dma_channel)) { printk("Unable to allocate DMA channel %u for UltraStor controller.\n", config.dma_channel);#ifndef NO_QUEUEING free_irq(config.interrupt);#endif return FALSE; } return TRUE;}const char *ultrastor_14f_info(void){ return "UltraStor 14F SCSI driver version " VERSION;}static struct mscp mscp = { OP_SCSI, DTD_SCSI, 0, 1, 0 /* This stuff doesn't change */};int ultrastor_14f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){ unsigned char in_byte;#if (ULTRASTOR_DEBUG & UD_COMMAND) printk("US14F: queuecommand: called\n");#endif /* Skip first (constant) byte */ memset((char *)&mscp + 1, 0, sizeof (struct mscp) - 1); mscp.target_id = SCpnt->target; mscp.lun = SCpnt->lun; mscp.transfer_data = *(Longword *)&SCpnt->request_buffer; mscp.transfer_data_length = *(Longword *)&SCpnt->request_bufflen; mscp.length_of_scsi_cdbs = ((*(unsigned char *)SCpnt->cmnd <= 0x1F) ? 6 : 10); memcpy(mscp.scsi_cdbs, SCpnt->cmnd, mscp.length_of_scsi_cdbs); /* Find free OGM slot (OGMINT bit is 0) */ do in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS)); while (!aborted && (in_byte & 1)); if (aborted) {#if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)) printk("US14F: queuecommand: aborted\n");#endif /* ??? is this right? */ return (aborted << 16); } /* Store pointer in OGM address bytes */ outb_p(BYTE(&mscp, 0), OGM_DATA_PTR(PORT_ADDRESS + 0)); outb_p(BYTE(&mscp, 1), OGM_DATA_PTR(PORT_ADDRESS + 1)); outb_p(BYTE(&mscp, 2), OGM_DATA_PTR(PORT_ADDRESS + 2)); outb_p(BYTE(&mscp, 3), OGM_DATA_PTR(PORT_ADDRESS + 3)); /* Issue OGM interrupt */ outb_p(0x1, LCL_DOORBELL_INTR(PORT_ADDRESS)); ultrastor_done = done; SCint = SCpnt;#if (ULTRASTOR_DEBUG & UD_COMMAND) printk("US14F: queuecommand: returning\n");#endif return 0;}#ifdef NO_QUEUEINGint ultrastor_14f_command(Scsi_Cmnd SCpnt){ unsigned char in_byte;#if (ULTRASTOR_DEBUG & UD_COMMAND) printk("US14F: command: called\n");#endif (void)ultrastor_14f_queuecommand(SCpnt, NULL); /* Wait for ICM interrupt */ do in_byte = inb_p(SYS_DOORBELL_INTR(PORT_ADDRESS)); while (!aborted && !(in_byte & 1)); if (aborted) {#if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)) printk("US14F: command: aborted\n");#endif /* ??? is this right? */ return (aborted << 16); } /* Clean ICM slot (set ICMINT bit to 0) */ outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));#if (ULTRASTOR_DEBUG & UD_COMMAND) printk("US14F: command: returning %08X\n", (mscp.adapter_status << 16) | mscp.target_status);#endif /* ??? not right, but okay for now? */ return (mscp.adapter_status << 16) | mscp.target_status;}#endifint ultrastor_14f_abort(Scsi_Cmnd *SCpnt, int code){#if (ULTRASTOR_DEBUG & UD_ABORT) printk("US14F: abort: called\n");#endif aborted = (code ? code : DID_ABORT);#if (ULTRASTOR_DEBUG & UD_ABORT) printk("US14F: abort: returning\n");#endif return 0;}/* Most of this is commented out because people were getting kernel crashes with it enabled. If you want to re-enable this, please figure out why the kernel was panicing. ERY */int ultrastor_14f_reset(void){ unsigned char in_byte;#if (ULTRASTOR_DEBUG & UD_RESET) printk("US14F: reset: called\n");#endif#if 0 /* Issue SCSI BUS reset */ outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS)); /* Wait for completion... */ do in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS)); while (in_byte & 0x20); aborted = DID_RESET;#endif#if (ULTRASTOR_DEBUG & UD_RESET) printk("US14F: reset: returning\n");#endif return 0;}#ifndef NO_QUEUEINGstatic void ultrastor_interrupt(int cpl){#if (ULTRASTOR_DEBUG & UD_INTERRUPT) printk("US14F: interrupt: called: status = %08X\n", (mscp.adapter_status << 16) | mscp.target_status);#endif if (ultrastor_done == 0) panic("US14F: interrupt: unexpected interrupt!\n"); else { void (*done)(Scsi_Cmnd *); /* Save ultrastor_done locally and zero before calling. This is needed as once we call done, we may get another command queued before this interrupt service routine can return. */ done = ultrastor_done; ultrastor_done = 0; /* Clean ICM slot (set ICMINT bit to 0) */ outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS)); /* Let the higher levels know that we're done */ /* ??? status is wrong here... */ SCint->result = (mscp.adapter_status << 16) | mscp.target_status; done(SCint); }#if (ULTRASTOR_DEBUG & UD_INTERRUPT) printk("US14F: interrupt: returning\n");#endif}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -