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

📄 floppy.c

📁 MINIX2.0操作系统源码 MINIX2.0操作系统源码
💻 C
📖 第 1 页 / 共 3 页
字号:
  /* Block, waiting for disk interrupt. */
  if (need_reset) return(ERR_TRANSFER);	/* if controller is sick, abort op */

  if (f_intr_wait() != OK) return(ERR_TIMEOUT);

  /* Get controller status and check for errors. */
  r = fdc_results();
  if (r != OK) return(r);

  if (f_results[ST1] & WRITE_PROTECT) {
	printf("%s: diskette is write protected.\n", f_name());
	return(ERR_WR_PROTECT);
  }

  if ((f_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_TRANSFER);
  if (f_results[ST1] | f_results[ST2]) return(ERR_TRANSFER);

  if (f_device & FORMAT_DEV_BIT) return(OK);

  /* Compare actual numbers of sectors transferred with expected number. */
  s =  (f_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * f_sectors;
  s += (f_results[ST_HEAD] - fp->fl_head) * f_sectors;
  s += (f_results[ST_SEC] - fp->fl_sector);
  if ((s << SECTOR_SHIFT) != tp->tr_count) return(ERR_TRANSFER);

  /* This sector is next for I/O: */
  fp->fl_sector = f_results[ST_SEC];
  return(OK);
}


/*==========================================================================*
 *				fdc_results				    *
 *==========================================================================*/
PRIVATE int fdc_results()
{
/* Extract results from the controller after an operation, then allow floppy
 * interrupts again.
 */

  int result_nr, status;
  struct milli_state ms;

  /* Extract bytes from FDC until it says it has no more.  The loop is
   * really an outer loop on result_nr and an inner loop on status.
   */
  result_nr = 0;
  milli_start(&ms);
  do {
	/* Reading one byte is almost a mirror of fdc_out() - the DIRECTION
	 * bit must be set instead of clear, but the CTL_BUSY bit destroys
	 * the perfection of the mirror.
	 */
	status = in_byte(FDC_STATUS) & (MASTER | DIRECTION | CTL_BUSY);
	if (status == (MASTER | DIRECTION | CTL_BUSY)) {
		if (result_nr >= MAX_RESULTS) break;	/* too many results */
		f_results[result_nr++] = in_byte(FDC_DATA);
		continue;
	}
	if (status == MASTER) {	/* all read */
		enable_irq(FLOPPY_IRQ);
		return(OK);	/* only good exit */
	}
  } while (milli_elapsed(&ms) < TIMEOUT);
  need_reset = TRUE;		/* controller chip must be reset */
  enable_irq(FLOPPY_IRQ);
  return(ERR_STATUS);
}


/*==========================================================================*
 *				f_handler				    *
 *==========================================================================*/
PRIVATE int f_handler(irq)
int irq;
{
/* FDC interrupt, send message to floppy task. */

  interrupt(FLOPPY);
  return 0;
}


/*===========================================================================*
 *				fdc_out					     *
 *===========================================================================*/
PRIVATE void fdc_out(val)
int val;		/* write this byte to floppy disk controller */
{
/* Output a byte to the controller.  This is not entirely trivial, since you
 * can only write to it when it is listening, and it decides when to listen.
 * If the controller refuses to listen, the FDC chip is given a hard reset.
 */

  struct milli_state ms;

  if (need_reset) return;	/* if controller is not listening, return */

  /* It may take several tries to get the FDC to accept a command. */
  milli_start(&ms);
  while ((in_byte(FDC_STATUS) & (MASTER | DIRECTION)) != (MASTER | 0)) {
	if (milli_elapsed(&ms) >= TIMEOUT) {
		/* Controller is not listening.  Hit it over the head. */
		need_reset = TRUE;
		return;
	}
  }
  out_byte(FDC_DATA, val);
}


/*===========================================================================*
 *				recalibrate				     *
 *===========================================================================*/
PRIVATE int recalibrate(fp)
struct floppy *fp;	/* pointer tot he drive struct */
{
/* The floppy disk controller has no way of determining its absolute arm
 * position (cylinder).  Instead, it steps the arm a cylinder at a time and
 * keeps track of where it thinks it is (in software).  However, after a
 * SEEK, the hardware reads information from the diskette telling where the
 * arm actually is.  If the arm is in the wrong place, a recalibration is done,
 * which forces the arm to cylinder 0.  This way the controller can get back
 * into sync with reality.
 */

  int r;

  /* Issue the RECALIBRATE command and wait for the interrupt. */
  start_motor();		/* can't recalibrate with motor off */
  fdc_out(FDC_RECALIBRATE);	/* tell drive to recalibrate itself */
  fdc_out(f_drive);		/* specify drive */
  if (need_reset) return(ERR_SEEK);	/* don't wait if controller is sick */
  if (f_intr_wait() != OK) return(ERR_TIMEOUT);

  /* Determine if the recalibration succeeded. */
  fdc_out(FDC_SENSE);		/* issue SENSE command to request results */
  r = fdc_results();		/* get results of the FDC_RECALIBRATE command*/
  fp->fl_curcyl = NO_CYL;	/* force a SEEK next time */
  if (r != OK ||		/* controller would not respond */
     (f_results[ST0] & ST0_BITS) != SEEK_ST0 || f_results[ST_PCN] != 0) {
	/* Recalibration failed.  FDC must be reset. */
	need_reset = TRUE;
	return(ERR_RECALIBRATE);
  } else {
	/* Recalibration succeeded. */
	fp->fl_calibration = CALIBRATED;
	return(OK);
  }
}


/*===========================================================================*
 *				f_reset					     *
 *===========================================================================*/
PRIVATE void f_reset()
{
/* Issue a reset to the controller.  This is done after any catastrophe,
 * like the controller refusing to respond.
 */

  int i;
  message mess;

  /* Disable interrupts and strobe reset bit low. */
  need_reset = FALSE;

  /* It is not clear why the next lock is needed.  Writing 0 to DOR causes
   * interrupt, while the PC documentation says turning bit 8 off disables
   * interrupts.  Without the lock:
   *   1) the interrupt handler sets the floppy mask bit in the 8259.
   *   2) writing ENABLE_INT to DOR causes the FDC to assert the interrupt
   *      line again, but the mask stops the cpu being interrupted.
   *   3) the sense interrupt clears the interrupt (not clear which one).
   * and for some reason the reset does not work.
   */
  lock();
  motor_status = 0;
  motor_goal = 0;
  out_byte(DOR, 0);		/* strobe reset bit low */
  out_byte(DOR, ENABLE_INT);	/* strobe it high again */
  unlock();
  receive(HARDWARE, &mess);	/* collect the RESET interrupt */

  /* The controller supports 4 drives and returns a result for each of them.
   * Collect all the results now.  The old version only collected the first
   * result.  This happens to work for 2 drives, but it doesn't work for 3
   * or more drives, at least with only drives 0 and 2 actually connected
   * (the controller generates an extra interrupt for the middle drive when
   * drive 2 is accessed and the driver panics).
   *
   * It would be better to keep collecting results until there are no more.
   * For this, fdc_results needs to return the number of results (instead
   * of OK) when it succeeds.
   */
  for (i = 0; i < 4; i++) {
	fdc_out(FDC_SENSE);	/* probe FDC to make it return status */
	(void) fdc_results();	/* flush controller */
  }
  for (i = 0; i < NR_DRIVES; i++)	/* clear each drive */
	floppy[i].fl_calibration = UNCALIBRATED;

  /* The current timing parameters must be specified again. */
  current_spec1 = 0;
}


/*===========================================================================*
 *				send_mess				     *
 *===========================================================================*/
PRIVATE void send_mess()
{
/* This routine is called when the clock task has timed out on motor startup.*/

  message mess;

  send(FLOPPY, &mess);
}


/*===========================================================================*
 *				f_intr_wait				     *
 *===========================================================================*/
PRIVATE int f_intr_wait()
{
/* Wait for an interrupt, but not forever.  The FDC may have all the time of
 * the world, but we humans do not.
 */
  message mess;

  f_busy = BSY_IO;
  clock_mess(WAKEUP, f_timeout);
  receive(HARDWARE, &mess);

  if (f_busy == BSY_WAKEN) {
	/* No interrupt from the FDC, this means that there is probably no
	 * floppy in the drive.  Get the FDC down to earth and return error.
	 */
	f_reset();
	return(ERR_TIMEOUT);
  }
  f_busy = BSY_IDLE;
  return(OK);
}


/*===========================================================================*
 *				f_timeout				     *
 *===========================================================================*/
PRIVATE void f_timeout()
{
/* When it takes too long for the FDC to get an interrupt (no floppy in the
 * drive), this routine is called.  It sets a flag and fakes a hardware
 * interrupt.
 */
  if (f_busy == BSY_IO) {
	f_busy = BSY_WAKEN;
	interrupt(FLOPPY);
  }
}


/*==========================================================================*
 *				read_id					    *
 *==========================================================================*/
PRIVATE int read_id(fp)
struct floppy *fp;	/* pointer to the drive struct */
{
/* Determine current cylinder and sector. */

  int result;

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

  /* The command is issued by outputting 2 bytes to the controller chip. */
  fdc_out(FDC_READ_ID);		/* issue the read id command */
  fdc_out( (f_fp->fl_head << 2) | f_drive);

  /* Block, waiting for disk interrupt. */
  if (need_reset) return(ERR_READ_ID);	/* if controller is sick, abort op */

  if (f_intr_wait() != OK) return(ERR_TIMEOUT);

  /* Get controller status and check for errors. */
  result = fdc_results();
  if (result != OK) return(result);

  if ((f_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_READ_ID);
  if (f_results[ST1] | f_results[ST2]) return(ERR_READ_ID);

  /* The next sector is next for I/O: */
  f_fp->fl_sector = f_results[ST_SEC] + 1;
  return(OK);
}


/*==========================================================================*
 *				f_do_open				    *
 *==========================================================================*/
PRIVATE int f_do_open(dp, m_ptr)
struct driver *dp;
message *m_ptr;			/* pointer to open message */
{
/* Handle an open on a floppy.  Determine diskette type if need be. */

  int dtype;
  struct test_order *top;

  /* Decode the message parameters. */
  if (f_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);

  dtype = f_device & DEV_TYPE_BITS;	/* get density from minor dev */
  if (dtype >= MINOR_fd0a) dtype = 0;
  if (dtype != 0) {
	/* All types except 0 indicate a specific drive/medium combination.*/
	dtype = (dtype >> DEV_TYPE_SHIFT) - 1;
	if (dtype >= NT) return(ENXIO);
	f_fp->fl_density = dtype;
	f_fp->fl_geom.dv_size = (long) nr_blocks[dtype] << SECTOR_SHIFT;
	return(OK);
  }
  if (f_device & FORMAT_DEV_BIT) return(EIO);	/* Can't format /dev/fdx */

  /* No need to test if the motor is still running. */
  if (motor_status & (1 << f_drive)) return(OK);

  /* The device opened is /dev/fdx.  Experimentally determine drive/medium.
   * First check fl_density.  If it is not NO_DENS, the drive has been used
   * before and the value of fl_density tells what was found last time. Try
   * that first.
   */
  if (f_fp->fl_density != NO_DENS && test_read(f_fp->fl_density) == OK)
	return(OK);

  /* Either drive type is unknown or a different diskette is now present.
   * Use test_order to try them one by one.
   */
  for (top = &test_order[0]; top < &test_order[NT-1]; top++) {
	dtype = top->t_density;

	/* Skip densities that have been proven to be impossible */
	if (!(f_fp->fl_class & (1 << dtype))) continue;

	if (test_read(dtype) == OK) {
		/* The test succeeded, use this knowledge to limit the
		 * drive class to match the density just read.
		 */
		f_fp->fl_class &= top->t_class;
		return(OK);
	}
	/* Test failed, wrong density or did it time out? */
	if (f_busy == BSY_WAKEN) break;
  }
  f_fp->fl_density = NO_DENS;
  return(EIO);			/* nothing worked */
}


/*==========================================================================*
 *				test_read				    *
 *==========================================================================*/
PRIVATE int test_read(density)
int density;
{
/* Try to read the highest numbered sector on cylinder 2.  Not all floppy
 * types have as many sectors per track, and trying cylinder 2 finds the
 * ones that need double stepping.
 */

  message m;
  int r, device;

  f_fp->fl_density = density;
  device = ((density + 1) << DEV_TYPE_SHIFT) + f_drive;
  f_fp->fl_geom.dv_size = (long) nr_blocks[density] << SECTOR_SHIFT;
  m.m_type = DEV_READ;
  m.DEVICE = device;
  m.PROC_NR = FLOPPY;
  m.COUNT = SECTOR_SIZE;
  m.POSITION = (long) test_sector[density] * SECTOR_SIZE;
  m.ADDRESS = (char *) tmp_buf;
  r = do_rdwt(&f_dtab, &m);
  if (r != SECTOR_SIZE) return(EIO);

  partition(&f_dtab, f_drive, P_FLOPPY);
  return(OK);
}


/*============================================================================*
 *				f_geometry				      *
 *============================================================================*/
PRIVATE void f_geometry(entry)
struct partition *entry;
{
  entry->cylinders = nr_blocks[d] / (NR_HEADS * f_sectors);
  entry->heads = NR_HEADS;
  entry->sectors = f_sectors;
}

⌨️ 快捷键说明

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