📄 aha_scsi.c
字号:
sp->at_eof = FALSE;
/* Write filemark if writes have been done. */
if (sp->need_eof && sp->tstat.mt_dsreg != DS_ERR) {
if (scsi_simple(SCSI_WREOF, 1) != SENSE_NO_SENSE) {
printf("%s: failed to add filemark\n", s_name());
} else {
sp->tstat.mt_dsreg = DS_OK;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno++;
}
}
/* Rewind if rewind device. */
if (s_type == TYPE_RST) {
if (scsi_simple(SCSI_REWIND, 1) != SENSE_NO_SENSE) {
printf("%s: failed to rewind\n", s_name());
} else {
sp->tstat.mt_dsreg = DS_OK;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno = 0;
}
}
return(OK);
}
/*===========================================================================*
* s_do_ioctl *
*===========================================================================*/
PRIVATE int s_do_ioctl(dp, m_ptr)
struct driver *dp;
message *m_ptr;
{
struct scsi *sp;
if (s_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
sp = s_sp;
/* Ioctls are device specific. */
switch (sp->devtype) {
case SCSI_DEVDISK:
case SCSI_DEVWORM:
case SCSI_DEVCDROM:
case SCSI_DEVOPTICAL:
if (m_ptr->REQUEST == DIOCEJECT) {
/* Eject disk. */
if (sp->open_ct > 1) return(EBUSY);
/* Send a start/stop command with code 2: stop and eject. */
if (scsi_simple(SCSI_STRTSTP, 2) != SENSE_NO_SENSE)
return(EIO);
return(OK);
}
/* Call the common code for disks and disk like devices. */
return(do_diocntl(dp, m_ptr));
default:
return(ENOTTY);
case SCSI_DEVTAPE:
break;
}
/* Further ioctls are for tapes. */
if (m_ptr->REQUEST == MTIOCTOP) {
struct mtop op;
phys_bytes op_phys;
long delta;
int key;
byte *buf = tmp_buf;
/* Basic tape commands: rewind, space, write eof marks, ... */
op_phys = numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, sizeof(op));
if (op_phys == 0) return(EINVAL);
phys_copy(op_phys, vir2phys(&op), (phys_bytes) sizeof(op));
switch(op.mt_op) {
case MTREW:
case MTOFFL:
case MTRETEN:
case MTFSF:
case MTFSR:
case MTBSF:
case MTBSR:
case MTEOM:
/* Write an EOF mark before spacing. */
if (sp->need_eof && sp->tstat.mt_dsreg != DS_ERR) {
if (scsi_simple(SCSI_WREOF, 1) != SENSE_NO_SENSE)
return(EIO);
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno++;
sp->need_eof = FALSE;
}
sp->at_eof = FALSE;
}
switch(op.mt_op) {
case MTREW:
case MTOFFL:
case MTRETEN:
case MTERASE:
/* Rewind, Offline, Retension, Erase. */
switch(op.mt_op) {
case MTOFFL:
if (scsi_simple(SCSI_LOADUNLD, 0) != SENSE_NO_SENSE)
return(EIO);
sp->state &= ~S_READY;
break;
case MTRETEN:
if (scsi_simple(SCSI_LOADUNLD, 3) != SENSE_NO_SENSE)
return(EIO);
break;
case MTERASE:
if (scsi_simple(SCSI_REWIND, 0) != SENSE_NO_SENSE)
return(EIO);
if (scsi_simple(SCSI_ERASE, 1) != SENSE_NO_SENSE)
return(EIO);
/* Rewind once more. */
/*FALL THROUGH*/
case MTREW:
if (scsi_simple(SCSI_REWIND, 0) != SENSE_NO_SENSE)
return(EIO);
}
sp->tstat.mt_dsreg = DS_OK;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno = 0;
break;
case MTFSF:
case MTFSR:
case MTBSF:
case MTBSR:
if (sp->tstat.mt_dsreg == DS_ERR) return(EIO);
group0();
rq->ccb.opcode = CCB_INIT;
ccb_cmd0(rq).scsi_op = SCSI_SPACE;
delta = op.mt_count;
if (op.mt_op == MTBSR) delta = -delta;
if (op.mt_op == MTBSF) delta = -delta - 1;
h2b24(ccb_cmd0(rq).trlength, delta);
ccb_cmd0(rq).fixed =
op.mt_op == MTFSR || op.mt_op == MTBSR ? 0 : 1;
if ((key = scsi_command(0L, 0)) != SENSE_NO_SENSE) {
if (sense_key(key) != SENSE_NO_SENSE) return(EIO);
if (sense_eom(key)) {
/* Banging into end of tape. */
if (op.mt_op == MTBSF || op.mt_op == MTBSR) {
/* Backspacing to start of tape. */
sp->tstat.mt_dsreg = DS_EOF;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno = 0;
} else {
/* Not forwards please! */
return(EIO);
}
}
if (sense_eof(key)) {
/* Reaching a filemark. */
sp->tstat.mt_dsreg = DS_EOF;
sp->at_eof = TRUE;
if (op.mt_op == MTFSR) {
/* Forwards. */
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno++;
} else {
/* Backwards (bad idea!) */
sp->tstat.mt_blkno = -1;
sp->tstat.mt_fileno--;
}
}
} else {
if (op.mt_op == MTFSR || op.mt_op == MTBSR) {
sp->tstat.mt_blkno += delta;
} else {
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno += delta;
}
if (op.mt_op == MTBSF) {
/* n+1 backwards, and 1 forward. */
group0();
rq->ccb.opcode = CCB_INIT;
ccb_cmd0(rq).scsi_op = SCSI_SPACE;
h2b24(ccb_cmd0(rq).trlength, 1L);
ccb_cmd0(rq).fixed = 1;
if (scsi_command(0L, 0) != SENSE_NO_SENSE)
return(EIO);
sp->tstat.mt_fileno++;
}
sp->tstat.mt_dsreg = DS_OK;
}
break;
case MTWEOF:
/* Write EOF marks. */
if (sp->tstat.mt_dsreg == DS_ERR) return(EIO);
if (op.mt_count < 0) return(EIO);
if (op.mt_count == 0) return(OK);
group0();
rq->ccb.opcode = CCB_INIT;
ccb_cmd0(rq).scsi_op = SCSI_WREOF;
h2b24(ccb_cmd0(rq).trlength, op.mt_count);
if (scsi_command(0L, 0) != SENSE_NO_SENSE) return(EIO);
sp->tstat.mt_dsreg = DS_OK;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno += op.mt_count;
sp->need_eof = FALSE;
break;
case MTEOM:
/* Forward space to end of media. */
if (sp->tstat.mt_dsreg == DS_ERR) return(EIO);
do {
group0();
rq->ccb.opcode = CCB_INIT;
ccb_cmd0(rq).scsi_op = SCSI_SPACE;
h2b24(ccb_cmd0(rq).trlength, 0x7FFFFF);
ccb_cmd0(rq).fixed = 1;
key = scsi_command(0L, 0);
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno += 0x7FFFFF;
if (key != SENSE_NO_SENSE) {
if (key != SENSE_BLANK_CHECK) return(EIO);
sp->tstat.mt_fileno -= sp->tstat.mt_resid;
}
} while (key == SENSE_NO_SENSE);
sp->tstat.mt_dsreg = DS_OK;
break;
case MTBLKZ:
case MTMODE:
/* Select tape block size or tape density. */
/* Rewind tape. */
if (scsi_simple(SCSI_REWIND, 0) != SENSE_NO_SENSE)
return(EIO);
sp->tstat.mt_dsreg = DS_OK;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_fileno = 0;
if (op.mt_op == MTBLKZ && op.mt_count == 0) {
/* Request for variable block size mode. */
sp->tfixed = FALSE;
sp->block_size = 1;
} else {
/* First a modesense to get the current values. */
if (scsi_simple(SCSI_MDSENSE, 255) != SENSE_NO_SENSE)
return(EIO);
/* Must at least have one block descriptor. */
if (buf[3] < 8) return(EIO);
buf[0] = 0;
buf[1] = 0;
/* buf[2]: buffered mode & speed */
buf[3] = 8;
if (op.mt_op == MTMODE) /* New density */
buf[4 + 0] = op.mt_count;
/* buf[4 + 1]: number of blocks */
buf[4 + 4] = 0;
if (op.mt_op == MTBLKZ) /* New block size */
h2b24(buf + 4 + 5, (long) op.mt_count);
/* Set the new density/blocksize. */
if (scsi_simple(SCSI_MDSELECT, 4+8) != SENSE_NO_SENSE)
return(EIO);
if (op.mt_op == MTBLKZ) {
sp->tfixed = TRUE;
sp->block_size= op.mt_count;
}
}
sp->state &= ~S_READY;
if (scsi_probe() != OK) return(EIO);
break;
default:
/* Not implemented. */
return(ENOTTY);
}
} else
if (m_ptr->REQUEST == MTIOCGET) {
/* Request tape status. */
phys_bytes get_phys;
get_phys = numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS,
sizeof(sp->tstat));
if (get_phys == 0) return(EINVAL);
if (sp->tstat.mt_dsreg == DS_OK) {
/* Old error data is never cleared (until now). */
sp->tstat.mt_erreg = 0;
sp->tstat.mt_resid = 0;
}
phys_copy(vir2phys(&sp->tstat), get_phys,
(phys_bytes) sizeof(sp->tstat));
} else {
/* Not implemented. */
return(ENOTTY);
}
return(OK);
}
/*===========================================================================*
* scsi_simple *
*===========================================================================*/
PRIVATE int scsi_simple(opcode, count)
int opcode; /* SCSI opcode */
int count; /* count or flag */
{
/* The average group 0 SCSI command with just a simple flag or count. */
vir_bytes len = 0; /* Sometimes a buffer is used. */
group0();
rq->ccb.opcode = CCB_INIT;
ccb_cmd0(rq).scsi_op = opcode;
/* Fill in the count argument at the proper place. */
switch (opcode) {
case SCSI_REQSENSE:
case SCSI_INQUIRY:
case SCSI_MDSENSE:
case SCSI_MDSELECT:
ccb_cmd0(rq).nblocks = count;
len = count;
break;
case SCSI_STRTSTP:
/* SCSI_LOADUNLD: (synonym) */
ccb_cmd0(rq).nblocks = count;
break;
case SCSI_RDLIMITS:
len = count;
break;
case SCSI_WREOF:
h2b24(ccb_cmd0(rq).trlength, (long) count);
break;
case SCSI_REWIND:
case SCSI_ERASE:
ccb_cmd0(rq).fixed = count;
break;
}
return(scsi_command(tmp_phys, len));
}
/*===========================================================================*
* group0 *
*===========================================================================*/
PRIVATE void group0()
{
/* Prepare the ccb for a group 0 SCSI command. */
rq->ccb.cmdlen = sizeof(cdb0_t);
/* Clear cdb to zeros the ugly way. */
* (u32_t *) (rq->ccb.cmd + 0) = 0;
* (u16_t *) (rq->ccb.cmd + 4) = 0;
}
/*===========================================================================*
* group1 *
*===========================================================================*/
PRIVATE void group1()
{
rq->ccb.cmdlen = sizeof(cdb1_t);
* (u32_t *) (rq->ccb.cmd + 0) = 0;
* (u32_t *) (rq->ccb.cmd + 4) = 0;
* (u16_t *) (rq->ccb.cmd + 8) = 0;
}
/*===========================================================================*
* scsi_command *
*===========================================================================*/
PRIVATE int scsi_command(data, len)
phys_bytes data;
vir_bytes len;
{
/* Execute a SCSI command and return the results. Unlike most other routines,
* this routine returns the sense key of a SCSI command instead of OK or EIO.
*/
struct scsi *sp = s_sp;
int key;
message intr_mess;
rq->ccb.addrcntl = ccb_scid(s_sp->targ) | ccb_lun(s_sp->lun);
if (rq->ccb.opcode == CCB_SCATTER) {
/* Device read/write; add checks and use scatter/gather vector. */
rq->ccb.addrcntl |= s_opcode == DEV_READ ? CCB_INCHECK : CCB_OUTCHECK;
data = vir2phys(rq->dmalist);
len = (byte *) rq->dmaptr - (byte *) rq->dmalist;
if (aha_model == AHA1540) {
/* A plain 1540 can't do s/g. */
rq->ccb.opcode = CCB_INIT;
data = b2h24(rq->dmalist[0].dataptr);
len = b2h24(rq->dmalist[0].datalen);
}
}
h2b24(rq->ccb.datalen, (u32_t) len);
h2b24(rq->ccb.dataptr, data);
dump_scsi_cmd();
mailbox[0].status = AHA_MBOXSTART;
out_byte(AHA_DATAREG, AHACOM_STARTSCSI); /* hey, you've got mail! */
/* Wait for the SCSI command to complete. */
while (mailbox[1].status == AHA_MBOXFREE) {
/* No mail yet, wait for an interrupt. */
receive(HARDWARE, &intr_mess);
}
mailbox[1].status = AHA_MBOXFREE; /* free up inbox */
/* Check the results of the operation. */
if (rq->ccb.hastat != 0) {
/* Weird host adapter status. */
printf("%s: host adapter error 0x%02x%s\n", s_name(), rq->ccb.hastat,
rq->ccb.hastat == HST_TIMEOUT ? " (Selection timeout)" : "");
errordump();
if (sp->devtype == SCSI_DEVTAPE) sp->tstat.mt_dsreg = DS_ERR;
memset((void *) &ccb_sense(rq), 0, sizeof(sense_t));
return(SENSE_HARDWARE);
}
if (rq->ccb.tarstat != 0) {
/* A SCSI error has occurred. */
sense_t *sense = &ccb_sense(rq);
if (sense->len < 2) {
/* No additional code and qualifier, zero them. */
sense->add_code = sense->add_qual = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -