xref: /freebsd/sys/dev/fdc/fdc.c (revision e7e0afea3bc97dd1a9df4726f64b0d805e76fa58)
1 /*#define DEBUG 1*/
2 /*-
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Don Ahn.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
38  *	$Id: fd.c,v 1.23 1994/03/02 18:34:41 ache Exp $
39  *
40  */
41 
42 #include "ft.h"
43 #if NFT < 1
44 #undef NFDC
45 #endif
46 #include "fd.h"
47 
48 #if NFDC > 0
49 
50 #include <sys/param.h>
51 #include <sys/dkbad.h>
52 #include <sys/systm.h>
53 #include <sys/kernel.h>
54 #include <sys/conf.h>
55 #include <sys/file.h>
56 #include <sys/ioctl.h>
57 #include <machine/ioctl_fd.h>
58 #include <sys/disklabel.h>
59 #include <sys/buf.h>
60 #include <sys/uio.h>
61 #include <sys/malloc.h>
62 #include <sys/syslog.h>
63 #include "i386/isa/isa.h"
64 #include "i386/isa/isa_device.h"
65 #include "i386/isa/fdreg.h"
66 #include "i386/isa/fdc.h"
67 #include "i386/isa/icu.h"
68 #include "i386/isa/rtc.h"
69 
70 #if NFT > 0
71 extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl();
72 #endif
73 
74 #define b_cylin b_resid
75 #define FDBLK 512
76 
77 /* misuse a flag to identify format operation */
78 #define B_FORMAT B_XXX
79 
80 #define NUMTYPES 14
81 #define NUMDENS  (NUMTYPES - 6)
82 
83 /* This defines (-1) must match index for fd_types */
84 #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
85 #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
86 #define FD_1720         1
87 #define FD_1480         2
88 #define FD_1440         3
89 #define FD_1200         4
90 #define FD_820          5
91 #define FD_800          6
92 #define FD_720          7
93 #define FD_360          8
94 
95 #define FD_1480in5_25   9
96 #define FD_1440in5_25   10
97 #define FD_820in5_25    11
98 #define FD_800in5_25    12
99 #define FD_720in5_25    13
100 #define FD_360in5_25    14
101 
102 
103 struct fd_type fd_types[NUMTYPES] =
104 {
105 { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
106 { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
107 { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
108 { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
109 { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
110 { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
111 {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
112 {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
113 
114 { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
115 { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
116 { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
117 { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
118 {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
119 {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
120 };
121 
122 #define DRVS_PER_CTLR 2		/* 2 floppies */
123 /***********************************************************************\
124 * Per controller structure.						*
125 \***********************************************************************/
126 struct fdc_data fdc_data[NFDC];
127 
128 /***********************************************************************\
129 * Per drive structure.							*
130 * N per controller  (DRVS_PER_CTLR)					*
131 \***********************************************************************/
132 struct fd_data {
133 	struct	fdc_data *fdc;	/* pointer to controller structure */
134 	int	fdsu;		/* this units number on this controller */
135 	int	type;		/* Drive type (HD, DD     */
136 	struct	fd_type *ft;	/* pointer to the type descriptor */
137 	int	flags;
138 #define	FD_OPEN		0x01	/* it's open		*/
139 #define	FD_ACTIVE	0x02	/* it's active		*/
140 #define	FD_MOTOR	0x04	/* motor should be on	*/
141 #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
142 	int skip;
143 	int hddrv;
144 	int track;		/* where we think the head is */
145 } fd_data[NFD];
146 
147 /***********************************************************************\
148 * Throughout this file the following conventions will be used:		*
149 * fd is a pointer to the fd_data struct for the drive in question	*
150 * fdc is a pointer to the fdc_data struct for the controller		*
151 * fdu is the floppy drive unit number					*
152 * fdcu is the floppy controller unit number				*
153 * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
154 \***********************************************************************/
155 
156 #define	id_physid id_scsiid	/* this biotab field doubles as a field */
157 				/* for the physical unit number on the controller */
158 
159 static int retrier(fdcu_t);
160 
161 #define DEVIDLE		0
162 #define FINDWORK	1
163 #define	DOSEEK		2
164 #define SEEKCOMPLETE 	3
165 #define	IOCOMPLETE	4
166 #define RECALCOMPLETE	5
167 #define	STARTRECAL	6
168 #define	RESETCTLR	7
169 #define	SEEKWAIT	8
170 #define	RECALWAIT	9
171 #define	MOTORWAIT	10
172 #define	IOTIMEDOUT	11
173 
174 #ifdef	DEBUG
175 char *fdstates[] =
176 {
177 "DEVIDLE",
178 "FINDWORK",
179 "DOSEEK",
180 "SEEKCOMPLETE",
181 "IOCOMPLETE",
182 "RECALCOMPLETE",
183 "STARTRECAL",
184 "RESETCTLR",
185 "SEEKWAIT",
186 "RECALWAIT",
187 "MOTORWAIT",
188 "IOTIMEDOUT"
189 };
190 
191 
192 int	fd_debug = 1;
193 #define TRACE0(arg) if(fd_debug) printf(arg)
194 #define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2)
195 #else /* DEBUG */
196 #define TRACE0(arg)
197 #define TRACE1(arg1,arg2)
198 #endif /* DEBUG */
199 
200 static void fdstart(fdcu_t);
201 void fdintr(fdcu_t);
202 static void fd_turnoff(caddr_t, int);
203 
204 /****************************************************************************/
205 /*                      autoconfiguration stuff                             */
206 /****************************************************************************/
207 static int fdprobe(struct isa_device *);
208 static int fdattach(struct isa_device *);
209 
210 struct	isa_driver fdcdriver = {
211 	fdprobe, fdattach, "fdc",
212 };
213 
214 /*
215  * probe for existance of controller
216  */
217 int
218 fdprobe(dev)
219 	struct isa_device *dev;
220 {
221 	fdcu_t	fdcu = dev->id_unit;
222 	if(fdc_data[fdcu].flags & FDC_ATTACHED)
223 	{
224 		printf("fdc: same unit (%d) used multiple times\n",fdcu);
225 		return 0;
226 	}
227 
228 	fdc_data[fdcu].baseport = dev->id_iobase;
229 
230 	/* First - lets reset the floppy controller */
231 
232 	outb(dev->id_iobase+fdout,0);
233 	DELAY(100);
234 	outb(dev->id_iobase+fdout,FDO_FRST);
235 
236 	/* see if it can handle a command */
237 	if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
238 	{
239 		return(0);
240 	}
241 	out_fdc(fdcu,0xDF);
242 	out_fdc(fdcu,2);
243 	return (IO_FDCSIZE);
244 }
245 
246 /*
247  * wire controller into system, look for floppy units
248  */
249 int
250 fdattach(dev)
251 	struct isa_device *dev;
252 {
253 	unsigned fdt,st0, cyl;
254 	int	hdr;
255 	fdu_t	fdu;
256 	fdcu_t	fdcu = dev->id_unit;
257 	fdc_p	fdc = fdc_data + fdcu;
258 	fd_p	fd;
259 	int	fdsu;
260 	struct isa_device *fdup;
261 
262 	fdc->fdcu = fdcu;
263 	fdc->flags |= FDC_ATTACHED;
264 	fdc->dmachan = dev->id_drq;
265 	fdc->state = DEVIDLE;
266 	hdr = 0;
267 	printf("fdc%d:", fdcu);
268 
269 	/* check for each floppy drive */
270 	for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
271 		if (fdup->id_iobase != dev->id_iobase)
272 			continue;
273 		fdu = fdup->id_unit;
274 		fd = &fd_data[fdu];
275 		if (fdu >= (NFD+NFT))
276 			continue;
277 		fdsu = fdup->id_physid;
278 				/* look up what bios thinks we have */
279 		switch (fdu) {
280 			case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
281 				break;
282 			case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
283 				break;
284 			default: fdt = RTCFDT_NONE;
285 				break;
286 		}
287 		/* is there a unit? */
288 		if ((fdt == RTCFDT_NONE)
289 #if NFT > 0
290 		|| (fdsu >= DRVS_PER_CTLR)) {
291 #else
292 		) {
293 			fd->type = NO_TYPE;
294 #endif
295 #if NFT > 0
296 				/* If BIOS says no floppy, or > 2nd device */
297 				/* Probe for and attach a floppy tape.     */
298 			if (ftattach(dev, fdup))
299 				continue;
300 			if (fdsu < DRVS_PER_CTLR)
301 				fd->type = NO_TYPE;
302 #endif
303 			continue;
304 		}
305 
306 #ifdef notyet
307 		/* select it */
308 		fd_turnon1(fdu);
309 		spinwait(1000);	/* 1 sec */
310 		out_fdc(fdcu,NE7CMD_RECAL);	/* Recalibrate Function */
311 		out_fdc(fdcu,fdsu);
312 		spinwait(1000);	/* 1 sec */
313 
314 		/* anything responding */
315 		out_fdc(fdcu,NE7CMD_SENSEI);
316 		st0 = in_fdc(fdcu);
317 		cyl = in_fdc(fdcu);
318 		if (st0 & 0xd0)
319 			continue;
320 
321 #endif
322 		fd->track = -2;
323 		fd->fdc = fdc;
324 		fd->fdsu = fdsu;
325 		printf(" [%d: fd%d: ", fdsu, fdu);
326 
327 		switch (fdt) {
328 		case RTCFDT_12M:
329 			printf("1.2MB 5.25in]");
330 			fd->type = FD_1200;
331 			break;
332 		case RTCFDT_144M:
333 			printf("1.44MB 3.5in]");
334 			fd->type = FD_1440;
335 			break;
336 		case RTCFDT_360K:
337 			printf("360KB 5.25in]");
338 			fd->type = FD_360;
339 			break;
340 		case RTCFDT_720K:
341 			printf("720KB 3.5in]");
342 			fd->type = FD_720;
343 			break;
344 		default:
345 			printf("unknown]");
346 			fd->type = NO_TYPE;
347 			break;
348 		}
349 
350 		fd_turnoff((caddr_t)fdu, 0);
351 		hdr = 1;
352 	}
353 	printf("\n");
354 
355 	/* Set transfer to 500kbps */
356 	outb(fdc->baseport+fdctl,0); /*XXX*/
357 	return 1;
358 }
359 
360 int
361 fdsize(dev)
362 	dev_t	dev;
363 {
364 	return(0);
365 }
366 
367 /****************************************************************************/
368 /*                               fdstrategy                                 */
369 /****************************************************************************/
370 void fdstrategy(struct buf *bp)
371 {
372 	register struct buf *dp,*dp0,*dp1;
373 	long nblocks,blknum;
374  	int	s;
375  	fdcu_t	fdcu;
376  	fdu_t	fdu;
377  	fdc_p	fdc;
378  	fd_p	fd;
379 
380  	fdu = FDUNIT(minor(bp->b_dev));
381 	fd = &fd_data[fdu];
382 	fdc = fd->fdc;
383 	fdcu = fdc->fdcu;
384 
385 #if NFT > 0
386 	/* check for controller already busy with tape */
387 	if (fdc->flags & FDC_TAPE_BUSY) {
388 		bp->b_error = EBUSY;
389 		bp->b_flags |= B_ERROR;
390 		return;
391 		}
392 #endif
393 	if ((fdu >= NFD) || (bp->b_blkno < 0)) {
394 		printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
395 			fdu, bp->b_blkno, bp->b_bcount);
396 		pg("fd:error in fdstrategy");
397 		bp->b_error = EINVAL;
398 		bp->b_flags |= B_ERROR;
399 		goto bad;
400 	}
401 	/*
402 	 * Set up block calculations.
403 	 */
404 	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
405  	nblocks = fd->ft->size;
406 	if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
407 		if (blknum == nblocks) {
408 			bp->b_resid = bp->b_bcount;
409 		} else {
410 			bp->b_error = ENOSPC;
411 			bp->b_flags |= B_ERROR;
412 		}
413 		goto bad;
414 	}
415  	bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
416 	dp = &(fdc->head);
417 	s = splbio();
418 	disksort(dp, bp);
419 	untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */
420 	fdstart(fdcu);
421 	splx(s);
422 	return;
423 
424 bad:
425 	biodone(bp);
426 	return;
427 }
428 
429 /****************************************************************************/
430 /*                            motor control stuff                           */
431 /*		remember to not deselect the drive we're working on         */
432 /****************************************************************************/
433 void
434 set_motor(fdcu, fdu, reset)
435 	fdcu_t fdcu;
436 	fdu_t fdu;
437 	int reset;
438 {
439 	int m0,m1;
440 	int selunit;
441 	fd_p fd;
442 	if(fd = fdc_data[fdcu].fd)/* yes an assign! */
443 	{
444 		selunit =  fd->fdsu;
445 	}
446 	else
447 	{
448 		selunit = 0;
449 	}
450 	m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
451 	m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
452 	outb(fdc_data[fdcu].baseport+fdout,
453 		selunit
454 		| (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
455 		| (m0 ? FDO_MOEN0 : 0)
456 		| (m1 ? FDO_MOEN1 : 0));
457 	TRACE1("[0x%x->fdout]",(
458 		selunit
459 		| (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
460 		| (m0 ? FDO_MOEN0 : 0)
461 		| (m1 ? FDO_MOEN1 : 0)));
462 }
463 
464 static void
465 fd_turnoff(caddr_t arg1, int arg2)
466 {
467 	fdu_t fdu = (fdu_t)arg1;
468 	int	s;
469 
470 	fd_p fd = fd_data + fdu;
471 	s = splbio();
472 	fd->flags &= ~FD_MOTOR;
473 	set_motor(fd->fdc->fdcu,fd->fdsu,0);
474 	splx(s);
475 }
476 
477 void
478 fd_motor_on(caddr_t arg1, int arg2)
479 {
480 	fdu_t fdu = (fdu_t)arg1;
481 	int	s;
482 
483 	fd_p fd = fd_data + fdu;
484 	s = splbio();
485 	fd->flags &= ~FD_MOTOR_WAIT;
486 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
487 	{
488 		fdintr(fd->fdc->fdcu);
489 	}
490 	splx(s);
491 }
492 
493 static void fd_turnon1(fdu_t);
494 
495 void
496 fd_turnon(fdu)
497 	fdu_t fdu;
498 {
499 	fd_p fd = fd_data + fdu;
500 	if(!(fd->flags & FD_MOTOR))
501 	{
502 		fd_turnon1(fdu);
503 		fd->flags |= FD_MOTOR_WAIT;
504 		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
505 	}
506 }
507 
508 static void
509 fd_turnon1(fdu_t fdu)
510 {
511 	fd_p fd = fd_data + fdu;
512 	fd->flags |= FD_MOTOR;
513 	set_motor(fd->fdc->fdcu,fd->fdsu,0);
514 }
515 
516 /****************************************************************************/
517 /*                             fdc in/out                                   */
518 /****************************************************************************/
519 int
520 in_fdc(fdcu)
521 	fdcu_t fdcu;
522 {
523 	int baseport = fdc_data[fdcu].baseport;
524 	int i, j = 100000;
525 	while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
526 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
527 		if (i == NE7_RQM) return -1;
528 	if (j <= 0)
529 		return(-1);
530 #ifdef	DEBUG
531 	i = inb(baseport+fddata);
532 	TRACE1("[fddata->0x%x]",(unsigned char)i);
533 	return(i);
534 #else
535 	return inb(baseport+fddata);
536 #endif
537 }
538 
539 int
540 out_fdc(fdcu, x)
541 	fdcu_t fdcu;
542 	int x;
543 {
544 	int baseport = fdc_data[fdcu].baseport;
545 	int i;
546 
547 	/* Check that the direction bit is set */
548 	i = 100000;
549 	while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
550 	if (i <= 0) return (-1);	/* Floppy timed out */
551 
552 	/* Check that the floppy controller is ready for a command */
553 	i = 100000;
554 	while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
555 	if (i <= 0) return (-1);	/* Floppy timed out */
556 
557 	/* Send the command and return */
558 	outb(baseport+fddata,x);
559 	TRACE1("[0x%x->fddata]",x);
560 	return (0);
561 }
562 
563 /****************************************************************************/
564 /*                           fdopen/fdclose                                 */
565 /****************************************************************************/
566 int
567 Fdopen(dev, flags)
568 	dev_t	dev;
569 	int	flags;
570 {
571  	fdu_t fdu = FDUNIT(minor(dev));
572 	int type = FDTYPE(minor(dev));
573 	fdc_p	fdc;
574 
575 #if NFT > 0
576 	/* check for a tape open */
577 	if (type & F_TAPE_TYPE)
578 		return(ftopen(dev, flags));
579 #endif
580 	/* check bounds */
581 	if (fdu >= NFD)
582 		return(ENXIO);
583 	fdc = fd_data[fdu].fdc;
584 	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
585 		return(ENXIO);
586 	if (type > NUMDENS)
587 		return(ENXIO);
588 	if (type == 0)
589 		type = fd_data[fdu].type;
590 	else {
591 		if (type != fd_data[fdu].type) {
592 			switch (fd_data[fdu].type) {
593 			case FD_360:
594 				return(ENXIO);
595 			case FD_720:
596 				if (   type != FD_820
597 				    && type != FD_800
598 				   )
599 					return(ENXIO);
600 				break;
601 			case FD_1200:
602 				switch (type) {
603 				case FD_1480:
604 					type = FD_1480in5_25;
605 					break;
606 				case FD_1440:
607 					type = FD_1440in5_25;
608 					break;
609 				case FD_820:
610 					type = FD_820in5_25;
611 					break;
612 				case FD_800:
613 					type = FD_800in5_25;
614 					break;
615 				case FD_720:
616 					type = FD_720in5_25;
617 					break;
618 				case FD_360:
619 					type = FD_360in5_25;
620 					break;
621 				default:
622 					return(ENXIO);
623 				}
624 				break;
625 			case FD_1440:
626 				if (   type != FD_1720
627 				    && type != FD_1480
628 				    && type != FD_1200
629 				    && type != FD_820
630 				    && type != FD_800
631 				    && type != FD_720
632 				    )
633 					return(ENXIO);
634 				break;
635 			}
636 		}
637 	}
638 	fd_data[fdu].ft = fd_types + type - 1;
639 	fd_data[fdu].flags |= FD_OPEN;
640 
641 	return 0;
642 }
643 
644 int
645 fdclose(dev, flags)
646 	dev_t dev;
647 	int flags;
648 {
649  	fdu_t fdu = FDUNIT(minor(dev));
650 	int type = FDTYPE(minor(dev));
651 
652 #if NFT > 0
653 	if (type & F_TAPE_TYPE)
654 		return ftclose(0);
655 #endif
656 	fd_data[fdu].flags &= ~FD_OPEN;
657 	return(0);
658 }
659 
660 
661 /***************************************************************\
662 *				fdstart				*
663 * We have just queued something.. if the controller is not busy	*
664 * then simulate the case where it has just finished a command	*
665 * So that it (the interrupt routine) looks on the queue for more*
666 * work to do and picks up what we just added.			*
667 * If the controller is already busy, we need do nothing, as it	*
668 * will pick up our work when the present work completes		*
669 \***************************************************************/
670 static void
671 fdstart(fdcu)
672 	fdcu_t fdcu;
673 {
674 	register struct buf *dp,*bp;
675 	int s;
676  	fdu_t fdu;
677 
678 	s = splbio();
679 	if(fdc_data[fdcu].state == DEVIDLE)
680 	{
681 		fdintr(fdcu);
682 	}
683 	splx(s);
684 }
685 
686 static void
687 fd_timeout(caddr_t arg1, int arg2)
688 {
689 	fdcu_t fdcu = (fdcu_t)arg1;
690 	fdu_t fdu = fdc_data[fdcu].fdu;
691 	int st0, st3, cyl;
692 	struct buf *dp,*bp;
693 	int	s;
694 
695 	dp = &fdc_data[fdcu].head;
696 	s = splbio();
697 	bp = dp->b_actf;
698 
699 	out_fdc(fdcu,NE7CMD_SENSED);
700 	out_fdc(fdcu,fd_data[fdu].hddrv);
701 	st3 = in_fdc(fdcu);
702 
703 	out_fdc(fdcu,NE7CMD_SENSEI);
704 	st0 = in_fdc(fdcu);
705 	cyl = in_fdc(fdcu);
706 	printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
707 			fdu,
708 			st0,
709 			NE7_ST0BITS,
710 			cyl,
711 			st3,
712 			NE7_ST3BITS);
713 
714 	if (bp)
715 	{
716 		retrier(fdcu);
717 		fdc_data[fdcu].status[0] = 0xc0;
718 		fdc_data[fdcu].state = IOTIMEDOUT;
719 		if( fdc_data[fdcu].retry < 6)
720 			fdc_data[fdcu].retry = 6;
721 	}
722 	else
723 	{
724 		fdc_data[fdcu].fd = (fd_p) 0;
725 		fdc_data[fdcu].fdu = -1;
726 		fdc_data[fdcu].state = DEVIDLE;
727 	}
728 	fdintr(fdcu);
729 	splx(s);
730 }
731 
732 /* just ensure it has the right spl */
733 static void
734 fd_pseudointr(caddr_t arg1, int arg2)
735 {
736 	fdcu_t fdcu = (fdcu_t)arg1;
737 	int	s;
738 	s = splbio();
739 	fdintr(fdcu);
740 	splx(s);
741 }
742 
743 /***********************************************************************\
744 *                                 fdintr				*
745 * keep calling the state machine until it returns a 0			*
746 * ALWAYS called at SPLBIO 						*
747 \***********************************************************************/
748 void
749 fdintr(fdcu_t fdcu)
750 {
751 	fdc_p fdc = fdc_data + fdcu;
752 #if NFT > 0
753 	fdu_t fdu = fdc->fdu;
754 
755 	if (fdc->flags & FDC_TAPE_BUSY)
756 		(ftintr(fdu));
757 	else
758 #endif
759 	while(fdstate(fdcu, fdc))
760 	  ;
761 }
762 
763 /***********************************************************************\
764 * The controller state machine.						*
765 * if it returns a non zero value, it should be called again immediatly	*
766 \***********************************************************************/
767 int
768 fdstate(fdcu, fdc)
769 	fdcu_t fdcu;
770 	fdc_p fdc;
771 {
772 	int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
773 	unsigned long blknum;
774 	fdu_t fdu = fdc->fdu;
775 	fd_p fd;
776 	register struct buf *dp,*bp;
777 	struct fd_formb *finfo = NULL;
778 
779 	dp = &(fdc->head);
780 	bp = dp->b_actf;
781 	if(!bp)
782 	{
783 		/***********************************************\
784 		* nothing left for this controller to do	*
785 		* Force into the IDLE state,			*
786 		\***********************************************/
787 		fdc->state = DEVIDLE;
788 		if(fdc->fd)
789 		{
790 			printf("unexpected valid fd pointer (fdu = %d)\n"
791 						,fdc->fdu);
792 			fdc->fd = (fd_p) 0;
793 			fdc->fdu = -1;
794 		}
795 		TRACE1("[fdc%d IDLE]",fdcu);
796  		return(0);
797 	}
798 	fdu = FDUNIT(minor(bp->b_dev));
799 	fd = fd_data + fdu;
800 	if (fdc->fd && (fd != fdc->fd))
801 	{
802 		printf("confused fd pointers\n");
803 	}
804 	read = bp->b_flags & B_READ;
805 	format = bp->b_flags & B_FORMAT;
806 	if(format)
807 		finfo = (struct fd_formb *)bp->b_un.b_addr;
808 	TRACE1("fd%d",fdu);
809 	TRACE1("[%s]",fdstates[fdc->state]);
810 	TRACE1("(0x%x)",fd->flags);
811 	untimeout(fd_turnoff, (caddr_t)fdu);
812 	timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
813 	switch (fdc->state)
814 	{
815 	case DEVIDLE:
816 	case FINDWORK:	/* we have found new work */
817 		fdc->retry = 0;
818 		fd->skip = 0;
819 		fdc->fd = fd;
820 		fdc->fdu = fdu;
821 		outb(fdc->baseport+fdctl, fd->ft->trans);
822 		/*******************************************************\
823 		* If the next drive has a motor startup pending, then	*
824 		* it will start up in it's own good time		*
825 		\*******************************************************/
826 		if(fd->flags & FD_MOTOR_WAIT)
827 		{
828 			fdc->state = MOTORWAIT;
829 			return(0); /* come back later */
830 		}
831 		/*******************************************************\
832 		* Maybe if it's not starting, it SHOULD be starting	*
833 		\*******************************************************/
834 		if (!(fd->flags & FD_MOTOR))
835 		{
836 			fdc->state = MOTORWAIT;
837 			fd_turnon(fdu);
838 			return(0);
839 		}
840 		else	/* at least make sure we are selected */
841 		{
842 			set_motor(fdcu,fd->fdsu,0);
843 		}
844 		fdc->state = DOSEEK;
845 		break;
846 	case DOSEEK:
847 		if (bp->b_cylin == fd->track)
848 		{
849 			fdc->state = SEEKCOMPLETE;
850 			break;
851 		}
852 		out_fdc(fdcu,NE7CMD_SEEK);	/* Seek function */
853 		out_fdc(fdcu,fd->fdsu);		/* Drive number */
854 		out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
855 		fd->track = -2;
856 		fdc->state = SEEKWAIT;
857 		timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
858 		return(0);	/* will return later */
859 	case SEEKWAIT:
860 		untimeout(fd_timeout, (caddr_t)fdcu);
861 		/* allow heads to settle */
862 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50);
863 		fdc->state = SEEKCOMPLETE;
864 		return(0);	/* will return later */
865 		break;
866 
867 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
868 		/* Make sure seek really happened*/
869 		if(fd->track == -2)
870 		{
871 			int descyl = bp->b_cylin * fd->ft->steptrac;
872 			out_fdc(fdcu,NE7CMD_SENSEI);
873 			i = in_fdc(fdcu);
874 			cyl = in_fdc(fdcu);
875 			if (cyl != descyl)
876 			{
877 				printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
878 				fdu, descyl, cyl, i, NE7_ST0BITS);
879 				return(retrier(fdcu));
880 			}
881 		}
882 
883 		fd->track = bp->b_cylin;
884 		if(format)
885 			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
886 				- (char *)finfo;
887 		isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
888 			format ? bp->b_bcount : FDBLK, fdc->dmachan);
889 		blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
890 			+ fd->skip/FDBLK;
891 		sectrac = fd->ft->sectrac;
892 		sec = blknum %  (sectrac * fd->ft->heads);
893 		head = sec / sectrac;
894 		sec = sec % sectrac + 1;
895 /*XXX*/		fd->hddrv = ((head&1)<<2)+fdu;
896 
897 		if(format)
898 		{
899 			/* formatting */
900 			out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
901 			out_fdc(fdcu,head << 2 | fdu);
902 			out_fdc(fdcu,finfo->fd_formb_secshift);
903 			out_fdc(fdcu,finfo->fd_formb_nsecs);
904 			out_fdc(fdcu,finfo->fd_formb_gaplen);
905 			out_fdc(fdcu,finfo->fd_formb_fillbyte);
906 		}
907 		else
908 		{
909 			if (read)
910 			{
911 				out_fdc(fdcu,NE7CMD_READ);      /* READ */
912 			}
913 			else
914 			{
915 				out_fdc(fdcu,NE7CMD_WRITE);     /* WRITE */
916 			}
917 			out_fdc(fdcu,head << 2 | fdu);  /* head & unit */
918 			out_fdc(fdcu,fd->track);        /* track */
919 			out_fdc(fdcu,head);
920 			out_fdc(fdcu,sec);              /* sector XXX +1? */
921 			out_fdc(fdcu,fd->ft->secsize);  /* sector size */
922 			out_fdc(fdcu,sectrac);          /* sectors/track */
923 			out_fdc(fdcu,fd->ft->gap);      /* gap size */
924 			out_fdc(fdcu,fd->ft->datalen);  /* data length */
925 		}
926 		fdc->state = IOCOMPLETE;
927 		timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
928 		return(0);	/* will return later */
929 	case IOCOMPLETE: /* IO DONE, post-analyze */
930 		untimeout(fd_timeout, (caddr_t)fdcu);
931 		for(i=0;i<7;i++)
932 		{
933 			fdc->status[i] = in_fdc(fdcu);
934 		}
935 	case IOTIMEDOUT: /*XXX*/
936 		isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
937 			format ? bp->b_bcount : FDBLK, fdc->dmachan);
938 		if (fdc->status[0]&0xF8)
939 		{
940                         if (fdc->status[1] & 0x10) {
941                                 /*
942 				 * Operation not completed in reasonable time.
943 				 * Just restart it, don't increment retry count.
944 				 * (vak)
945                                  */
946                                 fdc->state = SEEKCOMPLETE;
947                                 return (1);
948                         }
949 			return(retrier(fdcu));
950 		}
951 		/* All OK */
952 		fd->skip += FDBLK;
953 		if (!format && fd->skip < bp->b_bcount)
954 		{
955 			/* set up next transfer */
956 			blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
957 				+ fd->skip/FDBLK;
958 			bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
959 			fdc->state = DOSEEK;
960 		}
961 		else
962 		{
963 			/* ALL DONE */
964 			fd->skip = 0;
965 			bp->b_resid = 0;
966 			dp->b_actf = bp->av_forw;
967 			biodone(bp);
968 			fdc->fd = (fd_p) 0;
969 			fdc->fdu = -1;
970 			fdc->state = FINDWORK;
971 		}
972 		return(1);
973 	case RESETCTLR:
974 		/* Try a reset, keep motor on */
975 		set_motor(fdcu,fd->fdsu,1);
976 		DELAY(100);
977 		set_motor(fdcu,fd->fdsu,0);
978 		outb(fdc->baseport+fdctl,fd->ft->trans);
979 		TRACE1("[0x%x->fdctl]",fd->ft->trans);
980 		fdc->retry++;
981 		fdc->state = STARTRECAL;
982 		break;
983 	case STARTRECAL:
984 		out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
985 		out_fdc(fdcu,0xDF);
986 		out_fdc(fdcu,2);
987 		out_fdc(fdcu,NE7CMD_RECAL);	/* Recalibrate Function */
988 		out_fdc(fdcu,fdu);
989 		fdc->state = RECALWAIT;
990 		return(0);	/* will return later */
991 	case RECALWAIT:
992 		/* allow heads to settle */
993 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30);
994 		fdc->state = RECALCOMPLETE;
995 		return(0);	/* will return later */
996 	case RECALCOMPLETE:
997 		out_fdc(fdcu,NE7CMD_SENSEI);
998 		st0 = in_fdc(fdcu);
999 		cyl = in_fdc(fdcu);
1000 		if (cyl != 0)
1001 		{
1002 			printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
1003 				st0, NE7_ST0BITS, cyl);
1004 			return(retrier(fdcu));
1005 		}
1006 		fd->track = 0;
1007 		/* Seek (probably) necessary */
1008 		fdc->state = DOSEEK;
1009 		return(1);	/* will return immediatly */
1010 	case	MOTORWAIT:
1011 		if(fd->flags & FD_MOTOR_WAIT)
1012 		{
1013 			return(0); /* time's not up yet */
1014 		}
1015 		fdc->state = DOSEEK;
1016 		return(1);	/* will return immediatly */
1017 	default:
1018 		printf("Unexpected FD int->");
1019 		out_fdc(fdcu,NE7CMD_SENSEI);
1020 		st0 = in_fdc(fdcu);
1021 		cyl = in_fdc(fdcu);
1022 		printf("ST0 = %lx, PCN = %lx\n",i,sec);
1023 		out_fdc(fdcu,0x4A);
1024 		out_fdc(fdcu,fd->fdsu);
1025 		for(i=0;i<7;i++) {
1026 			fdc->status[i] = in_fdc(fdcu);
1027 		}
1028 	printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
1029 		fdc->status[0],
1030 		fdc->status[1],
1031 		fdc->status[2],
1032 		fdc->status[3],
1033 		fdc->status[4],
1034 		fdc->status[5],
1035 		fdc->status[6] );
1036 		return(0);
1037 	}
1038 	return(1); /* Come back immediatly to new state */
1039 }
1040 
1041 static int
1042 retrier(fdcu)
1043 	fdcu_t fdcu;
1044 {
1045 	fdc_p fdc = fdc_data + fdcu;
1046 	register struct buf *dp,*bp;
1047 
1048 	dp = &(fdc->head);
1049 	bp = dp->b_actf;
1050 
1051 	switch(fdc->retry)
1052 	{
1053 	case 0: case 1: case 2:
1054 		fdc->state = SEEKCOMPLETE;
1055 		break;
1056 	case 3: case 4: case 5:
1057 		fdc->state = STARTRECAL;
1058 		break;
1059 	case 6:
1060 		fdc->state = RESETCTLR;
1061 		break;
1062 	case 7:
1063 		break;
1064 	default:
1065 		{
1066 			dev_t sav_b_dev = bp->b_dev;
1067 			/* Trick diskerr */
1068 			bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3);
1069 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
1070 				fdc->fd->skip, (struct disklabel *)NULL);
1071 			bp->b_dev = sav_b_dev;
1072 			printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
1073 			printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
1074 			printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
1075 			printf("cyl %d hd %d sec %d)\n",
1076 			       fdc->status[3], fdc->status[4], fdc->status[5]);
1077 		}
1078 		bp->b_flags |= B_ERROR;
1079 		bp->b_error = EIO;
1080 		bp->b_resid = bp->b_bcount - fdc->fd->skip;
1081 		dp->b_actf = bp->av_forw;
1082 		fdc->fd->skip = 0;
1083 		biodone(bp);
1084 		fdc->state = FINDWORK;
1085 		fdc->fd = (fd_p) 0;
1086 		fdc->fdu = -1;
1087 		/* XXX abort current command, if any.  */
1088 		return(1);
1089 	}
1090 	fdc->retry++;
1091 	return(1);
1092 }
1093 
1094 static int
1095 fdformat(dev, finfo, p)
1096 	dev_t dev;
1097 	struct fd_formb *finfo;
1098 	struct proc *p;
1099 {
1100  	fdu_t	fdu;
1101  	fd_p	fd;
1102 
1103 	struct buf *bp;
1104 	int rv = 0, s;
1105 
1106  	fdu = FDUNIT(minor(dev));
1107 	fd = &fd_data[fdu];
1108 
1109 	/* set up a buffer header for fdstrategy() */
1110 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1111 	if(bp == 0)
1112 		return ENOBUFS;
1113 	bzero((void *)bp, sizeof(struct buf));
1114 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1115 	bp->b_proc = p;
1116 	bp->b_dev = dev;
1117 
1118 	/*
1119 	 * calculate a fake blkno, so fdstrategy() would initiate a
1120 	 * seek to the requested cylinder
1121 	 */
1122 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
1123 		+ finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
1124 
1125 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1126 	bp->b_un.b_addr = (caddr_t)finfo;
1127 
1128 	/* now do the format */
1129 	fdstrategy(bp);
1130 
1131 	/* ...and wait for it to complete */
1132 	s = splbio();
1133 	while(!(bp->b_flags & B_DONE))
1134 	{
1135 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1136 		if(rv == EWOULDBLOCK)
1137 			break;
1138 	}
1139 	splx(s);
1140 
1141 	if(rv == EWOULDBLOCK)
1142 	{
1143 		/* timed out */
1144 		biodone(bp);
1145 		rv = EIO;
1146 	}
1147 	free(bp, M_TEMP);
1148 	return rv;
1149 }
1150 
1151 /*
1152  * fdioctl() from jc@irbs.UUCP (John Capo)
1153  * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
1154  * defines fdioctl to be enxio.
1155  *
1156  * TODO: Reformat.
1157  *       Think about allocating buffer off stack.
1158  *       Don't pass uncast 0's and NULL's to read/write/setdisklabel().
1159  *       Watch out for NetBSD's different *disklabel() interface.
1160  *
1161  * Added functionality for floppy formatting
1162  * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
1163  */
1164 
1165 int
1166 fdioctl (dev, cmd, addr, flag, p)
1167 	dev_t dev;
1168 	int cmd;
1169 	caddr_t addr;
1170 	int flag;
1171 	struct proc *p;
1172 {
1173 	struct fd_type *fdt;
1174 	struct disklabel *dl;
1175 	char buffer[DEV_BSIZE];
1176 	int error;
1177 
1178 #if NFT > 0
1179 	int type = FDTYPE(minor(dev));
1180 
1181 	/* check for a tape ioctl */
1182 	if (type & F_TAPE_TYPE)
1183 		return ftioctl(dev, cmd, addr, flag, p);
1184 #endif
1185 
1186 	error = 0;
1187 
1188 	switch (cmd)
1189 	{
1190 	case DIOCGDINFO:
1191 		bzero(buffer, sizeof (buffer));
1192 		dl = (struct disklabel *)buffer;
1193 		dl->d_secsize = FDBLK;
1194 		fdt = fd_data[FDUNIT(minor(dev))].ft;
1195 		dl->d_secpercyl = fdt->size / fdt->tracks;
1196 		dl->d_type = DTYPE_FLOPPY;
1197 
1198 		if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
1199 			error = 0;
1200 		else
1201 			error = EINVAL;
1202 
1203 		*(struct disklabel *)addr = *dl;
1204 		break;
1205 
1206 	case DIOCSDINFO:
1207 		if ((flag & FWRITE) == 0)
1208 			error = EBADF;
1209 		break;
1210 
1211 	case DIOCWLABEL:
1212 		if ((flag & FWRITE) == 0)
1213 			error = EBADF;
1214 		break;
1215 
1216 	case DIOCWDINFO:
1217 		if ((flag & FWRITE) == 0)
1218 		{
1219 			error = EBADF;
1220 			break;
1221 		}
1222 
1223 		dl = (struct disklabel *)addr;
1224 
1225 		if (error = setdisklabel ((struct disklabel *)buffer,
1226 		    dl, 0, NULL))
1227 			break;
1228 
1229 		error = writedisklabel(dev, fdstrategy,
1230 			(struct disklabel *)buffer, NULL);
1231 		break;
1232 
1233 	case FD_FORM:
1234 		if((flag & FWRITE) == 0)
1235 			error = EBADF;	/* must be opened for writing */
1236 		else if(((struct fd_formb *)addr)->format_version !=
1237 			FD_FORMAT_VERSION)
1238 			error = EINVAL;	/* wrong version of formatting prog */
1239 		else
1240 			error = fdformat(dev, (struct fd_formb *)addr, p);
1241 		break;
1242 
1243 	case FD_GTYPE:                  /* get drive type */
1244 		*(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
1245 		break;
1246 
1247 	default:
1248 		error = EINVAL;
1249 		break;
1250 	}
1251 	return (error);
1252 }
1253 
1254 #endif
1255