xref: /freebsd/sys/dev/fdc/fdc.c (revision e34c71ea40a1a18d241a5459d22765bffe3eb719)
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  *
869acd21dSWarner Losh  * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu)
969acd21dSWarner Losh  * aided by the Linux floppy driver modifications from David Bateman
1069acd21dSWarner Losh  * (dbateman@eng.uts.edu.au).
1169acd21dSWarner Losh  *
12dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994 by
133a2f7427SDavid Greenman  *  jc@irbs.UUCP (John Capo)
143a2f7427SDavid Greenman  *  vak@zebub.msk.su (Serge Vakulenko)
153a2f7427SDavid Greenman  *  ache@astral.msk.su (Andrew A. Chernov)
16dc16046fSJoerg Wunsch  *
17dc16046fSJoerg Wunsch  * Copyright (c) 1993, 1994, 1995 by
183a2f7427SDavid Greenman  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
19dc5df763SJoerg Wunsch  *  dufault@hda.com (Peter Dufault)
203a2f7427SDavid Greenman  *
215b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
225b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
235b81b6b3SRodney W. Grimes  * are met:
245b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
255b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
265b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
275b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
285b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
295b81b6b3SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
305b81b6b3SRodney W. Grimes  *    must display the following acknowledgement:
315b81b6b3SRodney W. Grimes  *	This product includes software developed by the University of
325b81b6b3SRodney W. Grimes  *	California, Berkeley and its contributors.
335b81b6b3SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
345b81b6b3SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
355b81b6b3SRodney W. Grimes  *    without specific prior written permission.
365b81b6b3SRodney W. Grimes  *
375b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
385b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
395b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
405b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
415b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
425b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
435b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
445b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
455b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
465b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
475b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
485b81b6b3SRodney W. Grimes  *
49dc4ff321SRodney W. Grimes  *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
50c3aac50fSPeter Wemm  * $FreeBSD$
515b81b6b3SRodney W. Grimes  *
525b81b6b3SRodney W. Grimes  */
535b81b6b3SRodney W. Grimes 
54d2fb4892SJoerg Wunsch #include "opt_fdc.h"
555b81b6b3SRodney W. Grimes 
56b99f0a4aSAndrew Moore #include <sys/param.h>
57b99f0a4aSAndrew Moore #include <sys/systm.h>
58b99f0a4aSAndrew Moore #include <sys/kernel.h>
59b99f0a4aSAndrew Moore #include <sys/buf.h>
606182fdbdSPeter Wemm #include <sys/bus.h>
616182fdbdSPeter Wemm #include <sys/conf.h>
626182fdbdSPeter Wemm #include <sys/disklabel.h>
63b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
646182fdbdSPeter Wemm #include <sys/fcntl.h>
65b99f0a4aSAndrew Moore #include <sys/malloc.h>
666182fdbdSPeter Wemm #include <sys/module.h>
673a2f7427SDavid Greenman #include <sys/proc.h>
68b99f0a4aSAndrew Moore #include <sys/syslog.h>
696182fdbdSPeter Wemm 
706182fdbdSPeter Wemm #include <sys/bus.h>
716182fdbdSPeter Wemm #include <machine/bus.h>
726182fdbdSPeter Wemm #include <sys/rman.h>
736182fdbdSPeter Wemm 
746182fdbdSPeter Wemm #include <machine/clock.h>
756182fdbdSPeter Wemm #include <machine/ioctl_fd.h>
766182fdbdSPeter Wemm #include <machine/resource.h>
77dc5df763SJoerg Wunsch #include <machine/stdarg.h>
786182fdbdSPeter Wemm 
796182fdbdSPeter Wemm #include <isa/isavar.h>
80a97c75b7SDoug Rabson #include <isa/isareg.h>
81a97c75b7SDoug Rabson #include <isa/fdreg.h>
82a97c75b7SDoug Rabson #include <isa/fdc.h>
83a97c75b7SDoug Rabson #include <isa/rtc.h>
846182fdbdSPeter Wemm 
85edfdec19SPeter Wemm #ifdef FDC_YE
86edfdec19SPeter Wemm #undef FDC_YE
87edfdec19SPeter Wemm #warning "fix FDC_YE! - newbus casualty"
88edfdec19SPeter Wemm #endif
89edfdec19SPeter Wemm 
90b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
91b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
925b81b6b3SRodney W. Grimes 
930722d6abSJoerg Wunsch /* configuration flags */
940722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
9569acd21dSWarner Losh #ifdef FDC_YE
9669acd21dSWarner Losh #define FDC_IS_PCMCIA  (1 << 1)		/* if successful probe, then it's
9769acd21dSWarner Losh 					   a PCMCIA device */
9869acd21dSWarner Losh #endif
99e34c71eaSJoerg Wunsch #define FDC_NO_FIFO	(1 << 2)	/* do not enable FIFO  */
1000722d6abSJoerg Wunsch 
1010722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
1020722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
1030722d6abSJoerg Wunsch 
104dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
105dc5df763SJoerg Wunsch #define FD_FAILED -1
106dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
107dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
108dc5df763SJoerg Wunsch 
109b39c878eSAndrey A. Chernov #define NUMTYPES 14
110b39c878eSAndrey A. Chernov #define NUMDENS  (NUMTYPES - 6)
1117ca0641bSAndrey A. Chernov 
1123a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
113b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
114b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
115b99f0a4aSAndrew Moore #define FD_1720         1
116b99f0a4aSAndrew Moore #define FD_1480         2
117b99f0a4aSAndrew Moore #define FD_1440         3
118b99f0a4aSAndrew Moore #define FD_1200         4
119b99f0a4aSAndrew Moore #define FD_820          5
120b99f0a4aSAndrew Moore #define FD_800          6
121b99f0a4aSAndrew Moore #define FD_720          7
122b99f0a4aSAndrew Moore #define FD_360          8
123ed2fa05eSAndrey A. Chernov 
124b99f0a4aSAndrew Moore #define FD_1480in5_25   9
125b99f0a4aSAndrew Moore #define FD_1440in5_25   10
126b99f0a4aSAndrew Moore #define FD_820in5_25    11
127b99f0a4aSAndrew Moore #define FD_800in5_25    12
128b99f0a4aSAndrew Moore #define FD_720in5_25    13
129b99f0a4aSAndrew Moore #define FD_360in5_25    14
130b99f0a4aSAndrew Moore 
1317ca0641bSAndrey A. Chernov 
1326f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1335b81b6b3SRodney W. Grimes {
134126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
135126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
136126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
137126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
138126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
139126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
140126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
141b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
142ed2fa05eSAndrey A. Chernov 
143126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
144126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
145126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
146126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
147126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
148126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1495b81b6b3SRodney W. Grimes };
1505b81b6b3SRodney W. Grimes 
151b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
152dc16046fSJoerg Wunsch 
1535b81b6b3SRodney W. Grimes /***********************************************************************\
1545b81b6b3SRodney W. Grimes * Per controller structure.						*
1555b81b6b3SRodney W. Grimes \***********************************************************************/
1566182fdbdSPeter Wemm static devclass_t fdc_devclass;
1575b81b6b3SRodney W. Grimes 
1585b81b6b3SRodney W. Grimes /***********************************************************************\
1595b81b6b3SRodney W. Grimes * Per drive structure.							*
160b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1615b81b6b3SRodney W. Grimes \***********************************************************************/
1626182fdbdSPeter Wemm struct fd_data {
163b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1645b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1653a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1665b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1675b81b6b3SRodney W. Grimes 	int	flags;
1685b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1695b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1705b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1715b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1725b81b6b3SRodney W. Grimes 	int	skip;
1735b81b6b3SRodney W. Grimes 	int	hddrv;
174dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1755b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1763a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
17702a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
17802a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
179b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
1806182fdbdSPeter Wemm 	device_t dev;
1816182fdbdSPeter Wemm 	fdu_t	fdu;
1826182fdbdSPeter Wemm };
1836182fdbdSPeter Wemm static devclass_t fd_devclass;
1845b81b6b3SRodney W. Grimes 
1855b81b6b3SRodney W. Grimes /***********************************************************************\
1865b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1875b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1885b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
1895b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
1905b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
1915b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
1925b81b6b3SRodney W. Grimes \***********************************************************************/
193b99f0a4aSAndrew Moore 
19469acd21dSWarner Losh #ifdef FDC_YE
19569acd21dSWarner Losh #include "card.h"
19669acd21dSWarner Losh static int yeattach(struct isa_device *);
19769acd21dSWarner Losh #endif
19869acd21dSWarner Losh 
1993a2f7427SDavid Greenman /* needed for ft driver, thus exported */
2006182fdbdSPeter Wemm int in_fdc(struct fdc_data *);
2016182fdbdSPeter Wemm int out_fdc(struct fdc_data *, int);
2023a2f7427SDavid Greenman 
2033a2f7427SDavid Greenman /* internal functions */
2046182fdbdSPeter Wemm static	void fdc_add_device(device_t, const char *, int);
2056182fdbdSPeter Wemm static	void fdc_intr(void *);
2066182fdbdSPeter Wemm static void set_motor(struct fdc_data *, int, int);
2073a2f7427SDavid Greenman #  define TURNON 1
2083a2f7427SDavid Greenman #  define TURNOFF 0
2093a2f7427SDavid Greenman static timeout_t fd_turnoff;
2103a2f7427SDavid Greenman static timeout_t fd_motor_on;
2116182fdbdSPeter Wemm static void fd_turnon(struct fd_data *);
2123a2f7427SDavid Greenman static void fdc_reset(fdc_p);
2136182fdbdSPeter Wemm static int fd_in(struct fdc_data *, int *);
2146182fdbdSPeter Wemm static void fdstart(struct fdc_data *);
2155c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
2163a2f7427SDavid Greenman static timeout_t fd_pseudointr;
2176182fdbdSPeter Wemm static int fdstate(struct fdc_data *);
2186182fdbdSPeter Wemm static int retrier(struct fdc_data *);
2193a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2203a2f7427SDavid Greenman 
221d66c374fSTor Egge static int enable_fifo(fdc_p fdc);
222d66c374fSTor Egge 
223d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
224d66c374fSTor Egge 
225aaf08d94SGarrett Wollman 
2265b81b6b3SRodney W. Grimes #define DEVIDLE		0
2275b81b6b3SRodney W. Grimes #define FINDWORK	1
2285b81b6b3SRodney W. Grimes #define	DOSEEK		2
2295b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2305b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2315b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2325b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2335b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2345b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2355b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2365b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2375b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2385c1a1eaeSBruce Evans #define	RESETCOMPLETE	12
23969acd21dSWarner Losh #ifdef FDC_YE
24069acd21dSWarner Losh #define PIOREAD		13
24169acd21dSWarner Losh #endif
2425b81b6b3SRodney W. Grimes 
243d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
244cba2a7c6SBruce Evans static char const * const fdstates[] =
2455b81b6b3SRodney W. Grimes {
2465b81b6b3SRodney W. Grimes "DEVIDLE",
2475b81b6b3SRodney W. Grimes "FINDWORK",
2485b81b6b3SRodney W. Grimes "DOSEEK",
2495b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2505b81b6b3SRodney W. Grimes "IOCOMPLETE",
2515b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2525b81b6b3SRodney W. Grimes "STARTRECAL",
2535b81b6b3SRodney W. Grimes "RESETCTLR",
2545b81b6b3SRodney W. Grimes "SEEKWAIT",
2555b81b6b3SRodney W. Grimes "RECALWAIT",
2565b81b6b3SRodney W. Grimes "MOTORWAIT",
2575c1a1eaeSBruce Evans "IOTIMEDOUT",
2585c1a1eaeSBruce Evans "RESETCOMPLETE",
25969acd21dSWarner Losh #ifdef FDC_YE
260ff9607b0SBruce Evans "PIOREAD",
26169acd21dSWarner Losh #endif
2625b81b6b3SRodney W. Grimes };
2635b81b6b3SRodney W. Grimes 
2643a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
265cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2665b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2675b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
268d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2695b81b6b3SRodney W. Grimes #define TRACE0(arg)
2705b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
271d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2725b81b6b3SRodney W. Grimes 
273427ccf00SDoug Rabson static void
274427ccf00SDoug Rabson fdout_wr(fdc_p fdc, u_int8_t v)
275427ccf00SDoug Rabson {
276427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v);
277427ccf00SDoug Rabson }
278427ccf00SDoug Rabson 
279427ccf00SDoug Rabson static u_int8_t
280427ccf00SDoug Rabson fdsts_rd(fdc_p fdc)
281427ccf00SDoug Rabson {
282427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off);
283427ccf00SDoug Rabson }
284427ccf00SDoug Rabson 
285427ccf00SDoug Rabson static void
286427ccf00SDoug Rabson fddata_wr(fdc_p fdc, u_int8_t v)
287427ccf00SDoug Rabson {
288427ccf00SDoug Rabson 	bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v);
289427ccf00SDoug Rabson }
290427ccf00SDoug Rabson 
291427ccf00SDoug Rabson static u_int8_t
292427ccf00SDoug Rabson fddata_rd(fdc_p fdc)
293427ccf00SDoug Rabson {
294427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off);
295427ccf00SDoug Rabson }
296427ccf00SDoug Rabson 
297427ccf00SDoug Rabson static void
298427ccf00SDoug Rabson fdctl_wr(fdc_p fdc, u_int8_t v)
299427ccf00SDoug Rabson {
300427ccf00SDoug Rabson 	if (fdc->flags & FDC_ISPNP)
301427ccf00SDoug Rabson 		bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v);
302427ccf00SDoug Rabson 	else
303427ccf00SDoug Rabson 		bus_space_write_1(fdc->portt, fdc->porth, FDCTL, v);
304427ccf00SDoug Rabson }
305427ccf00SDoug Rabson 
306427ccf00SDoug Rabson #if 0
307427ccf00SDoug Rabson 
308427ccf00SDoug Rabson static u_int8_t
309427ccf00SDoug Rabson fdin_rd(fdc_p fdc)
310427ccf00SDoug Rabson {
311427ccf00SDoug Rabson 	return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
312427ccf00SDoug Rabson }
313427ccf00SDoug Rabson 
314427ccf00SDoug Rabson #endif
315427ccf00SDoug Rabson 
31669acd21dSWarner Losh #ifdef FDC_YE
31769acd21dSWarner Losh #if NCARD > 0
31869acd21dSWarner Losh #include <sys/select.h>
319d17e4ee6SPeter Wemm #include <sys/module.h>
32069acd21dSWarner Losh #include <pccard/cardinfo.h>
32169acd21dSWarner Losh #include <pccard/driver.h>
32269acd21dSWarner Losh #include <pccard/slot.h>
32369acd21dSWarner Losh 
32469acd21dSWarner Losh /*
32569acd21dSWarner Losh  *	PC-Card (PCMCIA) specific code.
32669acd21dSWarner Losh  */
32769acd21dSWarner Losh static int yeinit(struct pccard_devinfo *);		/* init device */
32869acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); 		/* Disable driver */
32969acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); 		/* Interrupt handler */
33069acd21dSWarner Losh 
331d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask);
33269acd21dSWarner Losh 
33369acd21dSWarner Losh /*
33469acd21dSWarner Losh  * this is the secret PIO data port (offset from base)
33569acd21dSWarner Losh  */
33669acd21dSWarner Losh #define FDC_YE_DATAPORT 6
33769acd21dSWarner Losh 
33869acd21dSWarner Losh /*
33969acd21dSWarner Losh  *	Initialize the device - called from Slot manager.
34069acd21dSWarner Losh  */
34169acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi)
34269acd21dSWarner Losh {
34369acd21dSWarner Losh 	fdc_p fdc = &fdc_data[devi->isahd.id_unit];
34469acd21dSWarner Losh 
34569acd21dSWarner Losh 	fdc->baseport = devi->isahd.id_iobase;
34669acd21dSWarner Losh 	/*
34769acd21dSWarner Losh 	 * reset controller
34869acd21dSWarner Losh 	 */
349427ccf00SDoug Rabson 	fdout_wr(fdc, 0);
35069acd21dSWarner Losh 	DELAY(100);
351427ccf00SDoug Rabson 	fdout_wr(fdc, FDO_FRST);
35269acd21dSWarner Losh 
35369acd21dSWarner Losh 	/*
35469acd21dSWarner Losh 	 * wire into system
35569acd21dSWarner Losh 	 */
35669acd21dSWarner Losh 	if (yeattach(&devi->isahd) == 0)
35769acd21dSWarner Losh 		return(ENXIO);
35869acd21dSWarner Losh 
35969acd21dSWarner Losh 	return(0);
36069acd21dSWarner Losh }
36169acd21dSWarner Losh 
36269acd21dSWarner Losh /*
36369acd21dSWarner Losh  *	yeunload - unload the driver and clear the table.
36469acd21dSWarner Losh  *	XXX TODO:
36569acd21dSWarner Losh  *	This is usually called when the card is ejected, but
36669acd21dSWarner Losh  *	can be caused by a modunload of a controller driver.
36769acd21dSWarner Losh  *	The idea is to reset the driver's view of the device
36869acd21dSWarner Losh  *	and ensure that any driver entry points such as
36969acd21dSWarner Losh  *	read and write do not hang.
37069acd21dSWarner Losh  */
37169acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi)
37269acd21dSWarner Losh {
37369acd21dSWarner Losh 	if (fd_data[devi->isahd.id_unit].type == NO_TYPE)
37469acd21dSWarner Losh 		return;
37569acd21dSWarner Losh 
37669acd21dSWarner Losh 	/*
37769acd21dSWarner Losh 	 * this prevents Fdopen() and fdstrategy() from attempting
37869acd21dSWarner Losh 	 * to access unloaded controller
37969acd21dSWarner Losh 	 */
38069acd21dSWarner Losh 	fd_data[devi->isahd.id_unit].type = NO_TYPE;
38169acd21dSWarner Losh 
38269acd21dSWarner Losh 	printf("fdc%d: unload\n", devi->isahd.id_unit);
38369acd21dSWarner Losh }
38469acd21dSWarner Losh 
38569acd21dSWarner Losh /*
38669acd21dSWarner Losh  *	yeintr - Shared interrupt called from
38769acd21dSWarner Losh  *	front end of PC-Card handler.
38869acd21dSWarner Losh  */
38969acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi)
39069acd21dSWarner Losh {
39169acd21dSWarner Losh 	fdintr((fdcu_t)devi->isahd.id_unit);
39269acd21dSWarner Losh 	return(1);
39369acd21dSWarner Losh }
39469acd21dSWarner Losh #endif /* NCARD > 0 */
39569acd21dSWarner Losh #endif /* FDC_YE */
39669acd21dSWarner Losh 
39787f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
39887f6c662SJulian Elischer static	d_close_t	fdclose;
39987f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
40087f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
40187f6c662SJulian Elischer 
40287f6c662SJulian Elischer #define CDEV_MAJOR 9
40387f6c662SJulian Elischer #define BDEV_MAJOR 2
404f7ea2f55SJulian Elischer 
4054e2f199eSPoul-Henning Kamp static struct cdevsw fd_cdevsw = {
4064e2f199eSPoul-Henning Kamp 	/* open */	Fdopen,
4074e2f199eSPoul-Henning Kamp 	/* close */	fdclose,
4084e2f199eSPoul-Henning Kamp 	/* read */	physread,
4094e2f199eSPoul-Henning Kamp 	/* write */	physwrite,
4104e2f199eSPoul-Henning Kamp 	/* ioctl */	fdioctl,
4114e2f199eSPoul-Henning Kamp 	/* poll */	nopoll,
4124e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
4134e2f199eSPoul-Henning Kamp 	/* strategy */	fdstrategy,
4144e2f199eSPoul-Henning Kamp 	/* name */	"fd",
4154e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
4164e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
4174e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
4184e2f199eSPoul-Henning Kamp 	/* flags */	D_DISK,
4194e2f199eSPoul-Henning Kamp 	/* bmaj */	BDEV_MAJOR
4204e2f199eSPoul-Henning Kamp };
4214e2f199eSPoul-Henning Kamp 
422dc5df763SJoerg Wunsch static int
4236182fdbdSPeter Wemm fdc_err(struct fdc_data *fdc, const char *s)
424dc5df763SJoerg Wunsch {
4256182fdbdSPeter Wemm 	fdc->fdc_errs++;
42616b04b6aSJoerg Wunsch 	if (s) {
4276182fdbdSPeter Wemm 		if (fdc->fdc_errs < FDC_ERRMAX) {
4286182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
4296182fdbdSPeter Wemm 			printf("%s", s);
4306182fdbdSPeter Wemm 		} else if (fdc->fdc_errs == FDC_ERRMAX) {
4316182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
4326182fdbdSPeter Wemm 			printf("too many errors, not logging any more\n");
4336182fdbdSPeter Wemm 		}
43416b04b6aSJoerg Wunsch 	}
435dc5df763SJoerg Wunsch 
436dc5df763SJoerg Wunsch 	return FD_FAILED;
437dc5df763SJoerg Wunsch }
438dc5df763SJoerg Wunsch 
439dc5df763SJoerg Wunsch /*
440dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
441dc5df763SJoerg Wunsch  * Unit number,
442dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
443dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
444dc5df763SJoerg Wunsch  */
4456f4e0bebSPoul-Henning Kamp static int
4466182fdbdSPeter Wemm fd_cmd(struct fdc_data *fdc, int n_out, ...)
447dc5df763SJoerg Wunsch {
448dc5df763SJoerg Wunsch 	u_char cmd;
449dc5df763SJoerg Wunsch 	int n_in;
450dc5df763SJoerg Wunsch 	int n;
451dc5df763SJoerg Wunsch 	va_list ap;
452dc5df763SJoerg Wunsch 
453dc5df763SJoerg Wunsch 	va_start(ap, n_out);
454dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
455dc5df763SJoerg Wunsch 	va_end(ap);
456dc5df763SJoerg Wunsch 	va_start(ap, n_out);
457dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
458dc5df763SJoerg Wunsch 	{
4596182fdbdSPeter Wemm 		if (out_fdc(fdc, va_arg(ap, int)) < 0)
460dc5df763SJoerg Wunsch 		{
461dc5df763SJoerg Wunsch 			char msg[50];
4622127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
463dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
464dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
4656182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
466dc5df763SJoerg Wunsch 		}
467dc5df763SJoerg Wunsch 	}
468dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
469dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
470dc5df763SJoerg Wunsch 	{
471dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
4726182fdbdSPeter Wemm 		if (fd_in(fdc, ptr) < 0)
473dc5df763SJoerg Wunsch 		{
474dc5df763SJoerg Wunsch 			char msg[50];
4752127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
476dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
477dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
4786182fdbdSPeter Wemm 			return fdc_err(fdc, msg);
479dc5df763SJoerg Wunsch 		}
480dc5df763SJoerg Wunsch 	}
481dc5df763SJoerg Wunsch 
482dc5df763SJoerg Wunsch 	return 0;
483dc5df763SJoerg Wunsch }
484dc5df763SJoerg Wunsch 
4856f4e0bebSPoul-Henning Kamp static int
486d66c374fSTor Egge enable_fifo(fdc_p fdc)
487d66c374fSTor Egge {
488d66c374fSTor Egge 	int i, j;
489d66c374fSTor Egge 
490d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
491d66c374fSTor Egge 
492d66c374fSTor Egge 		/*
493d66c374fSTor Egge 		 * XXX:
494d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
495d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
496d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
497d66c374fSTor Egge 		 */
498d66c374fSTor Egge 
4996182fdbdSPeter Wemm 		if (out_fdc(fdc, I8207X_CONFIGURE) < 0)
5006182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
501d66c374fSTor Egge 
502d66c374fSTor Egge 		/* If command is invalid, return */
503d66c374fSTor Egge 		j = 100000;
504427ccf00SDoug Rabson 		while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM))
505d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
506d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
507d66c374fSTor Egge 				fdc_reset(fdc);
508d66c374fSTor Egge 				return FD_FAILED;
509d66c374fSTor Egge 			}
510d66c374fSTor Egge 		if (j<0 ||
5116182fdbdSPeter Wemm 		    fd_cmd(fdc, 3,
512d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
513d66c374fSTor Egge 			fdc_reset(fdc);
5146182fdbdSPeter Wemm 			return fdc_err(fdc, "Enable FIFO failed\n");
515d66c374fSTor Egge 		}
516d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
517d66c374fSTor Egge 		return 0;
518d66c374fSTor Egge 	}
5196182fdbdSPeter Wemm 	if (fd_cmd(fdc, 4,
520d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
5216182fdbdSPeter Wemm 		return fdc_err(fdc, "Re-enable FIFO failed\n");
522d66c374fSTor Egge 	return 0;
523d66c374fSTor Egge }
524d66c374fSTor Egge 
525d66c374fSTor Egge static int
526dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
527dc5df763SJoerg Wunsch {
528dc5df763SJoerg Wunsch 	int st3;
529dc5df763SJoerg Wunsch 
5306182fdbdSPeter Wemm 	if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
531dc5df763SJoerg Wunsch 	{
5326182fdbdSPeter Wemm 		return fdc_err(fdc, "Sense Drive Status failed\n");
533dc5df763SJoerg Wunsch 	}
534dc5df763SJoerg Wunsch 	if (st3p)
535dc5df763SJoerg Wunsch 		*st3p = st3;
536dc5df763SJoerg Wunsch 
537dc5df763SJoerg Wunsch 	return 0;
538dc5df763SJoerg Wunsch }
539dc5df763SJoerg Wunsch 
5406f4e0bebSPoul-Henning Kamp static int
541dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
542dc5df763SJoerg Wunsch {
5436182fdbdSPeter Wemm 	int cyl, st0, ret;
544dc5df763SJoerg Wunsch 
5456182fdbdSPeter Wemm 	ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0);
5466182fdbdSPeter Wemm 	if (ret) {
5476182fdbdSPeter Wemm 		(void)fdc_err(fdc,
548dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
549dc5df763SJoerg Wunsch 		return ret;
550dc5df763SJoerg Wunsch 	}
551dc5df763SJoerg Wunsch 
552dc5df763SJoerg Wunsch 	if (st0p)
553dc5df763SJoerg Wunsch 		*st0p = st0;
554dc5df763SJoerg Wunsch 
5556182fdbdSPeter Wemm 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) {
556dc5df763SJoerg Wunsch 		/*
557dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
558dc5df763SJoerg Wunsch 		 */
559dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
560dc5df763SJoerg Wunsch 	}
561dc5df763SJoerg Wunsch 
5626182fdbdSPeter Wemm 	if (fd_in(fdc, &cyl) < 0) {
5636182fdbdSPeter Wemm 		return fdc_err(fdc, "can't get cyl num\n");
564dc5df763SJoerg Wunsch 	}
565dc5df763SJoerg Wunsch 
566dc5df763SJoerg Wunsch 	if (cylp)
567dc5df763SJoerg Wunsch 		*cylp = cyl;
568dc5df763SJoerg Wunsch 
569dc5df763SJoerg Wunsch 	return 0;
570dc5df763SJoerg Wunsch }
571dc5df763SJoerg Wunsch 
572dc5df763SJoerg Wunsch 
5736f4e0bebSPoul-Henning Kamp static int
574dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
575dc5df763SJoerg Wunsch {
576dc5df763SJoerg Wunsch 	int i, ret;
577b5e8ce9fSBruce Evans 
5786182fdbdSPeter Wemm 	for (i = 0; i < 7; i++) {
579b5e8ce9fSBruce Evans 		/*
580b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
581a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
582b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
583b5e8ce9fSBruce Evans 		 */
584b5e8ce9fSBruce Evans 		int status;
585b5e8ce9fSBruce Evans 
5866182fdbdSPeter Wemm 		ret = fd_in(fdc, &status);
587b5e8ce9fSBruce Evans 		fdc->status[i] = status;
588b5e8ce9fSBruce Evans 		if (ret != 0)
589dc5df763SJoerg Wunsch 			break;
590dc5df763SJoerg Wunsch 	}
591dc5df763SJoerg Wunsch 
592dc5df763SJoerg Wunsch 	if (ret == 0)
593dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
594dc5df763SJoerg Wunsch 	else
595dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
596dc5df763SJoerg Wunsch 
597dc5df763SJoerg Wunsch 	return ret;
598dc5df763SJoerg Wunsch }
599dc5df763SJoerg Wunsch 
6005b81b6b3SRodney W. Grimes /****************************************************************************/
6015b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
6025b81b6b3SRodney W. Grimes /****************************************************************************/
603dc5df763SJoerg Wunsch 
604427ccf00SDoug Rabson static struct isa_pnp_id fdc_ids[] = {
605427ccf00SDoug Rabson 	{0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */
606427ccf00SDoug Rabson 	{0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */
607427ccf00SDoug Rabson 	{0}
608427ccf00SDoug Rabson };
609427ccf00SDoug Rabson 
610427ccf00SDoug Rabson 
61116e68fc6SPeter Wemm /*
61216e68fc6SPeter Wemm  * fdc controller section.
61316e68fc6SPeter Wemm  */
6143a2f7427SDavid Greenman static int
6156182fdbdSPeter Wemm fdc_probe(device_t dev)
6165b81b6b3SRodney W. Grimes {
617427ccf00SDoug Rabson 	int	error, ispnp, ic_type;
6186182fdbdSPeter Wemm 	struct	fdc_data *fdc;
6195b81b6b3SRodney W. Grimes 
620427ccf00SDoug Rabson 	/* Check pnp ids */
621427ccf00SDoug Rabson 	error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids);
622427ccf00SDoug Rabson 	if (error == ENXIO)
623427ccf00SDoug Rabson 		return ENXIO;
624427ccf00SDoug Rabson 	ispnp = (error == 0);
625a97c75b7SDoug Rabson 
6266182fdbdSPeter Wemm 	fdc = device_get_softc(dev);
6276182fdbdSPeter Wemm 	bzero(fdc, sizeof *fdc);
6286182fdbdSPeter Wemm 	fdc->fdc_dev = dev;
6296182fdbdSPeter Wemm 	fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0;
6306182fdbdSPeter Wemm 	fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0;
6316182fdbdSPeter Wemm 
6326182fdbdSPeter Wemm 	fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
6336182fdbdSPeter Wemm 					     &fdc->rid_ioport, 0ul, ~0ul,
634427ccf00SDoug Rabson 					     ispnp ? 1 : IO_FDCSIZE,
635427ccf00SDoug Rabson 					     RF_ACTIVE);
6366182fdbdSPeter Wemm 	if (fdc->res_ioport == 0) {
637427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve I/O port range\n");
6386182fdbdSPeter Wemm 		error = ENXIO;
6396182fdbdSPeter Wemm 		goto out;
6406182fdbdSPeter Wemm 	}
641427ccf00SDoug Rabson 	fdc->portt = rman_get_bustag(fdc->res_ioport);
642427ccf00SDoug Rabson 	fdc->porth = rman_get_bushandle(fdc->res_ioport);
643427ccf00SDoug Rabson 
644427ccf00SDoug Rabson 	/*
645a2639a18SPeter Wemm 	 * Some bios' report the device at 0x3f2-0x3f5,0x3f7 and some at
646a2639a18SPeter Wemm 	 * 0x3f0-0x3f5,0x3f7. We detect the former by checking the size
647a2639a18SPeter Wemm 	 * and adjust the port address accordingly.
648a2639a18SPeter Wemm 	 *
649a2639a18SPeter Wemm 	 * And some (!!) report 0x3f2-0x3f5 and completely leave out the
650a2639a18SPeter Wemm 	 * control register!  It seems that some non-antique controller chips
651a2639a18SPeter Wemm 	 * have a different method of programming the transfer speed which
652a2639a18SPeter Wemm 	 * doesn't require the control register, but it's mighty bogus as the
653a2639a18SPeter Wemm 	 * chip still responds to the address for the control register.
654a2639a18SPeter Wemm 	 * This hack is truely evil as we use the 6th port in a 4-port chunk.
655427ccf00SDoug Rabson 	 */
656a2639a18SPeter Wemm 	/* 0x3f2-0x3f5 */
657a2639a18SPeter Wemm 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4 &&
658a2639a18SPeter Wemm 	    bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
659a2639a18SPeter Wemm 		fdc->port_off = -2;
660a2639a18SPeter Wemm 		ispnp = 0;	/* hack, don't reserve second port chunk */
661a2639a18SPeter Wemm 	}
662a2639a18SPeter Wemm 	/* 0x3f0-0x3f5 */
663a2639a18SPeter Wemm 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 6 &&
664a2639a18SPeter Wemm 	    bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) {
665a2639a18SPeter Wemm 		ispnp = 0;	/* hack, don't reserve second port chunk */
666a2639a18SPeter Wemm 	}
667a2639a18SPeter Wemm 	if (ispnp) {
668427ccf00SDoug Rabson 		if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4)
669427ccf00SDoug Rabson 			fdc->port_off = -2;
670427ccf00SDoug Rabson 		fdc->flags |= FDC_ISPNP;
671427ccf00SDoug Rabson 		fdc->rid_ctl = 1;
672427ccf00SDoug Rabson 		fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT,
673427ccf00SDoug Rabson 						  &fdc->rid_ctl, 0ul, ~0ul,
674427ccf00SDoug Rabson 						  1, RF_ACTIVE);
675427ccf00SDoug Rabson 		if (fdc->res_ctl == 0) {
676427ccf00SDoug Rabson 			device_printf(dev, "cannot reserve I/O port range\n");
677427ccf00SDoug Rabson 			error = ENXIO;
678427ccf00SDoug Rabson 			goto out;
679427ccf00SDoug Rabson 		}
680427ccf00SDoug Rabson 		fdc->ctlt = rman_get_bustag(fdc->res_ctl);
681427ccf00SDoug Rabson 		fdc->ctlh = rman_get_bushandle(fdc->res_ctl);
682427ccf00SDoug Rabson 	}
6836182fdbdSPeter Wemm 
6846182fdbdSPeter Wemm 	fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
6856182fdbdSPeter Wemm 					  &fdc->rid_irq, 0ul, ~0ul, 1,
6866182fdbdSPeter Wemm 					  RF_ACTIVE);
6876182fdbdSPeter Wemm 	if (fdc->res_irq == 0) {
688427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve interrupt line\n");
6896182fdbdSPeter Wemm 		error = ENXIO;
6906182fdbdSPeter Wemm 		goto out;
6916182fdbdSPeter Wemm 	}
6926182fdbdSPeter Wemm 	fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ,
6936182fdbdSPeter Wemm 					  &fdc->rid_drq, 0ul, ~0ul, 1,
6946182fdbdSPeter Wemm 					  RF_ACTIVE);
6956182fdbdSPeter Wemm 	if (fdc->res_drq == 0) {
696427ccf00SDoug Rabson 		device_printf(dev, "cannot reserve DMA request line\n");
6976182fdbdSPeter Wemm 		error = ENXIO;
6986182fdbdSPeter Wemm 		goto out;
6996182fdbdSPeter Wemm 	}
7006182fdbdSPeter Wemm 	fdc->dmachan = fdc->res_drq->r_start;
701566643e3SDoug Rabson 	error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq,
702566643e3SDoug Rabson 			       INTR_TYPE_BIO, fdc_intr, fdc, &fdc->fdc_intr);
7035b81b6b3SRodney W. Grimes 
70416111cedSAndrew Moore 	/* First - lets reset the floppy controller */
705427ccf00SDoug Rabson 	fdout_wr(fdc, 0);
70616111cedSAndrew Moore 	DELAY(100);
707427ccf00SDoug Rabson 	fdout_wr(fdc, FDO_FRST);
70816111cedSAndrew Moore 
7095b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
7106182fdbdSPeter Wemm 	if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240),
7116182fdbdSPeter Wemm 		   NE7_SPEC_2(2, 0), 0)) {
7126182fdbdSPeter Wemm 		error = ENXIO;
7136182fdbdSPeter Wemm 		goto out;
7145b81b6b3SRodney W. Grimes 	}
7156182fdbdSPeter Wemm 
7166182fdbdSPeter Wemm 	if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) {
7176182fdbdSPeter Wemm 		ic_type = (u_char)ic_type;
7186182fdbdSPeter Wemm 		switch (ic_type) {
7196182fdbdSPeter Wemm 		case 0x80:
7206182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 765 or clone");
7216182fdbdSPeter Wemm 			fdc->fdct = FDC_NE765;
7226182fdbdSPeter Wemm 			break;
7236182fdbdSPeter Wemm 		case 0x81:
7246182fdbdSPeter Wemm 			device_set_desc(dev, "Intel 82077 or clone");
7256182fdbdSPeter Wemm 			fdc->fdct = FDC_I82077;
7266182fdbdSPeter Wemm 			break;
7276182fdbdSPeter Wemm 		case 0x90:
7286182fdbdSPeter Wemm 			device_set_desc(dev, "NEC 72065B or clone");
7296182fdbdSPeter Wemm 			fdc->fdct = FDC_NE72065;
7306182fdbdSPeter Wemm 			break;
7316182fdbdSPeter Wemm 		default:
7326182fdbdSPeter Wemm 			device_set_desc(dev, "generic floppy controller");
7336182fdbdSPeter Wemm 			fdc->fdct = FDC_UNKNOWN;
7346182fdbdSPeter Wemm 			break;
7356182fdbdSPeter Wemm 		}
7366182fdbdSPeter Wemm 	}
7376182fdbdSPeter Wemm 
73869acd21dSWarner Losh #ifdef FDC_YE
73969acd21dSWarner Losh 	/*
74069acd21dSWarner Losh 	 * don't succeed on probe; wait
74169acd21dSWarner Losh 	 * for PCCARD subsystem to do it
74269acd21dSWarner Losh 	 */
743e34c71eaSJoerg Wunsch 	if (device_get_flags(fdc->fdc_dev) & FDC_IS_PCMCIA)
74469acd21dSWarner Losh 		return(0);
74569acd21dSWarner Losh #endif
7466182fdbdSPeter Wemm 	return (0);
7476182fdbdSPeter Wemm 
7486182fdbdSPeter Wemm out:
7496182fdbdSPeter Wemm 	if (fdc->fdc_intr)
7506182fdbdSPeter Wemm 		BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq,
7516182fdbdSPeter Wemm 				  fdc->fdc_intr);
7526182fdbdSPeter Wemm 	if (fdc->res_irq != 0) {
7536182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
7546182fdbdSPeter Wemm 					fdc->res_irq);
7556182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq,
7566182fdbdSPeter Wemm 				     fdc->res_irq);
7576182fdbdSPeter Wemm 	}
758427ccf00SDoug Rabson 	if (fdc->res_ctl != 0) {
759427ccf00SDoug Rabson 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
760427ccf00SDoug Rabson 					fdc->res_ctl);
761427ccf00SDoug Rabson 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl,
762427ccf00SDoug Rabson 				     fdc->res_ctl);
763427ccf00SDoug Rabson 	}
7646182fdbdSPeter Wemm 	if (fdc->res_ioport != 0) {
7656182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
7666182fdbdSPeter Wemm 					fdc->res_ioport);
7676182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport,
7686182fdbdSPeter Wemm 				     fdc->res_ioport);
7696182fdbdSPeter Wemm 	}
7706182fdbdSPeter Wemm 	if (fdc->res_drq != 0) {
7716182fdbdSPeter Wemm 		bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
7726182fdbdSPeter Wemm 					fdc->res_drq);
7736182fdbdSPeter Wemm 		bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq,
7746182fdbdSPeter Wemm 				     fdc->res_drq);
7756182fdbdSPeter Wemm 	}
7766182fdbdSPeter Wemm 	return (error);
7775b81b6b3SRodney W. Grimes }
7785b81b6b3SRodney W. Grimes 
7795b81b6b3SRodney W. Grimes /*
7806182fdbdSPeter Wemm  * Aped dfr@freebsd.org's isa_add_device().
7815b81b6b3SRodney W. Grimes  */
7826182fdbdSPeter Wemm static void
7836182fdbdSPeter Wemm fdc_add_device(device_t dev, const char *name, int unit)
7845b81b6b3SRodney W. Grimes {
7856182fdbdSPeter Wemm 	int	disabled, *ivar;
7866182fdbdSPeter Wemm 	device_t child;
78792200632SGarrett Wollman 
7886182fdbdSPeter Wemm 	ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT);
7896182fdbdSPeter Wemm 	if (ivar == 0)
7906182fdbdSPeter Wemm 		return;
791a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "drive", ivar) != 0)
7926182fdbdSPeter Wemm 		*ivar = 0;
793fe0d4089SMatthew N. Dodd 	child = device_add_child(dev, name, unit);
794fe0d4089SMatthew N. Dodd 	device_set_ivars(child, ivar);
7956182fdbdSPeter Wemm 	if (child == 0)
7966182fdbdSPeter Wemm 		return;
797a97c75b7SDoug Rabson 	if (resource_int_value(name, unit, "disabled", &disabled) == 0
798a97c75b7SDoug Rabson 	    && disabled != 0)
7996182fdbdSPeter Wemm 		device_disable(child);
8006182fdbdSPeter Wemm }
8016182fdbdSPeter Wemm 
8026182fdbdSPeter Wemm static int
8036182fdbdSPeter Wemm fdc_attach(device_t dev)
8046182fdbdSPeter Wemm {
8056182fdbdSPeter Wemm 	struct	fdc_data *fdc = device_get_softc(dev);
8066182fdbdSPeter Wemm 	fdcu_t	fdcu = device_get_unit(dev);
807427ccf00SDoug Rabson 	int	i;
808427ccf00SDoug Rabson 
809427ccf00SDoug Rabson 	for (i = resource_query_string(-1, "at", device_get_nameunit(dev));
810427ccf00SDoug Rabson 	     i != -1;
811427ccf00SDoug Rabson 	     i = resource_query_string(i, "at", device_get_nameunit(dev)))
812427ccf00SDoug Rabson 		fdc_add_device(dev, resource_query_name(i),
813427ccf00SDoug Rabson 			       resource_query_unit(i));
8146182fdbdSPeter Wemm 
8155b81b6b3SRodney W. Grimes 	fdc->fdcu = fdcu;
8165b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
8176182fdbdSPeter Wemm 
818100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
8196182fdbdSPeter Wemm 				/* XXX should integrate with rman */
820100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
821dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
8225b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
8236182fdbdSPeter Wemm 
8243a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
825427ccf00SDoug Rabson 	fdout_wr(fdc, ((fdc->fdout = 0)));
82602a19910SJustin T. Gibbs 	bufq_init(&fdc->head);
8275b81b6b3SRodney W. Grimes 
8286182fdbdSPeter Wemm 	/*
8296182fdbdSPeter Wemm 	 * Probe and attach any children as were configured above.
8306182fdbdSPeter Wemm 	 */
8316182fdbdSPeter Wemm 	return (bus_generic_attach(dev));
8326182fdbdSPeter Wemm }
8336182fdbdSPeter Wemm 
83415317dd8SMatthew N. Dodd static int
8356182fdbdSPeter Wemm fdc_print_child(device_t me, device_t child)
8366182fdbdSPeter Wemm {
83715317dd8SMatthew N. Dodd 	int retval = 0;
83815317dd8SMatthew N. Dodd 
83915317dd8SMatthew N. Dodd 	retval += bus_print_child_header(me, child);
84015317dd8SMatthew N. Dodd 	retval += printf(" on %s drive %d\n", device_get_nameunit(me),
8416182fdbdSPeter Wemm 	       *(int *)device_get_ivars(child));
84215317dd8SMatthew N. Dodd 
84315317dd8SMatthew N. Dodd 	return (retval);
8446182fdbdSPeter Wemm }
8456182fdbdSPeter Wemm 
84616e68fc6SPeter Wemm static device_method_t fdc_methods[] = {
84716e68fc6SPeter Wemm 	/* Device interface */
84816e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fdc_probe),
84916e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fdc_attach),
85016e68fc6SPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
85116e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
85216e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
85316e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
85416e68fc6SPeter Wemm 
85516e68fc6SPeter Wemm 	/* Bus interface */
85616e68fc6SPeter Wemm 	DEVMETHOD(bus_print_child,	fdc_print_child),
85716e68fc6SPeter Wemm 	/* Our children never use any other bus interface methods. */
85816e68fc6SPeter Wemm 
85916e68fc6SPeter Wemm 	{ 0, 0 }
86016e68fc6SPeter Wemm };
86116e68fc6SPeter Wemm 
86216e68fc6SPeter Wemm static driver_t fdc_driver = {
86316e68fc6SPeter Wemm 	"fdc",
86416e68fc6SPeter Wemm 	fdc_methods,
86516e68fc6SPeter Wemm 	sizeof(struct fdc_data)
86616e68fc6SPeter Wemm };
86716e68fc6SPeter Wemm 
86816e68fc6SPeter Wemm DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0);
86916e68fc6SPeter Wemm 
87016e68fc6SPeter Wemm /******************************************************************/
87116e68fc6SPeter Wemm /*
87216e68fc6SPeter Wemm  * devices attached to the controller section.
87316e68fc6SPeter Wemm  */
8746182fdbdSPeter Wemm static int
8756182fdbdSPeter Wemm fd_probe(device_t dev)
8766182fdbdSPeter Wemm {
8776182fdbdSPeter Wemm 	int	i;
8786182fdbdSPeter Wemm 	u_int	fdt, st0, st3;
8796182fdbdSPeter Wemm 	struct	fd_data *fd;
8806182fdbdSPeter Wemm 	struct	fdc_data *fdc;
8816182fdbdSPeter Wemm 	fdsu_t	fdsu;
8826182fdbdSPeter Wemm 	static int fd_fifo = 0;
8836182fdbdSPeter Wemm 
8846182fdbdSPeter Wemm 	fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
8856182fdbdSPeter Wemm 	fd = device_get_softc(dev);
8866182fdbdSPeter Wemm 	fdc = device_get_softc(device_get_parent(dev));
8876182fdbdSPeter Wemm 
8886182fdbdSPeter Wemm 	bzero(fd, sizeof *fd);
8896182fdbdSPeter Wemm 	fd->dev = dev;
8906182fdbdSPeter Wemm 	fd->fdc = fdc;
8916182fdbdSPeter Wemm 	fd->fdsu = fdsu;
8926182fdbdSPeter Wemm 	fd->fdu = device_get_unit(dev);
8936182fdbdSPeter Wemm 
894a97c75b7SDoug Rabson #ifdef __i386__
895b99f0a4aSAndrew Moore 	/* look up what bios thinks we have */
8966182fdbdSPeter Wemm 	switch (fd->fdu) {
8976182fdbdSPeter Wemm 	case 0:
898062acdb7SDoug Rabson 		if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
8990722d6abSJoerg Wunsch 			fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
9000722d6abSJoerg Wunsch 		else
9010722d6abSJoerg Wunsch 			fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
902b99f0a4aSAndrew Moore 		break;
9036182fdbdSPeter Wemm 	case 1:
9046182fdbdSPeter Wemm 		fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
905dc5df763SJoerg Wunsch 		break;
906dc5df763SJoerg Wunsch 	default:
9076182fdbdSPeter Wemm 		fdt = RTCFDT_NONE;
908dc5df763SJoerg Wunsch 		break;
9096b7bd95bSJoerg Wunsch 	}
910a97c75b7SDoug Rabson #else
911a97c75b7SDoug Rabson 	fdt = RTCFDT_144M;	/* XXX probably */
912a97c75b7SDoug Rabson #endif
9136182fdbdSPeter Wemm 
9146182fdbdSPeter Wemm 	/* is there a unit? */
9156182fdbdSPeter Wemm 	if (fdt == RTCFDT_NONE)
9166182fdbdSPeter Wemm 		return (ENXIO);
9176182fdbdSPeter Wemm 
9186182fdbdSPeter Wemm 	/* select it */
9196182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNON);
9206182fdbdSPeter Wemm 	DELAY(1000000);	/* 1 sec */
9216182fdbdSPeter Wemm 
9228de0675cSPeter Wemm 	/* XXX This doesn't work before the first set_motor() */
9236182fdbdSPeter Wemm 	if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
924e34c71eaSJoerg Wunsch 	    && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0
9256182fdbdSPeter Wemm 	    && enable_fifo(fdc) == 0) {
9266182fdbdSPeter Wemm 		device_print_prettyname(device_get_parent(dev));
9276182fdbdSPeter Wemm 		printf("FIFO enabled, %d bytes threshold\n", fifo_threshold);
928d66c374fSTor Egge 	}
9296182fdbdSPeter Wemm 	fd_fifo = 1;
9306182fdbdSPeter Wemm 
9316182fdbdSPeter Wemm 	if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
9326182fdbdSPeter Wemm 	    && (st3 & NE7_ST3_T0)) {
933dc5df763SJoerg Wunsch 		/* if at track 0, first seek inwards */
934dc5df763SJoerg Wunsch 		/* seek some steps: */
9356182fdbdSPeter Wemm 		fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
936dc5df763SJoerg Wunsch 		DELAY(300000); /* ...wait a moment... */
9376182fdbdSPeter Wemm 		fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
938dc5df763SJoerg Wunsch 	}
939dc5df763SJoerg Wunsch 
940dc5df763SJoerg Wunsch 	/* If we're at track 0 first seek inwards. */
9416182fdbdSPeter Wemm 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
942dc5df763SJoerg Wunsch 		/* Seek some steps... */
9436182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
944dc5df763SJoerg Wunsch 			/* ...wait a moment... */
945dc5df763SJoerg Wunsch 			DELAY(300000);
946dc5df763SJoerg Wunsch 			/* make ctrlr happy: */
9476182fdbdSPeter Wemm 			fd_sense_int(fdc, 0, 0);
948dc5df763SJoerg Wunsch 		}
949dc5df763SJoerg Wunsch 	}
950dc5df763SJoerg Wunsch 
9516b7bd95bSJoerg Wunsch 	for (i = 0; i < 2; i++) {
9526b7bd95bSJoerg Wunsch 		/*
9536b7bd95bSJoerg Wunsch 		 * we must recalibrate twice, just in case the
9546b7bd95bSJoerg Wunsch 		 * heads have been beyond cylinder 76, since most
9556b7bd95bSJoerg Wunsch 		 * FDCs still barf when attempting to recalibrate
9566b7bd95bSJoerg Wunsch 		 * more than 77 steps
9576b7bd95bSJoerg Wunsch 		 */
958dc5df763SJoerg Wunsch 		/* go back to 0: */
9596182fdbdSPeter Wemm 		if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
9606b7bd95bSJoerg Wunsch 			/* a second being enough for full stroke seek*/
9616b7bd95bSJoerg Wunsch 			DELAY(i == 0 ? 1000000 : 300000);
9625b81b6b3SRodney W. Grimes 
9636b7bd95bSJoerg Wunsch 			/* anything responding? */
964dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
965dc5df763SJoerg Wunsch 			    (st0 & NE7_ST0_EC) == 0)
9666b7bd95bSJoerg Wunsch 				break; /* already probed succesfully */
9676b7bd95bSJoerg Wunsch 		}
968dc5df763SJoerg Wunsch 	}
9696b7bd95bSJoerg Wunsch 
9706182fdbdSPeter Wemm 	set_motor(fdc, fdsu, TURNOFF);
9713a2f7427SDavid Greenman 
9723a2f7427SDavid Greenman 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
9736182fdbdSPeter Wemm 		return (ENXIO);
9745b81b6b3SRodney W. Grimes 
975dc5df763SJoerg Wunsch 	fd->track = FD_NO_TRACK;
976b99f0a4aSAndrew Moore 	fd->fdc = fdc;
977b99f0a4aSAndrew Moore 	fd->fdsu = fdsu;
9783a2f7427SDavid Greenman 	fd->options = 0;
97902a19910SJustin T. Gibbs 	callout_handle_init(&fd->toffhandle);
98002a19910SJustin T. Gibbs 	callout_handle_init(&fd->tohandle);
9815b81b6b3SRodney W. Grimes 
982b99f0a4aSAndrew Moore 	switch (fdt) {
9837ca0641bSAndrey A. Chernov 	case RTCFDT_12M:
9846182fdbdSPeter Wemm 		device_set_desc(dev, "1200-KB 5.25\" drive");
985b99f0a4aSAndrew Moore 		fd->type = FD_1200;
9867ca0641bSAndrey A. Chernov 		break;
9870722d6abSJoerg Wunsch 	case RTCFDT_144M | RTCFDT_144M_PRETENDED:
9886182fdbdSPeter Wemm 		device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
9890722d6abSJoerg Wunsch 		fdt = RTCFDT_144M;
9906182fdbdSPeter Wemm 		fd->type = FD_1440;
9917ca0641bSAndrey A. Chernov 	case RTCFDT_144M:
9926182fdbdSPeter Wemm 		device_set_desc(dev, "1440-KB 3.5\" drive");
993b99f0a4aSAndrew Moore 		fd->type = FD_1440;
9947ca0641bSAndrey A. Chernov 		break;
995290dd077SJoerg Wunsch 	case RTCFDT_288M:
99686a727d9SJoerg Wunsch 	case RTCFDT_288M_1:
9976182fdbdSPeter Wemm 		device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
998290dd077SJoerg Wunsch 		fd->type = FD_1440;
999290dd077SJoerg Wunsch 		break;
10007ca0641bSAndrey A. Chernov 	case RTCFDT_360K:
10016182fdbdSPeter Wemm 		device_set_desc(dev, "360-KB 5.25\" drive");
1002b99f0a4aSAndrew Moore 		fd->type = FD_360;
10037ca0641bSAndrey A. Chernov 		break;
1004ed2fa05eSAndrey A. Chernov 	case RTCFDT_720K:
10056182fdbdSPeter Wemm 		printf("720-KB 3.5\" drive");
1006b99f0a4aSAndrew Moore 		fd->type = FD_720;
1007ed2fa05eSAndrey A. Chernov 		break;
10087ca0641bSAndrey A. Chernov 	default:
10096182fdbdSPeter Wemm 		return (ENXIO);
10105b81b6b3SRodney W. Grimes 	}
10116182fdbdSPeter Wemm 	return (0);
10126182fdbdSPeter Wemm }
10136182fdbdSPeter Wemm 
10146182fdbdSPeter Wemm static int
10156182fdbdSPeter Wemm fd_attach(device_t dev)
10166182fdbdSPeter Wemm {
10176182fdbdSPeter Wemm 	struct	fd_data *fd;
1018ada9bd8cSJulian Elischer #if 0
1019ada9bd8cSJulian Elischer 	int	i;
1020ada9bd8cSJulian Elischer 	int	mynor;
1021ada9bd8cSJulian Elischer 	int	typemynor;
1022ada9bd8cSJulian Elischer 	int	typesize;
1023ada9bd8cSJulian Elischer #endif
10246182fdbdSPeter Wemm 
10256182fdbdSPeter Wemm 	fd = device_get_softc(dev);
10266182fdbdSPeter Wemm 
1027475ad603SPeter Wemm 	cdevsw_add(&fd_cdevsw);	/* XXX */
1028ada9bd8cSJulian Elischer 	make_dev(&fd_cdevsw, (fd->fdu << 6),
1029ada9bd8cSJulian Elischer 		UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fd->fdu);
1030ada9bd8cSJulian Elischer 
1031ada9bd8cSJulian Elischer #if 0
1032ada9bd8cSJulian Elischer 	/* Other make_dev() go here. */
1033ada9bd8cSJulian Elischer #endif
1034ada9bd8cSJulian Elischer 
1035671e2ceeSBruce Evans 	/*
1036b2dfb1f9SJustin T. Gibbs 	 * Export the drive to the devstat interface.
1037671e2ceeSBruce Evans 	 */
10386182fdbdSPeter Wemm 	devstat_add_entry(&fd->device_stats, device_get_name(dev),
10396182fdbdSPeter Wemm 			  device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS,
10402a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
10412a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_FD);
10426182fdbdSPeter Wemm 	return (0);
10435b81b6b3SRodney W. Grimes }
10445b81b6b3SRodney W. Grimes 
104516e68fc6SPeter Wemm static device_method_t fd_methods[] = {
104616e68fc6SPeter Wemm 	/* Device interface */
104716e68fc6SPeter Wemm 	DEVMETHOD(device_probe,		fd_probe),
104816e68fc6SPeter Wemm 	DEVMETHOD(device_attach,	fd_attach),
104916e68fc6SPeter Wemm 	DEVMETHOD(device_detach,	bus_generic_detach),
105016e68fc6SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
105116e68fc6SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend), /* XXX */
105216e68fc6SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume), /* XXX */
105316e68fc6SPeter Wemm 
105416e68fc6SPeter Wemm 	{ 0, 0 }
105516e68fc6SPeter Wemm };
105616e68fc6SPeter Wemm 
105716e68fc6SPeter Wemm static driver_t fd_driver = {
105816e68fc6SPeter Wemm 	"fd",
105916e68fc6SPeter Wemm 	fd_methods,
106016e68fc6SPeter Wemm 	sizeof(struct fd_data)
106116e68fc6SPeter Wemm };
106216e68fc6SPeter Wemm 
1063475ad603SPeter Wemm DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0);
106416e68fc6SPeter Wemm 
106516e68fc6SPeter Wemm /******************************************************************/
106616e68fc6SPeter Wemm 
106769acd21dSWarner Losh #ifdef FDC_YE
106869acd21dSWarner Losh /*
106969acd21dSWarner Losh  * this is a subset of fdattach() optimized for the Y-E Data
107069acd21dSWarner Losh  * PCMCIA floppy drive.
107169acd21dSWarner Losh  */
107269acd21dSWarner Losh static int yeattach(struct isa_device *dev)
107369acd21dSWarner Losh {
107469acd21dSWarner Losh 	fdcu_t  fdcu = dev->id_unit;
107569acd21dSWarner Losh 	fdc_p   fdc = fdc_data + fdcu;
107669acd21dSWarner Losh 	fdsu_t  fdsu = 0;               /* assume 1 drive per YE controller */
107769acd21dSWarner Losh 	fdu_t   fdu;
107869acd21dSWarner Losh 	fd_p    fd;
107969acd21dSWarner Losh 	int     st0, st3, i;
108069acd21dSWarner Losh 	fdc->fdcu = fdcu;
108169acd21dSWarner Losh 	/*
108269acd21dSWarner Losh 	 * the FDC_PCMCIA flag is used to to indicate special PIO is used
108369acd21dSWarner Losh 	 * instead of DMA
108469acd21dSWarner Losh 	 */
108569acd21dSWarner Losh 	fdc->flags = FDC_ATTACHED|FDC_PCMCIA;
108669acd21dSWarner Losh 	fdc->state = DEVIDLE;
108769acd21dSWarner Losh 	/* reset controller, turn motor off, clear fdout mirror reg */
1088427ccf00SDoug Rabson 	fdout_wr(fdc, ((fdc->fdout = 0)));
108969acd21dSWarner Losh 	bufq_init(&fdc->head);
109069acd21dSWarner Losh 	/*
109169acd21dSWarner Losh 	 * assume 2 drives/ "normal" controller
109269acd21dSWarner Losh 	 */
109369acd21dSWarner Losh 	fdu = fdcu * 2;
109469acd21dSWarner Losh 	if (fdu >= NFD) {
109569acd21dSWarner Losh 		printf("fdu %d >= NFD\n",fdu);
109669acd21dSWarner Losh 		return(0);
109769acd21dSWarner Losh 	};
109869acd21dSWarner Losh 	fd = &fd_data[fdu];
109969acd21dSWarner Losh 
110069acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNON);
110169acd21dSWarner Losh 	DELAY(1000000); /* 1 sec */
110269acd21dSWarner Losh 	fdc->fdct = FDC_NE765;
110369acd21dSWarner Losh 
110469acd21dSWarner Losh 	if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
110569acd21dSWarner Losh 		(st3 & NE7_ST3_T0)) {
110669acd21dSWarner Losh 		/* if at track 0, first seek inwards */
110769acd21dSWarner Losh 		/* seek some steps: */
110869acd21dSWarner Losh 		(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
110969acd21dSWarner Losh 		DELAY(300000); /* ...wait a moment... */
111069acd21dSWarner Losh 		(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
111169acd21dSWarner Losh 	}
111269acd21dSWarner Losh 
111369acd21dSWarner Losh 	/* If we're at track 0 first seek inwards. */
111469acd21dSWarner Losh 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
111569acd21dSWarner Losh 		/* Seek some steps... */
111669acd21dSWarner Losh 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
111769acd21dSWarner Losh 			/* ...wait a moment... */
111869acd21dSWarner Losh 			DELAY(300000);
111969acd21dSWarner Losh 			/* make ctrlr happy: */
112069acd21dSWarner Losh 			(void)fd_sense_int(fdc, 0, 0);
112169acd21dSWarner Losh 		}
112269acd21dSWarner Losh 	}
112369acd21dSWarner Losh 
112469acd21dSWarner Losh 	for(i = 0; i < 2; i++) {
112569acd21dSWarner Losh 		/*
112669acd21dSWarner Losh 		 * we must recalibrate twice, just in case the
112769acd21dSWarner Losh 		 * heads have been beyond cylinder 76, since most
112869acd21dSWarner Losh 		 * FDCs still barf when attempting to recalibrate
112969acd21dSWarner Losh 		 * more than 77 steps
113069acd21dSWarner Losh 		 */
113169acd21dSWarner Losh 		/* go back to 0: */
113269acd21dSWarner Losh 		if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
113369acd21dSWarner Losh 			/* a second being enough for full stroke seek*/
113469acd21dSWarner Losh 			DELAY(i == 0? 1000000: 300000);
113569acd21dSWarner Losh 
113669acd21dSWarner Losh 			/* anything responding? */
113769acd21dSWarner Losh 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
113869acd21dSWarner Losh 				(st0 & NE7_ST0_EC) == 0)
113969acd21dSWarner Losh 				break; /* already probed succesfully */
114069acd21dSWarner Losh 		}
114169acd21dSWarner Losh 	}
114269acd21dSWarner Losh 
114369acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNOFF);
114469acd21dSWarner Losh 
114569acd21dSWarner Losh 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
114669acd21dSWarner Losh 		return(0);
114769acd21dSWarner Losh 
114869acd21dSWarner Losh 	fd->track = FD_NO_TRACK;
114969acd21dSWarner Losh 	fd->fdc = fdc;
115069acd21dSWarner Losh 	fd->fdsu = fdsu;
115169acd21dSWarner Losh 	fd->options = 0;
115269acd21dSWarner Losh 	printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu);
115369acd21dSWarner Losh 	fd->type = FD_1440;
115469acd21dSWarner Losh 
115569acd21dSWarner Losh 	return (1);
115669acd21dSWarner Losh }
115769acd21dSWarner Losh #endif
115869acd21dSWarner Losh 
11595b81b6b3SRodney W. Grimes /****************************************************************************/
11605b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
11615e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
11625b81b6b3SRodney W. Grimes /****************************************************************************/
11633a2f7427SDavid Greenman static void
11646182fdbdSPeter Wemm set_motor(struct fdc_data *fdc, int fdsu, int turnon)
11655b81b6b3SRodney W. Grimes {
11666182fdbdSPeter Wemm 	int fdout = fdc->fdout;
11673a2f7427SDavid Greenman 	int needspecify = 0;
11683a2f7427SDavid Greenman 
11693a2f7427SDavid Greenman 	if(turnon) {
11703a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
11713a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
11723a2f7427SDavid Greenman 	} else
11733a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
11743a2f7427SDavid Greenman 
11755e235068SJordan K. Hubbard 	if(!turnon
11765e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
11775e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
11785e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
11795e235068SJordan K. Hubbard 	else {
11803a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
11813a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
11823a2f7427SDavid Greenman 			needspecify = 1;
11833a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
11845b81b6b3SRodney W. Grimes 	}
11855b81b6b3SRodney W. Grimes 
1186427ccf00SDoug Rabson 	fdout_wr(fdc, fdout);
11876182fdbdSPeter Wemm 	fdc->fdout = fdout;
11883a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
11893a2f7427SDavid Greenman 
11903a2f7427SDavid Greenman 	if (needspecify) {
1191dc8603e3SJoerg Wunsch 		/*
1192dc5df763SJoerg Wunsch 		 * XXX
1193dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
1194dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
1195dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
1196dc8603e3SJoerg Wunsch 		 */
11976182fdbdSPeter Wemm 		(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1198dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1199dc5df763SJoerg Wunsch 			     0);
12006182fdbdSPeter Wemm 		if (fdc->flags & FDC_HAS_FIFO)
12016182fdbdSPeter Wemm 			(void) enable_fifo(fdc);
12023a2f7427SDavid Greenman 	}
12033a2f7427SDavid Greenman }
12043a2f7427SDavid Greenman 
1205381fe1aaSGarrett Wollman static void
12066182fdbdSPeter Wemm fd_turnoff(void *xfd)
12075b81b6b3SRodney W. Grimes {
1208f5f7ba03SJordan K. Hubbard 	int	s;
12096182fdbdSPeter Wemm 	fd_p fd = xfd;
1210dc16046fSJoerg Wunsch 
12116182fdbdSPeter Wemm 	TRACE1("[fd%d: turnoff]", fd->fdu);
12128335c1b8SBruce Evans 
12138335c1b8SBruce Evans 	/*
12148335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
12158335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
12168335c1b8SBruce Evans 	 * and nothing is queued on it.
12178335c1b8SBruce Evans 	 */
12186182fdbdSPeter Wemm 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) {
12196182fdbdSPeter Wemm 		fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
12208335c1b8SBruce Evans 		return;
12218335c1b8SBruce Evans 	}
12228335c1b8SBruce Evans 
1223f5f7ba03SJordan K. Hubbard 	s = splbio();
12245b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
12256182fdbdSPeter Wemm 	set_motor(fd->fdc, fd->fdsu, TURNOFF);
1226f5f7ba03SJordan K. Hubbard 	splx(s);
12275b81b6b3SRodney W. Grimes }
12285b81b6b3SRodney W. Grimes 
12293a2f7427SDavid Greenman static void
12306182fdbdSPeter Wemm fd_motor_on(void *xfd)
12315b81b6b3SRodney W. Grimes {
1232f5f7ba03SJordan K. Hubbard 	int	s;
12336182fdbdSPeter Wemm 	fd_p fd = xfd;
1234f5f7ba03SJordan K. Hubbard 
1235f5f7ba03SJordan K. Hubbard 	s = splbio();
12365b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
12375b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
12385b81b6b3SRodney W. Grimes 	{
12396182fdbdSPeter Wemm 		fdc_intr(fd->fdc);
12405b81b6b3SRodney W. Grimes 	}
1241f5f7ba03SJordan K. Hubbard 	splx(s);
12425b81b6b3SRodney W. Grimes }
12435b81b6b3SRodney W. Grimes 
12443a2f7427SDavid Greenman static void
12456182fdbdSPeter Wemm fd_turnon(fd_p fd)
12465b81b6b3SRodney W. Grimes {
12475b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
12485b81b6b3SRodney W. Grimes 	{
12493a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
12506182fdbdSPeter Wemm 		set_motor(fd->fdc, fd->fdsu, TURNON);
12516182fdbdSPeter Wemm 		timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */
12525b81b6b3SRodney W. Grimes 	}
12535b81b6b3SRodney W. Grimes }
12545b81b6b3SRodney W. Grimes 
1255381fe1aaSGarrett Wollman static void
1256dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
12575b81b6b3SRodney W. Grimes {
12583a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
1259427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
12603a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
12613a2f7427SDavid Greenman 	DELAY(100);
12623a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
1263427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN);
12643a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
12653a2f7427SDavid Greenman 	DELAY(100);
1266427ccf00SDoug Rabson 	fdout_wr(fdc, fdc->fdout);
12673a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
12683a2f7427SDavid Greenman 
1269dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
12706182fdbdSPeter Wemm 	(void)fd_cmd(fdc, 3, NE7CMD_SPECIFY,
1271dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1272dc5df763SJoerg Wunsch 		     0);
1273d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1274d66c374fSTor Egge 		(void) enable_fifo(fdc);
12755b81b6b3SRodney W. Grimes }
12765b81b6b3SRodney W. Grimes 
12775b81b6b3SRodney W. Grimes /****************************************************************************/
12785b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
12795b81b6b3SRodney W. Grimes /****************************************************************************/
12805b81b6b3SRodney W. Grimes int
12816182fdbdSPeter Wemm in_fdc(struct fdc_data *fdc)
12825b81b6b3SRodney W. Grimes {
12835b81b6b3SRodney W. Grimes 	int i, j = 100000;
1284427ccf00SDoug Rabson 	while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM))
12855b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1286dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
12876182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
12885b81b6b3SRodney W. Grimes 	if (j <= 0)
12896182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1290d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1291427ccf00SDoug Rabson 	i = fddata_rd(fdc);
12923a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
12935b81b6b3SRodney W. Grimes 	return(i);
1294d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1295427ccf00SDoug Rabson 	return fddata_rd(fdc);
1296d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
12975b81b6b3SRodney W. Grimes }
12985b81b6b3SRodney W. Grimes 
1299dc5df763SJoerg Wunsch /*
1300dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
1301dc5df763SJoerg Wunsch  */
1302b5e8ce9fSBruce Evans static int
13036182fdbdSPeter Wemm fd_in(struct fdc_data *fdc, int *ptr)
1304dc5df763SJoerg Wunsch {
1305dc5df763SJoerg Wunsch 	int i, j = 100000;
1306427ccf00SDoug Rabson 	while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM))
1307dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1308dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
13096182fdbdSPeter Wemm 			return fdc_err(fdc, "ready for output in input\n");
1310dc5df763SJoerg Wunsch 	if (j <= 0)
13116182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "input ready timeout\n": 0);
1312d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1313427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1314dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1315dc5df763SJoerg Wunsch 	*ptr = i;
1316dc5df763SJoerg Wunsch 	return 0;
1317d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1318427ccf00SDoug Rabson 	i = fddata_rd(fdc);
1319dc5df763SJoerg Wunsch 	if (ptr)
1320dc5df763SJoerg Wunsch 		*ptr = i;
1321dc5df763SJoerg Wunsch 	return 0;
1322d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1323dc5df763SJoerg Wunsch }
1324dc5df763SJoerg Wunsch 
1325dc5df763SJoerg Wunsch int
13266182fdbdSPeter Wemm out_fdc(struct fdc_data *fdc, int x)
13275b81b6b3SRodney W. Grimes {
13283b3837dbSRodney W. Grimes 	int i;
13295b81b6b3SRodney W. Grimes 
13303b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
13313b3837dbSRodney W. Grimes 	i = 100000;
1332427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0);
13336182fdbdSPeter Wemm 	if (i <= 0) return fdc_err(fdc, "direction bit not set\n");
13343b3837dbSRodney W. Grimes 
13353b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
13363b3837dbSRodney W. Grimes 	i = 100000;
1337427ccf00SDoug Rabson 	while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0);
133816b04b6aSJoerg Wunsch 	if (i <= 0)
13396182fdbdSPeter Wemm 		return fdc_err(fdc, bootverbose? "output ready timeout\n": 0);
13403b3837dbSRodney W. Grimes 
13413b3837dbSRodney W. Grimes 	/* Send the command and return */
1342427ccf00SDoug Rabson 	fddata_wr(fdc, x);
13433a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
13445b81b6b3SRodney W. Grimes 	return (0);
13455b81b6b3SRodney W. Grimes }
13465b81b6b3SRodney W. Grimes 
13475b81b6b3SRodney W. Grimes /****************************************************************************/
13485b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
13495b81b6b3SRodney W. Grimes /****************************************************************************/
1350381fe1aaSGarrett Wollman int
1351671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
13525b81b6b3SRodney W. Grimes {
13535b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
135420a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
13556182fdbdSPeter Wemm 	fd_p	fd;
1356b99f0a4aSAndrew Moore 	fdc_p	fdc;
13575b81b6b3SRodney W. Grimes 
13585b81b6b3SRodney W. Grimes 	/* check bounds */
13596182fdbdSPeter Wemm 	if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
1360b99f0a4aSAndrew Moore 		return (ENXIO);
13616182fdbdSPeter Wemm 	fdc = fd->fdc;
13626182fdbdSPeter Wemm 	if ((fdc == NULL) || (fd->type == NO_TYPE))
1363b99f0a4aSAndrew Moore 		return (ENXIO);
1364b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1365b99f0a4aSAndrew Moore 		return (ENXIO);
13667ca0641bSAndrey A. Chernov 	if (type == 0)
13676182fdbdSPeter Wemm 		type = fd->type;
13687ca0641bSAndrey A. Chernov 	else {
13693e425b96SJulian Elischer 		/*
13703e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
13713e425b96SJulian Elischer 		 * to open a type it can do,
13723e425b96SJulian Elischer 		 */
13736182fdbdSPeter Wemm 		if (type != fd->type) {
13746182fdbdSPeter Wemm 			switch (fd->type) {
13757ca0641bSAndrey A. Chernov 			case FD_360:
13767ca0641bSAndrey A. Chernov 				return (ENXIO);
1377ed2fa05eSAndrey A. Chernov 			case FD_720:
1378b39c878eSAndrey A. Chernov 				if (   type != FD_820
1379b39c878eSAndrey A. Chernov 				    && type != FD_800
1380ed2fa05eSAndrey A. Chernov 				   )
1381ed2fa05eSAndrey A. Chernov 					return (ENXIO);
1382ed2fa05eSAndrey A. Chernov 				break;
13837ca0641bSAndrey A. Chernov 			case FD_1200:
1384b39c878eSAndrey A. Chernov 				switch (type) {
1385b39c878eSAndrey A. Chernov 				case FD_1480:
1386b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1387fa4700b4SAndrey A. Chernov 					break;
13887ca0641bSAndrey A. Chernov 				case FD_1440:
1389b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1390b39c878eSAndrey A. Chernov 					break;
1391b39c878eSAndrey A. Chernov 				case FD_820:
1392b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1393b39c878eSAndrey A. Chernov 					break;
1394b39c878eSAndrey A. Chernov 				case FD_800:
1395b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1396b39c878eSAndrey A. Chernov 					break;
1397b39c878eSAndrey A. Chernov 				case FD_720:
1398b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1399b39c878eSAndrey A. Chernov 					break;
1400b39c878eSAndrey A. Chernov 				case FD_360:
1401b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1402b39c878eSAndrey A. Chernov 					break;
1403b39c878eSAndrey A. Chernov 				default:
1404b39c878eSAndrey A. Chernov 					return(ENXIO);
1405b39c878eSAndrey A. Chernov 				}
1406b39c878eSAndrey A. Chernov 				break;
1407b39c878eSAndrey A. Chernov 			case FD_1440:
1408b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1409b39c878eSAndrey A. Chernov 				    && type != FD_1480
1410ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1411b39c878eSAndrey A. Chernov 				    && type != FD_820
1412b39c878eSAndrey A. Chernov 				    && type != FD_800
1413b39c878eSAndrey A. Chernov 				    && type != FD_720
14147ca0641bSAndrey A. Chernov 				    )
1415dffff499SAndrey A. Chernov 					return(ENXIO);
1416fa4700b4SAndrey A. Chernov 				break;
14177ca0641bSAndrey A. Chernov 			}
14187ca0641bSAndrey A. Chernov 		}
1419fa4700b4SAndrey A. Chernov 	}
14206182fdbdSPeter Wemm 	fd->ft = fd_types + type - 1;
14216182fdbdSPeter Wemm 	fd->flags |= FD_OPEN;
14226182fdbdSPeter Wemm 	device_busy(fd->dev);
14236182fdbdSPeter Wemm 	device_busy(fd->fdc->fdc_dev);
14245b81b6b3SRodney W. Grimes 	return 0;
14255b81b6b3SRodney W. Grimes }
14265b81b6b3SRodney W. Grimes 
1427381fe1aaSGarrett Wollman int
1428671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
14295b81b6b3SRodney W. Grimes {
14305b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
14316182fdbdSPeter Wemm 	struct fd_data *fd;
1432b99f0a4aSAndrew Moore 
14336182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
14346182fdbdSPeter Wemm 	fd->flags &= ~FD_OPEN;
14356182fdbdSPeter Wemm 	fd->options &= ~FDOPT_NORETRY;
1436dc16046fSJoerg Wunsch 
14375b81b6b3SRodney W. Grimes 	return (0);
14385b81b6b3SRodney W. Grimes }
14395b81b6b3SRodney W. Grimes 
14403a2f7427SDavid Greenman /****************************************************************************/
14413a2f7427SDavid Greenman /*                               fdstrategy                                 */
14423a2f7427SDavid Greenman /****************************************************************************/
14433a2f7427SDavid Greenman void
14443a2f7427SDavid Greenman fdstrategy(struct buf *bp)
14453a2f7427SDavid Greenman {
1446bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
14473a2f7427SDavid Greenman  	int	s;
14483a2f7427SDavid Greenman  	fdu_t	fdu;
14493a2f7427SDavid Greenman  	fdc_p	fdc;
14503a2f7427SDavid Greenman  	fd_p	fd;
14513a2f7427SDavid Greenman 	size_t	fdblk;
14523a2f7427SDavid Greenman 
14533a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
14546182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
14556182fdbdSPeter Wemm 	if (fd == 0)
14566182fdbdSPeter Wemm 		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
14576182fdbdSPeter Wemm 		      (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev));
14583a2f7427SDavid Greenman 	fdc = fd->fdc;
145969acd21dSWarner Losh #ifdef FDC_YE
146069acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
146169acd21dSWarner Losh 		bp->b_error = ENXIO;
146269acd21dSWarner Losh 		bp->b_flags |= B_ERROR;
146369acd21dSWarner Losh 		/*
146469acd21dSWarner Losh 		 * I _refuse_ to use a goto
146569acd21dSWarner Losh 		 */
146669acd21dSWarner Losh 		biodone(bp);
146769acd21dSWarner Losh 		return;
146869acd21dSWarner Losh 	};
146969acd21dSWarner Losh #endif
14703a2f7427SDavid Greenman 
1471d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
14723a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
14736182fdbdSPeter Wemm 		if (bp->b_blkno < 0) {
1474dc5df763SJoerg Wunsch 			printf(
14756a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1476702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
14773a2f7427SDavid Greenman 			bp->b_error = EINVAL;
14783a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
14793a2f7427SDavid Greenman 			goto bad;
14803a2f7427SDavid Greenman 		}
14813a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
14823a2f7427SDavid Greenman 			bp->b_error = EINVAL;
14833a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
14843a2f7427SDavid Greenman 			goto bad;
14853a2f7427SDavid Greenman 		}
14863a2f7427SDavid Greenman 	}
14873a2f7427SDavid Greenman 
14883a2f7427SDavid Greenman 	/*
14893a2f7427SDavid Greenman 	 * Set up block calculations.
14903a2f7427SDavid Greenman 	 */
1491bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1492bb6382faSJoerg Wunsch 		/*
1493bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1494bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1495bb6382faSJoerg Wunsch 		 */
1496bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
14973a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
14983a2f7427SDavid Greenman 		goto bad;
14993a2f7427SDavid Greenman 	}
1500bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1501bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1502bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1503bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1504bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1505bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1506bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1507bb6382faSJoerg Wunsch 			if (cando == 0)
1508bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1509bb6382faSJoerg Wunsch 		} else {
1510bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1511bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1512bb6382faSJoerg Wunsch 			goto bad;
1513bb6382faSJoerg Wunsch 		}
1514bb6382faSJoerg Wunsch 	}
151527513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
15163a2f7427SDavid Greenman 	s = splbio();
151702a19910SJustin T. Gibbs 	bufqdisksort(&fdc->head, bp);
15186182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */
1519b2dfb1f9SJustin T. Gibbs 
1520b2dfb1f9SJustin T. Gibbs 	/* Tell devstat we are starting on the transaction */
1521b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
1522b2dfb1f9SJustin T. Gibbs 
15236182fdbdSPeter Wemm 	fdstart(fdc);
15243a2f7427SDavid Greenman 	splx(s);
15253a2f7427SDavid Greenman 	return;
15263a2f7427SDavid Greenman 
15273a2f7427SDavid Greenman bad:
15283a2f7427SDavid Greenman 	biodone(bp);
15293a2f7427SDavid Greenman }
15303a2f7427SDavid Greenman 
15315b81b6b3SRodney W. Grimes /***************************************************************\
15325b81b6b3SRodney W. Grimes *				fdstart				*
15335b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
15345b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
15355b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
15365b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
15375b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
15385b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
15395b81b6b3SRodney W. Grimes \***************************************************************/
1540381fe1aaSGarrett Wollman static void
15416182fdbdSPeter Wemm fdstart(struct fdc_data *fdc)
15425b81b6b3SRodney W. Grimes {
15435b81b6b3SRodney W. Grimes 	int s;
15445b81b6b3SRodney W. Grimes 
15455b81b6b3SRodney W. Grimes 	s = splbio();
15466182fdbdSPeter Wemm 	if(fdc->state == DEVIDLE)
15475b81b6b3SRodney W. Grimes 	{
15486182fdbdSPeter Wemm 		fdc_intr(fdc);
15495b81b6b3SRodney W. Grimes 	}
15505b81b6b3SRodney W. Grimes 	splx(s);
15515b81b6b3SRodney W. Grimes }
15525b81b6b3SRodney W. Grimes 
1553381fe1aaSGarrett Wollman static void
15546182fdbdSPeter Wemm fd_iotimeout(void *xfdc)
15555b81b6b3SRodney W. Grimes {
15565c1a1eaeSBruce Evans  	fdc_p fdc;
1557f5f7ba03SJordan K. Hubbard 	int s;
15585b81b6b3SRodney W. Grimes 
15596182fdbdSPeter Wemm 	fdc = xfdc;
15605c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
15615b81b6b3SRodney W. Grimes 
15623a2f7427SDavid Greenman 	/*
15633a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
15643a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
15653a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
15663a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
15673a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
15685c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
15695c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
15705c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
15715c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
15723a2f7427SDavid Greenman 	 */
15733a2f7427SDavid Greenman 	s = splbio();
15745c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
15755c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
15765c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
15776182fdbdSPeter Wemm 	fdc_intr(fdc);
1578f5f7ba03SJordan K. Hubbard 	splx(s);
15795b81b6b3SRodney W. Grimes }
15805b81b6b3SRodney W. Grimes 
15815b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1582381fe1aaSGarrett Wollman static void
15836182fdbdSPeter Wemm fd_pseudointr(void *xfdc)
15845b81b6b3SRodney W. Grimes {
15855b81b6b3SRodney W. Grimes 	int	s;
15863a2f7427SDavid Greenman 
15875b81b6b3SRodney W. Grimes 	s = splbio();
15886182fdbdSPeter Wemm 	fdc_intr(xfdc);
15895b81b6b3SRodney W. Grimes 	splx(s);
15905b81b6b3SRodney W. Grimes }
15915b81b6b3SRodney W. Grimes 
15925b81b6b3SRodney W. Grimes /***********************************************************************\
15935b81b6b3SRodney W. Grimes *                                 fdintr				*
15945b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
15955b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
15965b81b6b3SRodney W. Grimes \***********************************************************************/
1597fe310de8SBruce Evans static void
15986182fdbdSPeter Wemm fdc_intr(void *xfdc)
15995b81b6b3SRodney W. Grimes {
16006182fdbdSPeter Wemm 	fdc_p fdc = xfdc;
16016182fdbdSPeter Wemm 	while(fdstate(fdc))
1602381fe1aaSGarrett Wollman 		;
16035b81b6b3SRodney W. Grimes }
16045b81b6b3SRodney W. Grimes 
160569acd21dSWarner Losh #ifdef FDC_YE
160669acd21dSWarner Losh /*
160769acd21dSWarner Losh  * magic pseudo-DMA initialization for YE FDC. Sets count and
160869acd21dSWarner Losh  * direction
160969acd21dSWarner Losh  */
161069acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \
161169acd21dSWarner Losh 	outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)))
161269acd21dSWarner Losh 
161369acd21dSWarner Losh /*
161469acd21dSWarner Losh  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy
161569acd21dSWarner Losh  */
161669acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count)
161769acd21dSWarner Losh {
161869acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
161969acd21dSWarner Losh 	fdc_p fdc = &fdc_data[fdcu];
162069acd21dSWarner Losh 	int io = fdc->baseport;
162169acd21dSWarner Losh 
162269acd21dSWarner Losh 	if (flags & B_READ) {
162369acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
162469acd21dSWarner Losh 			fdc->state = PIOREAD;
162569acd21dSWarner Losh 			return(0);
162669acd21dSWarner Losh 		};
162769acd21dSWarner Losh 		SET_BCDR(0,count,io);
162869acd21dSWarner Losh 		insb(io+FDC_YE_DATAPORT,cptr,count);
162969acd21dSWarner Losh 	} else {
163069acd21dSWarner Losh 		outsb(io+FDC_YE_DATAPORT,cptr,count);
163169acd21dSWarner Losh 		SET_BCDR(0,count,io);
163269acd21dSWarner Losh 	};
163369acd21dSWarner Losh 	return(1);
163469acd21dSWarner Losh }
163569acd21dSWarner Losh #endif /* FDC_YE */
163669acd21dSWarner Losh 
16375b81b6b3SRodney W. Grimes /***********************************************************************\
16385b81b6b3SRodney W. Grimes * The controller state machine.						*
16395b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
16405b81b6b3SRodney W. Grimes \***********************************************************************/
16413a2f7427SDavid Greenman static int
16426182fdbdSPeter Wemm fdstate(fdc_p fdc)
16435b81b6b3SRodney W. Grimes {
16445c1a1eaeSBruce Evans 	int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
1645bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
16465b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
16475b81b6b3SRodney W. Grimes 	fd_p fd;
164817542807SPoul-Henning Kamp 	register struct buf *bp;
1649b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
16503a2f7427SDavid Greenman 	size_t fdblk;
16515b81b6b3SRodney W. Grimes 
1652e93e63cbSBruce Evans 	bp = fdc->bp;
1653e93e63cbSBruce Evans 	if (bp == NULL) {
165402a19910SJustin T. Gibbs 		bp = bufq_first(&fdc->head);
1655e93e63cbSBruce Evans 		if (bp != NULL) {
1656e93e63cbSBruce Evans 			bufq_remove(&fdc->head, bp);
1657e93e63cbSBruce Evans 			fdc->bp = bp;
1658e93e63cbSBruce Evans 		}
1659e93e63cbSBruce Evans 	}
1660e93e63cbSBruce Evans 	if (bp == NULL) {
16615b81b6b3SRodney W. Grimes 		/***********************************************\
16625b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
16635b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
16645b81b6b3SRodney W. Grimes 		\***********************************************/
16655b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
16666182fdbdSPeter Wemm 		if (fdc->fd) {
16676182fdbdSPeter Wemm 			device_print_prettyname(fdc->fdc_dev);
16686182fdbdSPeter Wemm 			printf("unexpected valid fd pointer\n");
16695b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
16705b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
16715b81b6b3SRodney W. Grimes 		}
16726182fdbdSPeter Wemm 		TRACE1("[fdc%d IDLE]", fdc->fdcu);
16735b81b6b3SRodney W. Grimes  		return (0);
16745b81b6b3SRodney W. Grimes 	}
16755b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
16766182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
16773a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
16786182fdbdSPeter Wemm 	if (fdc->fd && (fd != fdc->fd)) {
16796182fdbdSPeter Wemm 		device_print_prettyname(fd->dev);
16806182fdbdSPeter Wemm 		printf("confused fd pointers\n");
16815b81b6b3SRodney W. Grimes 	}
16825b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1683b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1684bb6382faSJoerg Wunsch 	if (format) {
1685ab3f7469SPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->b_data;
1686bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1687bb6382faSJoerg Wunsch 			- (char *)finfo;
1688bb6382faSJoerg Wunsch 	}
1689bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
16903e425b96SJulian Elischer 		blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk +
1691bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1692bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1693bb6382faSJoerg Wunsch 	}
16945b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
16955b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
16965b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
16976182fdbdSPeter Wemm 	untimeout(fd_turnoff, fd, fd->toffhandle);
16986182fdbdSPeter Wemm 	fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz);
16995b81b6b3SRodney W. Grimes 	switch (fdc->state)
17005b81b6b3SRodney W. Grimes 	{
17015b81b6b3SRodney W. Grimes 	case DEVIDLE:
17025b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
17035b81b6b3SRodney W. Grimes 		fdc->retry = 0;
17045b81b6b3SRodney W. Grimes 		fd->skip = 0;
17055b81b6b3SRodney W. Grimes 		fdc->fd = fd;
17065b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
1707427ccf00SDoug Rabson 		fdctl_wr(fdc, fd->ft->trans);
17083a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
17095b81b6b3SRodney W. Grimes 		/*******************************************************\
17105b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
1711dc733423SDag-Erling Smørgrav 		* it will start up in its own good time		*
17125b81b6b3SRodney W. Grimes 		\*******************************************************/
17136182fdbdSPeter Wemm 		if(fd->flags & FD_MOTOR_WAIT) {
17145b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
17155b81b6b3SRodney W. Grimes 			return (0); /* come back later */
17165b81b6b3SRodney W. Grimes 		}
17175b81b6b3SRodney W. Grimes 		/*******************************************************\
17185b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
17195b81b6b3SRodney W. Grimes 		\*******************************************************/
17205b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
17215b81b6b3SRodney W. Grimes 		{
17225b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
17236182fdbdSPeter Wemm 			fd_turnon(fd);
17245b81b6b3SRodney W. Grimes 			return (0);
17255b81b6b3SRodney W. Grimes 		}
17265b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
17275b81b6b3SRodney W. Grimes 		{
17286182fdbdSPeter Wemm 			set_motor(fdc, fd->fdsu, TURNON);
17295b81b6b3SRodney W. Grimes 		}
17305c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
17315c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
17325c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
17335c1a1eaeSBruce Evans 		} else
17345e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
17355b81b6b3SRodney W. Grimes 		break;
17365b81b6b3SRodney W. Grimes 	case DOSEEK:
1737bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
17385b81b6b3SRodney W. Grimes 		{
17395b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
17405b81b6b3SRodney W. Grimes 			break;
17415b81b6b3SRodney W. Grimes 		}
17426182fdbdSPeter Wemm 		if (fd_cmd(fdc, 3, NE7CMD_SEEK,
1743bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1744dc5df763SJoerg Wunsch 			   0))
1745dc8603e3SJoerg Wunsch 		{
1746dc8603e3SJoerg Wunsch 			/*
1747dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1748dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1749dc8603e3SJoerg Wunsch 			 */
1750dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
17516182fdbdSPeter Wemm 			return(retrier(fdc));
1752dc8603e3SJoerg Wunsch 		}
1753dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
17545b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
17555b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
17565b81b6b3SRodney W. Grimes 	case SEEKWAIT:
17575b81b6b3SRodney W. Grimes 		/* allow heads to settle */
17586182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 16);
17595b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
17605b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
17615b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
17625b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
17636182fdbdSPeter Wemm 		if(fd->track == FD_NO_TRACK) {
1764bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
17653a2f7427SDavid Greenman 			do {
17663a2f7427SDavid Greenman 				/*
1767dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1768dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1769dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1770dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1771dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1772dc5df763SJoerg Wunsch 				 *
1773dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1774dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1775dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1776dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1777dc5df763SJoerg Wunsch 				 * real interrupt condition.
1778dc5df763SJoerg Wunsch 				 *
1779dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1780dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1781dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1782dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1783dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1784dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1785dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
17863a2f7427SDavid Greenman 				 */
1787dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1788dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1789dc5df763SJoerg Wunsch 					return 0;
1790dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1791dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1792dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
17933a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1794dc5df763SJoerg Wunsch 
17956182fdbdSPeter Wemm 			if (0 == descyl) {
1796dc5df763SJoerg Wunsch 				int failed = 0;
17973a2f7427SDavid Greenman 				/*
17983a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
17993a2f7427SDavid Greenman 				 * really there
18003a2f7427SDavid Greenman 				 */
1801dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1802dc5df763SJoerg Wunsch 					failed = 1;
18033a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
18043a2f7427SDavid Greenman 					printf(
18053a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
18063a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1807dc5df763SJoerg Wunsch 					failed = 1;
1808dc5df763SJoerg Wunsch 				}
1809dc5df763SJoerg Wunsch 
18106182fdbdSPeter Wemm 				if (failed) {
18113a2f7427SDavid Greenman 					if(fdc->retry < 3)
18123a2f7427SDavid Greenman 						fdc->retry = 3;
18136182fdbdSPeter Wemm 					return (retrier(fdc));
18143a2f7427SDavid Greenman 				}
18153a2f7427SDavid Greenman 			}
1816dc5df763SJoerg Wunsch 
18176182fdbdSPeter Wemm 			if (cyl != descyl) {
18183a2f7427SDavid Greenman 				printf(
18193a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
18202d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1821e5d7d243SBruce Evans 				if (fdc->retry < 3)
1822e5d7d243SBruce Evans 					fdc->retry = 3;
18236182fdbdSPeter Wemm 				return (retrier(fdc));
18245b81b6b3SRodney W. Grimes 			}
18255b81b6b3SRodney W. Grimes 		}
18265b81b6b3SRodney W. Grimes 
1827bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
182869acd21dSWarner Losh #ifdef FDC_YE
182969acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
183069acd21dSWarner Losh #endif
1831ab3f7469SPoul-Henning Kamp 			isa_dmastart(bp->b_flags, bp->b_data+fd->skip,
18323a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
18335b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
18345b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
18355b81b6b3SRodney W. Grimes 		head = sec / sectrac;
18365b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
18373a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
18383a2f7427SDavid Greenman 
18393a2f7427SDavid Greenman 		if(format || !read)
18403a2f7427SDavid Greenman 		{
18413a2f7427SDavid Greenman 			/* make sure the drive is writable */
1842dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1843dc8603e3SJoerg Wunsch 			{
1844dc8603e3SJoerg Wunsch 				/* stuck controller? */
18455c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18465c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18475c1a1eaeSBruce Evans 					    fdc->dmachan);
1848dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
18496182fdbdSPeter Wemm 				return (retrier(fdc));
1850dc8603e3SJoerg Wunsch 			}
18513a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
18523a2f7427SDavid Greenman 			{
18533a2f7427SDavid Greenman 				/*
18543a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
18553a2f7427SDavid Greenman 				 * in order to force the current operation
18563a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
18573a2f7427SDavid Greenman 				 * error - all error handling is done
18583a2f7427SDavid Greenman 				 * by the retrier()
18593a2f7427SDavid Greenman 				 */
18603a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
18613a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
18623a2f7427SDavid Greenman 				fdc->status[2] = 0;
18633a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
18643a2f7427SDavid Greenman 				fdc->status[4] = head;
18653a2f7427SDavid Greenman 				fdc->status[5] = sec;
18663a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
18673a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
18683a2f7427SDavid Greenman 				return (1);
18693a2f7427SDavid Greenman 			}
18703a2f7427SDavid Greenman 		}
18715b81b6b3SRodney W. Grimes 
18726182fdbdSPeter Wemm 		if (format) {
187369acd21dSWarner Losh #ifdef FDC_YE
187469acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA)
187569acd21dSWarner Losh 				(void)fdcpio(fdcu,bp->b_flags,
187669acd21dSWarner Losh 					bp->b_data+fd->skip,
187769acd21dSWarner Losh 					bp->b_bcount);
187869acd21dSWarner Losh #endif
1879b39c878eSAndrey A. Chernov 			/* formatting */
18806182fdbdSPeter Wemm 			if(fd_cmd(fdc, 6,  NE7CMD_FORMAT, head << 2 | fdu,
1881dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1882dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1883dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
18846182fdbdSPeter Wemm 				  finfo->fd_formb_fillbyte, 0)) {
1885dc8603e3SJoerg Wunsch 				/* controller fell over */
18865c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18875c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18885c1a1eaeSBruce Evans 					    fdc->dmachan);
1889dc8603e3SJoerg Wunsch 				fdc->retry = 6;
18906182fdbdSPeter Wemm 				return (retrier(fdc));
1891dc8603e3SJoerg Wunsch 			}
18926182fdbdSPeter Wemm 		} else {
189369acd21dSWarner Losh #ifdef FDC_YE
189469acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA) {
189569acd21dSWarner Losh 				/*
189669acd21dSWarner Losh 				 * this seems to be necessary even when
189769acd21dSWarner Losh 				 * reading data
189869acd21dSWarner Losh 				 */
189969acd21dSWarner Losh 				SET_BCDR(1,fdblk,fdc->baseport);
190069acd21dSWarner Losh 
190169acd21dSWarner Losh 				/*
190269acd21dSWarner Losh 				 * perform the write pseudo-DMA before
190369acd21dSWarner Losh 				 * the WRITE command is sent
190469acd21dSWarner Losh 				 */
190569acd21dSWarner Losh 				if (!read)
190669acd21dSWarner Losh 					(void)fdcpio(fdcu,bp->b_flags,
190769acd21dSWarner Losh 					    bp->b_data+fd->skip,
190869acd21dSWarner Losh 					    fdblk);
190969acd21dSWarner Losh 			}
191069acd21dSWarner Losh #endif
19116182fdbdSPeter Wemm 			if (fd_cmd(fdc, 9,
1912dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1913dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1914dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1915dc5df763SJoerg Wunsch 				   head,
1916dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1917dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1918dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1919dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1920dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
19216182fdbdSPeter Wemm 				   0)) {
1922dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
19235c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19245c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
19255c1a1eaeSBruce Evans 					    fdc->dmachan);
1926dc8603e3SJoerg Wunsch 				fdc->retry = 6;
19276182fdbdSPeter Wemm 				return (retrier(fdc));
19285b81b6b3SRodney W. Grimes 			}
1929b39c878eSAndrey A. Chernov 		}
193069acd21dSWarner Losh #ifdef FDC_YE
193169acd21dSWarner Losh 		if (fdc->flags & FDC_PCMCIA)
193269acd21dSWarner Losh 			/*
193369acd21dSWarner Losh 			 * if this is a read, then simply await interrupt
193469acd21dSWarner Losh 			 * before performing PIO
193569acd21dSWarner Losh 			 */
193669acd21dSWarner Losh 			if (read && !fdcpio(fdcu,bp->b_flags,
193769acd21dSWarner Losh 			    bp->b_data+fd->skip,fdblk)) {
193869acd21dSWarner Losh 				fd->tohandle = timeout(fd_iotimeout,
193969acd21dSWarner Losh 					(caddr_t)fdcu, hz);
194069acd21dSWarner Losh 				return(0);      /* will return later */
194169acd21dSWarner Losh 			};
194269acd21dSWarner Losh 
194369acd21dSWarner Losh 		/*
194469acd21dSWarner Losh 		 * write (or format) operation will fall through and
194569acd21dSWarner Losh 		 * await completion interrupt
194669acd21dSWarner Losh 		 */
194769acd21dSWarner Losh #endif
19485b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
19496182fdbdSPeter Wemm 		fd->tohandle = timeout(fd_iotimeout, fdc, hz);
19505b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
195169acd21dSWarner Losh #ifdef FDC_YE
195269acd21dSWarner Losh 	case PIOREAD:
195369acd21dSWarner Losh 		/*
195469acd21dSWarner Losh 		 * actually perform the PIO read.  The IOCOMPLETE case
195569acd21dSWarner Losh 		 * removes the timeout for us.
195669acd21dSWarner Losh 		 */
195769acd21dSWarner Losh 		(void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk);
195869acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
195969acd21dSWarner Losh 		/* FALLTHROUGH */
196069acd21dSWarner Losh #endif
19615b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
19626182fdbdSPeter Wemm 		untimeout(fd_iotimeout, fdc, fd->tohandle);
1963dc5df763SJoerg Wunsch 
19646182fdbdSPeter Wemm 		if (fd_read_status(fdc, fd->fdsu)) {
19655c1a1eaeSBruce Evans 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19665c1a1eaeSBruce Evans 				    format ? bp->b_bcount : fdblk,
19675c1a1eaeSBruce Evans 				    fdc->dmachan);
1968dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1969dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
19706182fdbdSPeter Wemm 			return (retrier(fdc));
19715b81b6b3SRodney W. Grimes   		}
1972dc5df763SJoerg Wunsch 
19733a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1974dc5df763SJoerg Wunsch 
19753a2f7427SDavid Greenman 		/* FALLTHROUGH */
1976dc5df763SJoerg Wunsch 
19773a2f7427SDavid Greenman 	case IOTIMEDOUT:
197869acd21dSWarner Losh #ifdef FDC_YE
197969acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
198069acd21dSWarner Losh #endif
1981ab3f7469SPoul-Henning Kamp 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
19823a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
19836182fdbdSPeter Wemm 		if (fdc->status[0] & NE7_ST0_IC) {
19843a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
19853a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1986b39c878eSAndrey A. Chernov                                 /*
19873a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
19883a2f7427SDavid Greenman 				 * and didn't release it in time for the
19893a2f7427SDavid Greenman 				 * next FDC transfer.
19903a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
19913a2f7427SDavid Greenman 				 * count. (vak)
1992b39c878eSAndrey A. Chernov                                  */
1993b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
1994b39c878eSAndrey A. Chernov                                 return (1);
1995b39c878eSAndrey A. Chernov                         }
19963a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
19973a2f7427SDavid Greenman 				&& fdc->retry < 6)
19983a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
19993a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
20003a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
20013a2f7427SDavid Greenman 				&& fdc->retry < 3)
20023a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
20036182fdbdSPeter Wemm 			return (retrier(fdc));
20045b81b6b3SRodney W. Grimes 		}
20055b81b6b3SRodney W. Grimes 		/* All OK */
20063a2f7427SDavid Greenman 		fd->skip += fdblk;
20076182fdbdSPeter Wemm 		if (!format && fd->skip < bp->b_bcount - bp->b_resid) {
20085b81b6b3SRodney W. Grimes 			/* set up next transfer */
20095b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
20106182fdbdSPeter Wemm 		} else {
20115b81b6b3SRodney W. Grimes 			/* ALL DONE */
20125b81b6b3SRodney W. Grimes 			fd->skip = 0;
2013e93e63cbSBruce Evans 			fdc->bp = NULL;
20142186cd9eSPoul-Henning Kamp 			devstat_end_transaction_buf(&fd->device_stats, bp);
20155b81b6b3SRodney W. Grimes 			biodone(bp);
20165b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
20175b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
20185b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
20195b81b6b3SRodney W. Grimes 		}
20205b81b6b3SRodney W. Grimes 		return (1);
20215b81b6b3SRodney W. Grimes 	case RESETCTLR:
20223a2f7427SDavid Greenman 		fdc_reset(fdc);
20235b81b6b3SRodney W. Grimes 		fdc->retry++;
20245c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
20255c1a1eaeSBruce Evans 		return (0);
20265c1a1eaeSBruce Evans 	case RESETCOMPLETE:
20275c1a1eaeSBruce Evans 		/*
20285c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
20295c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
20305c1a1eaeSBruce Evans 		 */
20310e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
20320e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
20335c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
20345c1a1eaeSBruce Evans 		/* Fall through. */
20355c1a1eaeSBruce Evans 	case STARTRECAL:
20366182fdbdSPeter Wemm 		if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) {
2037dc8603e3SJoerg Wunsch 			/* arrgl */
2038dc8603e3SJoerg Wunsch 			fdc->retry = 6;
20396182fdbdSPeter Wemm 			return (retrier(fdc));
2040dc8603e3SJoerg Wunsch 		}
20415b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
20425b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
20435b81b6b3SRodney W. Grimes 	case RECALWAIT:
20445b81b6b3SRodney W. Grimes 		/* allow heads to settle */
20456182fdbdSPeter Wemm 		timeout(fd_pseudointr, fdc, hz / 8);
20465b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
20475b81b6b3SRodney W. Grimes 		return (0);	/* will return later */
20485b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
20493a2f7427SDavid Greenman 		do {
20503a2f7427SDavid Greenman 			/*
2051dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
20523a2f7427SDavid Greenman 			 */
2053dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
2054dc5df763SJoerg Wunsch 				return 0;
2055dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
2056dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
2057dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
20583a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
20593a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
20605b81b6b3SRodney W. Grimes 		{
2061dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
2062dc8603e3SJoerg Wunsch 				/*
2063dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
2064dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
2065dc8603e3SJoerg Wunsch 				 * since people used to complain much about
2066dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
2067dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
2068dc8603e3SJoerg Wunsch 				 * time in a line
2069dc8603e3SJoerg Wunsch 				 */
2070dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
2071dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
20723a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
20736182fdbdSPeter Wemm 			return (retrier(fdc));
20745b81b6b3SRodney W. Grimes 		}
20755b81b6b3SRodney W. Grimes 		fd->track = 0;
20765b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
20775b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
20785b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20795b81b6b3SRodney W. Grimes 	case MOTORWAIT:
20805b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
20815b81b6b3SRodney W. Grimes 		{
20825b81b6b3SRodney W. Grimes 			return (0); /* time's not up yet */
20835b81b6b3SRodney W. Grimes 		}
20845c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
20855c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
20865c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
20875c1a1eaeSBruce Evans 		} else {
20885e235068SJordan K. Hubbard 			/*
20895c1a1eaeSBruce Evans 			 * If all motors were off, then the controller was
20905c1a1eaeSBruce Evans 			 * reset, so it has lost track of the current
20915c1a1eaeSBruce Evans 			 * cylinder.  Recalibrate to handle this case.
2092f86e4077SBruce Evans 			 * But first, discard the results of the reset.
20935e235068SJordan K. Hubbard 			 */
2094f86e4077SBruce Evans 			fdc->state = RESETCOMPLETE;
20955c1a1eaeSBruce Evans 		}
20965b81b6b3SRodney W. Grimes 		return (1);	/* will return immediatly */
20975b81b6b3SRodney W. Grimes 	default:
20986182fdbdSPeter Wemm 		device_print_prettyname(fdc->fdc_dev);
20996182fdbdSPeter Wemm 		printf("unexpected FD int->");
2100dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
2101a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
21025b81b6b3SRodney W. Grimes 			       fdc->status[0],
21035b81b6b3SRodney W. Grimes 			       fdc->status[1],
21045b81b6b3SRodney W. Grimes 			       fdc->status[2],
21055b81b6b3SRodney W. Grimes 			       fdc->status[3],
21065b81b6b3SRodney W. Grimes 			       fdc->status[4],
21075b81b6b3SRodney W. Grimes 			       fdc->status[5],
21085b81b6b3SRodney W. Grimes 			       fdc->status[6] );
21093a2f7427SDavid Greenman 		else
2110dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2111dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2112dac0f2dbSJoerg Wunsch 		{
2113dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
21145b81b6b3SRodney W. Grimes 			return (0);
21155b81b6b3SRodney W. Grimes 		}
2116dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2117dac0f2dbSJoerg Wunsch 		return (0);
2118dac0f2dbSJoerg Wunsch 	}
2119dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
21205b81b6b3SRodney W. Grimes 	return (1); /* Come back immediatly to new state */
21215b81b6b3SRodney W. Grimes }
21225b81b6b3SRodney W. Grimes 
2123aaf08d94SGarrett Wollman static int
21246182fdbdSPeter Wemm retrier(struct fdc_data *fdc)
21255b81b6b3SRodney W. Grimes {
212617542807SPoul-Henning Kamp 	register struct buf *bp;
21276182fdbdSPeter Wemm 	struct fd_data *fd;
21286182fdbdSPeter Wemm 	int fdu;
21295b81b6b3SRodney W. Grimes 
2130e93e63cbSBruce Evans 	bp = fdc->bp;
21315b81b6b3SRodney W. Grimes 
21326182fdbdSPeter Wemm 	/* XXX shouldn't this be cached somewhere?  */
21336182fdbdSPeter Wemm 	fdu = FDUNIT(minor(bp->b_dev));
21346182fdbdSPeter Wemm 	fd = devclass_get_softc(fd_devclass, fdu);
21356182fdbdSPeter Wemm 	if (fd->options & FDOPT_NORETRY)
21363a2f7427SDavid Greenman 		goto fail;
21376182fdbdSPeter Wemm 
21386182fdbdSPeter Wemm 	switch (fdc->retry) {
21395b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
21405b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
21415b81b6b3SRodney W. Grimes 		break;
21425b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
21435b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
21445b81b6b3SRodney W. Grimes 		break;
21455b81b6b3SRodney W. Grimes 	case 6:
21465b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
21475b81b6b3SRodney W. Grimes 		break;
21485b81b6b3SRodney W. Grimes 	case 7:
21495b81b6b3SRodney W. Grimes 		break;
21505b81b6b3SRodney W. Grimes 	default:
21513a2f7427SDavid Greenman 	fail:
21525b81b6b3SRodney W. Grimes 		{
21537ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
21547ca0641bSAndrey A. Chernov 			/* Trick diskerr */
21553a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
21563a2f7427SDavid Greenman 				    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
2157887ba12fSBruce Evans 			diskerr(bp, "hard error", LOG_PRINTF,
21583a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
21593a2f7427SDavid Greenman 				(struct disklabel *)NULL);
21607ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
2161dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
2162dc5df763SJoerg Wunsch 			{
2163dc5df763SJoerg Wunsch 				printf(
2164a838d83dSBruce Evans 			" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2165dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2166dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2167dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2168dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2169dc5df763SJoerg Wunsch 				       fdc->status[5]);
2170dc5df763SJoerg Wunsch 			}
2171dc5df763SJoerg Wunsch 			else
2172dc5df763SJoerg Wunsch 				printf(" (No status)\n");
21735b81b6b3SRodney W. Grimes 		}
21745b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
21755b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
2176bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
2177e93e63cbSBruce Evans 		fdc->bp = NULL;
21785b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
217911a0be87SPoul-Henning Kamp 		devstat_end_transaction_buf(&fdc->fd->device_stats, bp);
21805b81b6b3SRodney W. Grimes 		biodone(bp);
218192ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
21825c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
21835b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
21845b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
218592ed385aSRodney W. Grimes 		return (1);
21865b81b6b3SRodney W. Grimes 	}
21875b81b6b3SRodney W. Grimes 	fdc->retry++;
21885b81b6b3SRodney W. Grimes 	return (1);
21895b81b6b3SRodney W. Grimes }
21905b81b6b3SRodney W. Grimes 
2191b39c878eSAndrey A. Chernov static int
2192b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
2193b39c878eSAndrey A. Chernov 	dev_t dev;
2194b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
2195b39c878eSAndrey A. Chernov 	struct proc *p;
2196b39c878eSAndrey A. Chernov {
2197b39c878eSAndrey A. Chernov  	fdu_t	fdu;
2198b39c878eSAndrey A. Chernov  	fd_p	fd;
2199b39c878eSAndrey A. Chernov 
2200b39c878eSAndrey A. Chernov 	struct buf *bp;
2201b39c878eSAndrey A. Chernov 	int rv = 0, s;
22023a2f7427SDavid Greenman 	size_t fdblk;
2203b39c878eSAndrey A. Chernov 
2204b39c878eSAndrey A. Chernov  	fdu	= FDUNIT(minor(dev));
22056182fdbdSPeter Wemm 	fd	= devclass_get_softc(fd_devclass, fdu);
22063a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2207b39c878eSAndrey A. Chernov 
2208b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
2209b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
2210b39c878eSAndrey A. Chernov 	if(bp == 0)
2211b39c878eSAndrey A. Chernov 		return ENOBUFS;
221282f5379bSJoerg Wunsch 	/*
221382f5379bSJoerg Wunsch 	 * keep the process from being swapped
221482f5379bSJoerg Wunsch 	 */
22152ada239cSPeter Wemm 	PHOLD(p);
2216b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
221767812eacSKirk McKusick 	BUF_LOCKINIT(bp);
221867812eacSKirk McKusick 	BUF_LOCK(bp, LK_EXCLUSIVE);
221967812eacSKirk McKusick 	bp->b_flags = B_PHYS | B_FORMAT;
2220b39c878eSAndrey A. Chernov 
2221b39c878eSAndrey A. Chernov 	/*
2222b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
2223b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
2224b39c878eSAndrey A. Chernov 	 */
2225b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
22263a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
2227b39c878eSAndrey A. Chernov 
2228b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
2229ab3f7469SPoul-Henning Kamp 	bp->b_data = (caddr_t)finfo;
2230b39c878eSAndrey A. Chernov 
2231b39c878eSAndrey A. Chernov 	/* now do the format */
22323e425b96SJulian Elischer 	bp->b_dev = dev;
223349ff4debSPoul-Henning Kamp 	BUF_STRATEGY(bp, 0);
2234b39c878eSAndrey A. Chernov 
2235b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
2236b39c878eSAndrey A. Chernov 	s = splbio();
22376182fdbdSPeter Wemm 	while(!(bp->b_flags & B_DONE)) {
22385e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
2239b39c878eSAndrey A. Chernov 		if (rv == EWOULDBLOCK)
2240b39c878eSAndrey A. Chernov 			break;
2241b39c878eSAndrey A. Chernov 	}
2242b39c878eSAndrey A. Chernov 	splx(s);
2243b39c878eSAndrey A. Chernov 
224482f5379bSJoerg Wunsch 	if (rv == EWOULDBLOCK) {
2245b39c878eSAndrey A. Chernov 		/* timed out */
2246b39c878eSAndrey A. Chernov 		rv = EIO;
224782f5379bSJoerg Wunsch 		biodone(bp);
224882f5379bSJoerg Wunsch 	}
22493a2f7427SDavid Greenman 	if (bp->b_flags & B_ERROR)
22503a2f7427SDavid Greenman 		rv = bp->b_error;
225182f5379bSJoerg Wunsch 	/*
225282f5379bSJoerg Wunsch 	 * allow the process to be swapped
225382f5379bSJoerg Wunsch 	 */
22542ada239cSPeter Wemm 	PRELE(p);
225567812eacSKirk McKusick 	BUF_UNLOCK(bp);
225667812eacSKirk McKusick 	BUF_LOCKFREE(bp);
2257b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
2258b39c878eSAndrey A. Chernov 	return rv;
2259b39c878eSAndrey A. Chernov }
2260b39c878eSAndrey A. Chernov 
2261f5f7ba03SJordan K. Hubbard /*
2262671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
2263f5f7ba03SJordan K. Hubbard  */
22645b81b6b3SRodney W. Grimes 
22653e425b96SJulian Elischer static int
2266b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
2267f5f7ba03SJordan K. Hubbard 	dev_t dev;
2268ecbb00a2SDoug Rabson 	u_long cmd;
2269f5f7ba03SJordan K. Hubbard 	caddr_t addr;
2270f5f7ba03SJordan K. Hubbard 	int flag;
2271b39c878eSAndrey A. Chernov 	struct proc *p;
2272f5f7ba03SJordan K. Hubbard {
22733a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
22746182fdbdSPeter Wemm  	fd_p	fd = devclass_get_softc(fd_devclass, fdu);
22753a2f7427SDavid Greenman 	size_t fdblk;
22763a2f7427SDavid Greenman 
2277f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2278f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
2279f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
22803a2f7427SDavid Greenman 	int error = 0;
2281f5f7ba03SJordan K. Hubbard 
22823a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2283f5f7ba03SJordan K. Hubbard 
22846182fdbdSPeter Wemm 	switch (cmd) {
2285f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2286f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
2287f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
22883a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
22896182fdbdSPeter Wemm 		fdt = fd->ft;
2290f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
2291f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
2292f5f7ba03SJordan K. Hubbard 
229349ff4debSPoul-Henning Kamp 		if (readdisklabel(dkmodpart(dev, RAW_PART), dl)
2294191e1a59SBruce Evans 		    == NULL)
2295f5f7ba03SJordan K. Hubbard 			error = 0;
2296f5f7ba03SJordan K. Hubbard 		else
2297f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2298f5f7ba03SJordan K. Hubbard 
2299f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
2300f5f7ba03SJordan K. Hubbard 		break;
2301f5f7ba03SJordan K. Hubbard 
2302f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2303f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2304f5f7ba03SJordan K. Hubbard 			error = EBADF;
2305f5f7ba03SJordan K. Hubbard 		break;
2306f5f7ba03SJordan K. Hubbard 
2307f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2308f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2309f5f7ba03SJordan K. Hubbard 			error = EBADF;
2310f5f7ba03SJordan K. Hubbard 		break;
2311f5f7ba03SJordan K. Hubbard 
2312f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
23136182fdbdSPeter Wemm 		if ((flag & FWRITE) == 0) {
2314f5f7ba03SJordan K. Hubbard 			error = EBADF;
2315f5f7ba03SJordan K. Hubbard 			break;
2316f5f7ba03SJordan K. Hubbard 		}
2317f5f7ba03SJordan K. Hubbard 
2318f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
2319f5f7ba03SJordan K. Hubbard 
2320191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
2321191e1a59SBruce Evans 					  (u_long)0)) != 0)
2322f5f7ba03SJordan K. Hubbard 			break;
2323f5f7ba03SJordan K. Hubbard 
232449ff4debSPoul-Henning Kamp 		error = writedisklabel(dev, (struct disklabel *)buffer);
2325b39c878eSAndrey A. Chernov 		break;
2326b39c878eSAndrey A. Chernov 	case FD_FORM:
2327b39c878eSAndrey A. Chernov 		if ((flag & FWRITE) == 0)
2328b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
2329b39c878eSAndrey A. Chernov 		else if (((struct fd_formb *)addr)->format_version !=
2330b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
2331b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
2332b39c878eSAndrey A. Chernov 		else
2333b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
2334b39c878eSAndrey A. Chernov 		break;
2335b39c878eSAndrey A. Chernov 
2336b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
23373e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2338f5f7ba03SJordan K. Hubbard 		break;
2339f5f7ba03SJordan K. Hubbard 
23403a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
23413a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
2342f711d546SPoul-Henning Kamp 		if (suser(p) != 0)
23433a2f7427SDavid Greenman 			return EPERM;
23443e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
23453a2f7427SDavid Greenman 		break;
23463a2f7427SDavid Greenman 
23473a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
23483e425b96SJulian Elischer 		*(int *)addr = fd->options;
23493a2f7427SDavid Greenman 		break;
23503a2f7427SDavid Greenman 
23513a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
23523e425b96SJulian Elischer 		fd->options = *(int *)addr;
23533a2f7427SDavid Greenman 		break;
23543a2f7427SDavid Greenman 
2355f5f7ba03SJordan K. Hubbard 	default:
23563a2f7427SDavid Greenman 		error = ENOTTY;
2357f5f7ba03SJordan K. Hubbard 		break;
2358f5f7ba03SJordan K. Hubbard 	}
2359f5f7ba03SJordan K. Hubbard 	return (error);
2360f5f7ba03SJordan K. Hubbard }
2361f5f7ba03SJordan K. Hubbard 
23623a2f7427SDavid Greenman /*
23633a2f7427SDavid Greenman  * Hello emacs, these are the
23643a2f7427SDavid Greenman  * Local Variables:
23653a2f7427SDavid Greenman  *  c-indent-level:               8
23663a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
23673a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
23683a2f7427SDavid Greenman  *  c-brace-offset:              -8
23693a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
23703a2f7427SDavid Greenman  *  c-argdecl-indent:             8
23713a2f7427SDavid Greenman  *  c-label-offset:              -8
23723a2f7427SDavid Greenman  *  c++-hanging-braces:           1
23733a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
23743a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
23753a2f7427SDavid Greenman  *  c++-friend-offset:            0
23763a2f7427SDavid Greenman  * End:
23773a2f7427SDavid Greenman  */
2378