xref: /netbsd-src/sys/arch/atari/dev/hdfd.c (revision e4d7c2e329d54c97e0c0bd3016bbe74f550c3d5e)
1 /*	$NetBSD: hdfd.c,v 1.19 2000/02/07 20:16:50 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996 Leo Weppelman
5  * Copyright (c) 1993, 1994, 1995, 1996
6  *	Charles M. Hannum.  All rights reserved.
7  * Copyright (c) 1990 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Don Ahn.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *	This product includes software developed by the University of
24  *	California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
42  */
43 
44 /*
45  * Floppy formatting facilities merged from FreeBSD fd.c driver:
46  *	Id: fd.c,v 1.53 1995/03/12 22:40:56 joerg Exp
47  * which carries the same copyright/redistribution notice as shown above with
48  * the addition of the following statement before the "Redistribution and
49  * use ..." clause:
50  *
51  * Copyright (c) 1993, 1994 by
52  *  jc@irbs.UUCP (John Capo)
53  *  vak@zebub.msk.su (Serge Vakulenko)
54  *  ache@astral.msk.su (Andrew A. Chernov)
55  *
56  * Copyright (c) 1993, 1994, 1995 by
57  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
58  *  dufault@hda.com (Peter Dufault)
59  */
60 
61 #include "opt_ddb.h"
62 
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/kernel.h>
66 #include <sys/file.h>
67 #include <sys/ioctl.h>
68 #include <sys/device.h>
69 #include <sys/disklabel.h>
70 #include <sys/dkstat.h>
71 #include <sys/disk.h>
72 #include <sys/buf.h>
73 #include <sys/malloc.h>
74 #include <sys/uio.h>
75 #include <sys/syslog.h>
76 #include <sys/queue.h>
77 #include <sys/proc.h>
78 #include <sys/fdio.h>
79 #include <sys/conf.h>
80 #include <sys/device.h>
81 
82 #include <machine/cpu.h>
83 #include <machine/bus.h>
84 #include <machine/iomap.h>
85 #include <machine/mfp.h>
86 
87 #include <atari/dev/hdfdreg.h>
88 #include <atari/atari/intr.h>
89 #include <atari/atari/device.h>
90 
91 #include "locators.h"
92 
93 /*
94  * {b,c}devsw[] function prototypes
95  */
96 dev_type_open(fdopen);
97 dev_type_close(fdclose);
98 dev_type_read(fdread);
99 dev_type_write(fdwrite);
100 dev_type_ioctl(fdioctl);
101 dev_type_size(fdsize);
102 dev_type_dump(fddump);
103 
104 volatile u_char	*fdio_addr;
105 
106 #define wrt_fdc_reg(reg, val)	{ fdio_addr[reg] = val; }
107 #define rd_fdc_reg(reg)		( fdio_addr[reg] )
108 
109 #define	fdc_ienable()		MFP2->mf_ierb |= IB_DCHG;
110 
111 /*
112  * Interface to the pseudo-dma handler
113  */
114 void	fddma_intr(void);
115 caddr_t	fddmaaddr  = NULL;
116 int	fddmalen   = 0;
117 
118 extern void	mfp_hdfd_nf __P((void)), mfp_hdfd_fifo __P((void));
119 
120 /*
121  * Argument to fdcintr.....
122  */
123 static void	*intr_arg = NULL; /* XXX: arg. to intr_establish() */
124 
125 
126 
127 #define FDUNIT(dev)	(minor(dev) / 8)
128 #define FDTYPE(dev)	(minor(dev) % 8)
129 
130 /* XXX misuse a flag to identify format operation */
131 #define B_FORMAT B_XXX
132 
133 enum fdc_state {
134 	DEVIDLE = 0,
135 	MOTORWAIT,
136 	DOSEEK,
137 	SEEKWAIT,
138 	SEEKTIMEDOUT,
139 	SEEKCOMPLETE,
140 	DOIO,
141 	IOCOMPLETE,
142 	IOTIMEDOUT,
143 	DORESET,
144 	RESETCOMPLETE,
145 	RESETTIMEDOUT,
146 	DORECAL,
147 	RECALWAIT,
148 	RECALTIMEDOUT,
149 	RECALCOMPLETE,
150 };
151 
152 /* software state, per controller */
153 struct fdc_softc {
154 	struct device	sc_dev;		/* boilerplate */
155 	struct fd_softc	*sc_fd[4];	/* pointers to children */
156 	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
157 	enum fdc_state	sc_state;
158 	int		sc_errors;	/* number of retries so far */
159 	int		sc_overruns;	/* number of overruns so far */
160 	u_char		sc_status[7];	/* copy of registers */
161 };
162 
163 /* controller driver configuration */
164 int	fdcprobe __P((struct device *, struct cfdata *, void *));
165 int	fdprint __P((void *, const char *));
166 void	fdcattach __P((struct device *, struct device *, void *));
167 
168 struct cfattach fdc_ca = {
169 	sizeof(struct fdc_softc), fdcprobe, fdcattach
170 };
171 
172 /*
173  * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
174  * we tell them apart.
175  */
176 struct fd_type {
177 	int	sectrac;	/* sectors per track */
178 	int	heads;		/* number of heads */
179 	int	seccyl;		/* sectors per cylinder */
180 	int	secsize;	/* size code for sectors */
181 	int	datalen;	/* data len when secsize = 0 */
182 	int	steprate;	/* step rate and head unload time */
183 	int	gap1;		/* gap len between sectors */
184 	int	gap2;		/* formatting gap */
185 	int	tracks;		/* total num of tracks */
186 	int	size;		/* size of disk in sectors */
187 	int	step;		/* steps per cylinder */
188 	int	rate;		/* transfer speed code */
189 	u_char	fillbyte;	/* format fill byte */
190 	u_char	interleave;	/* interleave factor (formatting) */
191 	char	*name;
192 };
193 
194 /*
195  * The order of entries in the following table is important -- BEWARE!
196  * The order of the types is the same as for the TT/Falcon....
197  */
198 struct fd_type fd_types[] = {
199         /* 360kB in 720kB drive */
200         {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_125KBPS,0xf6,1,"360KB"  },
201         /* 3.5" 720kB diskette */
202         {  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_125KBPS,0xf6,1,"720KB"  },
203         /* 1.44MB diskette */
204         { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_250KBPS,0xf6,1,"1.44MB" },
205 };
206 
207 /* software state, per disk (with up to 4 disks per ctlr) */
208 struct fd_softc {
209 	struct device	sc_dev;
210 	struct disk	sc_dk;
211 
212 	struct fd_type	*sc_deftype;	/* default type descriptor */
213 	struct fd_type	*sc_type;	/* current type descriptor */
214 
215 	daddr_t		sc_blkno;	/* starting block number */
216 	int		sc_bcount;	/* byte count left */
217  	int		sc_opts;	/* user-set options */
218 	int		sc_skip;	/* bytes already transferred */
219 	int		sc_nblks;	/* #blocks currently tranferring */
220 	int		sc_nbytes;	/* #bytes currently tranferring */
221 
222 	int		sc_drive;	/* physical unit number */
223 	int		sc_flags;
224 #define	FD_OPEN		0x01		/* it's open */
225 #define	FD_MOTOR	0x02		/* motor should be on */
226 #define	FD_MOTOR_WAIT	0x04		/* motor coming up */
227 	int		sc_cylin;	/* where we think the head is */
228 
229 	void		*sc_sdhook;	/* saved shutdown hook for drive. */
230 
231 	TAILQ_ENTRY(fd_softc) sc_drivechain;
232 	int		sc_ops;		/* I/O ops since last switch */
233 	struct buf_queue sc_q;		/* pending I/O requests */
234 	int		sc_active;	/* number of active I/O operations */
235 };
236 
237 /* floppy driver configuration */
238 int	fdprobe __P((struct device *, struct cfdata *, void *));
239 void	fdattach __P((struct device *, struct device *, void *));
240 
241 struct cfattach hdfd_ca = {
242 	sizeof(struct fd_softc), fdprobe, fdattach
243 };
244 
245 extern struct cfdriver hdfd_cd;
246 
247 void	fdstrategy __P((struct buf *));
248 void	fdstart __P((struct fd_softc *));
249 
250 struct dkdriver fddkdriver = { fdstrategy };
251 
252 void	fd_set_motor __P((struct fdc_softc *fdc, int reset));
253 void	fd_motor_off __P((void *arg));
254 void	fd_motor_on __P((void *arg));
255 int	fdcresult __P((struct fdc_softc *fdc));
256 int	out_fdc __P((u_char x));
257 void	fdc_ctrl_intr __P((struct clockframe));
258 void	fdcstart __P((struct fdc_softc *fdc));
259 void	fdcstatus __P((struct device *dv, int n, char *s));
260 void	fdctimeout __P((void *arg));
261 void	fdcpseudointr __P((void *arg));
262 int	fdcintr __P((void *));
263 void	fdcretry __P((struct fdc_softc *fdc));
264 void	fdfinish __P((struct fd_softc *fd, struct buf *bp));
265 int	fdformat __P((dev_t, struct ne7_fd_formb *, struct proc *));
266 
267 static void	fdgetdisklabel __P((struct fd_softc *, dev_t));
268 static void	fdgetdefaultlabel __P((struct fd_softc *, struct disklabel *,
269 		    int));
270 
271 __inline struct fd_type *fd_dev_to_type __P((struct fd_softc *, dev_t));
272 
273 int
274 fdcprobe(parent, cfp, aux)
275 	struct device	*parent;
276 	struct cfdata	*cfp;
277 	void		*aux;
278 {
279 	int		rv   = 0;
280 	bus_space_tag_t mb_tag;
281 
282 	if(strcmp("fdc", aux) || cfp->cf_unit != 0)
283 		return(0);
284 
285 	if (!atari_realconfig)
286 		return 0;
287 
288 	if ((mb_tag = mb_alloc_bus_space_tag()) == NULL)
289 		return 0;
290 
291 	if (bus_space_map(mb_tag, FD_IOBASE, FD_IOSIZE, 0,
292 						(caddr_t*)&fdio_addr)) {
293 		printf("fdcprobe: cannot map io-area\n");
294 		mb_free_bus_space_tag(mb_tag);
295 		return (0);
296 	}
297 
298 #ifdef FD_DEBUG
299 	printf("fdcprobe: I/O mapping done va: %p\n", fdio_addr);
300 #endif
301 
302 	/* reset */
303 	wrt_fdc_reg(fdout, 0);
304 	delay(100);
305 	wrt_fdc_reg(fdout, FDO_FRST);
306 
307 	/* see if it can handle a command */
308 	if (out_fdc(NE7CMD_SPECIFY) < 0)
309 		goto out;
310 	out_fdc(0xdf);
311 	out_fdc(7);
312 
313 	rv = 1;
314 
315  out:
316 	if (rv == 0) {
317 		bus_space_unmap(mb_tag, (caddr_t)fdio_addr, FD_IOSIZE);
318 		mb_free_bus_space_tag(mb_tag);
319 	}
320 
321 	return rv;
322 }
323 
324 /*
325  * Arguments passed between fdcattach and fdprobe.
326  */
327 struct fdc_attach_args {
328 	int fa_drive;
329 	struct fd_type *fa_deftype;
330 };
331 
332 /*
333  * Print the location of a disk drive (called just before attaching the
334  * the drive).  If `fdc' is not NULL, the drive was found but was not
335  * in the system config file; print the drive name as well.
336  * Return QUIET (config_find ignores this if the device was configured) to
337  * avoid printing `fdN not configured' messages.
338  */
339 int
340 fdprint(aux, fdc)
341 	void *aux;
342 	const char *fdc;
343 {
344 	register struct fdc_attach_args *fa = aux;
345 
346 	if (!fdc)
347 		printf(" drive %d", fa->fa_drive);
348 	return QUIET;
349 }
350 
351 void
352 fdcattach(parent, self, aux)
353 	struct device *parent, *self;
354 	void *aux;
355 {
356 	struct fdc_softc	*fdc = (void *)self;
357 	struct fdc_attach_args	fa;
358 	int			has_fifo;
359 
360 	has_fifo = 0;
361 
362 	fdc->sc_state = DEVIDLE;
363 	TAILQ_INIT(&fdc->sc_drives);
364 
365 	out_fdc(NE7CMD_CONFIGURE);
366 	if (out_fdc(0) == 0) {
367 		out_fdc(0x1a);	/* No polling, fifo depth = 10	*/
368 		out_fdc(0);
369 
370 		/* Retain configuration across resets	*/
371 		out_fdc(NE7CMD_LOCK);
372 		(void)fdcresult(fdc);
373 		has_fifo = 1;
374 	}
375 	else {
376 		(void)rd_fdc_reg(fddata);
377 		printf(": no fifo");
378 	}
379 
380 	printf("\n");
381 
382 	if (intr_establish(22, USER_VEC|FAST_VEC, 0,
383 			   (hw_ifun_t)(has_fifo ? mfp_hdfd_fifo : mfp_hdfd_nf),
384 			   NULL) == NULL) {
385 		printf("fdcattach: Can't establish interrupt\n");
386 		return;
387 	}
388 
389 	/*
390 	 * Setup the interrupt logic.
391 	 */
392 	MFP2->mf_iprb  = (u_int8_t)~IB_DCHG;
393 	MFP2->mf_imrb |= IB_DCHG;
394 	MFP2->mf_aer  |= 0x10; /* fdc int low->high */
395 
396 	/* physical limit: four drives per controller. */
397 	for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
398 		/*
399 		 * XXX: Choose something sensible as a default...
400 		 */
401 		fa.fa_deftype = &fd_types[2]; /* 1.44MB */
402 		(void)config_found(self, (void *)&fa, fdprint);
403 	}
404 }
405 
406 int
407 fdprobe(parent, cfp, aux)
408 	struct device	*parent;
409 	struct cfdata	*cfp;
410 	void		*aux;
411 {
412 	struct fdc_softc	*fdc = (void *)parent;
413 	struct fdc_attach_args	*fa = aux;
414 	int			drive = fa->fa_drive;
415 	int			n;
416 
417 	if (cfp->cf_loc[FDCCF_UNIT] != FDCCF_UNIT_DEFAULT &&
418 	    cfp->cf_loc[FDCCF_UNIT] != drive)
419 		return 0;
420 	/*
421 	 * XXX
422 	 * This is to work around some odd interactions between this driver
423 	 * and SMC Ethernet cards.
424 	 */
425 	if (cfp->cf_loc[FDCCF_UNIT] == FDCCF_UNIT_DEFAULT && drive >= 2)
426 		return 0;
427 
428 	/* select drive and turn on motor */
429 	wrt_fdc_reg(fdout, drive | FDO_FRST | FDO_MOEN(drive));
430 
431 	/* wait for motor to spin up */
432 	delay(250000);
433 	out_fdc(NE7CMD_RECAL);
434 	out_fdc(drive);
435 
436 	/* wait for recalibrate */
437 	delay(2000000);
438 	out_fdc(NE7CMD_SENSEI);
439 	n = fdcresult(fdc);
440 
441 #ifdef FD_DEBUG
442 	{
443 		int i;
444 		printf("fdprobe: status");
445 		for (i = 0; i < n; i++)
446 			printf(" %x", fdc->sc_status[i]);
447 		printf("\n");
448 	}
449 #endif
450 	intr_arg = (void*)fdc;
451 	if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)
452 		return 0;
453 	/* turn off motor */
454 	wrt_fdc_reg(fdout, FDO_FRST);
455 
456 	return 1;
457 }
458 
459 /*
460  * Controller is working, and drive responded.  Attach it.
461  */
462 void
463 fdattach(parent, self, aux)
464 	struct device *parent, *self;
465 	void *aux;
466 {
467 	struct fdc_softc	*fdc  = (void *)parent;
468 	struct fd_softc		*fd   = (void *)self;
469 	struct fdc_attach_args	*fa   = aux;
470 	struct fd_type		*type = fa->fa_deftype;
471 	int			drive = fa->fa_drive;
472 
473 	/* XXX Allow `flags' to override device type? */
474 
475 	if (type)
476 		printf(": %s %d cyl, %d head, %d sec\n", type->name,
477 		    type->tracks, type->heads, type->sectrac);
478 	else
479 		printf(": density unknown\n");
480 
481 	BUFQ_INIT(&fd->sc_q);
482 	fd->sc_cylin      = -1;
483 	fd->sc_drive      = drive;
484 	fd->sc_deftype    = type;
485 	fdc->sc_fd[drive] = fd;
486 
487 	/*
488 	 * Initialize and attach the disk structure.
489 	 */
490 	fd->sc_dk.dk_name   = fd->sc_dev.dv_xname;
491 	fd->sc_dk.dk_driver = &fddkdriver;
492 	disk_attach(&fd->sc_dk);
493 
494 	/* XXX Need to do some more fiddling with sc_dk. */
495 	dk_establish(&fd->sc_dk, &fd->sc_dev);
496 
497 	/* Needed to power off if the motor is on when we halt. */
498 	fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd);
499 }
500 
501 /*
502  * This is called from the assembly part of the interrupt handler
503  * when it is clear that the interrupt was not related to shoving
504  * data.
505  */
506 void
507 fdc_ctrl_intr(frame)
508 	struct clockframe frame;
509 {
510 	int	s;
511 
512 	/*
513 	 * Disable further interrupts. The fdcintr() routine
514 	 * explicitely enables them when needed.
515 	 */
516 	MFP2->mf_ierb &= ~IB_DCHG;
517 
518 	/*
519 	 * Set fddmalen to zero so no pseudo-dma transfers will
520 	 * occur.
521 	 */
522 	fddmalen = 0;
523 
524 	if (!BASEPRI(frame.cf_sr)) {
525 		/*
526 		 * We don't want to stay on ipl6.....
527 		 */
528 		add_sicallback((si_farg)fdcpseudointr, intr_arg, 0);
529 	}
530 	else {
531 		s = splbio();
532 		(void) fdcintr(intr_arg);
533 		splx(s);
534 	}
535 }
536 
537 __inline struct fd_type *
538 fd_dev_to_type(fd, dev)
539 	struct fd_softc *fd;
540 	dev_t dev;
541 {
542 	int type = FDTYPE(dev);
543 
544 	if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
545 		return NULL;
546 	return type ? &fd_types[type - 1] : fd->sc_deftype;
547 }
548 
549 void
550 fdstrategy(bp)
551 	register struct buf *bp;	/* IO operation to perform */
552 {
553 	struct fd_softc *fd = hdfd_cd.cd_devs[FDUNIT(bp->b_dev)];
554 	int sz;
555  	int s;
556 
557 	/* Valid unit, controller, and request? */
558 	if (bp->b_blkno < 0 ||
559 	    ((bp->b_bcount % FDC_BSIZE) != 0 &&
560 	     (bp->b_flags & B_FORMAT) == 0)) {
561 		bp->b_error = EINVAL;
562 		goto bad;
563 	}
564 
565 	/* If it's a null transfer, return immediately. */
566 	if (bp->b_bcount == 0)
567 		goto done;
568 
569 	sz = howmany(bp->b_bcount, FDC_BSIZE);
570 
571 	if (bp->b_blkno + sz > fd->sc_type->size) {
572 		sz = fd->sc_type->size - bp->b_blkno;
573 		if (sz == 0) {
574 			/* If exactly at end of disk, return EOF. */
575 			goto done;
576 		}
577 		if (sz < 0) {
578 			/* If past end of disk, return EINVAL. */
579 			bp->b_error = EINVAL;
580 			goto bad;
581 		}
582 		/* Otherwise, truncate request. */
583 		bp->b_bcount = sz << DEV_BSHIFT;
584 	}
585 
586 	bp->b_rawblkno = bp->b_blkno;
587  	bp->b_cylinder = bp->b_blkno / (FDC_BSIZE/DEV_BSIZE) / fd->sc_type->seccyl;
588 
589 #ifdef FD_DEBUG
590 	printf("fdstrategy: b_blkno %d b_bcount %ld blkno %ld cylin %ld sz"
591 		" %d\n", bp->b_blkno, bp->b_bcount, (long)fd->sc_blkno,
592 		bp->b_cylinder, sz);
593 #endif
594 
595 	/* Queue transfer on drive, activate drive and controller if idle. */
596 	s = splbio();
597 	disksort_cylinder(&fd->sc_q, bp);
598 	untimeout(fd_motor_off, fd); /* a good idea */
599 	if (fd->sc_active == 0)
600 		fdstart(fd);
601 #ifdef DIAGNOSTIC
602 	else {
603 		struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
604 		if (fdc->sc_state == DEVIDLE) {
605 			printf("fdstrategy: controller inactive\n");
606 			fdcstart(fdc);
607 		}
608 	}
609 #endif
610 	splx(s);
611 	return;
612 
613 bad:
614 	bp->b_flags |= B_ERROR;
615 done:
616 	/* Toss transfer; we're done early. */
617 	bp->b_resid = bp->b_bcount;
618 	biodone(bp);
619 }
620 
621 void
622 fdstart(fd)
623 	struct fd_softc *fd;
624 {
625 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
626 	int active = fdc->sc_drives.tqh_first != 0;
627 
628 	/* Link into controller queue. */
629 	fd->sc_active = 1;
630 	TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
631 
632 	/* If controller not already active, start it. */
633 	if (!active)
634 		fdcstart(fdc);
635 }
636 
637 void
638 fdfinish(fd, bp)
639 	struct fd_softc *fd;
640 	struct buf *bp;
641 {
642 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
643 
644 	/*
645 	 * Move this drive to the end of the queue to give others a `fair'
646 	 * chance.  We only force a switch if N operations are completed while
647 	 * another drive is waiting to be serviced, since there is a long motor
648 	 * startup delay whenever we switch.
649 	 */
650 	if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
651 		fd->sc_ops = 0;
652 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
653 		if (BUFQ_NEXT(bp) != NULL)
654 			TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
655 		else
656 			fd->sc_active = 0;
657 	}
658 	bp->b_resid = fd->sc_bcount;
659 	fd->sc_skip = 0;
660 	BUFQ_REMOVE(&fd->sc_q, bp);
661 
662 	biodone(bp);
663 	/* turn off motor 5s from now */
664 	timeout(fd_motor_off, fd, 5 * hz);
665 	fdc->sc_state = DEVIDLE;
666 }
667 
668 int
669 fdread(dev, uio, flags)
670 	dev_t dev;
671 	struct uio *uio;
672 	int flags;
673 {
674 	return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio));
675 }
676 
677 int
678 fdwrite(dev, uio, flags)
679 	dev_t dev;
680 	struct uio *uio;
681 	int flags;
682 {
683 	return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio));
684 }
685 
686 void
687 fd_set_motor(fdc, reset)
688 	struct fdc_softc *fdc;
689 	int reset;
690 {
691 	struct fd_softc *fd;
692 	u_char status;
693 	int n;
694 
695 	if ((fd = fdc->sc_drives.tqh_first) != NULL)
696 		status = fd->sc_drive;
697 	else
698 		status = 0;
699 	if (!reset)
700 		status |= FDO_FRST | FDO_FDMAEN;
701 	for (n = 0; n < 4; n++)
702 		if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
703 			status |= FDO_MOEN(n);
704 	wrt_fdc_reg(fdout, status);
705 }
706 
707 void
708 fd_motor_off(arg)
709 	void *arg;
710 {
711 	struct fd_softc *fd = arg;
712 	int s;
713 
714 	s = splbio();
715 	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
716 	fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0);
717 	splx(s);
718 }
719 
720 void
721 fd_motor_on(arg)
722 	void *arg;
723 {
724 	struct fd_softc *fd = arg;
725 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
726 	int s;
727 
728 	s = splbio();
729 	fd->sc_flags &= ~FD_MOTOR_WAIT;
730 	if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
731 		(void) fdcintr(fdc);
732 	splx(s);
733 }
734 
735 int
736 fdcresult(fdc)
737 	struct fdc_softc *fdc;
738 {
739 	u_char i;
740 	int j = 100000,
741 	    n = 0;
742 
743 	for (; j; j--) {
744 		i = rd_fdc_reg(fdsts) & (NE7_DIO | NE7_RQM | NE7_CB);
745 		if (i == NE7_RQM)
746 			return n;
747 		if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
748 			if (n >= sizeof(fdc->sc_status)) {
749 				log(LOG_ERR, "fdcresult: overrun\n");
750 				return -1;
751 			}
752 			fdc->sc_status[n++] = rd_fdc_reg(fddata);
753 		}
754 		else delay(10);
755 	}
756 	log(LOG_ERR, "fdcresult: timeout\n");
757 	return -1;
758 }
759 
760 int
761 out_fdc(x)
762 	u_char x;
763 {
764 	int i = 100000;
765 
766 	while (((rd_fdc_reg(fdsts) & (NE7_DIO|NE7_RQM)) != NE7_RQM) && i-- > 0)
767 		delay(1);
768 	if (i <= 0)
769 		return -1;
770 	wrt_fdc_reg(fddata, x);
771 	return 0;
772 }
773 
774 int
775 fdopen(dev, flags, mode, p)
776 	dev_t dev;
777 	int flags;
778 	int mode;
779 	struct proc *p;
780 {
781  	int unit;
782 	struct fd_softc *fd;
783 	struct fd_type *type;
784 
785 	unit = FDUNIT(dev);
786 	if (unit >= hdfd_cd.cd_ndevs)
787 		return ENXIO;
788 	fd = hdfd_cd.cd_devs[unit];
789 	if (fd == 0)
790 		return ENXIO;
791 	type = fd_dev_to_type(fd, dev);
792 	if (type == NULL)
793 		return ENXIO;
794 
795 	if ((fd->sc_flags & FD_OPEN) != 0 &&
796 	    fd->sc_type != type)
797 		return EBUSY;
798 
799 	fd->sc_type = type;
800 	fd->sc_cylin = -1;
801 	fd->sc_flags |= FD_OPEN;
802 
803 	return 0;
804 }
805 
806 int
807 fdclose(dev, flags, mode, p)
808 	dev_t dev;
809 	int flags;
810 	int mode;
811 	struct proc *p;
812 {
813 	struct fd_softc *fd = hdfd_cd.cd_devs[FDUNIT(dev)];
814 
815 	fd->sc_flags &= ~FD_OPEN;
816 	fd->sc_opts  &= ~(FDOPT_NORETRY|FDOPT_SILENT);
817 	return 0;
818 }
819 
820 void
821 fdcstart(fdc)
822 	struct fdc_softc *fdc;
823 {
824 
825 #ifdef DIAGNOSTIC
826 	/* only got here if controller's drive queue was inactive; should
827 	   be in idle state */
828 	if (fdc->sc_state != DEVIDLE) {
829 		printf("fdcstart: not idle\n");
830 		return;
831 	}
832 #endif
833 	(void) fdcintr(fdc);
834 }
835 
836 void
837 fdcstatus(dv, n, s)
838 	struct device *dv;
839 	int n;
840 	char *s;
841 {
842 	struct fdc_softc *fdc = (void *)dv->dv_parent;
843 	char bits[64];
844 
845 	if (n == 0) {
846 		out_fdc(NE7CMD_SENSEI);
847 		(void) fdcresult(fdc);
848 		n = 2;
849 	}
850 
851 	printf("%s: %s", dv->dv_xname, s);
852 
853 	switch (n) {
854 	case 0:
855 		printf("\n");
856 		break;
857 	case 2:
858 		printf(" (st0 %s cyl %d)\n",
859 		    bitmask_snprintf(fdc->sc_status[0], NE7_ST0BITS,
860 		    bits, sizeof(bits)), fdc->sc_status[1]);
861 		break;
862 	case 7:
863 		printf(" (st0 %s", bitmask_snprintf(fdc->sc_status[0],
864 		    NE7_ST0BITS, bits, sizeof(bits)));
865 		printf(" st1 %s", bitmask_snprintf(fdc->sc_status[1],
866 		    NE7_ST1BITS, bits, sizeof(bits)));
867 		printf(" st2 %s", bitmask_snprintf(fdc->sc_status[2],
868 		    NE7_ST2BITS, bits, sizeof(bits)));
869 		printf(" cyl %d head %d sec %d)\n",
870 		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
871 		break;
872 #ifdef DIAGNOSTIC
873 	default:
874 		printf("\nfdcstatus: weird size");
875 		break;
876 #endif
877 	}
878 }
879 
880 void
881 fdctimeout(arg)
882 	void *arg;
883 {
884 	struct fdc_softc *fdc = arg;
885 	struct fd_softc *fd = fdc->sc_drives.tqh_first;
886 	int s;
887 
888 	s = splbio();
889 	fdcstatus(&fd->sc_dev, 0, "timeout");
890 
891 	if (BUFQ_FIRST(&fd->sc_q) != NULL)
892 		fdc->sc_state++;
893 	else
894 		fdc->sc_state = DEVIDLE;
895 
896 	(void) fdcintr(fdc);
897 	splx(s);
898 }
899 
900 void
901 fdcpseudointr(arg)
902 	void *arg;
903 {
904 	int s;
905 
906 	/* Just ensure it has the right spl. */
907 	s = splbio();
908 	(void) fdcintr(arg);
909 	splx(s);
910 }
911 
912 int
913 fdcintr(arg)
914 	void *arg;
915 {
916 	struct fdc_softc	*fdc = arg;
917 #define	st0	fdc->sc_status[0]
918 #define	st1	fdc->sc_status[1]
919 #define	cyl	fdc->sc_status[1]
920 
921 	struct fd_softc		*fd;
922 	struct buf		*bp;
923 	int			read, head, sec, i, nblks;
924 	struct fd_type		*type;
925 	struct ne7_fd_formb	*finfo = NULL;
926 
927 loop:
928 	/* Is there a drive for the controller to do a transfer with? */
929 	fd = fdc->sc_drives.tqh_first;
930 	if (fd == NULL) {
931 		fdc->sc_state = DEVIDLE;
932  		return 1;
933 	}
934 
935 	/* Is there a transfer to this drive?  If not, deactivate drive. */
936 	bp = BUFQ_FIRST(&fd->sc_q);
937 	if (bp == NULL) {
938 		fd->sc_ops = 0;
939 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
940 		fd->sc_active = 0;
941 		goto loop;
942 	}
943 
944 	if (bp->b_flags & B_FORMAT)
945 		finfo = (struct ne7_fd_formb *)bp->b_data;
946 
947 	switch (fdc->sc_state) {
948 	case DEVIDLE:
949 		fdc->sc_errors = 0;
950 		fdc->sc_overruns = 0;
951 		fd->sc_skip = 0;
952 		fd->sc_bcount = bp->b_bcount;
953 		fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE);
954 		untimeout(fd_motor_off, fd);
955 		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
956 			fdc->sc_state = MOTORWAIT;
957 			return 1;
958 		}
959 		if ((fd->sc_flags & FD_MOTOR) == 0) {
960 			/* Turn on the motor, being careful about pairing. */
961 			struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
962 			if (ofd && ofd->sc_flags & FD_MOTOR) {
963 				untimeout(fd_motor_off, ofd);
964 				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
965 			}
966 			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
967 			fd_set_motor(fdc, 0);
968 			fdc->sc_state = MOTORWAIT;
969 			/* Allow .25s for motor to stabilize. */
970 			timeout(fd_motor_on, fd, hz / 4);
971 			return 1;
972 		}
973 		/* Make sure the right drive is selected. */
974 		fd_set_motor(fdc, 0);
975 
976 		/* fall through */
977 	case DOSEEK:
978 	doseek:
979 		if (fd->sc_cylin == bp->b_cylinder)
980 			goto doio;
981 
982 		out_fdc(NE7CMD_SPECIFY);/* specify command */
983 		out_fdc(fd->sc_type->steprate);
984 		out_fdc(0x7);	/* XXX head load time == 6ms - non-dma */
985 
986 		fdc_ienable();
987 
988 		out_fdc(NE7CMD_SEEK);	/* seek function */
989 		out_fdc(fd->sc_drive);	/* drive number */
990 		out_fdc(bp->b_cylinder * fd->sc_type->step);
991 
992 		fd->sc_cylin = -1;
993 		fdc->sc_state = SEEKWAIT;
994 
995 		fd->sc_dk.dk_seek++;
996 		disk_busy(&fd->sc_dk);
997 
998 		timeout(fdctimeout, fdc, 4 * hz);
999 		return 1;
1000 
1001 	case DOIO:
1002 	doio:
1003 		if (finfo)
1004 			fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
1005 				      (char *)finfo;
1006 
1007 		type  = fd->sc_type;
1008 		sec   = fd->sc_blkno % type->seccyl;
1009 		head  = sec / type->sectrac;
1010 		sec  -= head * type->sectrac;
1011 		nblks = type->sectrac - sec;
1012 		nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
1013 		nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
1014 		fd->sc_nblks  = nblks;
1015 		fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE;
1016 #ifdef DIAGNOSTIC
1017 		{
1018 		     int block;
1019 
1020 		     block = (fd->sc_cylin * type->heads + head)
1021 				* type->sectrac + sec;
1022 		     if (block != fd->sc_blkno) {
1023 			 printf("fdcintr: block %d != blkno %d\n",
1024 						block, fd->sc_blkno);
1025 #ifdef DDB
1026 			 Debugger();
1027 #endif
1028 		     }
1029 		}
1030 #endif
1031 		read = bp->b_flags & B_READ ? 1 : 0;
1032 
1033 		/*
1034 		 * Setup pseudo-dma address & count
1035 		 */
1036 		fddmaaddr = bp->b_data + fd->sc_skip;
1037 		fddmalen  = fd->sc_nbytes;
1038 
1039 		wrt_fdc_reg(fdctl, type->rate);
1040 #ifdef FD_DEBUG
1041 		printf("fdcintr: %s drive %d track %d head %d sec %d"
1042 			" nblks %d\n", read ? "read" : "write",
1043 			fd->sc_drive, fd->sc_cylin, head, sec, nblks);
1044 #endif
1045 		fdc_ienable();
1046 
1047 		if (finfo) {
1048 			/* formatting */
1049 			if (out_fdc(NE7CMD_FORMAT) < 0) {
1050 				fdc->sc_errors = 4;
1051 				fdcretry(fdc);
1052 				goto loop;
1053 			}
1054 			out_fdc((head << 2) | fd->sc_drive);
1055 			out_fdc(finfo->fd_formb_secshift);
1056 			out_fdc(finfo->fd_formb_nsecs);
1057 			out_fdc(finfo->fd_formb_gaplen);
1058 			out_fdc(finfo->fd_formb_fillbyte);
1059 		} else {
1060 			if (read)
1061 				out_fdc(NE7CMD_READ);	/* READ */
1062 			else
1063 				out_fdc(NE7CMD_WRITE);	/* WRITE */
1064 			out_fdc((head << 2) | fd->sc_drive);
1065 			out_fdc(fd->sc_cylin);		/* track	 */
1066 			out_fdc(head);			/* head		 */
1067 			out_fdc(sec + 1);		/* sector +1	 */
1068 			out_fdc(type->secsize);		/* sector size   */
1069 			out_fdc(sec + nblks);		/* last sectors	 */
1070 			out_fdc(type->gap1);		/* gap1 size	 */
1071 			out_fdc(type->datalen);		/* data length	 */
1072 		}
1073 		fdc->sc_state = IOCOMPLETE;
1074 
1075 		disk_busy(&fd->sc_dk);
1076 
1077 		/* allow 2 seconds for operation */
1078 		timeout(fdctimeout, fdc, 2 * hz);
1079 		return 1;				/* will return later */
1080 
1081 	case SEEKWAIT:
1082 		untimeout(fdctimeout, fdc);
1083 		fdc->sc_state = SEEKCOMPLETE;
1084 		/* allow 1/50 second for heads to settle */
1085 		timeout(fdcpseudointr, fdc, hz / 50);
1086 		return 1;
1087 
1088 	case SEEKCOMPLETE:
1089 		disk_unbusy(&fd->sc_dk, 0);	/* no data on seek */
1090 
1091 		/* Make sure seek really happened. */
1092 		out_fdc(NE7CMD_SENSEI);
1093 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
1094 		    cyl != bp->b_cylinder * fd->sc_type->step) {
1095 #ifdef FD_DEBUG
1096 			fdcstatus(&fd->sc_dev, 2, "seek failed");
1097 #endif
1098 			fdcretry(fdc);
1099 			goto loop;
1100 		}
1101 		fd->sc_cylin = bp->b_cylinder;
1102 		goto doio;
1103 
1104 	case IOTIMEDOUT:
1105 	case SEEKTIMEDOUT:
1106 	case RECALTIMEDOUT:
1107 	case RESETTIMEDOUT:
1108 		fdcretry(fdc);
1109 		goto loop;
1110 
1111 	case IOCOMPLETE: /* IO DONE, post-analyze */
1112 		untimeout(fdctimeout, fdc);
1113 
1114 		disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid));
1115 
1116 		if (fdcresult(fdc) != 7 || (st1 & 0x37) != 0) {
1117 			/*
1118 			 * As the damn chip doesn't seem to have a FIFO,
1119 			 * accept a few overruns as a fact of life *sigh*
1120 			 */
1121 			if ((st1 & 0x10) && (++fdc->sc_overruns < 4)) {
1122 				fdc->sc_state = DOSEEK;
1123 				goto loop;
1124 			}
1125 #ifdef FD_DEBUG
1126 			fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
1127 			    "read failed" : "write failed");
1128 			printf("blkno %d nblks %d\n",
1129 			    fd->sc_blkno, fd->sc_nblks);
1130 #endif
1131 			fdcretry(fdc);
1132 			goto loop;
1133 		}
1134 		if (fdc->sc_errors) {
1135 			diskerr(bp, "fd", "soft error", LOG_PRINTF,
1136 			    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
1137 			printf("\n");
1138 			fdc->sc_errors = 0;
1139 		}
1140 		fdc->sc_overruns = 0;
1141 		fd->sc_blkno += fd->sc_nblks;
1142 		fd->sc_skip += fd->sc_nbytes;
1143 		fd->sc_bcount -= fd->sc_nbytes;
1144 		if (!finfo && fd->sc_bcount > 0) {
1145 			bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl;
1146 			goto doseek;
1147 		}
1148 		fdfinish(fd, bp);
1149 		goto loop;
1150 
1151 	case DORESET:
1152 		/* try a reset, keep motor on */
1153 		fd_set_motor(fdc, 1);
1154 		delay(100);
1155 		fd_set_motor(fdc, 0);
1156 		fdc->sc_state = RESETCOMPLETE;
1157 		timeout(fdctimeout, fdc, hz / 2);
1158 		return 1;			/* will return later */
1159 
1160 	case RESETCOMPLETE:
1161 		untimeout(fdctimeout, fdc);
1162 		/* clear the controller output buffer */
1163 		for (i = 0; i < 4; i++) {
1164 			out_fdc(NE7CMD_SENSEI);
1165 			(void) fdcresult(fdc);
1166 		}
1167 
1168 		/* fall through */
1169 	case DORECAL:
1170 		fdc_ienable();
1171 
1172 		out_fdc(NE7CMD_RECAL);	/* recalibrate function */
1173 		out_fdc(fd->sc_drive);
1174 		fdc->sc_state = RECALWAIT;
1175 		timeout(fdctimeout, fdc, 5 * hz);
1176 		return 1;			/* will return later */
1177 
1178 	case RECALWAIT:
1179 		untimeout(fdctimeout, fdc);
1180 		fdc->sc_state = RECALCOMPLETE;
1181 		/* allow 1/30 second for heads to settle */
1182 		timeout(fdcpseudointr, fdc, hz / 30);
1183 		return 1;			/* will return later */
1184 
1185 	case RECALCOMPLETE:
1186 		out_fdc(NE7CMD_SENSEI);
1187 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
1188 #ifdef FD_DEBUG
1189 			fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
1190 #endif
1191 			fdcretry(fdc);
1192 			goto loop;
1193 		}
1194 		fd->sc_cylin = 0;
1195 		goto doseek;
1196 
1197 	case MOTORWAIT:
1198 		if (fd->sc_flags & FD_MOTOR_WAIT)
1199 			return 1;		/* time's not up yet */
1200 		goto doseek;
1201 
1202 	default:
1203 		fdcstatus(&fd->sc_dev, 0, "stray interrupt");
1204 		return 1;
1205 	}
1206 #ifdef DIAGNOSTIC
1207 	panic("fdcintr: impossible");
1208 #endif
1209 #undef	st0
1210 #undef	st1
1211 #undef	cyl
1212 }
1213 
1214 void
1215 fdcretry(fdc)
1216 	struct fdc_softc *fdc;
1217 {
1218 	char bits[64];
1219 	struct fd_softc *fd;
1220 	struct buf *bp;
1221 
1222 	fd = fdc->sc_drives.tqh_first;
1223 	bp = BUFQ_FIRST(&fd->sc_q);
1224 
1225 	if (fd->sc_opts & FDOPT_NORETRY)
1226 	    goto fail;
1227 
1228 	switch (fdc->sc_errors) {
1229 	case 0:
1230 		/* try again */
1231 		fdc->sc_state = DOSEEK;
1232 		break;
1233 
1234 	case 1: case 2: case 3:
1235 		/* didn't work; try recalibrating */
1236 		fdc->sc_state = DORECAL;
1237 		break;
1238 
1239 	case 4:
1240 		/* still no go; reset the bastard */
1241 		fdc->sc_state = DORESET;
1242 		break;
1243 
1244 	default:
1245 	fail:
1246 		if ((fd->sc_opts & FDOPT_SILENT) == 0) {
1247 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
1248 				fd->sc_skip / FDC_BSIZE,
1249 				(struct disklabel *)NULL);
1250 
1251 			printf(" (st0 %s",
1252 			       bitmask_snprintf(fdc->sc_status[0],
1253 						NE7_ST0BITS, bits,
1254 						sizeof(bits)));
1255 			printf(" st1 %s",
1256 			       bitmask_snprintf(fdc->sc_status[1],
1257 						NE7_ST1BITS, bits,
1258 						sizeof(bits)));
1259 			printf(" st2 %s",
1260 			       bitmask_snprintf(fdc->sc_status[2],
1261 						NE7_ST2BITS, bits,
1262 						sizeof(bits)));
1263 			printf(" cyl %d head %d sec %d)\n",
1264 			       fdc->sc_status[3],
1265 			       fdc->sc_status[4],
1266 			       fdc->sc_status[5]);
1267 		}
1268 		bp->b_flags |= B_ERROR;
1269 		bp->b_error = EIO;
1270 		fdfinish(fd, bp);
1271 	}
1272 	fdc->sc_errors++;
1273 }
1274 
1275 int
1276 fdsize(dev)
1277 	dev_t dev;
1278 {
1279 
1280 	/* Swapping to floppies would not make sense. */
1281 	return -1;
1282 }
1283 
1284 int
1285 fddump(dev, blkno, va, size)
1286 	dev_t dev;
1287 	daddr_t blkno;
1288 	caddr_t va;
1289 	size_t size;
1290 {
1291 
1292 	/* Not implemented. */
1293 	return ENXIO;
1294 }
1295 
1296 int
1297 fdioctl(dev, cmd, addr, flag, p)
1298 	dev_t dev;
1299 	u_long cmd;
1300 	caddr_t addr;
1301 	int flag;
1302 	struct proc *p;
1303 {
1304 	struct fd_softc		*fd;
1305 	struct disklabel	buffer;
1306 	int			error;
1307 	struct fdformat_parms	*form_parms;
1308 	struct fdformat_cmd	*form_cmd;
1309 	struct ne7_fd_formb	fd_formb;
1310 	unsigned int		scratch;
1311 	int			il[FD_MAX_NSEC + 1];
1312 	register int		i, j;
1313 
1314 	fd = hdfd_cd.cd_devs[FDUNIT(dev)];
1315 
1316 	switch (cmd) {
1317 	case DIOCGDINFO:
1318 		fdgetdisklabel(fd, dev);
1319 		*(struct disklabel *)addr = *(fd->sc_dk.dk_label);
1320 		return 0;
1321 
1322 	case DIOCGPART:
1323 		fdgetdisklabel(fd, dev);
1324 		((struct partinfo *)addr)->disklab = fd->sc_dk.dk_label;
1325 		((struct partinfo *)addr)->part =
1326 			      &fd->sc_dk.dk_label->d_partitions[RAW_PART];
1327 		return(0);
1328 
1329 	case DIOCWLABEL:
1330 		if ((flag & FWRITE) == 0)
1331 			return EBADF;
1332 		/* XXX do something */
1333 		return 0;
1334 
1335 	case DIOCSDINFO:
1336 	case DIOCWDINFO:
1337 		if ((flag & FWRITE) == 0)
1338 		    return EBADF;
1339 
1340 		error = setdisklabel(&buffer, (struct disklabel *)addr, 0,NULL);
1341 		if (error)
1342 		    return error;
1343 
1344 		if (cmd == DIOCWDINFO)
1345 		    error = writedisklabel(dev, fdstrategy, &buffer, NULL);
1346 		return error;
1347 
1348 	case FDIOCGETFORMAT:
1349 		form_parms = (struct fdformat_parms *)addr;
1350 		form_parms->fdformat_version = FDFORMAT_VERSION;
1351 		form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
1352 		form_parms->ncyl = fd->sc_type->tracks;
1353 		form_parms->nspt = fd->sc_type->sectrac;
1354 		form_parms->ntrk = fd->sc_type->heads;
1355 		form_parms->stepspercyl = fd->sc_type->step;
1356 		form_parms->gaplen = fd->sc_type->gap2;
1357 		form_parms->fillbyte = fd->sc_type->fillbyte;
1358 		form_parms->interleave = fd->sc_type->interleave;
1359 		switch (fd->sc_type->rate) {
1360 		case FDC_500KBPS:
1361 			form_parms->xfer_rate = 500 * 1024;
1362 			break;
1363 		case FDC_300KBPS:
1364 			form_parms->xfer_rate = 300 * 1024;
1365 			break;
1366 		case FDC_250KBPS:
1367 			form_parms->xfer_rate = 250 * 1024;
1368 			break;
1369 		case FDC_125KBPS:
1370 			form_parms->xfer_rate = 125 * 1024;
1371 			break;
1372 		default:
1373 			return EINVAL;
1374 		}
1375 		return 0;
1376 
1377 	case FDIOCSETFORMAT:
1378 		if((flag & FWRITE) == 0)
1379 			return EBADF;	/* must be opened for writing */
1380 		form_parms = (struct fdformat_parms *)addr;
1381 		if (form_parms->fdformat_version != FDFORMAT_VERSION)
1382 			return EINVAL;	/* wrong version of formatting prog */
1383 
1384 		scratch = form_parms->nbps >> 7;
1385 		if ((form_parms->nbps & 0x7f) || ffs(scratch) == 0 ||
1386 		    scratch & ~(1 << (ffs(scratch)-1)))
1387 			/* not a power-of-two multiple of 128 */
1388 			return EINVAL;
1389 
1390 		switch (form_parms->xfer_rate) {
1391 		case 500 * 1024:
1392 			fd->sc_type->rate = FDC_500KBPS;
1393 			break;
1394 		case 300 * 1024:
1395 			fd->sc_type->rate = FDC_300KBPS;
1396 			break;
1397 		case 250 * 1024:
1398 			fd->sc_type->rate = FDC_250KBPS;
1399 			break;
1400 		case 125 * 1024:
1401 			fd->sc_type->rate = FDC_125KBPS;
1402 			break;
1403 		default:
1404 			return EINVAL;
1405 		}
1406 
1407 		if (form_parms->nspt > FD_MAX_NSEC ||
1408 		    form_parms->fillbyte > 0xff ||
1409 		    form_parms->interleave > 0xff)
1410 			return EINVAL;
1411 		fd->sc_type->sectrac = form_parms->nspt;
1412 		if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
1413 			return EINVAL;
1414 		fd->sc_type->heads = form_parms->ntrk;
1415 		fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
1416 		fd->sc_type->secsize = ffs(scratch)-1;
1417 		fd->sc_type->gap2 = form_parms->gaplen;
1418 		fd->sc_type->tracks = form_parms->ncyl;
1419 		fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
1420 			form_parms->nbps / DEV_BSIZE;
1421 		fd->sc_type->step = form_parms->stepspercyl;
1422 		fd->sc_type->fillbyte = form_parms->fillbyte;
1423 		fd->sc_type->interleave = form_parms->interleave;
1424 		return 0;
1425 
1426 	case FDIOCFORMAT_TRACK:
1427 		if((flag & FWRITE) == 0)
1428 			return EBADF;	/* must be opened for writing */
1429 		form_cmd = (struct fdformat_cmd *)addr;
1430 		if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
1431 			return EINVAL;	/* wrong version of formatting prog */
1432 
1433 		if (form_cmd->head >= fd->sc_type->heads ||
1434 		    form_cmd->cylinder >= fd->sc_type->tracks) {
1435 			return EINVAL;
1436 		}
1437 
1438 		fd_formb.head = form_cmd->head;
1439 		fd_formb.cyl = form_cmd->cylinder;
1440 		fd_formb.transfer_rate = fd->sc_type->rate;
1441 		fd_formb.fd_formb_secshift = fd->sc_type->secsize;
1442 		fd_formb.fd_formb_nsecs = fd->sc_type->sectrac;
1443 		fd_formb.fd_formb_gaplen = fd->sc_type->gap2;
1444 		fd_formb.fd_formb_fillbyte = fd->sc_type->fillbyte;
1445 
1446 		bzero(il,sizeof il);
1447 		for (j = 0, i = 1; i <= fd_formb.fd_formb_nsecs; i++) {
1448 			while (il[(j%fd_formb.fd_formb_nsecs)+1])
1449 				j++;
1450 			il[(j%fd_formb.fd_formb_nsecs)+1] = i;
1451 			j += fd->sc_type->interleave;
1452 		}
1453 		for (i = 0; i < fd_formb.fd_formb_nsecs; i++) {
1454 			fd_formb.fd_formb_cylno(i) = form_cmd->cylinder;
1455 			fd_formb.fd_formb_headno(i) = form_cmd->head;
1456 			fd_formb.fd_formb_secno(i) = il[i+1];
1457 			fd_formb.fd_formb_secsize(i) = fd->sc_type->secsize;
1458 		}
1459 	case FDIOCGETOPTS:		/* get drive options */
1460 		*(int *)addr = fd->sc_opts;
1461 		return 0;
1462 
1463 	case FDIOCSETOPTS:		/* set drive options */
1464 		fd->sc_opts = *(int *)addr;
1465 		return 0;
1466 
1467 
1468 	default:
1469 		return ENOTTY;
1470 	}
1471 
1472 #ifdef DIAGNOSTIC
1473 	panic("fdioctl: impossible");
1474 #endif
1475 }
1476 
1477 int
1478 fdformat(dev, finfo, p)
1479 	dev_t dev;
1480 	struct ne7_fd_formb *finfo;
1481 	struct proc *p;
1482 {
1483 	int rv = 0, s;
1484 	struct fd_softc *fd = hdfd_cd.cd_devs[FDUNIT(dev)];
1485 	struct fd_type *type = fd->sc_type;
1486 	struct buf *bp;
1487 
1488 	/* set up a buffer header for fdstrategy() */
1489 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1490 	if(bp == 0)
1491 		return ENOBUFS;
1492 	PHOLD(p);
1493 	bzero((void *)bp, sizeof(struct buf));
1494 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1495 	bp->b_proc = p;
1496 	bp->b_dev = dev;
1497 
1498 	/*
1499 	 * calculate a fake blkno, so fdstrategy() would initiate a
1500 	 * seek to the requested cylinder
1501 	 */
1502 	bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
1503 		       + finfo->head * type->sectrac) * FDC_BSIZE / DEV_BSIZE;
1504 
1505 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1506 	bp->b_data = (caddr_t)finfo;
1507 
1508 #ifdef DEBUG
1509 	printf("fdformat: blkno %x count %lx\n", bp->b_blkno, bp->b_bcount);
1510 #endif
1511 
1512 	/* now do the format */
1513 	fdstrategy(bp);
1514 
1515 	/* ...and wait for it to complete */
1516 	s = splbio();
1517 	while(!(bp->b_flags & B_DONE)) {
1518 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1519 		if (rv == EWOULDBLOCK)
1520 			break;
1521 	}
1522 	splx(s);
1523 
1524 	if (rv == EWOULDBLOCK) {
1525 		/* timed out */
1526 		rv = EIO;
1527 		biodone(bp);
1528 	}
1529 	if(bp->b_flags & B_ERROR) {
1530 		rv = bp->b_error;
1531 	}
1532 	PRELE(p);
1533 	free(bp, M_TEMP);
1534 	return rv;
1535 }
1536 
1537 
1538 /*
1539  * Obtain a disklabel. Either a real one from the disk or, if there
1540  * is none, a fake one.
1541  */
1542 static void
1543 fdgetdisklabel(fd, dev)
1544 struct fd_softc *fd;
1545 dev_t		dev;
1546 {
1547 	struct disklabel	*lp;
1548 	struct cpu_disklabel	cpulab;
1549 
1550 	lp   = fd->sc_dk.dk_label;
1551 
1552 	bzero(lp, sizeof(*lp));
1553 	bzero(&cpulab, sizeof(cpulab));
1554 
1555 	lp->d_secpercyl  = fd->sc_type->seccyl;
1556 	lp->d_type       = DTYPE_FLOPPY;
1557 	lp->d_secsize    = FDC_BSIZE;
1558 	lp->d_secperunit = fd->sc_type->size;
1559 
1560 	/*
1561 	 * If there is no label on the disk: fake one
1562 	 */
1563 	if (readdisklabel(dev, fdstrategy, lp, &cpulab) != NULL)
1564 		fdgetdefaultlabel(fd, lp, RAW_PART);
1565 
1566 	if ((FDC_BSIZE * fd->sc_type->size)
1567 		< (lp->d_secsize * lp->d_secperunit)) {
1568 		/*
1569 		 * XXX: Ignore these fields. If you drop a vnddisk
1570 		 *	on more than one floppy, you'll get disturbing
1571 		 *	sounds!
1572 		 */
1573 		lp->d_secpercyl  = fd->sc_type->seccyl;
1574 		lp->d_type       = DTYPE_FLOPPY;
1575 		lp->d_secsize    = FDC_BSIZE;
1576 		lp->d_secperunit = fd->sc_type->size;
1577 	}
1578 }
1579 
1580 /*
1581  * Build defaultdisk label. For now we only create a label from what we
1582  * know from 'sc'.
1583  */
1584 static void
1585 fdgetdefaultlabel(fd, lp, part)
1586 	struct fd_softc  *fd;
1587 	struct disklabel *lp;
1588 	int part;
1589 {
1590 	bzero(lp, sizeof(struct disklabel));
1591 
1592 	lp->d_secsize     = 128 * (1 << fd->sc_type->secsize);
1593 	lp->d_ntracks     = fd->sc_type->heads;
1594 	lp->d_nsectors    = fd->sc_type->sectrac;
1595 	lp->d_secpercyl   = lp->d_ntracks * lp->d_nsectors;
1596 	lp->d_ncylinders  = fd->sc_type->size / lp->d_secpercyl;
1597 	lp->d_secperunit  = fd->sc_type->size;
1598 
1599 	lp->d_type        = DTYPE_FLOPPY;
1600 	lp->d_rpm         = 300; 	/* good guess I suppose.	*/
1601 	lp->d_interleave  = 1;		/* FIXME: is this OK?		*/
1602 	lp->d_bbsize      = 0;
1603 	lp->d_sbsize      = 0;
1604 	lp->d_npartitions = part + 1;
1605 	lp->d_trkseek     = 6000; 	/* Who cares...			*/
1606 	lp->d_magic       = DISKMAGIC;
1607 	lp->d_magic2      = DISKMAGIC;
1608 	lp->d_checksum    = dkcksum(lp);
1609 	lp->d_partitions[part].p_size   = lp->d_secperunit;
1610 	lp->d_partitions[part].p_fstype = FS_UNUSED;
1611 	lp->d_partitions[part].p_fsize  = 1024;
1612 	lp->d_partitions[part].p_frag   = 8;
1613 }
1614