1*aee4fbd6Sniklas /* $OpenBSD: fd.c,v 1.37 1998/01/18 18:48:41 niklas 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. 26df930be7Sderaadt * 3. All advertising materials mentioning features or use of this software 27df930be7Sderaadt * must display the following acknowledgement: 28df930be7Sderaadt * This product includes software developed by the University of 29df930be7Sderaadt * California, Berkeley and its contributors. 30df930be7Sderaadt * 4. Neither the name of the University nor the names of its contributors 31df930be7Sderaadt * may be used to endorse or promote products derived from this software 32df930be7Sderaadt * without specific prior written permission. 33df930be7Sderaadt * 34df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 35df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 36df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 38df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 39df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 40df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 41df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 42df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 43df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 44df930be7Sderaadt * SUCH DAMAGE. 45df930be7Sderaadt * 46df930be7Sderaadt * @(#)fd.c 7.4 (Berkeley) 5/25/91 47df930be7Sderaadt */ 48df930be7Sderaadt 49df930be7Sderaadt #include <sys/param.h> 50df930be7Sderaadt #include <sys/systm.h> 51df930be7Sderaadt #include <sys/kernel.h> 52df930be7Sderaadt #include <sys/file.h> 53df930be7Sderaadt #include <sys/ioctl.h> 54df930be7Sderaadt #include <sys/device.h> 55df930be7Sderaadt #include <sys/disklabel.h> 56df930be7Sderaadt #include <sys/dkstat.h> 57df930be7Sderaadt #include <sys/disk.h> 58df930be7Sderaadt #include <sys/buf.h> 591921243fSdownsj #include <sys/malloc.h> 60df930be7Sderaadt #include <sys/uio.h> 614b64fdbfSderaadt #include <sys/mtio.h> 624e5d1609Sderaadt #include <sys/proc.h> 63df930be7Sderaadt #include <sys/syslog.h> 64df930be7Sderaadt #include <sys/queue.h> 65df930be7Sderaadt 66df930be7Sderaadt #include <machine/cpu.h> 67b9a85264Sniklas #include <machine/bus.h> 682226ce33Sderaadt #include <machine/conf.h> 694176a241Sderaadt #include <machine/intr.h> 701921243fSdownsj #include <machine/ioctl_fd.h> 71df930be7Sderaadt 72df930be7Sderaadt #include <dev/isa/isavar.h> 73df930be7Sderaadt #include <dev/isa/isadmavar.h> 7471e39c02Sdownsj #include <dev/isa/fdreg.h> 75df930be7Sderaadt 7671e39c02Sdownsj #if defined(i386) 77df930be7Sderaadt #include <i386/isa/nvram.h> 7871e39c02Sdownsj #endif 79df930be7Sderaadt 8071e39c02Sdownsj #include <dev/isa/fdlink.h> 813041a0b9Sdownsj 821921243fSdownsj /* XXX misuse a flag to identify format operation */ 831921243fSdownsj #define B_FORMAT B_XXX 841921243fSdownsj 85df930be7Sderaadt #define b_cylin b_resid 86df930be7Sderaadt 871921243fSdownsj /* fd_type struct now in ioctl_fd.h */ 88df930be7Sderaadt 89df930be7Sderaadt /* The order of entries in the following table is important -- BEWARE! */ 90df930be7Sderaadt struct fd_type fd_types[] = { 91df930be7Sderaadt { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB" }, /* 1.44MB diskette */ 92df930be7Sderaadt { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB" }, /* 1.2 MB AT-diskettes */ 93df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */ 94df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */ 95df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB" }, /* 3.5" 720kB diskette */ 96df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x" }, /* 720kB in 1.2MB drive */ 97df930be7Sderaadt { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x" }, /* 360kB in 720kB drive */ 9876c8686eSdownsj { 36,2,72,2,0xff,0xaf,0x1b,0x54,80,5760,1,FDC_500KBPS,"2.88MB" }, /* 2.88MB diskette */ 9976c8686eSdownsj { 8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,"1.2MB/[1024bytes/sector]" } /* 1.2 MB japanese format */ 100df930be7Sderaadt }; 101df930be7Sderaadt 102df930be7Sderaadt /* software state, per disk (with up to 4 disks per ctlr) */ 103df930be7Sderaadt struct fd_softc { 104df930be7Sderaadt struct device sc_dev; 1053dbef52bSderaadt struct disk sc_dk; 106df930be7Sderaadt 107df930be7Sderaadt struct fd_type *sc_deftype; /* default type descriptor */ 108df930be7Sderaadt struct fd_type *sc_type; /* current type descriptor */ 109df930be7Sderaadt 110df930be7Sderaadt daddr_t sc_blkno; /* starting block number */ 111df930be7Sderaadt int sc_bcount; /* byte count left */ 1121921243fSdownsj int sc_opts; /* user-set options */ 113df930be7Sderaadt int sc_skip; /* bytes already transferred */ 114df930be7Sderaadt int sc_nblks; /* number of blocks currently tranferring */ 115df930be7Sderaadt int sc_nbytes; /* number of bytes currently tranferring */ 116df930be7Sderaadt 117df930be7Sderaadt int sc_drive; /* physical unit number */ 118df930be7Sderaadt int sc_flags; 119df930be7Sderaadt #define FD_OPEN 0x01 /* it's open */ 120df930be7Sderaadt #define FD_MOTOR 0x02 /* motor should be on */ 121df930be7Sderaadt #define FD_MOTOR_WAIT 0x04 /* motor coming up */ 122df930be7Sderaadt int sc_cylin; /* where we think the head is */ 123df930be7Sderaadt 12452a6aad1Sderaadt void *sc_sdhook; /* saved shutdown hook for drive. */ 12552a6aad1Sderaadt 126df930be7Sderaadt TAILQ_ENTRY(fd_softc) sc_drivechain; 127df930be7Sderaadt int sc_ops; /* I/O ops since last switch */ 128df930be7Sderaadt struct buf sc_q; /* head of buf chain */ 129df930be7Sderaadt }; 130df930be7Sderaadt 131df930be7Sderaadt /* floppy driver configuration */ 132df930be7Sderaadt int fdprobe __P((struct device *, void *, void *)); 133df930be7Sderaadt void fdattach __P((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 143df930be7Sderaadt void fdgetdisklabel __P((struct fd_softc *)); 144df930be7Sderaadt int fd_get_parms __P((struct fd_softc *)); 145df930be7Sderaadt void fdstrategy __P((struct buf *)); 146df930be7Sderaadt void fdstart __P((struct fd_softc *)); 1474e5d1609Sderaadt int fdintr __P((struct fdc_softc *)); 148df930be7Sderaadt 149df930be7Sderaadt struct dkdriver fddkdriver = { fdstrategy }; 150df930be7Sderaadt 151df930be7Sderaadt void fd_set_motor __P((struct fdc_softc *fdc, int reset)); 152df930be7Sderaadt void fd_motor_off __P((void *arg)); 153df930be7Sderaadt void fd_motor_on __P((void *arg)); 154df930be7Sderaadt void fdfinish __P((struct fd_softc *fd, struct buf *bp)); 1551921243fSdownsj int fdformat __P((dev_t, struct fd_formb *, struct proc *)); 1562226ce33Sderaadt __inline struct fd_type *fd_dev_to_type __P((struct fd_softc *, dev_t)); 1573041a0b9Sdownsj void fdretry __P((struct fd_softc *)); 1583041a0b9Sdownsj void fdtimeout __P((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 207768789fcSrees /* flags & 0x20 forces the drive to be found even if it won't probe */ 208768789fcSrees if (!(fa->fa_flags & 0x20) && (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20)) 209df930be7Sderaadt return 0; 210df930be7Sderaadt /* turn off motor */ 211b9a85264Sniklas bus_space_write_1(iot, ioh, fdout, FDO_FRST); 212df930be7Sderaadt 213df930be7Sderaadt return 1; 214df930be7Sderaadt } 215df930be7Sderaadt 216df930be7Sderaadt /* 217df930be7Sderaadt * Controller is working, and drive responded. Attach it. 218df930be7Sderaadt */ 219df930be7Sderaadt void 220df930be7Sderaadt fdattach(parent, self, aux) 221df930be7Sderaadt struct device *parent, *self; 222df930be7Sderaadt void *aux; 223df930be7Sderaadt { 224df930be7Sderaadt struct fdc_softc *fdc = (void *)parent; 225df930be7Sderaadt struct fd_softc *fd = (void *)self; 226df930be7Sderaadt struct fdc_attach_args *fa = aux; 227df930be7Sderaadt struct fd_type *type = fa->fa_deftype; 228df930be7Sderaadt int drive = fa->fa_drive; 229df930be7Sderaadt 230e9475ca2Sdownsj if (!type || (fa->fa_flags & 0x10)) { 231e9475ca2Sdownsj /* The config has overridden this. */ 232e9475ca2Sdownsj switch (fa->fa_flags & 0x07) { 2333af98d30Sdownsj case 1: /* 2.88MB */ 2343af98d30Sdownsj type = &fd_types[7]; 2353af98d30Sdownsj break; 236e9475ca2Sdownsj case 2: /* 1.44MB */ 237e9475ca2Sdownsj type = &fd_types[0]; 238e9475ca2Sdownsj break; 239e9475ca2Sdownsj case 3: /* 1.2MB */ 240e9475ca2Sdownsj type = &fd_types[1]; 241e9475ca2Sdownsj break; 242e9475ca2Sdownsj case 4: /* 720K */ 243e9475ca2Sdownsj type = &fd_types[4]; 244e9475ca2Sdownsj break; 245e9475ca2Sdownsj case 5: /* 360K */ 246e9475ca2Sdownsj type = &fd_types[3]; 247e9475ca2Sdownsj break; 24876c8686eSdownsj case 6: /* 1.2 MB japanese format */ 24976c8686eSdownsj type = &fd_types[8]; 25076c8686eSdownsj break; 251e9475ca2Sdownsj } 252e9475ca2Sdownsj } 253df930be7Sderaadt 254df930be7Sderaadt if (type) 255df930be7Sderaadt printf(": %s %d cyl, %d head, %d sec\n", type->name, 256df930be7Sderaadt type->tracks, type->heads, type->sectrac); 257df930be7Sderaadt else 258df930be7Sderaadt printf(": density unknown\n"); 259df930be7Sderaadt 260df930be7Sderaadt fd->sc_cylin = -1; 261df930be7Sderaadt fd->sc_drive = drive; 262df930be7Sderaadt fd->sc_deftype = type; 2633af98d30Sdownsj fdc->sc_type[drive] = FDC_TYPE_DISK; 2643af98d30Sdownsj fdc->sc_link.fdlink.sc_fd[drive] = fd; 2653dbef52bSderaadt 2663dbef52bSderaadt /* 2673dbef52bSderaadt * Initialize and attach the disk structure. 2683dbef52bSderaadt */ 2693dbef52bSderaadt fd->sc_dk.dk_name = fd->sc_dev.dv_xname; 270df930be7Sderaadt fd->sc_dk.dk_driver = &fddkdriver; 2713dbef52bSderaadt disk_attach(&fd->sc_dk); 2723dbef52bSderaadt 273df930be7Sderaadt dk_establish(&fd->sc_dk, &fd->sc_dev); 27452a6aad1Sderaadt /* Needed to power off if the motor is on when we halt. */ 27552a6aad1Sderaadt fd->sc_sdhook = shutdownhook_establish(fd_motor_off, fd); 276df930be7Sderaadt } 277df930be7Sderaadt 278df930be7Sderaadt /* 279df930be7Sderaadt * Translate nvram type into internal data structure. Return NULL for 280df930be7Sderaadt * none/unknown/unusable. 281df930be7Sderaadt */ 282df930be7Sderaadt struct fd_type * 283df930be7Sderaadt fd_nvtotype(fdc, nvraminfo, drive) 284df930be7Sderaadt char *fdc; 285df930be7Sderaadt int nvraminfo, drive; 286df930be7Sderaadt { 287df930be7Sderaadt int type; 288df930be7Sderaadt 289df930be7Sderaadt type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0; 290df930be7Sderaadt switch (type) { 291df930be7Sderaadt case NVRAM_DISKETTE_NONE: 292df930be7Sderaadt return NULL; 293df930be7Sderaadt case NVRAM_DISKETTE_12M: 294df930be7Sderaadt return &fd_types[1]; 295df930be7Sderaadt case NVRAM_DISKETTE_TYPE5: 296df930be7Sderaadt case NVRAM_DISKETTE_TYPE6: 2973af98d30Sdownsj return &fd_types[7]; 298df930be7Sderaadt case NVRAM_DISKETTE_144M: 299df930be7Sderaadt return &fd_types[0]; 300df930be7Sderaadt case NVRAM_DISKETTE_360K: 301df930be7Sderaadt return &fd_types[3]; 302df930be7Sderaadt case NVRAM_DISKETTE_720K: 303df930be7Sderaadt return &fd_types[4]; 304df930be7Sderaadt default: 305df930be7Sderaadt printf("%s: drive %d: unknown device type 0x%x\n", 306df930be7Sderaadt fdc, drive, type); 307df930be7Sderaadt return NULL; 308df930be7Sderaadt } 309df930be7Sderaadt } 310df930be7Sderaadt 3112226ce33Sderaadt __inline struct fd_type * 312df930be7Sderaadt fd_dev_to_type(fd, dev) 313df930be7Sderaadt struct fd_softc *fd; 314df930be7Sderaadt dev_t dev; 315df930be7Sderaadt { 316df930be7Sderaadt int type = FDTYPE(dev); 317df930be7Sderaadt 318df930be7Sderaadt if (type > (sizeof(fd_types) / sizeof(fd_types[0]))) 319df930be7Sderaadt return NULL; 320df930be7Sderaadt return type ? &fd_types[type - 1] : fd->sc_deftype; 321df930be7Sderaadt } 322df930be7Sderaadt 323df930be7Sderaadt void 324df930be7Sderaadt fdstrategy(bp) 325df930be7Sderaadt register struct buf *bp; /* IO operation to perform */ 326df930be7Sderaadt { 3273af98d30Sdownsj struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(bp->b_dev)]; 328df930be7Sderaadt int sz; 329df930be7Sderaadt int s; 33076c8686eSdownsj int fd_bsize = 128 << fd->sc_type->secsize; 33176c8686eSdownsj int bf = fd_bsize / DEV_BSIZE; 332df930be7Sderaadt 333df930be7Sderaadt /* Valid unit, controller, and request? */ 3343af98d30Sdownsj if (bp->b_blkno < 0 || 33576c8686eSdownsj (((bp->b_blkno % bf) != 0 || 33676c8686eSdownsj (bp->b_bcount % fd_bsize) != 0) && 3371921243fSdownsj (bp->b_flags & B_FORMAT) == 0)) { 338df930be7Sderaadt bp->b_error = EINVAL; 339df930be7Sderaadt goto bad; 340df930be7Sderaadt } 341df930be7Sderaadt 342df930be7Sderaadt /* If it's a null transfer, return immediately. */ 343df930be7Sderaadt if (bp->b_bcount == 0) 344df930be7Sderaadt goto done; 345df930be7Sderaadt 34676c8686eSdownsj sz = howmany(bp->b_bcount, DEV_BSIZE); 347df930be7Sderaadt 34876c8686eSdownsj if (bp->b_blkno + sz > fd->sc_type->size * bf) { 34976c8686eSdownsj sz = fd->sc_type->size * bf - bp->b_blkno; 3506b174afeSderaadt if (sz == 0) 351df930be7Sderaadt /* If exactly at end of disk, return EOF. */ 352df930be7Sderaadt goto done; 353df930be7Sderaadt if (sz < 0) { 354df930be7Sderaadt /* If past end of disk, return EINVAL. */ 355df930be7Sderaadt bp->b_error = EINVAL; 356df930be7Sderaadt goto bad; 357df930be7Sderaadt } 358df930be7Sderaadt /* Otherwise, truncate request. */ 359df930be7Sderaadt bp->b_bcount = sz << DEV_BSHIFT; 360df930be7Sderaadt } 361df930be7Sderaadt 36276c8686eSdownsj bp->b_cylin = bp->b_blkno / (fd_bsize / DEV_BSIZE) / fd->sc_type->seccyl; 363df930be7Sderaadt 364df930be7Sderaadt #ifdef FD_DEBUG 365df930be7Sderaadt printf("fdstrategy: b_blkno %d b_bcount %d blkno %d cylin %d sz %d\n", 366df930be7Sderaadt bp->b_blkno, bp->b_bcount, fd->sc_blkno, bp->b_cylin, sz); 367df930be7Sderaadt #endif 368df930be7Sderaadt 369df930be7Sderaadt /* Queue transfer on drive, activate drive and controller if idle. */ 370df930be7Sderaadt s = splbio(); 371df930be7Sderaadt disksort(&fd->sc_q, bp); 372df930be7Sderaadt untimeout(fd_motor_off, fd); /* a good idea */ 373df930be7Sderaadt if (!fd->sc_q.b_active) 374df930be7Sderaadt fdstart(fd); 375df930be7Sderaadt #ifdef DIAGNOSTIC 376df930be7Sderaadt else { 377df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 378df930be7Sderaadt if (fdc->sc_state == DEVIDLE) { 379df930be7Sderaadt printf("fdstrategy: controller inactive\n"); 380df930be7Sderaadt fdcstart(fdc); 381df930be7Sderaadt } 382df930be7Sderaadt } 383df930be7Sderaadt #endif 384df930be7Sderaadt splx(s); 385df930be7Sderaadt return; 386df930be7Sderaadt 387df930be7Sderaadt bad: 388df930be7Sderaadt bp->b_flags |= B_ERROR; 389df930be7Sderaadt done: 390df930be7Sderaadt /* Toss transfer; we're done early. */ 3916b174afeSderaadt bp->b_resid = bp->b_bcount; 392df930be7Sderaadt biodone(bp); 393df930be7Sderaadt } 394df930be7Sderaadt 395df930be7Sderaadt void 396df930be7Sderaadt fdstart(fd) 397df930be7Sderaadt struct fd_softc *fd; 398df930be7Sderaadt { 399df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 4003af98d30Sdownsj int active = (fdc->sc_link.fdlink.sc_drives.tqh_first != NULL); 401df930be7Sderaadt 402df930be7Sderaadt /* Link into controller queue. */ 403df930be7Sderaadt fd->sc_q.b_active = 1; 4043af98d30Sdownsj TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 405df930be7Sderaadt 406df930be7Sderaadt /* If controller not already active, start it. */ 407df930be7Sderaadt if (!active) 408df930be7Sderaadt fdcstart(fdc); 409df930be7Sderaadt } 410df930be7Sderaadt 411df930be7Sderaadt void 412df930be7Sderaadt fdfinish(fd, bp) 413df930be7Sderaadt struct fd_softc *fd; 414df930be7Sderaadt struct buf *bp; 415df930be7Sderaadt { 416df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 417df930be7Sderaadt 418df930be7Sderaadt /* 419df930be7Sderaadt * Move this drive to the end of the queue to give others a `fair' 420df930be7Sderaadt * chance. We only force a switch if N operations are completed while 421df930be7Sderaadt * another drive is waiting to be serviced, since there is a long motor 422df930be7Sderaadt * startup delay whenever we switch. 423df930be7Sderaadt */ 424df930be7Sderaadt if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) { 425df930be7Sderaadt fd->sc_ops = 0; 4263af98d30Sdownsj TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 427df930be7Sderaadt if (bp->b_actf) { 4283af98d30Sdownsj TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, 4293af98d30Sdownsj sc_drivechain); 430df930be7Sderaadt } else 431df930be7Sderaadt fd->sc_q.b_active = 0; 432df930be7Sderaadt } 433df930be7Sderaadt bp->b_resid = fd->sc_bcount; 434df930be7Sderaadt fd->sc_skip = 0; 435df930be7Sderaadt fd->sc_q.b_actf = bp->b_actf; 4363dbef52bSderaadt 437df930be7Sderaadt biodone(bp); 438df930be7Sderaadt /* turn off motor 5s from now */ 439df930be7Sderaadt timeout(fd_motor_off, fd, 5 * hz); 440df930be7Sderaadt fdc->sc_state = DEVIDLE; 441df930be7Sderaadt } 442df930be7Sderaadt 443df930be7Sderaadt int 4442226ce33Sderaadt fdread(dev, uio, flags) 445df930be7Sderaadt dev_t dev; 446df930be7Sderaadt struct uio *uio; 4472226ce33Sderaadt int flags; 448df930be7Sderaadt { 449df930be7Sderaadt 450df930be7Sderaadt return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio)); 451df930be7Sderaadt } 452df930be7Sderaadt 453df930be7Sderaadt int 4542226ce33Sderaadt fdwrite(dev, uio, flags) 455df930be7Sderaadt dev_t dev; 456df930be7Sderaadt struct uio *uio; 4572226ce33Sderaadt int flags; 458df930be7Sderaadt { 459df930be7Sderaadt 460df930be7Sderaadt return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio)); 461df930be7Sderaadt } 462df930be7Sderaadt 463df930be7Sderaadt void 464df930be7Sderaadt fd_set_motor(fdc, reset) 465df930be7Sderaadt struct fdc_softc *fdc; 466df930be7Sderaadt int reset; 467df930be7Sderaadt { 468df930be7Sderaadt struct fd_softc *fd; 469df930be7Sderaadt u_char status; 470df930be7Sderaadt int n; 471df930be7Sderaadt 4723af98d30Sdownsj if ((fd = fdc->sc_link.fdlink.sc_drives.tqh_first) != NULL) 473df930be7Sderaadt status = fd->sc_drive; 474df930be7Sderaadt else 475df930be7Sderaadt status = 0; 476df930be7Sderaadt if (!reset) 477df930be7Sderaadt status |= FDO_FRST | FDO_FDMAEN; 478df930be7Sderaadt for (n = 0; n < 4; n++) 4793af98d30Sdownsj if ((fd = fdc->sc_link.fdlink.sc_fd[n]) 4803af98d30Sdownsj && (fd->sc_flags & FD_MOTOR)) 481df930be7Sderaadt status |= FDO_MOEN(n); 482b9a85264Sniklas bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, status); 483df930be7Sderaadt } 484df930be7Sderaadt 485df930be7Sderaadt void 486df930be7Sderaadt fd_motor_off(arg) 487df930be7Sderaadt void *arg; 488df930be7Sderaadt { 489df930be7Sderaadt struct fd_softc *fd = arg; 490df930be7Sderaadt int s; 491df930be7Sderaadt 492df930be7Sderaadt s = splbio(); 493df930be7Sderaadt fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); 494df930be7Sderaadt fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0); 495df930be7Sderaadt splx(s); 496df930be7Sderaadt } 497df930be7Sderaadt 498df930be7Sderaadt void 499df930be7Sderaadt fd_motor_on(arg) 500df930be7Sderaadt void *arg; 501df930be7Sderaadt { 502df930be7Sderaadt struct fd_softc *fd = arg; 503df930be7Sderaadt struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 504df930be7Sderaadt int s; 505df930be7Sderaadt 506df930be7Sderaadt s = splbio(); 507df930be7Sderaadt fd->sc_flags &= ~FD_MOTOR_WAIT; 5083af98d30Sdownsj if ((fdc->sc_link.fdlink.sc_drives.tqh_first == fd) 5093af98d30Sdownsj && (fdc->sc_state == MOTORWAIT)) 5103041a0b9Sdownsj (void) fdintr(fdc); 511df930be7Sderaadt splx(s); 512df930be7Sderaadt } 513df930be7Sderaadt 514df930be7Sderaadt int 5152226ce33Sderaadt fdopen(dev, flags, mode, p) 516df930be7Sderaadt dev_t dev; 517df930be7Sderaadt int flags; 5182226ce33Sderaadt int mode; 5192226ce33Sderaadt struct proc *p; 520df930be7Sderaadt { 521df930be7Sderaadt int unit; 522df930be7Sderaadt struct fd_softc *fd; 523df930be7Sderaadt struct fd_type *type; 524df930be7Sderaadt 525df930be7Sderaadt unit = FDUNIT(dev); 526d724e01aSderaadt if (unit >= fd_cd.cd_ndevs) 527df930be7Sderaadt return ENXIO; 528d724e01aSderaadt fd = fd_cd.cd_devs[unit]; 529df930be7Sderaadt if (fd == 0) 530df930be7Sderaadt return ENXIO; 531df930be7Sderaadt type = fd_dev_to_type(fd, dev); 532df930be7Sderaadt if (type == NULL) 533df930be7Sderaadt return ENXIO; 534df930be7Sderaadt 535df930be7Sderaadt if ((fd->sc_flags & FD_OPEN) != 0 && 536df930be7Sderaadt fd->sc_type != type) 537df930be7Sderaadt return EBUSY; 538df930be7Sderaadt 539df930be7Sderaadt fd->sc_type = type; 540df930be7Sderaadt fd->sc_cylin = -1; 541df930be7Sderaadt fd->sc_flags |= FD_OPEN; 542df930be7Sderaadt 543df930be7Sderaadt return 0; 544df930be7Sderaadt } 545df930be7Sderaadt 546df930be7Sderaadt int 5472226ce33Sderaadt fdclose(dev, flags, mode, p) 548df930be7Sderaadt dev_t dev; 549df930be7Sderaadt int flags; 5502226ce33Sderaadt int mode; 5512226ce33Sderaadt struct proc *p; 552df930be7Sderaadt { 553d724e01aSderaadt struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 554df930be7Sderaadt 555df930be7Sderaadt fd->sc_flags &= ~FD_OPEN; 5561921243fSdownsj fd->sc_opts &= ~FDOPT_NORETRY; 557df930be7Sderaadt return 0; 558df930be7Sderaadt } 559df930be7Sderaadt 5603041a0b9Sdownsj int 5613041a0b9Sdownsj fdsize(dev) 5623041a0b9Sdownsj dev_t dev; 563df930be7Sderaadt { 564df930be7Sderaadt 5653041a0b9Sdownsj /* Swapping to floppies would not make sense. */ 5663041a0b9Sdownsj return -1; 567df930be7Sderaadt } 568df930be7Sderaadt 569df930be7Sderaadt int 5703041a0b9Sdownsj fddump(dev, blkno, va, size) 5713041a0b9Sdownsj dev_t dev; 5723041a0b9Sdownsj daddr_t blkno; 5733041a0b9Sdownsj caddr_t va; 5743041a0b9Sdownsj size_t size; 575df930be7Sderaadt { 5763041a0b9Sdownsj 5773041a0b9Sdownsj /* Not implemented. */ 5783041a0b9Sdownsj return ENXIO; 5793041a0b9Sdownsj } 5803041a0b9Sdownsj 5813041a0b9Sdownsj /* 5823041a0b9Sdownsj * Called from the controller. 5833041a0b9Sdownsj */ 5843041a0b9Sdownsj int 5853041a0b9Sdownsj fdintr(fdc) 5863041a0b9Sdownsj struct fdc_softc *fdc; 5873041a0b9Sdownsj { 588df930be7Sderaadt #define st0 fdc->sc_status[0] 589df930be7Sderaadt #define cyl fdc->sc_status[1] 590df930be7Sderaadt struct fd_softc *fd; 591df930be7Sderaadt struct buf *bp; 592b9a85264Sniklas bus_space_tag_t iot = fdc->sc_iot; 593b9a85264Sniklas bus_space_handle_t ioh = fdc->sc_ioh; 5942226ce33Sderaadt int read, head, sec, i, nblks; 595df930be7Sderaadt struct fd_type *type; 5961921243fSdownsj struct fd_formb *finfo = NULL; 59776c8686eSdownsj int fd_bsize, bf; 598df930be7Sderaadt 599df930be7Sderaadt loop: 6003041a0b9Sdownsj /* Is there a transfer to this drive? If not, deactivate drive. */ 6013af98d30Sdownsj fd = fdc->sc_link.fdlink.sc_drives.tqh_first; 602df930be7Sderaadt if (fd == NULL) { 603df930be7Sderaadt fdc->sc_state = DEVIDLE; 604df930be7Sderaadt return 1; 605df930be7Sderaadt } 60676c8686eSdownsj fd_bsize = 128 << fd->sc_type->secsize; 60776c8686eSdownsj bf = fd_bsize / FDC_BSIZE; 608df930be7Sderaadt 609df930be7Sderaadt bp = fd->sc_q.b_actf; 610df930be7Sderaadt if (bp == NULL) { 611df930be7Sderaadt fd->sc_ops = 0; 6123af98d30Sdownsj TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain); 613df930be7Sderaadt fd->sc_q.b_active = 0; 614df930be7Sderaadt goto loop; 615df930be7Sderaadt } 616df930be7Sderaadt 6171921243fSdownsj if (bp->b_flags & B_FORMAT) 6181921243fSdownsj finfo = (struct fd_formb *)bp->b_data; 6191921243fSdownsj 620df930be7Sderaadt switch (fdc->sc_state) { 621df930be7Sderaadt case DEVIDLE: 622df930be7Sderaadt fdc->sc_errors = 0; 623df930be7Sderaadt fd->sc_skip = 0; 624df930be7Sderaadt fd->sc_bcount = bp->b_bcount; 62576c8686eSdownsj fd->sc_blkno = bp->b_blkno / (fd_bsize / DEV_BSIZE); 626df930be7Sderaadt untimeout(fd_motor_off, fd); 627df930be7Sderaadt if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { 628df930be7Sderaadt fdc->sc_state = MOTORWAIT; 629df930be7Sderaadt return 1; 630df930be7Sderaadt } 631df930be7Sderaadt if ((fd->sc_flags & FD_MOTOR) == 0) { 632df930be7Sderaadt /* Turn on the motor, being careful about pairing. */ 6333af98d30Sdownsj struct fd_softc *ofd = 6343af98d30Sdownsj fdc->sc_link.fdlink.sc_fd[fd->sc_drive ^ 1]; 635df930be7Sderaadt if (ofd && ofd->sc_flags & FD_MOTOR) { 636df930be7Sderaadt untimeout(fd_motor_off, ofd); 637df930be7Sderaadt ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); 638df930be7Sderaadt } 639df930be7Sderaadt fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; 640df930be7Sderaadt fd_set_motor(fdc, 0); 641df930be7Sderaadt fdc->sc_state = MOTORWAIT; 642df930be7Sderaadt /* Allow .25s for motor to stabilize. */ 643df930be7Sderaadt timeout(fd_motor_on, fd, hz / 4); 644df930be7Sderaadt return 1; 645df930be7Sderaadt } 646df930be7Sderaadt /* Make sure the right drive is selected. */ 647df930be7Sderaadt fd_set_motor(fdc, 0); 648df930be7Sderaadt 649df930be7Sderaadt /* fall through */ 650df930be7Sderaadt case DOSEEK: 651df930be7Sderaadt doseek: 652df930be7Sderaadt if (fd->sc_cylin == bp->b_cylin) 653df930be7Sderaadt goto doio; 654df930be7Sderaadt 655b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */ 656b9a85264Sniklas out_fdc(iot, ioh, fd->sc_type->steprate); 657b9a85264Sniklas out_fdc(iot, ioh, 6); /* XXX head load time == 6ms */ 658df930be7Sderaadt 659b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SEEK); /* seek function */ 660b9a85264Sniklas out_fdc(iot, ioh, fd->sc_drive); /* drive number */ 661b9a85264Sniklas out_fdc(iot, ioh, bp->b_cylin * fd->sc_type->step); 662df930be7Sderaadt 663df930be7Sderaadt fd->sc_cylin = -1; 664df930be7Sderaadt fdc->sc_state = SEEKWAIT; 6651eb11dafSderaadt 6661eb11dafSderaadt fd->sc_dk.dk_seek++; 6671eb11dafSderaadt disk_busy(&fd->sc_dk); 6681eb11dafSderaadt 6693041a0b9Sdownsj timeout(fdtimeout, fd, 4 * hz); 670df930be7Sderaadt return 1; 671df930be7Sderaadt 672df930be7Sderaadt case DOIO: 673df930be7Sderaadt doio: 674df930be7Sderaadt type = fd->sc_type; 6751921243fSdownsj if (finfo) 6761921243fSdownsj fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) - 6771921243fSdownsj (char *)finfo; 678df930be7Sderaadt sec = fd->sc_blkno % type->seccyl; 679df930be7Sderaadt nblks = type->seccyl - sec; 68076c8686eSdownsj nblks = min(nblks, fd->sc_bcount / fd_bsize); 68176c8686eSdownsj nblks = min(nblks, FDC_MAXIOSIZE / fd_bsize); 682df930be7Sderaadt fd->sc_nblks = nblks; 68376c8686eSdownsj fd->sc_nbytes = finfo ? bp->b_bcount : nblks * fd_bsize; 684df930be7Sderaadt head = sec / type->sectrac; 685df930be7Sderaadt sec -= head * type->sectrac; 686df930be7Sderaadt #ifdef DIAGNOSTIC 687df930be7Sderaadt {int block; 688df930be7Sderaadt block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec; 689df930be7Sderaadt if (block != fd->sc_blkno) { 6903041a0b9Sdownsj printf("fdintr: block %d != blkno %d\n", block, fd->sc_blkno); 691df930be7Sderaadt #ifdef DDB 692df930be7Sderaadt Debugger(); 693df930be7Sderaadt #endif 694df930be7Sderaadt }} 695df930be7Sderaadt #endif 696fa7cc844Sniklas read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; 697df930be7Sderaadt #ifdef NEWCONFIG 698df930be7Sderaadt at_dma(read, bp->b_data + fd->sc_skip, fd->sc_nbytes, 699df930be7Sderaadt fdc->sc_drq); 700df930be7Sderaadt #else 701*aee4fbd6Sniklas isadma_start(bp->b_data + fd->sc_skip, fd->sc_nbytes, 702*aee4fbd6Sniklas fdc->sc_drq, read); 703df930be7Sderaadt #endif 704b9a85264Sniklas bus_space_write_1(iot, ioh, fdctl, type->rate); 705df930be7Sderaadt #ifdef FD_DEBUG 7063041a0b9Sdownsj printf("fdintr: %s drive %d track %d head %d sec %d nblks %d\n", 707df930be7Sderaadt read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head, 708df930be7Sderaadt sec, nblks); 709df930be7Sderaadt #endif 7101921243fSdownsj if (finfo) { 7111921243fSdownsj /* formatting */ 712b9a85264Sniklas if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) { 7131921243fSdownsj fdc->sc_errors = 4; 7143041a0b9Sdownsj fdretry(fd); 7151921243fSdownsj goto loop; 7161921243fSdownsj } 717b9a85264Sniklas out_fdc(iot, ioh, (head << 2) | fd->sc_drive); 718b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_secshift); 719b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_nsecs); 720b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_gaplen); 721b9a85264Sniklas out_fdc(iot, ioh, finfo->fd_formb_fillbyte); 7221921243fSdownsj } else { 723df930be7Sderaadt if (read) 724b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_READ); /* READ */ 725df930be7Sderaadt else 726b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_WRITE);/* WRITE */ 727b9a85264Sniklas out_fdc(iot, ioh, (head << 2) | fd->sc_drive); 728b9a85264Sniklas out_fdc(iot, ioh, fd->sc_cylin); /* track */ 729b9a85264Sniklas out_fdc(iot, ioh, head); 730b9a85264Sniklas out_fdc(iot, ioh, sec + 1); /* sec +1 */ 731b9a85264Sniklas out_fdc(iot, ioh, type->secsize); /* sec size */ 732b9a85264Sniklas out_fdc(iot, ioh, type->sectrac); /* secs/track */ 733b9a85264Sniklas out_fdc(iot, ioh, type->gap1); /* gap1 size */ 734b9a85264Sniklas out_fdc(iot, ioh, type->datalen); /* data len */ 7351921243fSdownsj } 736df930be7Sderaadt fdc->sc_state = IOCOMPLETE; 7371eb11dafSderaadt 7381eb11dafSderaadt disk_busy(&fd->sc_dk); 7391eb11dafSderaadt 740df930be7Sderaadt /* allow 2 seconds for operation */ 7413041a0b9Sdownsj timeout(fdtimeout, fd, 2 * hz); 742df930be7Sderaadt return 1; /* will return later */ 743df930be7Sderaadt 744df930be7Sderaadt case SEEKWAIT: 7453041a0b9Sdownsj untimeout(fdtimeout, fd); 746df930be7Sderaadt fdc->sc_state = SEEKCOMPLETE; 747df930be7Sderaadt /* allow 1/50 second for heads to settle */ 748df930be7Sderaadt timeout(fdcpseudointr, fdc, hz / 50); 749df930be7Sderaadt return 1; 750df930be7Sderaadt 751df930be7Sderaadt case SEEKCOMPLETE: 7521eb11dafSderaadt disk_unbusy(&fd->sc_dk, 0); /* no data on seek */ 7531eb11dafSderaadt 754df930be7Sderaadt /* Make sure seek really happened. */ 755b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 756df930be7Sderaadt if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || 757df930be7Sderaadt cyl != bp->b_cylin * fd->sc_type->step) { 758df930be7Sderaadt #ifdef FD_DEBUG 759df930be7Sderaadt fdcstatus(&fd->sc_dev, 2, "seek failed"); 760df930be7Sderaadt #endif 7613041a0b9Sdownsj fdretry(fd); 762df930be7Sderaadt goto loop; 763df930be7Sderaadt } 764df930be7Sderaadt fd->sc_cylin = bp->b_cylin; 765df930be7Sderaadt goto doio; 766df930be7Sderaadt 767df930be7Sderaadt case IOTIMEDOUT: 768df930be7Sderaadt #ifdef NEWCONFIG 769df930be7Sderaadt at_dma_abort(fdc->sc_drq); 770df930be7Sderaadt #else 771*aee4fbd6Sniklas isadma_abort(fdc->sc_drq); 772df930be7Sderaadt #endif 773df930be7Sderaadt case SEEKTIMEDOUT: 774df930be7Sderaadt case RECALTIMEDOUT: 775df930be7Sderaadt case RESETTIMEDOUT: 7763041a0b9Sdownsj fdretry(fd); 777df930be7Sderaadt goto loop; 778df930be7Sderaadt 779df930be7Sderaadt case IOCOMPLETE: /* IO DONE, post-analyze */ 7803041a0b9Sdownsj untimeout(fdtimeout, fd); 7811eb11dafSderaadt 7821eb11dafSderaadt disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid)); 7831eb11dafSderaadt 784df930be7Sderaadt if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) { 785df930be7Sderaadt #ifdef NEWCONFIG 786df930be7Sderaadt at_dma_abort(fdc->sc_drq); 787df930be7Sderaadt #else 788*aee4fbd6Sniklas isadma_abort(fdc->sc_drq); 789df930be7Sderaadt #endif 790df930be7Sderaadt #ifdef FD_DEBUG 791df930be7Sderaadt fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ? 792df930be7Sderaadt "read failed" : "write failed"); 793df930be7Sderaadt printf("blkno %d nblks %d\n", 794df930be7Sderaadt fd->sc_blkno, fd->sc_nblks); 795df930be7Sderaadt #endif 7963041a0b9Sdownsj fdretry(fd); 797df930be7Sderaadt goto loop; 798df930be7Sderaadt } 799df930be7Sderaadt #ifdef NEWCONFIG 800df930be7Sderaadt at_dma_terminate(fdc->sc_drq); 801df930be7Sderaadt #else 802fa7cc844Sniklas read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; 803*aee4fbd6Sniklas isadma_done(fdc->sc_drq); 804df930be7Sderaadt #endif 805df930be7Sderaadt if (fdc->sc_errors) { 806df930be7Sderaadt diskerr(bp, "fd", "soft error", LOG_PRINTF, 80776c8686eSdownsj fd->sc_skip / fd_bsize, (struct disklabel *)NULL); 808df930be7Sderaadt printf("\n"); 809df930be7Sderaadt fdc->sc_errors = 0; 810df930be7Sderaadt } 811df930be7Sderaadt fd->sc_blkno += fd->sc_nblks; 812df930be7Sderaadt fd->sc_skip += fd->sc_nbytes; 813df930be7Sderaadt fd->sc_bcount -= fd->sc_nbytes; 8141921243fSdownsj if (!finfo && fd->sc_bcount > 0) { 815df930be7Sderaadt bp->b_cylin = fd->sc_blkno / fd->sc_type->seccyl; 816df930be7Sderaadt goto doseek; 817df930be7Sderaadt } 818df930be7Sderaadt fdfinish(fd, bp); 819df930be7Sderaadt goto loop; 820df930be7Sderaadt 821df930be7Sderaadt case DORESET: 822df930be7Sderaadt /* try a reset, keep motor on */ 823df930be7Sderaadt fd_set_motor(fdc, 1); 824df930be7Sderaadt delay(100); 825df930be7Sderaadt fd_set_motor(fdc, 0); 826df930be7Sderaadt fdc->sc_state = RESETCOMPLETE; 8273041a0b9Sdownsj timeout(fdtimeout, fd, hz / 2); 828df930be7Sderaadt return 1; /* will return later */ 829df930be7Sderaadt 830df930be7Sderaadt case RESETCOMPLETE: 8313041a0b9Sdownsj untimeout(fdtimeout, fd); 832df930be7Sderaadt /* clear the controller output buffer */ 833df930be7Sderaadt for (i = 0; i < 4; i++) { 834b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 835df930be7Sderaadt (void) fdcresult(fdc); 836df930be7Sderaadt } 837df930be7Sderaadt 838df930be7Sderaadt /* fall through */ 839df930be7Sderaadt case DORECAL: 840b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_RECAL); /* recal function */ 841b9a85264Sniklas out_fdc(iot, ioh, fd->sc_drive); 842df930be7Sderaadt fdc->sc_state = RECALWAIT; 8433041a0b9Sdownsj timeout(fdtimeout, fd, 5 * hz); 844df930be7Sderaadt return 1; /* will return later */ 845df930be7Sderaadt 846df930be7Sderaadt case RECALWAIT: 8473041a0b9Sdownsj untimeout(fdtimeout, fd); 848df930be7Sderaadt fdc->sc_state = RECALCOMPLETE; 849df930be7Sderaadt /* allow 1/30 second for heads to settle */ 850df930be7Sderaadt timeout(fdcpseudointr, fdc, hz / 30); 851df930be7Sderaadt return 1; /* will return later */ 852df930be7Sderaadt 853df930be7Sderaadt case RECALCOMPLETE: 854b9a85264Sniklas out_fdc(iot, ioh, NE7CMD_SENSEI); 855df930be7Sderaadt if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { 856df930be7Sderaadt #ifdef FD_DEBUG 857df930be7Sderaadt fdcstatus(&fd->sc_dev, 2, "recalibrate failed"); 858df930be7Sderaadt #endif 8593041a0b9Sdownsj fdretry(fd); 860df930be7Sderaadt goto loop; 861df930be7Sderaadt } 862df930be7Sderaadt fd->sc_cylin = 0; 863df930be7Sderaadt goto doseek; 864df930be7Sderaadt 865df930be7Sderaadt case MOTORWAIT: 866df930be7Sderaadt if (fd->sc_flags & FD_MOTOR_WAIT) 867df930be7Sderaadt return 1; /* time's not up yet */ 868df930be7Sderaadt goto doseek; 869df930be7Sderaadt 870df930be7Sderaadt default: 871df930be7Sderaadt fdcstatus(&fd->sc_dev, 0, "stray interrupt"); 872df930be7Sderaadt return 1; 873df930be7Sderaadt } 874df930be7Sderaadt #ifdef DIAGNOSTIC 8753041a0b9Sdownsj panic("fdintr: impossible"); 876df930be7Sderaadt #endif 877df930be7Sderaadt #undef st0 878df930be7Sderaadt #undef cyl 879df930be7Sderaadt } 880df930be7Sderaadt 881df930be7Sderaadt void 8823041a0b9Sdownsj fdtimeout(arg) 8833041a0b9Sdownsj void *arg; 884df930be7Sderaadt { 8853041a0b9Sdownsj struct fd_softc *fd = arg; 8863041a0b9Sdownsj struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 8873041a0b9Sdownsj int s; 888df930be7Sderaadt 8893041a0b9Sdownsj s = splbio(); 8903041a0b9Sdownsj #ifdef DEBUG 8913041a0b9Sdownsj log(LOG_ERR,"fdtimeout: state %d\n", fdc->sc_state); 8923041a0b9Sdownsj #endif 8933041a0b9Sdownsj fdcstatus(&fd->sc_dev, 0, "timeout"); 8943041a0b9Sdownsj 8953041a0b9Sdownsj if (fd->sc_q.b_actf) 8963041a0b9Sdownsj fdc->sc_state++; 8973041a0b9Sdownsj else 8983041a0b9Sdownsj fdc->sc_state = DEVIDLE; 8993041a0b9Sdownsj 9003041a0b9Sdownsj (void) fdintr(fdc); 9013041a0b9Sdownsj splx(s); 9023041a0b9Sdownsj } 9033041a0b9Sdownsj 9043041a0b9Sdownsj void 9053041a0b9Sdownsj fdretry(fd) 9063041a0b9Sdownsj struct fd_softc *fd; 9073041a0b9Sdownsj { 9083041a0b9Sdownsj struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent; 9093041a0b9Sdownsj struct buf *bp = fd->sc_q.b_actf; 910df930be7Sderaadt 9111921243fSdownsj if (fd->sc_opts & FDOPT_NORETRY) 9121921243fSdownsj goto fail; 913df930be7Sderaadt switch (fdc->sc_errors) { 914df930be7Sderaadt case 0: 915df930be7Sderaadt /* try again */ 916da3b7780Shannken fdc->sc_state = DOSEEK; 917df930be7Sderaadt break; 918df930be7Sderaadt 919df930be7Sderaadt case 1: case 2: case 3: 920df930be7Sderaadt /* didn't work; try recalibrating */ 921df930be7Sderaadt fdc->sc_state = DORECAL; 922df930be7Sderaadt break; 923df930be7Sderaadt 924df930be7Sderaadt case 4: 925df930be7Sderaadt /* still no go; reset the bastard */ 926df930be7Sderaadt fdc->sc_state = DORESET; 927df930be7Sderaadt break; 928df930be7Sderaadt 929df930be7Sderaadt default: 9301921243fSdownsj fail: 931df930be7Sderaadt diskerr(bp, "fd", "hard error", LOG_PRINTF, 93276c8686eSdownsj fd->sc_skip / (128 << fd->sc_type->secsize), 93376c8686eSdownsj (struct disklabel *)NULL); 934df930be7Sderaadt printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n", 935df930be7Sderaadt fdc->sc_status[0], NE7_ST0BITS, 936df930be7Sderaadt fdc->sc_status[1], NE7_ST1BITS, 937df930be7Sderaadt fdc->sc_status[2], NE7_ST2BITS, 938df930be7Sderaadt fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); 939df930be7Sderaadt 940df930be7Sderaadt bp->b_flags |= B_ERROR; 941df930be7Sderaadt bp->b_error = EIO; 942df930be7Sderaadt fdfinish(fd, bp); 943df930be7Sderaadt } 944df930be7Sderaadt fdc->sc_errors++; 945df930be7Sderaadt } 946df930be7Sderaadt 947df930be7Sderaadt int 9482226ce33Sderaadt fdioctl(dev, cmd, addr, flag, p) 949df930be7Sderaadt dev_t dev; 950df930be7Sderaadt u_long cmd; 951df930be7Sderaadt caddr_t addr; 952df930be7Sderaadt int flag; 9532226ce33Sderaadt struct proc *p; 954df930be7Sderaadt { 955d724e01aSderaadt struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 9563c27f4f2Sderaadt struct disklabel dl, *lp = &dl; 9573c27f4f2Sderaadt struct cpu_disklabel cdl; 95834f3058cSderaadt char *errstring; 959df930be7Sderaadt int error; 960df930be7Sderaadt 961df930be7Sderaadt switch (cmd) { 9624b64fdbfSderaadt case MTIOCTOP: 9634b64fdbfSderaadt if (((struct mtop *)addr)->mt_op != MTOFFL) 9644b64fdbfSderaadt return EIO; 9654b64fdbfSderaadt return (0); 966df930be7Sderaadt case DIOCGDINFO: 9673c27f4f2Sderaadt bzero(lp, sizeof(*lp)); 9683c27f4f2Sderaadt bzero(&cdl, sizeof(struct cpu_disklabel)); 969df930be7Sderaadt 9703c27f4f2Sderaadt lp->d_secsize = 128 << fd->sc_type->secsize; 9713c27f4f2Sderaadt lp->d_secpercyl = fd->sc_type->seccyl; 9723c27f4f2Sderaadt lp->d_ntracks = fd->sc_type->heads; 9730ebd42eeSderaadt lp->d_nsectors = fd->sc_type->sectrac; 9743c27f4f2Sderaadt lp->d_ncylinders = fd->sc_type->tracks; 975df930be7Sderaadt 9763c27f4f2Sderaadt strncpy(lp->d_typename, "floppy disk", 16); 9773c27f4f2Sderaadt lp->d_type = DTYPE_FLOPPY; 9783c27f4f2Sderaadt strncpy(lp->d_packname, "fictitious", 16); 9793c27f4f2Sderaadt lp->d_secperunit = fd->sc_type->size; 9803c27f4f2Sderaadt lp->d_rpm = 300; 9813c27f4f2Sderaadt lp->d_interleave = 1; 9823c27f4f2Sderaadt lp->d_flags = D_REMOVABLE; 983df930be7Sderaadt 9843c27f4f2Sderaadt lp->d_partitions[RAW_PART].p_offset = 0; 9853c27f4f2Sderaadt lp->d_partitions[RAW_PART].p_size = 9863c27f4f2Sderaadt lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 9873c27f4f2Sderaadt lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 9883c27f4f2Sderaadt lp->d_npartitions = RAW_PART + 1; 9893c27f4f2Sderaadt 9903c27f4f2Sderaadt lp->d_magic = DISKMAGIC; 9913c27f4f2Sderaadt lp->d_magic2 = DISKMAGIC; 9923c27f4f2Sderaadt lp->d_checksum = dkcksum(lp); 9933c27f4f2Sderaadt 99434f3058cSderaadt errstring = readdisklabel(dev, fdstrategy, lp, &cdl); 995a61ae37bSderaadt if (errstring) { 996a61ae37bSderaadt /*printf("%s: %s\n", fd->sc_dev.dv_xname, errstring); */ 997a61ae37bSderaadt } 9983c27f4f2Sderaadt 9993c27f4f2Sderaadt *(struct disklabel *)addr = *lp; 1000df930be7Sderaadt return 0; 1001df930be7Sderaadt 1002df930be7Sderaadt case DIOCWLABEL: 1003df930be7Sderaadt if ((flag & FWRITE) == 0) 1004df930be7Sderaadt return EBADF; 1005df930be7Sderaadt /* XXX do something */ 1006df930be7Sderaadt return 0; 1007df930be7Sderaadt 1008df930be7Sderaadt case DIOCWDINFO: 1009df930be7Sderaadt if ((flag & FWRITE) == 0) 1010df930be7Sderaadt return EBADF; 1011df930be7Sderaadt 10123c27f4f2Sderaadt error = setdisklabel(lp, (struct disklabel *)addr, 0, NULL); 1013df930be7Sderaadt if (error) 1014df930be7Sderaadt return error; 1015df930be7Sderaadt 10163c27f4f2Sderaadt error = writedisklabel(dev, fdstrategy, lp, NULL); 1017df930be7Sderaadt return error; 1018df930be7Sderaadt 10191921243fSdownsj case FD_FORM: 10201921243fSdownsj if((flag & FWRITE) == 0) 10211921243fSdownsj return EBADF; /* must be opened for writing */ 10221921243fSdownsj else if(((struct fd_formb *)addr)->format_version != 10231921243fSdownsj FD_FORMAT_VERSION) 10241921243fSdownsj return EINVAL; /* wrong version of formatting prog */ 10251921243fSdownsj else 10261921243fSdownsj return fdformat(dev, (struct fd_formb *)addr, p); 10271921243fSdownsj break; 10281921243fSdownsj 10291921243fSdownsj case FD_GTYPE: /* get drive type */ 10301921243fSdownsj *(struct fd_type *)addr = *fd->sc_type; 10311921243fSdownsj return 0; 10321921243fSdownsj 10331921243fSdownsj case FD_GOPTS: /* get drive options */ 10341921243fSdownsj *(int *)addr = fd->sc_opts; 10351921243fSdownsj return 0; 10361921243fSdownsj 10371921243fSdownsj case FD_SOPTS: /* set drive options */ 10381921243fSdownsj fd->sc_opts = *(int *)addr; 10391921243fSdownsj return 0; 10401921243fSdownsj 1041df930be7Sderaadt default: 1042df930be7Sderaadt return ENOTTY; 1043df930be7Sderaadt } 1044df930be7Sderaadt 1045df930be7Sderaadt #ifdef DIAGNOSTIC 1046df930be7Sderaadt panic("fdioctl: impossible"); 1047df930be7Sderaadt #endif 1048df930be7Sderaadt } 10491921243fSdownsj 10501921243fSdownsj int 10511921243fSdownsj fdformat(dev, finfo, p) 10521921243fSdownsj dev_t dev; 10531921243fSdownsj struct fd_formb *finfo; 10541921243fSdownsj struct proc *p; 10551921243fSdownsj { 10561921243fSdownsj int rv = 0, s; 10571921243fSdownsj struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)]; 10581921243fSdownsj struct fd_type *type = fd->sc_type; 10591921243fSdownsj struct buf *bp; 106076c8686eSdownsj int fd_bsize = 128 << fd->sc_type->secsize; 10611921243fSdownsj 10621921243fSdownsj /* set up a buffer header for fdstrategy() */ 10631921243fSdownsj bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); 10641921243fSdownsj if(bp == 0) 10651921243fSdownsj return ENOBUFS; 10661921243fSdownsj bzero((void *)bp, sizeof(struct buf)); 10671921243fSdownsj bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; 10681921243fSdownsj bp->b_proc = p; 10691921243fSdownsj bp->b_dev = dev; 10701921243fSdownsj 10711921243fSdownsj /* 10721921243fSdownsj * calculate a fake blkno, so fdstrategy() would initiate a 10731921243fSdownsj * seek to the requested cylinder 10741921243fSdownsj */ 10751921243fSdownsj bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads) 107676c8686eSdownsj + finfo->head * type->sectrac) * fd_bsize / DEV_BSIZE; 10771921243fSdownsj 10781921243fSdownsj bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; 10791921243fSdownsj bp->b_data = (caddr_t)finfo; 10801921243fSdownsj 10811921243fSdownsj #ifdef DEBUG 10821921243fSdownsj printf("fdformat: blkno %x count %x\n", bp->b_blkno, bp->b_bcount); 10831921243fSdownsj #endif 10841921243fSdownsj 10851921243fSdownsj /* now do the format */ 10861921243fSdownsj fdstrategy(bp); 10871921243fSdownsj 10881921243fSdownsj /* ...and wait for it to complete */ 10891921243fSdownsj s = splbio(); 10901921243fSdownsj while(!(bp->b_flags & B_DONE)) 10911921243fSdownsj { 10921921243fSdownsj rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 0); 10931921243fSdownsj if(rv == EWOULDBLOCK) 10941921243fSdownsj /*break*/; 10951921243fSdownsj } 10961921243fSdownsj splx(s); 10971921243fSdownsj 10981921243fSdownsj if(rv == EWOULDBLOCK) { 10991921243fSdownsj /* timed out */ 11001921243fSdownsj rv = EIO; 11011921243fSdownsj /* XXX what to do to the buf? it will eventually fall 11021921243fSdownsj out as finished, but ... ?*/ 11031921243fSdownsj /*biodone(bp);*/ 11041921243fSdownsj } 11051921243fSdownsj if(bp->b_flags & B_ERROR) 11061921243fSdownsj rv = bp->b_error; 11071921243fSdownsj free(bp, M_TEMP); 11081921243fSdownsj return rv; 11091921243fSdownsj } 1110