1*29295d1cSmillert /* $OpenBSD: fd.c,v 1.47 2003/06/02 23:28:02 millert 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. 26*29295d1cSmillert * 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/dkstat.h> 53df930be7Sderaadt #include <sys/disk.h> 54df930be7Sderaadt #include <sys/buf.h> 551921243fSdownsj #include <sys/malloc.h> 56df930be7Sderaadt #include <sys/uio.h> 574b64fdbfSderaadt #include <sys/mtio.h> 584e5d1609Sderaadt #include <sys/proc.h> 59df930be7Sderaadt #include <sys/syslog.h> 60df930be7Sderaadt #include <sys/queue.h> 617d33d8deSho #include <sys/timeout.h> 62df930be7Sderaadt 63df930be7Sderaadt #include <machine/cpu.h> 64b9a85264Sniklas #include <machine/bus.h> 652226ce33Sderaadt #include <machine/conf.h> 664176a241Sderaadt #include <machine/intr.h> 671921243fSdownsj #include <machine/ioctl_fd.h> 68df930be7Sderaadt 69df930be7Sderaadt #include <dev/isa/isavar.h> 70df930be7Sderaadt #include <dev/isa/isadmavar.h> 7171e39c02Sdownsj #include <dev/isa/fdreg.h> 72df930be7Sderaadt 7371e39c02Sdownsj #if defined(i386) 74df930be7Sderaadt #include <i386/isa/nvram.h> 7571e39c02Sdownsj #endif 76df930be7Sderaadt 7771e39c02Sdownsj #include <dev/isa/fdlink.h> 783041a0b9Sdownsj 791921243fSdownsj /* XXX misuse a flag to identify format operation */ 801921243fSdownsj #define B_FORMAT B_XXX 811921243fSdownsj 82df930be7Sderaadt #define b_cylin b_resid 83df930be7Sderaadt 841921243fSdownsj /* fd_type struct now in ioctl_fd.h */ 85df930be7Sderaadt 86df930be7Sderaadt /* The order of entries in the following table is important -- BEWARE! */ 87df930be7Sderaadt struct fd_type fd_types[] = { 88df930be7Sderaadt { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB" }, /* 1.44MB diskette */ 89df930be7Sderaadt { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB" }, /* 1.2 MB AT-diskettes */ 90df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */ 91df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */ 92df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB" }, /* 3.5" 720kB diskette */ 93df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x" }, /* 720kB in 1.2MB drive */ 94df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x" }, /* 360kB in 720kB drive */ 9576c8686eSdownsj { 36,2,72,2,0xff,0xaf,0x1b,0x54,80,5760,1,FDC_500KBPS,"2.88MB" }, /* 2.88MB diskette */ 9676c8686eSdownsj { 8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,"1.2MB/[1024bytes/sector]" } /* 1.2 MB japanese format */ 97df930be7Sderaadt }; 98df930be7Sderaadt 99df930be7Sderaadt /* software state, per disk (with up to 4 disks per ctlr) */ 100df930be7Sderaadt struct fd_softc { 101df930be7Sderaadt struct device sc_dev; 1023dbef52bSderaadt struct disk sc_dk; 103df930be7Sderaadt 104df930be7Sderaadt struct fd_type *sc_deftype; /* default type descriptor */ 105df930be7Sderaadt struct fd_type *sc_type; /* current type descriptor */ 106df930be7Sderaadt 107df930be7Sderaadt daddr_t sc_blkno; /* starting block number */ 108df930be7Sderaadt int sc_bcount; /* byte count left */ 1091921243fSdownsj int sc_opts; /* user-set options */ 110df930be7Sderaadt int sc_skip; /* bytes already transferred */ 111df930be7Sderaadt int sc_nblks; /* number of blocks currently tranferring */ 112df930be7Sderaadt int sc_nbytes; /* number of bytes currently tranferring */ 113df930be7Sderaadt 114df930be7Sderaadt int sc_drive; /* physical unit number */ 115df930be7Sderaadt int sc_flags; 116df930be7Sderaadt #define FD_OPEN 0x01 /* it's open */ 117df930be7Sderaadt #define FD_MOTOR 0x02 /* motor should be on */ 118df930be7Sderaadt #define FD_MOTOR_WAIT 0x04 /* motor coming up */ 119df930be7Sderaadt int sc_cylin; /* where we think the head is */ 120df930be7Sderaadt 12152a6aad1Sderaadt void *sc_sdhook; /* saved shutdown hook for drive. */ 12252a6aad1Sderaadt 123df930be7Sderaadt TAILQ_ENTRY(fd_softc) sc_drivechain; 124df930be7Sderaadt int sc_ops; /* I/O ops since last switch */ 125df930be7Sderaadt struct buf sc_q; /* head of buf chain */ 1267d33d8deSho struct timeout fd_motor_on_to; 1277d33d8deSho struct timeout fd_motor_off_to; 1287d33d8deSho struct timeout fdtimeout_to; 129df930be7Sderaadt }; 130df930be7Sderaadt 131df930be7Sderaadt /* floppy driver configuration */ 132c4071fd1Smillert int fdprobe(struct device *, void *, void *); 133c4071fd1Smillert void fdattach(struct device *, struct device *, void *); 134df930be7Sderaadt 135d724e01aSderaadt struct cfattach fd_ca = { 136d724e01aSderaadt sizeof(struct fd_softc), fdprobe, fdattach 137d724e01aSderaadt }; 138d724e01aSderaadt 139d724e01aSderaadt struct cfdriver fd_cd = { 140d724e01aSderaadt NULL, "fd", DV_DISK 141df930be7Sderaadt }; 142df930be7Sderaadt 143c4071fd1Smillert void fdgetdisklabel(struct fd_softc *); 144c4071fd1Smillert int fd_get_parms(struct fd_softc *); 145c4071fd1Smillert void fdstrategy(struct buf *); 146c4071fd1Smillert void fdstart(struct fd_softc *); 147c4071fd1Smillert int fdintr(struct fdc_softc *); 148df930be7Sderaadt 149df930be7Sderaadt struct dkdriver fddkdriver = { fdstrategy }; 150df930be7Sderaadt 151c4071fd1Smillert void fd_set_motor(struct fdc_softc *fdc, int reset); 152c4071fd1Smillert void fd_motor_off(void *arg); 153c4071fd1Smillert void fd_motor_on(void *arg); 154c4071fd1Smillert void fdfinish(struct fd_softc *fd, struct buf *bp); 155c4071fd1Smillert int fdformat(dev_t, struct fd_formb *, struct proc *); 156c4071fd1Smillert __inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t); 157c4071fd1Smillert void fdretry(struct fd_softc *); 158c4071fd1Smillert void fdtimeout(void *); 159df930be7Sderaadt 160df930be7Sderaadt int 161df930be7Sderaadt fdprobe(parent, match, aux) 162df930be7Sderaadt struct device *parent; 163df930be7Sderaadt void *match, *aux; 164df930be7Sderaadt { 165df930be7Sderaadt struct fdc_softc *fdc = (void *)parent; 166df930be7Sderaadt struct cfdata *cf = match; 167df930be7Sderaadt struct fdc_attach_args *fa = aux; 168df930be7Sderaadt int drive = fa->fa_drive; 169b9a85264Sniklas bus_space_tag_t iot = fdc->sc_iot; 170b9a85264Sniklas bus_space_handle_t ioh = fdc->sc_ioh; 171df930be7Sderaadt int n; 172df930be7Sderaadt 173df930be7Sderaadt if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive) 174df930be7Sderaadt return 0; 175df930be7Sderaadt /* 176df930be7Sderaadt * XXX 177df930be7Sderaadt * This is to work around some odd interactions between this driver 178df930be7Sderaadt * and SMC Ethernet cards. 179df930be7Sderaadt */ 180df930be7Sderaadt if (cf->cf_loc[0] == -1 && drive >= 2) 181df930be7Sderaadt return 0; 182df930be7Sderaadt 183e9475ca2Sdownsj /* 184e9475ca2Sdownsj * We want to keep the flags config gave us. 185e9475ca2Sdownsj */ 186e9475ca2Sdownsj fa->fa_flags = cf->cf_flags; 187e9475ca2Sdownsj 188df930be7Sderaadt /* select drive and turn on motor */ 189b9a85264Sniklas bus_space_write_1(iot, ioh, fdout, drive | FDO_FRST | FDO_MOEN(drive)); 190df930be7Sderaadt /* wait for motor to spin up */ 191df930be7Sderaadt delay(250000); 192b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_RECAL); 193b9a85264Sniklas out_fdc(iot, ioh, drive); 194df930be7Sderaadt /* wait for recalibrate */ 195df930be7Sderaadt delay(2000000); 196b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 197df930be7Sderaadt n = fdcresult(fdc); 198df930be7Sderaadt #ifdef FD_DEBUG 199df930be7Sderaadt { 200df930be7Sderaadt int i; 201df930be7Sderaadt printf("fdprobe: status"); 202df930be7Sderaadt for (i = 0; i < n; i++) 203df930be7Sderaadt printf(" %x", fdc->sc_status[i]); 204df930be7Sderaadt printf("\n"); 205df930be7Sderaadt } 206df930be7Sderaadt #endif 20759107f6cSdownsj 20859107f6cSdownsj /* turn off motor */ 20959107f6cSdownsj delay(250000); 21059107f6cSdownsj bus_space_write_1(iot, ioh, fdout, FDO_FRST); 21159107f6cSdownsj 212768789fcSrees /* flags & 0x20 forces the drive to be found even if it won't probe */ 213768789fcSrees if (!(fa->fa_flags & 0x20) && (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)) 214df930be7Sderaadt return 0; 215df930be7Sderaadt 216df930be7Sderaadt return 1; 217df930be7Sderaadt } 218df930be7Sderaadt 219df930be7Sderaadt /* 220df930be7Sderaadt * Controller is working, and drive responded. Attach it. 221df930be7Sderaadt */ 222df930be7Sderaadt void 223df930be7Sderaadt fdattach(parent, self, aux) 224df930be7Sderaadt struct device *parent, *self; 225df930be7Sderaadt void *aux; 226df930be7Sderaadt { 227df930be7Sderaadt struct fdc_softc *fdc = (void *)parent; 228df930be7Sderaadt struct fd_softc *fd = (void *)self; 229df930be7Sderaadt struct fdc_attach_args *fa = aux; 230df930be7Sderaadt struct fd_type *type = fa->fa_deftype; 231df930be7Sderaadt int drive = fa->fa_drive; 232df930be7Sderaadt 233e9475ca2Sdownsj if (!type || (fa->fa_flags & 0x10)) { 234e9475ca2Sdownsj /* The config has overridden this. */ 235e9475ca2Sdownsj switch (fa->fa_flags & 0x07) { 2363af98d30Sdownsj case 1: /* 2.88MB */ 2373af98d30Sdownsj type = &fd_types[7]; 2383af98d30Sdownsj break; 239e9475ca2Sdownsj case 2: /* 1.44MB */ 240e9475ca2Sdownsj type = &fd_types[0]; 241e9475ca2Sdownsj break; 242e9475ca2Sdownsj case 3: /* 1.2MB */ 243e9475ca2Sdownsj type = &fd_types[1]; 244e9475ca2Sdownsj break; 245e9475ca2Sdownsj case 4: /* 720K */ 246e9475ca2Sdownsj type = &fd_types[4]; 247e9475ca2Sdownsj break; 248e9475ca2Sdownsj case 5: /* 360K */ 249e9475ca2Sdownsj type = &fd_types[3]; 250e9475ca2Sdownsj break; 25176c8686eSdownsj case 6: /* 1.2 MB japanese format */ 25276c8686eSdownsj type = &fd_types[8]; 25376c8686eSdownsj break; 254d1688987Snate #ifdef __alpha__ 255d1688987Snate default: 256d1688987Snate /* 1.44MB, how to detect others? 257d1688987Snate * idea from NetBSD -- jay@rootaction.net 258d1688987Snate */ 259d1688987Snate type = &fd_types[0]; 260d1688987Snate #endif 261e9475ca2Sdownsj } 262e9475ca2Sdownsj } 263df930be7Sderaadt 264df930be7Sderaadt if (type) 265df930be7Sderaadt printf(": %s %d cyl, %d head, %d sec\n", type->name, 266df930be7Sderaadt type->tracks, type->heads, type->sectrac); 267df930be7Sderaadt else 268df930be7Sderaadt printf(": density unknown\n"); 269df930be7Sderaadt 270df930be7Sderaadt fd->sc_cylin = -1; 271df930be7Sderaadt fd->sc_drive = drive; 272df930be7Sderaadt fd->sc_deftype = type; 2733af98d30Sdownsj fdc->sc_type[drive] = FDC_TYPE_DISK; 2743af98d30Sdownsj fdc->sc_link.fdlink.sc_fd[drive] = fd; 2753dbef52bSderaadt 2763dbef52bSderaadt /* 2773dbef52bSderaadt * Initialize and attach the disk structure. 2783dbef52bSderaadt */ 2793dbef52bSderaadt fd->sc_dk.dk_name = fd->sc_dev.dv_xname; 280df930be7Sderaadt fd->sc_dk.dk_driver = &fddkdriver; 2813dbef52bSderaadt disk_attach(&fd->sc_dk); 2823dbef52bSderaadt 283df930be7Sderaadt dk_establish(&fd->sc_dk, &fd->sc_dev); 28452a6aad1Sderaadt /* Needed to power off if the motor is on when we halt. */ 28552a6aad1Sderaadt fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd); 2867d33d8deSho 2877d33d8deSho /* Setup timeout structures */ 2887d33d8deSho timeout_set(&fd->fd_motor_on_to, fd_motor_on, fd); 2897d33d8deSho timeout_set(&fd->fd_motor_off_to, fd_motor_off, fd); 2907d33d8deSho timeout_set(&fd->fdtimeout_to, fdtimeout, fd); 291df930be7Sderaadt } 292df930be7Sderaadt 293df930be7Sderaadt /* 294df930be7Sderaadt * Translate nvram type into internal data structure. Return NULL for 295df930be7Sderaadt * none/unknown/unusable. 296df930be7Sderaadt */ 297df930be7Sderaadt struct fd_type * 298df930be7Sderaadt fd_nvtotype(fdc, nvraminfo, drive) 299df930be7Sderaadt char *fdc; 300df930be7Sderaadt int nvraminfo, drive; 301df930be7Sderaadt { 302d1688987Snate #ifdef __alpha__ 303d1688987Snate /* Alpha: assume 1.44MB, idea from NetBSD sys/dev/isa/fd.c 304d1688987Snate * -- jay@rootaction.net 305d1688987Snate */ 306d1688987Snate return &fd_types[0]; /* 1.44MB */ 307d1688987Snate #else 308df930be7Sderaadt int type; 309df930be7Sderaadt 310df930be7Sderaadt type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0; 311df930be7Sderaadt switch (type) { 312df930be7Sderaadt case NVRAM_DISKETTE_NONE: 313df930be7Sderaadt return NULL; 314df930be7Sderaadt case NVRAM_DISKETTE_12M: 315df930be7Sderaadt return &fd_types[1]; 316df930be7Sderaadt case NVRAM_DISKETTE_TYPE5: 317df930be7Sderaadt case NVRAM_DISKETTE_TYPE6: 3183af98d30Sdownsj return &fd_types[7]; 319df930be7Sderaadt case NVRAM_DISKETTE_144M: 320df930be7Sderaadt return &fd_types[0]; 321df930be7Sderaadt case NVRAM_DISKETTE_360K: 322df930be7Sderaadt return &fd_types[3]; 323df930be7Sderaadt case NVRAM_DISKETTE_720K: 324df930be7Sderaadt return &fd_types[4]; 325df930be7Sderaadt default: 326df930be7Sderaadt printf("%s: drive %d: unknown device type 0x%x\n", 327df930be7Sderaadt fdc, drive, type); 328df930be7Sderaadt return NULL; 329df930be7Sderaadt } 330d1688987Snate #endif 331df930be7Sderaadt } 332df930be7Sderaadt 3332226ce33Sderaadt __inline struct fd_type * 334df930be7Sderaadt fd_dev_to_type(fd, dev) 335df930be7Sderaadt struct fd_softc *fd; 336df930be7Sderaadt dev_t dev; 337df930be7Sderaadt { 338df930be7Sderaadt int type = FDTYPE(dev); 339df930be7Sderaadt 340df930be7Sderaadt if (type > (sizeof(fd_types) / sizeof(fd_types[0]))) 341df930be7Sderaadt return NULL; 342df930be7Sderaadt return type ? &fd_types[type - 1] : fd->sc_deftype; 343df930be7Sderaadt } 344df930be7Sderaadt 345df930be7Sderaadt void 346df930be7Sderaadt fdstrategy(bp) 347df930be7Sderaadt register struct buf *bp; /* IO operation to perform */ 348df930be7Sderaadt { 3493af98d30Sdownsj struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(bp->b_dev)]; 350df930be7Sderaadt int sz; 351df930be7Sderaadt int s; 35276c8686eSdownsj int fd_bsize = 128 << fd->sc_type->secsize; 35376c8686eSdownsj int bf = fd_bsize / DEV_BSIZE; 354df930be7Sderaadt 355df930be7Sderaadt /* Valid unit, controller, and request? */ 3563af98d30Sdownsj if (bp->b_blkno < 0 || 35776c8686eSdownsj (((bp->b_blkno % bf) != 0 || 35876c8686eSdownsj (bp->b_bcount % fd_bsize) != 0) && 3591921243fSdownsj (bp->b_flags & B_FORMAT) == 0)) { 360df930be7Sderaadt bp->b_error = EINVAL; 361df930be7Sderaadt goto bad; 362df930be7Sderaadt } 363df930be7Sderaadt 364df930be7Sderaadt /* If it's a null transfer, return immediately. */ 365df930be7Sderaadt if (bp->b_bcount == 0) 366df930be7Sderaadt goto done; 367df930be7Sderaadt 36876c8686eSdownsj sz = howmany(bp->b_bcount, DEV_BSIZE); 369df930be7Sderaadt 37076c8686eSdownsj if (bp->b_blkno + sz > fd->sc_type->size * bf) { 37176c8686eSdownsj sz = fd->sc_type->size * bf - bp->b_blkno; 3726b174afeSderaadt if (sz == 0) 373df930be7Sderaadt /* If exactly at end of disk, return EOF. */ 374df930be7Sderaadt goto done; 375df930be7Sderaadt if (sz < 0) { 376df930be7Sderaadt /* If past end of disk, return EINVAL. */ 377df930be7Sderaadt bp->b_error = EINVAL; 378df930be7Sderaadt goto bad; 379df930be7Sderaadt } 380df930be7Sderaadt /* Otherwise, truncate request. */ 381df930be7Sderaadt bp->b_bcount = sz << DEV_BSHIFT; 382df930be7Sderaadt } 383df930be7Sderaadt 38476c8686eSdownsj bp->b_cylin = bp->b_blkno / (fd_bsize / DEV_BSIZE) / fd->sc_type->seccyl; 385df930be7Sderaadt 386df930be7Sderaadt #ifdef FD_DEBUG 387df930be7Sderaadt printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d sz %d\n", 388df930be7Sderaadt bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylin, sz); 389df930be7Sderaadt #endif 390df930be7Sderaadt 391df930be7Sderaadt /* Queue transfer on drive, activate drive and controller if idle. */ 392df930be7Sderaadt s = splbio(); 393df930be7Sderaadt disksort(&fd->sc_q, bp); 3947d33d8deSho timeout_del(&fd->fd_motor_off_to); /* a good idea */ 395df930be7Sderaadt if (!fd->sc_q.b_active) 396df930be7Sderaadt fdstart(fd); 397df930be7Sderaadt #ifdef DIAGNOSTIC 398df930be7Sderaadt else { 399df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 400df930be7Sderaadt if (fdc->sc_state == DEVIDLE) { 401df930be7Sderaadt printf("fdstrategy: controller inactive\n"); 402df930be7Sderaadt fdcstart(fdc); 403df930be7Sderaadt } 404df930be7Sderaadt } 405df930be7Sderaadt #endif 406df930be7Sderaadt splx(s); 407df930be7Sderaadt return; 408df930be7Sderaadt 409df930be7Sderaadt bad: 410df930be7Sderaadt bp->b_flags |= B_ERROR; 411df930be7Sderaadt done: 412df930be7Sderaadt /* Toss transfer; we're done early. */ 4136b174afeSderaadt bp->b_resid = bp->b_bcount; 414259d6dc6Sart s = splbio(); 415df930be7Sderaadt biodone(bp); 416259d6dc6Sart splx(s); 417df930be7Sderaadt } 418df930be7Sderaadt 419df930be7Sderaadt void 420df930be7Sderaadt fdstart(fd) 421df930be7Sderaadt struct fd_softc *fd; 422df930be7Sderaadt { 423df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 4243af98d30Sdownsj int active = (fdc->sc_link.fdlink.sc_drives.tqh_first != NULL); 425df930be7Sderaadt 426df930be7Sderaadt /* Link into controller queue. */ 427df930be7Sderaadt fd->sc_q.b_active = 1; 4283af98d30Sdownsj TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 429df930be7Sderaadt 430df930be7Sderaadt /* If controller not already active, start it. */ 431df930be7Sderaadt if (!active) 432df930be7Sderaadt fdcstart(fdc); 433df930be7Sderaadt } 434df930be7Sderaadt 435df930be7Sderaadt void 436df930be7Sderaadt fdfinish(fd, bp) 437df930be7Sderaadt struct fd_softc *fd; 438df930be7Sderaadt struct buf *bp; 439df930be7Sderaadt { 440df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 441df930be7Sderaadt 442259d6dc6Sart splassert(IPL_BIO); 443259d6dc6Sart 444df930be7Sderaadt /* 445df930be7Sderaadt * Move this drive to the end of the queue to give others a `fair' 446df930be7Sderaadt * chance. We only force a switch if N operations are completed while 447df930be7Sderaadt * another drive is waiting to be serviced, since there is a long motor 448df930be7Sderaadt * startup delay whenever we switch. 449df930be7Sderaadt */ 450df930be7Sderaadt if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) { 451df930be7Sderaadt fd->sc_ops = 0; 4523af98d30Sdownsj TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 453df930be7Sderaadt if (bp->b_actf) { 4543af98d30Sdownsj TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, 4553af98d30Sdownsj sc_drivechain); 456df930be7Sderaadt } else 457df930be7Sderaadt fd->sc_q.b_active = 0; 458df930be7Sderaadt } 459df930be7Sderaadt bp->b_resid = fd->sc_bcount; 460df930be7Sderaadt fd->sc_skip = 0; 461df930be7Sderaadt fd->sc_q.b_actf = bp->b_actf; 4623dbef52bSderaadt 463df930be7Sderaadt biodone(bp); 464df930be7Sderaadt /* turn off motor 5s from now */ 4657d33d8deSho timeout_add(&fd->fd_motor_off_to, 5 * hz); 466df930be7Sderaadt fdc->sc_state = DEVIDLE; 467df930be7Sderaadt } 468df930be7Sderaadt 469df930be7Sderaadt int 4702226ce33Sderaadt fdread(dev, uio, flags) 471df930be7Sderaadt dev_t dev; 472df930be7Sderaadt struct uio *uio; 4732226ce33Sderaadt int flags; 474df930be7Sderaadt { 475df930be7Sderaadt 476df930be7Sderaadt return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio)); 477df930be7Sderaadt } 478df930be7Sderaadt 479df930be7Sderaadt int 4802226ce33Sderaadt fdwrite(dev, uio, flags) 481df930be7Sderaadt dev_t dev; 482df930be7Sderaadt struct uio *uio; 4832226ce33Sderaadt int flags; 484df930be7Sderaadt { 485df930be7Sderaadt 486df930be7Sderaadt return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio)); 487df930be7Sderaadt } 488df930be7Sderaadt 489df930be7Sderaadt void 490df930be7Sderaadt fd_set_motor(fdc, reset) 491df930be7Sderaadt struct fdc_softc *fdc; 492df930be7Sderaadt int reset; 493df930be7Sderaadt { 494df930be7Sderaadt struct fd_softc *fd; 495df930be7Sderaadt u_char status; 496df930be7Sderaadt int n; 497df930be7Sderaadt 4983af98d30Sdownsj if ((fd = fdc->sc_link.fdlink.sc_drives.tqh_first) != NULL) 499df930be7Sderaadt status = fd->sc_drive; 500df930be7Sderaadt else 501df930be7Sderaadt status = 0; 502df930be7Sderaadt if (!reset) 503df930be7Sderaadt status |= FDO_FRST | FDO_FDMAEN; 504df930be7Sderaadt for (n = 0; n < 4; n++) 5053af98d30Sdownsj if ((fd = fdc->sc_link.fdlink.sc_fd[n]) 5063af98d30Sdownsj && (fd->sc_flags & FD_MOTOR)) 507df930be7Sderaadt status |= FDO_MOEN(n); 508b9a85264Sniklas bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, status); 509df930be7Sderaadt } 510df930be7Sderaadt 511df930be7Sderaadt void 512df930be7Sderaadt fd_motor_off(arg) 513df930be7Sderaadt void *arg; 514df930be7Sderaadt { 515df930be7Sderaadt struct fd_softc *fd = arg; 516df930be7Sderaadt int s; 517df930be7Sderaadt 518df930be7Sderaadt s = splbio(); 519df930be7Sderaadt fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); 520df930be7Sderaadt fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0); 521df930be7Sderaadt splx(s); 522df930be7Sderaadt } 523df930be7Sderaadt 524df930be7Sderaadt void 525df930be7Sderaadt fd_motor_on(arg) 526df930be7Sderaadt void *arg; 527df930be7Sderaadt { 528df930be7Sderaadt struct fd_softc *fd = arg; 529df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 530df930be7Sderaadt int s; 531df930be7Sderaadt 532df930be7Sderaadt s = splbio(); 533df930be7Sderaadt fd->sc_flags &= ~FD_MOTOR_WAIT; 5343af98d30Sdownsj if ((fdc->sc_link.fdlink.sc_drives.tqh_first == fd) 5353af98d30Sdownsj && (fdc->sc_state == MOTORWAIT)) 5363041a0b9Sdownsj (void) fdintr(fdc); 537df930be7Sderaadt splx(s); 538df930be7Sderaadt } 539df930be7Sderaadt 540df930be7Sderaadt int 5412226ce33Sderaadt fdopen(dev, flags, mode, p) 542df930be7Sderaadt dev_t dev; 543df930be7Sderaadt int flags; 5442226ce33Sderaadt int mode; 5452226ce33Sderaadt struct proc *p; 546df930be7Sderaadt { 547df930be7Sderaadt int unit; 548df930be7Sderaadt struct fd_softc *fd; 549df930be7Sderaadt struct fd_type *type; 550df930be7Sderaadt 551df930be7Sderaadt unit = FDUNIT(dev); 552d724e01aSderaadt if (unit >= fd_cd.cd_ndevs) 553df930be7Sderaadt return ENXIO; 554d724e01aSderaadt fd = fd_cd.cd_devs[unit]; 555df930be7Sderaadt if (fd == 0) 556df930be7Sderaadt return ENXIO; 557df930be7Sderaadt type = fd_dev_to_type(fd, dev); 558df930be7Sderaadt if (type == NULL) 559df930be7Sderaadt return ENXIO; 560df930be7Sderaadt 561df930be7Sderaadt if ((fd->sc_flags & FD_OPEN) != 0 && 562df930be7Sderaadt fd->sc_type != type) 563df930be7Sderaadt return EBUSY; 564df930be7Sderaadt 565df930be7Sderaadt fd->sc_type = type; 566df930be7Sderaadt fd->sc_cylin = -1; 567df930be7Sderaadt fd->sc_flags |= FD_OPEN; 568df930be7Sderaadt 569df930be7Sderaadt return 0; 570df930be7Sderaadt } 571df930be7Sderaadt 572df930be7Sderaadt int 5732226ce33Sderaadt fdclose(dev, flags, mode, p) 574df930be7Sderaadt dev_t dev; 575df930be7Sderaadt int flags; 5762226ce33Sderaadt int mode; 5772226ce33Sderaadt struct proc *p; 578df930be7Sderaadt { 579d724e01aSderaadt struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 580df930be7Sderaadt 581df930be7Sderaadt fd->sc_flags &= ~FD_OPEN; 5821921243fSdownsj fd->sc_opts &= ~FDOPT_NORETRY; 583df930be7Sderaadt return 0; 584df930be7Sderaadt } 585df930be7Sderaadt 5863041a0b9Sdownsj int 5873041a0b9Sdownsj fdsize(dev) 5883041a0b9Sdownsj dev_t dev; 589df930be7Sderaadt { 590df930be7Sderaadt 5913041a0b9Sdownsj /* Swapping to floppies would not make sense. */ 5923041a0b9Sdownsj return -1; 593df930be7Sderaadt } 594df930be7Sderaadt 595df930be7Sderaadt int 5963041a0b9Sdownsj fddump(dev, blkno, va, size) 5973041a0b9Sdownsj dev_t dev; 5983041a0b9Sdownsj daddr_t blkno; 5993041a0b9Sdownsj caddr_t va; 6003041a0b9Sdownsj size_t size; 601df930be7Sderaadt { 6023041a0b9Sdownsj 6033041a0b9Sdownsj /* Not implemented. */ 6043041a0b9Sdownsj return ENXIO; 6053041a0b9Sdownsj } 6063041a0b9Sdownsj 6073041a0b9Sdownsj /* 6083041a0b9Sdownsj * Called from the controller. 6093041a0b9Sdownsj */ 6103041a0b9Sdownsj int 6113041a0b9Sdownsj fdintr(fdc) 6123041a0b9Sdownsj struct fdc_softc *fdc; 6133041a0b9Sdownsj { 614df930be7Sderaadt #define st0 fdc->sc_status[0] 615df930be7Sderaadt #define cyl fdc->sc_status[1] 616df930be7Sderaadt struct fd_softc *fd; 617df930be7Sderaadt struct buf *bp; 618b9a85264Sniklas bus_space_tag_t iot = fdc->sc_iot; 619b9a85264Sniklas bus_space_handle_t ioh = fdc->sc_ioh; 620a06af799Sdownsj bus_space_handle_t ioh_ctl = fdc->sc_ioh_ctl; 6212226ce33Sderaadt int read, head, sec, i, nblks; 622df930be7Sderaadt struct fd_type *type; 6231921243fSdownsj struct fd_formb *finfo = NULL; 62476c8686eSdownsj int fd_bsize, bf; 625df930be7Sderaadt 626df930be7Sderaadt loop: 6273041a0b9Sdownsj /* Is there a transfer to this drive? If not, deactivate drive. */ 6283af98d30Sdownsj fd = fdc->sc_link.fdlink.sc_drives.tqh_first; 629df930be7Sderaadt if (fd == NULL) { 630df930be7Sderaadt fdc->sc_state = DEVIDLE; 631df930be7Sderaadt return 1; 632df930be7Sderaadt } 63376c8686eSdownsj fd_bsize = 128 << fd->sc_type->secsize; 63476c8686eSdownsj bf = fd_bsize / FDC_BSIZE; 635df930be7Sderaadt 636df930be7Sderaadt bp = fd->sc_q.b_actf; 637df930be7Sderaadt if (bp == NULL) { 638df930be7Sderaadt fd->sc_ops = 0; 6393af98d30Sdownsj TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 640df930be7Sderaadt fd->sc_q.b_active = 0; 641df930be7Sderaadt goto loop; 642df930be7Sderaadt } 643df930be7Sderaadt 6441921243fSdownsj if (bp->b_flags & B_FORMAT) 6451921243fSdownsj finfo = (struct fd_formb *)bp->b_data; 6461921243fSdownsj 647df930be7Sderaadt switch (fdc->sc_state) { 648df930be7Sderaadt case DEVIDLE: 649df930be7Sderaadt fdc->sc_errors = 0; 650df930be7Sderaadt fd->sc_skip = 0; 651df930be7Sderaadt fd->sc_bcount = bp->b_bcount; 65276c8686eSdownsj fd->sc_blkno = bp->b_blkno / (fd_bsize / DEV_BSIZE); 6537d33d8deSho timeout_del(&fd->fd_motor_off_to); 654df930be7Sderaadt if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { 655df930be7Sderaadt fdc->sc_state = MOTORWAIT; 656df930be7Sderaadt return 1; 657df930be7Sderaadt } 658df930be7Sderaadt if ((fd->sc_flags & FD_MOTOR) == 0) { 659df930be7Sderaadt /* Turn on the motor, being careful about pairing. */ 6603af98d30Sdownsj struct fd_softc *ofd = 6613af98d30Sdownsj fdc->sc_link.fdlink.sc_fd[fd->sc_drive ^ 1]; 662df930be7Sderaadt if (ofd && ofd->sc_flags & FD_MOTOR) { 6637d33d8deSho timeout_del(&ofd->fd_motor_off_to); 664df930be7Sderaadt ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); 665df930be7Sderaadt } 666df930be7Sderaadt fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; 667df930be7Sderaadt fd_set_motor(fdc, 0); 668df930be7Sderaadt fdc->sc_state = MOTORWAIT; 669df930be7Sderaadt /* Allow .25s for motor to stabilize. */ 6707d33d8deSho timeout_add(&fd->fd_motor_on_to, hz / 4); 671df930be7Sderaadt return 1; 672df930be7Sderaadt } 673df930be7Sderaadt /* Make sure the right drive is selected. */ 674df930be7Sderaadt fd_set_motor(fdc, 0); 675df930be7Sderaadt 676df930be7Sderaadt /* fall through */ 677df930be7Sderaadt case DOSEEK: 678df930be7Sderaadt doseek: 679df930be7Sderaadt if (fd->sc_cylin == bp->b_cylin) 680df930be7Sderaadt goto doio; 681df930be7Sderaadt 682b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */ 683b9a85264Sniklas out_fdc(iot, ioh, fd->sc_type->steprate); 684b9a85264Sniklas out_fdc(iot, ioh, 6); /* XXX head load time == 6ms */ 685df930be7Sderaadt 686b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SEEK); /* seek function */ 687b9a85264Sniklas out_fdc(iot, ioh, fd->sc_drive); /* drive number */ 688b9a85264Sniklas out_fdc(iot, ioh, bp->b_cylin * fd->sc_type->step); 689df930be7Sderaadt 690df930be7Sderaadt fd->sc_cylin = -1; 691df930be7Sderaadt fdc->sc_state = SEEKWAIT; 6921eb11dafSderaadt 6931eb11dafSderaadt fd->sc_dk.dk_seek++; 6941eb11dafSderaadt disk_busy(&fd->sc_dk); 6951eb11dafSderaadt 6967d33d8deSho timeout_add(&fd->fdtimeout_to, 4 * hz); 697df930be7Sderaadt return 1; 698df930be7Sderaadt 699df930be7Sderaadt case DOIO: 700df930be7Sderaadt doio: 701df930be7Sderaadt type = fd->sc_type; 7021921243fSdownsj if (finfo) 7031921243fSdownsj fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) - 7041921243fSdownsj (char *)finfo; 705df930be7Sderaadt sec = fd->sc_blkno % type->seccyl; 706df930be7Sderaadt nblks = type->seccyl - sec; 70776c8686eSdownsj nblks = min(nblks, fd->sc_bcount / fd_bsize); 70876c8686eSdownsj nblks = min(nblks, FDC_MAXIOSIZE / fd_bsize); 709df930be7Sderaadt fd->sc_nblks = nblks; 71076c8686eSdownsj fd->sc_nbytes = finfo ? bp->b_bcount : nblks * fd_bsize; 711df930be7Sderaadt head = sec / type->sectrac; 712df930be7Sderaadt sec -= head * type->sectrac; 713df930be7Sderaadt #ifdef DIAGNOSTIC 714df930be7Sderaadt {int block; 715df930be7Sderaadt block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec; 716df930be7Sderaadt if (block != fd->sc_blkno) { 7173041a0b9Sdownsj printf("fdintr: block %d != blkno %d\n", block, fd->sc_blkno); 718df930be7Sderaadt #ifdef DDB 719df930be7Sderaadt Debugger(); 720df930be7Sderaadt #endif 721df930be7Sderaadt }} 722df930be7Sderaadt #endif 723fa7cc844Sniklas read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; 724aee4fbd6Sniklas isadma_start(bp->b_data + fd->sc_skip, fd->sc_nbytes, 725aee4fbd6Sniklas fdc->sc_drq, read); 726a06af799Sdownsj bus_space_write_1(iot, ioh_ctl, fdctl, type->rate); 727df930be7Sderaadt #ifdef FD_DEBUG 7283041a0b9Sdownsj printf("fdintr: %s drive %d track %d head %d sec %d nblks %d\n", 729df930be7Sderaadt read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head, 730df930be7Sderaadt sec, nblks); 731df930be7Sderaadt #endif 7321921243fSdownsj if (finfo) { 7331921243fSdownsj /* formatting */ 734b9a85264Sniklas if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) { 7351921243fSdownsj fdc->sc_errors = 4; 7363041a0b9Sdownsj fdretry(fd); 7371921243fSdownsj goto loop; 7381921243fSdownsj } 739b9a85264Sniklas out_fdc(iot, ioh, (head << 2) | fd->sc_drive); 740b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_secshift); 741b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_nsecs); 742b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_gaplen); 743b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_fillbyte); 7441921243fSdownsj } else { 745df930be7Sderaadt if (read) 746b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_READ); /* READ */ 747df930be7Sderaadt else 748b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_WRITE);/* WRITE */ 749b9a85264Sniklas out_fdc(iot, ioh, (head << 2) | fd->sc_drive); 750b9a85264Sniklas out_fdc(iot, ioh, fd->sc_cylin); /* track */ 751b9a85264Sniklas out_fdc(iot, ioh, head); 752b9a85264Sniklas out_fdc(iot, ioh, sec + 1); /* sec +1 */ 753b9a85264Sniklas out_fdc(iot, ioh, type->secsize); /* sec size */ 754b9a85264Sniklas out_fdc(iot, ioh, type->sectrac); /* secs/track */ 755b9a85264Sniklas out_fdc(iot, ioh, type->gap1); /* gap1 size */ 756b9a85264Sniklas out_fdc(iot, ioh, type->datalen); /* data len */ 7571921243fSdownsj } 758df930be7Sderaadt fdc->sc_state = IOCOMPLETE; 7591eb11dafSderaadt 7601eb11dafSderaadt disk_busy(&fd->sc_dk); 7611eb11dafSderaadt 762df930be7Sderaadt /* allow 2 seconds for operation */ 7637d33d8deSho timeout_add(&fd->fdtimeout_to, 2 * hz); 764df930be7Sderaadt return 1; /* will return later */ 765df930be7Sderaadt 766df930be7Sderaadt case SEEKWAIT: 7677d33d8deSho timeout_del(&fd->fdtimeout_to); 768df930be7Sderaadt fdc->sc_state = SEEKCOMPLETE; 769df930be7Sderaadt /* allow 1/50 second for heads to settle */ 7707d33d8deSho timeout_add(&fdc->fdcpseudointr_to, hz / 50); 771df930be7Sderaadt return 1; 772df930be7Sderaadt 773df930be7Sderaadt case SEEKCOMPLETE: 7741eb11dafSderaadt disk_unbusy(&fd->sc_dk, 0); /* no data on seek */ 7751eb11dafSderaadt 776df930be7Sderaadt /* Make sure seek really happened. */ 777b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 778df930be7Sderaadt if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || 779df930be7Sderaadt cyl != bp->b_cylin * fd->sc_type->step) { 780df930be7Sderaadt #ifdef FD_DEBUG 781df930be7Sderaadt fdcstatus(&fd->sc_dev, 2, "seek failed"); 782df930be7Sderaadt #endif 7833041a0b9Sdownsj fdretry(fd); 784df930be7Sderaadt goto loop; 785df930be7Sderaadt } 786df930be7Sderaadt fd->sc_cylin = bp->b_cylin; 787df930be7Sderaadt goto doio; 788df930be7Sderaadt 789df930be7Sderaadt case IOTIMEDOUT: 790aee4fbd6Sniklas isadma_abort(fdc->sc_drq); 791df930be7Sderaadt case SEEKTIMEDOUT: 792df930be7Sderaadt case RECALTIMEDOUT: 793df930be7Sderaadt case RESETTIMEDOUT: 7943041a0b9Sdownsj fdretry(fd); 795df930be7Sderaadt goto loop; 796df930be7Sderaadt 797df930be7Sderaadt case IOCOMPLETE: /* IO DONE, post-analyze */ 7987d33d8deSho timeout_del(&fd->fdtimeout_to); 7991eb11dafSderaadt 8001eb11dafSderaadt disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid)); 8011eb11dafSderaadt 802df930be7Sderaadt if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) { 803aee4fbd6Sniklas isadma_abort(fdc->sc_drq); 804df930be7Sderaadt #ifdef FD_DEBUG 805df930be7Sderaadt fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ? 806df930be7Sderaadt "read failed" : "write failed"); 807df930be7Sderaadt printf("blkno %d nblks %d\n", 808df930be7Sderaadt fd->sc_blkno, fd->sc_nblks); 809df930be7Sderaadt #endif 8103041a0b9Sdownsj fdretry(fd); 811df930be7Sderaadt goto loop; 812df930be7Sderaadt } 813fa7cc844Sniklas read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; 814aee4fbd6Sniklas isadma_done(fdc->sc_drq); 815df930be7Sderaadt if (fdc->sc_errors) { 816df930be7Sderaadt diskerr(bp, "fd", "soft error", LOG_PRINTF, 81776c8686eSdownsj fd->sc_skip / fd_bsize, (struct disklabel *)NULL); 818df930be7Sderaadt printf("\n"); 819df930be7Sderaadt fdc->sc_errors = 0; 820df930be7Sderaadt } 821df930be7Sderaadt fd->sc_blkno += fd->sc_nblks; 822df930be7Sderaadt fd->sc_skip += fd->sc_nbytes; 823df930be7Sderaadt fd->sc_bcount -= fd->sc_nbytes; 8241921243fSdownsj if (!finfo && fd->sc_bcount > 0) { 825df930be7Sderaadt bp->b_cylin = fd->sc_blkno / fd->sc_type->seccyl; 826df930be7Sderaadt goto doseek; 827df930be7Sderaadt } 828df930be7Sderaadt fdfinish(fd, bp); 829df930be7Sderaadt goto loop; 830df930be7Sderaadt 831df930be7Sderaadt case DORESET: 832df930be7Sderaadt /* try a reset, keep motor on */ 833df930be7Sderaadt fd_set_motor(fdc, 1); 834df930be7Sderaadt delay(100); 835df930be7Sderaadt fd_set_motor(fdc, 0); 836df930be7Sderaadt fdc->sc_state = RESETCOMPLETE; 8377d33d8deSho timeout_add(&fd->fdtimeout_to, hz / 2); 838df930be7Sderaadt return 1; /* will return later */ 839df930be7Sderaadt 840df930be7Sderaadt case RESETCOMPLETE: 8417d33d8deSho timeout_del(&fd->fdtimeout_to); 842df930be7Sderaadt /* clear the controller output buffer */ 843df930be7Sderaadt for (i = 0; i < 4; i++) { 844b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 845df930be7Sderaadt (void) fdcresult(fdc); 846df930be7Sderaadt } 847df930be7Sderaadt 848df930be7Sderaadt /* fall through */ 849df930be7Sderaadt case DORECAL: 850b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_RECAL); /* recal function */ 851b9a85264Sniklas out_fdc(iot, ioh, fd->sc_drive); 852df930be7Sderaadt fdc->sc_state = RECALWAIT; 8537d33d8deSho timeout_add(&fd->fdtimeout_to, 5 * hz); 854df930be7Sderaadt return 1; /* will return later */ 855df930be7Sderaadt 856df930be7Sderaadt case RECALWAIT: 8577d33d8deSho timeout_del(&fd->fdtimeout_to); 858df930be7Sderaadt fdc->sc_state = RECALCOMPLETE; 859df930be7Sderaadt /* allow 1/30 second for heads to settle */ 8607d33d8deSho timeout_add(&fdc->fdcpseudointr_to, hz / 30); 861df930be7Sderaadt return 1; /* will return later */ 862df930be7Sderaadt 863df930be7Sderaadt case RECALCOMPLETE: 864b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 865df930be7Sderaadt if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { 866df930be7Sderaadt #ifdef FD_DEBUG 867df930be7Sderaadt fdcstatus(&fd->sc_dev, 2, "recalibrate failed"); 868df930be7Sderaadt #endif 8693041a0b9Sdownsj fdretry(fd); 870df930be7Sderaadt goto loop; 871df930be7Sderaadt } 872df930be7Sderaadt fd->sc_cylin = 0; 873df930be7Sderaadt goto doseek; 874df930be7Sderaadt 875df930be7Sderaadt case MOTORWAIT: 876df930be7Sderaadt if (fd->sc_flags & FD_MOTOR_WAIT) 877df930be7Sderaadt return 1; /* time's not up yet */ 878df930be7Sderaadt goto doseek; 879df930be7Sderaadt 880df930be7Sderaadt default: 881df930be7Sderaadt fdcstatus(&fd->sc_dev, 0, "stray interrupt"); 882df930be7Sderaadt return 1; 883df930be7Sderaadt } 884df930be7Sderaadt #ifdef DIAGNOSTIC 8853041a0b9Sdownsj panic("fdintr: impossible"); 886df930be7Sderaadt #endif 887df930be7Sderaadt #undef st0 888df930be7Sderaadt #undef cyl 889df930be7Sderaadt } 890df930be7Sderaadt 891df930be7Sderaadt void 8923041a0b9Sdownsj fdtimeout(arg) 8933041a0b9Sdownsj void *arg; 894df930be7Sderaadt { 8953041a0b9Sdownsj struct fd_softc *fd = arg; 8963041a0b9Sdownsj struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 8973041a0b9Sdownsj int s; 898df930be7Sderaadt 8993041a0b9Sdownsj s = splbio(); 9003041a0b9Sdownsj #ifdef DEBUG 9013041a0b9Sdownsj log(LOG_ERR,"fdtimeout: state %d\n", fdc->sc_state); 9023041a0b9Sdownsj #endif 9033041a0b9Sdownsj fdcstatus(&fd->sc_dev, 0, "timeout"); 9043041a0b9Sdownsj 9053041a0b9Sdownsj if (fd->sc_q.b_actf) 9063041a0b9Sdownsj fdc->sc_state++; 9073041a0b9Sdownsj else 9083041a0b9Sdownsj fdc->sc_state = DEVIDLE; 9093041a0b9Sdownsj 9103041a0b9Sdownsj (void) fdintr(fdc); 9113041a0b9Sdownsj splx(s); 9123041a0b9Sdownsj } 9133041a0b9Sdownsj 9143041a0b9Sdownsj void 9153041a0b9Sdownsj fdretry(fd) 9163041a0b9Sdownsj struct fd_softc *fd; 9173041a0b9Sdownsj { 9183041a0b9Sdownsj struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 9193041a0b9Sdownsj struct buf *bp = fd->sc_q.b_actf; 920df930be7Sderaadt 9211921243fSdownsj if (fd->sc_opts & FDOPT_NORETRY) 9221921243fSdownsj goto fail; 923df930be7Sderaadt switch (fdc->sc_errors) { 924df930be7Sderaadt case 0: 925df930be7Sderaadt /* try again */ 926da3b7780Shannken fdc->sc_state = DOSEEK; 927df930be7Sderaadt break; 928df930be7Sderaadt 929df930be7Sderaadt case 1: case 2: case 3: 930df930be7Sderaadt /* didn't work; try recalibrating */ 931df930be7Sderaadt fdc->sc_state = DORECAL; 932df930be7Sderaadt break; 933df930be7Sderaadt 934df930be7Sderaadt case 4: 935df930be7Sderaadt /* still no go; reset the bastard */ 936df930be7Sderaadt fdc->sc_state = DORESET; 937df930be7Sderaadt break; 938df930be7Sderaadt 939df930be7Sderaadt default: 9401921243fSdownsj fail: 941df930be7Sderaadt diskerr(bp, "fd", "hard error", LOG_PRINTF, 94276c8686eSdownsj fd->sc_skip / (128 << fd->sc_type->secsize), 94376c8686eSdownsj (struct disklabel *)NULL); 944df930be7Sderaadt printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n", 945df930be7Sderaadt fdc->sc_status[0], NE7_ST0BITS, 946df930be7Sderaadt fdc->sc_status[1], NE7_ST1BITS, 947df930be7Sderaadt fdc->sc_status[2], NE7_ST2BITS, 948df930be7Sderaadt fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); 949df930be7Sderaadt 950df930be7Sderaadt bp->b_flags |= B_ERROR; 951df930be7Sderaadt bp->b_error = EIO; 952df930be7Sderaadt fdfinish(fd, bp); 953df930be7Sderaadt } 954df930be7Sderaadt fdc->sc_errors++; 955df930be7Sderaadt } 956df930be7Sderaadt 957df930be7Sderaadt int 9582226ce33Sderaadt fdioctl(dev, cmd, addr, flag, p) 959df930be7Sderaadt dev_t dev; 960df930be7Sderaadt u_long cmd; 961df930be7Sderaadt caddr_t addr; 962df930be7Sderaadt int flag; 9632226ce33Sderaadt struct proc *p; 964df930be7Sderaadt { 965d724e01aSderaadt struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 9663c27f4f2Sderaadt struct disklabel dl, *lp = &dl; 9673c27f4f2Sderaadt struct cpu_disklabel cdl; 96834f3058cSderaadt char *errstring; 969df930be7Sderaadt int error; 970df930be7Sderaadt 971df930be7Sderaadt switch (cmd) { 9724b64fdbfSderaadt case MTIOCTOP: 9734b64fdbfSderaadt if (((struct mtop *)addr)->mt_op != MTOFFL) 9744b64fdbfSderaadt return EIO; 9754b64fdbfSderaadt return (0); 976df930be7Sderaadt case DIOCGDINFO: 9773c27f4f2Sderaadt bzero(lp, sizeof(*lp)); 9783c27f4f2Sderaadt bzero(&cdl, sizeof(struct cpu_disklabel)); 979df930be7Sderaadt 9803c27f4f2Sderaadt lp->d_secsize = 128 << fd->sc_type->secsize; 9813c27f4f2Sderaadt lp->d_secpercyl = fd->sc_type->seccyl; 9823c27f4f2Sderaadt lp->d_ntracks = fd->sc_type->heads; 9830ebd42eeSderaadt lp->d_nsectors = fd->sc_type->sectrac; 9843c27f4f2Sderaadt lp->d_ncylinders = fd->sc_type->tracks; 985df930be7Sderaadt 986bf3a6870Skrw strncpy(lp->d_typename, "floppy disk", sizeof lp->d_typename); 9873c27f4f2Sderaadt lp->d_type = DTYPE_FLOPPY; 988bf3a6870Skrw strncpy(lp->d_packname, "fictitious", sizeof lp->d_packname); 9893c27f4f2Sderaadt lp->d_secperunit = fd->sc_type->size; 9903c27f4f2Sderaadt lp->d_rpm = 300; 9913c27f4f2Sderaadt lp->d_interleave = 1; 9923c27f4f2Sderaadt lp->d_flags = D_REMOVABLE; 993df930be7Sderaadt 9943c27f4f2Sderaadt lp->d_partitions[RAW_PART].p_offset = 0; 9953c27f4f2Sderaadt lp->d_partitions[RAW_PART].p_size = 9963c27f4f2Sderaadt lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 9973c27f4f2Sderaadt lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 9983c27f4f2Sderaadt lp->d_npartitions = RAW_PART + 1; 9993c27f4f2Sderaadt 10003c27f4f2Sderaadt lp->d_magic = DISKMAGIC; 10013c27f4f2Sderaadt lp->d_magic2 = DISKMAGIC; 10023c27f4f2Sderaadt lp->d_checksum = dkcksum(lp); 10033c27f4f2Sderaadt 1004e9e400f5Smillert errstring = readdisklabel(dev, fdstrategy, lp, &cdl, 0); 1005a61ae37bSderaadt if (errstring) { 1006a61ae37bSderaadt /*printf("%s: %s\n", fd->sc_dev.dv_xname, errstring); */ 1007a61ae37bSderaadt } 10083c27f4f2Sderaadt 10093c27f4f2Sderaadt *(struct disklabel *)addr = *lp; 1010df930be7Sderaadt return 0; 1011df930be7Sderaadt 1012df930be7Sderaadt case DIOCWLABEL: 1013df930be7Sderaadt if ((flag & FWRITE) == 0) 1014df930be7Sderaadt return EBADF; 1015df930be7Sderaadt /* XXX do something */ 1016df930be7Sderaadt return 0; 1017df930be7Sderaadt 1018df930be7Sderaadt case DIOCWDINFO: 1019df930be7Sderaadt if ((flag & FWRITE) == 0) 1020df930be7Sderaadt return EBADF; 1021df930be7Sderaadt 10223c27f4f2Sderaadt error = setdisklabel(lp, (struct disklabel *)addr, 0, NULL); 1023df930be7Sderaadt if (error) 1024df930be7Sderaadt return error; 1025df930be7Sderaadt 10263c27f4f2Sderaadt error = writedisklabel(dev, fdstrategy, lp, NULL); 1027df930be7Sderaadt return error; 1028df930be7Sderaadt 10291921243fSdownsj case FD_FORM: 10301921243fSdownsj if((flag & FWRITE) == 0) 10311921243fSdownsj return EBADF; /* must be opened for writing */ 10321921243fSdownsj else if(((struct fd_formb *)addr)->format_version != 10331921243fSdownsj FD_FORMAT_VERSION) 10341921243fSdownsj return EINVAL; /* wrong version of formatting prog */ 10351921243fSdownsj else 10361921243fSdownsj return fdformat(dev, (struct fd_formb *)addr, p); 10371921243fSdownsj break; 10381921243fSdownsj 10391921243fSdownsj case FD_GTYPE: /* get drive type */ 10401921243fSdownsj *(struct fd_type *)addr = *fd->sc_type; 10411921243fSdownsj return 0; 10421921243fSdownsj 10431921243fSdownsj case FD_GOPTS: /* get drive options */ 10441921243fSdownsj *(int *)addr = fd->sc_opts; 10451921243fSdownsj return 0; 10461921243fSdownsj 10471921243fSdownsj case FD_SOPTS: /* set drive options */ 10481921243fSdownsj fd->sc_opts = *(int *)addr; 10491921243fSdownsj return 0; 10501921243fSdownsj 1051df930be7Sderaadt default: 1052df930be7Sderaadt return ENOTTY; 1053df930be7Sderaadt } 1054df930be7Sderaadt 1055df930be7Sderaadt #ifdef DIAGNOSTIC 1056df930be7Sderaadt panic("fdioctl: impossible"); 1057df930be7Sderaadt #endif 1058df930be7Sderaadt } 10591921243fSdownsj 10601921243fSdownsj int 10611921243fSdownsj fdformat(dev, finfo, p) 10621921243fSdownsj dev_t dev; 10631921243fSdownsj struct fd_formb *finfo; 10641921243fSdownsj struct proc *p; 10651921243fSdownsj { 10661921243fSdownsj int rv = 0, s; 10671921243fSdownsj struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 10681921243fSdownsj struct fd_type *type = fd->sc_type; 10691921243fSdownsj struct buf *bp; 107076c8686eSdownsj int fd_bsize = 128 << fd->sc_type->secsize; 10711921243fSdownsj 10721921243fSdownsj /* set up a buffer header for fdstrategy() */ 10731921243fSdownsj bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); 10741921243fSdownsj if(bp == 0) 10751921243fSdownsj return ENOBUFS; 10761921243fSdownsj bzero((void *)bp, sizeof(struct buf)); 10771921243fSdownsj bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; 10781921243fSdownsj bp->b_proc = p; 10791921243fSdownsj bp->b_dev = dev; 10801921243fSdownsj 10811921243fSdownsj /* 10821921243fSdownsj * calculate a fake blkno, so fdstrategy() would initiate a 10831921243fSdownsj * seek to the requested cylinder 10841921243fSdownsj */ 10851921243fSdownsj bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads) 108676c8686eSdownsj + finfo->head * type->sectrac) * fd_bsize / DEV_BSIZE; 10871921243fSdownsj 10881921243fSdownsj bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 10891921243fSdownsj bp->b_data = (caddr_t)finfo; 10901921243fSdownsj 10911921243fSdownsj #ifdef DEBUG 10921921243fSdownsj printf("fdformat: blkno %x count %x\n", bp->b_blkno, bp->b_bcount); 10931921243fSdownsj #endif 10941921243fSdownsj 10951921243fSdownsj /* now do the format */ 10961921243fSdownsj fdstrategy(bp); 10971921243fSdownsj 10981921243fSdownsj /* ...and wait for it to complete */ 10991921243fSdownsj s = splbio(); 11001921243fSdownsj while(!(bp->b_flags & B_DONE)) 11011921243fSdownsj { 11021921243fSdownsj rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 0); 11031921243fSdownsj if(rv == EWOULDBLOCK) 11041921243fSdownsj /*break*/; 11051921243fSdownsj } 11061921243fSdownsj splx(s); 11071921243fSdownsj 11081921243fSdownsj if(rv == EWOULDBLOCK) { 11091921243fSdownsj /* timed out */ 11101921243fSdownsj rv = EIO; 11111921243fSdownsj /* XXX what to do to the buf? it will eventually fall 11121921243fSdownsj out as finished, but ... ?*/ 11131921243fSdownsj /*biodone(bp);*/ 11141921243fSdownsj } 11151921243fSdownsj if(bp->b_flags & B_ERROR) 11161921243fSdownsj rv = bp->b_error; 11171921243fSdownsj free(bp, M_TEMP); 11181921243fSdownsj return rv; 11191921243fSdownsj } 1120