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