Home | History | Annotate | Line # | Download | only in mainbus
fd.c revision 1.2
      1 /*	$NetBSD: fd.c,v 1.2 2001/11/22 18:34:33 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Charles M. Hannum.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*-
     40  * Copyright (c) 1990 The Regents of the University of California.
     41  * All rights reserved.
     42  *
     43  * This code is derived from software contributed to Berkeley by
     44  * Don Ahn.
     45  *
     46  * Redistribution and use in source and binary forms, with or without
     47  * modification, are permitted provided that the following conditions
     48  * are met:
     49  * 1. Redistributions of source code must retain the above copyright
     50  *    notice, this list of conditions and the following disclaimer.
     51  * 2. Redistributions in binary form must reproduce the above copyright
     52  *    notice, this list of conditions and the following disclaimer in the
     53  *    documentation and/or other materials provided with the distribution.
     54  * 3. All advertising materials mentioning features or use of this software
     55  *    must display the following acknowledgement:
     56  *	This product includes software developed by the University of
     57  *	California, Berkeley and its contributors.
     58  * 4. Neither the name of the University nor the names of its contributors
     59  *    may be used to endorse or promote products derived from this software
     60  *    without specific prior written permission.
     61  *
     62  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     63  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     64  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     65  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     66  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     67  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     68  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     69  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     70  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     71  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     72  * SUCH DAMAGE.
     73  *
     74  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
     75  *	from: fd.c,v 1.104 1997/01/09 04:30:08 mycroft Exp
     76  */
     77 
     78 /*
     79  * Floppy formatting facilities merged from FreeBSD fd.c driver:
     80  *	Id: fd.c,v 1.53 1995/03/12 22:40:56 joerg Exp
     81  * which carries the same copyright/redistribution notice as shown above with
     82  * the addition of the following statement before the "Redistribution and
     83  * use ..." clause:
     84  *
     85  * Copyright (c) 1993, 1994 by
     86  *  jc (at) irbs.UUCP (John Capo)
     87  *  vak (at) zebub.msk.su (Serge Vakulenko)
     88  *  ache (at) astral.msk.su (Andrew A. Chernov)
     89  *
     90  * Copyright (c) 1993, 1994, 1995 by
     91  *  joerg_wunsch (at) uriah.sax.de (Joerg Wunsch)
     92  *  dufault (at) hda.com (Peter Dufault)
     93  */
     94 
     95 #include "opt_ddb.h"
     96 
     97 #include <sys/param.h>
     98 #include <sys/systm.h>
     99 #include <sys/callout.h>
    100 #include <sys/kernel.h>
    101 #include <sys/file.h>
    102 #include <sys/ioctl.h>
    103 #include <sys/device.h>
    104 #include <sys/disklabel.h>
    105 #include <sys/dkstat.h>
    106 #include <sys/disk.h>
    107 #include <sys/buf.h>
    108 #include <sys/malloc.h>
    109 #include <sys/uio.h>
    110 #include <sys/syslog.h>
    111 #include <sys/queue.h>
    112 #include <sys/proc.h>
    113 #include <sys/fdio.h>
    114 
    115 #include <uvm/uvm_extern.h>
    116 
    117 #include <machine/cpu.h>
    118 #include <machine/irqhandler.h>
    119 #include <machine/conf.h>
    120 #include <machine/io.h>
    121 #include <arm/arm32/katelib.h>
    122 #include <machine/bus.h>
    123 #include <arm/iomd/iomdreg.h>
    124 #include <acorn32/mainbus/piocvar.h>
    125 #include <acorn32/mainbus/fdreg.h>
    126 
    127 #include "locators.h"
    128 
    129 #define NE7CMD_CONFIGURE 0x13
    130 
    131 #define FDUNIT(dev)	(minor(dev) / 8)
    132 #define FDTYPE(dev)	(minor(dev) % 8)
    133 
    134 /* XXX misuse a flag to identify format operation */
    135 #define B_FORMAT B_XXX
    136 
    137 enum fdc_state {
    138 	DEVIDLE = 0,
    139 	MOTORWAIT,
    140 	DOSEEK,
    141 	SEEKWAIT,
    142 	SEEKTIMEDOUT,
    143 	SEEKCOMPLETE,
    144 	DOIO,
    145 	IOCOMPLETE,
    146 	IOTIMEDOUT,
    147 	DORESET,
    148 	RESETCOMPLETE,
    149 	RESETTIMEDOUT,
    150 	DORECAL,
    151 	RECALWAIT,
    152 	RECALTIMEDOUT,
    153 	RECALCOMPLETE,
    154 };
    155 
    156 /* software state, per controller */
    157 struct fdc_softc {
    158 	struct device sc_dev;		/* boilerplate */
    159 #ifdef NEWCONFIG
    160 	struct isadev sc_id;
    161 #endif
    162 	void *sc_ih;
    163 
    164 	bus_space_tag_t sc_iot;		/* ISA i/o space identifier */
    165 	bus_space_handle_t   sc_ioh;	/* ISA io handle */
    166 
    167 	struct callout sc_timo_ch;	/* timeout callout */
    168 	struct callout sc_intr_ch;	/* pseudo-intr callout */
    169 
    170 	int sc_drq;
    171 
    172 	struct fd_softc *sc_fd[4];	/* pointers to children */
    173 	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
    174 	enum fdc_state sc_state;
    175 	int sc_errors;			/* number of retries so far */
    176 	u_char sc_status[7];		/* copy of registers */
    177 };
    178 
    179 /* controller driver configuration */
    180 int fdcprobe __P((struct device *, struct cfdata *, void *));
    181 int fdprint __P((void *, const char *));
    182 #ifdef NEWCONFIG
    183 void fdcforceintr __P((void *));
    184 #endif
    185 void fdcattach __P((struct device *, struct device *, void *));
    186 
    187 struct cfattach fdc_ca = {
    188 	sizeof(struct fdc_softc), fdcprobe, fdcattach
    189 };
    190 
    191 /*
    192  * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
    193  * we tell them apart.
    194  */
    195 struct fd_type {
    196 	int	sectrac;	/* sectors per track */
    197 	int	heads;		/* number of heads */
    198 	int	seccyl;		/* sectors per cylinder */
    199 	int	secsize;	/* size code for sectors */
    200 	int	datalen;	/* data len when secsize = 0 */
    201 	int	steprate;	/* step rate and head unload time */
    202 	int	gap1;		/* gap len between sectors */
    203 	int	gap2;		/* formatting gap */
    204 	int	cyls;		/* total num of cylinders */
    205 	int	size;		/* size of disk in sectors */
    206 	int	step;		/* steps per cylinder */
    207 	int	rate;		/* transfer speed code */
    208 	u_char	fillbyte;	/* format fill byte */
    209 	u_char	interleave;	/* interleave factor (formatting) */
    210 	char	*name;
    211 };
    212 
    213 /* The order of entries in the following table is important -- BEWARE! */
    214 struct fd_type fd_types[] = {
    215 	{ 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,0xf6,1, "1.44MB"    }, /* 1.44MB diskette */
    216 	{ 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS,0xf6,1, "1.2MB"    }, /* 1.2 MB AT-diskettes */
    217 	{  9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS,0xf6,1, "360KB/AT" }, /* 360kB in 1.2MB drive */
    218 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS,0xf6,1, "360KB/PC" }, /* 360kB PC diskettes */
    219 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS,0xf6,1, "720KB"    }, /* 3.5" 720kB diskette */
    220 	{  9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS,0xf6,1, "720KB/x"  }, /* 720kB in 1.2MB drive */
    221 	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS,0xf6,1, "360KB/x"  }, /* 360kB in 720kB drive */
    222 };
    223 
    224 /* software state, per disk (with up to 4 disks per ctlr) */
    225 struct fd_softc {
    226 	struct device sc_dev;
    227 	struct disk sc_dk;
    228 
    229 	struct fd_type *sc_deftype;	/* default type descriptor */
    230 	struct fd_type *sc_type;	/* current type descriptor */
    231 	struct fd_type sc_type_copy;	/* copy for fiddling when formatting */
    232 
    233 	struct callout sc_motoron_ch;
    234 	struct callout sc_motoroff_ch;
    235 
    236 	daddr_t	sc_blkno;	/* starting block number */
    237 	int sc_bcount;		/* byte count left */
    238  	int sc_opts;			/* user-set options */
    239 	int sc_skip;		/* bytes already transferred */
    240 	int sc_nblks;		/* number of blocks currently transferring */
    241 	int sc_nbytes;		/* number of bytes currently transferring */
    242 
    243 	int sc_drive;		/* physical unit number */
    244 	int sc_flags;
    245 #define	FD_OPEN		0x01		/* it's open */
    246 #define	FD_MOTOR	0x02		/* motor should be on */
    247 #define	FD_MOTOR_WAIT	0x04		/* motor coming up */
    248 	int sc_cylin;		/* where we think the head is */
    249 
    250 	void *sc_sdhook;	/* saved shutdown hook for drive. */
    251 
    252 	TAILQ_ENTRY(fd_softc) sc_drivechain;
    253 	int sc_ops;		/* I/O ops since last switch */
    254 	struct buf_queue sc_q;	/* pending I/O requests */
    255 	int sc_active;		/* number of active I/O operations */
    256 };
    257 
    258 /* floppy driver configuration */
    259 int fdprobe __P((struct device *, struct cfdata *, void *));
    260 void fdattach __P((struct device *, struct device *, void *));
    261 
    262 static fiqhandler_t fiqhandler;
    263 
    264 void floppy_read_fiq __P((void));
    265 void floppy_write_fiq __P((void));
    266 
    267 struct cfattach fd_ca = {
    268 	sizeof(struct fd_softc), fdprobe, fdattach
    269 };
    270 
    271 extern struct cfdriver fd_cd;
    272 
    273 void fdgetdisklabel __P((struct fd_softc *));
    274 int fd_get_parms __P((struct fd_softc *));
    275 void fdstrategy __P((struct buf *));
    276 void fdstart __P((struct fd_softc *));
    277 
    278 struct dkdriver fddkdriver = { fdstrategy };
    279 
    280 struct fd_type *fd_nvtotype __P((char *, int, int));
    281 void fd_set_motor __P((struct fdc_softc *fdc, int reset));
    282 void fd_motor_off __P((void *arg));
    283 void fd_motor_on __P((void *arg));
    284 int fdcresult __P((struct fdc_softc *fdc));
    285 int out_fdc __P((bus_space_tag_t iot, bus_space_handle_t ioh, u_char x));
    286 void fdcstart __P((struct fdc_softc *fdc));
    287 void fdcstatus __P((struct device *dv, int n, char *s));
    288 void fdctimeout __P((void *arg));
    289 void fdcpseudointr __P((void *arg));
    290 int fdcintr __P((void *));
    291 void fdcretry __P((struct fdc_softc *fdc));
    292 void fdfinish __P((struct fd_softc *fd, struct buf *bp));
    293 __inline struct fd_type *fd_dev_to_type __P((struct fd_softc *, dev_t));
    294 int fdformat __P((dev_t, struct ne7_fd_formb *, struct proc *));
    295 
    296 int
    297 fdcprobe(parent, cf, aux)
    298 	struct device *parent;
    299 	struct cfdata *cf;
    300 	void *aux;
    301 {
    302 	struct pioc_attach_args *pa = aux;
    303 	bus_space_tag_t iot;
    304 	bus_space_handle_t ioh;
    305 	int rv;
    306 
    307 	if (pa->pa_name && strcmp(pa->pa_name, "fdc") != 0)
    308 		return(0);
    309 
    310 	iot = pa->pa_iot;
    311 	rv = 0;
    312 
    313 	/* Map the i/o space. */
    314 	if (bus_space_map(iot, pa->pa_iobase + pa->pa_offset, FDC_NPORT, 0, &ioh))
    315 		return 0;
    316 
    317 	/* reset */
    318 	bus_space_write_2(iot, ioh, fdout, 0);
    319 	delay(100);
    320 	bus_space_write_2(iot, ioh, fdout, FDO_FRST);
    321 
    322 	/* see if it can handle a command */
    323 	if (out_fdc(iot, ioh, NE7CMD_SPECIFY) < 0)
    324 		goto out;
    325 	out_fdc(iot, ioh, 0xdf);
    326 	out_fdc(iot, ioh, 2);
    327 
    328 #ifdef NEWCONFIG
    329 	if (pa->pa_iobase == PIOCCF_BASE_DEFAULT || pa->pa_drq == PIOCCF_DACK_DEFAULT)
    330 		return 0;
    331 
    332 	if (pa->pa_irq == PIOCCF_IRQ_DEFAULT) {
    333 		pa->pa_irq = isa_discoverintr(fdcforceintr, aux);
    334 		if (pa->pa_irq == IRQNONE)
    335 			goto out;
    336 
    337 		/* reset it again */
    338 		bus_space_write_2(iot, ioh, fdout, 0);
    339 		delay(100);
    340 		bus_space_write_2(iot, ioh, fdout, FDO_FRST);
    341 	}
    342 #endif
    343 
    344 	rv = 1;
    345 	pa->pa_iosize = FDC_NPORT;
    346 
    347  out:
    348 	bus_space_unmap(iot, ioh, FDC_NPORT);
    349 	return rv;
    350 }
    351 
    352 #ifdef NEWCONFIG
    353 /*
    354  * XXX This is broken, and needs fixing.  In general, the interface needs
    355  * XXX to change.
    356  */
    357 void
    358 fdcforceintr(aux)
    359 	void *aux;
    360 {
    361 	struct isa_attach_args *ia = aux;
    362 	int iobase = ia->ia_iobase;
    363 
    364 	/* the motor is off; this should generate an error with or
    365 	   without a disk drive present */
    366 	out_fdc(iot, ioh, NE7CMD_SEEK);
    367 	out_fdc(iot, ioh, 0);
    368 	out_fdc(iot, ioh, 0);
    369 }
    370 #endif
    371 
    372 /*
    373  * Arguments passed between fdcattach and fdprobe.
    374  */
    375 struct fdc_attach_args {
    376 	int fa_drive;
    377 	struct fd_type *fa_deftype;
    378 };
    379 
    380 /*
    381  * Print the location of a disk drive (called just before attaching the
    382  * the drive).  If `fdc' is not NULL, the drive was found but was not
    383  * in the system config file; print the drive name as well.
    384  * Return QUIET (config_find ignores this if the device was configured) to
    385  * avoid printing `fdN not configured' messages.
    386  */
    387 int
    388 fdprint(aux, fdc)
    389 	void *aux;
    390 	const char *fdc;
    391 {
    392 	register struct fdc_attach_args *fa = aux;
    393 
    394 	if (!fdc)
    395 		printf(" drive %d", fa->fa_drive);
    396 	return QUIET;
    397 }
    398 
    399 void
    400 fdcattach(parent, self, aux)
    401 	struct device *parent, *self;
    402 	void *aux;
    403 {
    404 	struct fdc_softc *fdc = (void *)self;
    405 	bus_space_tag_t iot;
    406 	bus_space_handle_t ioh;
    407 	struct pioc_attach_args *pa = aux;
    408 	struct fdc_attach_args fa;
    409 	int type;
    410 
    411 	iot = pa->pa_iot;
    412 
    413 	/* Re-map the I/O space. */
    414 	if (bus_space_map(iot, pa->pa_iobase + pa->pa_offset, FDC_NPORT, 0, &ioh))
    415 		panic("fdcattach: couldn't map I/O ports");
    416 
    417 	fdc->sc_iot = iot;
    418 	fdc->sc_ioh = ioh;
    419 
    420 	fdc->sc_drq = pa->pa_iobase + pa->pa_offset + pa->pa_drq;
    421 	fdc->sc_state = DEVIDLE;
    422 	TAILQ_INIT(&fdc->sc_drives);
    423 
    424 	printf("\n");
    425 
    426 	callout_init(&fdc->sc_timo_ch);
    427 	callout_init(&fdc->sc_intr_ch);
    428 
    429 #ifdef NEWCONFIG
    430 	at_setup_dmachan(fdc->sc_drq, FDC_MAXIOSIZE);
    431 	isa_establish(&fdc->sc_id, &fdc->sc_dev);
    432 #endif
    433 	fdc->sc_ih = intr_claim(pa->pa_irq, IPL_BIO, "fdc",
    434 	    fdcintr, fdc);
    435 	if (!fdc->sc_ih)
    436 		panic("%s: Cannot claim IRQ %d\n", self->dv_xname, pa->pa_irq);
    437 
    438 #if 0
    439 	/*
    440 	 * The NVRAM info only tells us about the first two disks on the
    441 	 * `primary' floppy controller.
    442 	 */
    443 	if (fdc->sc_dev.dv_unit == 0)
    444 		type = mc146818_read(NULL, NVRAM_DISKETTE); /* XXX softc */
    445 	else
    446 		type = -1;
    447 #endif
    448 	type = 0x10;	/* XXX - hardcoded for 1 floppy */
    449 
    450 	/* physical limit: four drives per controller. */
    451 	for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) {
    452 		if (type >= 0 && fa.fa_drive < 2)
    453 			fa.fa_deftype = fd_nvtotype(fdc->sc_dev.dv_xname,
    454 			    type, fa.fa_drive);
    455 		else
    456 			fa.fa_deftype = NULL;		/* unknown */
    457 		(void)config_found(self, (void *)&fa, fdprint);
    458 	}
    459 }
    460 
    461 int
    462 fdprobe(parent, cf, aux)
    463 	struct device *parent;
    464 	struct cfdata *cf;
    465 	void *aux;
    466 {
    467 	struct fdc_softc *fdc = (void *)parent;
    468 	struct fdc_attach_args *fa = aux;
    469 	int drive = fa->fa_drive;
    470 	bus_space_tag_t iot = fdc->sc_iot;
    471 	bus_space_handle_t ioh = fdc->sc_ioh;
    472 	int n;
    473 
    474 	if (cf->cf_loc[FDCCF_DRIVE] != FDCCF_DRIVE_DEFAULT
    475 	  && cf->cf_loc[FDCCF_DRIVE] != drive)
    476 		return 0;
    477 	/*
    478 	 * XXX
    479 	 * This is to work around some odd interactions between this driver
    480 	 * and SMC Ethernet cards.
    481 	 */
    482 
    483 	/* Don't need this for arm32 port but leave for the time being (it won't hurt) */
    484 
    485 	if (cf->cf_loc[FDCCF_DRIVE] == FDCCF_DRIVE_DEFAULT && drive >= 2)
    486 		return 0;
    487 
    488 	/* select drive and turn on motor */
    489 	bus_space_write_2(iot, ioh, fdout, drive | FDO_FRST | FDO_MOEN(drive));
    490 	/* wait for motor to spin up */
    491 	delay(250000);
    492 	out_fdc(iot, ioh, NE7CMD_RECAL);
    493 	out_fdc(iot, ioh, drive);
    494 	/* wait for recalibrate */
    495 	delay(2000000);
    496 	out_fdc(iot, ioh, NE7CMD_SENSEI);
    497 	n = fdcresult(fdc);
    498 #ifdef FD_DEBUG
    499 	{
    500 		int i;
    501 		printf("fdprobe: status");
    502 		for (i = 0; i < n; i++)
    503 			printf(" %x", fdc->sc_status[i]);
    504 		printf("\n");
    505 	}
    506 #endif
    507 	/* turn off motor */
    508 	bus_space_write_1(iot, ioh, fdout, FDO_FRST);
    509 
    510 	if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)
    511 		return 0;
    512 
    513 	return 1;
    514 }
    515 
    516 /*
    517  * Controller is working, and drive responded.  Attach it.
    518  */
    519 void
    520 fdattach(parent, self, aux)
    521 	struct device *parent, *self;
    522 	void *aux;
    523 {
    524 	struct fdc_softc *fdc = (void *)parent;
    525 	struct fd_softc *fd = (void *)self;
    526 	struct fdc_attach_args *fa = aux;
    527 	struct fd_type *type = fa->fa_deftype;
    528 	int drive = fa->fa_drive;
    529 
    530 	callout_init(&fd->sc_motoron_ch);
    531 	callout_init(&fd->sc_motoroff_ch);
    532 
    533 	/* XXX Allow `flags' to override device type? */
    534 
    535 	if (type)
    536 		printf(": %s %d cyl, %d head, %d sec\n", type->name,
    537 		    type->cyls, type->heads, type->sectrac);
    538 	else
    539 		printf(": density unknown\n");
    540 
    541 	BUFQ_INIT(&fd->sc_q);
    542 	fd->sc_cylin = -1;
    543 	fd->sc_drive = drive;
    544 	fd->sc_deftype = type;
    545 	fdc->sc_fd[drive] = fd;
    546 
    547 	/*
    548 	 * Initialize and attach the disk structure.
    549 	 */
    550 	fd->sc_dk.dk_name = fd->sc_dev.dv_xname;
    551 	fd->sc_dk.dk_driver = &fddkdriver;
    552 	disk_attach(&fd->sc_dk);
    553 
    554 	/* Needed to power off if the motor is on when we halt. */
    555 
    556 }
    557 
    558 /*
    559  * Translate nvram type into internal data structure.  Return NULL for
    560  * none/unknown/unusable.
    561  */
    562 struct fd_type *
    563 fd_nvtotype(fdc, nvraminfo, drive)
    564 	char *fdc;
    565 	int nvraminfo, drive;
    566 {
    567 	int type;
    568 
    569 	type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0;
    570 	switch (type) {
    571 #ifndef RC7500
    572 	case 0x00 :
    573 		return NULL;
    574 #else
    575 	case 0x00 :
    576 #endif	/* !RC7500 */
    577 	case 0x10 :
    578 		return &fd_types[0];
    579 	default:
    580 		printf("%s: drive %d: unknown device type 0x%x\n",
    581 		    fdc, drive, type);
    582 		return NULL;
    583 	}
    584 }
    585 
    586 __inline struct fd_type *
    587 fd_dev_to_type(fd, dev)
    588 	struct fd_softc *fd;
    589 	dev_t dev;
    590 {
    591 	int type = FDTYPE(dev);
    592 
    593 	if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
    594 		return NULL;
    595 	return type ? &fd_types[type - 1] : fd->sc_deftype;
    596 }
    597 
    598 void
    599 fdstrategy(bp)
    600 	register struct buf *bp;	/* IO operation to perform */
    601 {
    602 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(bp->b_dev)];
    603 	int sz;
    604  	int s;
    605 
    606 	/* Valid unit, controller, and request? */
    607 	if (bp->b_blkno < 0 ||
    608 	    ((bp->b_bcount % FDC_BSIZE) != 0 &&
    609 	     (bp->b_flags & B_FORMAT) == 0)) {
    610 		bp->b_error = EINVAL;
    611 		goto bad;
    612 	}
    613 
    614 	/* If it's a null transfer, return immediately. */
    615 	if (bp->b_bcount == 0)
    616 		goto done;
    617 
    618 	sz = howmany(bp->b_bcount, FDC_BSIZE);
    619 
    620 	if (bp->b_blkno + sz > fd->sc_type->size) {
    621 		sz = fd->sc_type->size - bp->b_blkno;
    622 		if (sz == 0) {
    623 			/* If exactly at end of disk, return EOF. */
    624 			goto done;
    625 		}
    626 		if (sz < 0) {
    627 			/* If past end of disk, return EINVAL. */
    628 			bp->b_error = EINVAL;
    629 			goto bad;
    630 		}
    631 		/* Otherwise, truncate request. */
    632 		bp->b_bcount = sz << DEV_BSHIFT;
    633 	}
    634 
    635 	bp->b_rawblkno = bp->b_blkno;
    636  	bp->b_cylinder = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE) / fd->sc_type->seccyl;
    637 
    638 #ifdef FD_DEBUG
    639 	printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d sz %d\n",
    640 	    bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylinder, sz);
    641 #endif
    642 
    643 	/* Queue transfer on drive, activate drive and controller if idle. */
    644 	s = splbio();
    645 	disksort_cylinder(&fd->sc_q, bp);
    646 	callout_stop(&fd->sc_motoroff_ch);		/* a good idea */
    647 	if (fd->sc_active == 0)
    648 		fdstart(fd);
    649 #ifdef DIAGNOSTIC
    650 	else {
    651 		struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    652 		if (fdc->sc_state == DEVIDLE) {
    653 			printf("fdstrategy: controller inactive\n");
    654 			fdcstart(fdc);
    655 		}
    656 	}
    657 #endif
    658 	splx(s);
    659 	return;
    660 
    661 bad:
    662 	bp->b_flags |= B_ERROR;
    663 done:
    664 	/* Toss transfer; we're done early. */
    665 	bp->b_resid = bp->b_bcount;
    666 	biodone(bp);
    667 }
    668 
    669 void
    670 fdstart(fd)
    671 	struct fd_softc *fd;
    672 {
    673 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    674 	int active = fdc->sc_drives.tqh_first != 0;
    675 
    676 	/* Link into controller queue. */
    677 	fd->sc_active = 1;
    678 	TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
    679 
    680 	/* If controller not already active, start it. */
    681 	if (!active)
    682 		fdcstart(fdc);
    683 }
    684 
    685 void
    686 fdfinish(fd, bp)
    687 	struct fd_softc *fd;
    688 	struct buf *bp;
    689 {
    690 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    691 
    692 	/*
    693 	 * Move this drive to the end of the queue to give others a `fair'
    694 	 * chance.  We only force a switch if N operations are completed while
    695 	 * another drive is waiting to be serviced, since there is a long motor
    696 	 * startup delay whenever we switch.
    697 	 */
    698 	if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
    699 		fd->sc_ops = 0;
    700 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
    701 		if (BUFQ_NEXT(bp) != NULL)
    702 			TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
    703 		else
    704 			fd->sc_active = 0;
    705 	}
    706 	bp->b_resid = fd->sc_bcount;
    707 	fd->sc_skip = 0;
    708 	BUFQ_REMOVE(&fd->sc_q, bp);
    709 
    710 	biodone(bp);
    711 	/* turn off motor 5s from now */
    712 	callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd);
    713 	fdc->sc_state = DEVIDLE;
    714 }
    715 
    716 int
    717 fdread(dev, uio, flags)
    718 	dev_t dev;
    719 	struct uio *uio;
    720 	int flags;
    721 {
    722 
    723 	return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio));
    724 }
    725 
    726 int
    727 fdwrite(dev, uio, flags)
    728 	dev_t dev;
    729 	struct uio *uio;
    730 	int flags;
    731 {
    732 
    733 	return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio));
    734 }
    735 
    736 void
    737 fd_set_motor(fdc, reset)
    738 	struct fdc_softc *fdc;
    739 	int reset;
    740 {
    741 	struct fd_softc *fd;
    742 	u_char status;
    743 	int n;
    744 
    745 	if ((fd = fdc->sc_drives.tqh_first) != NULL)
    746 		status = fd->sc_drive;
    747 	else
    748 		status = 0;
    749 	if (!reset)
    750 		status |= FDO_FRST | FDO_FDMAEN;
    751 	for (n = 0; n < 4; n++)
    752 		if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
    753 			status |= FDO_MOEN(n);
    754 	bus_space_write_2(fdc->sc_iot, fdc->sc_ioh, fdout, status);
    755 }
    756 
    757 void
    758 fd_motor_off(arg)
    759 	void *arg;
    760 {
    761 	struct fd_softc *fd = arg;
    762 	int s;
    763 
    764 	s = splbio();
    765 	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
    766 	fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0);
    767 	splx(s);
    768 }
    769 
    770 void
    771 fd_motor_on(arg)
    772 	void *arg;
    773 {
    774 	struct fd_softc *fd = arg;
    775 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    776 	int s;
    777 
    778 	s = splbio();
    779 	fd->sc_flags &= ~FD_MOTOR_WAIT;
    780 	if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
    781 		(void) fdcintr(fdc);
    782 	splx(s);
    783 }
    784 
    785 int
    786 fdcresult(fdc)
    787 	struct fdc_softc *fdc;
    788 {
    789 	bus_space_tag_t iot = fdc->sc_iot;
    790 	bus_space_handle_t ioh = fdc->sc_ioh;
    791 	u_char i;
    792 	int j = 100000,
    793 	    n = 0;
    794 
    795 	for (; j; j--) {
    796 		i = bus_space_read_1(iot, ioh, fdsts) &
    797 		    (NE7_DIO | NE7_RQM | NE7_CB);
    798 		if (i == NE7_RQM)
    799 			return n;
    800 		if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
    801 			if (n >= sizeof(fdc->sc_status)) {
    802 				log(LOG_ERR, "fdcresult: overrun\n");
    803 				return -1;
    804 			}
    805 			fdc->sc_status[n++] =
    806 			    bus_space_read_1(iot, ioh, fddata);
    807 		}
    808 		delay(10);
    809 	}
    810 	log(LOG_ERR, "fdcresult: timeout\n");
    811 	return -1;
    812 }
    813 
    814 int
    815 out_fdc(iot, ioh, x)
    816 	bus_space_tag_t iot;
    817 	bus_space_handle_t ioh;
    818 	u_char x;
    819 {
    820 	int i = 100000;
    821 
    822 	while ((bus_space_read_1(iot, ioh, fdsts) & NE7_DIO) && i-- > 0);
    823 	if (i <= 0)
    824 		return -1;
    825 	while ((bus_space_read_1(iot, ioh, fdsts) & NE7_RQM) == 0 && i-- > 0);
    826 	if (i <= 0)
    827 		return -1;
    828 	bus_space_write_2(iot, ioh, fddata, x);
    829 	return 0;
    830 }
    831 
    832 int
    833 fdopen(dev, flags, mode, p)
    834 	dev_t dev;
    835 	int flags;
    836 	int mode;
    837 	struct proc *p;
    838 {
    839  	int unit;
    840 	struct fd_softc *fd;
    841 	struct fd_type *type;
    842 
    843 	unit = FDUNIT(dev);
    844 	if (unit >= fd_cd.cd_ndevs)
    845 		return ENXIO;
    846 	fd = fd_cd.cd_devs[unit];
    847 	if (fd == 0)
    848 		return ENXIO;
    849 	type = fd_dev_to_type(fd, dev);
    850 	if (type == NULL)
    851 		return ENXIO;
    852 
    853 	if ((fd->sc_flags & FD_OPEN) != 0 &&
    854 	    memcmp(fd->sc_type, type, sizeof(*type)))
    855 		return EBUSY;
    856 
    857 	fd->sc_type_copy = *type;
    858 	fd->sc_type = &fd->sc_type_copy;
    859 	fd->sc_cylin = -1;
    860 	fd->sc_flags |= FD_OPEN;
    861 
    862 	return 0;
    863 }
    864 
    865 int
    866 fdclose(dev, flags, mode, p)
    867 	dev_t dev;
    868 	int flags;
    869 	int mode;
    870 	struct proc *p;
    871 {
    872 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
    873 
    874 	fd->sc_flags &= ~FD_OPEN;
    875 	fd->sc_opts &= ~(FDOPT_NORETRY|FDOPT_SILENT);
    876 	return 0;
    877 }
    878 
    879 void
    880 fdcstart(fdc)
    881 	struct fdc_softc *fdc;
    882 {
    883 
    884 #ifdef DIAGNOSTIC
    885 	/* only got here if controller's drive queue was inactive; should
    886 	   be in idle state */
    887 	if (fdc->sc_state != DEVIDLE) {
    888 		printf("fdcstart: not idle\n");
    889 		return;
    890 	}
    891 #endif
    892 	(void) fdcintr(fdc);
    893 }
    894 
    895 void
    896 fdcstatus(dv, n, s)
    897 	struct device *dv;
    898 	int n;
    899 	char *s;
    900 {
    901 	struct fdc_softc *fdc = (void *)dv->dv_parent;
    902 	char bits[64];
    903 
    904 	if (n == 0) {
    905 		out_fdc(fdc->sc_iot, fdc->sc_ioh, NE7CMD_SENSEI);
    906 		(void) fdcresult(fdc);
    907 		n = 2;
    908 	}
    909 
    910 	printf("%s: %s", dv->dv_xname, s);
    911 
    912 	switch (n) {
    913 	case 0:
    914 		printf("\n");
    915 		break;
    916 	case 2:
    917 		printf(" (st0 %s cyl %d)\n",
    918 		    bitmask_snprintf(fdc->sc_status[0], NE7_ST0BITS,
    919 		    bits, sizeof(bits)), fdc->sc_status[1]);
    920 		break;
    921 	case 7:
    922 		printf(" (st0 %s", bitmask_snprintf(fdc->sc_status[0],
    923 		    NE7_ST0BITS, bits, sizeof(bits)));
    924 		printf(" st1 %s", bitmask_snprintf(fdc->sc_status[1],
    925 		    NE7_ST1BITS, bits, sizeof(bits)));
    926 		printf(" st2 %s", bitmask_snprintf(fdc->sc_status[2],
    927 		    NE7_ST2BITS, bits, sizeof(bits)));
    928 		printf(" cyl %d head %d sec %d)\n",
    929 		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
    930 		break;
    931 #ifdef DIAGNOSTIC
    932 	default:
    933 		printf("\nfdcstatus: weird size");
    934 		break;
    935 #endif
    936 	}
    937 }
    938 
    939 void
    940 fdctimeout(arg)
    941 	void *arg;
    942 {
    943 	struct fdc_softc *fdc = arg;
    944 	struct fd_softc *fd = fdc->sc_drives.tqh_first;
    945 	int s;
    946 
    947 	s = splbio();
    948 #ifdef DEBUG
    949 	log(LOG_ERR,"fdctimeout: state %d\n", fdc->sc_state);
    950 #endif
    951 	fdcstatus(&fd->sc_dev, 0, "timeout");
    952 
    953 	if (BUFQ_FIRST(&fd->sc_q) != NULL)
    954 		fdc->sc_state++;
    955 	else
    956 		fdc->sc_state = DEVIDLE;
    957 
    958 	(void) fdcintr(fdc);
    959 	splx(s);
    960 }
    961 
    962 void
    963 fdcpseudointr(arg)
    964 	void *arg;
    965 {
    966 	int s;
    967 
    968 	/* Just ensure it has the right spl. */
    969 	s = splbio();
    970 	(void) fdcintr(arg);
    971 	splx(s);
    972 }
    973 
    974 int
    975 fdcintr(arg)
    976 	void *arg;
    977 {
    978 	struct fdc_softc *fdc = arg;
    979 #define	st0	fdc->sc_status[0]
    980 #define	cyl	fdc->sc_status[1]
    981 	struct fd_softc *fd;
    982 	struct buf *bp;
    983 	bus_space_tag_t iot = fdc->sc_iot;
    984 	bus_space_handle_t ioh = fdc->sc_ioh;
    985 	int read, head, sec, i, nblks;
    986 	struct fd_type *type;
    987 	struct ne7_fd_formb *finfo = NULL;
    988 
    989 loop:
    990 	/* Is there a drive for the controller to do a transfer with? */
    991 	fd = fdc->sc_drives.tqh_first;
    992 	if (fd == NULL) {
    993 		fdc->sc_state = DEVIDLE;
    994  		return 1;
    995 	}
    996 
    997 	/* Is there a transfer to this drive?  If not, deactivate drive. */
    998 	bp = BUFQ_FIRST(&fd->sc_q);
    999 	if (bp == NULL) {
   1000 		fd->sc_ops = 0;
   1001 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
   1002 		fd->sc_active = 0;
   1003 		goto loop;
   1004 	}
   1005 
   1006 	if (bp->b_flags & B_FORMAT)
   1007 		finfo = (struct ne7_fd_formb *)bp->b_data;
   1008 
   1009 	switch (fdc->sc_state) {
   1010 	case DEVIDLE:
   1011 		fdc->sc_errors = 0;
   1012 		fd->sc_skip = 0;
   1013 		fd->sc_bcount = bp->b_bcount;
   1014 		fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE);
   1015 		callout_stop(&fd->sc_motoroff_ch);
   1016 		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
   1017 			fdc->sc_state = MOTORWAIT;
   1018 			return 1;
   1019 		}
   1020 		if ((fd->sc_flags & FD_MOTOR) == 0) {
   1021 			/* Turn on the motor, being careful about pairing. */
   1022 			struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
   1023 			if (ofd && ofd->sc_flags & FD_MOTOR) {
   1024 				callout_stop(&ofd->sc_motoroff_ch);
   1025 				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
   1026 			}
   1027 			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
   1028 			fd_set_motor(fdc, 0);
   1029 			fdc->sc_state = MOTORWAIT;
   1030 			/* Allow .25s for motor to stabilize. */
   1031 			callout_reset(&fd->sc_motoron_ch, hz / 4,
   1032 			    fd_motor_on, fd);
   1033 			return 1;
   1034 		}
   1035 		/* Make sure the right drive is selected. */
   1036 		fd_set_motor(fdc, 0);
   1037 
   1038 		/* fall through */
   1039 	case DOSEEK:
   1040 	doseek:
   1041 		if (fd->sc_cylin == bp->b_cylinder)
   1042 			goto doio;
   1043 
   1044 #if 1
   1045 		out_fdc(iot, ioh, NE7CMD_CONFIGURE);/* configure command */
   1046 		out_fdc(iot, ioh, 0);
   1047 		out_fdc(iot, ioh, 0x18);
   1048 		out_fdc(iot, ioh, 0);
   1049 #endif
   1050 		out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */
   1051 		out_fdc(iot, ioh, fd->sc_type->steprate);
   1052 		out_fdc(iot, ioh, 6);		/* XXX head load time == 6ms */
   1053 
   1054 		out_fdc(iot, ioh, NE7CMD_SEEK);	/* seek function */
   1055 		out_fdc(iot, ioh, fd->sc_drive);	/* drive number */
   1056 		out_fdc(iot, ioh, bp->b_cylinder * fd->sc_type->step);
   1057 
   1058 		fd->sc_cylin = -1;
   1059 		fdc->sc_state = SEEKWAIT;
   1060 
   1061 		fd->sc_dk.dk_seek++;
   1062 		disk_busy(&fd->sc_dk);
   1063 
   1064 		callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
   1065 		return 1;
   1066 
   1067 	case DOIO:
   1068 	doio:
   1069 		type = fd->sc_type;
   1070 		if (finfo)
   1071 			fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
   1072 				      (char *)finfo;
   1073 		sec = fd->sc_blkno % type->seccyl;
   1074 		nblks = type->seccyl - sec;
   1075 		nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
   1076 		nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
   1077 		fd->sc_nblks = nblks;
   1078 		fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE;
   1079 		head = sec / type->sectrac;
   1080 		sec -= head * type->sectrac;
   1081 #ifdef DIAGNOSTIC
   1082 		{int block;
   1083 		 block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec;
   1084 		 if (block != fd->sc_blkno) {
   1085 			 printf("fdcintr: block %d != blkno %d\n",
   1086 				block, fd->sc_blkno);
   1087 #ifdef DDB
   1088 			 Debugger();
   1089 #endif
   1090 		 }}
   1091 #endif
   1092 		read = bp->b_flags & B_READ;
   1093 #ifdef NEWCONFIG
   1094 		at_dma(read, bp->b_data + fd->sc_skip, fd->sc_nbytes,
   1095 		       fdc->sc_drq);
   1096 #else
   1097 /*		isa_dmastart(read, bp->b_data + fd->sc_skip, fd->sc_nbytes,
   1098 		       fdc->sc_drq);*/
   1099 		if (read)
   1100 			fiqhandler.fh_func = floppy_read_fiq;
   1101 		else
   1102 			fiqhandler.fh_func = floppy_write_fiq;
   1103 		fiqhandler.fh_r9 = IOMD_BASE + (IOMD_FIQRQ << 2);
   1104 		fiqhandler.fh_r10 = fd->sc_nbytes;
   1105 		fiqhandler.fh_r11 = (u_int)(bp->b_data + fd->sc_skip);
   1106 		fiqhandler.fh_r12 = fdc->sc_drq;
   1107 /*		fiqhandler.fh_r13 = 0;*/
   1108 		fiqhandler.fh_mask = 0x01;
   1109 #ifdef FD_DEBUG
   1110 		printf("fdc-doio:r9=%x r10=%x r11=%x r12=%x data=%x skip=%x\n", fiqhandler.fh_r9,
   1111 		    fiqhandler.fh_r10, fiqhandler.fh_r11,
   1112 		    fiqhandler.fh_r12, (u_int)bp->b_data, fd->sc_skip);
   1113 #endif
   1114 		if (fiq_claim(&fiqhandler) == -1)
   1115 			panic("%s: Cannot claim FIQ vector\n", fdc->sc_dev.dv_xname);
   1116 #endif
   1117 		bus_space_write_2(iot, ioh, fdctl, type->rate);
   1118 #ifdef FD_DEBUG
   1119 		printf("fdcintr: %s drive %d track %d head %d sec %d nblks %d\n",
   1120 			read ? "read" : "write", fd->sc_drive, fd->sc_cylin,
   1121 			head, sec, nblks);
   1122 #endif
   1123 		if (finfo) {
   1124 			/* formatting */
   1125 			if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) {
   1126 				fdc->sc_errors = 4;
   1127 				fdcretry(fdc);
   1128 				goto loop;
   1129 			}
   1130 			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
   1131 			out_fdc(iot, ioh, finfo->fd_formb_secshift);
   1132 			out_fdc(iot, ioh, finfo->fd_formb_nsecs);
   1133 			out_fdc(iot, ioh, finfo->fd_formb_gaplen);
   1134 			out_fdc(iot, ioh, finfo->fd_formb_fillbyte);
   1135 		} else {
   1136 			if (read)
   1137 				out_fdc(iot, ioh, NE7CMD_READ);	/* READ */
   1138 			else
   1139 				out_fdc(iot, ioh, NE7CMD_WRITE); /* WRITE */
   1140 			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
   1141 			out_fdc(iot, ioh, fd->sc_cylin); /* track */
   1142 			out_fdc(iot, ioh, head);
   1143 			out_fdc(iot, ioh, sec + 1);	 /* sector +1 */
   1144 			out_fdc(iot, ioh, type->secsize);/* sector size */
   1145 			out_fdc(iot, ioh, type->sectrac);/* sectors/track */
   1146 			out_fdc(iot, ioh, type->gap1);	 /* gap1 size */
   1147 			out_fdc(iot, ioh, type->datalen);/* data length */
   1148 		}
   1149 		fdc->sc_state = IOCOMPLETE;
   1150 
   1151 		disk_busy(&fd->sc_dk);
   1152 
   1153 		/* allow 2 seconds for operation */
   1154 		callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
   1155 		return 1;				/* will return later */
   1156 
   1157 	case SEEKWAIT:
   1158 		callout_stop(&fdc->sc_timo_ch);
   1159 		fdc->sc_state = SEEKCOMPLETE;
   1160 		/* allow 1/50 second for heads to settle */
   1161 #if 0
   1162 		callout_reset(&fdc->sc_intr_ch, hz / 50, fdcpseudointr, fdc);
   1163 #endif
   1164 		return 1;
   1165 
   1166 	case SEEKCOMPLETE:
   1167 		disk_unbusy(&fd->sc_dk, 0);	/* no data on seek */
   1168 
   1169 		/* Make sure seek really happened. */
   1170 		out_fdc(iot, ioh, NE7CMD_SENSEI);
   1171 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
   1172 		    cyl != bp->b_cylinder * fd->sc_type->step) {
   1173 #ifdef FD_DEBUG
   1174 			fdcstatus(&fd->sc_dev, 2, "seek failed");
   1175 #endif
   1176 			fdcretry(fdc);
   1177 			goto loop;
   1178 		}
   1179 		fd->sc_cylin = bp->b_cylinder;
   1180 		goto doio;
   1181 
   1182 	case IOTIMEDOUT:
   1183 #ifdef NEWCONFIG
   1184 		at_dma_abort(fdc->sc_drq);
   1185 #else
   1186 /*		isa_dmaabort(fdc->sc_drq);*/
   1187 		if (fiq_release(&fiqhandler) == -1)
   1188 			panic("%s: Cannot release FIQ vector\n", fdc->sc_dev.dv_xname);
   1189 #endif
   1190 	case SEEKTIMEDOUT:
   1191 	case RECALTIMEDOUT:
   1192 	case RESETTIMEDOUT:
   1193 		fdcretry(fdc);
   1194 		goto loop;
   1195 
   1196 	case IOCOMPLETE: /* IO DONE, post-analyze */
   1197 		callout_stop(&fdc->sc_timo_ch);
   1198 
   1199 		disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid));
   1200 
   1201 		if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) {
   1202 #ifdef NEWCONFIG
   1203 			at_dma_abort(fdc->sc_drq);
   1204 #else
   1205 /*			isa_dmaabort(fdc->sc_drq);*/
   1206 			if (fiq_release(&fiqhandler) == -1)
   1207 				panic("%s: Cannot release FIQ vector\n", fdc->sc_dev.dv_xname);
   1208 #endif
   1209 #ifdef FD_DEBUG
   1210 			fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
   1211 			    "read failed" : "write failed");
   1212 			printf("blkno %d nblks %d\n",
   1213 			    fd->sc_blkno, fd->sc_nblks);
   1214 #endif
   1215 			fdcretry(fdc);
   1216 			goto loop;
   1217 		}
   1218 #ifdef NEWCONFIG
   1219 		at_dma_terminate(fdc->sc_drq);
   1220 #else
   1221 /*		read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
   1222 		isa_dmadone(read, bp->b_data + fd->sc_skip, fd->sc_nbytes,
   1223 		    fdc->sc_drq);*/
   1224 		if (fiq_release(&fiqhandler) == -1)
   1225 			panic("%s: Cannot release FIQ vector\n", fdc->sc_dev.dv_xname);
   1226 #endif
   1227 		if (fdc->sc_errors) {
   1228 #if 0
   1229 			diskerr(bp, "fd", "soft error (corrected)", LOG_PRINTF,
   1230 			    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
   1231 			printf("\n");
   1232 #endif
   1233 			fdc->sc_errors = 0;
   1234 		}
   1235 		fd->sc_blkno += fd->sc_nblks;
   1236 		fd->sc_skip += fd->sc_nbytes;
   1237 		fd->sc_bcount -= fd->sc_nbytes;
   1238 		if (!finfo && fd->sc_bcount > 0) {
   1239 			bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl;
   1240 			goto doseek;
   1241 		}
   1242 		fdfinish(fd, bp);
   1243 		goto loop;
   1244 
   1245 	case DORESET:
   1246 		/* try a reset, keep motor on */
   1247 		fd_set_motor(fdc, 1);
   1248 		delay(100);
   1249 		fd_set_motor(fdc, 0);
   1250 		fdc->sc_state = RESETCOMPLETE;
   1251 		callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc);
   1252 		return 1;			/* will return later */
   1253 
   1254 	case RESETCOMPLETE:
   1255 		callout_stop(&fdc->sc_timo_ch);
   1256 		/* clear the controller output buffer */
   1257 		for (i = 0; i < 4; i++) {
   1258 			out_fdc(iot, ioh, NE7CMD_SENSEI);
   1259 			(void) fdcresult(fdc);
   1260 		}
   1261 
   1262 		/* fall through */
   1263 	case DORECAL:
   1264 		out_fdc(iot, ioh, NE7CMD_RECAL);	/* recalibrate function */
   1265 		out_fdc(iot, ioh, fd->sc_drive);
   1266 		fdc->sc_state = RECALWAIT;
   1267 		callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc);
   1268 		return 1;			/* will return later */
   1269 
   1270 	case RECALWAIT:
   1271 		callout_stop(&fdc->sc_timo_ch);
   1272 		fdc->sc_state = RECALCOMPLETE;
   1273 		/* allow 1/30 second for heads to settle */
   1274 #if 0
   1275 		callout_reset(&fdc->sc_intr_ch, hz / 30, fdcpseudointr, fdc);
   1276 #endif
   1277 		return 1;			/* will return later */
   1278 
   1279 	case RECALCOMPLETE:
   1280 		out_fdc(iot, ioh, NE7CMD_SENSEI);
   1281 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
   1282 #ifdef FD_DEBUG
   1283 			fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
   1284 #endif
   1285 			fdcretry(fdc);
   1286 			goto loop;
   1287 		}
   1288 		fd->sc_cylin = 0;
   1289 		goto doseek;
   1290 
   1291 	case MOTORWAIT:
   1292 		if (fd->sc_flags & FD_MOTOR_WAIT)
   1293 			return 1;		/* time's not up yet */
   1294 		goto doseek;
   1295 
   1296 	default:
   1297 		fdcstatus(&fd->sc_dev, 0, "stray interrupt");
   1298 		return 1;
   1299 	}
   1300 #ifdef DIAGNOSTIC
   1301 	panic("fdcintr: impossible");
   1302 #endif
   1303 #undef	st0
   1304 #undef	cyl
   1305 }
   1306 
   1307 void
   1308 fdcretry(fdc)
   1309 	struct fdc_softc *fdc;
   1310 {
   1311 	char bits[64];
   1312 	struct fd_softc *fd;
   1313 	struct buf *bp;
   1314 
   1315 	fd = fdc->sc_drives.tqh_first;
   1316 	bp = BUFQ_FIRST(&fd->sc_q);
   1317 
   1318 	if (fd->sc_opts & FDOPT_NORETRY)
   1319 	    goto fail;
   1320 	switch (fdc->sc_errors) {
   1321 	case 0:
   1322 		/* try again */
   1323 		fdc->sc_state = DOSEEK;
   1324 		break;
   1325 
   1326 	case 1: case 2: case 3:
   1327 		/* didn't work; try recalibrating */
   1328 		fdc->sc_state = DORECAL;
   1329 		break;
   1330 
   1331 	case 4:
   1332 		/* still no go; reset the bastard */
   1333 		fdc->sc_state = DORESET;
   1334 		break;
   1335 
   1336 	default:
   1337 	fail:
   1338 		if ((fd->sc_opts & FDOPT_SILENT) == 0) {
   1339 			diskerr(bp, "fd", "hard error", LOG_PRINTF,
   1340 				fd->sc_skip / FDC_BSIZE,
   1341 				(struct disklabel *)NULL);
   1342 
   1343 			printf(" (st0 %s",
   1344 			       bitmask_snprintf(fdc->sc_status[0],
   1345 						NE7_ST0BITS, bits,
   1346 						sizeof(bits)));
   1347 			printf(" st1 %s",
   1348 			       bitmask_snprintf(fdc->sc_status[1],
   1349 						NE7_ST1BITS, bits,
   1350 						sizeof(bits)));
   1351 			printf(" st2 %s",
   1352 			       bitmask_snprintf(fdc->sc_status[2],
   1353 						NE7_ST2BITS, bits,
   1354 						sizeof(bits)));
   1355 			printf(" cyl %d head %d sec %d)\n",
   1356 			       fdc->sc_status[3],
   1357 			       fdc->sc_status[4],
   1358 			       fdc->sc_status[5]);
   1359 		}
   1360 
   1361 		bp->b_flags |= B_ERROR;
   1362 		bp->b_error = EIO;
   1363 		fdfinish(fd, bp);
   1364 	}
   1365 	fdc->sc_errors++;
   1366 }
   1367 
   1368 int
   1369 fdsize(dev)
   1370 	dev_t dev;
   1371 {
   1372 
   1373 	/* Swapping to floppies would not make sense. */
   1374 	return -1;
   1375 }
   1376 
   1377 int
   1378 fddump(dev, blkno, va, size)
   1379 	dev_t dev;
   1380 	daddr_t blkno;
   1381 	caddr_t va;
   1382 	size_t size;
   1383 {
   1384 
   1385 	/* Not implemented. */
   1386 	return ENXIO;
   1387 }
   1388 
   1389 int
   1390 fdioctl(dev, cmd, addr, flag, p)
   1391 	dev_t dev;
   1392 	u_long cmd;
   1393 	caddr_t addr;
   1394 	int flag;
   1395 	struct proc *p;
   1396 {
   1397 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
   1398 	struct fdformat_parms *form_parms;
   1399 	struct fdformat_cmd *form_cmd;
   1400 	struct ne7_fd_formb *fd_formb;
   1401 	struct disklabel buffer;
   1402 	int error;
   1403 	unsigned int scratch;
   1404 	int il[FD_MAX_NSEC + 1];
   1405 	register int i, j;
   1406 
   1407 	switch (cmd) {
   1408 	case DIOCGDINFO:
   1409 		memset(&buffer, 0, sizeof(buffer));
   1410 
   1411 		buffer.d_secpercyl = fd->sc_type->seccyl;
   1412 		buffer.d_type = DTYPE_FLOPPY;
   1413 		buffer.d_secsize = FDC_BSIZE;
   1414 
   1415 		if (readdisklabel(dev, fdstrategy, &buffer, NULL) != NULL)
   1416 			return EINVAL;
   1417 
   1418 		*(struct disklabel *)addr = buffer;
   1419 		return 0;
   1420 
   1421 	case DIOCWLABEL:
   1422 		if ((flag & FWRITE) == 0)
   1423 			return EBADF;
   1424 		/* XXX do something */
   1425 		return 0;
   1426 
   1427 	case DIOCWDINFO:
   1428 		if ((flag & FWRITE) == 0)
   1429 			return EBADF;
   1430 
   1431 		error = setdisklabel(&buffer, (struct disklabel *)addr, 0, NULL);
   1432 		if (error)
   1433 			return error;
   1434 
   1435 		error = writedisklabel(dev, fdstrategy, &buffer, NULL);
   1436 		return error;
   1437 
   1438 	case FDIOCGETFORMAT:
   1439 		form_parms = (struct fdformat_parms *)addr;
   1440 		form_parms->fdformat_version = FDFORMAT_VERSION;
   1441 		form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
   1442 		form_parms->ncyl = fd->sc_type->cyls;
   1443 		form_parms->nspt = fd->sc_type->sectrac;
   1444 		form_parms->ntrk = fd->sc_type->heads;
   1445 		form_parms->stepspercyl = fd->sc_type->step;
   1446 		form_parms->gaplen = fd->sc_type->gap2;
   1447 		form_parms->fillbyte = fd->sc_type->fillbyte;
   1448 		form_parms->interleave = fd->sc_type->interleave;
   1449 		switch (fd->sc_type->rate) {
   1450 		case FDC_500KBPS:
   1451 			form_parms->xfer_rate = 500 * 1024;
   1452 			break;
   1453 		case FDC_300KBPS:
   1454 			form_parms->xfer_rate = 300 * 1024;
   1455 			break;
   1456 		case FDC_250KBPS:
   1457 			form_parms->xfer_rate = 250 * 1024;
   1458 			break;
   1459 		default:
   1460 			return EINVAL;
   1461 		}
   1462 		return 0;
   1463 
   1464 	case FDIOCSETFORMAT:
   1465 		if((flag & FWRITE) == 0)
   1466 			return EBADF;	/* must be opened for writing */
   1467 		form_parms = (struct fdformat_parms *)addr;
   1468 		if (form_parms->fdformat_version != FDFORMAT_VERSION)
   1469 			return EINVAL;	/* wrong version of formatting prog */
   1470 
   1471 		scratch = form_parms->nbps >> 7;
   1472 		if ((form_parms->nbps & 0x7f) || ffs(scratch) == 0 ||
   1473 		    scratch & ~(1 << (ffs(scratch)-1)))
   1474 			/* not a power-of-two multiple of 128 */
   1475 			return EINVAL;
   1476 
   1477 		switch (form_parms->xfer_rate) {
   1478 		case 500 * 1024:
   1479 			fd->sc_type->rate = FDC_500KBPS;
   1480 			break;
   1481 		case 300 * 1024:
   1482 			fd->sc_type->rate = FDC_300KBPS;
   1483 			break;
   1484 		case 250 * 1024:
   1485 			fd->sc_type->rate = FDC_250KBPS;
   1486 			break;
   1487 		default:
   1488 			return EINVAL;
   1489 		}
   1490 
   1491 		if (form_parms->nspt > FD_MAX_NSEC ||
   1492 		    form_parms->fillbyte > 0xff ||
   1493 		    form_parms->interleave > 0xff)
   1494 			return EINVAL;
   1495 		fd->sc_type->sectrac = form_parms->nspt;
   1496 		if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
   1497 			return EINVAL;
   1498 		fd->sc_type->heads = form_parms->ntrk;
   1499 		fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
   1500 		fd->sc_type->secsize = ffs(scratch)-1;
   1501 		fd->sc_type->gap2 = form_parms->gaplen;
   1502 		fd->sc_type->cyls = form_parms->ncyl;
   1503 		fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
   1504 			form_parms->nbps / DEV_BSIZE;
   1505 		fd->sc_type->step = form_parms->stepspercyl;
   1506 		fd->sc_type->fillbyte = form_parms->fillbyte;
   1507 		fd->sc_type->interleave = form_parms->interleave;
   1508 		return 0;
   1509 
   1510 	case FDIOCFORMAT_TRACK:
   1511 		if((flag & FWRITE) == 0)
   1512 			return EBADF;	/* must be opened for writing */
   1513 		form_cmd = (struct fdformat_cmd *)addr;
   1514 		if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
   1515 			return EINVAL;	/* wrong version of formatting prog */
   1516 
   1517 		if (form_cmd->head >= fd->sc_type->heads ||
   1518 		    form_cmd->cylinder >= fd->sc_type->cyls) {
   1519 			return EINVAL;
   1520 		}
   1521 
   1522 		fd_formb = malloc(sizeof(struct ne7_fd_formb),
   1523 		    M_TEMP, M_NOWAIT);
   1524 		if(fd_formb == 0)
   1525 			return ENOMEM;
   1526 
   1527 
   1528 		fd_formb->head = form_cmd->head;
   1529 		fd_formb->cyl = form_cmd->cylinder;
   1530 		fd_formb->transfer_rate = fd->sc_type->rate;
   1531 		fd_formb->fd_formb_secshift = fd->sc_type->secsize;
   1532 		fd_formb->fd_formb_nsecs = fd->sc_type->sectrac;
   1533 		fd_formb->fd_formb_gaplen = fd->sc_type->gap2;
   1534 		fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte;
   1535 
   1536 		memset(il, 0, sizeof il);
   1537 		for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) {
   1538 			while (il[(j%fd_formb->fd_formb_nsecs)+1])
   1539 				j++;
   1540 			il[(j%fd_formb->fd_formb_nsecs)+1] = i;
   1541 			j += fd->sc_type->interleave;
   1542 		}
   1543 		for (i = 0; i < fd_formb->fd_formb_nsecs; i++) {
   1544 			fd_formb->fd_formb_cylno(i) = form_cmd->cylinder;
   1545 			fd_formb->fd_formb_headno(i) = form_cmd->head;
   1546 			fd_formb->fd_formb_secno(i) = il[i+1];
   1547 			fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize;
   1548 		}
   1549 
   1550 		error = fdformat(dev, fd_formb, p);
   1551 		free(fd_formb, M_TEMP);
   1552 		return error;
   1553 
   1554 	case FDIOCGETOPTS:		/* get drive options */
   1555 		*(int *)addr = fd->sc_opts;
   1556 		return 0;
   1557 
   1558 	case FDIOCSETOPTS:		/* set drive options */
   1559 		fd->sc_opts = *(int *)addr;
   1560 		return 0;
   1561 
   1562 	default:
   1563 		return ENOTTY;
   1564 	}
   1565 
   1566 #ifdef DIAGNOSTIC
   1567 	panic("fdioctl: impossible");
   1568 #endif
   1569 }
   1570 
   1571 int
   1572 fdformat(dev, finfo, p)
   1573 	dev_t dev;
   1574 	struct ne7_fd_formb *finfo;
   1575 	struct proc *p;
   1576 {
   1577 	int rv = 0, s;
   1578 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
   1579 	struct fd_type *type = fd->sc_type;
   1580 	struct buf *bp;
   1581 
   1582 	/* set up a buffer header for fdstrategy() */
   1583 	bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
   1584 	if(bp == 0)
   1585 		return ENOBUFS;
   1586 	memset((void *)bp, 0, sizeof(struct buf));
   1587 	bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
   1588 	bp->b_proc = p;
   1589 	bp->b_dev = dev;
   1590 
   1591 	/*
   1592 	 * calculate a fake blkno, so fdstrategy() would initiate a
   1593 	 * seek to the requested cylinder
   1594 	 */
   1595 	bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
   1596 		       + finfo->head * type->sectrac) * FDC_BSIZE / DEV_BSIZE;
   1597 
   1598 	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
   1599 	bp->b_data = (caddr_t)finfo;
   1600 
   1601 #ifdef DEBUG
   1602 	printf("fdformat: blkno %x count %lx\n", bp->b_blkno, bp->b_bcount);
   1603 #endif
   1604 
   1605 	/* now do the format */
   1606 	fdstrategy(bp);
   1607 
   1608 	/* ...and wait for it to complete */
   1609 	s = splbio();
   1610 	while(!(bp->b_flags & B_DONE)) {
   1611 		rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
   1612 		if (rv == EWOULDBLOCK)
   1613 			break;
   1614 	}
   1615 	splx(s);
   1616 
   1617 	if (rv == EWOULDBLOCK) {
   1618 		/* timed out */
   1619 		rv = EIO;
   1620 		biodone(bp);
   1621 	}
   1622 	if(bp->b_flags & B_ERROR) {
   1623 		rv = bp->b_error;
   1624 	}
   1625 	free(bp, M_TEMP);
   1626 	return rv;
   1627 }
   1628 
   1629 #include "md.h"
   1630 #if NMD > 0
   1631 
   1632 #include <dev/md.h>
   1633 
   1634 int load_memory_disc_from_floppy __P((struct md_conf *md, dev_t dev));
   1635 
   1636 int
   1637 load_memory_disc_from_floppy(md, dev)
   1638 	struct md_conf *md;
   1639 	dev_t dev;
   1640 {
   1641 	struct buf *bp;
   1642 	int loop;
   1643 	int s;
   1644 	int type;
   1645 	int floppysize;
   1646 
   1647 	if (major(dev) != 17)	/* XXX - nice if the major was defined elsewhere */
   1648 		return(EINVAL);
   1649 
   1650 	if (md->md_type == MD_UNCONFIGURED || md->md_addr == 0)
   1651 		return(EBUSY);
   1652 
   1653 	type = FDTYPE(dev) - 1;
   1654 	if (type < 0) type = 0;
   1655 	floppysize = fd_types[type].size << (fd_types[type].secsize + 7);
   1656 
   1657 	if (md->md_size < floppysize) {
   1658 		printf("Memory disc is not big enough for floppy image\n");
   1659 		return(EINVAL);
   1660 	}
   1661 
   1662 /* We have the memory disk ! */
   1663 
   1664 	printf("Loading memory disc : %4dK ", 0);
   1665 
   1666 /* obtain a buffer */
   1667 
   1668 	bp = geteblk(fd_types[type].sectrac * DEV_BSIZE);
   1669 
   1670 /* request no partition relocation by driver on I/O operations */
   1671 
   1672 	bp->b_dev = dev;
   1673 
   1674 	s = spl0();
   1675 
   1676 	if (fdopen(bp->b_dev, 0, 0, curproc) != 0) {
   1677 		brelse(bp);
   1678 		printf("Cannot open floppy device\n");
   1679 			return(EINVAL);
   1680 	}
   1681 
   1682 	for (loop = 0;
   1683 	    loop < (floppysize / DEV_BSIZE / fd_types[type].sectrac);
   1684 	    ++loop) {
   1685 		printf("\x08\x08\x08\x08\x08\x08%4dK ",
   1686 		    loop * fd_types[type].sectrac * DEV_BSIZE / 1024);
   1687 		bp->b_blkno = loop * fd_types[type].sectrac;
   1688 		bp->b_bcount = fd_types[type].sectrac * DEV_BSIZE;
   1689 		bp->b_flags |= B_READ;
   1690 		bp->b_error = 0;
   1691 		bp->b_resid = 0;
   1692 		fdstrategy(bp);
   1693 
   1694 		if (biowait(bp))
   1695 			panic("Cannot load floppy image\n");
   1696 
   1697 		memcpy((caddr_t)md->md_addr + loop * fd_types[type].sectrac
   1698 		    * DEV_BSIZE, (caddr_t)bp->b_data,
   1699 		    fd_types[type].sectrac * DEV_BSIZE);
   1700 	}
   1701 	printf("\x08\x08\x08\x08\x08\x08%4dK done\n",
   1702 	    loop * fd_types[type].sectrac * DEV_BSIZE / 1024);
   1703 
   1704 	fdclose(bp->b_dev, 0, 0, curproc);
   1705 
   1706 	brelse(bp);
   1707 
   1708 	splx(s);
   1709 	return(0);
   1710 }
   1711 
   1712 #endif
   1713