xref: /freebsd/sys/dev/fdc/fdc.c (revision 0722d6aba3943012e411513ac8b26c69634fe35b)
187f6c662SJulian Elischer /*
25b81b6b3SRodney W. Grimes  * Copyright (c) 1990 The Regents of the University of California.
35b81b6b3SRodney W. Grimes  * All rights reserved.
45b81b6b3SRodney W. Grimes  *
55b81b6b3SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
65b81b6b3SRodney W. Grimes  * Don Ahn.
75b81b6b3SRodney W. Grimes  *
8dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994 by
93a2f7427SDavid Greenman  *  jc@irbs.UUCP (John Capo)
103a2f7427SDavid Greenman  *  vak@zebub.msk.su (Serge Vakulenko)
113a2f7427SDavid Greenman  *  ache@astral.msk.su (Andrew A. Chernov)
12dc16046fSJoerg Wunsch  *
13dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994, 1995 by
143a2f7427SDavid Greenman  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
15dc5df763SJoerg Wunsch  *  dufault@hda.com (Peter Dufault)
163a2f7427SDavid Greenman  *
175b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
185b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
195b81b6b3SRodney W. Grimes  * are met:
205b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
215b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
225b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
235b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
245b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
255b81b6b3SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
265b81b6b3SRodney W. Grimes  *    must display the following acknowledgement:
275b81b6b3SRodney W. Grimes  *	This product includes software developed by the University of
285b81b6b3SRodney W. Grimes  *	California, Berkeley and its contributors.
295b81b6b3SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
305b81b6b3SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
315b81b6b3SRodney W. Grimes  *    without specific prior written permission.
325b81b6b3SRodney W. Grimes  *
335b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
345b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
355b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
365b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
375b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
385b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
395b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
405b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
415b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
425b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
435b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
445b81b6b3SRodney W. Grimes  *
45dc4ff321SRodney W. Grimes  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
460722d6abSJoerg Wunsch  *	$Id: fd.c,v 1.104 1997/09/23 22:14:43 gibbs Exp $
475b81b6b3SRodney W. Grimes  *
485b81b6b3SRodney W. Grimes  */
495b81b6b3SRodney W. Grimes 
50b99f0a4aSAndrew Moore #include "ft.h"
51b99f0a4aSAndrew Moore #if NFT < 1
52b99f0a4aSAndrew Moore #undef NFDC
53b99f0a4aSAndrew Moore #endif
545b81b6b3SRodney W. Grimes #include "fd.h"
55d2fb4892SJoerg Wunsch #include "opt_fdc.h"
565b81b6b3SRodney W. Grimes 
57b99f0a4aSAndrew Moore #if NFDC > 0
58b99f0a4aSAndrew Moore 
59b99f0a4aSAndrew Moore #include <sys/param.h>
60b99f0a4aSAndrew Moore #include <sys/systm.h>
61b99f0a4aSAndrew Moore #include <sys/kernel.h>
62b99f0a4aSAndrew Moore #include <sys/conf.h>
633ac4d1efSBruce Evans #include <sys/fcntl.h>
64671e2ceeSBruce Evans #include <machine/clock.h>
65b99f0a4aSAndrew Moore #include <machine/ioctl_fd.h>
66b99f0a4aSAndrew Moore #include <sys/disklabel.h>
67b99f0a4aSAndrew Moore #include <sys/buf.h>
68b99f0a4aSAndrew Moore #include <sys/malloc.h>
693a2f7427SDavid Greenman #include <sys/proc.h>
70b99f0a4aSAndrew Moore #include <sys/syslog.h>
71d4319080SBruce Evans #ifdef notyet
7292200632SGarrett Wollman #include <sys/dkstat.h>
73d4319080SBruce Evans #endif
74f540b106SGarrett Wollman #include <i386/isa/isa.h>
75f540b106SGarrett Wollman #include <i386/isa/isa_device.h>
76f540b106SGarrett Wollman #include <i386/isa/fdreg.h>
77f540b106SGarrett Wollman #include <i386/isa/fdc.h>
78f540b106SGarrett Wollman #include <i386/isa/rtc.h>
79dc5df763SJoerg Wunsch #include <machine/stdarg.h>
8087eafbcaSPoul-Henning Kamp #if NFT > 0
8187eafbcaSPoul-Henning Kamp #include <sys/ftape.h>
8287eafbcaSPoul-Henning Kamp #include <i386/isa/ftreg.h>
8387eafbcaSPoul-Henning Kamp #endif
848af5d536SJulian Elischer #ifdef DEVFS
858af5d536SJulian Elischer #include <sys/devfsext.h>
868af5d536SJulian Elischer #endif
875b81b6b3SRodney W. Grimes 
88b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
89b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
905b81b6b3SRodney W. Grimes 
910722d6abSJoerg Wunsch /* configuration flags */
920722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
930722d6abSJoerg Wunsch 
940722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
950722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
960722d6abSJoerg Wunsch 
973a2f7427SDavid Greenman /*
983a2f7427SDavid Greenman  * this biotab field doubles as a field for the physical unit number
993a2f7427SDavid Greenman  * on the controller
1003a2f7427SDavid Greenman  */
1013a2f7427SDavid Greenman #define id_physid id_scsiid
1023a2f7427SDavid Greenman 
103dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
104dc5df763SJoerg Wunsch #define FD_FAILED -1
105dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
106dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
107dc5df763SJoerg Wunsch 
108b39c878eSAndrey A. Chernov #define NUMTYPES 14
109b39c878eSAndrey A. Chernov #define NUMDENS  (NUMTYPES - 6)
1107ca0641bSAndrey A. Chernov 
1113a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
112b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
113b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
114b99f0a4aSAndrew Moore #define FD_1720         1
115b99f0a4aSAndrew Moore #define FD_1480         2
116b99f0a4aSAndrew Moore #define FD_1440         3
117b99f0a4aSAndrew Moore #define FD_1200         4
118b99f0a4aSAndrew Moore #define FD_820          5
119b99f0a4aSAndrew Moore #define FD_800          6
120b99f0a4aSAndrew Moore #define FD_720          7
121b99f0a4aSAndrew Moore #define FD_360          8
122ed2fa05eSAndrey A. Chernov 
123b99f0a4aSAndrew Moore #define FD_1480in5_25   9
124b99f0a4aSAndrew Moore #define FD_1440in5_25   10
125b99f0a4aSAndrew Moore #define FD_820in5_25    11
126b99f0a4aSAndrew Moore #define FD_800in5_25    12
127b99f0a4aSAndrew Moore #define FD_720in5_25    13
128b99f0a4aSAndrew Moore #define FD_360in5_25    14
129b99f0a4aSAndrew Moore 
1307ca0641bSAndrey A. Chernov 
1316f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1325b81b6b3SRodney W. Grimes {
133126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
134126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
135126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
136126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
137126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
138126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
139126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
140b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
141ed2fa05eSAndrey A. Chernov 
142126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
143126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
144126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
145126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
146126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
147126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1485b81b6b3SRodney W. Grimes };
1495b81b6b3SRodney W. Grimes 
150b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
151dc16046fSJoerg Wunsch 
1525b81b6b3SRodney W. Grimes /***********************************************************************\
1535b81b6b3SRodney W. Grimes * Per controller structure.						*
1545b81b6b3SRodney W. Grimes \***********************************************************************/
155b99f0a4aSAndrew Moore struct fdc_data fdc_data[NFDC];
1565b81b6b3SRodney W. Grimes 
1575b81b6b3SRodney W. Grimes /***********************************************************************\
1585b81b6b3SRodney W. Grimes * Per drive structure.							*
159b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1605b81b6b3SRodney W. Grimes \***********************************************************************/
1616f4e0bebSPoul-Henning Kamp static struct fd_data {
162b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1635b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1643a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1655b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1665b81b6b3SRodney W. Grimes 	int	flags;
1675b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1685b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1695b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1705b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1715b81b6b3SRodney W. Grimes 	int	skip;
1725b81b6b3SRodney W. Grimes 	int	hddrv;
173dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1745b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1753a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
176d4319080SBruce Evans #ifdef notyet
17792200632SGarrett Wollman 	int	dkunit;		/* disk stats unit number */
178d4319080SBruce Evans #endif
17902a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
18002a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
18187f6c662SJulian Elischer #ifdef DEVFS
18221519754SBruce Evans 	void	*bdevs[1 + NUMDENS + MAXPARTITIONS];
18321519754SBruce Evans 	void	*cdevs[1 + NUMDENS + MAXPARTITIONS];
18487f6c662SJulian Elischer #endif
1855b81b6b3SRodney W. Grimes } fd_data[NFD];
1865b81b6b3SRodney W. Grimes 
1875b81b6b3SRodney W. Grimes /***********************************************************************\
1885b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1895b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1905b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
1915b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
1925b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
1935b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
1945b81b6b3SRodney W. Grimes \***********************************************************************/
195b99f0a4aSAndrew Moore 
1963a2f7427SDavid Greenman #if NFT > 0
1973a2f7427SDavid Greenman int ftopen(dev_t, int);
1983a2f7427SDavid Greenman int ftintr(ftu_t ftu);
1993a2f7427SDavid Greenman int ftclose(dev_t, int);
2003a2f7427SDavid Greenman void ftstrategy(struct buf *);
2013a2f7427SDavid Greenman int ftioctl(dev_t, int, caddr_t, int, struct proc *);
2023a2f7427SDavid Greenman int ftdump(dev_t);
2033a2f7427SDavid Greenman int ftsize(dev_t);
20474fa89f4SRodney W. Grimes int ftattach(struct isa_device *, struct isa_device *, int);
2053a2f7427SDavid Greenman #endif
2065b81b6b3SRodney W. Grimes 
2073a2f7427SDavid Greenman /* autoconfig functions */
2083a2f7427SDavid Greenman static int fdprobe(struct isa_device *);
2093a2f7427SDavid Greenman static int fdattach(struct isa_device *);
2103a2f7427SDavid Greenman 
2113a2f7427SDavid Greenman /* needed for ft driver, thus exported */
2123a2f7427SDavid Greenman int in_fdc(fdcu_t);
2133a2f7427SDavid Greenman int out_fdc(fdcu_t, int);
2143a2f7427SDavid Greenman 
2153a2f7427SDavid Greenman /* internal functions */
2163a2f7427SDavid Greenman static void set_motor(fdcu_t, int, int);
2173a2f7427SDavid Greenman #  define TURNON 1
2183a2f7427SDavid Greenman #  define TURNOFF 0
2193a2f7427SDavid Greenman static timeout_t fd_turnoff;
2203a2f7427SDavid Greenman static timeout_t fd_motor_on;
2213a2f7427SDavid Greenman static void fd_turnon(fdu_t);
2223a2f7427SDavid Greenman static void fdc_reset(fdc_p);
223b5e8ce9fSBruce Evans static int fd_in(fdcu_t, int *);
2243a2f7427SDavid Greenman static void fdstart(fdcu_t);
2253a2f7427SDavid Greenman static timeout_t fd_timeout;
2263a2f7427SDavid Greenman static timeout_t fd_pseudointr;
2273a2f7427SDavid Greenman static int fdstate(fdcu_t, fdc_p);
228aaf08d94SGarrett Wollman static int retrier(fdcu_t);
2293a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2303a2f7427SDavid Greenman 
231d66c374fSTor Egge static int enable_fifo(fdc_p fdc);
232d66c374fSTor Egge 
233d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
234d66c374fSTor Egge 
235aaf08d94SGarrett Wollman 
2365b81b6b3SRodney W. Grimes #define DEVIDLE		0
2375b81b6b3SRodney W. Grimes #define FINDWORK	1
2385b81b6b3SRodney W. Grimes #define	DOSEEK		2
2395b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2405b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2415b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2425b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2435b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2445b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2455b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2465b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2475b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2485b81b6b3SRodney W. Grimes 
249d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
250cba2a7c6SBruce Evans static char const * const fdstates[] =
2515b81b6b3SRodney W. Grimes {
2525b81b6b3SRodney W. Grimes "DEVIDLE",
2535b81b6b3SRodney W. Grimes "FINDWORK",
2545b81b6b3SRodney W. Grimes "DOSEEK",
2555b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2565b81b6b3SRodney W. Grimes "IOCOMPLETE",
2575b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2585b81b6b3SRodney W. Grimes "STARTRECAL",
2595b81b6b3SRodney W. Grimes "RESETCTLR",
2605b81b6b3SRodney W. Grimes "SEEKWAIT",
2615b81b6b3SRodney W. Grimes "RECALWAIT",
2625b81b6b3SRodney W. Grimes "MOTORWAIT",
2635b81b6b3SRodney W. Grimes "IOTIMEDOUT"
2645b81b6b3SRodney W. Grimes };
2655b81b6b3SRodney W. Grimes 
2663a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
267cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2685b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2695b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
270d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2715b81b6b3SRodney W. Grimes #define TRACE0(arg)
2725b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
273d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2745b81b6b3SRodney W. Grimes 
275dc16046fSJoerg Wunsch /* autoconfig structure */
276dc16046fSJoerg Wunsch 
277dc16046fSJoerg Wunsch struct	isa_driver fdcdriver = {
278dc16046fSJoerg Wunsch 	fdprobe, fdattach, "fdc",
279dc16046fSJoerg Wunsch };
280dc16046fSJoerg Wunsch 
28187f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
28287f6c662SJulian Elischer static	d_close_t	fdclose;
28387f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
28487f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
28587f6c662SJulian Elischer 
28687f6c662SJulian Elischer #define CDEV_MAJOR 9
28787f6c662SJulian Elischer #define BDEV_MAJOR 2
288cba8a5ddSPoul-Henning Kamp static struct cdevsw fd_cdevsw;
289d2f265faSPoul-Henning Kamp static struct bdevsw fd_bdevsw =
29087f6c662SJulian Elischer 	{ Fdopen,	fdclose,	fdstrategy,	fdioctl,	/*2*/
291996c772fSJohn Dyson 	  nodump,	nopsize,	D_DISK,	"fd",	&fd_cdevsw,	-1 };
29287f6c662SJulian Elischer 
29387f6c662SJulian Elischer 
2946f4e0bebSPoul-Henning Kamp static struct isa_device *fdcdevs[NFDC];
29592200632SGarrett Wollman 
296dc5df763SJoerg Wunsch static int
297dc5df763SJoerg Wunsch fdc_err(fdcu_t fdcu, const char *s)
298dc5df763SJoerg Wunsch {
299dc5df763SJoerg Wunsch 	fdc_data[fdcu].fdc_errs++;
30016b04b6aSJoerg Wunsch 	if(s) {
301dc5df763SJoerg Wunsch 		if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX)
3026a0e6f42SRodney W. Grimes 			printf("fdc%d: %s", fdcu, s);
303dc5df763SJoerg Wunsch 		else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX)
304dc5df763SJoerg Wunsch 			printf("fdc%d: too many errors, not logging any more\n",
305dc5df763SJoerg Wunsch 			       fdcu);
30616b04b6aSJoerg Wunsch 	}
307dc5df763SJoerg Wunsch 
308dc5df763SJoerg Wunsch 	return FD_FAILED;
309dc5df763SJoerg Wunsch }
310dc5df763SJoerg Wunsch 
311dc5df763SJoerg Wunsch /*
312dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
313dc5df763SJoerg Wunsch  * Unit number,
314dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
315dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
316dc5df763SJoerg Wunsch  */
317dc5df763SJoerg Wunsch 
3186f4e0bebSPoul-Henning Kamp static int
319dc5df763SJoerg Wunsch fd_cmd(fdcu_t fdcu, int n_out, ...)
320dc5df763SJoerg Wunsch {
321dc5df763SJoerg Wunsch 	u_char cmd;
322dc5df763SJoerg Wunsch 	int n_in;
323dc5df763SJoerg Wunsch 	int n;
324dc5df763SJoerg Wunsch 	va_list ap;
325dc5df763SJoerg Wunsch 
326dc5df763SJoerg Wunsch 	va_start(ap, n_out);
327dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
328dc5df763SJoerg Wunsch 	va_end(ap);
329dc5df763SJoerg Wunsch 	va_start(ap, n_out);
330dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
331dc5df763SJoerg Wunsch 	{
332dc5df763SJoerg Wunsch 		if (out_fdc(fdcu, va_arg(ap, int)) < 0)
333dc5df763SJoerg Wunsch 		{
334dc5df763SJoerg Wunsch 			char msg[50];
335dc5df763SJoerg Wunsch 			sprintf(msg,
336dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
337dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
338dc5df763SJoerg Wunsch 			return fdc_err(fdcu, msg);
339dc5df763SJoerg Wunsch 		}
340dc5df763SJoerg Wunsch 	}
341dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
342dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
343dc5df763SJoerg Wunsch 	{
344dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
345dc5df763SJoerg Wunsch 		if (fd_in(fdcu, ptr) < 0)
346dc5df763SJoerg Wunsch 		{
347dc5df763SJoerg Wunsch 			char msg[50];
348dc5df763SJoerg Wunsch 			sprintf(msg,
349dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
350dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
351dc5df763SJoerg Wunsch 			return fdc_err(fdcu, msg);
352dc5df763SJoerg Wunsch 		}
353dc5df763SJoerg Wunsch 	}
354dc5df763SJoerg Wunsch 
355dc5df763SJoerg Wunsch 	return 0;
356dc5df763SJoerg Wunsch }
357dc5df763SJoerg Wunsch 
3586f4e0bebSPoul-Henning Kamp static int
359d66c374fSTor Egge enable_fifo(fdc_p fdc)
360d66c374fSTor Egge {
361d66c374fSTor Egge 	int i, j;
362d66c374fSTor Egge 
363d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
364d66c374fSTor Egge 
365d66c374fSTor Egge 		/*
366d66c374fSTor Egge 		 * XXX:
367d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
368d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
369d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
370d66c374fSTor Egge 		 */
371d66c374fSTor Egge 
372d66c374fSTor Egge 		if (out_fdc(fdc->fdcu, I8207X_CONFIGURE) < 0)
373d66c374fSTor Egge 			return fdc_err(fdc->fdcu, "Enable FIFO failed\n");
374d66c374fSTor Egge 
375d66c374fSTor Egge 		/* If command is invalid, return */
376d66c374fSTor Egge 		j = 100000;
377d66c374fSTor Egge 		while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM))
378d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
379d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
380d66c374fSTor Egge 				fdc_reset(fdc);
381d66c374fSTor Egge 				return FD_FAILED;
382d66c374fSTor Egge 			}
383d66c374fSTor Egge 		if (j<0 ||
384d66c374fSTor Egge 		    fd_cmd(fdc->fdcu, 3,
385d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
386d66c374fSTor Egge 			fdc_reset(fdc);
387d66c374fSTor Egge 			return fdc_err(fdc->fdcu, "Enable FIFO failed\n");
388d66c374fSTor Egge 		}
389d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
390d66c374fSTor Egge 		return 0;
391d66c374fSTor Egge 	}
392d66c374fSTor Egge 	if (fd_cmd(fdc->fdcu, 4,
393d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
394d66c374fSTor Egge 		return fdc_err(fdc->fdcu, "Re-enable FIFO failed\n");
395d66c374fSTor Egge 	return 0;
396d66c374fSTor Egge }
397d66c374fSTor Egge 
398d66c374fSTor Egge static int
399dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
400dc5df763SJoerg Wunsch {
401dc5df763SJoerg Wunsch 	int st3;
402dc5df763SJoerg Wunsch 
403dc5df763SJoerg Wunsch 	if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
404dc5df763SJoerg Wunsch 	{
4056a0e6f42SRodney W. Grimes 		return fdc_err(fdc->fdcu, "Sense Drive Status failed\n");
406dc5df763SJoerg Wunsch 	}
407dc5df763SJoerg Wunsch 	if (st3p)
408dc5df763SJoerg Wunsch 		*st3p = st3;
409dc5df763SJoerg Wunsch 
410dc5df763SJoerg Wunsch 	return 0;
411dc5df763SJoerg Wunsch }
412dc5df763SJoerg Wunsch 
4136f4e0bebSPoul-Henning Kamp static int
414dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
415dc5df763SJoerg Wunsch {
416dc5df763SJoerg Wunsch 	int st0, cyl;
417dc5df763SJoerg Wunsch 
418dc5df763SJoerg Wunsch 	int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0);
419dc5df763SJoerg Wunsch 
420dc5df763SJoerg Wunsch 	if (ret)
421dc5df763SJoerg Wunsch 	{
422dc5df763SJoerg Wunsch 		(void)fdc_err(fdc->fdcu,
423dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
424dc5df763SJoerg Wunsch 		return ret;
425dc5df763SJoerg Wunsch 	}
426dc5df763SJoerg Wunsch 
427dc5df763SJoerg Wunsch 	if (st0p)
428dc5df763SJoerg Wunsch 		*st0p = st0;
429dc5df763SJoerg Wunsch 
430dc5df763SJoerg Wunsch 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV)
431dc5df763SJoerg Wunsch 	{
432dc5df763SJoerg Wunsch 		/*
433dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
434dc5df763SJoerg Wunsch 		 */
435dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
436dc5df763SJoerg Wunsch 	}
437dc5df763SJoerg Wunsch 
438dc5df763SJoerg Wunsch 	if (fd_in(fdc->fdcu, &cyl) < 0)
439dc5df763SJoerg Wunsch 	{
440dc5df763SJoerg Wunsch 		return fdc_err(fdc->fdcu, "can't get cyl num\n");
441dc5df763SJoerg Wunsch 	}
442dc5df763SJoerg Wunsch 
443dc5df763SJoerg Wunsch 	if (cylp)
444dc5df763SJoerg Wunsch 		*cylp = cyl;
445dc5df763SJoerg Wunsch 
446dc5df763SJoerg Wunsch 	return 0;
447dc5df763SJoerg Wunsch }
448dc5df763SJoerg Wunsch 
449dc5df763SJoerg Wunsch 
4506f4e0bebSPoul-Henning Kamp static int
451dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
452dc5df763SJoerg Wunsch {
453dc5df763SJoerg Wunsch 	int i, ret;
454b5e8ce9fSBruce Evans 
455dc5df763SJoerg Wunsch 	for (i = 0; i < 7; i++)
456dc5df763SJoerg Wunsch 	{
457b5e8ce9fSBruce Evans 		/*
458b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
459b5e8ce9fSBruce Evans 		 * from the hardware, but fdc_status wants u_longs and
460b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
461b5e8ce9fSBruce Evans 		 */
462b5e8ce9fSBruce Evans 		int status;
463b5e8ce9fSBruce Evans 
464b5e8ce9fSBruce Evans 		ret = fd_in(fdc->fdcu, &status);
465b5e8ce9fSBruce Evans 		fdc->status[i] = status;
466b5e8ce9fSBruce Evans 		if (ret != 0)
467dc5df763SJoerg Wunsch 			break;
468dc5df763SJoerg Wunsch 	}
469dc5df763SJoerg Wunsch 
470dc5df763SJoerg Wunsch 	if (ret == 0)
471dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
472dc5df763SJoerg Wunsch 	else
473dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
474dc5df763SJoerg Wunsch 
475dc5df763SJoerg Wunsch 	return ret;
476dc5df763SJoerg Wunsch }
477dc5df763SJoerg Wunsch 
4785b81b6b3SRodney W. Grimes /****************************************************************************/
4795b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
4805b81b6b3SRodney W. Grimes /****************************************************************************/
481dc5df763SJoerg Wunsch 
4825b81b6b3SRodney W. Grimes /*
4835b81b6b3SRodney W. Grimes  * probe for existance of controller
4845b81b6b3SRodney W. Grimes  */
4853a2f7427SDavid Greenman static int
486dc5df763SJoerg Wunsch fdprobe(struct isa_device *dev)
4875b81b6b3SRodney W. Grimes {
4885b81b6b3SRodney W. Grimes 	fdcu_t	fdcu = dev->id_unit;
4895b81b6b3SRodney W. Grimes 	if(fdc_data[fdcu].flags & FDC_ATTACHED)
4905b81b6b3SRodney W. Grimes 	{
4916a0e6f42SRodney W. Grimes 		printf("fdc%d: unit used multiple times\n", fdcu);
4925b81b6b3SRodney W. Grimes 		return 0;
4935b81b6b3SRodney W. Grimes 	}
4945b81b6b3SRodney W. Grimes 
49592200632SGarrett Wollman 	fdcdevs[fdcu] = dev;
4965b81b6b3SRodney W. Grimes 	fdc_data[fdcu].baseport = dev->id_iobase;
4975b81b6b3SRodney W. Grimes 
49816111cedSAndrew Moore 	/* First - lets reset the floppy controller */
4993a2f7427SDavid Greenman 	outb(dev->id_iobase+FDOUT, 0);
50016111cedSAndrew Moore 	DELAY(100);
5013a2f7427SDavid Greenman 	outb(dev->id_iobase+FDOUT, FDO_FRST);
50216111cedSAndrew Moore 
5035b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
504dc5df763SJoerg Wunsch 	if (fd_cmd(fdcu,
505dc5df763SJoerg Wunsch 		   3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
506dc5df763SJoerg Wunsch 		   0))
5075b81b6b3SRodney W. Grimes 	{
5085b81b6b3SRodney W. Grimes 		return(0);
5095b81b6b3SRodney W. Grimes 	}
5105b81b6b3SRodney W. Grimes 	return (IO_FDCSIZE);
5115b81b6b3SRodney W. Grimes }
5125b81b6b3SRodney W. Grimes 
5135b81b6b3SRodney W. Grimes /*
5145b81b6b3SRodney W. Grimes  * wire controller into system, look for floppy units
5155b81b6b3SRodney W. Grimes  */
5163a2f7427SDavid Greenman static int
517dc5df763SJoerg Wunsch fdattach(struct isa_device *dev)
5185b81b6b3SRodney W. Grimes {
5193a2f7427SDavid Greenman 	unsigned fdt;
5205b81b6b3SRodney W. Grimes 	fdu_t	fdu;
5215b81b6b3SRodney W. Grimes 	fdcu_t	fdcu = dev->id_unit;
5225b81b6b3SRodney W. Grimes 	fdc_p	fdc = fdc_data + fdcu;
5235b81b6b3SRodney W. Grimes 	fd_p	fd;
524cba2a7c6SBruce Evans 	int	fdsu, st0, st3, i;
525cba2a7c6SBruce Evans #if NFT > 0
526cba2a7c6SBruce Evans 	int	unithasfd;
527cba2a7c6SBruce Evans #endif
528b99f0a4aSAndrew Moore 	struct isa_device *fdup;
529dc5df763SJoerg Wunsch 	int ic_type = 0;
530999422d7SJulian Elischer #ifdef DEVFS
531c8f2fe8dSBruce Evans 	int	mynor;
53221519754SBruce Evans 	int	typemynor;
53321519754SBruce Evans 	int	typesize;
53421519754SBruce Evans #endif
53592200632SGarrett Wollman 
5365b81b6b3SRodney W. Grimes 	fdc->fdcu = fdcu;
5375b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
5385b81b6b3SRodney W. Grimes 	fdc->dmachan = dev->id_drq;
539100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
540100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
541dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
5425b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
5433a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
5443a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
54502a19910SJustin T. Gibbs 	bufq_init(&fdc->head);
5465b81b6b3SRodney W. Grimes 
5475b81b6b3SRodney W. Grimes 	/* check for each floppy drive */
548b99f0a4aSAndrew Moore 	for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
549b99f0a4aSAndrew Moore 		if (fdup->id_iobase != dev->id_iobase)
550b99f0a4aSAndrew Moore 			continue;
551b99f0a4aSAndrew Moore 		fdu = fdup->id_unit;
552b99f0a4aSAndrew Moore 		fd = &fd_data[fdu];
553b99f0a4aSAndrew Moore 		if (fdu >= (NFD+NFT))
554b99f0a4aSAndrew Moore 			continue;
555b99f0a4aSAndrew Moore 		fdsu = fdup->id_physid;
556b99f0a4aSAndrew Moore 		/* look up what bios thinks we have */
557b99f0a4aSAndrew Moore 		switch (fdu) {
5580722d6abSJoerg Wunsch 			case 0: if (dev->id_flags & FDC_PRETEND_D0)
5590722d6abSJoerg Wunsch 					fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
5600722d6abSJoerg Wunsch 				else
5610722d6abSJoerg Wunsch 					fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
562b99f0a4aSAndrew Moore 				break;
563b99f0a4aSAndrew Moore 			case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
564b99f0a4aSAndrew Moore 				break;
565b99f0a4aSAndrew Moore 			default: fdt = RTCFDT_NONE;
566b99f0a4aSAndrew Moore 				break;
567b99f0a4aSAndrew Moore 		}
5685b81b6b3SRodney W. Grimes 		/* is there a unit? */
569b99f0a4aSAndrew Moore 		if ((fdt == RTCFDT_NONE)
570b99f0a4aSAndrew Moore #if NFT > 0
571b99f0a4aSAndrew Moore 		    || (fdsu >= DRVS_PER_CTLR)) {
572b99f0a4aSAndrew Moore #else
573b99f0a4aSAndrew Moore 		) {
57456ef0285SAndrew Moore 			fd->type = NO_TYPE;
575b99f0a4aSAndrew Moore #endif
576b99f0a4aSAndrew Moore #if NFT > 0
577b99f0a4aSAndrew Moore 			/* If BIOS says no floppy, or > 2nd device */
578b99f0a4aSAndrew Moore 			/* Probe for and attach a floppy tape.     */
57974fa89f4SRodney W. Grimes 			/* Tell FT if there was already a disk     */
58074fa89f4SRodney W. Grimes 			/* with this unit number found.            */
58174fa89f4SRodney W. Grimes 
58274fa89f4SRodney W. Grimes 			unithasfd = 0;
58374fa89f4SRodney W. Grimes 			if (fdu < NFD && fd->type != NO_TYPE)
58474fa89f4SRodney W. Grimes 				unithasfd = 1;
58585827d9cSJoerg Wunsch 			if (ftattach(dev, fdup, unithasfd))
586b99f0a4aSAndrew Moore 				continue;
58756ef0285SAndrew Moore 			if (fdsu < DRVS_PER_CTLR)
588b99f0a4aSAndrew Moore 				fd->type = NO_TYPE;
58956ef0285SAndrew Moore #endif
5905b81b6b3SRodney W. Grimes 			continue;
591f5f7ba03SJordan K. Hubbard 		}
5925b81b6b3SRodney W. Grimes 
5935b81b6b3SRodney W. Grimes 		/* select it */
5943a2f7427SDavid Greenman 		set_motor(fdcu, fdsu, TURNON);
5956b7bd95bSJoerg Wunsch 		DELAY(1000000);	/* 1 sec */
596dc5df763SJoerg Wunsch 
597dc5df763SJoerg Wunsch 		if (ic_type == 0 &&
598dc5df763SJoerg Wunsch 		    fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0)
599dc5df763SJoerg Wunsch 		{
600d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6016a0e6f42SRodney W. Grimes 			printf("fdc%d: ", fdcu);
602d2fb4892SJoerg Wunsch #endif
603dc5df763SJoerg Wunsch 			ic_type = (u_char)ic_type;
604dc5df763SJoerg Wunsch 			switch( ic_type ) {
605dc5df763SJoerg Wunsch 			case 0x80:
606d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6076a0e6f42SRodney W. Grimes 				printf("NEC 765\n");
608d2fb4892SJoerg Wunsch #endif
609dc5df763SJoerg Wunsch 				fdc->fdct = FDC_NE765;
610dc5df763SJoerg Wunsch 				break;
611dc5df763SJoerg Wunsch 			case 0x81:
612d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6136a0e6f42SRodney W. Grimes 				printf("Intel 82077\n");
614d2fb4892SJoerg Wunsch #endif
615dc5df763SJoerg Wunsch 				fdc->fdct = FDC_I82077;
616dc5df763SJoerg Wunsch 				break;
617dc5df763SJoerg Wunsch 			case 0x90:
618d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6196a0e6f42SRodney W. Grimes 				printf("NEC 72065B\n");
620d2fb4892SJoerg Wunsch #endif
621dc5df763SJoerg Wunsch 				fdc->fdct = FDC_NE72065;
622dc5df763SJoerg Wunsch 				break;
623dc5df763SJoerg Wunsch 			default:
624d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6256a0e6f42SRodney W. Grimes 				printf("unknown IC type %02x\n", ic_type);
626d2fb4892SJoerg Wunsch #endif
627dc5df763SJoerg Wunsch 				fdc->fdct = FDC_UNKNOWN;
628dc5df763SJoerg Wunsch 				break;
6296b7bd95bSJoerg Wunsch 			}
630d66c374fSTor Egge 			if (fdc->fdct != FDC_NE765 &&
631d66c374fSTor Egge 			    fdc->fdct != FDC_UNKNOWN &&
632d66c374fSTor Egge 			    enable_fifo(fdc) == 0) {
633d66c374fSTor Egge 				printf("fdc%d: FIFO enabled", fdcu);
634d66c374fSTor Egge 				printf(", %d bytes threshold\n",
635d66c374fSTor Egge 				       fifo_threshold);
636d66c374fSTor Egge 			}
637dc5df763SJoerg Wunsch 		}
638dc5df763SJoerg Wunsch 		if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
639dc5df763SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
640dc5df763SJoerg Wunsch 			/* if at track 0, first seek inwards */
641dc5df763SJoerg Wunsch 			/* seek some steps: */
642dc5df763SJoerg Wunsch 			(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
643dc5df763SJoerg Wunsch 			DELAY(300000); /* ...wait a moment... */
644dc5df763SJoerg Wunsch 			(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
645dc5df763SJoerg Wunsch 		}
646dc5df763SJoerg Wunsch 
647dc5df763SJoerg Wunsch 		/* If we're at track 0 first seek inwards. */
648dc5df763SJoerg Wunsch 		if ((fd_sense_drive_status(fdc, &st3) == 0) &&
649dc5df763SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
650dc5df763SJoerg Wunsch 			/* Seek some steps... */
651dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
652dc5df763SJoerg Wunsch 				/* ...wait a moment... */
653dc5df763SJoerg Wunsch 				DELAY(300000);
654dc5df763SJoerg Wunsch 				/* make ctrlr happy: */
655dc5df763SJoerg Wunsch 				(void)fd_sense_int(fdc, 0, 0);
656dc5df763SJoerg Wunsch 			}
657dc5df763SJoerg Wunsch 		}
658dc5df763SJoerg Wunsch 
6596b7bd95bSJoerg Wunsch 		for(i = 0; i < 2; i++) {
6606b7bd95bSJoerg Wunsch 			/*
6616b7bd95bSJoerg Wunsch 			 * we must recalibrate twice, just in case the
6626b7bd95bSJoerg Wunsch 			 * heads have been beyond cylinder 76, since most
6636b7bd95bSJoerg Wunsch 			 * FDCs still barf when attempting to recalibrate
6646b7bd95bSJoerg Wunsch 			 * more than 77 steps
6656b7bd95bSJoerg Wunsch 			 */
666dc5df763SJoerg Wunsch 			/* go back to 0: */
667dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
6686b7bd95bSJoerg Wunsch 				/* a second being enough for full stroke seek*/
6696b7bd95bSJoerg Wunsch 				DELAY(i == 0? 1000000: 300000);
6705b81b6b3SRodney W. Grimes 
6716b7bd95bSJoerg Wunsch 				/* anything responding? */
672dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, 0) == 0 &&
673dc5df763SJoerg Wunsch 				(st0 & NE7_ST0_EC) == 0)
6746b7bd95bSJoerg Wunsch 					break; /* already probed succesfully */
6756b7bd95bSJoerg Wunsch 			}
676dc5df763SJoerg Wunsch 		}
6776b7bd95bSJoerg Wunsch 
6783a2f7427SDavid Greenman 		set_motor(fdcu, fdsu, TURNOFF);
6793a2f7427SDavid Greenman 
6803a2f7427SDavid Greenman 		if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
6815b81b6b3SRodney W. Grimes 			continue;
6825b81b6b3SRodney W. Grimes 
683dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
684b99f0a4aSAndrew Moore 		fd->fdc = fdc;
685b99f0a4aSAndrew Moore 		fd->fdsu = fdsu;
6863a2f7427SDavid Greenman 		fd->options = 0;
68702a19910SJustin T. Gibbs 		callout_handle_init(&fd->toffhandle);
68802a19910SJustin T. Gibbs 		callout_handle_init(&fd->tohandle);
6892d9d0204SRodney W. Grimes 		printf("fd%d: ", fdu);
6905b81b6b3SRodney W. Grimes 
691b99f0a4aSAndrew Moore 		switch (fdt) {
6927ca0641bSAndrey A. Chernov 		case RTCFDT_12M:
6936a0e6f42SRodney W. Grimes 			printf("1.2MB 5.25in\n");
694b99f0a4aSAndrew Moore 			fd->type = FD_1200;
6957ca0641bSAndrey A. Chernov 			break;
6960722d6abSJoerg Wunsch 		case RTCFDT_144M | RTCFDT_144M_PRETENDED:
6970722d6abSJoerg Wunsch 			printf("config-pretended ");
6980722d6abSJoerg Wunsch 			fdt = RTCFDT_144M;
6990722d6abSJoerg Wunsch 			/* fallthrough */
7007ca0641bSAndrey A. Chernov 		case RTCFDT_144M:
7016a0e6f42SRodney W. Grimes 			printf("1.44MB 3.5in\n");
702b99f0a4aSAndrew Moore 			fd->type = FD_1440;
7037ca0641bSAndrey A. Chernov 			break;
704290dd077SJoerg Wunsch 		case RTCFDT_288M:
70586a727d9SJoerg Wunsch 		case RTCFDT_288M_1:
7066a0e6f42SRodney W. Grimes 			printf("2.88MB 3.5in - 1.44MB mode\n");
707290dd077SJoerg Wunsch 			fd->type = FD_1440;
708290dd077SJoerg Wunsch 			break;
7097ca0641bSAndrey A. Chernov 		case RTCFDT_360K:
7106a0e6f42SRodney W. Grimes 			printf("360KB 5.25in\n");
711b99f0a4aSAndrew Moore 			fd->type = FD_360;
7127ca0641bSAndrey A. Chernov 			break;
713ed2fa05eSAndrey A. Chernov 		case RTCFDT_720K:
7146a0e6f42SRodney W. Grimes 			printf("720KB 3.5in\n");
715b99f0a4aSAndrew Moore 			fd->type = FD_720;
716ed2fa05eSAndrey A. Chernov 			break;
7177ca0641bSAndrey A. Chernov 		default:
7186a0e6f42SRodney W. Grimes 			printf("unknown\n");
719b99f0a4aSAndrew Moore 			fd->type = NO_TYPE;
72021519754SBruce Evans 			continue;
7215b81b6b3SRodney W. Grimes 		}
722999422d7SJulian Elischer #ifdef DEVFS
72321519754SBruce Evans 		mynor = fdu << 6;
72421519754SBruce Evans 		fd->bdevs[0] = devfs_add_devswf(&fd_bdevsw, mynor, DV_BLK,
725f85120acSBruce Evans 						UID_ROOT, GID_OPERATOR, 0640,
72621519754SBruce Evans 						"fd%d", fdu);
72721519754SBruce Evans 		fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
728f85120acSBruce Evans 						UID_ROOT, GID_OPERATOR, 0640,
72921519754SBruce Evans 						"rfd%d", fdu);
73021519754SBruce Evans 		for (i = 1; i < 1 + NUMDENS; i++) {
73121519754SBruce Evans 			/*
73221519754SBruce Evans 			 * XXX this and the lookup in Fdopen() should be
73321519754SBruce Evans 			 * data driven.
73421519754SBruce Evans 			 */
73521519754SBruce Evans 			switch (fd->type) {
73621519754SBruce Evans 			case FD_360:
73721519754SBruce Evans 				if (i != FD_360)
73821519754SBruce Evans 					continue;
73921519754SBruce Evans 				break;
74021519754SBruce Evans 			case FD_720:
74121519754SBruce Evans 				if (i != FD_720 && i != FD_800 && i != FD_820)
74221519754SBruce Evans 					continue;
74321519754SBruce Evans 				break;
74421519754SBruce Evans 			case FD_1200:
74521519754SBruce Evans 				if (i != FD_360 && i != FD_720 && i != FD_800
74621519754SBruce Evans 				    && i != FD_820 && i != FD_1200
74721519754SBruce Evans 				    && i != FD_1440 && i != FD_1480)
74821519754SBruce Evans 					continue;
74921519754SBruce Evans 				break;
75021519754SBruce Evans 			case FD_1440:
75121519754SBruce Evans 				if (i != FD_720 && i != FD_800 && i != FD_820
75221519754SBruce Evans 				    && i != FD_1200 && i != FD_1440
75321519754SBruce Evans 				    && i != FD_1480 && i != FD_1720)
75421519754SBruce Evans 					continue;
75521519754SBruce Evans 				break;
75621519754SBruce Evans 			}
75721519754SBruce Evans 			typemynor = mynor | i;
75821519754SBruce Evans 			typesize = fd_types[i - 1].size / 2;
75921519754SBruce Evans 			/*
76021519754SBruce Evans 			 * XXX all these conversions give bloated code and
76121519754SBruce Evans 			 * confusing names.
76221519754SBruce Evans 			 */
76321519754SBruce Evans 			if (typesize == 1476)
76421519754SBruce Evans 				typesize = 1480;
76521519754SBruce Evans 			if (typesize == 1722)
76621519754SBruce Evans 				typesize = 1720;
76721519754SBruce Evans 			fd->bdevs[i] =
76821519754SBruce Evans 				devfs_add_devswf(&fd_bdevsw, typemynor, DV_BLK,
76921519754SBruce Evans 						 UID_ROOT, GID_OPERATOR, 0640,
77021519754SBruce Evans 						 "fd%d.%d", fdu, typesize);
77121519754SBruce Evans 			fd->cdevs[i] =
77221519754SBruce Evans 				devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR,
77321519754SBruce Evans 						 UID_ROOT, GID_OPERATOR, 0640,
77421519754SBruce Evans 						 "rfd%d.%d", fdu, typesize);
77521519754SBruce Evans 		}
77621519754SBruce Evans 		for (i = 0; i < MAXPARTITIONS; i++) {
77721519754SBruce Evans 			fd->bdevs[1 + NUMDENS + i] =
77821519754SBruce Evans 				devfs_link(fd->bdevs[0],
77921519754SBruce Evans 					   "fd%d%c", fdu, 'a' + i);
78021519754SBruce Evans 			fd->cdevs[1 + NUMDENS + i] =
78121519754SBruce Evans 				devfs_link(fd->cdevs[0],
78221519754SBruce Evans 					   "rfd%d%c", fdu, 'a' + i);
78321519754SBruce Evans 		}
784999422d7SJulian Elischer #endif /* DEVFS */
785d4319080SBruce Evans #ifdef notyet
78692200632SGarrett Wollman 		if (dk_ndrive < DK_NDRIVE) {
78792200632SGarrett Wollman 			sprintf(dk_names[dk_ndrive], "fd%d", fdu);
7886a0e6f42SRodney W. Grimes 			fd->dkunit = dk_ndrive++;
789671e2ceeSBruce Evans 			/*
790671e2ceeSBruce Evans 			 * XXX assume rate is FDC_500KBPS.
791671e2ceeSBruce Evans 			 */
792671e2ceeSBruce Evans 			dk_wpms[dk_ndrive] = 500000 / 8 / 2;
79392200632SGarrett Wollman 		} else {
79492200632SGarrett Wollman 			fd->dkunit = -1;
79592200632SGarrett Wollman 		}
796d4319080SBruce Evans #endif
7975b81b6b3SRodney W. Grimes 	}
7985b81b6b3SRodney W. Grimes 
7993a2f7427SDavid Greenman 	return (1);
8005b81b6b3SRodney W. Grimes }
8015b81b6b3SRodney W. Grimes 
8025b81b6b3SRodney W. Grimes /****************************************************************************/
8035b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
8045e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
8055b81b6b3SRodney W. Grimes /****************************************************************************/
8063a2f7427SDavid Greenman static void
807dc5df763SJoerg Wunsch set_motor(fdcu_t fdcu, int fdsu, int turnon)
8085b81b6b3SRodney W. Grimes {
8093a2f7427SDavid Greenman 	int fdout = fdc_data[fdcu].fdout;
8103a2f7427SDavid Greenman 	int needspecify = 0;
8113a2f7427SDavid Greenman 
8123a2f7427SDavid Greenman 	if(turnon) {
8133a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
8143a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
8153a2f7427SDavid Greenman 	} else
8163a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
8173a2f7427SDavid Greenman 
8185e235068SJordan K. Hubbard 	if(!turnon
8195e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
8205e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
8215e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
8225e235068SJordan K. Hubbard 	else {
8233a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
8243a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
8253a2f7427SDavid Greenman 			needspecify = 1;
8263a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
8275b81b6b3SRodney W. Grimes 	}
8285b81b6b3SRodney W. Grimes 
8293a2f7427SDavid Greenman 	outb(fdc_data[fdcu].baseport+FDOUT, fdout);
8303a2f7427SDavid Greenman 	fdc_data[fdcu].fdout = fdout;
8313a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
8323a2f7427SDavid Greenman 
8333a2f7427SDavid Greenman 	if(needspecify) {
834dc8603e3SJoerg Wunsch 		/*
835dc5df763SJoerg Wunsch 		 * XXX
836dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
837dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
838dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
839dc8603e3SJoerg Wunsch 		 */
840dc5df763SJoerg Wunsch 		(void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
841dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
842dc5df763SJoerg Wunsch 			     0);
843d66c374fSTor Egge 		if (fdc_data[fdcu].flags & FDC_HAS_FIFO)
844d66c374fSTor Egge 			(void) enable_fifo(&fdc_data[fdcu]);
8453a2f7427SDavid Greenman 	}
8463a2f7427SDavid Greenman }
8473a2f7427SDavid Greenman 
848381fe1aaSGarrett Wollman static void
849d0917939SPaul Richards fd_turnoff(void *arg1)
8505b81b6b3SRodney W. Grimes {
851381fe1aaSGarrett Wollman 	fdu_t fdu = (fdu_t)arg1;
852f5f7ba03SJordan K. Hubbard 	int	s;
8535b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
854dc16046fSJoerg Wunsch 
855dc16046fSJoerg Wunsch 	TRACE1("[fd%d: turnoff]", fdu);
8568335c1b8SBruce Evans 
8578335c1b8SBruce Evans 	/*
8588335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
8598335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
8608335c1b8SBruce Evans 	 * and nothing is queued on it.
8618335c1b8SBruce Evans 	 */
8628335c1b8SBruce Evans 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) {
86302a19910SJustin T. Gibbs 		fd->toffhandle = timeout(fd_turnoff, arg1, 4 * hz);
8648335c1b8SBruce Evans 		return;
8658335c1b8SBruce Evans 	}
8668335c1b8SBruce Evans 
867f5f7ba03SJordan K. Hubbard 	s = splbio();
8685b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
8693a2f7427SDavid Greenman 	set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
870f5f7ba03SJordan K. Hubbard 	splx(s);
8715b81b6b3SRodney W. Grimes }
8725b81b6b3SRodney W. Grimes 
8733a2f7427SDavid Greenman static void
874d0917939SPaul Richards fd_motor_on(void *arg1)
8755b81b6b3SRodney W. Grimes {
876381fe1aaSGarrett Wollman 	fdu_t fdu = (fdu_t)arg1;
877f5f7ba03SJordan K. Hubbard 	int	s;
878f5f7ba03SJordan K. Hubbard 
8795b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
880f5f7ba03SJordan K. Hubbard 	s = splbio();
8815b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
8825b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
8835b81b6b3SRodney W. Grimes 	{
884f5f7ba03SJordan K. Hubbard 		fdintr(fd->fdc->fdcu);
8855b81b6b3SRodney W. Grimes 	}
886f5f7ba03SJordan K. Hubbard 	splx(s);
8875b81b6b3SRodney W. Grimes }
8885b81b6b3SRodney W. Grimes 
8893a2f7427SDavid Greenman static void
890dc5df763SJoerg Wunsch fd_turnon(fdu_t fdu)
8915b81b6b3SRodney W. Grimes {
8925b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
8935b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
8945b81b6b3SRodney W. Grimes 	{
8953a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
8963a2f7427SDavid Greenman 		set_motor(fd->fdc->fdcu, fd->fdsu, TURNON);
8975e235068SJordan K. Hubbard 		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
8985b81b6b3SRodney W. Grimes 	}
8995b81b6b3SRodney W. Grimes }
9005b81b6b3SRodney W. Grimes 
901381fe1aaSGarrett Wollman static void
902dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
9035b81b6b3SRodney W. Grimes {
9043a2f7427SDavid Greenman 	fdcu_t fdcu = fdc->fdcu;
9053a2f7427SDavid Greenman 
9063a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
9073a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
9083a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
9093a2f7427SDavid Greenman 	DELAY(100);
9103a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
9113a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
9123a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
9133a2f7427SDavid Greenman 	DELAY(100);
9143a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout);
9153a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
9163a2f7427SDavid Greenman 
917dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
918dc5df763SJoerg Wunsch 	(void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
919dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
920dc5df763SJoerg Wunsch 		     0);
921d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
922d66c374fSTor Egge 		(void) enable_fifo(fdc);
9235b81b6b3SRodney W. Grimes }
9245b81b6b3SRodney W. Grimes 
9255b81b6b3SRodney W. Grimes /****************************************************************************/
9265b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
9275b81b6b3SRodney W. Grimes /****************************************************************************/
9285b81b6b3SRodney W. Grimes int
929dc5df763SJoerg Wunsch in_fdc(fdcu_t fdcu)
9305b81b6b3SRodney W. Grimes {
9315b81b6b3SRodney W. Grimes 	int baseport = fdc_data[fdcu].baseport;
9325b81b6b3SRodney W. Grimes 	int i, j = 100000;
9333a2f7427SDavid Greenman 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
9345b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
935dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
9366a0e6f42SRodney W. Grimes 			return fdc_err(fdcu, "ready for output in input\n");
9375b81b6b3SRodney W. Grimes 	if (j <= 0)
93816b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
939d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
9403a2f7427SDavid Greenman 	i = inb(baseport+FDDATA);
9413a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
9425b81b6b3SRodney W. Grimes 	return(i);
943d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
9443a2f7427SDavid Greenman 	return inb(baseport+FDDATA);
945d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
9465b81b6b3SRodney W. Grimes }
9475b81b6b3SRodney W. Grimes 
948dc5df763SJoerg Wunsch /*
949dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
950dc5df763SJoerg Wunsch  */
951b5e8ce9fSBruce Evans static int
952dc5df763SJoerg Wunsch fd_in(fdcu_t fdcu, int *ptr)
953dc5df763SJoerg Wunsch {
954dc5df763SJoerg Wunsch 	int baseport = fdc_data[fdcu].baseport;
955dc5df763SJoerg Wunsch 	int i, j = 100000;
956dc5df763SJoerg Wunsch 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
957dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
958dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
9596a0e6f42SRodney W. Grimes 			return fdc_err(fdcu, "ready for output in input\n");
960dc5df763SJoerg Wunsch 	if (j <= 0)
96116b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
962d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
963dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
964dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
965dc5df763SJoerg Wunsch 	*ptr = i;
966dc5df763SJoerg Wunsch 	return 0;
967d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
968dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
969dc5df763SJoerg Wunsch 	if (ptr)
970dc5df763SJoerg Wunsch 		*ptr = i;
971dc5df763SJoerg Wunsch 	return 0;
972d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
973dc5df763SJoerg Wunsch }
974dc5df763SJoerg Wunsch 
975dc5df763SJoerg Wunsch int
976dc5df763SJoerg Wunsch out_fdc(fdcu_t fdcu, int x)
9775b81b6b3SRodney W. Grimes {
9785b81b6b3SRodney W. Grimes 	int baseport = fdc_data[fdcu].baseport;
9793b3837dbSRodney W. Grimes 	int i;
9805b81b6b3SRodney W. Grimes 
9813b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
9823b3837dbSRodney W. Grimes 	i = 100000;
9833a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
9846a0e6f42SRodney W. Grimes 	if (i <= 0) return fdc_err(fdcu, "direction bit not set\n");
9853b3837dbSRodney W. Grimes 
9863b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
9873b3837dbSRodney W. Grimes 	i = 100000;
9883a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
98916b04b6aSJoerg Wunsch 	if (i <= 0)
99016b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0);
9913b3837dbSRodney W. Grimes 
9923b3837dbSRodney W. Grimes 	/* Send the command and return */
9933a2f7427SDavid Greenman 	outb(baseport+FDDATA, x);
9943a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
9955b81b6b3SRodney W. Grimes 	return (0);
9965b81b6b3SRodney W. Grimes }
9975b81b6b3SRodney W. Grimes 
9985b81b6b3SRodney W. Grimes /****************************************************************************/
9995b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
10005b81b6b3SRodney W. Grimes /****************************************************************************/
1001381fe1aaSGarrett Wollman int
1002671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
10035b81b6b3SRodney W. Grimes {
10045b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
100520a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
1006b99f0a4aSAndrew Moore 	fdc_p	fdc;
10075b81b6b3SRodney W. Grimes 
1008b99f0a4aSAndrew Moore #if NFT > 0
1009b99f0a4aSAndrew Moore 	/* check for a tape open */
1010b99f0a4aSAndrew Moore 	if (type & F_TAPE_TYPE)
1011b99f0a4aSAndrew Moore 		return(ftopen(dev, flags));
1012b99f0a4aSAndrew Moore #endif
10135b81b6b3SRodney W. Grimes 	/* check bounds */
1014b99f0a4aSAndrew Moore 	if (fdu >= NFD)
1015b99f0a4aSAndrew Moore 		return(ENXIO);
1016b99f0a4aSAndrew Moore 	fdc = fd_data[fdu].fdc;
1017b99f0a4aSAndrew Moore 	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
1018b99f0a4aSAndrew Moore 		return(ENXIO);
1019b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1020b99f0a4aSAndrew Moore 		return(ENXIO);
10217ca0641bSAndrey A. Chernov 	if (type == 0)
10227ca0641bSAndrey A. Chernov 		type = fd_data[fdu].type;
10237ca0641bSAndrey A. Chernov 	else {
10247ca0641bSAndrey A. Chernov 		if (type != fd_data[fdu].type) {
1025fa4700b4SAndrey A. Chernov 			switch (fd_data[fdu].type) {
10267ca0641bSAndrey A. Chernov 			case FD_360:
10277ca0641bSAndrey A. Chernov 				return(ENXIO);
1028ed2fa05eSAndrey A. Chernov 			case FD_720:
1029b39c878eSAndrey A. Chernov 				if (   type != FD_820
1030b39c878eSAndrey A. Chernov 				    && type != FD_800
1031ed2fa05eSAndrey A. Chernov 				   )
1032ed2fa05eSAndrey A. Chernov 					return(ENXIO);
1033ed2fa05eSAndrey A. Chernov 				break;
10347ca0641bSAndrey A. Chernov 			case FD_1200:
1035b39c878eSAndrey A. Chernov 				switch (type) {
1036b39c878eSAndrey A. Chernov 				case FD_1480:
1037b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1038fa4700b4SAndrey A. Chernov 					break;
10397ca0641bSAndrey A. Chernov 				case FD_1440:
1040b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1041b39c878eSAndrey A. Chernov 					break;
1042b39c878eSAndrey A. Chernov 				case FD_820:
1043b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1044b39c878eSAndrey A. Chernov 					break;
1045b39c878eSAndrey A. Chernov 				case FD_800:
1046b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1047b39c878eSAndrey A. Chernov 					break;
1048b39c878eSAndrey A. Chernov 				case FD_720:
1049b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1050b39c878eSAndrey A. Chernov 					break;
1051b39c878eSAndrey A. Chernov 				case FD_360:
1052b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1053b39c878eSAndrey A. Chernov 					break;
1054b39c878eSAndrey A. Chernov 				default:
1055b39c878eSAndrey A. Chernov 					return(ENXIO);
1056b39c878eSAndrey A. Chernov 				}
1057b39c878eSAndrey A. Chernov 				break;
1058b39c878eSAndrey A. Chernov 			case FD_1440:
1059b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1060b39c878eSAndrey A. Chernov 				    && type != FD_1480
1061ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1062b39c878eSAndrey A. Chernov 				    && type != FD_820
1063b39c878eSAndrey A. Chernov 				    && type != FD_800
1064b39c878eSAndrey A. Chernov 				    && type != FD_720
10657ca0641bSAndrey A. Chernov 				    )
1066dffff499SAndrey A. Chernov 					return(ENXIO);
1067fa4700b4SAndrey A. Chernov 				break;
10687ca0641bSAndrey A. Chernov 			}
10697ca0641bSAndrey A. Chernov 		}
1070fa4700b4SAndrey A. Chernov 	}
1071b99f0a4aSAndrew Moore 	fd_data[fdu].ft = fd_types + type - 1;
10725b81b6b3SRodney W. Grimes 	fd_data[fdu].flags |= FD_OPEN;
10735b81b6b3SRodney W. Grimes 
10745b81b6b3SRodney W. Grimes 	return 0;
10755b81b6b3SRodney W. Grimes }
10765b81b6b3SRodney W. Grimes 
1077381fe1aaSGarrett Wollman int
1078671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
10795b81b6b3SRodney W. Grimes {
10805b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
1081b99f0a4aSAndrew Moore 
1082b99f0a4aSAndrew Moore #if NFT > 0
10833a2f7427SDavid Greenman 	int type = FDTYPE(minor(dev));
10843a2f7427SDavid Greenman 
1085b99f0a4aSAndrew Moore 	if (type & F_TAPE_TYPE)
10863a2f7427SDavid Greenman 		return ftclose(dev, flags);
1087b99f0a4aSAndrew Moore #endif
10885b81b6b3SRodney W. Grimes 	fd_data[fdu].flags &= ~FD_OPEN;
10893a2f7427SDavid Greenman 	fd_data[fdu].options &= ~FDOPT_NORETRY;
1090dc16046fSJoerg Wunsch 
10915b81b6b3SRodney W. Grimes 	return(0);
10925b81b6b3SRodney W. Grimes }
10935b81b6b3SRodney W. Grimes 
10945b81b6b3SRodney W. Grimes 
10953a2f7427SDavid Greenman /****************************************************************************/
10963a2f7427SDavid Greenman /*                               fdstrategy                                 */
10973a2f7427SDavid Greenman /****************************************************************************/
10983a2f7427SDavid Greenman void
10993a2f7427SDavid Greenman fdstrategy(struct buf *bp)
11003a2f7427SDavid Greenman {
1101bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
11023a2f7427SDavid Greenman  	int	s;
11033a2f7427SDavid Greenman  	fdcu_t	fdcu;
11043a2f7427SDavid Greenman  	fdu_t	fdu;
11053a2f7427SDavid Greenman  	fdc_p	fdc;
11063a2f7427SDavid Greenman  	fd_p	fd;
11073a2f7427SDavid Greenman 	size_t	fdblk;
11083a2f7427SDavid Greenman 
11093a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
11103a2f7427SDavid Greenman 	fd = &fd_data[fdu];
11113a2f7427SDavid Greenman 	fdc = fd->fdc;
11123a2f7427SDavid Greenman 	fdcu = fdc->fdcu;
11133a2f7427SDavid Greenman 
11143a2f7427SDavid Greenman #if NFT > 0
11153a2f7427SDavid Greenman 	if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) {
11163a2f7427SDavid Greenman 		/* ft tapes do not (yet) support strategy i/o */
1117d3628763SRodney W. Grimes 		bp->b_error = ENODEV;
11183a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
11193a2f7427SDavid Greenman 		goto bad;
11203a2f7427SDavid Greenman 	}
11213a2f7427SDavid Greenman 	/* check for controller already busy with tape */
11223a2f7427SDavid Greenman 	if (fdc->flags & FDC_TAPE_BUSY) {
11233a2f7427SDavid Greenman 		bp->b_error = EBUSY;
11243a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
11253a2f7427SDavid Greenman 		goto bad;
11263a2f7427SDavid Greenman 	}
11273a2f7427SDavid Greenman #endif
1128d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
11293a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
11303a2f7427SDavid Greenman 		if ((fdu >= NFD) || (bp->b_blkno < 0)) {
1131dc5df763SJoerg Wunsch 			printf(
11326a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1133702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
11343a2f7427SDavid Greenman 			bp->b_error = EINVAL;
11353a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
11363a2f7427SDavid Greenman 			goto bad;
11373a2f7427SDavid Greenman 		}
11383a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
11393a2f7427SDavid Greenman 			bp->b_error = EINVAL;
11403a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
11413a2f7427SDavid Greenman 			goto bad;
11423a2f7427SDavid Greenman 		}
11433a2f7427SDavid Greenman 	}
11443a2f7427SDavid Greenman 
11453a2f7427SDavid Greenman 	/*
11463a2f7427SDavid Greenman 	 * Set up block calculations.
11473a2f7427SDavid Greenman 	 */
1148bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1149bb6382faSJoerg Wunsch 		/*
1150bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1151bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1152bb6382faSJoerg Wunsch 		 */
1153bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
11543a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
11553a2f7427SDavid Greenman 		goto bad;
11563a2f7427SDavid Greenman 	}
1157bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1158bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1159bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1160bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1161bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1162bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1163bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1164bb6382faSJoerg Wunsch 			if (cando == 0)
1165bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1166bb6382faSJoerg Wunsch 		} else {
1167bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1168bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1169bb6382faSJoerg Wunsch 			goto bad;
1170bb6382faSJoerg Wunsch 		}
1171bb6382faSJoerg Wunsch 	}
117227513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
11733a2f7427SDavid Greenman 	s = splbio();
117402a19910SJustin T. Gibbs 	bufqdisksort(&fdc->head, bp);
117502a19910SJustin T. Gibbs 	untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); /* a good idea */
11763a2f7427SDavid Greenman 	fdstart(fdcu);
11773a2f7427SDavid Greenman 	splx(s);
11783a2f7427SDavid Greenman 	return;
11793a2f7427SDavid Greenman 
11803a2f7427SDavid Greenman bad:
11813a2f7427SDavid Greenman 	biodone(bp);
11823a2f7427SDavid Greenman }
11833a2f7427SDavid Greenman 
11845b81b6b3SRodney W. Grimes /***************************************************************\
11855b81b6b3SRodney W. Grimes *				fdstart				*
11865b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
11875b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
11885b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
11895b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
11905b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
11915b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
11925b81b6b3SRodney W. Grimes \***************************************************************/
1193381fe1aaSGarrett Wollman static void
1194dc5df763SJoerg Wunsch fdstart(fdcu_t fdcu)
11955b81b6b3SRodney W. Grimes {
11965b81b6b3SRodney W. Grimes 	int s;
11975b81b6b3SRodney W. Grimes 
11985b81b6b3SRodney W. Grimes 	s = splbio();
11995b81b6b3SRodney W. Grimes 	if(fdc_data[fdcu].state == DEVIDLE)
12005b81b6b3SRodney W. Grimes 	{
12015b81b6b3SRodney W. Grimes 		fdintr(fdcu);
12025b81b6b3SRodney W. Grimes 	}
12035b81b6b3SRodney W. Grimes 	splx(s);
12045b81b6b3SRodney W. Grimes }
12055b81b6b3SRodney W. Grimes 
1206381fe1aaSGarrett Wollman static void
1207d0917939SPaul Richards fd_timeout(void *arg1)
12085b81b6b3SRodney W. Grimes {
1209381fe1aaSGarrett Wollman 	fdcu_t fdcu = (fdcu_t)arg1;
12105b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc_data[fdcu].fdu;
12113a2f7427SDavid Greenman 	int baseport = fdc_data[fdcu].baseport;
121217542807SPoul-Henning Kamp 	struct buf *bp;
1213f5f7ba03SJordan K. Hubbard 	int s;
12145b81b6b3SRodney W. Grimes 
121502a19910SJustin T. Gibbs 	bp = bufq_first(&fdc_data[fdcu].head);
12165b81b6b3SRodney W. Grimes 
12173a2f7427SDavid Greenman 	/*
12183a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
12193a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
12203a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
12213a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
12223a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
12233a2f7427SDavid Greenman 	 */
12245b81b6b3SRodney W. Grimes 
12253a2f7427SDavid Greenman 	s = splbio();
12263a2f7427SDavid Greenman 
12273a2f7427SDavid Greenman 	TRACE1("fd%d[fd_timeout()]", fdu);
12283a2f7427SDavid Greenman 	/* See if the controller is still busy (patiently awaiting data) */
12293a2f7427SDavid Greenman 	if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB)
12303a2f7427SDavid Greenman 	{
12313a2f7427SDavid Greenman 		TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS));
12323a2f7427SDavid Greenman 		/* yup, it is; kill it now */
12333a2f7427SDavid Greenman 		fdc_reset(&fdc_data[fdcu]);
12343a2f7427SDavid Greenman 		printf("fd%d: Operation timeout\n", fdu);
12353a2f7427SDavid Greenman 	}
12365b81b6b3SRodney W. Grimes 
12375b81b6b3SRodney W. Grimes 	if (bp)
12385b81b6b3SRodney W. Grimes 	{
12395b81b6b3SRodney W. Grimes 		retrier(fdcu);
12403a2f7427SDavid Greenman 		fdc_data[fdcu].status[0] = NE7_ST0_IC_RC;
12415b81b6b3SRodney W. Grimes 		fdc_data[fdcu].state = IOTIMEDOUT;
12425b81b6b3SRodney W. Grimes 		if( fdc_data[fdcu].retry < 6)
12435b81b6b3SRodney W. Grimes 			fdc_data[fdcu].retry = 6;
12445b81b6b3SRodney W. Grimes 	}
12455b81b6b3SRodney W. Grimes 	else
12465b81b6b3SRodney W. Grimes 	{
12475b81b6b3SRodney W. Grimes 		fdc_data[fdcu].fd = (fd_p) 0;
12485b81b6b3SRodney W. Grimes 		fdc_data[fdcu].fdu = -1;
12495b81b6b3SRodney W. Grimes 		fdc_data[fdcu].state = DEVIDLE;
12505b81b6b3SRodney W. Grimes 	}
1251f5f7ba03SJordan K. Hubbard 	fdintr(fdcu);
1252f5f7ba03SJordan K. Hubbard 	splx(s);
12535b81b6b3SRodney W. Grimes }
12545b81b6b3SRodney W. Grimes 
12555b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1256381fe1aaSGarrett Wollman static void
1257d0917939SPaul Richards fd_pseudointr(void *arg1)
12585b81b6b3SRodney W. Grimes {
1259381fe1aaSGarrett Wollman 	fdcu_t fdcu = (fdcu_t)arg1;
12605b81b6b3SRodney W. Grimes 	int	s;
12613a2f7427SDavid Greenman 
12625b81b6b3SRodney W. Grimes 	s = splbio();
12635b81b6b3SRodney W. Grimes 	fdintr(fdcu);
12645b81b6b3SRodney W. Grimes 	splx(s);
12655b81b6b3SRodney W. Grimes }
12665b81b6b3SRodney W. Grimes 
12675b81b6b3SRodney W. Grimes /***********************************************************************\
12685b81b6b3SRodney W. Grimes *                                 fdintr				*
12695b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
12705b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
12715b81b6b3SRodney W. Grimes \***********************************************************************/
1272381fe1aaSGarrett Wollman void
1273381fe1aaSGarrett Wollman fdintr(fdcu_t fdcu)
12745b81b6b3SRodney W. Grimes {
12755b81b6b3SRodney W. Grimes 	fdc_p fdc = fdc_data + fdcu;
1276b99f0a4aSAndrew Moore #if NFT > 0
1277b99f0a4aSAndrew Moore 	fdu_t fdu = fdc->fdu;
1278b99f0a4aSAndrew Moore 
1279b99f0a4aSAndrew Moore 	if (fdc->flags & FDC_TAPE_BUSY)
1280b99f0a4aSAndrew Moore 		(ftintr(fdu));
1281b99f0a4aSAndrew Moore 	else
1282b99f0a4aSAndrew Moore #endif
1283381fe1aaSGarrett Wollman 		while(fdstate(fdcu, fdc))
1284381fe1aaSGarrett Wollman 			;
12855b81b6b3SRodney W. Grimes }
12865b81b6b3SRodney W. Grimes 
12875b81b6b3SRodney W. Grimes /***********************************************************************\
12885b81b6b3SRodney W. Grimes * The controller state machine.						*
12895b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
12905b81b6b3SRodney W. Grimes \***********************************************************************/
12913a2f7427SDavid Greenman static int
1292dc5df763SJoerg Wunsch fdstate(fdcu_t fdcu, fdc_p fdc)
12935b81b6b3SRodney W. Grimes {
12944ccc87c5SPoul-Henning Kamp 	int read, format, head, sec = 0, sectrac, st0, cyl, st3;
1295bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
12965b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
12975b81b6b3SRodney W. Grimes 	fd_p fd;
129817542807SPoul-Henning Kamp 	register struct buf *bp;
1299b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
13003a2f7427SDavid Greenman 	size_t fdblk;
13015b81b6b3SRodney W. Grimes 
130202a19910SJustin T. Gibbs 	bp = bufq_first(&fdc->head);
130317542807SPoul-Henning Kamp 	if(!bp) {
13045b81b6b3SRodney W. Grimes 		/***********************************************\
13055b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
13065b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
13075b81b6b3SRodney W. Grimes 		\***********************************************/
13085b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
13095b81b6b3SRodney W. Grimes 		if(fdc->fd)
13105b81b6b3SRodney W. Grimes 		{
13116a0e6f42SRodney W. Grimes 			printf("fd%d: unexpected valid fd pointer\n",
13123a2f7427SDavid Greenman 			       fdc->fdu);
13135b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
13145b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
13155b81b6b3SRodney W. Grimes 		}
13165b81b6b3SRodney W. Grimes 		TRACE1("[fdc%d IDLE]", fdcu);
13175b81b6b3SRodney W. Grimes  		return(0);
13185b81b6b3SRodney W. Grimes 	}
13195b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
13205b81b6b3SRodney W. Grimes 	fd = fd_data + fdu;
13213a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
13225b81b6b3SRodney W. Grimes 	if (fdc->fd && (fd != fdc->fd))
13235b81b6b3SRodney W. Grimes 	{
13246a0e6f42SRodney W. Grimes 		printf("fd%d: confused fd pointers\n", fdu);
13255b81b6b3SRodney W. Grimes 	}
13265b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1327b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1328bb6382faSJoerg Wunsch 	if(format) {
1329b39c878eSAndrey A. Chernov 		finfo = (struct fd_formb *)bp->b_un.b_addr;
1330bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1331bb6382faSJoerg Wunsch 			- (char *)finfo;
1332bb6382faSJoerg Wunsch 	}
1333bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
1334bb6382faSJoerg Wunsch 		blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk +
1335bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1336bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1337bb6382faSJoerg Wunsch 	}
13385b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
13395b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
13405b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
134102a19910SJustin T. Gibbs 	untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle);
134227651b7fSJustin T. Gibbs 	fd->toffhandle = timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
13435b81b6b3SRodney W. Grimes 	switch (fdc->state)
13445b81b6b3SRodney W. Grimes 	{
13455b81b6b3SRodney W. Grimes 	case DEVIDLE:
13465b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
13475b81b6b3SRodney W. Grimes 		fdc->retry = 0;
13485b81b6b3SRodney W. Grimes 		fd->skip = 0;
13495b81b6b3SRodney W. Grimes 		fdc->fd = fd;
13505b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
13513a2f7427SDavid Greenman 		outb(fdc->baseport+FDCTL, fd->ft->trans);
13523a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
13535b81b6b3SRodney W. Grimes 		/*******************************************************\
13545b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
13555b81b6b3SRodney W. Grimes 		* it will start up in it's own good time		*
13565b81b6b3SRodney W. Grimes 		\*******************************************************/
13575b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
13585b81b6b3SRodney W. Grimes 		{
13595b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
13605b81b6b3SRodney W. Grimes 			return(0); /* come back later */
13615b81b6b3SRodney W. Grimes 		}
13625b81b6b3SRodney W. Grimes 		/*******************************************************\
13635b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
13645b81b6b3SRodney W. Grimes 		\*******************************************************/
13655b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
13665b81b6b3SRodney W. Grimes 		{
13675b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
13685b81b6b3SRodney W. Grimes 			fd_turnon(fdu);
13695b81b6b3SRodney W. Grimes 			return(0);
13705b81b6b3SRodney W. Grimes 		}
13715b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
13725b81b6b3SRodney W. Grimes 		{
13733a2f7427SDavid Greenman 			set_motor(fdcu, fd->fdsu, TURNON);
13745b81b6b3SRodney W. Grimes 		}
13755e235068SJordan K. Hubbard 		fdc->state = DOSEEK;
13765b81b6b3SRodney W. Grimes 		break;
13775b81b6b3SRodney W. Grimes 	case DOSEEK:
1378bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
13795b81b6b3SRodney W. Grimes 		{
13805b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
13815b81b6b3SRodney W. Grimes 			break;
13825b81b6b3SRodney W. Grimes 		}
1383dc5df763SJoerg Wunsch 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK,
1384bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1385dc5df763SJoerg Wunsch 			   0))
1386dc8603e3SJoerg Wunsch 		{
1387dc8603e3SJoerg Wunsch 			/*
1388dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1389dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1390dc8603e3SJoerg Wunsch 			 */
1391dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
1392dc8603e3SJoerg Wunsch 			return(retrier(fdcu));
1393dc8603e3SJoerg Wunsch 		}
1394dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
13955b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
13965b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
13975b81b6b3SRodney W. Grimes 	case SEEKWAIT:
13985b81b6b3SRodney W. Grimes 		/* allow heads to settle */
139904b734cfSPoul-Henning Kamp 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16);
14005b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
14015b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
14025b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
14035b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
1404dc5df763SJoerg Wunsch 		if(fd->track == FD_NO_TRACK)
14055b81b6b3SRodney W. Grimes 		{
1406bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
14073a2f7427SDavid Greenman 			do {
14083a2f7427SDavid Greenman 				/*
1409dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1410dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1411dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1412dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1413dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1414dc5df763SJoerg Wunsch 				 *
1415dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1416dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1417dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1418dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1419dc5df763SJoerg Wunsch 				 * real interrupt condition.
1420dc5df763SJoerg Wunsch 				 *
1421dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1422dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1423dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1424dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1425dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1426dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1427dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
14283a2f7427SDavid Greenman 				 */
1429dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1430dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1431dc5df763SJoerg Wunsch 					return 0;
1432dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1433dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1434dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
14353a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1436dc5df763SJoerg Wunsch 
14373a2f7427SDavid Greenman 			if (0 == descyl)
14383a2f7427SDavid Greenman 			{
1439dc5df763SJoerg Wunsch 				int failed = 0;
14403a2f7427SDavid Greenman 				/*
14413a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
14423a2f7427SDavid Greenman 				 * really there
14433a2f7427SDavid Greenman 				 */
1444dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1445dc5df763SJoerg Wunsch 					failed = 1;
14463a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
14473a2f7427SDavid Greenman 					printf(
14483a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
14493a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1450dc5df763SJoerg Wunsch 					failed = 1;
1451dc5df763SJoerg Wunsch 				}
1452dc5df763SJoerg Wunsch 
1453dc5df763SJoerg Wunsch 				if (failed)
1454dc5df763SJoerg Wunsch 				{
14553a2f7427SDavid Greenman 					if(fdc->retry < 3)
14563a2f7427SDavid Greenman 						fdc->retry = 3;
14573a2f7427SDavid Greenman 					return(retrier(fdcu));
14583a2f7427SDavid Greenman 				}
14593a2f7427SDavid Greenman 			}
1460dc5df763SJoerg Wunsch 
14615b81b6b3SRodney W. Grimes 			if (cyl != descyl)
14625b81b6b3SRodney W. Grimes 			{
14633a2f7427SDavid Greenman 				printf(
14643a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
14652d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
14665b81b6b3SRodney W. Grimes 				return(retrier(fdcu));
14675b81b6b3SRodney W. Grimes 			}
14685b81b6b3SRodney W. Grimes 		}
14695b81b6b3SRodney W. Grimes 
1470bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
14715b81b6b3SRodney W. Grimes 		isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
14723a2f7427SDavid Greenman 			format ? bp->b_bcount : fdblk, fdc->dmachan);
14735b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
14745b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
14755b81b6b3SRodney W. Grimes 		head = sec / sectrac;
14765b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
14773a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
14783a2f7427SDavid Greenman 
14793a2f7427SDavid Greenman 		if(format || !read)
14803a2f7427SDavid Greenman 		{
14813a2f7427SDavid Greenman 			/* make sure the drive is writable */
1482dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1483dc8603e3SJoerg Wunsch 			{
1484dc8603e3SJoerg Wunsch 				/* stuck controller? */
1485dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
1486dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
1487dc8603e3SJoerg Wunsch 			}
14883a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
14893a2f7427SDavid Greenman 			{
14903a2f7427SDavid Greenman 				/*
14913a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
14923a2f7427SDavid Greenman 				 * in order to force the current operation
14933a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
14943a2f7427SDavid Greenman 				 * error - all error handling is done
14953a2f7427SDavid Greenman 				 * by the retrier()
14963a2f7427SDavid Greenman 				 */
14973a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
14983a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
14993a2f7427SDavid Greenman 				fdc->status[2] = 0;
15003a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
15013a2f7427SDavid Greenman 				fdc->status[4] = head;
15023a2f7427SDavid Greenman 				fdc->status[5] = sec;
15033a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
15043a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
15053a2f7427SDavid Greenman 				return (1);
15063a2f7427SDavid Greenman 			}
15073a2f7427SDavid Greenman 		}
15085b81b6b3SRodney W. Grimes 
1509b39c878eSAndrey A. Chernov 		if(format)
1510b39c878eSAndrey A. Chernov 		{
1511b39c878eSAndrey A. Chernov 			/* formatting */
1512dc5df763SJoerg Wunsch 			if(fd_cmd(fdcu, 6,
1513dc5df763SJoerg Wunsch 				  NE7CMD_FORMAT,
1514dc5df763SJoerg Wunsch 				  head << 2 | fdu,
1515dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1516dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1517dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
1518dc5df763SJoerg Wunsch 				  finfo->fd_formb_fillbyte,
1519dc5df763SJoerg Wunsch 				  0))
1520dc8603e3SJoerg Wunsch 			{
1521dc8603e3SJoerg Wunsch 				/* controller fell over */
1522dc8603e3SJoerg Wunsch 				fdc->retry = 6;
1523dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
1524dc8603e3SJoerg Wunsch 			}
1525b39c878eSAndrey A. Chernov 		}
1526b39c878eSAndrey A. Chernov 		else
1527b39c878eSAndrey A. Chernov 		{
1528dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 9,
1529dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1530dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1531dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1532dc5df763SJoerg Wunsch 				   head,
1533dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1534dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1535dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1536dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1537dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
1538dc5df763SJoerg Wunsch 				   0))
15395b81b6b3SRodney W. Grimes 			{
1540dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
1541dc8603e3SJoerg Wunsch 				fdc->retry = 6;
1542dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
15435b81b6b3SRodney W. Grimes 			}
1544b39c878eSAndrey A. Chernov 		}
15455b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
154602a19910SJustin T. Gibbs 		fd->tohandle = timeout(fd_timeout, (caddr_t)fdcu, hz);
15475b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
15485b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
154902a19910SJustin T. Gibbs 		untimeout(fd_timeout, (caddr_t)fdcu, fd->tohandle);
1550dc5df763SJoerg Wunsch 
1551dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu))
15525b81b6b3SRodney W. Grimes 		{
1553dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1554dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
1555dc5df763SJoerg Wunsch 			return retrier(fdcu);
15565b81b6b3SRodney W. Grimes   		}
1557dc5df763SJoerg Wunsch 
15583a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1559dc5df763SJoerg Wunsch 
15603a2f7427SDavid Greenman 		/* FALLTHROUGH */
1561dc5df763SJoerg Wunsch 
15623a2f7427SDavid Greenman 	case IOTIMEDOUT:
15635b81b6b3SRodney W. Grimes 		isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
15643a2f7427SDavid Greenman 			    format ? bp->b_bcount : fdblk, fdc->dmachan);
15653a2f7427SDavid Greenman 		if (fdc->status[0] & NE7_ST0_IC)
15665b81b6b3SRodney W. Grimes 		{
15673a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
15683a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1569b39c878eSAndrey A. Chernov                                 /*
15703a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
15713a2f7427SDavid Greenman 				 * and didn't release it in time for the
15723a2f7427SDavid Greenman 				 * next FDC transfer.
15733a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
15743a2f7427SDavid Greenman 				 * count. (vak)
1575b39c878eSAndrey A. Chernov                                  */
1576b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
1577b39c878eSAndrey A. Chernov                                 return (1);
1578b39c878eSAndrey A. Chernov                         }
15793a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
15803a2f7427SDavid Greenman 				&& fdc->retry < 6)
15813a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
15823a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
15833a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
15843a2f7427SDavid Greenman 				&& fdc->retry < 3)
15853a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
15865b81b6b3SRodney W. Grimes 			return(retrier(fdcu));
15875b81b6b3SRodney W. Grimes 		}
15885b81b6b3SRodney W. Grimes 		/* All OK */
15893a2f7427SDavid Greenman 		fd->skip += fdblk;
1590bb6382faSJoerg Wunsch 		if (!format && fd->skip < bp->b_bcount - bp->b_resid)
15915b81b6b3SRodney W. Grimes 		{
15925b81b6b3SRodney W. Grimes 			/* set up next transfer */
15935b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
15945b81b6b3SRodney W. Grimes 		}
15955b81b6b3SRodney W. Grimes 		else
15965b81b6b3SRodney W. Grimes 		{
15975b81b6b3SRodney W. Grimes 			/* ALL DONE */
15985b81b6b3SRodney W. Grimes 			fd->skip = 0;
159902a19910SJustin T. Gibbs 			bufq_remove(&fdc->head, bp);
16005b81b6b3SRodney W. Grimes 			biodone(bp);
16015b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
16025b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
16035b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
16045b81b6b3SRodney W. Grimes 		}
16055b81b6b3SRodney W. Grimes 		return(1);
16065b81b6b3SRodney W. Grimes 	case RESETCTLR:
16073a2f7427SDavid Greenman 		fdc_reset(fdc);
16085b81b6b3SRodney W. Grimes 		fdc->retry++;
16095b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
16105b81b6b3SRodney W. Grimes 		break;
16115b81b6b3SRodney W. Grimes 	case STARTRECAL:
16120e317d05SJoerg Wunsch 		/* XXX clear the fdc results from the last reset, if any. */
16130e317d05SJoerg Wunsch 		{
16140e317d05SJoerg Wunsch 			int i;
16150e317d05SJoerg Wunsch 			for (i = 0; i < 4; i++)
16160e317d05SJoerg Wunsch 				(void)fd_sense_int(fdc, &st0, &cyl);
16170e317d05SJoerg Wunsch 		}
16180e317d05SJoerg Wunsch 
1619dc5df763SJoerg Wunsch 		if(fd_cmd(fdcu,
1620dc5df763SJoerg Wunsch 			  2, NE7CMD_RECAL, fdu,
1621dc5df763SJoerg Wunsch 			  0)) /* Recalibrate Function */
1622dc8603e3SJoerg Wunsch 		{
1623dc8603e3SJoerg Wunsch 			/* arrgl */
1624dc8603e3SJoerg Wunsch 			fdc->retry = 6;
1625dc8603e3SJoerg Wunsch 			return(retrier(fdcu));
1626dc8603e3SJoerg Wunsch 		}
16275b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
16285b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
16295b81b6b3SRodney W. Grimes 	case RECALWAIT:
16305b81b6b3SRodney W. Grimes 		/* allow heads to settle */
163104b734cfSPoul-Henning Kamp 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8);
16325b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
16335b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
16345b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
16353a2f7427SDavid Greenman 		do {
16363a2f7427SDavid Greenman 			/*
1637dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
16383a2f7427SDavid Greenman 			 */
1639dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
1640dc5df763SJoerg Wunsch 				return 0;
1641dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
1642dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1643dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
16443a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
16453a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
16465b81b6b3SRodney W. Grimes 		{
1647dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
1648dc8603e3SJoerg Wunsch 				/*
1649dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
1650dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
1651dc8603e3SJoerg Wunsch 				 * since people used to complain much about
1652dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
1653dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
1654dc8603e3SJoerg Wunsch 				 * time in a line
1655dc8603e3SJoerg Wunsch 				 */
1656dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
1657dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
16583a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
16595b81b6b3SRodney W. Grimes 			return(retrier(fdcu));
16605b81b6b3SRodney W. Grimes 		}
16615b81b6b3SRodney W. Grimes 		fd->track = 0;
16625b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
16635b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
16645b81b6b3SRodney W. Grimes 		return(1);	/* will return immediatly */
16655b81b6b3SRodney W. Grimes 	case MOTORWAIT:
16665b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
16675b81b6b3SRodney W. Grimes 		{
16685b81b6b3SRodney W. Grimes 			return(0); /* time's not up yet */
16695b81b6b3SRodney W. Grimes 		}
16705e235068SJordan K. Hubbard 		/*
16715e235068SJordan K. Hubbard 		 * since the controller was off, it has lost its
16725e235068SJordan K. Hubbard 		 * idea about the current track it were; thus,
16735e235068SJordan K. Hubbard 		 * recalibrate the bastard
16745e235068SJordan K. Hubbard 		 */
16755e235068SJordan K. Hubbard 		fdc->state = STARTRECAL;
16765b81b6b3SRodney W. Grimes 		return(1);	/* will return immediatly */
16775b81b6b3SRodney W. Grimes 	default:
1678dc5df763SJoerg Wunsch 		printf("fdc%d: Unexpected FD int->", fdcu);
1679dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
1680dac0f2dbSJoerg Wunsch 			printf("FDC status :%lx %lx %lx %lx %lx %lx %lx   ",
16815b81b6b3SRodney W. Grimes 			       fdc->status[0],
16825b81b6b3SRodney W. Grimes 			       fdc->status[1],
16835b81b6b3SRodney W. Grimes 			       fdc->status[2],
16845b81b6b3SRodney W. Grimes 			       fdc->status[3],
16855b81b6b3SRodney W. Grimes 			       fdc->status[4],
16865b81b6b3SRodney W. Grimes 			       fdc->status[5],
16875b81b6b3SRodney W. Grimes 			       fdc->status[6] );
16883a2f7427SDavid Greenman 		else
1689dac0f2dbSJoerg Wunsch 			printf("No status available   ");
1690dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
1691dac0f2dbSJoerg Wunsch 		{
1692dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
16935b81b6b3SRodney W. Grimes 			return(0);
16945b81b6b3SRodney W. Grimes 		}
1695dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
1696dac0f2dbSJoerg Wunsch 		return(0);
1697dac0f2dbSJoerg Wunsch 	}
1698dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
16995b81b6b3SRodney W. Grimes 	return(1); /* Come back immediatly to new state */
17005b81b6b3SRodney W. Grimes }
17015b81b6b3SRodney W. Grimes 
1702aaf08d94SGarrett Wollman static int
1703f5f7ba03SJordan K. Hubbard retrier(fdcu)
1704f5f7ba03SJordan K. Hubbard 	fdcu_t fdcu;
17055b81b6b3SRodney W. Grimes {
17065b81b6b3SRodney W. Grimes 	fdc_p fdc = fdc_data + fdcu;
170717542807SPoul-Henning Kamp 	register struct buf *bp;
17085b81b6b3SRodney W. Grimes 
170902a19910SJustin T. Gibbs 	bp = bufq_first(&fdc->head);
17105b81b6b3SRodney W. Grimes 
17113a2f7427SDavid Greenman 	if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY)
17123a2f7427SDavid Greenman 		goto fail;
17135b81b6b3SRodney W. Grimes 	switch(fdc->retry)
17145b81b6b3SRodney W. Grimes 	{
17155b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
17165b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
17175b81b6b3SRodney W. Grimes 		break;
17185b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
17195b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
17205b81b6b3SRodney W. Grimes 		break;
17215b81b6b3SRodney W. Grimes 	case 6:
17225b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
17235b81b6b3SRodney W. Grimes 		break;
17245b81b6b3SRodney W. Grimes 	case 7:
17255b81b6b3SRodney W. Grimes 		break;
17265b81b6b3SRodney W. Grimes 	default:
17273a2f7427SDavid Greenman 	fail:
17285b81b6b3SRodney W. Grimes 		{
17297ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
17307ca0641bSAndrey A. Chernov 			/* Trick diskerr */
17313a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
17323a2f7427SDavid Greenman 					    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
173392ed385aSRodney W. Grimes 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
17343a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
17353a2f7427SDavid Greenman 				(struct disklabel *)NULL);
17367ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
1737dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
1738dc5df763SJoerg Wunsch 			{
1739dc5df763SJoerg Wunsch 				printf(
1740dc5df763SJoerg Wunsch 			" (ST0 %b ST1 %b ST2 %b cyl %ld hd %ld sec %ld)\n",
1741dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
1742dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
1743dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
1744dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
1745dc5df763SJoerg Wunsch 				       fdc->status[5]);
1746dc5df763SJoerg Wunsch 			}
1747dc5df763SJoerg Wunsch 			else
1748dc5df763SJoerg Wunsch 				printf(" (No status)\n");
17495b81b6b3SRodney W. Grimes 		}
17505b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
17515b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
1752bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
175302a19910SJustin T. Gibbs 		bufq_remove(&fdc->head, bp);
17545b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
17555b81b6b3SRodney W. Grimes 		biodone(bp);
175692ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
17575b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
17585b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
1759f5f7ba03SJordan K. Hubbard 		/* XXX abort current command, if any.  */
176092ed385aSRodney W. Grimes 		return(1);
17615b81b6b3SRodney W. Grimes 	}
17625b81b6b3SRodney W. Grimes 	fdc->retry++;
17635b81b6b3SRodney W. Grimes 	return(1);
17645b81b6b3SRodney W. Grimes }
17655b81b6b3SRodney W. Grimes 
1766b39c878eSAndrey A. Chernov static int
1767b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
1768b39c878eSAndrey A. Chernov 	dev_t dev;
1769b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
1770b39c878eSAndrey A. Chernov 	struct proc *p;
1771b39c878eSAndrey A. Chernov {
1772b39c878eSAndrey A. Chernov  	fdu_t	fdu;
1773b39c878eSAndrey A. Chernov  	fd_p	fd;
1774b39c878eSAndrey A. Chernov 
1775b39c878eSAndrey A. Chernov 	struct buf *bp;
1776b39c878eSAndrey A. Chernov 	int rv = 0, s;
17773a2f7427SDavid Greenman 	size_t fdblk;
1778b39c878eSAndrey A. Chernov 
1779b39c878eSAndrey A. Chernov  	fdu = FDUNIT(minor(dev));
1780b39c878eSAndrey A. Chernov 	fd = &fd_data[fdu];
17813a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1782b39c878eSAndrey A. Chernov 
1783b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
1784b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1785b39c878eSAndrey A. Chernov 	if(bp == 0)
1786b39c878eSAndrey A. Chernov 		return ENOBUFS;
178782f5379bSJoerg Wunsch 	/*
178882f5379bSJoerg Wunsch 	 * keep the process from being swapped
178982f5379bSJoerg Wunsch 	 */
179082f5379bSJoerg Wunsch 	p->p_flag |= P_PHYSIO;
1791b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
1792b39c878eSAndrey A. Chernov 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1793b39c878eSAndrey A. Chernov 	bp->b_proc = p;
1794b39c878eSAndrey A. Chernov 	bp->b_dev = dev;
1795b39c878eSAndrey A. Chernov 
1796b39c878eSAndrey A. Chernov 	/*
1797b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
1798b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
1799b39c878eSAndrey A. Chernov 	 */
1800b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
18013a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
1802b39c878eSAndrey A. Chernov 
1803b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
18045e235068SJordan K. Hubbard 	bp->b_un.b_addr = (caddr_t)finfo;
1805b39c878eSAndrey A. Chernov 
1806b39c878eSAndrey A. Chernov 	/* now do the format */
1807b39c878eSAndrey A. Chernov 	fdstrategy(bp);
1808b39c878eSAndrey A. Chernov 
1809b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
1810b39c878eSAndrey A. Chernov 	s = splbio();
1811b39c878eSAndrey A. Chernov 	while(!(bp->b_flags & B_DONE))
1812b39c878eSAndrey A. Chernov 	{
18135e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1814b39c878eSAndrey A. Chernov 		if(rv == EWOULDBLOCK)
1815b39c878eSAndrey A. Chernov 			break;
1816b39c878eSAndrey A. Chernov 	}
1817b39c878eSAndrey A. Chernov 	splx(s);
1818b39c878eSAndrey A. Chernov 
181982f5379bSJoerg Wunsch 	if(rv == EWOULDBLOCK) {
1820b39c878eSAndrey A. Chernov 		/* timed out */
1821b39c878eSAndrey A. Chernov 		rv = EIO;
182282f5379bSJoerg Wunsch 		biodone(bp);
182382f5379bSJoerg Wunsch 	}
18243a2f7427SDavid Greenman 	if(bp->b_flags & B_ERROR)
18253a2f7427SDavid Greenman 		rv = bp->b_error;
182682f5379bSJoerg Wunsch 	/*
182782f5379bSJoerg Wunsch 	 * allow the process to be swapped
182882f5379bSJoerg Wunsch 	 */
182982f5379bSJoerg Wunsch 	p->p_flag &= ~P_PHYSIO;
1830b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
1831b39c878eSAndrey A. Chernov 	return rv;
1832b39c878eSAndrey A. Chernov }
1833b39c878eSAndrey A. Chernov 
1834f5f7ba03SJordan K. Hubbard /*
1835671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
1836f5f7ba03SJordan K. Hubbard  */
18375b81b6b3SRodney W. Grimes 
1838f5f7ba03SJordan K. Hubbard int
1839b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
1840f5f7ba03SJordan K. Hubbard 	dev_t dev;
1841f5f7ba03SJordan K. Hubbard 	int cmd;
1842f5f7ba03SJordan K. Hubbard 	caddr_t addr;
1843f5f7ba03SJordan K. Hubbard 	int flag;
1844b39c878eSAndrey A. Chernov 	struct proc *p;
1845f5f7ba03SJordan K. Hubbard {
18463a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
18473a2f7427SDavid Greenman  	fd_p	fd = &fd_data[fdu];
18483a2f7427SDavid Greenman 	size_t fdblk;
18493a2f7427SDavid Greenman 
1850f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
1851f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
1852f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
18533a2f7427SDavid Greenman 	int error = 0;
1854f5f7ba03SJordan K. Hubbard 
1855b99f0a4aSAndrew Moore #if NFT > 0
1856a60eff27SNate Williams 	int type = FDTYPE(minor(dev));
1857a60eff27SNate Williams 
1858a60eff27SNate Williams 	/* check for a tape ioctl */
1859a60eff27SNate Williams 	if (type & F_TAPE_TYPE)
1860b99f0a4aSAndrew Moore 		return ftioctl(dev, cmd, addr, flag, p);
1861b99f0a4aSAndrew Moore #endif
1862b99f0a4aSAndrew Moore 
18633a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1864f5f7ba03SJordan K. Hubbard 
1865f5f7ba03SJordan K. Hubbard 	switch (cmd)
1866f5f7ba03SJordan K. Hubbard 	{
1867f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
1868f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
1869f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
18703a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
187192ed385aSRodney W. Grimes 		fdt = fd_data[FDUNIT(minor(dev))].ft;
1872f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
1873f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
1874f5f7ba03SJordan K. Hubbard 
1875191e1a59SBruce Evans 		if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl)
1876191e1a59SBruce Evans 		    == NULL)
1877f5f7ba03SJordan K. Hubbard 			error = 0;
1878f5f7ba03SJordan K. Hubbard 		else
1879f5f7ba03SJordan K. Hubbard 			error = EINVAL;
1880f5f7ba03SJordan K. Hubbard 
1881f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
1882f5f7ba03SJordan K. Hubbard 		break;
1883f5f7ba03SJordan K. Hubbard 
1884f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
1885f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
1886f5f7ba03SJordan K. Hubbard 			error = EBADF;
1887f5f7ba03SJordan K. Hubbard 		break;
1888f5f7ba03SJordan K. Hubbard 
1889f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
1890f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
1891f5f7ba03SJordan K. Hubbard 			error = EBADF;
1892f5f7ba03SJordan K. Hubbard 		break;
1893f5f7ba03SJordan K. Hubbard 
1894f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
1895f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
1896f5f7ba03SJordan K. Hubbard 		{
1897f5f7ba03SJordan K. Hubbard 			error = EBADF;
1898f5f7ba03SJordan K. Hubbard 			break;
1899f5f7ba03SJordan K. Hubbard 		}
1900f5f7ba03SJordan K. Hubbard 
1901f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
1902f5f7ba03SJordan K. Hubbard 
1903191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
1904191e1a59SBruce Evans 					  (u_long)0)) != 0)
1905f5f7ba03SJordan K. Hubbard 			break;
1906f5f7ba03SJordan K. Hubbard 
1907b39c878eSAndrey A. Chernov 		error = writedisklabel(dev, fdstrategy,
190854c7241bSJordan K. Hubbard 				       (struct disklabel *)buffer);
1909b39c878eSAndrey A. Chernov 		break;
1910b39c878eSAndrey A. Chernov 
1911b39c878eSAndrey A. Chernov 	case FD_FORM:
1912b39c878eSAndrey A. Chernov 		if((flag & FWRITE) == 0)
1913b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
1914b39c878eSAndrey A. Chernov 		else if(((struct fd_formb *)addr)->format_version !=
1915b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
1916b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
1917b39c878eSAndrey A. Chernov 		else
1918b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
1919b39c878eSAndrey A. Chernov 		break;
1920b39c878eSAndrey A. Chernov 
1921b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
1922b39c878eSAndrey A. Chernov 		*(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
1923f5f7ba03SJordan K. Hubbard 		break;
1924f5f7ba03SJordan K. Hubbard 
19253a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
19263a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
19273a2f7427SDavid Greenman 		if(suser(p->p_ucred, &p->p_acflag) != 0)
19283a2f7427SDavid Greenman 			return EPERM;
19293a2f7427SDavid Greenman 		*fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr;
19303a2f7427SDavid Greenman 		break;
19313a2f7427SDavid Greenman 
19323a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
19333a2f7427SDavid Greenman 		*(int *)addr = fd_data[FDUNIT(minor(dev))].options;
19343a2f7427SDavid Greenman 		break;
19353a2f7427SDavid Greenman 
19363a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
19373a2f7427SDavid Greenman 		fd_data[FDUNIT(minor(dev))].options = *(int *)addr;
19383a2f7427SDavid Greenman 		break;
19393a2f7427SDavid Greenman 
1940f5f7ba03SJordan K. Hubbard 	default:
19413a2f7427SDavid Greenman 		error = ENOTTY;
1942f5f7ba03SJordan K. Hubbard 		break;
1943f5f7ba03SJordan K. Hubbard 	}
1944f5f7ba03SJordan K. Hubbard 	return (error);
1945f5f7ba03SJordan K. Hubbard }
1946f5f7ba03SJordan K. Hubbard 
19477146c13eSJulian Elischer 
19487146c13eSJulian Elischer static fd_devsw_installed = 0;
19497146c13eSJulian Elischer 
195087f6c662SJulian Elischer static void 	fd_drvinit(void *notused )
19517146c13eSJulian Elischer {
195287f6c662SJulian Elischer 
19537146c13eSJulian Elischer 	if( ! fd_devsw_installed ) {
1954cba8a5ddSPoul-Henning Kamp 		bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_bdevsw);
19557146c13eSJulian Elischer 		fd_devsw_installed = 1;
19567146c13eSJulian Elischer 	}
19577146c13eSJulian Elischer }
195887f6c662SJulian Elischer 
195987f6c662SJulian Elischer SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL)
196087f6c662SJulian Elischer 
1961f5f7ba03SJordan K. Hubbard #endif
19623a2f7427SDavid Greenman /*
19633a2f7427SDavid Greenman  * Hello emacs, these are the
19643a2f7427SDavid Greenman  * Local Variables:
19653a2f7427SDavid Greenman  *  c-indent-level:               8
19663a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
19673a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
19683a2f7427SDavid Greenman  *  c-brace-offset:              -8
19693a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
19703a2f7427SDavid Greenman  *  c-argdecl-indent:             8
19713a2f7427SDavid Greenman  *  c-label-offset:              -8
19723a2f7427SDavid Greenman  *  c++-hanging-braces:           1
19733a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
19743a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
19753a2f7427SDavid Greenman  *  c++-friend-offset:            0
19763a2f7427SDavid Greenman  * End:
19773a2f7427SDavid Greenman  */
1978