fdc.c revision 3085
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Don Ahn.
7 *
8 * Portions Copyright (c) 1993, 1994 by
9 *  jc@irbs.UUCP (John Capo)
10 *  vak@zebub.msk.su (Serge Vakulenko)
11 *  ache@astral.msk.su (Andrew A. Chernov)
12 *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *	This product includes software developed by the University of
25 *	California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 *    may be used to endorse or promote products derived from this software
28 *    without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 *	from:	@(#)fd.c	7.4 (Berkeley) 5/25/91
43 *	$Id: fd.c,v 1.31 1994/09/17 18:08:36 joerg Exp $
44 *
45 */
46
47#include "ft.h"
48#if NFT < 1
49#undef NFDC
50#endif
51#include "fd.h"
52
53#if NFDC > 0
54
55#include <sys/param.h>
56#include <sys/dkbad.h>
57#include <sys/systm.h>
58#include <sys/kernel.h>
59#include <sys/conf.h>
60#include <sys/file.h>
61#include <sys/ioctl.h>
62#include <machine/ioctl_fd.h>
63#include <sys/disklabel.h>
64#include <sys/buf.h>
65#include <sys/uio.h>
66#include <sys/malloc.h>
67#include <sys/proc.h>
68#include <sys/syslog.h>
69#include <i386/isa/isa.h>
70#include <i386/isa/isa_device.h>
71#include <i386/isa/fdreg.h>
72#include <i386/isa/fdc.h>
73#include <i386/isa/rtc.h>
74#if NFT > 0
75#include <sys/ftape.h>
76#include <i386/isa/ftreg.h>
77#endif
78
79#define RAW_PART 2
80#define b_cylin b_resid
81
82/* misuse a flag to identify format operation */
83#define B_FORMAT B_XXX
84
85/*
86 * this biotab field doubles as a field for the physical unit number
87 * on the controller
88 */
89#define id_physid id_scsiid
90
91#define NUMTYPES 14
92#define NUMDENS  (NUMTYPES - 6)
93
94/* These defines (-1) must match index for fd_types */
95#define F_TAPE_TYPE	0x020	/* bit for fd_types to indicate tape */
96#define NO_TYPE		0	/* must match NO_TYPE in ft.c */
97#define FD_1720         1
98#define FD_1480         2
99#define FD_1440         3
100#define FD_1200         4
101#define FD_820          5
102#define FD_800          6
103#define FD_720          7
104#define FD_360          8
105
106#define FD_1480in5_25   9
107#define FD_1440in5_25   10
108#define FD_820in5_25    11
109#define FD_800in5_25    12
110#define FD_720in5_25    13
111#define FD_360in5_25    14
112
113
114struct fd_type fd_types[NUMTYPES] =
115{
116{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
117{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
118{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
119{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
120{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
121{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
122{  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
123{  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
124
125{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
126{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
127{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
128{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
129{  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
130{  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
131};
132
133#define DRVS_PER_CTLR 2		/* 2 floppies */
134/***********************************************************************\
135* Per controller structure.						*
136\***********************************************************************/
137struct fdc_data fdc_data[NFDC];
138
139/***********************************************************************\
140* Per drive structure.							*
141* N per controller  (DRVS_PER_CTLR)					*
142\***********************************************************************/
143struct fd_data {
144	struct	fdc_data *fdc;	/* pointer to controller structure */
145	int	fdsu;		/* this units number on this controller */
146	int	type;		/* Drive type (FD_1440...) */
147	struct	fd_type *ft;	/* pointer to the type descriptor */
148	int	flags;
149#define	FD_OPEN		0x01	/* it's open		*/
150#define	FD_ACTIVE	0x02	/* it's active		*/
151#define	FD_MOTOR	0x04	/* motor should be on	*/
152#define	FD_MOTOR_WAIT	0x08	/* motor coming up	*/
153	int	skip;
154	int	hddrv;
155	int	track;		/* where we think the head is */
156	int	options;	/* user configurable options, see ioctl_fd.h */
157} fd_data[NFD];
158
159/***********************************************************************\
160* Throughout this file the following conventions will be used:		*
161* fd is a pointer to the fd_data struct for the drive in question	*
162* fdc is a pointer to the fdc_data struct for the controller		*
163* fdu is the floppy drive unit number					*
164* fdcu is the floppy controller unit number				*
165* fdsu is the floppy drive unit number on that controller. (sub-unit)	*
166\***********************************************************************/
167
168#if NFT > 0
169int ftopen(dev_t, int);
170int ftintr(ftu_t ftu);
171int ftclose(dev_t, int);
172void ftstrategy(struct buf *);
173int ftioctl(dev_t, int, caddr_t, int, struct proc *);
174int ftdump(dev_t);
175int ftsize(dev_t);
176int ftattach(struct isa_device *, struct isa_device *);
177#endif
178
179/* autoconfig functions */
180static int fdprobe(struct isa_device *);
181static int fdattach(struct isa_device *);
182
183/* exported functions */
184int fdsize (dev_t);
185void fdintr(fdcu_t);
186int Fdopen(dev_t, int);
187int fdclose(dev_t, int);
188void fdstrategy(struct buf *);
189int fdioctl(dev_t, int, caddr_t, int, struct proc *);
190
191/* needed for ft driver, thus exported */
192int in_fdc(fdcu_t);
193int out_fdc(fdcu_t, int);
194
195/* internal functions */
196static void set_motor(fdcu_t, int, int);
197#  define TURNON 1
198#  define TURNOFF 0
199static timeout_t fd_turnoff;
200static timeout_t fd_motor_on;
201static void fd_turnon(fdu_t);
202static void fdc_reset(fdc_p);
203static void fdstart(fdcu_t);
204static timeout_t fd_timeout;
205static timeout_t fd_pseudointr;
206static int fdstate(fdcu_t, fdc_p);
207static int retrier(fdcu_t);
208static int fdformat(dev_t, struct fd_formb *, struct proc *);
209
210
211#define DEVIDLE		0
212#define FINDWORK	1
213#define	DOSEEK		2
214#define SEEKCOMPLETE 	3
215#define	IOCOMPLETE	4
216#define RECALCOMPLETE	5
217#define	STARTRECAL	6
218#define	RESETCTLR	7
219#define	SEEKWAIT	8
220#define	RECALWAIT	9
221#define	MOTORWAIT	10
222#define	IOTIMEDOUT	11
223
224#ifdef	DEBUG
225char *fdstates[] =
226{
227"DEVIDLE",
228"FINDWORK",
229"DOSEEK",
230"SEEKCOMPLETE",
231"IOCOMPLETE",
232"RECALCOMPLETE",
233"STARTRECAL",
234"RESETCTLR",
235"SEEKWAIT",
236"RECALWAIT",
237"MOTORWAIT",
238"IOTIMEDOUT"
239};
240
241/* CAUTION: fd_debug causes huge amounts of logging output */
242int	fd_debug = 0;
243#define TRACE0(arg) if(fd_debug) printf(arg)
244#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
245#else /* DEBUG */
246#define TRACE0(arg)
247#define TRACE1(arg1, arg2)
248#endif /* DEBUG */
249
250/****************************************************************************/
251/*                      autoconfiguration stuff                             */
252/****************************************************************************/
253struct	isa_driver fdcdriver = {
254	fdprobe, fdattach, "fdc",
255};
256
257/*
258 * probe for existance of controller
259 */
260static int
261fdprobe(dev)
262	struct isa_device *dev;
263{
264	fdcu_t	fdcu = dev->id_unit;
265	if(fdc_data[fdcu].flags & FDC_ATTACHED)
266	{
267		printf("fdc: same unit (%d) used multiple times\n", fdcu);
268		return 0;
269	}
270
271	fdc_data[fdcu].baseport = dev->id_iobase;
272
273	/* First - lets reset the floppy controller */
274	outb(dev->id_iobase+FDOUT, 0);
275	DELAY(100);
276	outb(dev->id_iobase+FDOUT, FDO_FRST);
277
278	/* see if it can handle a command */
279	if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0)
280	{
281		return(0);
282	}
283	out_fdc(fdcu, NE7_SPEC_1(3, 240));
284	out_fdc(fdcu, NE7_SPEC_2(2, 0));
285	return (IO_FDCSIZE);
286}
287
288/*
289 * wire controller into system, look for floppy units
290 */
291static int
292fdattach(dev)
293	struct isa_device *dev;
294{
295	unsigned fdt;
296	fdu_t	fdu;
297	fdcu_t	fdcu = dev->id_unit;
298	fdc_p	fdc = fdc_data + fdcu;
299	fd_p	fd;
300	int	fdsu, st0, i;
301	struct isa_device *fdup;
302
303	fdc->fdcu = fdcu;
304	fdc->flags |= FDC_ATTACHED;
305	fdc->dmachan = dev->id_drq;
306	fdc->state = DEVIDLE;
307	/* reset controller, turn motor off, clear fdout mirror reg */
308	outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
309	printf("fdc%d:", fdcu);
310
311	/* check for each floppy drive */
312	for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
313		if (fdup->id_iobase != dev->id_iobase)
314			continue;
315		fdu = fdup->id_unit;
316		fd = &fd_data[fdu];
317		if (fdu >= (NFD+NFT))
318			continue;
319		fdsu = fdup->id_physid;
320				/* look up what bios thinks we have */
321		switch (fdu) {
322			case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
323				break;
324			case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
325				break;
326			default: fdt = RTCFDT_NONE;
327				break;
328		}
329		/* is there a unit? */
330		if ((fdt == RTCFDT_NONE)
331#if NFT > 0
332		|| (fdsu >= DRVS_PER_CTLR)) {
333#else
334		) {
335			fd->type = NO_TYPE;
336#endif
337#if NFT > 0
338				/* If BIOS says no floppy, or > 2nd device */
339				/* Probe for and attach a floppy tape.     */
340			if (ftattach(dev, fdup))
341				continue;
342			if (fdsu < DRVS_PER_CTLR)
343				fd->type = NO_TYPE;
344#endif
345			continue;
346		}
347
348		/* select it */
349		set_motor(fdcu, fdsu, TURNON);
350		DELAY(1000000);	/* 1 sec */
351		out_fdc(fdcu, NE7CMD_SENSED);
352		out_fdc(fdcu, fdsu);
353		if(in_fdc(fdcu) & NE7_ST3_T0) {
354			/* if at track 0, first seek inwards */
355			out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */
356			out_fdc(fdcu, fdsu);
357			out_fdc(fdcu, 10);
358			DELAY(300000); /* ...wait a moment... */
359			out_fdc(fdcu, NE7CMD_SENSEI); /* make ctrlr happy */
360			(void)in_fdc(fdcu);
361			(void)in_fdc(fdcu);
362		}
363		for(i = 0; i < 2; i++) {
364			/*
365			 * we must recalibrate twice, just in case the
366			 * heads have been beyond cylinder 76, since most
367			 * FDCs still barf when attempting to recalibrate
368			 * more than 77 steps
369			 */
370			out_fdc(fdcu, NE7CMD_RECAL); /* go back to 0 */
371			out_fdc(fdcu, fdsu);
372			/* a second being enough for full stroke seek */
373			DELAY(i == 0? 1000000: 300000);
374
375			/* anything responding? */
376			out_fdc(fdcu, NE7CMD_SENSEI);
377			st0 = in_fdc(fdcu);
378			(void)in_fdc(fdcu);
379
380			if ((st0 & NE7_ST0_EC) == 0)
381				break; /* already probed succesfully */
382		}
383
384		set_motor(fdcu, fdsu, TURNOFF);
385
386		if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
387			continue;
388
389		fd->track = -2;
390		fd->fdc = fdc;
391		fd->fdsu = fdsu;
392		fd->options = 0;
393		printf(" [%d: fd%d: ", fdsu, fdu);
394
395		switch (fdt) {
396		case RTCFDT_12M:
397			printf("1.2MB 5.25in]");
398			fd->type = FD_1200;
399			break;
400		case RTCFDT_144M:
401			printf("1.44MB 3.5in]");
402			fd->type = FD_1440;
403			break;
404		case RTCFDT_360K:
405			printf("360KB 5.25in]");
406			fd->type = FD_360;
407			break;
408		case RTCFDT_720K:
409			printf("720KB 3.5in]");
410			fd->type = FD_720;
411			break;
412		default:
413			printf("unknown]");
414			fd->type = NO_TYPE;
415			break;
416		}
417	}
418	printf("\n");
419
420	return (1);
421}
422
423int
424fdsize(dev)
425	dev_t	dev;
426{
427	return(0);
428}
429
430/****************************************************************************/
431/*                            motor control stuff                           */
432/*		remember to not deselect the drive we're working on         */
433/****************************************************************************/
434static void
435set_motor(fdcu, fdsu, turnon)
436	fdcu_t fdcu;
437	int fdsu;
438	int turnon;
439{
440	int fdout = fdc_data[fdcu].fdout;
441	int needspecify = 0;
442
443	if(turnon) {
444		fdout &= ~FDO_FDSEL;
445		fdout |= (FDO_MOEN0 << fdsu) + fdsu;
446	} else
447		fdout &= ~(FDO_MOEN0 << fdsu);
448
449	if(!turnon
450	   && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
451		/* gonna turn off the last drive, put FDC to bed */
452		fdout &= ~ (FDO_FRST|FDO_FDMAEN);
453	else {
454		/* make sure controller is selected and specified */
455		if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
456			needspecify = 1;
457		fdout |= (FDO_FRST|FDO_FDMAEN);
458	}
459
460	outb(fdc_data[fdcu].baseport+FDOUT, fdout);
461	fdc_data[fdcu].fdout = fdout;
462	TRACE1("[0x%x->FDOUT]", fdout);
463
464	if(needspecify) {
465		out_fdc(fdcu, NE7CMD_SPECIFY);
466		out_fdc(fdcu, NE7_SPEC_1(3, 240));
467		out_fdc(fdcu, NE7_SPEC_2(2, 0));
468	}
469}
470
471/* ARGSUSED */
472static void
473fd_turnoff(void *arg1)
474{
475	fdu_t fdu = (fdu_t)arg1;
476	int	s;
477
478	fd_p fd = fd_data + fdu;
479	s = splbio();
480	fd->flags &= ~FD_MOTOR;
481	set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
482	splx(s);
483}
484
485/* ARGSUSED */
486static void
487fd_motor_on(void *arg1)
488{
489	fdu_t fdu = (fdu_t)arg1;
490	int	s;
491
492	fd_p fd = fd_data + fdu;
493	s = splbio();
494	fd->flags &= ~FD_MOTOR_WAIT;
495	if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
496	{
497		fdintr(fd->fdc->fdcu);
498	}
499	splx(s);
500}
501
502static void
503fd_turnon(fdu)
504	fdu_t fdu;
505{
506	fd_p fd = fd_data + fdu;
507	if(!(fd->flags & FD_MOTOR))
508	{
509		fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
510		set_motor(fd->fdc->fdcu, fd->fdsu, TURNON);
511		timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
512	}
513}
514
515static void
516fdc_reset(fdc)
517	fdc_p fdc;
518{
519	fdcu_t fdcu = fdc->fdcu;
520
521	/* Try a reset, keep motor on */
522	outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
523	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
524	DELAY(100);
525	/* enable FDC, but defer interrupts a moment */
526	outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
527	TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
528	DELAY(100);
529	outb(fdc->baseport + FDOUT, fdc->fdout);
530	TRACE1("[0x%x->FDOUT]", fdc->fdout);
531
532	out_fdc(fdcu, NE7CMD_SPECIFY);
533	out_fdc(fdcu, NE7_SPEC_1(3, 240));
534	out_fdc(fdcu, NE7_SPEC_2(2, 0));
535}
536
537/****************************************************************************/
538/*                             fdc in/out                                   */
539/****************************************************************************/
540int
541in_fdc(fdcu)
542	fdcu_t fdcu;
543{
544	int baseport = fdc_data[fdcu].baseport;
545	int i, j = 100000;
546	while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
547		!= (NE7_DIO|NE7_RQM) && j-- > 0)
548		if (i == NE7_RQM) return -1;
549	if (j <= 0)
550		return(-1);
551#ifdef	DEBUG
552	i = inb(baseport+FDDATA);
553	TRACE1("[FDDATA->0x%x]", (unsigned char)i);
554	return(i);
555#else
556	return inb(baseport+FDDATA);
557#endif
558}
559
560int
561out_fdc(fdcu, x)
562	fdcu_t fdcu;
563	int x;
564{
565	int baseport = fdc_data[fdcu].baseport;
566	int i;
567
568	/* Check that the direction bit is set */
569	i = 100000;
570	while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
571	if (i <= 0) return (-1);	/* Floppy timed out */
572
573	/* Check that the floppy controller is ready for a command */
574	i = 100000;
575	while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
576	if (i <= 0) return (-1);	/* Floppy timed out */
577
578	/* Send the command and return */
579	outb(baseport+FDDATA, x);
580	TRACE1("[0x%x->FDDATA]", x);
581	return (0);
582}
583
584/****************************************************************************/
585/*                           fdopen/fdclose                                 */
586/****************************************************************************/
587int
588Fdopen(dev, flags)
589	dev_t	dev;
590	int	flags;
591{
592 	fdu_t fdu = FDUNIT(minor(dev));
593	int type = FDTYPE(minor(dev));
594	fdc_p	fdc;
595
596#if NFT > 0
597	/* check for a tape open */
598	if (type & F_TAPE_TYPE)
599		return(ftopen(dev, flags));
600#endif
601	/* check bounds */
602	if (fdu >= NFD)
603		return(ENXIO);
604	fdc = fd_data[fdu].fdc;
605	if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
606		return(ENXIO);
607	if (type > NUMDENS)
608		return(ENXIO);
609	if (type == 0)
610		type = fd_data[fdu].type;
611	else {
612		if (type != fd_data[fdu].type) {
613			switch (fd_data[fdu].type) {
614			case FD_360:
615				return(ENXIO);
616			case FD_720:
617				if (   type != FD_820
618				    && type != FD_800
619				   )
620					return(ENXIO);
621				break;
622			case FD_1200:
623				switch (type) {
624				case FD_1480:
625					type = FD_1480in5_25;
626					break;
627				case FD_1440:
628					type = FD_1440in5_25;
629					break;
630				case FD_820:
631					type = FD_820in5_25;
632					break;
633				case FD_800:
634					type = FD_800in5_25;
635					break;
636				case FD_720:
637					type = FD_720in5_25;
638					break;
639				case FD_360:
640					type = FD_360in5_25;
641					break;
642				default:
643					return(ENXIO);
644				}
645				break;
646			case FD_1440:
647				if (   type != FD_1720
648				    && type != FD_1480
649				    && type != FD_1200
650				    && type != FD_820
651				    && type != FD_800
652				    && type != FD_720
653				    )
654					return(ENXIO);
655				break;
656			}
657		}
658	}
659	fd_data[fdu].ft = fd_types + type - 1;
660	fd_data[fdu].flags |= FD_OPEN;
661
662	return 0;
663}
664
665int
666fdclose(dev, flags)
667	dev_t dev;
668	int flags;
669{
670 	fdu_t fdu = FDUNIT(minor(dev));
671
672#if NFT > 0
673	int type = FDTYPE(minor(dev));
674
675	if (type & F_TAPE_TYPE)
676		return ftclose(dev, flags);
677#endif
678	fd_data[fdu].flags &= ~FD_OPEN;
679	fd_data[fdu].options &= ~FDOPT_NORETRY;
680	return(0);
681}
682
683
684/****************************************************************************/
685/*                               fdstrategy                                 */
686/****************************************************************************/
687void
688fdstrategy(struct buf *bp)
689{
690	register struct buf *dp;
691	long nblocks, blknum;
692 	int	s;
693 	fdcu_t	fdcu;
694 	fdu_t	fdu;
695 	fdc_p	fdc;
696 	fd_p	fd;
697	size_t	fdblk;
698
699 	fdu = FDUNIT(minor(bp->b_dev));
700	fd = &fd_data[fdu];
701	fdc = fd->fdc;
702	fdcu = fdc->fdcu;
703	fdblk = 128 << (fd->ft->secsize);
704
705#if NFT > 0
706	if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) {
707		/* ft tapes do not (yet) support strategy i/o */
708		bp->b_error = ENXIO;
709		bp->b_flags |= B_ERROR;
710		goto bad;
711	}
712	/* check for controller already busy with tape */
713	if (fdc->flags & FDC_TAPE_BUSY) {
714		bp->b_error = EBUSY;
715		bp->b_flags |= B_ERROR;
716		goto bad;
717	}
718#endif
719	if (!(bp->b_flags & B_FORMAT)) {
720		if ((fdu >= NFD) || (bp->b_blkno < 0)) {
721			printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
722			       fdu, bp->b_blkno, bp->b_bcount);
723			bp->b_error = EINVAL;
724			bp->b_flags |= B_ERROR;
725			goto bad;
726		}
727		if ((bp->b_bcount % fdblk) != 0) {
728			bp->b_error = EINVAL;
729			bp->b_flags |= B_ERROR;
730			goto bad;
731		}
732	}
733
734	/*
735	 * Set up block calculations.
736	 */
737	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk;
738 	nblocks = fd->ft->size;
739	if (blknum + (bp->b_bcount / fdblk) > nblocks) {
740		if (blknum == nblocks) {
741			bp->b_resid = bp->b_bcount;
742		} else {
743			bp->b_error = ENOSPC;
744			bp->b_flags |= B_ERROR;
745		}
746		goto bad;
747	}
748 	bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
749	dp = &(fdc->head);
750	s = splbio();
751	disksort(dp, bp);
752	untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */
753	fdstart(fdcu);
754	splx(s);
755	return;
756
757bad:
758	biodone(bp);
759}
760
761/***************************************************************\
762*				fdstart				*
763* We have just queued something.. if the controller is not busy	*
764* then simulate the case where it has just finished a command	*
765* So that it (the interrupt routine) looks on the queue for more*
766* work to do and picks up what we just added.			*
767* If the controller is already busy, we need do nothing, as it	*
768* will pick up our work when the present work completes		*
769\***************************************************************/
770static void
771fdstart(fdcu)
772	fdcu_t fdcu;
773{
774	int s;
775
776	s = splbio();
777	if(fdc_data[fdcu].state == DEVIDLE)
778	{
779		fdintr(fdcu);
780	}
781	splx(s);
782}
783
784/* ARGSUSED */
785static void
786fd_timeout(void *arg1)
787{
788	fdcu_t fdcu = (fdcu_t)arg1;
789	fdu_t fdu = fdc_data[fdcu].fdu;
790	int baseport = fdc_data[fdcu].baseport;
791	struct buf *dp, *bp;
792	int s;
793
794	dp = &fdc_data[fdcu].head;
795	bp = dp->b_actf;
796
797	/*
798	 * Due to IBM's brain-dead design, the FDC has a faked ready
799	 * signal, hardwired to ready == true. Thus, any command
800	 * issued if there's no diskette in the drive will _never_
801	 * complete, and must be aborted by resetting the FDC.
802	 * Many thanks, Big Blue!
803	 */
804
805	s = splbio();
806
807	TRACE1("fd%d[fd_timeout()]", fdu);
808	/* See if the controller is still busy (patiently awaiting data) */
809	if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB)
810	{
811		TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS));
812		/* yup, it is; kill it now */
813		fdc_reset(&fdc_data[fdcu]);
814		printf("fd%d: Operation timeout\n", fdu);
815	}
816
817	if (bp)
818	{
819		retrier(fdcu);
820		fdc_data[fdcu].status[0] = NE7_ST0_IC_RC;
821		fdc_data[fdcu].state = IOTIMEDOUT;
822		if( fdc_data[fdcu].retry < 6)
823			fdc_data[fdcu].retry = 6;
824	}
825	else
826	{
827		fdc_data[fdcu].fd = (fd_p) 0;
828		fdc_data[fdcu].fdu = -1;
829		fdc_data[fdcu].state = DEVIDLE;
830	}
831	fdintr(fdcu);
832	splx(s);
833}
834
835/* just ensure it has the right spl */
836/* ARGSUSED */
837static void
838fd_pseudointr(void *arg1)
839{
840	fdcu_t fdcu = (fdcu_t)arg1;
841	int	s;
842
843	s = splbio();
844	fdintr(fdcu);
845	splx(s);
846}
847
848/***********************************************************************\
849*                                 fdintr				*
850* keep calling the state machine until it returns a 0			*
851* ALWAYS called at SPLBIO 						*
852\***********************************************************************/
853void
854fdintr(fdcu_t fdcu)
855{
856	fdc_p fdc = fdc_data + fdcu;
857#if NFT > 0
858	fdu_t fdu = fdc->fdu;
859
860	if (fdc->flags & FDC_TAPE_BUSY)
861		(ftintr(fdu));
862	else
863#endif
864		while(fdstate(fdcu, fdc))
865			;
866}
867
868/***********************************************************************\
869* The controller state machine.						*
870* if it returns a non zero value, it should be called again immediatly	*
871\***********************************************************************/
872static int
873fdstate(fdcu, fdc)
874	fdcu_t fdcu;
875	fdc_p fdc;
876{
877	int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3;
878	unsigned long blknum;
879	fdu_t fdu = fdc->fdu;
880	fd_p fd;
881	register struct buf *dp, *bp;
882	struct fd_formb *finfo = NULL;
883	size_t fdblk;
884
885	dp = &(fdc->head);
886	bp = dp->b_actf;
887	if(!bp)
888	{
889		/***********************************************\
890		* nothing left for this controller to do	*
891		* Force into the IDLE state,			*
892		\***********************************************/
893		fdc->state = DEVIDLE;
894		if(fdc->fd)
895		{
896			printf("unexpected valid fd pointer (fdu = %d)\n",
897			       fdc->fdu);
898			fdc->fd = (fd_p) 0;
899			fdc->fdu = -1;
900		}
901		TRACE1("[fdc%d IDLE]", fdcu);
902 		return(0);
903	}
904	fdu = FDUNIT(minor(bp->b_dev));
905	fd = fd_data + fdu;
906	fdblk = 128 << fd->ft->secsize;
907	if (fdc->fd && (fd != fdc->fd))
908	{
909		printf("confused fd pointers\n");
910	}
911	read = bp->b_flags & B_READ;
912	format = bp->b_flags & B_FORMAT;
913	if(format)
914		finfo = (struct fd_formb *)bp->b_un.b_addr;
915	TRACE1("fd%d", fdu);
916	TRACE1("[%s]", fdstates[fdc->state]);
917	TRACE1("(0x%x)", fd->flags);
918	untimeout(fd_turnoff, (caddr_t)fdu);
919	timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
920	switch (fdc->state)
921	{
922	case DEVIDLE:
923	case FINDWORK:	/* we have found new work */
924		fdc->retry = 0;
925		fd->skip = 0;
926		fdc->fd = fd;
927		fdc->fdu = fdu;
928		outb(fdc->baseport+FDCTL, fd->ft->trans);
929		TRACE1("[0x%x->FDCTL]", fd->ft->trans);
930		/*******************************************************\
931		* If the next drive has a motor startup pending, then	*
932		* it will start up in it's own good time		*
933		\*******************************************************/
934		if(fd->flags & FD_MOTOR_WAIT)
935		{
936			fdc->state = MOTORWAIT;
937			return(0); /* come back later */
938		}
939		/*******************************************************\
940		* Maybe if it's not starting, it SHOULD be starting	*
941		\*******************************************************/
942		if (!(fd->flags & FD_MOTOR))
943		{
944			fdc->state = MOTORWAIT;
945			fd_turnon(fdu);
946			return(0);
947		}
948		else	/* at least make sure we are selected */
949		{
950			set_motor(fdcu, fd->fdsu, TURNON);
951		}
952		fdc->state = DOSEEK;
953		break;
954	case DOSEEK:
955		if (bp->b_cylin == fd->track)
956		{
957			fdc->state = SEEKCOMPLETE;
958			break;
959		}
960		out_fdc(fdcu, NE7CMD_SEEK);	/* Seek function */
961		out_fdc(fdcu, fd->fdsu);	/* Drive number */
962		out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac);
963		fd->track = -2;
964		fdc->state = SEEKWAIT;
965		return(0);	/* will return later */
966	case SEEKWAIT:
967		/* allow heads to settle */
968		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32);
969		fdc->state = SEEKCOMPLETE;
970		return(0);	/* will return later */
971	case SEEKCOMPLETE : /* SEEK DONE, START DMA */
972		/* Make sure seek really happened*/
973		if(fd->track == -2)
974		{
975			int descyl = bp->b_cylin * fd->ft->steptrac;
976			do {
977				out_fdc(fdcu, NE7CMD_SENSEI);
978				st0 = in_fdc(fdcu);
979				cyl = in_fdc(fdcu);
980				/*
981				 * if this was a "ready changed" interrupt,
982				 * fetch status again (can happen after
983				 * enabling controller from reset state)
984				 */
985			} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
986			if (0 == descyl)
987			{
988				/*
989				 * seek to cyl 0 requested; make sure we are
990				 * really there
991				 */
992				out_fdc(fdcu, NE7CMD_SENSED);
993				out_fdc(fdcu, fdu);
994				st3 = in_fdc(fdcu);
995				if ((st3 & NE7_ST3_T0) == 0) {
996					printf(
997		"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
998					       fdu, st3, NE7_ST3BITS);
999					if(fdc->retry < 3)
1000						fdc->retry = 3;
1001					return(retrier(fdcu));
1002				}
1003			}
1004			if (cyl != descyl)
1005			{
1006				printf(
1007		"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
1008				       fdu, descyl, cyl, st0, NE7_ST0BITS);
1009				return(retrier(fdcu));
1010			}
1011		}
1012
1013		fd->track = bp->b_cylin;
1014		if(format)
1015			fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1016				- (char *)finfo;
1017		isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
1018			format ? bp->b_bcount : fdblk, fdc->dmachan);
1019		blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
1020			+ fd->skip/fdblk;
1021		sectrac = fd->ft->sectrac;
1022		sec = blknum %  (sectrac * fd->ft->heads);
1023		head = sec / sectrac;
1024		sec = sec % sectrac + 1;
1025		fd->hddrv = ((head&1)<<2)+fdu;
1026
1027		if(format || !read)
1028		{
1029			/* make sure the drive is writable */
1030			out_fdc(fdcu, NE7CMD_SENSED);
1031			out_fdc(fdcu, fdu);
1032			st3 = in_fdc(fdcu);
1033			if(st3 & NE7_ST3_WP)
1034			{
1035				/*
1036				 * XXX YES! this is ugly.
1037				 * in order to force the current operation
1038				 * to fail, we will have to fake an FDC
1039				 * error - all error handling is done
1040				 * by the retrier()
1041				 */
1042				fdc->status[0] = NE7_ST0_IC_AT;
1043				fdc->status[1] = NE7_ST1_NW;
1044				fdc->status[2] = 0;
1045				fdc->status[3] = fd->track;
1046				fdc->status[4] = head;
1047				fdc->status[5] = sec;
1048				fdc->retry = 8;	/* break out immediately */
1049				fdc->state = IOTIMEDOUT; /* not really... */
1050				return (1);
1051			}
1052		}
1053
1054		if(format)
1055		{
1056			/* formatting */
1057			out_fdc(fdcu, NE7CMD_FORMAT);
1058			out_fdc(fdcu, head << 2 | fdu);
1059			out_fdc(fdcu, finfo->fd_formb_secshift);
1060			out_fdc(fdcu, finfo->fd_formb_nsecs);
1061			out_fdc(fdcu, finfo->fd_formb_gaplen);
1062			out_fdc(fdcu, finfo->fd_formb_fillbyte);
1063		}
1064		else
1065		{
1066			if (read)
1067			{
1068				out_fdc(fdcu, NE7CMD_READ);      /* READ */
1069			}
1070			else
1071			{
1072				out_fdc(fdcu, NE7CMD_WRITE);     /* WRITE */
1073			}
1074			out_fdc(fdcu, head << 2 | fdu);  /* head & unit */
1075			out_fdc(fdcu, fd->track);        /* track */
1076			out_fdc(fdcu, head);
1077			out_fdc(fdcu, sec);              /* sector XXX +1? */
1078			out_fdc(fdcu, fd->ft->secsize);  /* sector size */
1079			out_fdc(fdcu, sectrac);          /* sectors/track */
1080			out_fdc(fdcu, fd->ft->gap);      /* gap size */
1081			out_fdc(fdcu, fd->ft->datalen);  /* data length */
1082		}
1083		fdc->state = IOCOMPLETE;
1084		timeout(fd_timeout, (caddr_t)fdcu, hz);
1085		return(0);	/* will return later */
1086	case IOCOMPLETE: /* IO DONE, post-analyze */
1087		untimeout(fd_timeout, (caddr_t)fdcu);
1088		for(i=0;i<7;i++)
1089		{
1090			fdc->status[i] = in_fdc(fdcu);
1091		}
1092		fdc->state = IOTIMEDOUT;
1093		/* FALLTHROUGH */
1094	case IOTIMEDOUT:
1095		isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
1096			    format ? bp->b_bcount : fdblk, fdc->dmachan);
1097		if (fdc->status[0] & NE7_ST0_IC)
1098		{
1099                        if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
1100			    && fdc->status[1] & NE7_ST1_OR) {
1101                                /*
1102				 * DMA overrun. Someone hogged the bus
1103				 * and didn't release it in time for the
1104				 * next FDC transfer.
1105				 * Just restart it, don't increment retry
1106				 * count. (vak)
1107                                 */
1108                                fdc->state = SEEKCOMPLETE;
1109                                return (1);
1110                        }
1111			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
1112				&& fdc->retry < 6)
1113				fdc->retry = 6;	/* force a reset */
1114			else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
1115				&& fdc->status[2] & NE7_ST2_WC
1116				&& fdc->retry < 3)
1117				fdc->retry = 3;	/* force recalibrate */
1118			return(retrier(fdcu));
1119		}
1120		/* All OK */
1121		fd->skip += fdblk;
1122		if (!format && fd->skip < bp->b_bcount)
1123		{
1124			/* set up next transfer */
1125			blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
1126				+ fd->skip/fdblk;
1127			bp->b_cylin =
1128				(blknum / (fd->ft->sectrac * fd->ft->heads));
1129			fdc->state = DOSEEK;
1130		}
1131		else
1132		{
1133			/* ALL DONE */
1134			fd->skip = 0;
1135			bp->b_resid = 0;
1136			dp->b_actf = bp->b_actf;
1137			biodone(bp);
1138			fdc->fd = (fd_p) 0;
1139			fdc->fdu = -1;
1140			fdc->state = FINDWORK;
1141		}
1142		return(1);
1143	case RESETCTLR:
1144		fdc_reset(fdc);
1145		fdc->retry++;
1146		fdc->state = STARTRECAL;
1147		break;
1148	case STARTRECAL:
1149		out_fdc(fdcu, NE7CMD_RECAL);	/* Recalibrate Function */
1150		out_fdc(fdcu, fdu);
1151		fdc->state = RECALWAIT;
1152		return(0);	/* will return later */
1153	case RECALWAIT:
1154		/* allow heads to settle */
1155		timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32);
1156		fdc->state = RECALCOMPLETE;
1157		return(0);	/* will return later */
1158	case RECALCOMPLETE:
1159		do {
1160			out_fdc(fdcu, NE7CMD_SENSEI);
1161			st0 = in_fdc(fdcu);
1162			cyl = in_fdc(fdcu);
1163			/*
1164			 * if this was a "ready changed" interrupt,
1165			 * fetch status again (can happen after
1166			 * enabling controller from reset state)
1167			 */
1168		} while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1169		if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
1170		{
1171			printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
1172				st0, NE7_ST0BITS, cyl);
1173			if(fdc->retry < 3) fdc->retry = 3;
1174			return(retrier(fdcu));
1175		}
1176		fd->track = 0;
1177		/* Seek (probably) necessary */
1178		fdc->state = DOSEEK;
1179		return(1);	/* will return immediatly */
1180	case MOTORWAIT:
1181		if(fd->flags & FD_MOTOR_WAIT)
1182		{
1183			return(0); /* time's not up yet */
1184		}
1185		/*
1186		 * since the controller was off, it has lost its
1187		 * idea about the current track it were; thus,
1188		 * recalibrate the bastard
1189		 */
1190		fdc->state = STARTRECAL;
1191		return(1);	/* will return immediatly */
1192	default:
1193		printf("Unexpected FD int->");
1194		out_fdc(fdcu, NE7CMD_SENSEI);
1195		st0 = in_fdc(fdcu);
1196		cyl = in_fdc(fdcu);
1197		printf("ST0 = %x, PCN = %x\n", st0, cyl);
1198		out_fdc(fdcu, NE7CMD_READID);
1199		out_fdc(fdcu, fd->fdsu);
1200		for(i=0;i<7;i++) {
1201			fdc->status[i] = in_fdc(fdcu);
1202		}
1203		if(fdc->status[0] != -1)
1204			printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n",
1205			       fdc->status[0],
1206			       fdc->status[1],
1207			       fdc->status[2],
1208			       fdc->status[3],
1209			       fdc->status[4],
1210			       fdc->status[5],
1211			       fdc->status[6] );
1212		else
1213			printf("FDC timed out\n");
1214		return(0);
1215	}
1216	return(1); /* Come back immediatly to new state */
1217}
1218
1219static int
1220retrier(fdcu)
1221	fdcu_t fdcu;
1222{
1223	fdc_p fdc = fdc_data + fdcu;
1224	register struct buf *dp, *bp;
1225
1226	dp = &(fdc->head);
1227	bp = dp->b_actf;
1228
1229	if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY)
1230		goto fail;
1231	switch(fdc->retry)
1232	{
1233	case 0: case 1: case 2:
1234		fdc->state = SEEKCOMPLETE;
1235		break;
1236	case 3: case 4: case 5:
1237		fdc->state = STARTRECAL;
1238		break;
1239	case 6:
1240		fdc->state = RESETCTLR;
1241		break;
1242	case 7:
1243		break;
1244	default:
1245	fail:
1246		{
1247			dev_t sav_b_dev = bp->b_dev;
1248			/* Trick diskerr */
1249			bp->b_dev = makedev(major(bp->b_dev),
1250					    (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
1251			diskerr(bp, "fd", "hard error", LOG_PRINTF,
1252				fdc->fd->skip / DEV_BSIZE,
1253				(struct disklabel *)NULL);
1254			bp->b_dev = sav_b_dev;
1255			printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS);
1256			printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
1257			printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
1258			printf("cyl %d hd %d sec %d)\n",
1259			       fdc->status[3], fdc->status[4], fdc->status[5]);
1260		}
1261		bp->b_flags |= B_ERROR;
1262		bp->b_error = EIO;
1263		bp->b_resid = bp->b_bcount - fdc->fd->skip;
1264		dp->b_actf = bp->b_actf;
1265		fdc->fd->skip = 0;
1266		biodone(bp);
1267		fdc->state = FINDWORK;
1268		fdc->fd = (fd_p) 0;
1269		fdc->fdu = -1;
1270		/* XXX abort current command, if any.  */
1271		return(1);
1272	}
1273	fdc->retry++;
1274	return(1);
1275}
1276
1277static int
1278fdformat(dev, finfo, p)
1279	dev_t dev;
1280	struct fd_formb *finfo;
1281	struct proc *p;
1282{
1283 	fdu_t	fdu;
1284 	fd_p	fd;
1285
1286	struct buf *bp;
1287	int rv = 0, s;
1288	size_t fdblk;
1289
1290 	fdu = FDUNIT(minor(dev));
1291	fd = &fd_data[fdu];
1292	fdblk = 128 << fd->ft->secsize;
1293
1294	/* set up a buffer header for fdstrategy() */
1295	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1296	if(bp == 0)
1297		return ENOBUFS;
1298	bzero((void *)bp, sizeof(struct buf));
1299	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1300	bp->b_proc = p;
1301	bp->b_dev = dev;
1302
1303	/*
1304	 * calculate a fake blkno, so fdstrategy() would initiate a
1305	 * seek to the requested cylinder
1306	 */
1307	bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
1308		+ finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
1309
1310	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1311	bp->b_un.b_addr = (caddr_t)finfo;
1312
1313	/* now do the format */
1314	fdstrategy(bp);
1315
1316	/* ...and wait for it to complete */
1317	s = splbio();
1318	while(!(bp->b_flags & B_DONE))
1319	{
1320		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1321		if(rv == EWOULDBLOCK)
1322			break;
1323	}
1324	splx(s);
1325
1326	if(rv == EWOULDBLOCK)
1327		/* timed out */
1328		rv = EIO;
1329	if(bp->b_flags & B_ERROR)
1330		rv = bp->b_error;
1331	biodone(bp);
1332	free(bp, M_TEMP);
1333	return rv;
1334}
1335
1336/*
1337 *
1338 * TODO: Think about allocating buffer off stack.
1339 *       Don't pass uncast 0's and NULL's to read/write/setdisklabel().
1340 *       Watch out for NetBSD's different *disklabel() interface.
1341 *
1342 */
1343
1344int
1345fdioctl(dev, cmd, addr, flag, p)
1346	dev_t dev;
1347	int cmd;
1348	caddr_t addr;
1349	int flag;
1350	struct proc *p;
1351{
1352 	fdu_t	fdu = FDUNIT(minor(dev));
1353 	fd_p	fd = &fd_data[fdu];
1354	size_t fdblk;
1355
1356	struct fd_type *fdt;
1357	struct disklabel *dl;
1358	char buffer[DEV_BSIZE];
1359	int error = 0;
1360
1361#if NFT > 0
1362	int type = FDTYPE(minor(dev));
1363
1364	/* check for a tape ioctl */
1365	if (type & F_TAPE_TYPE)
1366		return ftioctl(dev, cmd, addr, flag, p);
1367#endif
1368
1369	fdblk = 128 << fd->ft->secsize;
1370
1371	switch (cmd)
1372	{
1373	case DIOCGDINFO:
1374		bzero(buffer, sizeof (buffer));
1375		dl = (struct disklabel *)buffer;
1376		dl->d_secsize = fdblk;
1377		fdt = fd_data[FDUNIT(minor(dev))].ft;
1378		dl->d_secpercyl = fdt->size / fdt->tracks;
1379		dl->d_type = DTYPE_FLOPPY;
1380
1381		if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL)
1382			error = 0;
1383		else
1384			error = EINVAL;
1385
1386		*(struct disklabel *)addr = *dl;
1387		break;
1388
1389	case DIOCSDINFO:
1390		if ((flag & FWRITE) == 0)
1391			error = EBADF;
1392		break;
1393
1394	case DIOCWLABEL:
1395		if ((flag & FWRITE) == 0)
1396			error = EBADF;
1397		break;
1398
1399	case DIOCWDINFO:
1400		if ((flag & FWRITE) == 0)
1401		{
1402			error = EBADF;
1403			break;
1404		}
1405
1406		dl = (struct disklabel *)addr;
1407
1408		if ((error =
1409		     setdisklabel ((struct disklabel *)buffer, dl, 0, NULL)))
1410			break;
1411
1412		error = writedisklabel(dev, fdstrategy,
1413				       (struct disklabel *)buffer, NULL);
1414		break;
1415
1416	case FD_FORM:
1417		if((flag & FWRITE) == 0)
1418			error = EBADF;	/* must be opened for writing */
1419		else if(((struct fd_formb *)addr)->format_version !=
1420			FD_FORMAT_VERSION)
1421			error = EINVAL;	/* wrong version of formatting prog */
1422		else
1423			error = fdformat(dev, (struct fd_formb *)addr, p);
1424		break;
1425
1426	case FD_GTYPE:                  /* get drive type */
1427		*(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
1428		break;
1429
1430	case FD_STYPE:                  /* set drive type */
1431		/* this is considered harmful; only allow for superuser */
1432		if(suser(p->p_ucred, &p->p_acflag) != 0)
1433			return EPERM;
1434		*fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr;
1435		break;
1436
1437	case FD_GOPTS:			/* get drive options */
1438		*(int *)addr = fd_data[FDUNIT(minor(dev))].options;
1439		break;
1440
1441	case FD_SOPTS:			/* set drive options */
1442		fd_data[FDUNIT(minor(dev))].options = *(int *)addr;
1443		break;
1444
1445	default:
1446		error = ENOTTY;
1447		break;
1448	}
1449	return (error);
1450}
1451
1452#endif
1453/*
1454 * Hello emacs, these are the
1455 * Local Variables:
1456 *  c-indent-level:               8
1457 *  c-continued-statement-offset: 8
1458 *  c-continued-brace-offset:     0
1459 *  c-brace-offset:              -8
1460 *  c-brace-imaginary-offset:     0
1461 *  c-argdecl-indent:             8
1462 *  c-label-offset:              -8
1463 *  c++-hanging-braces:           1
1464 *  c++-access-specifier-offset: -8
1465 *  c++-empty-arglist-indent:     8
1466 *  c++-friend-offset:            0
1467 * End:
1468 */
1469