Home | History | Annotate | Line # | Download | only in jazz
fd.c revision 1.1
      1 /*	$NetBSD: fd.c,v 1.1 2000/12/24 09:25:28 ur Exp $	*/
      2 /*	$OpenBSD: fd.c,v 1.6 1998/10/03 21:18:57 millert Exp $	*/
      3 /*	NetBSD: fd.c,v 1.78 1995/07/04 07:23:09 mycroft Exp 	*/
      4 
      5 /*-
      6  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      7  * All rights reserved.
      8  *
      9  * This code is derived from software contributed to The NetBSD Foundation
     10  * by Charles M. Hannum.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  * 3. All advertising materials mentioning features or use of this software
     21  *    must display the following acknowledgement:
     22  *        This product includes software developed by the NetBSD
     23  *        Foundation, Inc. and its contributors.
     24  * 4. Neither the name of The NetBSD Foundation nor the names of its
     25  *    contributors may be used to endorse or promote products derived
     26  *    from this software without specific prior written permission.
     27  *
     28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     38  * POSSIBILITY OF SUCH DAMAGE.
     39  */
     40 
     41 /*-
     42  * Copyright (c) 1990 The Regents of the University of California.
     43  * All rights reserved.
     44  *
     45  * This code is derived from software contributed to Berkeley by
     46  * Don Ahn.
     47  *
     48  * Redistribution and use in source and binary forms, with or without
     49  * modification, are permitted provided that the following conditions
     50  * are met:
     51  * 1. Redistributions of source code must retain the above copyright
     52  *    notice, this list of conditions and the following disclaimer.
     53  * 2. Redistributions in binary form must reproduce the above copyright
     54  *    notice, this list of conditions and the following disclaimer in the
     55  *    documentation and/or other materials provided with the distribution.
     56  * 3. All advertising materials mentioning features or use of this software
     57  *    must display the following acknowledgement:
     58  *	This product includes software developed by the University of
     59  *	California, Berkeley and its contributors.
     60  * 4. Neither the name of the University nor the names of its contributors
     61  *    may be used to endorse or promote products derived from this software
     62  *    without specific prior written permission.
     63  *
     64  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     65  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     66  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     67  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     68  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     69  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     70  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     71  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     72  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     73  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     74  * SUCH DAMAGE.
     75  *
     76  *	@(#)fd.c	7.4 (Berkeley) 5/25/91
     77  */
     78 
     79 #include <sys/param.h>
     80 #include <sys/systm.h>
     81 #include <sys/callout.h>
     82 #include <sys/kernel.h>
     83 #include <sys/conf.h>
     84 #include <sys/file.h>
     85 #include <sys/ioctl.h>
     86 #include <sys/device.h>
     87 #include <sys/disklabel.h>
     88 #include <sys/dkstat.h>
     89 #include <sys/disk.h>
     90 #include <sys/buf.h>
     91 #include <sys/uio.h>
     92 #include <sys/syslog.h>
     93 #include <sys/queue.h>
     94 
     95 #include <uvm/uvm_extern.h>
     96 
     97 #include <machine/bus.h>
     98 #include <machine/cpu.h>
     99 #include <machine/pio.h>
    100 #include <machine/autoconf.h>
    101 
    102 #include <mips/locore.h> /* for mips3_HitFlushDCache() */
    103 #include <arc/jazz/fdreg.h>
    104 #include <arc/jazz/jazziovar.h>
    105 #include <arc/jazz/jazzdmatlbreg.h>
    106 #include <arc/jazz/dma.h>
    107 
    108 #include "locators.h"
    109 
    110 
    111 #define FDUNIT(dev)	((dev & 0x080) >> 7)
    112 #define FDTYPE(dev)	((minor(dev) & 0x70) >> 4)
    113 #define FDPART(dev)	(minor(dev) & 0x0f)
    114 
    115 enum fdc_state {
    116 	DEVIDLE = 0,
    117 	MOTORWAIT,
    118 	DOSEEK,
    119 	SEEKWAIT,
    120 	SEEKTIMEDOUT,
    121 	SEEKCOMPLETE,
    122 	DOIO,
    123 	IOCOMPLETE,
    124 	IOTIMEDOUT,
    125 	DORESET,
    126 	RESETCOMPLETE,
    127 	RESETTIMEDOUT,
    128 	DORECAL,
    129 	RECALWAIT,
    130 	RECALTIMEDOUT,
    131 	RECALCOMPLETE,
    132 };
    133 
    134 /* software state, per controller */
    135 struct fdc_softc {
    136 	struct device sc_dev;		/* boilerplate */
    137 
    138 	struct callout sc_timo_ch;	/* timeout callout */
    139 	struct callout sc_intr_ch;	/* pseudo-intr callout */
    140 
    141 	struct dma_softc __dma;
    142 	struct dma_softc *dma;
    143 
    144 	int sc_iobase;
    145 
    146 	struct fd_softc *sc_fd[4];	/* pointers to children */
    147 	TAILQ_HEAD(drivehead, fd_softc) sc_drives;
    148 	enum fdc_state sc_state;
    149 	int sc_errors;			/* number of retries so far */
    150 	u_char sc_status[7];		/* copy of registers */
    151 };
    152 
    153 /* controller driver configuration */
    154 int fdcprobe __P((struct device *, struct cfdata *, void *));
    155 void fdcattach __P((struct device *, struct device *, void *));
    156 
    157 struct cfattach fdc_ca = {
    158 	sizeof(struct fdc_softc), fdcprobe, fdcattach
    159 };
    160 
    161 /*
    162  * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
    163  * we tell them apart.
    164  */
    165 struct fd_type {
    166 	int	sectrac;	/* sectors per track */
    167 	int	heads;		/* number of heads */
    168 	int	seccyl;		/* sectors per cylinder */
    169 	int	secsize;	/* size code for sectors */
    170 	int	datalen;	/* data len when secsize = 0 */
    171 	int	steprate;	/* step rate and head unload time */
    172 	int	gap1;		/* gap len between sectors */
    173 	int	gap2;		/* formatting gap */
    174 	int	tracks;		/* total num of tracks */
    175 	int	size;		/* size of disk in sectors */
    176 	int	step;		/* steps per cylinder */
    177 	int	rate;		/* transfer speed code */
    178 	char	*name;
    179 };
    180 
    181 /* The order of entries in the following table is important -- BEWARE! */
    182 struct fd_type fd_types[] = {
    183         { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB"    }, /* 1.44MB diskette */
    184         { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB"    }, /* 1.2 MB AT-diskettes */
    185         {  9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */
    186         {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */
    187         {  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB"    }, /* 3.5" 720kB diskette */
    188         {  9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x"  }, /* 720kB in 1.2MB drive */
    189         {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x"  }, /* 360kB in 720kB drive */
    190 };
    191 
    192 /* software state, per disk (with up to 4 disks per ctlr) */
    193 struct fd_softc {
    194 	struct device sc_dev;
    195 	struct disk sc_dk;
    196 
    197 	struct fd_type *sc_deftype;	/* default type descriptor */
    198 	struct fd_type *sc_type;	/* current type descriptor */
    199 
    200 	struct callout sc_motoron_ch;
    201 	struct callout sc_motoroff_ch;
    202 
    203 	daddr_t	sc_blkno;	/* starting block number */
    204 	int sc_bcount;		/* byte count left */
    205 	int sc_skip;		/* bytes already transferred */
    206 	int sc_nblks;		/* number of blocks currently tranferring */
    207 	int sc_nbytes;		/* number of bytes currently tranferring */
    208 
    209 	int sc_drive;		/* physical unit number */
    210 	int sc_flags;
    211 #define	FD_OPEN		0x01		/* it's open */
    212 #define	FD_MOTOR	0x02		/* motor should be on */
    213 #define	FD_MOTOR_WAIT	0x04		/* motor coming up */
    214 	int sc_cylin;		/* where we think the head is */
    215 
    216 	void *sc_sdhook;        /* saved shutdown hook for drive. */
    217 
    218 	TAILQ_ENTRY(fd_softc) sc_drivechain;
    219 	int sc_ops;		/* I/O ops since last switch */
    220 	struct buf_queue sc_q;	/* pending I/O requests */
    221 	int sc_active;		/* number of active I/O operations */
    222 };
    223 
    224 /* floppy driver configuration */
    225 int fdprobe __P((struct device *, struct cfdata *, void *));
    226 void fdattach __P((struct device *, struct device *, void *));
    227 
    228 struct cfattach fd_ca = {
    229 	sizeof(struct fd_softc), fdprobe, fdattach
    230 };
    231 extern struct cfdriver fd_cd;
    232 
    233 void fdgetdisklabel __P((struct fd_softc *));
    234 int fd_get_parms __P((struct fd_softc *));
    235 void fdstrategy __P((struct buf *));
    236 void fdstart __P((struct fd_softc *));
    237 int fdioctl __P((dev_t, u_long, caddr_t, int));
    238 int fddump __P((dev_t, daddr_t, caddr_t, size_t));
    239 int fdsize __P((dev_t));
    240 int fdopen __P((dev_t, int));
    241 int fdclose __P((dev_t, int));
    242 int fdwrite __P((dev_t, struct uio *));
    243 int fdread __P((dev_t, struct uio *));
    244 
    245 struct dkdriver fddkdriver = { fdstrategy };
    246 
    247 int fdprint __P((void *, const char *));
    248 struct fd_type *fd_nvtotype __P((char *, int, int));
    249 void fd_set_motor __P((struct fdc_softc *fdc, int reset));
    250 void fd_motor_off __P((void *arg));
    251 void fd_motor_on __P((void *arg));
    252 int fdcresult __P((struct fdc_softc *fdc));
    253 int out_fdc __P((int iobase, u_char x));
    254 void fdcstart __P((struct fdc_softc *fdc));
    255 void fdcstatus __P((struct device *dv, int n, char *s));
    256 void fdctimeout __P((void *arg));
    257 void fdcpseudointr __P((void *arg));
    258 int fdcintr __P((void *));
    259 void fdcretry __P((struct fdc_softc *fdc));
    260 void fdfinish __P((struct fd_softc *fd, struct buf *bp));
    261 
    262 int
    263 fdcprobe(parent, match, aux)
    264 	struct device *parent;
    265 	struct cfdata *match;
    266 	void *aux;
    267 {
    268 	struct jazzio_attach_args *ja = aux;
    269 	int iobase = ja->ja_addr;
    270 
    271 	if (strcmp(ja->ja_name, "fdc") != 0)
    272 		return (0);
    273 
    274 	/* reset */
    275 	outb(iobase + fdout, 0);
    276 	delay(100);
    277 	outb(iobase + fdout, FDO_FRST);
    278 
    279 	/* see if it can handle a command */
    280 	if (out_fdc(iobase, NE7CMD_SPECIFY) < 0)
    281 		return 0;
    282 	out_fdc(iobase, 0xdf);
    283 	out_fdc(iobase, 2);
    284 
    285 	return 1;
    286 }
    287 
    288 /*
    289  * Arguments passed between fdcattach and fdprobe.
    290  */
    291 struct fdc_attach_args {
    292 	int fa_drive;
    293 	struct fd_type *fa_deftype;
    294 };
    295 
    296 /*
    297  * Print the location of a disk drive (called just before attaching the
    298  * the drive).  If `fdc' is not NULL, the drive was found but was not
    299  * in the system config file; print the drive name as well.
    300  * Return QUIET (config_find ignores this if the device was configured) to
    301  * avoid printing `fdN not configured' messages.
    302  */
    303 int
    304 fdprint(aux, fdc)
    305 	void *aux;
    306 	const char *fdc;
    307 {
    308 	register struct fdc_attach_args *fa = aux;
    309 
    310 	if (!fdc)
    311 		printf(" drive %d", fa->fa_drive);
    312 	return QUIET;
    313 }
    314 
    315 void
    316 fdcattach(parent, self, aux)
    317 	struct device *parent, *self;
    318 	void *aux;
    319 {
    320 	struct fdc_softc *fdc = (void *)self;
    321 	struct jazzio_attach_args *ja = aux;
    322 	struct fdc_attach_args fa;
    323 	int type;
    324 
    325 	fdc->sc_iobase = ja->ja_addr;
    326 	fdc->sc_state = DEVIDLE;
    327 	TAILQ_INIT(&fdc->sc_drives);
    328 
    329 	fdc->dma = &fdc->__dma;
    330 	fdc_dma_init(fdc->dma);
    331 
    332 	printf("\n");
    333 
    334 	callout_init(&fdc->sc_timo_ch);
    335 	callout_init(&fdc->sc_intr_ch);
    336 
    337 	jazzio_intr_establish(ja->ja_intr, fdcintr, fdc);
    338 
    339 	/*
    340 	 * No way yet to determine default disk types.
    341 	 * we assume 1.44 3.5" type for the moment.
    342 	 */
    343 	type = 0;
    344 
    345 	/* physical limit: two drives per controller. */
    346 	for (fa.fa_drive = 0; fa.fa_drive < 2; fa.fa_drive++) {
    347 		if (type >= 0 && fa.fa_drive < 2)
    348 			fa.fa_deftype = fd_nvtotype(fdc->sc_dev.dv_xname,
    349 			    type, fa.fa_drive);
    350 		else
    351 			fa.fa_deftype = NULL;		/* unknown */
    352 		(void)config_found(self, (void *)&fa, fdprint);
    353 	}
    354 }
    355 
    356 int
    357 fdprobe(parent, match, aux)
    358 	struct device *parent;
    359 	struct cfdata *match;
    360 	void *aux;
    361 {
    362 	struct fdc_softc *fdc = (void *)parent;
    363 	struct fdc_attach_args *fa = aux;
    364 	int drive = fa->fa_drive;
    365 	int iobase = fdc->sc_iobase;
    366 	int n;
    367 
    368 	if (match->cf_loc[FDCCF_DRIVE] != FDCCF_DRIVE_DEFAULT &&
    369 	    match->cf_loc[FDCCF_DRIVE] != drive)
    370 		return 0;
    371 
    372 	/* select drive and turn on motor */
    373 	outb(iobase + fdout, drive | FDO_FRST | FDO_MOEN(drive));
    374 	/* wait for motor to spin up */
    375 	delay(500000);
    376 	out_fdc(iobase, NE7CMD_RECAL);
    377 	out_fdc(iobase, drive);
    378 	/* wait for recalibrate */
    379 	delay(2000000);
    380 	out_fdc(iobase, NE7CMD_SENSEI);
    381 	n = fdcresult(fdc);
    382 #ifdef FD_DEBUG
    383 	{
    384 		int i;
    385 		printf("fdprobe: status");
    386 		for (i = 0; i < n; i++)
    387 			printf(" %x", fdc->sc_status[i]);
    388 		printf("\n");
    389 	}
    390 #endif
    391 	if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)
    392 		return 0;
    393 	/* turn off motor */
    394 	outb(iobase + fdout, FDO_FRST);
    395 
    396 	return 1;
    397 }
    398 
    399 /*
    400  * Controller is working, and drive responded.  Attach it.
    401  */
    402 void
    403 fdattach(parent, self, aux)
    404 	struct device *parent, *self;
    405 	void *aux;
    406 {
    407 	struct fdc_softc *fdc = (void *)parent;
    408 	struct fd_softc *fd = (void *)self;
    409 	struct fdc_attach_args *fa = aux;
    410 	struct fd_type *type = fa->fa_deftype;
    411 	int drive = fa->fa_drive;
    412 
    413 	callout_init(&fd->sc_motoron_ch);
    414 	callout_init(&fd->sc_motoroff_ch);
    415 
    416 	/* XXX Allow `flags' to override device type? */
    417 
    418 	if (type)
    419 		printf(": %s %d cyl, %d head, %d sec\n", type->name,
    420 		    type->tracks, type->heads, type->sectrac);
    421 	else
    422 		printf(": density unknown\n");
    423 
    424 	BUFQ_INIT(&fd->sc_q);
    425 	fd->sc_cylin = -1;
    426 	fd->sc_drive = drive;
    427 	fd->sc_deftype = type;
    428 	fdc->sc_fd[drive] = fd;
    429 	fd->sc_dk.dk_name = fd->sc_dev.dv_xname;
    430 	fd->sc_dk.dk_driver = &fddkdriver;
    431 
    432 	/* Needed to power off if the motor is on when we halt. */
    433 	fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd);
    434 }
    435 
    436 /*
    437  * Translate nvram type into internal data structure.  Return NULL for
    438  * none/unknown/unusable.
    439  */
    440 struct fd_type *
    441 fd_nvtotype(fdc, nvraminfo, drive)
    442 	char *fdc;
    443 	int nvraminfo, drive;
    444 {
    445 	int type;
    446 
    447 	type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0;
    448 #if 0
    449 	switch (type) {
    450 	case NVRAM_DISKETTE_NONE:
    451 		return NULL;
    452 	case NVRAM_DISKETTE_12M:
    453 		return &fd_types[1];
    454 	case NVRAM_DISKETTE_TYPE5:
    455 	case NVRAM_DISKETTE_TYPE6:
    456 		/* XXX We really ought to handle 2.88MB format. */
    457 	case NVRAM_DISKETTE_144M:
    458 		return &fd_types[0];
    459 	case NVRAM_DISKETTE_360K:
    460 		return &fd_types[3];
    461 	case NVRAM_DISKETTE_720K:
    462 		return &fd_types[4];
    463 	default:
    464 		printf("%s: drive %d: unknown device type 0x%x\n",
    465 		    fdc, drive, type);
    466 		return NULL;
    467 	}
    468 #else
    469 	return &fd_types[0]; /* Use only 1.44 for now */
    470 #endif
    471 }
    472 
    473 void
    474 fdstrategy(bp)
    475 	register struct buf *bp;	/* IO operation to perform */
    476 {
    477 	struct fd_softc *fd;
    478 	int unit = FDUNIT(bp->b_dev);
    479 	int sz;
    480  	int s;
    481 
    482 	/* Valid unit, controller, and request? */
    483 	if (unit >= fd_cd.cd_ndevs ||
    484 	    (fd = fd_cd.cd_devs[unit]) == 0 ||
    485 	    bp->b_blkno < 0 ||
    486 	    (bp->b_bcount % FDC_BSIZE) != 0) {
    487 		bp->b_error = EINVAL;
    488 		goto bad;
    489 	}
    490 
    491 	/* If it's a null transfer, return immediately. */
    492 	if (bp->b_bcount == 0)
    493 		goto done;
    494 
    495 	sz = howmany(bp->b_bcount, FDC_BSIZE);
    496 
    497 	if (bp->b_blkno + sz > fd->sc_type->size) {
    498 		sz = fd->sc_type->size - bp->b_blkno;
    499 		if (sz == 0) {
    500 			/* If exactly at end of disk, return EOF. */
    501 			bp->b_resid = bp->b_bcount;
    502 			goto done;
    503 		}
    504 		if (sz < 0) {
    505 			/* If past end of disk, return EINVAL. */
    506 			bp->b_error = EINVAL;
    507 			goto bad;
    508 		}
    509 		/* Otherwise, truncate request. */
    510 		bp->b_bcount = sz << DEV_BSHIFT;
    511 	}
    512 
    513 	bp->b_rawblkno = bp->b_blkno;
    514  	bp->b_cylinder = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE) / fd->sc_type->seccyl;
    515 
    516 #ifdef FD_DEBUG
    517 	printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d sz %d\n",
    518 	    bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylinder, sz);
    519 #endif
    520 
    521 	/* Queue transfer on drive, activate drive and controller if idle. */
    522 	s = splbio();
    523 	disksort_cylinder(&fd->sc_q, bp);
    524 	callout_stop(&fd->sc_motoroff_ch);		/* a good idea */
    525 	if (fd->sc_active == 0)
    526 		fdstart(fd);
    527 #ifdef DIAGNOSTIC
    528 	else {
    529 		struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    530 		if (fdc->sc_state == DEVIDLE) {
    531 			printf("fdstrategy: controller inactive\n");
    532 			fdcstart(fdc);
    533 		}
    534 	}
    535 #endif
    536 	splx(s);
    537 	return;
    538 
    539 bad:
    540 	bp->b_flags |= B_ERROR;
    541 done:
    542 	/* Toss transfer; we're done early. */
    543 	biodone(bp);
    544 }
    545 
    546 void
    547 fdstart(fd)
    548 	struct fd_softc *fd;
    549 {
    550 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    551 	int active = fdc->sc_drives.tqh_first != 0;
    552 
    553 	/* Link into controller queue. */
    554 	fd->sc_active = 1;
    555 	TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
    556 
    557 	/* If controller not already active, start it. */
    558 	if (!active)
    559 		fdcstart(fdc);
    560 }
    561 
    562 void
    563 fdfinish(fd, bp)
    564 	struct fd_softc *fd;
    565 	struct buf *bp;
    566 {
    567 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    568 
    569 	/*
    570 	 * Move this drive to the end of the queue to give others a `fair'
    571 	 * chance.  We only force a switch if N operations are completed while
    572 	 * another drive is waiting to be serviced, since there is a long motor
    573 	 * startup delay whenever we switch.
    574 	 */
    575 	if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
    576 		fd->sc_ops = 0;
    577 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
    578 		if (BUFQ_NEXT(bp) != NULL) {
    579 			TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
    580 		} else
    581 			fd->sc_active = 0;
    582 	}
    583 	bp->b_resid = fd->sc_bcount;
    584 	fd->sc_skip = 0;
    585 	BUFQ_REMOVE(&fd->sc_q, bp);
    586 	biodone(bp);
    587 	/* turn off motor 5s from now */
    588 	callout_reset(&fd->sc_motoroff_ch, 10 * hz, fd_motor_off, fd);
    589 	fdc->sc_state = DEVIDLE;
    590 }
    591 
    592 int
    593 fdread(dev, uio)
    594 	dev_t dev;
    595 	struct uio *uio;
    596 {
    597 
    598 	return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio));
    599 }
    600 
    601 int
    602 fdwrite(dev, uio)
    603 	dev_t dev;
    604 	struct uio *uio;
    605 {
    606 
    607 	return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio));
    608 }
    609 
    610 void
    611 fd_set_motor(fdc, reset)
    612 	struct fdc_softc *fdc;
    613 	int reset;
    614 {
    615 	struct fd_softc *fd;
    616 	u_char status;
    617 	int n;
    618 
    619 	if ((fd = fdc->sc_drives.tqh_first) != NULL)
    620 		status = fd->sc_drive;
    621 	else
    622 		status = 0;
    623 	if (!reset)
    624 		status |= FDO_FRST | FDO_FDMAEN;
    625 	for (n = 0; n < 4; n++)
    626 		if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR))
    627 			status |= FDO_MOEN(n);
    628 	outb(fdc->sc_iobase + fdout, status);
    629 }
    630 
    631 void
    632 fd_motor_off(arg)
    633 	void *arg;
    634 {
    635 	struct fd_softc *fd = arg;
    636 	int s;
    637 
    638 	s = splbio();
    639 	fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
    640 	fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0);
    641 	splx(s);
    642 }
    643 
    644 void
    645 fd_motor_on(arg)
    646 	void *arg;
    647 {
    648 	struct fd_softc *fd = arg;
    649 	struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
    650 	int s;
    651 
    652 	s = splbio();
    653 	fd->sc_flags &= ~FD_MOTOR_WAIT;
    654 	if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
    655 		(void) fdcintr(fdc);
    656 	splx(s);
    657 }
    658 
    659 int
    660 fdcresult(fdc)
    661 	struct fdc_softc *fdc;
    662 {
    663 	int iobase = fdc->sc_iobase;
    664 	u_char i;
    665 	int j = 400000,		/* Empirical, should do at 150 Mhz to */
    666 	    n = 0;
    667 
    668 	for (; j; --j) {
    669 		i = inb(iobase + fdsts) & (NE7_DIO | NE7_RQM | NE7_CB);
    670 		if (i == NE7_RQM) {
    671 			return n;
    672 		}
    673 		if (i == (NE7_DIO | NE7_RQM | NE7_CB)) {
    674 			if (n >= sizeof(fdc->sc_status)) {
    675 				log(LOG_ERR, "fdcresult: overrun\n");
    676 				return -1;
    677 			}
    678 			fdc->sc_status[n++] = inb(iobase + fddata);
    679 		}
    680 	}
    681 	log(LOG_ERR, "fdcresult: timeout\n");
    682 	return -1;
    683 }
    684 
    685 int
    686 out_fdc(iobase, x)
    687 	int iobase;
    688 	u_char x;
    689 {
    690 	int i = 100000;
    691 
    692 	while ((inb(iobase + fdsts) & NE7_DIO) && i-- > 0);
    693 	if (i <= 0)
    694 		return -1;
    695 	while ((inb(iobase + fdsts) & NE7_RQM) == 0 && i-- > 0);
    696 	if (i <= 0)
    697 		return -1;
    698 	outb(iobase + fddata, x);
    699 	return 0;
    700 }
    701 
    702 int
    703 fdopen(dev, flags)
    704 	dev_t dev;
    705 	int flags;
    706 {
    707  	int unit;
    708 	struct fd_softc *fd;
    709 	struct fd_type *type;
    710 
    711 	unit = FDUNIT(dev);
    712 	if (unit >= fd_cd.cd_ndevs)
    713 		return ENXIO;
    714 	fd = fd_cd.cd_devs[unit];
    715 	if (fd == 0)
    716 		return ENXIO;
    717 
    718 	if (FDTYPE(dev) > (sizeof(fd_types) / sizeof(fd_types[0])))
    719 		type = NULL;
    720 	else if(FDTYPE(dev))
    721 		type =  &fd_types[FDTYPE(dev) - 1];
    722 	else
    723 		type = fd->sc_deftype;
    724 
    725 	if (type == NULL)
    726 		return ENXIO;
    727 
    728 	if ((fd->sc_flags & FD_OPEN) != 0 &&
    729 	    fd->sc_type != type)
    730 		return EBUSY;
    731 
    732 	fd->sc_type = type;
    733 	fd->sc_cylin = -1;
    734 	fd->sc_flags |= FD_OPEN;
    735 
    736 	return 0;
    737 }
    738 
    739 int
    740 fdclose(dev, flags)
    741 	dev_t dev;
    742 	int flags;
    743 {
    744 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
    745 
    746 	fd->sc_flags &= ~FD_OPEN;
    747 	return 0;
    748 }
    749 
    750 void
    751 fdcstart(fdc)
    752 	struct fdc_softc *fdc;
    753 {
    754 
    755 #ifdef DIAGNOSTIC
    756 	/* only got here if controller's drive queue was inactive; should
    757 	   be in idle state */
    758 	if (fdc->sc_state != DEVIDLE) {
    759 		printf("fdcstart: not idle\n");
    760 		return;
    761 	}
    762 #endif
    763 	(void) fdcintr(fdc);
    764 }
    765 
    766 void
    767 fdcstatus(dv, n, s)
    768 	struct device *dv;
    769 	int n;
    770 	char *s;
    771 {
    772 	struct fdc_softc *fdc = (void *)dv->dv_parent;
    773 	char bits[64];
    774 
    775 	if (n == 0) {
    776 		out_fdc(fdc->sc_iobase, NE7CMD_SENSEI);
    777 		(void) fdcresult(fdc);
    778 		n = 2;
    779 	}
    780 
    781 	printf("%s: %s", dv->dv_xname, s);
    782 
    783 	switch (n) {
    784 	case 0:
    785 		printf("\n");
    786 		break;
    787 	case 2:
    788 		printf(" (st0 %s cyl %d)\n",
    789 		     bitmask_snprintf(fdc->sc_status[0], NE7_ST0BITS,
    790 		     bits, sizeof(bits)), fdc->sc_status[1]);
    791 		break;
    792 	case 7:
    793 		printf(" (st0 %s", bitmask_snprintf(fdc->sc_status[0],
    794 		    NE7_ST0BITS, bits, sizeof(bits)));
    795 		printf(" st1 %s", bitmask_snprintf(fdc->sc_status[1],
    796 		    NE7_ST1BITS, bits, sizeof(bits)));
    797 		printf(" st2 %s", bitmask_snprintf(fdc->sc_status[2],
    798 		    NE7_ST2BITS, bits, sizeof(bits)));
    799 		printf(" cyl %d head %d sec %d)\n",
    800 		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
    801 		break;
    802 #ifdef DIAGNOSTIC
    803 	default:
    804 		printf("\nfdcstatus: weird size");
    805 		break;
    806 #endif
    807 	}
    808 }
    809 
    810 void
    811 fdctimeout(arg)
    812 	void *arg;
    813 {
    814 	struct fdc_softc *fdc = arg;
    815 	struct fd_softc *fd = fdc->sc_drives.tqh_first;
    816 	int s;
    817 
    818 	s = splbio();
    819 	fdcstatus(&fd->sc_dev, 0, "timeout");
    820 
    821 	if (BUFQ_FIRST(&fd->sc_q) != NULL)
    822 		fdc->sc_state++;
    823 	else
    824 		fdc->sc_state = DEVIDLE;
    825 
    826 	(void) fdcintr(fdc);
    827 	splx(s);
    828 }
    829 
    830 void
    831 fdcpseudointr(arg)
    832 	void *arg;
    833 {
    834 	int s;
    835 
    836 	/* Just ensure it has the right spl. */
    837 	s = splbio();
    838 	(void) fdcintr(arg);
    839 	splx(s);
    840 }
    841 
    842 int
    843 fdcintr(arg)
    844 	void *arg;
    845 {
    846 	struct fdc_softc *fdc = arg;
    847 #define	st0	fdc->sc_status[0]
    848 #define	cyl	fdc->sc_status[1]
    849 	struct fd_softc *fd;
    850 	struct buf *bp;
    851 	int iobase = fdc->sc_iobase;
    852 	int read, head, sec, i, nblks;
    853 	struct fd_type *type;
    854 
    855 loop:
    856 	/* Is there a drive for the controller to do a transfer with? */
    857 	fd = fdc->sc_drives.tqh_first;
    858 	if (fd == NULL) {
    859 		fdc->sc_state = DEVIDLE;
    860  		return 1;
    861 	}
    862 
    863 	/* Is there a transfer to this drive?  If not, deactivate drive. */
    864 	bp = BUFQ_FIRST(&fd->sc_q);
    865 	if (bp == NULL) {
    866 		fd->sc_ops = 0;
    867 		TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
    868 		fd->sc_active = 0;
    869 		goto loop;
    870 	}
    871 
    872 	switch (fdc->sc_state) {
    873 	case DEVIDLE:
    874 		fdc->sc_errors = 0;
    875 		fd->sc_skip = 0;
    876 		fd->sc_bcount = bp->b_bcount;
    877 		fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE);
    878 		callout_stop(&fd->sc_motoroff_ch);
    879 		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
    880 			fdc->sc_state = MOTORWAIT;
    881 			return 1;
    882 		}
    883 		if ((fd->sc_flags & FD_MOTOR) == 0) {
    884 			/* Turn on the motor, being careful about pairing. */
    885 			struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
    886 			if (ofd && ofd->sc_flags & FD_MOTOR) {
    887 				callout_stop(&ofd->sc_motoroff_ch);
    888 				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
    889 			}
    890 			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
    891 			fd_set_motor(fdc, 0);
    892 			fdc->sc_state = MOTORWAIT;
    893 			/* Allow .5s for motor to stabilize. */
    894 			callout_reset(&fd->sc_motoron_ch, hz / 2,
    895 			    fd_motor_on, fd);
    896 			return 1;
    897 		}
    898 		/* Make sure the right drive is selected. */
    899 		fd_set_motor(fdc, 0);
    900 
    901 		/* fall through */
    902 	case DOSEEK:
    903 	doseek:
    904 		if (fd->sc_cylin == bp->b_cylinder)
    905 			goto doio;
    906 
    907 		out_fdc(iobase, NE7CMD_SPECIFY);/* specify command */
    908 		out_fdc(iobase, fd->sc_type->steprate);
    909 		out_fdc(iobase, 6);		/* XXX head load time == 6ms */
    910 
    911 		out_fdc(iobase, NE7CMD_SEEK);	/* seek function */
    912 		out_fdc(iobase, fd->sc_drive);	/* drive number */
    913 		out_fdc(iobase, bp->b_cylinder * fd->sc_type->step);
    914 
    915 		fd->sc_cylin = -1;
    916 		fdc->sc_state = SEEKWAIT;
    917 		callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
    918 		return 1;
    919 
    920 	case DOIO:
    921 	doio:
    922 		type = fd->sc_type;
    923 		sec = fd->sc_blkno % type->seccyl;
    924 		nblks = type->seccyl - sec;
    925 		nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
    926 		nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
    927 		fd->sc_nblks = nblks;
    928 		fd->sc_nbytes = nblks * FDC_BSIZE;
    929 		head = sec / type->sectrac;
    930 		sec -= head * type->sectrac;
    931 #ifdef DIAGNOSTIC
    932 		{int block;
    933 		 block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec;
    934 		 if (block != fd->sc_blkno) {
    935 			 printf("fdcintr: block %d != blkno %d\n", block, fd->sc_blkno);
    936 #ifdef DDB
    937 			 Debugger();
    938 #endif
    939 		 }}
    940 #endif
    941 		mips3_FlushDCache((vaddr_t) (bp->b_data + fd->sc_skip),
    942 				  (vsize_t) fd->sc_nbytes);
    943 		read = bp->b_flags & B_READ ? DMA_FROM_DEV : DMA_TO_DEV;
    944 		DMA_START(fdc->dma, bp->b_data + fd->sc_skip, fd->sc_nbytes, read);
    945 		outb(iobase + fdctl, type->rate);
    946 #ifdef FD_DEBUG
    947 		printf("fdcintr: %s drive %d track %d head %d sec %d nblks %d\n",
    948 		    read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head,
    949 		    sec, nblks);
    950 #endif
    951 		if (read)
    952 			out_fdc(iobase, NE7CMD_READ);	/* READ */
    953 		else
    954 			out_fdc(iobase, NE7CMD_WRITE);	/* WRITE */
    955 		out_fdc(iobase, (head << 2) | fd->sc_drive);
    956 		out_fdc(iobase, fd->sc_cylin);		/* track */
    957 		out_fdc(iobase, head);
    958 		out_fdc(iobase, sec + 1);		/* sector +1 */
    959 		out_fdc(iobase, type->secsize);		/* sector size */
    960 		out_fdc(iobase, type->sectrac);		/* sectors/track */
    961 		out_fdc(iobase, type->gap1);		/* gap1 size */
    962 		out_fdc(iobase, type->datalen);		/* data length */
    963 		fdc->sc_state = IOCOMPLETE;
    964 		/* allow 2 seconds for operation */
    965 		callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc);
    966 		return 1;				/* will return later */
    967 
    968 	case SEEKWAIT:
    969 		callout_stop(&fdc->sc_timo_ch);
    970 		fdc->sc_state = SEEKCOMPLETE;
    971 		/* allow 1/50 second for heads to settle */
    972 		callout_reset(&fdc->sc_intr_ch, hz / 50,
    973 		    fdcpseudointr, fdc);
    974 		return 1;
    975 
    976 	case SEEKCOMPLETE:
    977 		/* Make sure seek really happened. */
    978 		out_fdc(iobase, NE7CMD_SENSEI);
    979 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
    980 		    cyl != bp->b_cylinder * fd->sc_type->step) {
    981 #ifdef FD_DEBUG
    982 			fdcstatus(&fd->sc_dev, 2, "seek failed");
    983 #endif
    984 			fdcretry(fdc);
    985 			goto loop;
    986 		}
    987 		fd->sc_cylin = bp->b_cylinder;
    988 		goto doio;
    989 
    990 	case IOTIMEDOUT:
    991 		DMA_RESET(fdc->dma);
    992 
    993 	case SEEKTIMEDOUT:
    994 	case RECALTIMEDOUT:
    995 	case RESETTIMEDOUT:
    996 		fdcretry(fdc);
    997 		goto loop;
    998 
    999 	case IOCOMPLETE: /* IO DONE, post-analyze */
   1000 		callout_stop(&fdc->sc_timo_ch);
   1001 		if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) {
   1002 			DMA_RESET(fdc->dma);
   1003 #ifdef FD_DEBUG
   1004 			fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
   1005 			    "read failed" : "write failed");
   1006 			printf("blkno %d nblks %d\n",
   1007 			    fd->sc_blkno, fd->sc_nblks);
   1008 #endif
   1009 			fdcretry(fdc);
   1010 			goto loop;
   1011 		}
   1012 		DMA_END(fdc->dma);
   1013 		read = bp->b_flags & B_READ;
   1014 		if (fdc->sc_errors) {
   1015 			diskerr(bp, "fd", "soft error", LOG_PRINTF,
   1016 			    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
   1017 			printf("\n");
   1018 			fdc->sc_errors = 0;
   1019 		}
   1020 		fd->sc_blkno += fd->sc_nblks;
   1021 		fd->sc_skip += fd->sc_nbytes;
   1022 		fd->sc_bcount -= fd->sc_nbytes;
   1023 		if (fd->sc_bcount > 0) {
   1024 			bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl;
   1025 			goto doseek;
   1026 		}
   1027 		fdfinish(fd, bp);
   1028 		goto loop;
   1029 
   1030 	case DORESET:
   1031 		/* try a reset, keep motor on */
   1032 		fd_set_motor(fdc, 1);
   1033 		delay(100);
   1034 		fd_set_motor(fdc, 0);
   1035 		fdc->sc_state = RESETCOMPLETE;
   1036 		callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc);
   1037 		return 1;			/* will return later */
   1038 
   1039 	case RESETCOMPLETE:
   1040 		callout_stop(&fdc->sc_timo_ch);
   1041 		/* clear the controller output buffer */
   1042 		for (i = 0; i < 4; i++) {
   1043 			out_fdc(iobase, NE7CMD_SENSEI);
   1044 			(void) fdcresult(fdc);
   1045 		}
   1046 
   1047 		/* fall through */
   1048 	case DORECAL:
   1049 		out_fdc(iobase, NE7CMD_RECAL);	/* recalibrate function */
   1050 		out_fdc(iobase, fd->sc_drive);
   1051 		fdc->sc_state = RECALWAIT;
   1052 		callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc);
   1053 		return 1;			/* will return later */
   1054 
   1055 	case RECALWAIT:
   1056 		callout_stop(&fdc->sc_timo_ch);
   1057 		fdc->sc_state = RECALCOMPLETE;
   1058 		/* allow 1/30 second for heads to settle */
   1059 		callout_reset(&fdc->sc_intr_ch, hz / 30,
   1060 		    fdcpseudointr, fdc);
   1061 		return 1;			/* will return later */
   1062 
   1063 	case RECALCOMPLETE:
   1064 		out_fdc(iobase, NE7CMD_SENSEI);
   1065 		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
   1066 #ifdef FD_DEBUG
   1067 			fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
   1068 #endif
   1069 			fdcretry(fdc);
   1070 			goto loop;
   1071 		}
   1072 		fd->sc_cylin = 0;
   1073 		goto doseek;
   1074 
   1075 	case MOTORWAIT:
   1076 		if (fd->sc_flags & FD_MOTOR_WAIT)
   1077 			return 1;		/* time's not up yet */
   1078 		goto doseek;
   1079 
   1080 	default:
   1081 		fdcstatus(&fd->sc_dev, 0, "stray interrupt");
   1082 		return 1;
   1083 	}
   1084 #ifdef DIAGNOSTIC
   1085 	panic("fdcintr: impossible");
   1086 #endif
   1087 #undef	st0
   1088 #undef	cyl
   1089 }
   1090 
   1091 void
   1092 fdcretry(fdc)
   1093 	struct fdc_softc *fdc;
   1094 {
   1095 	struct fd_softc *fd;
   1096 	struct buf *bp;
   1097 	char bits[64];
   1098 
   1099 	fd = fdc->sc_drives.tqh_first;
   1100 	bp = BUFQ_FIRST(&fd->sc_q);
   1101 
   1102 	switch (fdc->sc_errors) {
   1103 	case 0:
   1104 		/* try again */
   1105 		fdc->sc_state = SEEKCOMPLETE;
   1106 		break;
   1107 
   1108 	case 1: case 2: case 3:
   1109 		/* didn't work; try recalibrating */
   1110 		fdc->sc_state = DORECAL;
   1111 		break;
   1112 
   1113 	case 4:
   1114 		/* still no go; reset the bastard */
   1115 		fdc->sc_state = DORESET;
   1116 		break;
   1117 
   1118 	default:
   1119 		diskerr(bp, "fd", "hard error", LOG_PRINTF,
   1120 		    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
   1121 
   1122 		printf(" (st0 %s", bitmask_snprintf(fdc->sc_status[0],
   1123 		    NE7_ST0BITS, bits, sizeof(bits)));
   1124 		printf(" st1 %s", bitmask_snprintf(fdc->sc_status[1],
   1125 		    NE7_ST1BITS, bits, sizeof(bits)));
   1126 		printf(" st2 %s", bitmask_snprintf(fdc->sc_status[2],
   1127 		    NE7_ST2BITS, bits, sizeof(bits)));
   1128 		printf(" cyl %d head %d sec %d)\n",
   1129 		    fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
   1130 
   1131 		bp->b_flags |= B_ERROR;
   1132 		bp->b_error = EIO;
   1133 		fdfinish(fd, bp);
   1134 	}
   1135 	fdc->sc_errors++;
   1136 }
   1137 
   1138 int
   1139 fdsize(dev)
   1140 	dev_t dev;
   1141 {
   1142 
   1143 	/* Swapping to floppies would not make sense. */
   1144 	return -1;
   1145 }
   1146 
   1147 int
   1148 fddump(dev, blkno, va, size)
   1149 	dev_t dev;
   1150 	daddr_t blkno;
   1151 	caddr_t va;
   1152 	size_t size;
   1153 {
   1154 
   1155 	/* Not implemented. */
   1156 	return ENXIO;
   1157 }
   1158 
   1159 int
   1160 fdioctl(dev, cmd, addr, flag)
   1161 	dev_t dev;
   1162 	u_long cmd;
   1163 	caddr_t addr;
   1164 	int flag;
   1165 {
   1166 	struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
   1167 	struct disklabel buffer;
   1168 	int error;
   1169 
   1170 	switch (cmd) {
   1171 	case DIOCGDINFO:
   1172 		bzero(&buffer, sizeof(buffer));
   1173 
   1174 		buffer.d_secpercyl = fd->sc_type->seccyl;
   1175 		buffer.d_type = DTYPE_FLOPPY;
   1176 		buffer.d_secsize = FDC_BSIZE;
   1177 
   1178 		if (readdisklabel(dev, fdstrategy, &buffer, NULL) != NULL)
   1179 			return EINVAL;
   1180 
   1181 		*(struct disklabel *)addr = buffer;
   1182 		return 0;
   1183 
   1184 	case DIOCWLABEL:
   1185 		if ((flag & FWRITE) == 0)
   1186 			return EBADF;
   1187 		/* XXX do something */
   1188 		return 0;
   1189 
   1190 	case DIOCWDINFO:
   1191 		if ((flag & FWRITE) == 0)
   1192 			return EBADF;
   1193 
   1194 		error = setdisklabel(&buffer, (struct disklabel *)addr, 0, NULL);
   1195 		if (error)
   1196 			return error;
   1197 
   1198 		error = writedisklabel(dev, fdstrategy, &buffer, NULL);
   1199 		return error;
   1200 
   1201 	default:
   1202 		return ENOTTY;
   1203 	}
   1204 
   1205 #ifdef DIAGNOSTIC
   1206 	panic("fdioctl: impossible");
   1207 #endif
   1208 }
   1209