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