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

📄 floppy.c

📁 MINIX2.0操作系统源码 MINIX2.0操作系统源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* This file contains the device dependent part of the driver for the Floppy
 * Disk Controller (FDC) using the NEC PD765 chip.
 *
 * The file contains one entry point:
 *
 *   floppy_task:	main entry when system is brought up
 *   floppy_stop:	stop all activity
 *
 *  Changes:
 *	27 Oct. 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz
 *	28 Nov. 1986 by Peter Kay: better resetting for 386
 *	06 Jan. 1988 by Al Crew: allow 1.44 MB diskettes
 *	        1989 by Bruce Evans: I/O vector to keep up with 1-1 interleave
 *	13 May  1991 by Don Chapman: renovated the errors loop.
 *		1991 by Bruce Evans: len[] / motors / reset / step rate / ...
 *	14 Feb  1992 by Andy Tanenbaum: check drive density on opens only
 *	27 Mar  1992 by Kees J. Bot: last details on density checking
 *	04 Apr  1992 by Kees J. Bot: device dependent/independent split
 */

#include "kernel.h"
#include "driver.h"
#include "drvlib.h"
#include <ibm/diskparm.h>

/* I/O Ports used by floppy disk task. */
#define DOR            0x3F2	/* motor drive control bits */
#define FDC_STATUS     0x3F4	/* floppy disk controller status register */
#define FDC_DATA       0x3F5	/* floppy disk controller data register */
#define FDC_RATE       0x3F7	/* transfer rate register */
#define DMA_ADDR       0x004	/* port for low 16 bits of DMA address */
#define DMA_TOP        0x081	/* port for top 4 bits of 20-bit DMA addr */
#define DMA_COUNT      0x005	/* port for DMA count (count =  bytes - 1) */
#define DMA_FLIPFLOP   0x00C	/* DMA byte pointer flip-flop */
#define DMA_MODE       0x00B	/* DMA mode port */
#define DMA_INIT       0x00A	/* DMA init port */
#define DMA_RESET_VAL   0x06

/* Status registers returned as result of operation. */
#define ST0             0x00	/* status register 0 */
#define ST1             0x01	/* status register 1 */
#define ST2             0x02	/* status register 2 */
#define ST3             0x00	/* status register 3 (return by DRIVE_SENSE) */
#define ST_CYL          0x03	/* slot where controller reports cylinder */
#define ST_HEAD         0x04	/* slot where controller reports head */
#define ST_SEC          0x05	/* slot where controller reports sector */
#define ST_PCN          0x01	/* slot where controller reports present cyl */

/* Fields within the I/O ports. */
/* Main status register. */
#define CTL_BUSY        0x10	/* bit is set when read or write in progress */
#define DIRECTION       0x40	/* bit is set when reading data reg is valid */
#define MASTER          0x80	/* bit is set when data reg can be accessed */

/* Digital output port (DOR). */
#define MOTOR_SHIFT        4	/* high 4 bits control the motors in DOR */
#define ENABLE_INT      0x0C	/* used for setting DOR port */

/* ST0. */
#define ST0_BITS        0xF8	/* check top 5 bits of seek status */
#define TRANS_ST0       0x00	/* top 5 bits of ST0 for READ/WRITE */
#define SEEK_ST0        0x20	/* top 5 bits of ST0 for SEEK */

/* ST1. */
#define BAD_SECTOR      0x05	/* if these bits are set in ST1, recalibrate */
#define WRITE_PROTECT   0x02	/* bit is set if diskette is write protected */

/* ST2. */
#define BAD_CYL         0x1F	/* if any of these bits are set, recalibrate */

/* ST3 (not used). */
#define ST3_FAULT       0x80	/* if this bit is set, drive is sick */
#define ST3_WR_PROTECT  0x40	/* set when diskette is write protected */
#define ST3_READY       0x20	/* set when drive is ready */

/* Floppy disk controller command bytes. */
#define FDC_SEEK        0x0F	/* command the drive to seek */
#define FDC_READ        0xE6	/* command the drive to read */
#define FDC_WRITE       0xC5	/* command the drive to write */
#define FDC_SENSE       0x08	/* command the controller to tell its status */
#define FDC_RECALIBRATE 0x07	/* command the drive to go to cyl 0 */
#define FDC_SPECIFY     0x03	/* command the drive to accept params */
#define FDC_READ_ID     0x4A	/* command the drive to read sector identity */
#define FDC_FORMAT      0x4D	/* command the drive to format a track */

/* DMA channel commands. */
#define DMA_READ        0x46	/* DMA read opcode */
#define DMA_WRITE       0x4A	/* DMA write opcode */

/* Parameters for the disk drive. */
#define HC_SIZE         2880	/* # sectors on largest legal disk (1.44MB) */
#define NR_HEADS        0x02	/* two heads (i.e., two tracks/cylinder) */
#define MAX_SECTORS	  18	/* largest # sectors per track */
#define DTL             0xFF	/* determines data length (sector size) */
#define SPEC2           0x02	/* second parameter to SPECIFY */
#define MOTOR_OFF       3*HZ	/* how long to wait before stopping motor */
#define WAKEUP		2*HZ	/* timeout on I/O, FDC won't quit. */

/* Error codes */
#define ERR_SEEK         (-1)	/* bad seek */
#define ERR_TRANSFER     (-2)	/* bad transfer */
#define ERR_STATUS       (-3)	/* something wrong when getting status */
#define ERR_READ_ID      (-4)	/* bad read id */
#define ERR_RECALIBRATE  (-5)	/* recalibrate didn't work properly */
#define ERR_DRIVE        (-6)	/* something wrong with a drive */
#define ERR_WR_PROTECT   (-7)	/* diskette is write protected */
#define ERR_TIMEOUT      (-8)	/* interrupt timeout */

/* No retries on some errors. */
#define err_no_retry(err)	((err) <= ERR_WR_PROTECT)

/* Encoding of drive type in minor device number. */
#define DEV_TYPE_BITS   0x7C	/* drive type + 1, if nonzero */
#define DEV_TYPE_SHIFT     2	/* right shift to normalize type bits */
#define FORMAT_DEV_BIT  0x80	/* bit in minor to turn write into format */

/* Miscellaneous. */
#define MAX_ERRORS         6	/* how often to try rd/wt before quitting */
#define MAX_RESULTS        7	/* max number of bytes controller returns */
#define NR_DRIVES          2	/* maximum number of drives */
#define DIVISOR          128	/* used for sector size encoding */
#define SECTOR_SIZE_CODE   2	/* code to say "512" to the controller */
#define TIMEOUT		 500	/* milliseconds waiting for FDC */
#define NT                 7	/* number of diskette/drive combinations */
#define UNCALIBRATED       0	/* drive needs to be calibrated at next use */
#define CALIBRATED         1	/* no calibration needed */
#define BASE_SECTOR        1	/* sectors are numbered starting at 1 */
#define NO_SECTOR          0	/* current sector unknown */
#define NO_CYL		 (-1)	/* current cylinder unknown, must seek */
#define NO_DENS		 100	/* current media unknown */
#define BSY_IDLE	   0	/* busy doing nothing */
#define BSY_IO		   1	/* doing I/O */
#define BSY_WAKEN	   2	/* got a wakeup call */

/* Variables. */
PRIVATE struct floppy {		/* main drive struct, one entry per drive */
  int fl_curcyl;		/* current cylinder */
  int fl_hardcyl;		/* hardware cylinder, as opposed to: */
  int fl_cylinder;		/* cylinder number addressed */
  int fl_sector;		/* sector addressed */
  int fl_head;			/* head number addressed */
  char fl_calibration;		/* CALIBRATED or UNCALIBRATED */
  char fl_density;		/* NO_DENS = ?, 0 = 360K; 1 = 360K/1.2M; etc.*/
  char fl_class;		/* bitmap for possible densities */
  struct device fl_geom;	/* Geometry of the drive */
  struct device fl_part[NR_PARTITIONS];  /* partition's base & size */
} floppy[NR_DRIVES], *f_fp;

/* Gather transfer data for each sector. */
PRIVATE struct trans {		/* precomputed transfer params */
  unsigned tr_count;		/* byte count */
  struct iorequest_s *tr_iop;	/* belongs to this I/O request */
  phys_bytes tr_phys;		/* user physical address */
  phys_bytes tr_dma;		/* DMA physical address */
} ftrans[MAX_SECTORS];

PRIVATE unsigned f_count;	/* this many bytes to transfer */
PRIVATE unsigned f_nexttrack;	/* don't do blocks above this */
PRIVATE int motor_status;	/* bitmap of current motor status */
PRIVATE int motor_goal;		/* bitmap of desired motor status */
PRIVATE int need_reset;		/* set to 1 when controller must be reset */
PRIVATE int d;			/* diskette/drive combination */
PRIVATE int f_drive;		/* selected drive */
PRIVATE int f_device;		/* selected minor device */
PRIVATE int f_opcode;		/* DEV_READ or DEV_WRITE */
PRIVATE int f_sectors;		/* sectors per track of the floppy */
PRIVATE int f_must;		/* must do part of the next track? */
PRIVATE int f_busy;		/* BSY_IDLE, BSY_IO, BSY_WAKEN */
PRIVATE int current_spec1;	/* latest spec1 sent to the controller */
PRIVATE struct device *f_dv;	/* device's base and size */
PRIVATE struct disk_parameter_s fmt_param; /* parameters for format */
PRIVATE char f_results[MAX_RESULTS];/* the controller can give lots of output */


/* Seven combinations of diskette/drive are supported.
 *
 * # Drive  diskette  Sectors  Tracks  Rotation Data-rate  Comment
 * 0  360K    360K      9       40     300 RPM  250 kbps   Standard PC DSDD
 * 1  1.2M    1.2M     15       80     360 RPM  500 kbps   AT disk in AT drive
 * 2  720K    360K      9       40     300 RPM  250 kbps   Quad density PC
 * 3  720K    720K      9       80     300 RPM  250 kbps   Toshiba, et al.
 * 4  1.2M    360K      9       40     360 RPM  300 kbps   PC disk in AT drive
 * 5  1.2M    720K      9       80     360 RPM  300 kbps   Toshiba in AT drive
 * 6  1.44M   1.44M    18	80     300 RPM  500 kbps   PS/2, et al.
 *
 * In addition, 720K diskettes can be read in 1.44MB drives, but that does
 * not need a different set of parameters.  This combination uses
 *
 * X  1.44M   720K	9	80     300 RPM  250 kbps   PS/2, et al.
 */
PRIVATE char gap[NT] =
	{0x2A, 0x1B, 0x2A, 0x2A, 0x23, 0x23, 0x1B}; /* gap size */
PRIVATE char rate[NT] =
	{0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x00}; /* 2=250,1=300,0=500 kbps*/
PRIVATE char nr_sectors[NT] =
	{9,    15,   9,    9,    9,    9,    18};   /* sectors/track */
PRIVATE int nr_blocks[NT] =
	{720,  2400, 720,  1440, 720,  1440, 2880}; /* sectors/diskette*/
PRIVATE char steps_per_cyl[NT] =
	{1,    1,    2,    1,    2,    1,     1};   /* 2 = dbl step */
PRIVATE char mtr_setup[NT] =
	{1*HZ/4,3*HZ/4,1*HZ/4,4*HZ/4,3*HZ/4,3*HZ/4,4*HZ/4}; /* in ticks */
PRIVATE char spec1[NT] =
	{0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF}; /* step rate, etc. */
PRIVATE char test_sector[NT] =
	{4*9,  14,   2*9,  4*9,  2*9,  4*9,  17};   /* to recognize it */

#define b(d)	(1 << (d))	/* bit for density d. */

/* The following table is used with the test_sector array to recognize a
 * drive/floppy combination.  The sector to test has been determined by
 * looking at the differences in gap size, sectors/track, and double stepping.
 * This means that types 0 and 3 can't be told apart, only the motor start
 * time differs.  If a read test succeeds then the drive is limited to the
 * set of densities it can support to avoid unnecessary tests in the future.
 */

PRIVATE struct test_order {
	char	t_density;	/* floppy/drive type */
	char	t_class;	/* limit drive to this class of densities */
} test_order[NT-1] = {
	{ 6,  b(3) | b(6) },		/* 1.44M  {720K, 1.44M} */
	{ 1,  b(1) | b(4) | b(5) },	/* 1.2M   {1.2M, 360K, 720K} */
	{ 3,  b(2) | b(3) | b(6) },	/* 720K   {360K, 720K, 1.44M} */
	{ 4,  b(1) | b(4) | b(5) },	/* 360K   {1.2M, 360K, 720K} */
	{ 5,  b(1) | b(4) | b(5) },	/* 720K   {1.2M, 360K, 720K} */
	{ 2,  b(2) | b(3) },		/* 360K   {360K, 720K} */
	/* Note that type 0 is missing, type 3 can read/write it too (alas). */
};

FORWARD _PROTOTYPE( struct device *f_prepare, (int device) );
FORWARD _PROTOTYPE( char *f_name, (void) );
FORWARD _PROTOTYPE( void f_cleanup, (void) );
FORWARD _PROTOTYPE( int f_schedule, (int proc_nr, struct iorequest_s *iop) );
FORWARD _PROTOTYPE( int f_finish, (void) );
FORWARD _PROTOTYPE( void defuse, (void) );
FORWARD _PROTOTYPE( void dma_setup, (struct trans *tp) );
FORWARD _PROTOTYPE( void start_motor, (void) );
FORWARD _PROTOTYPE( void stop_motor, (void) );
FORWARD _PROTOTYPE( int seek, (struct floppy *fp) );
FORWARD _PROTOTYPE( int f_transfer, (struct floppy *fp, struct trans *tp) );
FORWARD _PROTOTYPE( int fdc_results, (void) );
FORWARD _PROTOTYPE( int f_handler, (int irq) );
FORWARD _PROTOTYPE( void fdc_out, (int val) );
FORWARD _PROTOTYPE( int recalibrate, (struct floppy *fp) );
FORWARD _PROTOTYPE( void f_reset, (void) );
FORWARD _PROTOTYPE( void send_mess, (void) );
FORWARD _PROTOTYPE( int f_intr_wait, (void) );
FORWARD _PROTOTYPE( void f_timeout, (void) );
FORWARD _PROTOTYPE( int read_id, (struct floppy *fp) );
FORWARD _PROTOTYPE( int f_do_open, (struct driver *dp, message *m_ptr) );
FORWARD _PROTOTYPE( int test_read, (int density) );
FORWARD _PROTOTYPE( void f_geometry, (struct partition *entry));


/* Entry points to this driver. */
PRIVATE struct driver f_dtab = {
  f_name,	/* current device's name */
  f_do_open,	/* open or mount request, sense type of diskette */
  do_nop,	/* nothing on a close */
  do_diocntl,	/* get or set a partitions geometry */
  f_prepare,	/* prepare for I/O on a given minor device */
  f_schedule,	/* precompute cylinder, head, sector, etc. */
  f_finish,	/* do the I/O */
  f_cleanup,	/* cleanup before sending reply to user process */
  f_geometry	/* tell the geometry of the diskette */
};


/*===========================================================================*
 *				floppy_task				     *
 *===========================================================================*/
PUBLIC void floppy_task()
{
/* Initialize the floppy structure. */

  struct floppy *fp;

  for (fp = &floppy[0]; fp < &floppy[NR_DRIVES]; fp++) {
	fp->fl_curcyl = NO_CYL;
	fp->fl_density = NO_DENS;
	fp->fl_class = ~0;
  }

  put_irq_handler(FLOPPY_IRQ, f_handler);
  enable_irq(FLOPPY_IRQ);		/* ready for floppy interrupts */

  driver_task(&f_dtab);
}


/*===========================================================================*
 *				f_prepare				     *
 *===========================================================================*/
PRIVATE struct device *f_prepare(device)
int device;
{
/* Prepare for I/O on a device. */

  /* Leftover jobs after an I/O error must be removed */
  if (f_count > 0) defuse();

  f_device = device;
  f_drive = device & ~(DEV_TYPE_BITS | FORMAT_DEV_BIT);
  if (f_drive < 0 || f_drive >= NR_DRIVES) return(NIL_DEV);

  f_fp = &floppy[f_drive];
  f_dv = &f_fp->fl_geom;
  d = f_fp->fl_density;
  f_sectors = nr_sectors[d];

  f_must = TRUE;	/* the first transfers must be done */

  /* A partition? */
  if ((device &= DEV_TYPE_BITS) >= MINOR_fd0a)
	f_dv = &f_fp->fl_part[(device - MINOR_fd0a) >> DEV_TYPE_SHIFT];

  return f_dv;
}


/*===========================================================================*
 *				f_name					     *
 *===========================================================================*/
PRIVATE char *f_name()
{
/* Return a name for the current device. */
  static char name[] = "fd3";

  name[2] = '0' + f_drive;
  return name;
}


/*===========================================================================*
 *				f_cleanup				     *
 *===========================================================================*/
PRIVATE void f_cleanup()
{
  /* Start watchdog timer to turn all motors off in a few seconds.
   * There is a race here.  An old watchdog might bite before the
   * new delay is installed, and turn of the motors prematurely.
   * This cannot be solved simply by resetting motor_goal after
   * sending the message, because the new watchdog might bite
   * before motor_goal is reset.  Then the motors would stay on
   * until after the next floppy access.  This could be fixed with
   * extra code (call the clock task twice in some cases).  Or
   * stop_motor() could be replaced by send_mess(), and send a
   * STOP_MOTOR message to be accepted by the clock task.  This
   * would be slower but have the advantage that this comment could
   * be deleted!
   *
   * Since it is not likely and not serious for an old watchdog to
   * bite, accept that possibility for now.  A full solution to the
   * motor madness requires a lots of extra work anyway, such as
   * a separate timer for each motor, and smaller delays for motors
   * that have just been turned off or start faster than the spec.
   * (is there a motor-ready bit?).
   */
  motor_goal = 0;
  clock_mess(MOTOR_OFF, stop_motor);
}


/*===========================================================================*
 *				f_schedule				     *
 *===========================================================================*/
PRIVATE int f_schedule(proc_nr, iop)
int proc_nr;			/* process doing the request */
struct iorequest_s *iop;	/* pointer to read or write request */
{
  int r, opcode, spanning;
  unsigned long pos;
  unsigned block;	/* Seen any 32M floppies lately? */
  unsigned nbytes, count, dma_count;
  phys_bytes user_phys, dma_phys;
  struct trans *tp, *tp0;

  /* Ignore any alarm to turn motor off, now there is work to do. */
  motor_goal = motor_status;

  /* This many bytes to read/write */
  nbytes = iop->io_nbytes;
  if ((nbytes & SECTOR_MASK) != 0) return(iop->io_nbytes = EINVAL);

  /* From/to this position on disk */
  pos = iop->io_position;
  if ((pos & SECTOR_MASK) != 0) return(iop->io_nbytes = EINVAL);

  /* 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, write or format? */
  opcode = iop->io_request & ~OPTIONAL_IO;
  if (f_device & FORMAT_DEV_BIT) {
	if (opcode != DEV_WRITE) return(iop->io_nbytes = EIO);
	if (nbytes != BLOCK_SIZE) return(iop->io_nbytes = EINVAL);

	phys_copy(user_phys + SECTOR_SIZE, vir2phys(&fmt_param),
						(phys_bytes) sizeof fmt_param);

⌨️ 快捷键说明

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