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

📄 floppy.c

📁 MINIX2.0操作系统源码 MINIX2.0操作系统源码
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* Check that the number of sectors in the data is reasonable, to
	 * avoid division by 0.  Leave checking of other data to the FDC.
	 */
	if (fmt_param.sectors_per_cylinder == 0)
		return(iop->io_nbytes = EIO);

	/* Only the first sector of the parameters now needed. */
	iop->io_nbytes = nbytes = SECTOR_SIZE;
  }

  /* Which block on disk and how close to EOF? */
  if (pos >= f_dv->dv_size) return(OK);		/* At EOF */
  if (pos + nbytes > f_dv->dv_size) nbytes = f_dv->dv_size - pos;
  block = (f_dv->dv_base + pos) >> SECTOR_SHIFT;

  spanning = FALSE;	/* set if the block spans a track */

  /* While there are "unscheduled" bytes in the request: */
  do {
	count = nbytes;

	if (f_count > 0 && block >= f_nexttrack) {
		/* The new job leaves the track, finish all gathered jobs */
		if ((r = f_finish()) != OK) return(r);
		f_must = spanning;
	}

	if (f_count == 0) {
		/* This is the first job, compute cylinder and head */
		f_opcode = opcode;
		f_fp->fl_cylinder = block / (NR_HEADS * f_sectors);
		f_fp->fl_hardcyl = f_fp->fl_cylinder * steps_per_cyl[d];
		f_fp->fl_head = (block % (NR_HEADS * f_sectors)) / f_sectors;

		/* See where the next track starts, one is trouble enough */
		f_nexttrack = (f_fp->fl_cylinder * NR_HEADS
					+ f_fp->fl_head + 1) * f_sectors;
	}

	/* Don't do track spanning I/O. */
	if (block + (count >> SECTOR_SHIFT) > f_nexttrack)
		count = (f_nexttrack - block) << SECTOR_SHIFT;

	/* Memory chunk to DMA. */
	dma_phys = user_phys;
	dma_count = dma_bytes_left(dma_phys);

#if _WORD_SIZE > 2
	/* The DMA chip uses a 24 bit address, so don't DMA above 16MB. */
	if (dma_phys >= 0x1000000) dma_count = 0;
#endif
	if (dma_count < count) {
		/* Nearing a 64K boundary. */
		if (dma_count >= SECTOR_SIZE) {
			/* Can read a few sectors before hitting the
			 * boundary.
			 */
			count = dma_count & ~SECTOR_MASK;
		} else {
			/* Must use the special buffer for this. */
			count = SECTOR_SIZE;
			dma_phys = tmp_phys;
		}
	}

	/* Store the I/O parameters in the ftrans slots for the sectors to
	 * read.  The first slot specifies all sectors, the ones following
	 * it each specify one sector less.  This allows I/O to be started
	 * in the middle of a block.
	 */
	tp = tp0 = &ftrans[block % f_sectors];

	block += count >> SECTOR_SHIFT;
	nbytes -= count;
	f_count += count;
	if (!(iop->io_request & OPTIONAL_IO)) f_must = TRUE;

	do {
		tp->tr_count = count;
		tp->tr_iop = iop;
		tp->tr_phys = user_phys;
		tp->tr_dma = dma_phys;
		tp++;

		user_phys += SECTOR_SIZE;
		dma_phys += SECTOR_SIZE;
		count -= SECTOR_SIZE;
	} while (count > 0);

	spanning = TRUE;	/* the rest of the block may span a track */
  } while (nbytes > 0);

  return(OK);
}


/*===========================================================================*
 *				f_finish				     *
 *===========================================================================*/
PRIVATE int f_finish()
{
/* Carry out the I/O requests gathered in ftrans[].  */

  struct floppy *fp = f_fp;
  struct trans *tp;
  int r, errors;

  if (f_count == 0) return(OK);	/* Spurious finish. */

  /* If all the requests are optional then don't read from the next track.
   * (There may be enough buffers to read the next track, but doing so is
   * unwise.  It's no good to be greedy on a slow device.)
   */
  if (!f_must) {
	defuse();
	return(EAGAIN);
  }

  /* See if motor is running; if not, turn it on and wait */
  start_motor();

  /* Let read_id find out the next sector to read/write if it pays to do so.
   * Note that no read_id is done while formatting if there is one format
   * request per track as there should be.
   */
  fp->fl_sector = f_count >= (6 * SECTOR_SIZE) ? 0 : BASE_SECTOR;

  do {
	/* This loop allows a failed operation to be repeated. */
	errors = 0;
	for (;;) {
		/* First check to see if a reset is needed. */
		if (need_reset) f_reset();

		/* Set the stepping rate */
		if (current_spec1 != spec1[d]) {
			fdc_out(FDC_SPECIFY);
			current_spec1 = spec1[d];
			fdc_out(current_spec1);
			fdc_out(SPEC2);
		}

		/* Set the data rate */
		if (pc_at) out_byte(FDC_RATE, rate[d]);

		/* If we are going to a new cylinder, perform a seek. */
		r = seek(fp);

		if (fp->fl_sector == NO_SECTOR) {
			/* Don't retry read_id too often, we need tp soon */
			if (errors > 0) fp->fl_sector = BASE_SECTOR;

			/* Find out what the current sector is */
			if (r == OK) r = read_id(fp);
		}

		/* Look for the next job in ftrans[] */
		if (fp->fl_sector != NO_SECTOR) {
			for (;;) {
				if (fp->fl_sector >= BASE_SECTOR + f_sectors)
					fp->fl_sector = BASE_SECTOR;

				tp = &ftrans[fp->fl_sector - BASE_SECTOR];
				if (tp->tr_count > 0) break;
				fp->fl_sector++;
			}
			/* Do not transfer more than f_count bytes. */
			if (tp->tr_count > f_count) tp->tr_count = f_count;
		}

		if (r == OK && tp->tr_dma == tmp_phys
						&& f_opcode == DEV_WRITE) {
			/* Copy the bad user buffer to the DMA buffer. */
			phys_copy(tp->tr_phys, tp->tr_dma,
						(phys_bytes) tp->tr_count);
		}

		/* Set up the DMA chip and perform the transfer. */
		if (r == OK) {
			dma_setup(tp);
			r = f_transfer(fp, tp);
		}

		if (r == OK && tp->tr_dma == tmp_phys
						&& f_opcode == DEV_READ) {
			/* Copy the DMA buffer to the bad user buffer. */
			phys_copy(tp->tr_dma, tp->tr_phys,
						(phys_bytes) tp->tr_count);
		}

		if (r == OK) break;	/* if successful, exit loop */

		/* Don't retry if write protected or too many errors. */
		if (err_no_retry(r) || ++errors == MAX_ERRORS) {
			if (fp->fl_sector != 0) tp->tr_iop->io_nbytes = EIO;
			return(EIO);
		}

		/* Recalibrate if halfway, but bail out if optional I/O. */
		if (errors == MAX_ERRORS / 2) {
			fp->fl_calibration = UNCALIBRATED;
			if (tp->tr_iop->io_request & OPTIONAL_IO)
				return(tp->tr_iop->io_nbytes = EIO);
		}
	}
	f_count -= tp->tr_count;
	tp->tr_iop->io_nbytes -= tp->tr_count;
  } while (f_count > 0);

  /* Defuse the leftover partial jobs. */
  defuse();

  return(OK);
}


/*===========================================================================*
 *				defuse					     *
 *===========================================================================*/
PRIVATE void defuse()
{
/* Invalidate leftover requests in the transfer array. */

  struct trans *tp;

  for (tp = ftrans; tp < ftrans + MAX_SECTORS; tp++) tp->tr_count = 0;
  f_count = 0;
}


/*===========================================================================*
 *				dma_setup				     *
 *===========================================================================*/
PRIVATE void dma_setup(tp)
struct trans *tp;		/* pointer to the transfer struct */
{
/* The IBM PC can perform DMA operations by using the DMA chip.  To use it,
 * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
 * to be read from or written to, the byte count minus 1, and a read or write
 * opcode.  This routine sets up the DMA chip.  Note that the chip is not
 * capable of doing a DMA across a 64K boundary (e.g., you can't read a
 * 512-byte block starting at physical address 65520).
 */

  /* Set up the DMA registers.  (The comment on the reset is a bit strong,
   * it probably only resets the floppy channel.)
   */
  out_byte(DMA_INIT, DMA_RESET_VAL);    /* reset the dma controller */
  out_byte(DMA_FLIPFLOP, 0);		/* write anything to reset it */
  out_byte(DMA_MODE, f_opcode == DEV_WRITE ? DMA_WRITE : DMA_READ);
  out_byte(DMA_ADDR, (int) tp->tr_dma >>  0);
  out_byte(DMA_ADDR, (int) tp->tr_dma >>  8);
  out_byte(DMA_TOP, (int) (tp->tr_dma >> 16));
  out_byte(DMA_COUNT, (tp->tr_count - 1) >> 0);
  out_byte(DMA_COUNT, (tp->tr_count - 1) >> 8);
  out_byte(DMA_INIT, 2);	/* some sort of enable */
}


/*===========================================================================*
 *				start_motor				     *
 *===========================================================================*/
PRIVATE void start_motor()
{
/* Control of the floppy disk motors is a big pain.  If a motor is off, you
 * have to turn it on first, which takes 1/2 second.  You can't leave it on
 * all the time, since that would wear out the diskette.  However, if you turn
 * the motor off after each operation, the system performance will be awful.
 * The compromise used here is to leave it on for a few seconds after each
 * operation.  If a new operation is started in that interval, it need not be
 * turned on again.  If no new operation is started, a timer goes off and the
 * motor is turned off.  I/O port DOR has bits to control each of 4 drives.
 * The timer cannot go off while we are changing with the bits, since the
 * clock task cannot run while another (this) task is active, so there is no
 * need to lock().
 */

  int motor_bit, running;
  message mess;

  motor_bit = 1 << f_drive;		/* bit mask for this drive */
  running = motor_status & motor_bit;	/* nonzero if this motor is running */
  motor_goal = motor_status | motor_bit;/* want this drive running too */

  out_byte(DOR, (motor_goal << MOTOR_SHIFT) | ENABLE_INT | f_drive);
  motor_status = motor_goal;

  /* If the motor was already running, we don't have to wait for it. */
  if (running) return;			/* motor was already running */
  clock_mess(mtr_setup[d], send_mess);	/* motor was not running */
  receive(CLOCK, &mess);		/* wait for clock interrupt */
}


/*===========================================================================*
 *				stop_motor				     *
 *===========================================================================*/
PRIVATE void stop_motor()
{
/* This routine is called by the clock interrupt after several seconds have
 * elapsed with no floppy disk activity.  It checks to see if any drives are
 * supposed to be turned off, and if so, turns them off.
 */

  if (motor_goal != motor_status) {
	out_byte(DOR, (motor_goal << MOTOR_SHIFT) | ENABLE_INT);
	motor_status = motor_goal;
  }
}


/*===========================================================================*
 *				floppy_stop				     *
 *===========================================================================*/
PUBLIC void floppy_stop()
{
/* Stop all activity. */

  motor_goal = 0;
  stop_motor();
}


/*===========================================================================*
 *				seek					     *
 *===========================================================================*/
PRIVATE int seek(fp)
struct floppy *fp;		/* pointer to the drive struct */
{
/* Issue a SEEK command on the indicated drive unless the arm is already
 * positioned on the correct cylinder.
 */

  int r;
  message mess;

  /* Are we already on the correct cylinder? */
  if (fp->fl_calibration == UNCALIBRATED)
	if (recalibrate(fp) != OK) return(ERR_SEEK);
  if (fp->fl_curcyl == fp->fl_hardcyl) return(OK);

  /* No.  Wrong cylinder.  Issue a SEEK and wait for interrupt. */
  fdc_out(FDC_SEEK);
  fdc_out((fp->fl_head << 2) | f_drive);
  fdc_out(fp->fl_hardcyl);
  if (need_reset) return(ERR_SEEK);	/* if controller is sick, abort seek */
  if (f_intr_wait() != OK) return(ERR_TIMEOUT);

  /* Interrupt has been received.  Check drive status. */
  fdc_out(FDC_SENSE);		/* probe FDC to make it return status */
  r = fdc_results();		/* get controller status bytes */
  if (r != OK || (f_results[ST0] & ST0_BITS) != SEEK_ST0
				|| f_results[ST1] != fp->fl_hardcyl) {
	/* seek failed, may need a recalibrate */
	return(ERR_SEEK);
  }
  /* give head time to settle on a format, no retrying here! */
  if (f_device & FORMAT_DEV_BIT) {
	clock_mess(2, send_mess);
	receive(CLOCK, &mess);
  }
  fp->fl_curcyl = fp->fl_hardcyl;
  return(OK);
}


/*===========================================================================*
 *				f_transfer				     *
 *===========================================================================*/
PRIVATE int f_transfer(fp, tp)
struct floppy *fp;		/* pointer to the drive struct */
struct trans *tp;		/* pointer to the transfer struct */
{
/* The drive is now on the proper cylinder.  Read, write or format 1 block. */

  int r, s;

  /* Never attempt a transfer if the drive is uncalibrated or motor is off. */
  if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER);
  if ((motor_status & (1 << f_drive)) == 0) return(ERR_TRANSFER);

  /* The command is issued by outputting several bytes to the controller chip.
   */
  if (f_device & FORMAT_DEV_BIT) {
	fdc_out(FDC_FORMAT);
	fdc_out((fp->fl_head << 2) | f_drive);
	fdc_out(fmt_param.sector_size_code);
	fdc_out(fmt_param.sectors_per_cylinder);
	fdc_out(fmt_param.gap_length_for_format);
	fdc_out(fmt_param.fill_byte_for_format);
  } else {
	fdc_out(f_opcode == DEV_WRITE ? FDC_WRITE : FDC_READ);
	fdc_out((fp->fl_head << 2) | f_drive);
	fdc_out(fp->fl_cylinder);
	fdc_out(fp->fl_head);
	fdc_out(fp->fl_sector);
	fdc_out(SECTOR_SIZE_CODE);
	fdc_out(f_sectors);
	fdc_out(gap[d]);	/* sector gap */
	fdc_out(DTL);		/* data length */
  }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -