📄 aha_scsi.c
字号:
*===========================================================================*/
PRIVATE int scsi_ntape()
{
/* Gather tape data, block limits, fixed block size or not. */
struct scsi *sp = s_sp;
unsigned minblk;
unsigned long maxblk;
byte *buf = tmp_buf;
/* Minor device type must be for a tape. */
if (s_type != TYPE_RST && s_type != TYPE_NRST) return(EIO);
/* Read limits. */
if (scsi_simple(SCSI_RDLIMITS, 6) != SENSE_NO_SENSE) return(EIO);
minblk = b2h16(buf + 4);
maxblk = b2h24(buf + 1);
printf("%s: limits: min block len %u, max block len %lu\n",
s_name(), minblk, maxblk);
if (sp->state & S_PRESENT) {
/* Keep the current block size. */
if (sp->tfixed) minblk= maxblk= sp->block_size;
}
sp->tstat.mt_dsreg = DS_OK;
sp->tstat.mt_erreg = 0;
sp->tstat.mt_fileno = 0;
sp->tstat.mt_blkno = 0;
sp->tstat.mt_resid = 0;
if (minblk == maxblk) {
/* Fixed block length. */
sp->tfixed = TRUE;
sp->block_size = minblk;
sp->tstat.mt_blksize = minblk;
sp->count_max = UINT_MAX;
} else {
/* Variable block length. */
sp->tfixed = FALSE;
sp->block_size = 1;
sp->tstat.mt_blksize = 0;
sp->count_max = maxblk == 0 ? UINT_MAX : maxblk;
}
/* SCSI modesense. */
if (scsi_simple(SCSI_MDSENSE, 255) != SENSE_NO_SENSE) return(EIO);
/* Write protected? */
sp->state &= ~S_RDONLY;
if (buf[2] & 0x80) sp->state |= S_RDONLY;
/* Density and block size. */
if (buf[3] >= 8) {
printf("%s: density 0x%02x, nblocks %lu, block len ",
s_name(),
buf[4],
b2h24(buf + 4 + 1));
printf(sp->tfixed ? "%lu\n" : "variable\n", b2h24(buf + 4 + 5));
}
sp->state |= S_PRESENT|S_READY;
return(OK);
}
/*===========================================================================*
* s_schedule *
*===========================================================================*/
PRIVATE int s_schedule(proc_nr, iop)
int proc_nr; /* process doing the request */
struct iorequest_s *iop; /* pointer to read or write request */
{
/* Gather I/O requests on consecutive blocks so they may be read/written
* in one SCSI command using scatter/gather DMA.
*/
struct scsi *sp = s_sp;
int r, opcode, spanning;
unsigned nbytes, count;
unsigned long pos;
phys_bytes user_phys, dma_phys;
static unsigned dma_count;
static struct iorequest_s **iopp; /* to add I/O request pointers */
static phys_bytes dma_last; /* address of end of the last added entry */
/* This many bytes to read/write */
nbytes = iop->io_nbytes;
/* From/to this position on the device */
pos = iop->io_position;
/* To/from this user address */
user_phys = numap(proc_nr, (vir_bytes) iop->io_buf, nbytes);
if (user_phys == 0) return(iop->io_nbytes = EINVAL);
/* Read or write? */
opcode = iop->io_request & ~OPTIONAL_IO;
switch (sp->devtype) {
case SCSI_DEVCDROM:
case SCSI_DEVWORM:
case SCSI_DEVDISK:
case SCSI_DEVOPTICAL:
/* Which block on disk and how close to EOF? */
if (pos >= s_dv->dv_size) return(OK); /* At EOF */
if (pos + nbytes > s_dv->dv_size) nbytes = s_dv->dv_size - pos;
pos += s_dv->dv_base;
if ((nbytes % sp->block_size) != 0 || (pos % sp->block_size) != 0) {
/* Not on a device block boundary. CD-ROM? */
return(s_rdcdrom(proc_nr, iop, pos, nbytes, user_phys));
}
break;
case SCSI_DEVTAPE:
if ((nbytes % sp->block_size) != 0)
return(iop->io_nbytes = EINVAL);
/* Old error condition? */
if (sp->tstat.mt_dsreg == DS_ERR) return(iop->io_nbytes = EIO);
if (opcode == DEV_READ && sp->at_eof) return(OK);
s_nextpos = pos = 0; /* pos is ignored */
break;
default:
return(iop->io_nbytes = EIO);
}
/* Probe a device that isn't ready. */
if (!(sp->state & S_READY) && scsi_probe() != OK) return(EIO);
if (rq->count > 0 && pos != s_nextpos) {
/* This new request can't be chained to the job being built. */
if ((r = s_finish()) != OK) return(r);
}
/* The next consecutive block starts at byte position... */
s_nextpos = pos + nbytes;
spanning = FALSE; /* set if a request spans several DMA vectors */
/* While there are "unscheduled" bytes in the request: */
do {
dma_phys = user_phys;
if (rq->count > 0 && (
rq->count == sp->count_max
|| rq->dmaptr == rq->dmalimit
|| !DMA_CHECK(dma_last, dma_phys)
)) {
/* This request can not be added to the scatter/gather list. */
if ((r = s_finish()) != OK) return(r);
s_must = spanning;
continue; /* try again */
}
if (rq->count == 0) {
/* The first request in a row, initialize. */
rq->pos = pos;
s_opcode = opcode;
iopp = rq->iov;
rq->dmaptr = rq->dmalist;
rq->retry = 2;
}
count = nbytes;
/* Don't exceed the maximum transfer count. */
if (rq->count + count > sp->count_max)
count = sp->count_max - rq->count;
/* New scatter/gather entry. */
h2b24(rq->dmaptr->dataptr, dma_phys);
h2b24(rq->dmaptr->datalen, (u32_t) (dma_count = count));
rq->dmaptr++;
dma_last = dma_phys + count;
/* Which I/O request? */
*iopp++ = iop;
/* Update counters. */
rq->count += count;
pos += count;
user_phys += count;
nbytes -= count;
if (!(iop->io_request & OPTIONAL_IO)) s_must = TRUE;
spanning = TRUE; /* the rest of the request must be done */
} while (nbytes > 0);
return(OK);
}
/*===========================================================================*
* s_finish *
*===========================================================================*/
PRIVATE int s_finish()
{
/* Send the I/O requests gathered in *rq to the host adapter. */
struct scsi *sp = s_sp;
unsigned long block;
struct iorequest_s **iopp, *iop;
int key;
if (rq->count == 0) return(OK); /* spurious finish */
show_req();
/* If all the requests are optional then don't do just a few. */
if (!s_must && rq->count < 0x2000) {
rq->count = 0;
return(OK);
}
iopp = rq->iov;
iop = *iopp++;
retry:
switch (sp->devtype) {
case SCSI_DEVCDROM:
case SCSI_DEVWORM:
case SCSI_DEVDISK:
case SCSI_DEVOPTICAL:
/* A read or write SCSI command for a random access device. */
block = rq->pos / sp->block_size;
if (block < (1L << 21)) {
/* We can use a group 0 command for small disks. */
group0();
rq->ccb.opcode = CCB_SCATTER;
ccb_cmd0(rq).scsi_op =
s_opcode == DEV_WRITE ? SCSI_WRITE : SCSI_READ;
h2b24(ccb_cmd0(rq).lba, block);
ccb_cmd0(rq).nblocks = rq->count / sp->block_size;
} else {
/* Large disks require a group 1 command. */
group1();
rq->ccb.opcode = CCB_SCATTER;
ccb_cmd1(rq).scsi_op =
s_opcode == DEV_WRITE ? SCSI_WRITE1 : SCSI_READ1;
h2b32(ccb_cmd1(rq).lba, block);
h2b16(ccb_cmd1(rq).nblocks, rq->count / sp->block_size);
}
key = scsi_command(0L, 0);
if (key == SENSE_NO_SENSE) {
/* fine */;
} else
if (key == SENSE_UNIT_ATT || key == SENSE_ABORTED_CMD) {
/* Check condition? Bus reset most likely. */
/* Aborted command? Maybe retrying will help. */
if (--rq->retry > 0) goto retry;
return(iop->io_nbytes = EIO);
} else
if (key == SENSE_RECOVERED) {
/* Disk drive managed to recover from a read error. */
printf("%s: soft read error at block %lu (recovered)\n",
s_name(), b2h32(ccb_sense(rq).info));
key = SENSE_NO_SENSE;
break;
} else {
/* A fatal error occurred, bail out. */
return(iop->io_nbytes = EIO);
}
break;
case SCSI_DEVTAPE:
/* A read or write SCSI command for a sequential access device. */
group0();
rq->ccb.opcode = CCB_SCATTER;
ccb_cmd0(rq).scsi_op = s_opcode == DEV_WRITE ? SCSI_WRITE : SCSI_READ;
ccb_cmd0(rq).fixed = sp->tfixed;
h2b24(ccb_cmd0(rq).trlength, rq->count / sp->block_size);
key = scsi_command(0L, 0);
if (key != SENSE_NO_SENSE) {
/* Either at EOF or EOM, or an I/O error. */
if (sense_eof(key) || sense_eom(key)) {
/* Not an error, but EOF or EOM. */
sp->at_eof = TRUE;
sp->tstat.mt_dsreg = DS_EOF;
/* The residual tells how much has not been read. */
rq->count -= sp->tstat.mt_resid * sp->block_size;
if (sense_eof(key)) {
/* Went over a filemark. */
sp->tstat.mt_blkno = !sp->tfixed ? -1 :
- (int) (rq->count / sp->block_size);
sp->tstat.mt_fileno++;
}
}
if (sense_ili(key)) {
/* Incorrect length on a variable block length tape. */
if (sp->tstat.mt_resid <= 0) {
/* Large block could not be read. */
return(iop->io_nbytes = EIO);
}
/* Small block read, this is ok. */
rq->count -= sp->tstat.mt_resid;
sp->tstat.mt_dsreg = DS_OK;
}
if (key == SENSE_RECOVERED) {
/* Tape drive managed to recover from an error. */
printf("%s: soft %s error (recovered)\n",
s_name(),
s_opcode == DEV_READ ? "read" : "write");
key = SENSE_NO_SENSE;
sp->tstat.mt_dsreg = DS_OK;
}
if (sp->tstat.mt_dsreg == DS_ERR) {
/* Error was fatal. */
return(iop->io_nbytes = EIO);
}
} else {
sp->tstat.mt_dsreg = DS_OK;
}
if (!sp->tfixed) {
/* Variable block length tape reads record by record. */
sp->tstat.mt_blkno++;
} else {
/* Fixed length tape, multiple blocks transferred. */
sp->tstat.mt_blkno += rq->count / sp->block_size;
}
sp->need_eof = (s_opcode == DEV_WRITE);
break;
default:
assert(0);
}
/* Remove bytes transferred from the I/O requests. */
for (;;) {
if (rq->count > iop->io_nbytes) {
rq->count -= iop->io_nbytes;
iop->io_nbytes = 0;
} else {
iop->io_nbytes -= rq->count;
rq->count = 0;
break;
}
iop = *iopp++;
}
return(key == SENSE_NO_SENSE ? OK : EIO); /* may return EIO for EOF */
}
/*===========================================================================*
* s_rdcdrom *
*===========================================================================*/
PRIVATE int s_rdcdrom(proc_nr, iop, pos, nbytes, user_phys)
int proc_nr; /* process doing the request */
struct iorequest_s *iop; /* pointer to read or write request */
unsigned long pos; /* byte position */
unsigned nbytes; /* number of bytes */
phys_bytes user_phys; /* user address */
{
/* CD-ROM's have a basic block size of 2k. We could try to set a smaller
* virtual block size, but many don't support it. So we use this function.
*/
struct scsi *sp = s_sp;
int r, key;
unsigned offset, count;
unsigned long block;
/* Only do reads. */
if ((iop->io_request & ~OPTIONAL_IO) != DEV_READ)
return(iop->io_nbytes = EINVAL);
/* Finish any outstanding I/O. */
if ((r = s_finish()) != OK) return(r);
do {
/* Probe a device that isn't ready. */
if (!(sp->state & S_READY) && scsi_probe() != OK) return(EIO);
block = pos / sp->block_size;
if (block == s_buf_blk) {
/* Some of the requested bytes are in the buffer. */
offset = pos % sp->block_size;
count = sp->block_size - offset;
if (count > nbytes) count = nbytes;
phys_copy(tmp_phys + offset, user_phys, (phys_bytes) count);
pos += count;
user_phys += count;
nbytes -= count;
iop->io_nbytes -= count;
} else {
/* Read a block that contains (some of) the bytes wanted. */
rq->retry = 2;
do {
group1();
rq->ccb.opcode = CCB_INIT;
ccb_cmd1(rq).scsi_op = SCSI_READ1;
h2b32(ccb_cmd1(rq).lba, block);
h2b16(ccb_cmd1(rq).nblocks, 1);
key = scsi_command(tmp_phys, sp->block_size);
} while (key == SENSE_UNIT_ATT && --rq->retry > 0);
if (key != SENSE_NO_SENSE) return(iop->io_nbytes = EIO);
s_buf_blk = block; /* remember block in buffer */
}
} while (nbytes > 0);
return(OK);
}
/*===========================================================================*
* s_do_close *
*===========================================================================*/
PRIVATE int s_do_close(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;
sp->open_ct--;
/* Disks and such don't make trouble. */
if (sp->devtype != SCSI_DEVTAPE) return(OK);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -