📄 floppy.c
字号:
/* 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 + -