xref: /netbsd-src/sys/arch/x68k/dev/fd.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: fd.c,v 1.124 2021/04/24 23:36:51 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum and Minoura Makoto.
9  *
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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*-
33  * Copyright (c) 1990 The Regents of the University of California.
34  * All rights reserved.
35  *
36  * This code is derived from software contributed to Berkeley by
37  * Don Ahn.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  *
63  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
64  */
65 
66 #include <sys/cdefs.h>
67 __KERNEL_RCSID(0, "$NetBSD: fd.c,v 1.124 2021/04/24 23:36:51 thorpej Exp $");
68 
69 #include "opt_ddb.h"
70 #include "opt_m68k_arch.h"
71 
72 #include <sys/param.h>
73 #include <sys/systm.h>
74 #include <sys/bus.h>
75 #include <sys/callout.h>
76 #include <sys/kernel.h>
77 #include <sys/conf.h>
78 #include <sys/file.h>
79 #include <sys/stat.h>
80 #include <sys/ioctl.h>
81 #include <sys/malloc.h>
82 #include <sys/device.h>
83 #include <sys/disklabel.h>
84 #include <sys/disk.h>
85 #include <sys/buf.h>
86 #include <sys/bufq.h>
87 #include <sys/uio.h>
88 #include <sys/syslog.h>
89 #include <sys/queue.h>
90 #include <sys/proc.h>
91 #include <sys/fdio.h>
92 #include <sys/rndsource.h>
93 
94 #include <dev/cons.h>
95 
96 #include <machine/cpu.h>
97 
98 #include <arch/x68k/dev/intiovar.h>
99 #include <arch/x68k/dev/dmacvar.h>
100 #include <arch/x68k/dev/fdreg.h>
101 #include <arch/x68k/dev/opmvar.h> /* for CT1 access */
102 
103 #include "locators.h"
104 #include "ioconf.h"
105 
106 #ifdef FDDEBUG
107 #define DPRINTF(x)      if (fddebug) printf x
108 int     fddebug = 0;
109 #else
110 #define DPRINTF(x)
111 #endif
112 
113 #define FDUNIT(dev)	(minor(dev) / 8)
114 #define FDTYPE(dev)	(minor(dev) % 8)
115 
116 /* (mis)use device use flag to identify format operation */
117 #define B_FORMAT B_DEVPRIVATE
118 
119 enum fdc_state {
120 	DEVIDLE = 0,
121 	MOTORWAIT,
122 	DOSEEK,
123 	SEEKWAIT,
124 	SEEKTIMEDOUT,
125 	SEEKCOMPLETE,
126 	DOIO,
127 	IOCOMPLETE,
128 	IOTIMEDOUT,
129 	DORESET,
130 	RESETCOMPLETE,
131 	RESETTIMEDOUT,
132 	DORECAL,
133 	RECALWAIT,
134 	RECALTIMEDOUT,
135 	RECALCOMPLETE,
136 	DOCOPY,
137 	DOIOHALF,
138 	COPYCOMPLETE,
139 };
140 
141 /* software state, per controller */
142 struct fdc_softc {
143 	bus_space_tag_t sc_iot;		/* intio i/o space identifier */
144 	bus_space_handle_t sc_ioh;	/* intio io handle */
145 
146 	struct callout sc_timo_ch;	/* timeout callout */
147 	struct callout sc_intr_ch;	/* pseudo-intr callout */
148 
149 	bus_dma_tag_t sc_dmat;		/* intio DMA tag */
150 	bus_dmamap_t sc_dmamap;		/* DMA map */
151 	uint8_t *sc_addr;		/* physical address */
152 	struct dmac_channel_stat *sc_dmachan; /* intio DMA channel */
153 	struct dmac_dma_xfer *sc_xfer;	/* DMA transfer */
154 	int sc_read;
155 
156 	struct fd_softc *sc_fd[4];	/* pointers to children */
157 	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
158 	enum fdc_state sc_state;
159 	int sc_errors;			/* number of retries so far */
160 	uint8_t sc_status[7];		/* copy of registers */
161 } fdc_softc;
162 
163 int fdcintr(void *);
164 void fdcreset(struct fdc_softc *);
165 
166 /* controller driver configuration */
167 int fdcprobe(device_t, cfdata_t, void *);
168 void fdcattach(device_t, device_t, void *);
169 int fdprint(void *, const char *);
170 
171 CFATTACH_DECL_NEW(fdc, sizeof(struct fdc_softc),
172     fdcprobe, fdcattach, NULL, NULL);
173 
174 /*
175  * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
176  * we tell them apart.
177  */
178 struct fd_type {
179 	int	sectrac;	/* sectors per track */
180 	int	heads;		/* number of heads */
181 	int	seccyl;		/* sectors per cylinder */
182 	int	secsize;	/* size code for sectors */
183 	int	datalen;	/* data len when secsize = 0 */
184 	int	steprate;	/* step rate and head unload time */
185 	int	gap1;		/* gap len between sectors */
186 	int	gap2;		/* formatting gap */
187 	int	cyls;		/* total num of cylinders */
188 	int	size;		/* size of disk in sectors */
189 	int	step;		/* steps per cylinder */
190 	int	rate;		/* transfer speed code */
191 	uint8_t	fillbyte;	/* format fill byte */
192 	uint8_t	interleave;	/* interleave factor (formatting) */
193 	const char *name;
194 };
195 
196 /* The order of entries in the following table is important -- BEWARE! */
197 struct fd_type fd_types[] = {
198 	{  8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS, 0xf6, 1,
199 	    "1.2MB/[1024bytes/sector]"    }, /* 1.2 MB japanese format */
200 	{ 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS, 0xf6, 1,
201 	    "1.44MB"    }, /* 1.44MB diskette */
202 	{ 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, 0xf6, 1,
203 	    "1.2MB"    }, /* 1.2 MB AT-diskettes */
204 	{  9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, 0xf6, 1,
205 	    "360KB/AT" }, /* 360kB in 1.2MB drive */
206 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, 0xf6, 1,
207 	    "360KB/PC" }, /* 360kB PC diskettes */
208 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, 0xf6, 1,
209 	    "720KB"    }, /* 3.5" 720kB diskette */
210 	{  9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, 0xf6, 1,
211 	    "720KB/x"  }, /* 720kB in 1.2MB drive */
212 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, 0xf6, 1,
213 	    "360KB/x"  }, /* 360kB in 720kB drive */
214 };
215 
216 /* software state, per disk (with up to 4 disks per ctlr) */
217 struct fd_softc {
218 	device_t sc_dev;
219 	struct disk sc_dk;
220 
221 	struct fd_type *sc_deftype;	/* default type descriptor */
222 	struct fd_type *sc_type;	/* current type descriptor */
223 
224 #if 0	/* see comments in fd_motor_on() */
225 	struct callout sc_motoron_ch;
226 #endif
227 	struct callout sc_motoroff_ch;
228 
229 	daddr_t	sc_blkno;	/* starting block number */
230 	int sc_bcount;		/* byte count left */
231 	int sc_opts;		/* user-set options */
232 	int sc_skip;		/* bytes already transferred */
233 	int sc_nblks;		/* number of blocks currently transferring */
234 	int sc_nbytes;		/* number of bytes currently transferring */
235 
236 	int sc_drive;		/* physical unit number */
237 	int sc_flags;
238 #define	FD_BOPEN	0x01		/* it's open */
239 #define	FD_COPEN	0x02		/* it's open */
240 #define	FD_OPEN		(FD_BOPEN|FD_COPEN)	/* it's open */
241 #define	FD_MOTOR	0x04		/* motor should be on */
242 #define	FD_MOTOR_WAIT	0x08		/* motor coming up */
243 #define	FD_ALIVE	0x10		/* alive */
244 	int sc_cylin;		/* where we think the head is */
245 
246 	TAILQ_ENTRY(fd_softc) sc_drivechain;
247 	int sc_ops;		/* I/O ops since last switch */
248 	struct bufq_state *sc_q;/* pending I/O requests */
249 	int sc_active;		/* number of active I/O operations */
250 	uint8_t *sc_copybuf;	/* for secsize >=3 */
251 	uint8_t sc_part;	/* for secsize >=3 */
252 #define	SEC_P10	0x02		/* first part */
253 #define	SEC_P01	0x01		/* second part */
254 #define	SEC_P11	0x03		/* both part */
255 
256 	krndsource_t	rnd_source;
257 };
258 
259 /* floppy driver configuration */
260 int fdprobe(device_t, cfdata_t, void *);
261 void fdattach(device_t, device_t, void *);
262 
263 CFATTACH_DECL_NEW(fd, sizeof(struct fd_softc),
264     fdprobe, fdattach, NULL, NULL);
265 
266 dev_type_open(fdopen);
267 dev_type_close(fdclose);
268 dev_type_read(fdread);
269 dev_type_write(fdwrite);
270 dev_type_ioctl(fdioctl);
271 dev_type_strategy(fdstrategy);
272 
273 const struct bdevsw fd_bdevsw = {
274 	.d_open = fdopen,
275 	.d_close = fdclose,
276 	.d_strategy = fdstrategy,
277 	.d_ioctl = fdioctl,
278 	.d_dump = nodump,
279 	.d_psize = nosize,
280 	.d_discard = nodiscard,
281 	.d_flag = D_DISK
282 };
283 
284 const struct cdevsw fd_cdevsw = {
285 	.d_open = fdopen,
286 	.d_close = fdclose,
287 	.d_read = fdread,
288 	.d_write = fdwrite,
289 	.d_ioctl = fdioctl,
290 	.d_stop = nostop,
291 	.d_tty = notty,
292 	.d_poll = nopoll,
293 	.d_mmap = nommap,
294 	.d_kqfilter = nokqfilter,
295 	.d_discard = nodiscard,
296 	.d_flag = D_DISK
297 };
298 
299 void fdstart(struct fd_softc *);
300 
301 struct dkdriver fddkdriver = {
302 	.d_strategy = fdstrategy
303 };
304 
305 void fd_set_motor(struct fdc_softc *, int);
306 void fd_motor_off(void *);
307 #if 0
308 void fd_motor_on(void *);
309 #endif
310 int fdcresult(struct fdc_softc *);
311 int out_fdc(bus_space_tag_t, bus_space_handle_t, uint8_t);
312 void fdcstart(struct fdc_softc *);
313 void fdcstatus(device_t, int, const char *);
314 void fdctimeout(void *);
315 void fdcpseudointr(void *);
316 void fdcretry(struct fdc_softc *);
317 void fdfinish(struct fd_softc *, struct buf *);
318 static struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
319 int fdformat(dev_t, struct ne7_fd_formb *, struct lwp *);
320 static int fdcpoll(struct fdc_softc *);
321 static int fdgetdisklabel(struct fd_softc *, dev_t);
322 static void fd_do_eject(struct fdc_softc *, int);
323 
324 void fd_mountroot_hook(device_t);
325 
326 /* DMA transfer routines */
327 inline static void fdc_dmastart(struct fdc_softc *, int, void *, vsize_t);
328 inline static void fdc_dmaabort(struct fdc_softc *);
329 static int fdcdmaintr(void *);
330 static int fdcdmaerrintr(void *);
331 
332 inline static void
333 fdc_dmastart(struct fdc_softc *fdc, int read, void *addr, vsize_t count)
334 {
335 	int error;
336 
337 	DPRINTF(("fdc_dmastart: %s, addr = %p, count = %ld\n",
338 	    read ? "read" : "write", (void *)addr, count));
339 
340 	error = bus_dmamap_load(fdc->sc_dmat, fdc->sc_dmamap, addr, count,
341 	    0, BUS_DMA_NOWAIT);
342 	if (error) {
343 		panic("fdc_dmastart: cannot load dmamap");
344 	}
345 
346 	bus_dmamap_sync(fdc->sc_dmat, fdc->sc_dmamap, 0, count,
347 	    read ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
348 
349 	/*
350 	 * Note 1:
351 	 *  uPD72065 ignores A0 input (connected to x68k bus A1)
352 	 *  during DMA xfer access, but it's better to explicitly
353 	 *  specify FDC data register address for clarification.
354 	 * Note 2:
355 	 *  FDC is connected to LSB 8 bits of X68000 16 bit bus
356 	 *  (as BUS_SPACE_MAP_SHIFTED_ODD defined in bus.h)
357 	 *  so each FDC register is mapped at sparse odd address.
358 	 *
359 	 * XXX: No proper API to get DMA address of FDC register for DMAC.
360 	 */
361 	fdc->sc_xfer = dmac_prepare_xfer(fdc->sc_dmachan, fdc->sc_dmat,
362 	    fdc->sc_dmamap,
363 	    read ? DMAC_OCR_DIR_DTM : DMAC_OCR_DIR_MTD,
364 	    DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT,
365 	    fdc->sc_addr + fddata * 2 + 1);
366 
367 	fdc->sc_read = read;
368 	dmac_start_xfer(fdc->sc_dmachan->ch_softc, fdc->sc_xfer);
369 }
370 
371 inline static void
372 fdc_dmaabort(struct fdc_softc *fdc)
373 {
374 
375 	dmac_abort_xfer(fdc->sc_dmachan->ch_softc, fdc->sc_xfer);
376 	bus_dmamap_unload(fdc->sc_dmat, fdc->sc_dmamap);
377 }
378 
379 static int
380 fdcdmaintr(void *arg)
381 {
382 	struct fdc_softc *fdc = arg;
383 
384 	bus_dmamap_sync(fdc->sc_dmat, fdc->sc_dmamap,
385 	    0, fdc->sc_dmamap->dm_mapsize,
386 	    fdc->sc_read ?
387 	    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
388 	bus_dmamap_unload(fdc->sc_dmat, fdc->sc_dmamap);
389 
390 	return 0;
391 }
392 
393 static int
394 fdcdmaerrintr(void *dummy)
395 {
396 
397 	DPRINTF(("fdcdmaerrintr\n"));
398 
399 	return 0;
400 }
401 
402 /* ARGSUSED */
403 int
404 fdcprobe(device_t parent, cfdata_t cf, void *aux)
405 {
406 	struct intio_attach_args *ia = aux;
407 
408 	if (strcmp(ia->ia_name, "fdc") != 0)
409 		return 0;
410 
411 	if (ia->ia_addr == INTIOCF_ADDR_DEFAULT)
412 		ia->ia_addr = FDC_ADDR;
413 	if (ia->ia_intr == INTIOCF_INTR_DEFAULT)
414 		ia->ia_intr = FDC_INTR;
415 	if (ia->ia_dma == INTIOCF_DMA_DEFAULT)
416 		ia->ia_dma = FDC_DMA;
417 	if (ia->ia_dmaintr == INTIOCF_DMAINTR_DEFAULT)
418 		ia->ia_dmaintr = FDC_DMAINTR;
419 
420 	if ((ia->ia_intr & 0x03) != 0)
421 		return 0;
422 
423 	ia->ia_size = FDC_MAPSIZE;
424 	if (intio_map_allocate_region(parent, ia, INTIO_MAP_TESTONLY))
425 		return 0;
426 
427 	/* builtin device; always there */
428 	return 1;
429 }
430 
431 /*
432  * Arguments passed between fdcattach and fdprobe.
433  */
434 struct fdc_attach_args {
435 	int fa_drive;
436 	struct fd_type *fa_deftype;
437 };
438 
439 /*
440  * Print the location of a disk drive (called just before attaching the
441  * the drive).  If `fdc' is not NULL, the drive was found but was not
442  * in the system config file; print the drive name as well.
443  * Return QUIET (config_find ignores this if the device was configured) to
444  * avoid printing `fdN not configured' messages.
445  */
446 int
447 fdprint(void *aux, const char *fdc)
448 {
449 	struct fdc_attach_args *fa = aux;
450 
451 	if (fdc == NULL)
452 		aprint_normal(" drive %d", fa->fa_drive);
453 	return QUIET;
454 }
455 
456 void
457 fdcattach(device_t parent, device_t self, void *aux)
458 {
459 	struct fdc_softc *fdc = device_private(self);
460 	bus_space_tag_t iot;
461 	bus_space_handle_t ioh;
462 	struct intio_attach_args *ia = aux;
463 	struct fdc_attach_args fa;
464 
465 	iot = ia->ia_bst;
466 
467 	aprint_normal("\n");
468 
469 	/* Re-map the I/O space. */
470 	if (bus_space_map(iot, ia->ia_addr, ia->ia_size,
471 	    BUS_SPACE_MAP_SHIFTED_ODD, &ioh) != 0) {
472 		aprint_error_dev(self, "unable to map I/O space\n");
473 		return;
474 	}
475 
476 	callout_init(&fdc->sc_timo_ch, 0);
477 	callout_init(&fdc->sc_intr_ch, 0);
478 
479 	fdc->sc_iot = iot;
480 	fdc->sc_ioh = ioh;
481 	fdc->sc_addr = (void *)ia->ia_addr;
482 
483 	fdc->sc_dmat = ia->ia_dmat;
484 	fdc->sc_state = DEVIDLE;
485 	TAILQ_INIT(&fdc->sc_drives);
486 
487 	/* Initialize DMAC channel */
488 	fdc->sc_dmachan = dmac_alloc_channel(parent, ia->ia_dma, "fdc",
489 	    ia->ia_dmaintr, fdcdmaintr, fdc,
490 	    ia->ia_dmaintr + 1, fdcdmaerrintr, fdc,
491 	    (DMAC_DCR_XRM_CSWH | DMAC_DCR_OTYP_EASYNC | DMAC_DCR_OPS_8BIT),
492 	    (DMAC_OCR_SIZE_BYTE | DMAC_OCR_REQG_EXTERNAL));
493 
494 	if (bus_dmamap_create(fdc->sc_dmat, FDC_MAXIOSIZE, 1, DMAC_MAXSEGSZ,
495 	    0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &fdc->sc_dmamap)) {
496 		aprint_error_dev(self, "can't set up intio DMA map\n");
497 		return;
498 	}
499 
500 	if (intio_intr_establish(ia->ia_intr, "fdc", fdcintr, fdc) != 0)
501 		panic("Could not establish interrupt (duplicated vector?).");
502 	intio_set_ivec(ia->ia_intr);
503 
504 	/* reset */
505 	intio_disable_intr(SICILIAN_INTR_FDD);
506 	intio_enable_intr(SICILIAN_INTR_FDC);
507 	fdcresult(fdc);
508 	fdcreset(fdc);
509 
510 	aprint_normal_dev(self, "uPD72065 FDC\n");
511 	out_fdc(iot, ioh, NE7CMD_SPECIFY);	/* specify command */
512 	out_fdc(iot, ioh, 0xd0);
513 	out_fdc(iot, ioh, 0x10);
514 
515 	/* physical limit: four drives per controller. */
516 	for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
517 		(void)config_found(self, (void *)&fa, fdprint, CFARG_EOL);
518 	}
519 
520 	intio_enable_intr(SICILIAN_INTR_FDC);
521 }
522 
523 void
524 fdcreset(struct fdc_softc *fdc)
525 {
526 
527 	bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdsts, NE7CMD_RESET);
528 }
529 
530 static int
531 fdcpoll(struct fdc_softc *fdc)
532 {
533 	int i = 25000;
534 
535 	while (--i > 0) {
536 		if ((intio_get_sicilian_intr() & SICILIAN_STAT_FDC) != 0) {
537 			out_fdc(fdc->sc_iot, fdc->sc_ioh, NE7CMD_SENSEI);
538 			fdcresult(fdc);
539 			break;
540 		}
541 		DELAY(100);
542 	}
543 	return i;
544 }
545 
546 int
547 fdprobe(device_t parent, cfdata_t cf, void *aux)
548 {
549 	struct fdc_softc *fdc = device_private(parent);
550 	struct fd_type *type;
551 	struct fdc_attach_args *fa = aux;
552 	int drive = fa->fa_drive;
553 	bus_space_tag_t iot = fdc->sc_iot;
554 	bus_space_handle_t ioh = fdc->sc_ioh;
555 	int n = 0;
556 	int found = 0;
557 	int i;
558 
559 	if (cf->cf_loc[FDCCF_UNIT] != FDCCF_UNIT_DEFAULT &&
560 	    cf->cf_loc[FDCCF_UNIT] != drive)
561 		return 0;
562 
563 	type = &fd_types[0];	/* XXX 1.2MB */
564 
565 	/* toss any interrupt status */
566 	for (n = 0; n < 4; n++) {
567 		out_fdc(iot, ioh, NE7CMD_SENSEI);
568 		(void)fdcresult(fdc);
569 	}
570 	intio_disable_intr(SICILIAN_INTR_FDC);
571 
572 	/* select drive and turn on motor */
573 	bus_space_write_1(iot, ioh, fdctl, 0x80 | (type->rate << 4)| drive);
574 	fdc_force_ready(FDCRDY);
575 	fdcpoll(fdc);
576 
577  retry:
578 	out_fdc(iot, ioh, NE7CMD_RECAL);
579 	out_fdc(iot, ioh, drive);
580 
581 	i = 25000;
582 	while (--i > 0) {
583 		if ((intio_get_sicilian_intr() & SICILIAN_STAT_FDC) != 0) {
584 			out_fdc(iot, ioh, NE7CMD_SENSEI);
585 			n = fdcresult(fdc);
586 			break;
587 		}
588 		DELAY(100);
589 	}
590 
591 #ifdef FDDEBUG
592 	{
593 		int _i;
594 		DPRINTF(("fdprobe: status"));
595 		for (_i = 0; _i < n; _i++)
596 			DPRINTF((" %x", fdc->sc_status[_i]));
597 		DPRINTF(("\n"));
598 	}
599 #endif
600 
601 	if (n == 2) {
602 		if ((fdc->sc_status[0] & 0xf0) == 0x20)
603 			found = 1;
604 		else if ((fdc->sc_status[0] & 0xf0) == 0xc0)
605 			goto retry;
606 	}
607 
608 	/* turn off motor */
609 	bus_space_write_1(fdc->sc_iot, fdc->sc_ioh,
610 	    fdctl, (type->rate << 4) | drive);
611 	fdc_force_ready(FDCSTBY);
612 	if (!found) {
613 		intio_enable_intr(SICILIAN_INTR_FDC);
614 		return 0;
615 	}
616 
617 	return 1;
618 }
619 
620 /*
621  * Controller is working, and drive responded.  Attach it.
622  */
623 void
624 fdattach(device_t parent, device_t self, void *aux)
625 {
626 	struct fdc_softc *fdc = device_private(parent);
627 	struct fd_softc *fd = device_private(self);
628 	struct fdc_attach_args *fa = aux;
629 	struct fd_type *type = &fd_types[0];	/* XXX 1.2MB */
630 	int drive = fa->fa_drive;
631 
632 #if 0
633 	callout_init(&fd->sc_motoron_ch, 0);
634 #endif
635 	callout_init(&fd->sc_motoroff_ch, 0);
636 
637 	fd->sc_dev = self;
638 	fd->sc_flags = 0;
639 
640 	if (type)
641 		aprint_normal(": %s, %d cyl, %d head, %d sec\n", type->name,
642 		    type->cyls, type->heads, type->sectrac);
643 	else
644 		aprint_normal(": density unknown\n");
645 
646 	bufq_alloc(&fd->sc_q, "disksort", BUFQ_SORT_CYLINDER);
647 	fd->sc_cylin = -1;
648 	fd->sc_drive = drive;
649 	fd->sc_deftype = type;
650 	fdc->sc_fd[drive] = fd;
651 
652 	fd->sc_copybuf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
653 	if (fd->sc_copybuf == 0)
654 		aprint_error("%s: WARNING!! malloc() failed.\n", __func__);
655 	fd->sc_flags |= FD_ALIVE;
656 
657 	/*
658 	 * Initialize and attach the disk structure.
659 	 */
660 	disk_init(&fd->sc_dk, device_xname(fd->sc_dev), &fddkdriver);
661 	disk_attach(&fd->sc_dk);
662 
663 	/*
664 	 * Establish a mountroot_hook anyway in case we booted
665 	 * with RB_ASKNAME and get selected as the boot device.
666 	 */
667 	mountroothook_establish(fd_mountroot_hook, fd->sc_dev);
668 
669 	rnd_attach_source(&fd->rnd_source, device_xname(fd->sc_dev),
670 	    RND_TYPE_DISK, RND_FLAG_DEFAULT);
671 }
672 
673 static struct fd_type *
674 fd_dev_to_type(struct fd_softc *fd, dev_t dev)
675 {
676 	size_t type = FDTYPE(dev);
677 
678 	if (type >= __arraycount(fd_types))
679 		return NULL;
680 	return &fd_types[type];
681 }
682 
683 void
684 fdstrategy(struct buf *bp)
685 {
686 	struct fd_softc *fd;
687 	int unit;
688 	int sz;
689 	int s;
690 
691 	unit = FDUNIT(bp->b_dev);
692 	fd = device_lookup_private(&fd_cd, unit);
693 	if (fd == NULL) {
694 		bp->b_error = EINVAL;
695 		goto done;
696 	}
697 
698 	if (bp->b_blkno < 0 ||
699 	    ((bp->b_bcount % FDC_BSIZE) != 0 &&
700 	     (bp->b_flags & B_FORMAT) == 0)) {
701 		DPRINTF(("fdstrategy: unit=%d, blkno=%" PRId64 ", "
702 		    "bcount=%d\n", unit,
703 		    bp->b_blkno, bp->b_bcount));
704 		bp->b_error = EINVAL;
705 		goto done;
706 	}
707 
708 	/* If it's a null transfer, return immediately. */
709 	if (bp->b_bcount == 0)
710 		goto done;
711 
712 	sz = howmany(bp->b_bcount, FDC_BSIZE);
713 
714 	if (bp->b_blkno + sz >
715 	    (fd->sc_type->size << (fd->sc_type->secsize - 2))) {
716 		sz = (fd->sc_type->size << (fd->sc_type->secsize - 2))
717 		     - bp->b_blkno;
718 		if (sz == 0) {
719 			/* If exactly at end of disk, return EOF. */
720 			bp->b_resid = bp->b_bcount;
721 			goto done;
722 		}
723 		if (sz < 0) {
724 			/* If past end of disk, return EINVAL. */
725 			bp->b_error = EINVAL;
726 			goto done;
727 		}
728 		/* Otherwise, truncate request. */
729 		bp->b_bcount = sz << DEV_BSHIFT;
730 	}
731 
732 	bp->b_rawblkno = bp->b_blkno;
733 	bp->b_cylinder = (bp->b_blkno / (FDC_BSIZE / DEV_BSIZE)) /
734 	    (fd->sc_type->seccyl * (1 << (fd->sc_type->secsize - 2)));
735 
736 	DPRINTF(("fdstrategy: %s b_blkno %" PRId64 " b_bcount %d cylin %d\n",
737 	    bp->b_flags & B_READ ? "read" : "write",
738 	    bp->b_blkno, bp->b_bcount, bp->b_cylinder));
739 	/* Queue transfer on drive, activate drive and controller if idle. */
740 	s = splbio();
741 	bufq_put(fd->sc_q, bp);
742 	callout_stop(&fd->sc_motoroff_ch);		/* a good idea */
743 	if (fd->sc_active == 0)
744 		fdstart(fd);
745 #ifdef DIAGNOSTIC
746 	else {
747 		struct fdc_softc *fdc;
748 
749 		fdc = device_private(device_parent(fd->sc_dev));
750 		if (fdc->sc_state == DEVIDLE) {
751 			printf("fdstrategy: controller inactive\n");
752 			fdcstart(fdc);
753 		}
754 	}
755 #endif
756 	splx(s);
757 	return;
758 
759  done:
760 	/* Toss transfer; we're done early. */
761 	biodone(bp);
762 }
763 
764 void
765 fdstart(struct fd_softc *fd)
766 {
767 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
768 	int active = !TAILQ_EMPTY(&fdc->sc_drives);
769 
770 	/* Link into controller queue. */
771 	fd->sc_active = 1;
772 	TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
773 
774 	/* If controller not already active, start it. */
775 	if (!active)
776 		fdcstart(fdc);
777 }
778 
779 void
780 fdfinish(struct fd_softc *fd, struct buf *bp)
781 {
782 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
783 
784 	/*
785 	 * Move this drive to the end of the queue to give others a `fair'
786 	 * chance.  We only force a switch if N operations are completed while
787 	 * another drive is waiting to be serviced, since there is a long motor
788 	 * startup delay whenever we switch.
789 	 */
790 	(void)bufq_get(fd->sc_q);
791 	if (TAILQ_NEXT(fd, sc_drivechain) && ++fd->sc_ops >= 8) {
792 		fd->sc_ops = 0;
793 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
794 		if (bufq_peek(fd->sc_q) != NULL)
795 			TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
796 		else
797 			fd->sc_active = 0;
798 	}
799 	bp->b_resid = fd->sc_bcount;
800 	fd->sc_skip = 0;
801 
802 	rnd_add_uint32(&fd->rnd_source, bp->b_blkno);
803 
804 	biodone(bp);
805 	/* turn off motor 5s from now */
806 	callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd);
807 	fdc->sc_state = DEVIDLE;
808 }
809 
810 int
811 fdread(dev_t dev, struct uio *uio, int flags)
812 {
813 
814 	return physio(fdstrategy, NULL, dev, B_READ, minphys, uio);
815 }
816 
817 int
818 fdwrite(dev_t dev, struct uio *uio, int flags)
819 {
820 
821 	return physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio);
822 }
823 
824 void
825 fd_set_motor(struct fdc_softc *fdc, int reset)
826 {
827 	struct fd_softc *fd;
828 	int n;
829 
830 	DPRINTF(("fd_set_motor:\n"));
831 	for (n = 0; n < 4; n++) {
832 		fd = fdc->sc_fd[n];
833 		if (fd != NULL && (fd->sc_flags & FD_MOTOR) != 0)
834 			bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdctl,
835 			    0x80 | (fd->sc_type->rate << 4)| n);
836 	}
837 }
838 
839 void
840 fd_motor_off(void *arg)
841 {
842 	struct fd_softc *fd = arg;
843 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
844 	int s;
845 
846 	DPRINTF(("fd_motor_off:\n"));
847 
848 	s = splbio();
849 	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
850 	bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdctl,
851 	    (fd->sc_type->rate << 4) | fd->sc_drive);
852 #if 0
853 	fd_set_motor(fdc, 0); /* XXX */
854 #endif
855 	splx(s);
856 }
857 
858 #if 0 /* on x68k motor on triggers interrupts by state change of ready line. */
859 void
860 fd_motor_on(void *arg)
861 {
862 	struct fd_softc *fd = arg;
863 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
864 	int s;
865 
866 	DPRINTF(("fd_motor_on:\n"));
867 
868 	s = splbio();
869 	fd->sc_flags &= ~FD_MOTOR_WAIT;
870 	if ((TAILQ_FIRST(&fdc->sc_drives) == fd) &&
871 	    (fdc->sc_state == MOTORWAIT))
872 		(void)fdcintr(fdc);
873 	splx(s);
874 }
875 #endif
876 
877 int
878 fdcresult(struct fdc_softc *fdc)
879 {
880 	bus_space_tag_t iot = fdc->sc_iot;
881 	bus_space_handle_t ioh = fdc->sc_ioh;
882 	uint8_t i;
883 	int j, n;
884 
885 	n = 0;
886 	for (j = 100000; j != 0; j--) {
887 		i = bus_space_read_1(iot, ioh, fdsts) &
888 		  (NE7_DIO | NE7_RQM | NE7_CB);
889 
890 		if (i == NE7_RQM)
891 			return n;
892 		if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
893 			if (n >= sizeof(fdc->sc_status)) {
894 				log(LOG_ERR, "fdcresult: overrun\n");
895 				return -1;
896 			}
897 			fdc->sc_status[n++] =
898 			    bus_space_read_1(iot, ioh, fddata);
899 		}
900 		delay(10);
901 	}
902 	log(LOG_ERR, "fdcresult: timeout\n");
903 	return -1;
904 }
905 
906 int
907 out_fdc(bus_space_tag_t iot, bus_space_handle_t ioh, uint8_t x)
908 {
909 	int i = 100000;
910 
911 	while ((bus_space_read_1(iot, ioh, fdsts) & NE7_DIO) && i-- > 0);
912 	if (i <= 0)
913 		return -1;
914 	while ((bus_space_read_1(iot, ioh, fdsts) & NE7_RQM) == 0 && i-- > 0);
915 	if (i <= 0)
916 		return -1;
917 	bus_space_write_1(iot, ioh, fddata, x);
918 	return 0;
919 }
920 
921 int
922 fdopen(dev_t dev, int flags, int mode, struct lwp *l)
923 {
924 	int unit;
925 	struct fd_softc *fd;
926 	struct fd_type *type;
927 	struct fdc_softc *fdc;
928 
929 	unit = FDUNIT(dev);
930 	fd = device_lookup_private(&fd_cd, unit);
931 	if (fd == NULL)
932 		return ENXIO;
933 	type = fd_dev_to_type(fd, dev);
934 	if (type == NULL)
935 		return ENXIO;
936 
937 	if ((fd->sc_flags & FD_OPEN) != 0 &&
938 	    fd->sc_type != type)
939 		return EBUSY;
940 
941 	fdc = device_private(device_parent(fd->sc_dev));
942 	if ((fd->sc_flags & FD_OPEN) == 0) {
943 		/* Lock eject button */
944 		bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout,
945 		    0x40 | (1 << unit));
946 		bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, 0x40);
947 	}
948 
949 	fd->sc_type = type;
950 	fd->sc_cylin = -1;
951 
952 	switch (mode) {
953 	case S_IFCHR:
954 		fd->sc_flags |= FD_COPEN;
955 		break;
956 	case S_IFBLK:
957 		fd->sc_flags |= FD_BOPEN;
958 		break;
959 	}
960 
961 	fdgetdisklabel(fd, dev);
962 
963 	return 0;
964 }
965 
966 int
967 fdclose(dev_t dev, int flags, int mode, struct lwp *l)
968 {
969 	int unit = FDUNIT(dev);
970 	struct fd_softc *fd = device_lookup_private(&fd_cd, unit);
971 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
972 
973 	DPRINTF(("fdclose %d\n", unit));
974 
975 	switch (mode) {
976 	case S_IFCHR:
977 		fd->sc_flags &= ~FD_COPEN;
978 		break;
979 	case S_IFBLK:
980 		fd->sc_flags &= ~FD_BOPEN;
981 		break;
982 	}
983 
984 	/* clear flags */
985 	fd->sc_opts &= ~(FDOPT_NORETRY | FDOPT_SILENT);
986 
987 	if ((fd->sc_flags & FD_OPEN) == 0) {
988 		bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout,
989 		    (1 << unit));
990 		bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, 0);
991 	}
992 	return 0;
993 }
994 
995 void
996 fdcstart(struct fdc_softc *fdc)
997 {
998 
999 #ifdef DIAGNOSTIC
1000 	/*
1001 	 * only got here if controller's drive queue was inactive; should
1002 	 * be in idle state
1003 	 */
1004 	if (fdc->sc_state != DEVIDLE) {
1005 		printf("fdcstart: not idle\n");
1006 		return;
1007 	}
1008 #endif
1009 	(void)fdcintr(fdc);
1010 }
1011 
1012 
1013 static void
1014 fdcpstatus(int n, struct fdc_softc *fdc)
1015 {
1016 	char bits[64];
1017 
1018 	switch (n) {
1019 	case 0:
1020 		printf("\n");
1021 		break;
1022 	case 2:
1023 		snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
1024 		printf(" (st0 %s cyl %d)\n", bits, fdc->sc_status[1]);
1025 		break;
1026 	case 7:
1027 		snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
1028 		printf(" (st0 %s", bits);
1029 		snprintb(bits, sizeof(bits), NE7_ST1BITS, fdc->sc_status[1]);
1030 		printf(" st1 %s", bits);
1031 		snprintb(bits, sizeof(bits), NE7_ST2BITS, fdc->sc_status[2]);
1032 		printf(" st2 %s", bits);
1033 		printf(" cyl %d head %d sec %d)\n",
1034 		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
1035 		break;
1036 #ifdef DIAGNOSTIC
1037 	default:
1038 		printf("\nfdcstatus: weird size");
1039 		break;
1040 #endif
1041 	}
1042 }
1043 
1044 void
1045 fdcstatus(device_t dv, int n, const char *s)
1046 {
1047 	struct fdc_softc *fdc = device_private(device_parent(dv));
1048 
1049 	if (n == 0) {
1050 		out_fdc(fdc->sc_iot, fdc->sc_ioh, NE7CMD_SENSEI);
1051 		(void)fdcresult(fdc);
1052 		n = 2;
1053 	}
1054 
1055 	printf("%s: %s: state %d", device_xname(dv), s, fdc->sc_state);
1056 	fdcpstatus(n, fdc);
1057 }
1058 
1059 void
1060 fdctimeout(void *arg)
1061 {
1062 	struct fdc_softc *fdc = arg;
1063 	struct fd_softc *fd = TAILQ_FIRST(&fdc->sc_drives);
1064 	int s;
1065 
1066 	s = splbio();
1067 	fdcstatus(fd->sc_dev, 0, "timeout");
1068 
1069 	if (bufq_peek(fd->sc_q) != NULL)
1070 		fdc->sc_state++;
1071 	else
1072 		fdc->sc_state = DEVIDLE;
1073 
1074 	(void)fdcintr(fdc);
1075 	splx(s);
1076 }
1077 
1078 #if 0
1079 void
1080 fdcpseudointr(void *arg)
1081 {
1082 	int s;
1083 	struct fdc_softc *fdc = arg;
1084 
1085 	/* just ensure it has the right spl */
1086 	s = splbio();
1087 	(void)fdcintr(fdc);
1088 	splx(s);
1089 }
1090 #endif
1091 
1092 int
1093 fdcintr(void *arg)
1094 {
1095 	struct fdc_softc *fdc = arg;
1096 #define	st0	fdc->sc_status[0]
1097 #define	cyl	fdc->sc_status[1]
1098 	struct fd_softc *fd;
1099 	struct buf *bp;
1100 	bus_space_tag_t iot = fdc->sc_iot;
1101 	bus_space_handle_t ioh = fdc->sc_ioh;
1102 	int read, head, sec, pos, i, sectrac, nblks;
1103 	int tmp;
1104 	struct fd_type *type;
1105 	struct ne7_fd_formb *finfo = NULL;
1106 
1107  loop:
1108 	fd = TAILQ_FIRST(&fdc->sc_drives);
1109 	if (fd == NULL) {
1110 		DPRINTF(("fdcintr: set DEVIDLE\n"));
1111 		if (fdc->sc_state == DEVIDLE) {
1112 			if ((intio_get_sicilian_intr() & SICILIAN_STAT_FDC)
1113 			    != 0) {
1114 				out_fdc(iot, ioh, NE7CMD_SENSEI);
1115 				if ((tmp = fdcresult(fdc)) != 2 ||
1116 				    (st0 & 0xf8) != 0x20) {
1117 					goto loop;
1118 				}
1119 			}
1120 		}
1121 		/* no drives waiting; end */
1122 		fdc->sc_state = DEVIDLE;
1123 		return 1;
1124 	}
1125 
1126 	/* Is there a transfer to this drive?  If not, deactivate drive. */
1127 	bp = bufq_peek(fd->sc_q);
1128 	if (bp == NULL) {
1129 		fd->sc_ops = 0;
1130 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
1131 		fd->sc_active = 0;
1132 		goto loop;
1133 	}
1134 
1135 	if (bp->b_flags & B_FORMAT)
1136 		finfo = (struct ne7_fd_formb *)bp->b_data;
1137 
1138 	switch (fdc->sc_state) {
1139 	case DEVIDLE:
1140 		DPRINTF(("fdcintr: in DEVIDLE\n"));
1141 		fdc->sc_errors = 0;
1142 		fd->sc_skip = 0;
1143 		fd->sc_bcount = bp->b_bcount;
1144 		fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE);
1145 		callout_stop(&fd->sc_motoroff_ch);
1146 		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
1147 			fdc->sc_state = MOTORWAIT;
1148 			return 1;
1149 		}
1150 		if ((fd->sc_flags & FD_MOTOR) == 0) {
1151 			/* Turn on the motor */
1152 			/* being careful about other drives. */
1153 			for (i = 0; i < 4; i++) {
1154 				struct fd_softc *ofd = fdc->sc_fd[i];
1155 				if (ofd != NULL &&
1156 				    (ofd->sc_flags & FD_MOTOR) != 0) {
1157 					callout_stop(&ofd->sc_motoroff_ch);
1158 					ofd->sc_flags &=
1159 					    ~(FD_MOTOR | FD_MOTOR_WAIT);
1160 					break;
1161 				}
1162 			}
1163 			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
1164 			fd_set_motor(fdc, 0);
1165 			fdc->sc_state = MOTORWAIT;
1166 #if 0	/* no need to callout on x68k; motor on will trigger interrupts */
1167 			/* allow .5s for motor to stabilize */
1168 			callout_reset(&fd->sc_motoron_ch, hz / 2,
1169 			    fd_motor_on, fd);
1170 #endif
1171 			return 1;
1172 		}
1173 		/* Make sure the right drive is selected. */
1174 		fd_set_motor(fdc, 0);
1175 
1176 		/* fall through */
1177 	case DOSEEK:
1178 	doseek:
1179 		DPRINTF(("fdcintr: in DOSEEK\n"));
1180 		if (fd->sc_cylin == bp->b_cylinder)
1181 			goto doio;
1182 
1183 		out_fdc(iot, ioh, NE7CMD_SPECIFY);	/* specify command */
1184 		out_fdc(iot, ioh, 0xd0);		/* XXX const */
1185 		out_fdc(iot, ioh, 0x10);
1186 
1187 		out_fdc(iot, ioh, NE7CMD_SEEK);		/* seek function */
1188 		out_fdc(iot, ioh, fd->sc_drive);	/* drive number */
1189 		out_fdc(iot, ioh, bp->b_cylinder * fd->sc_type->step);
1190 
1191 		fd->sc_cylin = -1;
1192 		fdc->sc_state = SEEKWAIT;
1193 
1194 		iostat_seek(fd->sc_dk.dk_stats);
1195 		disk_busy(&fd->sc_dk);
1196 
1197 		callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
1198 		return 1;
1199 
1200 	case DOIO:
1201 	doio:
1202 		DPRINTF(("fdcintr: DOIO: "));
1203 		type = fd->sc_type;
1204 		if (finfo != NULL)
1205 			fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
1206 			    (char *)finfo;
1207 		sectrac = type->sectrac;
1208 		pos = fd->sc_blkno % (sectrac * (1 << (type->secsize - 2)));
1209 		sec = pos / (1 << (type->secsize - 2));
1210 		if (finfo != NULL || type->secsize == 2) {
1211 			fd->sc_part = SEC_P11;
1212 			nblks = (sectrac - sec) << (type->secsize - 2);
1213 			nblks = uimin(nblks, fd->sc_bcount / FDC_BSIZE);
1214 			DPRINTF(("nblks(0)"));
1215 		} else if ((fd->sc_blkno % 2) == 0) {
1216 			if (fd->sc_bcount & 0x00000200) {
1217 				if (fd->sc_bcount == FDC_BSIZE) {
1218 					fd->sc_part = SEC_P10;
1219 					nblks = 1;
1220 					DPRINTF(("nblks(1)"));
1221 				} else {
1222 					fd->sc_part = SEC_P11;
1223 					nblks = (sectrac - sec) * 2;
1224 					nblks = uimin(nblks,
1225 					    fd->sc_bcount / FDC_BSIZE - 1);
1226 					DPRINTF(("nblks(2)"));
1227 				}
1228 			} else {
1229 				fd->sc_part = SEC_P11;
1230 				nblks = (sectrac - sec) << (type->secsize - 2);
1231 				nblks = uimin(nblks, fd->sc_bcount / FDC_BSIZE);
1232 				DPRINTF(("nblks(3)"));
1233 			}
1234 		} else {
1235 			fd->sc_part = SEC_P01;
1236 			nblks = 1;
1237 			DPRINTF(("nblks(4)"));
1238 		}
1239 		nblks = uimin(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
1240 		DPRINTF((" %d\n", nblks));
1241 		fd->sc_nblks = nblks;
1242 		fd->sc_nbytes =
1243 		    (finfo != NULL) ? bp->b_bcount : nblks * FDC_BSIZE;
1244 		head = (fd->sc_blkno
1245 		    % (type->seccyl * (1 << (type->secsize - 2))))
1246 		    / (type->sectrac * (1 << (type->secsize - 2)));
1247 
1248 #ifdef DIAGNOSTIC
1249 		{
1250 			int block;
1251 			block = ((fd->sc_cylin * type->heads + head) *
1252 			    type->sectrac + sec) * (1 << (type->secsize - 2));
1253 			block += (fd->sc_part == SEC_P01) ? 1 : 0;
1254 			if (block != fd->sc_blkno) {
1255 				printf("C H R N: %d %d %d %d\n",
1256 				    fd->sc_cylin, head, sec, type->secsize);
1257 				printf("fdcintr: doio: block %d != blkno %"
1258 				    PRId64 "\n",
1259 				    block, fd->sc_blkno);
1260 #ifdef DDB
1261 				Debugger();
1262 #endif
1263 			}
1264 		}
1265 #endif
1266 		read = bp->b_flags & B_READ;
1267 		DPRINTF(("fdcintr: %s drive %d track %d "
1268 		    "head %d sec %d nblks %d, skip %d\n",
1269 		    read ? "read" : "write", fd->sc_drive, fd->sc_cylin,
1270 		    head, sec, nblks, fd->sc_skip));
1271 		DPRINTF(("C H R N: %d %d %d %d\n", fd->sc_cylin, head, sec,
1272 		    type->secsize));
1273 
1274 		if (finfo == NULL && fd->sc_part != SEC_P11)
1275 			goto docopy;
1276 
1277 		fdc_dmastart(fdc, read, (char *)bp->b_data + fd->sc_skip,
1278 		    fd->sc_nbytes);
1279 		if (finfo != NULL) {
1280 			/* formatting */
1281 			if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) {
1282 				fdc->sc_errors = 4;
1283 				fdcretry(fdc);
1284 				goto loop;
1285 			}
1286 			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
1287 			out_fdc(iot, ioh, finfo->fd_formb_secshift);
1288 			out_fdc(iot, ioh, finfo->fd_formb_nsecs);
1289 			out_fdc(iot, ioh, finfo->fd_formb_gaplen);
1290 			out_fdc(iot, ioh, finfo->fd_formb_fillbyte);
1291 		} else {
1292 			if (read)
1293 				out_fdc(iot, ioh, NE7CMD_READ);	/* READ */
1294 			else
1295 				out_fdc(iot, ioh, NE7CMD_WRITE); /* WRITE */
1296 			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
1297 			out_fdc(iot, ioh, bp->b_cylinder);	/* cylinder */
1298 			out_fdc(iot, ioh, head);
1299 			out_fdc(iot, ioh, sec + 1);		/* sector +1 */
1300 			out_fdc(iot, ioh, type->secsize); /* sector size */
1301 			out_fdc(iot, ioh, type->sectrac); /* sectors/track */
1302 			out_fdc(iot, ioh, type->gap1);		/* gap1 size */
1303 			out_fdc(iot, ioh, type->datalen); /* data length */
1304 		}
1305 		fdc->sc_state = IOCOMPLETE;
1306 
1307 		disk_busy(&fd->sc_dk);
1308 
1309 		/* allow 2 seconds for operation */
1310 		callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
1311 		return 1;				/* will return later */
1312 
1313 	case DOCOPY:
1314 	docopy:
1315 		DPRINTF(("fdcintr: DOCOPY:\n"));
1316 		type = fd->sc_type;
1317 		head = (fd->sc_blkno
1318 		    % (type->seccyl * (1 << (type->secsize - 2))))
1319 		    / (type->sectrac * (1 << (type->secsize - 2)));
1320 		pos = fd->sc_blkno %
1321 		    (type->sectrac * (1 << (type->secsize - 2)));
1322 		sec = pos / (1 << (type->secsize - 2));
1323 		fdc_dmastart(fdc, B_READ, fd->sc_copybuf, 1024);
1324 		out_fdc(iot, ioh, NE7CMD_READ);		/* READ */
1325 		out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
1326 		out_fdc(iot, ioh, bp->b_cylinder);	/* cylinder */
1327 		out_fdc(iot, ioh, head);
1328 		out_fdc(iot, ioh, sec + 1);		/* sector +1 */
1329 		out_fdc(iot, ioh, type->secsize);	/* sector size */
1330 		out_fdc(iot, ioh, type->sectrac);	/* sectors/track */
1331 		out_fdc(iot, ioh, type->gap1);		/* gap1 size */
1332 		out_fdc(iot, ioh, type->datalen);	/* data length */
1333 		fdc->sc_state = COPYCOMPLETE;
1334 		/* allow 2 seconds for operation */
1335 		callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
1336 		return 1;				/* will return later */
1337 
1338 	case DOIOHALF:
1339 	doiohalf:
1340 		DPRINTF((" DOIOHALF:\n"));
1341 
1342 		type = fd->sc_type;
1343 		sectrac = type->sectrac;
1344 		pos = fd->sc_blkno % (sectrac * (1 << (type->secsize - 2)));
1345 		sec = pos / (1 << (type->secsize - 2));
1346 		head = (fd->sc_blkno
1347 		    % (type->seccyl * (1 << (type->secsize - 2))))
1348 		    / (type->sectrac * (1 << (type->secsize - 2)));
1349 #ifdef DIAGNOSTIC
1350 		{
1351 			int block;
1352 			block = ((fd->sc_cylin * type->heads + head) *
1353 			    type->sectrac + sec) * (1 << (type->secsize - 2));
1354 			block += (fd->sc_part == SEC_P01) ? 1 : 0;
1355 			if (block != fd->sc_blkno) {
1356 				printf("fdcintr: block %d != blkno %" PRId64
1357 				    "\n",
1358 				    block, fd->sc_blkno);
1359 #ifdef DDB
1360 				Debugger();
1361 #endif
1362 			}
1363 		}
1364 #endif
1365 		if ((read = bp->b_flags & B_READ)) {
1366 			memcpy((char *)bp->b_data + fd->sc_skip, fd->sc_copybuf
1367 			    + (fd->sc_part & SEC_P01 ? FDC_BSIZE : 0),
1368 			    FDC_BSIZE);
1369 			fdc->sc_state = IOCOMPLETE;
1370 			goto iocomplete2;
1371 		} else {
1372 			memcpy((char *)fd->sc_copybuf
1373 			    + (fd->sc_part & SEC_P01 ? FDC_BSIZE : 0),
1374 			    (char *)bp->b_data + fd->sc_skip, FDC_BSIZE);
1375 			fdc_dmastart(fdc, read, fd->sc_copybuf, 1024);
1376 		}
1377 		out_fdc(iot, ioh, NE7CMD_WRITE);	/* WRITE */
1378 		out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
1379 		out_fdc(iot, ioh, bp->b_cylinder);	/* cylinder */
1380 		out_fdc(iot, ioh, head);
1381 		out_fdc(iot, ioh, sec + 1);		/* sector +1 */
1382 		out_fdc(iot, ioh, fd->sc_type->secsize); /* sector size */
1383 		out_fdc(iot, ioh, sectrac);		/* sectors/track */
1384 		out_fdc(iot, ioh, fd->sc_type->gap1);	/* gap1 size */
1385 		out_fdc(iot, ioh, fd->sc_type->datalen); /* data length */
1386 		fdc->sc_state = IOCOMPLETE;
1387 		/* allow 2 seconds for operation */
1388 		callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
1389 		return 1;				/* will return later */
1390 
1391 	case SEEKWAIT:
1392 		callout_stop(&fdc->sc_timo_ch);
1393 		fdc->sc_state = SEEKCOMPLETE;
1394 		/* allow 1/50 second for heads to settle */
1395 #if 0
1396 		callout_reset(&fdc->sc_intr_ch, hz / 50, fdcpseudointr, fdc);
1397 #endif
1398 		return 1;
1399 
1400 	case SEEKCOMPLETE:
1401 		/* Make sure seek really happened */
1402 		DPRINTF(("fdcintr: SEEKCOMPLETE: FDC status = %x\n",
1403 		    bus_space_read_1(fdc->sc_iot, fdc->sc_ioh, fdsts)));
1404 		out_fdc(iot, ioh, NE7CMD_SENSEI);
1405 		tmp = fdcresult(fdc);
1406 		if ((st0 & 0xf8) == 0xc0) {
1407 			DPRINTF(("fdcintr: first seek!\n"));
1408 			fdc->sc_state = DORECAL;
1409 			goto loop;
1410 		} else if (tmp != 2 ||
1411 		    (st0 & 0xf8) != 0x20 ||
1412 		    cyl != bp->b_cylinder) {
1413 #ifdef FDDEBUG
1414 			fdcstatus(fd->sc_dev, 2, "seek failed");
1415 #endif
1416 			fdcretry(fdc);
1417 			goto loop;
1418 		}
1419 		fd->sc_cylin = bp->b_cylinder;
1420 		goto doio;
1421 
1422 	case IOTIMEDOUT:
1423 		fdc_dmaabort(fdc);
1424 	case SEEKTIMEDOUT:
1425 	case RECALTIMEDOUT:
1426 	case RESETTIMEDOUT:
1427 		fdcretry(fdc);
1428 		goto loop;
1429 
1430 	case IOCOMPLETE: /* IO DONE, post-analyze */
1431 		callout_stop(&fdc->sc_timo_ch);
1432 		DPRINTF(("fdcintr: in IOCOMPLETE\n"));
1433 		if ((tmp = fdcresult(fdc)) != 7 || (st0 & 0xf8) != 0) {
1434 			fdc_dmaabort(fdc);
1435 			fdcstatus(fd->sc_dev, tmp, bp->b_flags & B_READ ?
1436 			    "read failed" : "write failed");
1437 			printf("blkno %" PRId64 " nblks %d\n",
1438 			    fd->sc_blkno, fd->sc_nblks);
1439 			fdcretry(fdc);
1440 			goto loop;
1441 		}
1442 	iocomplete2:
1443 		if (fdc->sc_errors) {
1444 			diskerr(bp, "fd", "soft error (corrected)", LOG_PRINTF,
1445 			    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
1446 			printf("\n");
1447 			fdc->sc_errors = 0;
1448 		}
1449 		fd->sc_blkno += fd->sc_nblks;
1450 		fd->sc_skip += fd->sc_nbytes;
1451 		fd->sc_bcount -= fd->sc_nbytes;
1452 		DPRINTF(("fd->sc_bcount = %d\n", fd->sc_bcount));
1453 		if (finfo == NULL && fd->sc_bcount > 0) {
1454 			bp->b_cylinder = fd->sc_blkno
1455 			    / (fd->sc_type->seccyl
1456 			    * (1 << (fd->sc_type->secsize - 2)));
1457 			goto doseek;
1458 		}
1459 		fdfinish(fd, bp);
1460 		goto loop;
1461 
1462 	case COPYCOMPLETE: /* IO DONE, post-analyze */
1463 		DPRINTF(("fdcintr: COPYCOMPLETE:"));
1464 		callout_stop(&fdc->sc_timo_ch);
1465 		if ((tmp = fdcresult(fdc)) != 7 || (st0 & 0xf8) != 0) {
1466 			printf("fdcintr: resnum=%d, st0=%x\n", tmp, st0);
1467 			fdc_dmaabort(fdc);
1468 			fdcstatus(fd->sc_dev, 7, bp->b_flags & B_READ ?
1469 			    "read failed" : "write failed");
1470 			printf("blkno %" PRId64 " nblks %d\n",
1471 			    fd->sc_blkno, fd->sc_nblks);
1472 			fdcretry(fdc);
1473 			goto loop;
1474 		}
1475 		goto doiohalf;
1476 
1477 	case DORESET:
1478 		DPRINTF(("fdcintr: in DORESET\n"));
1479 		/* try a reset, keep motor on */
1480 		fd_set_motor(fdc, 1);
1481 		DELAY(100);
1482 		fd_set_motor(fdc, 0);
1483 		fdc->sc_state = RESETCOMPLETE;
1484 		callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc);
1485 		return 1;			/* will return later */
1486 
1487 	case RESETCOMPLETE:
1488 		DPRINTF(("fdcintr: in RESETCOMPLETE\n"));
1489 		callout_stop(&fdc->sc_timo_ch);
1490 		/* clear the controller output buffer */
1491 		for (i = 0; i < 4; i++) {
1492 			out_fdc(iot, ioh, NE7CMD_SENSEI);
1493 			(void)fdcresult(fdc);
1494 		}
1495 
1496 		/* fall through */
1497 	case DORECAL:
1498 		DPRINTF(("fdcintr: in DORECAL\n"));
1499 		out_fdc(iot, ioh, NE7CMD_RECAL); /* recalibrate function */
1500 		out_fdc(iot, ioh, fd->sc_drive);
1501 		fdc->sc_state = RECALWAIT;
1502 		callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc);
1503 		return 1;			/* will return later */
1504 
1505 	case RECALWAIT:
1506 		DPRINTF(("fdcintr: in RECALWAIT\n"));
1507 		callout_stop(&fdc->sc_timo_ch);
1508 		fdc->sc_state = RECALCOMPLETE;
1509 		/* allow 1/30 second for heads to settle */
1510 #if 0
1511 		callout_reset(&fdc->sc_intr_ch, hz / 30, fdcpseudointr, fdc);
1512 #endif
1513 		return 1;			/* will return later */
1514 
1515 	case RECALCOMPLETE:
1516 		DPRINTF(("fdcintr: in RECALCOMPLETE\n"));
1517 		out_fdc(iot, ioh, NE7CMD_SENSEI);
1518 		tmp = fdcresult(fdc);
1519 		if ((st0 & 0xf8) == 0xc0) {
1520 			DPRINTF(("fdcintr: first seek!\n"));
1521 			fdc->sc_state = DORECAL;
1522 			goto loop;
1523 		} else if (tmp != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
1524 #ifdef FDDEBUG
1525 			fdcstatus(fd->sc_dev, 2, "recalibrate failed");
1526 #endif
1527 			fdcretry(fdc);
1528 			goto loop;
1529 		}
1530 		fd->sc_cylin = 0;
1531 		goto doseek;
1532 
1533 	case MOTORWAIT:
1534 #if 0 /* on x68k motor on triggers interrupts by state change of ready line. */
1535 		if (fd->sc_flags & FD_MOTOR_WAIT)
1536 			return 1;		/* time's not up yet */
1537 #else
1538 		/* check drive ready by state change interrupt */
1539 		KASSERT(fd->sc_flags & FD_MOTOR_WAIT);
1540 		out_fdc(iot, ioh, NE7CMD_SENSEI);
1541 		tmp = fdcresult(fdc);
1542 		if (tmp != 2 || (st0 & 0xc0) != 0xc0 /* ready changed */) {
1543 			printf("%s: unexpected interrupt during MOTORWAIT",
1544 			    device_xname(fd->sc_dev));
1545 			fdcpstatus(7, fdc);
1546 			return 1;
1547 		}
1548 		fd->sc_flags &= ~FD_MOTOR_WAIT;
1549 #endif
1550 		goto doseek;
1551 
1552 	default:
1553 		fdcstatus(fd->sc_dev, 0, "stray interrupt");
1554 		return 1;
1555 	}
1556 #ifdef DIAGNOSTIC
1557 	panic("fdcintr: impossible");
1558 #endif
1559 #undef	st0
1560 #undef	cyl
1561 }
1562 
1563 void
1564 fdcretry(struct fdc_softc *fdc)
1565 {
1566 	struct fd_softc *fd;
1567 	struct buf *bp;
1568 
1569 	DPRINTF(("fdcretry:\n"));
1570 	fd = TAILQ_FIRST(&fdc->sc_drives);
1571 	bp = bufq_peek(fd->sc_q);
1572 
1573 	if (fd->sc_opts & FDOPT_NORETRY)
1574 		goto fail;
1575 
1576 	switch (fdc->sc_errors) {
1577 	case 0:
1578 		/* try again */
1579 		fdc->sc_state = SEEKCOMPLETE;
1580 		break;
1581 
1582 	case 1:
1583 	case 2:
1584 	case 3:
1585 		/* didn't work; try recalibrating */
1586 		fdc->sc_state = DORECAL;
1587 		break;
1588 
1589 	case 4:
1590 		/* still no go; reset the bastard */
1591 		fdc->sc_state = DORESET;
1592 		break;
1593 
1594 	default:
1595 	fail:
1596 		if ((fd->sc_opts & FDOPT_SILENT) == 0) {
1597 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
1598 			    fd->sc_skip, (struct disklabel *)NULL);
1599 			fdcpstatus(7, fdc);
1600 		}
1601 
1602 		bp->b_error = EIO;
1603 		fdfinish(fd, bp);
1604 	}
1605 	fdc->sc_errors++;
1606 }
1607 
1608 int
1609 fdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
1610 {
1611 	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
1612 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
1613 	struct fdformat_parms *form_parms;
1614 	struct fdformat_cmd *form_cmd;
1615 	struct ne7_fd_formb *fd_formb;
1616 	int part = DISKPART(dev);
1617 	struct disklabel buffer;
1618 	int error;
1619 	unsigned int scratch;
1620 	int il[FD_MAX_NSEC + 1];
1621 	int i, j;
1622 
1623 	error = disk_ioctl(&fd->sc_dk, dev, cmd, addr, flag, l);
1624 	if (error != EPASSTHROUGH)
1625 		return error;
1626 
1627 	DPRINTF(("fdioctl:"));
1628 	switch (cmd) {
1629 	case DIOCWLABEL:
1630 		DPRINTF(("DIOCWLABEL\n"));
1631 		if ((flag & FWRITE) == 0)
1632 			return EBADF;
1633 		/* XXX do something */
1634 		return 0;
1635 
1636 	case DIOCWDINFO:
1637 		DPRINTF(("DIOCWDINFO\n"));
1638 		if ((flag & FWRITE) == 0)
1639 			return EBADF;
1640 
1641 		error = setdisklabel(&buffer, (struct disklabel *)addr,
1642 		    0, NULL);
1643 		if (error)
1644 			return error;
1645 
1646 		error = writedisklabel(dev, fdstrategy, &buffer, NULL);
1647 		return error;
1648 
1649 	case FDIOCGETFORMAT:
1650 		DPRINTF(("FDIOCGETFORMAT\n"));
1651 		form_parms = (struct fdformat_parms *)addr;
1652 		form_parms->fdformat_version = FDFORMAT_VERSION;
1653 		form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
1654 		form_parms->ncyl = fd->sc_type->cyls;
1655 		form_parms->nspt = fd->sc_type->sectrac;
1656 		form_parms->ntrk = fd->sc_type->heads;
1657 		form_parms->stepspercyl = fd->sc_type->step;
1658 		form_parms->gaplen = fd->sc_type->gap2;
1659 		form_parms->fillbyte = fd->sc_type->fillbyte;
1660 		form_parms->interleave = fd->sc_type->interleave;
1661 		switch (fd->sc_type->rate) {
1662 		case FDC_500KBPS:
1663 			form_parms->xfer_rate = 500 * 1024;
1664 			break;
1665 		case FDC_300KBPS:
1666 			form_parms->xfer_rate = 300 * 1024;
1667 			break;
1668 		case FDC_250KBPS:
1669 			form_parms->xfer_rate = 250 * 1024;
1670 			break;
1671 		default:
1672 			return EINVAL;
1673 		}
1674 		return 0;
1675 
1676 	case FDIOCSETFORMAT:
1677 		DPRINTF(("FDIOCSETFORMAT\n"));
1678 		if((flag & FWRITE) == 0)
1679 			return EBADF;	/* must be opened for writing */
1680 		form_parms = (struct fdformat_parms *)addr;
1681 		if (form_parms->fdformat_version != FDFORMAT_VERSION)
1682 			return EINVAL;	/* wrong version of formatting prog */
1683 
1684 		scratch = form_parms->nbps >> 7;
1685 		if ((form_parms->nbps & 0x7f) || ffs(scratch) == 0 ||
1686 		    scratch & ~(1 << (ffs(scratch) - 1)))
1687 			/* not a power-of-two multiple of 128 */
1688 			return EINVAL;
1689 
1690 		switch (form_parms->xfer_rate) {
1691 		case 500 * 1024:
1692 			fd->sc_type->rate = FDC_500KBPS;
1693 			break;
1694 		case 300 * 1024:
1695 			fd->sc_type->rate = FDC_300KBPS;
1696 			break;
1697 		case 250 * 1024:
1698 			fd->sc_type->rate = FDC_250KBPS;
1699 			break;
1700 		default:
1701 			return EINVAL;
1702 		}
1703 
1704 		if (form_parms->nspt > FD_MAX_NSEC ||
1705 		    form_parms->fillbyte > 0xff ||
1706 		    form_parms->interleave > 0xff)
1707 			return EINVAL;
1708 		fd->sc_type->sectrac = form_parms->nspt;
1709 		if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
1710 			return EINVAL;
1711 		fd->sc_type->heads = form_parms->ntrk;
1712 		fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
1713 		fd->sc_type->secsize = ffs(scratch)-1;
1714 		fd->sc_type->gap2 = form_parms->gaplen;
1715 		fd->sc_type->cyls = form_parms->ncyl;
1716 		fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
1717 		    form_parms->nbps / DEV_BSIZE;
1718 		fd->sc_type->step = form_parms->stepspercyl;
1719 		fd->sc_type->fillbyte = form_parms->fillbyte;
1720 		fd->sc_type->interleave = form_parms->interleave;
1721 		return 0;
1722 
1723 	case FDIOCFORMAT_TRACK:
1724 		DPRINTF(("FDIOCFORMAT_TRACK\n"));
1725 		if ((flag & FWRITE) == 0)
1726 			return EBADF;	/* must be opened for writing */
1727 		form_cmd = (struct fdformat_cmd *)addr;
1728 		if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
1729 			return EINVAL;	/* wrong version of formatting prog */
1730 
1731 		if (form_cmd->head >= fd->sc_type->heads ||
1732 		    form_cmd->cylinder >= fd->sc_type->cyls) {
1733 			return EINVAL;
1734 		}
1735 
1736 		fd_formb = malloc(sizeof(struct ne7_fd_formb),
1737 		    M_TEMP, M_WAITOK);
1738 		fd_formb->head = form_cmd->head;
1739 		fd_formb->cyl = form_cmd->cylinder;
1740 		fd_formb->transfer_rate = fd->sc_type->rate;
1741 		fd_formb->fd_formb_secshift = fd->sc_type->secsize;
1742 		fd_formb->fd_formb_nsecs = fd->sc_type->sectrac;
1743 		fd_formb->fd_formb_gaplen = fd->sc_type->gap2;
1744 		fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte;
1745 
1746 		memset(il, 0, sizeof il);
1747 		for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) {
1748 			while (il[(j % fd_formb->fd_formb_nsecs) + 1])
1749 				j++;
1750 			il[(j % fd_formb->fd_formb_nsecs)+  1] = i;
1751 			j += fd->sc_type->interleave;
1752 		}
1753 		for (i = 0; i < fd_formb->fd_formb_nsecs; i++) {
1754 			fd_formb->fd_formb_cylno(i) = form_cmd->cylinder;
1755 			fd_formb->fd_formb_headno(i) = form_cmd->head;
1756 			fd_formb->fd_formb_secno(i) = il[i + 1];
1757 			fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize;
1758 		}
1759 
1760 		error = fdformat(dev, fd_formb, l);
1761 		free(fd_formb, M_TEMP);
1762 		return error;
1763 
1764 	case FDIOCGETOPTS:		/* get drive options */
1765 		DPRINTF(("FDIOCGETOPTS\n"));
1766 		*(int *)addr = fd->sc_opts;
1767 		return 0;
1768 
1769 	case FDIOCSETOPTS:        	/* set drive options */
1770 		DPRINTF(("FDIOCSETOPTS\n"));
1771 		fd->sc_opts = *(int *)addr;
1772 		return 0;
1773 
1774 	case DIOCLOCK:
1775 		/*
1776 		 * Nothing to do here, really.
1777 		 */
1778 		return 0; /* XXX */
1779 
1780 	case DIOCEJECT:
1781 		DPRINTF(("DIOCEJECT\n"));
1782 		if (*(int *)addr == 0) {
1783 			/*
1784 			 * Don't force eject: check that we are the only
1785 			 * partition open. If so, unlock it.
1786 			 */
1787 			if ((fd->sc_dk.dk_openmask & ~(1 << part)) != 0 ||
1788 			    fd->sc_dk.dk_bopenmask + fd->sc_dk.dk_copenmask !=
1789 			    fd->sc_dk.dk_openmask) {
1790 				return EBUSY;
1791 			}
1792 		}
1793 		/* FALLTHROUGH */
1794 	case ODIOCEJECT:
1795 		DPRINTF(("ODIOCEJECT\n"));
1796 		fd_do_eject(fdc, FDUNIT(dev));
1797 		return 0;
1798 
1799 	default:
1800 		return ENOTTY;
1801 	}
1802 
1803 #ifdef DIAGNOSTIC
1804 	panic("fdioctl: impossible");
1805 #endif
1806 }
1807 
1808 int
1809 fdformat(dev_t dev, struct ne7_fd_formb *finfo, struct lwp *l)
1810 {
1811 	int rv = 0;
1812 	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
1813 	struct fd_type *type = fd->sc_type;
1814 	struct buf *bp;
1815 
1816 	/* set up a buffer header for fdstrategy() */
1817 	bp = getiobuf(NULL, false);
1818 	if (bp == NULL)
1819 		return ENOBUFS;
1820 
1821 	bp->b_cflags = BC_BUSY;
1822 	bp->b_flags = B_PHYS | B_FORMAT;
1823 	bp->b_proc = l->l_proc;
1824 	bp->b_dev = dev;
1825 
1826 	/*
1827 	 * calculate a fake blkno, so fdstrategy() would initiate a
1828 	 * seek to the requested cylinder
1829 	 */
1830 	bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
1831 	    + finfo->head * type->sectrac) * (128 << type->secsize) / DEV_BSIZE;
1832 
1833 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1834 	bp->b_data = (void *)finfo;
1835 
1836 #ifdef FD_DEBUG
1837 	printf("fdformat: blkno %" PRIx64 " count %x\n",
1838 	    bp->b_blkno, bp->b_bcount);
1839 #endif
1840 
1841 	/* now do the format */
1842 	fdstrategy(bp);
1843 
1844 	/* ...and wait for it to complete */
1845 	rv = biowait(bp);
1846 	putiobuf(bp);
1847 	return rv;
1848 }
1849 
1850 void
1851 fd_do_eject(struct fdc_softc *fdc, int unit)
1852 {
1853 
1854 	bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, 0x20 | (1 << unit));
1855 	DELAY(1); /* XXX */
1856 	bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, 0x20);
1857 }
1858 
1859 /*
1860  * Build disk label. For now we only create a label from what we know
1861  * from 'sc'.
1862  */
1863 static int
1864 fdgetdisklabel(struct fd_softc *sc, dev_t dev)
1865 {
1866 	struct disklabel *lp;
1867 	int part;
1868 
1869 	DPRINTF(("fdgetdisklabel()\n"));
1870 
1871 	part = DISKPART(dev);
1872 	lp = sc->sc_dk.dk_label;
1873 	memset(lp, 0, sizeof(struct disklabel));
1874 
1875 	lp->d_secsize     = 128 << sc->sc_type->secsize;
1876 	lp->d_ntracks     = sc->sc_type->heads;
1877 	lp->d_nsectors    = sc->sc_type->sectrac;
1878 	lp->d_secpercyl   = lp->d_ntracks * lp->d_nsectors;
1879 	lp->d_ncylinders  = sc->sc_type->size / lp->d_secpercyl;
1880 	lp->d_secperunit  = sc->sc_type->size;
1881 
1882 	lp->d_type        = DKTYPE_FLOPPY;
1883 	lp->d_rpm         = 300; 	/* XXX */
1884 	lp->d_interleave  = 1;		/* FIXME: is this OK?		*/
1885 	lp->d_bbsize      = 0;
1886 	lp->d_sbsize      = 0;
1887 	lp->d_npartitions = part + 1;
1888 #define STEP_DELAY	6000	/* 6ms (6000us) delay after stepping	*/
1889 	lp->d_trkseek     = STEP_DELAY; /* XXX */
1890 	lp->d_magic       = DISKMAGIC;
1891 	lp->d_magic2      = DISKMAGIC;
1892 	lp->d_checksum    = dkcksum(lp);
1893 	lp->d_partitions[part].p_size   = lp->d_secperunit;
1894 	lp->d_partitions[part].p_fstype = FS_UNUSED;
1895 	lp->d_partitions[part].p_fsize  = 1024;
1896 	lp->d_partitions[part].p_frag   = 8;
1897 
1898 	return 0;
1899 }
1900 
1901 /*
1902  * Mountroot hook: prompt the user to enter the root file system
1903  * floppy.
1904  */
1905 void
1906 fd_mountroot_hook(device_t dev)
1907 {
1908 	struct fd_softc *fd = device_private(dev);
1909 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
1910 	int c;
1911 
1912 	/* XXX device_unit() abuse */
1913 	fd_do_eject(fdc, device_unit(dev));
1914 	printf("Insert filesystem floppy and press return.");
1915 	for (;;) {
1916 		c = cngetc();
1917 		if ((c == '\r') || (c == '\n')) {
1918 			printf("\n");
1919 			break;
1920 		}
1921 	}
1922 }
1923