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