Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / arch / sun3 / dev / fd.c
blob71ae454b090d898daf873f94ff16068934bde910
1 /* $NetBSD: fd.c,v 1.70 2009/09/26 16:03:45 tsutsui Exp $ */
3 /*-
4 * Copyright (c) 1990 The Regents of the University of California.
5 * All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Don Ahn.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * @(#)fd.c 7.4 (Berkeley) 5/25/91
37 /*-
38 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum.
40 * This code is derived from software contributed to Berkeley by
41 * Don Ahn.
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
71 * @(#)fd.c 7.4 (Berkeley) 5/25/91
74 #include <sys/cdefs.h>
75 __KERNEL_RCSID(0, "$NetBSD: fd.c,v 1.70 2009/09/26 16:03:45 tsutsui Exp $");
77 #include "opt_ddb.h"
79 #include <sys/param.h>
80 #include <sys/systm.h>
81 #include <sys/callout.h>
82 #include <sys/kernel.h>
83 #include <sys/file.h>
84 #include <sys/ioctl.h>
85 #include <sys/device.h>
86 #include <sys/disklabel.h>
87 #include <sys/disk.h>
88 #include <sys/fdio.h>
89 #include <sys/buf.h>
90 #include <sys/bufq.h>
91 #include <sys/malloc.h>
92 #include <sys/proc.h>
93 #include <sys/uio.h>
94 #include <sys/stat.h>
95 #include <sys/syslog.h>
96 #include <sys/queue.h>
97 #include <sys/conf.h>
99 #include <dev/cons.h>
101 #include <uvm/uvm_extern.h>
103 #include <machine/cpu.h>
104 #include <machine/autoconf.h>
106 #include <sun3/dev/fdreg.h>
107 #include <sun3/dev/fdvar.h>
109 #include "ioconf.h"
112 * Print a complaint when no fd children were specified
113 * in the config file. Better than a link error...
115 * XXX: Some folks say this driver should be split in two,
116 * but that seems pointless with ONLY one type of child.
117 * (Thankfully, no 3/80 boxes have floppy tapes!:)
119 #include "fdc.h"
120 #if NFD == 0
121 #error "fdc but no fd?"
122 #endif
124 #define FDUNIT(dev) (minor(dev) / 8)
125 #define FDTYPE(dev) (minor(dev) % 8)
127 /* (mis)use device use flag to identify format operation */
128 #define B_FORMAT B_DEVPRIVATE
130 #ifdef FD_DEBUG
131 int fdc_debug = 0;
132 #endif
134 enum fdc_state {
135 DEVIDLE = 0,
136 MOTORWAIT,
137 DOSEEK,
138 SEEKWAIT,
139 SEEKTIMEDOUT,
140 SEEKCOMPLETE,
141 DOIO,
142 IOCOMPLETE,
143 IOTIMEDOUT,
144 DORESET,
145 RESETCOMPLETE,
146 RESETTIMEDOUT,
147 DORECAL,
148 RECALWAIT,
149 RECALTIMEDOUT,
150 RECALCOMPLETE,
153 /* software state, per controller */
154 struct fdc_softc {
155 device_t sc_dev; /* boilerplate */
156 void * sc_reg;
158 struct callout sc_timo_ch; /* timeout callout */
159 struct callout sc_intr_ch; /* pseudo-intr callout */
161 struct fd_softc *sc_fd[4]; /* pointers to children */
162 TAILQ_HEAD(drivehead, fd_softc) sc_drives;
163 enum fdc_state sc_state;
164 int sc_flags;
165 #define FDC_82077 0x01
166 #define FDC_NEEDHEADSETTLE 0x02
167 #define FDC_EIS 0x04
168 int sc_errors; /* number of retries so far */
169 int sc_overruns; /* number of DMA overruns */
170 int sc_cfg; /* current configuration */
171 int sc_fcr; /* current image of floppy ctrlr reg. */
172 struct fdcio sc_io;
173 #define sc_reg_msr sc_io.fdcio_reg_msr
174 #define sc_reg_fifo sc_io.fdcio_reg_fifo
175 #define sc_reg_fcr sc_io.fdcio_reg_fcr
176 #define sc_reg_fvr sc_io.fdcio_reg_fvr
177 #define sc_reg_drs sc_io.fdcio_reg_msr
178 #define sc_istate sc_io.fdcio_istate
179 #define sc_data sc_io.fdcio_data
180 #define sc_tc sc_io.fdcio_tc
181 #define sc_nstat sc_io.fdcio_nstat
182 #define sc_status sc_io.fdcio_status
183 #define sc_intrcnt sc_io.fdcio_intrcnt
184 void *sc_si; /* softintr cookie */
187 /* controller driver configuration */
188 static int fdcmatch(device_t, cfdata_t, void *);
189 static void fdcattach(device_t, device_t, void *);
191 CFATTACH_DECL_NEW(fdc, sizeof(struct fdc_softc),
192 fdcmatch, fdcattach, NULL, NULL);
194 static inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
197 * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
198 * we tell them apart.
200 struct fd_type {
201 int sectrac; /* sectors per track */
202 int heads; /* number of heads */
203 int seccyl; /* sectors per cylinder */
204 int secsize; /* size code for sectors */
205 int datalen; /* data len when secsize = 0 */
206 int steprate; /* step rate and head unload time */
207 int gap1; /* gap len between sectors */
208 int gap2; /* formatting gap */
209 int tracks; /* total num of tracks */
210 int size; /* size of disk in sectors */
211 int step; /* steps per cylinder */
212 int rate; /* transfer speed code */
213 int fillbyte; /* format fill byte */
214 int interleave; /* interleave factor (formatting) */
215 const char *name;
218 /* The order of entries in the following table is important -- BEWARE! */
219 static struct fd_type fd_types[] = {
220 { 18, 2, 36, 2, 0xff, 0xcf, 0x18, 0x50, 80, 2880, 1,
221 FDC_500KBPS, 0xf6, 1, "1.44MB" }, /* 1.44MB diskette */
222 { 15, 2, 30, 2, 0xff, 0xdf, 0x1b, 0x54, 80, 2400, 1,
223 FDC_500KBPS, 0xf6, 1, "1.2MB" }, /* 1.2 MB AT-diskettes */
224 { 9, 2, 18, 2, 0xff, 0xdf, 0x23, 0x50, 40, 720, 2,
225 FDC_300KBPS, 0xf6, 1, "360KB/AT" }, /* 360kB in 1.2MB drive */
226 { 9, 2, 18, 2, 0xff, 0xdf, 0x2a, 0x50, 40, 720, 1,
227 FDC_250KBPS, 0xf6, 1, "360KB/PC" }, /* 360kB PC diskettes */
228 { 9, 2, 18, 2, 0xff, 0xdf, 0x2a, 0x50, 80, 1440, 1,
229 FDC_250KBPS, 0xf6, 1, "720KB" }, /* 3.5" 720kB diskette */
230 { 9, 2, 18, 2, 0xff, 0xdf, 0x23, 0x50, 80, 1440, 1,
231 FDC_300KBPS, 0xf6, 1, "720KB/x" }, /* 720kB in 1.2MB drive */
232 { 9, 2, 18, 2, 0xff, 0xdf, 0x2a, 0x50, 40, 720, 2,
233 FDC_250KBPS,0xf6,1, "360KB/x" }, /* 360kB in 720kB drive */
236 /* software state, per disk (with up to 4 disks per ctlr) */
237 struct fd_softc {
238 device_t sc_dv; /* generic device info */
239 struct disk sc_dk; /* generic disk info */
241 struct fd_type *sc_deftype; /* default type descriptor */
242 struct fd_type *sc_type; /* current type descriptor */
244 struct callout sc_motoron_ch;
245 struct callout sc_motoroff_ch;
247 daddr_t sc_blkno; /* starting block number */
248 int sc_bcount; /* byte count left */
249 int sc_skip; /* bytes already transferred */
250 int sc_nblks; /* number of blocks currently transferring */
251 int sc_nbytes; /* number of bytes currently transferring */
253 int sc_drive; /* physical unit number */
254 int sc_flags;
255 #define FD_OPEN 0x01 /* it's open */
256 #define FD_MOTOR 0x02 /* motor should be on */
257 #define FD_MOTOR_WAIT 0x04 /* motor coming up */
258 int sc_cylin; /* where we think the head is */
259 int sc_opts; /* user-set options */
261 TAILQ_ENTRY(fd_softc) sc_drivechain;
262 int sc_ops; /* I/O ops since last switch */
263 struct bufq_state *sc_q;/* pending I/O requests */
264 int sc_active; /* number of active I/O operations */
267 /* floppy driver configuration */
268 static int fdmatch(device_t, cfdata_t, void *);
269 static void fdattach(device_t, device_t, void *);
271 CFATTACH_DECL_NEW(fd, sizeof(struct fd_softc),
272 fdmatch, fdattach, NULL, NULL);
274 dev_type_open(fdopen);
275 dev_type_close(fdclose);
276 dev_type_read(fdread);
277 dev_type_write(fdwrite);
278 dev_type_ioctl(fdioctl);
279 dev_type_strategy(fdstrategy);
281 const struct bdevsw fd_bdevsw = {
282 fdopen, fdclose, fdstrategy, fdioctl, nodump, nosize, D_DISK
285 const struct cdevsw fd_cdevsw = {
286 fdopen, fdclose, fdread, fdwrite, fdioctl,
287 nostop, notty, nopoll, nommap, nokqfilter, D_DISK
290 static bool fd_shutdown(device_t, int);
291 static void fdgetdisklabel(dev_t);
292 static void fdstart(struct fd_softc *);
293 static int fdprint(void *, const char *);
295 struct dkdriver fddkdriver = { fdstrategy };
297 static void fd_set_motor(struct fdc_softc *);
298 static void fd_motor_off(void *);
299 static void fd_motor_on(void *);
300 static int fdcresult(struct fdc_softc *);
301 static int out_fdc(struct fdc_softc *, u_char);
302 static void fdcstart(struct fdc_softc *);
303 static void fdcstatus(device_t, int, const char *);
304 static void fdc_reset(struct fdc_softc *);
305 static void fdctimeout(void *);
306 static void fdcpseudointr(void *);
307 static int fdchwintr(void *);
308 static void fdcswintr(void *);
309 static int fdcstate(struct fdc_softc *);
310 static void fdcretry(struct fdc_softc *);
311 static void fdfinish(struct fd_softc *, struct buf *);
312 static int fdformat(dev_t, struct ne7_fd_formb *, struct proc *);
313 static void fd_do_eject(struct fdc_softc *, int);
314 static void fd_mountroot_hook(device_t);
315 static void fdconf(struct fdc_softc *);
317 #define IPL_SOFTFD IPL_BIO
318 #define FDC_SOFTPRI 2
319 #define FD_SET_SWINTR() softint_schedule(fdc->sc_si);
322 * The Floppy Control Register on the sun3x, not to be confused with the
323 * Floppy ControllER Registers that this driver mostly insterfaces with,
324 * controls some of the auxillary functions of the floppy drive. These
325 * include asserting the floppy eject and terminal data count (or TC) pins
326 * of the floppy drive and controller chip respectively.
328 * Often it is necessary to toggle individual bits within this register
329 * while keeping the others untouched. However, the register does not
330 * present its latched data to the processor when read. This prevents the
331 * use of a read-modify-write cycle that would normally be used to modify
332 * individual bits. To get around this we must keep a copy of register's
333 * current value and always insure that when we wish to modify the register,
334 * we actually modify the copy and synchronize the register to it.
336 #define FCR_REG_SYNC() (*fdc->sc_reg_fcr = fdc->sc_fcr)
338 int
339 fdcmatch(device_t parent, cfdata_t cf, void *aux)
341 struct confargs *ca = aux;
343 if (bus_peek(ca->ca_bustype, ca->ca_paddr, sizeof(uint8_t)) == -1)
344 return 0;
346 return 1;
350 * Arguments passed between fdcattach and fdprobe.
352 struct fdc_attach_args {
353 int fa_drive;
354 struct bootpath *fa_bootpath;
355 struct fd_type *fa_deftype;
359 * Print the location of a disk drive (called just before attaching the
360 * the drive). If `fdc' is not NULL, the drive was found but was not
361 * in the system config file; print the drive name as well.
362 * Return QUIET (config_find ignores this if the device was configured) to
363 * avoid printing `fdN not configured' messages.
365 int
366 fdprint(void *aux, const char *fdc)
368 struct fdc_attach_args *fa = aux;
370 if (fdc == NULL)
371 aprint_normal(" drive %d", fa->fa_drive);
372 return QUIET;
375 static void
376 fdconf(struct fdc_softc *fdc)
378 int vroom;
380 if (out_fdc(fdc, NE7CMD_DUMPREG) || fdcresult(fdc) != 10)
381 return;
384 * dumpreg[7] seems to be a motor-off timeout; set it to whatever
385 * the PROM thinks is appropriate.
387 if ((vroom = fdc->sc_status[7]) == 0)
388 vroom = 0x64;
390 /* Configure controller to use FIFO and Implied Seek */
391 out_fdc(fdc, NE7CMD_CFG);
392 out_fdc(fdc, vroom);
393 out_fdc(fdc, fdc->sc_cfg);
394 out_fdc(fdc, 0); /* PRETRK */
395 /* No result phase */
398 void
399 fdcattach(struct device *parent, struct device *self, void *aux)
401 struct confargs *ca = aux;
402 struct fdc_softc *fdc = device_private(self);
403 struct fdc_attach_args fa;
404 int pri, vec;
405 char code;
407 fdc->sc_dev = self;
409 fdc->sc_reg = (void *)bus_mapin(ca->ca_bustype, ca->ca_paddr,
410 sizeof(union fdreg));
412 callout_init(&fdc->sc_timo_ch, 0);
413 callout_init(&fdc->sc_intr_ch, 0);
415 fdc->sc_state = DEVIDLE;
416 fdc->sc_istate = ISTATE_IDLE;
417 fdc->sc_flags |= FDC_EIS;
418 TAILQ_INIT(&fdc->sc_drives);
420 /* Assume a 82072 */
421 code = '2';
423 if (code == '7') {
424 panic("no 82077 fdc in this kernel");
425 /* NOTREACHED */
426 } else {
427 fdc->sc_reg_msr = &((struct fdreg_72 *)fdc->sc_reg)->fd_msr;
428 fdc->sc_reg_fifo = &((struct fdreg_72 *)fdc->sc_reg)->fd_fifo;
430 fdc->sc_reg_fcr = ((volatile uint8_t *)fdc->sc_reg)
431 + FDC_FCR_OFFSET;
432 fdc->sc_reg_fvr = ((volatile uint8_t *)fdc->sc_reg)
433 + FDC_FVR_OFFSET;
436 pri = ca->ca_intpri;
437 vec = ca->ca_intvec;
438 if (vec == -1) {
439 /* Tell the FDC to fake an autovector. */
440 vec = 0x18 + pri; /* XXX */
441 isr_add_autovect(fdchwintr, fdc, pri);
442 } else {
443 /* An OBIO bus with vectors? Weird exception. */
444 isr_add_vectored(fdchwintr, fdc, pri, vec);
446 *fdc->sc_reg_fvr = vec; /* Program controller w/ interrupt vector */
448 fdc->sc_si = softint_establish(SOFTINT_BIO, fdcswintr, fdc);
449 #if 0
450 aprint_normal(": (softpri %d) chip 8207%c\n", FDC_SOFTPRI, code);
451 #else
452 aprint_normal(": chip 8207%c\n", code);
453 #endif
455 #ifdef FD_DEBUG
456 if (out_fdc(fdc, NE7CMD_VERSION) == 0 &&
457 fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x90) {
458 if (fdc_debug)
459 aprint_debug("[version cmd]");
461 #endif
463 fdc_reset(fdc);
465 * Configure controller; enable FIFO, Implied seek, no POLL mode?.
466 * Note: CFG_EFIFO is active-low, initial threshold value: 8
468 fdc->sc_cfg = CFG_EIS|/*CFG_EFIFO|*/CFG_POLL|(8 & CFG_THRHLD_MASK);
469 fdconf(fdc);
471 evcnt_attach_dynamic(&fdc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
472 device_xname(self), "intr");
474 /* physical limit: four drives per controller. */
475 for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
476 fa.fa_deftype = NULL; /* unknown */
477 fa.fa_deftype = &fd_types[0]; /* XXX */
478 (void)config_found(self, (void *)&fa, fdprint);
482 int
483 fdmatch(device_t parent, cfdata_t cf, void *aux)
485 struct fdc_softc *fdc = device_private(parent);
486 struct fdc_attach_args *fa = aux;
487 int drive = fa->fa_drive;
488 int n, ok;
490 if (drive > 0)
491 /* XXX - for now, punt > 1 drives */
492 return 0;
494 /* select drive and turn on motor */
495 fdc->sc_fcr |= FCR_DSEL(drive) | FCR_MTRON;
496 FCR_REG_SYNC();
497 /* wait for motor to spin up */
498 delay(250000);
500 fdc->sc_nstat = 0;
501 out_fdc(fdc, NE7CMD_RECAL);
502 out_fdc(fdc, drive);
503 /* wait for recalibrate */
504 for (n = 0; n < 10000; n++) {
505 delay(1000);
506 if ((*fdc->sc_reg_msr & (NE7_RQM|NE7_DIO|NE7_CB)) == NE7_RQM) {
507 /* wait a bit longer till device *really* is ready */
508 delay(100000);
509 if (out_fdc(fdc, NE7CMD_SENSEI))
510 break;
511 if (fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x80)
513 * Got `invalid command'; we interpret it
514 * to mean that the re-calibrate hasn't in
515 * fact finished yet
517 continue;
518 break;
521 n = fdc->sc_nstat;
522 #ifdef FD_DEBUG
523 if (fdc_debug) {
524 int i;
525 aprint_debug("%s: %d stati:", __func__, n);
526 for (i = 0; i < n; i++)
527 aprint_debug(" %x", fdc->sc_status[i]);
528 aprint_debug("\n");
530 #endif
531 ok = (n == 2 && (fdc->sc_status[0] & 0xf8) == 0x20) ? 1 : 0;
533 /* turn off motor */
534 fdc->sc_fcr &= ~(FCR_DSEL(drive)|FCR_MTRON);
535 FCR_REG_SYNC();
537 return ok;
541 * Controller is working, and drive responded. Attach it.
543 void
544 fdattach(device_t parent, device_t self, void *aux)
546 struct fdc_softc *fdc = device_private(parent);
547 struct fd_softc *fd = device_private(self);
548 struct fdc_attach_args *fa = aux;
549 struct fd_type *type = fa->fa_deftype;
550 int drive = fa->fa_drive;
552 fd->sc_dv = self;
554 callout_init(&fd->sc_motoron_ch, 0);
555 callout_init(&fd->sc_motoroff_ch, 0);
557 /* XXX Allow `flags' to override device type? */
559 if (type)
560 aprint_normal(": %s %d cyl, %d head, %d sec\n", type->name,
561 type->tracks, type->heads, type->sectrac);
562 else
563 aprint_normal(": density unknown\n");
565 bufq_alloc(&fd->sc_q, "disksort", BUFQ_SORT_CYLINDER);
566 fd->sc_cylin = -1;
567 fd->sc_drive = drive;
568 fd->sc_deftype = type;
569 fdc->sc_fd[drive] = fd;
572 * Initialize and attach the disk structure.
574 disk_init(&fd->sc_dk, device_xname(self), &fddkdriver);
575 disk_attach(&fd->sc_dk);
577 #ifdef sparc
579 * We're told if we're the boot device in fdcattach().
581 if (fa->fa_bootpath)
582 fa->fa_bootpath->dev = self;
583 #endif
584 #define OUT_FDC(sc, c) do { \
585 if (out_fdc((sc), (c)) != 0) \
586 printf("fdc: specify command failed.\n"); \
587 } while (/* CONSTCOND */ 0)
589 /* specify command */
590 OUT_FDC(fdc, NE7CMD_SPECIFY);
591 OUT_FDC(fdc, type->steprate);
593 * The '|1' in the following statement turns on the 'Non-DMA' bit
594 * specifier in the last byte of the SPECIFY command as described in the
595 * datasheet I have. This is necessary for the driver to work on the
596 * sun3x, because the system will not respond to the chip's requests
597 * for DMA; there is no hardware on the motherboard to support it.
598 * By enabling this bit, we will force the chip to interrupt when its
599 * FIFO is full, at which point the interrupt handler will empty it and
600 * continue. This is ``pseudo-DMA''.
601 * -J
603 OUT_FDC(fdc, 6|1); /* XXX head load time == 6ms */
604 #undef OUT_FDC
607 * Establish a mountroot_hook anyway in case we booted
608 * with RB_ASKNAME and get selected as the boot device.
610 mountroothook_establish(fd_mountroot_hook, self);
612 /* Make sure the drive motor gets turned off at shutdown time. */
613 if (!pmf_device_register1(self, NULL, NULL, fd_shutdown))
614 aprint_error_dev(self, "couldn't establish power handler\n");
617 bool
618 fd_shutdown(device_t self, int howto)
620 struct fd_softc *fd;
622 fd = device_private(self);
623 fd_motor_off(fd);
625 return true;
628 inline struct fd_type *
629 fd_dev_to_type(struct fd_softc *fd, dev_t dev)
631 int type = FDTYPE(dev);
633 if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
634 return NULL;
635 return type ? &fd_types[type - 1] : fd->sc_deftype;
638 void
639 fdstrategy(struct buf *bp)
641 struct fd_softc *fd;
642 int unit = FDUNIT(bp->b_dev);
643 int sz;
644 int s;
646 /* Valid unit, controller, and request? */
647 if ((fd = device_lookup_private(&fd_cd, unit)) == 0 ||
648 bp->b_blkno < 0 ||
649 ((bp->b_bcount % FDC_BSIZE) != 0 &&
650 (bp->b_flags & B_FORMAT) == 0)) {
651 bp->b_error = EINVAL;
652 goto done;
655 /* If it's a null transfer, return immediately. */
656 if (bp->b_bcount == 0)
657 goto done;
659 sz = howmany(bp->b_bcount, FDC_BSIZE);
661 if (bp->b_blkno + sz > fd->sc_type->size) {
662 sz = fd->sc_type->size - bp->b_blkno;
663 if (sz == 0) {
664 /* If exactly at end of disk, return EOF. */
665 bp->b_resid = bp->b_bcount;
666 goto done;
668 if (sz < 0) {
669 /* If past end of disk, return EINVAL. */
670 bp->b_error = EINVAL;
671 goto done;
673 /* Otherwise, truncate request. */
674 bp->b_bcount = sz << DEV_BSHIFT;
677 bp->b_rawblkno = bp->b_blkno;
678 bp->b_cylinder =
679 bp->b_blkno / (FDC_BSIZE / DEV_BSIZE) / fd->sc_type->seccyl;
681 #ifdef FD_DEBUG
682 if (fdc_debug > 1)
683 printf("%s: b_blkno %d b_bcount %d blkno %d cylin %d\n",
684 __func__, (int)bp->b_blkno, bp->b_bcount,
685 (int)fd->sc_blkno, bp->b_cylinder);
686 #endif
688 /* Queue transfer on drive, activate drive and controller if idle. */
689 s = splbio();
690 bufq_put(fd->sc_q, bp);
691 callout_stop(&fd->sc_motoroff_ch); /* a good idea */
692 if (fd->sc_active == 0)
693 fdstart(fd);
694 #ifdef DIAGNOSTIC
695 else {
696 struct fdc_softc *fdc;
698 fdc = device_private(device_parent(fd->sc_dv));
699 if (fdc->sc_state == DEVIDLE) {
700 printf("%s: controller inactive\n", __func__);
701 fdcstart(fdc);
704 #endif
705 splx(s);
706 return;
708 done:
709 /* Toss transfer; we're done early. */
710 biodone(bp);
713 void
714 fdstart(struct fd_softc *fd)
716 struct fdc_softc *fdc = device_private(device_parent(fd->sc_dv));
717 bool active = fdc->sc_drives.tqh_first != 0;
719 /* Link into controller queue. */
720 fd->sc_active = 1;
721 TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
723 /* If controller not already active, start it. */
724 if (!active)
725 fdcstart(fdc);
728 void
729 fdfinish(struct fd_softc *fd, struct buf *bp)
731 struct fdc_softc *fdc = device_private(device_parent(fd->sc_dv));
734 * Move this drive to the end of the queue to give others a `fair'
735 * chance. We only force a switch if N operations are completed while
736 * another drive is waiting to be serviced, since there is a long motor
737 * startup delay whenever we switch.
739 (void)bufq_get(fd->sc_q);
740 if (TAILQ_NEXT(fd, sc_drivechain) && ++fd->sc_ops >= 8) {
741 fd->sc_ops = 0;
742 TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
743 if (bufq_peek(fd->sc_q) != NULL) {
744 TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
745 } else
746 fd->sc_active = 0;
748 bp->b_resid = fd->sc_bcount;
749 fd->sc_skip = 0;
751 biodone(bp);
752 /* turn off motor 5s from now */
753 callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd);
754 fdc->sc_state = DEVIDLE;
757 void
758 fdc_reset(struct fdc_softc *fdc)
761 fdc->sc_fcr = 0;
762 FCR_REG_SYNC();
764 *fdc->sc_reg_drs = DRS_RESET;
765 delay(10);
766 *fdc->sc_reg_drs = 0;
768 #ifdef FD_DEBUG
769 if (fdc_debug)
770 printf("fdc reset\n");
771 #endif
774 void
775 fd_set_motor(struct fdc_softc *fdc)
777 struct fd_softc *fd;
778 int n;
780 int on = 0;
782 for (n = 0; n < 4; n++)
783 if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
784 on = 1;
785 if (on) {
786 fdc->sc_fcr |= FCR_DSEL(0)|FCR_MTRON; /* XXX */
787 } else {
788 fdc->sc_fcr &= ~(FCR_DSEL(0)|FCR_MTRON); /* XXX */
790 FCR_REG_SYNC();
793 void
794 fd_motor_off(void *arg)
796 struct fd_softc *fd = arg;
797 int s;
799 s = splbio();
800 fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
801 fd_set_motor(device_private(device_parent(fd->sc_dv)));
802 splx(s);
805 void
806 fd_motor_on(void *arg)
808 struct fd_softc *fd = arg;
809 struct fdc_softc *fdc = device_private(device_parent(fd->sc_dv));
810 int s;
812 s = splbio();
813 fd->sc_flags &= ~FD_MOTOR_WAIT;
814 if ((TAILQ_FIRST(&fdc->sc_drives) == fd) &&
815 (fdc->sc_state == MOTORWAIT))
816 (void)fdcstate(fdc);
817 splx(s);
820 int
821 fdcresult(struct fdc_softc *fdc)
823 uint8_t i;
824 int j, n = 0;
826 for (j = 100000; j; j--) {
827 i = *fdc->sc_reg_msr & (NE7_DIO | NE7_RQM | NE7_CB);
828 if (i == NE7_RQM) {
829 fdc->sc_nstat = n;
830 return n;
832 if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
833 if (n >= sizeof(fdc->sc_status)) {
834 log(LOG_ERR, "fdcresult: overrun\n");
835 return -1;
837 fdc->sc_status[n++] = *fdc->sc_reg_fifo;
838 } else
839 delay(10);
841 log(LOG_ERR, "fdcresult: timeout\n");
842 fdc->sc_nstat = -1;
843 return -1;
847 out_fdc(struct fdc_softc *fdc, u_char x)
849 int i = 100000;
851 while (((*fdc->sc_reg_msr & (NE7_DIO|NE7_RQM)) != NE7_RQM) && i-- > 0)
852 delay(1);
853 if (i <= 0)
854 return -1;
856 *fdc->sc_reg_fifo = x;
857 return 0;
860 int
861 fdopen(dev_t dev, int flags, int fmt, struct lwp *l)
863 int unit, pmask;
864 struct fd_softc *fd;
865 struct fd_type *type;
867 unit = FDUNIT(dev);
868 fd = device_lookup_private(&fd_cd, unit);
869 if (fd == NULL)
870 return ENXIO;
871 type = fd_dev_to_type(fd, dev);
872 if (type == NULL)
873 return ENXIO;
875 if ((fd->sc_flags & FD_OPEN) != 0 &&
876 fd->sc_type != type)
877 return EBUSY;
879 fd->sc_type = type;
880 fd->sc_cylin = -1;
881 fd->sc_flags |= FD_OPEN;
884 * Only update the disklabel if we're not open anywhere else.
886 if (fd->sc_dk.dk_openmask == 0)
887 fdgetdisklabel(dev);
889 pmask = (1 << DISKPART(dev));
891 switch (fmt) {
892 case S_IFCHR:
893 fd->sc_dk.dk_copenmask |= pmask;
894 break;
896 case S_IFBLK:
897 fd->sc_dk.dk_bopenmask |= pmask;
898 break;
900 fd->sc_dk.dk_openmask =
901 fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask;
903 return 0;
906 int
907 fdclose(dev_t dev, int flags, int fmt, struct lwp *l)
909 struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
910 int pmask = (1 << DISKPART(dev));
912 fd->sc_flags &= ~FD_OPEN;
913 fd->sc_opts &= ~(FDOPT_NORETRY|FDOPT_SILENT);
915 switch (fmt) {
916 case S_IFCHR:
917 fd->sc_dk.dk_copenmask &= ~pmask;
918 break;
920 case S_IFBLK:
921 fd->sc_dk.dk_bopenmask &= ~pmask;
922 break;
924 fd->sc_dk.dk_openmask =
925 fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask;
927 return 0;
930 int
931 fdread(dev_t dev, struct uio *uio, int flag)
934 return physio(fdstrategy, NULL, dev, B_READ, minphys, uio);
937 int
938 fdwrite(dev_t dev, struct uio *uio, int flag)
941 return physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio);
944 void
945 fdcstart(struct fdc_softc *fdc)
948 #ifdef DIAGNOSTIC
949 /* only got here if controller's drive queue was inactive; should
950 be in idle state */
951 if (fdc->sc_state != DEVIDLE) {
952 printf("%s: not idle\n", __func__);
953 return;
955 #endif
956 (void)fdcstate(fdc);
959 static void
960 fdcpstatus(int n, struct fdc_softc *fdc)
962 char bits[64];
964 switch (n) {
965 case 0:
966 printf("\n");
967 break;
968 case 2:
969 snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
970 printf(" (st0 %s cyl %d)\n", bits, fdc->sc_status[1]);
971 break;
972 case 7:
973 snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
974 printf(" (st0 %s", bits);
975 snprintb(bits, sizeof(bits), NE7_ST1BITS, fdc->sc_status[1]);
976 printf(" st1 %s", bits);
977 snprintb(bits, sizeof(bits), NE7_ST2BITS, fdc->sc_status[2]);
978 printf(" st2 %s", bits);
979 printf(" cyl %d head %d sec %d)\n",
980 fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
981 break;
982 #ifdef DIAGNOSTIC
983 default:
984 printf("\nfdcstatus: weird size");
985 break;
986 #endif
990 void
991 fdcstatus(device_t dv, int n, const char *s)
993 struct fdc_softc *fdc = device_private(device_parent(dv));
994 #if 0
996 * A 82072 seems to return <invalid command> on
997 * gratuitous Sense Interrupt commands.
999 if (n == 0 && (fdc->sc_flags & FDC_82077)) {
1000 out_fdc(fdc, NE7CMD_SENSEI);
1001 (void)fdcresult(fdc);
1002 n = 2;
1004 #endif
1006 /* Just print last status */
1007 n = fdc->sc_nstat;
1009 printf("%s: %s: state %d", device_xname(dv), s, fdc->sc_state);
1011 fdcpstatus(n, fdc);
1014 void
1015 fdctimeout(void *arg)
1017 struct fdc_softc *fdc = arg;
1018 struct fd_softc *fd = TAILQ_FIRST(&fdc->sc_drives);
1019 int s;
1021 s = splbio();
1022 fdcstatus(fd->sc_dv, 0, "timeout");
1024 if (bufq_peek(fd->sc_q) != NULL)
1025 fdc->sc_state++;
1026 else
1027 fdc->sc_state = DEVIDLE;
1029 (void)fdcstate(fdc);
1030 splx(s);
1033 void
1034 fdcpseudointr(void *arg)
1036 struct fdc_softc *fdc = arg;
1037 int s;
1039 /* Just ensure it has the right spl. */
1040 s = splbio();
1041 (void)fdcstate(fdc);
1042 splx(s);
1047 * hardware interrupt entry point: must be converted to `fast'
1048 * (in-window) handler.
1050 int
1051 fdchwintr(void *arg)
1053 struct fdc_softc *fdc = arg;
1056 * This code was reverse engineered from the SPARC bsd_fdintr.s.
1058 switch (fdc->sc_istate) {
1059 case ISTATE_IDLE:
1060 return 0;
1061 case ISTATE_SENSEI:
1062 out_fdc(fdc, NE7CMD_SENSEI);
1063 fdcresult(fdc);
1064 fdc->sc_istate = ISTATE_DONE;
1065 FD_SET_SWINTR();
1066 return 1;
1067 case ISTATE_DMA:
1068 break;
1069 default:
1070 log(LOG_ERR, "fdc: stray hard interrupt.\n");
1071 fdc->sc_fcr &= ~(FCR_DSEL(0)); /* Does this help? */
1072 fdc->sc_istate = ISTATE_SPURIOUS;
1073 FD_SET_SWINTR();
1074 return 1;
1077 for (;;) {
1078 int msr;
1080 msr = *fdc->sc_reg_msr;
1082 if ((msr & NE7_RQM) == 0)
1083 break;
1085 if ((msr & NE7_NDM) == 0) {
1086 /* Execution phase finished, get result. */
1087 fdcresult(fdc);
1088 fdc->sc_istate = ISTATE_DONE;
1089 FD_SET_SWINTR();
1090 #ifdef FD_DEBUG
1091 if (fdc_debug)
1092 log(LOG_ERR, "fdc: overrun: tc = %d\n",
1093 fdc->sc_tc);
1094 #endif
1095 break;
1098 if (msr & NE7_DIO) {
1099 *fdc->sc_data++ = *fdc->sc_reg_fifo;
1100 } else {
1101 *fdc->sc_reg_fifo = *fdc->sc_data++;
1103 if (--fdc->sc_tc == 0) {
1104 fdc->sc_fcr |= FCR_TC;
1105 FCR_REG_SYNC();
1106 delay(10);
1107 fdc->sc_fcr &= ~FCR_TC;
1108 FCR_REG_SYNC();
1109 break;
1112 return 1;
1115 void
1116 fdcswintr(void *arg)
1118 struct fdc_softc *fdc = arg;
1119 int s;
1121 if (fdc->sc_istate != ISTATE_DONE)
1122 return;
1124 fdc->sc_istate = ISTATE_IDLE;
1125 s = splbio();
1126 fdcstate(fdc);
1127 splx(s);
1130 int
1131 fdcstate(struct fdc_softc *fdc)
1133 #define st0 fdc->sc_status[0]
1134 #define st1 fdc->sc_status[1]
1135 #define cyl fdc->sc_status[1]
1136 #define OUT_FDC(fdc, c, s) \
1137 do { \
1138 if (out_fdc(fdc, (c))) { \
1139 (fdc)->sc_state = (s); \
1140 goto loop; \
1142 } while (/* CONSTCOND */ 0)
1144 struct fd_softc *fd;
1145 struct buf *bp;
1146 int read, head, sec, nblks;
1147 struct fd_type *type;
1148 struct ne7_fd_formb *finfo = NULL;
1151 if (fdc->sc_istate != ISTATE_IDLE) {
1152 /* Trouble... */
1153 printf("fdc: spurious interrupt: state %d, istate=%d\n",
1154 fdc->sc_state, fdc->sc_istate);
1155 fdc->sc_istate = ISTATE_IDLE;
1156 if (fdc->sc_state == RESETCOMPLETE ||
1157 fdc->sc_state == RESETTIMEDOUT) {
1158 panic("%s: spurious interrupt can't be cleared",
1159 __func__);
1161 goto doreset;
1164 loop:
1165 /* Is there a drive for the controller to do a transfer with? */
1166 fd = TAILQ_FIRST(&fdc->sc_drives);
1167 if (fd == NULL) {
1168 fdc->sc_state = DEVIDLE;
1169 return 0;
1172 /* Is there a transfer to this drive? If not, deactivate drive. */
1173 bp = bufq_peek(fd->sc_q);
1174 if (bp == NULL) {
1175 fd->sc_ops = 0;
1176 TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
1177 fd->sc_active = 0;
1178 goto loop;
1181 if (bp->b_flags & B_FORMAT)
1182 finfo = (struct ne7_fd_formb *)bp->b_data;
1184 switch (fdc->sc_state) {
1185 case DEVIDLE:
1186 fdc->sc_errors = 0;
1187 fd->sc_skip = 0;
1188 fd->sc_bcount = bp->b_bcount;
1189 fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE);
1190 callout_stop(&fd->sc_motoroff_ch);
1191 if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
1192 fdc->sc_state = MOTORWAIT;
1193 return 1;
1195 if ((fd->sc_flags & FD_MOTOR) == 0) {
1196 /* Turn on the motor, being careful about pairing. */
1197 struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
1198 if (ofd && ofd->sc_flags & FD_MOTOR) {
1199 callout_stop(&ofd->sc_motoroff_ch);
1200 ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
1202 fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
1203 fd_set_motor(fdc);
1204 fdc->sc_state = MOTORWAIT;
1205 if (fdc->sc_flags & FDC_82077) { /* XXX */
1206 /* Allow .25s for motor to stabilize. */
1207 callout_reset(&fd->sc_motoron_ch, hz / 4,
1208 fd_motor_on, fd);
1209 } else {
1210 fd->sc_flags &= ~FD_MOTOR_WAIT;
1211 goto loop;
1213 return 1;
1215 /* Make sure the right drive is selected. */
1216 fd_set_motor(fdc);
1218 /*FALLTHROUGH*/
1219 case DOSEEK:
1220 doseek:
1221 if ((fdc->sc_flags & FDC_EIS) &&
1222 (bp->b_flags & B_FORMAT) == 0) {
1223 fd->sc_cylin = bp->b_cylinder;
1224 /* We use implied seek */
1225 goto doio;
1228 if (fd->sc_cylin == bp->b_cylinder)
1229 goto doio;
1231 /* specify command */
1232 OUT_FDC(fdc, NE7CMD_SPECIFY, SEEKTIMEDOUT);
1233 OUT_FDC(fdc, fd->sc_type->steprate, SEEKTIMEDOUT);
1234 OUT_FDC(fdc, 6|1, SEEKTIMEDOUT); /* XXX head load time == 6ms */
1236 fdc->sc_istate = ISTATE_SENSEI;
1237 /* seek function */
1238 OUT_FDC(fdc, NE7CMD_SEEK, SEEKTIMEDOUT);
1239 OUT_FDC(fdc, fd->sc_drive, SEEKTIMEDOUT); /* drive number */
1240 OUT_FDC(fdc, bp->b_cylinder * fd->sc_type->step, SEEKTIMEDOUT);
1242 fd->sc_cylin = -1;
1243 fdc->sc_state = SEEKWAIT;
1244 fdc->sc_nstat = 0;
1246 iostat_seek(fd->sc_dk.dk_stats);
1247 disk_busy(&fd->sc_dk);
1249 callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
1250 return 1;
1252 case DOIO:
1253 doio:
1254 #ifdef NOTYET
1255 /* Check to see if the disk has changed */
1256 if (fdc->sc_reg_dir & FDI_DCHG) {
1258 * The disk in the drive has changed since
1259 * the last transfer. We need to see if its geometry
1260 * has changed.
1263 #endif /* NOTYET */
1265 if (finfo)
1266 fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
1267 (char *)finfo;
1268 type = fd->sc_type;
1269 sec = fd->sc_blkno % type->seccyl;
1270 nblks = type->seccyl - sec;
1271 nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
1272 nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
1273 fd->sc_nblks = nblks;
1274 fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE;
1275 head = sec / type->sectrac;
1276 sec -= head * type->sectrac;
1277 #ifdef DIAGNOSTIC
1279 int block;
1281 block = (fd->sc_cylin * type->heads + head) *
1282 type->sectrac + sec;
1283 if (block != fd->sc_blkno) {
1284 printf("%s: block %d != blkno %" PRIu64 "\n",
1285 device_xname(fdc->sc_dev), block,
1286 fd->sc_blkno);
1287 #ifdef DDB
1288 Debugger();
1289 #endif
1292 #endif
1293 read = bp->b_flags & B_READ;
1295 /* Setup for pseudo DMA */
1296 fdc->sc_data = (char *)bp->b_data + fd->sc_skip;
1297 fdc->sc_tc = fd->sc_nbytes;
1299 *fdc->sc_reg_drs = type->rate;
1300 #ifdef FD_DEBUG
1301 if (fdc_debug > 1)
1302 printf("%s: %s drive %d track %d head %d sec %d "
1303 "nblks %d\n", __func__,
1304 read ? "read" : "write", fd->sc_drive,
1305 fd->sc_cylin, head, sec, nblks);
1306 #endif
1307 fdc->sc_state = IOCOMPLETE;
1308 fdc->sc_istate = ISTATE_DMA;
1309 fdc->sc_nstat = 0;
1310 if (finfo) {
1311 /* formatting */
1312 OUT_FDC(fdc, NE7CMD_FORMAT, IOTIMEDOUT);
1313 OUT_FDC(fdc, (head << 2) | fd->sc_drive, IOTIMEDOUT);
1314 OUT_FDC(fdc, finfo->fd_formb_secshift, IOTIMEDOUT);
1315 OUT_FDC(fdc, finfo->fd_formb_nsecs, IOTIMEDOUT);
1316 OUT_FDC(fdc, finfo->fd_formb_gaplen, IOTIMEDOUT);
1317 OUT_FDC(fdc, finfo->fd_formb_fillbyte, IOTIMEDOUT);
1318 } else {
1319 if (read)
1320 OUT_FDC(fdc, NE7CMD_READ, IOTIMEDOUT);
1321 else
1322 OUT_FDC(fdc, NE7CMD_WRITE, IOTIMEDOUT);
1323 OUT_FDC(fdc, (head << 2) | fd->sc_drive, IOTIMEDOUT);
1324 OUT_FDC(fdc, fd->sc_cylin, IOTIMEDOUT); /*track*/
1325 OUT_FDC(fdc, head, IOTIMEDOUT);
1326 OUT_FDC(fdc, sec + 1, IOTIMEDOUT); /*sector+1*/
1327 OUT_FDC(fdc, type->secsize, IOTIMEDOUT);/*sector size*/
1328 OUT_FDC(fdc, type->sectrac, IOTIMEDOUT);/*secs/track*/
1329 OUT_FDC(fdc, type->gap1, IOTIMEDOUT); /*gap1 size*/
1330 OUT_FDC(fdc, type->datalen, IOTIMEDOUT);/*data length*/
1333 disk_busy(&fd->sc_dk);
1335 /* allow 2 seconds for operation */
1336 callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
1337 return 1; /* will return later */
1339 case SEEKWAIT:
1340 callout_stop(&fdc->sc_timo_ch);
1341 fdc->sc_state = SEEKCOMPLETE;
1342 if (fdc->sc_flags & FDC_NEEDHEADSETTLE) {
1343 /* allow 1/50 second for heads to settle */
1344 callout_reset(&fdc->sc_intr_ch, hz / 50,
1345 fdcpseudointr, fdc);
1346 return 1; /* will return later */
1348 /*FALLTHROUGH*/
1349 case SEEKCOMPLETE:
1350 /* no data on seek */
1351 disk_unbusy(&fd->sc_dk, 0, 0);
1353 /* Make sure seek really happened. */
1354 if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 ||
1355 cyl != bp->b_cylinder * fd->sc_type->step) {
1356 #ifdef FD_DEBUG
1357 if (fdc_debug)
1358 fdcstatus(fd->sc_dv, 2, "seek failed");
1359 #endif
1360 fdcretry(fdc);
1361 goto loop;
1363 fd->sc_cylin = bp->b_cylinder;
1364 goto doio;
1366 case IOTIMEDOUT:
1367 fdc->sc_fcr |= FCR_TC;
1368 FCR_REG_SYNC();
1369 delay(10);
1370 fdc->sc_fcr &= ~FCR_TC;
1371 FCR_REG_SYNC();
1372 (void)fdcresult(fdc);
1373 /* FALLTHROUGH */
1374 case SEEKTIMEDOUT:
1375 case RECALTIMEDOUT:
1376 case RESETTIMEDOUT:
1377 fdcretry(fdc);
1378 goto loop;
1380 case IOCOMPLETE: /* IO DONE, post-analyze */
1381 callout_stop(&fdc->sc_timo_ch);
1383 disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
1384 (bp->b_flags & B_READ));
1386 if (fdc->sc_nstat != 7 || (st0 & 0xf8) != 0 || st1 != 0) {
1387 #ifdef FD_DEBUG
1388 if (fdc_debug) {
1389 fdcstatus(fd->sc_dv, 7,
1390 bp->b_flags & B_READ
1391 ? "read failed" : "write failed");
1392 printf("blkno %d nblks %d tc %d\n",
1393 (int)fd->sc_blkno, fd->sc_nblks,
1394 fdc->sc_tc);
1396 #endif
1397 if (fdc->sc_nstat == 7 &&
1398 (st1 & ST1_OVERRUN) == ST1_OVERRUN) {
1401 * Silently retry overruns if no other
1402 * error bit is set. Adjust threshold.
1404 int thr = fdc->sc_cfg & CFG_THRHLD_MASK;
1405 if (thr < 15) {
1406 thr++;
1407 fdc->sc_cfg &= ~CFG_THRHLD_MASK;
1408 fdc->sc_cfg |= (thr & CFG_THRHLD_MASK);
1409 #ifdef FD_DEBUG
1410 if (fdc_debug)
1411 printf("fdc: %d -> threshold\n",
1412 thr);
1413 #endif
1414 fdconf(fdc);
1415 fdc->sc_overruns = 0;
1417 if (++fdc->sc_overruns < 3) {
1418 fdc->sc_state = DOIO;
1419 goto loop;
1422 fdcretry(fdc);
1423 goto loop;
1425 if (fdc->sc_errors) {
1426 diskerr(bp, "fd", "soft error", LOG_PRINTF,
1427 fd->sc_skip / FDC_BSIZE, NULL);
1428 printf("\n");
1429 fdc->sc_errors = 0;
1430 } else {
1431 if (--fdc->sc_overruns < -20) {
1432 int thr = fdc->sc_cfg & CFG_THRHLD_MASK;
1433 if (thr > 0) {
1434 thr--;
1435 fdc->sc_cfg &= ~CFG_THRHLD_MASK;
1436 fdc->sc_cfg |= (thr & CFG_THRHLD_MASK);
1437 #ifdef FD_DEBUG
1438 if (fdc_debug)
1439 printf("fdc: %d -> threshold\n",
1440 thr);
1441 #endif
1442 fdconf(fdc);
1444 fdc->sc_overruns = 0;
1447 fd->sc_blkno += fd->sc_nblks;
1448 fd->sc_skip += fd->sc_nbytes;
1449 fd->sc_bcount -= fd->sc_nbytes;
1450 if (!finfo && fd->sc_bcount > 0) {
1451 bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl;
1452 goto doseek;
1454 fdfinish(fd, bp);
1455 goto loop;
1457 case DORESET:
1458 doreset:
1459 /* try a reset, keep motor on */
1460 fd_set_motor(fdc);
1461 delay(100);
1462 fdc_reset(fdc);
1463 fdc->sc_nstat = 0;
1464 fdc->sc_istate = ISTATE_SENSEI;
1465 fdc->sc_state = RESETCOMPLETE;
1466 callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc);
1467 return 1; /* will return later */
1469 case RESETCOMPLETE:
1470 callout_stop(&fdc->sc_timo_ch);
1471 fdconf(fdc);
1473 /* FALLTHROUGH */
1474 case DORECAL:
1475 fdc->sc_state = RECALWAIT;
1476 fdc->sc_istate = ISTATE_SENSEI;
1477 fdc->sc_nstat = 0;
1478 /* recalibrate function */
1479 OUT_FDC(fdc, NE7CMD_RECAL, RECALTIMEDOUT);
1480 OUT_FDC(fdc, fd->sc_drive, RECALTIMEDOUT);
1481 callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc);
1482 return 1; /* will return later */
1484 case RECALWAIT:
1485 callout_stop(&fdc->sc_timo_ch);
1486 fdc->sc_state = RECALCOMPLETE;
1487 if (fdc->sc_flags & FDC_NEEDHEADSETTLE) {
1488 /* allow 1/30 second for heads to settle */
1489 callout_reset(&fdc->sc_intr_ch, hz / 30,
1490 fdcpseudointr, fdc);
1491 return 1; /* will return later */
1494 case RECALCOMPLETE:
1495 if (fdc->sc_nstat != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
1496 #ifdef FD_DEBUG
1497 if (fdc_debug)
1498 fdcstatus(fd->sc_dv, 2, "recalibrate failed");
1499 #endif
1500 fdcretry(fdc);
1501 goto loop;
1503 fd->sc_cylin = 0;
1504 goto doseek;
1506 case MOTORWAIT:
1507 if (fd->sc_flags & FD_MOTOR_WAIT)
1508 return 1; /* time's not up yet */
1509 goto doseek;
1511 default:
1512 fdcstatus(fd->sc_dv, 0, "stray interrupt");
1513 return 1;
1515 #ifdef DIAGNOSTIC
1516 panic("%s: impossible", __func__);
1517 #endif
1518 #undef st0
1519 #undef st1
1520 #undef cyl
1523 void
1524 fdcretry(struct fdc_softc *fdc)
1526 struct fd_softc *fd;
1527 struct buf *bp;
1529 fd = fdc->sc_drives.tqh_first;
1530 bp = bufq_peek(fd->sc_q);
1532 fdc->sc_overruns = 0;
1533 if (fd->sc_opts & FDOPT_NORETRY)
1534 goto fail;
1536 switch (fdc->sc_errors) {
1537 case 0:
1538 /* try again */
1539 fdc->sc_state =
1540 (fdc->sc_flags & FDC_EIS) ? DOIO : DOSEEK;
1541 break;
1543 case 1:
1544 case 2:
1545 case 3:
1546 /* didn't work; try recalibrating */
1547 fdc->sc_state = DORECAL;
1548 break;
1550 case 4:
1551 /* still no go; reset the bastard */
1552 fdc->sc_state = DORESET;
1553 break;
1555 default:
1556 fail:
1557 if ((fd->sc_opts & FDOPT_SILENT) == 0) {
1558 diskerr(bp, "fd", "hard error", LOG_PRINTF,
1559 fd->sc_skip / FDC_BSIZE, NULL);
1561 fdcpstatus(7, fdc);
1564 bp->b_error = EIO;
1565 fdfinish(fd, bp);
1567 fdc->sc_errors++;
1570 int
1571 fdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
1573 struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
1574 struct fdformat_parms *form_parms;
1575 struct fdformat_cmd *form_cmd;
1576 struct ne7_fd_formb *fd_formb;
1577 int il[FD_MAX_NSEC + 1];
1578 int i, j;
1579 int error;
1581 switch (cmd) {
1582 case DIOCGDINFO:
1583 *(struct disklabel *)addr = *(fd->sc_dk.dk_label);
1584 return 0;
1586 case DIOCWLABEL:
1587 if ((flag & FWRITE) == 0)
1588 return EBADF;
1589 /* XXX do something */
1590 return 0;
1592 case DIOCWDINFO:
1593 if ((flag & FWRITE) == 0)
1594 return EBADF;
1596 error = setdisklabel(fd->sc_dk.dk_label,
1597 (struct disklabel *)addr, 0, fd->sc_dk.dk_cpulabel);
1598 if (error)
1599 return error;
1601 error = writedisklabel(dev, fdstrategy, fd->sc_dk.dk_label,
1602 fd->sc_dk.dk_cpulabel);
1603 return error;
1605 case DIOCLOCK:
1607 * Nothing to do here, really.
1609 return 0;
1611 case DIOCEJECT:
1612 if (*(int *)addr == 0) {
1613 int part = DISKPART(dev);
1615 * Don't force eject: check that we are the only
1616 * partition open. If so, unlock it.
1618 if ((fd->sc_dk.dk_openmask & ~(1 << part)) != 0 ||
1619 fd->sc_dk.dk_bopenmask + fd->sc_dk.dk_copenmask !=
1620 fd->sc_dk.dk_openmask) {
1621 return EBUSY;
1624 /* FALLTHROUGH */
1625 case ODIOCEJECT:
1626 fd_do_eject(device_private(device_parent(fd->sc_dv)),
1627 fd->sc_drive);
1628 return 0;
1630 case FDIOCGETFORMAT:
1631 form_parms = (struct fdformat_parms *)addr;
1632 form_parms->fdformat_version = FDFORMAT_VERSION;
1633 form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
1634 form_parms->ncyl = fd->sc_type->tracks;
1635 form_parms->nspt = fd->sc_type->sectrac;
1636 form_parms->ntrk = fd->sc_type->heads;
1637 form_parms->stepspercyl = fd->sc_type->step;
1638 form_parms->gaplen = fd->sc_type->gap2;
1639 form_parms->fillbyte = fd->sc_type->fillbyte;
1640 form_parms->interleave = fd->sc_type->interleave;
1641 switch (fd->sc_type->rate) {
1642 case FDC_500KBPS:
1643 form_parms->xfer_rate = 500 * 1024;
1644 break;
1645 case FDC_300KBPS:
1646 form_parms->xfer_rate = 300 * 1024;
1647 break;
1648 case FDC_250KBPS:
1649 form_parms->xfer_rate = 250 * 1024;
1650 break;
1651 default:
1652 return EINVAL;
1654 return 0;
1656 case FDIOCSETFORMAT:
1657 if ((flag & FWRITE) == 0)
1658 return EBADF; /* must be opened for writing */
1660 form_parms = (struct fdformat_parms *)addr;
1661 if (form_parms->fdformat_version != FDFORMAT_VERSION)
1662 return EINVAL; /* wrong version of formatting prog */
1664 i = form_parms->nbps >> 7;
1665 if ((form_parms->nbps & 0x7f) || ffs(i) == 0 ||
1666 i & ~(1 << (ffs(i) - 1)))
1667 /* not a power-of-two multiple of 128 */
1668 return EINVAL;
1670 switch (form_parms->xfer_rate) {
1671 case 500 * 1024:
1672 fd->sc_type->rate = FDC_500KBPS;
1673 break;
1674 case 300 * 1024:
1675 fd->sc_type->rate = FDC_300KBPS;
1676 break;
1677 case 250 * 1024:
1678 fd->sc_type->rate = FDC_250KBPS;
1679 break;
1680 default:
1681 return EINVAL;
1684 if (form_parms->nspt > FD_MAX_NSEC ||
1685 form_parms->fillbyte > 0xff ||
1686 form_parms->interleave > 0xff)
1687 return EINVAL;
1688 fd->sc_type->sectrac = form_parms->nspt;
1689 if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
1690 return EINVAL;
1691 fd->sc_type->heads = form_parms->ntrk;
1692 fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
1693 fd->sc_type->secsize = ffs(i) - 1;
1694 fd->sc_type->gap2 = form_parms->gaplen;
1695 fd->sc_type->tracks = form_parms->ncyl;
1696 fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
1697 form_parms->nbps / DEV_BSIZE;
1698 fd->sc_type->step = form_parms->stepspercyl;
1699 fd->sc_type->fillbyte = form_parms->fillbyte;
1700 fd->sc_type->interleave = form_parms->interleave;
1701 return 0;
1703 case FDIOCFORMAT_TRACK:
1704 if((flag & FWRITE) == 0)
1705 /* must be opened for writing */
1706 return EBADF;
1707 form_cmd = (struct fdformat_cmd *)addr;
1708 if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
1709 /* wrong version of formatting prog */
1710 return EINVAL;
1712 if (form_cmd->head >= fd->sc_type->heads ||
1713 form_cmd->cylinder >= fd->sc_type->tracks) {
1714 return EINVAL;
1717 fd_formb = malloc(sizeof(struct ne7_fd_formb),
1718 M_TEMP, M_NOWAIT);
1719 if (fd_formb == 0)
1720 return ENOMEM;
1722 fd_formb->head = form_cmd->head;
1723 fd_formb->cyl = form_cmd->cylinder;
1724 fd_formb->transfer_rate = fd->sc_type->rate;
1725 fd_formb->fd_formb_secshift = fd->sc_type->secsize;
1726 fd_formb->fd_formb_nsecs = fd->sc_type->sectrac;
1727 fd_formb->fd_formb_gaplen = fd->sc_type->gap2;
1728 fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte;
1730 memset(il, 0, sizeof(il));
1731 for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) {
1732 while (il[(j%fd_formb->fd_formb_nsecs) + 1])
1733 j++;
1734 il[(j % fd_formb->fd_formb_nsecs) + 1] = i;
1735 j += fd->sc_type->interleave;
1737 for (i = 0; i < fd_formb->fd_formb_nsecs; i++) {
1738 fd_formb->fd_formb_cylno(i) = form_cmd->cylinder;
1739 fd_formb->fd_formb_headno(i) = form_cmd->head;
1740 fd_formb->fd_formb_secno(i) = il[i+1];
1741 fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize;
1744 error = fdformat(dev, fd_formb, l->l_proc);
1745 free(fd_formb, M_TEMP);
1746 return error;
1748 case FDIOCGETOPTS: /* get drive options */
1749 *(int *)addr = fd->sc_opts;
1750 return 0;
1752 case FDIOCSETOPTS: /* set drive options */
1753 fd->sc_opts = *(int *)addr;
1754 return 0;
1756 #ifdef DEBUG
1757 case _IO('f', 100):
1759 int k;
1760 struct fdc_softc *fdc =
1761 device_private(device_parent(fd->sc_dv));
1763 out_fdc(fdc, NE7CMD_DUMPREG);
1764 fdcresult(fdc);
1765 printf("dumpreg(%d regs): <", fdc->sc_nstat);
1766 for (k = 0; k < fdc->sc_nstat; k++)
1767 printf(" %x", fdc->sc_status[k]);
1768 printf(">\n");
1770 return 0;
1772 case _IOW('f', 101, int):
1773 struct fdc_softc *fdc =
1774 device_private(device_parent(fd->sc_dv));
1776 fdc->sc_cfg &= ~CFG_THRHLD_MASK;
1777 fdc->sc_cfg |= (*(int *)addr & CFG_THRHLD_MASK);
1778 fdconf(fdc);
1779 return 0;
1781 case _IO('f', 102):
1783 int k;
1784 struct fdc_softc *fdc =
1785 device_private(device_parent(fd->sc_dv));
1787 out_fdc(fdc, NE7CMD_SENSEI);
1788 fdcresult(fdc);
1789 printf("sensei(%d regs): <", fdc->sc_nstat);
1790 for (k=0; k < fdc->sc_nstat; k++)
1791 printf(" 0x%x", fdc->sc_status[k]);
1793 printf(">\n");
1794 return 0;
1795 #endif
1796 default:
1797 return ENOTTY;
1800 #ifdef DIAGNOSTIC
1801 panic("%s: impossible", __func__);
1802 #endif
1805 int
1806 fdformat(dev_t dev, struct ne7_fd_formb *finfo, struct proc *p)
1808 int rv = 0;
1809 struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
1810 struct fd_type *type = fd->sc_type;
1811 struct buf *bp;
1813 /* set up a buffer header for fdstrategy() */
1814 bp = getiobuf(NULL, false);
1815 if (bp == NULL)
1816 return ENOBUFS;
1818 bp->b_vp = NULL;
1819 bp->b_cflags = BC_BUSY;
1820 bp->b_flags = B_PHYS | B_FORMAT;
1821 bp->b_proc = p;
1822 bp->b_dev = dev;
1825 * Calculate a fake blkno, so fdstrategy() would initiate a
1826 * seek to the requested cylinder.
1828 bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
1829 + finfo->head * type->sectrac) * FDC_BSIZE / DEV_BSIZE;
1831 bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1832 bp->b_data = (void *)finfo;
1834 #ifdef FD_DEBUG
1835 if (fdc_debug)
1836 printf("%s: blkno %x count %d\n",
1837 __func__, (int)bp->b_blkno, bp->b_bcount);
1838 #endif
1840 /* now do the format */
1841 fdstrategy(bp);
1843 /* ...and wait for it to complete */
1844 rv = biowait(bp);
1845 putiobuf(bp);
1846 return rv;
1849 void
1850 fdgetdisklabel(dev_t dev)
1852 int unit = FDUNIT(dev), i;
1853 struct fd_softc *fd = device_lookup_private(&fd_cd, unit);
1854 struct disklabel *lp = fd->sc_dk.dk_label;
1855 struct cpu_disklabel *clp = fd->sc_dk.dk_cpulabel;
1857 memset(lp, 0, sizeof(struct disklabel));
1858 memset(lp, 0, sizeof(struct cpu_disklabel));
1860 lp->d_type = DTYPE_FLOPPY;
1861 lp->d_secsize = FDC_BSIZE;
1862 lp->d_secpercyl = fd->sc_type->seccyl;
1863 lp->d_nsectors = fd->sc_type->sectrac;
1864 lp->d_ncylinders = fd->sc_type->tracks;
1865 lp->d_ntracks = fd->sc_type->heads; /* Go figure... */
1866 lp->d_rpm = 3600; /* XXX like it matters... */
1868 strncpy(lp->d_typename, "floppy", sizeof(lp->d_typename));
1869 strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1870 lp->d_interleave = 1;
1872 lp->d_partitions[RAW_PART].p_offset = 0;
1873 lp->d_partitions[RAW_PART].p_size = lp->d_secpercyl * lp->d_ncylinders;
1874 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
1875 lp->d_npartitions = RAW_PART + 1;
1877 lp->d_magic = DISKMAGIC;
1878 lp->d_magic2 = DISKMAGIC;
1879 lp->d_checksum = dkcksum(lp);
1882 * Call the generic disklabel extraction routine. If there's
1883 * not a label there, fake it.
1885 if (readdisklabel(dev, fdstrategy, lp, clp) != NULL) {
1886 strncpy(lp->d_packname, "default label",
1887 sizeof(lp->d_packname));
1889 * Reset the partition info; it might have gotten
1890 * trashed in readdisklabel().
1892 * XXX Why do we have to do this? readdisklabel()
1893 * should be safe...
1895 for (i = 0; i < MAXPARTITIONS; ++i) {
1896 lp->d_partitions[i].p_offset = 0;
1897 if (i == RAW_PART) {
1898 lp->d_partitions[i].p_size =
1899 lp->d_secpercyl * lp->d_ncylinders;
1900 lp->d_partitions[i].p_fstype = FS_BSDFFS;
1901 } else {
1902 lp->d_partitions[i].p_size = 0;
1903 lp->d_partitions[i].p_fstype = FS_UNUSED;
1906 lp->d_npartitions = RAW_PART + 1;
1910 void
1911 fd_do_eject(struct fdc_softc *fdc, int unit)
1914 fdc->sc_fcr |= FCR_DSEL(unit)|FCR_EJECT;
1915 FCR_REG_SYNC();
1916 delay(10);
1917 fdc->sc_fcr &= ~(FCR_DSEL(unit)|FCR_EJECT);
1918 FCR_REG_SYNC();
1921 #ifdef MEMORY_DISK_HOOKS_sun3x_not_yet
1922 int fd_read_md_image(size_t *, void **);
1923 #endif
1925 /* ARGSUSED */
1926 void
1927 fd_mountroot_hook(device_t dev)
1929 struct fd_softc *fd;
1930 struct fdc_softc *fdc;
1931 int c;
1933 fd = device_private(dev);
1934 fdc = device_private(device_parent(dev));
1935 fd_do_eject(fdc, fd->sc_drive);
1936 printf("Insert filesystem floppy and press return.");
1937 for (;;) {
1938 c = cngetc();
1939 if ((c == '\r') || (c == '\n')) {
1940 printf("\n");
1941 break;
1944 #ifdef MEMORY_DISK_HOOKS_sun3x_not_yet
1946 extern int (*md_read_image)(size_t *, void **);
1948 md_read_image = fd_read_md_image;
1950 #endif
1953 #ifdef MEMORY_DISK_HOOKS_sun3x_not_yet
1955 #define FDMICROROOTSIZE ((2*18*80) << DEV_BSHIFT)
1957 int
1958 fd_read_md_image(size_t *sizep, void **addrp)
1960 struct fdc_softc *fdc;
1961 struct fd_softc *fd;
1962 struct buf buf, *bp = &buf;
1963 dev_t dev;
1964 off_t offset;
1965 void *addr;
1967 dev = makedev(cdevsw_lookup_major(&fd_cdevsw), 0); /* XXX */
1969 addr = malloc(FDMICROROOTSIZE, M_DEVBUF, M_WAITOK);
1970 *addrp = addr;
1972 if (fdopen(dev, 0, S_IFCHR, NULL))
1973 panic("fd: mountroot: fdopen");
1975 offset = 0;
1977 for (;;) {
1978 bp->b_dev = dev;
1979 bp->b_error = 0;
1980 bp->b_resid = 0;
1981 bp->b_proc = NULL;
1982 bp->b_flags = B_PHYS | B_RAW | B_READ;
1983 bp->b_cflags = BC_BUSY;
1984 bp->b_blkno = btodb(offset);
1985 bp->b_bcount = DEV_BSIZE;
1986 bp->b_data = addr;
1987 fdstrategy(bp);
1988 biowait(bp);
1989 if (bp->b_error)
1990 panic("fd: mountroot: fdread error %d", bp->b_error);
1992 if (bp->b_resid != 0)
1993 break;
1995 addr += DEV_BSIZE;
1996 offset += DEV_BSIZE;
1997 if (offset + DEV_BSIZE > FDMICROROOTSIZE)
1998 break;
2000 (void)fdclose(dev, 0, S_IFCHR, NULL);
2001 *sizep = offset;
2002 fd = device_lookup_private(&fd_cd, 0);
2003 fdc = device_private(device_parent(fd->sc_dv));
2004 fd_do_eject(fdc, 0); /* XXX */
2005 return 0;
2007 #endif