xref: /freebsd/sys/dev/fdc/fdc.c (revision bb6382fae2da7a1d5ba046f628a6cee698255f17)
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
46bb6382faSJoerg Wunsch  *	$Id: fd.c,v 1.92 1996/09/06 23:07:18 phk 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"
555b81b6b3SRodney W. Grimes 
56b99f0a4aSAndrew Moore #if NFDC > 0
57b99f0a4aSAndrew Moore 
58b99f0a4aSAndrew Moore #include <sys/param.h>
59b99f0a4aSAndrew Moore #include <sys/systm.h>
60b99f0a4aSAndrew Moore #include <sys/kernel.h>
61b99f0a4aSAndrew Moore #include <sys/conf.h>
62b99f0a4aSAndrew Moore #include <sys/file.h>
63b99f0a4aSAndrew Moore #include <sys/ioctl.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/uio.h>
69b99f0a4aSAndrew Moore #include <sys/malloc.h>
703a2f7427SDavid Greenman #include <sys/proc.h>
71b99f0a4aSAndrew Moore #include <sys/syslog.h>
7292200632SGarrett Wollman #include <sys/dkstat.h>
73f540b106SGarrett Wollman #include <i386/isa/isa.h>
74f540b106SGarrett Wollman #include <i386/isa/isa_device.h>
75f540b106SGarrett Wollman #include <i386/isa/fdreg.h>
76f540b106SGarrett Wollman #include <i386/isa/fdc.h>
77f540b106SGarrett Wollman #include <i386/isa/rtc.h>
78dc5df763SJoerg Wunsch #include <machine/stdarg.h>
7987eafbcaSPoul-Henning Kamp #if NFT > 0
8087eafbcaSPoul-Henning Kamp #include <sys/ftape.h>
8187eafbcaSPoul-Henning Kamp #include <i386/isa/ftreg.h>
8287eafbcaSPoul-Henning Kamp #endif
838af5d536SJulian Elischer #ifdef DEVFS
848af5d536SJulian Elischer #include <sys/devfsext.h>
858af5d536SJulian Elischer #endif
865b81b6b3SRodney W. Grimes 
87b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
88b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
895b81b6b3SRodney W. Grimes 
903a2f7427SDavid Greenman /*
913a2f7427SDavid Greenman  * this biotab field doubles as a field for the physical unit number
923a2f7427SDavid Greenman  * on the controller
933a2f7427SDavid Greenman  */
943a2f7427SDavid Greenman #define id_physid id_scsiid
953a2f7427SDavid Greenman 
96dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
97dc5df763SJoerg Wunsch #define FD_FAILED -1
98dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
99dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
100dc5df763SJoerg Wunsch 
101b39c878eSAndrey A. Chernov #define NUMTYPES 14
102b39c878eSAndrey A. Chernov #define NUMDENS  (NUMTYPES - 6)
1037ca0641bSAndrey A. Chernov 
1043a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
105b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
106b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
107b99f0a4aSAndrew Moore #define FD_1720         1
108b99f0a4aSAndrew Moore #define FD_1480         2
109b99f0a4aSAndrew Moore #define FD_1440         3
110b99f0a4aSAndrew Moore #define FD_1200         4
111b99f0a4aSAndrew Moore #define FD_820          5
112b99f0a4aSAndrew Moore #define FD_800          6
113b99f0a4aSAndrew Moore #define FD_720          7
114b99f0a4aSAndrew Moore #define FD_360          8
115ed2fa05eSAndrey A. Chernov 
116b99f0a4aSAndrew Moore #define FD_1480in5_25   9
117b99f0a4aSAndrew Moore #define FD_1440in5_25   10
118b99f0a4aSAndrew Moore #define FD_820in5_25    11
119b99f0a4aSAndrew Moore #define FD_800in5_25    12
120b99f0a4aSAndrew Moore #define FD_720in5_25    13
121b99f0a4aSAndrew Moore #define FD_360in5_25    14
122b99f0a4aSAndrew Moore 
1237ca0641bSAndrey A. Chernov 
1246f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1255b81b6b3SRodney W. Grimes {
126126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
127126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
128126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
129126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
130126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
131126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
132126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
133b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
134ed2fa05eSAndrey A. Chernov 
135126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
136126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
137126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
138126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
139126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
140126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1415b81b6b3SRodney W. Grimes };
1425b81b6b3SRodney W. Grimes 
143b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
144dc16046fSJoerg Wunsch 
1455b81b6b3SRodney W. Grimes /***********************************************************************\
1465b81b6b3SRodney W. Grimes * Per controller structure.						*
1475b81b6b3SRodney W. Grimes \***********************************************************************/
148b99f0a4aSAndrew Moore struct fdc_data fdc_data[NFDC];
1495b81b6b3SRodney W. Grimes 
1505b81b6b3SRodney W. Grimes /***********************************************************************\
1515b81b6b3SRodney W. Grimes * Per drive structure.							*
152b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1535b81b6b3SRodney W. Grimes \***********************************************************************/
1546f4e0bebSPoul-Henning Kamp static struct fd_data {
155b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1565b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1573a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1585b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1595b81b6b3SRodney W. Grimes 	int	flags;
1605b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1615b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1625b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1635b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1645b81b6b3SRodney W. Grimes 	int	skip;
1655b81b6b3SRodney W. Grimes 	int	hddrv;
166dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1675b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1683a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
16992200632SGarrett Wollman 	int	dkunit;		/* disk stats unit number */
17087f6c662SJulian Elischer #ifdef DEVFS
17121519754SBruce Evans 	void	*bdevs[1 + NUMDENS + MAXPARTITIONS];
17221519754SBruce Evans 	void	*cdevs[1 + NUMDENS + MAXPARTITIONS];
17387f6c662SJulian Elischer #endif
1745b81b6b3SRodney W. Grimes } fd_data[NFD];
1755b81b6b3SRodney W. Grimes 
1765b81b6b3SRodney W. Grimes /***********************************************************************\
1775b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1785b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1795b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
1805b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
1815b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
1825b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
1835b81b6b3SRodney W. Grimes \***********************************************************************/
184b99f0a4aSAndrew Moore 
1853a2f7427SDavid Greenman #if NFT > 0
1863a2f7427SDavid Greenman int ftopen(dev_t, int);
1873a2f7427SDavid Greenman int ftintr(ftu_t ftu);
1883a2f7427SDavid Greenman int ftclose(dev_t, int);
1893a2f7427SDavid Greenman void ftstrategy(struct buf *);
1903a2f7427SDavid Greenman int ftioctl(dev_t, int, caddr_t, int, struct proc *);
1913a2f7427SDavid Greenman int ftdump(dev_t);
1923a2f7427SDavid Greenman int ftsize(dev_t);
19374fa89f4SRodney W. Grimes int ftattach(struct isa_device *, struct isa_device *, int);
1943a2f7427SDavid Greenman #endif
1955b81b6b3SRodney W. Grimes 
1963a2f7427SDavid Greenman /* autoconfig functions */
1973a2f7427SDavid Greenman static int fdprobe(struct isa_device *);
1983a2f7427SDavid Greenman static int fdattach(struct isa_device *);
1993a2f7427SDavid Greenman 
2003a2f7427SDavid Greenman /* needed for ft driver, thus exported */
2013a2f7427SDavid Greenman int in_fdc(fdcu_t);
2023a2f7427SDavid Greenman int out_fdc(fdcu_t, int);
2033a2f7427SDavid Greenman 
2043a2f7427SDavid Greenman /* internal functions */
2053a2f7427SDavid Greenman static void set_motor(fdcu_t, int, int);
2063a2f7427SDavid Greenman #  define TURNON 1
2073a2f7427SDavid Greenman #  define TURNOFF 0
2083a2f7427SDavid Greenman static timeout_t fd_turnoff;
2093a2f7427SDavid Greenman static timeout_t fd_motor_on;
2103a2f7427SDavid Greenman static void fd_turnon(fdu_t);
2113a2f7427SDavid Greenman static void fdc_reset(fdc_p);
212b5e8ce9fSBruce Evans static int fd_in(fdcu_t, int *);
2133a2f7427SDavid Greenman static void fdstart(fdcu_t);
2143a2f7427SDavid Greenman static timeout_t fd_timeout;
2153a2f7427SDavid Greenman static timeout_t fd_pseudointr;
2163a2f7427SDavid Greenman static int fdstate(fdcu_t, fdc_p);
217aaf08d94SGarrett Wollman static int retrier(fdcu_t);
2183a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2193a2f7427SDavid Greenman 
220aaf08d94SGarrett Wollman 
2215b81b6b3SRodney W. Grimes #define DEVIDLE		0
2225b81b6b3SRodney W. Grimes #define FINDWORK	1
2235b81b6b3SRodney W. Grimes #define	DOSEEK		2
2245b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2255b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2265b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2275b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2285b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2295b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2305b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2315b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2325b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2335b81b6b3SRodney W. Grimes 
2345b81b6b3SRodney W. Grimes #ifdef	DEBUG
235cba2a7c6SBruce Evans static char const * const fdstates[] =
2365b81b6b3SRodney W. Grimes {
2375b81b6b3SRodney W. Grimes "DEVIDLE",
2385b81b6b3SRodney W. Grimes "FINDWORK",
2395b81b6b3SRodney W. Grimes "DOSEEK",
2405b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2415b81b6b3SRodney W. Grimes "IOCOMPLETE",
2425b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2435b81b6b3SRodney W. Grimes "STARTRECAL",
2445b81b6b3SRodney W. Grimes "RESETCTLR",
2455b81b6b3SRodney W. Grimes "SEEKWAIT",
2465b81b6b3SRodney W. Grimes "RECALWAIT",
2475b81b6b3SRodney W. Grimes "MOTORWAIT",
2485b81b6b3SRodney W. Grimes "IOTIMEDOUT"
2495b81b6b3SRodney W. Grimes };
2505b81b6b3SRodney W. Grimes 
2513a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
252cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2535b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2545b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
255381fe1aaSGarrett Wollman #else /* DEBUG */
2565b81b6b3SRodney W. Grimes #define TRACE0(arg)
2575b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
258381fe1aaSGarrett Wollman #endif /* DEBUG */
2595b81b6b3SRodney W. Grimes 
260dc16046fSJoerg Wunsch /* autoconfig structure */
261dc16046fSJoerg Wunsch 
262dc16046fSJoerg Wunsch struct	isa_driver fdcdriver = {
263dc16046fSJoerg Wunsch 	fdprobe, fdattach, "fdc",
264dc16046fSJoerg Wunsch };
265dc16046fSJoerg Wunsch 
26687f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
26787f6c662SJulian Elischer static	d_close_t	fdclose;
26887f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
26987f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
27087f6c662SJulian Elischer 
27187f6c662SJulian Elischer #define CDEV_MAJOR 9
27287f6c662SJulian Elischer #define BDEV_MAJOR 2
273cba8a5ddSPoul-Henning Kamp static struct cdevsw fd_cdevsw;
274d2f265faSPoul-Henning Kamp static struct bdevsw fd_bdevsw =
27587f6c662SJulian Elischer 	{ Fdopen,	fdclose,	fdstrategy,	fdioctl,	/*2*/
276f332b8a1SBruce Evans 	  nodump,	nopsize,	0,	"fd",	&fd_cdevsw,	-1 };
27787f6c662SJulian Elischer 
27887f6c662SJulian Elischer 
2796f4e0bebSPoul-Henning Kamp static struct isa_device *fdcdevs[NFDC];
28092200632SGarrett Wollman 
281dc5df763SJoerg Wunsch static int
282dc5df763SJoerg Wunsch fdc_err(fdcu_t fdcu, const char *s)
283dc5df763SJoerg Wunsch {
284dc5df763SJoerg Wunsch 	fdc_data[fdcu].fdc_errs++;
28516b04b6aSJoerg Wunsch 	if(s) {
286dc5df763SJoerg Wunsch 		if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX)
2876a0e6f42SRodney W. Grimes 			printf("fdc%d: %s", fdcu, s);
288dc5df763SJoerg Wunsch 		else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX)
289dc5df763SJoerg Wunsch 			printf("fdc%d: too many errors, not logging any more\n",
290dc5df763SJoerg Wunsch 			       fdcu);
29116b04b6aSJoerg Wunsch 	}
292dc5df763SJoerg Wunsch 
293dc5df763SJoerg Wunsch 	return FD_FAILED;
294dc5df763SJoerg Wunsch }
295dc5df763SJoerg Wunsch 
296dc5df763SJoerg Wunsch /*
297dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
298dc5df763SJoerg Wunsch  * Unit number,
299dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
300dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
301dc5df763SJoerg Wunsch  */
302dc5df763SJoerg Wunsch 
3036f4e0bebSPoul-Henning Kamp static int
304dc5df763SJoerg Wunsch fd_cmd(fdcu_t fdcu, int n_out, ...)
305dc5df763SJoerg Wunsch {
306dc5df763SJoerg Wunsch 	u_char cmd;
307dc5df763SJoerg Wunsch 	int n_in;
308dc5df763SJoerg Wunsch 	int n;
309dc5df763SJoerg Wunsch 	va_list ap;
310dc5df763SJoerg Wunsch 
311dc5df763SJoerg Wunsch 	va_start(ap, n_out);
312dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
313dc5df763SJoerg Wunsch 	va_end(ap);
314dc5df763SJoerg Wunsch 	va_start(ap, n_out);
315dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
316dc5df763SJoerg Wunsch 	{
317dc5df763SJoerg Wunsch 		if (out_fdc(fdcu, va_arg(ap, int)) < 0)
318dc5df763SJoerg Wunsch 		{
319dc5df763SJoerg Wunsch 			char msg[50];
320dc5df763SJoerg Wunsch 			sprintf(msg,
321dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
322dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
323dc5df763SJoerg Wunsch 			return fdc_err(fdcu, msg);
324dc5df763SJoerg Wunsch 		}
325dc5df763SJoerg Wunsch 	}
326dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
327dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
328dc5df763SJoerg Wunsch 	{
329dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
330dc5df763SJoerg Wunsch 		if (fd_in(fdcu, ptr) < 0)
331dc5df763SJoerg Wunsch 		{
332dc5df763SJoerg Wunsch 			char msg[50];
333dc5df763SJoerg Wunsch 			sprintf(msg,
334dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
335dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
336dc5df763SJoerg Wunsch 			return fdc_err(fdcu, msg);
337dc5df763SJoerg Wunsch 		}
338dc5df763SJoerg Wunsch 	}
339dc5df763SJoerg Wunsch 
340dc5df763SJoerg Wunsch 	return 0;
341dc5df763SJoerg Wunsch }
342dc5df763SJoerg Wunsch 
3436f4e0bebSPoul-Henning Kamp static int
344dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
345dc5df763SJoerg Wunsch {
346dc5df763SJoerg Wunsch 	int st3;
347dc5df763SJoerg Wunsch 
348dc5df763SJoerg Wunsch 	if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
349dc5df763SJoerg Wunsch 	{
3506a0e6f42SRodney W. Grimes 		return fdc_err(fdc->fdcu, "Sense Drive Status failed\n");
351dc5df763SJoerg Wunsch 	}
352dc5df763SJoerg Wunsch 	if (st3p)
353dc5df763SJoerg Wunsch 		*st3p = st3;
354dc5df763SJoerg Wunsch 
355dc5df763SJoerg Wunsch 	return 0;
356dc5df763SJoerg Wunsch }
357dc5df763SJoerg Wunsch 
3586f4e0bebSPoul-Henning Kamp static int
359dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
360dc5df763SJoerg Wunsch {
361dc5df763SJoerg Wunsch 	int st0, cyl;
362dc5df763SJoerg Wunsch 
363dc5df763SJoerg Wunsch 	int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0);
364dc5df763SJoerg Wunsch 
365dc5df763SJoerg Wunsch 	if (ret)
366dc5df763SJoerg Wunsch 	{
367dc5df763SJoerg Wunsch 		(void)fdc_err(fdc->fdcu,
368dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
369dc5df763SJoerg Wunsch 		return ret;
370dc5df763SJoerg Wunsch 	}
371dc5df763SJoerg Wunsch 
372dc5df763SJoerg Wunsch 	if (st0p)
373dc5df763SJoerg Wunsch 		*st0p = st0;
374dc5df763SJoerg Wunsch 
375dc5df763SJoerg Wunsch 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV)
376dc5df763SJoerg Wunsch 	{
377dc5df763SJoerg Wunsch 		/*
378dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
379dc5df763SJoerg Wunsch 		 */
380dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
381dc5df763SJoerg Wunsch 	}
382dc5df763SJoerg Wunsch 
383dc5df763SJoerg Wunsch 	if (fd_in(fdc->fdcu, &cyl) < 0)
384dc5df763SJoerg Wunsch 	{
385dc5df763SJoerg Wunsch 		return fdc_err(fdc->fdcu, "can't get cyl num\n");
386dc5df763SJoerg Wunsch 	}
387dc5df763SJoerg Wunsch 
388dc5df763SJoerg Wunsch 	if (cylp)
389dc5df763SJoerg Wunsch 		*cylp = cyl;
390dc5df763SJoerg Wunsch 
391dc5df763SJoerg Wunsch 	return 0;
392dc5df763SJoerg Wunsch }
393dc5df763SJoerg Wunsch 
394dc5df763SJoerg Wunsch 
3956f4e0bebSPoul-Henning Kamp static int
396dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
397dc5df763SJoerg Wunsch {
398dc5df763SJoerg Wunsch 	int i, ret;
399b5e8ce9fSBruce Evans 
400dc5df763SJoerg Wunsch 	for (i = 0; i < 7; i++)
401dc5df763SJoerg Wunsch 	{
402b5e8ce9fSBruce Evans 		/*
403b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
404b5e8ce9fSBruce Evans 		 * from the hardware, but fdc_status wants u_longs and
405b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
406b5e8ce9fSBruce Evans 		 */
407b5e8ce9fSBruce Evans 		int status;
408b5e8ce9fSBruce Evans 
409b5e8ce9fSBruce Evans 		ret = fd_in(fdc->fdcu, &status);
410b5e8ce9fSBruce Evans 		fdc->status[i] = status;
411b5e8ce9fSBruce Evans 		if (ret != 0)
412dc5df763SJoerg Wunsch 			break;
413dc5df763SJoerg Wunsch 	}
414dc5df763SJoerg Wunsch 
415dc5df763SJoerg Wunsch 	if (ret == 0)
416dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
417dc5df763SJoerg Wunsch 	else
418dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
419dc5df763SJoerg Wunsch 
420dc5df763SJoerg Wunsch 	return ret;
421dc5df763SJoerg Wunsch }
422dc5df763SJoerg Wunsch 
4235b81b6b3SRodney W. Grimes /****************************************************************************/
4245b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
4255b81b6b3SRodney W. Grimes /****************************************************************************/
426dc5df763SJoerg Wunsch 
4275b81b6b3SRodney W. Grimes /*
4285b81b6b3SRodney W. Grimes  * probe for existance of controller
4295b81b6b3SRodney W. Grimes  */
4303a2f7427SDavid Greenman static int
431dc5df763SJoerg Wunsch fdprobe(struct isa_device *dev)
4325b81b6b3SRodney W. Grimes {
4335b81b6b3SRodney W. Grimes 	fdcu_t	fdcu = dev->id_unit;
4345b81b6b3SRodney W. Grimes 	if(fdc_data[fdcu].flags & FDC_ATTACHED)
4355b81b6b3SRodney W. Grimes 	{
4366a0e6f42SRodney W. Grimes 		printf("fdc%d: unit used multiple times\n", fdcu);
4375b81b6b3SRodney W. Grimes 		return 0;
4385b81b6b3SRodney W. Grimes 	}
4395b81b6b3SRodney W. Grimes 
44092200632SGarrett Wollman 	fdcdevs[fdcu] = dev;
4415b81b6b3SRodney W. Grimes 	fdc_data[fdcu].baseport = dev->id_iobase;
4425b81b6b3SRodney W. Grimes 
44316111cedSAndrew Moore 	/* First - lets reset the floppy controller */
4443a2f7427SDavid Greenman 	outb(dev->id_iobase+FDOUT, 0);
44516111cedSAndrew Moore 	DELAY(100);
4463a2f7427SDavid Greenman 	outb(dev->id_iobase+FDOUT, FDO_FRST);
44716111cedSAndrew Moore 
4485b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
449dc5df763SJoerg Wunsch 	if (fd_cmd(fdcu,
450dc5df763SJoerg Wunsch 		   3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
451dc5df763SJoerg Wunsch 		   0))
4525b81b6b3SRodney W. Grimes 	{
4535b81b6b3SRodney W. Grimes 		return(0);
4545b81b6b3SRodney W. Grimes 	}
4555b81b6b3SRodney W. Grimes 	return (IO_FDCSIZE);
4565b81b6b3SRodney W. Grimes }
4575b81b6b3SRodney W. Grimes 
4585b81b6b3SRodney W. Grimes /*
4595b81b6b3SRodney W. Grimes  * wire controller into system, look for floppy units
4605b81b6b3SRodney W. Grimes  */
4613a2f7427SDavid Greenman static int
462dc5df763SJoerg Wunsch fdattach(struct isa_device *dev)
4635b81b6b3SRodney W. Grimes {
4643a2f7427SDavid Greenman 	unsigned fdt;
4655b81b6b3SRodney W. Grimes 	fdu_t	fdu;
4665b81b6b3SRodney W. Grimes 	fdcu_t	fdcu = dev->id_unit;
4675b81b6b3SRodney W. Grimes 	fdc_p	fdc = fdc_data + fdcu;
4685b81b6b3SRodney W. Grimes 	fd_p	fd;
469cba2a7c6SBruce Evans 	int	fdsu, st0, st3, i;
470cba2a7c6SBruce Evans #if NFT > 0
471cba2a7c6SBruce Evans 	int	unithasfd;
472cba2a7c6SBruce Evans #endif
473b99f0a4aSAndrew Moore 	struct isa_device *fdup;
474dc5df763SJoerg Wunsch 	int ic_type = 0;
475999422d7SJulian Elischer #ifdef DEVFS
476c8f2fe8dSBruce Evans 	int	mynor;
47721519754SBruce Evans 	int	typemynor;
47821519754SBruce Evans 	int	typesize;
47921519754SBruce Evans #endif
48092200632SGarrett Wollman 
4815b81b6b3SRodney W. Grimes 	fdc->fdcu = fdcu;
4825b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
4835b81b6b3SRodney W. Grimes 	fdc->dmachan = dev->id_drq;
484100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
485100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
486dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
4875b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
4883a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
4893a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
49017542807SPoul-Henning Kamp 	TAILQ_INIT(&fdc->head);
4915b81b6b3SRodney W. Grimes 
4925b81b6b3SRodney W. Grimes 	/* check for each floppy drive */
493b99f0a4aSAndrew Moore 	for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
494b99f0a4aSAndrew Moore 		if (fdup->id_iobase != dev->id_iobase)
495b99f0a4aSAndrew Moore 			continue;
496b99f0a4aSAndrew Moore 		fdu = fdup->id_unit;
497b99f0a4aSAndrew Moore 		fd = &fd_data[fdu];
498b99f0a4aSAndrew Moore 		if (fdu >= (NFD+NFT))
499b99f0a4aSAndrew Moore 			continue;
500b99f0a4aSAndrew Moore 		fdsu = fdup->id_physid;
501b99f0a4aSAndrew Moore 		/* look up what bios thinks we have */
502b99f0a4aSAndrew Moore 		switch (fdu) {
503b99f0a4aSAndrew Moore 			case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
504b99f0a4aSAndrew Moore 				break;
505b99f0a4aSAndrew Moore 			case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
506b99f0a4aSAndrew Moore 				break;
507b99f0a4aSAndrew Moore 			default: fdt = RTCFDT_NONE;
508b99f0a4aSAndrew Moore 				break;
509b99f0a4aSAndrew Moore 		}
5105b81b6b3SRodney W. Grimes 		/* is there a unit? */
511b99f0a4aSAndrew Moore 		if ((fdt == RTCFDT_NONE)
512b99f0a4aSAndrew Moore #if NFT > 0
513b99f0a4aSAndrew Moore 		    || (fdsu >= DRVS_PER_CTLR)) {
514b99f0a4aSAndrew Moore #else
515b99f0a4aSAndrew Moore 		) {
51656ef0285SAndrew Moore 			fd->type = NO_TYPE;
517b99f0a4aSAndrew Moore #endif
518b99f0a4aSAndrew Moore #if NFT > 0
519b99f0a4aSAndrew Moore 			/* If BIOS says no floppy, or > 2nd device */
520b99f0a4aSAndrew Moore 			/* Probe for and attach a floppy tape.     */
52174fa89f4SRodney W. Grimes 			/* Tell FT if there was already a disk     */
52274fa89f4SRodney W. Grimes 			/* with this unit number found.            */
52374fa89f4SRodney W. Grimes 
52474fa89f4SRodney W. Grimes 			unithasfd = 0;
52574fa89f4SRodney W. Grimes 			if (fdu < NFD && fd->type != NO_TYPE)
52674fa89f4SRodney W. Grimes 				unithasfd = 1;
52785827d9cSJoerg Wunsch 			if (ftattach(dev, fdup, unithasfd))
528b99f0a4aSAndrew Moore 				continue;
52956ef0285SAndrew Moore 			if (fdsu < DRVS_PER_CTLR)
530b99f0a4aSAndrew Moore 				fd->type = NO_TYPE;
53156ef0285SAndrew Moore #endif
5325b81b6b3SRodney W. Grimes 			continue;
533f5f7ba03SJordan K. Hubbard 		}
5345b81b6b3SRodney W. Grimes 
5355b81b6b3SRodney W. Grimes 		/* select it */
5363a2f7427SDavid Greenman 		set_motor(fdcu, fdsu, TURNON);
5376b7bd95bSJoerg Wunsch 		DELAY(1000000);	/* 1 sec */
538dc5df763SJoerg Wunsch 
539dc5df763SJoerg Wunsch 		if (ic_type == 0 &&
540dc5df763SJoerg Wunsch 		    fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0)
541dc5df763SJoerg Wunsch 		{
5426a0e6f42SRodney W. Grimes 			printf("fdc%d: ", fdcu);
543dc5df763SJoerg Wunsch 			ic_type = (u_char)ic_type;
544dc5df763SJoerg Wunsch 			switch( ic_type ) {
545dc5df763SJoerg Wunsch 			case 0x80:
5466a0e6f42SRodney W. Grimes 				printf("NEC 765\n");
547dc5df763SJoerg Wunsch 				fdc->fdct = FDC_NE765;
548dc5df763SJoerg Wunsch 				break;
549dc5df763SJoerg Wunsch 			case 0x81:
5506a0e6f42SRodney W. Grimes 				printf("Intel 82077\n");
551dc5df763SJoerg Wunsch 				fdc->fdct = FDC_I82077;
552dc5df763SJoerg Wunsch 				break;
553dc5df763SJoerg Wunsch 			case 0x90:
5546a0e6f42SRodney W. Grimes 				printf("NEC 72065B\n");
555dc5df763SJoerg Wunsch 				fdc->fdct = FDC_NE72065;
556dc5df763SJoerg Wunsch 				break;
557dc5df763SJoerg Wunsch 			default:
5586a0e6f42SRodney W. Grimes 				printf("unknown IC type %02x\n", ic_type);
559dc5df763SJoerg Wunsch 				fdc->fdct = FDC_UNKNOWN;
560dc5df763SJoerg Wunsch 				break;
5616b7bd95bSJoerg Wunsch 			}
562dc5df763SJoerg Wunsch 		}
563dc5df763SJoerg Wunsch 		if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
564dc5df763SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
565dc5df763SJoerg Wunsch 			/* if at track 0, first seek inwards */
566dc5df763SJoerg Wunsch 			/* seek some steps: */
567dc5df763SJoerg Wunsch 			(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
568dc5df763SJoerg Wunsch 			DELAY(300000); /* ...wait a moment... */
569dc5df763SJoerg Wunsch 			(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
570dc5df763SJoerg Wunsch 		}
571dc5df763SJoerg Wunsch 
572dc5df763SJoerg Wunsch 		/* If we're at track 0 first seek inwards. */
573dc5df763SJoerg Wunsch 		if ((fd_sense_drive_status(fdc, &st3) == 0) &&
574dc5df763SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
575dc5df763SJoerg Wunsch 			/* Seek some steps... */
576dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
577dc5df763SJoerg Wunsch 				/* ...wait a moment... */
578dc5df763SJoerg Wunsch 				DELAY(300000);
579dc5df763SJoerg Wunsch 				/* make ctrlr happy: */
580dc5df763SJoerg Wunsch 				(void)fd_sense_int(fdc, 0, 0);
581dc5df763SJoerg Wunsch 			}
582dc5df763SJoerg Wunsch 		}
583dc5df763SJoerg Wunsch 
5846b7bd95bSJoerg Wunsch 		for(i = 0; i < 2; i++) {
5856b7bd95bSJoerg Wunsch 			/*
5866b7bd95bSJoerg Wunsch 			 * we must recalibrate twice, just in case the
5876b7bd95bSJoerg Wunsch 			 * heads have been beyond cylinder 76, since most
5886b7bd95bSJoerg Wunsch 			 * FDCs still barf when attempting to recalibrate
5896b7bd95bSJoerg Wunsch 			 * more than 77 steps
5906b7bd95bSJoerg Wunsch 			 */
591dc5df763SJoerg Wunsch 			/* go back to 0: */
592dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
5936b7bd95bSJoerg Wunsch 				/* a second being enough for full stroke seek*/
5946b7bd95bSJoerg Wunsch 				DELAY(i == 0? 1000000: 300000);
5955b81b6b3SRodney W. Grimes 
5966b7bd95bSJoerg Wunsch 				/* anything responding? */
597dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, 0) == 0 &&
598dc5df763SJoerg Wunsch 				(st0 & NE7_ST0_EC) == 0)
5996b7bd95bSJoerg Wunsch 					break; /* already probed succesfully */
6006b7bd95bSJoerg Wunsch 			}
601dc5df763SJoerg Wunsch 		}
6026b7bd95bSJoerg Wunsch 
6033a2f7427SDavid Greenman 		set_motor(fdcu, fdsu, TURNOFF);
6043a2f7427SDavid Greenman 
6053a2f7427SDavid Greenman 		if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
6065b81b6b3SRodney W. Grimes 			continue;
6075b81b6b3SRodney W. Grimes 
608dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
609b99f0a4aSAndrew Moore 		fd->fdc = fdc;
610b99f0a4aSAndrew Moore 		fd->fdsu = fdsu;
6113a2f7427SDavid Greenman 		fd->options = 0;
6122d9d0204SRodney W. Grimes 		printf("fd%d: ", fdu);
6135b81b6b3SRodney W. Grimes 
614b99f0a4aSAndrew Moore 		switch (fdt) {
6157ca0641bSAndrey A. Chernov 		case RTCFDT_12M:
6166a0e6f42SRodney W. Grimes 			printf("1.2MB 5.25in\n");
617b99f0a4aSAndrew Moore 			fd->type = FD_1200;
6187ca0641bSAndrey A. Chernov 			break;
6197ca0641bSAndrey A. Chernov 		case RTCFDT_144M:
6206a0e6f42SRodney W. Grimes 			printf("1.44MB 3.5in\n");
621b99f0a4aSAndrew Moore 			fd->type = FD_1440;
6227ca0641bSAndrey A. Chernov 			break;
623290dd077SJoerg Wunsch 		case RTCFDT_288M:
62486a727d9SJoerg Wunsch 		case RTCFDT_288M_1:
6256a0e6f42SRodney W. Grimes 			printf("2.88MB 3.5in - 1.44MB mode\n");
626290dd077SJoerg Wunsch 			fd->type = FD_1440;
627290dd077SJoerg Wunsch 			break;
6287ca0641bSAndrey A. Chernov 		case RTCFDT_360K:
6296a0e6f42SRodney W. Grimes 			printf("360KB 5.25in\n");
630b99f0a4aSAndrew Moore 			fd->type = FD_360;
6317ca0641bSAndrey A. Chernov 			break;
632ed2fa05eSAndrey A. Chernov 		case RTCFDT_720K:
6336a0e6f42SRodney W. Grimes 			printf("720KB 3.5in\n");
634b99f0a4aSAndrew Moore 			fd->type = FD_720;
635ed2fa05eSAndrey A. Chernov 			break;
6367ca0641bSAndrey A. Chernov 		default:
6376a0e6f42SRodney W. Grimes 			printf("unknown\n");
638b99f0a4aSAndrew Moore 			fd->type = NO_TYPE;
63921519754SBruce Evans 			continue;
6405b81b6b3SRodney W. Grimes 		}
641999422d7SJulian Elischer #ifdef DEVFS
64221519754SBruce Evans 		mynor = fdu << 6;
64321519754SBruce Evans 		fd->bdevs[0] = devfs_add_devswf(&fd_bdevsw, mynor, DV_BLK,
644f85120acSBruce Evans 						UID_ROOT, GID_OPERATOR, 0640,
64521519754SBruce Evans 						"fd%d", fdu);
64621519754SBruce Evans 		fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
647f85120acSBruce Evans 						UID_ROOT, GID_OPERATOR, 0640,
64821519754SBruce Evans 						"rfd%d", fdu);
64921519754SBruce Evans 		for (i = 1; i < 1 + NUMDENS; i++) {
65021519754SBruce Evans 			/*
65121519754SBruce Evans 			 * XXX this and the lookup in Fdopen() should be
65221519754SBruce Evans 			 * data driven.
65321519754SBruce Evans 			 */
65421519754SBruce Evans 			switch (fd->type) {
65521519754SBruce Evans 			case FD_360:
65621519754SBruce Evans 				if (i != FD_360)
65721519754SBruce Evans 					continue;
65821519754SBruce Evans 				break;
65921519754SBruce Evans 			case FD_720:
66021519754SBruce Evans 				if (i != FD_720 && i != FD_800 && i != FD_820)
66121519754SBruce Evans 					continue;
66221519754SBruce Evans 				break;
66321519754SBruce Evans 			case FD_1200:
66421519754SBruce Evans 				if (i != FD_360 && i != FD_720 && i != FD_800
66521519754SBruce Evans 				    && i != FD_820 && i != FD_1200
66621519754SBruce Evans 				    && i != FD_1440 && i != FD_1480)
66721519754SBruce Evans 					continue;
66821519754SBruce Evans 				break;
66921519754SBruce Evans 			case FD_1440:
67021519754SBruce Evans 				if (i != FD_720 && i != FD_800 && i != FD_820
67121519754SBruce Evans 				    && i != FD_1200 && i != FD_1440
67221519754SBruce Evans 				    && i != FD_1480 && i != FD_1720)
67321519754SBruce Evans 					continue;
67421519754SBruce Evans 				break;
67521519754SBruce Evans 			}
67621519754SBruce Evans 			typemynor = mynor | i;
67721519754SBruce Evans 			typesize = fd_types[i - 1].size / 2;
67821519754SBruce Evans 			/*
67921519754SBruce Evans 			 * XXX all these conversions give bloated code and
68021519754SBruce Evans 			 * confusing names.
68121519754SBruce Evans 			 */
68221519754SBruce Evans 			if (typesize == 1476)
68321519754SBruce Evans 				typesize = 1480;
68421519754SBruce Evans 			if (typesize == 1722)
68521519754SBruce Evans 				typesize = 1720;
68621519754SBruce Evans 			fd->bdevs[i] =
68721519754SBruce Evans 				devfs_add_devswf(&fd_bdevsw, typemynor, DV_BLK,
68821519754SBruce Evans 						 UID_ROOT, GID_OPERATOR, 0640,
68921519754SBruce Evans 						 "fd%d.%d", fdu, typesize);
69021519754SBruce Evans 			fd->cdevs[i] =
69121519754SBruce Evans 				devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR,
69221519754SBruce Evans 						 UID_ROOT, GID_OPERATOR, 0640,
69321519754SBruce Evans 						 "rfd%d.%d", fdu, typesize);
69421519754SBruce Evans 		}
69521519754SBruce Evans 		for (i = 0; i < MAXPARTITIONS; i++) {
69621519754SBruce Evans 			fd->bdevs[1 + NUMDENS + i] =
69721519754SBruce Evans 				devfs_link(fd->bdevs[0],
69821519754SBruce Evans 					   "fd%d%c", fdu, 'a' + i);
69921519754SBruce Evans 			fd->cdevs[1 + NUMDENS + i] =
70021519754SBruce Evans 				devfs_link(fd->cdevs[0],
70121519754SBruce Evans 					   "rfd%d%c", fdu, 'a' + i);
70221519754SBruce Evans 		}
703999422d7SJulian Elischer #endif /* DEVFS */
70492200632SGarrett Wollman 		if (dk_ndrive < DK_NDRIVE) {
70592200632SGarrett Wollman 			sprintf(dk_names[dk_ndrive], "fd%d", fdu);
7066a0e6f42SRodney W. Grimes 			fd->dkunit = dk_ndrive++;
707671e2ceeSBruce Evans 			/*
708671e2ceeSBruce Evans 			 * XXX assume rate is FDC_500KBPS.
709671e2ceeSBruce Evans 			 */
710671e2ceeSBruce Evans 			dk_wpms[dk_ndrive] = 500000 / 8 / 2;
71192200632SGarrett Wollman 		} else {
71292200632SGarrett Wollman 			fd->dkunit = -1;
71392200632SGarrett Wollman 		}
7145b81b6b3SRodney W. Grimes 	}
7155b81b6b3SRodney W. Grimes 
7163a2f7427SDavid Greenman 	return (1);
7175b81b6b3SRodney W. Grimes }
7185b81b6b3SRodney W. Grimes 
7195b81b6b3SRodney W. Grimes /****************************************************************************/
7205b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
7215e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
7225b81b6b3SRodney W. Grimes /****************************************************************************/
7233a2f7427SDavid Greenman static void
724dc5df763SJoerg Wunsch set_motor(fdcu_t fdcu, int fdsu, int turnon)
7255b81b6b3SRodney W. Grimes {
7263a2f7427SDavid Greenman 	int fdout = fdc_data[fdcu].fdout;
7273a2f7427SDavid Greenman 	int needspecify = 0;
7283a2f7427SDavid Greenman 
7293a2f7427SDavid Greenman 	if(turnon) {
7303a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
7313a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
7323a2f7427SDavid Greenman 	} else
7333a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
7343a2f7427SDavid Greenman 
7355e235068SJordan K. Hubbard 	if(!turnon
7365e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
7375e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
7385e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
7395e235068SJordan K. Hubbard 	else {
7403a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
7413a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
7423a2f7427SDavid Greenman 			needspecify = 1;
7433a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
7445b81b6b3SRodney W. Grimes 	}
7455b81b6b3SRodney W. Grimes 
7463a2f7427SDavid Greenman 	outb(fdc_data[fdcu].baseport+FDOUT, fdout);
7473a2f7427SDavid Greenman 	fdc_data[fdcu].fdout = fdout;
7483a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
7493a2f7427SDavid Greenman 
7503a2f7427SDavid Greenman 	if(needspecify) {
751dc8603e3SJoerg Wunsch 		/*
752dc5df763SJoerg Wunsch 		 * XXX
753dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
754dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
755dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
756dc8603e3SJoerg Wunsch 		 */
757dc5df763SJoerg Wunsch 		(void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
758dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
759dc5df763SJoerg Wunsch 			     0);
7603a2f7427SDavid Greenman 	}
7613a2f7427SDavid Greenman }
7623a2f7427SDavid Greenman 
763381fe1aaSGarrett Wollman static void
764d0917939SPaul Richards fd_turnoff(void *arg1)
7655b81b6b3SRodney W. Grimes {
766381fe1aaSGarrett Wollman 	fdu_t fdu = (fdu_t)arg1;
767f5f7ba03SJordan K. Hubbard 	int	s;
7685b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
769dc16046fSJoerg Wunsch 
770dc16046fSJoerg Wunsch 	TRACE1("[fd%d: turnoff]", fdu);
7718335c1b8SBruce Evans 
7728335c1b8SBruce Evans 	/*
7738335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
7748335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
7758335c1b8SBruce Evans 	 * and nothing is queued on it.
7768335c1b8SBruce Evans 	 */
7778335c1b8SBruce Evans 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) {
7788335c1b8SBruce Evans 		timeout(fd_turnoff, arg1, 4 * hz);
7798335c1b8SBruce Evans 		return;
7808335c1b8SBruce Evans 	}
7818335c1b8SBruce Evans 
782f5f7ba03SJordan K. Hubbard 	s = splbio();
7835b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
7843a2f7427SDavid Greenman 	set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
785f5f7ba03SJordan K. Hubbard 	splx(s);
7865b81b6b3SRodney W. Grimes }
7875b81b6b3SRodney W. Grimes 
7883a2f7427SDavid Greenman static void
789d0917939SPaul Richards fd_motor_on(void *arg1)
7905b81b6b3SRodney W. Grimes {
791381fe1aaSGarrett Wollman 	fdu_t fdu = (fdu_t)arg1;
792f5f7ba03SJordan K. Hubbard 	int	s;
793f5f7ba03SJordan K. Hubbard 
7945b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
795f5f7ba03SJordan K. Hubbard 	s = splbio();
7965b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
7975b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
7985b81b6b3SRodney W. Grimes 	{
799f5f7ba03SJordan K. Hubbard 		fdintr(fd->fdc->fdcu);
8005b81b6b3SRodney W. Grimes 	}
801f5f7ba03SJordan K. Hubbard 	splx(s);
8025b81b6b3SRodney W. Grimes }
8035b81b6b3SRodney W. Grimes 
8043a2f7427SDavid Greenman static void
805dc5df763SJoerg Wunsch fd_turnon(fdu_t fdu)
8065b81b6b3SRodney W. Grimes {
8075b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
8085b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
8095b81b6b3SRodney W. Grimes 	{
8103a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
8113a2f7427SDavid Greenman 		set_motor(fd->fdc->fdcu, fd->fdsu, TURNON);
8125e235068SJordan K. Hubbard 		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
8135b81b6b3SRodney W. Grimes 	}
8145b81b6b3SRodney W. Grimes }
8155b81b6b3SRodney W. Grimes 
816381fe1aaSGarrett Wollman static void
817dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
8185b81b6b3SRodney W. Grimes {
8193a2f7427SDavid Greenman 	fdcu_t fdcu = fdc->fdcu;
8203a2f7427SDavid Greenman 
8213a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
8223a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
8233a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
8243a2f7427SDavid Greenman 	DELAY(100);
8253a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
8263a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
8273a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
8283a2f7427SDavid Greenman 	DELAY(100);
8293a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout);
8303a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
8313a2f7427SDavid Greenman 
832dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
833dc5df763SJoerg Wunsch 	(void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
834dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
835dc5df763SJoerg Wunsch 		     0);
8365b81b6b3SRodney W. Grimes }
8375b81b6b3SRodney W. Grimes 
8385b81b6b3SRodney W. Grimes /****************************************************************************/
8395b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
8405b81b6b3SRodney W. Grimes /****************************************************************************/
8415b81b6b3SRodney W. Grimes int
842dc5df763SJoerg Wunsch in_fdc(fdcu_t fdcu)
8435b81b6b3SRodney W. Grimes {
8445b81b6b3SRodney W. Grimes 	int baseport = fdc_data[fdcu].baseport;
8455b81b6b3SRodney W. Grimes 	int i, j = 100000;
8463a2f7427SDavid Greenman 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
8475b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
848dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
8496a0e6f42SRodney W. Grimes 			return fdc_err(fdcu, "ready for output in input\n");
8505b81b6b3SRodney W. Grimes 	if (j <= 0)
85116b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
8525b81b6b3SRodney W. Grimes #ifdef	DEBUG
8533a2f7427SDavid Greenman 	i = inb(baseport+FDDATA);
8543a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
8555b81b6b3SRodney W. Grimes 	return(i);
8565b81b6b3SRodney W. Grimes #else
8573a2f7427SDavid Greenman 	return inb(baseport+FDDATA);
8585b81b6b3SRodney W. Grimes #endif
8595b81b6b3SRodney W. Grimes }
8605b81b6b3SRodney W. Grimes 
861dc5df763SJoerg Wunsch /*
862dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
863dc5df763SJoerg Wunsch  */
864b5e8ce9fSBruce Evans static int
865dc5df763SJoerg Wunsch fd_in(fdcu_t fdcu, int *ptr)
866dc5df763SJoerg Wunsch {
867dc5df763SJoerg Wunsch 	int baseport = fdc_data[fdcu].baseport;
868dc5df763SJoerg Wunsch 	int i, j = 100000;
869dc5df763SJoerg Wunsch 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
870dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
871dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
8726a0e6f42SRodney W. Grimes 			return fdc_err(fdcu, "ready for output in input\n");
873dc5df763SJoerg Wunsch 	if (j <= 0)
87416b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
875dc5df763SJoerg Wunsch #ifdef	DEBUG
876dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
877dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
878dc5df763SJoerg Wunsch 	*ptr = i;
879dc5df763SJoerg Wunsch 	return 0;
880dc5df763SJoerg Wunsch #else
881dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
882dc5df763SJoerg Wunsch 	if (ptr)
883dc5df763SJoerg Wunsch 		*ptr = i;
884dc5df763SJoerg Wunsch 	return 0;
885dc5df763SJoerg Wunsch #endif
886dc5df763SJoerg Wunsch }
887dc5df763SJoerg Wunsch 
888dc5df763SJoerg Wunsch int
889dc5df763SJoerg Wunsch out_fdc(fdcu_t fdcu, int x)
8905b81b6b3SRodney W. Grimes {
8915b81b6b3SRodney W. Grimes 	int baseport = fdc_data[fdcu].baseport;
8923b3837dbSRodney W. Grimes 	int i;
8935b81b6b3SRodney W. Grimes 
8943b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
8953b3837dbSRodney W. Grimes 	i = 100000;
8963a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
8976a0e6f42SRodney W. Grimes 	if (i <= 0) return fdc_err(fdcu, "direction bit not set\n");
8983b3837dbSRodney W. Grimes 
8993b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
9003b3837dbSRodney W. Grimes 	i = 100000;
9013a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
90216b04b6aSJoerg Wunsch 	if (i <= 0)
90316b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0);
9043b3837dbSRodney W. Grimes 
9053b3837dbSRodney W. Grimes 	/* Send the command and return */
9063a2f7427SDavid Greenman 	outb(baseport+FDDATA, x);
9073a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
9085b81b6b3SRodney W. Grimes 	return (0);
9095b81b6b3SRodney W. Grimes }
9105b81b6b3SRodney W. Grimes 
9115b81b6b3SRodney W. Grimes /****************************************************************************/
9125b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
9135b81b6b3SRodney W. Grimes /****************************************************************************/
914381fe1aaSGarrett Wollman int
915671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
9165b81b6b3SRodney W. Grimes {
9175b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
91820a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
919b99f0a4aSAndrew Moore 	fdc_p	fdc;
9205b81b6b3SRodney W. Grimes 
921b99f0a4aSAndrew Moore #if NFT > 0
922b99f0a4aSAndrew Moore 	/* check for a tape open */
923b99f0a4aSAndrew Moore 	if (type & F_TAPE_TYPE)
924b99f0a4aSAndrew Moore 		return(ftopen(dev, flags));
925b99f0a4aSAndrew Moore #endif
9265b81b6b3SRodney W. Grimes 	/* check bounds */
927b99f0a4aSAndrew Moore 	if (fdu >= NFD)
928b99f0a4aSAndrew Moore 		return(ENXIO);
929b99f0a4aSAndrew Moore 	fdc = fd_data[fdu].fdc;
930b99f0a4aSAndrew Moore 	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
931b99f0a4aSAndrew Moore 		return(ENXIO);
932b99f0a4aSAndrew Moore 	if (type > NUMDENS)
933b99f0a4aSAndrew Moore 		return(ENXIO);
9347ca0641bSAndrey A. Chernov 	if (type == 0)
9357ca0641bSAndrey A. Chernov 		type = fd_data[fdu].type;
9367ca0641bSAndrey A. Chernov 	else {
9377ca0641bSAndrey A. Chernov 		if (type != fd_data[fdu].type) {
938fa4700b4SAndrey A. Chernov 			switch (fd_data[fdu].type) {
9397ca0641bSAndrey A. Chernov 			case FD_360:
9407ca0641bSAndrey A. Chernov 				return(ENXIO);
941ed2fa05eSAndrey A. Chernov 			case FD_720:
942b39c878eSAndrey A. Chernov 				if (   type != FD_820
943b39c878eSAndrey A. Chernov 				    && type != FD_800
944ed2fa05eSAndrey A. Chernov 				   )
945ed2fa05eSAndrey A. Chernov 					return(ENXIO);
946ed2fa05eSAndrey A. Chernov 				break;
9477ca0641bSAndrey A. Chernov 			case FD_1200:
948b39c878eSAndrey A. Chernov 				switch (type) {
949b39c878eSAndrey A. Chernov 				case FD_1480:
950b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
951fa4700b4SAndrey A. Chernov 					break;
9527ca0641bSAndrey A. Chernov 				case FD_1440:
953b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
954b39c878eSAndrey A. Chernov 					break;
955b39c878eSAndrey A. Chernov 				case FD_820:
956b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
957b39c878eSAndrey A. Chernov 					break;
958b39c878eSAndrey A. Chernov 				case FD_800:
959b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
960b39c878eSAndrey A. Chernov 					break;
961b39c878eSAndrey A. Chernov 				case FD_720:
962b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
963b39c878eSAndrey A. Chernov 					break;
964b39c878eSAndrey A. Chernov 				case FD_360:
965b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
966b39c878eSAndrey A. Chernov 					break;
967b39c878eSAndrey A. Chernov 				default:
968b39c878eSAndrey A. Chernov 					return(ENXIO);
969b39c878eSAndrey A. Chernov 				}
970b39c878eSAndrey A. Chernov 				break;
971b39c878eSAndrey A. Chernov 			case FD_1440:
972b39c878eSAndrey A. Chernov 				if (   type != FD_1720
973b39c878eSAndrey A. Chernov 				    && type != FD_1480
974ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
975b39c878eSAndrey A. Chernov 				    && type != FD_820
976b39c878eSAndrey A. Chernov 				    && type != FD_800
977b39c878eSAndrey A. Chernov 				    && type != FD_720
9787ca0641bSAndrey A. Chernov 				    )
979dffff499SAndrey A. Chernov 					return(ENXIO);
980fa4700b4SAndrey A. Chernov 				break;
9817ca0641bSAndrey A. Chernov 			}
9827ca0641bSAndrey A. Chernov 		}
983fa4700b4SAndrey A. Chernov 	}
984b99f0a4aSAndrew Moore 	fd_data[fdu].ft = fd_types + type - 1;
9855b81b6b3SRodney W. Grimes 	fd_data[fdu].flags |= FD_OPEN;
9865b81b6b3SRodney W. Grimes 
9875b81b6b3SRodney W. Grimes 	return 0;
9885b81b6b3SRodney W. Grimes }
9895b81b6b3SRodney W. Grimes 
990381fe1aaSGarrett Wollman int
991671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
9925b81b6b3SRodney W. Grimes {
9935b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
994b99f0a4aSAndrew Moore 
995b99f0a4aSAndrew Moore #if NFT > 0
9963a2f7427SDavid Greenman 	int type = FDTYPE(minor(dev));
9973a2f7427SDavid Greenman 
998b99f0a4aSAndrew Moore 	if (type & F_TAPE_TYPE)
9993a2f7427SDavid Greenman 		return ftclose(dev, flags);
1000b99f0a4aSAndrew Moore #endif
10015b81b6b3SRodney W. Grimes 	fd_data[fdu].flags &= ~FD_OPEN;
10023a2f7427SDavid Greenman 	fd_data[fdu].options &= ~FDOPT_NORETRY;
1003dc16046fSJoerg Wunsch 
10045b81b6b3SRodney W. Grimes 	return(0);
10055b81b6b3SRodney W. Grimes }
10065b81b6b3SRodney W. Grimes 
10075b81b6b3SRodney W. Grimes 
10083a2f7427SDavid Greenman /****************************************************************************/
10093a2f7427SDavid Greenman /*                               fdstrategy                                 */
10103a2f7427SDavid Greenman /****************************************************************************/
10113a2f7427SDavid Greenman void
10123a2f7427SDavid Greenman fdstrategy(struct buf *bp)
10133a2f7427SDavid Greenman {
1014bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
10153a2f7427SDavid Greenman  	int	s;
10163a2f7427SDavid Greenman  	fdcu_t	fdcu;
10173a2f7427SDavid Greenman  	fdu_t	fdu;
10183a2f7427SDavid Greenman  	fdc_p	fdc;
10193a2f7427SDavid Greenman  	fd_p	fd;
10203a2f7427SDavid Greenman 	size_t	fdblk;
10213a2f7427SDavid Greenman 
10223a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
10233a2f7427SDavid Greenman 	fd = &fd_data[fdu];
10243a2f7427SDavid Greenman 	fdc = fd->fdc;
10253a2f7427SDavid Greenman 	fdcu = fdc->fdcu;
10263a2f7427SDavid Greenman 
10273a2f7427SDavid Greenman #if NFT > 0
10283a2f7427SDavid Greenman 	if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) {
10293a2f7427SDavid Greenman 		/* ft tapes do not (yet) support strategy i/o */
1030d3628763SRodney W. Grimes 		bp->b_error = ENODEV;
10313a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
10323a2f7427SDavid Greenman 		goto bad;
10333a2f7427SDavid Greenman 	}
10343a2f7427SDavid Greenman 	/* check for controller already busy with tape */
10353a2f7427SDavid Greenman 	if (fdc->flags & FDC_TAPE_BUSY) {
10363a2f7427SDavid Greenman 		bp->b_error = EBUSY;
10373a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
10383a2f7427SDavid Greenman 		goto bad;
10393a2f7427SDavid Greenman 	}
10403a2f7427SDavid Greenman #endif
1041d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
10423a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
10433a2f7427SDavid Greenman 		if ((fdu >= NFD) || (bp->b_blkno < 0)) {
1044dc5df763SJoerg Wunsch 			printf(
10456a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1046702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
10473a2f7427SDavid Greenman 			bp->b_error = EINVAL;
10483a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
10493a2f7427SDavid Greenman 			goto bad;
10503a2f7427SDavid Greenman 		}
10513a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
10523a2f7427SDavid Greenman 			bp->b_error = EINVAL;
10533a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
10543a2f7427SDavid Greenman 			goto bad;
10553a2f7427SDavid Greenman 		}
10563a2f7427SDavid Greenman 	}
10573a2f7427SDavid Greenman 
10583a2f7427SDavid Greenman 	/*
10593a2f7427SDavid Greenman 	 * Set up block calculations.
10603a2f7427SDavid Greenman 	 */
1061bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1062bb6382faSJoerg Wunsch 		/*
1063bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1064bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1065bb6382faSJoerg Wunsch 		 */
1066bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
10673a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
10683a2f7427SDavid Greenman 		goto bad;
10693a2f7427SDavid Greenman 	}
1070bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1071bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1072bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1073bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1074bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1075bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1076bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1077bb6382faSJoerg Wunsch 			if (cando == 0)
1078bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1079bb6382faSJoerg Wunsch 		} else {
1080bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1081bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1082bb6382faSJoerg Wunsch 			goto bad;
1083bb6382faSJoerg Wunsch 		}
1084bb6382faSJoerg Wunsch 	}
108527513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
10863a2f7427SDavid Greenman 	s = splbio();
108717542807SPoul-Henning Kamp 	tqdisksort(&fdc->head, bp);
10885e235068SJordan K. Hubbard 	untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */
10893a2f7427SDavid Greenman 	fdstart(fdcu);
10903a2f7427SDavid Greenman 	splx(s);
10913a2f7427SDavid Greenman 	return;
10923a2f7427SDavid Greenman 
10933a2f7427SDavid Greenman bad:
10943a2f7427SDavid Greenman 	biodone(bp);
10953a2f7427SDavid Greenman }
10963a2f7427SDavid Greenman 
10975b81b6b3SRodney W. Grimes /***************************************************************\
10985b81b6b3SRodney W. Grimes *				fdstart				*
10995b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
11005b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
11015b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
11025b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
11035b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
11045b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
11055b81b6b3SRodney W. Grimes \***************************************************************/
1106381fe1aaSGarrett Wollman static void
1107dc5df763SJoerg Wunsch fdstart(fdcu_t fdcu)
11085b81b6b3SRodney W. Grimes {
11095b81b6b3SRodney W. Grimes 	int s;
11105b81b6b3SRodney W. Grimes 
11115b81b6b3SRodney W. Grimes 	s = splbio();
11125b81b6b3SRodney W. Grimes 	if(fdc_data[fdcu].state == DEVIDLE)
11135b81b6b3SRodney W. Grimes 	{
11145b81b6b3SRodney W. Grimes 		fdintr(fdcu);
11155b81b6b3SRodney W. Grimes 	}
11165b81b6b3SRodney W. Grimes 	splx(s);
11175b81b6b3SRodney W. Grimes }
11185b81b6b3SRodney W. Grimes 
1119381fe1aaSGarrett Wollman static void
1120d0917939SPaul Richards fd_timeout(void *arg1)
11215b81b6b3SRodney W. Grimes {
1122381fe1aaSGarrett Wollman 	fdcu_t fdcu = (fdcu_t)arg1;
11235b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc_data[fdcu].fdu;
11243a2f7427SDavid Greenman 	int baseport = fdc_data[fdcu].baseport;
112517542807SPoul-Henning Kamp 	struct buf *bp;
1126f5f7ba03SJordan K. Hubbard 	int s;
11275b81b6b3SRodney W. Grimes 
112817542807SPoul-Henning Kamp 	bp = TAILQ_FIRST(&fdc_data[fdcu].head);
11295b81b6b3SRodney W. Grimes 
11303a2f7427SDavid Greenman 	/*
11313a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
11323a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
11333a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
11343a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
11353a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
11363a2f7427SDavid Greenman 	 */
11375b81b6b3SRodney W. Grimes 
11383a2f7427SDavid Greenman 	s = splbio();
11393a2f7427SDavid Greenman 
11403a2f7427SDavid Greenman 	TRACE1("fd%d[fd_timeout()]", fdu);
11413a2f7427SDavid Greenman 	/* See if the controller is still busy (patiently awaiting data) */
11423a2f7427SDavid Greenman 	if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB)
11433a2f7427SDavid Greenman 	{
11443a2f7427SDavid Greenman 		TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS));
11453a2f7427SDavid Greenman 		/* yup, it is; kill it now */
11463a2f7427SDavid Greenman 		fdc_reset(&fdc_data[fdcu]);
11473a2f7427SDavid Greenman 		printf("fd%d: Operation timeout\n", fdu);
11483a2f7427SDavid Greenman 	}
11495b81b6b3SRodney W. Grimes 
11505b81b6b3SRodney W. Grimes 	if (bp)
11515b81b6b3SRodney W. Grimes 	{
11525b81b6b3SRodney W. Grimes 		retrier(fdcu);
11533a2f7427SDavid Greenman 		fdc_data[fdcu].status[0] = NE7_ST0_IC_RC;
11545b81b6b3SRodney W. Grimes 		fdc_data[fdcu].state = IOTIMEDOUT;
11555b81b6b3SRodney W. Grimes 		if( fdc_data[fdcu].retry < 6)
11565b81b6b3SRodney W. Grimes 			fdc_data[fdcu].retry = 6;
11575b81b6b3SRodney W. Grimes 	}
11585b81b6b3SRodney W. Grimes 	else
11595b81b6b3SRodney W. Grimes 	{
11605b81b6b3SRodney W. Grimes 		fdc_data[fdcu].fd = (fd_p) 0;
11615b81b6b3SRodney W. Grimes 		fdc_data[fdcu].fdu = -1;
11625b81b6b3SRodney W. Grimes 		fdc_data[fdcu].state = DEVIDLE;
11635b81b6b3SRodney W. Grimes 	}
1164f5f7ba03SJordan K. Hubbard 	fdintr(fdcu);
1165f5f7ba03SJordan K. Hubbard 	splx(s);
11665b81b6b3SRodney W. Grimes }
11675b81b6b3SRodney W. Grimes 
11685b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1169381fe1aaSGarrett Wollman static void
1170d0917939SPaul Richards fd_pseudointr(void *arg1)
11715b81b6b3SRodney W. Grimes {
1172381fe1aaSGarrett Wollman 	fdcu_t fdcu = (fdcu_t)arg1;
11735b81b6b3SRodney W. Grimes 	int	s;
11743a2f7427SDavid Greenman 
11755b81b6b3SRodney W. Grimes 	s = splbio();
11765b81b6b3SRodney W. Grimes 	fdintr(fdcu);
11775b81b6b3SRodney W. Grimes 	splx(s);
11785b81b6b3SRodney W. Grimes }
11795b81b6b3SRodney W. Grimes 
11805b81b6b3SRodney W. Grimes /***********************************************************************\
11815b81b6b3SRodney W. Grimes *                                 fdintr				*
11825b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
11835b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
11845b81b6b3SRodney W. Grimes \***********************************************************************/
1185381fe1aaSGarrett Wollman void
1186381fe1aaSGarrett Wollman fdintr(fdcu_t fdcu)
11875b81b6b3SRodney W. Grimes {
11885b81b6b3SRodney W. Grimes 	fdc_p fdc = fdc_data + fdcu;
1189b99f0a4aSAndrew Moore #if NFT > 0
1190b99f0a4aSAndrew Moore 	fdu_t fdu = fdc->fdu;
1191b99f0a4aSAndrew Moore 
1192b99f0a4aSAndrew Moore 	if (fdc->flags & FDC_TAPE_BUSY)
1193b99f0a4aSAndrew Moore 		(ftintr(fdu));
1194b99f0a4aSAndrew Moore 	else
1195b99f0a4aSAndrew Moore #endif
1196381fe1aaSGarrett Wollman 		while(fdstate(fdcu, fdc))
1197381fe1aaSGarrett Wollman 			;
11985b81b6b3SRodney W. Grimes }
11995b81b6b3SRodney W. Grimes 
12005b81b6b3SRodney W. Grimes /***********************************************************************\
12015b81b6b3SRodney W. Grimes * The controller state machine.						*
12025b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
12035b81b6b3SRodney W. Grimes \***********************************************************************/
12043a2f7427SDavid Greenman static int
1205dc5df763SJoerg Wunsch fdstate(fdcu_t fdcu, fdc_p fdc)
12065b81b6b3SRodney W. Grimes {
12074ccc87c5SPoul-Henning Kamp 	int read, format, head, sec = 0, sectrac, st0, cyl, st3;
1208bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
12095b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
12105b81b6b3SRodney W. Grimes 	fd_p fd;
121117542807SPoul-Henning Kamp 	register struct buf *bp;
1212b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
12133a2f7427SDavid Greenman 	size_t fdblk;
12145b81b6b3SRodney W. Grimes 
121514212c9dSPoul-Henning Kamp 	bp = TAILQ_FIRST(&fdc->head);
121617542807SPoul-Henning Kamp 	if(!bp) {
12175b81b6b3SRodney W. Grimes 		/***********************************************\
12185b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
12195b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
12205b81b6b3SRodney W. Grimes 		\***********************************************/
12215b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
12225b81b6b3SRodney W. Grimes 		if(fdc->fd)
12235b81b6b3SRodney W. Grimes 		{
12246a0e6f42SRodney W. Grimes 			printf("fd%d: unexpected valid fd pointer\n",
12253a2f7427SDavid Greenman 			       fdc->fdu);
12265b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
12275b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
12285b81b6b3SRodney W. Grimes 		}
12295b81b6b3SRodney W. Grimes 		TRACE1("[fdc%d IDLE]", fdcu);
12305b81b6b3SRodney W. Grimes  		return(0);
12315b81b6b3SRodney W. Grimes 	}
12325b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
12335b81b6b3SRodney W. Grimes 	fd = fd_data + fdu;
12343a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
12355b81b6b3SRodney W. Grimes 	if (fdc->fd && (fd != fdc->fd))
12365b81b6b3SRodney W. Grimes 	{
12376a0e6f42SRodney W. Grimes 		printf("fd%d: confused fd pointers\n", fdu);
12385b81b6b3SRodney W. Grimes 	}
12395b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1240b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1241bb6382faSJoerg Wunsch 	if(format) {
1242b39c878eSAndrey A. Chernov 		finfo = (struct fd_formb *)bp->b_un.b_addr;
1243bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1244bb6382faSJoerg Wunsch 			- (char *)finfo;
1245bb6382faSJoerg Wunsch 	}
1246bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
1247bb6382faSJoerg Wunsch 		blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk +
1248bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1249bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1250bb6382faSJoerg Wunsch 	}
12515b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
12525b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
12535b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
12545e235068SJordan K. Hubbard 	untimeout(fd_turnoff, (caddr_t)fdu);
12555e235068SJordan K. Hubbard 	timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
12565b81b6b3SRodney W. Grimes 	switch (fdc->state)
12575b81b6b3SRodney W. Grimes 	{
12585b81b6b3SRodney W. Grimes 	case DEVIDLE:
12595b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
12605b81b6b3SRodney W. Grimes 		fdc->retry = 0;
12615b81b6b3SRodney W. Grimes 		fd->skip = 0;
12625b81b6b3SRodney W. Grimes 		fdc->fd = fd;
12635b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
12643a2f7427SDavid Greenman 		outb(fdc->baseport+FDCTL, fd->ft->trans);
12653a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
12665b81b6b3SRodney W. Grimes 		/*******************************************************\
12675b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
12685b81b6b3SRodney W. Grimes 		* it will start up in it's own good time		*
12695b81b6b3SRodney W. Grimes 		\*******************************************************/
12705b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
12715b81b6b3SRodney W. Grimes 		{
12725b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
12735b81b6b3SRodney W. Grimes 			return(0); /* come back later */
12745b81b6b3SRodney W. Grimes 		}
12755b81b6b3SRodney W. Grimes 		/*******************************************************\
12765b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
12775b81b6b3SRodney W. Grimes 		\*******************************************************/
12785b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
12795b81b6b3SRodney W. Grimes 		{
12805b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
12815b81b6b3SRodney W. Grimes 			fd_turnon(fdu);
12825b81b6b3SRodney W. Grimes 			return(0);
12835b81b6b3SRodney W. Grimes 		}
12845b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
12855b81b6b3SRodney W. Grimes 		{
12863a2f7427SDavid Greenman 			set_motor(fdcu, fd->fdsu, TURNON);
12875b81b6b3SRodney W. Grimes 		}
12885e235068SJordan K. Hubbard 		fdc->state = DOSEEK;
12895b81b6b3SRodney W. Grimes 		break;
12905b81b6b3SRodney W. Grimes 	case DOSEEK:
1291bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
12925b81b6b3SRodney W. Grimes 		{
12935b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
12945b81b6b3SRodney W. Grimes 			break;
12955b81b6b3SRodney W. Grimes 		}
1296dc5df763SJoerg Wunsch 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK,
1297bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1298dc5df763SJoerg Wunsch 			   0))
1299dc8603e3SJoerg Wunsch 		{
1300dc8603e3SJoerg Wunsch 			/*
1301dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1302dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1303dc8603e3SJoerg Wunsch 			 */
1304dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
1305dc8603e3SJoerg Wunsch 			return(retrier(fdcu));
1306dc8603e3SJoerg Wunsch 		}
1307dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
13085b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
13095b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
13105b81b6b3SRodney W. Grimes 	case SEEKWAIT:
13115b81b6b3SRodney W. Grimes 		/* allow heads to settle */
131204b734cfSPoul-Henning Kamp 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16);
13135b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
13145b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
13155b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
13165b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
1317dc5df763SJoerg Wunsch 		if(fd->track == FD_NO_TRACK)
13185b81b6b3SRodney W. Grimes 		{
1319bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
13203a2f7427SDavid Greenman 			do {
13213a2f7427SDavid Greenman 				/*
1322dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1323dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1324dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1325dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1326dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1327dc5df763SJoerg Wunsch 				 *
1328dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1329dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1330dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1331dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1332dc5df763SJoerg Wunsch 				 * real interrupt condition.
1333dc5df763SJoerg Wunsch 				 *
1334dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1335dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1336dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1337dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1338dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1339dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1340dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
13413a2f7427SDavid Greenman 				 */
1342dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1343dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1344dc5df763SJoerg Wunsch 					return 0;
1345dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1346dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1347dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
13483a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1349dc5df763SJoerg Wunsch 
13503a2f7427SDavid Greenman 			if (0 == descyl)
13513a2f7427SDavid Greenman 			{
1352dc5df763SJoerg Wunsch 				int failed = 0;
13533a2f7427SDavid Greenman 				/*
13543a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
13553a2f7427SDavid Greenman 				 * really there
13563a2f7427SDavid Greenman 				 */
1357dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1358dc5df763SJoerg Wunsch 					failed = 1;
13593a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
13603a2f7427SDavid Greenman 					printf(
13613a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
13623a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1363dc5df763SJoerg Wunsch 					failed = 1;
1364dc5df763SJoerg Wunsch 				}
1365dc5df763SJoerg Wunsch 
1366dc5df763SJoerg Wunsch 				if (failed)
1367dc5df763SJoerg Wunsch 				{
13683a2f7427SDavid Greenman 					if(fdc->retry < 3)
13693a2f7427SDavid Greenman 						fdc->retry = 3;
13703a2f7427SDavid Greenman 					return(retrier(fdcu));
13713a2f7427SDavid Greenman 				}
13723a2f7427SDavid Greenman 			}
1373dc5df763SJoerg Wunsch 
13745b81b6b3SRodney W. Grimes 			if (cyl != descyl)
13755b81b6b3SRodney W. Grimes 			{
13763a2f7427SDavid Greenman 				printf(
13773a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
13782d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
13795b81b6b3SRodney W. Grimes 				return(retrier(fdcu));
13805b81b6b3SRodney W. Grimes 			}
13815b81b6b3SRodney W. Grimes 		}
13825b81b6b3SRodney W. Grimes 
1383bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
13845b81b6b3SRodney W. Grimes 		isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
13853a2f7427SDavid Greenman 			format ? bp->b_bcount : fdblk, fdc->dmachan);
13865b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
13875b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
13885b81b6b3SRodney W. Grimes 		head = sec / sectrac;
13895b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
13903a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
13913a2f7427SDavid Greenman 
13923a2f7427SDavid Greenman 		if(format || !read)
13933a2f7427SDavid Greenman 		{
13943a2f7427SDavid Greenman 			/* make sure the drive is writable */
1395dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1396dc8603e3SJoerg Wunsch 			{
1397dc8603e3SJoerg Wunsch 				/* stuck controller? */
1398dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
1399dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
1400dc8603e3SJoerg Wunsch 			}
14013a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
14023a2f7427SDavid Greenman 			{
14033a2f7427SDavid Greenman 				/*
14043a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
14053a2f7427SDavid Greenman 				 * in order to force the current operation
14063a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
14073a2f7427SDavid Greenman 				 * error - all error handling is done
14083a2f7427SDavid Greenman 				 * by the retrier()
14093a2f7427SDavid Greenman 				 */
14103a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
14113a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
14123a2f7427SDavid Greenman 				fdc->status[2] = 0;
14133a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
14143a2f7427SDavid Greenman 				fdc->status[4] = head;
14153a2f7427SDavid Greenman 				fdc->status[5] = sec;
14163a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
14173a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
14183a2f7427SDavid Greenman 				return (1);
14193a2f7427SDavid Greenman 			}
14203a2f7427SDavid Greenman 		}
14215b81b6b3SRodney W. Grimes 
1422b39c878eSAndrey A. Chernov 		if(format)
1423b39c878eSAndrey A. Chernov 		{
1424b39c878eSAndrey A. Chernov 			/* formatting */
1425dc5df763SJoerg Wunsch 			if(fd_cmd(fdcu, 6,
1426dc5df763SJoerg Wunsch 				  NE7CMD_FORMAT,
1427dc5df763SJoerg Wunsch 				  head << 2 | fdu,
1428dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1429dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1430dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
1431dc5df763SJoerg Wunsch 				  finfo->fd_formb_fillbyte,
1432dc5df763SJoerg Wunsch 				  0))
1433dc8603e3SJoerg Wunsch 			{
1434dc8603e3SJoerg Wunsch 				/* controller fell over */
1435dc8603e3SJoerg Wunsch 				fdc->retry = 6;
1436dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
1437dc8603e3SJoerg Wunsch 			}
1438b39c878eSAndrey A. Chernov 		}
1439b39c878eSAndrey A. Chernov 		else
1440b39c878eSAndrey A. Chernov 		{
1441dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 9,
1442dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1443dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1444dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1445dc5df763SJoerg Wunsch 				   head,
1446dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1447dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1448dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1449dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1450dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
1451dc5df763SJoerg Wunsch 				   0))
14525b81b6b3SRodney W. Grimes 			{
1453dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
1454dc8603e3SJoerg Wunsch 				fdc->retry = 6;
1455dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
14565b81b6b3SRodney W. Grimes 			}
1457b39c878eSAndrey A. Chernov 		}
14585b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
14595e235068SJordan K. Hubbard 		timeout(fd_timeout, (caddr_t)fdcu, hz);
14605b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
14615b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
14625e235068SJordan K. Hubbard 		untimeout(fd_timeout, (caddr_t)fdcu);
1463dc5df763SJoerg Wunsch 
1464dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu))
14655b81b6b3SRodney W. Grimes 		{
1466dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1467dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
1468dc5df763SJoerg Wunsch 			return retrier(fdcu);
14695b81b6b3SRodney W. Grimes   		}
1470dc5df763SJoerg Wunsch 
14713a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1472dc5df763SJoerg Wunsch 
14733a2f7427SDavid Greenman 		/* FALLTHROUGH */
1474dc5df763SJoerg Wunsch 
14753a2f7427SDavid Greenman 	case IOTIMEDOUT:
14765b81b6b3SRodney W. Grimes 		isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
14773a2f7427SDavid Greenman 			    format ? bp->b_bcount : fdblk, fdc->dmachan);
14783a2f7427SDavid Greenman 		if (fdc->status[0] & NE7_ST0_IC)
14795b81b6b3SRodney W. Grimes 		{
14803a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
14813a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1482b39c878eSAndrey A. Chernov                                 /*
14833a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
14843a2f7427SDavid Greenman 				 * and didn't release it in time for the
14853a2f7427SDavid Greenman 				 * next FDC transfer.
14863a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
14873a2f7427SDavid Greenman 				 * count. (vak)
1488b39c878eSAndrey A. Chernov                                  */
1489b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
1490b39c878eSAndrey A. Chernov                                 return (1);
1491b39c878eSAndrey A. Chernov                         }
14923a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
14933a2f7427SDavid Greenman 				&& fdc->retry < 6)
14943a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
14953a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
14963a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
14973a2f7427SDavid Greenman 				&& fdc->retry < 3)
14983a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
14995b81b6b3SRodney W. Grimes 			return(retrier(fdcu));
15005b81b6b3SRodney W. Grimes 		}
15015b81b6b3SRodney W. Grimes 		/* All OK */
15023a2f7427SDavid Greenman 		fd->skip += fdblk;
1503bb6382faSJoerg Wunsch 		if (!format && fd->skip < bp->b_bcount - bp->b_resid)
15045b81b6b3SRodney W. Grimes 		{
15055b81b6b3SRodney W. Grimes 			/* set up next transfer */
15065b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
15075b81b6b3SRodney W. Grimes 		}
15085b81b6b3SRodney W. Grimes 		else
15095b81b6b3SRodney W. Grimes 		{
15105b81b6b3SRodney W. Grimes 			/* ALL DONE */
15115b81b6b3SRodney W. Grimes 			fd->skip = 0;
151217542807SPoul-Henning Kamp 			TAILQ_REMOVE(&fdc->head, bp, b_act);
15135b81b6b3SRodney W. Grimes 			biodone(bp);
15145b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
15155b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
15165b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
15175b81b6b3SRodney W. Grimes 		}
15185b81b6b3SRodney W. Grimes 		return(1);
15195b81b6b3SRodney W. Grimes 	case RESETCTLR:
15203a2f7427SDavid Greenman 		fdc_reset(fdc);
15215b81b6b3SRodney W. Grimes 		fdc->retry++;
15225b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
15235b81b6b3SRodney W. Grimes 		break;
15245b81b6b3SRodney W. Grimes 	case STARTRECAL:
15250e317d05SJoerg Wunsch 		/* XXX clear the fdc results from the last reset, if any. */
15260e317d05SJoerg Wunsch 		{
15270e317d05SJoerg Wunsch 			int i;
15280e317d05SJoerg Wunsch 			for (i = 0; i < 4; i++)
15290e317d05SJoerg Wunsch 				(void)fd_sense_int(fdc, &st0, &cyl);
15300e317d05SJoerg Wunsch 		}
15310e317d05SJoerg Wunsch 
1532dc5df763SJoerg Wunsch 		if(fd_cmd(fdcu,
1533dc5df763SJoerg Wunsch 			  2, NE7CMD_RECAL, fdu,
1534dc5df763SJoerg Wunsch 			  0)) /* Recalibrate Function */
1535dc8603e3SJoerg Wunsch 		{
1536dc8603e3SJoerg Wunsch 			/* arrgl */
1537dc8603e3SJoerg Wunsch 			fdc->retry = 6;
1538dc8603e3SJoerg Wunsch 			return(retrier(fdcu));
1539dc8603e3SJoerg Wunsch 		}
15405b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
15415b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
15425b81b6b3SRodney W. Grimes 	case RECALWAIT:
15435b81b6b3SRodney W. Grimes 		/* allow heads to settle */
154404b734cfSPoul-Henning Kamp 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8);
15455b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
15465b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
15475b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
15483a2f7427SDavid Greenman 		do {
15493a2f7427SDavid Greenman 			/*
1550dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
15513a2f7427SDavid Greenman 			 */
1552dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
1553dc5df763SJoerg Wunsch 				return 0;
1554dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
1555dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1556dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
15573a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
15583a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
15595b81b6b3SRodney W. Grimes 		{
1560dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
1561dc8603e3SJoerg Wunsch 				/*
1562dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
1563dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
1564dc8603e3SJoerg Wunsch 				 * since people used to complain much about
1565dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
1566dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
1567dc8603e3SJoerg Wunsch 				 * time in a line
1568dc8603e3SJoerg Wunsch 				 */
1569dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
1570dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
15713a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
15725b81b6b3SRodney W. Grimes 			return(retrier(fdcu));
15735b81b6b3SRodney W. Grimes 		}
15745b81b6b3SRodney W. Grimes 		fd->track = 0;
15755b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
15765b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
15775b81b6b3SRodney W. Grimes 		return(1);	/* will return immediatly */
15785b81b6b3SRodney W. Grimes 	case MOTORWAIT:
15795b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
15805b81b6b3SRodney W. Grimes 		{
15815b81b6b3SRodney W. Grimes 			return(0); /* time's not up yet */
15825b81b6b3SRodney W. Grimes 		}
15835e235068SJordan K. Hubbard 		/*
15845e235068SJordan K. Hubbard 		 * since the controller was off, it has lost its
15855e235068SJordan K. Hubbard 		 * idea about the current track it were; thus,
15865e235068SJordan K. Hubbard 		 * recalibrate the bastard
15875e235068SJordan K. Hubbard 		 */
15885e235068SJordan K. Hubbard 		fdc->state = STARTRECAL;
15895b81b6b3SRodney W. Grimes 		return(1);	/* will return immediatly */
15905b81b6b3SRodney W. Grimes 	default:
1591dc5df763SJoerg Wunsch 		printf("fdc%d: Unexpected FD int->", fdcu);
1592dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
1593dac0f2dbSJoerg Wunsch 			printf("FDC status :%lx %lx %lx %lx %lx %lx %lx   ",
15945b81b6b3SRodney W. Grimes 			       fdc->status[0],
15955b81b6b3SRodney W. Grimes 			       fdc->status[1],
15965b81b6b3SRodney W. Grimes 			       fdc->status[2],
15975b81b6b3SRodney W. Grimes 			       fdc->status[3],
15985b81b6b3SRodney W. Grimes 			       fdc->status[4],
15995b81b6b3SRodney W. Grimes 			       fdc->status[5],
16005b81b6b3SRodney W. Grimes 			       fdc->status[6] );
16013a2f7427SDavid Greenman 		else
1602dac0f2dbSJoerg Wunsch 			printf("No status available   ");
1603dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
1604dac0f2dbSJoerg Wunsch 		{
1605dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
16065b81b6b3SRodney W. Grimes 			return(0);
16075b81b6b3SRodney W. Grimes 		}
1608dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
1609dac0f2dbSJoerg Wunsch 		return(0);
1610dac0f2dbSJoerg Wunsch 	}
1611dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
16125b81b6b3SRodney W. Grimes 	return(1); /* Come back immediatly to new state */
16135b81b6b3SRodney W. Grimes }
16145b81b6b3SRodney W. Grimes 
1615aaf08d94SGarrett Wollman static int
1616f5f7ba03SJordan K. Hubbard retrier(fdcu)
1617f5f7ba03SJordan K. Hubbard 	fdcu_t fdcu;
16185b81b6b3SRodney W. Grimes {
16195b81b6b3SRodney W. Grimes 	fdc_p fdc = fdc_data + fdcu;
162017542807SPoul-Henning Kamp 	register struct buf *bp;
16215b81b6b3SRodney W. Grimes 
162217542807SPoul-Henning Kamp 	bp = TAILQ_FIRST(&fdc->head);
16235b81b6b3SRodney W. Grimes 
16243a2f7427SDavid Greenman 	if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY)
16253a2f7427SDavid Greenman 		goto fail;
16265b81b6b3SRodney W. Grimes 	switch(fdc->retry)
16275b81b6b3SRodney W. Grimes 	{
16285b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
16295b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
16305b81b6b3SRodney W. Grimes 		break;
16315b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
16325b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
16335b81b6b3SRodney W. Grimes 		break;
16345b81b6b3SRodney W. Grimes 	case 6:
16355b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
16365b81b6b3SRodney W. Grimes 		break;
16375b81b6b3SRodney W. Grimes 	case 7:
16385b81b6b3SRodney W. Grimes 		break;
16395b81b6b3SRodney W. Grimes 	default:
16403a2f7427SDavid Greenman 	fail:
16415b81b6b3SRodney W. Grimes 		{
16427ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
16437ca0641bSAndrey A. Chernov 			/* Trick diskerr */
16443a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
16453a2f7427SDavid Greenman 					    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
164692ed385aSRodney W. Grimes 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
16473a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
16483a2f7427SDavid Greenman 				(struct disklabel *)NULL);
16497ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
1650dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
1651dc5df763SJoerg Wunsch 			{
1652dc5df763SJoerg Wunsch 				printf(
1653dc5df763SJoerg Wunsch 			" (ST0 %b ST1 %b ST2 %b cyl %ld hd %ld sec %ld)\n",
1654dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
1655dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
1656dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
1657dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
1658dc5df763SJoerg Wunsch 				       fdc->status[5]);
1659dc5df763SJoerg Wunsch 			}
1660dc5df763SJoerg Wunsch 			else
1661dc5df763SJoerg Wunsch 				printf(" (No status)\n");
16625b81b6b3SRodney W. Grimes 		}
16635b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
16645b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
1665bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
166617542807SPoul-Henning Kamp 		TAILQ_REMOVE(&fdc->head, bp, b_act);
16675b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
16685b81b6b3SRodney W. Grimes 		biodone(bp);
166992ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
16705b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
16715b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
1672f5f7ba03SJordan K. Hubbard 		/* XXX abort current command, if any.  */
167392ed385aSRodney W. Grimes 		return(1);
16745b81b6b3SRodney W. Grimes 	}
16755b81b6b3SRodney W. Grimes 	fdc->retry++;
16765b81b6b3SRodney W. Grimes 	return(1);
16775b81b6b3SRodney W. Grimes }
16785b81b6b3SRodney W. Grimes 
1679b39c878eSAndrey A. Chernov static int
1680b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
1681b39c878eSAndrey A. Chernov 	dev_t dev;
1682b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
1683b39c878eSAndrey A. Chernov 	struct proc *p;
1684b39c878eSAndrey A. Chernov {
1685b39c878eSAndrey A. Chernov  	fdu_t	fdu;
1686b39c878eSAndrey A. Chernov  	fd_p	fd;
1687b39c878eSAndrey A. Chernov 
1688b39c878eSAndrey A. Chernov 	struct buf *bp;
1689b39c878eSAndrey A. Chernov 	int rv = 0, s;
16903a2f7427SDavid Greenman 	size_t fdblk;
1691b39c878eSAndrey A. Chernov 
1692b39c878eSAndrey A. Chernov  	fdu = FDUNIT(minor(dev));
1693b39c878eSAndrey A. Chernov 	fd = &fd_data[fdu];
16943a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1695b39c878eSAndrey A. Chernov 
1696b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
1697b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1698b39c878eSAndrey A. Chernov 	if(bp == 0)
1699b39c878eSAndrey A. Chernov 		return ENOBUFS;
170082f5379bSJoerg Wunsch 	/*
170182f5379bSJoerg Wunsch 	 * keep the process from being swapped
170282f5379bSJoerg Wunsch 	 */
170382f5379bSJoerg Wunsch 	p->p_flag |= P_PHYSIO;
1704b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
1705b39c878eSAndrey A. Chernov 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1706b39c878eSAndrey A. Chernov 	bp->b_proc = p;
1707b39c878eSAndrey A. Chernov 	bp->b_dev = dev;
1708b39c878eSAndrey A. Chernov 
1709b39c878eSAndrey A. Chernov 	/*
1710b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
1711b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
1712b39c878eSAndrey A. Chernov 	 */
1713b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
17143a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
1715b39c878eSAndrey A. Chernov 
1716b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
17175e235068SJordan K. Hubbard 	bp->b_un.b_addr = (caddr_t)finfo;
1718b39c878eSAndrey A. Chernov 
1719b39c878eSAndrey A. Chernov 	/* now do the format */
1720b39c878eSAndrey A. Chernov 	fdstrategy(bp);
1721b39c878eSAndrey A. Chernov 
1722b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
1723b39c878eSAndrey A. Chernov 	s = splbio();
1724b39c878eSAndrey A. Chernov 	while(!(bp->b_flags & B_DONE))
1725b39c878eSAndrey A. Chernov 	{
17265e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1727b39c878eSAndrey A. Chernov 		if(rv == EWOULDBLOCK)
1728b39c878eSAndrey A. Chernov 			break;
1729b39c878eSAndrey A. Chernov 	}
1730b39c878eSAndrey A. Chernov 	splx(s);
1731b39c878eSAndrey A. Chernov 
173282f5379bSJoerg Wunsch 	if(rv == EWOULDBLOCK) {
1733b39c878eSAndrey A. Chernov 		/* timed out */
1734b39c878eSAndrey A. Chernov 		rv = EIO;
173582f5379bSJoerg Wunsch 		biodone(bp);
173682f5379bSJoerg Wunsch 	}
17373a2f7427SDavid Greenman 	if(bp->b_flags & B_ERROR)
17383a2f7427SDavid Greenman 		rv = bp->b_error;
173982f5379bSJoerg Wunsch 	/*
174082f5379bSJoerg Wunsch 	 * allow the process to be swapped
174182f5379bSJoerg Wunsch 	 */
174282f5379bSJoerg Wunsch 	p->p_flag &= ~P_PHYSIO;
1743b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
1744b39c878eSAndrey A. Chernov 	return rv;
1745b39c878eSAndrey A. Chernov }
1746b39c878eSAndrey A. Chernov 
1747f5f7ba03SJordan K. Hubbard /*
1748671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
1749f5f7ba03SJordan K. Hubbard  */
17505b81b6b3SRodney W. Grimes 
1751f5f7ba03SJordan K. Hubbard int
1752b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
1753f5f7ba03SJordan K. Hubbard 	dev_t dev;
1754f5f7ba03SJordan K. Hubbard 	int cmd;
1755f5f7ba03SJordan K. Hubbard 	caddr_t addr;
1756f5f7ba03SJordan K. Hubbard 	int flag;
1757b39c878eSAndrey A. Chernov 	struct proc *p;
1758f5f7ba03SJordan K. Hubbard {
17593a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
17603a2f7427SDavid Greenman  	fd_p	fd = &fd_data[fdu];
17613a2f7427SDavid Greenman 	size_t fdblk;
17623a2f7427SDavid Greenman 
1763f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
1764f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
1765f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
17663a2f7427SDavid Greenman 	int error = 0;
1767f5f7ba03SJordan K. Hubbard 
1768b99f0a4aSAndrew Moore #if NFT > 0
1769a60eff27SNate Williams 	int type = FDTYPE(minor(dev));
1770a60eff27SNate Williams 
1771a60eff27SNate Williams 	/* check for a tape ioctl */
1772a60eff27SNate Williams 	if (type & F_TAPE_TYPE)
1773b99f0a4aSAndrew Moore 		return ftioctl(dev, cmd, addr, flag, p);
1774b99f0a4aSAndrew Moore #endif
1775b99f0a4aSAndrew Moore 
17763a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
1777f5f7ba03SJordan K. Hubbard 
1778f5f7ba03SJordan K. Hubbard 	switch (cmd)
1779f5f7ba03SJordan K. Hubbard 	{
1780f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
1781f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
1782f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
17833a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
178492ed385aSRodney W. Grimes 		fdt = fd_data[FDUNIT(minor(dev))].ft;
1785f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
1786f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
1787f5f7ba03SJordan K. Hubbard 
1788191e1a59SBruce Evans 		if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl)
1789191e1a59SBruce Evans 		    == NULL)
1790f5f7ba03SJordan K. Hubbard 			error = 0;
1791f5f7ba03SJordan K. Hubbard 		else
1792f5f7ba03SJordan K. Hubbard 			error = EINVAL;
1793f5f7ba03SJordan K. Hubbard 
1794f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
1795f5f7ba03SJordan K. Hubbard 		break;
1796f5f7ba03SJordan K. Hubbard 
1797f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
1798f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
1799f5f7ba03SJordan K. Hubbard 			error = EBADF;
1800f5f7ba03SJordan K. Hubbard 		break;
1801f5f7ba03SJordan K. Hubbard 
1802f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
1803f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
1804f5f7ba03SJordan K. Hubbard 			error = EBADF;
1805f5f7ba03SJordan K. Hubbard 		break;
1806f5f7ba03SJordan K. Hubbard 
1807f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
1808f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
1809f5f7ba03SJordan K. Hubbard 		{
1810f5f7ba03SJordan K. Hubbard 			error = EBADF;
1811f5f7ba03SJordan K. Hubbard 			break;
1812f5f7ba03SJordan K. Hubbard 		}
1813f5f7ba03SJordan K. Hubbard 
1814f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
1815f5f7ba03SJordan K. Hubbard 
1816191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
1817191e1a59SBruce Evans 					  (u_long)0)) != 0)
1818f5f7ba03SJordan K. Hubbard 			break;
1819f5f7ba03SJordan K. Hubbard 
1820b39c878eSAndrey A. Chernov 		error = writedisklabel(dev, fdstrategy,
182154c7241bSJordan K. Hubbard 				       (struct disklabel *)buffer);
1822b39c878eSAndrey A. Chernov 		break;
1823b39c878eSAndrey A. Chernov 
1824b39c878eSAndrey A. Chernov 	case FD_FORM:
1825b39c878eSAndrey A. Chernov 		if((flag & FWRITE) == 0)
1826b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
1827b39c878eSAndrey A. Chernov 		else if(((struct fd_formb *)addr)->format_version !=
1828b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
1829b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
1830b39c878eSAndrey A. Chernov 		else
1831b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
1832b39c878eSAndrey A. Chernov 		break;
1833b39c878eSAndrey A. Chernov 
1834b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
1835b39c878eSAndrey A. Chernov 		*(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
1836f5f7ba03SJordan K. Hubbard 		break;
1837f5f7ba03SJordan K. Hubbard 
18383a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
18393a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
18403a2f7427SDavid Greenman 		if(suser(p->p_ucred, &p->p_acflag) != 0)
18413a2f7427SDavid Greenman 			return EPERM;
18423a2f7427SDavid Greenman 		*fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr;
18433a2f7427SDavid Greenman 		break;
18443a2f7427SDavid Greenman 
18453a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
18463a2f7427SDavid Greenman 		*(int *)addr = fd_data[FDUNIT(minor(dev))].options;
18473a2f7427SDavid Greenman 		break;
18483a2f7427SDavid Greenman 
18493a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
18503a2f7427SDavid Greenman 		fd_data[FDUNIT(minor(dev))].options = *(int *)addr;
18513a2f7427SDavid Greenman 		break;
18523a2f7427SDavid Greenman 
1853f5f7ba03SJordan K. Hubbard 	default:
18543a2f7427SDavid Greenman 		error = ENOTTY;
1855f5f7ba03SJordan K. Hubbard 		break;
1856f5f7ba03SJordan K. Hubbard 	}
1857f5f7ba03SJordan K. Hubbard 	return (error);
1858f5f7ba03SJordan K. Hubbard }
1859f5f7ba03SJordan K. Hubbard 
18607146c13eSJulian Elischer 
18617146c13eSJulian Elischer static fd_devsw_installed = 0;
18627146c13eSJulian Elischer 
186387f6c662SJulian Elischer static void 	fd_drvinit(void *notused )
18647146c13eSJulian Elischer {
186587f6c662SJulian Elischer 
18667146c13eSJulian Elischer 	if( ! fd_devsw_installed ) {
1867cba8a5ddSPoul-Henning Kamp 		bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_bdevsw);
18687146c13eSJulian Elischer 		fd_devsw_installed = 1;
18697146c13eSJulian Elischer 	}
18707146c13eSJulian Elischer }
187187f6c662SJulian Elischer 
187287f6c662SJulian Elischer SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL)
187387f6c662SJulian Elischer 
1874f5f7ba03SJordan K. Hubbard #endif
18753a2f7427SDavid Greenman /*
18763a2f7427SDavid Greenman  * Hello emacs, these are the
18773a2f7427SDavid Greenman  * Local Variables:
18783a2f7427SDavid Greenman  *  c-indent-level:               8
18793a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
18803a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
18813a2f7427SDavid Greenman  *  c-brace-offset:              -8
18823a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
18833a2f7427SDavid Greenman  *  c-argdecl-indent:             8
18843a2f7427SDavid Greenman  *  c-label-offset:              -8
18853a2f7427SDavid Greenman  *  c++-hanging-braces:           1
18863a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
18873a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
18883a2f7427SDavid Greenman  *  c++-friend-offset:            0
18893a2f7427SDavid Greenman  * End:
18903a2f7427SDavid Greenman  */
1891