📄 wd7000.c
字号:
* an execution phase code. * * There are other formats besides these; these are the ones I've tried * to use. Formats for some of the defined ICB opcodes are not defined * (notably, get/set unsolicited interrupt status) in my copy of the OEM * manual, and others are ambiguous/hard to follow. */#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ /* 0x87 is reserved */#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */typedef struct icbRecvCmd { unchar op; unchar IDlun; /* Initiator SCSI ID/lun */ unchar len[3]; /* command buffer length */ unchar ptr[3]; /* command buffer address */ unchar rsvd[7]; /* reserved */ volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbRecvCmd;typedef struct icbSendStat { unchar op; unchar IDlun; /* Target SCSI ID/lun */ unchar stat; /* (outgoing) completion status byte 1 */ unchar rsvd[12]; /* reserved */ volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbSendStat;typedef struct icbRevLvl { unchar op; volatile unchar primary; /* primary revision level (returned) */ volatile unchar secondary; /* secondary revision level (returned) */ unchar rsvd[12]; /* reserved */ volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbRevLvl;typedef struct icbUnsMask { /* I'm totally guessing here */ unchar op; volatile unchar mask[14]; /* mask bits */#if 0 unchar rsvd[12]; /* reserved */#endif volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbUnsMask;typedef struct icbDiag { unchar op; unchar type; /* diagnostics type code (0-3) */ unchar len[3]; /* buffer length */ unchar ptr[3]; /* buffer address */ unchar rsvd[7]; /* reserved */ volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbDiag;#define ICB_DIAG_POWERUP 0 /* Power-up diags only */#define ICB_DIAG_WALKING 1 /* walking 1's pattern */#define ICB_DIAG_DMA 2 /* DMA - system memory diags */#define ICB_DIAG_FULL 3 /* do both 1 & 2 */typedef struct icbParms { unchar op; unchar rsvd1; /* reserved */ unchar len[3]; /* parms buffer length */ unchar ptr[3]; /* parms buffer address */ unchar idx[2]; /* index (MSB-LSB) */ unchar rsvd2[5]; /* reserved */ volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbParms;typedef struct icbAny { unchar op; unchar data[14]; /* format-specific data */ volatile unchar vue; /* vendor-unique error code */ volatile unchar status; /* returned (icmb) status */ volatile unchar phase; /* used by interrupt handler */} IcbAny;typedef union icb { unchar op; /* ICB opcode */ IcbRecvCmd recv_cmd; /* format for receive command */ IcbSendStat send_stat; /* format for send status */ IcbRevLvl rev_lvl; /* format for get revision level */ IcbDiag diag; /* format for execute diagnostics */ IcbParms eparms; /* format for get/set exec parms */ IcbAny icb; /* generic format */ unchar data[18];} Icb;#ifdef MODULEstatic char * wd7000 = NULL;MODULE_PARM(wd7000, "s");#endif/* * Driver SCB structure pool. * * The SCBs declared here are shared by all host adapters; hence, this * structure is not part of the Adapter structure. */static Scb scbs[MAX_SCBS];static Scb *scbfree = NULL; /* free list */static int freescbs = MAX_SCBS; /* free list counter *//* * END of data/declarations - code follows. */static void setup_error (char *mesg, int *ints){ if (ints[0] == 3) printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n", ints[1], ints[2], ints[3], mesg); else if (ints[0] == 4) printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], mesg); else printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], ints[5], mesg);}/* * Note: You can now set these options from the kernel's "command line". * The syntax is: * * wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]] * * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values * are 8000ns for BUS_ON and 1875ns for BUS_OFF. * eg: * wd7000=7,6,0x350 * * will configure the driver for a WD-7000 controller * using IRQ 15 with a DMA channel 6, at IO base address 0x350. */static int __init wd7000_setup(char *str){ static short wd7000_card_num = 0; short i, j; int ints[6]; (void)get_options(str, ARRAY_SIZE(ints), ints); if (wd7000_card_num >= NUM_CONFIGS) { printk("wd7000_setup: Too many \"wd7000=\" configurations in " "command line!\n"); return 0; } if ((ints[0] < 3) || (ints[0] > 5)) { printk("wd7000_setup: Error in command line! " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>[,<BUS_OFF>]]\n"); } else { for (i = 0; i < NUM_IRQS; i++) if (ints[1] == wd7000_irq[i]) break; if (i == NUM_IRQS) { setup_error("invalid IRQ.", ints); return 0; } else configs[wd7000_card_num].irq = ints[1]; for (i = 0; i < NUM_DMAS; i++) if (ints[2] == wd7000_dma[i]) break; if (i == NUM_DMAS) { setup_error("invalid DMA channel.", ints); return 0; } else configs[wd7000_card_num].dma = ints[2]; for (i = 0; i < NUM_IOPORTS; i++) if (ints[3] == wd7000_iobase[i]) break; if (i == NUM_IOPORTS) { setup_error("invalid I/O base address.", ints); return 0; } else configs[wd7000_card_num].iobase = ints[3]; if (ints[0] > 3) { if ((ints[4] < 500) || (ints[4] > 31875)) { setup_error("BUS_ON value is out of range (500 to 31875 nanoseconds)!", ints); configs[wd7000_card_num].bus_on = BUS_ON; } else configs[wd7000_card_num].bus_on = ints[4] / 125; } else configs[wd7000_card_num].bus_on = BUS_ON; if (ints[0] > 4) { if ((ints[5] < 500) || (ints[5] > 31875)) { setup_error("BUS_OFF value is out of range (500 to 31875 nanoseconds)!", ints); configs[wd7000_card_num].bus_off = BUS_OFF; } else configs[wd7000_card_num].bus_off = ints[5] / 125; } else configs[wd7000_card_num].bus_off = BUS_OFF; if (wd7000_card_num) { for (i = 0; i < (wd7000_card_num - 1); i++) for (j = i + 1; j < wd7000_card_num; j++) if (configs[i].irq == configs[j].irq) { setup_error("duplicated IRQ!", ints); return 0; } else if (configs[i].dma == configs[j].dma) { setup_error("duplicated DMA channel!", ints); return 0; } else if (configs[i].iobase == configs[j].iobase) { setup_error ("duplicated I/O base address!", ints); return 0; } }#ifdef WD7000_DEBUG printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n", configs[wd7000_card_num].irq, configs[wd7000_card_num].dma, configs[wd7000_card_num].iobase, configs[wd7000_card_num].bus_on * 125, configs[wd7000_card_num].bus_off * 125);#endif wd7000_card_num++; } return 1;}__setup("wd7000=", wd7000_setup);#ifdef ANY2SCSI_INLINE/* * Since they're used a lot, I've redone the following from the macros * formerly in wd7000.h, hopefully to speed them up by getting rid of * all the shifting (it may not matter; GCC might have done as well anyway). * * xany2scsi and xscsi2int were not being used, and are no longer defined. * (They were simply 4-byte versions of these routines). */typedef union { /* let's cheat... */ int i; unchar u[sizeof (int)]; /* the sizeof(int) makes it more portable */} i_u;static inline void any2scsi (unchar * scsi, int any){ *scsi++ = ((i_u) any).u[2]; *scsi++ = ((i_u) any).u[1]; *scsi++ = ((i_u) any).u[0];}static inline int scsi2int (unchar * scsi){ i_u result; result.i = 0; /* clears unused bytes */ result.u[2] = *scsi++; result.u[1] = *scsi++; result.u[0] = *scsi++; return (result.i);}#else/* * These are the old ones - I've just moved them here... */#undef any2scsi#define any2scsi(up, p) (up)[0] = (((unsigned long) (p)) >> 16); \ (up)[1] = ((unsigned long) (p)) >> 8; \ (up)[2] = ((unsigned long) (p));#undef scsi2int#define scsi2int(up) ( (((unsigned long) *(up)) << 16) + \ (((unsigned long) (up)[1]) << 8) + \ ((unsigned long) (up)[2]) )#endifstatic inline void wd7000_enable_intr (Adapter *host){ host->control |= INT_EN; outb (host->control, host->iobase + ASC_CONTROL);}static inline void wd7000_enable_dma (Adapter *host){ unsigned long flags; host->control |= DMA_EN; outb (host->control, host->iobase + ASC_CONTROL); flags = claim_dma_lock(); set_dma_mode (host->dma, DMA_MODE_CASCADE); enable_dma (host->dma); release_dma_lock(flags); }#define WAITnexttimeout 200 /* 2 seconds */static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned noneof){ register unsigned WAITbits; register unsigned long WAITtimeout = jiffies + WAITnexttimeout; while (time_before_eq(jiffies, WAITtimeout)) { WAITbits = inb (port) & mask; if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0)) return (0); } return (1);}static inline void delay (unsigned how_long){ register unsigned long time = jiffies + how_long; while (time_before(jiffies, time));}static inline int command_out (Adapter * host, unchar * cmd, int len){ if (!WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { while (len--) { do { outb (*cmd, host->iobase + ASC_COMMAND); WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0); } while (inb (host->iobase + ASC_STAT) & CMD_REJ); cmd++; } return (1); } printk ("wd7000 command_out: WAIT failed(%d)\n", len + 1); return (0);}/* * This version of alloc_scbs is in preparation for supporting multiple * commands per lun and command chaining, by queueing pending commands. * We will need to allocate Scbs in blocks since they will wait to be * executed so there is the possibility of deadlock otherwise. * Also, to keep larger requests from being starved by smaller requests, * we limit access to this routine with an internal busy flag, so that * the satisfiability of a request is not dependent on the size of the * request. */static inline Scb *alloc_scbs (int needed){ register Scb *scb, *p; register unsigned long flags; register unsigned long timeout = jiffies + WAITnexttimeout; register unsigned long now; static int busy = 0; int i; if (needed <= 0) return (NULL); /* sanity check */ save_flags (flags); cli (); while (busy) { /* someone else is allocating */ spin_unlock_irq(&io_request_lock); for (now = jiffies; now == jiffies; ); /* wait a jiffy */ spin_lock_irq(&io_request_lock); } busy = 1; /* not busy now; it's our turn */ while (freescbs < needed) { timeout = jiffies + WAITnexttimeout; do { spin_unlock_irq(&io_request_lock); for (now = jiffies; now == jiffies; ); /* wait a jiffy */ spin_lock_irq(&io_request_lock); } while (freescbs < needed && time_before_eq(jiffies, timeout)); /* * If we get here with enough free Scbs, we can take them. * Otherwise, we timed out and didn't get enough. */ if (freescbs < needed) { busy = 0; panic ("wd7000: can't get enough free SCBs.\n"); restore_flags (flags); return (NULL); } } scb = scbfree; freescbs -= needed; for (i = 0; i < needed; i++) { p = scbfree; scbfree = p->next; } p->next = NULL; busy = 0; /* we're done */ restore_flags (flags); return (scb);}static inline void free_scb (Scb *scb){ register unsigned long flags; save_flags (flags); cli (); memset (scb, 0, sizeof (Scb)); scb->next = scbfree;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -