1*27f37992Sdlg /* $OpenBSD: fd.c,v 1.97 2013/11/21 00:13:33 dlg Exp $ */ 24176a241Sderaadt /* $NetBSD: fd.c,v 1.90 1996/05/12 23:12:03 mycroft Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /*- 53af98d30Sdownsj * Copyright (c) 1993, 1994, 1995, 1996 Charles Hannum. 6df930be7Sderaadt * Copyright (c) 1990 The Regents of the University of California. 7df930be7Sderaadt * All rights reserved. 8df930be7Sderaadt * 9df930be7Sderaadt * This code is derived from software contributed to Berkeley by 10df930be7Sderaadt * Don Ahn. 11df930be7Sderaadt * 121921243fSdownsj * Portions Copyright (c) 1993, 1994 by 131921243fSdownsj * jc@irbs.UUCP (John Capo) 141921243fSdownsj * vak@zebub.msk.su (Serge Vakulenko) 151921243fSdownsj * ache@astral.msk.su (Andrew A. Chernov) 161921243fSdownsj * joerg_wunsch@uriah.sax.de (Joerg Wunsch) 171921243fSdownsj * 18df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 19df930be7Sderaadt * modification, are permitted provided that the following conditions 20df930be7Sderaadt * are met: 21df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 22df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 23df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 24df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 25df930be7Sderaadt * documentation and/or other materials provided with the distribution. 2629295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 27df930be7Sderaadt * may be used to endorse or promote products derived from this software 28df930be7Sderaadt * without specific prior written permission. 29df930be7Sderaadt * 30df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 34df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40df930be7Sderaadt * SUCH DAMAGE. 41df930be7Sderaadt * 42df930be7Sderaadt * @(#)fd.c 7.4 (Berkeley) 5/25/91 43df930be7Sderaadt */ 44df930be7Sderaadt 45df930be7Sderaadt #include <sys/param.h> 46df930be7Sderaadt #include <sys/systm.h> 47df930be7Sderaadt #include <sys/kernel.h> 48df930be7Sderaadt #include <sys/file.h> 49df930be7Sderaadt #include <sys/ioctl.h> 50df930be7Sderaadt #include <sys/device.h> 51df930be7Sderaadt #include <sys/disklabel.h> 52df930be7Sderaadt #include <sys/disk.h> 53df930be7Sderaadt #include <sys/buf.h> 541921243fSdownsj #include <sys/malloc.h> 55df930be7Sderaadt #include <sys/uio.h> 564b64fdbfSderaadt #include <sys/mtio.h> 574e5d1609Sderaadt #include <sys/proc.h> 58df930be7Sderaadt #include <sys/syslog.h> 59df930be7Sderaadt #include <sys/queue.h> 60190f1464Skrw #include <sys/stat.h> 617d33d8deSho #include <sys/timeout.h> 6291f4f7d8Sdlg #include <sys/dkio.h> 63df930be7Sderaadt 64df930be7Sderaadt #include <machine/cpu.h> 65b9a85264Sniklas #include <machine/bus.h> 662226ce33Sderaadt #include <machine/conf.h> 674176a241Sderaadt #include <machine/intr.h> 681921243fSdownsj #include <machine/ioctl_fd.h> 69df930be7Sderaadt 70df930be7Sderaadt #include <dev/isa/isavar.h> 71df930be7Sderaadt #include <dev/isa/isadmavar.h> 7271e39c02Sdownsj #include <dev/isa/fdreg.h> 73df930be7Sderaadt 74aff9e93dSderaadt #if defined(__i386__) || defined(__amd64__) /* XXX */ 75df930be7Sderaadt #include <i386/isa/nvram.h> 7671e39c02Sdownsj #endif 77df930be7Sderaadt 7871e39c02Sdownsj #include <dev/isa/fdlink.h> 793041a0b9Sdownsj 801921243fSdownsj /* XXX misuse a flag to identify format operation */ 811921243fSdownsj #define B_FORMAT B_XXX 821921243fSdownsj 831921243fSdownsj /* fd_type struct now in ioctl_fd.h */ 84df930be7Sderaadt 85df930be7Sderaadt /* The order of entries in the following table is important -- BEWARE! */ 86df930be7Sderaadt struct fd_type fd_types[] = { 87df930be7Sderaadt { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB" }, /* 1.44MB diskette */ 88df930be7Sderaadt { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB" }, /* 1.2 MB AT-diskettes */ 89df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */ 90df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */ 91df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB" }, /* 3.5" 720kB diskette */ 92df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x" }, /* 720kB in 1.2MB drive */ 93df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x" }, /* 360kB in 720kB drive */ 9476c8686eSdownsj { 36,2,72,2,0xff,0xaf,0x1b,0x54,80,5760,1,FDC_500KBPS,"2.88MB" }, /* 2.88MB diskette */ 9576c8686eSdownsj { 8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,"1.2MB/[1024bytes/sector]" } /* 1.2 MB japanese format */ 96df930be7Sderaadt }; 97df930be7Sderaadt 98df930be7Sderaadt /* software state, per disk (with up to 4 disks per ctlr) */ 99df930be7Sderaadt struct fd_softc { 100df930be7Sderaadt struct device sc_dev; 1013dbef52bSderaadt struct disk sc_dk; 102df930be7Sderaadt 103df930be7Sderaadt struct fd_type *sc_deftype; /* default type descriptor */ 104df930be7Sderaadt struct fd_type *sc_type; /* current type descriptor */ 105df930be7Sderaadt 1061abdbfdeSderaadt daddr_t sc_blkno; /* starting block number */ 107df930be7Sderaadt int sc_bcount; /* byte count left */ 1081921243fSdownsj int sc_opts; /* user-set options */ 109df930be7Sderaadt int sc_skip; /* bytes already transferred */ 11084d3ec93Skrw int sc_nblks; /* number of blocks currently transferring */ 11184d3ec93Skrw int sc_nbytes; /* number of bytes currently transferring */ 112df930be7Sderaadt 113df930be7Sderaadt int sc_drive; /* physical unit number */ 114df930be7Sderaadt int sc_flags; 115df930be7Sderaadt #define FD_OPEN 0x01 /* it's open */ 116df930be7Sderaadt #define FD_MOTOR 0x02 /* motor should be on */ 117df930be7Sderaadt #define FD_MOTOR_WAIT 0x04 /* motor coming up */ 118df930be7Sderaadt int sc_cylin; /* where we think the head is */ 119df930be7Sderaadt 120df930be7Sderaadt TAILQ_ENTRY(fd_softc) sc_drivechain; 121df930be7Sderaadt int sc_ops; /* I/O ops since last switch */ 1228ea83772Sdlg struct bufq sc_bufq; /* pending I/O */ 1238ea83772Sdlg struct buf *sc_bp; /* the current I/O */ 1247d33d8deSho struct timeout fd_motor_on_to; 1257d33d8deSho struct timeout fd_motor_off_to; 1267d33d8deSho struct timeout fdtimeout_to; 127df930be7Sderaadt }; 128df930be7Sderaadt 129df930be7Sderaadt /* floppy driver configuration */ 130c4071fd1Smillert int fdprobe(struct device *, void *, void *); 131c4071fd1Smillert void fdattach(struct device *, struct device *, void *); 132c06fda6dSderaadt int fdactivate(struct device *, int); 133df930be7Sderaadt 134d724e01aSderaadt struct cfattach fd_ca = { 135c06fda6dSderaadt sizeof(struct fd_softc), fdprobe, fdattach, NULL, fdactivate 136d724e01aSderaadt }; 137d724e01aSderaadt 138d724e01aSderaadt struct cfdriver fd_cd = { 139d724e01aSderaadt NULL, "fd", DV_DISK 140df930be7Sderaadt }; 141df930be7Sderaadt 142df591ed6Sderaadt int fdgetdisklabel(dev_t, struct fd_softc *, struct disklabel *, int); 143c4071fd1Smillert int fd_get_parms(struct fd_softc *); 144c4071fd1Smillert void fdstrategy(struct buf *); 145c4071fd1Smillert void fdstart(struct fd_softc *); 146c4071fd1Smillert int fdintr(struct fdc_softc *); 147df930be7Sderaadt 148c4071fd1Smillert void fd_set_motor(struct fdc_softc *fdc, int reset); 149c4071fd1Smillert void fd_motor_off(void *arg); 150c4071fd1Smillert void fd_motor_on(void *arg); 151c4071fd1Smillert void fdfinish(struct fd_softc *fd, struct buf *bp); 152c4071fd1Smillert int fdformat(dev_t, struct fd_formb *, struct proc *); 1535398df4bSderaadt static __inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t); 154c4071fd1Smillert void fdretry(struct fd_softc *); 155c4071fd1Smillert void fdtimeout(void *); 156df930be7Sderaadt 157df591ed6Sderaadt int 158190f1464Skrw fdgetdisklabel(dev_t dev, struct fd_softc *fd, struct disklabel *lp, 159190f1464Skrw int spoofonly) 160190f1464Skrw { 161190f1464Skrw bzero(lp, sizeof(struct disklabel)); 162190f1464Skrw 163190f1464Skrw lp->d_type = DTYPE_FLOPPY; 164190f1464Skrw lp->d_secsize = FD_BSIZE(fd); 165190f1464Skrw lp->d_secpercyl = fd->sc_type->seccyl; 166190f1464Skrw lp->d_nsectors = fd->sc_type->sectrac; 167190f1464Skrw lp->d_ncylinders = fd->sc_type->tracks; 168190f1464Skrw lp->d_ntracks = fd->sc_type->heads; /* Go figure... */ 169190f1464Skrw DL_SETDSIZE(lp, fd->sc_type->size); 170190f1464Skrw 171190f1464Skrw strncpy(lp->d_typename, "floppy disk", sizeof(lp->d_typename)); 172190f1464Skrw strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 173190f1464Skrw lp->d_version = 1; 174190f1464Skrw 175190f1464Skrw lp->d_magic = DISKMAGIC; 176190f1464Skrw lp->d_magic2 = DISKMAGIC; 177190f1464Skrw lp->d_checksum = dkcksum(lp); 178190f1464Skrw 179190f1464Skrw /* 180190f1464Skrw * Call the generic disklabel extraction routine. If there's 181190f1464Skrw * not a label there, fake it. 182190f1464Skrw */ 183df591ed6Sderaadt return readdisklabel(DISKLABELDEV(dev), fdstrategy, lp, spoofonly); 184190f1464Skrw } 185190f1464Skrw 186df930be7Sderaadt int 1870e9d7eafSmatthew fdprobe(struct device *parent, void *match, void *aux) 188df930be7Sderaadt { 189df930be7Sderaadt struct fdc_softc *fdc = (void *)parent; 190df930be7Sderaadt struct cfdata *cf = match; 191df930be7Sderaadt struct fdc_attach_args *fa = aux; 192df930be7Sderaadt int drive = fa->fa_drive; 193b9a85264Sniklas bus_space_tag_t iot = fdc->sc_iot; 194b9a85264Sniklas bus_space_handle_t ioh = fdc->sc_ioh; 195df930be7Sderaadt int n; 196df930be7Sderaadt 197df930be7Sderaadt if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive) 198df930be7Sderaadt return 0; 199df930be7Sderaadt /* 200df930be7Sderaadt * XXX 201df930be7Sderaadt * This is to work around some odd interactions between this driver 202df930be7Sderaadt * and SMC Ethernet cards. 203df930be7Sderaadt */ 204df930be7Sderaadt if (cf->cf_loc[0] == -1 && drive >= 2) 205df930be7Sderaadt return 0; 206df930be7Sderaadt 207e9475ca2Sdownsj /* 208e9475ca2Sdownsj * We want to keep the flags config gave us. 209e9475ca2Sdownsj */ 210e9475ca2Sdownsj fa->fa_flags = cf->cf_flags; 211e9475ca2Sdownsj 212df930be7Sderaadt /* select drive and turn on motor */ 213b9a85264Sniklas bus_space_write_1(iot, ioh, fdout, drive | FDO_FRST | FDO_MOEN(drive)); 214df930be7Sderaadt /* wait for motor to spin up */ 215df930be7Sderaadt delay(250000); 216b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_RECAL); 217b9a85264Sniklas out_fdc(iot, ioh, drive); 218df930be7Sderaadt /* wait for recalibrate */ 219df930be7Sderaadt delay(2000000); 220b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 221df930be7Sderaadt n = fdcresult(fdc); 222df930be7Sderaadt #ifdef FD_DEBUG 223df930be7Sderaadt { 224df930be7Sderaadt int i; 225df930be7Sderaadt printf("fdprobe: status"); 226df930be7Sderaadt for (i = 0; i < n; i++) 227df930be7Sderaadt printf(" %x", fdc->sc_status[i]); 228df930be7Sderaadt printf("\n"); 229df930be7Sderaadt } 230df930be7Sderaadt #endif 23159107f6cSdownsj 23259107f6cSdownsj /* turn off motor */ 23359107f6cSdownsj delay(250000); 23459107f6cSdownsj bus_space_write_1(iot, ioh, fdout, FDO_FRST); 23559107f6cSdownsj 236768789fcSrees /* flags & 0x20 forces the drive to be found even if it won't probe */ 237768789fcSrees if (!(fa->fa_flags & 0x20) && (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)) 238df930be7Sderaadt return 0; 239df930be7Sderaadt 240df930be7Sderaadt return 1; 241df930be7Sderaadt } 242df930be7Sderaadt 243df930be7Sderaadt /* 244df930be7Sderaadt * Controller is working, and drive responded. Attach it. 245df930be7Sderaadt */ 246df930be7Sderaadt void 2470e9d7eafSmatthew fdattach(struct device *parent, struct device *self, void *aux) 248df930be7Sderaadt { 249df930be7Sderaadt struct fdc_softc *fdc = (void *)parent; 250df930be7Sderaadt struct fd_softc *fd = (void *)self; 251df930be7Sderaadt struct fdc_attach_args *fa = aux; 252df930be7Sderaadt struct fd_type *type = fa->fa_deftype; 253df930be7Sderaadt int drive = fa->fa_drive; 254df930be7Sderaadt 255e9475ca2Sdownsj if (!type || (fa->fa_flags & 0x10)) { 256e9475ca2Sdownsj /* The config has overridden this. */ 257e9475ca2Sdownsj switch (fa->fa_flags & 0x07) { 2583af98d30Sdownsj case 1: /* 2.88MB */ 2593af98d30Sdownsj type = &fd_types[7]; 2603af98d30Sdownsj break; 261e9475ca2Sdownsj case 2: /* 1.44MB */ 262e9475ca2Sdownsj type = &fd_types[0]; 263e9475ca2Sdownsj break; 264e9475ca2Sdownsj case 3: /* 1.2MB */ 265e9475ca2Sdownsj type = &fd_types[1]; 266e9475ca2Sdownsj break; 267e9475ca2Sdownsj case 4: /* 720K */ 268e9475ca2Sdownsj type = &fd_types[4]; 269e9475ca2Sdownsj break; 270e9475ca2Sdownsj case 5: /* 360K */ 271e9475ca2Sdownsj type = &fd_types[3]; 272e9475ca2Sdownsj break; 27376c8686eSdownsj case 6: /* 1.2 MB japanese format */ 27476c8686eSdownsj type = &fd_types[8]; 27576c8686eSdownsj break; 276d1688987Snate #ifdef __alpha__ 277d1688987Snate default: 278d1688987Snate /* 1.44MB, how to detect others? 279d1688987Snate * idea from NetBSD -- jay@rootaction.net 280d1688987Snate */ 281d1688987Snate type = &fd_types[0]; 282d1688987Snate #endif 283e9475ca2Sdownsj } 284e9475ca2Sdownsj } 285df930be7Sderaadt 286df930be7Sderaadt if (type) 287df930be7Sderaadt printf(": %s %d cyl, %d head, %d sec\n", type->name, 288df930be7Sderaadt type->tracks, type->heads, type->sectrac); 289df930be7Sderaadt else 290df930be7Sderaadt printf(": density unknown\n"); 291df930be7Sderaadt 292df930be7Sderaadt fd->sc_cylin = -1; 293df930be7Sderaadt fd->sc_drive = drive; 294df930be7Sderaadt fd->sc_deftype = type; 2953af98d30Sdownsj fdc->sc_type[drive] = FDC_TYPE_DISK; 2963af98d30Sdownsj fdc->sc_link.fdlink.sc_fd[drive] = fd; 2973dbef52bSderaadt 2983dbef52bSderaadt /* 2993dbef52bSderaadt * Initialize and attach the disk structure. 3003dbef52bSderaadt */ 30128696e49Sjsing fd->sc_dk.dk_flags = DKF_NOLABELREAD; 3023dbef52bSderaadt fd->sc_dk.dk_name = fd->sc_dev.dv_xname; 3038ea83772Sdlg bufq_init(&fd->sc_bufq, BUFQ_DEFAULT); 3042690bc4bSjsing disk_attach(&fd->sc_dev, &fd->sc_dk); 3053dbef52bSderaadt 3067d33d8deSho /* Setup timeout structures */ 3077d33d8deSho timeout_set(&fd->fd_motor_on_to, fd_motor_on, fd); 3087d33d8deSho timeout_set(&fd->fd_motor_off_to, fd_motor_off, fd); 3097d33d8deSho timeout_set(&fd->fdtimeout_to, fdtimeout, fd); 310df930be7Sderaadt } 311df930be7Sderaadt 312c06fda6dSderaadt int 313c06fda6dSderaadt fdactivate(struct device *self, int act) 314c06fda6dSderaadt { 315c06fda6dSderaadt int rv = 0; 316c06fda6dSderaadt 317c06fda6dSderaadt switch (act) { 318c06fda6dSderaadt case DVACT_POWERDOWN: 319c06fda6dSderaadt fd_motor_off(self); 320c06fda6dSderaadt break; 321c06fda6dSderaadt } 322c06fda6dSderaadt 323c06fda6dSderaadt return (rv); 324c06fda6dSderaadt } 325c06fda6dSderaadt 326df930be7Sderaadt /* 327df930be7Sderaadt * Translate nvram type into internal data structure. Return NULL for 328df930be7Sderaadt * none/unknown/unusable. 329df930be7Sderaadt */ 330df930be7Sderaadt struct fd_type * 3310e9d7eafSmatthew fd_nvtotype(char *fdc, int nvraminfo, int drive) 332df930be7Sderaadt { 333d1688987Snate #ifdef __alpha__ 334d1688987Snate /* Alpha: assume 1.44MB, idea from NetBSD sys/dev/isa/fd.c 335d1688987Snate * -- jay@rootaction.net 336d1688987Snate */ 337d1688987Snate return &fd_types[0]; /* 1.44MB */ 338d1688987Snate #else 339df930be7Sderaadt int type; 340df930be7Sderaadt 341df930be7Sderaadt type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0; 342df930be7Sderaadt switch (type) { 343df930be7Sderaadt case NVRAM_DISKETTE_NONE: 344df930be7Sderaadt return NULL; 345df930be7Sderaadt case NVRAM_DISKETTE_12M: 346df930be7Sderaadt return &fd_types[1]; 347df930be7Sderaadt case NVRAM_DISKETTE_TYPE5: 348df930be7Sderaadt case NVRAM_DISKETTE_TYPE6: 3493af98d30Sdownsj return &fd_types[7]; 350df930be7Sderaadt case NVRAM_DISKETTE_144M: 351df930be7Sderaadt return &fd_types[0]; 352df930be7Sderaadt case NVRAM_DISKETTE_360K: 353df930be7Sderaadt return &fd_types[3]; 354df930be7Sderaadt case NVRAM_DISKETTE_720K: 355df930be7Sderaadt return &fd_types[4]; 356df930be7Sderaadt default: 357df930be7Sderaadt printf("%s: drive %d: unknown device type 0x%x\n", 358df930be7Sderaadt fdc, drive, type); 359df930be7Sderaadt return NULL; 360df930be7Sderaadt } 361d1688987Snate #endif 362df930be7Sderaadt } 363df930be7Sderaadt 3645398df4bSderaadt static __inline struct fd_type * 3650e9d7eafSmatthew fd_dev_to_type(struct fd_softc *fd, dev_t dev) 366df930be7Sderaadt { 367df930be7Sderaadt int type = FDTYPE(dev); 368df930be7Sderaadt 369df930be7Sderaadt if (type > (sizeof(fd_types) / sizeof(fd_types[0]))) 370df930be7Sderaadt return NULL; 371df930be7Sderaadt return type ? &fd_types[type - 1] : fd->sc_deftype; 372df930be7Sderaadt } 373df930be7Sderaadt 374df930be7Sderaadt void 3750e9d7eafSmatthew fdstrategy(struct buf *bp) 376df930be7Sderaadt { 3773af98d30Sdownsj struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(bp->b_dev)]; 378df930be7Sderaadt int sz; 379df930be7Sderaadt int s; 380cfe658c7Skrw int fd_bsize = FD_BSIZE(fd); 38176c8686eSdownsj int bf = fd_bsize / DEV_BSIZE; 382df930be7Sderaadt 383df930be7Sderaadt /* Valid unit, controller, and request? */ 3843af98d30Sdownsj if (bp->b_blkno < 0 || 38576c8686eSdownsj (((bp->b_blkno % bf) != 0 || 38676c8686eSdownsj (bp->b_bcount % fd_bsize) != 0) && 3871921243fSdownsj (bp->b_flags & B_FORMAT) == 0)) { 388df930be7Sderaadt bp->b_error = EINVAL; 389df930be7Sderaadt goto bad; 390df930be7Sderaadt } 391df930be7Sderaadt 392df930be7Sderaadt /* If it's a null transfer, return immediately. */ 393df930be7Sderaadt if (bp->b_bcount == 0) 394df930be7Sderaadt goto done; 395df930be7Sderaadt 39676c8686eSdownsj sz = howmany(bp->b_bcount, DEV_BSIZE); 397df930be7Sderaadt 39876c8686eSdownsj if (bp->b_blkno + sz > fd->sc_type->size * bf) { 39976c8686eSdownsj sz = fd->sc_type->size * bf - bp->b_blkno; 4006b174afeSderaadt if (sz == 0) 401df930be7Sderaadt /* If exactly at end of disk, return EOF. */ 402df930be7Sderaadt goto done; 403df930be7Sderaadt if (sz < 0) { 404df930be7Sderaadt /* If past end of disk, return EINVAL. */ 405df930be7Sderaadt bp->b_error = EINVAL; 406df930be7Sderaadt goto bad; 407df930be7Sderaadt } 408df930be7Sderaadt /* Otherwise, truncate request. */ 409df930be7Sderaadt bp->b_bcount = sz << DEV_BSHIFT; 410df930be7Sderaadt } 411df930be7Sderaadt 412*27f37992Sdlg bp->b_resid = bp->b_bcount; 413df930be7Sderaadt 414df930be7Sderaadt #ifdef FD_DEBUG 415*27f37992Sdlg printf("fdstrategy: b_blkno %lld b_bcount %d blkno %lld sz %d\n", 416*27f37992Sdlg (long long)bp->b_blkno, bp->b_bcount, 417*27f37992Sdlg (long long)fd->sc_blkno, sz); 418df930be7Sderaadt #endif 419df930be7Sderaadt 4208ea83772Sdlg /* Queue I/O */ 4218ea83772Sdlg bufq_queue(&fd->sc_bufq, bp); 4228ea83772Sdlg 423df930be7Sderaadt /* Queue transfer on drive, activate drive and controller if idle. */ 424df930be7Sderaadt s = splbio(); 4257d33d8deSho timeout_del(&fd->fd_motor_off_to); /* a good idea */ 4268ea83772Sdlg if (fd->sc_bp == NULL) 427df930be7Sderaadt fdstart(fd); 428df930be7Sderaadt #ifdef DIAGNOSTIC 429df930be7Sderaadt else { 430df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 431df930be7Sderaadt if (fdc->sc_state == DEVIDLE) { 432df930be7Sderaadt printf("fdstrategy: controller inactive\n"); 433df930be7Sderaadt fdcstart(fdc); 434df930be7Sderaadt } 435df930be7Sderaadt } 436df930be7Sderaadt #endif 437df930be7Sderaadt splx(s); 438df930be7Sderaadt return; 439df930be7Sderaadt 440df930be7Sderaadt bad: 441df930be7Sderaadt bp->b_flags |= B_ERROR; 442df930be7Sderaadt done: 443df930be7Sderaadt /* Toss transfer; we're done early. */ 4446b174afeSderaadt bp->b_resid = bp->b_bcount; 445259d6dc6Sart s = splbio(); 446df930be7Sderaadt biodone(bp); 447259d6dc6Sart splx(s); 448df930be7Sderaadt } 449df930be7Sderaadt 450df930be7Sderaadt void 4510e9d7eafSmatthew fdstart(struct fd_softc *fd) 452df930be7Sderaadt { 453df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 4549d08f8e5Smiod int active = !TAILQ_EMPTY(&fdc->sc_link.fdlink.sc_drives); 455df930be7Sderaadt 456df930be7Sderaadt /* Link into controller queue. */ 4578ea83772Sdlg fd->sc_bp = bufq_dequeue(&fd->sc_bufq); 4583af98d30Sdownsj TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 459df930be7Sderaadt 460df930be7Sderaadt /* If controller not already active, start it. */ 461df930be7Sderaadt if (!active) 462df930be7Sderaadt fdcstart(fdc); 463df930be7Sderaadt } 464df930be7Sderaadt 465df930be7Sderaadt void 4660e9d7eafSmatthew fdfinish(struct fd_softc *fd, struct buf *bp) 467df930be7Sderaadt { 468df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 469df930be7Sderaadt 470259d6dc6Sart splassert(IPL_BIO); 471259d6dc6Sart 4728ea83772Sdlg fd->sc_skip = 0; 4738ea83772Sdlg fd->sc_bp = bufq_dequeue(&fd->sc_bufq); 4748ea83772Sdlg 475df930be7Sderaadt /* 476df930be7Sderaadt * Move this drive to the end of the queue to give others a `fair' 477df930be7Sderaadt * chance. We only force a switch if N operations are completed while 478df930be7Sderaadt * another drive is waiting to be serviced, since there is a long motor 479df930be7Sderaadt * startup delay whenever we switch. 480df930be7Sderaadt */ 4819d08f8e5Smiod if (TAILQ_NEXT(fd, sc_drivechain) != NULL && ++fd->sc_ops >= 8) { 482df930be7Sderaadt fd->sc_ops = 0; 4833af98d30Sdownsj TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 4848ea83772Sdlg if (fd->sc_bp != NULL) { 4853af98d30Sdownsj TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, 4863af98d30Sdownsj sc_drivechain); 487df930be7Sderaadt } 4888ea83772Sdlg } 4893dbef52bSderaadt 490df930be7Sderaadt biodone(bp); 491df930be7Sderaadt /* turn off motor 5s from now */ 49229e86e5eSblambert timeout_add_sec(&fd->fd_motor_off_to, 5); 493df930be7Sderaadt fdc->sc_state = DEVIDLE; 494df930be7Sderaadt } 495df930be7Sderaadt 496df930be7Sderaadt int 4970e9d7eafSmatthew fdread(dev_t dev, struct uio *uio, int flags) 498df930be7Sderaadt { 49990f6ca67Smatthew return (physio(fdstrategy, dev, B_READ, minphys, uio)); 500df930be7Sderaadt } 501df930be7Sderaadt 502df930be7Sderaadt int 5030e9d7eafSmatthew fdwrite(dev_t dev, struct uio *uio, int flags) 504df930be7Sderaadt { 50590f6ca67Smatthew return (physio(fdstrategy, dev, B_WRITE, minphys, uio)); 506df930be7Sderaadt } 507df930be7Sderaadt 508df930be7Sderaadt void 5090e9d7eafSmatthew fd_set_motor(struct fdc_softc *fdc, int reset) 510df930be7Sderaadt { 511df930be7Sderaadt struct fd_softc *fd; 512df930be7Sderaadt u_char status; 513df930be7Sderaadt int n; 514df930be7Sderaadt 5159d08f8e5Smiod if ((fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives)) != NULL) 516df930be7Sderaadt status = fd->sc_drive; 517df930be7Sderaadt else 518df930be7Sderaadt status = 0; 519df930be7Sderaadt if (!reset) 520df930be7Sderaadt status |= FDO_FRST | FDO_FDMAEN; 521df930be7Sderaadt for (n = 0; n < 4; n++) 5223af98d30Sdownsj if ((fd = fdc->sc_link.fdlink.sc_fd[n]) 5233af98d30Sdownsj && (fd->sc_flags & FD_MOTOR)) 524df930be7Sderaadt status |= FDO_MOEN(n); 525b9a85264Sniklas bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, status); 526df930be7Sderaadt } 527df930be7Sderaadt 528df930be7Sderaadt void 5290e9d7eafSmatthew fd_motor_off(void *arg) 530df930be7Sderaadt { 531df930be7Sderaadt struct fd_softc *fd = arg; 532df930be7Sderaadt int s; 533df930be7Sderaadt 534df930be7Sderaadt s = splbio(); 535df930be7Sderaadt fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); 536df930be7Sderaadt fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0); 537df930be7Sderaadt splx(s); 538df930be7Sderaadt } 539df930be7Sderaadt 540df930be7Sderaadt void 5410e9d7eafSmatthew fd_motor_on(void *arg) 542df930be7Sderaadt { 543df930be7Sderaadt struct fd_softc *fd = arg; 544df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 545df930be7Sderaadt int s; 546df930be7Sderaadt 547df930be7Sderaadt s = splbio(); 548df930be7Sderaadt fd->sc_flags &= ~FD_MOTOR_WAIT; 5499d08f8e5Smiod if ((TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives) == fd) 5503af98d30Sdownsj && (fdc->sc_state == MOTORWAIT)) 5513041a0b9Sdownsj (void) fdintr(fdc); 552df930be7Sderaadt splx(s); 553df930be7Sderaadt } 554df930be7Sderaadt 555df930be7Sderaadt int 5560e9d7eafSmatthew fdopen(dev_t dev, int flags, int fmt, struct proc *p) 557df930be7Sderaadt { 558190f1464Skrw int unit, pmask; 559df930be7Sderaadt struct fd_softc *fd; 560df930be7Sderaadt struct fd_type *type; 561df930be7Sderaadt 562df930be7Sderaadt unit = FDUNIT(dev); 563d724e01aSderaadt if (unit >= fd_cd.cd_ndevs) 564df930be7Sderaadt return ENXIO; 565d724e01aSderaadt fd = fd_cd.cd_devs[unit]; 566df930be7Sderaadt if (fd == 0) 567df930be7Sderaadt return ENXIO; 568df930be7Sderaadt type = fd_dev_to_type(fd, dev); 569df930be7Sderaadt if (type == NULL) 570df930be7Sderaadt return ENXIO; 571df930be7Sderaadt 572df930be7Sderaadt if ((fd->sc_flags & FD_OPEN) != 0 && 573df930be7Sderaadt fd->sc_type != type) 574df930be7Sderaadt return EBUSY; 575df930be7Sderaadt 576df930be7Sderaadt fd->sc_type = type; 577df930be7Sderaadt fd->sc_cylin = -1; 578df930be7Sderaadt fd->sc_flags |= FD_OPEN; 579df930be7Sderaadt 580190f1464Skrw /* 581190f1464Skrw * Only update the disklabel if we're not open anywhere else. 582190f1464Skrw */ 583190f1464Skrw if (fd->sc_dk.dk_openmask == 0) 584190f1464Skrw fdgetdisklabel(dev, fd, fd->sc_dk.dk_label, 0); 585190f1464Skrw 586190f1464Skrw pmask = (1 << FDPART(dev)); 587190f1464Skrw 588190f1464Skrw switch (fmt) { 589190f1464Skrw case S_IFCHR: 590190f1464Skrw fd->sc_dk.dk_copenmask |= pmask; 591190f1464Skrw break; 592190f1464Skrw 593190f1464Skrw case S_IFBLK: 594190f1464Skrw fd->sc_dk.dk_bopenmask |= pmask; 595190f1464Skrw break; 596190f1464Skrw } 597190f1464Skrw fd->sc_dk.dk_openmask = 598190f1464Skrw fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask; 599190f1464Skrw 600df930be7Sderaadt return 0; 601df930be7Sderaadt } 602df930be7Sderaadt 603df930be7Sderaadt int 6040e9d7eafSmatthew fdclose(dev_t dev, int flags, int fmt, struct proc *p) 605df930be7Sderaadt { 606d724e01aSderaadt struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 607190f1464Skrw int pmask = (1 << FDPART(dev)); 608df930be7Sderaadt 609df930be7Sderaadt fd->sc_flags &= ~FD_OPEN; 6101921243fSdownsj fd->sc_opts &= ~FDOPT_NORETRY; 611190f1464Skrw 612190f1464Skrw switch (fmt) { 613190f1464Skrw case S_IFCHR: 614190f1464Skrw fd->sc_dk.dk_copenmask &= ~pmask; 615190f1464Skrw break; 616190f1464Skrw 617190f1464Skrw case S_IFBLK: 618190f1464Skrw fd->sc_dk.dk_bopenmask &= ~pmask; 619190f1464Skrw break; 620190f1464Skrw } 621190f1464Skrw fd->sc_dk.dk_openmask = 622190f1464Skrw fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask; 623190f1464Skrw 624190f1464Skrw return (0); 625df930be7Sderaadt } 626df930be7Sderaadt 6271abdbfdeSderaadt daddr_t 6280e9d7eafSmatthew fdsize(dev_t dev) 629df930be7Sderaadt { 6303041a0b9Sdownsj /* Swapping to floppies would not make sense. */ 6313041a0b9Sdownsj return -1; 632df930be7Sderaadt } 633df930be7Sderaadt 634df930be7Sderaadt int 6351abdbfdeSderaadt fddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 636df930be7Sderaadt { 6373041a0b9Sdownsj /* Not implemented. */ 6383041a0b9Sdownsj return ENXIO; 6393041a0b9Sdownsj } 6403041a0b9Sdownsj 6413041a0b9Sdownsj /* 6423041a0b9Sdownsj * Called from the controller. 6433041a0b9Sdownsj */ 6443041a0b9Sdownsj int 6450e9d7eafSmatthew fdintr(struct fdc_softc *fdc) 6463041a0b9Sdownsj { 647df930be7Sderaadt #define st0 fdc->sc_status[0] 648df930be7Sderaadt #define cyl fdc->sc_status[1] 649df930be7Sderaadt struct fd_softc *fd; 650df930be7Sderaadt struct buf *bp; 651b9a85264Sniklas bus_space_tag_t iot = fdc->sc_iot; 652b9a85264Sniklas bus_space_handle_t ioh = fdc->sc_ioh; 653a06af799Sdownsj bus_space_handle_t ioh_ctl = fdc->sc_ioh_ctl; 654*27f37992Sdlg int read, head, sec, i, nblks, cylin; 655df930be7Sderaadt struct fd_type *type; 6561921243fSdownsj struct fd_formb *finfo = NULL; 657cfe658c7Skrw int fd_bsize; 658df930be7Sderaadt 659df930be7Sderaadt loop: 6603041a0b9Sdownsj /* Is there a transfer to this drive? If not, deactivate drive. */ 6619d08f8e5Smiod fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives); 662df930be7Sderaadt if (fd == NULL) { 663df930be7Sderaadt fdc->sc_state = DEVIDLE; 664df930be7Sderaadt return 1; 665df930be7Sderaadt } 666cfe658c7Skrw fd_bsize = FD_BSIZE(fd); 667df930be7Sderaadt 6688ea83772Sdlg bp = fd->sc_bp; 669df930be7Sderaadt if (bp == NULL) { 670df930be7Sderaadt fd->sc_ops = 0; 6713af98d30Sdownsj TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 672df930be7Sderaadt goto loop; 673df930be7Sderaadt } 674df930be7Sderaadt 6751921243fSdownsj if (bp->b_flags & B_FORMAT) 6761921243fSdownsj finfo = (struct fd_formb *)bp->b_data; 6771921243fSdownsj 678*27f37992Sdlg cylin = ((bp->b_blkno * DEV_BSIZE) + (bp->b_bcount - bp->b_resid)) / 679*27f37992Sdlg (fd_bsize * fd->sc_type->seccyl); 680*27f37992Sdlg 681df930be7Sderaadt switch (fdc->sc_state) { 682df930be7Sderaadt case DEVIDLE: 683df930be7Sderaadt fdc->sc_errors = 0; 684df930be7Sderaadt fd->sc_skip = 0; 685df930be7Sderaadt fd->sc_bcount = bp->b_bcount; 68676c8686eSdownsj fd->sc_blkno = bp->b_blkno / (fd_bsize / DEV_BSIZE); 6877d33d8deSho timeout_del(&fd->fd_motor_off_to); 688df930be7Sderaadt if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { 689df930be7Sderaadt fdc->sc_state = MOTORWAIT; 690df930be7Sderaadt return 1; 691df930be7Sderaadt } 692df930be7Sderaadt if ((fd->sc_flags & FD_MOTOR) == 0) { 693df930be7Sderaadt /* Turn on the motor, being careful about pairing. */ 6943af98d30Sdownsj struct fd_softc *ofd = 6953af98d30Sdownsj fdc->sc_link.fdlink.sc_fd[fd->sc_drive ^ 1]; 696df930be7Sderaadt if (ofd && ofd->sc_flags & FD_MOTOR) { 6977d33d8deSho timeout_del(&ofd->fd_motor_off_to); 698df930be7Sderaadt ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); 699df930be7Sderaadt } 700df930be7Sderaadt fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; 701df930be7Sderaadt fd_set_motor(fdc, 0); 702df930be7Sderaadt fdc->sc_state = MOTORWAIT; 703df930be7Sderaadt /* Allow .25s for motor to stabilize. */ 70457f30c4fSblambert timeout_add_msec(&fd->fd_motor_on_to, 250); 705df930be7Sderaadt return 1; 706df930be7Sderaadt } 707df930be7Sderaadt /* Make sure the right drive is selected. */ 708df930be7Sderaadt fd_set_motor(fdc, 0); 709df930be7Sderaadt 71083344e41Sjsg /* FALLTHROUGH */ 711df930be7Sderaadt case DOSEEK: 712df930be7Sderaadt doseek: 713*27f37992Sdlg if (fd->sc_cylin == cylin) 714df930be7Sderaadt goto doio; 715df930be7Sderaadt 716b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */ 717b9a85264Sniklas out_fdc(iot, ioh, fd->sc_type->steprate); 718b9a85264Sniklas out_fdc(iot, ioh, 6); /* XXX head load time == 6ms */ 719df930be7Sderaadt 720b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SEEK); /* seek function */ 721b9a85264Sniklas out_fdc(iot, ioh, fd->sc_drive); /* drive number */ 722*27f37992Sdlg out_fdc(iot, ioh, cylin * fd->sc_type->step); 723df930be7Sderaadt 724df930be7Sderaadt fd->sc_cylin = -1; 725df930be7Sderaadt fdc->sc_state = SEEKWAIT; 7261eb11dafSderaadt 7271eb11dafSderaadt fd->sc_dk.dk_seek++; 7281eb11dafSderaadt disk_busy(&fd->sc_dk); 7291eb11dafSderaadt 73029e86e5eSblambert timeout_add_sec(&fd->fdtimeout_to, 4); 731df930be7Sderaadt return 1; 732df930be7Sderaadt 733df930be7Sderaadt case DOIO: 734df930be7Sderaadt doio: 735df930be7Sderaadt type = fd->sc_type; 7361921243fSdownsj if (finfo) 7371921243fSdownsj fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) - 7381921243fSdownsj (char *)finfo; 739df930be7Sderaadt sec = fd->sc_blkno % type->seccyl; 740df930be7Sderaadt nblks = type->seccyl - sec; 74176c8686eSdownsj nblks = min(nblks, fd->sc_bcount / fd_bsize); 74276c8686eSdownsj nblks = min(nblks, FDC_MAXIOSIZE / fd_bsize); 743df930be7Sderaadt fd->sc_nblks = nblks; 74476c8686eSdownsj fd->sc_nbytes = finfo ? bp->b_bcount : nblks * fd_bsize; 745df930be7Sderaadt head = sec / type->sectrac; 746df930be7Sderaadt sec -= head * type->sectrac; 747df930be7Sderaadt #ifdef DIAGNOSTIC 748df930be7Sderaadt {int block; 749df930be7Sderaadt block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec; 750df930be7Sderaadt if (block != fd->sc_blkno) { 751bd8c82ffSjasper panic("fdintr: block %d != blkno %llu", block, fd->sc_blkno); 752df930be7Sderaadt }} 753df930be7Sderaadt #endif 754fa7cc844Sniklas read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; 755aee4fbd6Sniklas isadma_start(bp->b_data + fd->sc_skip, fd->sc_nbytes, 756aee4fbd6Sniklas fdc->sc_drq, read); 757a06af799Sdownsj bus_space_write_1(iot, ioh_ctl, fdctl, type->rate); 758df930be7Sderaadt #ifdef FD_DEBUG 7593041a0b9Sdownsj printf("fdintr: %s drive %d track %d head %d sec %d nblks %d\n", 760df930be7Sderaadt read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head, 761df930be7Sderaadt sec, nblks); 762df930be7Sderaadt #endif 7631921243fSdownsj if (finfo) { 7641921243fSdownsj /* formatting */ 765b9a85264Sniklas if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) { 7661921243fSdownsj fdc->sc_errors = 4; 7673041a0b9Sdownsj fdretry(fd); 7681921243fSdownsj goto loop; 7691921243fSdownsj } 770b9a85264Sniklas out_fdc(iot, ioh, (head << 2) | fd->sc_drive); 771b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_secshift); 772b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_nsecs); 773b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_gaplen); 774b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_fillbyte); 7751921243fSdownsj } else { 776df930be7Sderaadt if (read) 777b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_READ); /* READ */ 778df930be7Sderaadt else 779b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_WRITE);/* WRITE */ 780b9a85264Sniklas out_fdc(iot, ioh, (head << 2) | fd->sc_drive); 781b9a85264Sniklas out_fdc(iot, ioh, fd->sc_cylin); /* track */ 782b9a85264Sniklas out_fdc(iot, ioh, head); 783b9a85264Sniklas out_fdc(iot, ioh, sec + 1); /* sec +1 */ 784b9a85264Sniklas out_fdc(iot, ioh, type->secsize); /* sec size */ 785b9a85264Sniklas out_fdc(iot, ioh, type->sectrac); /* secs/track */ 786b9a85264Sniklas out_fdc(iot, ioh, type->gap1); /* gap1 size */ 787b9a85264Sniklas out_fdc(iot, ioh, type->datalen); /* data len */ 7881921243fSdownsj } 789df930be7Sderaadt fdc->sc_state = IOCOMPLETE; 7901eb11dafSderaadt 7911eb11dafSderaadt disk_busy(&fd->sc_dk); 7921eb11dafSderaadt 793df930be7Sderaadt /* allow 2 seconds for operation */ 79429e86e5eSblambert timeout_add_sec(&fd->fdtimeout_to, 2); 795df930be7Sderaadt return 1; /* will return later */ 796df930be7Sderaadt 797df930be7Sderaadt case SEEKWAIT: 7987d33d8deSho timeout_del(&fd->fdtimeout_to); 799df930be7Sderaadt fdc->sc_state = SEEKCOMPLETE; 800df930be7Sderaadt /* allow 1/50 second for heads to settle */ 80157f30c4fSblambert timeout_add_msec(&fdc->fdcpseudointr_to, 20); 802df930be7Sderaadt return 1; 803df930be7Sderaadt 804df930be7Sderaadt case SEEKCOMPLETE: 80535fa4c39Stedu disk_unbusy(&fd->sc_dk, 0, 0); /* no data on seek */ 8061eb11dafSderaadt 807df930be7Sderaadt /* Make sure seek really happened. */ 808b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 809df930be7Sderaadt if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || 810*27f37992Sdlg cyl != cylin * fd->sc_type->step) { 811df930be7Sderaadt #ifdef FD_DEBUG 812df930be7Sderaadt fdcstatus(&fd->sc_dev, 2, "seek failed"); 813df930be7Sderaadt #endif 8143041a0b9Sdownsj fdretry(fd); 815df930be7Sderaadt goto loop; 816df930be7Sderaadt } 817*27f37992Sdlg fd->sc_cylin = cylin; 818df930be7Sderaadt goto doio; 819df930be7Sderaadt 820df930be7Sderaadt case IOTIMEDOUT: 821aee4fbd6Sniklas isadma_abort(fdc->sc_drq); 822df930be7Sderaadt case SEEKTIMEDOUT: 823df930be7Sderaadt case RECALTIMEDOUT: 824df930be7Sderaadt case RESETTIMEDOUT: 8253041a0b9Sdownsj fdretry(fd); 826df930be7Sderaadt goto loop; 827df930be7Sderaadt 828df930be7Sderaadt case IOCOMPLETE: /* IO DONE, post-analyze */ 8297d33d8deSho timeout_del(&fd->fdtimeout_to); 8301eb11dafSderaadt 83135fa4c39Stedu disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid), 83235fa4c39Stedu (bp->b_flags & B_READ)); 8331eb11dafSderaadt 834df930be7Sderaadt if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) { 835aee4fbd6Sniklas isadma_abort(fdc->sc_drq); 836df930be7Sderaadt #ifdef FD_DEBUG 837df930be7Sderaadt fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ? 838df930be7Sderaadt "read failed" : "write failed"); 8398234ed2eSjasper printf("blkno %lld nblks %d\n", 840bb4f4faeSkrw (long long)fd->sc_blkno, fd->sc_nblks); 841df930be7Sderaadt #endif 8423041a0b9Sdownsj fdretry(fd); 843df930be7Sderaadt goto loop; 844df930be7Sderaadt } 845fa7cc844Sniklas read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; 846aee4fbd6Sniklas isadma_done(fdc->sc_drq); 847df930be7Sderaadt if (fdc->sc_errors) { 848df930be7Sderaadt diskerr(bp, "fd", "soft error", LOG_PRINTF, 84976c8686eSdownsj fd->sc_skip / fd_bsize, (struct disklabel *)NULL); 850df930be7Sderaadt printf("\n"); 851df930be7Sderaadt fdc->sc_errors = 0; 852df930be7Sderaadt } 853*27f37992Sdlg 854df930be7Sderaadt fd->sc_blkno += fd->sc_nblks; 855df930be7Sderaadt fd->sc_skip += fd->sc_nbytes; 856df930be7Sderaadt fd->sc_bcount -= fd->sc_nbytes; 857*27f37992Sdlg bp->b_resid -= fd->sc_nbytes; 8581921243fSdownsj if (!finfo && fd->sc_bcount > 0) { 859*27f37992Sdlg cylin = fd->sc_blkno / fd->sc_type->seccyl; 860df930be7Sderaadt goto doseek; 861df930be7Sderaadt } 862df930be7Sderaadt fdfinish(fd, bp); 863df930be7Sderaadt goto loop; 864df930be7Sderaadt 865df930be7Sderaadt case DORESET: 866df930be7Sderaadt /* try a reset, keep motor on */ 867df930be7Sderaadt fd_set_motor(fdc, 1); 868df930be7Sderaadt delay(100); 869df930be7Sderaadt fd_set_motor(fdc, 0); 870df930be7Sderaadt fdc->sc_state = RESETCOMPLETE; 87157f30c4fSblambert timeout_add_msec(&fd->fdtimeout_to, 500); 872df930be7Sderaadt return 1; /* will return later */ 873df930be7Sderaadt 874df930be7Sderaadt case RESETCOMPLETE: 8757d33d8deSho timeout_del(&fd->fdtimeout_to); 876df930be7Sderaadt /* clear the controller output buffer */ 877df930be7Sderaadt for (i = 0; i < 4; i++) { 878b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 879df930be7Sderaadt (void) fdcresult(fdc); 880df930be7Sderaadt } 881df930be7Sderaadt 88283344e41Sjsg /* FALLTHROUGH */ 883df930be7Sderaadt case DORECAL: 884b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_RECAL); /* recal function */ 885b9a85264Sniklas out_fdc(iot, ioh, fd->sc_drive); 886df930be7Sderaadt fdc->sc_state = RECALWAIT; 88729e86e5eSblambert timeout_add_sec(&fd->fdtimeout_to, 5); 888df930be7Sderaadt return 1; /* will return later */ 889df930be7Sderaadt 890df930be7Sderaadt case RECALWAIT: 8917d33d8deSho timeout_del(&fd->fdtimeout_to); 892df930be7Sderaadt fdc->sc_state = RECALCOMPLETE; 893df930be7Sderaadt /* allow 1/30 second for heads to settle */ 8947d33d8deSho timeout_add(&fdc->fdcpseudointr_to, hz / 30); 895df930be7Sderaadt return 1; /* will return later */ 896df930be7Sderaadt 897df930be7Sderaadt case RECALCOMPLETE: 898b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 899df930be7Sderaadt if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { 900df930be7Sderaadt #ifdef FD_DEBUG 901df930be7Sderaadt fdcstatus(&fd->sc_dev, 2, "recalibrate failed"); 902df930be7Sderaadt #endif 9033041a0b9Sdownsj fdretry(fd); 904df930be7Sderaadt goto loop; 905df930be7Sderaadt } 906df930be7Sderaadt fd->sc_cylin = 0; 907df930be7Sderaadt goto doseek; 908df930be7Sderaadt 909df930be7Sderaadt case MOTORWAIT: 910df930be7Sderaadt if (fd->sc_flags & FD_MOTOR_WAIT) 911df930be7Sderaadt return 1; /* time's not up yet */ 912df930be7Sderaadt goto doseek; 913df930be7Sderaadt 914df930be7Sderaadt default: 915df930be7Sderaadt fdcstatus(&fd->sc_dev, 0, "stray interrupt"); 916df930be7Sderaadt return 1; 917df930be7Sderaadt } 918df930be7Sderaadt #ifdef DIAGNOSTIC 9193041a0b9Sdownsj panic("fdintr: impossible"); 920df930be7Sderaadt #endif 921df930be7Sderaadt #undef st0 922df930be7Sderaadt #undef cyl 923df930be7Sderaadt } 924df930be7Sderaadt 925df930be7Sderaadt void 9260e9d7eafSmatthew fdtimeout(void *arg) 927df930be7Sderaadt { 9283041a0b9Sdownsj struct fd_softc *fd = arg; 9293041a0b9Sdownsj struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 9303041a0b9Sdownsj int s; 931df930be7Sderaadt 9323041a0b9Sdownsj s = splbio(); 9333041a0b9Sdownsj #ifdef DEBUG 9343041a0b9Sdownsj log(LOG_ERR,"fdtimeout: state %d\n", fdc->sc_state); 9353041a0b9Sdownsj #endif 9363041a0b9Sdownsj fdcstatus(&fd->sc_dev, 0, "timeout"); 9373041a0b9Sdownsj 9388ea83772Sdlg if (fd->sc_bp != NULL) 9393041a0b9Sdownsj fdc->sc_state++; 9403041a0b9Sdownsj else 9413041a0b9Sdownsj fdc->sc_state = DEVIDLE; 9423041a0b9Sdownsj 9433041a0b9Sdownsj (void) fdintr(fdc); 9443041a0b9Sdownsj splx(s); 9453041a0b9Sdownsj } 9463041a0b9Sdownsj 9473041a0b9Sdownsj void 9480e9d7eafSmatthew fdretry(struct fd_softc *fd) 9493041a0b9Sdownsj { 9503041a0b9Sdownsj struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 9518ea83772Sdlg struct buf *bp = fd->sc_bp; 952df930be7Sderaadt 9531921243fSdownsj if (fd->sc_opts & FDOPT_NORETRY) 9541921243fSdownsj goto fail; 955df930be7Sderaadt switch (fdc->sc_errors) { 956df930be7Sderaadt case 0: 957df930be7Sderaadt /* try again */ 958da3b7780Shannken fdc->sc_state = DOSEEK; 959df930be7Sderaadt break; 960df930be7Sderaadt 961df930be7Sderaadt case 1: case 2: case 3: 962df930be7Sderaadt /* didn't work; try recalibrating */ 963df930be7Sderaadt fdc->sc_state = DORECAL; 964df930be7Sderaadt break; 965df930be7Sderaadt 966df930be7Sderaadt case 4: 967df930be7Sderaadt /* still no go; reset the bastard */ 968df930be7Sderaadt fdc->sc_state = DORESET; 969df930be7Sderaadt break; 970df930be7Sderaadt 971df930be7Sderaadt default: 9721921243fSdownsj fail: 973df930be7Sderaadt diskerr(bp, "fd", "hard error", LOG_PRINTF, 974cfe658c7Skrw fd->sc_skip / FD_BSIZE(fd), (struct disklabel *)NULL); 975df930be7Sderaadt printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n", 976df930be7Sderaadt fdc->sc_status[0], NE7_ST0BITS, 977df930be7Sderaadt fdc->sc_status[1], NE7_ST1BITS, 978df930be7Sderaadt fdc->sc_status[2], NE7_ST2BITS, 979df930be7Sderaadt fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); 980df930be7Sderaadt 981df930be7Sderaadt bp->b_flags |= B_ERROR; 982df930be7Sderaadt bp->b_error = EIO; 983*27f37992Sdlg bp->b_resid = bp->b_bcount; 984df930be7Sderaadt fdfinish(fd, bp); 985df930be7Sderaadt } 986df930be7Sderaadt fdc->sc_errors++; 987df930be7Sderaadt } 988df930be7Sderaadt 989df930be7Sderaadt int 9900e9d7eafSmatthew fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 991df930be7Sderaadt { 992d724e01aSderaadt struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 993a08af95aSkrw struct disklabel *lp; 994df930be7Sderaadt int error; 995df930be7Sderaadt 996df930be7Sderaadt switch (cmd) { 9974b64fdbfSderaadt case MTIOCTOP: 9984b64fdbfSderaadt if (((struct mtop *)addr)->mt_op != MTOFFL) 9994b64fdbfSderaadt return EIO; 10004b64fdbfSderaadt return (0); 1001e6586b6fSkrw 1002e6586b6fSkrw case DIOCRLDINFO: 1003e6586b6fSkrw lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK); 1004190f1464Skrw fdgetdisklabel(dev, fd, lp, 0); 1005e6586b6fSkrw bcopy(lp, fd->sc_dk.dk_label, sizeof(*lp)); 1006e6586b6fSkrw free(lp, M_TEMP); 1007e6586b6fSkrw return 0; 1008e6586b6fSkrw 1009e6586b6fSkrw case DIOCGPDINFO: 1010e6586b6fSkrw fdgetdisklabel(dev, fd, (struct disklabel *)addr, 1); 1011e6586b6fSkrw return 0; 1012e6586b6fSkrw 1013e6586b6fSkrw case DIOCGDINFO: 1014e6586b6fSkrw *(struct disklabel *)addr = *(fd->sc_dk.dk_label); 1015e6586b6fSkrw return 0; 1016e6586b6fSkrw 1017e6586b6fSkrw case DIOCGPART: 1018e6586b6fSkrw ((struct partinfo *)addr)->disklab = fd->sc_dk.dk_label; 1019e6586b6fSkrw ((struct partinfo *)addr)->part = 1020e6586b6fSkrw &fd->sc_dk.dk_label->d_partitions[FDPART(dev)]; 1021df930be7Sderaadt return 0; 1022df930be7Sderaadt 1023df930be7Sderaadt case DIOCWDINFO: 10242e4cb731Skrw case DIOCSDINFO: 1025df930be7Sderaadt if ((flag & FWRITE) == 0) 1026df930be7Sderaadt return EBADF; 1027df930be7Sderaadt 10282e4cb731Skrw error = setdisklabel(fd->sc_dk.dk_label, 10292e4cb731Skrw (struct disklabel *)addr, 0); 10302e4cb731Skrw if (error == 0) { 10312e4cb731Skrw if (cmd == DIOCWDINFO) 10322e4cb731Skrw error = writedisklabel(DISKLABELDEV(dev), 10332e4cb731Skrw fdstrategy, fd->sc_dk.dk_label); 10342e4cb731Skrw } 1035df930be7Sderaadt return error; 1036df930be7Sderaadt 10371921243fSdownsj case FD_FORM: 10381921243fSdownsj if((flag & FWRITE) == 0) 10391921243fSdownsj return EBADF; /* must be opened for writing */ 10401921243fSdownsj else if(((struct fd_formb *)addr)->format_version != 10411921243fSdownsj FD_FORMAT_VERSION) 10421921243fSdownsj return EINVAL; /* wrong version of formatting prog */ 10431921243fSdownsj else 10441921243fSdownsj return fdformat(dev, (struct fd_formb *)addr, p); 10451921243fSdownsj break; 10461921243fSdownsj 10471921243fSdownsj case FD_GTYPE: /* get drive type */ 10481921243fSdownsj *(struct fd_type *)addr = *fd->sc_type; 10491921243fSdownsj return 0; 10501921243fSdownsj 10511921243fSdownsj case FD_GOPTS: /* get drive options */ 10521921243fSdownsj *(int *)addr = fd->sc_opts; 10531921243fSdownsj return 0; 10541921243fSdownsj 10551921243fSdownsj case FD_SOPTS: /* set drive options */ 10561921243fSdownsj fd->sc_opts = *(int *)addr; 10571921243fSdownsj return 0; 10581921243fSdownsj 1059df930be7Sderaadt default: 1060df930be7Sderaadt return ENOTTY; 1061df930be7Sderaadt } 1062df930be7Sderaadt 1063df930be7Sderaadt #ifdef DIAGNOSTIC 1064df930be7Sderaadt panic("fdioctl: impossible"); 1065df930be7Sderaadt #endif 1066df930be7Sderaadt } 10671921243fSdownsj 10681921243fSdownsj int 10690e9d7eafSmatthew fdformat(dev_t dev, struct fd_formb *finfo, struct proc *p) 10701921243fSdownsj { 1071fed3d83dSmiod int rv = 0; 10721921243fSdownsj struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 10731921243fSdownsj struct fd_type *type = fd->sc_type; 10741921243fSdownsj struct buf *bp; 1075cfe658c7Skrw int fd_bsize = FD_BSIZE(fd); 10761921243fSdownsj 10771921243fSdownsj /* set up a buffer header for fdstrategy() */ 10782b585ff0Skrw bp = malloc(sizeof(*bp), M_TEMP, M_NOWAIT | M_ZERO); 1079fed3d83dSmiod if (bp == NULL) 10801921243fSdownsj return ENOBUFS; 1081fed3d83dSmiod 1082c10f2b41Sderaadt bp->b_flags = B_BUSY | B_PHYS | B_FORMAT | B_RAW; 10831921243fSdownsj bp->b_proc = p; 10841921243fSdownsj bp->b_dev = dev; 10851921243fSdownsj 10861921243fSdownsj /* 10871921243fSdownsj * calculate a fake blkno, so fdstrategy() would initiate a 10881921243fSdownsj * seek to the requested cylinder 10891921243fSdownsj */ 10901921243fSdownsj bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads) 109176c8686eSdownsj + finfo->head * type->sectrac) * fd_bsize / DEV_BSIZE; 10921921243fSdownsj 10931921243fSdownsj bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 10941921243fSdownsj bp->b_data = (caddr_t)finfo; 10951921243fSdownsj 10961921243fSdownsj #ifdef DEBUG 10971921243fSdownsj printf("fdformat: blkno %x count %x\n", bp->b_blkno, bp->b_bcount); 10981921243fSdownsj #endif 10991921243fSdownsj 11001921243fSdownsj /* now do the format */ 11011921243fSdownsj fdstrategy(bp); 11021921243fSdownsj 11031921243fSdownsj /* ...and wait for it to complete */ 1104fed3d83dSmiod rv = biowait(bp); 11051921243fSdownsj free(bp, M_TEMP); 1106fed3d83dSmiod return (rv); 11071921243fSdownsj } 1108