⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aha_scsi.c

📁 MINIX2.0操作系统源码 MINIX2.0操作系统源码
💻 C
📖 第 1 页 / 共 5 页
字号:
 *===========================================================================*/
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 + -