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 |
|
---|