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