| 1 | /*#define DEBUG 1*/ |
| 2 | /*- |
| 3 | * Copyright (c) 1990 The Regents of the University of California. |
| 4 | * All rights reserved. |
| 5 | * |
| 6 | * This code is derived from software contributed to Berkeley by |
| 7 | * Don Ahn. |
| 8 | * |
| 9 | * Redistribution and use in source and binary forms, with or without |
| 10 | * modification, are permitted provided that the following conditions |
| 11 | * are met: |
| 12 | * 1. Redistributions of source code must retain the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer. |
| 14 | * 2. Redistributions in binary form must reproduce the above copyright |
| 15 | * notice, this list of conditions and the following disclaimer in the |
| 16 | * documentation and/or other materials provided with the distribution. |
| 17 | * 3. All advertising materials mentioning features or use of this software |
| 18 | * must display the following acknowledgement: |
| 19 | * This product includes software developed by the University of |
| 20 | * California, Berkeley and its contributors. |
| 21 | * 4. Neither the name of the University nor the names of its contributors |
| 22 | * may be used to endorse or promote products derived from this software |
| 23 | * without specific prior written permission. |
| 24 | * |
| 25 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 26 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 34 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 35 | * SUCH DAMAGE. |
| 36 | * |
| 37 | * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 |
| 38 | * $Id: fd.c,v 1.6 1993/09/23 15:22:57 rgrimes Exp $ |
| 39 | * |
| 40 | */ |
| 41 | |
| 42 | #include "fd.h" |
| 43 | #if NFD > 0 |
| 44 | |
| 45 | #include "param.h" |
| 46 | #include "dkbad.h" |
| 47 | #include "systm.h" |
| 48 | #include "kernel.h" |
| 49 | #include "conf.h" |
| 50 | #include "file.h" |
| 51 | #include "ioctl.h" |
| 52 | #include "disklabel.h" |
| 53 | #include "buf.h" |
| 54 | #include "uio.h" |
| 55 | #include "syslog.h" |
| 56 | #include "i386/isa/isa.h" |
| 57 | #include "i386/isa/isa_device.h" |
| 58 | #include "i386/isa/fdreg.h" |
| 59 | #include "i386/isa/icu.h" |
| 60 | #include "i386/isa/rtc.h" |
| 61 | #undef NFD |
| 62 | #define NFD 2 |
| 63 | |
| 64 | #define FDUNIT(s) ((s>>3)&1) |
| 65 | #define FDTYPE(s) ((s)&7) |
| 66 | |
| 67 | #define b_cylin b_resid |
| 68 | #define FDBLK 512 |
| 69 | #define NUMTYPES 4 |
| 70 | |
| 71 | struct fd_type { |
| 72 | int sectrac; /* sectors per track */ |
| 73 | int secsize; /* size code for sectors */ |
| 74 | int datalen; /* data len when secsize = 0 */ |
| 75 | int gap; /* gap len between sectors */ |
| 76 | int tracks; /* total num of tracks */ |
| 77 | int size; /* size of disk in sectors */ |
| 78 | int steptrac; /* steps per cylinder */ |
| 79 | int trans; /* transfer speed code */ |
| 80 | int heads; /* number of heads */ |
| 81 | }; |
| 82 | |
| 83 | struct fd_type fd_types[NUMTYPES] = |
| 84 | { |
| 85 | { 18,2,0xFF,0x1B,80,2880,1,0,2 }, /* 1.44 meg HD 3.5in floppy */ |
| 86 | { 15,2,0xFF,0x1B,80,2400,1,0,2 }, /* 1.2 meg HD floppy */ |
| 87 | { 9,2,0xFF,0x23,40,720,2,1,2 }, /* 360k floppy in 1.2meg drive */ |
| 88 | { 9,2,0xFF,0x2A,40,720,1,1,2 }, /* 360k floppy in DD drive */ |
| 89 | }; |
| 90 | |
| 91 | #define DRVS_PER_CTLR 2 |
| 92 | /***********************************************************************\ |
| 93 | * Per controller structure. * |
| 94 | \***********************************************************************/ |
| 95 | struct fdc_data |
| 96 | { |
| 97 | int fdcu; /* our unit number */ |
| 98 | int baseport; |
| 99 | int dmachan; |
| 100 | int flags; |
| 101 | #define FDC_ATTACHED 0x01 |
| 102 | struct fd_data *fd; |
| 103 | int fdu; /* the active drive */ |
| 104 | struct buf head; /* Head of buf chain */ |
| 105 | struct buf rhead; /* Raw head of buf chain */ |
| 106 | int state; |
| 107 | int retry; |
| 108 | int status[7]; /* copy of the registers */ |
| 109 | }fdc_data[(NFD+1)/DRVS_PER_CTLR]; |
| 110 | |
| 111 | /***********************************************************************\ |
| 112 | * Per drive structure. * |
| 113 | * N per controller (presently 2) (DRVS_PER_CTLR) * |
| 114 | \***********************************************************************/ |
| 115 | struct fd_data { |
| 116 | struct fdc_data *fdc; |
| 117 | int fdu; /* this unit number */ |
| 118 | int fdsu; /* this units number on this controller */ |
| 119 | int type; /* Drive type (HD, DD */ |
| 120 | struct fd_type *ft; /* pointer to the type descriptor */ |
| 121 | int flags; |
| 122 | #define FD_OPEN 0x01 /* it's open */ |
| 123 | #define FD_ACTIVE 0x02 /* it's active */ |
| 124 | #define FD_MOTOR 0x04 /* motor should be on */ |
| 125 | #define FD_MOTOR_WAIT 0x08 /* motor coming up */ |
| 126 | int skip; |
| 127 | int hddrv; |
| 128 | int track; /* where we think the head is */ |
| 129 | } fd_data[NFD]; |
| 130 | |
| 131 | /***********************************************************************\ |
| 132 | * Throughout this file the following conventions will be used: * |
| 133 | * fd is a pointer to the fd_data struct for the drive in question * |
| 134 | * fdc is a pointer to the fdc_data struct for the controller * |
| 135 | * fdu is the floppy drive unit number * |
| 136 | * fdcu is the floppy controller unit number * |
| 137 | * fdsu is the floppy drive unit number on that controller. (sub-unit) * |
| 138 | \***********************************************************************/ |
| 139 | typedef int fdu_t; |
| 140 | typedef int fdcu_t; |
| 141 | typedef int fdsu_t; |
| 142 | typedef struct fd_data *fd_p; |
| 143 | typedef struct fdc_data *fdc_p; |
| 144 | |
| 145 | #define DEVIDLE 0 |
| 146 | #define FINDWORK 1 |
| 147 | #define DOSEEK 2 |
| 148 | #define SEEKCOMPLETE 3 |
| 149 | #define IOCOMPLETE 4 |
| 150 | #define RECALCOMPLETE 5 |
| 151 | #define STARTRECAL 6 |
| 152 | #define RESETCTLR 7 |
| 153 | #define SEEKWAIT 8 |
| 154 | #define RECALWAIT 9 |
| 155 | #define MOTORWAIT 10 |
| 156 | #define IOTIMEDOUT 11 |
| 157 | |
| 158 | #ifdef DEBUG |
| 159 | char *fdstates[] = |
| 160 | { |
| 161 | "DEVIDLE", |
| 162 | "FINDWORK", |
| 163 | "DOSEEK", |
| 164 | "SEEKCOMPLETE", |
| 165 | "IOCOMPLETE", |
| 166 | "RECALCOMPLETE", |
| 167 | "STARTRECAL", |
| 168 | "RESETCTLR", |
| 169 | "SEEKWAIT", |
| 170 | "RECALWAIT", |
| 171 | "MOTORWAIT", |
| 172 | "IOTIMEDOUT" |
| 173 | }; |
| 174 | |
| 175 | |
| 176 | int fd_debug = 1; |
| 177 | #define TRACE0(arg) if(fd_debug) printf(arg) |
| 178 | #define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) |
| 179 | #else /* DEBUG */ |
| 180 | #define TRACE0(arg) |
| 181 | #define TRACE1(arg1,arg2) |
| 182 | #endif /* DEBUG */ |
| 183 | |
| 184 | static void fdstart(fdcu_t); |
| 185 | void fdintr(fdcu_t); |
| 186 | static void fd_turnoff(caddr_t, int); |
| 187 | |
| 188 | /****************************************************************************/ |
| 189 | /* autoconfiguration stuff */ |
| 190 | /****************************************************************************/ |
| 191 | static int fdprobe(struct isa_device *); |
| 192 | static int fdattach(struct isa_device *); |
| 193 | |
| 194 | struct isa_driver fddriver = { |
| 195 | fdprobe, fdattach, "fd", |
| 196 | }; |
| 197 | |
| 198 | /* |
| 199 | * probe for existance of controller |
| 200 | */ |
| 201 | int |
| 202 | fdprobe(dev) |
| 203 | struct isa_device *dev; |
| 204 | { |
| 205 | fdcu_t fdcu = dev->id_unit; |
| 206 | if(fdc_data[fdcu].flags & FDC_ATTACHED) |
| 207 | { |
| 208 | printf("fdc: same unit (%d) used multiple times\n",fdcu); |
| 209 | return 0; |
| 210 | } |
| 211 | |
| 212 | fdc_data[fdcu].baseport = dev->id_iobase; |
| 213 | |
| 214 | /* see if it can handle a command */ |
| 215 | if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) |
| 216 | { |
| 217 | return(0); |
| 218 | } |
| 219 | out_fdc(fdcu,0xDF); |
| 220 | out_fdc(fdcu,2); |
| 221 | return (IO_FDCSIZE); |
| 222 | } |
| 223 | |
| 224 | /* |
| 225 | * wire controller into system, look for floppy units |
| 226 | */ |
| 227 | int |
| 228 | fdattach(dev) |
| 229 | struct isa_device *dev; |
| 230 | { |
| 231 | unsigned fdt,st0, cyl; |
| 232 | int hdr; |
| 233 | fdu_t fdu; |
| 234 | fdcu_t fdcu = dev->id_unit; |
| 235 | fdc_p fdc = fdc_data + fdcu; |
| 236 | fd_p fd; |
| 237 | int fdsu; |
| 238 | |
| 239 | fdc->fdcu = fdcu; |
| 240 | fdc->flags |= FDC_ATTACHED; |
| 241 | fdc->dmachan = dev->id_drq; |
| 242 | fdc->state = DEVIDLE; |
| 243 | |
| 244 | fdt = rtcin(RTC_FDISKETTE); |
| 245 | hdr = 0; |
| 246 | |
| 247 | /* check for each floppy drive */ |
| 248 | for (fdu = (fdcu * DRVS_PER_CTLR),fdsu = 0; |
| 249 | ((fdu < NFD) && (fdsu < DRVS_PER_CTLR)); |
| 250 | fdu++,fdsu++) |
| 251 | { |
| 252 | /* is there a unit? */ |
| 253 | if ((fdt & 0xf0) == RTCFDT_NONE) { |
| 254 | #define NO_TYPE NUMTYPES |
| 255 | fd_data[fdu].type = NO_TYPE; |
| 256 | continue; |
| 257 | } |
| 258 | |
| 259 | #ifdef notyet |
| 260 | /* select it */ |
| 261 | fd_turnon1(fdu); |
| 262 | spinwait(1000); /* 1 sec */ |
| 263 | out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ |
| 264 | out_fdc(fdcu,fdsu); |
| 265 | spinwait(1000); /* 1 sec */ |
| 266 | |
| 267 | /* anything responding */ |
| 268 | out_fdc(fdcu,NE7CMD_SENSEI); |
| 269 | st0 = in_fdc(fdcu); |
| 270 | cyl = in_fdc(fdcu); |
| 271 | if (st0 & 0xd0) |
| 272 | continue; |
| 273 | |
| 274 | #endif |
| 275 | fd_data[fdu].track = -2; |
| 276 | fd_data[fdu].fdc = fdc; |
| 277 | fd_data[fdu].fdsu = fdsu; |
| 278 | printf("fd%d: unit %d type ", fdcu, fdu); |
| 279 | |
| 280 | if ((fdt & 0xf0) == RTCFDT_12M) { |
| 281 | printf("1.2MB 5.25in\n"); |
| 282 | fd_data[fdu].type = 1; |
| 283 | fd_data[fdu].ft = fd_types + 1; |
| 284 | |
| 285 | } |
| 286 | if ((fdt & 0xf0) == RTCFDT_144M) { |
| 287 | printf("1.44MB 3.5in\n"); |
| 288 | fd_data[fdu].type = 0; |
| 289 | fd_data[fdu].ft = fd_types + 0; |
| 290 | } |
| 291 | |
| 292 | fdt <<= 4; |
| 293 | fd_turnoff((caddr_t)fdu, 0); |
| 294 | hdr = 1; |
| 295 | } |
| 296 | |
| 297 | /* Set transfer to 500kbps */ |
| 298 | outb(fdc->baseport+fdctl,0); /*XXX*/ |
| 299 | return 1; |
| 300 | } |
| 301 | |
| 302 | int |
| 303 | fdsize(dev) |
| 304 | dev_t dev; |
| 305 | { |
| 306 | return(0); |
| 307 | } |
| 308 | |
| 309 | /****************************************************************************/ |
| 310 | /* fdstrategy */ |
| 311 | /****************************************************************************/ |
| 312 | void fdstrategy(struct buf *bp) |
| 313 | { |
| 314 | register struct buf *dp,*dp0,*dp1; |
| 315 | long nblocks,blknum; |
| 316 | int s; |
| 317 | fdcu_t fdcu; |
| 318 | fdu_t fdu; |
| 319 | fdc_p fdc; |
| 320 | fd_p fd; |
| 321 | |
| 322 | fdu = FDUNIT(minor(bp->b_dev)); |
| 323 | fd = &fd_data[fdu]; |
| 324 | fdc = fd->fdc; |
| 325 | fdcu = fdc->fdcu; |
| 326 | /*type = FDTYPE(minor(bp->b_dev));*/ |
| 327 | |
| 328 | if ((fdu >= NFD) || (bp->b_blkno < 0)) { |
| 329 | printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", |
| 330 | fdu, bp->b_blkno, bp->b_bcount); |
| 331 | pg("fd:error in fdstrategy"); |
| 332 | bp->b_error = EINVAL; |
| 333 | bp->b_flags |= B_ERROR; |
| 334 | goto bad; |
| 335 | } |
| 336 | /* |
| 337 | * Set up block calculations. |
| 338 | */ |
| 339 | blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; |
| 340 | nblocks = fd->ft->size; |
| 341 | if (blknum + (bp->b_bcount / FDBLK) > nblocks) { |
| 342 | if (blknum == nblocks) { |
| 343 | bp->b_resid = bp->b_bcount; |
| 344 | } else { |
| 345 | bp->b_error = ENOSPC; |
| 346 | bp->b_flags |= B_ERROR; |
| 347 | } |
| 348 | goto bad; |
| 349 | } |
| 350 | bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); |
| 351 | dp = &(fdc->head); |
| 352 | s = splbio(); |
| 353 | disksort(dp, bp); |
| 354 | untimeout(fd_turnoff,fdu); /* a good idea */ |
| 355 | fdstart(fdcu); |
| 356 | splx(s); |
| 357 | return; |
| 358 | |
| 359 | bad: |
| 360 | biodone(bp); |
| 361 | } |
| 362 | |
| 363 | /****************************************************************************/ |
| 364 | /* motor control stuff */ |
| 365 | /* remember to not deselect the drive we're working on */ |
| 366 | /****************************************************************************/ |
| 367 | void |
| 368 | set_motor(fdcu, fdu, reset) |
| 369 | fdcu_t fdcu; |
| 370 | fdu_t fdu; |
| 371 | int reset; |
| 372 | { |
| 373 | int m0,m1; |
| 374 | int selunit; |
| 375 | fd_p fd; |
| 376 | if(fd = fdc_data[fdcu].fd)/* yes an assign! */ |
| 377 | { |
| 378 | selunit = fd->fdsu; |
| 379 | } |
| 380 | else |
| 381 | { |
| 382 | selunit = 0; |
| 383 | } |
| 384 | m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; |
| 385 | m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; |
| 386 | outb(fdc_data[fdcu].baseport+fdout, |
| 387 | selunit |
| 388 | | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) |
| 389 | | (m0 ? FDO_MOEN0 : 0) |
| 390 | | (m1 ? FDO_MOEN1 : 0)); |
| 391 | TRACE1("[0x%x->fdout]",( |
| 392 | selunit |
| 393 | | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) |
| 394 | | (m0 ? FDO_MOEN0 : 0) |
| 395 | | (m1 ? FDO_MOEN1 : 0))); |
| 396 | } |
| 397 | |
| 398 | static void |
| 399 | fd_turnoff(caddr_t arg1, int arg2) |
| 400 | { |
| 401 | fdu_t fdu = (fdu_t)arg1; |
| 402 | int s; |
| 403 | |
| 404 | fd_p fd = fd_data + fdu; |
| 405 | s = splbio(); |
| 406 | fd->flags &= ~FD_MOTOR; |
| 407 | set_motor(fd->fdc->fdcu,fd->fdsu,0); |
| 408 | splx(s); |
| 409 | } |
| 410 | |
| 411 | void |
| 412 | fd_motor_on(caddr_t arg1, int arg2) |
| 413 | { |
| 414 | fdu_t fdu = (fdu_t)arg1; |
| 415 | int s; |
| 416 | |
| 417 | fd_p fd = fd_data + fdu; |
| 418 | s = splbio(); |
| 419 | fd->flags &= ~FD_MOTOR_WAIT; |
| 420 | if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) |
| 421 | { |
| 422 | fdintr(fd->fdc->fdcu); |
| 423 | } |
| 424 | splx(s); |
| 425 | } |
| 426 | |
| 427 | static void fd_turnon1(fdu_t); |
| 428 | |
| 429 | void |
| 430 | fd_turnon(fdu) |
| 431 | fdu_t fdu; |
| 432 | { |
| 433 | fd_p fd = fd_data + fdu; |
| 434 | if(!(fd->flags & FD_MOTOR)) |
| 435 | { |
| 436 | fd_turnon1(fdu); |
| 437 | fd->flags |= FD_MOTOR_WAIT; |
| 438 | timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | static void |
| 443 | fd_turnon1(fdu_t fdu) |
| 444 | { |
| 445 | fd_p fd = fd_data + fdu; |
| 446 | fd->flags |= FD_MOTOR; |
| 447 | set_motor(fd->fdc->fdcu,fd->fdsu,0); |
| 448 | } |
| 449 | |
| 450 | /****************************************************************************/ |
| 451 | /* fdc in/out */ |
| 452 | /****************************************************************************/ |
| 453 | int |
| 454 | in_fdc(fdcu) |
| 455 | fdcu_t fdcu; |
| 456 | { |
| 457 | int baseport = fdc_data[fdcu].baseport; |
| 458 | int i, j = 100000; |
| 459 | while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) |
| 460 | != (NE7_DIO|NE7_RQM) && j-- > 0) |
| 461 | if (i == NE7_RQM) return -1; |
| 462 | if (j <= 0) |
| 463 | return(-1); |
| 464 | #ifdef DEBUG |
| 465 | i = inb(baseport+fddata); |
| 466 | TRACE1("[fddata->0x%x]",(unsigned char)i); |
| 467 | return(i); |
| 468 | #else |
| 469 | return inb(baseport+fddata); |
| 470 | #endif |
| 471 | } |
| 472 | |
| 473 | int |
| 474 | out_fdc(fdcu, x) |
| 475 | fdcu_t fdcu; |
| 476 | int x; |
| 477 | { |
| 478 | int baseport = fdc_data[fdcu].baseport; |
| 479 | int i; |
| 480 | |
| 481 | /* Check that the direction bit is set */ |
| 482 | i = 100000; |
| 483 | while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); |
| 484 | if (i <= 0) return (-1); /* Floppy timed out */ |
| 485 | |
| 486 | /* Check that the floppy controller is ready for a command */ |
| 487 | i = 100000; |
| 488 | while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); |
| 489 | if (i <= 0) return (-1); /* Floppy timed out */ |
| 490 | |
| 491 | /* Send the command and return */ |
| 492 | outb(baseport+fddata,x); |
| 493 | TRACE1("[0x%x->fddata]",x); |
| 494 | return (0); |
| 495 | } |
| 496 | |
| 497 | /****************************************************************************/ |
| 498 | /* fdopen/fdclose */ |
| 499 | /****************************************************************************/ |
| 500 | int |
| 501 | Fdopen(dev, flags) |
| 502 | dev_t dev; |
| 503 | int flags; |
| 504 | { |
| 505 | fdu_t fdu = FDUNIT(minor(dev)); |
| 506 | /*int type = FDTYPE(minor(dev));*/ |
| 507 | int s; |
| 508 | |
| 509 | /* check bounds */ |
| 510 | if (fdu >= NFD || fd_data[fdu].type == NO_TYPE) return(ENXIO); |
| 511 | /*if (type >= NUMTYPES) return(ENXIO);*/ |
| 512 | fd_data[fdu].flags |= FD_OPEN; |
| 513 | |
| 514 | return 0; |
| 515 | } |
| 516 | |
| 517 | int |
| 518 | fdclose(dev, flags) |
| 519 | dev_t dev; |
| 520 | int flags; |
| 521 | { |
| 522 | fdu_t fdu = FDUNIT(minor(dev)); |
| 523 | fd_data[fdu].flags &= ~FD_OPEN; |
| 524 | return(0); |
| 525 | } |
| 526 | |
| 527 | |
| 528 | /***************************************************************\ |
| 529 | * fdstart * |
| 530 | * We have just queued something.. if the controller is not busy * |
| 531 | * then simulate the case where it has just finished a command * |
| 532 | * So that it (the interrupt routine) looks on the queue for more* |
| 533 | * work to do and picks up what we just added. * |
| 534 | * If the controller is already busy, we need do nothing, as it * |
| 535 | * will pick up our work when the present work completes * |
| 536 | \***************************************************************/ |
| 537 | static void |
| 538 | fdstart(fdcu) |
| 539 | fdcu_t fdcu; |
| 540 | { |
| 541 | register struct buf *dp,*bp; |
| 542 | int s; |
| 543 | fdu_t fdu; |
| 544 | |
| 545 | s = splbio(); |
| 546 | if(fdc_data[fdcu].state == DEVIDLE) |
| 547 | { |
| 548 | fdintr(fdcu); |
| 549 | } |
| 550 | splx(s); |
| 551 | } |
| 552 | |
| 553 | static void |
| 554 | fd_timeout(caddr_t arg1, int arg2) |
| 555 | { |
| 556 | fdcu_t fdcu = (fdcu_t)arg1; |
| 557 | fdu_t fdu = fdc_data[fdcu].fdu; |
| 558 | int st0, st3, cyl; |
| 559 | struct buf *dp,*bp; |
| 560 | int s; |
| 561 | |
| 562 | dp = &fdc_data[fdcu].head; |
| 563 | s = splbio(); |
| 564 | bp = dp->b_actf; |
| 565 | |
| 566 | out_fdc(fdcu,NE7CMD_SENSED); |
| 567 | out_fdc(fdcu,fd_data[fdu].hddrv); |
| 568 | st3 = in_fdc(fdcu); |
| 569 | |
| 570 | out_fdc(fdcu,NE7CMD_SENSEI); |
| 571 | st0 = in_fdc(fdcu); |
| 572 | cyl = in_fdc(fdcu); |
| 573 | printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", |
| 574 | fdu, |
| 575 | st0, |
| 576 | NE7_ST0BITS, |
| 577 | cyl, |
| 578 | st3, |
| 579 | NE7_ST3BITS); |
| 580 | |
| 581 | if (bp) |
| 582 | { |
| 583 | retrier(fdcu); |
| 584 | fdc_data[fdcu].status[0] = 0xc0; |
| 585 | fdc_data[fdcu].state = IOTIMEDOUT; |
| 586 | if( fdc_data[fdcu].retry < 6) |
| 587 | fdc_data[fdcu].retry = 6; |
| 588 | } |
| 589 | else |
| 590 | { |
| 591 | fdc_data[fdcu].fd = (fd_p) 0; |
| 592 | fdc_data[fdcu].fdu = -1; |
| 593 | fdc_data[fdcu].state = DEVIDLE; |
| 594 | } |
| 595 | fdintr(fdcu); |
| 596 | splx(s); |
| 597 | } |
| 598 | |
| 599 | /* just ensure it has the right spl */ |
| 600 | static void |
| 601 | fd_pseudointr(caddr_t arg1, int arg2) |
| 602 | { |
| 603 | fdcu_t fdcu = (fdcu_t)arg1; |
| 604 | int s; |
| 605 | s = splbio(); |
| 606 | fdintr(fdcu); |
| 607 | splx(s); |
| 608 | } |
| 609 | |
| 610 | /***********************************************************************\ |
| 611 | * fdintr * |
| 612 | * keep calling the state machine until it returns a 0 * |
| 613 | * ALWAYS called at SPLBIO * |
| 614 | \***********************************************************************/ |
| 615 | void |
| 616 | fdintr(fdcu_t fdcu) |
| 617 | { |
| 618 | fdc_p fdc = fdc_data + fdcu; |
| 619 | while(fdstate(fdcu, fdc)) |
| 620 | ; |
| 621 | } |
| 622 | |
| 623 | /***********************************************************************\ |
| 624 | * The controller state machine. * |
| 625 | * if it returns a non zero value, it should be called again immediatly * |
| 626 | \***********************************************************************/ |
| 627 | int |
| 628 | fdstate(fdcu, fdc) |
| 629 | fdcu_t fdcu; |
| 630 | fdc_p fdc; |
| 631 | { |
| 632 | int read, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; |
| 633 | unsigned long blknum; |
| 634 | fdu_t fdu = fdc->fdu; |
| 635 | fd_p fd; |
| 636 | register struct buf *dp,*bp; |
| 637 | |
| 638 | dp = &(fdc->head); |
| 639 | bp = dp->b_actf; |
| 640 | if(!bp) |
| 641 | { |
| 642 | /***********************************************\ |
| 643 | * nothing left for this controller to do * |
| 644 | * Force into the IDLE state, * |
| 645 | \***********************************************/ |
| 646 | fdc->state = DEVIDLE; |
| 647 | if(fdc->fd) |
| 648 | { |
| 649 | printf("unexpected valid fd pointer (fdu = %d)\n" |
| 650 | ,fdc->fdu); |
| 651 | fdc->fd = (fd_p) 0; |
| 652 | fdc->fdu = -1; |
| 653 | } |
| 654 | TRACE1("[fdc%d IDLE]",fdcu); |
| 655 | return(0); |
| 656 | } |
| 657 | fdu = FDUNIT(minor(bp->b_dev)); |
| 658 | fd = fd_data + fdu; |
| 659 | if (fdc->fd && (fd != fdc->fd)) |
| 660 | { |
| 661 | printf("confused fd pointers\n"); |
| 662 | } |
| 663 | read = bp->b_flags & B_READ; |
| 664 | TRACE1("fd%d",fdu); |
| 665 | TRACE1("[%s]",fdstates[fdc->state]); |
| 666 | TRACE1("(0x%x)",fd->flags); |
| 667 | untimeout(fd_turnoff, fdu); |
| 668 | timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); |
| 669 | switch (fdc->state) |
| 670 | { |
| 671 | case DEVIDLE: |
| 672 | case FINDWORK: /* we have found new work */ |
| 673 | fdc->retry = 0; |
| 674 | fd->skip = 0; |
| 675 | fdc->fd = fd; |
| 676 | fdc->fdu = fdu; |
| 677 | /*******************************************************\ |
| 678 | * If the next drive has a motor startup pending, then * |
| 679 | * it will start up in it's own good time * |
| 680 | \*******************************************************/ |
| 681 | if(fd->flags & FD_MOTOR_WAIT) |
| 682 | { |
| 683 | fdc->state = MOTORWAIT; |
| 684 | return(0); /* come back later */ |
| 685 | } |
| 686 | /*******************************************************\ |
| 687 | * Maybe if it's not starting, it SHOULD be starting * |
| 688 | \*******************************************************/ |
| 689 | if (!(fd->flags & FD_MOTOR)) |
| 690 | { |
| 691 | fdc->state = MOTORWAIT; |
| 692 | fd_turnon(fdu); |
| 693 | return(0); |
| 694 | } |
| 695 | else /* at least make sure we are selected */ |
| 696 | { |
| 697 | set_motor(fdcu,fd->fdsu,0); |
| 698 | } |
| 699 | fdc->state = DOSEEK; |
| 700 | break; |
| 701 | case DOSEEK: |
| 702 | if (bp->b_cylin == fd->track) |
| 703 | { |
| 704 | fdc->state = SEEKCOMPLETE; |
| 705 | break; |
| 706 | } |
| 707 | out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ |
| 708 | out_fdc(fdcu,fd->fdsu); /* Drive number */ |
| 709 | out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); |
| 710 | fd->track = -2; |
| 711 | fdc->state = SEEKWAIT; |
| 712 | timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); |
| 713 | return(0); /* will return later */ |
| 714 | case SEEKWAIT: |
| 715 | untimeout(fd_timeout,fdcu); |
| 716 | /* allow heads to settle */ |
| 717 | timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); |
| 718 | fdc->state = SEEKCOMPLETE; |
| 719 | return(0); /* will return later */ |
| 720 | break; |
| 721 | |
| 722 | case SEEKCOMPLETE : /* SEEK DONE, START DMA */ |
| 723 | /* Make sure seek really happened*/ |
| 724 | if(fd->track == -2) |
| 725 | { |
| 726 | int descyl = bp->b_cylin * fd->ft->steptrac; |
| 727 | out_fdc(fdcu,NE7CMD_SENSEI); |
| 728 | i = in_fdc(fdcu); |
| 729 | cyl = in_fdc(fdcu); |
| 730 | if (cyl != descyl) |
| 731 | { |
| 732 | printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, |
| 733 | descyl, cyl, i, NE7_ST0BITS); |
| 734 | return(retrier(fdcu)); |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | fd->track = bp->b_cylin; |
| 739 | isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, |
| 740 | FDBLK, fdc->dmachan); |
| 741 | blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK |
| 742 | + fd->skip/FDBLK; |
| 743 | sectrac = fd->ft->sectrac; |
| 744 | sec = blknum % (sectrac * fd->ft->heads); |
| 745 | head = sec / sectrac; |
| 746 | sec = sec % sectrac + 1; |
| 747 | /*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; |
| 748 | |
| 749 | if (read) |
| 750 | { |
| 751 | out_fdc(fdcu,NE7CMD_READ); /* READ */ |
| 752 | } |
| 753 | else |
| 754 | { |
| 755 | out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ |
| 756 | } |
| 757 | out_fdc(fdcu,head << 2 | fdu); /* head & unit */ |
| 758 | out_fdc(fdcu,fd->track); /* track */ |
| 759 | out_fdc(fdcu,head); |
| 760 | out_fdc(fdcu,sec); /* sector XXX +1? */ |
| 761 | out_fdc(fdcu,fd->ft->secsize); /* sector size */ |
| 762 | out_fdc(fdcu,sectrac); /* sectors/track */ |
| 763 | out_fdc(fdcu,fd->ft->gap); /* gap size */ |
| 764 | out_fdc(fdcu,fd->ft->datalen); /* data length */ |
| 765 | fdc->state = IOCOMPLETE; |
| 766 | timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); |
| 767 | return(0); /* will return later */ |
| 768 | case IOCOMPLETE: /* IO DONE, post-analyze */ |
| 769 | untimeout(fd_timeout,fdcu); |
| 770 | for(i=0;i<7;i++) |
| 771 | { |
| 772 | fdc->status[i] = in_fdc(fdcu); |
| 773 | } |
| 774 | case IOTIMEDOUT: /*XXX*/ |
| 775 | isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, |
| 776 | FDBLK, fdc->dmachan); |
| 777 | if (fdc->status[0]&0xF8) |
| 778 | { |
| 779 | return(retrier(fdcu)); |
| 780 | } |
| 781 | /* All OK */ |
| 782 | fd->skip += FDBLK; |
| 783 | if (fd->skip < bp->b_bcount) |
| 784 | { |
| 785 | /* set up next transfer */ |
| 786 | blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK |
| 787 | + fd->skip/FDBLK; |
| 788 | bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); |
| 789 | fdc->state = DOSEEK; |
| 790 | } |
| 791 | else |
| 792 | { |
| 793 | /* ALL DONE */ |
| 794 | fd->skip = 0; |
| 795 | bp->b_resid = 0; |
| 796 | dp->b_actf = bp->av_forw; |
| 797 | biodone(bp); |
| 798 | fdc->fd = (fd_p) 0; |
| 799 | fdc->fdu = -1; |
| 800 | fdc->state = FINDWORK; |
| 801 | } |
| 802 | return(1); |
| 803 | case RESETCTLR: |
| 804 | /* Try a reset, keep motor on */ |
| 805 | set_motor(fdcu,fd->fdsu,1); |
| 806 | DELAY(100); |
| 807 | set_motor(fdcu,fd->fdsu,0); |
| 808 | outb(fdc->baseport+fdctl,fd->ft->trans); |
| 809 | TRACE1("[0x%x->fdctl]",fd->ft->trans); |
| 810 | fdc->retry++; |
| 811 | fdc->state = STARTRECAL; |
| 812 | break; |
| 813 | case STARTRECAL: |
| 814 | out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ |
| 815 | out_fdc(fdcu,0xDF); |
| 816 | out_fdc(fdcu,2); |
| 817 | out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ |
| 818 | out_fdc(fdcu,fdu); |
| 819 | fdc->state = RECALWAIT; |
| 820 | return(0); /* will return later */ |
| 821 | case RECALWAIT: |
| 822 | /* allow heads to settle */ |
| 823 | timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); |
| 824 | fdc->state = RECALCOMPLETE; |
| 825 | return(0); /* will return later */ |
| 826 | case RECALCOMPLETE: |
| 827 | out_fdc(fdcu,NE7CMD_SENSEI); |
| 828 | st0 = in_fdc(fdcu); |
| 829 | cyl = in_fdc(fdcu); |
| 830 | if (cyl != 0) |
| 831 | { |
| 832 | printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, |
| 833 | st0, NE7_ST0BITS, cyl); |
| 834 | return(retrier(fdcu)); |
| 835 | } |
| 836 | fd->track = 0; |
| 837 | /* Seek (probably) necessary */ |
| 838 | fdc->state = DOSEEK; |
| 839 | return(1); /* will return immediatly */ |
| 840 | case MOTORWAIT: |
| 841 | if(fd->flags & FD_MOTOR_WAIT) |
| 842 | { |
| 843 | return(0); /* time's not up yet */ |
| 844 | } |
| 845 | fdc->state = DOSEEK; |
| 846 | return(1); /* will return immediatly */ |
| 847 | default: |
| 848 | printf("Unexpected FD int->"); |
| 849 | out_fdc(fdcu,NE7CMD_SENSEI); |
| 850 | st0 = in_fdc(fdcu); |
| 851 | cyl = in_fdc(fdcu); |
| 852 | printf("ST0 = %lx, PCN = %lx\n",i,sec); |
| 853 | out_fdc(fdcu,0x4A); |
| 854 | out_fdc(fdcu,fd->fdsu); |
| 855 | for(i=0;i<7;i++) { |
| 856 | fdc->status[i] = in_fdc(fdcu); |
| 857 | } |
| 858 | printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", |
| 859 | fdc->status[0], |
| 860 | fdc->status[1], |
| 861 | fdc->status[2], |
| 862 | fdc->status[3], |
| 863 | fdc->status[4], |
| 864 | fdc->status[5], |
| 865 | fdc->status[6] ); |
| 866 | return(0); |
| 867 | } |
| 868 | return(1); /* Come back immediatly to new state */ |
| 869 | } |
| 870 | |
| 871 | int |
| 872 | retrier(fdcu) |
| 873 | fdcu_t fdcu; |
| 874 | { |
| 875 | fdc_p fdc = fdc_data + fdcu; |
| 876 | register struct buf *dp,*bp; |
| 877 | |
| 878 | dp = &(fdc->head); |
| 879 | bp = dp->b_actf; |
| 880 | |
| 881 | switch(fdc->retry) |
| 882 | { |
| 883 | case 0: case 1: case 2: |
| 884 | fdc->state = SEEKCOMPLETE; |
| 885 | break; |
| 886 | case 3: case 4: case 5: |
| 887 | fdc->state = STARTRECAL; |
| 888 | break; |
| 889 | case 6: |
| 890 | fdc->state = RESETCTLR; |
| 891 | break; |
| 892 | case 7: |
| 893 | break; |
| 894 | default: |
| 895 | { |
| 896 | diskerr(bp, "fd", "hard error", LOG_PRINTF, |
| 897 | fdc->fd->skip, (struct disklabel *)NULL); |
| 898 | printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); |
| 899 | printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); |
| 900 | printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS); |
| 901 | printf("cyl %d hd %d sec %d)\n", |
| 902 | fdc->status[3], fdc->status[4], fdc->status[5]); |
| 903 | } |
| 904 | bp->b_flags |= B_ERROR; |
| 905 | bp->b_error = EIO; |
| 906 | bp->b_resid = bp->b_bcount - fdc->fd->skip; |
| 907 | dp->b_actf = bp->av_forw; |
| 908 | fdc->fd->skip = 0; |
| 909 | biodone(bp); |
| 910 | fdc->state = FINDWORK; |
| 911 | fdc->fd = (fd_p) 0; |
| 912 | fdc->fdu = -1; |
| 913 | /* XXX abort current command, if any. */ |
| 914 | return(1); |
| 915 | } |
| 916 | fdc->retry++; |
| 917 | return(1); |
| 918 | } |
| 919 | |
| 920 | /* |
| 921 | * fdioctl() from jc@irbs.UUCP (John Capo) |
| 922 | * i386/i386/conf.c needs to have fdioctl() declared and remove the line that |
| 923 | * defines fdioctl to be enxio. |
| 924 | * |
| 925 | * TODO: Reformat. |
| 926 | * Think about allocating buffer off stack. |
| 927 | * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). |
| 928 | * Watch out for NetBSD's different *disklabel() interface. |
| 929 | */ |
| 930 | |
| 931 | int |
| 932 | fdioctl (dev, cmd, addr, flag) |
| 933 | dev_t dev; |
| 934 | int cmd; |
| 935 | caddr_t addr; |
| 936 | int flag; |
| 937 | { |
| 938 | struct fd_type *fdt; |
| 939 | struct disklabel *dl; |
| 940 | char buffer[DEV_BSIZE]; |
| 941 | int error; |
| 942 | |
| 943 | error = 0; |
| 944 | |
| 945 | switch (cmd) |
| 946 | { |
| 947 | case DIOCGDINFO: |
| 948 | bzero(buffer, sizeof (buffer)); |
| 949 | dl = (struct disklabel *)buffer; |
| 950 | dl->d_secsize = FDBLK; |
| 951 | fdt = fd_data[FDUNIT(minor(dev))].ft; |
| 952 | dl->d_secpercyl = fdt->size / fdt->tracks; |
| 953 | dl->d_type = DTYPE_FLOPPY; |
| 954 | |
| 955 | if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL) |
| 956 | error = 0; |
| 957 | else |
| 958 | error = EINVAL; |
| 959 | |
| 960 | *(struct disklabel *)addr = *dl; |
| 961 | break; |
| 962 | |
| 963 | case DIOCSDINFO: |
| 964 | |
| 965 | if ((flag & FWRITE) == 0) |
| 966 | error = EBADF; |
| 967 | |
| 968 | break; |
| 969 | |
| 970 | case DIOCWLABEL: |
| 971 | if ((flag & FWRITE) == 0) |
| 972 | error = EBADF; |
| 973 | |
| 974 | break; |
| 975 | |
| 976 | case DIOCWDINFO: |
| 977 | if ((flag & FWRITE) == 0) |
| 978 | { |
| 979 | error = EBADF; |
| 980 | break; |
| 981 | } |
| 982 | |
| 983 | dl = (struct disklabel *)addr; |
| 984 | |
| 985 | if (error = setdisklabel ((struct disklabel *)buffer, dl, 0, NULL)) |
| 986 | break; |
| 987 | |
| 988 | error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer, NULL); |
| 989 | break; |
| 990 | |
| 991 | default: |
| 992 | error = EINVAL; |
| 993 | break; |
| 994 | } |
| 995 | return (error); |
| 996 | } |
| 997 | |
| 998 | #endif |