fdc.c revision 1379
1202375Srdivacky/*#define DEBUG 1*/
2202375Srdivacky/*-
3202375Srdivacky * Copyright (c) 1990 The Regents of the University of California.
4202375Srdivacky * All rights reserved.
5202375Srdivacky *
6202375Srdivacky * This code is derived from software contributed to Berkeley by
7202375Srdivacky * Don Ahn.
8202375Srdivacky *
9202375Srdivacky * Redistribution and use in source and binary forms, with or without
10202375Srdivacky * modification, are permitted provided that the following conditions
11202375Srdivacky * are met:
12202375Srdivacky * 1. Redistributions of source code must retain the above copyright
13202375Srdivacky *    notice, this list of conditions and the following disclaimer.
14202375Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
15202375Srdivacky *    notice, this list of conditions and the following disclaimer in the
16252723Sdim *    documentation and/or other materials provided with the distribution.
17221345Sdim * 3. All advertising materials mentioning features or use of this software
18202375Srdivacky *    must display the following acknowledgement:
19252723Sdim *	This product includes software developed by the University of
20202375Srdivacky *	California, Berkeley and its contributors.
21202375Srdivacky * 4. Neither the name of the University nor the names of its contributors
22202375Srdivacky *    may be used to endorse or promote products derived from this software
23202375Srdivacky *    without specific prior written permission.
24202375Srdivacky *
25252723Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26252723Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27202375Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28202375Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29202375Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30202375Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31202375Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32202375Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33202375Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34202375Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35202375Srdivacky * SUCH DAMAGE.
36202375Srdivacky *
37202375Srdivacky *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
38202375Srdivacky *	$Id: fd.c,v 1.24 1994/03/08 16:25:29 nate Exp $
39252723Sdim *
40202375Srdivacky */
41202375Srdivacky
42202375Srdivacky#include "ft.h"
43252723Sdim#if NFT < 1
44202375Srdivacky#undef NFDC
45202375Srdivacky#endif
46202375Srdivacky#include "fd.h"
47252723Sdim
48202375Srdivacky#if NFDC > 0
49202375Srdivacky
50202375Srdivacky#include <sys/param.h>
51202375Srdivacky#include <sys/dkbad.h>
52202375Srdivacky#include <sys/systm.h>
53202375Srdivacky#include <sys/kernel.h>
54202375Srdivacky#include <sys/conf.h>
55202375Srdivacky#include <sys/file.h>
56202375Srdivacky#include <sys/ioctl.h>
57202375Srdivacky#include <machine/ioctl_fd.h>
58202375Srdivacky#include <sys/disklabel.h>
59252723Sdim#include <sys/buf.h>
60202375Srdivacky#include <sys/uio.h>
61202375Srdivacky#include <sys/malloc.h>
62202375Srdivacky#include <sys/syslog.h>
63202375Srdivacky#include "i386/isa/isa.h"
64202375Srdivacky#include "i386/isa/isa_device.h"
65202375Srdivacky#include "i386/isa/fdreg.h"
66202375Srdivacky#include "i386/isa/fdc.h"
67202375Srdivacky#include "i386/isa/icu.h"
68202375Srdivacky#include "i386/isa/rtc.h"
69202375Srdivacky
70202375Srdivacky#if NFT > 0
71202375Srdivackyextern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl();
72202375Srdivacky#endif
73202375Srdivacky
74202375Srdivacky#define b_cylin b_resid
75202375Srdivacky#define FDBLK 512
76202375Srdivacky
77202375Srdivacky/* misuse a flag to identify format operation */
78202375Srdivacky#define B_FORMAT B_XXX
79202375Srdivacky
80202375Srdivacky#define NUMTYPES 14
81202375Srdivacky#define NUMDENS  (NUMTYPES - 6)
82202375Srdivacky
83202375Srdivacky/* This defines (-1) must match index for fd_types */
84202375Srdivacky#define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
85202375Srdivacky#define NO_TYPE		0	/* must match NO_TYPE in ft.c */
86202375Srdivacky#define FD_1720         1
87202375Srdivacky#define FD_1480         2
88202375Srdivacky#define FD_1440         3
89202375Srdivacky#define FD_1200         4
90202375Srdivacky#define FD_820          5
91202375Srdivacky#define FD_800          6
92202375Srdivacky#define FD_720          7
93235633Sdim#define FD_360          8
94252723Sdim
95202375Srdivacky#define FD_1480in5_25   9
96202375Srdivacky#define FD_1440in5_25   10
97235633Sdim#define FD_820in5_25    11
98235633Sdim#define FD_800in5_25    12
99235633Sdim#define FD_720in5_25    13
100235633Sdim#define FD_360in5_25    14
101235633Sdim
102235633Sdim
103202375Srdivackystruct fd_type fd_types[NUMTYPES] =
104202375Srdivacky{
105202375Srdivacky{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
106202375Srdivacky{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
107202375Srdivacky{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
108202375Srdivacky{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
109204792Srdivacky{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
110204792Srdivacky{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
111204792Srdivacky{  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
112202375Srdivacky{  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
113235633Sdim
114204792Srdivacky{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
115204792Srdivacky{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
116204792Srdivacky{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
117204792Srdivacky{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
118204792Srdivacky{  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
119204792Srdivacky{  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
120204792Srdivacky};
121252723Sdim
122218893Sdim#define DRVS_PER_CTLR 2		/* 2 floppies */
123218893Sdim/***********************************************************************\
124202375Srdivacky* Per controller structure.						*
125204792Srdivacky\***********************************************************************/
126202375Srdivackystruct fdc_data fdc_data[NFDC];
127202375Srdivacky
128202375Srdivacky/***********************************************************************\
129202375Srdivacky* Per drive structure.							*
130202375Srdivacky* N per controller  (DRVS_PER_CTLR)					*
131202375Srdivacky\***********************************************************************/
132202375Srdivackystruct fd_data {
133202375Srdivacky	struct	fdc_data *fdc;	/* pointer to controller structure */
134202375Srdivacky	int	fdsu;		/* this units number on this controller */
135202375Srdivacky	int	type;		/* Drive type (HD, DD     */
136202375Srdivacky	struct	fd_type *ft;	/* pointer to the type descriptor */
137202375Srdivacky	int	flags;
138202375Srdivacky#define	FD_OPEN		0x01	/* it's open		*/
139202375Srdivacky#define	FD_ACTIVE	0x02	/* it's active		*/
140202375Srdivacky#define	FD_MOTOR	0x04	/* motor should be on	*/
141202375Srdivacky#define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
142202375Srdivacky	int skip;
143202375Srdivacky	int hddrv;
144202375Srdivacky	int track;		/* where we think the head is */
145202375Srdivacky} fd_data[NFD];
146202375Srdivacky
147202375Srdivacky/***********************************************************************\
148202375Srdivacky* Throughout this file the following conventions will be used:		*
149202375Srdivacky* fd is a pointer to the fd_data struct for the drive in question	*
150218893Sdim* fdc is a pointer to the fdc_data struct for the controller		*
151218893Sdim* fdu is the floppy drive unit number					*
152218893Sdim* fdcu is the floppy controller unit number				*
153218893Sdim* fdsu is the floppy drive unit number on that controller. (sub-unit)	*
154218893Sdim\***********************************************************************/
155218893Sdim
156218893Sdim#define	id_physid id_scsiid	/* this biotab field doubles as a field */
157252723Sdim				/* for the physical unit number on the controller */
158218893Sdim
159218893Sdimstatic int retrier(fdcu_t);
160218893Sdim
161218893Sdim#define DEVIDLE		0
162218893Sdim#define FINDWORK	1
163218893Sdim#define	DOSEEK		2
164218893Sdim#define SEEKCOMPLETE 	3
165218893Sdim#define	IOCOMPLETE	4
166218893Sdim#define RECALCOMPLETE	5
167218893Sdim#define	STARTRECAL	6
168202375Srdivacky#define	RESETCTLR	7
169252723Sdim#define	SEEKWAIT	8
170202375Srdivacky#define	RECALWAIT	9
171202375Srdivacky#define	MOTORWAIT	10
172202375Srdivacky#define	IOTIMEDOUT	11
173202375Srdivacky
174202375Srdivacky#ifdef	DEBUG
175202375Srdivackychar *fdstates[] =
176263509Sdim{
177202375Srdivacky"DEVIDLE",
178202375Srdivacky"FINDWORK",
179202375Srdivacky"DOSEEK",
180202375Srdivacky"SEEKCOMPLETE",
181202375Srdivacky"IOCOMPLETE",
182202375Srdivacky"RECALCOMPLETE",
183263509Sdim"STARTRECAL",
184202375Srdivacky"RESETCTLR",
185202375Srdivacky"SEEKWAIT",
186202375Srdivacky"RECALWAIT",
187202375Srdivacky"MOTORWAIT",
188202375Srdivacky"IOTIMEDOUT"
189202375Srdivacky};
190202375Srdivacky
191202375Srdivacky
192202375Srdivackyint	fd_debug = 1;
193202375Srdivacky#define TRACE0(arg) if(fd_debug) printf(arg)
194202375Srdivacky#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2)
195202375Srdivacky#else /* DEBUG */
196202375Srdivacky#define TRACE0(arg)
197202375Srdivacky#define TRACE1(arg1,arg2)
198202375Srdivacky#endif /* DEBUG */
199202375Srdivacky
200202375Srdivackystatic void fdstart(fdcu_t);
201202375Srdivackyvoid fdintr(fdcu_t);
202202375Srdivackystatic void fd_turnoff(caddr_t, int);
203202375Srdivacky
204202375Srdivacky/****************************************************************************/
205202375Srdivacky/*                      autoconfiguration stuff                             */
206202375Srdivacky/****************************************************************************/
207202375Srdivackystatic int fdprobe(struct isa_device *);
208202375Srdivackystatic int fdattach(struct isa_device *);
209202375Srdivacky
210202375Srdivackystruct	isa_driver fdcdriver = {
211202375Srdivacky	fdprobe, fdattach, "fdc",
212263509Sdim};
213202375Srdivacky
214218893Sdim/*
215218893Sdim * probe for existance of controller
216202375Srdivacky */
217252723Sdimint
218218893Sdimfdprobe(dev)
219202375Srdivacky	struct isa_device *dev;
220202375Srdivacky{
221202375Srdivacky	fdcu_t	fdcu = dev->id_unit;
222202375Srdivacky	if(fdc_data[fdcu].flags & FDC_ATTACHED)
223202375Srdivacky	{
224202375Srdivacky		printf("fdc: same unit (%d) used multiple times\n",fdcu);
225202375Srdivacky		return 0;
226202375Srdivacky	}
227202375Srdivacky
228202375Srdivacky	fdc_data[fdcu].baseport = dev->id_iobase;
229202375Srdivacky
230202375Srdivacky	/* First - lets reset the floppy controller */
231202375Srdivacky
232263509Sdim	outb(dev->id_iobase+fdout,0);
233202375Srdivacky	DELAY(100);
234218893Sdim	outb(dev->id_iobase+fdout,FDO_FRST);
235218893Sdim
236202375Srdivacky	/* see if it can handle a command */
237252723Sdim	if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
238218893Sdim	{
239202375Srdivacky		return(0);
240202375Srdivacky	}
241202375Srdivacky	out_fdc(fdcu,0xDF);
242202375Srdivacky	out_fdc(fdcu,2);
243202375Srdivacky	return (IO_FDCSIZE);
244202375Srdivacky}
245202375Srdivacky
246202375Srdivacky/*
247202375Srdivacky * wire controller into system, look for floppy units
248202375Srdivacky */
249202375Srdivackyint
250202375Srdivackyfdattach(dev)
251202375Srdivacky	struct isa_device *dev;
252263509Sdim{
253202375Srdivacky	unsigned fdt,st0, cyl;
254202375Srdivacky	int	hdr;
255202375Srdivacky	fdu_t	fdu;
256202375Srdivacky	fdcu_t	fdcu = dev->id_unit;
257202375Srdivacky	fdc_p	fdc = fdc_data + fdcu;
258202375Srdivacky	fd_p	fd;
259202375Srdivacky	int	fdsu;
260202375Srdivacky	struct isa_device *fdup;
261202375Srdivacky
262202375Srdivacky	fdc->fdcu = fdcu;
263202375Srdivacky	fdc->flags |= FDC_ATTACHED;
264202375Srdivacky	fdc->dmachan = dev->id_drq;
265202375Srdivacky	fdc->state = DEVIDLE;
266252723Sdim	hdr = 0;
267252723Sdim	printf("fdc%d:", fdcu);
268252723Sdim
269202375Srdivacky	/* check for each floppy drive */
270202375Srdivacky	for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
271204792Srdivacky		if (fdup->id_iobase != dev->id_iobase)
272204792Srdivacky			continue;
273252723Sdim		fdu = fdup->id_unit;
274202375Srdivacky		fd = &fd_data[fdu];
275202375Srdivacky		if (fdu >= (NFD+NFT))
276252723Sdim			continue;
277202375Srdivacky		fdsu = fdup->id_physid;
278202375Srdivacky				/* look up what bios thinks we have */
279263509Sdim		switch (fdu) {
280202375Srdivacky			case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
281202375Srdivacky				break;
282202375Srdivacky			case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
283252723Sdim				break;
284202375Srdivacky			default: fdt = RTCFDT_NONE;
285204792Srdivacky				break;
286202375Srdivacky		}
287202375Srdivacky		/* is there a unit? */
288202375Srdivacky		if ((fdt == RTCFDT_NONE)
289202375Srdivacky#if NFT > 0
290202375Srdivacky		|| (fdsu >= DRVS_PER_CTLR)) {
291202375Srdivacky#else
292204792Srdivacky		) {
293202375Srdivacky			fd->type = NO_TYPE;
294202375Srdivacky#endif
295202375Srdivacky#if NFT > 0
296263509Sdim				/* If BIOS says no floppy, or > 2nd device */
297202375Srdivacky				/* Probe for and attach a floppy tape.     */
298202375Srdivacky			if (ftattach(dev, fdup))
299202375Srdivacky				continue;
300202375Srdivacky			if (fdsu < DRVS_PER_CTLR)
301252723Sdim				fd->type = NO_TYPE;
302202375Srdivacky#endif
303204792Srdivacky			continue;
304202375Srdivacky		}
305202375Srdivacky
306202375Srdivacky#ifdef notyet
307202375Srdivacky		/* select it */
308202375Srdivacky		fd_turnon1(fdu);
309202375Srdivacky		spinwait(1000);	/* 1 sec */
310202375Srdivacky		out_fdc(fdcu,NE7CMD_RECAL);	/* Recalibrate Function */
311204792Srdivacky		out_fdc(fdcu,fdsu);
312202375Srdivacky		spinwait(1000);	/* 1 sec */
313202375Srdivacky
314202375Srdivacky		/* anything responding */
315202375Srdivacky		out_fdc(fdcu,NE7CMD_SENSEI);
316202375Srdivacky		st0 = in_fdc(fdcu);
317202375Srdivacky		cyl = in_fdc(fdcu);
318202375Srdivacky		if (st0 & 0xd0)
319202375Srdivacky			continue;
320202375Srdivacky
321202375Srdivacky#endif
322202375Srdivacky		fd->track = -2;
323202375Srdivacky		fd->fdc = fdc;
324202375Srdivacky		fd->fdsu = fdsu;
325202375Srdivacky		printf(" [%d: fd%d: ", fdsu, fdu);
326252723Sdim
327202375Srdivacky		switch (fdt) {
328202375Srdivacky		case RTCFDT_12M:
329202375Srdivacky			printf("1.2MB 5.25in]");
330202375Srdivacky			fd->type = FD_1200;
331202375Srdivacky			break;
332202375Srdivacky		case RTCFDT_144M:
333252723Sdim			printf("1.44MB 3.5in]");
334202375Srdivacky			fd->type = FD_1440;
335202375Srdivacky			break;
336202375Srdivacky		case RTCFDT_360K:
337202375Srdivacky			printf("360KB 5.25in]");
338202375Srdivacky			fd->type = FD_360;
339202375Srdivacky			break;
340202375Srdivacky		case RTCFDT_720K:
341202375Srdivacky			printf("720KB 3.5in]");
342202375Srdivacky			fd->type = FD_720;
343202375Srdivacky			break;
344202375Srdivacky		default:
345202375Srdivacky			printf("unknown]");
346202375Srdivacky			fd->type = NO_TYPE;
347202375Srdivacky			break;
348202375Srdivacky		}
349202375Srdivacky
350202375Srdivacky		fd_turnoff((caddr_t)fdu, 0);
351202375Srdivacky		hdr = 1;
352202375Srdivacky	}
353202375Srdivacky	printf("\n");
354252723Sdim
355252723Sdim	/* Set transfer to 500kbps */
356202375Srdivacky	outb(fdc->baseport+fdctl,0); /*XXX*/
357202375Srdivacky	return 1;
358202375Srdivacky}
359202375Srdivacky
360202375Srdivackyint
361202375Srdivackyfdsize(dev)
362202375Srdivacky	dev_t	dev;
363202375Srdivacky{
364202375Srdivacky	return(0);
365202375Srdivacky}
366202375Srdivacky
367202375Srdivacky/****************************************************************************/
368202375Srdivacky/*                               fdstrategy                                 */
369202375Srdivacky/****************************************************************************/
370202375Srdivackyvoid fdstrategy(struct buf *bp)
371202375Srdivacky{
372202375Srdivacky	register struct buf *dp,*dp0,*dp1;
373202375Srdivacky	long nblocks,blknum;
374252723Sdim 	int	s;
375202375Srdivacky 	fdcu_t	fdcu;
376202375Srdivacky 	fdu_t	fdu;
377202375Srdivacky 	fdc_p	fdc;
378202375Srdivacky 	fd_p	fd;
379202375Srdivacky
380252723Sdim 	fdu = FDUNIT(minor(bp->b_dev));
381202375Srdivacky	fd = &fd_data[fdu];
382202375Srdivacky	fdc = fd->fdc;
383202375Srdivacky	fdcu = fdc->fdcu;
384202375Srdivacky
385202375Srdivacky#if NFT > 0
386218893Sdim	/* check for controller already busy with tape */
387252723Sdim	if (fdc->flags & FDC_TAPE_BUSY) {
388252723Sdim		bp->b_error = EBUSY;
389218893Sdim		bp->b_flags |= B_ERROR;
390218893Sdim		return;
391218893Sdim		}
392218893Sdim#endif
393218893Sdim	if ((fdu >= NFD) || (bp->b_blkno < 0)) {
394252723Sdim		printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
395218893Sdim			fdu, bp->b_blkno, bp->b_bcount);
396218893Sdim		pg("fd:error in fdstrategy");
397252723Sdim		bp->b_error = EINVAL;
398218893Sdim		bp->b_flags |= B_ERROR;
399218893Sdim		goto bad;
400252723Sdim	}
401218893Sdim	/*
402218893Sdim	 * Set up block calculations.
403218893Sdim	 */
404218893Sdim	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
405218893Sdim 	nblocks = fd->ft->size;
406218893Sdim	if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
407218893Sdim		if (blknum == nblocks) {
408218893Sdim			bp->b_resid = bp->b_bcount;
409218893Sdim		} else {
410218893Sdim			bp->b_error = ENOSPC;
411218893Sdim			bp->b_flags |= B_ERROR;
412218893Sdim		}
413218893Sdim		goto bad;
414218893Sdim	}
415218893Sdim 	bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
416218893Sdim	bp->b_pblkno = bp->b_blkno;
417218893Sdim	dp = &(fdc->head);
418218893Sdim	s = splbio();
419218893Sdim	disksort(dp, bp);
420218893Sdim	untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */
421218893Sdim	fdstart(fdcu);
422218893Sdim	splx(s);
423218893Sdim	return;
424252723Sdim
425218893Sdimbad:
426218893Sdim	biodone(bp);
427218893Sdim	return;
428218893Sdim}
429218893Sdim
430218893Sdim/****************************************************************************/
431252723Sdim/*                            motor control stuff                           */
432218893Sdim/*		remember to not deselect the drive we're working on         */
433252723Sdim/****************************************************************************/
434218893Sdimvoid
435218893Sdimset_motor(fdcu, fdu, reset)
436218893Sdim	fdcu_t fdcu;
437218893Sdim	fdu_t fdu;
438218893Sdim	int reset;
439218893Sdim{
440218893Sdim	int m0,m1;
441218893Sdim	int selunit;
442218893Sdim	fd_p fd;
443218893Sdim	if(fd = fdc_data[fdcu].fd)/* yes an assign! */
444218893Sdim	{
445218893Sdim		selunit =  fd->fdsu;
446218893Sdim	}
447218893Sdim	else
448252723Sdim	{
449218893Sdim		selunit = 0;
450218893Sdim	}
451218893Sdim	m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
452218893Sdim	m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
453252723Sdim	outb(fdc_data[fdcu].baseport+fdout,
454218893Sdim		selunit
455218893Sdim		| (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
456218893Sdim		| (m0 ? FDO_MOEN0 : 0)
457218893Sdim		| (m1 ? FDO_MOEN1 : 0));
458218893Sdim	TRACE1("[0x%x->fdout]",(
459218893Sdim		selunit
460218893Sdim		| (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
461218893Sdim		| (m0 ? FDO_MOEN0 : 0)
462218893Sdim		| (m1 ? FDO_MOEN1 : 0)));
463218893Sdim}
464218893Sdim
465218893Sdimstatic void
466218893Sdimfd_turnoff(caddr_t arg1, int arg2)
467218893Sdim{
468252723Sdim	fdu_t fdu = (fdu_t)arg1;
469252723Sdim	int	s;
470218893Sdim
471218893Sdim	fd_p fd = fd_data + fdu;
472218893Sdim	s = splbio();
473252723Sdim	fd->flags &= ~FD_MOTOR;
474218893Sdim	set_motor(fd->fdc->fdcu,fd->fdsu,0);
475218893Sdim	splx(s);
476218893Sdim}
477218893Sdim
478218893Sdimvoid
479218893Sdimfd_motor_on(caddr_t arg1, int arg2)
480252723Sdim{
481218893Sdim	fdu_t fdu = (fdu_t)arg1;
482218893Sdim	int	s;
483252723Sdim
484252723Sdim	fd_p fd = fd_data + fdu;
485218893Sdim	s = splbio();
486218893Sdim	fd->flags &= ~FD_MOTOR_WAIT;
487218893Sdim	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
488218893Sdim	{
489218893Sdim		fdintr(fd->fdc->fdcu);
490218893Sdim	}
491263509Sdim	splx(s);
492263509Sdim}
493263509Sdim
494263509Sdimstatic void fd_turnon1(fdu_t);
495263509Sdim
496263509Sdimvoid
497263509Sdimfd_turnon(fdu)
498263509Sdim	fdu_t fdu;
499263509Sdim{
500263509Sdim	fd_p fd = fd_data + fdu;
501263509Sdim	if(!(fd->flags & FD_MOTOR))
502263509Sdim	{
503263509Sdim		fd_turnon1(fdu);
504263509Sdim		fd->flags |= FD_MOTOR_WAIT;
505263509Sdim		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
506263509Sdim	}
507263509Sdim}
508263509Sdim
509263509Sdimstatic void
510263509Sdimfd_turnon1(fdu_t fdu)
511235633Sdim{
512235633Sdim	fd_p fd = fd_data + fdu;
513235633Sdim	fd->flags |= FD_MOTOR;
514235633Sdim	set_motor(fd->fdc->fdcu,fd->fdsu,0);
515235633Sdim}
516235633Sdim
517235633Sdim/****************************************************************************/
518235633Sdim/*                             fdc in/out                                   */
519235633Sdim/****************************************************************************/
520235633Sdimint
521235633Sdimin_fdc(fdcu)
522235633Sdim	fdcu_t fdcu;
523235633Sdim{
524235633Sdim	int baseport = fdc_data[fdcu].baseport;
525235633Sdim	int i, j = 100000;
526235633Sdim	while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
527235633Sdim		!= (NE7_DIO|NE7_RQM) && j-- > 0)
528235633Sdim		if (i == NE7_RQM) return -1;
529235633Sdim	if (j <= 0)
530235633Sdim		return(-1);
531235633Sdim#ifdef	DEBUG
532235633Sdim	i = inb(baseport+fddata);
533235633Sdim	TRACE1("[fddata->0x%x]",(unsigned char)i);
534235633Sdim	return(i);
535235633Sdim#else
536235633Sdim	return inb(baseport+fddata);
537235633Sdim#endif
538235633Sdim}
539235633Sdim
540235633Sdimint
541235633Sdimout_fdc(fdcu, x)
542235633Sdim	fdcu_t fdcu;
543218893Sdim	int x;
544218893Sdim{
545218893Sdim	int baseport = fdc_data[fdcu].baseport;
546218893Sdim	int i;
547252723Sdim
548218893Sdim	/* Check that the direction bit is set */
549218893Sdim	i = 100000;
550235633Sdim	while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
551235633Sdim	if (i <= 0) return (-1);	/* Floppy timed out */
552235633Sdim
553218893Sdim	/* Check that the floppy controller is ready for a command */
554218893Sdim	i = 100000;
555218893Sdim	while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
556218893Sdim	if (i <= 0) return (-1);	/* Floppy timed out */
557218893Sdim
558252723Sdim	/* Send the command and return */
559218893Sdim	outb(baseport+fddata,x);
560218893Sdim	TRACE1("[0x%x->fddata]",x);
561252723Sdim	return (0);
562218893Sdim}
563218893Sdim
564218893Sdim/****************************************************************************/
565218893Sdim/*                           fdopen/fdclose                                 */
566235633Sdim/****************************************************************************/
567235633Sdimint
568235633SdimFdopen(dev, flags)
569235633Sdim	dev_t	dev;
570235633Sdim	int	flags;
571263509Sdim{
572263509Sdim 	fdu_t fdu = FDUNIT(minor(dev));
573263509Sdim	int type = FDTYPE(minor(dev));
574263509Sdim	fdc_p	fdc;
575263509Sdim
576263509Sdim#if NFT > 0
577263509Sdim	/* check for a tape open */
578263509Sdim	if (type & F_TAPE_TYPE)
579263509Sdim		return(ftopen(dev, flags));
580263509Sdim#endif
581263509Sdim	/* check bounds */
582263509Sdim	if (fdu >= NFD)
583218893Sdim		return(ENXIO);
584263509Sdim	fdc = fd_data[fdu].fdc;
585263509Sdim	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
586263509Sdim		return(ENXIO);
587235633Sdim	if (type > NUMDENS)
588218893Sdim		return(ENXIO);
589218893Sdim	if (type == 0)
590235633Sdim		type = fd_data[fdu].type;
591235633Sdim	else {
592235633Sdim		if (type != fd_data[fdu].type) {
593235633Sdim			switch (fd_data[fdu].type) {
594218893Sdim			case FD_360:
595218893Sdim				return(ENXIO);
596218893Sdim			case FD_720:
597218893Sdim				if (   type != FD_820
598235633Sdim				    && type != FD_800
599235633Sdim				   )
600235633Sdim					return(ENXIO);
601235633Sdim				break;
602235633Sdim			case FD_1200:
603235633Sdim				switch (type) {
604235633Sdim				case FD_1480:
605235633Sdim					type = FD_1480in5_25;
606235633Sdim					break;
607263509Sdim				case FD_1440:
608263509Sdim					type = FD_1440in5_25;
609263509Sdim					break;
610263509Sdim				case FD_820:
611263509Sdim					type = FD_820in5_25;
612263509Sdim					break;
613263509Sdim				case FD_800:
614263509Sdim					type = FD_800in5_25;
615235633Sdim					break;
616218893Sdim				case FD_720:
617235633Sdim					type = FD_720in5_25;
618218893Sdim					break;
619218893Sdim				case FD_360:
620218893Sdim					type = FD_360in5_25;
621235633Sdim					break;
622235633Sdim				default:
623235633Sdim					return(ENXIO);
624235633Sdim				}
625235633Sdim				break;
626235633Sdim			case FD_1440:
627263509Sdim				if (   type != FD_1720
628263509Sdim				    && type != FD_1480
629263509Sdim				    && type != FD_1200
630263509Sdim				    && type != FD_820
631263509Sdim				    && type != FD_800
632263509Sdim				    && type != FD_720
633235633Sdim				    )
634235633Sdim					return(ENXIO);
635235633Sdim				break;
636218893Sdim			}
637235633Sdim		}
638235633Sdim	}
639218893Sdim	fd_data[fdu].ft = fd_types + type - 1;
640218893Sdim	fd_data[fdu].flags |= FD_OPEN;
641218893Sdim
642218893Sdim	return 0;
643218893Sdim}
644218893Sdim
645218893Sdimint
646252723Sdimfdclose(dev, flags)
647218893Sdim	dev_t dev;
648252723Sdim	int flags;
649218893Sdim{
650252723Sdim 	fdu_t fdu = FDUNIT(minor(dev));
651218893Sdim	int type = FDTYPE(minor(dev));
652218893Sdim
653218893Sdim#if NFT > 0
654218893Sdim	if (type & F_TAPE_TYPE)
655218893Sdim		return ftclose(0);
656218893Sdim#endif
657218893Sdim	fd_data[fdu].flags &= ~FD_OPEN;
658218893Sdim	return(0);
659218893Sdim}
660218893Sdim
661263509Sdim
662218893Sdim/***************************************************************\
663218893Sdim*				fdstart				*
664235633Sdim* We have just queued something.. if the controller is not busy	*
665235633Sdim* then simulate the case where it has just finished a command	*
666235633Sdim* So that it (the interrupt routine) looks on the queue for more*
667218893Sdim* work to do and picks up what we just added.			*
668235633Sdim* If the controller is already busy, we need do nothing, as it	*
669235633Sdim* will pick up our work when the present work completes		*
670218893Sdim\***************************************************************/
671263509Sdimstatic void
672263509Sdimfdstart(fdcu)
673263509Sdim	fdcu_t fdcu;
674263509Sdim{
675263509Sdim	register struct buf *dp,*bp;
676263509Sdim	int s;
677263509Sdim 	fdu_t fdu;
678263509Sdim
679263509Sdim	s = splbio();
680263509Sdim	if(fdc_data[fdcu].state == DEVIDLE)
681218893Sdim	{
682263509Sdim		fdintr(fdcu);
683263509Sdim	}
684263509Sdim	splx(s);
685263509Sdim}
686263509Sdim
687263509Sdimstatic void
688263509Sdimfd_timeout(caddr_t arg1, int arg2)
689263509Sdim{
690218893Sdim	fdcu_t fdcu = (fdcu_t)arg1;
691252723Sdim	fdu_t fdu = fdc_data[fdcu].fdu;
692218893Sdim	int st0, st3, cyl;
693218893Sdim	struct buf *dp,*bp;
694218893Sdim	int	s;
695218893Sdim
696252723Sdim	dp = &fdc_data[fdcu].head;
697218893Sdim	s = splbio();
698218893Sdim	bp = dp->b_actf;
699218893Sdim
700218893Sdim	out_fdc(fdcu,NE7CMD_SENSED);
701252723Sdim	out_fdc(fdcu,fd_data[fdu].hddrv);
702252723Sdim	st3 = in_fdc(fdcu);
703218893Sdim
704218893Sdim	out_fdc(fdcu,NE7CMD_SENSEI);
705218893Sdim	st0 = in_fdc(fdcu);
706218893Sdim	cyl = in_fdc(fdcu);
707252723Sdim	printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
708252723Sdim			fdu,
709252723Sdim			st0,
710218893Sdim			NE7_ST0BITS,
711218893Sdim			cyl,
712218893Sdim			st3,
713218893Sdim			NE7_ST3BITS);
714218893Sdim
715263509Sdim	if (bp)
716263509Sdim	{
717263509Sdim		retrier(fdcu);
718263509Sdim		fdc_data[fdcu].status[0] = 0xc0;
719263509Sdim		fdc_data[fdcu].state = IOTIMEDOUT;
720263509Sdim		if( fdc_data[fdcu].retry < 6)
721263509Sdim			fdc_data[fdcu].retry = 6;
722263509Sdim	}
723263509Sdim	else
724263509Sdim	{
725263509Sdim		fdc_data[fdcu].fd = (fd_p) 0;
726263509Sdim		fdc_data[fdcu].fdu = -1;
727263509Sdim		fdc_data[fdcu].state = DEVIDLE;
728263509Sdim	}
729263509Sdim	fdintr(fdcu);
730263509Sdim	splx(s);
731263509Sdim}
732263509Sdim
733263509Sdim/* just ensure it has the right spl */
734263509Sdimstatic void
735263509Sdimfd_pseudointr(caddr_t arg1, int arg2)
736263509Sdim{
737263509Sdim	fdcu_t fdcu = (fdcu_t)arg1;
738263509Sdim	int	s;
739263509Sdim	s = splbio();
740263509Sdim	fdintr(fdcu);
741263509Sdim	splx(s);
742263509Sdim}
743263509Sdim
744263509Sdim/***********************************************************************\
745263509Sdim*                                 fdintr				*
746263509Sdim* keep calling the state machine until it returns a 0			*
747263509Sdim* ALWAYS called at SPLBIO 						*
748263509Sdim\***********************************************************************/
749252723Sdimvoid
750252723Sdimfdintr(fdcu_t fdcu)
751218893Sdim{
752218893Sdim	fdc_p fdc = fdc_data + fdcu;
753218893Sdim#if NFT > 0
754218893Sdim	fdu_t fdu = fdc->fdu;
755218893Sdim
756218893Sdim	if (fdc->flags & FDC_TAPE_BUSY)
757218893Sdim		(ftintr(fdu));
758252723Sdim	else
759218893Sdim#endif
760218893Sdim	while(fdstate(fdcu, fdc))
761218893Sdim	  ;
762235633Sdim}
763218893Sdim
764218893Sdim/***********************************************************************\
765218893Sdim* The controller state machine.						*
766235633Sdim* if it returns a non zero value, it should be called again immediatly	*
767218893Sdim\***********************************************************************/
768218893Sdimint
769218893Sdimfdstate(fdcu, fdc)
770218893Sdim	fdcu_t fdcu;
771218893Sdim	fdc_p fdc;
772218893Sdim{
773218893Sdim	int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0;
774218893Sdim	unsigned long blknum;
775218893Sdim	fdu_t fdu = fdc->fdu;
776218893Sdim	fd_p fd;
777218893Sdim	register struct buf *dp,*bp;
778218893Sdim	struct fd_formb *finfo = NULL;
779218893Sdim
780218893Sdim	dp = &(fdc->head);
781218893Sdim	bp = dp->b_actf;
782218893Sdim	if(!bp)
783202375Srdivacky	{
784204792Srdivacky		/***********************************************\
785202375Srdivacky		* nothing left for this controller to do	*
786202375Srdivacky		* Force into the IDLE state,			*
787202375Srdivacky		\***********************************************/
788202375Srdivacky		fdc->state = DEVIDLE;
789202375Srdivacky		if(fdc->fd)
790202375Srdivacky		{
791202375Srdivacky			printf("unexpected valid fd pointer (fdu = %d)\n"
792202375Srdivacky						,fdc->fdu);
793202375Srdivacky			fdc->fd = (fd_p) 0;
794202375Srdivacky			fdc->fdu = -1;
795202375Srdivacky		}
796202375Srdivacky		TRACE1("[fdc%d IDLE]",fdcu);
797235633Sdim 		return(0);
798202375Srdivacky	}
799202375Srdivacky	fdu = FDUNIT(minor(bp->b_dev));
800218893Sdim	fd = fd_data + fdu;
801218893Sdim	if (fdc->fd && (fd != fdc->fd))
802263509Sdim	{
803218893Sdim		printf("confused fd pointers\n");
804252723Sdim	}
805202375Srdivacky	read = bp->b_flags & B_READ;
806202375Srdivacky	format = bp->b_flags & B_FORMAT;
807202375Srdivacky	if(format)
808202375Srdivacky		finfo = (struct fd_formb *)bp->b_un.b_addr;
809202375Srdivacky	TRACE1("fd%d",fdu);
810252723Sdim	TRACE1("[%s]",fdstates[fdc->state]);
811202375Srdivacky	TRACE1("(0x%x)",fd->flags);
812202375Srdivacky	untimeout(fd_turnoff, (caddr_t)fdu);
813202375Srdivacky	timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
814202375Srdivacky	switch (fdc->state)
815202375Srdivacky	{
816202375Srdivacky	case DEVIDLE:
817204792Srdivacky	case FINDWORK:	/* we have found new work */
818202375Srdivacky		fdc->retry = 0;
819252723Sdim		fd->skip = 0;
820202375Srdivacky		fdc->fd = fd;
821202375Srdivacky		fdc->fdu = fdu;
822202375Srdivacky		outb(fdc->baseport+fdctl, fd->ft->trans);
823204792Srdivacky		/*******************************************************\
824202375Srdivacky		* If the next drive has a motor startup pending, then	*
825202375Srdivacky		* it will start up in it's own good time		*
826221345Sdim		\*******************************************************/
827221345Sdim		if(fd->flags & FD_MOTOR_WAIT)
828221345Sdim		{
829221345Sdim			fdc->state = MOTORWAIT;
830235633Sdim			return(0); /* come back later */
831221345Sdim		}
832221345Sdim		/*******************************************************\
833221345Sdim		* Maybe if it's not starting, it SHOULD be starting	*
834221345Sdim		\*******************************************************/
835221345Sdim		if (!(fd->flags & FD_MOTOR))
836252723Sdim		{
837221345Sdim			fdc->state = MOTORWAIT;
838221345Sdim			fd_turnon(fdu);
839221345Sdim			return(0);
840221345Sdim		}
841252723Sdim		else	/* at least make sure we are selected */
842252723Sdim		{
843221345Sdim			set_motor(fdcu,fd->fdsu,0);
844221345Sdim		}
845221345Sdim		fdc->state = DOSEEK;
846221345Sdim		break;
847221345Sdim	case DOSEEK:
848221345Sdim		if (bp->b_cylin == fd->track)
849221345Sdim		{
850221345Sdim			fdc->state = SEEKCOMPLETE;
851221345Sdim			break;
852221345Sdim		}
853221345Sdim		out_fdc(fdcu,NE7CMD_SEEK);	/* Seek function */
854221345Sdim		out_fdc(fdcu,fd->fdsu);		/* Drive number */
855221345Sdim		out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
856221345Sdim		fd->track = -2;
857221345Sdim		fdc->state = SEEKWAIT;
858221345Sdim		timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
859221345Sdim		return(0);	/* will return later */
860221345Sdim	case SEEKWAIT:
861235633Sdim		untimeout(fd_timeout, (caddr_t)fdcu);
862202375Srdivacky		/* allow heads to settle */
863202375Srdivacky		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50);
864202375Srdivacky		fdc->state = SEEKCOMPLETE;
865252723Sdim		return(0);	/* will return later */
866202375Srdivacky		break;
867202375Srdivacky
868202375Srdivacky	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
869202375Srdivacky		/* Make sure seek really happened*/
870202375Srdivacky		if(fd->track == -2)
871202375Srdivacky		{
872221345Sdim			int descyl = bp->b_cylin * fd->ft->steptrac;
873221345Sdim			out_fdc(fdcu,NE7CMD_SENSEI);
874221345Sdim			i = in_fdc(fdcu);
875252723Sdim			cyl = in_fdc(fdcu);
876221345Sdim			if (cyl != descyl)
877252723Sdim			{
878221345Sdim				printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
879221345Sdim				fdu, descyl, cyl, i, NE7_ST0BITS);
880221345Sdim				return(retrier(fdcu));
881221345Sdim			}
882221345Sdim		}
883202375Srdivacky
884202375Srdivacky		fd->track = bp->b_cylin;
885202375Srdivacky		if(format)
886252723Sdim			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
887202375Srdivacky				- (char *)finfo;
888202375Srdivacky		isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
889202375Srdivacky			format ? bp->b_bcount : FDBLK, fdc->dmachan);
890252723Sdim		blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
891202375Srdivacky			+ fd->skip/FDBLK;
892202375Srdivacky		sectrac = fd->ft->sectrac;
893202375Srdivacky		sec = blknum %  (sectrac * fd->ft->heads);
894202375Srdivacky		head = sec / sectrac;
895252723Sdim		sec = sec % sectrac + 1;
896202375Srdivacky/*XXX*/		fd->hddrv = ((head&1)<<2)+fdu;
897202375Srdivacky
898202375Srdivacky		if(format)
899202375Srdivacky		{
900202375Srdivacky			/* formatting */
901202375Srdivacky			out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d);
902203954Srdivacky			out_fdc(fdcu,head << 2 | fdu);
903202375Srdivacky			out_fdc(fdcu,finfo->fd_formb_secshift);
904202375Srdivacky			out_fdc(fdcu,finfo->fd_formb_nsecs);
905252723Sdim			out_fdc(fdcu,finfo->fd_formb_gaplen);
906252723Sdim			out_fdc(fdcu,finfo->fd_formb_fillbyte);
907202375Srdivacky		}
908202375Srdivacky		else
909202375Srdivacky		{
910202375Srdivacky			if (read)
911202375Srdivacky			{
912202375Srdivacky				out_fdc(fdcu,NE7CMD_READ);      /* READ */
913202375Srdivacky			}
914202375Srdivacky			else
915202375Srdivacky			{
916202375Srdivacky				out_fdc(fdcu,NE7CMD_WRITE);     /* WRITE */
917202375Srdivacky			}
918204792Srdivacky			out_fdc(fdcu,head << 2 | fdu);  /* head & unit */
919202375Srdivacky			out_fdc(fdcu,fd->track);        /* track */
920202375Srdivacky			out_fdc(fdcu,head);
921202375Srdivacky			out_fdc(fdcu,sec);              /* sector XXX +1? */
922202375Srdivacky			out_fdc(fdcu,fd->ft->secsize);  /* sector size */
923202375Srdivacky			out_fdc(fdcu,sectrac);          /* sectors/track */
924202375Srdivacky			out_fdc(fdcu,fd->ft->gap);      /* gap size */
925204792Srdivacky			out_fdc(fdcu,fd->ft->datalen);  /* data length */
926202375Srdivacky		}
927202375Srdivacky		fdc->state = IOCOMPLETE;
928202375Srdivacky		timeout(fd_timeout, (caddr_t)fdcu, 2 * hz);
929204792Srdivacky		return(0);	/* will return later */
930202375Srdivacky	case IOCOMPLETE: /* IO DONE, post-analyze */
931202375Srdivacky		untimeout(fd_timeout, (caddr_t)fdcu);
932202375Srdivacky		for(i=0;i<7;i++)
933202375Srdivacky		{
934204792Srdivacky			fdc->status[i] = in_fdc(fdcu);
935202375Srdivacky		}
936263509Sdim	case IOTIMEDOUT: /*XXX*/
937263509Sdim		isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
938263509Sdim			format ? bp->b_bcount : FDBLK, fdc->dmachan);
939263509Sdim		if (fdc->status[0]&0xF8)
940202375Srdivacky		{
941202375Srdivacky                        if (fdc->status[1] & 0x10) {
942202375Srdivacky                                /*
943263509Sdim				 * Operation not completed in reasonable time.
944263509Sdim				 * Just restart it, don't increment retry count.
945202375Srdivacky				 * (vak)
946202375Srdivacky                                 */
947202375Srdivacky                                fdc->state = SEEKCOMPLETE;
948202375Srdivacky                                return (1);
949202375Srdivacky                        }
950202375Srdivacky			return(retrier(fdcu));
951202375Srdivacky		}
952202375Srdivacky		/* All OK */
953202375Srdivacky		fd->skip += FDBLK;
954204792Srdivacky		if (!format && fd->skip < bp->b_bcount)
955202375Srdivacky		{
956202375Srdivacky			/* set up next transfer */
957202375Srdivacky			blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
958202375Srdivacky				+ fd->skip/FDBLK;
959204792Srdivacky			bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
960202375Srdivacky			fdc->state = DOSEEK;
961202375Srdivacky		}
962202375Srdivacky		else
963202375Srdivacky		{
964202375Srdivacky			/* ALL DONE */
965202375Srdivacky			fd->skip = 0;
966202375Srdivacky			bp->b_resid = 0;
967202375Srdivacky			dp->b_actf = bp->av_forw;
968202375Srdivacky			biodone(bp);
969202375Srdivacky			fdc->fd = (fd_p) 0;
970202375Srdivacky			fdc->fdu = -1;
971204792Srdivacky			fdc->state = FINDWORK;
972202375Srdivacky		}
973202375Srdivacky		return(1);
974202375Srdivacky	case RESETCTLR:
975202375Srdivacky		/* Try a reset, keep motor on */
976202375Srdivacky		set_motor(fdcu,fd->fdsu,1);
977202375Srdivacky		DELAY(100);
978202375Srdivacky		set_motor(fdcu,fd->fdsu,0);
979202375Srdivacky		outb(fdc->baseport+fdctl,fd->ft->trans);
980202375Srdivacky		TRACE1("[0x%x->fdctl]",fd->ft->trans);
981204792Srdivacky		fdc->retry++;
982202375Srdivacky		fdc->state = STARTRECAL;
983202375Srdivacky		break;
984202375Srdivacky	case STARTRECAL:
985202375Srdivacky		out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
986204792Srdivacky		out_fdc(fdcu,0xDF);
987202375Srdivacky		out_fdc(fdcu,2);
988202375Srdivacky		out_fdc(fdcu,NE7CMD_RECAL);	/* Recalibrate Function */
989204792Srdivacky		out_fdc(fdcu,fdu);
990202375Srdivacky		fdc->state = RECALWAIT;
991202375Srdivacky		return(0);	/* will return later */
992202375Srdivacky	case RECALWAIT:
993202375Srdivacky		/* allow heads to settle */
994202375Srdivacky		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30);
995202375Srdivacky		fdc->state = RECALCOMPLETE;
996202375Srdivacky		return(0);	/* will return later */
997202375Srdivacky	case RECALCOMPLETE:
998202375Srdivacky		out_fdc(fdcu,NE7CMD_SENSEI);
999204792Srdivacky		st0 = in_fdc(fdcu);
1000202375Srdivacky		cyl = in_fdc(fdcu);
1001202375Srdivacky		if (cyl != 0)
1002202375Srdivacky		{
1003202375Srdivacky			printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
1004204792Srdivacky				st0, NE7_ST0BITS, cyl);
1005202375Srdivacky			return(retrier(fdcu));
1006202375Srdivacky		}
1007204792Srdivacky		fd->track = 0;
1008202375Srdivacky		/* Seek (probably) necessary */
1009202375Srdivacky		fdc->state = DOSEEK;
1010202375Srdivacky		return(1);	/* will return immediatly */
1011202375Srdivacky	case	MOTORWAIT:
1012202375Srdivacky		if(fd->flags & FD_MOTOR_WAIT)
1013252723Sdim		{
1014202375Srdivacky			return(0); /* time's not up yet */
1015202375Srdivacky		}
1016202375Srdivacky		fdc->state = DOSEEK;
1017204792Srdivacky		return(1);	/* will return immediatly */
1018204792Srdivacky	default:
1019204792Srdivacky		printf("Unexpected FD int->");
1020204792Srdivacky		out_fdc(fdcu,NE7CMD_SENSEI);
1021202375Srdivacky		st0 = in_fdc(fdcu);
1022202375Srdivacky		cyl = in_fdc(fdcu);
1023252723Sdim		printf("ST0 = %lx, PCN = %lx\n",i,sec);
1024252723Sdim		out_fdc(fdcu,0x4A);
1025252723Sdim		out_fdc(fdcu,fd->fdsu);
1026202375Srdivacky		for(i=0;i<7;i++) {
1027202375Srdivacky			fdc->status[i] = in_fdc(fdcu);
1028202375Srdivacky		}
1029202375Srdivacky	printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
1030202375Srdivacky		fdc->status[0],
1031202375Srdivacky		fdc->status[1],
1032263509Sdim		fdc->status[2],
1033204792Srdivacky		fdc->status[3],
1034202375Srdivacky		fdc->status[4],
1035252723Sdim		fdc->status[5],
1036202375Srdivacky		fdc->status[6] );
1037202375Srdivacky		return(0);
1038202375Srdivacky	}
1039202375Srdivacky	return(1); /* Come back immediatly to new state */
1040204792Srdivacky}
1041202375Srdivacky
1042202375Srdivackystatic int
1043252723Sdimretrier(fdcu)
1044202375Srdivacky	fdcu_t fdcu;
1045202375Srdivacky{
1046202375Srdivacky	fdc_p fdc = fdc_data + fdcu;
1047252723Sdim	register struct buf *dp,*bp;
1048252723Sdim
1049202375Srdivacky	dp = &(fdc->head);
1050202375Srdivacky	bp = dp->b_actf;
1051202375Srdivacky
1052202375Srdivacky	switch(fdc->retry)
1053202375Srdivacky	{
1054252723Sdim	case 0: case 1: case 2:
1055202375Srdivacky		fdc->state = SEEKCOMPLETE;
1056202375Srdivacky		break;
1057202375Srdivacky	case 3: case 4: case 5:
1058204792Srdivacky		fdc->state = STARTRECAL;
1059202375Srdivacky		break;
1060204792Srdivacky	case 6:
1061202375Srdivacky		fdc->state = RESETCTLR;
1062204792Srdivacky		break;
1063202375Srdivacky	case 7:
1064204792Srdivacky		break;
1065252723Sdim	default:
1066202375Srdivacky		{
1067202375Srdivacky			dev_t sav_b_dev = bp->b_dev;
1068202375Srdivacky			/* Trick diskerr */
1069202375Srdivacky			bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3);
1070245431Sdim			diskerr(bp, "fd", "hard error", LOG_PRINTF,
1071245431Sdim				fdc->fd->skip, (struct disklabel *)NULL);
1072245431Sdim			bp->b_dev = sav_b_dev;
1073202375Srdivacky			printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
1074202375Srdivacky			printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
1075202375Srdivacky			printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
1076202375Srdivacky			printf("cyl %d hd %d sec %d)\n",
1077202375Srdivacky			       fdc->status[3], fdc->status[4], fdc->status[5]);
1078202375Srdivacky		}
1079245431Sdim		bp->b_flags |= B_ERROR;
1080202375Srdivacky		bp->b_error = EIO;
1081245431Sdim		bp->b_resid = bp->b_bcount - fdc->fd->skip;
1082245431Sdim		dp->b_actf = bp->av_forw;
1083245431Sdim		fdc->fd->skip = 0;
1084204792Srdivacky		biodone(bp);
1085252723Sdim		fdc->state = FINDWORK;
1086202375Srdivacky		fdc->fd = (fd_p) 0;
1087202375Srdivacky		fdc->fdu = -1;
1088204792Srdivacky		/* XXX abort current command, if any.  */
1089202375Srdivacky		return(1);
1090204792Srdivacky	}
1091202375Srdivacky	fdc->retry++;
1092202375Srdivacky	return(1);
1093202375Srdivacky}
1094202375Srdivacky
1095202375Srdivackystatic int
1096202375Srdivackyfdformat(dev, finfo, p)
1097202375Srdivacky	dev_t dev;
1098202375Srdivacky	struct fd_formb *finfo;
1099218893Sdim	struct proc *p;
1100202375Srdivacky{
1101202375Srdivacky 	fdu_t	fdu;
1102202375Srdivacky 	fd_p	fd;
1103202375Srdivacky
1104202375Srdivacky	struct buf *bp;
1105218893Sdim	int rv = 0, s;
1106218893Sdim
1107218893Sdim 	fdu = FDUNIT(minor(dev));
1108218893Sdim	fd = &fd_data[fdu];
1109252723Sdim
1110202375Srdivacky	/* set up a buffer header for fdstrategy() */
1111202375Srdivacky	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1112252723Sdim	if(bp == 0)
1113202375Srdivacky		return ENOBUFS;
1114202375Srdivacky	bzero((void *)bp, sizeof(struct buf));
1115202375Srdivacky	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1116202375Srdivacky	bp->b_proc = p;
1117202375Srdivacky	bp->b_dev = dev;
1118202375Srdivacky
1119202375Srdivacky	/*
1120202375Srdivacky	 * calculate a fake blkno, so fdstrategy() would initiate a
1121202375Srdivacky	 * seek to the requested cylinder
1122202375Srdivacky	 */
1123202375Srdivacky	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
1124218893Sdim		+ finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE;
1125202375Srdivacky
1126202375Srdivacky	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1127252723Sdim	bp->b_un.b_addr = (caddr_t)finfo;
1128218893Sdim
1129202375Srdivacky	/* now do the format */
1130202375Srdivacky	fdstrategy(bp);
1131202375Srdivacky
1132202375Srdivacky	/* ...and wait for it to complete */
1133202375Srdivacky	s = splbio();
1134202375Srdivacky	while(!(bp->b_flags & B_DONE))
1135202375Srdivacky	{
1136202375Srdivacky		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1137202375Srdivacky		if(rv == EWOULDBLOCK)
1138202375Srdivacky			break;
1139202375Srdivacky	}
1140202375Srdivacky	splx(s);
1141202375Srdivacky
1142202375Srdivacky	if(rv == EWOULDBLOCK)
1143202375Srdivacky	{
1144218893Sdim		/* timed out */
1145202375Srdivacky		biodone(bp);
1146202375Srdivacky		rv = EIO;
1147202375Srdivacky	}
1148202375Srdivacky	free(bp, M_TEMP);
1149202375Srdivacky	return rv;
1150202375Srdivacky}
1151202375Srdivacky
1152202375Srdivacky/*
1153202375Srdivacky * fdioctl() from jc@irbs.UUCP (John Capo)
1154202375Srdivacky * i386/i386/conf.c needs to have fdioctl() declared and remove the line that
1155202375Srdivacky * defines fdioctl to be enxio.
1156202375Srdivacky *
1157202375Srdivacky * TODO: Reformat.
1158202375Srdivacky *       Think about allocating buffer off stack.
1159202375Srdivacky *       Don't pass uncast 0's and NULL's to read/write/setdisklabel().
1160202375Srdivacky *       Watch out for NetBSD's different *disklabel() interface.
1161202375Srdivacky *
1162202375Srdivacky * Added functionality for floppy formatting
1163202375Srdivacky * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
1164218893Sdim */
1165202375Srdivacky
1166202375Srdivackyint
1167202375Srdivackyfdioctl (dev, cmd, addr, flag, p)
1168202375Srdivacky	dev_t dev;
1169218893Sdim	int cmd;
1170202375Srdivacky	caddr_t addr;
1171202375Srdivacky	int flag;
1172202375Srdivacky	struct proc *p;
1173202375Srdivacky{
1174202375Srdivacky	struct fd_type *fdt;
1175202375Srdivacky	struct disklabel *dl;
1176202375Srdivacky	char buffer[DEV_BSIZE];
1177202375Srdivacky	int error;
1178202375Srdivacky
1179202375Srdivacky#if NFT > 0
1180202375Srdivacky	int type = FDTYPE(minor(dev));
1181202375Srdivacky
1182202375Srdivacky	/* check for a tape ioctl */
1183202375Srdivacky	if (type & F_TAPE_TYPE)
1184202375Srdivacky		return ftioctl(dev, cmd, addr, flag, p);
1185202375Srdivacky#endif
1186202375Srdivacky
1187252723Sdim	error = 0;
1188202375Srdivacky
1189202375Srdivacky	switch (cmd)
1190202375Srdivacky	{
1191218893Sdim	case DIOCGDINFO:
1192252723Sdim		bzero(buffer, sizeof (buffer));
1193218893Sdim		dl = (struct disklabel *)buffer;
1194218893Sdim		dl->d_secsize = FDBLK;
1195218893Sdim		fdt = fd_data[FDUNIT(minor(dev))].ft;
1196218893Sdim		dl->d_secpercyl = fdt->size / fdt->tracks;
1197218893Sdim		dl->d_type = DTYPE_FLOPPY;
1198218893Sdim
1199218893Sdim		if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
1200252723Sdim			error = 0;
1201218893Sdim		else
1202218893Sdim			error = EINVAL;
1203218893Sdim
1204218893Sdim		*(struct disklabel *)addr = *dl;
1205218893Sdim		break;
1206202375Srdivacky
1207202375Srdivacky	case DIOCSDINFO:
1208202375Srdivacky		if ((flag & FWRITE) == 0)
1209202375Srdivacky			error = EBADF;
1210202375Srdivacky		break;
1211202375Srdivacky
1212202375Srdivacky	case DIOCWLABEL:
1213202375Srdivacky		if ((flag & FWRITE) == 0)
1214202375Srdivacky			error = EBADF;
1215202375Srdivacky		break;
1216202375Srdivacky
1217202375Srdivacky	case DIOCWDINFO:
1218202375Srdivacky		if ((flag & FWRITE) == 0)
1219202375Srdivacky		{
1220202375Srdivacky			error = EBADF;
1221202375Srdivacky			break;
1222202375Srdivacky		}
1223202375Srdivacky
1224202375Srdivacky		dl = (struct disklabel *)addr;
1225202375Srdivacky
1226202375Srdivacky		if (error = setdisklabel ((struct disklabel *)buffer,
1227252723Sdim		    dl, 0, NULL))
1228202375Srdivacky			break;
1229202375Srdivacky
1230202375Srdivacky		error = writedisklabel(dev, fdstrategy,
1231202375Srdivacky			(struct disklabel *)buffer, NULL);
1232202375Srdivacky		break;
1233202375Srdivacky
1234202375Srdivacky	case FD_FORM:
1235252723Sdim		if((flag & FWRITE) == 0)
1236202375Srdivacky			error = EBADF;	/* must be opened for writing */
1237202375Srdivacky		else if(((struct fd_formb *)addr)->format_version !=
1238202375Srdivacky			FD_FORMAT_VERSION)
1239202375Srdivacky			error = EINVAL;	/* wrong version of formatting prog */
1240202375Srdivacky		else
1241252723Sdim			error = fdformat(dev, (struct fd_formb *)addr, p);
1242226890Sdim		break;
1243226890Sdim
1244226890Sdim	case FD_GTYPE:                  /* get drive type */
1245226890Sdim		*(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
1246226890Sdim		break;
1247226890Sdim
1248226890Sdim	default:
1249226890Sdim		error = EINVAL;
1250226890Sdim		break;
1251226890Sdim	}
1252226890Sdim	return (error);
1253202375Srdivacky}
1254202375Srdivacky
1255226890Sdim#endif
1256226890Sdim