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