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