xref: /freebsd/sys/dev/fdc/fdc.c (revision 0e195446b70cdb3130378505b24603031c1f3ce0)
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.24 1994/03/08 16:25:29 nate 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 	bp->b_pblkno = bp->b_blkno;
417 	dp = &(fdc->head);
418 	s = splbio();
419 	disksort(dp, bp);
420 	untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */
421 	fdstart(fdcu);
422 	splx(s);
423 	return;
424 
425 bad:
426 	biodone(bp);
427 	return;
428 }
429 
430 /****************************************************************************/
431 /*                            motor control stuff                           */
432 /*		remember to not deselect the drive we're working on         */
433 /****************************************************************************/
434 void
435 set_motor(fdcu, fdu, reset)
436 	fdcu_t fdcu;
437 	fdu_t fdu;
438 	int reset;
439 {
440 	int m0,m1;
441 	int selunit;
442 	fd_p fd;
443 	if(fd = fdc_data[fdcu].fd)/* yes an assign! */
444 	{
445 		selunit =  fd->fdsu;
446 	}
447 	else
448 	{
449 		selunit = 0;
450 	}
451 	m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
452 	m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
453 	outb(fdc_data[fdcu].baseport+fdout,
454 		selunit
455 		| (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
456 		| (m0 ? FDO_MOEN0 : 0)
457 		| (m1 ? FDO_MOEN1 : 0));
458 	TRACE1("[0x%x->fdout]",(
459 		selunit
460 		| (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
461 		| (m0 ? FDO_MOEN0 : 0)
462 		| (m1 ? FDO_MOEN1 : 0)));
463 }
464 
465 static void
466 fd_turnoff(caddr_t arg1, int arg2)
467 {
468 	fdu_t fdu = (fdu_t)arg1;
469 	int	s;
470 
471 	fd_p fd = fd_data + fdu;
472 	s = splbio();
473 	fd->flags &= ~FD_MOTOR;
474 	set_motor(fd->fdc->fdcu,fd->fdsu,0);
475 	splx(s);
476 }
477 
478 void
479 fd_motor_on(caddr_t arg1, int arg2)
480 {
481 	fdu_t fdu = (fdu_t)arg1;
482 	int	s;
483 
484 	fd_p fd = fd_data + fdu;
485 	s = splbio();
486 	fd->flags &= ~FD_MOTOR_WAIT;
487 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
488 	{
489 		fdintr(fd->fdc->fdcu);
490 	}
491 	splx(s);
492 }
493 
494 static void fd_turnon1(fdu_t);
495 
496 void
497 fd_turnon(fdu)
498 	fdu_t fdu;
499 {
500 	fd_p fd = fd_data + fdu;
501 	if(!(fd->flags & FD_MOTOR))
502 	{
503 		fd_turnon1(fdu);
504 		fd->flags |= FD_MOTOR_WAIT;
505 		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
506 	}
507 }
508 
509 static void
510 fd_turnon1(fdu_t fdu)
511 {
512 	fd_p fd = fd_data + fdu;
513 	fd->flags |= FD_MOTOR;
514 	set_motor(fd->fdc->fdcu,fd->fdsu,0);
515 }
516 
517 /****************************************************************************/
518 /*                             fdc in/out                                   */
519 /****************************************************************************/
520 int
521 in_fdc(fdcu)
522 	fdcu_t fdcu;
523 {
524 	int baseport = fdc_data[fdcu].baseport;
525 	int i, j = 100000;
526 	while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
527 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
528 		if (i == NE7_RQM) return -1;
529 	if (j <= 0)
530 		return(-1);
531 #ifdef	DEBUG
532 	i = inb(baseport+fddata);
533 	TRACE1("[fddata->0x%x]",(unsigned char)i);
534 	return(i);
535 #else
536 	return inb(baseport+fddata);
537 #endif
538 }
539 
540 int
541 out_fdc(fdcu, x)
542 	fdcu_t fdcu;
543 	int x;
544 {
545 	int baseport = fdc_data[fdcu].baseport;
546 	int i;
547 
548 	/* Check that the direction bit is set */
549 	i = 100000;
550 	while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
551 	if (i <= 0) return (-1);	/* Floppy timed out */
552 
553 	/* Check that the floppy controller is ready for a command */
554 	i = 100000;
555 	while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
556 	if (i <= 0) return (-1);	/* Floppy timed out */
557 
558 	/* Send the command and return */
559 	outb(baseport+fddata,x);
560 	TRACE1("[0x%x->fddata]",x);
561 	return (0);
562 }
563 
564 /****************************************************************************/
565 /*                           fdopen/fdclose                                 */
566 /****************************************************************************/
567 int
568 Fdopen(dev, flags)
569 	dev_t	dev;
570 	int	flags;
571 {
572  	fdu_t fdu = FDUNIT(minor(dev));
573 	int type = FDTYPE(minor(dev));
574 	fdc_p	fdc;
575 
576 #if NFT > 0
577 	/* check for a tape open */
578 	if (type & F_TAPE_TYPE)
579 		return(ftopen(dev, flags));
580 #endif
581 	/* check bounds */
582 	if (fdu >= NFD)
583 		return(ENXIO);
584 	fdc = fd_data[fdu].fdc;
585 	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
586 		return(ENXIO);
587 	if (type > NUMDENS)
588 		return(ENXIO);
589 	if (type == 0)
590 		type = fd_data[fdu].type;
591 	else {
592 		if (type != fd_data[fdu].type) {
593 			switch (fd_data[fdu].type) {
594 			case FD_360:
595 				return(ENXIO);
596 			case FD_720:
597 				if (   type != FD_820
598 				    && type != FD_800
599 				   )
600 					return(ENXIO);
601 				break;
602 			case FD_1200:
603 				switch (type) {
604 				case FD_1480:
605 					type = FD_1480in5_25;
606 					break;
607 				case FD_1440:
608 					type = FD_1440in5_25;
609 					break;
610 				case FD_820:
611 					type = FD_820in5_25;
612 					break;
613 				case FD_800:
614 					type = FD_800in5_25;
615 					break;
616 				case FD_720:
617 					type = FD_720in5_25;
618 					break;
619 				case FD_360:
620 					type = FD_360in5_25;
621 					break;
622 				default:
623 					return(ENXIO);
624 				}
625 				break;
626 			case FD_1440:
627 				if (   type != FD_1720
628 				    && type != FD_1480
629 				    && type != FD_1200
630 				    && type != FD_820
631 				    && type != FD_800
632 				    && type != FD_720
633 				    )
634 					return(ENXIO);
635 				break;
636 			}
637 		}
638 	}
639 	fd_data[fdu].ft = fd_types + type - 1;
640 	fd_data[fdu].flags |= FD_OPEN;
641 
642 	return 0;
643 }
644 
645 int
646 fdclose(dev, flags)
647 	dev_t dev;
648 	int flags;
649 {
650  	fdu_t fdu = FDUNIT(minor(dev));
651 	int type = FDTYPE(minor(dev));
652 
653 #if NFT > 0
654 	if (type & F_TAPE_TYPE)
655 		return ftclose(0);
656 #endif
657 	fd_data[fdu].flags &= ~FD_OPEN;
658 	return(0);
659 }
660 
661 
662 /***************************************************************\
663 *				fdstart				*
664 * We have just queued something.. if the controller is not busy	*
665 * then simulate the case where it has just finished a command	*
666 * So that it (the interrupt routine) looks on the queue for more*
667 * work to do and picks up what we just added.			*
668 * If the controller is already busy, we need do nothing, as it	*
669 * will pick up our work when the present work completes		*
670 \***************************************************************/
671 static void
672 fdstart(fdcu)
673 	fdcu_t fdcu;
674 {
675 	register struct buf *dp,*bp;
676 	int s;
677  	fdu_t fdu;
678 
679 	s = splbio();
680 	if(fdc_data[fdcu].state == DEVIDLE)
681 	{
682 		fdintr(fdcu);
683 	}
684 	splx(s);
685 }
686 
687 static void
688 fd_timeout(caddr_t arg1, int arg2)
689 {
690 	fdcu_t fdcu = (fdcu_t)arg1;
691 	fdu_t fdu = fdc_data[fdcu].fdu;
692 	int st0, st3, cyl;
693 	struct buf *dp,*bp;
694 	int	s;
695 
696 	dp = &fdc_data[fdcu].head;
697 	s = splbio();
698 	bp = dp->b_actf;
699 
700 	out_fdc(fdcu,NE7CMD_SENSED);
701 	out_fdc(fdcu,fd_data[fdu].hddrv);
702 	st3 = in_fdc(fdcu);
703 
704 	out_fdc(fdcu,NE7CMD_SENSEI);
705 	st0 = in_fdc(fdcu);
706 	cyl = in_fdc(fdcu);
707 	printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
708 			fdu,
709 			st0,
710 			NE7_ST0BITS,
711 			cyl,
712 			st3,
713 			NE7_ST3BITS);
714 
715 	if (bp)
716 	{
717 		retrier(fdcu);
718 		fdc_data[fdcu].status[0] = 0xc0;
719 		fdc_data[fdcu].state = IOTIMEDOUT;
720 		if( fdc_data[fdcu].retry < 6)
721 			fdc_data[fdcu].retry = 6;
722 	}
723 	else
724 	{
725 		fdc_data[fdcu].fd = (fd_p) 0;
726 		fdc_data[fdcu].fdu = -1;
727 		fdc_data[fdcu].state = DEVIDLE;
728 	}
729 	fdintr(fdcu);
730 	splx(s);
731 }
732 
733 /* just ensure it has the right spl */
734 static void
735 fd_pseudointr(caddr_t arg1, int arg2)
736 {
737 	fdcu_t fdcu = (fdcu_t)arg1;
738 	int	s;
739 	s = splbio();
740 	fdintr(fdcu);
741 	splx(s);
742 }
743 
744 /***********************************************************************\
745 *                                 fdintr				*
746 * keep calling the state machine until it returns a 0			*
747 * ALWAYS called at SPLBIO 						*
748 \***********************************************************************/
749 void
750 fdintr(fdcu_t fdcu)
751 {
752 	fdc_p fdc = fdc_data + fdcu;
753 #if NFT > 0
754 	fdu_t fdu = fdc->fdu;
755 
756 	if (fdc->flags & FDC_TAPE_BUSY)
757 		(ftintr(fdu));
758 	else
759 #endif
760 	while(fdstate(fdcu, fdc))
761 	  ;
762 }
763 
764 /***********************************************************************\
765 * The controller state machine.						*
766 * if it returns a non zero value, it should be called again immediatly	*
767 \***********************************************************************/
768 int
769 fdstate(fdcu, fdc)
770 	fdcu_t fdcu;
771 	fdc_p fdc;
772 {
773 	int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
774 	unsigned long blknum;
775 	fdu_t fdu = fdc->fdu;
776 	fd_p fd;
777 	register struct buf *dp,*bp;
778 	struct fd_formb *finfo = NULL;
779 
780 	dp = &(fdc->head);
781 	bp = dp->b_actf;
782 	if(!bp)
783 	{
784 		/***********************************************\
785 		* nothing left for this controller to do	*
786 		* Force into the IDLE state,			*
787 		\***********************************************/
788 		fdc->state = DEVIDLE;
789 		if(fdc->fd)
790 		{
791 			printf("unexpected valid fd pointer (fdu = %d)\n"
792 						,fdc->fdu);
793 			fdc->fd = (fd_p) 0;
794 			fdc->fdu = -1;
795 		}
796 		TRACE1("[fdc%d IDLE]",fdcu);
797  		return(0);
798 	}
799 	fdu = FDUNIT(minor(bp->b_dev));
800 	fd = fd_data + fdu;
801 	if (fdc->fd && (fd != fdc->fd))
802 	{
803 		printf("confused fd pointers\n");
804 	}
805 	read = bp->b_flags & B_READ;
806 	format = bp->b_flags & B_FORMAT;
807 	if(format)
808 		finfo = (struct fd_formb *)bp->b_un.b_addr;
809 	TRACE1("fd%d",fdu);
810 	TRACE1("[%s]",fdstates[fdc->state]);
811 	TRACE1("(0x%x)",fd->flags);
812 	untimeout(fd_turnoff, (caddr_t)fdu);
813 	timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
814 	switch (fdc->state)
815 	{
816 	case DEVIDLE:
817 	case FINDWORK:	/* we have found new work */
818 		fdc->retry = 0;
819 		fd->skip = 0;
820 		fdc->fd = fd;
821 		fdc->fdu = fdu;
822 		outb(fdc->baseport+fdctl, fd->ft->trans);
823 		/*******************************************************\
824 		* If the next drive has a motor startup pending, then	*
825 		* it will start up in it's own good time		*
826 		\*******************************************************/
827 		if(fd->flags & FD_MOTOR_WAIT)
828 		{
829 			fdc->state = MOTORWAIT;
830 			return(0); /* come back later */
831 		}
832 		/*******************************************************\
833 		* Maybe if it's not starting, it SHOULD be starting	*
834 		\*******************************************************/
835 		if (!(fd->flags & FD_MOTOR))
836 		{
837 			fdc->state = MOTORWAIT;
838 			fd_turnon(fdu);
839 			return(0);
840 		}
841 		else	/* at least make sure we are selected */
842 		{
843 			set_motor(fdcu,fd->fdsu,0);
844 		}
845 		fdc->state = DOSEEK;
846 		break;
847 	case DOSEEK:
848 		if (bp->b_cylin == fd->track)
849 		{
850 			fdc->state = SEEKCOMPLETE;
851 			break;
852 		}
853 		out_fdc(fdcu,NE7CMD_SEEK);	/* Seek function */
854 		out_fdc(fdcu,fd->fdsu);		/* Drive number */
855 		out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
856 		fd->track = -2;
857 		fdc->state = SEEKWAIT;
858 		timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
859 		return(0);	/* will return later */
860 	case SEEKWAIT:
861 		untimeout(fd_timeout, (caddr_t)fdcu);
862 		/* allow heads to settle */
863 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50);
864 		fdc->state = SEEKCOMPLETE;
865 		return(0);	/* will return later */
866 		break;
867 
868 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
869 		/* Make sure seek really happened*/
870 		if(fd->track == -2)
871 		{
872 			int descyl = bp->b_cylin * fd->ft->steptrac;
873 			out_fdc(fdcu,NE7CMD_SENSEI);
874 			i = in_fdc(fdcu);
875 			cyl = in_fdc(fdcu);
876 			if (cyl != descyl)
877 			{
878 				printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
879 				fdu, descyl, cyl, i, NE7_ST0BITS);
880 				return(retrier(fdcu));
881 			}
882 		}
883 
884 		fd->track = bp->b_cylin;
885 		if(format)
886 			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
887 				- (char *)finfo;
888 		isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
889 			format ? bp->b_bcount : FDBLK, fdc->dmachan);
890 		blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
891 			+ fd->skip/FDBLK;
892 		sectrac = fd->ft->sectrac;
893 		sec = blknum %  (sectrac * fd->ft->heads);
894 		head = sec / sectrac;
895 		sec = sec % sectrac + 1;
896 /*XXX*/		fd->hddrv = ((head&1)<<2)+fdu;
897 
898 		if(format)
899 		{
900 			/* formatting */
901 			out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
902 			out_fdc(fdcu,head << 2 | fdu);
903 			out_fdc(fdcu,finfo->fd_formb_secshift);
904 			out_fdc(fdcu,finfo->fd_formb_nsecs);
905 			out_fdc(fdcu,finfo->fd_formb_gaplen);
906 			out_fdc(fdcu,finfo->fd_formb_fillbyte);
907 		}
908 		else
909 		{
910 			if (read)
911 			{
912 				out_fdc(fdcu,NE7CMD_READ);      /* READ */
913 			}
914 			else
915 			{
916 				out_fdc(fdcu,NE7CMD_WRITE);     /* WRITE */
917 			}
918 			out_fdc(fdcu,head << 2 | fdu);  /* head & unit */
919 			out_fdc(fdcu,fd->track);        /* track */
920 			out_fdc(fdcu,head);
921 			out_fdc(fdcu,sec);              /* sector XXX +1? */
922 			out_fdc(fdcu,fd->ft->secsize);  /* sector size */
923 			out_fdc(fdcu,sectrac);          /* sectors/track */
924 			out_fdc(fdcu,fd->ft->gap);      /* gap size */
925 			out_fdc(fdcu,fd->ft->datalen);  /* data length */
926 		}
927 		fdc->state = IOCOMPLETE;
928 		timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
929 		return(0);	/* will return later */
930 	case IOCOMPLETE: /* IO DONE, post-analyze */
931 		untimeout(fd_timeout, (caddr_t)fdcu);
932 		for(i=0;i<7;i++)
933 		{
934 			fdc->status[i] = in_fdc(fdcu);
935 		}
936 	case IOTIMEDOUT: /*XXX*/
937 		isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
938 			format ? bp->b_bcount : FDBLK, fdc->dmachan);
939 		if (fdc->status[0]&0xF8)
940 		{
941                         if (fdc->status[1] & 0x10) {
942                                 /*
943 				 * Operation not completed in reasonable time.
944 				 * Just restart it, don't increment retry count.
945 				 * (vak)
946                                  */
947                                 fdc->state = SEEKCOMPLETE;
948                                 return (1);
949                         }
950 			return(retrier(fdcu));
951 		}
952 		/* All OK */
953 		fd->skip += FDBLK;
954 		if (!format && fd->skip < bp->b_bcount)
955 		{
956 			/* set up next transfer */
957 			blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
958 				+ fd->skip/FDBLK;
959 			bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
960 			fdc->state = DOSEEK;
961 		}
962 		else
963 		{
964 			/* ALL DONE */
965 			fd->skip = 0;
966 			bp->b_resid = 0;
967 			dp->b_actf = bp->av_forw;
968 			biodone(bp);
969 			fdc->fd = (fd_p) 0;
970 			fdc->fdu = -1;
971 			fdc->state = FINDWORK;
972 		}
973 		return(1);
974 	case RESETCTLR:
975 		/* Try a reset, keep motor on */
976 		set_motor(fdcu,fd->fdsu,1);
977 		DELAY(100);
978 		set_motor(fdcu,fd->fdsu,0);
979 		outb(fdc->baseport+fdctl,fd->ft->trans);
980 		TRACE1("[0x%x->fdctl]",fd->ft->trans);
981 		fdc->retry++;
982 		fdc->state = STARTRECAL;
983 		break;
984 	case STARTRECAL:
985 		out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
986 		out_fdc(fdcu,0xDF);
987 		out_fdc(fdcu,2);
988 		out_fdc(fdcu,NE7CMD_RECAL);	/* Recalibrate Function */
989 		out_fdc(fdcu,fdu);
990 		fdc->state = RECALWAIT;
991 		return(0);	/* will return later */
992 	case RECALWAIT:
993 		/* allow heads to settle */
994 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30);
995 		fdc->state = RECALCOMPLETE;
996 		return(0);	/* will return later */
997 	case RECALCOMPLETE:
998 		out_fdc(fdcu,NE7CMD_SENSEI);
999 		st0 = in_fdc(fdcu);
1000 		cyl = in_fdc(fdcu);
1001 		if (cyl != 0)
1002 		{
1003 			printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
1004 				st0, NE7_ST0BITS, cyl);
1005 			return(retrier(fdcu));
1006 		}
1007 		fd->track = 0;
1008 		/* Seek (probably) necessary */
1009 		fdc->state = DOSEEK;
1010 		return(1);	/* will return immediatly */
1011 	case	MOTORWAIT:
1012 		if(fd->flags & FD_MOTOR_WAIT)
1013 		{
1014 			return(0); /* time's not up yet */
1015 		}
1016 		fdc->state = DOSEEK;
1017 		return(1);	/* will return immediatly */
1018 	default:
1019 		printf("Unexpected FD int->");
1020 		out_fdc(fdcu,NE7CMD_SENSEI);
1021 		st0 = in_fdc(fdcu);
1022 		cyl = in_fdc(fdcu);
1023 		printf("ST0 = %lx, PCN = %lx\n",i,sec);
1024 		out_fdc(fdcu,0x4A);
1025 		out_fdc(fdcu,fd->fdsu);
1026 		for(i=0;i<7;i++) {
1027 			fdc->status[i] = in_fdc(fdcu);
1028 		}
1029 	printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
1030 		fdc->status[0],
1031 		fdc->status[1],
1032 		fdc->status[2],
1033 		fdc->status[3],
1034 		fdc->status[4],
1035 		fdc->status[5],
1036 		fdc->status[6] );
1037 		return(0);
1038 	}
1039 	return(1); /* Come back immediatly to new state */
1040 }
1041 
1042 static int
1043 retrier(fdcu)
1044 	fdcu_t fdcu;
1045 {
1046 	fdc_p fdc = fdc_data + fdcu;
1047 	register struct buf *dp,*bp;
1048 
1049 	dp = &(fdc->head);
1050 	bp = dp->b_actf;
1051 
1052 	switch(fdc->retry)
1053 	{
1054 	case 0: case 1: case 2:
1055 		fdc->state = SEEKCOMPLETE;
1056 		break;
1057 	case 3: case 4: case 5:
1058 		fdc->state = STARTRECAL;
1059 		break;
1060 	case 6:
1061 		fdc->state = RESETCTLR;
1062 		break;
1063 	case 7:
1064 		break;
1065 	default:
1066 		{
1067 			dev_t sav_b_dev = bp->b_dev;
1068 			/* Trick diskerr */
1069 			bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3);
1070 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
1071 				fdc->fd->skip, (struct disklabel *)NULL);
1072 			bp->b_dev = sav_b_dev;
1073 			printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
1074 			printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
1075 			printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
1076 			printf("cyl %d hd %d sec %d)\n",
1077 			       fdc->status[3], fdc->status[4], fdc->status[5]);
1078 		}
1079 		bp->b_flags |= B_ERROR;
1080 		bp->b_error = EIO;
1081 		bp->b_resid = bp->b_bcount - fdc->fd->skip;
1082 		dp->b_actf = bp->av_forw;
1083 		fdc->fd->skip = 0;
1084 		biodone(bp);
1085 		fdc->state = FINDWORK;
1086 		fdc->fd = (fd_p) 0;
1087 		fdc->fdu = -1;
1088 		/* XXX abort current command, if any.  */
1089 		return(1);
1090 	}
1091 	fdc->retry++;
1092 	return(1);
1093 }
1094 
1095 static int
1096 fdformat(dev, finfo, p)
1097 	dev_t dev;
1098 	struct fd_formb *finfo;
1099 	struct proc *p;
1100 {
1101  	fdu_t	fdu;
1102  	fd_p	fd;
1103 
1104 	struct buf *bp;
1105 	int rv = 0, s;
1106 
1107  	fdu = FDUNIT(minor(dev));
1108 	fd = &fd_data[fdu];
1109 
1110 	/* set up a buffer header for fdstrategy() */
1111 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1112 	if(bp == 0)
1113 		return ENOBUFS;
1114 	bzero((void *)bp, sizeof(struct buf));
1115 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1116 	bp->b_proc = p;
1117 	bp->b_dev = dev;
1118 
1119 	/*
1120 	 * calculate a fake blkno, so fdstrategy() would initiate a
1121 	 * seek to the requested cylinder
1122 	 */
1123 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
1124 		+ finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
1125 
1126 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1127 	bp->b_un.b_addr = (caddr_t)finfo;
1128 
1129 	/* now do the format */
1130 	fdstrategy(bp);
1131 
1132 	/* ...and wait for it to complete */
1133 	s = splbio();
1134 	while(!(bp->b_flags & B_DONE))
1135 	{
1136 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1137 		if(rv == EWOULDBLOCK)
1138 			break;
1139 	}
1140 	splx(s);
1141 
1142 	if(rv == EWOULDBLOCK)
1143 	{
1144 		/* timed out */
1145 		biodone(bp);
1146 		rv = EIO;
1147 	}
1148 	free(bp, M_TEMP);
1149 	return rv;
1150 }
1151 
1152 /*
1153  * fdioctl() from jc@irbs.UUCP (John Capo)
1154  * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
1155  * defines fdioctl to be enxio.
1156  *
1157  * TODO: Reformat.
1158  *       Think about allocating buffer off stack.
1159  *       Don't pass uncast 0's and NULL's to read/write/setdisklabel().
1160  *       Watch out for NetBSD's different *disklabel() interface.
1161  *
1162  * Added functionality for floppy formatting
1163  * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
1164  */
1165 
1166 int
1167 fdioctl (dev, cmd, addr, flag, p)
1168 	dev_t dev;
1169 	int cmd;
1170 	caddr_t addr;
1171 	int flag;
1172 	struct proc *p;
1173 {
1174 	struct fd_type *fdt;
1175 	struct disklabel *dl;
1176 	char buffer[DEV_BSIZE];
1177 	int error;
1178 
1179 #if NFT > 0
1180 	int type = FDTYPE(minor(dev));
1181 
1182 	/* check for a tape ioctl */
1183 	if (type & F_TAPE_TYPE)
1184 		return ftioctl(dev, cmd, addr, flag, p);
1185 #endif
1186 
1187 	error = 0;
1188 
1189 	switch (cmd)
1190 	{
1191 	case DIOCGDINFO:
1192 		bzero(buffer, sizeof (buffer));
1193 		dl = (struct disklabel *)buffer;
1194 		dl->d_secsize = FDBLK;
1195 		fdt = fd_data[FDUNIT(minor(dev))].ft;
1196 		dl->d_secpercyl = fdt->size / fdt->tracks;
1197 		dl->d_type = DTYPE_FLOPPY;
1198 
1199 		if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
1200 			error = 0;
1201 		else
1202 			error = EINVAL;
1203 
1204 		*(struct disklabel *)addr = *dl;
1205 		break;
1206 
1207 	case DIOCSDINFO:
1208 		if ((flag & FWRITE) == 0)
1209 			error = EBADF;
1210 		break;
1211 
1212 	case DIOCWLABEL:
1213 		if ((flag & FWRITE) == 0)
1214 			error = EBADF;
1215 		break;
1216 
1217 	case DIOCWDINFO:
1218 		if ((flag & FWRITE) == 0)
1219 		{
1220 			error = EBADF;
1221 			break;
1222 		}
1223 
1224 		dl = (struct disklabel *)addr;
1225 
1226 		if (error = setdisklabel ((struct disklabel *)buffer,
1227 		    dl, 0, NULL))
1228 			break;
1229 
1230 		error = writedisklabel(dev, fdstrategy,
1231 			(struct disklabel *)buffer, NULL);
1232 		break;
1233 
1234 	case FD_FORM:
1235 		if((flag & FWRITE) == 0)
1236 			error = EBADF;	/* must be opened for writing */
1237 		else if(((struct fd_formb *)addr)->format_version !=
1238 			FD_FORMAT_VERSION)
1239 			error = EINVAL;	/* wrong version of formatting prog */
1240 		else
1241 			error = fdformat(dev, (struct fd_formb *)addr, p);
1242 		break;
1243 
1244 	case FD_GTYPE:                  /* get drive type */
1245 		*(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
1246 		break;
1247 
1248 	default:
1249 		error = EINVAL;
1250 		break;
1251 	}
1252 	return (error);
1253 }
1254 
1255 #endif
1256