xref: /netbsd-src/sys/arch/amiga/dev/fd.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
1 /*	$NetBSD: fd.c,v 1.14 1994/12/13 21:17:23 mycroft Exp $	*/
2 
3 /*
4  * Copyright (c) 1994 Christian E. Hopps
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Christian E. Hopps.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/buf.h>
37 #include <sys/device.h>
38 #include <sys/ioctl.h>
39 #include <sys/fcntl.h>
40 #include <sys/conf.h>
41 #include <sys/disklabel.h>
42 #include <sys/disk.h>
43 #include <sys/dkbad.h>
44 #include <amiga/amiga/device.h>
45 #include <amiga/amiga/custom.h>
46 #include <amiga/amiga/cia.h>
47 #include <amiga/amiga/cc.h>
48 
49 enum fdc_bits { FDB_CHANGED = 2, FDB_PROTECT, FDB_CYLZERO, FDB_READY };
50 /*
51  * partitions in fd represent different format floppies
52  * partition a is 0 etc..
53  */
54 enum fd_parttypes {
55 	FDAMIGAPART = 0,
56 #ifdef not_yet
57 	FDMSDOSPART,
58 #endif
59 	FDMAXPARTS
60 };
61 
62 #define FDBBSIZE	(8192)
63 #define FDSBSIZE	(8192)
64 
65 #define b_cylin	b_resid
66 #define FDUNIT(dev)	DISKUNIT(dev)
67 #define FDPART(dev)	DISKPART(dev)
68 #define FDMAKEDEV(m, u, p)	MAKEDISKDEV((m), (u), (p))
69 
70 #define FDNHEADS	(2)	/* amiga drives always have 2 heads */
71 #define FDSECSIZE	(512)	/* amiga drives always have 512 byte sectors */
72 #define FDSECLWORDS	(128)
73 
74 #define FDSETTLEDELAY	(18000)	/* usec delay after seeking after switch dir */
75 #define FDSTEPDELAY	(3500)	/* usec delay after steping */
76 #define FDPRESIDEDELAY	(1000)	/* usec delay before writing can occur */
77 #define FDWRITEDELAY	(1300)	/* usec delay after write */
78 
79 #define FDSTEPOUT	(1)	/* decrease track step */
80 #define FDSTEPIN	(0)	/* increase track step */
81 
82 #define FDCUNITMASK	(0x78)	/* mask for all units (bits 6-3) */
83 
84 #define FDRETRIES	(2)	/* default number of retries */
85 #define FDMAXUNITS	(4)	/* maximum number of supported units */
86 
87 #define DISKLEN_READ	(0)	/* fake mask for reading */
88 #define DISKLEN_WRITE	(1 << 14)	/* bit for writing */
89 #define DISKLEN_DMAEN	(1 << 15)	/* dma go */
90 #define DMABUFSZ ((DISKLEN_WRITE - 1) * 2)	/* largest dma possible */
91 
92 #define FDMFMSYNC	(0x4489)
93 
94 /*
95  * floppy device type
96  */
97 struct fdtype {
98 	u_int driveid;		/* drive identification (from drive) */
99 	u_int ncylinders;	/* number of cylinders on drive */
100 	u_int amiga_nsectors;	/* number of sectors per amiga track */
101 	u_int msdos_nsectors;	/* number of sectors per msdos track */
102 	u_int nreadw;		/* number of words (short) read per track */
103 	u_int nwritew;		/* number of words (short) written per track */
104 	u_int gap;		/* track gap size in long words */
105 	u_int precomp[2];	/* 1st and 2nd precomp values */
106 	char *desc;		/* description of drive type (useq) */
107 };
108 
109 /*
110  * floppy disk device data
111  */
112 struct fd_softc {
113 	struct dkdevice dkdev;
114 	struct buf bufq;	/* queue of buf's */
115 	struct fdtype *type;
116 	void *cachep;		/* cached track data (write through) */
117 	int cachetrk;		/* cahced track -1 for none */
118 	int hwunit;		/* unit for amiga controlling hw */
119 	int unitmask;		/* mask for cia select deslect */
120 	int pstepdir;		/* previous step direction */
121 	int curcyl;		/* current curcyl head positioned on */
122 	int flags;		/* misc flags */
123 	int wlabel;
124 	int stepdelay;		/* useq to delay after seek user setable */
125 	int nsectors;		/* number of sectors per track */
126 	int openpart;		/* which partition [ab] == [12] is open */
127 	short retries;		/* number of times to retry failed io */
128 	short retried;		/* number of times current io retried */
129 };
130 
131 /* fd_softc->flags */
132 #define FDF_MOTORON	(0x01)	/* motor is running */
133 #define FDF_MOTOROFF	(0x02)	/* motor is waiting to be turned off */
134 #define FDF_WMOTOROFF	(0x04)	/* unit wants a wakeup after off */
135 #define FDF_DIRTY	(0x08)	/* track cache needs write */
136 #define FDF_WRITEWAIT	(0x10)	/* need to head select delay on next setpos */
137 #define FDF_HAVELABEL	(0x20)	/* label is valid */
138 #define FDF_JUSTFLUSH	(0x40)	/* don't bother caching track. */
139 #define FDF_NOTRACK0	(0x80)	/* was not able to recalibrate drive */
140 
141 int fdc_wantwakeup;
142 int fdc_side;
143 void  *fdc_dmap;
144 struct fd_softc *fdc_indma;
145 
146 struct fdcargs {
147 	struct fdtype *type;
148 	int unit;
149 };
150 
151 int fdmatch __P((struct device *, struct cfdata *, void *));
152 int fdcmatch __P((struct device *, struct cfdata *, void *));
153 int fdcprint __P((void *, char *));
154 void fdcattach __P((struct device *, struct device *, void *));
155 void fdattach __P((struct device *, struct device *, void *));
156 
157 void fdstart __P((struct fd_softc *));
158 void fddone __P((struct fd_softc *));
159 void fdfindwork __P((int));
160 void fddmastart __P((struct fd_softc *, int));
161 void fddmadone __P((struct fd_softc *, int));
162 void fdsetpos __P((struct fd_softc *, int, int));
163 void fdmotoroff __P((void *));
164 void fdmotorwait __P((void *));
165 int fdminphys __P((struct buf *));
166 void fdcachetoraw __P((struct fd_softc *));
167 int fdrawtocache __P((struct fd_softc *));
168 int fdloaddisk __P((struct fd_softc *));
169 u_long *mfmblkencode __P((u_long *, u_long *, u_long *, int));
170 u_long *mfmblkdecode __P((u_long *, u_long *, u_long *, int));
171 struct fdtype * fdcgetfdtype __P((int));
172 
173 void fdstrategy __P((struct buf *));
174 
175 struct dkdriver fddkdriver = { fdstrategy };
176 
177 /*
178  * read size is (nsectors + 1) * mfm secsize + gap bytes + 2 shorts
179  * write size is nsectors * mfm secsize + gap bytes + 3 shorts
180  * the extra shorts are to deal with a dma hw bug in the controller
181  * they are probably too much (I belive the bug is 1 short on write and
182  * 3 bits on read) but there is no need to be cheap here.
183  */
184 #define MAXTRKSZ (22 * FDSECSIZE)
185 struct fdtype fdtype[] = {
186 	{ 0x00000000, 80, 11, 9, 7358, 6815, 414, { 80, 161 }, "3.5dd" },
187 	{ 0x55555555, 40, 11, 9, 7358, 6815, 414, { 80, 161 }, "5.25dd" },
188 	{ 0xAAAAAAAA, 80, 22, 18, 14716, 13630, 828, { 80, 161 }, "3.5hd" }
189 };
190 int nfdtype = sizeof(fdtype) / sizeof(*fdtype);
191 
192 struct cfdriver fdcd = {
193 	NULL, "fd", (cfmatch_t)fdmatch, fdattach, DV_DISK,
194 	sizeof(struct fd_softc), NULL, 0 };
195 
196 struct cfdriver fdccd = {
197 	NULL, "fdc", (cfmatch_t)fdcmatch, fdcattach, DV_DULL,
198 	sizeof(struct device), NULL, 0 };
199 
200 /*
201  * all hw access through macros, this helps to hide the active low
202  * properties
203  */
204 
205 #define FDUNITMASK(unit)	(1 << (3 + (unit)))
206 
207 /*
208  * select units using mask
209  */
210 #define FDSELECT(um)	do { ciab.prb &= ~(um); } while (0)
211 
212 /*
213  * deselect units using mask
214  */
215 #define FDDESELECT(um)	do { ciab.prb |= (um); delay(1); } while (0)
216 
217 /*
218  * test hw condition bits
219  */
220 #define FDTESTC(bit)	((ciaa.pra & (1 << (bit))) == 0)
221 
222 /*
223  * set motor for select units, true motor on else off
224  */
225 #define FDSETMOTOR(on)	do { \
226 	if (on) ciab.prb &= ~CIAB_PRB_MTR; else ciab.prb |= CIAB_PRB_MTR; \
227 	} while (0)
228 
229 /*
230  * set head for select units
231  */
232 #define FDSETHEAD(head)	do { \
233 	if (head) ciab.prb &= ~CIAB_PRB_SIDE; else ciab.prb |= CIAB_PRB_SIDE; \
234 	delay(1); } while (0)
235 
236 /*
237  * select direction, true towards spindle else outwards
238  */
239 #define FDSETDIR(in)	do { \
240 	if (in) ciab.prb &= ~CIAB_PRB_DIR; else ciab.prb |= CIAB_PRB_DIR; \
241 	delay(1); } while (0)
242 
243 /*
244  * step the selected units
245  */
246 #define FDSTEP	do { \
247     ciab.prb &= ~CIAB_PRB_STEP; ciab.prb |= CIAB_PRB_STEP; \
248     } while (0)
249 
250 #define FDDMASTART(len, towrite)	do { \
251     int dmasz = (len) | ((towrite) ? DISKLEN_WRITE : 0) | DISKLEN_DMAEN; \
252     custom.dsklen = dmasz; custom.dsklen = dmasz; } while (0)
253 
254 #define FDDMASTOP	do { custom.dsklen = 0; } while (0)
255 
256 
257 int
258 fdcmatch(pdp, cfp, auxp)
259 	struct device *pdp;
260 	struct cfdata *cfp;
261 	void *auxp;
262 {
263 	if (matchname("fdc", auxp) == 0 || cfp->cf_unit != 0)
264 		return(0);
265 	if ((fdc_dmap = alloc_chipmem(DMABUFSZ)) == NULL) {
266 		printf("fdc: unable to allocate dma buffer\n");
267 		return(0);
268 	}
269 	return(1);
270 }
271 
272 void
273 fdcattach(pdp, dp, auxp)
274 	struct device *pdp, *dp;
275 	void *auxp;
276 {
277 	struct fdcargs args;
278 
279 	printf(": dmabuf pa 0x%x\n", kvtop(fdc_dmap));
280 	args.unit = 0;
281 	args.type = fdcgetfdtype(args.unit);
282 
283 	fdc_side = -1;
284 	config_found(dp, &args, fdcprint);
285 	for (args.unit++; args.unit < FDMAXUNITS; args.unit++) {
286 		if ((args.type = fdcgetfdtype(args.unit)) == NULL)
287 			continue;
288 		config_found(dp, &args, fdcprint);
289 	}
290 }
291 
292 int
293 fdcprint(auxp, pnp)
294 	void *auxp;
295 	char *pnp;
296 {
297 	return(UNCONF);
298 }
299 
300 /*ARGSUSED*/
301 int
302 fdmatch(pdp, cfp, auxp)
303 	struct device *pdp;
304 	struct cfdata *cfp;
305 	void *auxp;
306 {
307 #define cf_unit	cf_loc[0]
308 	struct fdcargs *fdap;
309 
310 	fdap = auxp;
311 	if (cfp->cf_unit == fdap->unit || cfp->cf_unit == -1)
312 		return(1);
313 	return(0);
314 #undef cf_unit
315 }
316 
317 void
318 fdattach(pdp, dp, auxp)
319 	struct device *pdp, *dp;
320 	void *auxp;
321 {
322 	struct fdcargs *ap;
323 	struct fd_softc *sc;
324 
325 	ap = auxp;
326 	sc = (struct fd_softc *)dp;
327 
328 	sc->curcyl = sc->cachetrk = -1;
329 	sc->openpart = -1;
330 	sc->type = ap->type;
331 	sc->hwunit = ap->unit;
332 	sc->unitmask = 1 << (3 + ap->unit);
333 	sc->retries = FDRETRIES;
334 	sc->dkdev.dk_driver = &fddkdriver;
335 	sc->stepdelay = FDSTEPDELAY;
336 	printf(": %s %d cyl, %d head, %d sec [%d sec], 512 bytes/sec\n",
337 	    sc->type->desc, sc->type->ncylinders, FDNHEADS,
338 	    sc->type->amiga_nsectors, sc->type->msdos_nsectors);
339 
340 	/*
341 	 * calibrate the drive
342 	 */
343 	fdsetpos(sc, 0, 0);
344 	fdsetpos(sc, sc->type->ncylinders, 0);
345 	fdsetpos(sc, 0, 0);
346 	fdmotoroff(sc);
347 
348 	/*
349 	 * enable disk related interrupts
350 	 */
351 	custom.dmacon = DMAF_SETCLR | DMAF_DISK;
352 	/* XXX why softint */
353 	custom.intena = INTF_SETCLR |INTF_SOFTINT | INTF_DSKBLK;
354 	ciaa.icr = CIA_ICR_IR_SC | CIA_ICR_FLG;
355 }
356 
357 /*ARGSUSED*/
358 int
359 Fdopen(dev, flags, devtype, p)
360 	dev_t dev;
361 	int flags, devtype;
362 	struct proc *p;
363 {
364 	struct fd_softc *sc;
365 	int wasopen, fwork, error, s;
366 
367 	error = 0;
368 
369 	if (FDPART(dev) >= FDMAXPARTS)
370 		return(ENXIO);
371 
372 	if ((sc = getsoftc(fdcd, FDUNIT(dev))) == NULL)
373 		return(ENXIO);
374 	if (sc->flags & FDF_NOTRACK0)
375 		return(ENXIO);
376 	if (sc->cachep == NULL)
377 		sc->cachep = malloc(MAXTRKSZ, M_DEVBUF, M_WAITOK);
378 
379 	s = splbio();
380 	/*
381 	 * if we are sleeping in Fdclose(); waiting for a chance to
382 	 * shut the motor off, do a sleep here also.
383 	 */
384 	while (sc->flags & FDF_WMOTOROFF)
385 		tsleep(fdmotoroff, PRIBIO, "Fdopen", 0);
386 
387 	fwork = 0;
388 	/*
389 	 * if not open let user open request type, otherwise
390 	 * ensure they are trying to open same type.
391 	 */
392 	if (sc->openpart == FDPART(dev))
393 		wasopen = 1;
394 	else if (sc->openpart == -1) {
395 		sc->openpart = FDPART(dev);
396 		wasopen = 0;
397 	} else {
398 		wasopen = 1;
399 		error = EPERM;
400 		goto done;
401 	}
402 
403 	/*
404 	 * wait for current io to complete if any
405 	 */
406 	if (fdc_indma) {
407 		fwork = 1;
408 		fdc_wantwakeup++;
409 		tsleep(Fdopen, PRIBIO, "Fdopen", 0);
410 	}
411 	if (error = fdloaddisk(sc))
412 		goto done;
413 	if (error = fdgetdisklabel(sc, dev))
414 		goto done;
415 #ifdef FDDEBUG
416 	printf("  open successful\n");
417 #endif
418 done:
419 	/*
420 	 * if we requested that fddone()->fdfindwork() wake us, allow it to
421 	 * complete its job now
422 	 */
423 	if (fwork)
424 		fdfindwork(FDUNIT(dev));
425 	splx(s);
426 
427 	/*
428 	 * if we were not open and we marked us so reverse that.
429 	 */
430 	if (error && wasopen == 0)
431 		sc->openpart = 0;
432 	return(error);
433 }
434 
435 /*ARGSUSED*/
436 int
437 Fdclose(dev, flags, devtype, p)
438 	dev_t dev;
439 	int flags, devtype;
440 	struct proc *p;
441 {
442 	struct fd_softc *sc;
443 	int s;
444 
445 #ifdef FDDEBUG
446 	printf("Fdclose()\n");
447 #endif
448 	sc = getsoftc(fdcd, FDUNIT(dev));
449 	s = splbio();
450 	if (sc->flags & FDF_MOTORON) {
451 		sc->flags |= FDF_WMOTOROFF;
452 		tsleep(fdmotoroff, PRIBIO, "Fdclose", 0);
453 		sc->flags &= ~FDF_WMOTOROFF;
454 		wakeup(fdmotoroff);
455 	}
456 	sc->openpart = -1;
457 	splx(s);
458 	return(0);
459 }
460 
461 int
462 fdioctl(dev, cmd, addr, flag, p)
463 	dev_t dev;
464 	u_long cmd;
465 	caddr_t addr;
466 	int flag;
467 	struct proc *p;
468 {
469 	struct fd_softc *sc;
470 	void *data;
471 	int error, wlab;
472 
473 	sc = getsoftc(fdcd, FDUNIT(dev));
474 
475 	if ((sc->flags & FDF_HAVELABEL) == 0)
476 		return(EBADF);
477 
478 	switch (cmd) {
479 	case DIOCSBAD:
480 		return(EINVAL);
481 	case DIOCSRETRIES:
482 		if (*(int *)addr < 0)
483 			return(EINVAL);
484 		sc->retries = *(int *)addr;
485 		return(0);
486 	case DIOCSSTEP:
487 		if (*(int *)addr < FDSTEPDELAY)
488 			return(EINVAL);
489 		sc->dkdev.dk_label.d_trkseek = sc->stepdelay = *(int *)addr;
490 		return(0);
491 	case DIOCGDINFO:
492 		*(struct disklabel *)addr = sc->dkdev.dk_label;
493 		return(0);
494 	case DIOCGPART:
495 		((struct partinfo *)addr)->disklab = &sc->dkdev.dk_label;
496 		((struct partinfo *)addr)->part =
497 		    &sc->dkdev.dk_label.d_partitions[FDPART(dev)];
498 		return(0);
499 	case DIOCSDINFO:
500 		if ((flag & FWRITE) == 0)
501 			return(EBADF);
502 		return(fdsetdisklabel(sc, (struct disklabel *)addr));
503 	case DIOCWDINFO:
504 		if ((flag & FWRITE) == 0)
505 			return(EBADF);
506 		if (error = fdsetdisklabel(sc, (struct disklabel *)addr))
507 			return(error);
508 		wlab = sc->wlabel;
509 		sc->wlabel = 1;
510 		error = fdputdisklabel(sc, dev);
511 		sc->wlabel = wlab;
512 		return(error);
513 	case DIOCWLABEL:
514 		if ((flag & FWRITE) == 0)
515 			return(EBADF);
516 		sc->wlabel = *(int *)addr;
517 		return(0);
518 	default:
519 		return(ENOTTY);
520 	}
521 }
522 
523 /*
524  * no dumps to floppy disks thank you.
525  */
526 int
527 fdsize(dev)
528 	dev_t dev;
529 {
530 	return(-1);
531 }
532 
533 int
534 fdread(dev, uio)
535 	dev_t dev;
536 	struct uio *uio;
537 {
538 	return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL,
539 	    dev, B_READ, fdminphys, uio));
540 }
541 
542 int
543 fdwrite(dev, uio)
544 	dev_t dev;
545 	struct uio *uio;
546 {
547 	return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL,
548 	    dev, B_WRITE, fdminphys, uio));
549 }
550 
551 
552 int
553 fdintr()
554 {
555 	int s;
556 
557 	s = splbio();
558 	if (fdc_indma)
559 		fddmadone(fdc_indma, 0);
560 	splx(s);
561 }
562 
563 void
564 fdstrategy(bp)
565 	struct buf *bp;
566 {
567 	struct disklabel *lp;
568 	struct fd_softc *sc;
569 	struct buf *dp;
570 	int unit, part, s;
571 
572 	unit = FDUNIT(bp->b_dev);
573 	part = FDPART(bp->b_dev);
574 	sc = getsoftc(fdcd, unit);
575 
576 #ifdef FDDEBUG
577 	printf("fdstrategy: 0x%x\n", bp);
578 #endif
579 	/*
580 	 * check for valid partition and bounds
581 	 */
582 	lp = &sc->dkdev.dk_label;
583 	if ((sc->flags & FDF_HAVELABEL) == 0) {
584 		bp->b_error = EIO;
585 		goto bad;
586 	}
587 	if (bounds_check_with_label(bp, lp, sc->wlabel) <= 0)
588 		goto done;
589 
590 	/*
591 	 * trans count of zero or bounds check indicates io is done
592 	 * we are done.
593 	 */
594 	if (bp->b_bcount == 0)
595 		goto done;
596 
597 	/*
598 	 * queue the buf and kick the low level code
599 	 */
600 	s = splbio();
601 	dp = &sc->bufq;
602 	disksort(dp, bp);
603 	fdstart(sc);
604 	splx(s);
605 	return;
606 bad:
607 	bp->b_flags |= B_ERROR;
608 done:
609 	bp->b_resid = bp->b_bcount;
610 	biodone(bp);
611 }
612 
613 /*
614  * make sure disk is loaded and label is up-to-date.
615  */
616 int
617 fdloaddisk(sc)
618 	struct fd_softc *sc;
619 {
620 	/*
621 	 * if diskchange is low step drive to 0 then up one then to zero.
622 	 */
623 	fdsetpos(sc, 0, 0);
624 	if (FDTESTC(FDB_CHANGED)) {
625 		sc->cachetrk = -1;		/* invalidate the cache */
626 		sc->flags &= ~FDF_HAVELABEL;
627 		fdsetpos(sc, FDNHEADS, 0);
628 		fdsetpos(sc, 0, 0);
629 		if (FDTESTC(FDB_CHANGED)) {
630 			fdmotoroff(sc);
631 			return(ENXIO);
632 		}
633 	}
634 	fdmotoroff(sc);
635 	sc->type = fdcgetfdtype(sc->hwunit);
636 	if (sc->type == NULL)
637 		return(ENXIO);
638 #ifdef not_yet
639 	if (sc->openpart == FDMSDOSPART)
640 		sc->nsectors = sc->type->msdos_nsectors;
641 	else
642 #endif
643 		sc->nsectors = sc->type->amiga_nsectors;
644 	return(0);
645 }
646 
647 /*
648  * read disk label, if present otherwise create one
649  * return a new label if raw part and none found, otherwise err.
650  */
651 int
652 fdgetdisklabel(sc, dev)
653 	struct fd_softc *sc;
654 	dev_t dev;
655 {
656 	struct disklabel *lp, *dlp;
657 	struct cpu_disklabel *clp;
658 	struct buf *bp;
659 	int error, part;
660 
661 	if (sc->flags & FDF_HAVELABEL)
662 		return(0);
663 #ifdef FDDEBUG
664 	printf("fdgetdisklabel()\n");
665 #endif
666 	part = FDPART(dev);
667 	lp = &sc->dkdev.dk_label;
668 	clp =  &sc->dkdev.dk_cpulabel;
669 	bzero(lp, sizeof(struct disklabel));
670 	bzero(clp, sizeof(struct cpu_disklabel));
671 
672 	lp->d_secsize = FDSECSIZE;
673 	lp->d_ntracks = FDNHEADS;
674 	lp->d_ncylinders = sc->type->ncylinders;
675 	lp->d_nsectors = sc->nsectors;
676 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
677 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
678 	lp->d_npartitions = part + 1;
679 	lp->d_partitions[part].p_size = lp->d_secperunit;
680 	lp->d_partitions[part].p_fstype = FS_UNUSED;
681 	lp->d_partitions[part].p_fsize = 1024;
682 	lp->d_partitions[part].p_frag = 8;
683 
684 	sc->flags |= FDF_HAVELABEL;
685 
686 	bp = (void *)geteblk((int)lp->d_secsize);
687 	bp->b_dev = dev;
688 	bp->b_blkno = 0;
689 	bp->b_cylin = 0;
690 	bp->b_bcount = FDSECSIZE;
691 	bp->b_flags = B_BUSY | B_READ;
692 	fdstrategy(bp);
693 	if (error = biowait(bp))
694 		goto nolabel;
695 	dlp = (struct disklabel *)(bp->b_data + LABELOFFSET);
696 	if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC ||
697 	    dkcksum(dlp)) {
698 		error = EINVAL;
699 		goto nolabel;
700 	}
701 	bcopy(dlp, lp, sizeof(struct disklabel));
702 	if (lp->d_trkseek > FDSTEPDELAY)
703 		sc->stepdelay = lp->d_trkseek;
704 	brelse(bp);
705 	return(0);
706 nolabel:
707 	bzero(lp, sizeof(struct disklabel));
708 	lp->d_secsize = FDSECSIZE;
709 	lp->d_ntracks = FDNHEADS;
710 	lp->d_ncylinders = sc->type->ncylinders;
711 	lp->d_nsectors = sc->nsectors;
712 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
713 	lp->d_type = DTYPE_FLOPPY;
714 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
715 	lp->d_rpm = 300; 		/* good guess I suppose. */
716 	lp->d_interleave = 1;		/* should change when adding msdos */
717 	sc->stepdelay = lp->d_trkseek = FDSTEPDELAY;
718 	lp->d_bbsize = 0;
719 	lp->d_sbsize = 0;
720 	lp->d_partitions[part].p_size = lp->d_secperunit;
721 	lp->d_partitions[part].p_fstype = FS_UNUSED;
722 	lp->d_partitions[part].p_fsize = 1024;
723 	lp->d_partitions[part].p_frag = 8;
724 	lp->d_npartitions = part + 1;
725 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
726 	lp->d_checksum = dkcksum(lp);
727 	brelse(bp);
728 	return(0);
729 }
730 
731 /*
732  * set the incore copy of this units disklabel
733  */
734 int
735 fdsetdisklabel(sc, lp)
736 	struct fd_softc *sc;
737 	struct disklabel *lp;
738 {
739 	struct disklabel *clp;
740 	struct partition *pp;
741 
742 	/*
743 	 * must have at least opened raw unit to fetch the
744 	 * raw_part stuff.
745 	 */
746 	if ((sc->flags & FDF_HAVELABEL) == 0)
747 		return(EINVAL);
748 	clp = &sc->dkdev.dk_label;
749 	/*
750 	 * make sure things check out and we only have one valid
751 	 * partition
752 	 */
753 #ifdef FDDEBUG
754 	printf("fdsetdisklabel\n");
755 #endif
756 	if (lp->d_secsize != FDSECSIZE ||
757 	    lp->d_nsectors != clp->d_nsectors ||
758 	    lp->d_ntracks != FDNHEADS ||
759 	    lp->d_ncylinders != clp->d_ncylinders ||
760 	    lp->d_secpercyl != clp->d_secpercyl ||
761 	    lp->d_secperunit != clp->d_secperunit ||
762 	    lp->d_magic != DISKMAGIC ||
763 	    lp->d_magic2 != DISKMAGIC ||
764 	    lp->d_npartitions == 0 ||
765 	    lp->d_npartitions > FDMAXPARTS ||
766 	    (lp->d_partitions[0].p_offset && lp->d_partitions[1].p_offset) ||
767 	    dkcksum(lp))
768 		return(EINVAL);
769 	/*
770 	 * if any partitions are present make sure they
771 	 * represent the currently open type
772 	 */
773 	if ((pp = &lp->d_partitions[0])->p_size) {
774 		if ((pp = &lp->d_partitions[1])->p_size == 0)
775 			goto done;
776 		else if (sc->openpart != 1)
777 			return(EINVAL);
778 	} else if (sc->openpart != 0)
779 		return(EINVAL);
780 	/*
781 	 * make sure selected partition is within bounds
782 	 * XXX on the second check, its to handle a bug in
783 	 * XXX the cluster routines as they require mutliples
784 	 * XXX of CLBYTES currently
785 	 */
786 	if ((pp->p_offset + pp->p_size >= lp->d_secperunit) ||
787 	    (pp->p_frag * pp->p_fsize % CLBYTES))
788 		return(EINVAL);
789 done:
790 	bcopy(lp, clp, sizeof(struct disklabel));
791 	return(0);
792 }
793 
794 /*
795  * write out the incore copy of this units disklabel
796  */
797 int
798 fdputdisklabel(sc, dev)
799 	struct fd_softc *sc;
800 	dev_t dev;
801 {
802 	struct disklabel *lp, *dlp;
803 	struct buf *bp;
804 	int error;
805 
806 	if ((sc->flags & FDF_HAVELABEL) == 0)
807 		return(EBADF);
808 #ifdef FDDEBUG
809 	printf("fdputdisklabel\n");
810 #endif
811 	/*
812 	 * get buf and read in sector 0
813 	 */
814 	lp = &sc->dkdev.dk_label;
815 	bp = (void *)geteblk((int)lp->d_secsize);
816 	bp->b_dev = FDMAKEDEV(major(dev), FDUNIT(dev), RAW_PART);
817 	bp->b_blkno = 0;
818 	bp->b_cylin = 0;
819 	bp->b_bcount = FDSECSIZE;
820 	bp->b_flags = B_BUSY | B_READ;
821 	fdstrategy(bp);
822 	if (error = biowait(bp))
823 		goto done;
824 	/*
825 	 * copy disklabel to buf and write it out syncronous
826 	 */
827 	dlp = (struct disklabel *)(bp->b_data + LABELOFFSET);
828 	bcopy(lp, dlp, sizeof(struct disklabel));
829 	bp->b_blkno = 0;
830 	bp->b_cylin = 0;
831 	bp->b_flags = B_WRITE;
832 	fdstrategy(bp);
833 	error = biowait(bp);
834 done:
835 	brelse(bp);
836 	return(error);
837 }
838 
839 /*
840  * figure out drive type or NULL if none.
841  */
842 struct fdtype *
843 fdcgetfdtype(unit)
844 	int unit;
845 {
846 	struct fdtype *ftp;
847 	u_long id, idb;
848 	int cnt, umask;
849 
850 	id = 0;
851 	umask = 1 << (3 + unit);
852 
853 	FDDESELECT(FDCUNITMASK);
854 
855 	FDSETMOTOR(1);
856 	delay(1);
857 	FDSELECT(umask);
858 	delay(1);
859 	FDDESELECT(umask);
860 
861 	FDSETMOTOR(0);
862 	delay(1);
863 	FDSELECT(umask);
864 	delay(1);
865 	FDDESELECT(umask);
866 
867 	for (idb = 0x80000000; idb; idb >>= 1) {
868 		FDSELECT(umask);
869 		delay(1);
870 		if (FDTESTC(FDB_READY) == 0)
871 			id |= idb;
872 		FDDESELECT(umask);
873 		delay(1);
874 	}
875 #ifdef FDDEBUG
876 	printf("fdcgettype unit %d id 0x%x\n", unit, id);
877 #endif
878 
879 	for (cnt = 0, ftp = fdtype; cnt < nfdtype; ftp++, cnt++)
880 		if (ftp->driveid == id)
881 			return(ftp);
882 	/*
883 	 * 3.5dd's at unit 0 do not always return id.
884 	 */
885 	if (unit == 0)
886 		return(fdtype);
887 	return(NULL);
888 }
889 
890 /*
891  * turn motor off if possible otherwise mark as needed and will be done
892  * later.
893  */
894 void
895 fdmotoroff(arg)
896 	void *arg;
897 {
898 	struct fd_softc *sc;
899 	int unitmask, s;
900 
901 	sc = arg;
902 	s = splbio();
903 
904 #ifdef FDDEBUG
905 	printf("fdmotoroff: unit %d\n", sc->hwunit);
906 #endif
907 	if ((sc->flags & FDF_MOTORON) == 0)
908 		goto done;
909 	/*
910 	 * if we have a timeout on a dma operation let fddmadone()
911 	 * deal with it.
912 	 */
913 	if (fdc_indma == sc) {
914 		fddmadone(sc, 1);
915 		goto done;
916 	}
917 #ifdef FDDEBUG
918 	printf(" motor was on, turning off\n");
919 #endif
920 
921 	/*
922 	 * flush cache if needed
923 	 */
924 	if (sc->flags & FDF_DIRTY) {
925 		sc->flags |= FDF_JUSTFLUSH | FDF_MOTOROFF;
926 #ifdef FDDEBUG
927 		printf("  flushing dirty buffer first\n");
928 #endif
929 		/*
930 		 * if dma'ing done for now, fddone() will call us again
931 		 */
932 		if (fdc_indma)
933 			goto done;
934 		fddmastart(sc, sc->cachetrk);
935 		goto done;
936 	}
937 
938 	/*
939 	 * if controller is busy just schedule us to be called back
940 	 */
941 	if (fdc_indma) {
942 		/*
943 		 * someone else has the controller now
944 		 * just set flag and let fddone() call us again.
945 		 */
946 		sc->flags |= FDF_MOTOROFF;
947 		goto done;
948 	}
949 
950 #ifdef FDDEBUG
951 	printf("  hw turing unit off\n");
952 #endif
953 
954 	sc->flags &= ~(FDF_MOTORON | FDF_MOTOROFF);
955 	FDDESELECT(FDCUNITMASK);
956 	FDSETMOTOR(0);
957 	delay(1);
958 	FDSELECT(sc->unitmask);
959 	delay(4);
960 	FDDESELECT(sc->unitmask);
961 	delay(1);
962 	if (sc->flags & FDF_WMOTOROFF)
963 		wakeup(fdmotoroff);
964 done:
965 	splx(s);
966 }
967 
968 /*
969  * select drive seek to track exit with motor on.
970  * fdsetpos(x, 0, 0) does calibrates the drive.
971  */
972 void
973 fdsetpos(sc, trk, towrite)
974 	struct fd_softc *sc;
975 	int trk, towrite;
976 {
977 	int nstep, sdir, ondly, ncyl, nside;
978 
979 	FDDESELECT(FDCUNITMASK);
980 	FDSETMOTOR(1);
981 	delay(1);
982 	FDSELECT(sc->unitmask);
983 	delay(1);
984 	if ((sc->flags & FDF_MOTORON) == 0) {
985 		ondly = 0;
986 		while (FDTESTC(FDB_READY) == 0) {
987 			delay(1000);
988 			if (++ondly >= 1000)
989 				break;
990 		}
991 	}
992 	sc->flags |= FDF_MOTORON;
993 
994 	ncyl = trk / FDNHEADS;
995 	nside = trk % FDNHEADS;
996 
997 	if (sc->curcyl == ncyl && fdc_side == nside)
998 		return;
999 
1000 	if (towrite)
1001 		sc->flags |= FDF_WRITEWAIT;
1002 
1003 #ifdef FDDEBUG
1004 	printf("fdsetpos: cyl %d head %d towrite %d\n", trk / FDNHEADS,
1005 	    trk % FDNHEADS, towrite);
1006 #endif
1007 	nstep = ncyl - sc->curcyl;
1008 	if (nstep) {
1009 		/*
1010 		 * figure direction
1011 		 */
1012 		if (nstep > 0 && ncyl != 0) {
1013 			sdir = FDSTEPIN;
1014 			FDSETDIR(1);
1015 		} else {
1016 			nstep = -nstep;
1017 			sdir = FDSTEPOUT;
1018 			FDSETDIR(0);
1019 		}
1020 		if (ncyl == 0) {
1021 			/*
1022 			 * either just want cylinder 0 or doing
1023 			 * a calibrate.
1024 			 */
1025 			nstep = 256;
1026 			while (FDTESTC(FDB_CYLZERO) == 0 && nstep--) {
1027 				FDSTEP;
1028 				delay(sc->stepdelay);
1029 			}
1030 			if (nstep < 0)
1031 				sc->flags |= FDF_NOTRACK0;
1032 		} else {
1033 			/*
1034 			 * step the needed amount amount.
1035 			 */
1036 			while (nstep--) {
1037 				FDSTEP;
1038 				delay(sc->stepdelay);
1039 			}
1040 		}
1041 		/*
1042 		 * if switched directions
1043 		 * allow drive to settle.
1044 		 */
1045 		if (sc->pstepdir != sdir)
1046 			delay(FDSETTLEDELAY);
1047 		sc->pstepdir = sdir;
1048 		sc->curcyl = ncyl;
1049 	}
1050 	if (nside == fdc_side)
1051 		return;
1052 	/*
1053 	 * select side
1054 	 */
1055 	fdc_side = nside;
1056 	FDSETHEAD(nside);
1057 	delay(FDPRESIDEDELAY);
1058 }
1059 
1060 void
1061 fdselunit(sc)
1062 	struct fd_softc *sc;
1063 {
1064 	FDDESELECT(FDCUNITMASK);		/* deselect all */
1065 	FDSETMOTOR(sc->flags & FDF_MOTORON);	/* set motor to unit's state */
1066 	delay(1);
1067 	FDSELECT(sc->unitmask);			/* select unit */
1068 	delay(1);
1069 }
1070 
1071 /*
1072  * process next buf on device queue.
1073  * normall sequence of events:
1074  * fdstart() -> fddmastart();
1075  * fdintr() -> fddmadone() -> fddone();
1076  * if the track is in the cache then fdstart() will short-circuit
1077  * to fddone() else if the track cache is dirty it will flush.  If
1078  * the buf is not an entire track it will cache the requested track.
1079  */
1080 void
1081 fdstart(sc)
1082 	struct fd_softc *sc;
1083 {
1084 	int trk, error, write;
1085 	struct buf *bp, *dp;
1086 
1087 #ifdef FDDEBUG
1088 	printf("fdstart: unit %d\n", sc->hwunit);
1089 #endif
1090 
1091 	/*
1092 	 * if dma'ing just return. we must have been called from fdstartegy.
1093 	 */
1094 	if (fdc_indma)
1095 		return;
1096 
1097 	/*
1098 	 * get next buf if there.
1099 	 */
1100 	dp = &sc->bufq;
1101 	if ((bp = dp->b_actf) == NULL) {
1102 #ifdef FDDEBUG
1103 		printf("  nothing to do\n");
1104 #endif
1105 		return;
1106 	}
1107 
1108 	/*
1109 	 * make sure same disk is loaded
1110 	 */
1111 	fdselunit(sc);
1112 	if (FDTESTC(FDB_CHANGED)) {
1113 		/*
1114 		 * disk missing, invalidate all future io on
1115 		 * this unit until re-open()'ed also invalidate
1116 		 * all current io
1117 		 */
1118 #ifdef FDDEBUG
1119 		printf("  disk was removed invalidating all io\n");
1120 #endif
1121 		sc->flags &= ~FDF_HAVELABEL;
1122 		for (;;) {
1123 			bp->b_flags |= B_ERROR;
1124 			bp->b_error = EIO;
1125 			if (bp->b_actf == NULL)
1126 				break;
1127 			biodone(bp);
1128 			bp = bp->b_actf;
1129 		}
1130 		/*
1131 		 * do fddone() on last buf to allow other units to start.
1132 		 */
1133 		dp->b_actf = bp;
1134 		fddone(sc);
1135 		return;
1136 	}
1137 
1138 	/*
1139 	 * we have a valid buf, setup our local version
1140 	 * we use this count to allow reading over multiple tracks.
1141 	 * into a single buffer
1142 	 */
1143 	dp->b_bcount = bp->b_bcount;
1144 	dp->b_blkno = bp->b_blkno;
1145 	dp->b_data = bp->b_data;
1146 	dp->b_flags = bp->b_flags;
1147 	dp->b_resid = 0;
1148 
1149 	if (bp->b_flags & B_READ)
1150 		write = 0;
1151 	else if (FDTESTC(FDB_PROTECT) == 0)
1152 		write = 1;
1153 	else {
1154 		error = EPERM;
1155 		goto bad;
1156 	}
1157 
1158 	/*
1159 	 * figure trk given blkno
1160 	 */
1161 	trk = bp->b_blkno / sc->nsectors;
1162 
1163 	/*
1164 	 * check to see if same as currently cached track
1165 	 * if so we need to do no dma read.
1166 	 */
1167 	if (trk == sc->cachetrk) {
1168 		fddone(sc);
1169 		return;
1170 	}
1171 
1172 	/*
1173 	 * if we will be overwriting the entire cache, don't bother to
1174 	 * fetch it.
1175 	 */
1176 	if (bp->b_bcount == (sc->nsectors * FDSECSIZE) && write &&
1177 	    bp->b_blkno % sc->nsectors == 0) {
1178 		if (sc->flags & FDF_DIRTY)
1179 			sc->flags |= FDF_JUSTFLUSH;
1180 		else {
1181 			sc->cachetrk = trk;
1182 			fddone(sc);
1183 			return;
1184 		}
1185 	}
1186 
1187 	/*
1188 	 * start dma read of `trk'
1189 	 */
1190 	fddmastart(sc, trk);
1191 	return;
1192 bad:
1193 	bp->b_flags |= B_ERROR;
1194 	bp->b_error = error;
1195 	fddone(sc);
1196 }
1197 
1198 /*
1199  * continue a started operation on next track. always begin at
1200  * sector 0 on the next track.
1201  */
1202 void
1203 fdcont(sc)
1204 	struct fd_softc *sc;
1205 {
1206 	struct buf *dp, *bp;
1207 	int trk, write;
1208 
1209 	dp = &sc->bufq;
1210 	bp = dp->b_actf;
1211 	dp->b_data += (dp->b_bcount - bp->b_resid);
1212 	dp->b_blkno += (dp->b_bcount - bp->b_resid) / FDSECSIZE;
1213 	dp->b_bcount = bp->b_resid;
1214 
1215 	/*
1216 	 * figure trk given blkno
1217 	 */
1218 	trk = dp->b_blkno / sc->nsectors;
1219 #ifdef DEBUG
1220 	if (trk != sc->cachetrk + 1 || dp->b_blkno % sc->nsectors != 0)
1221 		panic("fdcont: confused");
1222 #endif
1223 	if (dp->b_flags & B_READ)
1224 		write = 0;
1225 	else
1226 		write = 1;
1227 	/*
1228 	 * if we will be overwriting the entire cache, don't bother to
1229 	 * fetch it.
1230 	 */
1231 	if (dp->b_bcount == (sc->nsectors * FDSECSIZE) && write) {
1232 		if (sc->flags & FDF_DIRTY)
1233 			sc->flags |= FDF_JUSTFLUSH;
1234 		else {
1235 			sc->cachetrk = trk;
1236 			fddone(sc);
1237 			return;
1238 		}
1239 	}
1240 	/*
1241 	 * start dma read of `trk'
1242 	 */
1243 	fddmastart(sc, trk);
1244 	return;
1245 }
1246 
1247 void
1248 fddmastart(sc, trk)
1249 	struct fd_softc *sc;
1250 	int trk;
1251 {
1252 	int adkmask, ndmaw, write, dmatrk;
1253 
1254 #ifdef FDDEBUG
1255 	printf("fddmastart: unit %d cyl %d head %d", sc->hwunit,
1256 	    trk / FDNHEADS, trk % FDNHEADS);
1257 #endif
1258 	/*
1259 	 * flush the cached track if dirty else read requested track.
1260 	 */
1261 	if (sc->flags & FDF_DIRTY) {
1262 		fdcachetoraw(sc);
1263 		ndmaw = sc->type->nwritew;
1264 		dmatrk = sc->cachetrk;
1265 		write = 1;
1266 	} else {
1267 		ndmaw = sc->type->nreadw;
1268 		dmatrk = trk;
1269 		write = 0;
1270 	}
1271 
1272 #ifdef FDDEBUG
1273 	printf(" %s", write ? " flushing cache\n" : " loading cache\n");
1274 #endif
1275 	sc->cachetrk = trk;
1276 	fdc_indma = sc;
1277 	fdsetpos(sc, dmatrk, write);
1278 
1279 	/*
1280 	 * setup dma stuff
1281 	 */
1282 	if (write == 0) {
1283 		custom.adkcon = ADKF_MSBSYNC;
1284 		custom.adkcon = ADKF_SETCLR | ADKF_WORDSYNC | ADKF_FAST;
1285 		custom.dsksync = FDMFMSYNC;
1286 	} else {
1287 		custom.adkcon = ADKF_PRECOMP1 | ADKF_PRECOMP0 | ADKF_WORDSYNC |
1288 		    ADKF_MSBSYNC;
1289 		adkmask = ADKF_SETCLR | ADKF_FAST | ADKF_MFMPREC;
1290 		if (dmatrk >= sc->type->precomp[0])
1291 			adkmask |= ADKF_PRECOMP0;
1292 		if (dmatrk >= sc->type->precomp[1])
1293 			adkmask |= ADKF_PRECOMP1;
1294 		custom.adkcon = adkmask;
1295 	}
1296 	custom.dskpt = (u_char *)kvtop(fdc_dmap);
1297 	FDDMASTART(ndmaw, write);
1298 
1299 #ifdef FDDEBUG
1300 	printf("  dma started\n");
1301 #endif
1302 }
1303 
1304 /*
1305  * recalibrate the drive
1306  */
1307 void
1308 fdcalibrate(arg)
1309 	void *arg;
1310 {
1311 	struct fd_softc *sc;
1312 	static int loopcnt;
1313 
1314 	sc = arg;
1315 
1316 	if (loopcnt == 0) {
1317 		/*
1318 		 * seek cyl 0
1319 		 */
1320 		fdc_indma = sc;
1321 		sc->stepdelay += 900;
1322 		if (sc->cachetrk > 1)
1323 			fdsetpos(sc, sc->cachetrk % FDNHEADS, 0);
1324 		sc->stepdelay -= 900;
1325 	}
1326 	if (loopcnt++ & 1)
1327 		fdsetpos(sc, sc->cachetrk, 0);
1328 	else
1329 		fdsetpos(sc, sc->cachetrk + FDNHEADS, 0);
1330 	/*
1331 	 * trk++, trk, trk++, trk, trk++, trk, trk++, trk and dma
1332 	 */
1333 	if (loopcnt < 8)
1334 		timeout(fdcalibrate, sc, hz / 8);
1335 	else {
1336 		loopcnt = 0;
1337 		fdc_indma = NULL;
1338 		timeout(fdmotoroff, sc, 3 * hz / 2);
1339 		fddmastart(sc, sc->cachetrk);
1340 	}
1341 }
1342 
1343 void
1344 fddmadone(sc, timeo)
1345 	struct fd_softc *sc;
1346 	int timeo;
1347 {
1348 #ifdef FDDEBUG
1349 	printf("fddmadone: unit %d, timeo %d\n", sc->hwunit, timeo);
1350 #endif
1351 	fdc_indma = NULL;
1352 	untimeout(fdmotoroff, sc);
1353 	FDDMASTOP;
1354 
1355 	/*
1356 	 * guarantee the drive has been at current head and cyl
1357 	 * for at least FDWRITEDELAY after a write.
1358 	 */
1359 	if (sc->flags & FDF_WRITEWAIT) {
1360 		delay(FDWRITEDELAY);
1361 		sc->flags &= ~FDF_WRITEWAIT;
1362 	}
1363 
1364 	if ((sc->flags & FDF_MOTOROFF) == 0) {
1365 		/*
1366 		 * motor runs for 1.5 seconds after last dma
1367 		 */
1368 		timeout(fdmotoroff, sc, 3 * hz / 2);
1369 	}
1370 	if (sc->flags & FDF_DIRTY) {
1371 		/*
1372 		 * if buffer dirty, the last dma cleaned it
1373 		 */
1374 		sc->flags &= ~FDF_DIRTY;
1375 		if (timeo)
1376 			printf("%s: write of track cache timed out.\n",
1377 			    sc->dkdev.dk_dev.dv_xname);
1378 		if (sc->flags & FDF_JUSTFLUSH) {
1379 			sc->flags &= ~FDF_JUSTFLUSH;
1380 			/*
1381 			 * we are done dma'ing
1382 			 */
1383 			fddone(sc);
1384 			return;
1385 		}
1386 		/*
1387 		 * load the cache
1388 		 */
1389 		fddmastart(sc, sc->cachetrk);
1390 		return;
1391 	}
1392 #ifdef FDDEBUG
1393 	else if (sc->flags & FDF_MOTOROFF)
1394 		panic("fddmadone: FDF_MOTOROFF with no FDF_DIRTY");
1395 #endif
1396 
1397 	/*
1398 	 * cache loaded decode it into cache buffer
1399 	 */
1400 	if (timeo == 0 && fdrawtocache(sc) == 0)
1401 		sc->retried = 0;
1402 	else {
1403 #ifdef FDDEBUG
1404 		if (timeo)
1405 			printf("%s: fddmadone: cache load timed out.\n",
1406 			    sc->dkdev.dk_dev.dv_xname);
1407 #endif
1408 		if (sc->retried >= sc->retries) {
1409 			sc->retried = 0;
1410 			sc->cachetrk = -1;
1411 		} else {
1412 			sc->retried++;
1413 			/*
1414 			 * this will be restarted at end of calibrate loop.
1415 			 */
1416 			untimeout(fdmotoroff, sc);
1417 			fdcalibrate(sc);
1418 			return;
1419 		}
1420 	}
1421 	fddone(sc);
1422 }
1423 
1424 void
1425 fddone(sc)
1426 	struct fd_softc *sc;
1427 {
1428 	struct buf *dp, *bp;
1429 	char *data;
1430 	int sz, blk;
1431 
1432 #ifdef FDDEBUG
1433 	printf("fddone: unit %d\n", sc->hwunit);
1434 #endif
1435 	/*
1436 	 * check to see if unit is just flushing the cache,
1437 	 * that is we have no io queued.
1438 	 */
1439 	if (sc->flags & FDF_MOTOROFF)
1440 		goto nobuf;
1441 
1442 	dp = &sc->bufq;
1443 	if ((bp = dp->b_actf) == NULL)
1444 		panic ("fddone");
1445 	/*
1446 	 * check for an error that may have occured
1447 	 * while getting the track.
1448 	 */
1449 	if (sc->cachetrk == -1) {
1450 		sc->retried = 0;
1451 		bp->b_flags |= B_ERROR;
1452 		bp->b_error = EIO;
1453 	} else if ((bp->b_flags & B_ERROR) == 0) {
1454 		data = sc->cachep;
1455 		/*
1456 		 * get offset of data in track cache and limit
1457 		 * the copy size to not exceed the cache's end.
1458 		 */
1459 		data += (dp->b_blkno % sc->nsectors) * FDSECSIZE;
1460 		sz = sc->nsectors - dp->b_blkno % sc->nsectors;
1461 		sz *= FDSECSIZE;
1462 		sz = min(dp->b_bcount, sz);
1463 		if (bp->b_flags & B_READ)
1464 			bcopy(data, dp->b_data, sz);
1465 		else {
1466 			bcopy(dp->b_data, data, sz);
1467 			sc->flags |= FDF_DIRTY;
1468 		}
1469 		bp->b_resid = dp->b_bcount - sz;
1470 		if (bp->b_resid == 0) {
1471 			bp->b_error = 0;
1472 		} else {
1473 			/*
1474 			 * not done yet need to read next track
1475 			 */
1476 			fdcont(sc);
1477 			return;
1478 		}
1479 	}
1480 	/*
1481 	 * remove from queue.
1482 	 */
1483 	dp->b_actf = bp->b_actf;
1484 	biodone(bp);
1485 nobuf:
1486 	fdfindwork(sc->dkdev.dk_dev.dv_unit);
1487 }
1488 
1489 void
1490 fdfindwork(unit)
1491 	int unit;
1492 {
1493 	struct fd_softc *ssc, *sc;
1494 	int i, last;
1495 
1496 	/*
1497 	 * first see if we have any Fdopen()'s waiting
1498 	 */
1499 	if (fdc_wantwakeup) {
1500 		wakeup(Fdopen);
1501 		fdc_wantwakeup--;
1502 		return;
1503 	}
1504 
1505 	/*
1506 	 * start next available unit, linear search from the next unit
1507 	 * wrapping and finally this unit.
1508 	 */
1509 	last = 0;
1510 	ssc = NULL;
1511 	for (i = unit + 1; last == 0; i++) {
1512 		if (i == unit)
1513 			last = 1;
1514 		if (i >= fdcd.cd_ndevs) {
1515 			i = -1;
1516 			continue;
1517 		}
1518 		if ((sc = fdcd.cd_devs[i]) == NULL)
1519 			continue;
1520 
1521 		/*
1522 		 * if unit has requested to be turned off
1523 		 * and it has no buf's queued do it now
1524 		 */
1525 		if (sc->flags & FDF_MOTOROFF) {
1526 			if (sc->bufq.b_actf == NULL)
1527 				fdmotoroff(sc);
1528 			else {
1529 				/*
1530 				 * we gained a buf request while
1531 				 * we waited, forget the motoroff
1532 				 */
1533 				sc->flags &= ~FDF_MOTOROFF;
1534 			}
1535 			/*
1536 			 * if we now have dma unit must have needed
1537 			 * flushing, quit
1538 			 */
1539 			if (fdc_indma)
1540 				return;
1541 		}
1542 		/*
1543 		 * if we have no start unit and the current unit has
1544 		 * io waiting choose this unit to start.
1545 		 */
1546 		if (ssc == NULL && sc->bufq.b_actf)
1547 			ssc = sc;
1548 	}
1549 	if (ssc)
1550 		fdstart(ssc);
1551 }
1552 
1553 /*
1554  * min byte count to whats left of the track in question
1555  */
1556 int
1557 fdminphys(bp)
1558 	struct buf *bp;
1559 {
1560 	struct fd_softc *sc;
1561 	int trk, sec, toff, tsz;
1562 
1563 	if ((sc = getsoftc(fdcd, FDUNIT(bp->b_dev))) == NULL)
1564 		return(ENXIO);
1565 
1566 	trk = bp->b_blkno / sc->nsectors;
1567 	sec = bp->b_blkno % sc->nsectors;
1568 
1569 	toff = sec * FDSECSIZE;
1570 	tsz = sc->nsectors * FDSECSIZE;
1571 #ifdef FDDEBUG
1572 	printf("fdminphys: before %d", bp->b_bcount);
1573 #endif
1574 	bp->b_bcount = min(bp->b_bcount, tsz - toff);
1575 #ifdef FDDEBUG
1576 	printf(" after %d\n", bp->b_bcount);
1577 #endif
1578 	return(bp->b_bcount);
1579 }
1580 
1581 /*
1582  * encode the track cache into raw MFM ready for dma
1583  * when we go to multiple disk formats, this will call type dependent
1584  * functions
1585  */
1586 void
1587 fdcachetoraw(sc)
1588 	struct fd_softc *sc;
1589 {
1590 	static u_long mfmnull[4];
1591 	u_long *rp, *crp, *dp, hcksum, dcksum, info, zero;
1592 	int sec, i;
1593 
1594 	rp = fdc_dmap;
1595 
1596 	/*
1597 	 * not yet one sector (- 1 long) gap.
1598 	 * for now use previous drivers values
1599 	 */
1600 	for (i = 0; i < sc->type->gap; i++)
1601 		*rp++ = 0xaaaaaaaa;
1602 	/*
1603 	 * process sectors
1604 	 */
1605 	dp = sc->cachep;
1606 	zero = 0;
1607 	info = 0xff000000 | (sc->cachetrk << 16) | sc->nsectors;
1608 	for (sec = 0; sec < sc->nsectors; sec++, info += (1 << 8) - 1) {
1609 		hcksum = dcksum = 0;
1610 		/*
1611 		 * sector format
1612 		 *	offset		description
1613 		 *-----------------------------------
1614 		 *  0			null
1615 		 *  1			sync
1616 		 * oddbits	evenbits
1617 		 *----------------------
1618 		 *  2		3	[0xff]b [trk]b [sec]b [togap]b
1619 		 *  4-7		8-11	null
1620 		 * 12		13	header cksum [2-11]
1621 		 * 14		15	data cksum [16-271]
1622 		 * 16-143	144-271	data
1623 		 */
1624 		*rp = 0xaaaaaaaa;
1625 		if (*(rp - 1) & 0x1)
1626 			*rp &= 0x7fffffff;	/* clock bit correction */
1627 		rp++;
1628 		*rp++ = (FDMFMSYNC << 16) | FDMFMSYNC;
1629 		rp = mfmblkencode(&info, rp, &hcksum, 1);
1630 		rp = mfmblkencode(mfmnull, rp, &hcksum, 4);
1631 		rp = mfmblkencode(&hcksum, rp, NULL, 1);
1632 
1633 		crp = rp;
1634 		rp = mfmblkencode(dp, rp + 2, &dcksum, FDSECLWORDS);
1635 		dp += FDSECLWORDS;
1636 		crp = mfmblkencode(&dcksum, crp, NULL, 1);
1637 		if (*(crp - 1) & 0x1)
1638 			*crp &= 0x7fffffff;	/* clock bit correction */
1639 		else if ((*crp & 0x40000000) == 0)
1640 			*crp |= 0x80000000;
1641         }
1642 	*rp = 0xaaa80000;
1643 	if (*(rp - 1) & 0x1)
1644 		*rp &= 0x7fffffff;
1645 }
1646 
1647 u_long *
1648 fdfindsync(rp, ep)
1649 	u_long *rp, *ep;
1650 {
1651 	u_short *sp;
1652 
1653 	sp = (u_short *)rp;
1654 	while ((u_long *)sp < ep && *sp != FDMFMSYNC)
1655 		sp++;
1656 	while ((u_long *)sp < ep && *sp == FDMFMSYNC)
1657 		sp++;
1658 	if ((u_long *)sp < ep)
1659 		return((u_long *)sp);
1660 	return(NULL);
1661 }
1662 
1663 /*
1664  * decode raw MFM from dma into units track cache.
1665  * when we go to multiple disk formats, this will call type dependent
1666  * functions
1667  */
1668 int
1669 fdrawtocache(sc)
1670 	struct fd_softc *sc;
1671 {
1672 	u_long mfmnull[4];
1673 	u_long *dp, *rp, *erp, *crp, *srp, hcksum, dcksum, info, cktmp;
1674 	int cnt, doagain;
1675 
1676 	doagain = 1;
1677 	srp = rp = fdc_dmap;
1678 	erp = (u_long *)((u_short *)rp + sc->type->nreadw);
1679 	cnt = 0;
1680 again:
1681 	if (doagain == 0 || (rp = srp = fdfindsync(srp, erp)) == NULL) {
1682 #ifdef DIAGNOSTIC
1683 		printf("%s: corrupted track (%d) data.\n",
1684 		    sc->dkdev.dk_dev.dv_xname, sc->cachetrk);
1685 #endif
1686 		return(-1);
1687 	}
1688 
1689 	/*
1690 	 * process sectors
1691 	 */
1692 	for (; cnt < sc->nsectors; cnt++) {
1693 		hcksum = dcksum = 0;
1694 		rp = mfmblkdecode(rp, &info, &hcksum, 1);
1695 		rp = mfmblkdecode(rp, mfmnull, &hcksum, 4);
1696 		rp = mfmblkdecode(rp, &cktmp, NULL, 1);
1697 		if (cktmp != hcksum) {
1698 #ifdef FDDEBUG
1699 			printf("  info 0x%x hchksum 0x%x trkhcksum 0x%x\n",
1700 			    info, hcksum, cktmp);
1701 #endif
1702 			goto again;
1703 		}
1704 		if (((info >> 16) & 0xff) != sc->cachetrk) {
1705 #ifdef DEBUG
1706 			printf("%s: incorrect track found: 0x%0x %d\n",
1707 			    sc->dkdev.dk_dev.dv_xname, info, sc->cachetrk);
1708 #endif
1709 			goto again;
1710 		}
1711 #ifdef FDDEBUG
1712 		printf("  info 0x%x\n", info);
1713 #endif
1714 
1715 		rp = mfmblkdecode(rp, &cktmp, NULL, 1);
1716 		dp = sc->cachep;
1717 		dp += FDSECLWORDS * ((info >> 8) & 0xff);
1718 		crp = mfmblkdecode(rp, dp, &dcksum, FDSECLWORDS);
1719 		if (cktmp != dcksum) {
1720 #ifdef FDDEBUG
1721 			printf("  info 0x%x dchksum 0x%x trkdcksum 0x%x\n",
1722 			    info, dcksum, cktmp);
1723 #endif
1724 			goto again;
1725 		}
1726 
1727 		/*
1728 		 * if we are at gap then we can no longer be sure
1729 		 * of correct sync marks
1730 		 */
1731 		if ((info && 0xff) == 1)
1732 			doagain = 1;
1733 		else
1734 			doagain = 0;
1735 		srp = rp = fdfindsync(crp, erp);
1736 	}
1737 	return(0);
1738 }
1739 
1740 /*
1741  * encode len longwords of `dp' data in amiga mfm block format (`rp')
1742  * this format specified that the odd bits are at current pos and even
1743  * bits at len + current pos
1744  */
1745 u_long *
1746 mfmblkencode(dp, rp, cp, len)
1747 	u_long *dp, *rp, *cp;
1748 	int len;
1749 {
1750 	u_long *sdp, *edp, d, dtmp, correct;
1751 	int i;
1752 
1753 	sdp = dp;
1754 	edp = dp + len;
1755 
1756 	if (*(rp - 1) & 0x1)
1757 		correct = 1;
1758 	else
1759 		correct = 0;
1760 	/*
1761 	 * do odd bits
1762 	 */
1763 	while (dp < edp) {
1764 		d = (*dp >> 1) & 0x55555555;	/* remove clock bits */
1765 		dtmp = d ^ 0x55555555;
1766 		d |= ((dtmp >> 1) | 0x80000000) & (dtmp << 1);
1767 		/*
1768 		 * correct upper clock bit if needed
1769 		 */
1770 		if (correct)
1771 			d &= 0x7fffffff;
1772 		if (d & 0x1)
1773 			correct = 1;
1774 		else
1775 			correct = 0;
1776 		/*
1777 		 * do checksums and store in raw buffer
1778 		 */
1779 		if (cp)
1780 			*cp ^= d;
1781 		*rp++ = d;
1782 		dp++;
1783 	}
1784 	/*
1785 	 * do even bits
1786 	 */
1787 	dp = sdp;
1788 	while (dp < edp) {
1789 		d = *dp & 0x55555555;	/* remove clock bits */
1790 		dtmp = d ^ 0x55555555;
1791 		d |= ((dtmp >> 1) | 0x80000000) & (dtmp << 1);
1792 		/*
1793 		 * correct upper clock bit if needed
1794 		 */
1795 		if (correct)
1796 			d &= 0x7fffffff;
1797 		if (d & 0x1)
1798 			correct = 1;
1799 		else
1800 			correct = 0;
1801 		/*
1802 		 * do checksums and store in raw buffer
1803 		 */
1804 		if (cp)
1805 			*cp ^= d;
1806 		*rp++ = d;
1807 		dp++;
1808 	}
1809 	if (cp)
1810 		*cp &= 0x55555555;
1811 	return(rp);
1812 }
1813 
1814 /*
1815  * decode len longwords of `dp' data in amiga mfm block format (`rp')
1816  * this format specified that the odd bits are at current pos and even
1817  * bits at len + current pos
1818  */
1819 u_long *
1820 mfmblkdecode(rp, dp, cp, len)
1821 	u_long *rp, *dp, *cp;
1822 	int len;
1823 {
1824 	u_long o, e;
1825 	int cnt;
1826 
1827 	cnt = len;
1828 	while (cnt--) {
1829 		o = *rp;
1830 		e = *(rp + len);
1831 		if (cp) {
1832 			*cp ^= o;
1833 			*cp ^= e;
1834 		}
1835 		o &= 0x55555555;
1836 		e &= 0x55555555;
1837 		*dp++ = (o << 1) | e;
1838 		rp++;
1839 	}
1840 	if (cp)
1841 		*cp &= 0x55555555;
1842 	return(rp + len);
1843 }
1844