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