1 /*	$OpenBSD: fd.c,v 1.50 2005/03/16 19:58:40 miod Exp $	*/
2 /*	$NetBSD: fd.c,v 1.90 1996/05/12 23:12:03 mycroft Exp $	*/
3 
4 /*-
5  * Copyright (c) 1993, 1994, 1995, 1996 Charles Hannum.
6  * Copyright (c) 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Don Ahn.
11  *
12  * Portions Copyright (c) 1993, 1994 by
13  *  jc@irbs.UUCP (John Capo)
14  *  vak@zebub.msk.su (Serge Vakulenko)
15  *  ache@astral.msk.su (Andrew A. Chernov)
16  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
43  */
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/file.h>
49 #include <sys/ioctl.h>
50 #include <sys/device.h>
51 #include <sys/disklabel.h>
52 #include <sys/dkstat.h>
53 #include <sys/disk.h>
54 #include <sys/buf.h>
55 #include <sys/malloc.h>
56 #include <sys/uio.h>
57 #include <sys/mtio.h>
58 #include <sys/proc.h>
59 #include <sys/syslog.h>
60 #include <sys/queue.h>
61 #include <sys/timeout.h>
62 
63 #include <machine/cpu.h>
64 #include <machine/bus.h>
65 #include <machine/conf.h>
66 #include <machine/intr.h>
67 #include <machine/ioctl_fd.h>
68 
69 #include <dev/isa/isavar.h>
70 #include <dev/isa/isadmavar.h>
71 #include <dev/isa/fdreg.h>
72 
73 #if defined(i386)
74 #include <i386/isa/nvram.h>
75 #endif
76 
77 #include <dev/isa/fdlink.h>
78 
79 /* XXX misuse a flag to identify format operation */
80 #define B_FORMAT B_XXX
81 
82 #define b_cylin b_resid
83 
84 /* fd_type struct now in ioctl_fd.h */
85 
86 /* The order of entries in the following table is important -- BEWARE! */
87 struct fd_type fd_types[] = {
88         { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB"    }, /* 1.44MB diskette */
89         { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB"    }, /* 1.2 MB AT-diskettes */
90         {  9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */
91         {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */
92         {  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB"    }, /* 3.5" 720kB diskette */
93         {  9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x"  }, /* 720kB in 1.2MB drive */
94         {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x"  }, /* 360kB in 720kB drive */
95 	{ 36,2,72,2,0xff,0xaf,0x1b,0x54,80,5760,1,FDC_500KBPS,"2.88MB"    },  /* 2.88MB diskette */
96 	{  8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,"1.2MB/[1024bytes/sector]" }	/* 1.2 MB japanese format */
97 };
98 
99 /* software state, per disk (with up to 4 disks per ctlr) */
100 struct fd_softc {
101 	struct device sc_dev;
102 	struct disk sc_dk;
103 
104 	struct fd_type *sc_deftype;	/* default type descriptor */
105 	struct fd_type *sc_type;	/* current type descriptor */
106 
107 	daddr_t	sc_blkno;	/* starting block number */
108 	int sc_bcount;		/* byte count left */
109  	int sc_opts;			/* user-set options */
110 	int sc_skip;		/* bytes already transferred */
111 	int sc_nblks;		/* number of blocks currently tranferring */
112 	int sc_nbytes;		/* number of bytes currently tranferring */
113 
114 	int sc_drive;		/* physical unit number */
115 	int sc_flags;
116 #define	FD_OPEN		0x01		/* it's open */
117 #define	FD_MOTOR	0x02		/* motor should be on */
118 #define	FD_MOTOR_WAIT	0x04		/* motor coming up */
119 	int sc_cylin;		/* where we think the head is */
120 
121 	void *sc_sdhook;	/* saved shutdown hook for drive. */
122 
123 	TAILQ_ENTRY(fd_softc) sc_drivechain;
124 	int sc_ops;		/* I/O ops since last switch */
125 	struct buf sc_q;	/* head of buf chain */
126 	struct timeout fd_motor_on_to;
127 	struct timeout fd_motor_off_to;
128 	struct timeout fdtimeout_to;
129 };
130 
131 /* floppy driver configuration */
132 int fdprobe(struct device *, void *, void *);
133 void fdattach(struct device *, struct device *, void *);
134 
135 struct cfattach fd_ca = {
136 	sizeof(struct fd_softc), fdprobe, fdattach
137 };
138 
139 struct cfdriver fd_cd = {
140 	NULL, "fd", DV_DISK
141 };
142 
143 void fdgetdisklabel(struct fd_softc *);
144 int fd_get_parms(struct fd_softc *);
145 void fdstrategy(struct buf *);
146 void fdstart(struct fd_softc *);
147 int fdintr(struct fdc_softc *);
148 
149 struct dkdriver fddkdriver = { fdstrategy };
150 
151 void fd_set_motor(struct fdc_softc *fdc, int reset);
152 void fd_motor_off(void *arg);
153 void fd_motor_on(void *arg);
154 void fdfinish(struct fd_softc *fd, struct buf *bp);
155 int fdformat(dev_t, struct fd_formb *, struct proc *);
156 __inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
157 void fdretry(struct fd_softc *);
158 void fdtimeout(void *);
159 
160 int
fdprobe(parent,match,aux)161 fdprobe(parent, match, aux)
162 	struct device *parent;
163 	void *match, *aux;
164 {
165 	struct fdc_softc *fdc = (void *)parent;
166 	struct cfdata *cf = match;
167 	struct fdc_attach_args *fa = aux;
168 	int drive = fa->fa_drive;
169 	bus_space_tag_t iot = fdc->sc_iot;
170 	bus_space_handle_t ioh = fdc->sc_ioh;
171 	int n;
172 
173 	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive)
174 		return 0;
175 	/*
176 	 * XXX
177 	 * This is to work around some odd interactions between this driver
178 	 * and SMC Ethernet cards.
179 	 */
180 	if (cf->cf_loc[0] == -1 && drive >= 2)
181 		return 0;
182 
183 	/*
184 	 * We want to keep the flags config gave us.
185 	 */
186 	fa->fa_flags = cf->cf_flags;
187 
188 	/* select drive and turn on motor */
189 	bus_space_write_1(iot, ioh, fdout, drive | FDO_FRST | FDO_MOEN(drive));
190 	/* wait for motor to spin up */
191 	delay(250000);
192 	out_fdc(iot, ioh, NE7CMD_RECAL);
193 	out_fdc(iot, ioh, drive);
194 	/* wait for recalibrate */
195 	delay(2000000);
196 	out_fdc(iot, ioh, NE7CMD_SENSEI);
197 	n = fdcresult(fdc);
198 #ifdef FD_DEBUG
199 	{
200 		int i;
201 		printf("fdprobe: status");
202 		for (i = 0; i < n; i++)
203 			printf(" %x", fdc->sc_status[i]);
204 		printf("\n");
205 	}
206 #endif
207 
208 	/* turn off motor */
209 	delay(250000);
210 	bus_space_write_1(iot, ioh, fdout, FDO_FRST);
211 
212 	/* flags & 0x20 forces the drive to be found even if it won't probe */
213 	if (!(fa->fa_flags & 0x20) && (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20))
214 		return 0;
215 
216 	return 1;
217 }
218 
219 /*
220  * Controller is working, and drive responded.  Attach it.
221  */
222 void
fdattach(parent,self,aux)223 fdattach(parent, self, aux)
224 	struct device *parent, *self;
225 	void *aux;
226 {
227 	struct fdc_softc *fdc = (void *)parent;
228 	struct fd_softc *fd = (void *)self;
229 	struct fdc_attach_args *fa = aux;
230 	struct fd_type *type = fa->fa_deftype;
231 	int drive = fa->fa_drive;
232 
233 	if (!type || (fa->fa_flags & 0x10)) {
234 		/* The config has overridden this. */
235 		switch (fa->fa_flags & 0x07) {
236 		case 1:	/* 2.88MB */
237 			type = &fd_types[7];
238 			break;
239 		case 2:	/* 1.44MB */
240 			type = &fd_types[0];
241 			break;
242 		case 3: /* 1.2MB */
243 			type = &fd_types[1];
244 			break;
245 		case 4: /* 720K */
246 			type = &fd_types[4];
247 			break;
248 		case 5: /* 360K */
249 			type = &fd_types[3];
250 			break;
251 		case 6:	/* 1.2 MB japanese format */
252 			type = &fd_types[8];
253 			break;
254 #ifdef __alpha__
255 		default:
256 			/* 1.44MB, how to detect others?
257 			 * idea from NetBSD -- jay@rootaction.net
258                          */
259 			type = &fd_types[0];
260 #endif
261 		}
262 	}
263 
264 	if (type)
265 		printf(": %s %d cyl, %d head, %d sec\n", type->name,
266 		    type->tracks, type->heads, type->sectrac);
267 	else
268 		printf(": density unknown\n");
269 
270 	fd->sc_cylin = -1;
271 	fd->sc_drive = drive;
272 	fd->sc_deftype = type;
273 	fdc->sc_type[drive] = FDC_TYPE_DISK;
274 	fdc->sc_link.fdlink.sc_fd[drive] = fd;
275 
276 	/*
277 	 * Initialize and attach the disk structure.
278 	 */
279 	fd->sc_dk.dk_name = fd->sc_dev.dv_xname;
280 	fd->sc_dk.dk_driver = &fddkdriver;
281 	disk_attach(&fd->sc_dk);
282 
283 	dk_establish(&fd->sc_dk, &fd->sc_dev);
284 	/* Needed to power off if the motor is on when we halt. */
285 	fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd);
286 
287 	/* Setup timeout structures */
288 	timeout_set(&fd->fd_motor_on_to, fd_motor_on, fd);
289 	timeout_set(&fd->fd_motor_off_to, fd_motor_off, fd);
290 	timeout_set(&fd->fdtimeout_to, fdtimeout, fd);
291 }
292 
293 /*
294  * Translate nvram type into internal data structure.  Return NULL for
295  * none/unknown/unusable.
296  */
297 struct fd_type *
fd_nvtotype(fdc,nvraminfo,drive)298 fd_nvtotype(fdc, nvraminfo, drive)
299 	char *fdc;
300 	int nvraminfo, drive;
301 {
302 #ifdef __alpha__
303 	/* Alpha:  assume 1.44MB, idea from NetBSD sys/dev/isa/fd.c
304 	 * -- jay@rootaction.net
305 	 */
306 	return &fd_types[0]; /* 1.44MB */
307 #else
308 	int type;
309 
310 	type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0;
311 	switch (type) {
312 	case NVRAM_DISKETTE_NONE:
313 		return NULL;
314 	case NVRAM_DISKETTE_12M:
315 		return &fd_types[1];
316 	case NVRAM_DISKETTE_TYPE5:
317 	case NVRAM_DISKETTE_TYPE6:
318 		return &fd_types[7];
319 	case NVRAM_DISKETTE_144M:
320 		return &fd_types[0];
321 	case NVRAM_DISKETTE_360K:
322 		return &fd_types[3];
323 	case NVRAM_DISKETTE_720K:
324 		return &fd_types[4];
325 	default:
326 		printf("%s: drive %d: unknown device type 0x%x\n",
327 		    fdc, drive, type);
328 		return NULL;
329 	}
330 #endif
331 }
332 
333 __inline struct fd_type *
fd_dev_to_type(fd,dev)334 fd_dev_to_type(fd, dev)
335 	struct fd_softc *fd;
336 	dev_t dev;
337 {
338 	int type = FDTYPE(dev);
339 
340 	if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
341 		return NULL;
342 	return type ? &fd_types[type - 1] : fd->sc_deftype;
343 }
344 
345 void
fdstrategy(bp)346 fdstrategy(bp)
347 	register struct buf *bp;	/* IO operation to perform */
348 {
349 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(bp->b_dev)];
350 	int sz;
351  	int s;
352 	int fd_bsize = 128 << fd->sc_type->secsize;
353 	int bf = fd_bsize / DEV_BSIZE;
354 
355 	/* Valid unit, controller, and request? */
356 	if (bp->b_blkno < 0 ||
357 	    (((bp->b_blkno % bf) != 0 ||
358 	      (bp->b_bcount % fd_bsize) != 0) &&
359 	     (bp->b_flags & B_FORMAT) == 0)) {
360 		bp->b_error = EINVAL;
361 		goto bad;
362 	}
363 
364 	/* If it's a null transfer, return immediately. */
365 	if (bp->b_bcount == 0)
366 		goto done;
367 
368 	sz = howmany(bp->b_bcount, DEV_BSIZE);
369 
370 	if (bp->b_blkno + sz > fd->sc_type->size * bf) {
371 		sz = fd->sc_type->size * bf - bp->b_blkno;
372 		if (sz == 0)
373 			/* If exactly at end of disk, return EOF. */
374 			goto done;
375 		if (sz < 0) {
376 			/* If past end of disk, return EINVAL. */
377 			bp->b_error = EINVAL;
378 			goto bad;
379 		}
380 		/* Otherwise, truncate request. */
381 		bp->b_bcount = sz << DEV_BSHIFT;
382 	}
383 
384  	bp->b_cylin = bp->b_blkno / (fd_bsize / DEV_BSIZE) / fd->sc_type->seccyl;
385 
386 #ifdef FD_DEBUG
387 	printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d sz %d\n",
388 	    bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylin, sz);
389 #endif
390 
391 	/* Queue transfer on drive, activate drive and controller if idle. */
392 	s = splbio();
393 	disksort(&fd->sc_q, bp);
394 	timeout_del(&fd->fd_motor_off_to); /* a good idea */
395 	if (!fd->sc_q.b_active)
396 		fdstart(fd);
397 #ifdef DIAGNOSTIC
398 	else {
399 		struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
400 		if (fdc->sc_state == DEVIDLE) {
401 			printf("fdstrategy: controller inactive\n");
402 			fdcstart(fdc);
403 		}
404 	}
405 #endif
406 	splx(s);
407 	return;
408 
409 bad:
410 	bp->b_flags |= B_ERROR;
411 done:
412 	/* Toss transfer; we're done early. */
413 	bp->b_resid = bp->b_bcount;
414 	s = splbio();
415 	biodone(bp);
416 	splx(s);
417 }
418 
419 void
fdstart(fd)420 fdstart(fd)
421 	struct fd_softc *fd;
422 {
423 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
424 	int active = !TAILQ_EMPTY(&fdc->sc_link.fdlink.sc_drives);
425 
426 	/* Link into controller queue. */
427 	fd->sc_q.b_active = 1;
428 	TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
429 
430 	/* If controller not already active, start it. */
431 	if (!active)
432 		fdcstart(fdc);
433 }
434 
435 void
fdfinish(fd,bp)436 fdfinish(fd, bp)
437 	struct fd_softc *fd;
438 	struct buf *bp;
439 {
440 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
441 
442 	splassert(IPL_BIO);
443 
444 	/*
445 	 * Move this drive to the end of the queue to give others a `fair'
446 	 * chance.  We only force a switch if N operations are completed while
447 	 * another drive is waiting to be serviced, since there is a long motor
448 	 * startup delay whenever we switch.
449 	 */
450 	if (TAILQ_NEXT(fd, sc_drivechain) != NULL && ++fd->sc_ops >= 8) {
451 		fd->sc_ops = 0;
452 		TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
453 		if (bp->b_actf) {
454 			TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd,
455 					  sc_drivechain);
456 		} else
457 			fd->sc_q.b_active = 0;
458 	}
459 	bp->b_resid = fd->sc_bcount;
460 	fd->sc_skip = 0;
461 	fd->sc_q.b_actf = bp->b_actf;
462 
463 	biodone(bp);
464 	/* turn off motor 5s from now */
465 	timeout_add(&fd->fd_motor_off_to, 5 * hz);
466 	fdc->sc_state = DEVIDLE;
467 }
468 
469 int
fdread(dev,uio,flags)470 fdread(dev, uio, flags)
471 	dev_t dev;
472 	struct uio *uio;
473 	int flags;
474 {
475 
476 	return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio));
477 }
478 
479 int
fdwrite(dev,uio,flags)480 fdwrite(dev, uio, flags)
481 	dev_t dev;
482 	struct uio *uio;
483 	int flags;
484 {
485 
486 	return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio));
487 }
488 
489 void
fd_set_motor(fdc,reset)490 fd_set_motor(fdc, reset)
491 	struct fdc_softc *fdc;
492 	int reset;
493 {
494 	struct fd_softc *fd;
495 	u_char status;
496 	int n;
497 
498 	if ((fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives)) != NULL)
499 		status = fd->sc_drive;
500 	else
501 		status = 0;
502 	if (!reset)
503 		status |= FDO_FRST | FDO_FDMAEN;
504 	for (n = 0; n < 4; n++)
505 		if ((fd = fdc->sc_link.fdlink.sc_fd[n])
506 		    && (fd->sc_flags & FD_MOTOR))
507 			status |= FDO_MOEN(n);
508 	bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, status);
509 }
510 
511 void
fd_motor_off(arg)512 fd_motor_off(arg)
513 	void *arg;
514 {
515 	struct fd_softc *fd = arg;
516 	int s;
517 
518 	s = splbio();
519 	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
520 	fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0);
521 	splx(s);
522 }
523 
524 void
fd_motor_on(arg)525 fd_motor_on(arg)
526 	void *arg;
527 {
528 	struct fd_softc *fd = arg;
529 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
530 	int s;
531 
532 	s = splbio();
533 	fd->sc_flags &= ~FD_MOTOR_WAIT;
534 	if ((TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives) == fd)
535 	    && (fdc->sc_state == MOTORWAIT))
536 		(void) fdintr(fdc);
537 	splx(s);
538 }
539 
540 int
fdopen(dev,flags,mode,p)541 fdopen(dev, flags, mode, p)
542 	dev_t dev;
543 	int flags;
544 	int mode;
545 	struct proc *p;
546 {
547  	int unit;
548 	struct fd_softc *fd;
549 	struct fd_type *type;
550 
551 	unit = FDUNIT(dev);
552 	if (unit >= fd_cd.cd_ndevs)
553 		return ENXIO;
554 	fd = fd_cd.cd_devs[unit];
555 	if (fd == 0)
556 		return ENXIO;
557 	type = fd_dev_to_type(fd, dev);
558 	if (type == NULL)
559 		return ENXIO;
560 
561 	if ((fd->sc_flags & FD_OPEN) != 0 &&
562 	    fd->sc_type != type)
563 		return EBUSY;
564 
565 	fd->sc_type = type;
566 	fd->sc_cylin = -1;
567 	fd->sc_flags |= FD_OPEN;
568 
569 	return 0;
570 }
571 
572 int
fdclose(dev,flags,mode,p)573 fdclose(dev, flags, mode, p)
574 	dev_t dev;
575 	int flags;
576 	int mode;
577 	struct proc *p;
578 {
579 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
580 
581 	fd->sc_flags &= ~FD_OPEN;
582 	fd->sc_opts &= ~FDOPT_NORETRY;
583 	return 0;
584 }
585 
586 int
fdsize(dev)587 fdsize(dev)
588 	dev_t dev;
589 {
590 
591 	/* Swapping to floppies would not make sense. */
592 	return -1;
593 }
594 
595 int
fddump(dev,blkno,va,size)596 fddump(dev, blkno, va, size)
597 	dev_t dev;
598 	daddr_t blkno;
599 	caddr_t va;
600 	size_t size;
601 {
602 
603 	/* Not implemented. */
604 	return ENXIO;
605 }
606 
607 /*
608  * Called from the controller.
609  */
610 int
fdintr(fdc)611 fdintr(fdc)
612 	struct fdc_softc *fdc;
613 {
614 #define	st0	fdc->sc_status[0]
615 #define	cyl	fdc->sc_status[1]
616 	struct fd_softc *fd;
617 	struct buf *bp;
618 	bus_space_tag_t iot = fdc->sc_iot;
619 	bus_space_handle_t ioh = fdc->sc_ioh;
620 	bus_space_handle_t ioh_ctl = fdc->sc_ioh_ctl;
621 	int read, head, sec, i, nblks;
622 	struct fd_type *type;
623 	struct fd_formb *finfo = NULL;
624 	int fd_bsize, bf;
625 
626 loop:
627 	/* Is there a transfer to this drive?  If not, deactivate drive. */
628 	fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives);
629 	if (fd == NULL) {
630 		fdc->sc_state = DEVIDLE;
631 		return 1;
632 	}
633 	fd_bsize = 128 << fd->sc_type->secsize;
634 	bf = fd_bsize / FDC_BSIZE;
635 
636 	bp = fd->sc_q.b_actf;
637 	if (bp == NULL) {
638 		fd->sc_ops = 0;
639 		TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
640 		fd->sc_q.b_active = 0;
641 		goto loop;
642 	}
643 
644 	if (bp->b_flags & B_FORMAT)
645 	    finfo = (struct fd_formb *)bp->b_data;
646 
647 	switch (fdc->sc_state) {
648 	case DEVIDLE:
649 		fdc->sc_errors = 0;
650 		fd->sc_skip = 0;
651 		fd->sc_bcount = bp->b_bcount;
652 		fd->sc_blkno = bp->b_blkno / (fd_bsize / DEV_BSIZE);
653 		timeout_del(&fd->fd_motor_off_to);
654 		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
655 			fdc->sc_state = MOTORWAIT;
656 			return 1;
657 		}
658 		if ((fd->sc_flags & FD_MOTOR) == 0) {
659 			/* Turn on the motor, being careful about pairing. */
660 			struct fd_softc *ofd =
661 				fdc->sc_link.fdlink.sc_fd[fd->sc_drive ^ 1];
662 			if (ofd && ofd->sc_flags & FD_MOTOR) {
663 				timeout_del(&ofd->fd_motor_off_to);
664 				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
665 			}
666 			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
667 			fd_set_motor(fdc, 0);
668 			fdc->sc_state = MOTORWAIT;
669 			/* Allow .25s for motor to stabilize. */
670 			timeout_add(&fd->fd_motor_on_to, hz / 4);
671 			return 1;
672 		}
673 		/* Make sure the right drive is selected. */
674 		fd_set_motor(fdc, 0);
675 
676 		/* fall through */
677 	case DOSEEK:
678 	doseek:
679 		if (fd->sc_cylin == bp->b_cylin)
680 			goto doio;
681 
682 		out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */
683 		out_fdc(iot, ioh, fd->sc_type->steprate);
684 		out_fdc(iot, ioh, 6);		/* XXX head load time == 6ms */
685 
686 		out_fdc(iot, ioh, NE7CMD_SEEK);	/* seek function */
687 		out_fdc(iot, ioh, fd->sc_drive);	/* drive number */
688 		out_fdc(iot, ioh, bp->b_cylin * fd->sc_type->step);
689 
690 		fd->sc_cylin = -1;
691 		fdc->sc_state = SEEKWAIT;
692 
693 		fd->sc_dk.dk_seek++;
694 		disk_busy(&fd->sc_dk);
695 
696 		timeout_add(&fd->fdtimeout_to, 4 * hz);
697 		return 1;
698 
699 	case DOIO:
700 	doio:
701 		type = fd->sc_type;
702 		if (finfo)
703 		    fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
704 			(char *)finfo;
705 		sec = fd->sc_blkno % type->seccyl;
706 		nblks = type->seccyl - sec;
707 		nblks = min(nblks, fd->sc_bcount / fd_bsize);
708 		nblks = min(nblks, FDC_MAXIOSIZE / fd_bsize);
709 		fd->sc_nblks = nblks;
710 		fd->sc_nbytes = finfo ? bp->b_bcount : nblks * fd_bsize;
711 		head = sec / type->sectrac;
712 		sec -= head * type->sectrac;
713 #ifdef DIAGNOSTIC
714 		{int block;
715 		 block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec;
716 		 if (block != fd->sc_blkno) {
717 			 printf("fdintr: block %d != blkno %d\n", block, fd->sc_blkno);
718 #ifdef DDB
719 			 Debugger();
720 #endif
721 		 }}
722 #endif
723 		read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
724 		isadma_start(bp->b_data + fd->sc_skip, fd->sc_nbytes,
725 		    fdc->sc_drq, read);
726 		bus_space_write_1(iot, ioh_ctl, fdctl, type->rate);
727 #ifdef FD_DEBUG
728 		printf("fdintr: %s drive %d track %d head %d sec %d nblks %d\n",
729 		    read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head,
730 		    sec, nblks);
731 #endif
732 		if (finfo) {
733                         /* formatting */
734 			if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) {
735 			    fdc->sc_errors = 4;
736 			    fdretry(fd);
737 			    goto loop;
738 			}
739                         out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
740                         out_fdc(iot, ioh, finfo->fd_formb_secshift);
741                         out_fdc(iot, ioh, finfo->fd_formb_nsecs);
742                         out_fdc(iot, ioh, finfo->fd_formb_gaplen);
743                         out_fdc(iot, ioh, finfo->fd_formb_fillbyte);
744 		} else {
745 			if (read)
746 				out_fdc(iot, ioh, NE7CMD_READ);	/* READ */
747 			else
748 				out_fdc(iot, ioh, NE7CMD_WRITE);/* WRITE */
749 			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
750 			out_fdc(iot, ioh, fd->sc_cylin);	/* track */
751 			out_fdc(iot, ioh, head);
752 			out_fdc(iot, ioh, sec + 1);		/* sec +1 */
753 			out_fdc(iot, ioh, type->secsize);	/* sec size */
754 			out_fdc(iot, ioh, type->sectrac);	/* secs/track */
755 			out_fdc(iot, ioh, type->gap1);		/* gap1 size */
756 			out_fdc(iot, ioh, type->datalen);	/* data len */
757 		}
758 		fdc->sc_state = IOCOMPLETE;
759 
760 		disk_busy(&fd->sc_dk);
761 
762 		/* allow 2 seconds for operation */
763 		timeout_add(&fd->fdtimeout_to, 2 * hz);
764 		return 1;				/* will return later */
765 
766 	case SEEKWAIT:
767 		timeout_del(&fd->fdtimeout_to);
768 		fdc->sc_state = SEEKCOMPLETE;
769 		/* allow 1/50 second for heads to settle */
770 		timeout_add(&fdc->fdcpseudointr_to, hz / 50);
771 		return 1;
772 
773 	case SEEKCOMPLETE:
774 		disk_unbusy(&fd->sc_dk, 0, 0);	/* no data on seek */
775 
776 		/* Make sure seek really happened. */
777 		out_fdc(iot, ioh, NE7CMD_SENSEI);
778 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
779 		    cyl != bp->b_cylin * fd->sc_type->step) {
780 #ifdef FD_DEBUG
781 			fdcstatus(&fd->sc_dev, 2, "seek failed");
782 #endif
783 			fdretry(fd);
784 			goto loop;
785 		}
786 		fd->sc_cylin = bp->b_cylin;
787 		goto doio;
788 
789 	case IOTIMEDOUT:
790 		isadma_abort(fdc->sc_drq);
791 	case SEEKTIMEDOUT:
792 	case RECALTIMEDOUT:
793 	case RESETTIMEDOUT:
794 		fdretry(fd);
795 		goto loop;
796 
797 	case IOCOMPLETE: /* IO DONE, post-analyze */
798 		timeout_del(&fd->fdtimeout_to);
799 
800 		disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
801 		    (bp->b_flags & B_READ));
802 
803 		if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) {
804 			isadma_abort(fdc->sc_drq);
805 #ifdef FD_DEBUG
806 			fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
807 			    "read failed" : "write failed");
808 			printf("blkno %d nblks %d\n",
809 			    fd->sc_blkno, fd->sc_nblks);
810 #endif
811 			fdretry(fd);
812 			goto loop;
813 		}
814 		read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
815 		isadma_done(fdc->sc_drq);
816 		if (fdc->sc_errors) {
817 			diskerr(bp, "fd", "soft error", LOG_PRINTF,
818 			    fd->sc_skip / fd_bsize, (struct disklabel *)NULL);
819 			printf("\n");
820 			fdc->sc_errors = 0;
821 		}
822 		fd->sc_blkno += fd->sc_nblks;
823 		fd->sc_skip += fd->sc_nbytes;
824 		fd->sc_bcount -= fd->sc_nbytes;
825 		if (!finfo && fd->sc_bcount > 0) {
826 			bp->b_cylin = fd->sc_blkno / fd->sc_type->seccyl;
827 			goto doseek;
828 		}
829 		fdfinish(fd, bp);
830 		goto loop;
831 
832 	case DORESET:
833 		/* try a reset, keep motor on */
834 		fd_set_motor(fdc, 1);
835 		delay(100);
836 		fd_set_motor(fdc, 0);
837 		fdc->sc_state = RESETCOMPLETE;
838 		timeout_add(&fd->fdtimeout_to, hz / 2);
839 		return 1;			/* will return later */
840 
841 	case RESETCOMPLETE:
842 		timeout_del(&fd->fdtimeout_to);
843 		/* clear the controller output buffer */
844 		for (i = 0; i < 4; i++) {
845 			out_fdc(iot, ioh, NE7CMD_SENSEI);
846 			(void) fdcresult(fdc);
847 		}
848 
849 		/* fall through */
850 	case DORECAL:
851 		out_fdc(iot, ioh, NE7CMD_RECAL);	/* recal function */
852 		out_fdc(iot, ioh, fd->sc_drive);
853 		fdc->sc_state = RECALWAIT;
854 		timeout_add(&fd->fdtimeout_to, 5 * hz);
855 		return 1;			/* will return later */
856 
857 	case RECALWAIT:
858 		timeout_del(&fd->fdtimeout_to);
859 		fdc->sc_state = RECALCOMPLETE;
860 		/* allow 1/30 second for heads to settle */
861 		timeout_add(&fdc->fdcpseudointr_to, hz / 30);
862 		return 1;			/* will return later */
863 
864 	case RECALCOMPLETE:
865 		out_fdc(iot, ioh, NE7CMD_SENSEI);
866 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
867 #ifdef FD_DEBUG
868 			fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
869 #endif
870 			fdretry(fd);
871 			goto loop;
872 		}
873 		fd->sc_cylin = 0;
874 		goto doseek;
875 
876 	case MOTORWAIT:
877 		if (fd->sc_flags & FD_MOTOR_WAIT)
878 			return 1;		/* time's not up yet */
879 		goto doseek;
880 
881 	default:
882 		fdcstatus(&fd->sc_dev, 0, "stray interrupt");
883 		return 1;
884 	}
885 #ifdef DIAGNOSTIC
886 	panic("fdintr: impossible");
887 #endif
888 #undef	st0
889 #undef	cyl
890 }
891 
892 void
fdtimeout(arg)893 fdtimeout(arg)
894 	void *arg;
895 {
896 	struct fd_softc *fd = arg;
897 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
898 	int s;
899 
900 	s = splbio();
901 #ifdef DEBUG
902 	log(LOG_ERR,"fdtimeout: state %d\n", fdc->sc_state);
903 #endif
904 	fdcstatus(&fd->sc_dev, 0, "timeout");
905 
906 	if (fd->sc_q.b_actf)
907 		fdc->sc_state++;
908 	else
909 		fdc->sc_state = DEVIDLE;
910 
911 	(void) fdintr(fdc);
912 	splx(s);
913 }
914 
915 void
fdretry(fd)916 fdretry(fd)
917 	struct fd_softc *fd;
918 {
919 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
920 	struct buf *bp = fd->sc_q.b_actf;
921 
922 	if (fd->sc_opts & FDOPT_NORETRY)
923 	    goto fail;
924 	switch (fdc->sc_errors) {
925 	case 0:
926 		/* try again */
927 		fdc->sc_state = DOSEEK;
928 		break;
929 
930 	case 1: case 2: case 3:
931 		/* didn't work; try recalibrating */
932 		fdc->sc_state = DORECAL;
933 		break;
934 
935 	case 4:
936 		/* still no go; reset the bastard */
937 		fdc->sc_state = DORESET;
938 		break;
939 
940 	default:
941 	fail:
942 		diskerr(bp, "fd", "hard error", LOG_PRINTF,
943 		    fd->sc_skip / (128 << fd->sc_type->secsize),
944 		    (struct disklabel *)NULL);
945 		printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n",
946 		    fdc->sc_status[0], NE7_ST0BITS,
947 		    fdc->sc_status[1], NE7_ST1BITS,
948 		    fdc->sc_status[2], NE7_ST2BITS,
949 		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
950 
951 		bp->b_flags |= B_ERROR;
952 		bp->b_error = EIO;
953 		fdfinish(fd, bp);
954 	}
955 	fdc->sc_errors++;
956 }
957 
958 int
fdioctl(dev,cmd,addr,flag,p)959 fdioctl(dev, cmd, addr, flag, p)
960 	dev_t dev;
961 	u_long cmd;
962 	caddr_t addr;
963 	int flag;
964 	struct proc *p;
965 {
966 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
967 	struct disklabel dl, *lp = &dl;
968 	struct cpu_disklabel cdl;
969 	char *errstring;
970 	int error;
971 
972 	switch (cmd) {
973 	case MTIOCTOP:
974 		if (((struct mtop *)addr)->mt_op != MTOFFL)
975 			return EIO;
976 		return (0);
977 	case DIOCGDINFO:
978 		bzero(lp, sizeof(*lp));
979 		bzero(&cdl, sizeof(struct cpu_disklabel));
980 
981 		lp->d_secsize = 128 << fd->sc_type->secsize;
982 		lp->d_secpercyl = fd->sc_type->seccyl;
983 		lp->d_ntracks = fd->sc_type->heads;
984 		lp->d_nsectors = fd->sc_type->sectrac;
985 		lp->d_ncylinders = fd->sc_type->tracks;
986 
987 		strncpy(lp->d_typename, "floppy disk", sizeof lp->d_typename);
988 		lp->d_type = DTYPE_FLOPPY;
989 		strncpy(lp->d_packname, "fictitious", sizeof lp->d_packname);
990 		lp->d_secperunit = fd->sc_type->size;
991 		lp->d_rpm = 300;
992 		lp->d_interleave = 1;
993 		lp->d_flags = D_REMOVABLE;
994 
995 		lp->d_partitions[RAW_PART].p_offset = 0;
996 		lp->d_partitions[RAW_PART].p_size =
997 		    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
998 		lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
999 		lp->d_npartitions = RAW_PART + 1;
1000 
1001 		lp->d_magic = DISKMAGIC;
1002 		lp->d_magic2 = DISKMAGIC;
1003 		lp->d_checksum = dkcksum(lp);
1004 
1005 		errstring = readdisklabel(dev, fdstrategy, lp, &cdl, 0);
1006 		if (errstring) {
1007 			/*printf("%s: %s\n", fd->sc_dev.dv_xname, errstring); */
1008 		}
1009 
1010 		*(struct disklabel *)addr = *lp;
1011 		return 0;
1012 
1013 	case DIOCWLABEL:
1014 		if ((flag & FWRITE) == 0)
1015 			return EBADF;
1016 		/* XXX do something */
1017 		return 0;
1018 
1019 	case DIOCWDINFO:
1020 		if ((flag & FWRITE) == 0)
1021 			return EBADF;
1022 
1023 		error = setdisklabel(lp, (struct disklabel *)addr, 0, NULL);
1024 		if (error)
1025 			return error;
1026 
1027 		error = writedisklabel(dev, fdstrategy, lp, NULL);
1028 		return error;
1029 
1030         case FD_FORM:
1031                 if((flag & FWRITE) == 0)
1032                         return EBADF;  /* must be opened for writing */
1033                 else if(((struct fd_formb *)addr)->format_version !=
1034                         FD_FORMAT_VERSION)
1035                         return EINVAL; /* wrong version of formatting prog */
1036                 else
1037                         return fdformat(dev, (struct fd_formb *)addr, p);
1038                 break;
1039 
1040         case FD_GTYPE:                  /* get drive type */
1041                 *(struct fd_type *)addr = *fd->sc_type;
1042 		return 0;
1043 
1044         case FD_GOPTS:                  /* get drive options */
1045                 *(int *)addr = fd->sc_opts;
1046                 return 0;
1047 
1048         case FD_SOPTS:                  /* set drive options */
1049                 fd->sc_opts = *(int *)addr;
1050 		return 0;
1051 
1052 	default:
1053 		return ENOTTY;
1054 	}
1055 
1056 #ifdef DIAGNOSTIC
1057 	panic("fdioctl: impossible");
1058 #endif
1059 }
1060 
1061 int
fdformat(dev,finfo,p)1062 fdformat(dev, finfo, p)
1063         dev_t dev;
1064         struct fd_formb *finfo;
1065         struct proc *p;
1066 {
1067         int rv = 0;
1068 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
1069 	struct fd_type *type = fd->sc_type;
1070         struct buf *bp;
1071 	int fd_bsize = 128 << fd->sc_type->secsize;
1072 
1073         /* set up a buffer header for fdstrategy() */
1074         bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1075         if (bp == NULL)
1076                 return ENOBUFS;
1077 
1078 	PHOLD(p);
1079         bzero((void *)bp, sizeof(struct buf));
1080         bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1081         bp->b_proc = p;
1082         bp->b_dev = dev;
1083 
1084         /*
1085          * calculate a fake blkno, so fdstrategy() would initiate a
1086          * seek to the requested cylinder
1087          */
1088         bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
1089                 + finfo->head * type->sectrac) * fd_bsize / DEV_BSIZE;
1090 
1091         bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1092         bp->b_data = (caddr_t)finfo;
1093 
1094 #ifdef DEBUG
1095 	printf("fdformat: blkno %x count %x\n", bp->b_blkno, bp->b_bcount);
1096 #endif
1097 
1098         /* now do the format */
1099         fdstrategy(bp);
1100 
1101         /* ...and wait for it to complete */
1102 	rv = biowait(bp);
1103 	PRELE(p);
1104         free(bp, M_TEMP);
1105         return (rv);
1106 }
1107