📄 wd7000.c
字号:
/* The interrupt is for an incoming mailbox */ icmb = flag & 0x3f; scb = (struct scb *) scsi2int(mb.icmb[icmb].scbptr); icmb_status = mb.icmb[icmb].status; mb.icmb[icmb].status = 0;#ifdef DEBUG printk(" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x", icmb, scb, icmb_status, scb->vue );#endif if (!(scb->op & 0x80)) { /* an SCB is done */ SCpnt = scb->SCpnt; if (--(SCpnt->SCp.phase) <= 0) { /* all scbs for SCpnt are done */ host_error = scb->vue | (icmb_status << 8); scsi_error = scb->status; errstatus = make_code(host_error,scsi_error); SCpnt->result = errstatus; if (SCpnt->host_scribble != NULL) scsi_free(SCpnt->host_scribble,WD7000_SCRIBBLE); free_scb(scb); SCpnt->scsi_done(SCpnt); } } else { /* an ICB is done */ icb = (unchar *) scb; icb[ICB_STATUS] = icmb_status; icb[ICB_PHASE] = 0; } wd7000_intr_ack(); DEB(printk(".\n");) return;}int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)){ Scb *scb; Sgb *sgb; unchar *cdb; unchar idlun; short cdblen; cdb = (unchar *) SCpnt->cmnd; cdblen = (*cdb <= 0x1f ? 6 : 10); idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; scb = alloc_scb(); scb->idlun = idlun; memcpy(scb->cdb, cdb, cdblen); scb->direc = 0x40; /* Disable direction check */ scb->SCpnt = SCpnt; /* so we can find stuff later */ SCpnt->host_scribble = NULL; DEB(printk("request_bufflen is %x, bufflen is %x\n",\ SCpnt->request_bufflen, SCpnt->bufflen);) if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; unsigned i; if (scsi_hosts[wd7000_host].sg_tablesize <= 0) { panic("wd7000_queuecommand: scatter/gather not supported.\n"); }#ifdef DEBUG printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg);#endif /* Allocate memory for a scatter/gather-list in wd7000 format. Save the pointer at host_scribble. */#ifdef DEBUG if (SCpnt->use_sg > WD7000_SG) panic("WD7000: requesting too many scatterblocks\n");#endif SCpnt->host_scribble = scsi_malloc(WD7000_SCRIBBLE); sgb = (Sgb *) SCpnt->host_scribble; if (sgb == NULL) panic("wd7000_queuecommand: scsi_malloc() failed.\n"); scb->op = 1; any2scsi(scb->dataptr, sgb); any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) ); for (i = 0; i < SCpnt->use_sg; i++) { any2scsi(sgb->ptr, sg[i].address); any2scsi(sgb->len, sg[i].length); sgb++; } DEB(printk("Using %d bytes for %d scatter/gather blocks\n",\ scsi2int(scb->maxlen), SCpnt->use_sg);) } else { scb->op = 0; any2scsi(scb->dataptr, SCpnt->request_buffer); any2scsi(scb->maxlen, SCpnt->request_bufflen); } return mail_out(scb);}int wd7000_command(Scsi_Cmnd *SCpnt){ wd7000_queuecommand(SCpnt, wd7000_scsi_done); while (SCpnt->SCp.phase > 0); /* phase counts scbs down to 0 */ return SCpnt->result;}int wd7000_init(void){ int i; unchar init_block[] = { INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT }; /* Reset the adapter. */ outb(SCSI_RES|ASC_RES, CONTROL); delay(1); /* reset pulse: this is 10ms, only need 25us */ outb(0,CONTROL); controlstat = 0; /* Wait 2 seconds, then expect Command Port Ready. I suspect something else needs to be done here, but I don't know what. The OEM doc says power-up diagnostics take 2 seconds, and indeed, SCSI commands submitted before then will time out, but none of what follows seems deterred by _not_ waiting 2 secs. */ delay(200); WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); DEB(printk("wd7000_init: Power-on Diagnostics finished\n");) if ((i=inb(INTR_STAT)) != 1) { panic("wd7000_init: Power-on Diagnostics error\n"); return 0; } /* Clear mailboxes */ memset(&mb,0,sizeof (mb)); /* Set up SCB free list */ init_scbs(); /* Set up init block */ any2scsi(init_block+5,&mb); /* Execute init command */ if (!command_out(init_block,sizeof(init_block))) { panic("WD-7000 Initialization failed.\n"); return 0; } /* Wait until init finished */ WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); outb(DISABLE_UNS_INTR, COMMAND); WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); /* Enable Interrupt and DMA */ if (request_irq(IRQ_LVL, wd7000_intr_handle)) { panic("Unable to allocate IRQ for WD-7000.\n"); return 0; }; if(request_dma(DMA_CH)) { panic("Unable to allocate DMA channel for WD-7000.\n"); free_irq(IRQ_LVL); return 0; }; wd7000_enable_dma(); wd7000_enable_intr(); printk("WD-7000 initialized.\n"); return 1; fail: return 0; /* 0 = not ok */}void wd7000_revision(void){ volatile unchar icb[ICB_LEN] = {0x8c}; /* read firmware revision level */ icb[ICB_PHASE] = 1; mail_out( (struct scb *) icb ); while (icb[ICB_PHASE]) /* wait for completion */; rev_1 = icb[1]; rev_2 = icb[2]; /* For boards at rev 7.0 or later, enable scatter/gather. */ if (rev_1 >= 7) scsi_hosts[wd7000_host].sg_tablesize = WD7000_SG;}static const char *wd_bases[] = {(char *)0xce000};typedef struct { char * signature; unsigned offset; unsigned length;} Signature;static const Signature signatures[] = {{"SSTBIOS",0xd,0x7}};#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))int wd7000_detect(int hostnum)/* * return non-zero on detection */{ int i,j; char const *base_address = NULL; for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){ for(j=0;j<NUM_SIGNATURES;j++){ if(!memcmp((void *)(wd_bases[i] + signatures[j].offset), (void *) signatures[j].signature,signatures[j].length)){ base_address=wd_bases[i]; printk("WD-7000 detected.\n"); } } } if (base_address == NULL) return 0; /* Store our host number */ wd7000_host = hostnum; wd7000_init(); wd7000_revision(); /* will set scatter/gather by rev level */ return 1;}static void wd7000_append_info( char *info, const char *fmt, ... )/* * This is just so I can use vsprintf... */{ va_list args; extern int vsprintf(char *buf, const char *fmt, va_list args); va_start(args, fmt); vsprintf(info, fmt, args); va_end(args); return;}const char *wd7000_info(void){ static char info[80] = "Western Digital WD-7000, Firmware Revision "; wd7000_revision(); wd7000_append_info( info+strlen(info), "%d.%d.\n", rev_1, rev_2 ); return info;}void wd7000_set_sync(int id){ volatile unchar icb[ICB_LEN] = {0x8a}; unchar speedval = 0x2c; /* Sets 4MHz for SBIC Revision A */ any2scsi(icb+2,1); /* Transfer 1 byte */ any2scsi(icb+5,&speedval); /* The speed buffer address */ icb[8]=0; icb[9]=2*id; /* The index into the table */ icb[ICB_PHASE] = 1; mail_out( (struct scb *) icb ); while (icb[ICB_PHASE]) /* wait for completion */;}int wd7000_abort(Scsi_Cmnd * SCpnt, int i){#ifdef DEBUG printk("wd7000_abort: Scsi_Cmnd = 0x%08x, code = %d ", SCpnt, i); printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun); { int j; unchar *cdbj = (unchar *) SCpnt->cmnd; for (j=0; j < (*cdbj <= 0x1f?6:10); j++) printk(" %02x", *(cdbj++)); printk(" result %08x\n", SCpnt->result); }#endif return 0;}int wd7000_reset(void){#ifdef DEBUG printk("wd7000_reset\n");#endif return 0;}int wd7000_biosparam(int size, int dev, int* info)/* * This is borrowed directly from aha1542.c, but my disks are organized * this way, so I think it will work OK. */{ info[0] = 32; info[1] = 64; info[2] = (size + 2047) >> 11; if (info[2] >= 1024) info[2] = 1024; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -