[9] | 1 | /* This file contains the device dependent part of the driver for the Floppy
|
---|
| 2 | * Disk Controller (FDC) using the NEC PD765 chip.
|
---|
| 3 | *
|
---|
| 4 | * The file contains two entry points:
|
---|
| 5 | *
|
---|
| 6 | * floppy_task: main entry when system is brought up
|
---|
| 7 | *
|
---|
| 8 | * Changes:
|
---|
| 9 | * Sep 11, 2005 code cleanup (Andy Tanenbaum)
|
---|
| 10 | * Dec 01, 2004 floppy driver moved to user-space (Jorrit N. Herder)
|
---|
| 11 | * Sep 15, 2004 sync alarms/ local timer management (Jorrit N. Herder)
|
---|
| 12 | * Aug 12, 2003 null seek no interrupt fix (Mike Haertel)
|
---|
| 13 | * May 14, 2000 d-d/i rewrite (Kees J. Bot)
|
---|
| 14 | * Apr 04, 1992 device dependent/independent split (Kees J. Bot)
|
---|
| 15 | * Mar 27, 1992 last details on density checking (Kees J. Bot)
|
---|
| 16 | * Feb 14, 1992 check drive density on opens only (Andy Tanenbaum)
|
---|
| 17 | * 1991 len[] / motors / reset / step rate / ... (Bruce Evans)
|
---|
| 18 | * May 13, 1991 renovated the errors loop (Don Chapman)
|
---|
| 19 | * 1989 I/O vector to keep up with 1-1 interleave (Bruce Evans)
|
---|
| 20 | * Jan 06, 1988 allow 1.44 MB diskettes (Al Crew)
|
---|
| 21 | * Nov 28, 1986 better resetting for 386 (Peter Kay)
|
---|
| 22 | * Oct 27, 1986 fdc_results fixed for 8 MHz (Jakob Schripsema)
|
---|
| 23 | */
|
---|
| 24 |
|
---|
| 25 | #include "floppy.h"
|
---|
| 26 | #include <timers.h>
|
---|
| 27 | #include <ibm/diskparm.h>
|
---|
| 28 | #include <minix/sysutil.h>
|
---|
| 29 | #include <minix/syslib.h>
|
---|
| 30 |
|
---|
| 31 | /* I/O Ports used by floppy disk task. */
|
---|
| 32 | #define DOR 0x3F2 /* motor drive control bits */
|
---|
| 33 | #define FDC_STATUS 0x3F4 /* floppy disk controller status register */
|
---|
| 34 | #define FDC_DATA 0x3F5 /* floppy disk controller data register */
|
---|
| 35 | #define FDC_RATE 0x3F7 /* transfer rate register */
|
---|
| 36 | #define DMA_ADDR 0x004 /* port for low 16 bits of DMA address */
|
---|
| 37 | #define DMA_TOP 0x081 /* port for top 8 bits of 24-bit DMA addr */
|
---|
| 38 | #define DMA_COUNT 0x005 /* port for DMA count (count = bytes - 1) */
|
---|
| 39 | #define DMA_FLIPFLOP 0x00C /* DMA byte pointer flip-flop */
|
---|
| 40 | #define DMA_MODE 0x00B /* DMA mode port */
|
---|
| 41 | #define DMA_INIT 0x00A /* DMA init port */
|
---|
| 42 | #define DMA_RESET_VAL 0x006
|
---|
| 43 |
|
---|
| 44 | #define DMA_ADDR_MASK 0xFFFFFF /* mask to verify DMA address is 24-bit */
|
---|
| 45 |
|
---|
| 46 | /* Status registers returned as result of operation. */
|
---|
| 47 | #define ST0 0x00 /* status register 0 */
|
---|
| 48 | #define ST1 0x01 /* status register 1 */
|
---|
| 49 | #define ST2 0x02 /* status register 2 */
|
---|
| 50 | #define ST3 0x00 /* status register 3 (return by DRIVE_SENSE) */
|
---|
| 51 | #define ST_CYL 0x03 /* slot where controller reports cylinder */
|
---|
| 52 | #define ST_HEAD 0x04 /* slot where controller reports head */
|
---|
| 53 | #define ST_SEC 0x05 /* slot where controller reports sector */
|
---|
| 54 | #define ST_PCN 0x01 /* slot where controller reports present cyl */
|
---|
| 55 |
|
---|
| 56 | /* Fields within the I/O ports. */
|
---|
| 57 | /* Main status register. */
|
---|
| 58 | #define CTL_BUSY 0x10 /* bit is set when read or write in progress */
|
---|
| 59 | #define DIRECTION 0x40 /* bit is set when reading data reg is valid */
|
---|
| 60 | #define MASTER 0x80 /* bit is set when data reg can be accessed */
|
---|
| 61 |
|
---|
| 62 | /* Digital output port (DOR). */
|
---|
| 63 | #define MOTOR_SHIFT 4 /* high 4 bits control the motors in DOR */
|
---|
| 64 | #define ENABLE_INT 0x0C /* used for setting DOR port */
|
---|
| 65 |
|
---|
| 66 | /* ST0. */
|
---|
| 67 | #define ST0_BITS_TRANS 0xD8 /* check 4 bits of status */
|
---|
| 68 | #define TRANS_ST0 0x00 /* 4 bits of ST0 for READ/WRITE */
|
---|
| 69 | #define ST0_BITS_SEEK 0xF8 /* check top 5 bits of seek status */
|
---|
| 70 | #define SEEK_ST0 0x20 /* top 5 bits of ST0 for SEEK */
|
---|
| 71 |
|
---|
| 72 | /* ST1. */
|
---|
| 73 | #define BAD_SECTOR 0x05 /* if these bits are set in ST1, recalibrate */
|
---|
| 74 | #define WRITE_PROTECT 0x02 /* bit is set if diskette is write protected */
|
---|
| 75 |
|
---|
| 76 | /* ST2. */
|
---|
| 77 | #define BAD_CYL 0x1F /* if any of these bits are set, recalibrate */
|
---|
| 78 |
|
---|
| 79 | /* ST3 (not used). */
|
---|
| 80 | #define ST3_FAULT 0x80 /* if this bit is set, drive is sick */
|
---|
| 81 | #define ST3_WR_PROTECT 0x40 /* set when diskette is write protected */
|
---|
| 82 | #define ST3_READY 0x20 /* set when drive is ready */
|
---|
| 83 |
|
---|
| 84 | /* Floppy disk controller command bytes. */
|
---|
| 85 | #define FDC_SEEK 0x0F /* command the drive to seek */
|
---|
| 86 | #define FDC_READ 0xE6 /* command the drive to read */
|
---|
| 87 | #define FDC_WRITE 0xC5 /* command the drive to write */
|
---|
| 88 | #define FDC_SENSE 0x08 /* command the controller to tell its status */
|
---|
| 89 | #define FDC_RECALIBRATE 0x07 /* command the drive to go to cyl 0 */
|
---|
| 90 | #define FDC_SPECIFY 0x03 /* command the drive to accept params */
|
---|
| 91 | #define FDC_READ_ID 0x4A /* command the drive to read sector identity */
|
---|
| 92 | #define FDC_FORMAT 0x4D /* command the drive to format a track */
|
---|
| 93 |
|
---|
| 94 | /* DMA channel commands. */
|
---|
| 95 | #define DMA_READ 0x46 /* DMA read opcode */
|
---|
| 96 | #define DMA_WRITE 0x4A /* DMA write opcode */
|
---|
| 97 |
|
---|
| 98 | /* Parameters for the disk drive. */
|
---|
| 99 | #define HC_SIZE 2880 /* # sectors on largest legal disk (1.44MB) */
|
---|
| 100 | #define NR_HEADS 0x02 /* two heads (i.e., two tracks/cylinder) */
|
---|
| 101 | #define MAX_SECTORS 18 /* largest # sectors per track */
|
---|
| 102 | #define DTL 0xFF /* determines data length (sector size) */
|
---|
| 103 | #define SPEC2 0x02 /* second parameter to SPECIFY */
|
---|
| 104 | #define MOTOR_OFF (3*HZ) /* how long to wait before stopping motor */
|
---|
| 105 | #define WAKEUP (2*HZ) /* timeout on I/O, FDC won't quit. */
|
---|
| 106 |
|
---|
| 107 | /* Error codes */
|
---|
| 108 | #define ERR_SEEK (-1) /* bad seek */
|
---|
| 109 | #define ERR_TRANSFER (-2) /* bad transfer */
|
---|
| 110 | #define ERR_STATUS (-3) /* something wrong when getting status */
|
---|
| 111 | #define ERR_READ_ID (-4) /* bad read id */
|
---|
| 112 | #define ERR_RECALIBRATE (-5) /* recalibrate didn't work properly */
|
---|
| 113 | #define ERR_DRIVE (-6) /* something wrong with a drive */
|
---|
| 114 | #define ERR_WR_PROTECT (-7) /* diskette is write protected */
|
---|
| 115 | #define ERR_TIMEOUT (-8) /* interrupt timeout */
|
---|
| 116 |
|
---|
| 117 | /* No retries on some errors. */
|
---|
| 118 | #define err_no_retry(err) ((err) <= ERR_WR_PROTECT)
|
---|
| 119 |
|
---|
| 120 | /* Encoding of drive type in minor device number. */
|
---|
| 121 | #define DEV_TYPE_BITS 0x7C /* drive type + 1, if nonzero */
|
---|
| 122 | #define DEV_TYPE_SHIFT 2 /* right shift to normalize type bits */
|
---|
| 123 | #define FORMAT_DEV_BIT 0x80 /* bit in minor to turn write into format */
|
---|
| 124 |
|
---|
| 125 | /* Miscellaneous. */
|
---|
| 126 | #define MAX_ERRORS 6 /* how often to try rd/wt before quitting */
|
---|
| 127 | #define MAX_RESULTS 7 /* max number of bytes controller returns */
|
---|
| 128 | #define NR_DRIVES 2 /* maximum number of drives */
|
---|
| 129 | #define DIVISOR 128 /* used for sector size encoding */
|
---|
| 130 | #define SECTOR_SIZE_CODE 2 /* code to say "512" to the controller */
|
---|
| 131 | #define TIMEOUT_MICROS 500000L /* microseconds waiting for FDC */
|
---|
| 132 | #define TIMEOUT_TICKS 30 /* ticks waiting for FDC */
|
---|
| 133 | #define NT 7 /* number of diskette/drive combinations */
|
---|
| 134 | #define UNCALIBRATED 0 /* drive needs to be calibrated at next use */
|
---|
| 135 | #define CALIBRATED 1 /* no calibration needed */
|
---|
| 136 | #define BASE_SECTOR 1 /* sectors are numbered starting at 1 */
|
---|
| 137 | #define NO_SECTOR (-1) /* current sector unknown */
|
---|
| 138 | #define NO_CYL (-1) /* current cylinder unknown, must seek */
|
---|
| 139 | #define NO_DENS 100 /* current media unknown */
|
---|
| 140 | #define BSY_IDLE 0 /* busy doing nothing */
|
---|
| 141 | #define BSY_IO 1 /* busy doing I/O */
|
---|
| 142 | #define BSY_WAKEN 2 /* got a wakeup call */
|
---|
| 143 |
|
---|
| 144 | /* Seven combinations of diskette/drive are supported.
|
---|
| 145 | *
|
---|
| 146 | * # Diskette Drive Sectors Tracks Rotation Data-rate Comment
|
---|
| 147 | * 0 360K 360K 9 40 300 RPM 250 kbps Standard PC DSDD
|
---|
| 148 | * 1 1.2M 1.2M 15 80 360 RPM 500 kbps AT disk in AT drive
|
---|
| 149 | * 2 360K 720K 9 40 300 RPM 250 kbps Quad density PC
|
---|
| 150 | * 3 720K 720K 9 80 300 RPM 250 kbps Toshiba, et al.
|
---|
| 151 | * 4 360K 1.2M 9 40 360 RPM 300 kbps PC disk in AT drive
|
---|
| 152 | * 5 720K 1.2M 9 80 360 RPM 300 kbps Toshiba in AT drive
|
---|
| 153 | * 6 1.44M 1.44M 18 80 300 RPM 500 kbps PS/2, et al.
|
---|
| 154 | *
|
---|
| 155 | * In addition, 720K diskettes can be read in 1.44MB drives, but that does
|
---|
| 156 | * not need a different set of parameters. This combination uses
|
---|
| 157 | *
|
---|
| 158 | * 3 720K 1.44M 9 80 300 RPM 250 kbps PS/2, et al.
|
---|
| 159 | */
|
---|
| 160 | PRIVATE struct density {
|
---|
| 161 | u8_t secpt; /* sectors per track */
|
---|
| 162 | u8_t cyls; /* tracks per side */
|
---|
| 163 | u8_t steps; /* steps per cylinder (2 = double step) */
|
---|
| 164 | u8_t test; /* sector to try for density test */
|
---|
| 165 | u8_t rate; /* data rate (2=250, 1=300, 0=500 kbps) */
|
---|
| 166 | u8_t start; /* motor start (clock ticks) */
|
---|
| 167 | u8_t gap; /* gap size */
|
---|
| 168 | u8_t spec1; /* first specify byte (SRT/HUT) */
|
---|
| 169 | } fdensity[NT] = {
|
---|
| 170 | { 9, 40, 1, 4*9, 2, 4*HZ/8, 0x2A, 0xDF }, /* 360K / 360K */
|
---|
| 171 | { 15, 80, 1, 14, 0, 4*HZ/8, 0x1B, 0xDF }, /* 1.2M / 1.2M */
|
---|
| 172 | { 9, 40, 2, 2*9, 2, 4*HZ/8, 0x2A, 0xDF }, /* 360K / 720K */
|
---|
| 173 | { 9, 80, 1, 4*9, 2, 6*HZ/8, 0x2A, 0xDF }, /* 720K / 720K */
|
---|
| 174 | { 9, 40, 2, 2*9, 1, 4*HZ/8, 0x23, 0xDF }, /* 360K / 1.2M */
|
---|
| 175 | { 9, 80, 1, 4*9, 1, 4*HZ/8, 0x23, 0xDF }, /* 720K / 1.2M */
|
---|
| 176 | { 18, 80, 1, 17, 0, 6*HZ/8, 0x1B, 0xCF }, /* 1.44M / 1.44M */
|
---|
| 177 | };
|
---|
| 178 |
|
---|
| 179 | /* The following table is used with the test_sector array to recognize a
|
---|
| 180 | * drive/floppy combination. The sector to test has been determined by
|
---|
| 181 | * looking at the differences in gap size, sectors/track, and double stepping.
|
---|
| 182 | * This means that types 0 and 3 can't be told apart, only the motor start
|
---|
| 183 | * time differs. If a read test succeeds then the drive is limited to the
|
---|
| 184 | * set of densities it can support to avoid unnecessary tests in the future.
|
---|
| 185 | */
|
---|
| 186 |
|
---|
| 187 | #define b(d) (1 << (d)) /* bit for density d. */
|
---|
| 188 |
|
---|
| 189 | PRIVATE struct test_order {
|
---|
| 190 | u8_t t_density; /* floppy/drive type */
|
---|
| 191 | u8_t t_class; /* limit drive to this class of densities */
|
---|
| 192 | } test_order[NT-1] = {
|
---|
| 193 | { 6, b(3) | b(6) }, /* 1.44M {720K, 1.44M} */
|
---|
| 194 | { 1, b(1) | b(4) | b(5) }, /* 1.2M {1.2M, 360K, 720K} */
|
---|
| 195 | { 3, b(2) | b(3) | b(6) }, /* 720K {360K, 720K, 1.44M} */
|
---|
| 196 | { 4, b(1) | b(4) | b(5) }, /* 360K {1.2M, 360K, 720K} */
|
---|
| 197 | { 5, b(1) | b(4) | b(5) }, /* 720K {1.2M, 360K, 720K} */
|
---|
| 198 | { 2, b(2) | b(3) }, /* 360K {360K, 720K} */
|
---|
| 199 | /* Note that type 0 is missing, type 3 can read/write it too, which is
|
---|
| 200 | * why the type 3 parameters have been pessimized to be like type 0.
|
---|
| 201 | */
|
---|
| 202 | };
|
---|
| 203 |
|
---|
| 204 | /* Variables. */
|
---|
| 205 | PRIVATE struct floppy { /* main drive struct, one entry per drive */
|
---|
| 206 | unsigned fl_curcyl; /* current cylinder */
|
---|
| 207 | unsigned fl_hardcyl; /* hardware cylinder, as opposed to: */
|
---|
| 208 | unsigned fl_cylinder; /* cylinder number addressed */
|
---|
| 209 | unsigned fl_sector; /* sector addressed */
|
---|
| 210 | unsigned fl_head; /* head number addressed */
|
---|
| 211 | char fl_calibration; /* CALIBRATED or UNCALIBRATED */
|
---|
| 212 | u8_t fl_density; /* NO_DENS = ?, 0 = 360K; 1 = 360K/1.2M; etc.*/
|
---|
| 213 | u8_t fl_class; /* bitmap for possible densities */
|
---|
| 214 | timer_t fl_tmr_stop; /* timer to stop motor */
|
---|
| 215 | struct device fl_geom; /* Geometry of the drive */
|
---|
| 216 | struct device fl_part[NR_PARTITIONS]; /* partition's base & size */
|
---|
| 217 | } floppy[NR_DRIVES];
|
---|
| 218 |
|
---|
| 219 | PRIVATE int irq_hook_id; /* id of irq hook at the kernel */
|
---|
| 220 | PRIVATE int motor_status; /* bitmap of current motor status */
|
---|
| 221 | PRIVATE int need_reset; /* set to 1 when controller must be reset */
|
---|
| 222 | PRIVATE unsigned f_drive; /* selected drive */
|
---|
| 223 | PRIVATE unsigned f_device; /* selected minor device */
|
---|
| 224 | PRIVATE struct floppy *f_fp; /* current drive */
|
---|
| 225 | PRIVATE struct density *f_dp; /* current density parameters */
|
---|
| 226 | PRIVATE struct density *prev_dp;/* previous density parameters */
|
---|
| 227 | PRIVATE unsigned f_sectors; /* equal to f_dp->secpt (needed a lot) */
|
---|
| 228 | PRIVATE u16_t f_busy; /* BSY_IDLE, BSY_IO, BSY_WAKEN */
|
---|
| 229 | PRIVATE struct device *f_dv; /* device's base and size */
|
---|
| 230 | PRIVATE struct disk_parameter_s fmt_param; /* parameters for format */
|
---|
| 231 | PRIVATE u8_t f_results[MAX_RESULTS];/* the controller can give lots of output */
|
---|
| 232 |
|
---|
| 233 | /* The floppy uses various timers. These are managed by the floppy driver
|
---|
| 234 | * itself, because only a single synchronous alarm is available per process.
|
---|
| 235 | * Besides the 'f_tmr_timeout' timer below, the floppy structure for each
|
---|
| 236 | * floppy disk drive contains a 'fl_tmr_stop' timer.
|
---|
| 237 | */
|
---|
| 238 | PRIVATE timer_t f_tmr_timeout; /* timer for various timeouts */
|
---|
| 239 | PRIVATE timer_t *f_timers; /* queue of floppy timers */
|
---|
| 240 | PRIVATE clock_t f_next_timeout; /* the next timeout time */
|
---|
| 241 | FORWARD _PROTOTYPE( void f_expire_tmrs, (struct driver *dp, message *m_ptr) );
|
---|
| 242 | FORWARD _PROTOTYPE( void f_set_timer, (timer_t *tp, clock_t delta,
|
---|
| 243 | tmr_func_t watchdog) );
|
---|
| 244 | FORWARD _PROTOTYPE( void stop_motor, (timer_t *tp) );
|
---|
| 245 | FORWARD _PROTOTYPE( void f_timeout, (timer_t *tp) );
|
---|
| 246 |
|
---|
| 247 | FORWARD _PROTOTYPE( struct device *f_prepare, (int device) );
|
---|
| 248 | FORWARD _PROTOTYPE( char *f_name, (void) );
|
---|
| 249 | FORWARD _PROTOTYPE( void f_cleanup, (void) );
|
---|
| 250 | FORWARD _PROTOTYPE( int f_transfer, (int proc_nr, int opcode, off_t position,
|
---|
| 251 | iovec_t *iov, unsigned nr_req) );
|
---|
| 252 | FORWARD _PROTOTYPE( int dma_setup, (int opcode) );
|
---|
| 253 | FORWARD _PROTOTYPE( void start_motor, (void) );
|
---|
| 254 | FORWARD _PROTOTYPE( int seek, (void) );
|
---|
| 255 | FORWARD _PROTOTYPE( int fdc_transfer, (int opcode) );
|
---|
| 256 | FORWARD _PROTOTYPE( int fdc_results, (void) );
|
---|
| 257 | FORWARD _PROTOTYPE( int fdc_command, (u8_t *cmd, int len) );
|
---|
| 258 | FORWARD _PROTOTYPE( void fdc_out, (int val) );
|
---|
| 259 | FORWARD _PROTOTYPE( int recalibrate, (void) );
|
---|
| 260 | FORWARD _PROTOTYPE( void f_reset, (void) );
|
---|
| 261 | FORWARD _PROTOTYPE( int f_intr_wait, (void) );
|
---|
| 262 | FORWARD _PROTOTYPE( int read_id, (void) );
|
---|
| 263 | FORWARD _PROTOTYPE( int f_do_open, (struct driver *dp, message *m_ptr) );
|
---|
| 264 | FORWARD _PROTOTYPE( void floppy_stop, (struct driver *dp, message *m_ptr));
|
---|
| 265 | FORWARD _PROTOTYPE( int test_read, (int density) );
|
---|
| 266 | FORWARD _PROTOTYPE( void f_geometry, (struct partition *entry) );
|
---|
| 267 |
|
---|
| 268 | /* Entry points to this driver. */
|
---|
| 269 | PRIVATE struct driver f_dtab = {
|
---|
| 270 | f_name, /* current device's name */
|
---|
| 271 | f_do_open, /* open or mount request, sense type of diskette */
|
---|
| 272 | do_nop, /* nothing on a close */
|
---|
| 273 | do_diocntl, /* get or set a partitions geometry */
|
---|
| 274 | f_prepare, /* prepare for I/O on a given minor device */
|
---|
| 275 | f_transfer, /* do the I/O */
|
---|
| 276 | f_cleanup, /* cleanup before sending reply to user process */
|
---|
| 277 | f_geometry, /* tell the geometry of the diskette */
|
---|
| 278 | floppy_stop, /* floppy cleanup on shutdown */
|
---|
| 279 | f_expire_tmrs,/* expire all alarm timers */
|
---|
| 280 | nop_cancel,
|
---|
| 281 | nop_select,
|
---|
| 282 | NULL,
|
---|
| 283 | NULL
|
---|
| 284 | };
|
---|
| 285 |
|
---|
| 286 | /*===========================================================================*
|
---|
| 287 | * floppy_task *
|
---|
| 288 | *===========================================================================*/
|
---|
| 289 | PUBLIC void main()
|
---|
| 290 | {
|
---|
| 291 | /* Initialize the floppy structure and the timers. */
|
---|
| 292 |
|
---|
| 293 | struct floppy *fp;
|
---|
| 294 | int s;
|
---|
| 295 |
|
---|
| 296 | f_next_timeout = TMR_NEVER;
|
---|
| 297 | tmr_inittimer(&f_tmr_timeout);
|
---|
| 298 |
|
---|
| 299 | for (fp = &floppy[0]; fp < &floppy[NR_DRIVES]; fp++) {
|
---|
| 300 | fp->fl_curcyl = NO_CYL;
|
---|
| 301 | fp->fl_density = NO_DENS;
|
---|
| 302 | fp->fl_class = ~0;
|
---|
| 303 | tmr_inittimer(&fp->fl_tmr_stop);
|
---|
| 304 | }
|
---|
| 305 |
|
---|
| 306 | /* Set IRQ policy, only request notifications, do not automatically
|
---|
| 307 | * reenable interrupts. ID return on interrupt is the IRQ line number.
|
---|
| 308 | */
|
---|
| 309 | irq_hook_id = FLOPPY_IRQ;
|
---|
| 310 | if ((s=sys_irqsetpolicy(FLOPPY_IRQ, 0, &irq_hook_id )) != OK)
|
---|
| 311 | panic("FLOPPY", "Couldn't set IRQ policy", s);
|
---|
| 312 | if ((s=sys_irqenable(&irq_hook_id)) != OK)
|
---|
| 313 | panic("FLOPPY", "Couldn't enable IRQs", s);
|
---|
| 314 |
|
---|
| 315 | driver_task(&f_dtab);
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 | /*===========================================================================*
|
---|
| 319 | * f_expire_tmrs *
|
---|
| 320 | *===========================================================================*/
|
---|
| 321 | PRIVATE void f_expire_tmrs(struct driver *dp, message *m_ptr)
|
---|
| 322 | {
|
---|
| 323 | /* A synchronous alarm message was received. Check if there are any expired
|
---|
| 324 | * timers. Possibly reschedule the next alarm.
|
---|
| 325 | */
|
---|
| 326 | clock_t now; /* current time */
|
---|
| 327 | timer_t *tp;
|
---|
| 328 | int s;
|
---|
| 329 |
|
---|
| 330 | /* Get the current time to compare the timers against. */
|
---|
| 331 | if ((s=getuptime(&now)) != OK)
|
---|
| 332 | panic("FLOPPY","Couldn't get uptime from clock.", s);
|
---|
| 333 |
|
---|
| 334 | /* Scan the timers queue for expired timers. Dispatch the watchdog function
|
---|
| 335 | * for each expired timers. FLOPPY watchdog functions are f_tmr_timeout()
|
---|
| 336 | * and stop_motor(). Possibly a new alarm call must be scheduled.
|
---|
| 337 | */
|
---|
| 338 | tmrs_exptimers(&f_timers, now, NULL);
|
---|
| 339 | if (f_timers == NULL) {
|
---|
| 340 | f_next_timeout = TMR_NEVER;
|
---|
| 341 | } else { /* set new sync alarm */
|
---|
| 342 | f_next_timeout = f_timers->tmr_exp_time;
|
---|
| 343 | if ((s=sys_setalarm(f_next_timeout, 1)) != OK)
|
---|
| 344 | panic("FLOPPY","Couldn't set synchronous alarm.", s);
|
---|
| 345 | }
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 | /*===========================================================================*
|
---|
| 349 | * f_set_timer *
|
---|
| 350 | *===========================================================================*/
|
---|
| 351 | PRIVATE void f_set_timer(tp, delta, watchdog)
|
---|
| 352 | timer_t *tp; /* timer to be set */
|
---|
| 353 | clock_t delta; /* in how many ticks */
|
---|
| 354 | tmr_func_t watchdog; /* watchdog function to be called */
|
---|
| 355 | {
|
---|
| 356 | clock_t now; /* current time */
|
---|
| 357 | int s;
|
---|
| 358 |
|
---|
| 359 | /* Get the current time. */
|
---|
| 360 | if ((s=getuptime(&now)) != OK)
|
---|
| 361 | panic("FLOPPY","Couldn't get uptime from clock.", s);
|
---|
| 362 |
|
---|
| 363 | /* Add the timer to the local timer queue. */
|
---|
| 364 | tmrs_settimer(&f_timers, tp, now + delta, watchdog, NULL);
|
---|
| 365 |
|
---|
| 366 | /* Possibly reschedule an alarm call. This happens when the front of the
|
---|
| 367 | * timers queue was reinserted at another position, i.e., when a timer was
|
---|
| 368 | * reset, or when a new timer was added in front.
|
---|
| 369 | */
|
---|
| 370 | if (f_timers->tmr_exp_time != f_next_timeout) {
|
---|
| 371 | f_next_timeout = f_timers->tmr_exp_time;
|
---|
| 372 | if ((s=sys_setalarm(f_next_timeout, 1)) != OK)
|
---|
| 373 | panic("FLOPPY","Couldn't set synchronous alarm.", s);
|
---|
| 374 | }
|
---|
| 375 | }
|
---|
| 376 |
|
---|
| 377 | /*===========================================================================*
|
---|
| 378 | * f_prepare *
|
---|
| 379 | *===========================================================================*/
|
---|
| 380 | PRIVATE struct device *f_prepare(device)
|
---|
| 381 | int device;
|
---|
| 382 | {
|
---|
| 383 | /* Prepare for I/O on a device. */
|
---|
| 384 |
|
---|
| 385 | f_device = device;
|
---|
| 386 | f_drive = device & ~(DEV_TYPE_BITS | FORMAT_DEV_BIT);
|
---|
| 387 | if (f_drive < 0 || f_drive >= NR_DRIVES) return(NIL_DEV);
|
---|
| 388 |
|
---|
| 389 | f_fp = &floppy[f_drive];
|
---|
| 390 | f_dv = &f_fp->fl_geom;
|
---|
| 391 | if (f_fp->fl_density < NT) {
|
---|
| 392 | f_dp = &fdensity[f_fp->fl_density];
|
---|
| 393 | f_sectors = f_dp->secpt;
|
---|
| 394 | f_fp->fl_geom.dv_size = mul64u((long) (NR_HEADS * f_sectors
|
---|
| 395 | * f_dp->cyls), SECTOR_SIZE);
|
---|
| 396 | }
|
---|
| 397 |
|
---|
| 398 | /* A partition? */
|
---|
| 399 | if ((device &= DEV_TYPE_BITS) >= MINOR_fd0p0)
|
---|
| 400 | f_dv = &f_fp->fl_part[(device - MINOR_fd0p0) >> DEV_TYPE_SHIFT];
|
---|
| 401 |
|
---|
| 402 | return f_dv;
|
---|
| 403 | }
|
---|
| 404 |
|
---|
| 405 | /*===========================================================================*
|
---|
| 406 | * f_name *
|
---|
| 407 | *===========================================================================*/
|
---|
| 408 | PRIVATE char *f_name()
|
---|
| 409 | {
|
---|
| 410 | /* Return a name for the current device. */
|
---|
| 411 | static char name[] = "fd0";
|
---|
| 412 |
|
---|
| 413 | name[2] = '0' + f_drive;
|
---|
| 414 | return name;
|
---|
| 415 | }
|
---|
| 416 |
|
---|
| 417 | /*===========================================================================*
|
---|
| 418 | * f_cleanup *
|
---|
| 419 | *===========================================================================*/
|
---|
| 420 | PRIVATE void f_cleanup()
|
---|
| 421 | {
|
---|
| 422 | /* Start a timer to turn the motor off in a few seconds. */
|
---|
| 423 | tmr_arg(&f_fp->fl_tmr_stop)->ta_int = f_drive;
|
---|
| 424 | f_set_timer(&f_fp->fl_tmr_stop, MOTOR_OFF, stop_motor);
|
---|
| 425 |
|
---|
| 426 | /* Exiting the floppy driver, so forget where we are. */
|
---|
| 427 | f_fp->fl_sector = NO_SECTOR;
|
---|
| 428 | }
|
---|
| 429 |
|
---|
| 430 | /*===========================================================================*
|
---|
| 431 | * f_transfer *
|
---|
| 432 | *===========================================================================*/
|
---|
| 433 | PRIVATE int f_transfer(proc_nr, opcode, position, iov, nr_req)
|
---|
| 434 | int proc_nr; /* process doing the request */
|
---|
| 435 | int opcode; /* DEV_GATHER or DEV_SCATTER */
|
---|
| 436 | off_t position; /* offset on device to read or write */
|
---|
| 437 | iovec_t *iov; /* pointer to read or write request vector */
|
---|
| 438 | unsigned nr_req; /* length of request vector */
|
---|
| 439 | {
|
---|
| 440 | struct floppy *fp = f_fp;
|
---|
| 441 | iovec_t *iop, *iov_end = iov + nr_req;
|
---|
| 442 | int s, r, errors;
|
---|
| 443 | unsigned block; /* Seen any 32M floppies lately? */
|
---|
| 444 | unsigned nbytes, count, chunk, sector;
|
---|
| 445 | unsigned long dv_size = cv64ul(f_dv->dv_size);
|
---|
| 446 | vir_bytes user_addr;
|
---|
| 447 | vir_bytes uaddrs[MAX_SECTORS], *up;
|
---|
| 448 | u8_t cmd[3];
|
---|
| 449 |
|
---|
| 450 | /* Check disk address. */
|
---|
| 451 | if ((position & SECTOR_MASK) != 0) return(EINVAL);
|
---|
| 452 |
|
---|
| 453 | errors = 0;
|
---|
| 454 | while (nr_req > 0) {
|
---|
| 455 | /* How many bytes to transfer? */
|
---|
| 456 | nbytes = 0;
|
---|
| 457 | for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size;
|
---|
| 458 |
|
---|
| 459 | /* Which block on disk and how close to EOF? */
|
---|
| 460 | if (position >= dv_size) return(OK); /* At EOF */
|
---|
| 461 | if (position + nbytes > dv_size) nbytes = dv_size - position;
|
---|
| 462 | block = div64u(add64ul(f_dv->dv_base, position), SECTOR_SIZE);
|
---|
| 463 |
|
---|
| 464 | if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
|
---|
| 465 |
|
---|
| 466 | /* Using a formatting device? */
|
---|
| 467 | if (f_device & FORMAT_DEV_BIT) {
|
---|
| 468 | if (opcode != DEV_SCATTER) return(EIO);
|
---|
| 469 | if (iov->iov_size < SECTOR_SIZE + sizeof(fmt_param))
|
---|
| 470 | return(EINVAL);
|
---|
| 471 |
|
---|
| 472 | if ((s=sys_datacopy(proc_nr, iov->iov_addr + SECTOR_SIZE,
|
---|
| 473 | SELF, (vir_bytes) &fmt_param,
|
---|
| 474 | (phys_bytes) sizeof(fmt_param))) != OK)
|
---|
| 475 | panic("FLOPPY", "Sys_vircopy failed", s);
|
---|
| 476 |
|
---|
| 477 | /* Check that the number of sectors in the data is reasonable,
|
---|
| 478 | * to avoid division by 0. Leave checking of other data to
|
---|
| 479 | * the FDC.
|
---|
| 480 | */
|
---|
| 481 | if (fmt_param.sectors_per_cylinder == 0) return(EIO);
|
---|
| 482 |
|
---|
| 483 | /* Only the first sector of the parameters now needed. */
|
---|
| 484 | iov->iov_size = nbytes = SECTOR_SIZE;
|
---|
| 485 | }
|
---|
| 486 |
|
---|
| 487 | /* Only try one sector if there were errors. */
|
---|
| 488 | if (errors > 0) nbytes = SECTOR_SIZE;
|
---|
| 489 |
|
---|
| 490 | /* Compute cylinder and head of the track to access. */
|
---|
| 491 | fp->fl_cylinder = block / (NR_HEADS * f_sectors);
|
---|
| 492 | fp->fl_hardcyl = fp->fl_cylinder * f_dp->steps;
|
---|
| 493 | fp->fl_head = (block % (NR_HEADS * f_sectors)) / f_sectors;
|
---|
| 494 |
|
---|
| 495 | /* For each sector on this track compute the user address it is to
|
---|
| 496 | * go or to come from.
|
---|
| 497 | */
|
---|
| 498 | for (up = uaddrs; up < uaddrs + MAX_SECTORS; up++) *up = 0;
|
---|
| 499 | count = 0;
|
---|
| 500 | iop = iov;
|
---|
| 501 | sector = block % f_sectors;
|
---|
| 502 | for (;;) {
|
---|
| 503 | user_addr = iop->iov_addr;
|
---|
| 504 | chunk = iop->iov_size;
|
---|
| 505 | if ((chunk & SECTOR_MASK) != 0) return(EINVAL);
|
---|
| 506 |
|
---|
| 507 | while (chunk > 0) {
|
---|
| 508 | uaddrs[sector++] = user_addr;
|
---|
| 509 | chunk -= SECTOR_SIZE;
|
---|
| 510 | user_addr += SECTOR_SIZE;
|
---|
| 511 | count += SECTOR_SIZE;
|
---|
| 512 | if (sector == f_sectors || count == nbytes)
|
---|
| 513 | goto track_set_up;
|
---|
| 514 | }
|
---|
| 515 | iop++;
|
---|
| 516 | }
|
---|
| 517 | track_set_up:
|
---|
| 518 |
|
---|
| 519 | /* First check to see if a reset is needed. */
|
---|
| 520 | if (need_reset) f_reset();
|
---|
| 521 |
|
---|
| 522 | /* See if motor is running; if not, turn it on and wait. */
|
---|
| 523 | start_motor();
|
---|
| 524 |
|
---|
| 525 | /* Set the stepping rate and data rate */
|
---|
| 526 | if (f_dp != prev_dp) {
|
---|
| 527 | cmd[0] = FDC_SPECIFY;
|
---|
| 528 | cmd[1] = f_dp->spec1;
|
---|
| 529 | cmd[2] = SPEC2;
|
---|
| 530 | (void) fdc_command(cmd, 3);
|
---|
| 531 | if ((s=sys_outb(FDC_RATE, f_dp->rate)) != OK)
|
---|
| 532 | panic("FLOPPY","Sys_outb failed", s);
|
---|
| 533 | prev_dp = f_dp;
|
---|
| 534 | }
|
---|
| 535 |
|
---|
| 536 | /* If we are going to a new cylinder, perform a seek. */
|
---|
| 537 | r = seek();
|
---|
| 538 |
|
---|
| 539 | /* Avoid read_id() if we don't plan to read much. */
|
---|
| 540 | if (fp->fl_sector == NO_SECTOR && count < (6 * SECTOR_SIZE))
|
---|
| 541 | fp->fl_sector = 0;
|
---|
| 542 |
|
---|
| 543 | for (nbytes = 0; nbytes < count; nbytes += SECTOR_SIZE) {
|
---|
| 544 | if (fp->fl_sector == NO_SECTOR) {
|
---|
| 545 | /* Find out what the current sector is. This often
|
---|
| 546 | * fails right after a seek, so try it twice.
|
---|
| 547 | */
|
---|
| 548 | if (r == OK && read_id() != OK) r = read_id();
|
---|
| 549 | }
|
---|
| 550 |
|
---|
| 551 | /* Look for the next job in uaddrs[] */
|
---|
| 552 | if (r == OK) {
|
---|
| 553 | for (;;) {
|
---|
| 554 | if (fp->fl_sector >= f_sectors)
|
---|
| 555 | fp->fl_sector = 0;
|
---|
| 556 |
|
---|
| 557 | up = &uaddrs[fp->fl_sector];
|
---|
| 558 | if (*up != 0) break;
|
---|
| 559 | fp->fl_sector++;
|
---|
| 560 | }
|
---|
| 561 | }
|
---|
| 562 |
|
---|
| 563 | if (r == OK && opcode == DEV_SCATTER) {
|
---|
| 564 | /* Copy the user bytes to the DMA buffer. */
|
---|
| 565 | if ((s=sys_datacopy(proc_nr, *up, SELF,
|
---|
| 566 | (vir_bytes) tmp_buf,
|
---|
| 567 | (phys_bytes) SECTOR_SIZE)) != OK)
|
---|
| 568 | panic("FLOPPY", "Sys_vircopy failed", s);
|
---|
| 569 | }
|
---|
| 570 |
|
---|
| 571 | /* Set up the DMA chip and perform the transfer. */
|
---|
| 572 | if (r == OK) {
|
---|
| 573 | if (dma_setup(opcode) != OK) {
|
---|
| 574 | /* This can only fail for addresses above 16MB
|
---|
| 575 | * that cannot be handled by the controller,
|
---|
| 576 | * because it uses 24-bit addressing.
|
---|
| 577 | */
|
---|
| 578 | return(EIO);
|
---|
| 579 | }
|
---|
| 580 | r = fdc_transfer(opcode);
|
---|
| 581 | }
|
---|
| 582 |
|
---|
| 583 | if (r == OK && opcode == DEV_GATHER) {
|
---|
| 584 | /* Copy the DMA buffer to user space. */
|
---|
| 585 | if ((s=sys_datacopy(SELF, (vir_bytes) tmp_buf,
|
---|
| 586 | proc_nr, *up,
|
---|
| 587 | (phys_bytes) SECTOR_SIZE)) != OK)
|
---|
| 588 | panic("FLOPPY", "Sys_vircopy failed", s);
|
---|
| 589 | }
|
---|
| 590 |
|
---|
| 591 | if (r != OK) {
|
---|
| 592 | /* Don't retry if write protected or too many errors. */
|
---|
| 593 | if (err_no_retry(r) || ++errors == MAX_ERRORS) {
|
---|
| 594 | return(EIO);
|
---|
| 595 | }
|
---|
| 596 |
|
---|
| 597 | /* Recalibrate if halfway. */
|
---|
| 598 | if (errors == MAX_ERRORS / 2)
|
---|
| 599 | fp->fl_calibration = UNCALIBRATED;
|
---|
| 600 |
|
---|
| 601 | nbytes = 0;
|
---|
| 602 | break; /* retry */
|
---|
| 603 | }
|
---|
| 604 | }
|
---|
| 605 |
|
---|
| 606 | /* Book the bytes successfully transferred. */
|
---|
| 607 | position += nbytes;
|
---|
| 608 | for (;;) {
|
---|
| 609 | if (nbytes < iov->iov_size) {
|
---|
| 610 | /* Not done with this one yet. */
|
---|
| 611 | iov->iov_addr += nbytes;
|
---|
| 612 | iov->iov_size -= nbytes;
|
---|
| 613 | break;
|
---|
| 614 | }
|
---|
| 615 | nbytes -= iov->iov_size;
|
---|
| 616 | iov->iov_addr += iov->iov_size;
|
---|
| 617 | iov->iov_size = 0;
|
---|
| 618 | if (nbytes == 0) {
|
---|
| 619 | /* The rest is optional, so we return to give FS a
|
---|
| 620 | * chance to think it over.
|
---|
| 621 | */
|
---|
| 622 | return(OK);
|
---|
| 623 | }
|
---|
| 624 | iov++;
|
---|
| 625 | nr_req--;
|
---|
| 626 | }
|
---|
| 627 | }
|
---|
| 628 | return(OK);
|
---|
| 629 | }
|
---|
| 630 |
|
---|
| 631 | /*===========================================================================*
|
---|
| 632 | * dma_setup *
|
---|
| 633 | *===========================================================================*/
|
---|
| 634 | PRIVATE int dma_setup(opcode)
|
---|
| 635 | int opcode; /* DEV_GATHER or DEV_SCATTER */
|
---|
| 636 | {
|
---|
| 637 | /* The IBM PC can perform DMA operations by using the DMA chip. To use it,
|
---|
| 638 | * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
|
---|
| 639 | * to be read from or written to, the byte count minus 1, and a read or write
|
---|
| 640 | * opcode. This routine sets up the DMA chip. Note that the chip is not
|
---|
| 641 | * capable of doing a DMA across a 64K boundary (e.g., you can't read a
|
---|
| 642 | * 512-byte block starting at physical address 65520).
|
---|
| 643 | *
|
---|
| 644 | * Warning! Also note that it's not possible to do DMA above 16 MB because
|
---|
| 645 | * the ISA bus uses 24-bit addresses. Addresses above 16 MB therefore will
|
---|
| 646 | * be interpreted modulo 16 MB, dangerously overwriting arbitrary memory.
|
---|
| 647 | * A check here denies the I/O if the address is out of range.
|
---|
| 648 | */
|
---|
| 649 | pvb_pair_t byte_out[9];
|
---|
| 650 | int s;
|
---|
| 651 |
|
---|
| 652 | /* First check the DMA memory address not to exceed maximum. */
|
---|
| 653 | if (tmp_phys != (tmp_phys & DMA_ADDR_MASK)) {
|
---|
| 654 | report("FLOPPY", "DMA denied because address out of range", NO_NUM);
|
---|
| 655 | return(EIO);
|
---|
| 656 | }
|
---|
| 657 |
|
---|
| 658 | /* Set up the DMA registers. (The comment on the reset is a bit strong,
|
---|
| 659 | * it probably only resets the floppy channel.)
|
---|
| 660 | */
|
---|
| 661 | pv_set(byte_out[0], DMA_INIT, DMA_RESET_VAL); /* reset the dma controller */
|
---|
| 662 | pv_set(byte_out[1], DMA_FLIPFLOP, 0); /* write anything to reset it */
|
---|
| 663 | pv_set(byte_out[2], DMA_MODE, opcode == DEV_SCATTER ? DMA_WRITE : DMA_READ);
|
---|
| 664 | pv_set(byte_out[3], DMA_ADDR, (unsigned) tmp_phys >> 0);
|
---|
| 665 | pv_set(byte_out[4], DMA_ADDR, (unsigned) tmp_phys >> 8);
|
---|
| 666 | pv_set(byte_out[5], DMA_TOP, (unsigned) (tmp_phys >> 16));
|
---|
| 667 | pv_set(byte_out[6], DMA_COUNT, (((SECTOR_SIZE - 1) >> 0) & 0xff));
|
---|
| 668 | pv_set(byte_out[7], DMA_COUNT, (SECTOR_SIZE - 1) >> 8);
|
---|
| 669 | pv_set(byte_out[8], DMA_INIT, 2); /* some sort of enable */
|
---|
| 670 |
|
---|
| 671 | if ((s=sys_voutb(byte_out, 9)) != OK)
|
---|
| 672 | panic("FLOPPY","Sys_voutb in dma_setup() failed", s);
|
---|
| 673 | return(OK);
|
---|
| 674 | }
|
---|
| 675 |
|
---|
| 676 | /*===========================================================================*
|
---|
| 677 | * start_motor *
|
---|
| 678 | *===========================================================================*/
|
---|
| 679 | PRIVATE void start_motor()
|
---|
| 680 | {
|
---|
| 681 | /* Control of the floppy disk motors is a big pain. If a motor is off, you
|
---|
| 682 | * have to turn it on first, which takes 1/2 second. You can't leave it on
|
---|
| 683 | * all the time, since that would wear out the diskette. However, if you turn
|
---|
| 684 | * the motor off after each operation, the system performance will be awful.
|
---|
| 685 | * The compromise used here is to leave it on for a few seconds after each
|
---|
| 686 | * operation. If a new operation is started in that interval, it need not be
|
---|
| 687 | * turned on again. If no new operation is started, a timer goes off and the
|
---|
| 688 | * motor is turned off. I/O port DOR has bits to control each of 4 drives.
|
---|
| 689 | */
|
---|
| 690 |
|
---|
| 691 | int s, motor_bit, running;
|
---|
| 692 | message mess;
|
---|
| 693 |
|
---|
| 694 | motor_bit = 1 << f_drive; /* bit mask for this drive */
|
---|
| 695 | running = motor_status & motor_bit; /* nonzero if this motor is running */
|
---|
| 696 | motor_status |= motor_bit; /* want this drive running too */
|
---|
| 697 |
|
---|
| 698 | if ((s=sys_outb(DOR,
|
---|
| 699 | (motor_status << MOTOR_SHIFT) | ENABLE_INT | f_drive)) != OK)
|
---|
| 700 | panic("FLOPPY","Sys_outb in start_motor() failed", s);
|
---|
| 701 |
|
---|
| 702 | /* If the motor was already running, we don't have to wait for it. */
|
---|
| 703 | if (running) return; /* motor was already running */
|
---|
| 704 |
|
---|
| 705 | /* Set an alarm timer to force a timeout if the hardware does not interrupt
|
---|
| 706 | * in time. Expect HARD_INT message, but check for SYN_ALARM timeout.
|
---|
| 707 | */
|
---|
| 708 | f_set_timer(&f_tmr_timeout, f_dp->start, f_timeout);
|
---|
| 709 | f_busy = BSY_IO;
|
---|
| 710 | do {
|
---|
| 711 | receive(ANY, &mess);
|
---|
| 712 | if (mess.m_type == SYN_ALARM) {
|
---|
| 713 | f_expire_tmrs(NULL, NULL);
|
---|
| 714 | } else if(mess.m_type == DEV_PING) {
|
---|
| 715 | notify(mess.m_source);
|
---|
| 716 | } else {
|
---|
| 717 | f_busy = BSY_IDLE;
|
---|
| 718 | }
|
---|
| 719 | } while (f_busy == BSY_IO);
|
---|
| 720 | f_fp->fl_sector = NO_SECTOR;
|
---|
| 721 | }
|
---|
| 722 |
|
---|
| 723 | /*===========================================================================*
|
---|
| 724 | * stop_motor *
|
---|
| 725 | *===========================================================================*/
|
---|
| 726 | PRIVATE void stop_motor(tp)
|
---|
| 727 | timer_t *tp;
|
---|
| 728 | {
|
---|
| 729 | /* This routine is called from an alarm timer after several seconds have
|
---|
| 730 | * elapsed with no floppy disk activity. It turns the drive motor off.
|
---|
| 731 | */
|
---|
| 732 | int s;
|
---|
| 733 | motor_status &= ~(1 << tmr_arg(tp)->ta_int);
|
---|
| 734 | if ((s=sys_outb(DOR, (motor_status << MOTOR_SHIFT) | ENABLE_INT)) != OK)
|
---|
| 735 | panic("FLOPPY","Sys_outb in stop_motor() failed", s);
|
---|
| 736 | }
|
---|
| 737 |
|
---|
| 738 | /*===========================================================================*
|
---|
| 739 | * floppy_stop *
|
---|
| 740 | *===========================================================================*/
|
---|
| 741 | PRIVATE void floppy_stop(struct driver *dp, message *m_ptr)
|
---|
| 742 | {
|
---|
| 743 | /* Stop all activity and cleanly exit with the system. */
|
---|
| 744 | int s;
|
---|
| 745 | sigset_t sigset = m_ptr->NOTIFY_ARG;
|
---|
| 746 | if (sigismember(&sigset, SIGTERM) || sigismember(&sigset, SIGKSTOP)) {
|
---|
| 747 | if ((s=sys_outb(DOR, ENABLE_INT)) != OK)
|
---|
| 748 | panic("FLOPPY","Sys_outb in floppy_stop() failed", s);
|
---|
| 749 | exit(0);
|
---|
| 750 | }
|
---|
| 751 | }
|
---|
| 752 |
|
---|
| 753 | /*===========================================================================*
|
---|
| 754 | * seek *
|
---|
| 755 | *===========================================================================*/
|
---|
| 756 | PRIVATE int seek()
|
---|
| 757 | {
|
---|
| 758 | /* Issue a SEEK command on the indicated drive unless the arm is already
|
---|
| 759 | * positioned on the correct cylinder.
|
---|
| 760 | */
|
---|
| 761 |
|
---|
| 762 | struct floppy *fp = f_fp;
|
---|
| 763 | int r;
|
---|
| 764 | message mess;
|
---|
| 765 | u8_t cmd[3];
|
---|
| 766 |
|
---|
| 767 | /* Are we already on the correct cylinder? */
|
---|
| 768 | if (fp->fl_calibration == UNCALIBRATED)
|
---|
| 769 | if (recalibrate() != OK) return(ERR_SEEK);
|
---|
| 770 | if (fp->fl_curcyl == fp->fl_hardcyl) return(OK);
|
---|
| 771 |
|
---|
| 772 | /* No. Wrong cylinder. Issue a SEEK and wait for interrupt. */
|
---|
| 773 | cmd[0] = FDC_SEEK;
|
---|
| 774 | cmd[1] = (fp->fl_head << 2) | f_drive;
|
---|
| 775 | cmd[2] = fp->fl_hardcyl;
|
---|
| 776 | if (fdc_command(cmd, 3) != OK) return(ERR_SEEK);
|
---|
| 777 | if (f_intr_wait() != OK) return(ERR_TIMEOUT);
|
---|
| 778 |
|
---|
| 779 | /* Interrupt has been received. Check drive status. */
|
---|
| 780 | fdc_out(FDC_SENSE); /* probe FDC to make it return status */
|
---|
| 781 | r = fdc_results(); /* get controller status bytes */
|
---|
| 782 | if (r != OK || (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0
|
---|
| 783 | || f_results[ST1] != fp->fl_hardcyl) {
|
---|
| 784 | /* seek failed, may need a recalibrate */
|
---|
| 785 | return(ERR_SEEK);
|
---|
| 786 | }
|
---|
| 787 | /* Give head time to settle on a format, no retrying here! */
|
---|
| 788 | if (f_device & FORMAT_DEV_BIT) {
|
---|
| 789 | /* Set a synchronous alarm to force a timeout if the hardware does
|
---|
| 790 | * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout.
|
---|
| 791 | */
|
---|
| 792 | f_set_timer(&f_tmr_timeout, HZ/30, f_timeout);
|
---|
| 793 | f_busy = BSY_IO;
|
---|
| 794 | do {
|
---|
| 795 | receive(ANY, &mess);
|
---|
| 796 | if (mess.m_type == SYN_ALARM) {
|
---|
| 797 | f_expire_tmrs(NULL, NULL);
|
---|
| 798 | } else if(mess.m_type == DEV_PING) {
|
---|
| 799 | notify(mess.m_source);
|
---|
| 800 | } else {
|
---|
| 801 | f_busy = BSY_IDLE;
|
---|
| 802 | }
|
---|
| 803 | } while (f_busy == BSY_IO);
|
---|
| 804 | }
|
---|
| 805 | fp->fl_curcyl = fp->fl_hardcyl;
|
---|
| 806 | fp->fl_sector = NO_SECTOR;
|
---|
| 807 | return(OK);
|
---|
| 808 | }
|
---|
| 809 |
|
---|
| 810 | /*===========================================================================*
|
---|
| 811 | * fdc_transfer *
|
---|
| 812 | *===========================================================================*/
|
---|
| 813 | PRIVATE int fdc_transfer(opcode)
|
---|
| 814 | int opcode; /* DEV_GATHER or DEV_SCATTER */
|
---|
| 815 | {
|
---|
| 816 | /* The drive is now on the proper cylinder. Read, write or format 1 block. */
|
---|
| 817 |
|
---|
| 818 | struct floppy *fp = f_fp;
|
---|
| 819 | int r, s;
|
---|
| 820 | u8_t cmd[9];
|
---|
| 821 |
|
---|
| 822 | /* Never attempt a transfer if the drive is uncalibrated or motor is off. */
|
---|
| 823 | if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER);
|
---|
| 824 | if ((motor_status & (1 << f_drive)) == 0) return(ERR_TRANSFER);
|
---|
| 825 |
|
---|
| 826 | /* The command is issued by outputting several bytes to the controller chip.
|
---|
| 827 | */
|
---|
| 828 | if (f_device & FORMAT_DEV_BIT) {
|
---|
| 829 | cmd[0] = FDC_FORMAT;
|
---|
| 830 | cmd[1] = (fp->fl_head << 2) | f_drive;
|
---|
| 831 | cmd[2] = fmt_param.sector_size_code;
|
---|
| 832 | cmd[3] = fmt_param.sectors_per_cylinder;
|
---|
| 833 | cmd[4] = fmt_param.gap_length_for_format;
|
---|
| 834 | cmd[5] = fmt_param.fill_byte_for_format;
|
---|
| 835 | if (fdc_command(cmd, 6) != OK) return(ERR_TRANSFER);
|
---|
| 836 | } else {
|
---|
| 837 | cmd[0] = opcode == DEV_SCATTER ? FDC_WRITE : FDC_READ;
|
---|
| 838 | cmd[1] = (fp->fl_head << 2) | f_drive;
|
---|
| 839 | cmd[2] = fp->fl_cylinder;
|
---|
| 840 | cmd[3] = fp->fl_head;
|
---|
| 841 | cmd[4] = BASE_SECTOR + fp->fl_sector;
|
---|
| 842 | cmd[5] = SECTOR_SIZE_CODE;
|
---|
| 843 | cmd[6] = f_sectors;
|
---|
| 844 | cmd[7] = f_dp->gap; /* sector gap */
|
---|
| 845 | cmd[8] = DTL; /* data length */
|
---|
| 846 | if (fdc_command(cmd, 9) != OK) return(ERR_TRANSFER);
|
---|
| 847 | }
|
---|
| 848 |
|
---|
| 849 | /* Block, waiting for disk interrupt. */
|
---|
| 850 | if (f_intr_wait() != OK) {
|
---|
| 851 | printf("%s: disk interrupt timed out.\n", f_name());
|
---|
| 852 | return(ERR_TIMEOUT);
|
---|
| 853 | }
|
---|
| 854 |
|
---|
| 855 | /* Get controller status and check for errors. */
|
---|
| 856 | r = fdc_results();
|
---|
| 857 | if (r != OK) return(r);
|
---|
| 858 |
|
---|
| 859 | if (f_results[ST1] & WRITE_PROTECT) {
|
---|
| 860 | printf("%s: diskette is write protected.\n", f_name());
|
---|
| 861 | return(ERR_WR_PROTECT);
|
---|
| 862 | }
|
---|
| 863 |
|
---|
| 864 | if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_TRANSFER);
|
---|
| 865 | if (f_results[ST1] | f_results[ST2]) return(ERR_TRANSFER);
|
---|
| 866 |
|
---|
| 867 | if (f_device & FORMAT_DEV_BIT) return(OK);
|
---|
| 868 |
|
---|
| 869 | /* Compare actual numbers of sectors transferred with expected number. */
|
---|
| 870 | s = (f_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * f_sectors;
|
---|
| 871 | s += (f_results[ST_HEAD] - fp->fl_head) * f_sectors;
|
---|
| 872 | s += (f_results[ST_SEC] - BASE_SECTOR - fp->fl_sector);
|
---|
| 873 | if (s != 1) return(ERR_TRANSFER);
|
---|
| 874 |
|
---|
| 875 | /* This sector is next for I/O: */
|
---|
| 876 | fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR;
|
---|
| 877 | #if 0
|
---|
| 878 | if (processor < 386) fp->fl_sector++; /* Old CPU can't keep up. */
|
---|
| 879 | #endif
|
---|
| 880 | return(OK);
|
---|
| 881 | }
|
---|
| 882 |
|
---|
| 883 | /*===========================================================================*
|
---|
| 884 | * fdc_results *
|
---|
| 885 | *===========================================================================*/
|
---|
| 886 | PRIVATE int fdc_results()
|
---|
| 887 | {
|
---|
| 888 | /* Extract results from the controller after an operation, then allow floppy
|
---|
| 889 | * interrupts again.
|
---|
| 890 | */
|
---|
| 891 |
|
---|
| 892 | int s, result_nr;
|
---|
| 893 | unsigned long status;
|
---|
| 894 | clock_t t0,t1;
|
---|
| 895 |
|
---|
| 896 | /* Extract bytes from FDC until it says it has no more. The loop is
|
---|
| 897 | * really an outer loop on result_nr and an inner loop on status.
|
---|
| 898 | * A timeout flag alarm is set.
|
---|
| 899 | */
|
---|
| 900 | result_nr = 0;
|
---|
| 901 | getuptime(&t0);
|
---|
| 902 | do {
|
---|
| 903 | /* Reading one byte is almost a mirror of fdc_out() - the DIRECTION
|
---|
| 904 | * bit must be set instead of clear, but the CTL_BUSY bit destroys
|
---|
| 905 | * the perfection of the mirror.
|
---|
| 906 | */
|
---|
| 907 | if ((s=sys_inb(FDC_STATUS, &status)) != OK)
|
---|
| 908 | panic("FLOPPY","Sys_inb in fdc_results() failed", s);
|
---|
| 909 | status &= (MASTER | DIRECTION | CTL_BUSY);
|
---|
| 910 | if (status == (MASTER | DIRECTION | CTL_BUSY)) {
|
---|
| 911 | unsigned long tmp_r;
|
---|
| 912 | if (result_nr >= MAX_RESULTS) break; /* too many results */
|
---|
| 913 | if ((s=sys_inb(FDC_DATA, &tmp_r)) != OK)
|
---|
| 914 | panic("FLOPPY","Sys_inb in fdc_results() failed", s);
|
---|
| 915 | f_results[result_nr] = tmp_r;
|
---|
| 916 | result_nr ++;
|
---|
| 917 | continue;
|
---|
| 918 | }
|
---|
| 919 | if (status == MASTER) { /* all read */
|
---|
| 920 | if ((s=sys_irqenable(&irq_hook_id)) != OK)
|
---|
| 921 | panic("FLOPPY", "Couldn't enable IRQs", s);
|
---|
| 922 |
|
---|
| 923 | return(OK); /* only good exit */
|
---|
| 924 | }
|
---|
| 925 | } while ( (s=getuptime(&t1))==OK && (t1-t0) < TIMEOUT_TICKS );
|
---|
| 926 | if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s);
|
---|
| 927 | need_reset = TRUE; /* controller chip must be reset */
|
---|
| 928 |
|
---|
| 929 | if ((s=sys_irqenable(&irq_hook_id)) != OK)
|
---|
| 930 | panic("FLOPPY", "Couldn't enable IRQs", s);
|
---|
| 931 | return(ERR_STATUS);
|
---|
| 932 | }
|
---|
| 933 |
|
---|
| 934 | /*===========================================================================*
|
---|
| 935 | * fdc_command *
|
---|
| 936 | *===========================================================================*/
|
---|
| 937 | PRIVATE int fdc_command(cmd, len)
|
---|
| 938 | u8_t *cmd; /* command bytes */
|
---|
| 939 | int len; /* command length */
|
---|
| 940 | {
|
---|
| 941 | /* Output a command to the controller. */
|
---|
| 942 |
|
---|
| 943 | /* Set a synchronous alarm to force a timeout if the hardware does
|
---|
| 944 | * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout.
|
---|
| 945 | * Note that the actual check is done by the code that issued the
|
---|
| 946 | * fdc_command() call.
|
---|
| 947 | */
|
---|
| 948 | f_set_timer(&f_tmr_timeout, WAKEUP, f_timeout);
|
---|
| 949 |
|
---|
| 950 | f_busy = BSY_IO;
|
---|
| 951 | while (len > 0) {
|
---|
| 952 | fdc_out(*cmd++);
|
---|
| 953 | len--;
|
---|
| 954 | }
|
---|
| 955 | return(need_reset ? ERR_DRIVE : OK);
|
---|
| 956 | }
|
---|
| 957 |
|
---|
| 958 | /*===========================================================================*
|
---|
| 959 | * fdc_out *
|
---|
| 960 | *===========================================================================*/
|
---|
| 961 | PRIVATE void fdc_out(val)
|
---|
| 962 | int val; /* write this byte to floppy disk controller */
|
---|
| 963 | {
|
---|
| 964 | /* Output a byte to the controller. This is not entirely trivial, since you
|
---|
| 965 | * can only write to it when it is listening, and it decides when to listen.
|
---|
| 966 | * If the controller refuses to listen, the FDC chip is given a hard reset.
|
---|
| 967 | */
|
---|
| 968 | clock_t t0, t1;
|
---|
| 969 | int s;
|
---|
| 970 | unsigned long status;
|
---|
| 971 |
|
---|
| 972 | if (need_reset) return; /* if controller is not listening, return */
|
---|
| 973 |
|
---|
| 974 | /* It may take several tries to get the FDC to accept a command. */
|
---|
| 975 | getuptime(&t0);
|
---|
| 976 | do {
|
---|
| 977 | if ( (s=getuptime(&t1))==OK && (t1-t0) > TIMEOUT_TICKS ) {
|
---|
| 978 | if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s);
|
---|
| 979 | need_reset = TRUE; /* hit it over the head */
|
---|
| 980 | return;
|
---|
| 981 | }
|
---|
| 982 | if ((s=sys_inb(FDC_STATUS, &status)) != OK)
|
---|
| 983 | panic("FLOPPY","Sys_inb in fdc_out() failed", s);
|
---|
| 984 | }
|
---|
| 985 | while ((status & (MASTER | DIRECTION)) != (MASTER | 0));
|
---|
| 986 |
|
---|
| 987 | if ((s=sys_outb(FDC_DATA, val)) != OK)
|
---|
| 988 | panic("FLOPPY","Sys_outb in fdc_out() failed", s);
|
---|
| 989 | }
|
---|
| 990 |
|
---|
| 991 | /*===========================================================================*
|
---|
| 992 | * recalibrate *
|
---|
| 993 | *===========================================================================*/
|
---|
| 994 | PRIVATE int recalibrate()
|
---|
| 995 | {
|
---|
| 996 | /* The floppy disk controller has no way of determining its absolute arm
|
---|
| 997 | * position (cylinder). Instead, it steps the arm a cylinder at a time and
|
---|
| 998 | * keeps track of where it thinks it is (in software). However, after a
|
---|
| 999 | * SEEK, the hardware reads information from the diskette telling where the
|
---|
| 1000 | * arm actually is. If the arm is in the wrong place, a recalibration is done,
|
---|
| 1001 | * which forces the arm to cylinder 0. This way the controller can get back
|
---|
| 1002 | * into sync with reality.
|
---|
| 1003 | */
|
---|
| 1004 |
|
---|
| 1005 | struct floppy *fp = f_fp;
|
---|
| 1006 | int r;
|
---|
| 1007 | u8_t cmd[2];
|
---|
| 1008 |
|
---|
| 1009 | /* Issue the RECALIBRATE command and wait for the interrupt. */
|
---|
| 1010 | cmd[0] = FDC_RECALIBRATE; /* tell drive to recalibrate itself */
|
---|
| 1011 | cmd[1] = f_drive; /* specify drive */
|
---|
| 1012 | if (fdc_command(cmd, 2) != OK) return(ERR_SEEK);
|
---|
| 1013 | if (f_intr_wait() != OK) return(ERR_TIMEOUT);
|
---|
| 1014 |
|
---|
| 1015 | /* Determine if the recalibration succeeded. */
|
---|
| 1016 | fdc_out(FDC_SENSE); /* issue SENSE command to request results */
|
---|
| 1017 | r = fdc_results(); /* get results of the FDC_RECALIBRATE command*/
|
---|
| 1018 | fp->fl_curcyl = NO_CYL; /* force a SEEK next time */
|
---|
| 1019 | fp->fl_sector = NO_SECTOR;
|
---|
| 1020 | if (r != OK || /* controller would not respond */
|
---|
| 1021 | (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0 || f_results[ST_PCN] != 0) {
|
---|
| 1022 | /* Recalibration failed. FDC must be reset. */
|
---|
| 1023 | need_reset = TRUE;
|
---|
| 1024 | return(ERR_RECALIBRATE);
|
---|
| 1025 | } else {
|
---|
| 1026 | /* Recalibration succeeded. */
|
---|
| 1027 | fp->fl_calibration = CALIBRATED;
|
---|
| 1028 | fp->fl_curcyl = f_results[ST_PCN];
|
---|
| 1029 | return(OK);
|
---|
| 1030 | }
|
---|
| 1031 | }
|
---|
| 1032 |
|
---|
| 1033 | /*===========================================================================*
|
---|
| 1034 | * f_reset *
|
---|
| 1035 | *===========================================================================*/
|
---|
| 1036 | PRIVATE void f_reset()
|
---|
| 1037 | {
|
---|
| 1038 | /* Issue a reset to the controller. This is done after any catastrophe,
|
---|
| 1039 | * like the controller refusing to respond.
|
---|
| 1040 | */
|
---|
| 1041 | pvb_pair_t byte_out[2];
|
---|
| 1042 | int s,i;
|
---|
| 1043 | message mess;
|
---|
| 1044 |
|
---|
| 1045 | /* Disable interrupts and strobe reset bit low. */
|
---|
| 1046 | need_reset = FALSE;
|
---|
| 1047 |
|
---|
| 1048 | /* It is not clear why the next lock is needed. Writing 0 to DOR causes
|
---|
| 1049 | * interrupt, while the PC documentation says turning bit 8 off disables
|
---|
| 1050 | * interrupts. Without the lock:
|
---|
| 1051 | * 1) the interrupt handler sets the floppy mask bit in the 8259.
|
---|
| 1052 | * 2) writing ENABLE_INT to DOR causes the FDC to assert the interrupt
|
---|
| 1053 | * line again, but the mask stops the cpu being interrupted.
|
---|
| 1054 | * 3) the sense interrupt clears the interrupt (not clear which one).
|
---|
| 1055 | * and for some reason the reset does not work.
|
---|
| 1056 | */
|
---|
| 1057 | (void) fdc_command((u8_t *) 0, 0); /* need only the timer */
|
---|
| 1058 | motor_status = 0;
|
---|
| 1059 | pv_set(byte_out[0], DOR, 0); /* strobe reset bit low */
|
---|
| 1060 | pv_set(byte_out[1], DOR, ENABLE_INT); /* strobe it high again */
|
---|
| 1061 | if ((s=sys_voutb(byte_out, 2)) != OK)
|
---|
| 1062 | panic("FLOPPY", "Sys_voutb in f_reset() failed", s);
|
---|
| 1063 |
|
---|
| 1064 | /* A synchronous alarm timer was set in fdc_command. Expect a HARD_INT
|
---|
| 1065 | * message to collect the reset interrupt, but be prepared to handle the
|
---|
| 1066 | * SYN_ALARM message on a timeout.
|
---|
| 1067 | */
|
---|
| 1068 | do {
|
---|
| 1069 | receive(ANY, &mess);
|
---|
| 1070 | if (mess.m_type == SYN_ALARM) {
|
---|
| 1071 | f_expire_tmrs(NULL, NULL);
|
---|
| 1072 | } else if(mess.m_type == DEV_PING) {
|
---|
| 1073 | notify(mess.m_source);
|
---|
| 1074 | } else { /* expect HARD_INT */
|
---|
| 1075 | f_busy = BSY_IDLE;
|
---|
| 1076 | }
|
---|
| 1077 | } while (f_busy == BSY_IO);
|
---|
| 1078 |
|
---|
| 1079 | /* The controller supports 4 drives and returns a result for each of them.
|
---|
| 1080 | * Collect all the results now. The old version only collected the first
|
---|
| 1081 | * result. This happens to work for 2 drives, but it doesn't work for 3
|
---|
| 1082 | * or more drives, at least with only drives 0 and 2 actually connected
|
---|
| 1083 | * (the controller generates an extra interrupt for the middle drive when
|
---|
| 1084 | * drive 2 is accessed and the driver panics).
|
---|
| 1085 | *
|
---|
| 1086 | * It would be better to keep collecting results until there are no more.
|
---|
| 1087 | * For this, fdc_results needs to return the number of results (instead
|
---|
| 1088 | * of OK) when it succeeds.
|
---|
| 1089 | */
|
---|
| 1090 | for (i = 0; i < 4; i++) {
|
---|
| 1091 | fdc_out(FDC_SENSE); /* probe FDC to make it return status */
|
---|
| 1092 | (void) fdc_results(); /* flush controller */
|
---|
| 1093 | }
|
---|
| 1094 | for (i = 0; i < NR_DRIVES; i++) /* clear each drive */
|
---|
| 1095 | floppy[i].fl_calibration = UNCALIBRATED;
|
---|
| 1096 |
|
---|
| 1097 | /* The current timing parameters must be specified again. */
|
---|
| 1098 | prev_dp = NULL;
|
---|
| 1099 | }
|
---|
| 1100 |
|
---|
| 1101 | /*===========================================================================*
|
---|
| 1102 | * f_intr_wait *
|
---|
| 1103 | *===========================================================================*/
|
---|
| 1104 | PRIVATE int f_intr_wait()
|
---|
| 1105 | {
|
---|
| 1106 | /* Wait for an interrupt, but not forever. The FDC may have all the time of
|
---|
| 1107 | * the world, but we humans do not.
|
---|
| 1108 | */
|
---|
| 1109 | message mess;
|
---|
| 1110 |
|
---|
| 1111 | /* We expect a HARD_INT message from the interrupt handler, but if there is
|
---|
| 1112 | * a timeout, a SYN_ALARM notification is received instead. If a timeout
|
---|
| 1113 | * occurs, report an error.
|
---|
| 1114 | */
|
---|
| 1115 | do {
|
---|
| 1116 | receive(ANY, &mess);
|
---|
| 1117 | if (mess.m_type == SYN_ALARM) {
|
---|
| 1118 | f_expire_tmrs(NULL, NULL);
|
---|
| 1119 | } else if(mess.m_type == DEV_PING) {
|
---|
| 1120 | notify(mess.m_source);
|
---|
| 1121 | } else {
|
---|
| 1122 | f_busy = BSY_IDLE;
|
---|
| 1123 | }
|
---|
| 1124 | } while (f_busy == BSY_IO);
|
---|
| 1125 |
|
---|
| 1126 | if (f_busy == BSY_WAKEN) {
|
---|
| 1127 |
|
---|
| 1128 | /* No interrupt from the FDC, this means that there is probably no
|
---|
| 1129 | * floppy in the drive. Get the FDC down to earth and return error.
|
---|
| 1130 | */
|
---|
| 1131 | need_reset = TRUE;
|
---|
| 1132 | return(ERR_TIMEOUT);
|
---|
| 1133 | }
|
---|
| 1134 | return(OK);
|
---|
| 1135 | }
|
---|
| 1136 |
|
---|
| 1137 | /*===========================================================================*
|
---|
| 1138 | * f_timeout *
|
---|
| 1139 | *===========================================================================*/
|
---|
| 1140 | PRIVATE void f_timeout(tp)
|
---|
| 1141 | timer_t *tp;
|
---|
| 1142 | {
|
---|
| 1143 | /* This routine is called when a timer expires. Usually to tell that a
|
---|
| 1144 | * motor has spun up, but also to forge an interrupt when it takes too long
|
---|
| 1145 | * for the FDC to interrupt (no floppy in the drive). It sets a flag to tell
|
---|
| 1146 | * what has happened.
|
---|
| 1147 | */
|
---|
| 1148 | if (f_busy == BSY_IO) {
|
---|
| 1149 | f_busy = BSY_WAKEN;
|
---|
| 1150 | }
|
---|
| 1151 | }
|
---|
| 1152 |
|
---|
| 1153 | /*===========================================================================*
|
---|
| 1154 | * read_id *
|
---|
| 1155 | *===========================================================================*/
|
---|
| 1156 | PRIVATE int read_id()
|
---|
| 1157 | {
|
---|
| 1158 | /* Determine current cylinder and sector. */
|
---|
| 1159 |
|
---|
| 1160 | struct floppy *fp = f_fp;
|
---|
| 1161 | int result;
|
---|
| 1162 | u8_t cmd[2];
|
---|
| 1163 |
|
---|
| 1164 | /* Never attempt a read id if the drive is uncalibrated or motor is off. */
|
---|
| 1165 | if (fp->fl_calibration == UNCALIBRATED) return(ERR_READ_ID);
|
---|
| 1166 | if ((motor_status & (1 << f_drive)) == 0) return(ERR_READ_ID);
|
---|
| 1167 |
|
---|
| 1168 | /* The command is issued by outputting 2 bytes to the controller chip. */
|
---|
| 1169 | cmd[0] = FDC_READ_ID; /* issue the read id command */
|
---|
| 1170 | cmd[1] = (fp->fl_head << 2) | f_drive;
|
---|
| 1171 | if (fdc_command(cmd, 2) != OK) return(ERR_READ_ID);
|
---|
| 1172 | if (f_intr_wait() != OK) return(ERR_TIMEOUT);
|
---|
| 1173 |
|
---|
| 1174 | /* Get controller status and check for errors. */
|
---|
| 1175 | result = fdc_results();
|
---|
| 1176 | if (result != OK) return(result);
|
---|
| 1177 |
|
---|
| 1178 | if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_READ_ID);
|
---|
| 1179 | if (f_results[ST1] | f_results[ST2]) return(ERR_READ_ID);
|
---|
| 1180 |
|
---|
| 1181 | /* The next sector is next for I/O: */
|
---|
| 1182 | fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR + 1;
|
---|
| 1183 | return(OK);
|
---|
| 1184 | }
|
---|
| 1185 |
|
---|
| 1186 | /*===========================================================================*
|
---|
| 1187 | * f_do_open *
|
---|
| 1188 | *===========================================================================*/
|
---|
| 1189 | PRIVATE int f_do_open(dp, m_ptr)
|
---|
| 1190 | struct driver *dp;
|
---|
| 1191 | message *m_ptr; /* pointer to open message */
|
---|
| 1192 | {
|
---|
| 1193 | /* Handle an open on a floppy. Determine diskette type if need be. */
|
---|
| 1194 |
|
---|
| 1195 | int dtype;
|
---|
| 1196 | struct test_order *top;
|
---|
| 1197 |
|
---|
| 1198 | /* Decode the message parameters. */
|
---|
| 1199 | if (f_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
|
---|
| 1200 |
|
---|
| 1201 | dtype = f_device & DEV_TYPE_BITS; /* get density from minor dev */
|
---|
| 1202 | if (dtype >= MINOR_fd0p0) dtype = 0;
|
---|
| 1203 |
|
---|
| 1204 | if (dtype != 0) {
|
---|
| 1205 | /* All types except 0 indicate a specific drive/medium combination.*/
|
---|
| 1206 | dtype = (dtype >> DEV_TYPE_SHIFT) - 1;
|
---|
| 1207 | if (dtype >= NT) return(ENXIO);
|
---|
| 1208 | f_fp->fl_density = dtype;
|
---|
| 1209 | (void) f_prepare(f_device); /* Recompute parameters. */
|
---|
| 1210 | return(OK);
|
---|
| 1211 | }
|
---|
| 1212 | if (f_device & FORMAT_DEV_BIT) return(EIO); /* Can't format /dev/fdN */
|
---|
| 1213 |
|
---|
| 1214 | /* The device opened is /dev/fdN. Experimentally determine drive/medium.
|
---|
| 1215 | * First check fl_density. If it is not NO_DENS, the drive has been used
|
---|
| 1216 | * before and the value of fl_density tells what was found last time. Try
|
---|
| 1217 | * that first. If the motor is still running then assume nothing changed.
|
---|
| 1218 | */
|
---|
| 1219 | if (f_fp->fl_density != NO_DENS) {
|
---|
| 1220 | if (motor_status & (1 << f_drive)) return(OK);
|
---|
| 1221 | if (test_read(f_fp->fl_density) == OK) return(OK);
|
---|
| 1222 | }
|
---|
| 1223 |
|
---|
| 1224 | /* Either drive type is unknown or a different diskette is now present.
|
---|
| 1225 | * Use test_order to try them one by one.
|
---|
| 1226 | */
|
---|
| 1227 | for (top = &test_order[0]; top < &test_order[NT-1]; top++) {
|
---|
| 1228 | dtype = top->t_density;
|
---|
| 1229 |
|
---|
| 1230 | /* Skip densities that have been proven to be impossible */
|
---|
| 1231 | if (!(f_fp->fl_class & (1 << dtype))) continue;
|
---|
| 1232 |
|
---|
| 1233 | if (test_read(dtype) == OK) {
|
---|
| 1234 | /* The test succeeded, use this knowledge to limit the
|
---|
| 1235 | * drive class to match the density just read.
|
---|
| 1236 | */
|
---|
| 1237 | f_fp->fl_class &= top->t_class;
|
---|
| 1238 | return(OK);
|
---|
| 1239 | }
|
---|
| 1240 | /* Test failed, wrong density or did it time out? */
|
---|
| 1241 | if (f_busy == BSY_WAKEN) break;
|
---|
| 1242 | }
|
---|
| 1243 | f_fp->fl_density = NO_DENS;
|
---|
| 1244 | return(EIO); /* nothing worked */
|
---|
| 1245 | }
|
---|
| 1246 |
|
---|
| 1247 | /*===========================================================================*
|
---|
| 1248 | * test_read *
|
---|
| 1249 | *===========================================================================*/
|
---|
| 1250 | PRIVATE int test_read(density)
|
---|
| 1251 | int density;
|
---|
| 1252 | {
|
---|
| 1253 | /* Try to read the highest numbered sector on cylinder 2. Not all floppy
|
---|
| 1254 | * types have as many sectors per track, and trying cylinder 2 finds the
|
---|
| 1255 | * ones that need double stepping.
|
---|
| 1256 | */
|
---|
| 1257 | int device;
|
---|
| 1258 | off_t position;
|
---|
| 1259 | iovec_t iovec1;
|
---|
| 1260 | int result;
|
---|
| 1261 |
|
---|
| 1262 | f_fp->fl_density = density;
|
---|
| 1263 | device = ((density + 1) << DEV_TYPE_SHIFT) + f_drive;
|
---|
| 1264 |
|
---|
| 1265 | (void) f_prepare(device);
|
---|
| 1266 | position = (off_t) f_dp->test << SECTOR_SHIFT;
|
---|
| 1267 | iovec1.iov_addr = (vir_bytes) tmp_buf;
|
---|
| 1268 | iovec1.iov_size = SECTOR_SIZE;
|
---|
| 1269 | result = f_transfer(SELF, DEV_GATHER, position, &iovec1, 1);
|
---|
| 1270 |
|
---|
| 1271 | if (iovec1.iov_size != 0) return(EIO);
|
---|
| 1272 |
|
---|
| 1273 | partition(&f_dtab, f_drive, P_FLOPPY, 0);
|
---|
| 1274 | return(OK);
|
---|
| 1275 | }
|
---|
| 1276 |
|
---|
| 1277 | /*===========================================================================*
|
---|
| 1278 | * f_geometry *
|
---|
| 1279 | *===========================================================================*/
|
---|
| 1280 | PRIVATE void f_geometry(entry)
|
---|
| 1281 | struct partition *entry;
|
---|
| 1282 | {
|
---|
| 1283 | entry->cylinders = f_dp->cyls;
|
---|
| 1284 | entry->heads = NR_HEADS;
|
---|
| 1285 | entry->sectors = f_sectors;
|
---|
| 1286 | }
|
---|
| 1287 |
|
---|