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