hdfd.c revision 1.67 1 /* $NetBSD: hdfd.c,v 1.67 2009/03/14 15:36:03 dsl Exp $ */
2
3 /*-
4 * Copyright (c) 1996 Leo Weppelman
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Don Ahn.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * @(#)fd.c 7.4 (Berkeley) 5/25/91
36 */
37
38 /*-
39 * Copyright (c) 1993, 1994, 1995, 1996
40 * Charles M. Hannum. All rights reserved.
41 *
42 * This code is derived from software contributed to Berkeley by
43 * Don Ahn.
44 *
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions
47 * are met:
48 * 1. Redistributions of source code must retain the above copyright
49 * notice, this list of conditions and the following disclaimer.
50 * 2. Redistributions in binary form must reproduce the above copyright
51 * notice, this list of conditions and the following disclaimer in the
52 * documentation and/or other materials provided with the distribution.
53 * 3. All advertising materials mentioning features or use of this software
54 * must display the following acknowledgement:
55 * This product includes software developed by the University of
56 * California, Berkeley and its contributors.
57 * 4. Neither the name of the University nor the names of its contributors
58 * may be used to endorse or promote products derived from this software
59 * without specific prior written permission.
60 *
61 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
62 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
63 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
65 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
66 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
67 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
69 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71 * SUCH DAMAGE.
72 *
73 * @(#)fd.c 7.4 (Berkeley) 5/25/91
74 */
75
76 /*
77 * Floppy formatting facilities merged from FreeBSD fd.c driver:
78 * Id: fd.c,v 1.53 1995/03/12 22:40:56 joerg Exp
79 * which carries the same copyright/redistribution notice as shown above with
80 * the addition of the following statement before the "Redistribution and
81 * use ..." clause:
82 *
83 * Copyright (c) 1993, 1994 by
84 * jc (at) irbs.UUCP (John Capo)
85 * vak (at) zebub.msk.su (Serge Vakulenko)
86 * ache (at) astral.msk.su (Andrew A. Chernov)
87 *
88 * Copyright (c) 1993, 1994, 1995 by
89 * joerg_wunsch (at) uriah.sax.de (Joerg Wunsch)
90 * dufault (at) hda.com (Peter Dufault)
91 */
92
93 #include <sys/cdefs.h>
94 __KERNEL_RCSID(0, "$NetBSD: hdfd.c,v 1.67 2009/03/14 15:36:03 dsl Exp $");
95
96 #include "opt_ddb.h"
97
98 #include <sys/param.h>
99 #include <sys/systm.h>
100 #include <sys/callout.h>
101 #include <sys/kernel.h>
102 #include <sys/file.h>
103 #include <sys/ioctl.h>
104 #include <sys/device.h>
105 #include <sys/disklabel.h>
106 #include <sys/disk.h>
107 #include <sys/buf.h>
108 #include <sys/bufq.h>
109 #include <sys/malloc.h>
110 #include <sys/uio.h>
111 #include <sys/syslog.h>
112 #include <sys/queue.h>
113 #include <sys/proc.h>
114 #include <sys/fdio.h>
115 #include <sys/conf.h>
116
117 #include <uvm/uvm_extern.h>
118
119 #include <machine/cpu.h>
120 #include <machine/bus.h>
121 #include <machine/iomap.h>
122 #include <machine/mfp.h>
123
124 #include <atari/dev/hdfdreg.h>
125 #include <atari/atari/intr.h>
126 #include <atari/atari/device.h>
127
128 #include "locators.h"
129
130 /*
131 * {b,c}devsw[] function prototypes
132 */
133 dev_type_open(fdopen);
134 dev_type_close(fdclose);
135 dev_type_read(fdread);
136 dev_type_write(fdwrite);
137 dev_type_ioctl(fdioctl);
138 dev_type_strategy(fdstrategy);
139
140 volatile u_char *fdio_addr;
141
142 #define wrt_fdc_reg(reg, val) { fdio_addr[reg] = val; }
143 #define rd_fdc_reg(reg) ( fdio_addr[reg] )
144
145 #define fdc_ienable() MFP2->mf_ierb |= IB_DCHG;
146
147 /*
148 * Interface to the pseudo-DMA handler
149 */
150 void fddma_intr(void);
151 void * fddmaaddr = NULL;
152 int fddmalen = 0;
153
154 extern void mfp_hdfd_nf(void)), mfp_hdfd_fifo(void);
155
156 /*
157 * Argument to fdcintr.....
158 */
159 static void *intr_arg = NULL; /* XXX: arg. to intr_establish() */
160
161
162
163 #define FDUNIT(dev) (minor(dev) / 8)
164 #define FDTYPE(dev) (minor(dev) % 8)
165
166 /* (mis)use device use flag to identify format operation */
167 #define B_FORMAT B_DEVPRIVATE
168
169 enum fdc_state {
170 DEVIDLE = 0,
171 MOTORWAIT,
172 DOSEEK,
173 SEEKWAIT,
174 SEEKTIMEDOUT,
175 SEEKCOMPLETE,
176 DOIO,
177 IOCOMPLETE,
178 IOTIMEDOUT,
179 DORESET,
180 RESETCOMPLETE,
181 RESETTIMEDOUT,
182 DORECAL,
183 RECALWAIT,
184 RECALTIMEDOUT,
185 RECALCOMPLETE,
186 };
187
188 /* software state, per controller */
189 struct fdc_softc {
190 struct device sc_dev; /* boilerplate */
191
192 struct callout sc_timo_ch; /* timeout callout */
193 struct callout sc_intr_ch; /* pseudo-intr callout */
194
195 struct fd_softc *sc_fd[4]; /* pointers to children */
196 TAILQ_HEAD(drivehead, fd_softc) sc_drives;
197 enum fdc_state sc_state;
198 int sc_errors; /* number of retries so far */
199 int sc_overruns; /* number of overruns so far */
200 u_char sc_status[7]; /* copy of registers */
201 };
202
203 /* controller driver configuration */
204 int fdcprobe(struct device *, struct cfdata *, void *);
205 int fdprint(void *, const char *);
206 void fdcattach(struct device *, struct device *, void *);
207
208 CFATTACH_DECL(fdc, sizeof(struct fdc_softc),
209 fdcprobe, fdcattach, NULL, NULL);
210
211 /*
212 * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
213 * we tell them apart.
214 */
215 struct fd_type {
216 int sectrac; /* sectors per track */
217 int heads; /* number of heads */
218 int seccyl; /* sectors per cylinder */
219 int secsize; /* size code for sectors */
220 int datalen; /* data len when secsize = 0 */
221 int steprate; /* step rate and head unload time */
222 int gap1; /* gap len between sectors */
223 int gap2; /* formatting gap */
224 int tracks; /* total num of tracks */
225 int size; /* size of disk in sectors */
226 int step; /* steps per cylinder */
227 int rate; /* transfer speed code */
228 u_char fillbyte; /* format fill byte */
229 u_char interleave; /* interleave factor (formatting) */
230 const char *name;
231 };
232
233 /*
234 * The order of entries in the following table is important -- BEWARE!
235 * The order of the types is the same as for the TT/Falcon....
236 */
237 struct fd_type fd_types[] = {
238 /* 360kB in 720kB drive */
239 { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_125KBPS,0xf6,1,"360KB" },
240 /* 3.5" 720kB diskette */
241 { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_125KBPS,0xf6,1,"720KB" },
242 /* 1.44MB diskette */
243 { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_250KBPS,0xf6,1,"1.44MB" },
244 };
245
246 /* software state, per disk (with up to 4 disks per ctlr) */
247 struct fd_softc {
248 struct device sc_dev;
249 struct disk sc_dk;
250
251 struct fd_type *sc_deftype; /* default type descriptor */
252 struct fd_type *sc_type; /* current type descriptor */
253
254 struct callout sc_motoron_ch;
255 struct callout sc_motoroff_ch;
256
257 daddr_t sc_blkno; /* starting block number */
258 int sc_bcount; /* byte count left */
259 int sc_opts; /* user-set options */
260 int sc_skip; /* bytes already transferred */
261 int sc_nblks; /* #blocks currently transferring */
262 int sc_nbytes; /* #bytes currently transferring */
263
264 int sc_drive; /* physical unit number */
265 int sc_flags;
266 #define FD_OPEN 0x01 /* it's open */
267 #define FD_MOTOR 0x02 /* motor should be on */
268 #define FD_MOTOR_WAIT 0x04 /* motor coming up */
269 #define FD_HAVELAB 0x08 /* got a disklabel */
270 int sc_cylin; /* where we think the head is */
271
272 void *sc_sdhook; /* saved shutdown hook for drive. */
273
274 TAILQ_ENTRY(fd_softc) sc_drivechain;
275 int sc_ops; /* I/O ops since last switch */
276 struct bufq_state *sc_q; /* pending I/O requests */
277 int sc_active; /* number of active I/O operations */
278 };
279
280 /* floppy driver configuration */
281 int fdprobe(struct device *, struct cfdata *, void *);
282 void fdattach(struct device *, struct device *, void *);
283
284 CFATTACH_DECL(hdfd, sizeof(struct fd_softc),
285 fdprobe, fdattach, NULL, NULL);
286
287 extern struct cfdriver hdfd_cd;
288
289 const struct bdevsw fd_bdevsw = {
290 fdopen, fdclose, fdstrategy, fdioctl, nodump, nosize, D_DISK
291 };
292
293 const struct cdevsw fd_cdevsw = {
294 fdopen, fdclose, fdread, fdwrite, fdioctl,
295 nostop, notty, nopoll, nommap, nokqfilter, D_DISK
296 };
297
298 void fdstart(struct fd_softc *);
299
300 struct dkdriver fddkdriver = { fdstrategy };
301
302 void fd_set_motor(struct fdc_softc *fdc, int reset);
303 void fd_motor_off(void *arg);
304 void fd_motor_on(void *arg);
305 int fdcresult(struct fdc_softc *fdc);
306 int out_fdc(u_char x);
307 void fdc_ctrl_intr(struct clockframe);
308 void fdcstart(struct fdc_softc *fdc);
309 void fdcstatus(struct device *dv, int n, const char *s);
310 void fdctimeout(void *arg);
311 void fdcpseudointr(void *arg);
312 int fdcintr(void *);
313 void fdcretry(struct fdc_softc *fdc);
314 void fdfinish(struct fd_softc *fd, struct buf *bp);
315 int fdformat(dev_t, struct ne7_fd_formb *, struct proc *);
316
317 static void fdgetdisklabel(struct fd_softc *, dev_t);
318 static void fdgetdefaultlabel(struct fd_softc *, struct disklabel *,
319 int);
320
321 inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
322
323 int
324 fdcprobe(struct device *parent, struct cfdata *cfp, void *aux)
325 {
326 static int fdc_matched = 0;
327 bus_space_tag_t mb_tag;
328 bus_space_handle_t handle;
329
330 /* Match only once */
331 if(strcmp("fdc", aux) || fdc_matched)
332 return(0);
333
334 if (!atari_realconfig)
335 return 0;
336
337 if ((mb_tag = mb_alloc_bus_space_tag() == NULL)
338 return 0;
339
340 if (bus_space_map(mb_tag, FD_IOBASE, FD_IOSIZE, 0, &handle)) {
341 printf("fdcprobe: cannot map io-area\n");
342 mb_free_bus_space_tag(mb_tag);
343 return (0);
344 }
345 fdio_addr = bus_space_vaddr(mb_tag, handle); /* XXX */
346
347 #ifdef FD_DEBUG
348 printf("fdcprobe: I/O mapping done va: %p\n", fdio_addr);
349 #endif
350
351 /* reset */
352 wrt_fdc_reg(fdout, 0);
353 delay(100);
354 wrt_fdc_reg(fdout, FDO_FRST);
355
356 /* see if it can handle a command */
357 if (out_fdc(NE7CMD_SPECIFY) < 0)
358 goto out;
359 out_fdc(0xdf);
360 out_fdc(7);
361
362 fdc_matched = 1;
363
364 out:
365 if (fdc_matched == 0) {
366 bus_space_unmap(mb_tag, handle, FD_IOSIZE);
367 mb_free_bus_space_tag(mb_tag);
368 }
369
370 return fdc_matched;
371 }
372
373 /*
374 * Arguments passed between fdcattach and fdprobe.
375 */
376 struct fdc_attach_args {
377 int fa_drive;
378 struct fd_type *fa_deftype;
379 };
380
381 /*
382 * Print the location of a disk drive (called just before attaching the
383 * the drive). If `fdc' is not NULL, the drive was found but was not
384 * in the system config file; print the drive name as well.
385 * Return QUIET (config_find ignores this if the device was configured) to
386 * avoid printing `fdN not configured' messages.
387 */
388 int
389 fdprint(void *aux, const char *fdc)
390 {
391 register struct fdc_attach_args *fa = aux;
392
393 if (!fdc)
394 aprint_normal(" drive %d", fa->fa_drive);
395 return QUIET;
396 }
397
398 void
399 fdcattach(parent, self, aux)
400 struct device *parent, *self;
401 void *aux;
402 {
403 struct fdc_softc *fdc = (void *)self;
404 struct fdc_attach_args fa;
405 int has_fifo;
406
407 has_fifo = 0;
408
409 fdc->sc_state = DEVIDLE;
410 TAILQ_INIT(&fdc->sc_drives);
411
412 out_fdc(NE7CMD_CONFIGURE);
413 if (out_fdc(0) == 0) {
414 out_fdc(0x1a); /* No polling, fifo depth = 10 */
415 out_fdc(0);
416
417 /* Retain configuration across resets */
418 out_fdc(NE7CMD_LOCK);
419 (void)fdcresult(fdc);
420 has_fifo = 1;
421 }
422 else {
423 (void)rd_fdc_reg(fddata);
424 printf(": no fifo");
425 }
426
427 printf("\n");
428
429 callout_init(&fdc->sc_timo_ch, 0);
430 callout_init(&fdc->sc_intr_ch, 0);
431
432 if (intr_establish(22, USER_VEC|FAST_VEC, 0,
433 (hw_ifun_t)(has_fifo ? mfp_hdfd_fifo : mfp_hdfd_nf),
434 NULL) == NULL) {
435 printf("fdcattach: Can't establish interrupt\n");
436 return;
437 }
438
439 /*
440 * Setup the interrupt logic.
441 */
442 MFP2->mf_iprb = (u_int8_t)~IB_DCHG;
443 MFP2->mf_imrb |= IB_DCHG;
444 MFP2->mf_aer |= 0x10; /* fdc int low->high */
445
446 /* physical limit: four drives per controller. */
447 for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
448 /*
449 * XXX: Choose something sensible as a default...
450 */
451 fa.fa_deftype = &fd_types[2]; /* 1.44MB */
452 (void)config_found(self, (void *)&fa, fdprint);
453 }
454 }
455
456 int
457 fdprobe(struct device *parent, struct cfdata *cfp, void *aux)
458 {
459 struct fdc_softc *fdc = (void *)parent;
460 struct fdc_attach_args *fa = aux;
461 int drive = fa->fa_drive;
462 int n;
463
464 if (cfp->cf_loc[FDCCF_UNIT] != FDCCF_UNIT_DEFAULT &&
465 cfp->cf_loc[FDCCF_UNIT] != drive)
466 return 0;
467 /*
468 * XXX
469 * This is to work around some odd interactions between this driver
470 * and SMC Ethernet cards.
471 */
472 if (cfp->cf_loc[FDCCF_UNIT] == FDCCF_UNIT_DEFAULT && drive >= 2)
473 return 0;
474
475 /* select drive and turn on motor */
476 wrt_fdc_reg(fdout, drive | FDO_FRST | FDO_MOEN(drive));
477
478 /* wait for motor to spin up */
479 delay(250000);
480 out_fdc(NE7CMD_RECAL);
481 out_fdc(drive);
482
483 /* wait for recalibrate */
484 delay(2000000);
485 out_fdc(NE7CMD_SENSEI);
486 n = fdcresult(fdc);
487
488 #ifdef FD_DEBUG
489 {
490 int i;
491 printf("fdprobe: status");
492 for (i = 0; i < n; i++)
493 printf(" %x", fdc->sc_status[i]);
494 printf("\n");
495 }
496 #endif
497 intr_arg = (void*)fdc;
498 if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)
499 return 0;
500 /* turn off motor */
501 wrt_fdc_reg(fdout, FDO_FRST);
502
503 return 1;
504 }
505
506 /*
507 * Controller is working, and drive responded. Attach it.
508 */
509 void
510 fdattach(parent, self, aux)
511 struct device *parent, *self;
512 void *aux;
513 {
514 struct fdc_softc *fdc = (void *)parent;
515 struct fd_softc *fd = (void *)self;
516 struct fdc_attach_args *fa = aux;
517 struct fd_type *type = fa->fa_deftype;
518 int drive = fa->fa_drive;
519
520 callout_init(&fd->sc_motoron_ch, 0);
521 callout_init(&fd->sc_motoroff_ch, 0);
522
523 /* XXX Allow `flags' to override device type? */
524
525 if (type)
526 printf(": %s %d cyl, %d head, %d sec\n", type->name,
527 type->tracks, type->heads, type->sectrac);
528 else
529 printf(": density unknown\n");
530
531 bufq_alloc(&fd->sc_q, "disksort", BUFQ_SORT_CYLINDER);
532 fd->sc_cylin = -1;
533 fd->sc_drive = drive;
534 fd->sc_deftype = type;
535 fdc->sc_fd[drive] = fd;
536
537 /*
538 * Initialize and attach the disk structure.
539 */
540 disk_init(&fd->sc_dk, fd->sc_dev.dv_xname, &fddkdriver);
541 disk_attach(&fd->sc_dk);
542
543 /* Needed to power off if the motor is on when we halt. */
544 fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd);
545 }
546
547 /*
548 * This is called from the assembly part of the interrupt handler
549 * when it is clear that the interrupt was not related to shoving
550 * data.
551 */
552 void
553 fdc_ctrl_intr(struct clockframe frame)
554 {
555 int s;
556
557 /*
558 * Disable further interrupts. The fdcintr() routine
559 * explicitly enables them when needed.
560 */
561 MFP2->mf_ierb &= ~IB_DCHG;
562
563 /*
564 * Set fddmalen to zero so no pseudo-DMA transfers will
565 * occur.
566 */
567 fddmalen = 0;
568
569 if (!BASEPRI(frame.cf_sr)) {
570 /*
571 * We don't want to stay on ipl6.....
572 */
573 add_sicallback((si_farg)fdcpseudointr, intr_arg, 0);
574 }
575 else {
576 s = splbio();
577 (void) fdcintr(intr_arg);
578 splx(s);
579 }
580 }
581
582 inline struct fd_type *
583 fd_dev_to_type(struct fd_softc *fd, dev_t dev)
584 {
585 int type = FDTYPE(dev);
586
587 if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
588 return NULL;
589 return type ? &fd_types[type - 1] : fd->sc_deftype;
590 }
591
592 void
593 fdstrategy(struct buf *bp)
594 {
595 struct fd_softc *fd = device_lookup_private(&hdfd_cd, FDUNIT(bp->b_dev));
596 int sz;
597 int s;
598
599 /* Valid unit, controller, and request? */
600 if (bp->b_blkno < 0 ||
601 ((bp->b_bcount % FDC_BSIZE) != 0 &&
602 (bp->b_flags & B_FORMAT) == 0)) {
603 bp->b_error = EINVAL;
604 goto done;
605 }
606
607 /* If it's a null transfer, return immediately. */
608 if (bp->b_bcount == 0)
609 goto done;
610
611 sz = howmany(bp->b_bcount, FDC_BSIZE);
612
613 if (bp->b_blkno + sz > fd->sc_type->size) {
614 sz = fd->sc_type->size - bp->b_blkno;
615 if (sz == 0) {
616 /* If exactly at end of disk, return EOF. */
617 goto done;
618 }
619 if (sz < 0) {
620 /* If past end of disk, return EINVAL. */
621 bp->b_error = EINVAL;
622 goto done;
623 }
624 /* Otherwise, truncate request. */
625 bp->b_bcount = sz << DEV_BSHIFT;
626 }
627
628 bp->b_rawblkno = bp->b_blkno;
629 bp->b_cylinder = bp->b_blkno / (FDC_BSIZE/DEV_BSIZE) / fd->sc_type->seccyl;
630
631 #ifdef FD_DEBUG
632 printf("fdstrategy: b_blkno %d b_bcount %ld blkno %qd cylin %ld sz"
633 " %d\n", bp->b_blkno, bp->b_bcount, (long)fd->sc_blkno,
634 bp->b_cylinder, sz);
635 #endif
636
637 /* Queue transfer on drive, activate drive and controller if idle. */
638 s = splbio();
639 bufq_put(fd->sc_q, bp);
640 callout_stop(&fd->sc_motoroff_ch); /* a good idea */
641 if (fd->sc_active == 0)
642 fdstart(fd);
643 #ifdef DIAGNOSTIC
644 else {
645 struct fdc_softc *fdc = (void *) device_parent(&fd->sc_dev);
646 if (fdc->sc_state == DEVIDLE) {
647 printf("fdstrategy: controller inactive\n");
648 fdcstart(fdc);
649 }
650 }
651 #endif
652 splx(s);
653 return;
654
655 done:
656 /* Toss transfer; we're done early. */
657 bp->b_resid = bp->b_bcount;
658 biodone(bp);
659 }
660
661 void
662 fdstart(struct fd_softc *fd)
663 {
664 struct fdc_softc *fdc = (void *) device_parent(&fd->sc_dev);
665 int active = fdc->sc_drives.tqh_first != 0;
666
667 /* Link into controller queue. */
668 fd->sc_active = 1;
669 TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
670
671 /* If controller not already active, start it. */
672 if (!active)
673 fdcstart(fdc);
674 }
675
676 void
677 fdfinish(struct fd_softc *fd, struct buf *bp)
678 {
679 struct fdc_softc *fdc = (void *) device_parent(&fd->sc_dev);
680
681 /*
682 * Move this drive to the end of the queue to give others a `fair'
683 * chance. We only force a switch if N operations are completed while
684 * another drive is waiting to be serviced, since there is a long motor
685 * startup delay whenever we switch.
686 */
687 (void)bufq_get(fd->sc_q);
688 if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
689 fd->sc_ops = 0;
690 TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
691 if (bufq_peek(fd->sc_q) != NULL)
692 TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
693 else
694 fd->sc_active = 0;
695 }
696 bp->b_resid = fd->sc_bcount;
697 fd->sc_skip = 0;
698
699 biodone(bp);
700 /* turn off motor 5s from now */
701 callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd);
702 fdc->sc_state = DEVIDLE;
703 }
704
705 int
706 fdread(dev_t dev, struct uio *uio, int flags)
707 {
708 return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio));
709 }
710
711 int
712 fdwrite(dev_t dev, struct uio *uio, int flags)
713 {
714 return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio));
715 }
716
717 void
718 fd_set_motor(struct fdc_softc *fdc, int reset)
719 {
720 struct fd_softc *fd;
721 u_char status;
722 int n;
723
724 if ((fd = fdc->sc_drives.tqh_first) != NULL)
725 status = fd->sc_drive;
726 else
727 status = 0;
728 if (!reset)
729 status |= FDO_FRST | FDO_FDMAEN;
730 for (n = 0; n < 4; n++)
731 if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
732 status |= FDO_MOEN(n);
733 wrt_fdc_reg(fdout, status);
734 }
735
736 void
737 fd_motor_off(void *arg)
738 {
739 struct fd_softc *fd = arg;
740 int s;
741
742 s = splbio();
743 fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
744 fd_set_motor((struct fdc_softc *) device_parent(&fd->sc_dev), 0);
745 splx(s);
746 }
747
748 void
749 fd_motor_on(void *arg)
750 {
751 struct fd_softc *fd = arg;
752 struct fdc_softc *fdc = (void *) device_parent(&fd->sc_dev);
753 int s;
754
755 s = splbio();
756 fd->sc_flags &= ~FD_MOTOR_WAIT;
757 if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
758 (void) fdcintr(fdc);
759 splx(s);
760 }
761
762 int
763 fdcresult(struct fdc_softc *fdc)
764 {
765 u_char i;
766 int j = 100000,
767 n = 0;
768
769 for (; j; j--) {
770 i = rd_fdc_reg(fdsts) & (NE7_DIO | NE7_RQM | NE7_CB);
771 if (i == NE7_RQM)
772 return n;
773 if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
774 if (n >= sizeof(fdc->sc_status)) {
775 log(LOG_ERR, "fdcresult: overrun\n");
776 return -1;
777 }
778 fdc->sc_status[n++] = rd_fdc_reg(fddata);
779 }
780 else delay(10);
781 }
782 log(LOG_ERR, "fdcresult: timeout\n");
783 return -1;
784 }
785
786 int
787 out_fdc(u_char x)
788 {
789 int i = 100000;
790
791 while (((rd_fdc_reg(fdsts) & (NE7_DIO|NE7_RQM)) != NE7_RQM) && i-- > 0)
792 delay(1);
793 if (i <= 0)
794 return -1;
795 wrt_fdc_reg(fddata, x);
796 return 0;
797 }
798
799 int
800 fdopen(dev_t dev, int flags, int mode, struct lwp *l)
801 {
802 struct fd_softc *fd;
803 struct fd_type *type;
804
805 fd = device_lookup_private(&hdfd_cd, FDUNIT(dev));
806 if (fd == NULL)
807 return ENXIO;
808 type = fd_dev_to_type(fd, dev);
809 if (type == NULL)
810 return ENXIO;
811
812 if ((fd->sc_flags & FD_OPEN) != 0 &&
813 fd->sc_type != type)
814 return EBUSY;
815
816 fd->sc_type = type;
817 fd->sc_cylin = -1;
818 fd->sc_flags |= FD_OPEN;
819 fdgetdisklabel(fd, dev);
820
821 return 0;
822 }
823
824 int
825 fdclose(dev_t dev, int flags, int mode, struct lwp *l)
826 {
827 struct fd_softc *fd = device_lookup_private(&hdfd_cd, FDUNIT(dev));
828
829 fd->sc_flags &= ~(FD_OPEN|FD_HAVELAB);
830 fd->sc_opts &= ~(FDOPT_NORETRY|FDOPT_SILENT);
831 return 0;
832 }
833
834 void
835 fdcstart(struct fdc_softc *fdc)
836 {
837
838 #ifdef DIAGNOSTIC
839 /* only got here if controller's drive queue was inactive; should
840 be in idle state */
841 if (fdc->sc_state != DEVIDLE) {
842 printf("fdcstart: not idle\n");
843 return;
844 }
845 #endif
846 (void) fdcintr(fdc);
847 }
848
849 static void
850 fdcpstatus(struct fdc_softc *fdc)
851 {
852 char bits[64];
853
854 snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
855 printf(" (st0 %s", bits);
856 snprintb(bits, sizeof(bits), NE7_ST1BITS, fdc->sc_status[1]);
857 printf(" st1 %s", bits);
858 snprintb(bits, sizeof(bits), NE7_ST2BITS, fdc->sc_status[2]);
859 printf(" st2 %s", bits);
860 printf(" cyl %d head %d sec %d)\n",
861 fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
862 }
863
864 void
865 fdcstatus(struct device *dv, int n, const char *s)
866 {
867 struct fdc_softc *fdc = (void *) device_parent(dv);
868 char bits[64];
869
870 if (n == 0) {
871 out_fdc(NE7CMD_SENSEI);
872 (void) fdcresult(fdc);
873 n = 2;
874 }
875
876 printf("%s: %s", dv->dv_xname, s);
877
878 switch (n) {
879 case 0:
880 printf("\n");
881 break;
882 case 2:
883 snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]);
884 printf(" (st0 %s cyl %d)\n", bits, fdc->sc_status[1]);
885 break;
886 case 7:
887 fdcpstatus(fdc);
888 break;
889 #ifdef DIAGNOSTIC
890 default:
891 printf("\nfdcstatus: weird size");
892 break;
893 #endif
894 }
895 }
896
897 void
898 fdctimeout(void *arg)
899 {
900 struct fdc_softc *fdc = arg;
901 struct fd_softc *fd = fdc->sc_drives.tqh_first;
902 int s;
903
904 s = splbio();
905 fdcstatus(&fd->sc_dev, 0, "timeout");
906
907 if (bufq_peek(fd->sc_q) != NULL)
908 fdc->sc_state++;
909 else
910 fdc->sc_state = DEVIDLE;
911
912 (void) fdcintr(fdc);
913 splx(s);
914 }
915
916 void
917 fdcpseudointr(void *arg)
918 {
919 int s;
920
921 /* Just ensure it has the right spl. */
922 s = splbio();
923 (void) fdcintr(arg);
924 splx(s);
925 }
926
927 int
928 fdcintr(void *arg)
929 {
930 struct fdc_softc *fdc = arg;
931 #define st0 fdc->sc_status[0]
932 #define st1 fdc->sc_status[1]
933 #define cyl fdc->sc_status[1]
934
935 struct fd_softc *fd;
936 struct buf *bp;
937 int read, head, sec, i, nblks;
938 struct fd_type *type;
939 struct ne7_fd_formb *finfo = NULL;
940
941 loop:
942 /* Is there a drive for the controller to do a transfer with? */
943 fd = fdc->sc_drives.tqh_first;
944 if (fd == NULL) {
945 fdc->sc_state = DEVIDLE;
946 return 1;
947 }
948
949 /* Is there a transfer to this drive? If not, deactivate drive. */
950 bp = bufq_peek(fd->sc_q);
951 if (bp == NULL) {
952 fd->sc_ops = 0;
953 TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
954 fd->sc_active = 0;
955 goto loop;
956 }
957
958 if (bp->b_flags & B_FORMAT)
959 finfo = (struct ne7_fd_formb *)bp->b_data;
960
961 switch (fdc->sc_state) {
962 case DEVIDLE:
963 fdc->sc_errors = 0;
964 fdc->sc_overruns = 0;
965 fd->sc_skip = 0;
966 fd->sc_bcount = bp->b_bcount;
967 fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE);
968 callout_stop(&fd->sc_motoroff_ch);
969 if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
970 fdc->sc_state = MOTORWAIT;
971 return 1;
972 }
973 if ((fd->sc_flags & FD_MOTOR) == 0) {
974 /* Turn on the motor, being careful about pairing. */
975 struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
976 if (ofd && ofd->sc_flags & FD_MOTOR) {
977 callout_stop(&ofd->sc_motoroff_ch);
978 ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
979 }
980 fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
981 fd_set_motor(fdc, 0);
982 fdc->sc_state = MOTORWAIT;
983 /* Allow .25s for motor to stabilize. */
984 callout_reset(&fd->sc_motoron_ch, hz / 4,
985 fd_motor_on, fd);
986 return 1;
987 }
988 /* Make sure the right drive is selected. */
989 fd_set_motor(fdc, 0);
990
991 /* fall through */
992 case DOSEEK:
993 doseek:
994 if (fd->sc_cylin == bp->b_cylinder)
995 goto doio;
996
997 out_fdc(NE7CMD_SPECIFY);/* specify command */
998 out_fdc(fd->sc_type->steprate);
999 out_fdc(0x7); /* XXX head load time == 6ms - non-DMA */
1000
1001 fdc_ienable();
1002
1003 out_fdc(NE7CMD_SEEK); /* seek function */
1004 out_fdc(fd->sc_drive); /* drive number */
1005 out_fdc(bp->b_cylinder * fd->sc_type->step);
1006
1007 fd->sc_cylin = -1;
1008 fdc->sc_state = SEEKWAIT;
1009
1010 iostat_seek(fd->sc_dk.dk_stats);
1011 disk_busy(&fd->sc_dk);
1012
1013 callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
1014 return 1;
1015
1016 case DOIO:
1017 doio:
1018 if (finfo)
1019 fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
1020 (char *)finfo;
1021
1022 type = fd->sc_type;
1023 sec = fd->sc_blkno % type->seccyl;
1024 head = sec / type->sectrac;
1025 sec -= head * type->sectrac;
1026 nblks = type->sectrac - sec;
1027 nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
1028 nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
1029 fd->sc_nblks = nblks;
1030 fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE;
1031 #ifdef DIAGNOSTIC
1032 {
1033 int block;
1034
1035 block = (fd->sc_cylin * type->heads + head)
1036 * type->sectrac + sec;
1037 if (block != fd->sc_blkno) {
1038 printf("fdcintr: block %d != blkno %qd\n",
1039 block, fd->sc_blkno);
1040 #ifdef DDB
1041 Debugger();
1042 #endif
1043 }
1044 }
1045 #endif
1046 read = bp->b_flags & B_READ ? 1 : 0;
1047
1048 /*
1049 * Setup pseudo-DMA address & count
1050 */
1051 fddmaaddr = (char *)bp->b_data + fd->sc_skip;
1052 fddmalen = fd->sc_nbytes;
1053
1054 wrt_fdc_reg(fdctl, type->rate);
1055 #ifdef FD_DEBUG
1056 printf("fdcintr: %s drive %d track %d head %d sec %d"
1057 " nblks %d\n", read ? "read" : "write",
1058 fd->sc_drive, fd->sc_cylin, head, sec, nblks);
1059 #endif
1060 fdc_ienable();
1061
1062 if (finfo) {
1063 /* formatting */
1064 if (out_fdc(NE7CMD_FORMAT) < 0) {
1065 fdc->sc_errors = 4;
1066 fdcretry(fdc);
1067 goto loop;
1068 }
1069 out_fdc((head << 2) | fd->sc_drive);
1070 out_fdc(finfo->fd_formb_secshift);
1071 out_fdc(finfo->fd_formb_nsecs);
1072 out_fdc(finfo->fd_formb_gaplen);
1073 out_fdc(finfo->fd_formb_fillbyte);
1074 } else {
1075 if (read)
1076 out_fdc(NE7CMD_READ); /* READ */
1077 else
1078 out_fdc(NE7CMD_WRITE); /* WRITE */
1079 out_fdc((head << 2) | fd->sc_drive);
1080 out_fdc(fd->sc_cylin); /* track */
1081 out_fdc(head); /* head */
1082 out_fdc(sec + 1); /* sector +1 */
1083 out_fdc(type->secsize); /* sector size */
1084 out_fdc(sec + nblks); /* last sectors */
1085 out_fdc(type->gap1); /* gap1 size */
1086 out_fdc(type->datalen); /* data length */
1087 }
1088 fdc->sc_state = IOCOMPLETE;
1089
1090 disk_busy(&fd->sc_dk);
1091
1092 /* allow 2 seconds for operation */
1093 callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
1094 return 1; /* will return later */
1095
1096 case SEEKWAIT:
1097 callout_stop(&fdc->sc_timo_ch);
1098 fdc->sc_state = SEEKCOMPLETE;
1099 /* allow 1/50 second for heads to settle */
1100 callout_reset(&fdc->sc_intr_ch, hz / 50, fdcpseudointr, fdc);
1101 return 1;
1102
1103 case SEEKCOMPLETE:
1104 /* no data on seek */
1105 disk_unbusy(&fd->sc_dk, 0, 0);
1106
1107 /* Make sure seek really happened. */
1108 out_fdc(NE7CMD_SENSEI);
1109 if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
1110 cyl != bp->b_cylinder * fd->sc_type->step) {
1111 #ifdef FD_DEBUG
1112 fdcstatus(&fd->sc_dev, 2, "seek failed");
1113 #endif
1114 fdcretry(fdc);
1115 goto loop;
1116 }
1117 fd->sc_cylin = bp->b_cylinder;
1118 goto doio;
1119
1120 case IOTIMEDOUT:
1121 case SEEKTIMEDOUT:
1122 case RECALTIMEDOUT:
1123 case RESETTIMEDOUT:
1124 fdcretry(fdc);
1125 goto loop;
1126
1127 case IOCOMPLETE: /* IO DONE, post-analyze */
1128 callout_stop(&fdc->sc_timo_ch);
1129
1130 disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
1131 (bp->b_flags & B_READ));
1132
1133 if (fdcresult(fdc) != 7 || (st1 & 0x37) != 0) {
1134 /*
1135 * As the damn chip doesn't seem to have a FIFO,
1136 * accept a few overruns as a fact of life *sigh*
1137 */
1138 if ((st1 & 0x10) && (++fdc->sc_overruns < 4)) {
1139 fdc->sc_state = DOSEEK;
1140 goto loop;
1141 }
1142 #ifdef FD_DEBUG
1143 fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
1144 "read failed" : "write failed");
1145 printf("blkno %qd nblks %d\n",
1146 fd->sc_blkno, fd->sc_nblks);
1147 #endif
1148 fdcretry(fdc);
1149 goto loop;
1150 }
1151 if (fdc->sc_errors) {
1152 diskerr(bp, "fd", "soft error", LOG_PRINTF,
1153 fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
1154 printf("\n");
1155 fdc->sc_errors = 0;
1156 }
1157 fdc->sc_overruns = 0;
1158 fd->sc_blkno += fd->sc_nblks;
1159 fd->sc_skip += fd->sc_nbytes;
1160 fd->sc_bcount -= fd->sc_nbytes;
1161 if (!finfo && fd->sc_bcount > 0) {
1162 bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl;
1163 goto doseek;
1164 }
1165 fdfinish(fd, bp);
1166 goto loop;
1167
1168 case DORESET:
1169 /* try a reset, keep motor on */
1170 fd_set_motor(fdc, 1);
1171 delay(100);
1172 fd_set_motor(fdc, 0);
1173 fdc->sc_state = RESETCOMPLETE;
1174 callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc);
1175 return 1; /* will return later */
1176
1177 case RESETCOMPLETE:
1178 callout_stop(&fdc->sc_timo_ch);
1179 /* clear the controller output buffer */
1180 for (i = 0; i < 4; i++) {
1181 out_fdc(NE7CMD_SENSEI);
1182 (void) fdcresult(fdc);
1183 }
1184
1185 /* fall through */
1186 case DORECAL:
1187 fdc_ienable();
1188
1189 out_fdc(NE7CMD_RECAL); /* recalibrate function */
1190 out_fdc(fd->sc_drive);
1191 fdc->sc_state = RECALWAIT;
1192 callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc);
1193 return 1; /* will return later */
1194
1195 case RECALWAIT:
1196 callout_stop(&fdc->sc_timo_ch);
1197 fdc->sc_state = RECALCOMPLETE;
1198 /* allow 1/30 second for heads to settle */
1199 callout_reset(&fdc->sc_intr_ch, hz / 30, fdcpseudointr, fdc);
1200 return 1; /* will return later */
1201
1202 case RECALCOMPLETE:
1203 out_fdc(NE7CMD_SENSEI);
1204 if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
1205 #ifdef FD_DEBUG
1206 fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
1207 #endif
1208 fdcretry(fdc);
1209 goto loop;
1210 }
1211 fd->sc_cylin = 0;
1212 goto doseek;
1213
1214 case MOTORWAIT:
1215 if (fd->sc_flags & FD_MOTOR_WAIT)
1216 return 1; /* time's not up yet */
1217 goto doseek;
1218
1219 default:
1220 fdcstatus(&fd->sc_dev, 0, "stray interrupt");
1221 return 1;
1222 }
1223 #ifdef DIAGNOSTIC
1224 panic("fdcintr: impossible");
1225 #endif
1226 #undef st0
1227 #undef st1
1228 #undef cyl
1229 }
1230
1231 void
1232 fdcretry(struct fdc_softc *fdc)
1233 {
1234 struct fd_softc *fd;
1235 struct buf *bp;
1236
1237 fd = fdc->sc_drives.tqh_first;
1238 bp = bufq_peek(fd->sc_q);
1239
1240 if (fd->sc_opts & FDOPT_NORETRY)
1241 goto fail;
1242
1243 switch (fdc->sc_errors) {
1244 case 0:
1245 /* try again */
1246 fdc->sc_state = DOSEEK;
1247 break;
1248
1249 case 1: case 2: case 3:
1250 /* didn't work; try recalibrating */
1251 fdc->sc_state = DORECAL;
1252 break;
1253
1254 case 4:
1255 /* still no go; reset the bastard */
1256 fdc->sc_state = DORESET;
1257 break;
1258
1259 default:
1260 fail:
1261 if ((fd->sc_opts & FDOPT_SILENT) == 0) {
1262 diskerr(bp, "fd", "hard error", LOG_PRINTF,
1263 fd->sc_skip / FDC_BSIZE,
1264 (struct disklabel *)NULL);
1265 fdcpstatus(fdc);
1266 }
1267 bp->b_error = EIO;
1268 fdfinish(fd, bp);
1269 }
1270 fdc->sc_errors++;
1271 }
1272
1273 int
1274 fdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
1275 {
1276 struct fd_softc *fd;
1277 struct disklabel buffer;
1278 int error;
1279 struct fdformat_parms *form_parms;
1280 struct fdformat_cmd *form_cmd;
1281 struct ne7_fd_formb *fd_formb;
1282 unsigned int scratch;
1283 int il[FD_MAX_NSEC + 1];
1284 register int i, j;
1285
1286 fd = device_lookup_private(&hdfd_cd, FDUNIT(dev));
1287
1288 switch (cmd) {
1289 case DIOCGDINFO:
1290 fdgetdisklabel(fd, dev);
1291 *(struct disklabel *)addr = *(fd->sc_dk.dk_label);
1292 return 0;
1293
1294 case DIOCGPART:
1295 fdgetdisklabel(fd, dev);
1296 ((struct partinfo *)addr)->disklab = fd->sc_dk.dk_label;
1297 ((struct partinfo *)addr)->part =
1298 &fd->sc_dk.dk_label->d_partitions[RAW_PART];
1299 return(0);
1300
1301 case DIOCWLABEL:
1302 if ((flag & FWRITE) == 0)
1303 return EBADF;
1304 /* XXX do something */
1305 return 0;
1306
1307 case DIOCSDINFO:
1308 case DIOCWDINFO:
1309 if ((flag & FWRITE) == 0)
1310 return EBADF;
1311
1312 fd->sc_flags &= ~FD_HAVELAB; /* Invalid */
1313 error = setdisklabel(&buffer, (struct disklabel *)addr, 0,NULL);
1314 if (error)
1315 return error;
1316
1317 if (cmd == DIOCWDINFO)
1318 error = writedisklabel(dev, fdstrategy, &buffer, NULL);
1319 return error;
1320
1321 case FDIOCGETFORMAT:
1322 form_parms = (struct fdformat_parms *)addr;
1323 form_parms->fdformat_version = FDFORMAT_VERSION;
1324 form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
1325 form_parms->ncyl = fd->sc_type->tracks;
1326 form_parms->nspt = fd->sc_type->sectrac;
1327 form_parms->ntrk = fd->sc_type->heads;
1328 form_parms->stepspercyl = fd->sc_type->step;
1329 form_parms->gaplen = fd->sc_type->gap2;
1330 form_parms->fillbyte = fd->sc_type->fillbyte;
1331 form_parms->interleave = fd->sc_type->interleave;
1332 switch (fd->sc_type->rate) {
1333 case FDC_500KBPS:
1334 form_parms->xfer_rate = 500 * 1024;
1335 break;
1336 case FDC_300KBPS:
1337 form_parms->xfer_rate = 300 * 1024;
1338 break;
1339 case FDC_250KBPS:
1340 form_parms->xfer_rate = 250 * 1024;
1341 break;
1342 case FDC_125KBPS:
1343 form_parms->xfer_rate = 125 * 1024;
1344 break;
1345 default:
1346 return EINVAL;
1347 }
1348 return 0;
1349
1350 case FDIOCSETFORMAT:
1351 if((flag & FWRITE) == 0)
1352 return EBADF; /* must be opened for writing */
1353 form_parms = (struct fdformat_parms *)addr;
1354 if (form_parms->fdformat_version != FDFORMAT_VERSION)
1355 return EINVAL; /* wrong version of formatting prog */
1356
1357 scratch = form_parms->nbps >> 7;
1358 if ((form_parms->nbps & 0x7f) || ffs(scratch) == 0 ||
1359 scratch & ~(1 << (ffs(scratch)-1)))
1360 /* not a power-of-two multiple of 128 */
1361 return EINVAL;
1362
1363 switch (form_parms->xfer_rate) {
1364 case 500 * 1024:
1365 fd->sc_type->rate = FDC_500KBPS;
1366 break;
1367 case 300 * 1024:
1368 fd->sc_type->rate = FDC_300KBPS;
1369 break;
1370 case 250 * 1024:
1371 fd->sc_type->rate = FDC_250KBPS;
1372 break;
1373 case 125 * 1024:
1374 fd->sc_type->rate = FDC_125KBPS;
1375 break;
1376 default:
1377 return EINVAL;
1378 }
1379
1380 if (form_parms->nspt > FD_MAX_NSEC ||
1381 form_parms->fillbyte > 0xff ||
1382 form_parms->interleave > 0xff)
1383 return EINVAL;
1384 fd->sc_type->sectrac = form_parms->nspt;
1385 if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
1386 return EINVAL;
1387 fd->sc_type->heads = form_parms->ntrk;
1388 fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
1389 fd->sc_type->secsize = ffs(scratch)-1;
1390 fd->sc_type->gap2 = form_parms->gaplen;
1391 fd->sc_type->tracks = form_parms->ncyl;
1392 fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
1393 form_parms->nbps / DEV_BSIZE;
1394 fd->sc_type->step = form_parms->stepspercyl;
1395 fd->sc_type->fillbyte = form_parms->fillbyte;
1396 fd->sc_type->interleave = form_parms->interleave;
1397 return 0;
1398
1399 case FDIOCFORMAT_TRACK:
1400 if((flag & FWRITE) == 0)
1401 return EBADF; /* must be opened for writing */
1402 form_cmd = (struct fdformat_cmd *)addr;
1403 if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
1404 return EINVAL; /* wrong version of formatting prog */
1405
1406 if (form_cmd->head >= fd->sc_type->heads ||
1407 form_cmd->cylinder >= fd->sc_type->tracks) {
1408 return EINVAL;
1409 }
1410
1411 fd_formb = malloc(sizeof(struct ne7_fd_formb),
1412 M_TEMP, M_NOWAIT);
1413 if (fd_formb == 0)
1414 return ENOMEM;
1415
1416 fd_formb->head = form_cmd->head;
1417 fd_formb->cyl = form_cmd->cylinder;
1418 fd_formb->transfer_rate = fd->sc_type->rate;
1419 fd_formb->fd_formb_secshift = fd->sc_type->secsize;
1420 fd_formb->fd_formb_nsecs = fd->sc_type->sectrac;
1421 fd_formb->fd_formb_gaplen = fd->sc_type->gap2;
1422 fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte;
1423
1424 bzero(il,sizeof il);
1425 for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) {
1426 while (il[(j%fd_formb->fd_formb_nsecs)+1])
1427 j++;
1428 il[(j%fd_formb->fd_formb_nsecs)+1] = i;
1429 j += fd->sc_type->interleave;
1430 }
1431 for (i = 0; i < fd_formb->fd_formb_nsecs; i++) {
1432 fd_formb->fd_formb_cylno(i) = form_cmd->cylinder;
1433 fd_formb->fd_formb_headno(i) = form_cmd->head;
1434 fd_formb->fd_formb_secno(i) = il[i+1];
1435 fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize;
1436 }
1437
1438 error = fdformat(dev, fd_formb, l->l_proc);
1439 free(fd_formb, M_TEMP);
1440 return error;
1441
1442 case FDIOCGETOPTS: /* get drive options */
1443 *(int *)addr = fd->sc_opts;
1444 return 0;
1445
1446 case FDIOCSETOPTS: /* set drive options */
1447 fd->sc_opts = *(int *)addr;
1448 return 0;
1449
1450
1451 default:
1452 return ENOTTY;
1453 }
1454
1455 #ifdef DIAGNOSTIC
1456 panic("fdioctl: impossible");
1457 #endif
1458 }
1459
1460 int
1461 fdformat(dev_t dev, struct ne7_fd_formb *finfo, struct proc *p)
1462 {
1463 int rv = 0;
1464 struct fd_softc *fd = device_lookup_private(&hdfd_cd, FDUNIT(dev));
1465 struct fd_type *type = fd->sc_type;
1466 struct buf *bp;
1467
1468 /* set up a buffer header for fdstrategy() */
1469 bp = getiobuf(NULL, false);
1470 if(bp == 0)
1471 return ENOBUFS;
1472 bzero((void *)bp, sizeof(struct buf));
1473 bp->b_flags = B_PHYS | B_FORMAT;
1474 bp->b_cflags |= BC_BUSY;
1475 bp->b_proc = p;
1476 bp->b_dev = dev;
1477
1478 /*
1479 * calculate a fake blkno, so fdstrategy() would initiate a
1480 * seek to the requested cylinder
1481 */
1482 bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
1483 + finfo->head * type->sectrac) * FDC_BSIZE / DEV_BSIZE;
1484
1485 bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1486 bp->b_data = (void *)finfo;
1487
1488 #ifdef DEBUG
1489 printf("fdformat: blkno %x count %lx\n", bp->b_blkno, bp->b_bcount);
1490 #endif
1491
1492 /* now do the format */
1493 fdstrategy(bp);
1494
1495 /* ...and wait for it to complete */
1496 mutex_enter(bp->b_objlock);
1497 while(!(bp->b_oflags & BO_DONE)) {
1498 rv = cv_timedwait(&bp->b_done, bp->b_objlock, 20 * hz);
1499 if (rv == EWOULDBLOCK)
1500 break;
1501 }
1502 mutex_exit(bp->b_objlock);
1503
1504 if (rv == EWOULDBLOCK) {
1505 /* timed out */
1506 rv = EIO;
1507 biodone(bp);
1508 } else if (bp->b_error != 0) {
1509 rv = bp->b_error;
1510 }
1511 putiobuf(bp);
1512 return rv;
1513 }
1514
1515
1516 /*
1517 * Obtain a disklabel. Either a real one from the disk or, if there
1518 * is none, a fake one.
1519 */
1520 static void
1521 fdgetdisklabel(struct fd_softc *fd, dev_t dev)
1522 {
1523 struct disklabel *lp;
1524 struct cpu_disklabel cpulab;
1525
1526 if (fd->sc_flags & FD_HAVELAB)
1527 return; /* Already got one */
1528
1529 lp = fd->sc_dk.dk_label;
1530
1531 bzero(lp, sizeof(*lp));
1532 bzero(&cpulab, sizeof(cpulab));
1533
1534 lp->d_secpercyl = fd->sc_type->seccyl;
1535 lp->d_type = DTYPE_FLOPPY;
1536 lp->d_secsize = FDC_BSIZE;
1537 lp->d_secperunit = fd->sc_type->size;
1538
1539 /*
1540 * If there is no label on the disk: fake one
1541 */
1542 if (readdisklabel(dev, fdstrategy, lp, &cpulab) != NULL)
1543 fdgetdefaultlabel(fd, lp, RAW_PART);
1544 fd->sc_flags |= FD_HAVELAB;
1545
1546 if ((FDC_BSIZE * fd->sc_type->size)
1547 < (lp->d_secsize * lp->d_secperunit)) {
1548 /*
1549 * XXX: Ignore these fields. If you drop a vnddisk
1550 * on more than one floppy, you'll get disturbing
1551 * sounds!
1552 */
1553 lp->d_secpercyl = fd->sc_type->seccyl;
1554 lp->d_type = DTYPE_FLOPPY;
1555 lp->d_secsize = FDC_BSIZE;
1556 lp->d_secperunit = fd->sc_type->size;
1557 }
1558 }
1559
1560 /*
1561 * Build defaultdisk label. For now we only create a label from what we
1562 * know from 'sc'.
1563 */
1564 static void
1565 fdgetdefaultlabel(struct fd_softc *fd, struct disklabel *lp, int part)
1566 {
1567 bzero(lp, sizeof(struct disklabel));
1568
1569 lp->d_secsize = 128 * (1 << fd->sc_type->secsize);
1570 lp->d_ntracks = fd->sc_type->heads;
1571 lp->d_nsectors = fd->sc_type->sectrac;
1572 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1573 lp->d_ncylinders = fd->sc_type->size / lp->d_secpercyl;
1574 lp->d_secperunit = fd->sc_type->size;
1575
1576 lp->d_type = DTYPE_FLOPPY;
1577 lp->d_rpm = 300; /* good guess I suppose. */
1578 lp->d_interleave = 1; /* FIXME: is this OK? */
1579 lp->d_bbsize = 0;
1580 lp->d_sbsize = 0;
1581 lp->d_npartitions = part + 1;
1582 lp->d_trkseek = 6000; /* Who cares... */
1583 lp->d_magic = DISKMAGIC;
1584 lp->d_magic2 = DISKMAGIC;
1585 lp->d_checksum = dkcksum(lp);
1586 lp->d_partitions[part].p_size = lp->d_secperunit;
1587 lp->d_partitions[part].p_fstype = FS_UNUSED;
1588 lp->d_partitions[part].p_fsize = 1024;
1589 lp->d_partitions[part].p_frag = 8;
1590 }
1591