1433d6423SLionel Sambuc /* This file contains the device dependent part of the driver for the Floppy
2433d6423SLionel Sambuc * Disk Controller (FDC) using the NEC PD765 chip.
3433d6423SLionel Sambuc *
4433d6423SLionel Sambuc * The file contains two entry points:
5433d6423SLionel Sambuc *
6433d6423SLionel Sambuc * floppy_task: main entry when system is brought up
7433d6423SLionel Sambuc *
8433d6423SLionel Sambuc * Changes:
9433d6423SLionel Sambuc * Sep 11, 2005 code cleanup (Andy Tanenbaum)
10433d6423SLionel Sambuc * Dec 01, 2004 floppy driver moved to user-space (Jorrit N. Herder)
11433d6423SLionel Sambuc * Sep 15, 2004 sync alarms/ local timer management (Jorrit N. Herder)
12433d6423SLionel Sambuc * Aug 12, 2003 null seek no interrupt fix (Mike Haertel)
13433d6423SLionel Sambuc * May 14, 2000 d-d/i rewrite (Kees J. Bot)
14433d6423SLionel Sambuc * Apr 04, 1992 device dependent/independent split (Kees J. Bot)
15433d6423SLionel Sambuc * Mar 27, 1992 last details on density checking (Kees J. Bot)
16433d6423SLionel Sambuc * Feb 14, 1992 check drive density on opens only (Andy Tanenbaum)
17433d6423SLionel Sambuc * 1991 len[] / motors / reset / step rate / ... (Bruce Evans)
18433d6423SLionel Sambuc * May 13, 1991 renovated the errors loop (Don Chapman)
19433d6423SLionel Sambuc * 1989 I/O vector to keep up with 1-1 interleave (Bruce Evans)
20433d6423SLionel Sambuc * Jan 06, 1988 allow 1.44 MB diskettes (Al Crew)
21433d6423SLionel Sambuc * Nov 28, 1986 better resetting for 386 (Peter Kay)
22433d6423SLionel Sambuc * Oct 27, 1986 fdc_results fixed for 8 MHz (Jakob Schripsema)
23433d6423SLionel Sambuc */
24433d6423SLionel Sambuc
25433d6423SLionel Sambuc #include "floppy.h"
26433d6423SLionel Sambuc #include <minix/timers.h>
27433d6423SLionel Sambuc #include <machine/diskparm.h>
28433d6423SLionel Sambuc #include <minix/sysutil.h>
29433d6423SLionel Sambuc #include <minix/syslib.h>
30433d6423SLionel Sambuc #include <minix/endpoint.h>
31433d6423SLionel Sambuc #include <stdio.h>
32433d6423SLionel Sambuc
33433d6423SLionel Sambuc /* I/O Ports used by floppy disk task. */
34433d6423SLionel Sambuc #define DOR 0x3F2 /* motor drive control bits */
35433d6423SLionel Sambuc #define FDC_STATUS 0x3F4 /* floppy disk controller status register */
36433d6423SLionel Sambuc #define FDC_DATA 0x3F5 /* floppy disk controller data register */
37433d6423SLionel Sambuc #define FDC_RATE 0x3F7 /* transfer rate register */
38433d6423SLionel Sambuc #define DMA_ADDR 0x004 /* port for low 16 bits of DMA address */
39433d6423SLionel Sambuc #define DMA_TOP 0x081 /* port for top 8 bits of 24-bit DMA addr */
40433d6423SLionel Sambuc #define DMA_COUNT 0x005 /* port for DMA count (count = bytes - 1) */
41433d6423SLionel Sambuc #define DMA_FLIPFLOP 0x00C /* DMA byte pointer flip-flop */
42433d6423SLionel Sambuc #define DMA_MODE 0x00B /* DMA mode port */
43433d6423SLionel Sambuc #define DMA_INIT 0x00A /* DMA init port */
44433d6423SLionel Sambuc #define DMA_RESET_VAL 0x006
45433d6423SLionel Sambuc
46433d6423SLionel Sambuc #define DMA_ADDR_MASK 0xFFFFFF /* mask to verify DMA address is 24-bit */
47433d6423SLionel Sambuc
48433d6423SLionel Sambuc /* Status registers returned as result of operation. */
49433d6423SLionel Sambuc #define ST0 0x00 /* status register 0 */
50433d6423SLionel Sambuc #define ST1 0x01 /* status register 1 */
51433d6423SLionel Sambuc #define ST2 0x02 /* status register 2 */
52433d6423SLionel Sambuc #define ST3 0x00 /* status register 3 (return by DRIVE_SENSE) */
53433d6423SLionel Sambuc #define ST_CYL 0x03 /* slot where controller reports cylinder */
54433d6423SLionel Sambuc #define ST_HEAD 0x04 /* slot where controller reports head */
55433d6423SLionel Sambuc #define ST_SEC 0x05 /* slot where controller reports sector */
56433d6423SLionel Sambuc #define ST_PCN 0x01 /* slot where controller reports present cyl */
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc /* Fields within the I/O ports. */
59433d6423SLionel Sambuc /* Main status register. */
60433d6423SLionel Sambuc #define CTL_BUSY 0x10 /* bit is set when read or write in progress */
61433d6423SLionel Sambuc #define DIRECTION 0x40 /* bit is set when reading data reg is valid */
62433d6423SLionel Sambuc #define MASTER 0x80 /* bit is set when data reg can be accessed */
63433d6423SLionel Sambuc
64433d6423SLionel Sambuc /* Digital output port (DOR). */
65433d6423SLionel Sambuc #define MOTOR_SHIFT 4 /* high 4 bits control the motors in DOR */
66433d6423SLionel Sambuc #define ENABLE_INT 0x0C /* used for setting DOR port */
67433d6423SLionel Sambuc
68433d6423SLionel Sambuc /* ST0. */
69433d6423SLionel Sambuc #define ST0_BITS_TRANS 0xD8 /* check 4 bits of status */
70433d6423SLionel Sambuc #define TRANS_ST0 0x00 /* 4 bits of ST0 for READ/WRITE */
71433d6423SLionel Sambuc #define ST0_BITS_SEEK 0xF8 /* check top 5 bits of seek status */
72433d6423SLionel Sambuc #define SEEK_ST0 0x20 /* top 5 bits of ST0 for SEEK */
73433d6423SLionel Sambuc
74433d6423SLionel Sambuc /* ST1. */
75433d6423SLionel Sambuc #define BAD_SECTOR 0x05 /* if these bits are set in ST1, recalibrate */
76433d6423SLionel Sambuc #define WRITE_PROTECT 0x02 /* bit is set if diskette is write protected */
77433d6423SLionel Sambuc
78433d6423SLionel Sambuc /* ST2. */
79433d6423SLionel Sambuc #define BAD_CYL 0x1F /* if any of these bits are set, recalibrate */
80433d6423SLionel Sambuc
81433d6423SLionel Sambuc /* ST3 (not used). */
82433d6423SLionel Sambuc #define ST3_FAULT 0x80 /* if this bit is set, drive is sick */
83433d6423SLionel Sambuc #define ST3_WR_PROTECT 0x40 /* set when diskette is write protected */
84433d6423SLionel Sambuc #define ST3_READY 0x20 /* set when drive is ready */
85433d6423SLionel Sambuc
86433d6423SLionel Sambuc /* Floppy disk controller command bytes. */
87433d6423SLionel Sambuc #define FDC_SEEK 0x0F /* command the drive to seek */
88433d6423SLionel Sambuc #define FDC_READ 0xE6 /* command the drive to read */
89433d6423SLionel Sambuc #define FDC_WRITE 0xC5 /* command the drive to write */
90433d6423SLionel Sambuc #define FDC_SENSE 0x08 /* command the controller to tell its status */
91433d6423SLionel Sambuc #define FDC_RECALIBRATE 0x07 /* command the drive to go to cyl 0 */
92433d6423SLionel Sambuc #define FDC_SPECIFY 0x03 /* command the drive to accept params */
93433d6423SLionel Sambuc #define FDC_READ_ID 0x4A /* command the drive to read sector identity */
94433d6423SLionel Sambuc #define FDC_FORMAT 0x4D /* command the drive to format a track */
95433d6423SLionel Sambuc
96433d6423SLionel Sambuc /* DMA channel commands. */
97433d6423SLionel Sambuc #define DMA_READ 0x46 /* DMA read opcode */
98433d6423SLionel Sambuc #define DMA_WRITE 0x4A /* DMA write opcode */
99433d6423SLionel Sambuc
100433d6423SLionel Sambuc /* Parameters for the disk drive. */
101433d6423SLionel Sambuc #define HC_SIZE 2880 /* # sectors on largest legal disk (1.44MB) */
102433d6423SLionel Sambuc #define NR_HEADS 0x02 /* two heads (i.e., two tracks/cylinder) */
103433d6423SLionel Sambuc #define MAX_SECTORS 18 /* largest # sectors per track */
104433d6423SLionel Sambuc #define DTL 0xFF /* determines data length (sector size) */
105433d6423SLionel Sambuc #define SPEC2 0x02 /* second parameter to SPECIFY */
106433d6423SLionel Sambuc #define MOTOR_OFF (3*system_hz) /* how long to wait before stopping motor */
107433d6423SLionel Sambuc #define WAKEUP (2*system_hz) /* timeout on I/O, FDC won't quit. */
108433d6423SLionel Sambuc
109433d6423SLionel Sambuc /* Error codes */
110433d6423SLionel Sambuc #define ERR_SEEK (-1) /* bad seek */
111433d6423SLionel Sambuc #define ERR_TRANSFER (-2) /* bad transfer */
112433d6423SLionel Sambuc #define ERR_STATUS (-3) /* something wrong when getting status */
113433d6423SLionel Sambuc #define ERR_READ_ID (-4) /* bad read id */
114433d6423SLionel Sambuc #define ERR_RECALIBRATE (-5) /* recalibrate didn't work properly */
115433d6423SLionel Sambuc #define ERR_DRIVE (-6) /* something wrong with a drive */
116433d6423SLionel Sambuc #define ERR_WR_PROTECT (-7) /* diskette is write protected */
117433d6423SLionel Sambuc #define ERR_TIMEOUT (-8) /* interrupt timeout */
118433d6423SLionel Sambuc
119433d6423SLionel Sambuc /* No retries on some errors. */
120433d6423SLionel Sambuc #define err_no_retry(err) ((err) <= ERR_WR_PROTECT)
121433d6423SLionel Sambuc
122433d6423SLionel Sambuc /* Encoding of drive type in minor device number. */
123433d6423SLionel Sambuc #define DEV_TYPE_BITS 0x7C /* drive type + 1, if nonzero */
124433d6423SLionel Sambuc #define DEV_TYPE_SHIFT 2 /* right shift to normalize type bits */
125433d6423SLionel Sambuc #define FORMAT_DEV_BIT 0x80 /* bit in minor to turn write into format */
126433d6423SLionel Sambuc
127433d6423SLionel Sambuc /* Miscellaneous. */
128433d6423SLionel Sambuc #define MAX_ERRORS 6 /* how often to try rd/wt before quitting */
129433d6423SLionel Sambuc #define MAX_RESULTS 7 /* max number of bytes controller returns */
130433d6423SLionel Sambuc #define NR_DRIVES 2 /* maximum number of drives */
131433d6423SLionel Sambuc #define DIVISOR 128 /* used for sector size encoding */
132433d6423SLionel Sambuc #define SECTOR_SIZE_CODE 2 /* code to say "512" to the controller */
133433d6423SLionel Sambuc #define TIMEOUT_MICROS 5000000L /* microseconds waiting for FDC */
134433d6423SLionel Sambuc #define NT 7 /* number of diskette/drive combinations */
135433d6423SLionel Sambuc #define UNCALIBRATED 0 /* drive needs to be calibrated at next use */
136433d6423SLionel Sambuc #define CALIBRATED 1 /* no calibration needed */
137433d6423SLionel Sambuc #define BASE_SECTOR 1 /* sectors are numbered starting at 1 */
138433d6423SLionel Sambuc #define NO_SECTOR ((unsigned) -1) /* current sector unknown */
139433d6423SLionel Sambuc #define NO_CYL (-1) /* current cylinder unknown, must seek */
140433d6423SLionel Sambuc #define NO_DENS 100 /* current media unknown */
141433d6423SLionel Sambuc #define BSY_IDLE 0 /* busy doing nothing */
142433d6423SLionel Sambuc #define BSY_IO 1 /* busy doing I/O */
143433d6423SLionel Sambuc #define BSY_WAKEN 2 /* got a wakeup call */
144433d6423SLionel Sambuc
145433d6423SLionel Sambuc /* Seven combinations of diskette/drive are supported.
146433d6423SLionel Sambuc *
147433d6423SLionel Sambuc * # Diskette Drive Sectors Tracks Rotation Data-rate Comment
148433d6423SLionel Sambuc * 0 360K 360K 9 40 300 RPM 250 kbps Standard PC DSDD
149433d6423SLionel Sambuc * 1 1.2M 1.2M 15 80 360 RPM 500 kbps AT disk in AT drive
150433d6423SLionel Sambuc * 2 360K 720K 9 40 300 RPM 250 kbps Quad density PC
151433d6423SLionel Sambuc * 3 720K 720K 9 80 300 RPM 250 kbps Toshiba, et al.
152433d6423SLionel Sambuc * 4 360K 1.2M 9 40 360 RPM 300 kbps PC disk in AT drive
153433d6423SLionel Sambuc * 5 720K 1.2M 9 80 360 RPM 300 kbps Toshiba in AT drive
154433d6423SLionel Sambuc * 6 1.44M 1.44M 18 80 300 RPM 500 kbps PS/2, et al.
155433d6423SLionel Sambuc *
156433d6423SLionel Sambuc * In addition, 720K diskettes can be read in 1.44MB drives, but that does
157433d6423SLionel Sambuc * not need a different set of parameters. This combination uses
158433d6423SLionel Sambuc *
159433d6423SLionel Sambuc * 3 720K 1.44M 9 80 300 RPM 250 kbps PS/2, et al.
160433d6423SLionel Sambuc */
161433d6423SLionel Sambuc static struct density {
162433d6423SLionel Sambuc u8_t secpt; /* sectors per track */
163433d6423SLionel Sambuc u8_t cyls; /* tracks per side */
164433d6423SLionel Sambuc u8_t steps; /* steps per cylinder (2 = double step) */
165433d6423SLionel Sambuc u8_t test; /* sector to try for density test */
166433d6423SLionel Sambuc u8_t rate; /* data rate (2=250, 1=300, 0=500 kbps) */
167433d6423SLionel Sambuc clock_t start_ms; /* motor start (milliseconds) */
168433d6423SLionel Sambuc u8_t gap; /* gap size */
169433d6423SLionel Sambuc u8_t spec1; /* first specify byte (SRT/HUT) */
170433d6423SLionel Sambuc } fdensity[NT] = {
171433d6423SLionel Sambuc { 9, 40, 1, 4*9, 2, 500, 0x2A, 0xDF }, /* 360K / 360K */
172433d6423SLionel Sambuc { 15, 80, 1, 14, 0, 500, 0x1B, 0xDF }, /* 1.2M / 1.2M */
173433d6423SLionel Sambuc { 9, 40, 2, 2*9, 2, 500, 0x2A, 0xDF }, /* 360K / 720K */
174433d6423SLionel Sambuc { 9, 80, 1, 4*9, 2, 750, 0x2A, 0xDF }, /* 720K / 720K */
175433d6423SLionel Sambuc { 9, 40, 2, 2*9, 1, 500, 0x23, 0xDF }, /* 360K / 1.2M */
176433d6423SLionel Sambuc { 9, 80, 1, 4*9, 1, 500, 0x23, 0xDF }, /* 720K / 1.2M */
177433d6423SLionel Sambuc { 18, 80, 1, 17, 0, 750, 0x1B, 0xCF }, /* 1.44M / 1.44M */
178433d6423SLionel Sambuc };
179433d6423SLionel Sambuc
180433d6423SLionel Sambuc /* The following table is used with the test_sector array to recognize a
181433d6423SLionel Sambuc * drive/floppy combination. The sector to test has been determined by
182433d6423SLionel Sambuc * looking at the differences in gap size, sectors/track, and double stepping.
183433d6423SLionel Sambuc * This means that types 0 and 3 can't be told apart, only the motor start
184433d6423SLionel Sambuc * time differs. If a read test succeeds then the drive is limited to the
185433d6423SLionel Sambuc * set of densities it can support to avoid unnecessary tests in the future.
186433d6423SLionel Sambuc */
187433d6423SLionel Sambuc
188433d6423SLionel Sambuc #define b(d) (1 << (d)) /* bit for density d. */
189433d6423SLionel Sambuc
190433d6423SLionel Sambuc static struct test_order {
191433d6423SLionel Sambuc u8_t t_density; /* floppy/drive type */
192433d6423SLionel Sambuc u8_t t_class; /* limit drive to this class of densities */
193433d6423SLionel Sambuc } test_order[NT-1] = {
194433d6423SLionel Sambuc { 6, b(3) | b(6) }, /* 1.44M {720K, 1.44M} */
195433d6423SLionel Sambuc { 1, b(1) | b(4) | b(5) }, /* 1.2M {1.2M, 360K, 720K} */
196433d6423SLionel Sambuc { 3, b(2) | b(3) | b(6) }, /* 720K {360K, 720K, 1.44M} */
197433d6423SLionel Sambuc { 4, b(1) | b(4) | b(5) }, /* 360K {1.2M, 360K, 720K} */
198433d6423SLionel Sambuc { 5, b(1) | b(4) | b(5) }, /* 720K {1.2M, 360K, 720K} */
199433d6423SLionel Sambuc { 2, b(2) | b(3) }, /* 360K {360K, 720K} */
200433d6423SLionel Sambuc /* Note that type 0 is missing, type 3 can read/write it too, which is
201433d6423SLionel Sambuc * why the type 3 parameters have been pessimized to be like type 0.
202433d6423SLionel Sambuc */
203433d6423SLionel Sambuc };
204433d6423SLionel Sambuc
205433d6423SLionel Sambuc /* Variables. */
206433d6423SLionel Sambuc static struct floppy { /* main drive struct, one entry per drive */
207433d6423SLionel Sambuc unsigned fl_curcyl; /* current cylinder */
208433d6423SLionel Sambuc unsigned fl_hardcyl; /* hardware cylinder, as opposed to: */
209433d6423SLionel Sambuc unsigned fl_cylinder; /* cylinder number addressed */
210433d6423SLionel Sambuc unsigned fl_sector; /* sector addressed */
211433d6423SLionel Sambuc unsigned fl_head; /* head number addressed */
212433d6423SLionel Sambuc char fl_calibration; /* CALIBRATED or UNCALIBRATED */
213433d6423SLionel Sambuc u8_t fl_density; /* NO_DENS = ?, 0 = 360K; 1 = 360K/1.2M; etc.*/
214433d6423SLionel Sambuc u8_t fl_class; /* bitmap for possible densities */
215433d6423SLionel Sambuc minix_timer_t fl_tmr_stop; /* timer to stop motor */
216433d6423SLionel Sambuc struct device fl_geom; /* Geometry of the drive */
217433d6423SLionel Sambuc struct device fl_part[NR_PARTITIONS]; /* partition's base & size */
218433d6423SLionel Sambuc } floppy[NR_DRIVES];
219433d6423SLionel Sambuc
220433d6423SLionel Sambuc static int irq_hook_id; /* id of irq hook at the kernel */
221433d6423SLionel Sambuc int motor_status; /* bitmap of current motor status */
222433d6423SLionel Sambuc static int need_reset; /* set to 1 when controller must be reset */
223433d6423SLionel Sambuc unsigned f_drive; /* selected drive */
224433d6423SLionel Sambuc static unsigned f_device; /* selected minor device */
225433d6423SLionel Sambuc static struct floppy *f_fp; /* current drive */
226433d6423SLionel Sambuc static struct density *f_dp; /* current density parameters */
227433d6423SLionel Sambuc static struct density *prev_dp;/* previous density parameters */
228433d6423SLionel Sambuc static unsigned f_sectors; /* equal to f_dp->secpt (needed a lot) */
229433d6423SLionel Sambuc u16_t f_busy; /* BSY_IDLE, BSY_IO, BSY_WAKEN */
230433d6423SLionel Sambuc static struct device *f_dv; /* device's base and size */
231433d6423SLionel Sambuc static struct disk_parameter_s fmt_param; /* parameters for format */
232433d6423SLionel Sambuc static u8_t f_results[MAX_RESULTS];/* the controller can give lots of output */
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc /* The floppy uses various timers. These are managed by the floppy driver
235433d6423SLionel Sambuc * itself, because only a single synchronous alarm is available per process.
236433d6423SLionel Sambuc * Besides the 'f_tmr_timeout' timer below, the floppy structure for each
237433d6423SLionel Sambuc * floppy disk drive contains a 'fl_tmr_stop' timer.
238433d6423SLionel Sambuc */
239433d6423SLionel Sambuc static minix_timer_t f_tmr_timeout; /* timer for various timeouts */
240433d6423SLionel Sambuc static u32_t system_hz; /* system clock frequency */
241433d6423SLionel Sambuc static void f_expire_tmrs(clock_t stamp);
242*cfd712b4SDavid van Moolenbroek static void stop_motor(int arg);
243*cfd712b4SDavid van Moolenbroek static void f_timeout(int arg);
244433d6423SLionel Sambuc
245433d6423SLionel Sambuc static struct device *f_prepare(devminor_t device);
246433d6423SLionel Sambuc static struct device *f_part(devminor_t minor);
247433d6423SLionel Sambuc static void f_cleanup(void);
248433d6423SLionel Sambuc static ssize_t f_transfer(devminor_t minor, int do_write, u64_t position,
249433d6423SLionel Sambuc endpoint_t proc_nr, iovec_t *iov, unsigned int nr_req, int flags);
250433d6423SLionel Sambuc static int dma_setup(int do_write);
251433d6423SLionel Sambuc static void start_motor(void);
252433d6423SLionel Sambuc static int seek(void);
253433d6423SLionel Sambuc static int fdc_transfer(int do_write);
254433d6423SLionel Sambuc static int fdc_results(void);
255433d6423SLionel Sambuc static int fdc_command(const u8_t *cmd, int len);
256433d6423SLionel Sambuc static void fdc_out(int val);
257433d6423SLionel Sambuc static int recalibrate(void);
258433d6423SLionel Sambuc static void f_reset(void);
259433d6423SLionel Sambuc static int f_intr_wait(void);
260433d6423SLionel Sambuc static int read_id(void);
261433d6423SLionel Sambuc static int f_do_open(devminor_t minor, int access);
262433d6423SLionel Sambuc static int f_do_close(devminor_t minor);
263433d6423SLionel Sambuc static int test_read(int density);
264433d6423SLionel Sambuc static void f_geometry(devminor_t minor, struct part_geom *entry);
265433d6423SLionel Sambuc
266433d6423SLionel Sambuc /* Entry points to this driver. */
267433d6423SLionel Sambuc static struct blockdriver f_dtab = {
268433d6423SLionel Sambuc .bdr_type = BLOCKDRIVER_TYPE_DISK, /* handle partition requests */
269433d6423SLionel Sambuc .bdr_open = f_do_open, /* open request, sense type of diskette */
270433d6423SLionel Sambuc .bdr_close = f_do_close, /* nothing on a close */
271433d6423SLionel Sambuc .bdr_transfer = f_transfer, /* do the I/O */
272433d6423SLionel Sambuc .bdr_cleanup = f_cleanup, /* cleanup before sending reply to caller */
273433d6423SLionel Sambuc .bdr_part = f_part, /* return partition information structure */
274433d6423SLionel Sambuc .bdr_geometry = f_geometry, /* tell the geometry of the diskette */
275433d6423SLionel Sambuc .bdr_alarm = f_expire_tmrs /* expire all alarm timers */
276433d6423SLionel Sambuc };
277433d6423SLionel Sambuc
278433d6423SLionel Sambuc static char *floppy_buf;
279433d6423SLionel Sambuc static phys_bytes floppy_buf_phys;
280433d6423SLionel Sambuc
281433d6423SLionel Sambuc /* SEF functions and variables. */
282433d6423SLionel Sambuc static void sef_local_startup(void);
283433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *info);
284433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo);
285433d6423SLionel Sambuc EXTERN int sef_cb_lu_prepare(int state);
286e1f889d2SCristiano Giuffrida EXTERN int sef_cb_lu_state_isvalid(int state, int flags);
287433d6423SLionel Sambuc EXTERN void sef_cb_lu_state_dump(int state);
288433d6423SLionel Sambuc int last_was_write;
289433d6423SLionel Sambuc
290433d6423SLionel Sambuc /*===========================================================================*
291433d6423SLionel Sambuc * floppy_task *
292433d6423SLionel Sambuc *===========================================================================*/
main(void)293433d6423SLionel Sambuc int main(void)
294433d6423SLionel Sambuc {
295433d6423SLionel Sambuc /* SEF local startup. */
296433d6423SLionel Sambuc sef_local_startup();
297433d6423SLionel Sambuc
298433d6423SLionel Sambuc /* Call the generic receive loop. */
299433d6423SLionel Sambuc blockdriver_task(&f_dtab);
300433d6423SLionel Sambuc
301433d6423SLionel Sambuc return(OK);
302433d6423SLionel Sambuc }
303433d6423SLionel Sambuc
304433d6423SLionel Sambuc /*===========================================================================*
305433d6423SLionel Sambuc * sef_local_startup *
306433d6423SLionel Sambuc *===========================================================================*/
sef_local_startup(void)307433d6423SLionel Sambuc static void sef_local_startup(void)
308433d6423SLionel Sambuc {
309433d6423SLionel Sambuc /* Register init callbacks. */
310433d6423SLionel Sambuc sef_setcb_init_fresh(sef_cb_init_fresh);
311433d6423SLionel Sambuc
312433d6423SLionel Sambuc /* Register live update callbacks. */
313433d6423SLionel Sambuc sef_setcb_lu_prepare(sef_cb_lu_prepare);
314433d6423SLionel Sambuc sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
315433d6423SLionel Sambuc sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
316433d6423SLionel Sambuc
317433d6423SLionel Sambuc /* Register signal callbacks. */
318433d6423SLionel Sambuc sef_setcb_signal_handler(sef_cb_signal_handler);
319433d6423SLionel Sambuc
320433d6423SLionel Sambuc /* Let SEF perform startup. */
321433d6423SLionel Sambuc sef_startup();
322433d6423SLionel Sambuc }
323433d6423SLionel Sambuc
324433d6423SLionel Sambuc /*===========================================================================*
325433d6423SLionel Sambuc * sef_cb_init_fresh *
326433d6423SLionel Sambuc *===========================================================================*/
sef_cb_init_fresh(int type,sef_init_info_t * UNUSED (info))327433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
328433d6423SLionel Sambuc {
329433d6423SLionel Sambuc /* Initialize the floppy driver. */
330433d6423SLionel Sambuc struct floppy *fp;
331433d6423SLionel Sambuc int s;
332433d6423SLionel Sambuc
333433d6423SLionel Sambuc /* Initialize the floppy structure and the timers. */
334433d6423SLionel Sambuc system_hz = sys_hz();
335433d6423SLionel Sambuc
336433d6423SLionel Sambuc if(!(floppy_buf = alloc_contig(2*DMA_BUF_SIZE,
337433d6423SLionel Sambuc AC_LOWER16M | AC_ALIGN4K, &floppy_buf_phys)))
338433d6423SLionel Sambuc panic("couldn't allocate dma buffer");
339433d6423SLionel Sambuc
340433d6423SLionel Sambuc init_timer(&f_tmr_timeout);
341433d6423SLionel Sambuc
342433d6423SLionel Sambuc for (fp = &floppy[0]; fp < &floppy[NR_DRIVES]; fp++) {
343433d6423SLionel Sambuc fp->fl_curcyl = NO_CYL;
344433d6423SLionel Sambuc fp->fl_density = NO_DENS;
345433d6423SLionel Sambuc fp->fl_class = ~0;
346433d6423SLionel Sambuc init_timer(&fp->fl_tmr_stop);
347433d6423SLionel Sambuc }
348433d6423SLionel Sambuc
349433d6423SLionel Sambuc /* Set IRQ policy, only request notifications, do not automatically
350433d6423SLionel Sambuc * reenable interrupts. ID return on interrupt is the IRQ line number.
351433d6423SLionel Sambuc */
352433d6423SLionel Sambuc irq_hook_id = FLOPPY_IRQ;
353433d6423SLionel Sambuc if ((s=sys_irqsetpolicy(FLOPPY_IRQ, 0, &irq_hook_id )) != OK)
354433d6423SLionel Sambuc panic("Couldn't set IRQ policy: %d", s);
355433d6423SLionel Sambuc if ((s=sys_irqenable(&irq_hook_id)) != OK)
356433d6423SLionel Sambuc panic("Couldn't enable IRQs: %d", s);
357433d6423SLionel Sambuc
358433d6423SLionel Sambuc /* Announce we are up! */
359433d6423SLionel Sambuc blockdriver_announce(type);
360433d6423SLionel Sambuc
361433d6423SLionel Sambuc return(OK);
362433d6423SLionel Sambuc }
363433d6423SLionel Sambuc
364433d6423SLionel Sambuc /*===========================================================================*
365433d6423SLionel Sambuc * sef_cb_signal_handler *
366433d6423SLionel Sambuc *===========================================================================*/
sef_cb_signal_handler(int signo)367433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo)
368433d6423SLionel Sambuc {
369433d6423SLionel Sambuc int s;
370433d6423SLionel Sambuc
371433d6423SLionel Sambuc /* Only check for termination signal, ignore anything else. */
372433d6423SLionel Sambuc if (signo != SIGTERM) return;
373433d6423SLionel Sambuc
374433d6423SLionel Sambuc /* Stop all activity and cleanly exit with the system. */
375433d6423SLionel Sambuc if ((s=sys_outb(DOR, ENABLE_INT)) != OK)
376433d6423SLionel Sambuc panic("Sys_outb failed: %d", s);
377433d6423SLionel Sambuc exit(0);
378433d6423SLionel Sambuc }
379433d6423SLionel Sambuc
380433d6423SLionel Sambuc /*===========================================================================*
381433d6423SLionel Sambuc * f_expire_tmrs *
382433d6423SLionel Sambuc *===========================================================================*/
f_expire_tmrs(clock_t stamp)383433d6423SLionel Sambuc static void f_expire_tmrs(clock_t stamp)
384433d6423SLionel Sambuc {
385433d6423SLionel Sambuc /* A synchronous alarm message was received. Call the watchdog function for
386433d6423SLionel Sambuc * each expired timer, if any.
387433d6423SLionel Sambuc */
388433d6423SLionel Sambuc
389433d6423SLionel Sambuc expire_timers(stamp);
390433d6423SLionel Sambuc }
391433d6423SLionel Sambuc
392433d6423SLionel Sambuc /*===========================================================================*
393433d6423SLionel Sambuc * f_prepare *
394433d6423SLionel Sambuc *===========================================================================*/
f_prepare(devminor_t device)395433d6423SLionel Sambuc static struct device *f_prepare(devminor_t device)
396433d6423SLionel Sambuc {
397433d6423SLionel Sambuc /* Prepare for I/O on a device. */
398433d6423SLionel Sambuc
399433d6423SLionel Sambuc f_device = device;
400433d6423SLionel Sambuc f_drive = device & ~(DEV_TYPE_BITS | FORMAT_DEV_BIT);
401433d6423SLionel Sambuc if (device < 0 || f_drive >= NR_DRIVES) return(NULL);
402433d6423SLionel Sambuc
403433d6423SLionel Sambuc f_fp = &floppy[f_drive];
404433d6423SLionel Sambuc f_dv = &f_fp->fl_geom;
405433d6423SLionel Sambuc if (f_fp->fl_density < NT) {
406433d6423SLionel Sambuc f_dp = &fdensity[f_fp->fl_density];
407433d6423SLionel Sambuc f_sectors = f_dp->secpt;
408433d6423SLionel Sambuc f_fp->fl_geom.dv_size = (u64_t)(NR_HEADS * f_sectors * f_dp->cyls) *
409433d6423SLionel Sambuc SECTOR_SIZE;
410433d6423SLionel Sambuc }
411433d6423SLionel Sambuc
412433d6423SLionel Sambuc /* A partition? */
413433d6423SLionel Sambuc if ((device &= DEV_TYPE_BITS) >= MINOR_fd0p0)
414433d6423SLionel Sambuc f_dv = &f_fp->fl_part[(device - MINOR_fd0p0) >> DEV_TYPE_SHIFT];
415433d6423SLionel Sambuc
416433d6423SLionel Sambuc return f_dv;
417433d6423SLionel Sambuc }
418433d6423SLionel Sambuc
419433d6423SLionel Sambuc /*===========================================================================*
420433d6423SLionel Sambuc * f_part *
421433d6423SLionel Sambuc *===========================================================================*/
f_part(devminor_t minor)422433d6423SLionel Sambuc static struct device *f_part(devminor_t minor)
423433d6423SLionel Sambuc {
424433d6423SLionel Sambuc /* Return a pointer to the partition information of the given minor device. */
425433d6423SLionel Sambuc
426433d6423SLionel Sambuc return f_prepare(minor);
427433d6423SLionel Sambuc }
428433d6423SLionel Sambuc
429433d6423SLionel Sambuc /*===========================================================================*
430433d6423SLionel Sambuc * f_cleanup *
431433d6423SLionel Sambuc *===========================================================================*/
f_cleanup(void)432433d6423SLionel Sambuc static void f_cleanup(void)
433433d6423SLionel Sambuc {
434433d6423SLionel Sambuc /* Start a timer to turn the motor off in a few seconds. */
435433d6423SLionel Sambuc set_timer(&f_fp->fl_tmr_stop, MOTOR_OFF, stop_motor, f_drive);
436433d6423SLionel Sambuc
437433d6423SLionel Sambuc /* Exiting the floppy driver, so forget where we are. */
438433d6423SLionel Sambuc f_fp->fl_sector = NO_SECTOR;
439433d6423SLionel Sambuc }
440433d6423SLionel Sambuc
441433d6423SLionel Sambuc /*===========================================================================*
442433d6423SLionel Sambuc * f_transfer *
443433d6423SLionel Sambuc *===========================================================================*/
f_transfer(devminor_t minor,int do_write,u64_t pos64,endpoint_t proc_nr,iovec_t * iov,unsigned int nr_req,int UNUSED (flags))444433d6423SLionel Sambuc static ssize_t f_transfer(
445433d6423SLionel Sambuc devminor_t minor, /* minor device number */
446433d6423SLionel Sambuc int do_write, /* read or write? */
447433d6423SLionel Sambuc u64_t pos64, /* offset on device to read or write */
448433d6423SLionel Sambuc endpoint_t proc_nr, /* process doing the request */
449433d6423SLionel Sambuc iovec_t *iov, /* pointer to read or write request vector */
450433d6423SLionel Sambuc unsigned int nr_req, /* length of request vector */
451433d6423SLionel Sambuc int UNUSED(flags) /* transfer flags */
452433d6423SLionel Sambuc )
453433d6423SLionel Sambuc {
454433d6423SLionel Sambuc #define NO_OFFSET -1
455433d6423SLionel Sambuc struct floppy *fp;
456433d6423SLionel Sambuc iovec_t *iop, *iov_end = iov + nr_req;
457433d6423SLionel Sambuc int s, r, errors, nr;
458433d6423SLionel Sambuc unsigned block, nbytes, count, chunk, sector;
459433d6423SLionel Sambuc u64_t dv_size;
460433d6423SLionel Sambuc vir_bytes user_offset, iov_offset = 0, iop_offset;
461433d6423SLionel Sambuc unsigned long position;
462433d6423SLionel Sambuc signed long uoffsets[MAX_SECTORS], *up;
463433d6423SLionel Sambuc cp_grant_id_t ugrants[MAX_SECTORS], *ug = NULL;
464433d6423SLionel Sambuc u8_t cmd[3];
465433d6423SLionel Sambuc ssize_t total;
466433d6423SLionel Sambuc
467433d6423SLionel Sambuc if (f_prepare(minor) == NULL) return(ENXIO);
468433d6423SLionel Sambuc
469433d6423SLionel Sambuc fp = f_fp;
470433d6423SLionel Sambuc dv_size = f_dv->dv_size;
471433d6423SLionel Sambuc
472433d6423SLionel Sambuc if (ex64hi(pos64) != 0)
473433d6423SLionel Sambuc return OK; /* Way beyond EOF */
474433d6423SLionel Sambuc position= pos64;
475433d6423SLionel Sambuc total = 0;
476433d6423SLionel Sambuc
477433d6423SLionel Sambuc /* Record the direction of the last transfer performed. */
478433d6423SLionel Sambuc last_was_write = do_write;
479433d6423SLionel Sambuc
480433d6423SLionel Sambuc /* Check disk address. */
481433d6423SLionel Sambuc if ((position & SECTOR_MASK) != 0) return(EINVAL);
482433d6423SLionel Sambuc
483433d6423SLionel Sambuc #if 0 /* XXX hack to create a disk driver that crashes */
484433d6423SLionel Sambuc { static int count= 0; if (++count > 10) {
485433d6423SLionel Sambuc printf("floppy: time to die\n"); *(int *)-1= 42;
486433d6423SLionel Sambuc }}
487433d6423SLionel Sambuc #endif
488433d6423SLionel Sambuc
489433d6423SLionel Sambuc errors = 0;
490433d6423SLionel Sambuc while (nr_req > 0) {
491433d6423SLionel Sambuc /* How many bytes to transfer? */
492433d6423SLionel Sambuc nbytes = 0;
493433d6423SLionel Sambuc for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size;
494433d6423SLionel Sambuc
495433d6423SLionel Sambuc /* Which block on disk and how close to EOF? */
496433d6423SLionel Sambuc if (position >= dv_size) return(total); /* At EOF */
497433d6423SLionel Sambuc if (position + nbytes > dv_size) nbytes = dv_size - position;
498433d6423SLionel Sambuc block = (unsigned long)((f_dv->dv_base + position) / SECTOR_SIZE);
499433d6423SLionel Sambuc
500433d6423SLionel Sambuc if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
501433d6423SLionel Sambuc
502433d6423SLionel Sambuc /* Using a formatting device? */
503433d6423SLionel Sambuc if (f_device & FORMAT_DEV_BIT) {
504433d6423SLionel Sambuc if (!do_write) return(EIO);
505433d6423SLionel Sambuc if (iov->iov_size < SECTOR_SIZE + sizeof(fmt_param))
506433d6423SLionel Sambuc return(EINVAL);
507433d6423SLionel Sambuc
508433d6423SLionel Sambuc if(proc_nr != SELF) {
509433d6423SLionel Sambuc s=sys_safecopyfrom(proc_nr, iov->iov_addr,
510433d6423SLionel Sambuc SECTOR_SIZE + iov_offset, (vir_bytes) &fmt_param,
511433d6423SLionel Sambuc (phys_bytes) sizeof(fmt_param));
512433d6423SLionel Sambuc if(s != OK)
513433d6423SLionel Sambuc panic("sys_safecopyfrom failed: %d", s);
514433d6423SLionel Sambuc } else {
515433d6423SLionel Sambuc memcpy(&fmt_param, (void *) (iov->iov_addr +
516433d6423SLionel Sambuc SECTOR_SIZE + iov_offset),
517433d6423SLionel Sambuc (phys_bytes) sizeof(fmt_param));
518433d6423SLionel Sambuc }
519433d6423SLionel Sambuc
520433d6423SLionel Sambuc /* Check that the number of sectors in the data is reasonable,
521433d6423SLionel Sambuc * to avoid division by 0. Leave checking of other data to
522433d6423SLionel Sambuc * the FDC.
523433d6423SLionel Sambuc */
524433d6423SLionel Sambuc if (fmt_param.sectors_per_cylinder == 0) return(EIO);
525433d6423SLionel Sambuc
526433d6423SLionel Sambuc /* Only the first sector of the parameters now needed. */
527433d6423SLionel Sambuc iov->iov_size = nbytes = SECTOR_SIZE;
528433d6423SLionel Sambuc }
529433d6423SLionel Sambuc
530433d6423SLionel Sambuc /* Only try one sector if there were errors. */
531433d6423SLionel Sambuc if (errors > 0) nbytes = SECTOR_SIZE;
532433d6423SLionel Sambuc
533433d6423SLionel Sambuc /* Compute cylinder and head of the track to access. */
534433d6423SLionel Sambuc fp->fl_cylinder = block / (NR_HEADS * f_sectors);
535433d6423SLionel Sambuc fp->fl_hardcyl = fp->fl_cylinder * f_dp->steps;
536433d6423SLionel Sambuc fp->fl_head = (block % (NR_HEADS * f_sectors)) / f_sectors;
537433d6423SLionel Sambuc
538433d6423SLionel Sambuc /* For each sector on this track compute the user address it is to
539433d6423SLionel Sambuc * go or to come from.
540433d6423SLionel Sambuc */
541433d6423SLionel Sambuc for (up = uoffsets; up < uoffsets + MAX_SECTORS; up++) *up = NO_OFFSET;
542433d6423SLionel Sambuc count = 0;
543433d6423SLionel Sambuc iop = iov;
544433d6423SLionel Sambuc sector = block % f_sectors;
545433d6423SLionel Sambuc nr = 0;
546433d6423SLionel Sambuc iop_offset = iov_offset;
547433d6423SLionel Sambuc for (;;) {
548433d6423SLionel Sambuc nr++;
549433d6423SLionel Sambuc user_offset = iop_offset;
550433d6423SLionel Sambuc chunk = iop->iov_size;
551433d6423SLionel Sambuc if ((chunk & SECTOR_MASK) != 0) return(EINVAL);
552433d6423SLionel Sambuc
553433d6423SLionel Sambuc while (chunk > 0) {
554433d6423SLionel Sambuc ugrants[sector] = iop->iov_addr;
555433d6423SLionel Sambuc uoffsets[sector++] = user_offset;
556433d6423SLionel Sambuc chunk -= SECTOR_SIZE;
557433d6423SLionel Sambuc user_offset += SECTOR_SIZE;
558433d6423SLionel Sambuc count += SECTOR_SIZE;
559433d6423SLionel Sambuc if (sector == f_sectors || count == nbytes)
560433d6423SLionel Sambuc goto track_set_up;
561433d6423SLionel Sambuc }
562433d6423SLionel Sambuc iop_offset = 0;
563433d6423SLionel Sambuc iop++;
564433d6423SLionel Sambuc }
565433d6423SLionel Sambuc track_set_up:
566433d6423SLionel Sambuc
567433d6423SLionel Sambuc /* First check to see if a reset is needed. */
568433d6423SLionel Sambuc if (need_reset) f_reset();
569433d6423SLionel Sambuc
570433d6423SLionel Sambuc /* See if motor is running; if not, turn it on and wait. */
571433d6423SLionel Sambuc start_motor();
572433d6423SLionel Sambuc
573433d6423SLionel Sambuc /* Set the stepping rate and data rate */
574433d6423SLionel Sambuc if (f_dp != prev_dp) {
575433d6423SLionel Sambuc cmd[0] = FDC_SPECIFY;
576433d6423SLionel Sambuc cmd[1] = f_dp->spec1;
577433d6423SLionel Sambuc cmd[2] = SPEC2;
578433d6423SLionel Sambuc (void) fdc_command(cmd, 3);
579433d6423SLionel Sambuc if ((s=sys_outb(FDC_RATE, f_dp->rate)) != OK)
580433d6423SLionel Sambuc panic("Sys_outb failed: %d", s);
581433d6423SLionel Sambuc prev_dp = f_dp;
582433d6423SLionel Sambuc }
583433d6423SLionel Sambuc
584433d6423SLionel Sambuc /* If we are going to a new cylinder, perform a seek. */
585433d6423SLionel Sambuc r = seek();
586433d6423SLionel Sambuc
587433d6423SLionel Sambuc /* Avoid read_id() if we don't plan to read much. */
588433d6423SLionel Sambuc if (fp->fl_sector == NO_SECTOR && count < (6 * SECTOR_SIZE))
589433d6423SLionel Sambuc fp->fl_sector = 0;
590433d6423SLionel Sambuc
591433d6423SLionel Sambuc for (nbytes = 0; nbytes < count; nbytes += SECTOR_SIZE) {
592433d6423SLionel Sambuc if (fp->fl_sector == NO_SECTOR) {
593433d6423SLionel Sambuc /* Find out what the current sector is. This often
594433d6423SLionel Sambuc * fails right after a seek, so try it twice.
595433d6423SLionel Sambuc */
596433d6423SLionel Sambuc if (r == OK && read_id() != OK) r = read_id();
597433d6423SLionel Sambuc }
598433d6423SLionel Sambuc
599433d6423SLionel Sambuc /* Look for the next job in uoffsets[] */
600433d6423SLionel Sambuc if (r == OK) {
601433d6423SLionel Sambuc for (;;) {
602433d6423SLionel Sambuc if (fp->fl_sector >= f_sectors)
603433d6423SLionel Sambuc fp->fl_sector = 0;
604433d6423SLionel Sambuc
605433d6423SLionel Sambuc up = &uoffsets[fp->fl_sector];
606433d6423SLionel Sambuc ug = &ugrants[fp->fl_sector];
607433d6423SLionel Sambuc if (*up != NO_OFFSET) break;
608433d6423SLionel Sambuc fp->fl_sector++;
609433d6423SLionel Sambuc }
610433d6423SLionel Sambuc
611433d6423SLionel Sambuc if (do_write) {
612433d6423SLionel Sambuc /* Copy the user bytes to the DMA buffer. */
613433d6423SLionel Sambuc if(proc_nr != SELF) {
614433d6423SLionel Sambuc s=sys_safecopyfrom(proc_nr, *ug, *up,
615433d6423SLionel Sambuc (vir_bytes) floppy_buf,
616433d6423SLionel Sambuc (phys_bytes) SECTOR_SIZE);
617433d6423SLionel Sambuc if(s != OK)
618433d6423SLionel Sambuc panic("sys_safecopyfrom failed: %d", s);
619433d6423SLionel Sambuc } else {
620433d6423SLionel Sambuc memcpy(floppy_buf, (void *) (*ug + *up), SECTOR_SIZE);
621433d6423SLionel Sambuc }
622433d6423SLionel Sambuc }
623433d6423SLionel Sambuc }
624433d6423SLionel Sambuc
625433d6423SLionel Sambuc /* Set up the DMA chip and perform the transfer. */
626433d6423SLionel Sambuc if (r == OK) {
627433d6423SLionel Sambuc if (dma_setup(do_write) != OK) {
628433d6423SLionel Sambuc /* This can only fail for addresses above 16MB
629433d6423SLionel Sambuc * that cannot be handled by the controller,
630433d6423SLionel Sambuc * because it uses 24-bit addressing.
631433d6423SLionel Sambuc */
632433d6423SLionel Sambuc return(EIO);
633433d6423SLionel Sambuc }
634433d6423SLionel Sambuc r = fdc_transfer(do_write);
635433d6423SLionel Sambuc }
636433d6423SLionel Sambuc
637433d6423SLionel Sambuc if (r == OK && !do_write) {
638433d6423SLionel Sambuc /* Copy the DMA buffer to user space. */
639433d6423SLionel Sambuc if(proc_nr != SELF) {
640433d6423SLionel Sambuc s=sys_safecopyto(proc_nr, *ug, *up,
641433d6423SLionel Sambuc (vir_bytes) floppy_buf,
642433d6423SLionel Sambuc (phys_bytes) SECTOR_SIZE);
643433d6423SLionel Sambuc if(s != OK)
644433d6423SLionel Sambuc panic("sys_safecopyto failed: %d", s);
645433d6423SLionel Sambuc } else {
646433d6423SLionel Sambuc memcpy((void *) (*ug + *up), floppy_buf, SECTOR_SIZE);
647433d6423SLionel Sambuc }
648433d6423SLionel Sambuc }
649433d6423SLionel Sambuc
650433d6423SLionel Sambuc if (r != OK) {
651433d6423SLionel Sambuc /* Don't retry if write protected or too many errors. */
652433d6423SLionel Sambuc if (err_no_retry(r) || ++errors == MAX_ERRORS) {
653433d6423SLionel Sambuc return(EIO);
654433d6423SLionel Sambuc }
655433d6423SLionel Sambuc
656433d6423SLionel Sambuc /* Recalibrate if halfway. */
657433d6423SLionel Sambuc if (errors == MAX_ERRORS / 2)
658433d6423SLionel Sambuc fp->fl_calibration = UNCALIBRATED;
659433d6423SLionel Sambuc
660433d6423SLionel Sambuc nbytes = 0;
661433d6423SLionel Sambuc break; /* retry */
662433d6423SLionel Sambuc }
663433d6423SLionel Sambuc }
664433d6423SLionel Sambuc
665433d6423SLionel Sambuc /* Book the bytes successfully transferred. */
666433d6423SLionel Sambuc position += nbytes;
667433d6423SLionel Sambuc total += nbytes;
668433d6423SLionel Sambuc while (nbytes > 0) {
669433d6423SLionel Sambuc if (nbytes < iov->iov_size) {
670433d6423SLionel Sambuc /* Not done with this one yet. */
671433d6423SLionel Sambuc iov_offset += nbytes;
672433d6423SLionel Sambuc iov->iov_size -= nbytes;
673433d6423SLionel Sambuc break;
674433d6423SLionel Sambuc }
675433d6423SLionel Sambuc iov_offset = 0;
676433d6423SLionel Sambuc nbytes -= iov->iov_size;
677433d6423SLionel Sambuc iov->iov_size = 0;
678433d6423SLionel Sambuc iov++;
679433d6423SLionel Sambuc nr_req--;
680433d6423SLionel Sambuc }
681433d6423SLionel Sambuc }
682433d6423SLionel Sambuc return(total);
683433d6423SLionel Sambuc }
684433d6423SLionel Sambuc
685433d6423SLionel Sambuc /*===========================================================================*
686433d6423SLionel Sambuc * dma_setup *
687433d6423SLionel Sambuc *===========================================================================*/
dma_setup(int do_write)688433d6423SLionel Sambuc static int dma_setup(int do_write)
689433d6423SLionel Sambuc {
690433d6423SLionel Sambuc /* The IBM PC can perform DMA operations by using the DMA chip. To use it,
691433d6423SLionel Sambuc * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
692433d6423SLionel Sambuc * to be read from or written to, the byte count minus 1, and a read or write
693433d6423SLionel Sambuc * opcode. This routine sets up the DMA chip. Note that the chip is not
694433d6423SLionel Sambuc * capable of doing a DMA across a 64K boundary (e.g., you can't read a
695433d6423SLionel Sambuc * 512-byte block starting at physical address 65520).
696433d6423SLionel Sambuc *
697433d6423SLionel Sambuc * Warning! Also note that it's not possible to do DMA above 16 MB because
698433d6423SLionel Sambuc * the ISA bus uses 24-bit addresses. Addresses above 16 MB therefore will
699433d6423SLionel Sambuc * be interpreted modulo 16 MB, dangerously overwriting arbitrary memory.
700433d6423SLionel Sambuc * A check here denies the I/O if the address is out of range.
701433d6423SLionel Sambuc */
702433d6423SLionel Sambuc pvb_pair_t byte_out[9];
703433d6423SLionel Sambuc int s;
704433d6423SLionel Sambuc
705433d6423SLionel Sambuc /* First check the DMA memory address not to exceed maximum. */
706433d6423SLionel Sambuc if (floppy_buf_phys != (floppy_buf_phys & DMA_ADDR_MASK)) {
707433d6423SLionel Sambuc printf("floppy: DMA denied because address out of range\n");
708433d6423SLionel Sambuc return(EIO);
709433d6423SLionel Sambuc }
710433d6423SLionel Sambuc
711433d6423SLionel Sambuc /* Set up the DMA registers. (The comment on the reset is a bit strong,
712433d6423SLionel Sambuc * it probably only resets the floppy channel.)
713433d6423SLionel Sambuc */
714433d6423SLionel Sambuc pv_set(byte_out[0], DMA_INIT, DMA_RESET_VAL); /* reset the dma controller */
715433d6423SLionel Sambuc pv_set(byte_out[1], DMA_FLIPFLOP, 0); /* write anything to reset it */
716433d6423SLionel Sambuc pv_set(byte_out[2], DMA_MODE, do_write ? DMA_WRITE : DMA_READ);
717433d6423SLionel Sambuc pv_set(byte_out[3], DMA_ADDR, (unsigned) (floppy_buf_phys >> 0) & 0xff);
718433d6423SLionel Sambuc pv_set(byte_out[4], DMA_ADDR, (unsigned) (floppy_buf_phys >> 8) & 0xff);
719433d6423SLionel Sambuc pv_set(byte_out[5], DMA_TOP, (unsigned) (floppy_buf_phys >> 16) & 0xff);
720433d6423SLionel Sambuc pv_set(byte_out[6], DMA_COUNT, (((SECTOR_SIZE - 1) >> 0)) & 0xff);
721433d6423SLionel Sambuc pv_set(byte_out[7], DMA_COUNT, (SECTOR_SIZE - 1) >> 8);
722433d6423SLionel Sambuc pv_set(byte_out[8], DMA_INIT, 2); /* some sort of enable */
723433d6423SLionel Sambuc
724433d6423SLionel Sambuc if ((s=sys_voutb(byte_out, 9)) != OK)
725433d6423SLionel Sambuc panic("Sys_voutb in dma_setup() failed: %d", s);
726433d6423SLionel Sambuc return(OK);
727433d6423SLionel Sambuc }
728433d6423SLionel Sambuc
729433d6423SLionel Sambuc /*===========================================================================*
730433d6423SLionel Sambuc * start_motor *
731433d6423SLionel Sambuc *===========================================================================*/
start_motor(void)732433d6423SLionel Sambuc static void start_motor(void)
733433d6423SLionel Sambuc {
734433d6423SLionel Sambuc /* Control of the floppy disk motors is a big pain. If a motor is off, you
735433d6423SLionel Sambuc * have to turn it on first, which takes 1/2 second. You can't leave it on
736433d6423SLionel Sambuc * all the time, since that would wear out the diskette. However, if you turn
737433d6423SLionel Sambuc * the motor off after each operation, the system performance will be awful.
738433d6423SLionel Sambuc * The compromise used here is to leave it on for a few seconds after each
739433d6423SLionel Sambuc * operation. If a new operation is started in that interval, it need not be
740433d6423SLionel Sambuc * turned on again. If no new operation is started, a timer goes off and the
741433d6423SLionel Sambuc * motor is turned off. I/O port DOR has bits to control each of 4 drives.
742433d6423SLionel Sambuc */
743433d6423SLionel Sambuc
744433d6423SLionel Sambuc int s, motor_bit, running;
745433d6423SLionel Sambuc message mess;
746433d6423SLionel Sambuc int ipc_status;
747433d6423SLionel Sambuc
748433d6423SLionel Sambuc motor_bit = 1 << f_drive; /* bit mask for this drive */
749433d6423SLionel Sambuc running = motor_status & motor_bit; /* nonzero if this motor is running */
750433d6423SLionel Sambuc motor_status |= motor_bit; /* want this drive running too */
751433d6423SLionel Sambuc
752433d6423SLionel Sambuc if ((s=sys_outb(DOR,
753433d6423SLionel Sambuc (motor_status << MOTOR_SHIFT) | ENABLE_INT | f_drive)) != OK)
754433d6423SLionel Sambuc panic("Sys_outb in start_motor() failed: %d", s);
755433d6423SLionel Sambuc
756433d6423SLionel Sambuc /* If the motor was already running, we don't have to wait for it. */
757433d6423SLionel Sambuc if (running) return; /* motor was already running */
758433d6423SLionel Sambuc
759433d6423SLionel Sambuc /* Set an alarm timer to force a timeout if the hardware does not interrupt
760433d6423SLionel Sambuc * in time. Expect an interrupt, but check for a timeout.
761433d6423SLionel Sambuc */
762433d6423SLionel Sambuc set_timer(&f_tmr_timeout, f_dp->start_ms * system_hz / 1000, f_timeout, 0);
763433d6423SLionel Sambuc f_busy = BSY_IO;
764433d6423SLionel Sambuc do {
765433d6423SLionel Sambuc if ((s = driver_receive(ANY, &mess, &ipc_status)) != OK)
766433d6423SLionel Sambuc panic("Couldn't receive message: %d", s);
767433d6423SLionel Sambuc
768433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
769433d6423SLionel Sambuc switch (_ENDPOINT_P(mess.m_source)) {
770433d6423SLionel Sambuc case CLOCK:
771433d6423SLionel Sambuc f_expire_tmrs(mess.m_notify.timestamp);
772433d6423SLionel Sambuc break;
773433d6423SLionel Sambuc default :
774433d6423SLionel Sambuc f_busy = BSY_IDLE;
775433d6423SLionel Sambuc break;
776433d6423SLionel Sambuc }
777433d6423SLionel Sambuc } else {
778433d6423SLionel Sambuc f_busy = BSY_IDLE;
779433d6423SLionel Sambuc }
780433d6423SLionel Sambuc } while (f_busy == BSY_IO);
781433d6423SLionel Sambuc f_fp->fl_sector = NO_SECTOR;
782433d6423SLionel Sambuc }
783433d6423SLionel Sambuc
784433d6423SLionel Sambuc /*===========================================================================*
785433d6423SLionel Sambuc * stop_motor *
786433d6423SLionel Sambuc *===========================================================================*/
stop_motor(int arg)787*cfd712b4SDavid van Moolenbroek static void stop_motor(int arg)
788433d6423SLionel Sambuc {
789433d6423SLionel Sambuc /* This routine is called from an alarm timer after several seconds have
790433d6423SLionel Sambuc * elapsed with no floppy disk activity. It turns the drive motor off.
791433d6423SLionel Sambuc */
792433d6423SLionel Sambuc int s;
793*cfd712b4SDavid van Moolenbroek motor_status &= ~(1 << arg);
794433d6423SLionel Sambuc if ((s=sys_outb(DOR, (motor_status << MOTOR_SHIFT) | ENABLE_INT)) != OK)
795433d6423SLionel Sambuc panic("Sys_outb in stop_motor() failed: %d", s);
796433d6423SLionel Sambuc }
797433d6423SLionel Sambuc
798433d6423SLionel Sambuc /*===========================================================================*
799433d6423SLionel Sambuc * seek *
800433d6423SLionel Sambuc *===========================================================================*/
seek(void)801433d6423SLionel Sambuc static int seek(void)
802433d6423SLionel Sambuc {
803433d6423SLionel Sambuc /* Issue a SEEK command on the indicated drive unless the arm is already
804433d6423SLionel Sambuc * positioned on the correct cylinder.
805433d6423SLionel Sambuc */
806433d6423SLionel Sambuc
807433d6423SLionel Sambuc struct floppy *fp = f_fp;
808433d6423SLionel Sambuc int r;
809433d6423SLionel Sambuc message mess;
810433d6423SLionel Sambuc int ipc_status;
811433d6423SLionel Sambuc u8_t cmd[3];
812433d6423SLionel Sambuc
813433d6423SLionel Sambuc /* Are we already on the correct cylinder? */
814433d6423SLionel Sambuc if (fp->fl_calibration == UNCALIBRATED)
815433d6423SLionel Sambuc if (recalibrate() != OK) return(ERR_SEEK);
816433d6423SLionel Sambuc if (fp->fl_curcyl == fp->fl_hardcyl) return(OK);
817433d6423SLionel Sambuc
818433d6423SLionel Sambuc /* No. Wrong cylinder. Issue a SEEK and wait for interrupt. */
819433d6423SLionel Sambuc cmd[0] = FDC_SEEK;
820433d6423SLionel Sambuc cmd[1] = (fp->fl_head << 2) | f_drive;
821433d6423SLionel Sambuc cmd[2] = fp->fl_hardcyl;
822433d6423SLionel Sambuc if (fdc_command(cmd, 3) != OK) return(ERR_SEEK);
823433d6423SLionel Sambuc if (f_intr_wait() != OK) return(ERR_TIMEOUT);
824433d6423SLionel Sambuc
825433d6423SLionel Sambuc /* Interrupt has been received. Check drive status. */
826433d6423SLionel Sambuc fdc_out(FDC_SENSE); /* probe FDC to make it return status */
827433d6423SLionel Sambuc r = fdc_results(); /* get controller status bytes */
828433d6423SLionel Sambuc if (r != OK || (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0
829433d6423SLionel Sambuc || f_results[ST1] != fp->fl_hardcyl) {
830433d6423SLionel Sambuc /* seek failed, may need a recalibrate */
831433d6423SLionel Sambuc return(ERR_SEEK);
832433d6423SLionel Sambuc }
833433d6423SLionel Sambuc /* Give head time to settle on a format, no retrying here! */
834433d6423SLionel Sambuc if (f_device & FORMAT_DEV_BIT) {
835433d6423SLionel Sambuc /* Set a synchronous alarm to force a timeout if the hardware does
836433d6423SLionel Sambuc * not interrupt.
837433d6423SLionel Sambuc */
838433d6423SLionel Sambuc set_timer(&f_tmr_timeout, system_hz/30, f_timeout, 0);
839433d6423SLionel Sambuc f_busy = BSY_IO;
840433d6423SLionel Sambuc do {
841433d6423SLionel Sambuc if ((r = driver_receive(ANY, &mess, &ipc_status)) != OK)
842433d6423SLionel Sambuc panic("Couldn't receive message: %d", r);
843433d6423SLionel Sambuc
844433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
845433d6423SLionel Sambuc switch (_ENDPOINT_P(mess.m_source)) {
846433d6423SLionel Sambuc case CLOCK:
847433d6423SLionel Sambuc f_expire_tmrs(mess.m_notify.timestamp);
848433d6423SLionel Sambuc break;
849433d6423SLionel Sambuc default :
850433d6423SLionel Sambuc f_busy = BSY_IDLE;
851433d6423SLionel Sambuc break;
852433d6423SLionel Sambuc }
853433d6423SLionel Sambuc } else {
854433d6423SLionel Sambuc f_busy = BSY_IDLE;
855433d6423SLionel Sambuc }
856433d6423SLionel Sambuc } while (f_busy == BSY_IO);
857433d6423SLionel Sambuc }
858433d6423SLionel Sambuc fp->fl_curcyl = fp->fl_hardcyl;
859433d6423SLionel Sambuc fp->fl_sector = NO_SECTOR;
860433d6423SLionel Sambuc return(OK);
861433d6423SLionel Sambuc }
862433d6423SLionel Sambuc
863433d6423SLionel Sambuc /*===========================================================================*
864433d6423SLionel Sambuc * fdc_transfer *
865433d6423SLionel Sambuc *===========================================================================*/
fdc_transfer(int do_write)866433d6423SLionel Sambuc static int fdc_transfer(int do_write)
867433d6423SLionel Sambuc {
868433d6423SLionel Sambuc /* The drive is now on the proper cylinder. Read, write or format 1 block. */
869433d6423SLionel Sambuc
870433d6423SLionel Sambuc struct floppy *fp = f_fp;
871433d6423SLionel Sambuc int r, s;
872433d6423SLionel Sambuc u8_t cmd[9];
873433d6423SLionel Sambuc
874433d6423SLionel Sambuc /* Never attempt a transfer if the drive is uncalibrated or motor is off. */
875433d6423SLionel Sambuc if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER);
876433d6423SLionel Sambuc if ((motor_status & (1 << f_drive)) == 0) return(ERR_TRANSFER);
877433d6423SLionel Sambuc
878433d6423SLionel Sambuc /* The command is issued by outputting several bytes to the controller chip.
879433d6423SLionel Sambuc */
880433d6423SLionel Sambuc if (f_device & FORMAT_DEV_BIT) {
881433d6423SLionel Sambuc cmd[0] = FDC_FORMAT;
882433d6423SLionel Sambuc cmd[1] = (fp->fl_head << 2) | f_drive;
883433d6423SLionel Sambuc cmd[2] = fmt_param.sector_size_code;
884433d6423SLionel Sambuc cmd[3] = fmt_param.sectors_per_cylinder;
885433d6423SLionel Sambuc cmd[4] = fmt_param.gap_length_for_format;
886433d6423SLionel Sambuc cmd[5] = fmt_param.fill_byte_for_format;
887433d6423SLionel Sambuc if (fdc_command(cmd, 6) != OK) return(ERR_TRANSFER);
888433d6423SLionel Sambuc } else {
889433d6423SLionel Sambuc cmd[0] = do_write ? FDC_WRITE : FDC_READ;
890433d6423SLionel Sambuc cmd[1] = (fp->fl_head << 2) | f_drive;
891433d6423SLionel Sambuc cmd[2] = fp->fl_cylinder;
892433d6423SLionel Sambuc cmd[3] = fp->fl_head;
893433d6423SLionel Sambuc cmd[4] = BASE_SECTOR + fp->fl_sector;
894433d6423SLionel Sambuc cmd[5] = SECTOR_SIZE_CODE;
895433d6423SLionel Sambuc cmd[6] = f_sectors;
896433d6423SLionel Sambuc cmd[7] = f_dp->gap; /* sector gap */
897433d6423SLionel Sambuc cmd[8] = DTL; /* data length */
898433d6423SLionel Sambuc if (fdc_command(cmd, 9) != OK) return(ERR_TRANSFER);
899433d6423SLionel Sambuc }
900433d6423SLionel Sambuc
901433d6423SLionel Sambuc /* Block, waiting for disk interrupt. */
902433d6423SLionel Sambuc if (f_intr_wait() != OK) {
903433d6423SLionel Sambuc printf("fd%u: disk interrupt timed out.\n", f_drive);
904433d6423SLionel Sambuc return(ERR_TIMEOUT);
905433d6423SLionel Sambuc }
906433d6423SLionel Sambuc
907433d6423SLionel Sambuc /* Get controller status and check for errors. */
908433d6423SLionel Sambuc r = fdc_results();
909433d6423SLionel Sambuc if (r != OK) return(r);
910433d6423SLionel Sambuc
911433d6423SLionel Sambuc if (f_results[ST1] & WRITE_PROTECT) {
912433d6423SLionel Sambuc printf("fd%u: diskette is write protected.\n", f_drive);
913433d6423SLionel Sambuc return(ERR_WR_PROTECT);
914433d6423SLionel Sambuc }
915433d6423SLionel Sambuc
916433d6423SLionel Sambuc if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_TRANSFER);
917433d6423SLionel Sambuc if (f_results[ST1] | f_results[ST2]) return(ERR_TRANSFER);
918433d6423SLionel Sambuc
919433d6423SLionel Sambuc if (f_device & FORMAT_DEV_BIT) return(OK);
920433d6423SLionel Sambuc
921433d6423SLionel Sambuc /* Compare actual numbers of sectors transferred with expected number. */
922433d6423SLionel Sambuc s = (f_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * f_sectors;
923433d6423SLionel Sambuc s += (f_results[ST_HEAD] - fp->fl_head) * f_sectors;
924433d6423SLionel Sambuc s += (f_results[ST_SEC] - BASE_SECTOR - fp->fl_sector);
925433d6423SLionel Sambuc if (s != 1) return(ERR_TRANSFER);
926433d6423SLionel Sambuc
927433d6423SLionel Sambuc /* This sector is next for I/O: */
928433d6423SLionel Sambuc fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR;
929433d6423SLionel Sambuc #if 0
930433d6423SLionel Sambuc if (processor < 386) fp->fl_sector++; /* Old CPU can't keep up. */
931433d6423SLionel Sambuc #endif
932433d6423SLionel Sambuc return(OK);
933433d6423SLionel Sambuc }
934433d6423SLionel Sambuc
935433d6423SLionel Sambuc /*===========================================================================*
936433d6423SLionel Sambuc * fdc_results *
937433d6423SLionel Sambuc *===========================================================================*/
fdc_results(void)938433d6423SLionel Sambuc static int fdc_results(void)
939433d6423SLionel Sambuc {
940433d6423SLionel Sambuc /* Extract results from the controller after an operation, then allow floppy
941433d6423SLionel Sambuc * interrupts again.
942433d6423SLionel Sambuc */
943433d6423SLionel Sambuc
944433d6423SLionel Sambuc int s, result_nr;
945433d6423SLionel Sambuc u32_t status;
946433d6423SLionel Sambuc spin_t spin;
947433d6423SLionel Sambuc
948433d6423SLionel Sambuc /* Extract bytes from FDC until it says it has no more. The loop is
949433d6423SLionel Sambuc * really an outer loop on result_nr and an inner loop on status.
950433d6423SLionel Sambuc * A timeout flag alarm is set.
951433d6423SLionel Sambuc */
952433d6423SLionel Sambuc result_nr = 0;
953433d6423SLionel Sambuc SPIN_FOR(&spin, TIMEOUT_MICROS) {
954433d6423SLionel Sambuc /* Reading one byte is almost a mirror of fdc_out() - the DIRECTION
955433d6423SLionel Sambuc * bit must be set instead of clear, but the CTL_BUSY bit destroys
956433d6423SLionel Sambuc * the perfection of the mirror.
957433d6423SLionel Sambuc */
958433d6423SLionel Sambuc if ((s=sys_inb(FDC_STATUS, &status)) != OK)
959433d6423SLionel Sambuc panic("Sys_inb in fdc_results() failed: %d", s);
960433d6423SLionel Sambuc status &= (MASTER | DIRECTION | CTL_BUSY);
961433d6423SLionel Sambuc if (status == (MASTER | DIRECTION | CTL_BUSY)) {
962433d6423SLionel Sambuc u32_t tmp_r;
963433d6423SLionel Sambuc if (result_nr >= MAX_RESULTS) break; /* too many results */
964433d6423SLionel Sambuc if ((s=sys_inb(FDC_DATA, &tmp_r)) != OK)
965433d6423SLionel Sambuc panic("Sys_inb in fdc_results() failed: %d", s);
966433d6423SLionel Sambuc f_results[result_nr] = tmp_r;
967433d6423SLionel Sambuc result_nr ++;
968433d6423SLionel Sambuc continue;
969433d6423SLionel Sambuc }
970433d6423SLionel Sambuc if (status == MASTER) { /* all read */
971433d6423SLionel Sambuc if ((s=sys_irqenable(&irq_hook_id)) != OK)
972433d6423SLionel Sambuc panic("Couldn't enable IRQs: %d", s);
973433d6423SLionel Sambuc
974433d6423SLionel Sambuc return(OK); /* only good exit */
975433d6423SLionel Sambuc }
976433d6423SLionel Sambuc }
977433d6423SLionel Sambuc need_reset = TRUE; /* controller chip must be reset */
978433d6423SLionel Sambuc
979433d6423SLionel Sambuc if ((s=sys_irqenable(&irq_hook_id)) != OK)
980433d6423SLionel Sambuc panic("Couldn't enable IRQs: %d", s);
981433d6423SLionel Sambuc return(ERR_STATUS);
982433d6423SLionel Sambuc }
983433d6423SLionel Sambuc
984433d6423SLionel Sambuc /*===========================================================================*
985433d6423SLionel Sambuc * fdc_command *
986433d6423SLionel Sambuc *===========================================================================*/
fdc_command(const u8_t * cmd,int len)987433d6423SLionel Sambuc static int fdc_command(
988433d6423SLionel Sambuc const u8_t *cmd, /* command bytes */
989433d6423SLionel Sambuc int len /* command length */
990433d6423SLionel Sambuc )
991433d6423SLionel Sambuc {
992433d6423SLionel Sambuc /* Output a command to the controller. */
993433d6423SLionel Sambuc
994433d6423SLionel Sambuc /* Set a synchronous alarm to force a timeout if the hardware does
995433d6423SLionel Sambuc * not interrupt.
996433d6423SLionel Sambuc * Note that the actual check is done by the code that issued the
997433d6423SLionel Sambuc * fdc_command() call.
998433d6423SLionel Sambuc */
999433d6423SLionel Sambuc set_timer(&f_tmr_timeout, WAKEUP, f_timeout, 0);
1000433d6423SLionel Sambuc
1001433d6423SLionel Sambuc f_busy = BSY_IO;
1002433d6423SLionel Sambuc while (len > 0) {
1003433d6423SLionel Sambuc fdc_out(*cmd++);
1004433d6423SLionel Sambuc len--;
1005433d6423SLionel Sambuc }
1006433d6423SLionel Sambuc return(need_reset ? ERR_DRIVE : OK);
1007433d6423SLionel Sambuc }
1008433d6423SLionel Sambuc
1009433d6423SLionel Sambuc /*===========================================================================*
1010433d6423SLionel Sambuc * fdc_out *
1011433d6423SLionel Sambuc *===========================================================================*/
fdc_out(int val)1012433d6423SLionel Sambuc static void fdc_out(
1013433d6423SLionel Sambuc int val /* write this byte to floppy disk controller */
1014433d6423SLionel Sambuc )
1015433d6423SLionel Sambuc {
1016433d6423SLionel Sambuc /* Output a byte to the controller. This is not entirely trivial, since you
1017433d6423SLionel Sambuc * can only write to it when it is listening, and it decides when to listen.
1018433d6423SLionel Sambuc * If the controller refuses to listen, the FDC chip is given a hard reset.
1019433d6423SLionel Sambuc */
1020433d6423SLionel Sambuc spin_t spin;
1021433d6423SLionel Sambuc int s;
1022433d6423SLionel Sambuc u32_t status;
1023433d6423SLionel Sambuc
1024433d6423SLionel Sambuc if (need_reset) return; /* if controller is not listening, return */
1025433d6423SLionel Sambuc
1026433d6423SLionel Sambuc /* It may take several tries to get the FDC to accept a command. */
1027433d6423SLionel Sambuc SPIN_FOR(&spin, TIMEOUT_MICROS) {
1028433d6423SLionel Sambuc if ((s=sys_inb(FDC_STATUS, &status)) != OK)
1029433d6423SLionel Sambuc panic("Sys_inb in fdc_out() failed: %d", s);
1030433d6423SLionel Sambuc
1031433d6423SLionel Sambuc if ((status & (MASTER | DIRECTION)) == (MASTER | 0)) {
1032433d6423SLionel Sambuc if ((s=sys_outb(FDC_DATA, val)) != OK)
1033433d6423SLionel Sambuc panic("Sys_outb in fdc_out() failed: %d", s);
1034433d6423SLionel Sambuc
1035433d6423SLionel Sambuc return;
1036433d6423SLionel Sambuc }
1037433d6423SLionel Sambuc }
1038433d6423SLionel Sambuc
1039433d6423SLionel Sambuc need_reset = TRUE; /* hit it over the head */
1040433d6423SLionel Sambuc }
1041433d6423SLionel Sambuc
1042433d6423SLionel Sambuc /*===========================================================================*
1043433d6423SLionel Sambuc * recalibrate *
1044433d6423SLionel Sambuc *===========================================================================*/
recalibrate(void)1045433d6423SLionel Sambuc static int recalibrate(void)
1046433d6423SLionel Sambuc {
1047433d6423SLionel Sambuc /* The floppy disk controller has no way of determining its absolute arm
1048433d6423SLionel Sambuc * position (cylinder). Instead, it steps the arm a cylinder at a time and
1049433d6423SLionel Sambuc * keeps track of where it thinks it is (in software). However, after a
1050433d6423SLionel Sambuc * SEEK, the hardware reads information from the diskette telling where the
1051433d6423SLionel Sambuc * arm actually is. If the arm is in the wrong place, a recalibration is done,
1052433d6423SLionel Sambuc * which forces the arm to cylinder 0. This way the controller can get back
1053433d6423SLionel Sambuc * into sync with reality.
1054433d6423SLionel Sambuc */
1055433d6423SLionel Sambuc
1056433d6423SLionel Sambuc struct floppy *fp = f_fp;
1057433d6423SLionel Sambuc int r;
1058433d6423SLionel Sambuc u8_t cmd[2];
1059433d6423SLionel Sambuc
1060433d6423SLionel Sambuc /* Issue the RECALIBRATE command and wait for the interrupt. */
1061433d6423SLionel Sambuc cmd[0] = FDC_RECALIBRATE; /* tell drive to recalibrate itself */
1062433d6423SLionel Sambuc cmd[1] = f_drive; /* specify drive */
1063433d6423SLionel Sambuc if (fdc_command(cmd, 2) != OK) return(ERR_SEEK);
1064433d6423SLionel Sambuc if (f_intr_wait() != OK) return(ERR_TIMEOUT);
1065433d6423SLionel Sambuc
1066433d6423SLionel Sambuc /* Determine if the recalibration succeeded. */
1067433d6423SLionel Sambuc fdc_out(FDC_SENSE); /* issue SENSE command to request results */
1068433d6423SLionel Sambuc r = fdc_results(); /* get results of the FDC_RECALIBRATE command*/
1069433d6423SLionel Sambuc fp->fl_curcyl = NO_CYL; /* force a SEEK next time */
1070433d6423SLionel Sambuc fp->fl_sector = NO_SECTOR;
1071433d6423SLionel Sambuc if (r != OK || /* controller would not respond */
1072433d6423SLionel Sambuc (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0 || f_results[ST_PCN] != 0) {
1073433d6423SLionel Sambuc /* Recalibration failed. FDC must be reset. */
1074433d6423SLionel Sambuc need_reset = TRUE;
1075433d6423SLionel Sambuc return(ERR_RECALIBRATE);
1076433d6423SLionel Sambuc } else {
1077433d6423SLionel Sambuc /* Recalibration succeeded. */
1078433d6423SLionel Sambuc fp->fl_calibration = CALIBRATED;
1079433d6423SLionel Sambuc fp->fl_curcyl = f_results[ST_PCN];
1080433d6423SLionel Sambuc return(OK);
1081433d6423SLionel Sambuc }
1082433d6423SLionel Sambuc }
1083433d6423SLionel Sambuc
1084433d6423SLionel Sambuc /*===========================================================================*
1085433d6423SLionel Sambuc * f_reset *
1086433d6423SLionel Sambuc *===========================================================================*/
f_reset(void)1087433d6423SLionel Sambuc static void f_reset(void)
1088433d6423SLionel Sambuc {
1089433d6423SLionel Sambuc /* Issue a reset to the controller. This is done after any catastrophe,
1090433d6423SLionel Sambuc * like the controller refusing to respond.
1091433d6423SLionel Sambuc */
1092433d6423SLionel Sambuc pvb_pair_t byte_out[2];
1093433d6423SLionel Sambuc int s,i;
1094433d6423SLionel Sambuc message mess;
1095433d6423SLionel Sambuc int ipc_status;
1096433d6423SLionel Sambuc
1097433d6423SLionel Sambuc /* Disable interrupts and strobe reset bit low. */
1098433d6423SLionel Sambuc need_reset = FALSE;
1099433d6423SLionel Sambuc
1100433d6423SLionel Sambuc /* It is not clear why the next lock is needed. Writing 0 to DOR causes
1101433d6423SLionel Sambuc * interrupt, while the PC documentation says turning bit 8 off disables
1102433d6423SLionel Sambuc * interrupts. Without the lock:
1103433d6423SLionel Sambuc * 1) the interrupt handler sets the floppy mask bit in the 8259.
1104433d6423SLionel Sambuc * 2) writing ENABLE_INT to DOR causes the FDC to assert the interrupt
1105433d6423SLionel Sambuc * line again, but the mask stops the cpu being interrupted.
1106433d6423SLionel Sambuc * 3) the sense interrupt clears the interrupt (not clear which one).
1107433d6423SLionel Sambuc * and for some reason the reset does not work.
1108433d6423SLionel Sambuc */
1109433d6423SLionel Sambuc (void) fdc_command((u8_t *) 0, 0); /* need only the timer */
1110433d6423SLionel Sambuc motor_status = 0;
1111433d6423SLionel Sambuc pv_set(byte_out[0], DOR, 0); /* strobe reset bit low */
1112433d6423SLionel Sambuc pv_set(byte_out[1], DOR, ENABLE_INT); /* strobe it high again */
1113433d6423SLionel Sambuc if ((s=sys_voutb(byte_out, 2)) != OK)
1114433d6423SLionel Sambuc panic("Sys_voutb in f_reset() failed: %d", s);
1115433d6423SLionel Sambuc
1116433d6423SLionel Sambuc /* A synchronous alarm timer was set in fdc_command. Expect an interrupt,
1117433d6423SLionel Sambuc * but be prepared to handle a timeout.
1118433d6423SLionel Sambuc */
1119433d6423SLionel Sambuc do {
1120433d6423SLionel Sambuc if ((s = driver_receive(ANY, &mess, &ipc_status)) != OK)
1121433d6423SLionel Sambuc panic("Couldn't receive message: %d", s);
1122433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
1123433d6423SLionel Sambuc switch (_ENDPOINT_P(mess.m_source)) {
1124433d6423SLionel Sambuc case CLOCK:
1125433d6423SLionel Sambuc f_expire_tmrs(mess.m_notify.timestamp);
1126433d6423SLionel Sambuc break;
1127433d6423SLionel Sambuc default :
1128433d6423SLionel Sambuc f_busy = BSY_IDLE;
1129433d6423SLionel Sambuc break;
1130433d6423SLionel Sambuc }
1131433d6423SLionel Sambuc } else { /* expect hw interrupt */
1132433d6423SLionel Sambuc f_busy = BSY_IDLE;
1133433d6423SLionel Sambuc }
1134433d6423SLionel Sambuc } while (f_busy == BSY_IO);
1135433d6423SLionel Sambuc
1136433d6423SLionel Sambuc /* The controller supports 4 drives and returns a result for each of them.
1137433d6423SLionel Sambuc * Collect all the results now. The old version only collected the first
1138433d6423SLionel Sambuc * result. This happens to work for 2 drives, but it doesn't work for 3
1139433d6423SLionel Sambuc * or more drives, at least with only drives 0 and 2 actually connected
1140433d6423SLionel Sambuc * (the controller generates an extra interrupt for the middle drive when
1141433d6423SLionel Sambuc * drive 2 is accessed and the driver panics).
1142433d6423SLionel Sambuc *
1143433d6423SLionel Sambuc * It would be better to keep collecting results until there are no more.
1144433d6423SLionel Sambuc * For this, fdc_results needs to return the number of results (instead
1145433d6423SLionel Sambuc * of OK) when it succeeds.
1146433d6423SLionel Sambuc */
1147433d6423SLionel Sambuc for (i = 0; i < 4; i++) {
1148433d6423SLionel Sambuc fdc_out(FDC_SENSE); /* probe FDC to make it return status */
1149433d6423SLionel Sambuc (void) fdc_results(); /* flush controller */
1150433d6423SLionel Sambuc }
1151433d6423SLionel Sambuc for (i = 0; i < NR_DRIVES; i++) /* clear each drive */
1152433d6423SLionel Sambuc floppy[i].fl_calibration = UNCALIBRATED;
1153433d6423SLionel Sambuc
1154433d6423SLionel Sambuc /* The current timing parameters must be specified again. */
1155433d6423SLionel Sambuc prev_dp = NULL;
1156433d6423SLionel Sambuc }
1157433d6423SLionel Sambuc
1158433d6423SLionel Sambuc /*===========================================================================*
1159433d6423SLionel Sambuc * f_intr_wait *
1160433d6423SLionel Sambuc *===========================================================================*/
f_intr_wait(void)1161433d6423SLionel Sambuc static int f_intr_wait(void)
1162433d6423SLionel Sambuc {
1163433d6423SLionel Sambuc /* Wait for an interrupt, but not forever. The FDC may have all the time of
1164433d6423SLionel Sambuc * the world, but we humans do not.
1165433d6423SLionel Sambuc */
1166433d6423SLionel Sambuc message mess;
1167433d6423SLionel Sambuc int r, ipc_status;
1168433d6423SLionel Sambuc
1169433d6423SLionel Sambuc /* We expect an interrupt, but if a timeout, occurs, report an error. */
1170433d6423SLionel Sambuc do {
1171433d6423SLionel Sambuc if ((r = driver_receive(ANY, &mess, &ipc_status)) != OK)
1172433d6423SLionel Sambuc panic("Couldn't receive message: %d", r);
1173433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
1174433d6423SLionel Sambuc switch (_ENDPOINT_P(mess.m_source)) {
1175433d6423SLionel Sambuc case CLOCK:
1176433d6423SLionel Sambuc f_expire_tmrs(mess.m_notify.timestamp);
1177433d6423SLionel Sambuc break;
1178433d6423SLionel Sambuc default :
1179433d6423SLionel Sambuc f_busy = BSY_IDLE;
1180433d6423SLionel Sambuc break;
1181433d6423SLionel Sambuc }
1182433d6423SLionel Sambuc } else {
1183433d6423SLionel Sambuc f_busy = BSY_IDLE;
1184433d6423SLionel Sambuc }
1185433d6423SLionel Sambuc } while (f_busy == BSY_IO);
1186433d6423SLionel Sambuc
1187433d6423SLionel Sambuc if (f_busy == BSY_WAKEN) {
1188433d6423SLionel Sambuc
1189433d6423SLionel Sambuc /* No interrupt from the FDC, this means that there is probably no
1190433d6423SLionel Sambuc * floppy in the drive. Get the FDC down to earth and return error.
1191433d6423SLionel Sambuc */
1192433d6423SLionel Sambuc need_reset = TRUE;
1193433d6423SLionel Sambuc return(ERR_TIMEOUT);
1194433d6423SLionel Sambuc }
1195433d6423SLionel Sambuc return(OK);
1196433d6423SLionel Sambuc }
1197433d6423SLionel Sambuc
1198433d6423SLionel Sambuc /*===========================================================================*
1199433d6423SLionel Sambuc * f_timeout *
1200433d6423SLionel Sambuc *===========================================================================*/
f_timeout(int arg __unused)1201*cfd712b4SDavid van Moolenbroek static void f_timeout(int arg __unused)
1202433d6423SLionel Sambuc {
1203433d6423SLionel Sambuc /* This routine is called when a timer expires. Usually to tell that a
1204433d6423SLionel Sambuc * motor has spun up, but also to forge an interrupt when it takes too long
1205433d6423SLionel Sambuc * for the FDC to interrupt (no floppy in the drive). It sets a flag to tell
1206433d6423SLionel Sambuc * what has happened.
1207433d6423SLionel Sambuc */
1208433d6423SLionel Sambuc if (f_busy == BSY_IO) {
1209433d6423SLionel Sambuc f_busy = BSY_WAKEN;
1210433d6423SLionel Sambuc }
1211433d6423SLionel Sambuc }
1212433d6423SLionel Sambuc
1213433d6423SLionel Sambuc /*===========================================================================*
1214433d6423SLionel Sambuc * read_id *
1215433d6423SLionel Sambuc *===========================================================================*/
read_id(void)1216433d6423SLionel Sambuc static int read_id(void)
1217433d6423SLionel Sambuc {
1218433d6423SLionel Sambuc /* Determine current cylinder and sector. */
1219433d6423SLionel Sambuc
1220433d6423SLionel Sambuc struct floppy *fp = f_fp;
1221433d6423SLionel Sambuc int result;
1222433d6423SLionel Sambuc u8_t cmd[2];
1223433d6423SLionel Sambuc
1224433d6423SLionel Sambuc /* Never attempt a read id if the drive is uncalibrated or motor is off. */
1225433d6423SLionel Sambuc if (fp->fl_calibration == UNCALIBRATED) return(ERR_READ_ID);
1226433d6423SLionel Sambuc if ((motor_status & (1 << f_drive)) == 0) return(ERR_READ_ID);
1227433d6423SLionel Sambuc
1228433d6423SLionel Sambuc /* The command is issued by outputting 2 bytes to the controller chip. */
1229433d6423SLionel Sambuc cmd[0] = FDC_READ_ID; /* issue the read id command */
1230433d6423SLionel Sambuc cmd[1] = (fp->fl_head << 2) | f_drive;
1231433d6423SLionel Sambuc if (fdc_command(cmd, 2) != OK) return(ERR_READ_ID);
1232433d6423SLionel Sambuc if (f_intr_wait() != OK) return(ERR_TIMEOUT);
1233433d6423SLionel Sambuc
1234433d6423SLionel Sambuc /* Get controller status and check for errors. */
1235433d6423SLionel Sambuc result = fdc_results();
1236433d6423SLionel Sambuc if (result != OK) return(result);
1237433d6423SLionel Sambuc
1238433d6423SLionel Sambuc if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_READ_ID);
1239433d6423SLionel Sambuc if (f_results[ST1] | f_results[ST2]) return(ERR_READ_ID);
1240433d6423SLionel Sambuc
1241433d6423SLionel Sambuc /* The next sector is next for I/O: */
1242433d6423SLionel Sambuc fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR + 1;
1243433d6423SLionel Sambuc return(OK);
1244433d6423SLionel Sambuc }
1245433d6423SLionel Sambuc
1246433d6423SLionel Sambuc /*===========================================================================*
1247433d6423SLionel Sambuc * f_do_open *
1248433d6423SLionel Sambuc *===========================================================================*/
f_do_open(devminor_t minor,int UNUSED (access))1249433d6423SLionel Sambuc static int f_do_open(devminor_t minor, int UNUSED(access))
1250433d6423SLionel Sambuc {
1251433d6423SLionel Sambuc /* Handle an open on a floppy. Determine diskette type if need be. */
1252433d6423SLionel Sambuc
1253433d6423SLionel Sambuc int dtype;
1254433d6423SLionel Sambuc struct test_order *top;
1255433d6423SLionel Sambuc
1256433d6423SLionel Sambuc /* Decode the message parameters. */
1257433d6423SLionel Sambuc if (f_prepare(minor) == NULL) return(ENXIO);
1258433d6423SLionel Sambuc
1259433d6423SLionel Sambuc dtype = f_device & DEV_TYPE_BITS; /* get density from minor dev */
1260433d6423SLionel Sambuc if (dtype >= MINOR_fd0p0) dtype = 0;
1261433d6423SLionel Sambuc
1262433d6423SLionel Sambuc if (dtype != 0) {
1263433d6423SLionel Sambuc /* All types except 0 indicate a specific drive/medium combination.*/
1264433d6423SLionel Sambuc dtype = (dtype >> DEV_TYPE_SHIFT) - 1;
1265433d6423SLionel Sambuc if (dtype >= NT) return(ENXIO);
1266433d6423SLionel Sambuc f_fp->fl_density = dtype;
1267433d6423SLionel Sambuc (void) f_prepare(f_device); /* Recompute parameters. */
1268433d6423SLionel Sambuc return(OK);
1269433d6423SLionel Sambuc }
1270433d6423SLionel Sambuc if (f_device & FORMAT_DEV_BIT) return(EIO); /* Can't format /dev/fdN */
1271433d6423SLionel Sambuc
1272433d6423SLionel Sambuc /* The device opened is /dev/fdN. Experimentally determine drive/medium.
1273433d6423SLionel Sambuc * First check fl_density. If it is not NO_DENS, the drive has been used
1274433d6423SLionel Sambuc * before and the value of fl_density tells what was found last time. Try
1275433d6423SLionel Sambuc * that first. If the motor is still running then assume nothing changed.
1276433d6423SLionel Sambuc */
1277433d6423SLionel Sambuc if (f_fp->fl_density != NO_DENS) {
1278433d6423SLionel Sambuc if (motor_status & (1 << f_drive)) return(OK);
1279433d6423SLionel Sambuc if (test_read(f_fp->fl_density) == OK) return(OK);
1280433d6423SLionel Sambuc }
1281433d6423SLionel Sambuc
1282433d6423SLionel Sambuc /* Either drive type is unknown or a different diskette is now present.
1283433d6423SLionel Sambuc * Use test_order to try them one by one.
1284433d6423SLionel Sambuc */
1285433d6423SLionel Sambuc for (top = &test_order[0]; top < &test_order[NT-1]; top++) {
1286433d6423SLionel Sambuc dtype = top->t_density;
1287433d6423SLionel Sambuc
1288433d6423SLionel Sambuc /* Skip densities that have been proven to be impossible */
1289433d6423SLionel Sambuc if (!(f_fp->fl_class & (1 << dtype))) continue;
1290433d6423SLionel Sambuc
1291433d6423SLionel Sambuc if (test_read(dtype) == OK) {
1292433d6423SLionel Sambuc /* The test succeeded, use this knowledge to limit the
1293433d6423SLionel Sambuc * drive class to match the density just read.
1294433d6423SLionel Sambuc */
1295433d6423SLionel Sambuc f_fp->fl_class &= top->t_class;
1296433d6423SLionel Sambuc return(OK);
1297433d6423SLionel Sambuc }
1298433d6423SLionel Sambuc /* Test failed, wrong density or did it time out? */
1299433d6423SLionel Sambuc if (f_busy == BSY_WAKEN) break;
1300433d6423SLionel Sambuc }
1301433d6423SLionel Sambuc f_fp->fl_density = NO_DENS;
1302433d6423SLionel Sambuc return(EIO); /* nothing worked */
1303433d6423SLionel Sambuc }
1304433d6423SLionel Sambuc
1305433d6423SLionel Sambuc /*===========================================================================*
1306433d6423SLionel Sambuc * f_do_close *
1307433d6423SLionel Sambuc *===========================================================================*/
f_do_close(devminor_t UNUSED (minor))1308433d6423SLionel Sambuc static int f_do_close(devminor_t UNUSED(minor))
1309433d6423SLionel Sambuc {
1310433d6423SLionel Sambuc /* Handle a close on a floppy. Nothing to do here. */
1311433d6423SLionel Sambuc
1312433d6423SLionel Sambuc return(OK);
1313433d6423SLionel Sambuc }
1314433d6423SLionel Sambuc
1315433d6423SLionel Sambuc /*===========================================================================*
1316433d6423SLionel Sambuc * test_read *
1317433d6423SLionel Sambuc *===========================================================================*/
test_read(int density)1318433d6423SLionel Sambuc static int test_read(int density)
1319433d6423SLionel Sambuc {
1320433d6423SLionel Sambuc /* Try to read the highest numbered sector on cylinder 2. Not all floppy
1321433d6423SLionel Sambuc * types have as many sectors per track, and trying cylinder 2 finds the
1322433d6423SLionel Sambuc * ones that need double stepping.
1323433d6423SLionel Sambuc */
1324433d6423SLionel Sambuc int device;
1325433d6423SLionel Sambuc off_t position;
1326433d6423SLionel Sambuc iovec_t iovec1;
1327433d6423SLionel Sambuc ssize_t result;
1328433d6423SLionel Sambuc
1329433d6423SLionel Sambuc f_fp->fl_density = density;
1330433d6423SLionel Sambuc device = ((density + 1) << DEV_TYPE_SHIFT) + f_drive;
1331433d6423SLionel Sambuc
1332433d6423SLionel Sambuc (void) f_prepare(device);
1333433d6423SLionel Sambuc position = (off_t) f_dp->test << SECTOR_SHIFT;
1334433d6423SLionel Sambuc iovec1.iov_addr = (vir_bytes) floppy_buf;
1335433d6423SLionel Sambuc iovec1.iov_size = SECTOR_SIZE;
1336433d6423SLionel Sambuc result = f_transfer(device, FALSE /*do_write*/, position, SELF,
1337433d6423SLionel Sambuc &iovec1, 1, BDEV_NOFLAGS);
1338433d6423SLionel Sambuc
1339433d6423SLionel Sambuc if (result != SECTOR_SIZE) return(EIO);
1340433d6423SLionel Sambuc
1341433d6423SLionel Sambuc partition(&f_dtab, f_drive, P_FLOPPY, 0);
1342433d6423SLionel Sambuc return(OK);
1343433d6423SLionel Sambuc }
1344433d6423SLionel Sambuc
1345433d6423SLionel Sambuc /*===========================================================================*
1346433d6423SLionel Sambuc * f_geometry *
1347433d6423SLionel Sambuc *===========================================================================*/
f_geometry(devminor_t minor,struct part_geom * entry)1348433d6423SLionel Sambuc static void f_geometry(devminor_t minor, struct part_geom *entry)
1349433d6423SLionel Sambuc {
1350433d6423SLionel Sambuc if (f_prepare(minor) == NULL) return;
1351433d6423SLionel Sambuc
1352433d6423SLionel Sambuc entry->cylinders = f_dp->cyls;
1353433d6423SLionel Sambuc entry->heads = NR_HEADS;
1354433d6423SLionel Sambuc entry->sectors = f_sectors;
1355433d6423SLionel Sambuc }
1356