xref: /freebsd/sys/dev/fdc/fdc.c (revision 2a888f938e5f02e6579c0a12283121a626a90fe1)
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
502a888f93SKenneth D. Merry  *	$Id: fd.c,v 1.132 1999/01/19 00:21:36 peter Exp $
515b81b6b3SRodney W. Grimes  *
525b81b6b3SRodney W. Grimes  */
535b81b6b3SRodney W. Grimes 
545b81b6b3SRodney W. Grimes #include "fd.h"
557b778b5eSEivind Eklund #include "opt_devfs.h"
56d2fb4892SJoerg Wunsch #include "opt_fdc.h"
575b81b6b3SRodney W. Grimes 
58b99f0a4aSAndrew Moore #if NFDC > 0
59b99f0a4aSAndrew Moore 
60b99f0a4aSAndrew Moore #include <sys/param.h>
61b99f0a4aSAndrew Moore #include <sys/systm.h>
62b99f0a4aSAndrew Moore #include <sys/kernel.h>
63b99f0a4aSAndrew Moore #include <sys/conf.h>
643ac4d1efSBruce Evans #include <sys/fcntl.h>
65671e2ceeSBruce Evans #include <machine/clock.h>
66b99f0a4aSAndrew Moore #include <machine/ioctl_fd.h>
67b99f0a4aSAndrew Moore #include <sys/disklabel.h>
68b99f0a4aSAndrew Moore #include <sys/buf.h>
69b2dfb1f9SJustin T. Gibbs #include <sys/devicestat.h>
70b99f0a4aSAndrew Moore #include <sys/malloc.h>
713a2f7427SDavid Greenman #include <sys/proc.h>
72b99f0a4aSAndrew Moore #include <sys/syslog.h>
73f540b106SGarrett Wollman #include <i386/isa/isa.h>
74f540b106SGarrett Wollman #include <i386/isa/isa_device.h>
75f540b106SGarrett Wollman #include <i386/isa/fdreg.h>
76f540b106SGarrett Wollman #include <i386/isa/fdc.h>
77f540b106SGarrett Wollman #include <i386/isa/rtc.h>
78dc5df763SJoerg Wunsch #include <machine/stdarg.h>
798af5d536SJulian Elischer #ifdef	DEVFS
808af5d536SJulian Elischer #include <sys/devfsext.h>
813e425b96SJulian Elischer #endif	/* DEVFS */
825b81b6b3SRodney W. Grimes 
83b39c878eSAndrey A. Chernov /* misuse a flag to identify format operation */
84b39c878eSAndrey A. Chernov #define B_FORMAT B_XXX
855b81b6b3SRodney W. Grimes 
860722d6abSJoerg Wunsch /* configuration flags */
870722d6abSJoerg Wunsch #define FDC_PRETEND_D0	(1 << 0)	/* pretend drive 0 to be there */
8869acd21dSWarner Losh #ifdef FDC_YE
8969acd21dSWarner Losh #define FDC_IS_PCMCIA  (1 << 1)		/* if successful probe, then it's
9069acd21dSWarner Losh 					   a PCMCIA device */
9169acd21dSWarner Losh #endif
920722d6abSJoerg Wunsch 
930722d6abSJoerg Wunsch /* internally used only, not really from CMOS: */
940722d6abSJoerg Wunsch #define RTCFDT_144M_PRETENDED	0x1000
950722d6abSJoerg Wunsch 
963a2f7427SDavid Greenman /*
973a2f7427SDavid Greenman  * this biotab field doubles as a field for the physical unit number
983a2f7427SDavid Greenman  * on the controller
993a2f7427SDavid Greenman  */
1003a2f7427SDavid Greenman #define id_physid id_scsiid
1013a2f7427SDavid Greenman 
102dc5df763SJoerg Wunsch /* error returns for fd_cmd() */
103dc5df763SJoerg Wunsch #define FD_FAILED -1
104dc5df763SJoerg Wunsch #define FD_NOT_VALID -2
105dc5df763SJoerg Wunsch #define FDC_ERRMAX	100	/* do not log more */
106dc5df763SJoerg Wunsch 
107b39c878eSAndrey A. Chernov #define NUMTYPES 14
108b39c878eSAndrey A. Chernov #define NUMDENS  (NUMTYPES - 6)
1097ca0641bSAndrey A. Chernov 
1103a2f7427SDavid Greenman /* These defines (-1) must match index for fd_types */
111b99f0a4aSAndrew Moore #define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
112b99f0a4aSAndrew Moore #define NO_TYPE		0	/* must match NO_TYPE in ft.c */
113b99f0a4aSAndrew Moore #define FD_1720         1
114b99f0a4aSAndrew Moore #define FD_1480         2
115b99f0a4aSAndrew Moore #define FD_1440         3
116b99f0a4aSAndrew Moore #define FD_1200         4
117b99f0a4aSAndrew Moore #define FD_820          5
118b99f0a4aSAndrew Moore #define FD_800          6
119b99f0a4aSAndrew Moore #define FD_720          7
120b99f0a4aSAndrew Moore #define FD_360          8
121ed2fa05eSAndrey A. Chernov 
122b99f0a4aSAndrew Moore #define FD_1480in5_25   9
123b99f0a4aSAndrew Moore #define FD_1440in5_25   10
124b99f0a4aSAndrew Moore #define FD_820in5_25    11
125b99f0a4aSAndrew Moore #define FD_800in5_25    12
126b99f0a4aSAndrew Moore #define FD_720in5_25    13
127b99f0a4aSAndrew Moore #define FD_360in5_25    14
128b99f0a4aSAndrew Moore 
1297ca0641bSAndrey A. Chernov 
1306f4e0bebSPoul-Henning Kamp static struct fd_type fd_types[NUMTYPES] =
1315b81b6b3SRodney W. Grimes {
132126518a1SAndrey A. Chernov { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
133126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
134126518a1SAndrey A. Chernov { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
135126518a1SAndrey A. Chernov { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
136126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
137126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
138126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
139b0568305SAndrey A. Chernov {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
140ed2fa05eSAndrey A. Chernov 
141126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
142126518a1SAndrey A. Chernov { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
143126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
144126518a1SAndrey A. Chernov { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
145126518a1SAndrey A. Chernov {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
146126518a1SAndrey A. Chernov {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
1475b81b6b3SRodney W. Grimes };
1485b81b6b3SRodney W. Grimes 
149b99f0a4aSAndrew Moore #define DRVS_PER_CTLR 2		/* 2 floppies */
150dc16046fSJoerg Wunsch 
1515b81b6b3SRodney W. Grimes /***********************************************************************\
1525b81b6b3SRodney W. Grimes * Per controller structure.						*
1535b81b6b3SRodney W. Grimes \***********************************************************************/
154b99f0a4aSAndrew Moore struct fdc_data fdc_data[NFDC];
1555b81b6b3SRodney W. Grimes 
1565b81b6b3SRodney W. Grimes /***********************************************************************\
1575b81b6b3SRodney W. Grimes * Per drive structure.							*
158b99f0a4aSAndrew Moore * N per controller  (DRVS_PER_CTLR)					*
1595b81b6b3SRodney W. Grimes \***********************************************************************/
1606f4e0bebSPoul-Henning Kamp static struct fd_data {
161b99f0a4aSAndrew Moore 	struct	fdc_data *fdc;	/* pointer to controller structure */
1625b81b6b3SRodney W. Grimes 	int	fdsu;		/* this units number on this controller */
1633a2f7427SDavid Greenman 	int	type;		/* Drive type (FD_1440...) */
1645b81b6b3SRodney W. Grimes 	struct	fd_type *ft;	/* pointer to the type descriptor */
1655b81b6b3SRodney W. Grimes 	int	flags;
1665b81b6b3SRodney W. Grimes #define	FD_OPEN		0x01	/* it's open		*/
1675b81b6b3SRodney W. Grimes #define	FD_ACTIVE	0x02	/* it's active		*/
1685b81b6b3SRodney W. Grimes #define	FD_MOTOR	0x04	/* motor should be on	*/
1695b81b6b3SRodney W. Grimes #define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
1705b81b6b3SRodney W. Grimes 	int	skip;
1715b81b6b3SRodney W. Grimes 	int	hddrv;
172dc5df763SJoerg Wunsch #define FD_NO_TRACK -2
1735b81b6b3SRodney W. Grimes 	int	track;		/* where we think the head is */
1743a2f7427SDavid Greenman 	int	options;	/* user configurable options, see ioctl_fd.h */
17502a19910SJustin T. Gibbs 	struct	callout_handle toffhandle;
17602a19910SJustin T. Gibbs 	struct	callout_handle tohandle;
177b2dfb1f9SJustin T. Gibbs 	struct	devstat device_stats;
17887f6c662SJulian Elischer #ifdef DEVFS
17921519754SBruce Evans 	void	*bdevs[1 + NUMDENS + MAXPARTITIONS];
18021519754SBruce Evans 	void	*cdevs[1 + NUMDENS + MAXPARTITIONS];
18187f6c662SJulian Elischer #endif
1825b81b6b3SRodney W. Grimes } fd_data[NFD];
1835b81b6b3SRodney W. Grimes 
1845b81b6b3SRodney W. Grimes /***********************************************************************\
1855b81b6b3SRodney W. Grimes * Throughout this file the following conventions will be used:		*
1865b81b6b3SRodney W. Grimes * fd is a pointer to the fd_data struct for the drive in question	*
1875b81b6b3SRodney W. Grimes * fdc is a pointer to the fdc_data struct for the controller		*
1885b81b6b3SRodney W. Grimes * fdu is the floppy drive unit number					*
1895b81b6b3SRodney W. Grimes * fdcu is the floppy controller unit number				*
1905b81b6b3SRodney W. Grimes * fdsu is the floppy drive unit number on that controller. (sub-unit)	*
1915b81b6b3SRodney W. Grimes \***********************************************************************/
192b99f0a4aSAndrew Moore 
19369acd21dSWarner Losh #ifdef FDC_YE
19469acd21dSWarner Losh #include "card.h"
19569acd21dSWarner Losh static int yeattach(struct isa_device *);
19669acd21dSWarner Losh #endif
19769acd21dSWarner Losh 
1983a2f7427SDavid Greenman /* autoconfig functions */
1993a2f7427SDavid Greenman static int fdprobe(struct isa_device *);
2003a2f7427SDavid Greenman static int fdattach(struct isa_device *);
2013a2f7427SDavid Greenman 
2023a2f7427SDavid Greenman /* needed for ft driver, thus exported */
2033a2f7427SDavid Greenman int in_fdc(fdcu_t);
2043a2f7427SDavid Greenman int out_fdc(fdcu_t, int);
2053a2f7427SDavid Greenman 
2063a2f7427SDavid Greenman /* internal functions */
2073a2f7427SDavid Greenman static void set_motor(fdcu_t, int, int);
2083a2f7427SDavid Greenman #  define TURNON 1
2093a2f7427SDavid Greenman #  define TURNOFF 0
2103a2f7427SDavid Greenman static timeout_t fd_turnoff;
2113a2f7427SDavid Greenman static timeout_t fd_motor_on;
2123a2f7427SDavid Greenman static void fd_turnon(fdu_t);
2133a2f7427SDavid Greenman static void fdc_reset(fdc_p);
214b5e8ce9fSBruce Evans static int fd_in(fdcu_t, int *);
2153a2f7427SDavid Greenman static void fdstart(fdcu_t);
2165c1a1eaeSBruce Evans static timeout_t fd_iotimeout;
2173a2f7427SDavid Greenman static timeout_t fd_pseudointr;
218fe310de8SBruce Evans static ointhand2_t fdintr;
2193a2f7427SDavid Greenman static int fdstate(fdcu_t, fdc_p);
220aaf08d94SGarrett Wollman static int retrier(fdcu_t);
2213a2f7427SDavid Greenman static int fdformat(dev_t, struct fd_formb *, struct proc *);
2223a2f7427SDavid Greenman 
223d66c374fSTor Egge static int enable_fifo(fdc_p fdc);
224d66c374fSTor Egge 
225d66c374fSTor Egge static int fifo_threshold = 8;	/* XXX: should be accessible via sysctl */
226d66c374fSTor Egge 
227aaf08d94SGarrett Wollman 
2285b81b6b3SRodney W. Grimes #define DEVIDLE		0
2295b81b6b3SRodney W. Grimes #define FINDWORK	1
2305b81b6b3SRodney W. Grimes #define	DOSEEK		2
2315b81b6b3SRodney W. Grimes #define SEEKCOMPLETE 	3
2325b81b6b3SRodney W. Grimes #define	IOCOMPLETE	4
2335b81b6b3SRodney W. Grimes #define RECALCOMPLETE	5
2345b81b6b3SRodney W. Grimes #define	STARTRECAL	6
2355b81b6b3SRodney W. Grimes #define	RESETCTLR	7
2365b81b6b3SRodney W. Grimes #define	SEEKWAIT	8
2375b81b6b3SRodney W. Grimes #define	RECALWAIT	9
2385b81b6b3SRodney W. Grimes #define	MOTORWAIT	10
2395b81b6b3SRodney W. Grimes #define	IOTIMEDOUT	11
2405c1a1eaeSBruce Evans #define	RESETCOMPLETE	12
24169acd21dSWarner Losh #ifdef FDC_YE
24269acd21dSWarner Losh #define PIOREAD		13
24369acd21dSWarner Losh #endif
2445b81b6b3SRodney W. Grimes 
245d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
246cba2a7c6SBruce Evans static char const * const fdstates[] =
2475b81b6b3SRodney W. Grimes {
2485b81b6b3SRodney W. Grimes "DEVIDLE",
2495b81b6b3SRodney W. Grimes "FINDWORK",
2505b81b6b3SRodney W. Grimes "DOSEEK",
2515b81b6b3SRodney W. Grimes "SEEKCOMPLETE",
2525b81b6b3SRodney W. Grimes "IOCOMPLETE",
2535b81b6b3SRodney W. Grimes "RECALCOMPLETE",
2545b81b6b3SRodney W. Grimes "STARTRECAL",
2555b81b6b3SRodney W. Grimes "RESETCTLR",
2565b81b6b3SRodney W. Grimes "SEEKWAIT",
2575b81b6b3SRodney W. Grimes "RECALWAIT",
2585b81b6b3SRodney W. Grimes "MOTORWAIT",
2595c1a1eaeSBruce Evans "IOTIMEDOUT",
2605c1a1eaeSBruce Evans "RESETCOMPLETE",
26169acd21dSWarner Losh #ifdef FDC_YE
262ff9607b0SBruce Evans "PIOREAD",
26369acd21dSWarner Losh #endif
2645b81b6b3SRodney W. Grimes };
2655b81b6b3SRodney W. Grimes 
2663a2f7427SDavid Greenman /* CAUTION: fd_debug causes huge amounts of logging output */
267cba2a7c6SBruce Evans static int volatile fd_debug = 0;
2685b81b6b3SRodney W. Grimes #define TRACE0(arg) if(fd_debug) printf(arg)
2695b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
270d2fb4892SJoerg Wunsch #else /* FDC_DEBUG */
2715b81b6b3SRodney W. Grimes #define TRACE0(arg)
2725b81b6b3SRodney W. Grimes #define TRACE1(arg1, arg2)
273d2fb4892SJoerg Wunsch #endif /* FDC_DEBUG */
2745b81b6b3SRodney W. Grimes 
27569acd21dSWarner Losh #ifdef FDC_YE
27669acd21dSWarner Losh #if NCARD > 0
27769acd21dSWarner Losh #include <sys/select.h>
278d17e4ee6SPeter Wemm #include <sys/module.h>
27969acd21dSWarner Losh #include <pccard/cardinfo.h>
28069acd21dSWarner Losh #include <pccard/driver.h>
28169acd21dSWarner Losh #include <pccard/slot.h>
28269acd21dSWarner Losh 
28369acd21dSWarner Losh /*
28469acd21dSWarner Losh  *	PC-Card (PCMCIA) specific code.
28569acd21dSWarner Losh  */
28669acd21dSWarner Losh static int yeinit(struct pccard_devinfo *);		/* init device */
28769acd21dSWarner Losh static void yeunload(struct pccard_devinfo *); 		/* Disable driver */
28869acd21dSWarner Losh static int yeintr(struct pccard_devinfo *); 		/* Interrupt handler */
28969acd21dSWarner Losh 
290d17e4ee6SPeter Wemm PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask);
29169acd21dSWarner Losh 
29269acd21dSWarner Losh /*
29369acd21dSWarner Losh  * this is the secret PIO data port (offset from base)
29469acd21dSWarner Losh  */
29569acd21dSWarner Losh #define FDC_YE_DATAPORT 6
29669acd21dSWarner Losh 
29769acd21dSWarner Losh /*
29869acd21dSWarner Losh  *	Initialize the device - called from Slot manager.
29969acd21dSWarner Losh  */
30069acd21dSWarner Losh static int yeinit(struct pccard_devinfo *devi)
30169acd21dSWarner Losh {
30269acd21dSWarner Losh 	fdc_p fdc = &fdc_data[devi->isahd.id_unit];
30369acd21dSWarner Losh 
30469acd21dSWarner Losh 	/* validate unit number. */
30569acd21dSWarner Losh 	if (devi->isahd.id_unit >= NFDC)
30669acd21dSWarner Losh 		return(ENODEV);
30769acd21dSWarner Losh 	fdc->baseport = devi->isahd.id_iobase;
30869acd21dSWarner Losh 	/*
30969acd21dSWarner Losh 	 * reset controller
31069acd21dSWarner Losh 	 */
31169acd21dSWarner Losh 	outb(fdc->baseport+FDOUT, 0);
31269acd21dSWarner Losh 	DELAY(100);
31369acd21dSWarner Losh 	outb(fdc->baseport+FDOUT, FDO_FRST);
31469acd21dSWarner Losh 
31569acd21dSWarner Losh 	/*
31669acd21dSWarner Losh 	 * wire into system
31769acd21dSWarner Losh 	 */
31869acd21dSWarner Losh 	if (yeattach(&devi->isahd) == 0)
31969acd21dSWarner Losh 		return(ENXIO);
32069acd21dSWarner Losh 
32169acd21dSWarner Losh 	return(0);
32269acd21dSWarner Losh }
32369acd21dSWarner Losh 
32469acd21dSWarner Losh /*
32569acd21dSWarner Losh  *	yeunload - unload the driver and clear the table.
32669acd21dSWarner Losh  *	XXX TODO:
32769acd21dSWarner Losh  *	This is usually called when the card is ejected, but
32869acd21dSWarner Losh  *	can be caused by a modunload of a controller driver.
32969acd21dSWarner Losh  *	The idea is to reset the driver's view of the device
33069acd21dSWarner Losh  *	and ensure that any driver entry points such as
33169acd21dSWarner Losh  *	read and write do not hang.
33269acd21dSWarner Losh  */
33369acd21dSWarner Losh static void yeunload(struct pccard_devinfo *devi)
33469acd21dSWarner Losh {
33569acd21dSWarner Losh 	if (fd_data[devi->isahd.id_unit].type == NO_TYPE)
33669acd21dSWarner Losh 		return;
33769acd21dSWarner Losh 
33869acd21dSWarner Losh 	/*
33969acd21dSWarner Losh 	 * this prevents Fdopen() and fdstrategy() from attempting
34069acd21dSWarner Losh 	 * to access unloaded controller
34169acd21dSWarner Losh 	 */
34269acd21dSWarner Losh 	fd_data[devi->isahd.id_unit].type = NO_TYPE;
34369acd21dSWarner Losh 
34469acd21dSWarner Losh 	printf("fdc%d: unload\n", devi->isahd.id_unit);
34569acd21dSWarner Losh }
34669acd21dSWarner Losh 
34769acd21dSWarner Losh /*
34869acd21dSWarner Losh  *	yeintr - Shared interrupt called from
34969acd21dSWarner Losh  *	front end of PC-Card handler.
35069acd21dSWarner Losh  */
35169acd21dSWarner Losh static int yeintr(struct pccard_devinfo *devi)
35269acd21dSWarner Losh {
35369acd21dSWarner Losh 	fdintr((fdcu_t)devi->isahd.id_unit);
35469acd21dSWarner Losh 	return(1);
35569acd21dSWarner Losh }
35669acd21dSWarner Losh #endif /* NCARD > 0 */
35769acd21dSWarner Losh #endif /* FDC_YE */
35869acd21dSWarner Losh 
35969acd21dSWarner Losh 
360dc16046fSJoerg Wunsch /* autoconfig structure */
361dc16046fSJoerg Wunsch 
362dc16046fSJoerg Wunsch struct	isa_driver fdcdriver = {
363dc16046fSJoerg Wunsch 	fdprobe, fdattach, "fdc",
364dc16046fSJoerg Wunsch };
365dc16046fSJoerg Wunsch 
36687f6c662SJulian Elischer static	d_open_t	Fdopen;	/* NOTE, not fdopen */
367f7ea2f55SJulian Elischer static	d_read_t	fdread;
368f7ea2f55SJulian Elischer static	d_write_t	fdwrite;
36987f6c662SJulian Elischer static	d_close_t	fdclose;
37087f6c662SJulian Elischer static	d_ioctl_t	fdioctl;
37187f6c662SJulian Elischer static	d_strategy_t	fdstrategy;
37287f6c662SJulian Elischer 
3733e425b96SJulian Elischer /* even if SLICE defined, these are needed for the ft support. */
37487f6c662SJulian Elischer #define CDEV_MAJOR 9
37587f6c662SJulian Elischer #define BDEV_MAJOR 2
376f7ea2f55SJulian Elischer 
377f7ea2f55SJulian Elischer 
378f7ea2f55SJulian Elischer static struct cdevsw fd_cdevsw = {
379f7ea2f55SJulian Elischer 	  Fdopen,	fdclose,	fdread,	fdwrite,
380f7ea2f55SJulian Elischer 	  fdioctl,	nostop,		nullreset,	nodevtotty,
381f7ea2f55SJulian Elischer 	  seltrue,	nommap,		fdstrategy,	"fd",
382f7ea2f55SJulian Elischer 	  NULL,		-1,		nodump,		nopsize,
383f7ea2f55SJulian Elischer 	  D_DISK,	0,		-1 };
38487f6c662SJulian Elischer 
38587f6c662SJulian Elischer 
3866f4e0bebSPoul-Henning Kamp static struct isa_device *fdcdevs[NFDC];
38792200632SGarrett Wollman 
3883e425b96SJulian Elischer 
389dc5df763SJoerg Wunsch static int
390dc5df763SJoerg Wunsch fdc_err(fdcu_t fdcu, const char *s)
391dc5df763SJoerg Wunsch {
392dc5df763SJoerg Wunsch 	fdc_data[fdcu].fdc_errs++;
39316b04b6aSJoerg Wunsch 	if(s) {
394dc5df763SJoerg Wunsch 		if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX)
3956a0e6f42SRodney W. Grimes 			printf("fdc%d: %s", fdcu, s);
396dc5df763SJoerg Wunsch 		else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX)
397dc5df763SJoerg Wunsch 			printf("fdc%d: too many errors, not logging any more\n",
398dc5df763SJoerg Wunsch 			    fdcu);
39916b04b6aSJoerg Wunsch 	}
400dc5df763SJoerg Wunsch 
401dc5df763SJoerg Wunsch 	return FD_FAILED;
402dc5df763SJoerg Wunsch }
403dc5df763SJoerg Wunsch 
404dc5df763SJoerg Wunsch /*
405dc5df763SJoerg Wunsch  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
406dc5df763SJoerg Wunsch  * Unit number,
407dc5df763SJoerg Wunsch  * # of output bytes, output bytes as ints ...,
408dc5df763SJoerg Wunsch  * # of input bytes, input bytes as ints ...
409dc5df763SJoerg Wunsch  */
410dc5df763SJoerg Wunsch 
4116f4e0bebSPoul-Henning Kamp static int
412dc5df763SJoerg Wunsch fd_cmd(fdcu_t fdcu, int n_out, ...)
413dc5df763SJoerg Wunsch {
414dc5df763SJoerg Wunsch 	u_char cmd;
415dc5df763SJoerg Wunsch 	int n_in;
416dc5df763SJoerg Wunsch 	int n;
417dc5df763SJoerg Wunsch 	va_list ap;
418dc5df763SJoerg Wunsch 
419dc5df763SJoerg Wunsch 	va_start(ap, n_out);
420dc5df763SJoerg Wunsch 	cmd = (u_char)(va_arg(ap, int));
421dc5df763SJoerg Wunsch 	va_end(ap);
422dc5df763SJoerg Wunsch 	va_start(ap, n_out);
423dc5df763SJoerg Wunsch 	for (n = 0; n < n_out; n++)
424dc5df763SJoerg Wunsch 	{
425dc5df763SJoerg Wunsch 		if (out_fdc(fdcu, va_arg(ap, int)) < 0)
426dc5df763SJoerg Wunsch 		{
427dc5df763SJoerg Wunsch 			char msg[50];
4282127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
429dc5df763SJoerg Wunsch 				"cmd %x failed at out byte %d of %d\n",
430dc5df763SJoerg Wunsch 				cmd, n + 1, n_out);
431dc5df763SJoerg Wunsch 			return fdc_err(fdcu, msg);
432dc5df763SJoerg Wunsch 		}
433dc5df763SJoerg Wunsch 	}
434dc5df763SJoerg Wunsch 	n_in = va_arg(ap, int);
435dc5df763SJoerg Wunsch 	for (n = 0; n < n_in; n++)
436dc5df763SJoerg Wunsch 	{
437dc5df763SJoerg Wunsch 		int *ptr = va_arg(ap, int *);
438dc5df763SJoerg Wunsch 		if (fd_in(fdcu, ptr) < 0)
439dc5df763SJoerg Wunsch 		{
440dc5df763SJoerg Wunsch 			char msg[50];
4412127f260SArchie Cobbs 			snprintf(msg, sizeof(msg),
442dc5df763SJoerg Wunsch 				"cmd %02x failed at in byte %d of %d\n",
443dc5df763SJoerg Wunsch 				cmd, n + 1, n_in);
444dc5df763SJoerg Wunsch 			return fdc_err(fdcu, msg);
445dc5df763SJoerg Wunsch 		}
446dc5df763SJoerg Wunsch 	}
447dc5df763SJoerg Wunsch 
448dc5df763SJoerg Wunsch 	return 0;
449dc5df763SJoerg Wunsch }
450dc5df763SJoerg Wunsch 
4516f4e0bebSPoul-Henning Kamp static int
452d66c374fSTor Egge enable_fifo(fdc_p fdc)
453d66c374fSTor Egge {
454d66c374fSTor Egge 	int i, j;
455d66c374fSTor Egge 
456d66c374fSTor Egge 	if ((fdc->flags & FDC_HAS_FIFO) == 0) {
457d66c374fSTor Egge 
458d66c374fSTor Egge 		/*
459d66c374fSTor Egge 		 * XXX:
460d66c374fSTor Egge 		 * Cannot use fd_cmd the normal way here, since
461d66c374fSTor Egge 		 * this might be an invalid command. Thus we send the
462d66c374fSTor Egge 		 * first byte, and check for an early turn of data directon.
463d66c374fSTor Egge 		 */
464d66c374fSTor Egge 
465d66c374fSTor Egge 		if (out_fdc(fdc->fdcu, I8207X_CONFIGURE) < 0)
466d66c374fSTor Egge 			return fdc_err(fdc->fdcu, "Enable FIFO failed\n");
467d66c374fSTor Egge 
468d66c374fSTor Egge 		/* If command is invalid, return */
469d66c374fSTor Egge 		j = 100000;
470d66c374fSTor Egge 		while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM))
471d66c374fSTor Egge 		       != NE7_RQM && j-- > 0)
472d66c374fSTor Egge 			if (i == (NE7_DIO | NE7_RQM)) {
473d66c374fSTor Egge 				fdc_reset(fdc);
474d66c374fSTor Egge 				return FD_FAILED;
475d66c374fSTor Egge 			}
476d66c374fSTor Egge 		if (j<0 ||
477d66c374fSTor Egge 		    fd_cmd(fdc->fdcu, 3,
478d66c374fSTor Egge 			   0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
479d66c374fSTor Egge 			fdc_reset(fdc);
480d66c374fSTor Egge 			return fdc_err(fdc->fdcu, "Enable FIFO failed\n");
481d66c374fSTor Egge 		}
482d66c374fSTor Egge 		fdc->flags |= FDC_HAS_FIFO;
483d66c374fSTor Egge 		return 0;
484d66c374fSTor Egge 	}
485d66c374fSTor Egge 	if (fd_cmd(fdc->fdcu, 4,
486d66c374fSTor Egge 		   I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
487d66c374fSTor Egge 		return fdc_err(fdc->fdcu, "Re-enable FIFO failed\n");
488d66c374fSTor Egge 	return 0;
489d66c374fSTor Egge }
490d66c374fSTor Egge 
491d66c374fSTor Egge static int
492dc5df763SJoerg Wunsch fd_sense_drive_status(fdc_p fdc, int *st3p)
493dc5df763SJoerg Wunsch {
494dc5df763SJoerg Wunsch 	int st3;
495dc5df763SJoerg Wunsch 
496dc5df763SJoerg Wunsch 	if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
497dc5df763SJoerg Wunsch 	{
4986a0e6f42SRodney W. Grimes 		return fdc_err(fdc->fdcu, "Sense Drive Status failed\n");
499dc5df763SJoerg Wunsch 	}
500dc5df763SJoerg Wunsch 	if (st3p)
501dc5df763SJoerg Wunsch 		*st3p = st3;
502dc5df763SJoerg Wunsch 
503dc5df763SJoerg Wunsch 	return 0;
504dc5df763SJoerg Wunsch }
505dc5df763SJoerg Wunsch 
5066f4e0bebSPoul-Henning Kamp static int
507dc5df763SJoerg Wunsch fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
508dc5df763SJoerg Wunsch {
509dc5df763SJoerg Wunsch 	int st0, cyl;
510dc5df763SJoerg Wunsch 
511dc5df763SJoerg Wunsch 	int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0);
512dc5df763SJoerg Wunsch 
513dc5df763SJoerg Wunsch 	if (ret)
514dc5df763SJoerg Wunsch 	{
515dc5df763SJoerg Wunsch 		(void)fdc_err(fdc->fdcu,
516dc5df763SJoerg Wunsch 			      "sense intr err reading stat reg 0\n");
517dc5df763SJoerg Wunsch 		return ret;
518dc5df763SJoerg Wunsch 	}
519dc5df763SJoerg Wunsch 
520dc5df763SJoerg Wunsch 	if (st0p)
521dc5df763SJoerg Wunsch 		*st0p = st0;
522dc5df763SJoerg Wunsch 
523dc5df763SJoerg Wunsch 	if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV)
524dc5df763SJoerg Wunsch 	{
525dc5df763SJoerg Wunsch 		/*
526dc5df763SJoerg Wunsch 		 * There doesn't seem to have been an interrupt.
527dc5df763SJoerg Wunsch 		 */
528dc5df763SJoerg Wunsch 		return FD_NOT_VALID;
529dc5df763SJoerg Wunsch 	}
530dc5df763SJoerg Wunsch 
531dc5df763SJoerg Wunsch 	if (fd_in(fdc->fdcu, &cyl) < 0)
532dc5df763SJoerg Wunsch 	{
533dc5df763SJoerg Wunsch 		return fdc_err(fdc->fdcu, "can't get cyl num\n");
534dc5df763SJoerg Wunsch 	}
535dc5df763SJoerg Wunsch 
536dc5df763SJoerg Wunsch 	if (cylp)
537dc5df763SJoerg Wunsch 		*cylp = cyl;
538dc5df763SJoerg Wunsch 
539dc5df763SJoerg Wunsch 	return 0;
540dc5df763SJoerg Wunsch }
541dc5df763SJoerg Wunsch 
542dc5df763SJoerg Wunsch 
5436f4e0bebSPoul-Henning Kamp static int
544dc5df763SJoerg Wunsch fd_read_status(fdc_p fdc, int fdsu)
545dc5df763SJoerg Wunsch {
546dc5df763SJoerg Wunsch 	int i, ret;
547b5e8ce9fSBruce Evans 
548dc5df763SJoerg Wunsch 	for (i = 0; i < 7; i++)
549dc5df763SJoerg Wunsch 	{
550b5e8ce9fSBruce Evans 		/*
551b5e8ce9fSBruce Evans 		 * XXX types are poorly chosen.  Only bytes can by read
552a838d83dSBruce Evans 		 * from the hardware, but fdc->status[] wants u_ints and
553b5e8ce9fSBruce Evans 		 * fd_in() gives ints.
554b5e8ce9fSBruce Evans 		 */
555b5e8ce9fSBruce Evans 		int status;
556b5e8ce9fSBruce Evans 
557b5e8ce9fSBruce Evans 		ret = fd_in(fdc->fdcu, &status);
558b5e8ce9fSBruce Evans 		fdc->status[i] = status;
559b5e8ce9fSBruce Evans 		if (ret != 0)
560dc5df763SJoerg Wunsch 			break;
561dc5df763SJoerg Wunsch 	}
562dc5df763SJoerg Wunsch 
563dc5df763SJoerg Wunsch 	if (ret == 0)
564dc5df763SJoerg Wunsch 		fdc->flags |= FDC_STAT_VALID;
565dc5df763SJoerg Wunsch 	else
566dc5df763SJoerg Wunsch 		fdc->flags &= ~FDC_STAT_VALID;
567dc5df763SJoerg Wunsch 
568dc5df763SJoerg Wunsch 	return ret;
569dc5df763SJoerg Wunsch }
570dc5df763SJoerg Wunsch 
5715b81b6b3SRodney W. Grimes /****************************************************************************/
5725b81b6b3SRodney W. Grimes /*                      autoconfiguration stuff                             */
5735b81b6b3SRodney W. Grimes /****************************************************************************/
574dc5df763SJoerg Wunsch 
5755b81b6b3SRodney W. Grimes /*
5765b81b6b3SRodney W. Grimes  * probe for existance of controller
5775b81b6b3SRodney W. Grimes  */
5783a2f7427SDavid Greenman static int
579dc5df763SJoerg Wunsch fdprobe(struct isa_device *dev)
5805b81b6b3SRodney W. Grimes {
5815b81b6b3SRodney W. Grimes 	fdcu_t	fdcu = dev->id_unit;
5825b81b6b3SRodney W. Grimes 	if(fdc_data[fdcu].flags & FDC_ATTACHED)
5835b81b6b3SRodney W. Grimes 	{
5846a0e6f42SRodney W. Grimes 		printf("fdc%d: unit used multiple times\n", fdcu);
5855b81b6b3SRodney W. Grimes 		return 0;
5865b81b6b3SRodney W. Grimes 	}
5875b81b6b3SRodney W. Grimes 
58892200632SGarrett Wollman 	fdcdevs[fdcu] = dev;
5895b81b6b3SRodney W. Grimes 	fdc_data[fdcu].baseport = dev->id_iobase;
5905b81b6b3SRodney W. Grimes 
59116111cedSAndrew Moore 	/* First - lets reset the floppy controller */
5923a2f7427SDavid Greenman 	outb(dev->id_iobase+FDOUT, 0);
59316111cedSAndrew Moore 	DELAY(100);
5943a2f7427SDavid Greenman 	outb(dev->id_iobase+FDOUT, FDO_FRST);
59516111cedSAndrew Moore 
5965b81b6b3SRodney W. Grimes 	/* see if it can handle a command */
597dc5df763SJoerg Wunsch 	if (fd_cmd(fdcu,
598dc5df763SJoerg Wunsch 		   3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
599dc5df763SJoerg Wunsch 		   0))
6005b81b6b3SRodney W. Grimes 	{
6015b81b6b3SRodney W. Grimes 		return(0);
6025b81b6b3SRodney W. Grimes 	}
60369acd21dSWarner Losh #ifdef FDC_YE
60469acd21dSWarner Losh 	/*
60569acd21dSWarner Losh 	 * don't succeed on probe; wait
60669acd21dSWarner Losh 	 * for PCCARD subsystem to do it
60769acd21dSWarner Losh 	 */
60869acd21dSWarner Losh 	if (dev->id_flags & FDC_IS_PCMCIA)
60969acd21dSWarner Losh 		return(0);
61069acd21dSWarner Losh #endif
6115b81b6b3SRodney W. Grimes 	return (IO_FDCSIZE);
6125b81b6b3SRodney W. Grimes }
6135b81b6b3SRodney W. Grimes 
6145b81b6b3SRodney W. Grimes /*
6155b81b6b3SRodney W. Grimes  * wire controller into system, look for floppy units
6165b81b6b3SRodney W. Grimes  */
6173a2f7427SDavid Greenman static int
618dc5df763SJoerg Wunsch fdattach(struct isa_device *dev)
6195b81b6b3SRodney W. Grimes {
6203a2f7427SDavid Greenman 	unsigned fdt;
6215b81b6b3SRodney W. Grimes 	fdu_t	fdu;
6225b81b6b3SRodney W. Grimes 	fdcu_t	fdcu = dev->id_unit;
6235b81b6b3SRodney W. Grimes 	fdc_p	fdc = fdc_data + fdcu;
6245b81b6b3SRodney W. Grimes 	fd_p	fd;
625cba2a7c6SBruce Evans 	int	fdsu, st0, st3, i;
626b99f0a4aSAndrew Moore 	struct isa_device *fdup;
627dc5df763SJoerg Wunsch 	int ic_type = 0;
628999422d7SJulian Elischer #ifdef DEVFS
629c8f2fe8dSBruce Evans 	int	mynor;
63021519754SBruce Evans 	int	typemynor;
63121519754SBruce Evans 	int	typesize;
63221519754SBruce Evans #endif
63392200632SGarrett Wollman 
634fe310de8SBruce Evans 	dev->id_ointr = fdintr;
6355b81b6b3SRodney W. Grimes 	fdc->fdcu = fdcu;
6365b81b6b3SRodney W. Grimes 	fdc->flags |= FDC_ATTACHED;
6375b81b6b3SRodney W. Grimes 	fdc->dmachan = dev->id_drq;
638100f78bbSSujal Patel 	/* Acquire the DMA channel forever, The driver will do the rest */
639100f78bbSSujal Patel 	isa_dma_acquire(fdc->dmachan);
640dd87702aSBruce Evans 	isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
6415b81b6b3SRodney W. Grimes 	fdc->state = DEVIDLE;
6423a2f7427SDavid Greenman 	/* reset controller, turn motor off, clear fdout mirror reg */
6433a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
64402a19910SJustin T. Gibbs 	bufq_init(&fdc->head);
6455b81b6b3SRodney W. Grimes 
6465b81b6b3SRodney W. Grimes 	/* check for each floppy drive */
647b99f0a4aSAndrew Moore 	for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
648b99f0a4aSAndrew Moore 		if (fdup->id_iobase != dev->id_iobase)
649b99f0a4aSAndrew Moore 			continue;
650b99f0a4aSAndrew Moore 		fdu = fdup->id_unit;
651b99f0a4aSAndrew Moore 		fd = &fd_data[fdu];
652fc47545eSPoul-Henning Kamp 		if (fdu >= (NFD))
653b99f0a4aSAndrew Moore 			continue;
654b99f0a4aSAndrew Moore 		fdsu = fdup->id_physid;
655b99f0a4aSAndrew Moore 		/* look up what bios thinks we have */
656b99f0a4aSAndrew Moore 		switch (fdu) {
6570722d6abSJoerg Wunsch 			case 0: if (dev->id_flags & FDC_PRETEND_D0)
6580722d6abSJoerg Wunsch 					fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
6590722d6abSJoerg Wunsch 				else
6600722d6abSJoerg Wunsch 					fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
661b99f0a4aSAndrew Moore 				break;
662b99f0a4aSAndrew Moore 			case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
663b99f0a4aSAndrew Moore 				break;
664b99f0a4aSAndrew Moore 			default: fdt = RTCFDT_NONE;
665b99f0a4aSAndrew Moore 				break;
666b99f0a4aSAndrew Moore 		}
6675b81b6b3SRodney W. Grimes 		/* is there a unit? */
668b99f0a4aSAndrew Moore 		if ((fdt == RTCFDT_NONE)
669b99f0a4aSAndrew Moore 		) {
67056ef0285SAndrew Moore 			fd->type = NO_TYPE;
6715b81b6b3SRodney W. Grimes 			continue;
672f5f7ba03SJordan K. Hubbard 		}
6735b81b6b3SRodney W. Grimes 
6745b81b6b3SRodney W. Grimes 		/* select it */
6753a2f7427SDavid Greenman 		set_motor(fdcu, fdsu, TURNON);
6766b7bd95bSJoerg Wunsch 		DELAY(1000000);	/* 1 sec */
677dc5df763SJoerg Wunsch 
678dc5df763SJoerg Wunsch 		if (ic_type == 0 &&
679dc5df763SJoerg Wunsch 		    fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0)
680dc5df763SJoerg Wunsch 		{
681d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6826a0e6f42SRodney W. Grimes 			printf("fdc%d: ", fdcu);
683d2fb4892SJoerg Wunsch #endif
684dc5df763SJoerg Wunsch 			ic_type = (u_char)ic_type;
685dc5df763SJoerg Wunsch 			switch( ic_type ) {
686dc5df763SJoerg Wunsch 			case 0x80:
687d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6886a0e6f42SRodney W. Grimes 				printf("NEC 765\n");
689d2fb4892SJoerg Wunsch #endif
690dc5df763SJoerg Wunsch 				fdc->fdct = FDC_NE765;
691dc5df763SJoerg Wunsch 				break;
692dc5df763SJoerg Wunsch 			case 0x81:
693d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
6946a0e6f42SRodney W. Grimes 				printf("Intel 82077\n");
695d2fb4892SJoerg Wunsch #endif
696dc5df763SJoerg Wunsch 				fdc->fdct = FDC_I82077;
697dc5df763SJoerg Wunsch 				break;
698dc5df763SJoerg Wunsch 			case 0x90:
699d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
7006a0e6f42SRodney W. Grimes 				printf("NEC 72065B\n");
701d2fb4892SJoerg Wunsch #endif
702dc5df763SJoerg Wunsch 				fdc->fdct = FDC_NE72065;
703dc5df763SJoerg Wunsch 				break;
704dc5df763SJoerg Wunsch 			default:
705d2fb4892SJoerg Wunsch #ifdef FDC_PRINT_BOGUS_CHIPTYPE
7066a0e6f42SRodney W. Grimes 				printf("unknown IC type %02x\n", ic_type);
707d2fb4892SJoerg Wunsch #endif
708dc5df763SJoerg Wunsch 				fdc->fdct = FDC_UNKNOWN;
709dc5df763SJoerg Wunsch 				break;
7106b7bd95bSJoerg Wunsch 			}
711d66c374fSTor Egge 			if (fdc->fdct != FDC_NE765 &&
712d66c374fSTor Egge 			    fdc->fdct != FDC_UNKNOWN &&
713d66c374fSTor Egge 			    enable_fifo(fdc) == 0) {
714d66c374fSTor Egge 				printf("fdc%d: FIFO enabled", fdcu);
715d66c374fSTor Egge 				printf(", %d bytes threshold\n",
716d66c374fSTor Egge 				    fifo_threshold);
717d66c374fSTor Egge 			}
718dc5df763SJoerg Wunsch 		}
719dc5df763SJoerg Wunsch 		if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
720dc5df763SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
721dc5df763SJoerg Wunsch 			/* if at track 0, first seek inwards */
722dc5df763SJoerg Wunsch 			/* seek some steps: */
723dc5df763SJoerg Wunsch 			(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
724dc5df763SJoerg Wunsch 			DELAY(300000); /* ...wait a moment... */
725dc5df763SJoerg Wunsch 			(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
726dc5df763SJoerg Wunsch 		}
727dc5df763SJoerg Wunsch 
728dc5df763SJoerg Wunsch 		/* If we're at track 0 first seek inwards. */
729dc5df763SJoerg Wunsch 		if ((fd_sense_drive_status(fdc, &st3) == 0) &&
730dc5df763SJoerg Wunsch 		    (st3 & NE7_ST3_T0)) {
731dc5df763SJoerg Wunsch 			/* Seek some steps... */
732dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
733dc5df763SJoerg Wunsch 				/* ...wait a moment... */
734dc5df763SJoerg Wunsch 				DELAY(300000);
735dc5df763SJoerg Wunsch 				/* make ctrlr happy: */
736dc5df763SJoerg Wunsch 				(void)fd_sense_int(fdc, 0, 0);
737dc5df763SJoerg Wunsch 			}
738dc5df763SJoerg Wunsch 		}
739dc5df763SJoerg Wunsch 
7406b7bd95bSJoerg Wunsch 		for(i = 0; i < 2; i++) {
7416b7bd95bSJoerg Wunsch 			/*
7426b7bd95bSJoerg Wunsch 			 * we must recalibrate twice, just in case the
7436b7bd95bSJoerg Wunsch 			 * heads have been beyond cylinder 76, since most
7446b7bd95bSJoerg Wunsch 			 * FDCs still barf when attempting to recalibrate
7456b7bd95bSJoerg Wunsch 			 * more than 77 steps
7466b7bd95bSJoerg Wunsch 			 */
747dc5df763SJoerg Wunsch 			/* go back to 0: */
748dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
7496b7bd95bSJoerg Wunsch 				/* a second being enough for full stroke seek*/
7506b7bd95bSJoerg Wunsch 				DELAY(i == 0? 1000000: 300000);
7515b81b6b3SRodney W. Grimes 
7526b7bd95bSJoerg Wunsch 				/* anything responding? */
753dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, 0) == 0 &&
754dc5df763SJoerg Wunsch 				(st0 & NE7_ST0_EC) == 0)
7556b7bd95bSJoerg Wunsch 					break; /* already probed succesfully */
7566b7bd95bSJoerg Wunsch 			}
757dc5df763SJoerg Wunsch 		}
7586b7bd95bSJoerg Wunsch 
7593a2f7427SDavid Greenman 		set_motor(fdcu, fdsu, TURNOFF);
7603a2f7427SDavid Greenman 
7613a2f7427SDavid Greenman 		if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
7625b81b6b3SRodney W. Grimes 			continue;
7635b81b6b3SRodney W. Grimes 
764dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
765b99f0a4aSAndrew Moore 		fd->fdc = fdc;
766b99f0a4aSAndrew Moore 		fd->fdsu = fdsu;
7673a2f7427SDavid Greenman 		fd->options = 0;
76802a19910SJustin T. Gibbs 		callout_handle_init(&fd->toffhandle);
76902a19910SJustin T. Gibbs 		callout_handle_init(&fd->tohandle);
7702d9d0204SRodney W. Grimes 		printf("fd%d: ", fdu);
7715b81b6b3SRodney W. Grimes 
772b99f0a4aSAndrew Moore 		switch (fdt) {
7737ca0641bSAndrey A. Chernov 		case RTCFDT_12M:
7746a0e6f42SRodney W. Grimes 			printf("1.2MB 5.25in\n");
775b99f0a4aSAndrew Moore 			fd->type = FD_1200;
7767ca0641bSAndrey A. Chernov 			break;
7770722d6abSJoerg Wunsch 		case RTCFDT_144M | RTCFDT_144M_PRETENDED:
7780722d6abSJoerg Wunsch 			printf("config-pretended ");
7790722d6abSJoerg Wunsch 			fdt = RTCFDT_144M;
7800722d6abSJoerg Wunsch 			/* fallthrough */
7817ca0641bSAndrey A. Chernov 		case RTCFDT_144M:
7826a0e6f42SRodney W. Grimes 			printf("1.44MB 3.5in\n");
783b99f0a4aSAndrew Moore 			fd->type = FD_1440;
7847ca0641bSAndrey A. Chernov 			break;
785290dd077SJoerg Wunsch 		case RTCFDT_288M:
78686a727d9SJoerg Wunsch 		case RTCFDT_288M_1:
7876a0e6f42SRodney W. Grimes 			printf("2.88MB 3.5in - 1.44MB mode\n");
788290dd077SJoerg Wunsch 			fd->type = FD_1440;
789290dd077SJoerg Wunsch 			break;
7907ca0641bSAndrey A. Chernov 		case RTCFDT_360K:
7916a0e6f42SRodney W. Grimes 			printf("360KB 5.25in\n");
792b99f0a4aSAndrew Moore 			fd->type = FD_360;
7937ca0641bSAndrey A. Chernov 			break;
794ed2fa05eSAndrey A. Chernov 		case RTCFDT_720K:
7956a0e6f42SRodney W. Grimes 			printf("720KB 3.5in\n");
796b99f0a4aSAndrew Moore 			fd->type = FD_720;
797ed2fa05eSAndrey A. Chernov 			break;
7987ca0641bSAndrey A. Chernov 		default:
7996a0e6f42SRodney W. Grimes 			printf("unknown\n");
800b99f0a4aSAndrew Moore 			fd->type = NO_TYPE;
80121519754SBruce Evans 			continue;
8025b81b6b3SRodney W. Grimes 		}
803999422d7SJulian Elischer #ifdef DEVFS
80421519754SBruce Evans 		mynor = fdu << 6;
805f7ea2f55SJulian Elischer 		fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK,
806f85120acSBruce Evans 						UID_ROOT, GID_OPERATOR, 0640,
80721519754SBruce Evans 						"fd%d", fdu);
80821519754SBruce Evans 		fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
809f85120acSBruce Evans 						UID_ROOT, GID_OPERATOR, 0640,
81021519754SBruce Evans 						"rfd%d", fdu);
81121519754SBruce Evans 		for (i = 1; i < 1 + NUMDENS; i++) {
81221519754SBruce Evans 			/*
81321519754SBruce Evans 			 * XXX this and the lookup in Fdopen() should be
81421519754SBruce Evans 			 * data driven.
81521519754SBruce Evans 			 */
81621519754SBruce Evans 			switch (fd->type) {
81721519754SBruce Evans 			case FD_360:
81821519754SBruce Evans 				if (i != FD_360)
81921519754SBruce Evans 					continue;
82021519754SBruce Evans 				break;
82121519754SBruce Evans 			case FD_720:
82221519754SBruce Evans 				if (i != FD_720 && i != FD_800 && i != FD_820)
82321519754SBruce Evans 					continue;
82421519754SBruce Evans 				break;
82521519754SBruce Evans 			case FD_1200:
82621519754SBruce Evans 				if (i != FD_360 && i != FD_720 && i != FD_800
82721519754SBruce Evans 				    && i != FD_820 && i != FD_1200
82821519754SBruce Evans 				    && i != FD_1440 && i != FD_1480)
82921519754SBruce Evans 					continue;
83021519754SBruce Evans 				break;
83121519754SBruce Evans 			case FD_1440:
83221519754SBruce Evans 				if (i != FD_720 && i != FD_800 && i != FD_820
83321519754SBruce Evans 				    && i != FD_1200 && i != FD_1440
83421519754SBruce Evans 				    && i != FD_1480 && i != FD_1720)
83521519754SBruce Evans 					continue;
83621519754SBruce Evans 				break;
83721519754SBruce Evans 			}
83821519754SBruce Evans 			typesize = fd_types[i - 1].size / 2;
83921519754SBruce Evans 			/*
84021519754SBruce Evans 			 * XXX all these conversions give bloated code and
84121519754SBruce Evans 			 * confusing names.
84221519754SBruce Evans 			 */
84321519754SBruce Evans 			if (typesize == 1476)
84421519754SBruce Evans 				typesize = 1480;
84521519754SBruce Evans 			if (typesize == 1722)
84621519754SBruce Evans 				typesize = 1720;
8473e425b96SJulian Elischer 			typemynor = mynor | i;
84821519754SBruce Evans 			fd->bdevs[i] =
849f7ea2f55SJulian Elischer 				devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK,
85021519754SBruce Evans 						 UID_ROOT, GID_OPERATOR, 0640,
85121519754SBruce Evans 						 "fd%d.%d", fdu, typesize);
85221519754SBruce Evans 			fd->cdevs[i] =
85321519754SBruce Evans 				devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR,
85421519754SBruce Evans 						 UID_ROOT, GID_OPERATOR, 0640,
85521519754SBruce Evans 						 "rfd%d.%d", fdu, typesize);
85621519754SBruce Evans 		}
8573e425b96SJulian Elischer 
85821519754SBruce Evans 		for (i = 0; i < MAXPARTITIONS; i++) {
8592ae353f9SEivind Eklund 			fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0],
86021519754SBruce Evans 					   "fd%d%c", fdu, 'a' + i);
86121519754SBruce Evans 			fd->cdevs[1 + NUMDENS + i] =
8622ae353f9SEivind Eklund 				devfs_makelink(fd->cdevs[0],
86321519754SBruce Evans 					   "rfd%d%c", fdu, 'a' + i);
86421519754SBruce Evans 		}
865999422d7SJulian Elischer #endif /* DEVFS */
866671e2ceeSBruce Evans 		/*
867b2dfb1f9SJustin T. Gibbs 		 * Export the drive to the devstat interface.
868671e2ceeSBruce Evans 		 */
869b2dfb1f9SJustin T. Gibbs 		devstat_add_entry(&fd->device_stats, "fd",
870b2dfb1f9SJustin T. Gibbs 				  fdu, 512,
871b2dfb1f9SJustin T. Gibbs 				  DEVSTAT_NO_ORDERED_TAGS,
8722a888f93SKenneth D. Merry 				  DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER,
8732a888f93SKenneth D. Merry 				  DEVSTAT_PRIORITY_FD);
874b2dfb1f9SJustin T. Gibbs 
8755b81b6b3SRodney W. Grimes 	}
8765b81b6b3SRodney W. Grimes 
8773a2f7427SDavid Greenman 	return (1);
8785b81b6b3SRodney W. Grimes }
8795b81b6b3SRodney W. Grimes 
8803e425b96SJulian Elischer 
8813e425b96SJulian Elischer 
88269acd21dSWarner Losh #ifdef FDC_YE
88369acd21dSWarner Losh /*
88469acd21dSWarner Losh  * this is a subset of fdattach() optimized for the Y-E Data
88569acd21dSWarner Losh  * PCMCIA floppy drive.
88669acd21dSWarner Losh  */
88769acd21dSWarner Losh static int yeattach(struct isa_device *dev)
88869acd21dSWarner Losh {
88969acd21dSWarner Losh 	fdcu_t  fdcu = dev->id_unit;
89069acd21dSWarner Losh 	fdc_p   fdc = fdc_data + fdcu;
89169acd21dSWarner Losh 	fdsu_t  fdsu = 0;               /* assume 1 drive per YE controller */
89269acd21dSWarner Losh 	fdu_t   fdu;
89369acd21dSWarner Losh 	fd_p    fd;
89469acd21dSWarner Losh 	int     st0, st3, i;
89569acd21dSWarner Losh #ifdef DEVFS
89669acd21dSWarner Losh 	int     mynor;
89769acd21dSWarner Losh 	int     typemynor;
89869acd21dSWarner Losh 	int     typesize;
89969acd21dSWarner Losh #endif
90069acd21dSWarner Losh 	fdc->fdcu = fdcu;
90169acd21dSWarner Losh 	/*
90269acd21dSWarner Losh 	 * the FDC_PCMCIA flag is used to to indicate special PIO is used
90369acd21dSWarner Losh 	 * instead of DMA
90469acd21dSWarner Losh 	 */
90569acd21dSWarner Losh 	fdc->flags = FDC_ATTACHED|FDC_PCMCIA;
90669acd21dSWarner Losh 	fdc->state = DEVIDLE;
90769acd21dSWarner Losh 	/* reset controller, turn motor off, clear fdout mirror reg */
90869acd21dSWarner Losh 	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
90969acd21dSWarner Losh 	bufq_init(&fdc->head);
91069acd21dSWarner Losh 	/*
91169acd21dSWarner Losh 	 * assume 2 drives/ "normal" controller
91269acd21dSWarner Losh 	 */
91369acd21dSWarner Losh 	fdu = fdcu * 2;
91469acd21dSWarner Losh 	if (fdu >= NFD) {
91569acd21dSWarner Losh 		printf("fdu %d >= NFD\n",fdu);
91669acd21dSWarner Losh 		return(0);
91769acd21dSWarner Losh 	};
91869acd21dSWarner Losh 	fd = &fd_data[fdu];
91969acd21dSWarner Losh 
92069acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNON);
92169acd21dSWarner Losh 	DELAY(1000000); /* 1 sec */
92269acd21dSWarner Losh 	fdc->fdct = FDC_NE765;
92369acd21dSWarner Losh 
92469acd21dSWarner Losh 	if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
92569acd21dSWarner Losh 		(st3 & NE7_ST3_T0)) {
92669acd21dSWarner Losh 		/* if at track 0, first seek inwards */
92769acd21dSWarner Losh 		/* seek some steps: */
92869acd21dSWarner Losh 		(void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
92969acd21dSWarner Losh 		DELAY(300000); /* ...wait a moment... */
93069acd21dSWarner Losh 		(void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
93169acd21dSWarner Losh 	}
93269acd21dSWarner Losh 
93369acd21dSWarner Losh 	/* If we're at track 0 first seek inwards. */
93469acd21dSWarner Losh 	if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
93569acd21dSWarner Losh 		/* Seek some steps... */
93669acd21dSWarner Losh 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
93769acd21dSWarner Losh 			/* ...wait a moment... */
93869acd21dSWarner Losh 			DELAY(300000);
93969acd21dSWarner Losh 			/* make ctrlr happy: */
94069acd21dSWarner Losh 			(void)fd_sense_int(fdc, 0, 0);
94169acd21dSWarner Losh 		}
94269acd21dSWarner Losh 	}
94369acd21dSWarner Losh 
94469acd21dSWarner Losh 	for(i = 0; i < 2; i++) {
94569acd21dSWarner Losh 		/*
94669acd21dSWarner Losh 		 * we must recalibrate twice, just in case the
94769acd21dSWarner Losh 		 * heads have been beyond cylinder 76, since most
94869acd21dSWarner Losh 		 * FDCs still barf when attempting to recalibrate
94969acd21dSWarner Losh 		 * more than 77 steps
95069acd21dSWarner Losh 		 */
95169acd21dSWarner Losh 		/* go back to 0: */
95269acd21dSWarner Losh 		if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
95369acd21dSWarner Losh 			/* a second being enough for full stroke seek*/
95469acd21dSWarner Losh 			DELAY(i == 0? 1000000: 300000);
95569acd21dSWarner Losh 
95669acd21dSWarner Losh 			/* anything responding? */
95769acd21dSWarner Losh 			if (fd_sense_int(fdc, &st0, 0) == 0 &&
95869acd21dSWarner Losh 				(st0 & NE7_ST0_EC) == 0)
95969acd21dSWarner Losh 				break; /* already probed succesfully */
96069acd21dSWarner Losh 		}
96169acd21dSWarner Losh 	}
96269acd21dSWarner Losh 
96369acd21dSWarner Losh 	set_motor(fdcu, fdsu, TURNOFF);
96469acd21dSWarner Losh 
96569acd21dSWarner Losh 	if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
96669acd21dSWarner Losh 		return(0);
96769acd21dSWarner Losh 
96869acd21dSWarner Losh 	fd->track = FD_NO_TRACK;
96969acd21dSWarner Losh 	fd->fdc = fdc;
97069acd21dSWarner Losh 	fd->fdsu = fdsu;
97169acd21dSWarner Losh 	fd->options = 0;
97269acd21dSWarner Losh 	printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu);
97369acd21dSWarner Losh 	fd->type = FD_1440;
97469acd21dSWarner Losh 
97569acd21dSWarner Losh #ifdef DEVFS
97669acd21dSWarner Losh 	mynor = fdcu << 6;
977ff9607b0SBruce Evans 	fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK,
97869acd21dSWarner Losh 		UID_ROOT, GID_OPERATOR, 0640,
97969acd21dSWarner Losh 		"fd%d", fdu);
98069acd21dSWarner Losh 	fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
98169acd21dSWarner Losh 		UID_ROOT, GID_OPERATOR, 0640,
98269acd21dSWarner Losh 		"rfd%d", fdu);
98369acd21dSWarner Losh 	/*
98469acd21dSWarner Losh 	 * XXX this and the lookup in Fdopen() should be
98569acd21dSWarner Losh 	 * data driven.
98669acd21dSWarner Losh 	 */
98769acd21dSWarner Losh 	typemynor = mynor | FD_1440;
98869acd21dSWarner Losh 	typesize = fd_types[FD_1440 - 1].size / 2;
98969acd21dSWarner Losh 	/*
99069acd21dSWarner Losh 	 * XXX all these conversions give bloated code and
99169acd21dSWarner Losh 	 * confusing names.
99269acd21dSWarner Losh 	 */
99369acd21dSWarner Losh 	if (typesize == 1476)
99469acd21dSWarner Losh 		typesize = 1480;
99569acd21dSWarner Losh 	if (typesize == 1722)
99669acd21dSWarner Losh 		typesize = 1720;
997ff9607b0SBruce Evans 	fd->bdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor,
99869acd21dSWarner Losh 		DV_BLK, UID_ROOT, GID_OPERATOR,
99969acd21dSWarner Losh 		0640, "fd%d.%d", fdu, typesize);
100069acd21dSWarner Losh 	fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor,
100169acd21dSWarner Losh 		DV_CHR, UID_ROOT, GID_OPERATOR,
100269acd21dSWarner Losh 		0640,"rfd%d.%d", fdu, typesize);
100369acd21dSWarner Losh 	for (i = 0; i < MAXPARTITIONS; i++) {
1004ff9607b0SBruce Evans 		fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0],
100569acd21dSWarner Losh 			"fd%d%c", fdu, 'a' + i);
1006ff9607b0SBruce Evans 		fd->cdevs[1 + NUMDENS + i] = devfs_makelink(fd->cdevs[0],
100769acd21dSWarner Losh 			"rfd%d%c", fdu, 'a' + i);
100869acd21dSWarner Losh 	}
100969acd21dSWarner Losh #endif /* DEVFS */
101069acd21dSWarner Losh 	return (1);
101169acd21dSWarner Losh }
101269acd21dSWarner Losh #endif
101369acd21dSWarner Losh 
10145b81b6b3SRodney W. Grimes /****************************************************************************/
10155b81b6b3SRodney W. Grimes /*                            motor control stuff                           */
10165e235068SJordan K. Hubbard /*		remember to not deselect the drive we're working on         */
10175b81b6b3SRodney W. Grimes /****************************************************************************/
10183a2f7427SDavid Greenman static void
1019dc5df763SJoerg Wunsch set_motor(fdcu_t fdcu, int fdsu, int turnon)
10205b81b6b3SRodney W. Grimes {
10213a2f7427SDavid Greenman 	int fdout = fdc_data[fdcu].fdout;
10223a2f7427SDavid Greenman 	int needspecify = 0;
10233a2f7427SDavid Greenman 
10243a2f7427SDavid Greenman 	if(turnon) {
10253a2f7427SDavid Greenman 		fdout &= ~FDO_FDSEL;
10263a2f7427SDavid Greenman 		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
10273a2f7427SDavid Greenman 	} else
10283a2f7427SDavid Greenman 		fdout &= ~(FDO_MOEN0 << fdsu);
10293a2f7427SDavid Greenman 
10305e235068SJordan K. Hubbard 	if(!turnon
10315e235068SJordan K. Hubbard 	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
10325e235068SJordan K. Hubbard 		/* gonna turn off the last drive, put FDC to bed */
10335e235068SJordan K. Hubbard 		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
10345e235068SJordan K. Hubbard 	else {
10353a2f7427SDavid Greenman 		/* make sure controller is selected and specified */
10363a2f7427SDavid Greenman 		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
10373a2f7427SDavid Greenman 			needspecify = 1;
10383a2f7427SDavid Greenman 		fdout |= (FDO_FRST|FDO_FDMAEN);
10395b81b6b3SRodney W. Grimes 	}
10405b81b6b3SRodney W. Grimes 
10413a2f7427SDavid Greenman 	outb(fdc_data[fdcu].baseport+FDOUT, fdout);
10423a2f7427SDavid Greenman 	fdc_data[fdcu].fdout = fdout;
10433a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdout);
10443a2f7427SDavid Greenman 
10453a2f7427SDavid Greenman 	if(needspecify) {
1046dc8603e3SJoerg Wunsch 		/*
1047dc5df763SJoerg Wunsch 		 * XXX
1048dc8603e3SJoerg Wunsch 		 * special case: since we have just woken up the FDC
1049dc8603e3SJoerg Wunsch 		 * from its sleep, we silently assume the command will
1050dc8603e3SJoerg Wunsch 		 * be accepted, and do not test for a timeout
1051dc8603e3SJoerg Wunsch 		 */
1052dc5df763SJoerg Wunsch 		(void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
1053dc5df763SJoerg Wunsch 			     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1054dc5df763SJoerg Wunsch 			     0);
1055d66c374fSTor Egge 		if (fdc_data[fdcu].flags & FDC_HAS_FIFO)
1056d66c374fSTor Egge 			(void) enable_fifo(&fdc_data[fdcu]);
10573a2f7427SDavid Greenman 	}
10583a2f7427SDavid Greenman }
10593a2f7427SDavid Greenman 
1060381fe1aaSGarrett Wollman static void
1061d0917939SPaul Richards fd_turnoff(void *arg1)
10625b81b6b3SRodney W. Grimes {
1063381fe1aaSGarrett Wollman 	fdu_t fdu = (fdu_t)arg1;
1064f5f7ba03SJordan K. Hubbard 	int	s;
10655b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
1066dc16046fSJoerg Wunsch 
1067dc16046fSJoerg Wunsch 	TRACE1("[fd%d: turnoff]", fdu);
10688335c1b8SBruce Evans 
10698335c1b8SBruce Evans 	/*
10708335c1b8SBruce Evans 	 * Don't turn off the motor yet if the drive is active.
10718335c1b8SBruce Evans 	 * XXX shouldn't even schedule turnoff until drive is inactive
10728335c1b8SBruce Evans 	 * and nothing is queued on it.
10738335c1b8SBruce Evans 	 */
10748335c1b8SBruce Evans 	if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) {
107502a19910SJustin T. Gibbs 		fd->toffhandle = timeout(fd_turnoff, arg1, 4 * hz);
10768335c1b8SBruce Evans 		return;
10778335c1b8SBruce Evans 	}
10788335c1b8SBruce Evans 
1079f5f7ba03SJordan K. Hubbard 	s = splbio();
10805b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR;
10813a2f7427SDavid Greenman 	set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
1082f5f7ba03SJordan K. Hubbard 	splx(s);
10835b81b6b3SRodney W. Grimes }
10845b81b6b3SRodney W. Grimes 
10853a2f7427SDavid Greenman static void
1086d0917939SPaul Richards fd_motor_on(void *arg1)
10875b81b6b3SRodney W. Grimes {
1088381fe1aaSGarrett Wollman 	fdu_t fdu = (fdu_t)arg1;
1089f5f7ba03SJordan K. Hubbard 	int	s;
1090f5f7ba03SJordan K. Hubbard 
10915b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
1092f5f7ba03SJordan K. Hubbard 	s = splbio();
10935b81b6b3SRodney W. Grimes 	fd->flags &= ~FD_MOTOR_WAIT;
10945b81b6b3SRodney W. Grimes 	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
10955b81b6b3SRodney W. Grimes 	{
1096f5f7ba03SJordan K. Hubbard 		fdintr(fd->fdc->fdcu);
10975b81b6b3SRodney W. Grimes 	}
1098f5f7ba03SJordan K. Hubbard 	splx(s);
10995b81b6b3SRodney W. Grimes }
11005b81b6b3SRodney W. Grimes 
11013a2f7427SDavid Greenman static void
1102dc5df763SJoerg Wunsch fd_turnon(fdu_t fdu)
11035b81b6b3SRodney W. Grimes {
11045b81b6b3SRodney W. Grimes 	fd_p fd = fd_data + fdu;
11055b81b6b3SRodney W. Grimes 	if(!(fd->flags & FD_MOTOR))
11065b81b6b3SRodney W. Grimes 	{
11073a2f7427SDavid Greenman 		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
11083a2f7427SDavid Greenman 		set_motor(fd->fdc->fdcu, fd->fdsu, TURNON);
11095e235068SJordan K. Hubbard 		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
11105b81b6b3SRodney W. Grimes 	}
11115b81b6b3SRodney W. Grimes }
11125b81b6b3SRodney W. Grimes 
1113381fe1aaSGarrett Wollman static void
1114dc5df763SJoerg Wunsch fdc_reset(fdc_p fdc)
11155b81b6b3SRodney W. Grimes {
11163a2f7427SDavid Greenman 	fdcu_t fdcu = fdc->fdcu;
11173a2f7427SDavid Greenman 
11183a2f7427SDavid Greenman 	/* Try a reset, keep motor on */
11193a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
11203a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
11213a2f7427SDavid Greenman 	DELAY(100);
11223a2f7427SDavid Greenman 	/* enable FDC, but defer interrupts a moment */
11233a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
11243a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
11253a2f7427SDavid Greenman 	DELAY(100);
11263a2f7427SDavid Greenman 	outb(fdc->baseport + FDOUT, fdc->fdout);
11273a2f7427SDavid Greenman 	TRACE1("[0x%x->FDOUT]", fdc->fdout);
11283a2f7427SDavid Greenman 
1129dc5df763SJoerg Wunsch 	/* XXX after a reset, silently believe the FDC will accept commands */
1130dc5df763SJoerg Wunsch 	(void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
1131dc5df763SJoerg Wunsch 		     NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
1132dc5df763SJoerg Wunsch 		     0);
1133d66c374fSTor Egge 	if (fdc->flags & FDC_HAS_FIFO)
1134d66c374fSTor Egge 		(void) enable_fifo(fdc);
11355b81b6b3SRodney W. Grimes }
11365b81b6b3SRodney W. Grimes 
11375b81b6b3SRodney W. Grimes /****************************************************************************/
11385b81b6b3SRodney W. Grimes /*                             fdc in/out                                   */
11395b81b6b3SRodney W. Grimes /****************************************************************************/
11405b81b6b3SRodney W. Grimes int
1141dc5df763SJoerg Wunsch in_fdc(fdcu_t fdcu)
11425b81b6b3SRodney W. Grimes {
11435b81b6b3SRodney W. Grimes 	int baseport = fdc_data[fdcu].baseport;
11445b81b6b3SRodney W. Grimes 	int i, j = 100000;
11453a2f7427SDavid Greenman 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
11465b81b6b3SRodney W. Grimes 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1147dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
11486a0e6f42SRodney W. Grimes 			return fdc_err(fdcu, "ready for output in input\n");
11495b81b6b3SRodney W. Grimes 	if (j <= 0)
115016b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
1151d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
11523a2f7427SDavid Greenman 	i = inb(baseport+FDDATA);
11533a2f7427SDavid Greenman 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
11545b81b6b3SRodney W. Grimes 	return(i);
1155d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
11563a2f7427SDavid Greenman 	return inb(baseport+FDDATA);
1157d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
11585b81b6b3SRodney W. Grimes }
11595b81b6b3SRodney W. Grimes 
1160dc5df763SJoerg Wunsch /*
1161dc5df763SJoerg Wunsch  * fd_in: Like in_fdc, but allows you to see if it worked.
1162dc5df763SJoerg Wunsch  */
1163b5e8ce9fSBruce Evans static int
1164dc5df763SJoerg Wunsch fd_in(fdcu_t fdcu, int *ptr)
1165dc5df763SJoerg Wunsch {
1166dc5df763SJoerg Wunsch 	int baseport = fdc_data[fdcu].baseport;
1167dc5df763SJoerg Wunsch 	int i, j = 100000;
1168dc5df763SJoerg Wunsch 	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
1169dc5df763SJoerg Wunsch 		!= (NE7_DIO|NE7_RQM) && j-- > 0)
1170dc5df763SJoerg Wunsch 		if (i == NE7_RQM)
11716a0e6f42SRodney W. Grimes 			return fdc_err(fdcu, "ready for output in input\n");
1172dc5df763SJoerg Wunsch 	if (j <= 0)
117316b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
1174d2fb4892SJoerg Wunsch #ifdef	FDC_DEBUG
1175dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
1176dc5df763SJoerg Wunsch 	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
1177dc5df763SJoerg Wunsch 	*ptr = i;
1178dc5df763SJoerg Wunsch 	return 0;
1179d2fb4892SJoerg Wunsch #else	/* !FDC_DEBUG */
1180dc5df763SJoerg Wunsch 	i = inb(baseport+FDDATA);
1181dc5df763SJoerg Wunsch 	if (ptr)
1182dc5df763SJoerg Wunsch 		*ptr = i;
1183dc5df763SJoerg Wunsch 	return 0;
1184d2fb4892SJoerg Wunsch #endif	/* FDC_DEBUG */
1185dc5df763SJoerg Wunsch }
1186dc5df763SJoerg Wunsch 
1187dc5df763SJoerg Wunsch int
1188dc5df763SJoerg Wunsch out_fdc(fdcu_t fdcu, int x)
11895b81b6b3SRodney W. Grimes {
11905b81b6b3SRodney W. Grimes 	int baseport = fdc_data[fdcu].baseport;
11913b3837dbSRodney W. Grimes 	int i;
11925b81b6b3SRodney W. Grimes 
11933b3837dbSRodney W. Grimes 	/* Check that the direction bit is set */
11943b3837dbSRodney W. Grimes 	i = 100000;
11953a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
11966a0e6f42SRodney W. Grimes 	if (i <= 0) return fdc_err(fdcu, "direction bit not set\n");
11973b3837dbSRodney W. Grimes 
11983b3837dbSRodney W. Grimes 	/* Check that the floppy controller is ready for a command */
11993b3837dbSRodney W. Grimes 	i = 100000;
12003a2f7427SDavid Greenman 	while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
120116b04b6aSJoerg Wunsch 	if (i <= 0)
120216b04b6aSJoerg Wunsch 		return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0);
12033b3837dbSRodney W. Grimes 
12043b3837dbSRodney W. Grimes 	/* Send the command and return */
12053a2f7427SDavid Greenman 	outb(baseport+FDDATA, x);
12063a2f7427SDavid Greenman 	TRACE1("[0x%x->FDDATA]", x);
12075b81b6b3SRodney W. Grimes 	return (0);
12085b81b6b3SRodney W. Grimes }
12095b81b6b3SRodney W. Grimes 
12105b81b6b3SRodney W. Grimes /****************************************************************************/
12115b81b6b3SRodney W. Grimes /*                           fdopen/fdclose                                 */
12125b81b6b3SRodney W. Grimes /****************************************************************************/
1213381fe1aaSGarrett Wollman int
1214671e2ceeSBruce Evans Fdopen(dev_t dev, int flags, int mode, struct proc *p)
12155b81b6b3SRodney W. Grimes {
12165b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
121720a29168SAndrey A. Chernov 	int type = FDTYPE(minor(dev));
1218b99f0a4aSAndrew Moore 	fdc_p	fdc;
12195b81b6b3SRodney W. Grimes 
12205b81b6b3SRodney W. Grimes 	/* check bounds */
1221b99f0a4aSAndrew Moore 	if (fdu >= NFD)
1222b99f0a4aSAndrew Moore 		return(ENXIO);
1223b99f0a4aSAndrew Moore 	fdc = fd_data[fdu].fdc;
1224b99f0a4aSAndrew Moore 	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
1225b99f0a4aSAndrew Moore 		return(ENXIO);
1226b99f0a4aSAndrew Moore 	if (type > NUMDENS)
1227b99f0a4aSAndrew Moore 		return(ENXIO);
12287ca0641bSAndrey A. Chernov 	if (type == 0)
12297ca0641bSAndrey A. Chernov 		type = fd_data[fdu].type;
12307ca0641bSAndrey A. Chernov 	else {
12313e425b96SJulian Elischer 		/*
12323e425b96SJulian Elischer 		 * For each type of basic drive, make sure we are trying
12333e425b96SJulian Elischer 		 * to open a type it can do,
12343e425b96SJulian Elischer 		 */
12357ca0641bSAndrey A. Chernov 		if (type != fd_data[fdu].type) {
1236fa4700b4SAndrey A. Chernov 			switch (fd_data[fdu].type) {
12377ca0641bSAndrey A. Chernov 			case FD_360:
12387ca0641bSAndrey A. Chernov 				return(ENXIO);
1239ed2fa05eSAndrey A. Chernov 			case FD_720:
1240b39c878eSAndrey A. Chernov 				if (   type != FD_820
1241b39c878eSAndrey A. Chernov 				    && type != FD_800
1242ed2fa05eSAndrey A. Chernov 				   )
1243ed2fa05eSAndrey A. Chernov 					return(ENXIO);
1244ed2fa05eSAndrey A. Chernov 				break;
12457ca0641bSAndrey A. Chernov 			case FD_1200:
1246b39c878eSAndrey A. Chernov 				switch (type) {
1247b39c878eSAndrey A. Chernov 				case FD_1480:
1248b39c878eSAndrey A. Chernov 					type = FD_1480in5_25;
1249fa4700b4SAndrey A. Chernov 					break;
12507ca0641bSAndrey A. Chernov 				case FD_1440:
1251b39c878eSAndrey A. Chernov 					type = FD_1440in5_25;
1252b39c878eSAndrey A. Chernov 					break;
1253b39c878eSAndrey A. Chernov 				case FD_820:
1254b39c878eSAndrey A. Chernov 					type = FD_820in5_25;
1255b39c878eSAndrey A. Chernov 					break;
1256b39c878eSAndrey A. Chernov 				case FD_800:
1257b39c878eSAndrey A. Chernov 					type = FD_800in5_25;
1258b39c878eSAndrey A. Chernov 					break;
1259b39c878eSAndrey A. Chernov 				case FD_720:
1260b39c878eSAndrey A. Chernov 					type = FD_720in5_25;
1261b39c878eSAndrey A. Chernov 					break;
1262b39c878eSAndrey A. Chernov 				case FD_360:
1263b39c878eSAndrey A. Chernov 					type = FD_360in5_25;
1264b39c878eSAndrey A. Chernov 					break;
1265b39c878eSAndrey A. Chernov 				default:
1266b39c878eSAndrey A. Chernov 					return(ENXIO);
1267b39c878eSAndrey A. Chernov 				}
1268b39c878eSAndrey A. Chernov 				break;
1269b39c878eSAndrey A. Chernov 			case FD_1440:
1270b39c878eSAndrey A. Chernov 				if (   type != FD_1720
1271b39c878eSAndrey A. Chernov 				    && type != FD_1480
1272ed2fa05eSAndrey A. Chernov 				    && type != FD_1200
1273b39c878eSAndrey A. Chernov 				    && type != FD_820
1274b39c878eSAndrey A. Chernov 				    && type != FD_800
1275b39c878eSAndrey A. Chernov 				    && type != FD_720
12767ca0641bSAndrey A. Chernov 				    )
1277dffff499SAndrey A. Chernov 					return(ENXIO);
1278fa4700b4SAndrey A. Chernov 				break;
12797ca0641bSAndrey A. Chernov 			}
12807ca0641bSAndrey A. Chernov 		}
1281fa4700b4SAndrey A. Chernov 	}
1282b99f0a4aSAndrew Moore 	fd_data[fdu].ft = fd_types + type - 1;
12835b81b6b3SRodney W. Grimes 	fd_data[fdu].flags |= FD_OPEN;
12845b81b6b3SRodney W. Grimes 
12855b81b6b3SRodney W. Grimes 	return 0;
12865b81b6b3SRodney W. Grimes }
12875b81b6b3SRodney W. Grimes 
1288381fe1aaSGarrett Wollman int
1289671e2ceeSBruce Evans fdclose(dev_t dev, int flags, int mode, struct proc *p)
12905b81b6b3SRodney W. Grimes {
12915b81b6b3SRodney W. Grimes  	fdu_t fdu = FDUNIT(minor(dev));
1292b99f0a4aSAndrew Moore 
12935b81b6b3SRodney W. Grimes 	fd_data[fdu].flags &= ~FD_OPEN;
12943a2f7427SDavid Greenman 	fd_data[fdu].options &= ~FDOPT_NORETRY;
1295dc16046fSJoerg Wunsch 
12965b81b6b3SRodney W. Grimes 	return(0);
12975b81b6b3SRodney W. Grimes }
12985b81b6b3SRodney W. Grimes 
1299f7ea2f55SJulian Elischer static int
1300f7ea2f55SJulian Elischer fdread(dev_t dev, struct uio *uio, int ioflag)
1301f7ea2f55SJulian Elischer {
1302f7ea2f55SJulian Elischer 	return (physio(fdstrategy, NULL, dev, 1, minphys, uio));
1303f7ea2f55SJulian Elischer }
1304f7ea2f55SJulian Elischer 
1305f7ea2f55SJulian Elischer static int
1306f7ea2f55SJulian Elischer fdwrite(dev_t dev, struct uio *uio, int ioflag)
1307f7ea2f55SJulian Elischer {
1308f7ea2f55SJulian Elischer 	return (physio(fdstrategy, NULL, dev, 0, minphys, uio));
1309f7ea2f55SJulian Elischer }
1310f7ea2f55SJulian Elischer 
13115b81b6b3SRodney W. Grimes 
13123a2f7427SDavid Greenman /****************************************************************************/
13133a2f7427SDavid Greenman /*                               fdstrategy                                 */
13143a2f7427SDavid Greenman /****************************************************************************/
13153a2f7427SDavid Greenman void
13163a2f7427SDavid Greenman fdstrategy(struct buf *bp)
13173a2f7427SDavid Greenman {
1318bb6382faSJoerg Wunsch 	unsigned nblocks, blknum, cando;
13193a2f7427SDavid Greenman  	int	s;
13203a2f7427SDavid Greenman  	fdcu_t	fdcu;
13213a2f7427SDavid Greenman  	fdu_t	fdu;
13223a2f7427SDavid Greenman  	fdc_p	fdc;
13233a2f7427SDavid Greenman  	fd_p	fd;
13243a2f7427SDavid Greenman 	size_t	fdblk;
13253a2f7427SDavid Greenman 
13263a2f7427SDavid Greenman  	fdu = FDUNIT(minor(bp->b_dev));
13273a2f7427SDavid Greenman 	fd = &fd_data[fdu];
13283a2f7427SDavid Greenman 	fdc = fd->fdc;
13293a2f7427SDavid Greenman 	fdcu = fdc->fdcu;
133069acd21dSWarner Losh #ifdef FDC_YE
133169acd21dSWarner Losh 	if (fd->type == NO_TYPE) {
133269acd21dSWarner Losh 		bp->b_error = ENXIO;
133369acd21dSWarner Losh 		bp->b_flags |= B_ERROR;
133469acd21dSWarner Losh 		/*
133569acd21dSWarner Losh 		 * I _refuse_ to use a goto
133669acd21dSWarner Losh 		 */
133769acd21dSWarner Losh 		biodone(bp);
133869acd21dSWarner Losh 		return;
133969acd21dSWarner Losh 	};
134069acd21dSWarner Losh #endif
13413a2f7427SDavid Greenman 
1342d3628763SRodney W. Grimes 	fdblk = 128 << (fd->ft->secsize);
13433a2f7427SDavid Greenman 	if (!(bp->b_flags & B_FORMAT)) {
13443a2f7427SDavid Greenman 		if ((fdu >= NFD) || (bp->b_blkno < 0)) {
1345dc5df763SJoerg Wunsch 			printf(
13466a0e6f42SRodney W. Grimes 		"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1347702c623aSPoul-Henning Kamp 			       fdu, (u_long)bp->b_blkno, bp->b_bcount);
13483a2f7427SDavid Greenman 			bp->b_error = EINVAL;
13493a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
13503a2f7427SDavid Greenman 			goto bad;
13513a2f7427SDavid Greenman 		}
13523a2f7427SDavid Greenman 		if ((bp->b_bcount % fdblk) != 0) {
13533a2f7427SDavid Greenman 			bp->b_error = EINVAL;
13543a2f7427SDavid Greenman 			bp->b_flags |= B_ERROR;
13553a2f7427SDavid Greenman 			goto bad;
13563a2f7427SDavid Greenman 		}
13573a2f7427SDavid Greenman 	}
13583a2f7427SDavid Greenman 
13593a2f7427SDavid Greenman 	/*
13603a2f7427SDavid Greenman 	 * Set up block calculations.
13613a2f7427SDavid Greenman 	 */
1362bb6382faSJoerg Wunsch 	if (bp->b_blkno > 20000000) {
1363bb6382faSJoerg Wunsch 		/*
1364bb6382faSJoerg Wunsch 		 * Reject unreasonably high block number, prevent the
1365bb6382faSJoerg Wunsch 		 * multiplication below from overflowing.
1366bb6382faSJoerg Wunsch 		 */
1367bb6382faSJoerg Wunsch 		bp->b_error = EINVAL;
13683a2f7427SDavid Greenman 		bp->b_flags |= B_ERROR;
13693a2f7427SDavid Greenman 		goto bad;
13703a2f7427SDavid Greenman 	}
1371bb6382faSJoerg Wunsch 	blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1372bb6382faSJoerg Wunsch  	nblocks = fd->ft->size;
1373bb6382faSJoerg Wunsch 	bp->b_resid = 0;
1374bb6382faSJoerg Wunsch 	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1375bb6382faSJoerg Wunsch 		if (blknum <= nblocks) {
1376bb6382faSJoerg Wunsch 			cando = (nblocks - blknum) * fdblk;
1377bb6382faSJoerg Wunsch 			bp->b_resid = bp->b_bcount - cando;
1378bb6382faSJoerg Wunsch 			if (cando == 0)
1379bb6382faSJoerg Wunsch 				goto bad;	/* not actually bad but EOF */
1380bb6382faSJoerg Wunsch 		} else {
1381bb6382faSJoerg Wunsch 			bp->b_error = EINVAL;
1382bb6382faSJoerg Wunsch 			bp->b_flags |= B_ERROR;
1383bb6382faSJoerg Wunsch 			goto bad;
1384bb6382faSJoerg Wunsch 		}
1385bb6382faSJoerg Wunsch 	}
138627513ca7SBruce Evans  	bp->b_pblkno = bp->b_blkno;
13873a2f7427SDavid Greenman 	s = splbio();
138802a19910SJustin T. Gibbs 	bufqdisksort(&fdc->head, bp);
138902a19910SJustin T. Gibbs 	untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); /* a good idea */
1390b2dfb1f9SJustin T. Gibbs 
1391b2dfb1f9SJustin T. Gibbs 	/* Tell devstat we are starting on the transaction */
1392b2dfb1f9SJustin T. Gibbs 	devstat_start_transaction(&fd->device_stats);
1393b2dfb1f9SJustin T. Gibbs 
13943a2f7427SDavid Greenman 	fdstart(fdcu);
13953a2f7427SDavid Greenman 	splx(s);
13963a2f7427SDavid Greenman 	return;
13973a2f7427SDavid Greenman 
13983a2f7427SDavid Greenman bad:
13993a2f7427SDavid Greenman 	biodone(bp);
14003a2f7427SDavid Greenman }
14013a2f7427SDavid Greenman 
14025b81b6b3SRodney W. Grimes /***************************************************************\
14035b81b6b3SRodney W. Grimes *				fdstart				*
14045b81b6b3SRodney W. Grimes * We have just queued something.. if the controller is not busy	*
14055b81b6b3SRodney W. Grimes * then simulate the case where it has just finished a command	*
14065b81b6b3SRodney W. Grimes * So that it (the interrupt routine) looks on the queue for more*
14075b81b6b3SRodney W. Grimes * work to do and picks up what we just added.			*
14085b81b6b3SRodney W. Grimes * If the controller is already busy, we need do nothing, as it	*
14095b81b6b3SRodney W. Grimes * will pick up our work when the present work completes		*
14105b81b6b3SRodney W. Grimes \***************************************************************/
1411381fe1aaSGarrett Wollman static void
1412dc5df763SJoerg Wunsch fdstart(fdcu_t fdcu)
14135b81b6b3SRodney W. Grimes {
14145b81b6b3SRodney W. Grimes 	int s;
14155b81b6b3SRodney W. Grimes 
14165b81b6b3SRodney W. Grimes 	s = splbio();
14175b81b6b3SRodney W. Grimes 	if(fdc_data[fdcu].state == DEVIDLE)
14185b81b6b3SRodney W. Grimes 	{
14195b81b6b3SRodney W. Grimes 		fdintr(fdcu);
14205b81b6b3SRodney W. Grimes 	}
14215b81b6b3SRodney W. Grimes 	splx(s);
14225b81b6b3SRodney W. Grimes }
14235b81b6b3SRodney W. Grimes 
1424381fe1aaSGarrett Wollman static void
14255c1a1eaeSBruce Evans fd_iotimeout(void *arg1)
14265b81b6b3SRodney W. Grimes {
14275c1a1eaeSBruce Evans  	fdc_p fdc;
14285c1a1eaeSBruce Evans 	fdcu_t fdcu;
1429f5f7ba03SJordan K. Hubbard 	int s;
14305b81b6b3SRodney W. Grimes 
14315c1a1eaeSBruce Evans 	fdcu = (fdcu_t)arg1;
14325c1a1eaeSBruce Evans 	fdc = fdc_data + fdcu;
14335c1a1eaeSBruce Evans 	TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
14345b81b6b3SRodney W. Grimes 
14353a2f7427SDavid Greenman 	/*
14363a2f7427SDavid Greenman 	 * Due to IBM's brain-dead design, the FDC has a faked ready
14373a2f7427SDavid Greenman 	 * signal, hardwired to ready == true. Thus, any command
14383a2f7427SDavid Greenman 	 * issued if there's no diskette in the drive will _never_
14393a2f7427SDavid Greenman 	 * complete, and must be aborted by resetting the FDC.
14403a2f7427SDavid Greenman 	 * Many thanks, Big Blue!
14415c1a1eaeSBruce Evans 	 * The FDC must not be reset directly, since that would
14425c1a1eaeSBruce Evans 	 * interfere with the state machine.  Instead, pretend that
14435c1a1eaeSBruce Evans 	 * the command completed but was invalid.  The state machine
14445c1a1eaeSBruce Evans 	 * will reset the FDC and retry once.
14453a2f7427SDavid Greenman 	 */
14463a2f7427SDavid Greenman 	s = splbio();
14475c1a1eaeSBruce Evans 	fdc->status[0] = NE7_ST0_IC_IV;
14485c1a1eaeSBruce Evans 	fdc->flags &= ~FDC_STAT_VALID;
14495c1a1eaeSBruce Evans 	fdc->state = IOTIMEDOUT;
1450f5f7ba03SJordan K. Hubbard 	fdintr(fdcu);
1451f5f7ba03SJordan K. Hubbard 	splx(s);
14525b81b6b3SRodney W. Grimes }
14535b81b6b3SRodney W. Grimes 
14545b81b6b3SRodney W. Grimes /* just ensure it has the right spl */
1455381fe1aaSGarrett Wollman static void
1456d0917939SPaul Richards fd_pseudointr(void *arg1)
14575b81b6b3SRodney W. Grimes {
1458381fe1aaSGarrett Wollman 	fdcu_t fdcu = (fdcu_t)arg1;
14595b81b6b3SRodney W. Grimes 	int	s;
14603a2f7427SDavid Greenman 
14615b81b6b3SRodney W. Grimes 	s = splbio();
14625b81b6b3SRodney W. Grimes 	fdintr(fdcu);
14635b81b6b3SRodney W. Grimes 	splx(s);
14645b81b6b3SRodney W. Grimes }
14655b81b6b3SRodney W. Grimes 
14665b81b6b3SRodney W. Grimes /***********************************************************************\
14675b81b6b3SRodney W. Grimes *                                 fdintr				*
14685b81b6b3SRodney W. Grimes * keep calling the state machine until it returns a 0			*
14695b81b6b3SRodney W. Grimes * ALWAYS called at SPLBIO 						*
14705b81b6b3SRodney W. Grimes \***********************************************************************/
1471fe310de8SBruce Evans static void
1472381fe1aaSGarrett Wollman fdintr(fdcu_t fdcu)
14735b81b6b3SRodney W. Grimes {
14745b81b6b3SRodney W. Grimes 	fdc_p fdc = fdc_data + fdcu;
1475381fe1aaSGarrett Wollman 		while(fdstate(fdcu, fdc))
1476381fe1aaSGarrett Wollman 			;
14775b81b6b3SRodney W. Grimes }
14785b81b6b3SRodney W. Grimes 
147969acd21dSWarner Losh #ifdef FDC_YE
148069acd21dSWarner Losh /*
148169acd21dSWarner Losh  * magic pseudo-DMA initialization for YE FDC. Sets count and
148269acd21dSWarner Losh  * direction
148369acd21dSWarner Losh  */
148469acd21dSWarner Losh #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \
148569acd21dSWarner Losh 	outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f)))
148669acd21dSWarner Losh 
148769acd21dSWarner Losh /*
148869acd21dSWarner Losh  * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy
148969acd21dSWarner Losh  */
149069acd21dSWarner Losh static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count)
149169acd21dSWarner Losh {
149269acd21dSWarner Losh 	u_char *cptr = (u_char *)addr;
149369acd21dSWarner Losh 	fdc_p fdc = &fdc_data[fdcu];
149469acd21dSWarner Losh 	int io = fdc->baseport;
149569acd21dSWarner Losh 
149669acd21dSWarner Losh 	if (flags & B_READ) {
149769acd21dSWarner Losh 		if (fdc->state != PIOREAD) {
149869acd21dSWarner Losh 			fdc->state = PIOREAD;
149969acd21dSWarner Losh 			return(0);
150069acd21dSWarner Losh 		};
150169acd21dSWarner Losh 		SET_BCDR(0,count,io);
150269acd21dSWarner Losh 		insb(io+FDC_YE_DATAPORT,cptr,count);
150369acd21dSWarner Losh 	} else {
150469acd21dSWarner Losh 		outsb(io+FDC_YE_DATAPORT,cptr,count);
150569acd21dSWarner Losh 		SET_BCDR(0,count,io);
150669acd21dSWarner Losh 	};
150769acd21dSWarner Losh 	return(1);
150869acd21dSWarner Losh }
150969acd21dSWarner Losh #endif /* FDC_YE */
151069acd21dSWarner Losh 
15115b81b6b3SRodney W. Grimes /***********************************************************************\
15125b81b6b3SRodney W. Grimes * The controller state machine.						*
15135b81b6b3SRodney W. Grimes * if it returns a non zero value, it should be called again immediatly	*
15145b81b6b3SRodney W. Grimes \***********************************************************************/
15153a2f7427SDavid Greenman static int
1516dc5df763SJoerg Wunsch fdstate(fdcu_t fdcu, fdc_p fdc)
15175b81b6b3SRodney W. Grimes {
15185c1a1eaeSBruce Evans 	int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
1519bb6382faSJoerg Wunsch 	unsigned blknum = 0, b_cylinder = 0;
15205b81b6b3SRodney W. Grimes 	fdu_t fdu = fdc->fdu;
15215b81b6b3SRodney W. Grimes 	fd_p fd;
152217542807SPoul-Henning Kamp 	register struct buf *bp;
1523b39c878eSAndrey A. Chernov 	struct fd_formb *finfo = NULL;
15243a2f7427SDavid Greenman 	size_t fdblk;
15255b81b6b3SRodney W. Grimes 
1526e93e63cbSBruce Evans 	bp = fdc->bp;
1527e93e63cbSBruce Evans 	if (bp == NULL) {
152802a19910SJustin T. Gibbs 		bp = bufq_first(&fdc->head);
1529e93e63cbSBruce Evans 		if (bp != NULL) {
1530e93e63cbSBruce Evans 			bufq_remove(&fdc->head, bp);
1531e93e63cbSBruce Evans 			fdc->bp = bp;
1532e93e63cbSBruce Evans 		}
1533e93e63cbSBruce Evans 	}
1534e93e63cbSBruce Evans 	if (bp == NULL) {
15355b81b6b3SRodney W. Grimes 		/***********************************************\
15365b81b6b3SRodney W. Grimes 		* nothing left for this controller to do	*
15375b81b6b3SRodney W. Grimes 		* Force into the IDLE state,			*
15385b81b6b3SRodney W. Grimes 		\***********************************************/
15395b81b6b3SRodney W. Grimes 		fdc->state = DEVIDLE;
15405b81b6b3SRodney W. Grimes 		if(fdc->fd)
15415b81b6b3SRodney W. Grimes 		{
15426a0e6f42SRodney W. Grimes 			printf("fd%d: unexpected valid fd pointer\n",
15433a2f7427SDavid Greenman 			       fdc->fdu);
15445b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
15455b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
15465b81b6b3SRodney W. Grimes 		}
15475b81b6b3SRodney W. Grimes 		TRACE1("[fdc%d IDLE]", fdcu);
15485b81b6b3SRodney W. Grimes  		return(0);
15495b81b6b3SRodney W. Grimes 	}
15505b81b6b3SRodney W. Grimes 	fdu = FDUNIT(minor(bp->b_dev));
15515b81b6b3SRodney W. Grimes 	fd = fd_data + fdu;
15523a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
15535b81b6b3SRodney W. Grimes 	if (fdc->fd && (fd != fdc->fd))
15545b81b6b3SRodney W. Grimes 	{
15556a0e6f42SRodney W. Grimes 		printf("fd%d: confused fd pointers\n", fdu);
15565b81b6b3SRodney W. Grimes 	}
15575b81b6b3SRodney W. Grimes 	read = bp->b_flags & B_READ;
1558b39c878eSAndrey A. Chernov 	format = bp->b_flags & B_FORMAT;
1559bb6382faSJoerg Wunsch 	if(format) {
1560ab3f7469SPoul-Henning Kamp 		finfo = (struct fd_formb *)bp->b_data;
1561bb6382faSJoerg Wunsch 		fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1562bb6382faSJoerg Wunsch 			- (char *)finfo;
1563bb6382faSJoerg Wunsch 	}
1564bb6382faSJoerg Wunsch 	if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
15653e425b96SJulian Elischer 		blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk +
1566bb6382faSJoerg Wunsch 			fd->skip/fdblk;
1567bb6382faSJoerg Wunsch 		b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1568bb6382faSJoerg Wunsch 	}
15695b81b6b3SRodney W. Grimes 	TRACE1("fd%d", fdu);
15705b81b6b3SRodney W. Grimes 	TRACE1("[%s]", fdstates[fdc->state]);
15715b81b6b3SRodney W. Grimes 	TRACE1("(0x%x)", fd->flags);
157202a19910SJustin T. Gibbs 	untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle);
157327651b7fSJustin T. Gibbs 	fd->toffhandle = timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
15745b81b6b3SRodney W. Grimes 	switch (fdc->state)
15755b81b6b3SRodney W. Grimes 	{
15765b81b6b3SRodney W. Grimes 	case DEVIDLE:
15775b81b6b3SRodney W. Grimes 	case FINDWORK:	/* we have found new work */
15785b81b6b3SRodney W. Grimes 		fdc->retry = 0;
15795b81b6b3SRodney W. Grimes 		fd->skip = 0;
15805b81b6b3SRodney W. Grimes 		fdc->fd = fd;
15815b81b6b3SRodney W. Grimes 		fdc->fdu = fdu;
15823a2f7427SDavid Greenman 		outb(fdc->baseport+FDCTL, fd->ft->trans);
15833a2f7427SDavid Greenman 		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
15845b81b6b3SRodney W. Grimes 		/*******************************************************\
15855b81b6b3SRodney W. Grimes 		* If the next drive has a motor startup pending, then	*
1586dc733423SDag-Erling Smørgrav 		* it will start up in its own good time		*
15875b81b6b3SRodney W. Grimes 		\*******************************************************/
15885b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
15895b81b6b3SRodney W. Grimes 		{
15905b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
15915b81b6b3SRodney W. Grimes 			return(0); /* come back later */
15925b81b6b3SRodney W. Grimes 		}
15935b81b6b3SRodney W. Grimes 		/*******************************************************\
15945b81b6b3SRodney W. Grimes 		* Maybe if it's not starting, it SHOULD be starting	*
15955b81b6b3SRodney W. Grimes 		\*******************************************************/
15965b81b6b3SRodney W. Grimes 		if (!(fd->flags & FD_MOTOR))
15975b81b6b3SRodney W. Grimes 		{
15985b81b6b3SRodney W. Grimes 			fdc->state = MOTORWAIT;
15995b81b6b3SRodney W. Grimes 			fd_turnon(fdu);
16005b81b6b3SRodney W. Grimes 			return(0);
16015b81b6b3SRodney W. Grimes 		}
16025b81b6b3SRodney W. Grimes 		else	/* at least make sure we are selected */
16035b81b6b3SRodney W. Grimes 		{
16043a2f7427SDavid Greenman 			set_motor(fdcu, fd->fdsu, TURNON);
16055b81b6b3SRodney W. Grimes 		}
16065c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
16075c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
16085c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
16095c1a1eaeSBruce Evans 		} else
16105e235068SJordan K. Hubbard 			fdc->state = DOSEEK;
16115b81b6b3SRodney W. Grimes 		break;
16125b81b6b3SRodney W. Grimes 	case DOSEEK:
1613bb6382faSJoerg Wunsch 		if (b_cylinder == (unsigned)fd->track)
16145b81b6b3SRodney W. Grimes 		{
16155b81b6b3SRodney W. Grimes 			fdc->state = SEEKCOMPLETE;
16165b81b6b3SRodney W. Grimes 			break;
16175b81b6b3SRodney W. Grimes 		}
1618dc5df763SJoerg Wunsch 		if (fd_cmd(fdcu, 3, NE7CMD_SEEK,
1619bb6382faSJoerg Wunsch 			   fd->fdsu, b_cylinder * fd->ft->steptrac,
1620dc5df763SJoerg Wunsch 			   0))
1621dc8603e3SJoerg Wunsch 		{
1622dc8603e3SJoerg Wunsch 			/*
1623dc8603e3SJoerg Wunsch 			 * seek command not accepted, looks like
1624dc8603e3SJoerg Wunsch 			 * the FDC went off to the Saints...
1625dc8603e3SJoerg Wunsch 			 */
1626dc8603e3SJoerg Wunsch 			fdc->retry = 6;	/* try a reset */
1627dc8603e3SJoerg Wunsch 			return(retrier(fdcu));
1628dc8603e3SJoerg Wunsch 		}
1629dc5df763SJoerg Wunsch 		fd->track = FD_NO_TRACK;
16305b81b6b3SRodney W. Grimes 		fdc->state = SEEKWAIT;
16315b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
16325b81b6b3SRodney W. Grimes 	case SEEKWAIT:
16335b81b6b3SRodney W. Grimes 		/* allow heads to settle */
163404b734cfSPoul-Henning Kamp 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16);
16355b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
16365b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
16375b81b6b3SRodney W. Grimes 	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
16385b81b6b3SRodney W. Grimes 		/* Make sure seek really happened*/
1639dc5df763SJoerg Wunsch 		if(fd->track == FD_NO_TRACK)
16405b81b6b3SRodney W. Grimes 		{
1641bb6382faSJoerg Wunsch 			int descyl = b_cylinder * fd->ft->steptrac;
16423a2f7427SDavid Greenman 			do {
16433a2f7427SDavid Greenman 				/*
1644dc5df763SJoerg Wunsch 				 * This might be a "ready changed" interrupt,
1645dc5df763SJoerg Wunsch 				 * which cannot really happen since the
1646dc5df763SJoerg Wunsch 				 * RDY pin is hardwired to + 5 volts.  This
1647dc5df763SJoerg Wunsch 				 * generally indicates a "bouncing" intr
1648dc5df763SJoerg Wunsch 				 * line, so do one of the following:
1649dc5df763SJoerg Wunsch 				 *
1650dc5df763SJoerg Wunsch 				 * When running on an enhanced FDC that is
1651dc5df763SJoerg Wunsch 				 * known to not go stuck after responding
1652dc5df763SJoerg Wunsch 				 * with INVALID, fetch all interrupt states
1653dc5df763SJoerg Wunsch 				 * until seeing either an INVALID or a
1654dc5df763SJoerg Wunsch 				 * real interrupt condition.
1655dc5df763SJoerg Wunsch 				 *
1656dc5df763SJoerg Wunsch 				 * When running on a dumb old NE765, give
1657dc5df763SJoerg Wunsch 				 * up immediately.  The controller will
1658dc5df763SJoerg Wunsch 				 * provide up to four dummy RC interrupt
1659dc5df763SJoerg Wunsch 				 * conditions right after reset (for the
1660dc5df763SJoerg Wunsch 				 * corresponding four drives), so this is
1661dc5df763SJoerg Wunsch 				 * our only chance to get notice that it
1662dc5df763SJoerg Wunsch 				 * was not the FDC that caused the interrupt.
16633a2f7427SDavid Greenman 				 */
1664dc5df763SJoerg Wunsch 				if (fd_sense_int(fdc, &st0, &cyl)
1665dc5df763SJoerg Wunsch 				    == FD_NOT_VALID)
1666dc5df763SJoerg Wunsch 					return 0;
1667dc5df763SJoerg Wunsch 				if(fdc->fdct == FDC_NE765
1668dc5df763SJoerg Wunsch 				   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1669dc5df763SJoerg Wunsch 					return 0; /* hope for a real intr */
16703a2f7427SDavid Greenman 			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1671dc5df763SJoerg Wunsch 
16723a2f7427SDavid Greenman 			if (0 == descyl)
16733a2f7427SDavid Greenman 			{
1674dc5df763SJoerg Wunsch 				int failed = 0;
16753a2f7427SDavid Greenman 				/*
16763a2f7427SDavid Greenman 				 * seek to cyl 0 requested; make sure we are
16773a2f7427SDavid Greenman 				 * really there
16783a2f7427SDavid Greenman 				 */
1679dc5df763SJoerg Wunsch 				if (fd_sense_drive_status(fdc, &st3))
1680dc5df763SJoerg Wunsch 					failed = 1;
16813a2f7427SDavid Greenman 				if ((st3 & NE7_ST3_T0) == 0) {
16823a2f7427SDavid Greenman 					printf(
16833a2f7427SDavid Greenman 		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
16843a2f7427SDavid Greenman 					       fdu, st3, NE7_ST3BITS);
1685dc5df763SJoerg Wunsch 					failed = 1;
1686dc5df763SJoerg Wunsch 				}
1687dc5df763SJoerg Wunsch 
1688dc5df763SJoerg Wunsch 				if (failed)
1689dc5df763SJoerg Wunsch 				{
16903a2f7427SDavid Greenman 					if(fdc->retry < 3)
16913a2f7427SDavid Greenman 						fdc->retry = 3;
16923a2f7427SDavid Greenman 					return(retrier(fdcu));
16933a2f7427SDavid Greenman 				}
16943a2f7427SDavid Greenman 			}
1695dc5df763SJoerg Wunsch 
16965b81b6b3SRodney W. Grimes 			if (cyl != descyl)
16975b81b6b3SRodney W. Grimes 			{
16983a2f7427SDavid Greenman 				printf(
16993a2f7427SDavid Greenman 		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
17002d9d0204SRodney W. Grimes 				       fdu, descyl, cyl, st0);
1701e5d7d243SBruce Evans 				if (fdc->retry < 3)
1702e5d7d243SBruce Evans 					fdc->retry = 3;
17035b81b6b3SRodney W. Grimes 				return(retrier(fdcu));
17045b81b6b3SRodney W. Grimes 			}
17055b81b6b3SRodney W. Grimes 		}
17065b81b6b3SRodney W. Grimes 
1707bb6382faSJoerg Wunsch 		fd->track = b_cylinder;
170869acd21dSWarner Losh #ifdef FDC_YE
170969acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
171069acd21dSWarner Losh #endif
1711ab3f7469SPoul-Henning Kamp 			isa_dmastart(bp->b_flags, bp->b_data+fd->skip,
17123a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
17135b81b6b3SRodney W. Grimes 		sectrac = fd->ft->sectrac;
17145b81b6b3SRodney W. Grimes 		sec = blknum %  (sectrac * fd->ft->heads);
17155b81b6b3SRodney W. Grimes 		head = sec / sectrac;
17165b81b6b3SRodney W. Grimes 		sec = sec % sectrac + 1;
17173a2f7427SDavid Greenman 		fd->hddrv = ((head&1)<<2)+fdu;
17183a2f7427SDavid Greenman 
17193a2f7427SDavid Greenman 		if(format || !read)
17203a2f7427SDavid Greenman 		{
17213a2f7427SDavid Greenman 			/* make sure the drive is writable */
1722dc5df763SJoerg Wunsch 			if(fd_sense_drive_status(fdc, &st3) != 0)
1723dc8603e3SJoerg Wunsch 			{
1724dc8603e3SJoerg Wunsch 				/* stuck controller? */
17255c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
17265c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
17275c1a1eaeSBruce Evans 					    fdc->dmachan);
1728dc8603e3SJoerg Wunsch 				fdc->retry = 6;	/* reset the beast */
1729dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
1730dc8603e3SJoerg Wunsch 			}
17313a2f7427SDavid Greenman 			if(st3 & NE7_ST3_WP)
17323a2f7427SDavid Greenman 			{
17333a2f7427SDavid Greenman 				/*
17343a2f7427SDavid Greenman 				 * XXX YES! this is ugly.
17353a2f7427SDavid Greenman 				 * in order to force the current operation
17363a2f7427SDavid Greenman 				 * to fail, we will have to fake an FDC
17373a2f7427SDavid Greenman 				 * error - all error handling is done
17383a2f7427SDavid Greenman 				 * by the retrier()
17393a2f7427SDavid Greenman 				 */
17403a2f7427SDavid Greenman 				fdc->status[0] = NE7_ST0_IC_AT;
17413a2f7427SDavid Greenman 				fdc->status[1] = NE7_ST1_NW;
17423a2f7427SDavid Greenman 				fdc->status[2] = 0;
17433a2f7427SDavid Greenman 				fdc->status[3] = fd->track;
17443a2f7427SDavid Greenman 				fdc->status[4] = head;
17453a2f7427SDavid Greenman 				fdc->status[5] = sec;
17463a2f7427SDavid Greenman 				fdc->retry = 8;	/* break out immediately */
17473a2f7427SDavid Greenman 				fdc->state = IOTIMEDOUT; /* not really... */
17483a2f7427SDavid Greenman 				return (1);
17493a2f7427SDavid Greenman 			}
17503a2f7427SDavid Greenman 		}
17515b81b6b3SRodney W. Grimes 
1752b39c878eSAndrey A. Chernov 		if(format)
1753b39c878eSAndrey A. Chernov 		{
175469acd21dSWarner Losh #ifdef FDC_YE
175569acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA)
175669acd21dSWarner Losh 				(void)fdcpio(fdcu,bp->b_flags,
175769acd21dSWarner Losh 					bp->b_data+fd->skip,
175869acd21dSWarner Losh 					bp->b_bcount);
175969acd21dSWarner Losh #endif
1760b39c878eSAndrey A. Chernov 			/* formatting */
1761dc5df763SJoerg Wunsch 			if(fd_cmd(fdcu, 6,
1762dc5df763SJoerg Wunsch 				  NE7CMD_FORMAT,
1763dc5df763SJoerg Wunsch 				  head << 2 | fdu,
1764dc5df763SJoerg Wunsch 				  finfo->fd_formb_secshift,
1765dc5df763SJoerg Wunsch 				  finfo->fd_formb_nsecs,
1766dc5df763SJoerg Wunsch 				  finfo->fd_formb_gaplen,
1767dc5df763SJoerg Wunsch 				  finfo->fd_formb_fillbyte,
1768dc5df763SJoerg Wunsch 				  0))
1769dc8603e3SJoerg Wunsch 			{
1770dc8603e3SJoerg Wunsch 				/* controller fell over */
17715c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
17725c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
17735c1a1eaeSBruce Evans 					    fdc->dmachan);
1774dc8603e3SJoerg Wunsch 				fdc->retry = 6;
1775dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
1776dc8603e3SJoerg Wunsch 			}
1777b39c878eSAndrey A. Chernov 		}
1778b39c878eSAndrey A. Chernov 		else
1779b39c878eSAndrey A. Chernov 		{
178069acd21dSWarner Losh #ifdef FDC_YE
178169acd21dSWarner Losh 			if (fdc->flags & FDC_PCMCIA) {
178269acd21dSWarner Losh 				/*
178369acd21dSWarner Losh 				 * this seems to be necessary even when
178469acd21dSWarner Losh 				 * reading data
178569acd21dSWarner Losh 				 */
178669acd21dSWarner Losh 				SET_BCDR(1,fdblk,fdc->baseport);
178769acd21dSWarner Losh 
178869acd21dSWarner Losh 				/*
178969acd21dSWarner Losh 				 * perform the write pseudo-DMA before
179069acd21dSWarner Losh 				 * the WRITE command is sent
179169acd21dSWarner Losh 				 */
179269acd21dSWarner Losh 				if (!read)
179369acd21dSWarner Losh 					(void)fdcpio(fdcu,bp->b_flags,
179469acd21dSWarner Losh 					    bp->b_data+fd->skip,
179569acd21dSWarner Losh 					    fdblk);
179669acd21dSWarner Losh 			}
179769acd21dSWarner Losh #endif
1798dc5df763SJoerg Wunsch 			if (fd_cmd(fdcu, 9,
1799dc5df763SJoerg Wunsch 				   (read ? NE7CMD_READ : NE7CMD_WRITE),
1800dc5df763SJoerg Wunsch 				   head << 2 | fdu,  /* head & unit */
1801dc5df763SJoerg Wunsch 				   fd->track,        /* track */
1802dc5df763SJoerg Wunsch 				   head,
1803dc5df763SJoerg Wunsch 				   sec,              /* sector + 1 */
1804dc5df763SJoerg Wunsch 				   fd->ft->secsize,  /* sector size */
1805dc5df763SJoerg Wunsch 				   sectrac,          /* sectors/track */
1806dc5df763SJoerg Wunsch 				   fd->ft->gap,      /* gap size */
1807dc5df763SJoerg Wunsch 				   fd->ft->datalen,  /* data length */
1808dc5df763SJoerg Wunsch 				   0))
18095b81b6b3SRodney W. Grimes 			{
1810dc8603e3SJoerg Wunsch 				/* the beast is sleeping again */
18115c1a1eaeSBruce Evans 				isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18125c1a1eaeSBruce Evans 					    format ? bp->b_bcount : fdblk,
18135c1a1eaeSBruce Evans 					    fdc->dmachan);
1814dc8603e3SJoerg Wunsch 				fdc->retry = 6;
1815dc8603e3SJoerg Wunsch 				return(retrier(fdcu));
18165b81b6b3SRodney W. Grimes 			}
1817b39c878eSAndrey A. Chernov 		}
181869acd21dSWarner Losh #ifdef FDC_YE
181969acd21dSWarner Losh 		if (fdc->flags & FDC_PCMCIA)
182069acd21dSWarner Losh 			/*
182169acd21dSWarner Losh 			 * if this is a read, then simply await interrupt
182269acd21dSWarner Losh 			 * before performing PIO
182369acd21dSWarner Losh 			 */
182469acd21dSWarner Losh 			if (read && !fdcpio(fdcu,bp->b_flags,
182569acd21dSWarner Losh 			    bp->b_data+fd->skip,fdblk)) {
182669acd21dSWarner Losh 				fd->tohandle = timeout(fd_iotimeout,
182769acd21dSWarner Losh 					(caddr_t)fdcu, hz);
182869acd21dSWarner Losh 				return(0);      /* will return later */
182969acd21dSWarner Losh 			};
183069acd21dSWarner Losh 
183169acd21dSWarner Losh 		/*
183269acd21dSWarner Losh 		 * write (or format) operation will fall through and
183369acd21dSWarner Losh 		 * await completion interrupt
183469acd21dSWarner Losh 		 */
183569acd21dSWarner Losh #endif
18365b81b6b3SRodney W. Grimes 		fdc->state = IOCOMPLETE;
18375c1a1eaeSBruce Evans 		fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz);
18385b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
183969acd21dSWarner Losh #ifdef FDC_YE
184069acd21dSWarner Losh 	case PIOREAD:
184169acd21dSWarner Losh 		/*
184269acd21dSWarner Losh 		 * actually perform the PIO read.  The IOCOMPLETE case
184369acd21dSWarner Losh 		 * removes the timeout for us.
184469acd21dSWarner Losh 		 */
184569acd21dSWarner Losh 		(void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk);
184669acd21dSWarner Losh 		fdc->state = IOCOMPLETE;
184769acd21dSWarner Losh 		/* FALLTHROUGH */
184869acd21dSWarner Losh #endif
18495b81b6b3SRodney W. Grimes 	case IOCOMPLETE: /* IO DONE, post-analyze */
18505c1a1eaeSBruce Evans 		untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle);
1851dc5df763SJoerg Wunsch 
1852dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu))
18535b81b6b3SRodney W. Grimes 		{
18545c1a1eaeSBruce Evans 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18555c1a1eaeSBruce Evans 				    format ? bp->b_bcount : fdblk,
18565c1a1eaeSBruce Evans 				    fdc->dmachan);
1857dc5df763SJoerg Wunsch 			if (fdc->retry < 6)
1858dc5df763SJoerg Wunsch 				fdc->retry = 6;	/* force a reset */
1859dc5df763SJoerg Wunsch 			return retrier(fdcu);
18605b81b6b3SRodney W. Grimes   		}
1861dc5df763SJoerg Wunsch 
18623a2f7427SDavid Greenman 		fdc->state = IOTIMEDOUT;
1863dc5df763SJoerg Wunsch 
18643a2f7427SDavid Greenman 		/* FALLTHROUGH */
1865dc5df763SJoerg Wunsch 
18663a2f7427SDavid Greenman 	case IOTIMEDOUT:
186769acd21dSWarner Losh #ifdef FDC_YE
186869acd21dSWarner Losh 		if (!(fdc->flags & FDC_PCMCIA))
186969acd21dSWarner Losh #endif
1870ab3f7469SPoul-Henning Kamp 			isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
18713a2f7427SDavid Greenman 				format ? bp->b_bcount : fdblk, fdc->dmachan);
18723a2f7427SDavid Greenman 		if (fdc->status[0] & NE7_ST0_IC)
18735b81b6b3SRodney W. Grimes 		{
18743a2f7427SDavid Greenman                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
18753a2f7427SDavid Greenman 			    && fdc->status[1] & NE7_ST1_OR) {
1876b39c878eSAndrey A. Chernov                                 /*
18773a2f7427SDavid Greenman 				 * DMA overrun. Someone hogged the bus
18783a2f7427SDavid Greenman 				 * and didn't release it in time for the
18793a2f7427SDavid Greenman 				 * next FDC transfer.
18803a2f7427SDavid Greenman 				 * Just restart it, don't increment retry
18813a2f7427SDavid Greenman 				 * count. (vak)
1882b39c878eSAndrey A. Chernov                                  */
1883b39c878eSAndrey A. Chernov                                 fdc->state = SEEKCOMPLETE;
1884b39c878eSAndrey A. Chernov                                 return (1);
1885b39c878eSAndrey A. Chernov                         }
18863a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
18873a2f7427SDavid Greenman 				&& fdc->retry < 6)
18883a2f7427SDavid Greenman 				fdc->retry = 6;	/* force a reset */
18893a2f7427SDavid Greenman 			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
18903a2f7427SDavid Greenman 				&& fdc->status[2] & NE7_ST2_WC
18913a2f7427SDavid Greenman 				&& fdc->retry < 3)
18923a2f7427SDavid Greenman 				fdc->retry = 3;	/* force recalibrate */
18935b81b6b3SRodney W. Grimes 			return(retrier(fdcu));
18945b81b6b3SRodney W. Grimes 		}
18955b81b6b3SRodney W. Grimes 		/* All OK */
18963a2f7427SDavid Greenman 		fd->skip += fdblk;
1897bb6382faSJoerg Wunsch 		if (!format && fd->skip < bp->b_bcount - bp->b_resid)
18985b81b6b3SRodney W. Grimes 		{
18995b81b6b3SRodney W. Grimes 			/* set up next transfer */
19005b81b6b3SRodney W. Grimes 			fdc->state = DOSEEK;
19015b81b6b3SRodney W. Grimes 		}
19025b81b6b3SRodney W. Grimes 		else
19035b81b6b3SRodney W. Grimes 		{
19045b81b6b3SRodney W. Grimes 			/* ALL DONE */
19055b81b6b3SRodney W. Grimes 			fd->skip = 0;
1906e93e63cbSBruce Evans 			fdc->bp = NULL;
1907b2dfb1f9SJustin T. Gibbs 			/* Tell devstat we have finished with the transaction */
1908b2dfb1f9SJustin T. Gibbs 			devstat_end_transaction(&fd->device_stats,
1909b2dfb1f9SJustin T. Gibbs 						bp->b_bcount - bp->b_resid,
1910b2dfb1f9SJustin T. Gibbs 						DEVSTAT_TAG_NONE,
1911b2dfb1f9SJustin T. Gibbs 						(bp->b_flags & B_READ) ?
1912b2dfb1f9SJustin T. Gibbs 						DEVSTAT_READ : DEVSTAT_WRITE);
19135b81b6b3SRodney W. Grimes 			biodone(bp);
19145b81b6b3SRodney W. Grimes 			fdc->fd = (fd_p) 0;
19155b81b6b3SRodney W. Grimes 			fdc->fdu = -1;
19165b81b6b3SRodney W. Grimes 			fdc->state = FINDWORK;
19175b81b6b3SRodney W. Grimes 		}
19185b81b6b3SRodney W. Grimes 		return(1);
19195b81b6b3SRodney W. Grimes 	case RESETCTLR:
19203a2f7427SDavid Greenman 		fdc_reset(fdc);
19215b81b6b3SRodney W. Grimes 		fdc->retry++;
19225c1a1eaeSBruce Evans 		fdc->state = RESETCOMPLETE;
19235c1a1eaeSBruce Evans 		return (0);
19245c1a1eaeSBruce Evans 	case RESETCOMPLETE:
19255c1a1eaeSBruce Evans 		/*
19265c1a1eaeSBruce Evans 		 * Discard all the results from the reset so that they
19275c1a1eaeSBruce Evans 		 * can't cause an unexpected interrupt later.
19285c1a1eaeSBruce Evans 		 */
19290e317d05SJoerg Wunsch 		for (i = 0; i < 4; i++)
19300e317d05SJoerg Wunsch 			(void)fd_sense_int(fdc, &st0, &cyl);
19315c1a1eaeSBruce Evans 		fdc->state = STARTRECAL;
19325c1a1eaeSBruce Evans 		/* Fall through. */
19335c1a1eaeSBruce Evans 	case STARTRECAL:
1934dc5df763SJoerg Wunsch 		if(fd_cmd(fdcu,
1935dc5df763SJoerg Wunsch 			  2, NE7CMD_RECAL, fdu,
1936dc5df763SJoerg Wunsch 			  0)) /* Recalibrate Function */
1937dc8603e3SJoerg Wunsch 		{
1938dc8603e3SJoerg Wunsch 			/* arrgl */
1939dc8603e3SJoerg Wunsch 			fdc->retry = 6;
1940dc8603e3SJoerg Wunsch 			return(retrier(fdcu));
1941dc8603e3SJoerg Wunsch 		}
19425b81b6b3SRodney W. Grimes 		fdc->state = RECALWAIT;
19435b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
19445b81b6b3SRodney W. Grimes 	case RECALWAIT:
19455b81b6b3SRodney W. Grimes 		/* allow heads to settle */
194604b734cfSPoul-Henning Kamp 		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8);
19475b81b6b3SRodney W. Grimes 		fdc->state = RECALCOMPLETE;
19485b81b6b3SRodney W. Grimes 		return(0);	/* will return later */
19495b81b6b3SRodney W. Grimes 	case RECALCOMPLETE:
19503a2f7427SDavid Greenman 		do {
19513a2f7427SDavid Greenman 			/*
1952dc5df763SJoerg Wunsch 			 * See SEEKCOMPLETE for a comment on this:
19533a2f7427SDavid Greenman 			 */
1954dc5df763SJoerg Wunsch 			if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
1955dc5df763SJoerg Wunsch 				return 0;
1956dc5df763SJoerg Wunsch 			if(fdc->fdct == FDC_NE765
1957dc5df763SJoerg Wunsch 			   && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1958dc5df763SJoerg Wunsch 				return 0; /* hope for a real intr */
19593a2f7427SDavid Greenman 		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
19603a2f7427SDavid Greenman 		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
19615b81b6b3SRodney W. Grimes 		{
1962dc8603e3SJoerg Wunsch 			if(fdc->retry > 3)
1963dc8603e3SJoerg Wunsch 				/*
1964dc8603e3SJoerg Wunsch 				 * a recalibrate from beyond cylinder 77
1965dc8603e3SJoerg Wunsch 				 * will "fail" due to the FDC limitations;
1966dc8603e3SJoerg Wunsch 				 * since people used to complain much about
1967dc8603e3SJoerg Wunsch 				 * the failure message, try not logging
1968dc8603e3SJoerg Wunsch 				 * this one if it seems to be the first
1969dc8603e3SJoerg Wunsch 				 * time in a line
1970dc8603e3SJoerg Wunsch 				 */
1971dc8603e3SJoerg Wunsch 				printf("fd%d: recal failed ST0 %b cyl %d\n",
1972dc8603e3SJoerg Wunsch 				       fdu, st0, NE7_ST0BITS, cyl);
19733a2f7427SDavid Greenman 			if(fdc->retry < 3) fdc->retry = 3;
19745b81b6b3SRodney W. Grimes 			return(retrier(fdcu));
19755b81b6b3SRodney W. Grimes 		}
19765b81b6b3SRodney W. Grimes 		fd->track = 0;
19775b81b6b3SRodney W. Grimes 		/* Seek (probably) necessary */
19785b81b6b3SRodney W. Grimes 		fdc->state = DOSEEK;
19795b81b6b3SRodney W. Grimes 		return(1);	/* will return immediatly */
19805b81b6b3SRodney W. Grimes 	case MOTORWAIT:
19815b81b6b3SRodney W. Grimes 		if(fd->flags & FD_MOTOR_WAIT)
19825b81b6b3SRodney W. Grimes 		{
19835b81b6b3SRodney W. Grimes 			return(0); /* time's not up yet */
19845b81b6b3SRodney W. Grimes 		}
19855c1a1eaeSBruce Evans 		if (fdc->flags & FDC_NEEDS_RESET) {
19865c1a1eaeSBruce Evans 			fdc->state = RESETCTLR;
19875c1a1eaeSBruce Evans 			fdc->flags &= ~FDC_NEEDS_RESET;
19885c1a1eaeSBruce Evans 		} else {
19895e235068SJordan K. Hubbard 			/*
19905c1a1eaeSBruce Evans 			 * If all motors were off, then the controller was
19915c1a1eaeSBruce Evans 			 * reset, so it has lost track of the current
19925c1a1eaeSBruce Evans 			 * cylinder.  Recalibrate to handle this case.
19935e235068SJordan K. Hubbard 			 */
19945e235068SJordan K. Hubbard 			fdc->state = STARTRECAL;
19955c1a1eaeSBruce Evans 		}
19965b81b6b3SRodney W. Grimes 		return(1);	/* will return immediatly */
19975b81b6b3SRodney W. Grimes 	default:
1998dc5df763SJoerg Wunsch 		printf("fdc%d: Unexpected FD int->", fdcu);
1999dc5df763SJoerg Wunsch 		if (fd_read_status(fdc, fd->fdsu) == 0)
2000a838d83dSBruce Evans 			printf("FDC status :%x %x %x %x %x %x %x   ",
20015b81b6b3SRodney W. Grimes 			       fdc->status[0],
20025b81b6b3SRodney W. Grimes 			       fdc->status[1],
20035b81b6b3SRodney W. Grimes 			       fdc->status[2],
20045b81b6b3SRodney W. Grimes 			       fdc->status[3],
20055b81b6b3SRodney W. Grimes 			       fdc->status[4],
20065b81b6b3SRodney W. Grimes 			       fdc->status[5],
20075b81b6b3SRodney W. Grimes 			       fdc->status[6] );
20083a2f7427SDavid Greenman 		else
2009dac0f2dbSJoerg Wunsch 			printf("No status available   ");
2010dac0f2dbSJoerg Wunsch 		if (fd_sense_int(fdc, &st0, &cyl) != 0)
2011dac0f2dbSJoerg Wunsch 		{
2012dac0f2dbSJoerg Wunsch 			printf("[controller is dead now]\n");
20135b81b6b3SRodney W. Grimes 			return(0);
20145b81b6b3SRodney W. Grimes 		}
2015dac0f2dbSJoerg Wunsch 		printf("ST0 = %x, PCN = %x\n", st0, cyl);
2016dac0f2dbSJoerg Wunsch 		return(0);
2017dac0f2dbSJoerg Wunsch 	}
2018dac0f2dbSJoerg Wunsch 	/*XXX confusing: some branches return immediately, others end up here*/
20195b81b6b3SRodney W. Grimes 	return(1); /* Come back immediatly to new state */
20205b81b6b3SRodney W. Grimes }
20215b81b6b3SRodney W. Grimes 
2022aaf08d94SGarrett Wollman static int
2023f5f7ba03SJordan K. Hubbard retrier(fdcu)
2024f5f7ba03SJordan K. Hubbard 	fdcu_t fdcu;
20255b81b6b3SRodney W. Grimes {
20265b81b6b3SRodney W. Grimes 	fdc_p fdc = fdc_data + fdcu;
202717542807SPoul-Henning Kamp 	register struct buf *bp;
20285b81b6b3SRodney W. Grimes 
2029e93e63cbSBruce Evans 	bp = fdc->bp;
20305b81b6b3SRodney W. Grimes 
20313a2f7427SDavid Greenman 	if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY)
20323a2f7427SDavid Greenman 		goto fail;
20335b81b6b3SRodney W. Grimes 	switch(fdc->retry)
20345b81b6b3SRodney W. Grimes 	{
20355b81b6b3SRodney W. Grimes 	case 0: case 1: case 2:
20365b81b6b3SRodney W. Grimes 		fdc->state = SEEKCOMPLETE;
20375b81b6b3SRodney W. Grimes 		break;
20385b81b6b3SRodney W. Grimes 	case 3: case 4: case 5:
20395b81b6b3SRodney W. Grimes 		fdc->state = STARTRECAL;
20405b81b6b3SRodney W. Grimes 		break;
20415b81b6b3SRodney W. Grimes 	case 6:
20425b81b6b3SRodney W. Grimes 		fdc->state = RESETCTLR;
20435b81b6b3SRodney W. Grimes 		break;
20445b81b6b3SRodney W. Grimes 	case 7:
20455b81b6b3SRodney W. Grimes 		break;
20465b81b6b3SRodney W. Grimes 	default:
20473a2f7427SDavid Greenman 	fail:
20485b81b6b3SRodney W. Grimes 		{
20497ca0641bSAndrey A. Chernov 			dev_t sav_b_dev = bp->b_dev;
20507ca0641bSAndrey A. Chernov 			/* Trick diskerr */
20513a2f7427SDavid Greenman 			bp->b_dev = makedev(major(bp->b_dev),
20523a2f7427SDavid Greenman 				    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
205392ed385aSRodney W. Grimes 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
20543a2f7427SDavid Greenman 				fdc->fd->skip / DEV_BSIZE,
20553a2f7427SDavid Greenman 				(struct disklabel *)NULL);
20567ca0641bSAndrey A. Chernov 			bp->b_dev = sav_b_dev;
2057dc5df763SJoerg Wunsch 			if (fdc->flags & FDC_STAT_VALID)
2058dc5df763SJoerg Wunsch 			{
2059dc5df763SJoerg Wunsch 				printf(
2060a838d83dSBruce Evans 			" (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
2061dc5df763SJoerg Wunsch 				       fdc->status[0], NE7_ST0BITS,
2062dc5df763SJoerg Wunsch 				       fdc->status[1], NE7_ST1BITS,
2063dc5df763SJoerg Wunsch 				       fdc->status[2], NE7_ST2BITS,
2064dc5df763SJoerg Wunsch 				       fdc->status[3], fdc->status[4],
2065dc5df763SJoerg Wunsch 				       fdc->status[5]);
2066dc5df763SJoerg Wunsch 			}
2067dc5df763SJoerg Wunsch 			else
2068dc5df763SJoerg Wunsch 				printf(" (No status)\n");
20695b81b6b3SRodney W. Grimes 		}
20705b81b6b3SRodney W. Grimes 		bp->b_flags |= B_ERROR;
20715b81b6b3SRodney W. Grimes 		bp->b_error = EIO;
2072bb6382faSJoerg Wunsch 		bp->b_resid += bp->b_bcount - fdc->fd->skip;
2073e93e63cbSBruce Evans 		fdc->bp = NULL;
2074b2dfb1f9SJustin T. Gibbs 
2075b2dfb1f9SJustin T. Gibbs 		/* Tell devstat we have finished with the transaction */
20769f02ad60SJustin T. Gibbs 		devstat_end_transaction(&fdc->fd->device_stats,
2077b2dfb1f9SJustin T. Gibbs 					bp->b_bcount - bp->b_resid,
2078b2dfb1f9SJustin T. Gibbs 					DEVSTAT_TAG_NONE,
2079b2dfb1f9SJustin T. Gibbs 					(bp->b_flags & B_READ) ? DEVSTAT_READ :
2080b2dfb1f9SJustin T. Gibbs 								 DEVSTAT_WRITE);
20815b81b6b3SRodney W. Grimes 		fdc->fd->skip = 0;
20825b81b6b3SRodney W. Grimes 		biodone(bp);
208392ed385aSRodney W. Grimes 		fdc->state = FINDWORK;
20845c1a1eaeSBruce Evans 		fdc->flags |= FDC_NEEDS_RESET;
20855b81b6b3SRodney W. Grimes 		fdc->fd = (fd_p) 0;
20865b81b6b3SRodney W. Grimes 		fdc->fdu = -1;
208792ed385aSRodney W. Grimes 		return(1);
20885b81b6b3SRodney W. Grimes 	}
20895b81b6b3SRodney W. Grimes 	fdc->retry++;
20905b81b6b3SRodney W. Grimes 	return(1);
20915b81b6b3SRodney W. Grimes }
20925b81b6b3SRodney W. Grimes 
2093b39c878eSAndrey A. Chernov static int
2094b39c878eSAndrey A. Chernov fdformat(dev, finfo, p)
2095b39c878eSAndrey A. Chernov 	dev_t dev;
2096b39c878eSAndrey A. Chernov 	struct fd_formb *finfo;
2097b39c878eSAndrey A. Chernov 	struct proc *p;
2098b39c878eSAndrey A. Chernov {
2099b39c878eSAndrey A. Chernov  	fdu_t	fdu;
2100b39c878eSAndrey A. Chernov  	fd_p	fd;
2101b39c878eSAndrey A. Chernov 
2102b39c878eSAndrey A. Chernov 	struct buf *bp;
2103b39c878eSAndrey A. Chernov 	int rv = 0, s;
21043a2f7427SDavid Greenman 	size_t fdblk;
2105b39c878eSAndrey A. Chernov 
2106b39c878eSAndrey A. Chernov  	fdu	= FDUNIT(minor(dev));
2107b39c878eSAndrey A. Chernov 	fd	= &fd_data[fdu];
21083a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2109b39c878eSAndrey A. Chernov 
2110b39c878eSAndrey A. Chernov 	/* set up a buffer header for fdstrategy() */
2111b39c878eSAndrey A. Chernov 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
2112b39c878eSAndrey A. Chernov 	if(bp == 0)
2113b39c878eSAndrey A. Chernov 		return ENOBUFS;
211482f5379bSJoerg Wunsch 	/*
211582f5379bSJoerg Wunsch 	 * keep the process from being swapped
211682f5379bSJoerg Wunsch 	 */
211782f5379bSJoerg Wunsch 	p->p_flag |= P_PHYSIO;
2118b39c878eSAndrey A. Chernov 	bzero((void *)bp, sizeof(struct buf));
2119b39c878eSAndrey A. Chernov 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
2120b39c878eSAndrey A. Chernov 	bp->b_proc = p;
2121b39c878eSAndrey A. Chernov 
2122b39c878eSAndrey A. Chernov 	/*
2123b39c878eSAndrey A. Chernov 	 * calculate a fake blkno, so fdstrategy() would initiate a
2124b39c878eSAndrey A. Chernov 	 * seek to the requested cylinder
2125b39c878eSAndrey A. Chernov 	 */
2126b39c878eSAndrey A. Chernov 	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
21273a2f7427SDavid Greenman 		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
2128b39c878eSAndrey A. Chernov 
2129b39c878eSAndrey A. Chernov 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
2130ab3f7469SPoul-Henning Kamp 	bp->b_data = (caddr_t)finfo;
2131b39c878eSAndrey A. Chernov 
2132b39c878eSAndrey A. Chernov 	/* now do the format */
21333e425b96SJulian Elischer 	bp->b_dev = dev;
2134b39c878eSAndrey A. Chernov 	fdstrategy(bp);
2135b39c878eSAndrey A. Chernov 
2136b39c878eSAndrey A. Chernov 	/* ...and wait for it to complete */
2137b39c878eSAndrey A. Chernov 	s = splbio();
2138b39c878eSAndrey A. Chernov 	while(!(bp->b_flags & B_DONE))
2139b39c878eSAndrey A. Chernov 	{
21405e235068SJordan K. Hubbard 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
2141b39c878eSAndrey A. Chernov 		if(rv == EWOULDBLOCK)
2142b39c878eSAndrey A. Chernov 			break;
2143b39c878eSAndrey A. Chernov 	}
2144b39c878eSAndrey A. Chernov 	splx(s);
2145b39c878eSAndrey A. Chernov 
214682f5379bSJoerg Wunsch 	if(rv == EWOULDBLOCK) {
2147b39c878eSAndrey A. Chernov 		/* timed out */
2148b39c878eSAndrey A. Chernov 		rv = EIO;
214982f5379bSJoerg Wunsch 		biodone(bp);
215082f5379bSJoerg Wunsch 	}
21513a2f7427SDavid Greenman 	if(bp->b_flags & B_ERROR)
21523a2f7427SDavid Greenman 		rv = bp->b_error;
215382f5379bSJoerg Wunsch 	/*
215482f5379bSJoerg Wunsch 	 * allow the process to be swapped
215582f5379bSJoerg Wunsch 	 */
215682f5379bSJoerg Wunsch 	p->p_flag &= ~P_PHYSIO;
2157b39c878eSAndrey A. Chernov 	free(bp, M_TEMP);
2158b39c878eSAndrey A. Chernov 	return rv;
2159b39c878eSAndrey A. Chernov }
2160b39c878eSAndrey A. Chernov 
2161f5f7ba03SJordan K. Hubbard /*
2162671e2ceeSBruce Evans  * TODO: don't allocate buffer on stack.
2163f5f7ba03SJordan K. Hubbard  */
21645b81b6b3SRodney W. Grimes 
21653e425b96SJulian Elischer static int
2166b39c878eSAndrey A. Chernov fdioctl(dev, cmd, addr, flag, p)
2167f5f7ba03SJordan K. Hubbard 	dev_t dev;
2168ecbb00a2SDoug Rabson 	u_long cmd;
2169f5f7ba03SJordan K. Hubbard 	caddr_t addr;
2170f5f7ba03SJordan K. Hubbard 	int flag;
2171b39c878eSAndrey A. Chernov 	struct proc *p;
2172f5f7ba03SJordan K. Hubbard {
21733a2f7427SDavid Greenman  	fdu_t	fdu = FDUNIT(minor(dev));
21743a2f7427SDavid Greenman  	fd_p	fd = &fd_data[fdu];
21753a2f7427SDavid Greenman 	size_t fdblk;
21763a2f7427SDavid Greenman 
2177f5f7ba03SJordan K. Hubbard 	struct fd_type *fdt;
2178f5f7ba03SJordan K. Hubbard 	struct disklabel *dl;
2179f5f7ba03SJordan K. Hubbard 	char buffer[DEV_BSIZE];
21803a2f7427SDavid Greenman 	int error = 0;
2181f5f7ba03SJordan K. Hubbard 
21823a2f7427SDavid Greenman 	fdblk = 128 << fd->ft->secsize;
2183f5f7ba03SJordan K. Hubbard 
2184f5f7ba03SJordan K. Hubbard 	switch (cmd)
2185f5f7ba03SJordan K. Hubbard 	{
2186f5f7ba03SJordan K. Hubbard 	case DIOCGDINFO:
2187f5f7ba03SJordan K. Hubbard 		bzero(buffer, sizeof (buffer));
2188f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)buffer;
21893a2f7427SDavid Greenman 		dl->d_secsize = fdblk;
219092ed385aSRodney W. Grimes 		fdt = fd_data[FDUNIT(minor(dev))].ft;
2191f5f7ba03SJordan K. Hubbard 		dl->d_secpercyl = fdt->size / fdt->tracks;
2192f5f7ba03SJordan K. Hubbard 		dl->d_type = DTYPE_FLOPPY;
2193f5f7ba03SJordan K. Hubbard 
2194191e1a59SBruce Evans 		if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl)
2195191e1a59SBruce Evans 		    == NULL)
2196f5f7ba03SJordan K. Hubbard 			error = 0;
2197f5f7ba03SJordan K. Hubbard 		else
2198f5f7ba03SJordan K. Hubbard 			error = EINVAL;
2199f5f7ba03SJordan K. Hubbard 
2200f5f7ba03SJordan K. Hubbard 		*(struct disklabel *)addr = *dl;
2201f5f7ba03SJordan K. Hubbard 		break;
2202f5f7ba03SJordan K. Hubbard 
2203f5f7ba03SJordan K. Hubbard 	case DIOCSDINFO:
2204f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2205f5f7ba03SJordan K. Hubbard 			error = EBADF;
2206f5f7ba03SJordan K. Hubbard 		break;
2207f5f7ba03SJordan K. Hubbard 
2208f5f7ba03SJordan K. Hubbard 	case DIOCWLABEL:
2209f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2210f5f7ba03SJordan K. Hubbard 			error = EBADF;
2211f5f7ba03SJordan K. Hubbard 		break;
2212f5f7ba03SJordan K. Hubbard 
2213f5f7ba03SJordan K. Hubbard 	case DIOCWDINFO:
2214f5f7ba03SJordan K. Hubbard 		if ((flag & FWRITE) == 0)
2215f5f7ba03SJordan K. Hubbard 		{
2216f5f7ba03SJordan K. Hubbard 			error = EBADF;
2217f5f7ba03SJordan K. Hubbard 			break;
2218f5f7ba03SJordan K. Hubbard 		}
2219f5f7ba03SJordan K. Hubbard 
2220f5f7ba03SJordan K. Hubbard 		dl = (struct disklabel *)addr;
2221f5f7ba03SJordan K. Hubbard 
2222191e1a59SBruce Evans 		if ((error = setdisklabel((struct disklabel *)buffer, dl,
2223191e1a59SBruce Evans 					  (u_long)0)) != 0)
2224f5f7ba03SJordan K. Hubbard 			break;
2225f5f7ba03SJordan K. Hubbard 
2226b39c878eSAndrey A. Chernov 		error = writedisklabel(dev, fdstrategy,
222754c7241bSJordan K. Hubbard 				       (struct disklabel *)buffer);
2228b39c878eSAndrey A. Chernov 		break;
2229b39c878eSAndrey A. Chernov 	case FD_FORM:
2230b39c878eSAndrey A. Chernov 		if((flag & FWRITE) == 0)
2231b39c878eSAndrey A. Chernov 			error = EBADF;	/* must be opened for writing */
2232b39c878eSAndrey A. Chernov 		else if(((struct fd_formb *)addr)->format_version !=
2233b39c878eSAndrey A. Chernov 			FD_FORMAT_VERSION)
2234b39c878eSAndrey A. Chernov 			error = EINVAL;	/* wrong version of formatting prog */
2235b39c878eSAndrey A. Chernov 		else
2236b39c878eSAndrey A. Chernov 			error = fdformat(dev, (struct fd_formb *)addr, p);
2237b39c878eSAndrey A. Chernov 		break;
2238b39c878eSAndrey A. Chernov 
2239b39c878eSAndrey A. Chernov 	case FD_GTYPE:                  /* get drive type */
22403e425b96SJulian Elischer 		*(struct fd_type *)addr = *fd->ft;
2241f5f7ba03SJordan K. Hubbard 		break;
2242f5f7ba03SJordan K. Hubbard 
22433a2f7427SDavid Greenman 	case FD_STYPE:                  /* set drive type */
22443a2f7427SDavid Greenman 		/* this is considered harmful; only allow for superuser */
22453a2f7427SDavid Greenman 		if(suser(p->p_ucred, &p->p_acflag) != 0)
22463a2f7427SDavid Greenman 			return EPERM;
22473e425b96SJulian Elischer 		*fd->ft = *(struct fd_type *)addr;
22483a2f7427SDavid Greenman 		break;
22493a2f7427SDavid Greenman 
22503a2f7427SDavid Greenman 	case FD_GOPTS:			/* get drive options */
22513e425b96SJulian Elischer 		*(int *)addr = fd->options;
22523a2f7427SDavid Greenman 		break;
22533a2f7427SDavid Greenman 
22543a2f7427SDavid Greenman 	case FD_SOPTS:			/* set drive options */
22553e425b96SJulian Elischer 		fd->options = *(int *)addr;
22563a2f7427SDavid Greenman 		break;
22573a2f7427SDavid Greenman 
2258f5f7ba03SJordan K. Hubbard 	default:
22593a2f7427SDavid Greenman 		error = ENOTTY;
2260f5f7ba03SJordan K. Hubbard 		break;
2261f5f7ba03SJordan K. Hubbard 	}
2262f5f7ba03SJordan K. Hubbard 	return (error);
2263f5f7ba03SJordan K. Hubbard }
2264f5f7ba03SJordan K. Hubbard 
22657146c13eSJulian Elischer 
22667146c13eSJulian Elischer static fd_devsw_installed = 0;
22677146c13eSJulian Elischer 
226887f6c662SJulian Elischer static void 	fd_drvinit(void *notused )
22697146c13eSJulian Elischer {
227087f6c662SJulian Elischer 
22717146c13eSJulian Elischer 	if( ! fd_devsw_installed ) {
2272f7ea2f55SJulian Elischer 		cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_cdevsw);
22737146c13eSJulian Elischer 		fd_devsw_installed = 1;
22747146c13eSJulian Elischer 	}
22757146c13eSJulian Elischer }
227687f6c662SJulian Elischer 
227787f6c662SJulian Elischer SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL)
227887f6c662SJulian Elischer 
22793e425b96SJulian Elischer 
2280f5f7ba03SJordan K. Hubbard #endif
22813e425b96SJulian Elischer 
22823a2f7427SDavid Greenman /*
22833a2f7427SDavid Greenman  * Hello emacs, these are the
22843a2f7427SDavid Greenman  * Local Variables:
22853a2f7427SDavid Greenman  *  c-indent-level:               8
22863a2f7427SDavid Greenman  *  c-continued-statement-offset: 8
22873a2f7427SDavid Greenman  *  c-continued-brace-offset:     0
22883a2f7427SDavid Greenman  *  c-brace-offset:              -8
22893a2f7427SDavid Greenman  *  c-brace-imaginary-offset:     0
22903a2f7427SDavid Greenman  *  c-argdecl-indent:             8
22913a2f7427SDavid Greenman  *  c-label-offset:              -8
22923a2f7427SDavid Greenman  *  c++-hanging-braces:           1
22933a2f7427SDavid Greenman  *  c++-access-specifier-offset: -8
22943a2f7427SDavid Greenman  *  c++-empty-arglist-indent:     8
22953a2f7427SDavid Greenman  *  c++-friend-offset:            0
22963a2f7427SDavid Greenman  * End:
22973a2f7427SDavid Greenman  */
2298