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