📄 aha_scsi.c
字号:
}
/* Check sense data, report error if interesting. */
if (rq->ccb.tarstat == TST_CHECK) {
if ((sense->errc & 0x7E) == 0x70) {
/* Standard SCSI error. */
key = sense->key;
} else {
/* Blame the vendor for any other nonsense. */
key = SENSE_VENDOR;
}
} else {
if (rq->ccb.tarstat == TST_LUNBUSY) {
/* Logical unit is too busy to react... */
key = SENSE_NOT_READY;
} else {
/* The adapter shoudn't do this... */
key = SENSE_HARDWARE;
}
memset((void *) sense, 0, sizeof(sense_t));
}
if (sense_serious(sense_key(key))) {
/* Something bad happened. */
printf("%s: error on command 0x%02x, ", s_name(),
rq->ccb.cmd[0]);
if (rq->ccb.tarstat != TST_CHECK) {
printf("target status 0x%02x\n", rq->ccb.tarstat);
} else {
printf("sense key 0x%02x (%s), additional 0x%02x%02x\n",
sense->key,
str_scsi_sense[sense_key(key)],
sense->add_code, sense->add_qual);
}
errordump();
}
if (sp->devtype == SCSI_DEVTAPE) {
/* Store details of tape error. */
sp->tstat.mt_dsreg = DS_ERR;
sp->tstat.mt_erreg = key;
sp->tstat.mt_resid = b2h32(sense->info);
}
/* Keep only the ILI, EOM and EOF bits of key 0. */
if (sense_key(key) != SENSE_NO_SENSE) key = sense_key(key);
return(key);
}
return(SENSE_NO_SENSE);
}
/*===========================================================================*
* aha_command *
*===========================================================================*/
PRIVATE void aha_command(outlen, outptr, inlen, inptr)
int outlen, inlen;
byte *outptr, *inptr;
{
/* Send a low level command to the host adapter. */
int i;
/* Send command bytes. */
for (i = 0; i < outlen; i++) {
while (in_byte(AHA_STATREG) & AHA_CDF) {} /* !! timeout */
out_byte(AHA_DATAREG, *outptr++);
}
/* Receive data bytes. */
for (i = 0; i < inlen; i++) {
while (!(in_byte(AHA_STATREG) & AHA_DF)
&& !(in_byte(AHA_INTRREG) & AHA_HACC)) {} /* !! timeout */
*inptr++ = in_byte(AHA_DATAREG);
}
/* Wait for command completion. */
while (!(in_byte(AHA_INTRREG) & AHA_HACC)) {} /* !! timeout */
out_byte(AHA_CNTLREG, AHA_IRST); /* clear interrupt */
if (aha_irq != 0) enable_irq(aha_irq);
/* !! should check status register here for invalid command */
}
/*===========================================================================*
* aha_reset *
*===========================================================================*/
PRIVATE int aha_reset()
{
int stat;
int irq, bus_on, bus_off, tr_speed;
unsigned sg_max;
long v;
static char aha0_env[] = "AHA0", aha_fmt[] = "x:d:d:x";
byte cmd[5], haidata[4], getcdata[3], extbios[2];
struct milli_state ms;
/* Get the configuration info from the environment. */
v = AHA_BASEREG;
if (env_parse(aha0_env, aha_fmt, 0, &v, 0x000L, 0x3FFL) == EP_OFF) return 0;
aha_basereg = v;
v = 15;
(void) env_parse(aha0_env, aha_fmt, 1, &v, 2L, 15L);
bus_on = v;
v = 1;
(void) env_parse(aha0_env, aha_fmt, 2, &v, 1L, 64L);
bus_off = v;
v = 0x00;
(void) env_parse(aha0_env, aha_fmt, 3, &v, 0x00L, 0xFFL);
tr_speed = v;
/* Reset controller, wait for self test to complete. */
out_byte(AHA_CNTLREG, AHA_HRST);
milli_start(&ms);
while ((stat = in_byte(AHA_STATREG)) & AHA_STST) {
if (milli_elapsed(&ms) >= AHA_TIMEOUT) {
printf("aha0: AHA154x controller not responding\n");
return(0);
}
}
/* Check for self-test failure. */
if ((stat & (AHA_DIAGF | AHA_INIT | AHA_IDLE | AHA_CDF | AHA_DF))
!= (AHA_INIT | AHA_IDLE)) {
printf("aha0: AHA154x controller failed self-test\n");
return(0);
}
/* !! maybe a santity check here: make sure IDLE and INIT are set? */
/* Get information about controller type and configuration. */
cmd[0] = AHACOM_HAINQUIRY;
aha_command(1, cmd, 4, haidata);
cmd[0] = AHACOM_GETCONFIG;
aha_command(1, cmd, 3, getcdata);
/* First inquiry byte tells what type of board. */
aha_model = haidata[0];
/* Unlock the 1540C or 1540CF's mailbox interface. (This is to keep old
* drivers from using the adapter if extended features are enabled.)
*/
if (aha_model >= AHA1540C) {
cmd[0] = AHACOM_EXTBIOS; /* get extended BIOS information */
aha_command(1, cmd, 2, extbios);
if (extbios[1] != 0) {
/* Mailbox interface is locked, so unlock it. */
cmd[0] = AHACOM_MBOX_ENABLE;
cmd[1] = 0; /* bit 0 = 0 (enable mailbox) */
cmd[2] = extbios[1]; /* lock code to unlock mailbox */
aha_command(3, cmd, 0, 0);
}
}
/* The maximum scatter/gather DMA list length depends on the board model. */
sg_max = 16;
if (aha_model == AHA1540) sg_max = 1; /* 1540 has no s/g */
if (aha_model >= AHA1540C) sg_max = 255; /* 1540C has plenty */
/* Set up the DMA channel. */
switch (getcdata[0]) {
case 0x80: /* channel 7 */
out_byte(0xD6, 0xC3);
out_byte(0xD4, 0x03);
break;
case 0x40: /* channel 6 */
out_byte(0xD6, 0xC2);
out_byte(0xD4, 0x02);
break;
case 0x20: /* channel 5 */
out_byte(0xD6, 0xC1);
out_byte(0xD4, 0x01);
break;
case 0x01: /* channel 0 */
out_byte(0x0B, 0x0C);
out_byte(0x0A, 0x00);
break;
default:
printf("aha0: AHA154x: strange DMA channel\n");
return(0);
}
/* Get the configured IRQ. */
switch (getcdata[1]) {
case 0x40: irq = 15; break;
case 0x20: irq = 14; break;
case 0x08: irq = 12; break;
case 0x04: irq = 11; break;
case 0x02: irq = 10; break;
case 0x01: irq = 9; break;
default:
printf("aha0: strange IRQ setting\n");
return(0);
}
/* Enable interrupts on the given irq. */
put_irq_handler(irq, s_handler);
aha_irq = irq;
enable_irq(irq);
/* Initialize request related data: Command Control Block, mailboxes.
* (We want to have the mailboxes initialized early, because the 1540C
* wants to know it now.)
*/
/* Init ccb. */
rq->ccb.senselen = CCB_SENSEREQ; /* always want sense info */
h2b24(rq->ccb.linkptr, 0L); /* never link commands */
rq->ccb.linkid = 0;
rq->ccb.reserved[0] = 0;
rq->ccb.reserved[1] = 0;
/* Scatter/gather maximum. */
rq->dmalimit = rq->dmalist + (sg_max < NR_IOREQS ? sg_max : NR_IOREQS);
/* Outgoing mailbox. */
mailbox[0].status = AHA_MBOXFREE;
h2b24(mailbox[0].ccbptr, vir2phys(&rq->ccb));
/* Incoming mailbox. */
mailbox[1].status = AHA_MBOXFREE;
/* mailbox[1].ccbptr filled by adapter after command execution. */
/* Tell controller where the mailboxes are and how many. */
cmd[0] = AHACOM_INITBOX;
cmd[1] = 1;
h2b24(cmd + 2, vir2phys(mailbox));
aha_command(5, cmd, 0, 0);
/* !! maybe sanity check: check status reg for initialization success */
/* Set bus on, bus off and transfer speed. */
cmd[0] = AHACOM_BUSON;
cmd[1] = bus_on;
aha_command(2, cmd, 0, 0);
cmd[0] = AHACOM_BUSOFF;
cmd[1] = bus_off;
aha_command(2, cmd, 0, 0);
cmd[0] = AHACOM_SPEED;
cmd[1] = tr_speed;
aha_command(2, cmd, 0, 0);
/* Set SCSI selection timeout. */
cmd[0] = AHACOM_SETIMEOUT;
cmd[1] = SCSI_TIMEOUT != 0; /* timeouts on/off */
cmd[2] = 0; /* reserved */
cmd[3] = SCSI_TIMEOUT / 256; /* MSB */
cmd[4] = SCSI_TIMEOUT % 256; /* LSB */
aha_command(5, cmd, 0, 0);
return(1);
}
/*===========================================================================*
* s_handler *
*===========================================================================*/
PRIVATE int s_handler(irq)
int irq;
{
/* Host adapter interrupt, send message to SCSI task and reenable interrupts. */
if (in_byte(AHA_INTRREG) & AHA_HACC) {
/* Simple commands are polled. */
return 0;
} else {
out_byte(AHA_CNTLREG, AHA_IRST); /* clear interrupt */
interrupt(SCSI);
return 1;
}
}
/*===========================================================================*
* h2b16 *
*===========================================================================*/
PRIVATE void h2b16(b, h)
big16 b;
U16_t h;
{
/* Host byte order to Big Endian conversion. */
b[0] = h >> 8;
b[1] = h >> 0;
}
/*===========================================================================*
* h2b24 *
*===========================================================================*/
PRIVATE void h2b24(b, h)
big24 b;
u32_t h;
{
b[0] = h >> 16;
b[1] = h >> 8;
b[2] = h >> 0;
}
/*===========================================================================*
* h2b32 *
*===========================================================================*/
PRIVATE void h2b32(b, h)
big32 b;
u32_t h;
{
b[0] = h >> 24;
b[1] = h >> 16;
b[2] = h >> 8;
b[3] = h >> 0;
}
/*===========================================================================*
* b2h16 *
*===========================================================================*/
PRIVATE u16_t b2h16(b)
big16 b;
{
return ((u16_t) b[0] << 8)
| ((u16_t) b[1] << 0);
}
/*===========================================================================*
* b2h24 *
*===========================================================================*/
PRIVATE u32_t b2h24(b)
big24 b;
{
return ((u32_t) b[0] << 16)
| ((u32_t) b[1] << 8)
| ((u32_t) b[2] << 0);
}
/*===========================================================================*
* b2h32 *
*===========================================================================*/
PRIVATE u32_t b2h32(b)
big32 b;
{
return ((u32_t) b[0] << 24)
| ((u32_t) b[1] << 16)
| ((u32_t) b[2] << 8)
| ((u32_t) b[3] << 0);
}
#if AHA_DEBUG & 2
/*===========================================================================*
* errordump *
*===========================================================================*/
PRIVATE void errordump()
{
int i;
printf("aha ccb dump:");
for (i = 0; i < sizeof(rq->ccb); i++) {
if (i % 26 == 0) printf("\n");
printf(" %02x", ((byte *) &rq->ccb)[i]);
}
printf("\n");
}
#endif /* AHA_DEBUG & 2 */
#if AHA_DEBUG & 4
/*===========================================================================*
* show_req *
*===========================================================================*/
PRIVATE void show_req()
{
struct iorequest_s **iopp;
dma_t *dmap;
unsigned count, nbytes, len;
iopp = rq->iov;
dmap = rq->dmalist;
count = rq->count;
nbytes = 0;
printf("%lu:%u", rq->pos, count);
while (count > 0) {
if (iopp == rq->iov || *iopp != iopp[-1])
nbytes = (*iopp)->io_nbytes;
printf(" (%u,%lx,%u)", nbytes, b2h24(dmap->dataptr),
len = b2h24(dmap->datalen));
dmap++;
iopp++;
count -= len;
nbytes -= len;
}
if (nbytes > 0) printf(" ...(%u)", nbytes);
printf("\n");
}
#endif /* AHA_DEBUG & 4 */
#if AHA_DEBUG & 8
/*===========================================================================*
* dump_scsi_cmd *
*===========================================================================*/
PRIVATE void dump_scsi_cmd()
{
int i;
printf("scsi cmd:");
for (i = 0; i < rq->ccb.cmdlen; i++) printf(" %02x", rq->ccb.cmd[i]);
printf("\n");
}
#endif /* AHA_DEBUG & 8 */
/*============================================================================*
* s_geometry *
*============================================================================*/
PRIVATE void s_geometry(entry)
struct partition *entry;
{
/* The geometry of a SCSI drive is a complete fake, the Adaptec onboard BIOS
* makes the drive look like a regular drive on the outside. A DO
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -