1 /* $NetBSD: iwm_fd.c,v 1.62 2025/08/06 17:25:38 hauke Exp $ */ 2 3 /* 4 * Copyright (c) 1997, 1998 Hauke Fath. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* 28 * iwm_fd.c -- Sony (floppy disk) driver for m68k Macintoshes 29 * 30 * The present implementation supports the GCR format (800K) on 31 * non-{DMA,IOP} machines. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: iwm_fd.c,v 1.62 2025/08/06 17:25:38 hauke Exp $"); 36 37 #include "locators.h" 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/callout.h> 42 #include <sys/kernel.h> 43 #include <sys/file.h> 44 #include <sys/ioctl.h> 45 #include <sys/kmem.h> 46 #include <sys/device.h> 47 #include <sys/event.h> 48 49 #define FSTYPENAMES 50 #define DKTYPENAMES 51 #include <sys/disklabel.h> 52 53 #include <sys/disk.h> 54 #include <sys/dkbad.h> 55 #include <sys/buf.h> 56 #include <sys/bufq.h> 57 #include <sys/uio.h> 58 #include <sys/stat.h> 59 #include <sys/syslog.h> 60 #include <sys/conf.h> 61 62 #include <machine/autoconf.h> 63 #include <machine/cpu.h> 64 65 #include <mac68k/obio/iwmreg.h> 66 #include <mac68k/obio/iwm_fdvar.h> 67 68 /* Autoconfig */ 69 int iwm_match(device_t, cfdata_t, void *); 70 void iwm_attach(device_t, device_t, void *); 71 int iwm_print(void *, const char *); 72 int fd_match(device_t, cfdata_t, void *); 73 void fd_attach(device_t, device_t, void *); 74 int fd_print(void *, const char *); 75 76 /** 77 ** Private functions 78 **/ 79 80 /* Disklabel stuff */ 81 static void fdGetDiskLabel(fd_softc_t *, dev_t); 82 static void fdPrintDiskLabel(struct disklabel *); 83 84 static fdInfo_t *getFDType(short); 85 static fdInfo_t *fdDeviceToType(fd_softc_t *, dev_t); 86 87 static void fdstart(fd_softc_t *); 88 static void remap_geometry(daddr_t, int, diskPosition_t *); 89 static void motor_off(void *); 90 static int seek(fd_softc_t *, int); 91 static int checkTrack(diskPosition_t *, int); 92 static int initCylinderCache(fd_softc_t *); 93 static void invalidateCylinderCache(fd_softc_t *); 94 95 static int fdstart_Init(fd_softc_t *); 96 static int fdstart_Seek(fd_softc_t *); 97 static int fdstart_Read(fd_softc_t *); 98 static int fdstart_Write(fd_softc_t *); 99 static int fdstart_Flush(fd_softc_t *); 100 static int fdstart_IOFinish(fd_softc_t *); 101 static int fdstart_IOErr(fd_softc_t *); 102 static int fdstart_Fault(fd_softc_t *); 103 static int fdstart_Exit(fd_softc_t *); 104 105 106 /** 107 ** Driver debugging 108 **/ 109 110 #ifdef DEBUG 111 #define IWM_DEBUG 112 #endif 113 114 115 static void hexDump(u_char *, int); 116 117 /* 118 * Stuff taken from Egan/Teixeira ch 8: 'if(TRACE_FOO)' debug output 119 * statements don't break indentation, and when DEBUG is not defined, 120 * the compiler code optimizer drops them as dead code. 121 */ 122 #ifdef IWM_DEBUG 123 #define M_TRACE_CONFIG 0x0001 124 #define M_TRACE_OPEN 0x0002 125 #define M_TRACE_CLOSE 0x0004 126 #define M_TRACE_READ 0x0008 127 #define M_TRACE_WRITE 0x0010 128 #define M_TRACE_STRAT (M_TRACE_READ | M_TRACE_WRITE) 129 #define M_TRACE_IOCTL 0x0020 130 #define M_TRACE_STEP 0x0040 131 #define M_TRACE_ALL 0xFFFF 132 133 #define TRACE_CONFIG (iwmDebugging & M_TRACE_CONFIG) 134 #define TRACE_OPEN (iwmDebugging & M_TRACE_OPEN) 135 #define TRACE_CLOSE (iwmDebugging & M_TRACE_CLOSE) 136 #define TRACE_READ (iwmDebugging & M_TRACE_READ) 137 #define TRACE_WRITE (iwmDebugging & M_TRACE_WRITE) 138 #define TRACE_STRAT (iwmDebugging & M_TRACE_STRAT) 139 #define TRACE_IOCTL (iwmDebugging & M_TRACE_IOCTL) 140 #define TRACE_STEP (iwmDebugging & M_TRACE_STEP) 141 #define TRACE_ALL (iwmDebugging & M_TRACE_ALL) 142 143 /* -1 = all active */ 144 int iwmDebugging = 0 /* | M_TRACE_OPEN | M_TRACE_STRAT | M_TRACE_IOCTL */ ; 145 146 #else 147 #define TRACE_CONFIG 0 148 #define TRACE_OPEN 0 149 #define TRACE_CLOSE 0 150 #define TRACE_READ 0 151 #define TRACE_WRITE 0 152 #define TRACE_STRAT 0 153 #define TRACE_IOCTL 0 154 #define TRACE_STEP 0 155 #define TRACE_ALL 0 156 #endif 157 158 #define DISABLED 0 159 160 161 162 /** 163 ** Module-global Variables 164 **/ 165 166 /* The controller base address */ 167 u_long IWMBase; 168 169 /* 170 * Table of supported disk types. 171 * The table order seems to be pretty standardized across NetBSD ports, but 172 * then, they are all MFM... So we roll our own for now. 173 */ 174 static fdInfo_t fdTypes[] = { 175 {1, 80, 512, 10, 10, 800, 12, 2, IWM_GCR, "400K Sony"}, 176 {2, 80, 512, 10, 20, 1600, 12, 2, IWM_GCR, "800K Sony"} 177 }; 178 179 /* Table of GCR disk zones for one side (see IM II-211, The Disk Driver) */ 180 static diskZone_t diskZones[] = { 181 {16, 12, 0, 191}, 182 {16, 11, 192, 367}, 183 {16, 10, 368, 527}, 184 {16, 9, 528, 671}, 185 {16, 8, 672, 799} 186 }; 187 188 /* Drive format codes/indexes */ 189 enum { 190 IWM_400K_GCR = 0, 191 IWM_800K_GCR = 1, 192 IWM_720K_MFM = 2, 193 IWM_1440K_MFM = 3 194 }; 195 196 197 /** 198 ** Autoconfiguration code 199 **/ 200 201 /* 202 * Autoconfig data structures 203 * 204 * These data structures (see <sys/device.h>) are referenced in 205 * compile/$KERNEL/ioconf.c, which is generated by config(8). 206 * Their names are formed like {device}_{ca,cd}. 207 * 208 * {device}_ca 209 * is used for dynamically allocating driver data, probing and 210 * attaching a device; 211 * 212 * {device}_cd 213 * references all found devices of a type. 214 */ 215 216 extern struct cfdriver iwm_cd; 217 extern struct cfdriver fd_cd; 218 219 /* IWM floppy disk controller */ 220 CFATTACH_DECL_NEW(iwm, sizeof(iwm_softc_t), 221 iwm_match, iwm_attach, NULL, NULL); 222 223 /* Attached floppy disk drives */ 224 CFATTACH_DECL_NEW(fd, sizeof(fd_softc_t), 225 fd_match, fd_attach, NULL, NULL); 226 227 dev_type_open(fdopen); 228 dev_type_close(fdclose); 229 dev_type_read(fdread); 230 dev_type_write(fdwrite); 231 dev_type_ioctl(fdioctl); 232 dev_type_strategy(fdstrategy); 233 234 const struct bdevsw fd_bdevsw = { 235 .d_open = fdopen, 236 .d_close = fdclose, 237 .d_strategy = fdstrategy, 238 .d_ioctl = fdioctl, 239 .d_dump = nodump, 240 .d_psize = nosize, 241 .d_discard = nodiscard, 242 .d_flag = D_DISK 243 }; 244 245 const struct cdevsw fd_cdevsw = { 246 .d_open = fdopen, 247 .d_close = fdclose, 248 .d_read = fdread, 249 .d_write = fdwrite, 250 .d_ioctl = fdioctl, 251 .d_stop = nostop, 252 .d_tty = notty, 253 .d_poll = nopoll, 254 .d_mmap = nommap, 255 .d_kqfilter = nokqfilter, 256 .d_discard = nodiscard, 257 .d_flag = D_DISK 258 }; 259 260 /* disk(9) framework device switch */ 261 struct dkdriver fd_dkDriver = { 262 .d_strategy = fdstrategy 263 }; 264 265 /*** Configure the IWM controller ***/ 266 267 /* 268 * iwm_match 269 * 270 * Is the IWM chip present? Here, *aux is a ptr to struct confargs 271 * (see <mac68k/mac68k/autoconf.h>), which does not hold any information 272 * to match against. After all, that's what the obio concept is 273 * about: Onboard components that are present depending (only) 274 * on machine type. 275 * 276 * While here, map the machine-dependent physical IO address of IWM 277 * to VM address. 278 * 279 * We do not match, nor return an IWMBase address, for machines whose 280 * SWIM does not support the IWM register set used by this driver 281 * (SWIM II/III, SWIM behind IOP, AV models' DMA based controllers). 282 * Unfortunately, this distinction does not run cleanly along 283 * MACH_CLASS* lines, and we will have to look at MACH_MAC{model} tags. 284 * 285 * See also "What chips are in what Macs?" at 286 * <http://bitsavers.org/pdf/apple/mac/mess/Mac_Technical_Notes.html>, 287 */ 288 int 289 iwm_match(device_t parent, cfdata_t match, void *aux) 290 { 291 int matched = 0; 292 extern u_long IOBase; /* from mac68k/machdep.c */ 293 extern u_long IWMBase; 294 295 IWMBase = 0L; 296 297 switch (current_mac_model->class) { 298 case MACH_CLASSPB: /* Not: Powerbook 5x0, 190x */ 299 if (current_mac_model->machineid != MACH_MACPB500 && 300 current_mac_model->machineid != MACH_MACPB190 && 301 current_mac_model->machineid != MACH_MACPB190CS) { 302 IWMBase = IOBase + 0x16000; 303 matched = 1; 304 break; 305 } 306 /* FALLTHROUGH */ 307 case MACH_CLASSLC: /* Only: LC II, Classic II */ 308 if (current_mac_model->machineid == MACH_MACLCII || 309 current_mac_model->machineid == MACH_MACCLASSICII) { 310 IWMBase = IOBase + 0x16000; 311 matched = 1; 312 break; 313 } 314 /* FALLTHROUGH */ 315 case MACH_CLASSII: /* All */ 316 case MACH_CLASSIIci: /* All */ 317 case MACH_CLASSIIsi: /* All */ 318 case MACH_CLASSIIvx: /* All */ 319 case MACH_CLASSDUO: /* All */ 320 IWMBase = IOBase + 0x16000; 321 matched = 1; 322 break; 323 case MACH_CLASSQ: /* Only: Quadra 700 */ 324 if (current_mac_model->machineid == MACH_MACQ700) { 325 IWMBase = IOBase + 0x1E000; 326 matched = 1; 327 break; 328 } 329 /* FALLTHROUGH */ 330 case MACH_CLASSQ2: /* None */ 331 case MACH_CLASSP580: /* None */ 332 case MACH_CLASSIIfx: /* None */ 333 case MACH_CLASSAV: /* None */ 334 default: 335 break; 336 } 337 338 if (TRACE_CONFIG) { 339 if (matched == 0) 340 printf("IWM or original SWIM not found.\n"); 341 else 342 printf("IWMBase mapped to VM addr 0x%lx.\n", 343 IWMBase); 344 } 345 return matched; 346 } 347 348 349 /* 350 * iwm_attach 351 * 352 * The IWM is present, initialize it. Then look up the connected drives 353 * and attach them. 354 */ 355 void 356 iwm_attach(device_t parent, device_t self, void *aux) 357 { 358 int iwmErr; 359 iwm_softc_t *iwm; 360 iwmAttachArgs_t ia; 361 362 printf(": Apple GCR floppy disk controller\n"); 363 iwm = device_private(self); 364 365 iwmErr = iwmInit(); 366 if (TRACE_CONFIG) 367 printf("initIWM() says %d.\n", iwmErr); 368 369 if (0 == iwmErr) { 370 /* Set up the IWM softc */ 371 iwm->maxRetries = 10; 372 373 /* Look for attached drives */ 374 for (ia.unit = 0; ia.unit < IWM_MAX_DRIVE; ia.unit++) { 375 iwm->fd[ia.unit] = NULL; 376 ia.driveType = getFDType(ia.unit); 377 if (NULL != ia.driveType) 378 config_found(self, (void *)&ia, 379 fd_print, CFARGS_NONE); 380 } 381 if (TRACE_CONFIG) 382 printf("iwm: Initialization completed.\n"); 383 } else { 384 printf("iwm: Initialization failed (%d)\n", iwmErr); 385 } 386 } 387 388 389 /* 390 * iwm_print -- print device configuration. 391 * 392 * If the device is not configured 'controller' it is NULL and 393 * we print a message in the *Attach routine; the return value 394 * of *Print() is ignored. 395 */ 396 int 397 iwm_print(void *aux, const char *controller) 398 { 399 return UNCONF; 400 } 401 402 403 404 /*** Configure Sony disk drive(s) ***/ 405 406 /* 407 * fd_match 408 */ 409 int 410 fd_match(device_t parent, cfdata_t match, void *aux) 411 { 412 int matched, cfUnit; 413 struct cfdata *cfp; 414 iwmAttachArgs_t *fdParams; 415 416 cfp = match; 417 fdParams = aux; 418 cfUnit = cfp->cf_loc[IWMCF_DRIVE]; 419 matched = (cfUnit == fdParams->unit || cfUnit == -1) ? 1 : 0; 420 if (TRACE_CONFIG) { 421 printf("fdMatch() drive %d ? cfUnit = %d\n", 422 fdParams->unit, cfUnit); 423 } 424 return matched; 425 } 426 427 428 /* 429 * fd_attach 430 * 431 * We have checked that the IWM is fine and the drive is present, 432 * so we can attach it. 433 */ 434 void 435 fd_attach(device_t parent, device_t self, void *aux) 436 { 437 iwm_softc_t *iwm; 438 fd_softc_t *fd; 439 iwmAttachArgs_t *ia; 440 int driveInfo; 441 442 iwm = device_private(parent); 443 fd = device_private(self); 444 fd->sc_dev = self; 445 ia = aux; 446 447 driveInfo = iwmCheckDrive(ia->unit); 448 449 fd->currentType = ia->driveType; 450 fd->unit = ia->unit; 451 fd->defaultType = &fdTypes[IWM_800K_GCR]; 452 fd->stepDirection = 0; 453 454 iwm->fd[ia->unit] = fd; /* iwm has ptr to this drive */ 455 iwm->drives++; 456 457 bufq_alloc(&fd->bufQueue, "disksort", BUFQ_SORT_CYLINDER); 458 callout_init(&fd->motor_ch, 0); 459 460 printf(" drive %d: ", fd->unit); 461 462 if (IWM_NO_DISK & driveInfo) { 463 printf("(drive empty)\n"); 464 } else 465 if (!(IWM_DD_DISK & driveInfo)) { 466 printf("(HD disk -- not supported)\n"); 467 iwmDiskEject(fd->unit); /* XXX */ 468 } else { 469 printf("%s %d cyl, %d head(s)\n", 470 fd->currentType->description, 471 fd->currentType->tracks, 472 fd->currentType->heads); 473 } 474 if (TRACE_CONFIG) { 475 int reg, flags, spl; 476 477 /* List contents of drive status registers */ 478 spl = spl6(); 479 for (reg = 0; reg < 0x10; reg++) { 480 flags = iwmQueryDrvFlag(fd->unit, reg); 481 printf("iwm: Drive register 0x%x = 0x%x\n", reg, flags); 482 } 483 splx(spl); 484 } 485 disk_init(&fd->diskInfo, device_xname(fd->sc_dev), &fd_dkDriver); 486 disk_attach(&fd->diskInfo); 487 } 488 489 490 /* 491 * fdPrint -- print device configuration. 492 * 493 * If the device is not configured 'controller' refers to a name string 494 * we print here. 495 * Else it is NULL and we print a message in the *Attach routine; the 496 * return value of *Print() is ignored. 497 */ 498 int 499 fd_print(void *aux, const char *controller) 500 { 501 iwmAttachArgs_t *ia; 502 503 ia = aux; 504 if (NULL != controller) 505 aprint_normal("fd%d at %s", ia->unit, controller); 506 return UNCONF; 507 } 508 509 /** 510 ** Implementation section of driver interface 511 ** 512 ** The prototypes for these functions are set up automagically 513 ** by macros in mac68k/conf.c. Their names are generated from {fd} 514 ** and {open,close,strategy,dump,size,read,write}. The driver entry 515 ** points are then plugged into bdevsw[] and cdevsw[]. 516 **/ 517 518 519 /* 520 * fdopen 521 * 522 * Open a floppy disk device. 523 */ 524 int 525 fdopen(dev_t dev, int flags, int devType, struct lwp *l) 526 { 527 fd_softc_t *fd; 528 fdInfo_t *info; 529 int partitionMask; 530 int fdType, fdUnit; 531 int ierr, err; 532 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); /* XXX */ 533 info = NULL; /* XXX shut up egcs */ 534 fd = NULL; /* XXX shut up gcc3 */ 535 536 /* 537 * See <device.h> for struct cfdriver, <disklabel.h> for 538 * DISKUNIT() and <atari/atari/device.h> for getsoftc(). 539 */ 540 fdType = minor(dev) % MAXPARTITIONS; 541 fdUnit = minor(dev) / MAXPARTITIONS; 542 if (TRACE_OPEN) 543 printf("iwm: Open drive %d", fdUnit); 544 545 /* Check if device # is valid */ 546 err = (iwm->drives < fdUnit) ? ENXIO : 0; 547 if (!err) { 548 (void)iwmSelectDrive(fdUnit); 549 if (TRACE_OPEN) 550 printf(".\n Get softc"); 551 552 /* Get fd state */ 553 fd = iwm->fd[fdUnit]; 554 err = (NULL == fd) ? ENXIO : 0; 555 } 556 if (!err) { 557 if (fd->state & IWM_FD_IS_OPEN) { 558 /* 559 * Allow multiple open calls only if for identical 560 * floppy format. 561 */ 562 if (TRACE_OPEN) 563 printf(".\n Drive already opened!\n"); 564 err = (fd->partition == fdType) ? 0 : ENXIO; 565 } else { 566 if (TRACE_OPEN) 567 printf(".\n Get format info"); 568 569 /* Get format type */ 570 info = fdDeviceToType(fd, dev); 571 if (NULL == info) { 572 err = ENXIO; 573 if (TRACE_OPEN) 574 printf(".\n No such drive.\n"); 575 } 576 } 577 } 578 if (!err && !(fd->state & IWM_FD_IS_OPEN)) { 579 if (TRACE_OPEN) 580 printf(".\n Set diskInfo flags.\n"); 581 582 fd->writeLabel = 0; /* XXX currently unused */ 583 fd->partition = fdType; 584 fd->currentType = info; 585 fd->drvFlags = iwmCheckDrive(fd->unit); 586 587 if (fd->drvFlags & IWM_NO_DISK) { 588 err = EIO; 589 #ifdef DIAGNOSTIC 590 printf(" Drive %d is empty.\n", fd->unit); 591 #endif 592 } else { 593 if (!(fd->drvFlags & IWM_WRITABLE) && (flags & FWRITE)) { 594 595 err = EPERM; 596 #ifdef DIAGNOSTIC 597 printf(" Disk is write protected.\n"); 598 #endif 599 } else { 600 if (!(fd->drvFlags & IWM_DD_DISK)) { 601 err = ENXIO; 602 #ifdef DIAGNOSTIC 603 printf(" HD format not supported.\n"); 604 #endif 605 (void)iwmDiskEject(fd->unit); 606 } else { 607 /* We're open now! */ 608 fd->state |= IWM_FD_IS_OPEN; 609 err = initCylinderCache(fd); 610 } 611 } 612 } 613 } 614 if (!err) { 615 /* 616 * Later, we might not want to recalibrate the drive when it 617 * is already open. For now, it doesn't hurt. 618 */ 619 if (TRACE_OPEN) 620 printf(" Seek track 00 says"); 621 622 memset(&fd->pos, 0, sizeof(diskPosition_t)); 623 ierr = seek(fd, IWM_SEEK_RECAL); 624 if (TRACE_OPEN) 625 printf(" %d.\n", ierr); 626 err = (0 == ierr) ? 0 : EIO; 627 } 628 if (!err) { 629 /* 630 * Update disklabel if we are not yet open. 631 * (We shouldn't be: We are synchronous.) 632 */ 633 if (fd->diskInfo.dk_openmask == 0) 634 fdGetDiskLabel(fd, dev); 635 636 partitionMask = (1 << fdType); 637 638 switch (devType) { 639 case S_IFCHR: 640 fd->diskInfo.dk_copenmask |= partitionMask; 641 break; 642 643 case S_IFBLK: 644 fd->diskInfo.dk_bopenmask |= partitionMask; 645 break; 646 } 647 fd->diskInfo.dk_openmask = 648 fd->diskInfo.dk_copenmask | fd->diskInfo.dk_bopenmask; 649 } 650 if (TRACE_OPEN) 651 printf("iwm: fdopen() says %d.\n", err); 652 return err; 653 } 654 655 656 /* 657 * fdclose 658 */ 659 int 660 fdclose(dev_t dev, int flags, int devType, struct lwp *l) 661 { 662 fd_softc_t *fd; 663 int partitionMask, fdUnit, fdType; 664 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); 665 666 if (TRACE_CLOSE) 667 printf("iwm: Closing driver."); 668 fdUnit = minor(dev) / MAXPARTITIONS; 669 fdType = minor(dev) % MAXPARTITIONS; 670 fd = iwm->fd[fdUnit]; 671 /* release cylinder cache memory */ 672 if (fd->cbuf != NULL) { 673 kmem_free(fd->cbuf, 674 IWM_MAX_GCR_SECTORS * fd->currentType->sectorSize); 675 } 676 677 partitionMask = (1 << fdType); 678 679 /* Set state flag. */ 680 fd->state &= ~IWM_FD_IS_OPEN; 681 682 switch (devType) { 683 case S_IFCHR: 684 fd->diskInfo.dk_copenmask &= ~partitionMask; 685 break; 686 687 case S_IFBLK: 688 fd->diskInfo.dk_bopenmask &= ~partitionMask; 689 break; 690 } 691 fd->diskInfo.dk_openmask = 692 fd->diskInfo.dk_copenmask | fd->diskInfo.dk_bopenmask; 693 return 0; 694 } 695 696 697 /* 698 * fdioctl 699 * 700 * We deal with all the disk-specific ioctls in <sys/dkio.h> here even if 701 * we do not support them. 702 */ 703 int 704 fdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 705 { 706 int result, fdUnit, fdType; 707 fd_softc_t *fd; 708 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); 709 int error; 710 711 if (TRACE_IOCTL) 712 printf("iwm: Execute ioctl... "); 713 714 /* Check if device # is valid and get its softc */ 715 fdUnit = minor(dev) / MAXPARTITIONS; 716 fdType = minor(dev) % MAXPARTITIONS; 717 if (fdUnit >= iwm->drives) { 718 if (TRACE_IOCTL) { 719 printf("iwm: Wanted device no (%d) is >= %d.\n", 720 fdUnit, iwm->drives); 721 } 722 return ENXIO; 723 } 724 fd = iwm->fd[fdUnit]; 725 result = 0; 726 727 error = disk_ioctl(&fd->diskInfo, fdType, cmd, data, flag, l); 728 if (error != EPASSTHROUGH) 729 return error; 730 731 switch (cmd) { 732 case DIOCSDINFO: 733 if (TRACE_IOCTL) 734 printf(" DIOCSDINFO: Set in-core disklabel.\n"); 735 result = ((flag & FWRITE) == 0) ? EBADF : 0; 736 if (result == 0) 737 result = setdisklabel(fd->diskInfo.dk_label, 738 (struct disklabel *)data, 0, 739 fd->diskInfo.dk_cpulabel); 740 break; 741 742 case DIOCWDINFO: 743 if (TRACE_IOCTL) 744 printf(" DIOCWDINFO: Set in-core disklabel " 745 "& update disk.\n"); 746 result = ((flag & FWRITE) == 0) ? EBADF : 0; 747 748 if (result == 0) 749 result = setdisklabel(fd->diskInfo.dk_label, 750 (struct disklabel *)data, 0, 751 fd->diskInfo.dk_cpulabel); 752 if (result == 0) 753 result = writedisklabel(dev, fdstrategy, 754 fd->diskInfo.dk_label, 755 fd->diskInfo.dk_cpulabel); 756 break; 757 758 case DIOCRFORMAT: 759 case DIOCWFORMAT: 760 if (TRACE_IOCTL) 761 printf(" DIOC{R,W}FORMAT: No formatter support (yet?).\n"); 762 result = EINVAL; 763 break; 764 765 case DIOCSSTEP: 766 if (TRACE_IOCTL) 767 printf(" DIOCSSTEP: IWM does step handshake.\n"); 768 result = EINVAL; 769 break; 770 771 case DIOCSRETRIES: 772 if (TRACE_IOCTL) 773 printf(" DIOCSRETRIES: Set max. # of retries.\n"); 774 if (*(int *)data < 0) 775 result = EINVAL; 776 else { 777 iwm->maxRetries = *(int *)data; 778 result = 0; 779 } 780 break; 781 782 case DIOCWLABEL: 783 if (TRACE_IOCTL) 784 printf(" DIOCWLABEL: Set write access to disklabel.\n"); 785 result = ((flag & FWRITE) == 0) ? EBADF : 0; 786 787 if (result == 0) 788 fd->writeLabel = *(int *)data; 789 break; 790 791 case DIOCSBAD: 792 if (TRACE_IOCTL) 793 printf(" DIOCSBAD: No bad144-style handling.\n"); 794 result = EINVAL; 795 break; 796 797 case ODIOCEJECT: 798 case DIOCEJECT: 799 /* XXX Eject disk only when unlocked */ 800 if (TRACE_IOCTL) 801 printf(" DIOCEJECT: Eject disk from unit %d.\n", 802 fd->unit); 803 result = iwmDiskEject(fd->unit); 804 break; 805 806 case DIOCLOCK: 807 /* XXX Use lock to prevent ejectimg a mounted disk */ 808 if (TRACE_IOCTL) 809 printf(" DIOCLOCK: No need to (un)lock Sony drive.\n"); 810 result = 0; 811 break; 812 813 default: 814 if (TRACE_IOCTL) 815 printf(" Not a disk related ioctl!\n"); 816 result = ENOTTY; 817 break; 818 } 819 return result; 820 } 821 822 823 /* 824 * fdread 825 */ 826 int 827 fdread(dev_t dev, struct uio *uio, int flags) 828 { 829 return physio(fdstrategy, NULL, dev, B_READ, minphys, uio); 830 } 831 832 833 /* 834 * fdwrite 835 */ 836 int 837 fdwrite(dev_t dev, struct uio *uio, int flags) 838 { 839 return physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio); 840 } 841 842 843 /* 844 * fdstrategy 845 * 846 * Entry point for read and write requests. The strategy routine usually 847 * queues io requests and kicks off the next transfer if the device is idle; 848 * but we get no interrupts from the IWM and have to do synchronous 849 * transfers - no queue. 850 */ 851 void 852 fdstrategy(struct buf *bp) 853 { 854 int fdUnit, err, done, spl; 855 int sectSize, transferSize; 856 diskPosition_t physDiskLoc; 857 fd_softc_t *fd; 858 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); 859 860 err = 0; 861 done = 0; 862 sectSize = 0; /* XXX shut up gcc3 */ 863 fd = NULL; /* XXX shut up gcc3 */ 864 865 fdUnit = minor(bp->b_dev) / MAXPARTITIONS; 866 if (TRACE_STRAT) { 867 printf("iwm: fdstrategy()...\n"); 868 printf(" struct buf is at %p\n", bp); 869 printf(" Allocated buffer size (b_bufsize): 0x0%x\n", 870 bp->b_bufsize); 871 printf(" Base address of buffer (b_data): %p\n", 872 bp->b_data); 873 printf(" Bytes to be transferred (b_bcount): 0x0%x\n", 874 bp->b_bcount); 875 printf(" Remaining I/O (b_resid): 0x0%x\n", 876 bp->b_resid); 877 } 878 /* Check for valid fd unit, controller and io request */ 879 880 if (fdUnit >= iwm->drives) { 881 if (TRACE_STRAT) 882 printf(" No such unit (%d)\n", fdUnit); 883 err = EINVAL; 884 } 885 if (!err) { 886 fd = iwm->fd[fdUnit]; 887 err = (NULL == fd) ? EINVAL : 0; 888 } 889 if (!err) { 890 sectSize = fd->currentType->sectorSize; 891 if (bp->b_blkno < 0 892 || (bp->b_bcount % sectSize) != 0) { 893 if (TRACE_STRAT) 894 printf(" Illegal transfer size: " 895 "block %lld, %d bytes\n", 896 (long long) bp->b_blkno, bp->b_bcount); 897 err = EINVAL; 898 } 899 } 900 if (!err) { 901 /* Null transfer: Return, nothing to do. */ 902 if (0 == bp->b_bcount) { 903 if (TRACE_STRAT) 904 printf(" Zero transfer length.\n"); 905 done = 1; 906 } 907 } 908 if (!err && !done) { 909 /* What to do if we touch the boundaries of the disk? */ 910 transferSize = (bp->b_bcount + (sectSize - 1)) / sectSize; 911 if (bp->b_blkno + transferSize > fd->currentType->secPerDisk) { 912 if (TRACE_STRAT) { 913 printf("iwm: Transfer beyond end of disk!\n" \ 914 " (Starting block %lld, # of blocks %d," \ 915 " last disk block %d).\n", 916 (long long) bp->b_blkno, transferSize, 917 fd->currentType->secPerDisk); 918 } 919 /* Return EOF if we are exactly at the end of the 920 * disk, EINVAL if we try to reach past the end; else 921 * truncate the request. */ 922 transferSize = fd->currentType->secPerDisk - 923 bp->b_blkno; 924 if (0 == transferSize) { 925 bp->b_resid = bp->b_bcount; 926 done = 1; 927 } else 928 if (0 > transferSize) 929 err = EINVAL; 930 else 931 bp->b_bcount = transferSize << DEV_BSHIFT; 932 } 933 } 934 if (!err && !done) { 935 /* 936 * Calculate cylinder # for disksort(). 937 * 938 * XXX Shouldn't we use the (fake) logical cyl no here? 939 */ 940 remap_geometry(bp->b_blkno, fd->currentType->heads, 941 &physDiskLoc); 942 bp->b_rawblkno = bp->b_blkno; 943 bp->b_cylinder = physDiskLoc.track; 944 945 if (TRACE_STRAT) { 946 printf(" This job starts at b_blkno %lld; ", 947 (long long) bp->b_blkno); 948 printf("it gets sorted for cylinder # %d.\n", 949 bp->b_cylinder); 950 } 951 spl = splbio(); 952 callout_stop(&fd->motor_ch); 953 bufq_put(fd->bufQueue, bp); 954 if (fd->sc_active == 0) 955 fdstart(fd); 956 splx(spl); 957 } 958 /* Clean up, if necessary */ 959 else { 960 if (TRACE_STRAT) 961 printf(" fdstrategy() finished early, err = %d.\n", 962 err); 963 if (err) 964 bp->b_error = err; 965 bp->b_resid = bp->b_bcount; 966 biodone(bp); 967 } 968 /* Comment on results */ 969 if (TRACE_STRAT) { 970 printf("iwm: fdstrategy() done.\n"); 971 printf(" We have b_resid = %d bytes left, " \ 972 "b_error is %d;\n", bp->b_resid, bp->b_error); 973 printf(" b_flags are 0x0%x.\n", bp->b_flags); 974 } 975 } 976 977 978 979 /* ======================================================================== */ 980 981 982 /* 983 * fdstart 984 * 985 * we are called from the strategy() routine to perform a data transfer. 986 * 987 * The disk(9) framework demands we run at splbio(); our caller 988 * takes care of that. 989 * 990 * Wish we had pascalish local functions here... 991 */ 992 993 /* fdstart FSM states */ 994 enum { 995 state_Init = 0, 996 state_Seek, 997 state_Read, 998 state_Write, 999 state_Flush, 1000 state_IOFinish, 1001 state_IOErr, 1002 state_Fault, 1003 state_Exit, 1004 state_Done 1005 }; 1006 1007 static void 1008 fdstart(fd_softc_t *fd) 1009 { 1010 int st; 1011 1012 static const char *stateDesc[] = { 1013 "Init", 1014 "Seek", 1015 "Read", 1016 "Write", 1017 "Flush", 1018 "IOFinish", 1019 "IOErr", 1020 "Fault", 1021 "Exit", 1022 "Done" 1023 }; 1024 int (*state[])(fd_softc_t *) = { 1025 fdstart_Init, 1026 fdstart_Seek, 1027 fdstart_Read, 1028 fdstart_Write, 1029 fdstart_Flush, 1030 fdstart_IOFinish, 1031 fdstart_IOErr, 1032 fdstart_Fault, 1033 fdstart_Exit 1034 }; 1035 1036 st = state_Init; 1037 do { 1038 if (TRACE_STRAT) 1039 printf(" fdstart state %d [%s] ", 1040 st, stateDesc[st]); 1041 1042 st = (*state[st])(fd); 1043 1044 if (TRACE_STRAT) 1045 printf(".\n"); 1046 } while (st != state_Done); 1047 } 1048 1049 1050 /* 1051 * fdstart_Init 1052 * 1053 * Set up things 1054 */ 1055 static int 1056 fdstart_Init(fd_softc_t *fd) 1057 { 1058 struct buf *bp; 1059 1060 /* 1061 * Get the first entry from the queue. This is the buf we gave to 1062 * fdstrategy(); disksort() put it into our softc. 1063 */ 1064 bp = bufq_peek(fd->bufQueue); 1065 if (NULL == bp) { 1066 if (TRACE_STRAT) 1067 printf("Queue empty: Nothing to do"); 1068 return state_Done; 1069 } 1070 fd->ioDirection = bp->b_flags & B_READ; 1071 1072 disk_busy(&fd->diskInfo); 1073 if (!(fd->state & IWM_FD_MOTOR_ON)) { 1074 iwmMotor(fd->unit, 1); 1075 fd->state |= IWM_FD_MOTOR_ON; 1076 } 1077 fd->current_buffer = bp->b_data; 1078 1079 /* XXX - assumes blocks of 512 bytes */ 1080 fd->startBlk = bp->b_blkno; 1081 1082 fd->iwmErr = 0; 1083 fd->ioRetries = 0; /* XXX */ 1084 fd->seekRetries = 0; 1085 fd->bytesDone = 0; 1086 fd->bytesLeft = bp->b_bcount; 1087 return state_Seek; 1088 } 1089 1090 1091 /* 1092 * fdstart_Seek 1093 */ 1094 static int 1095 fdstart_Seek(fd_softc_t *fd) 1096 { 1097 int state; 1098 1099 /* Calculate the side/track/sector our block is at. */ 1100 if (TRACE_STRAT) 1101 printf(" Remap block %lld ", (long long) fd->startBlk); 1102 remap_geometry(fd->startBlk, 1103 fd->currentType->heads, &fd->pos); 1104 if (TRACE_STRAT) 1105 printf("to c%d_h%d_s%d ", fd->pos.track, 1106 fd->pos.side, fd->pos.sector); 1107 1108 if (fd->cachedSide != fd->pos.side) { 1109 if (TRACE_STRAT) 1110 printf(" (invalidate cache) "); 1111 invalidateCylinderCache(fd); 1112 fd->cachedSide = fd->pos.side; 1113 } 1114 1115 /* 1116 * If necessary, seek to wanted track. Note that 1117 * seek() performs any necessary retries. 1118 */ 1119 if (fd->pos.track != fd->pos.oldTrack && 1120 0 != (fd->iwmErr = seek(fd, IWM_SEEK_VANILLA))) { 1121 state = state_Fault; 1122 } else { 1123 state = (fd->ioDirection == IWM_WRITE) 1124 ? state_Write : state_Read; 1125 } 1126 return state; 1127 } 1128 1129 1130 /* 1131 * fdstart_Read 1132 * 1133 * Transfer a sector from disk. Get it from the track cache, if available; 1134 * otherwise, while we are at it, store in the cache all the sectors we find 1135 * on the way. 1136 * 1137 * Track buffering reads: 1138 * o Look if the sector is already cached. 1139 * o Else, read sectors into track cache until we meet the header of 1140 * the sector we want. 1141 * o Read that sector directly to fs buffer and return. 1142 */ 1143 static int 1144 fdstart_Read(fd_softc_t *fd) 1145 { 1146 int i; 1147 diskPosition_t *pos; 1148 sectorHdr_t *shdr; 1149 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); /* XXX */ 1150 1151 /* Initialize retry counters */ 1152 fd->seekRetries = 0; 1153 fd->sectRetries = 0; 1154 pos = &fd->pos; 1155 shdr = &fd->sHdr; 1156 1157 if (TRACE_STRAT) 1158 printf("<%s c%d_h%d_s%d> ", 1159 fd->ioDirection ? "Read" : "Write", 1160 pos->track, pos->side, pos->sector); 1161 1162 /* Sector already cached? */ 1163 i = pos->sector; 1164 if (fd->r_slots[i].valid) { 1165 if (TRACE_STRAT) 1166 printf("(cached)"); 1167 memcpy(fd->current_buffer, fd->r_slots[i].secbuf, 1168 fd->currentType->sectorSize); 1169 return state_IOFinish; 1170 } 1171 1172 /* Get sector from disk */ 1173 shdr->side = pos->side; 1174 shdr->sector = pos->sector; 1175 shdr->track = pos->track; 1176 1177 (void)iwmSelectSide(pos->side); 1178 fd->iwmErr = iwmReadSector(&fd->sHdr, fd->r_slots, 1179 fd->current_buffer); 1180 1181 /* Check possible error conditions */ 1182 if (TRACE_STRAT) 1183 printf("c%d_h%d_s%d_err(%d)_sr%d ", 1184 shdr->track, shdr->side >> 3, 1185 shdr->sector, fd->iwmErr, fd->sectRetries); 1186 1187 /* IWM IO error? */ 1188 if (fd->iwmErr != 0) 1189 return state_IOErr; 1190 1191 /* Bad seek? Retry */ 1192 if (shdr->track != pos->track) { 1193 if (TRACE_STRAT) { 1194 printf("Wanted track %d, got %d, %d seek retries.\n", 1195 pos->track, shdr->track, fd->seekRetries); 1196 } 1197 if (iwm->maxRetries > fd->seekRetries++) { 1198 fd->iwmErr = seek(fd, IWM_SEEK_RECAL); 1199 if (TRACE_STRAT) { 1200 printf("[%d]", fd->seekRetries); 1201 (void)checkTrack(&fd->pos, 1); 1202 } 1203 } else 1204 fd->iwmErr = seekErr; 1205 return (0 == fd->iwmErr) ? state_Read : state_Fault; 1206 } 1207 1208 /* Sector not found? */ 1209 if (shdr->sector != pos->sector) { 1210 if (TRACE_STRAT) 1211 printf("c%d_h%d_s%d sect not found, %d retries ", 1212 shdr->track, shdr->side >> 3, 1213 shdr->sector, fd->sectRetries); 1214 fd->iwmErr = noAdrMkErr; 1215 return state_Fault; 1216 } 1217 1218 /* Success */ 1219 return state_IOFinish; 1220 } 1221 1222 1223 /* 1224 * fdstart_Write 1225 * 1226 * Insert a sector into a write buffer slot and mark the slot dirty. 1227 */ 1228 static int 1229 fdstart_Write(fd_softc_t *fd) 1230 { 1231 int i; 1232 1233 /* XXX let's see... */ 1234 fd->sHdr.side = fd->pos.side; 1235 fd->sHdr.sector = fd->pos.sector; 1236 fd->sHdr.track = fd->pos.track; 1237 1238 i = fd->pos.sector; 1239 fd->w_slots[i].secbuf = fd->current_buffer; 1240 fd->w_slots[i].valid = 1; /* "valid" is a dirty buffer here */ 1241 1242 if (TRACE_STRAT) 1243 printf("<%s c%d_h%d_s%d> (cached) ", 1244 fd->ioDirection ? "Read" : "Write", 1245 fd->pos.track, fd->pos.side, fd->pos.sector); 1246 return state_IOFinish; 1247 } 1248 1249 1250 1251 /* 1252 * fdstart_Flush 1253 * 1254 * Flush dirty buffers in the track cache to disk. 1255 */ 1256 static int 1257 fdstart_Flush(fd_softc_t *fd) 1258 { 1259 int state; 1260 int i, dcnt; 1261 diskPosition_t *pos; 1262 sectorHdr_t *shdr; 1263 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); /* XXX */ 1264 1265 dcnt = 0; 1266 pos = &fd->pos; 1267 shdr = &fd->sHdr; 1268 1269 if (TRACE_STRAT) { 1270 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) 1271 if (fd->w_slots[i].valid) { 1272 printf("|%d", i); 1273 dcnt++; 1274 } 1275 printf("|\n"); 1276 1277 printf(" <%s c%d_h%d_#s%d>\n", 1278 fd->ioDirection ? "Read" : "Write", 1279 pos->track, pos->side, dcnt); 1280 } 1281 (void)iwmSelectSide(pos->side); 1282 fd->iwmErr = iwmWriteSector(&fd->sHdr, fd->w_slots); 1283 1284 switch (fd->iwmErr) { 1285 case noErr: /* Success */ 1286 #ifdef DIAGNOSTIC 1287 /* XXX Panic if buffer not clean? */ 1288 for (i=0; i<IWM_MAX_GCR_SECTORS; i++) 1289 if (0 != fd->w_slots[i].valid) 1290 printf("Oops! <c%d_h%d_s%d> not flushed.\n", 1291 fd->pos.track, fd->pos.side, 1292 fd->pos.sector); 1293 #endif 1294 if (TRACE_STRAT) 1295 printf("(Cache flushed, re-initialize) "); 1296 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) { 1297 fd->w_slots[i].valid = 0; 1298 fd->w_slots[i].secbuf = NULL; 1299 } 1300 fd->seekRetries = 0; 1301 state = state_Exit; 1302 break; 1303 1304 case seekErr: /* Bad seek? Retry */ 1305 if (TRACE_STRAT) { 1306 printf("Wanted track %d, got %d, %d seek retries.\n", 1307 pos->track, shdr->track, fd->seekRetries); 1308 } 1309 if (iwm->maxRetries > fd->seekRetries++) { 1310 fd->iwmErr = seek(fd, IWM_SEEK_RECAL); 1311 if (TRACE_STRAT) { 1312 printf("[%d]", fd->seekRetries); 1313 } 1314 } 1315 state = (0 == fd->iwmErr) ? state_Exit : state_Fault; 1316 break; 1317 1318 default: /* General IWM IO error? */ 1319 state = state_IOErr; 1320 } 1321 return state; 1322 } 1323 1324 1325 /* 1326 * fdstart_IOFinish 1327 * 1328 * Prepare for next block, if any is available 1329 */ 1330 static int 1331 fdstart_IOFinish(fd_softc_t *fd) 1332 { 1333 int state; 1334 1335 if (DISABLED && TRACE_STRAT) 1336 printf("%s c%d_h%d_s%d ok ", 1337 fd->ioDirection ? "Read" : "Write", 1338 fd->sHdr.track, fd->sHdr.side >> 3, fd->sHdr.sector); 1339 1340 fd->bytesDone += fd->currentType->sectorSize; 1341 fd->bytesLeft -= fd->currentType->sectorSize; 1342 fd->current_buffer += fd->currentType->sectorSize; 1343 /* 1344 * Instead of recalculating the chs mapping for 1345 * each and every sector, check for 1346 * 'current sector# <= max sector#' and recalculate 1347 * after overflow. 1348 */ 1349 fd->startBlk++; 1350 if (fd->bytesLeft > 0) { 1351 if (++fd->pos.sector < fd->pos.maxSect) { 1352 if (TRACE_STRAT) 1353 printf("continue"); 1354 state = (fd->ioDirection == IWM_WRITE) 1355 ? state_Write : state_Read; 1356 } 1357 else { 1358 /* 1359 * Invalidate read cache when changing track; 1360 * flush write cache to disk. 1361 */ 1362 if (fd->ioDirection == IWM_WRITE) { 1363 if (TRACE_STRAT) 1364 printf("flush "); 1365 state = (state_Exit == fdstart_Flush(fd)) 1366 ? state_Seek : state_IOErr; 1367 } 1368 else { 1369 if (TRACE_STRAT) 1370 printf("step "); 1371 invalidateCylinderCache(fd); 1372 state = state_Seek; 1373 } 1374 } 1375 } else { 1376 state = (fd->ioDirection == IWM_WRITE) 1377 ? state_Flush : state_Exit; 1378 } 1379 return state; 1380 } 1381 1382 1383 /* 1384 * fdstart_IOErr 1385 * 1386 * Bad IO, repeat 1387 */ 1388 static int 1389 fdstart_IOErr(fd_softc_t *fd) 1390 { 1391 int state; 1392 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); /* XXX */ 1393 1394 #ifdef DIAGNOSTIC 1395 printf("iwm%sSector() err = %d, %d retries, on c%d_h%d_s%d.\n", 1396 fd->ioDirection ? "Read" : "Write", 1397 fd->iwmErr, fd->ioRetries, fd->pos.track, 1398 fd->pos.side, fd->pos.sector); 1399 #endif 1400 /* XXX Do statistics */ 1401 if (fd->ioRetries++ < iwm->maxRetries) 1402 state = (fd->ioDirection == IWM_WRITE) 1403 ? state_Flush : state_Read; 1404 else 1405 state = state_Fault; 1406 return state; 1407 } 1408 1409 1410 /* 1411 * fdstart_Fault 1412 * 1413 * A non-recoverable error 1414 */ 1415 static int 1416 fdstart_Fault(fd_softc_t *fd) 1417 { 1418 #ifdef DIAGNOSTIC 1419 printf("Seek retries %d, IO retries %d, sect retries %d :\n" \ 1420 "\t\t only found c%d_h%d_s%d \n", 1421 fd->seekRetries, fd->ioRetries, fd->sectRetries, 1422 fd->sHdr.track, fd->sHdr.side >> 3, fd->sHdr.sector); 1423 printf("A non-recoverable error: %d ", fd->iwmErr); 1424 #else 1425 /* ARGSUSED */ 1426 #endif 1427 return state_Exit; 1428 } 1429 1430 1431 /* 1432 * fdstart_Exit 1433 * 1434 * We are done, for good or bad 1435 */ 1436 static int 1437 fdstart_Exit(fd_softc_t *fd) 1438 { 1439 struct buf *bp; 1440 #ifdef DIAGNOSTIC 1441 int i; 1442 #endif 1443 1444 invalidateCylinderCache(fd); 1445 1446 #ifdef DIAGNOSTIC 1447 /* XXX Panic if buffer not clean? */ 1448 for (i=0; i<IWM_MAX_GCR_SECTORS; i++) 1449 if (0 != fd->w_slots[i].valid) 1450 printf("Oops! <c%d_h%d_s%d> not flushed.\n", 1451 fd->pos.track, fd->pos.side, fd->pos.sector); 1452 #endif 1453 1454 bp = bufq_get(fd->bufQueue); 1455 1456 bp->b_resid = fd->bytesLeft; 1457 bp->b_error = (0 == fd->iwmErr) ? 0 : EIO; 1458 1459 if (TRACE_STRAT) { 1460 printf(" fdstart() finished job; fd->iwmErr = %d, b_error = %d", 1461 fd->iwmErr, bp->b_error); 1462 if (DISABLED) 1463 hexDump(bp->b_data, bp->b_bcount); 1464 } 1465 if (DISABLED && TRACE_STRAT) 1466 printf(" Next buf (bufQueue first) at %p\n", 1467 bufq_peek(fd->bufQueue)); 1468 disk_unbusy(&fd->diskInfo, bp->b_bcount - bp->b_resid, 1469 (bp->b_flags & B_READ)); 1470 biodone(bp); 1471 /* 1472 * Stop motor after 10s 1473 * 1474 * XXX Unloading the module while the timeout is still 1475 * running WILL crash the machine. 1476 */ 1477 callout_reset(&fd->motor_ch, 10 * hz, motor_off, fd); 1478 1479 return state_Done; 1480 } 1481 1482 1483 /* 1484 * remap_geometry 1485 * 1486 * Remap the rigid UN*X view of a disk's cylinder/sector geometry 1487 * to our zone recorded real Sony drive by splitting the disk 1488 * into zones. 1489 * 1490 * Loop { 1491 * Look if logical block number is in current zone 1492 * NO: Add # of tracks for current zone to track counter 1493 * Process next zone 1494 * 1495 * YES: Subtract (number of first sector of current zone times heads) 1496 * from logical block number, then break up the difference 1497 * in tracks/side/sectors (spt is constant within a zone). 1498 * Done 1499 * } 1500 */ 1501 static void 1502 remap_geometry(daddr_t block, int heads, diskPosition_t *loc) 1503 { 1504 int zone, spt; 1505 extern diskZone_t diskZones[]; 1506 1507 loc->oldTrack = loc->track; 1508 loc->track = 0; 1509 spt = 0; 1510 1511 for (zone = 0; zone < IWM_GCR_DISK_ZONES; zone++) { 1512 if (block >= heads * (diskZones[zone].lastBlock + 1)) { 1513 /* Process full zones */ 1514 loc->track += diskZones[zone].tracks; 1515 } else { 1516 /* Process partial zone */ 1517 spt = diskZones[zone].sectPerTrack; 1518 block -= heads * diskZones[zone].firstBlock; 1519 loc->track += block / (spt * heads); 1520 loc->sector = (block % spt); 1521 loc->side = (block % (spt * heads)) / spt; 1522 break; 1523 } 1524 } 1525 loc->maxSect = spt; 1526 } 1527 1528 1529 /* 1530 * motor_off 1531 * 1532 * Callback for timeout() 1533 */ 1534 static void 1535 motor_off(void *param) 1536 { 1537 int spl; 1538 fd_softc_t *fd; 1539 1540 fd = param; 1541 if (TRACE_STRAT) 1542 printf("iwm: Switching motor OFF (timeout).\n"); 1543 spl = spl6(); 1544 (void)iwmMotor(fd->unit, 0); 1545 fd->state &= ~IWM_FD_MOTOR_ON; 1546 splx(spl); 1547 } 1548 1549 1550 /* 1551 * fdGetDiskLabel 1552 * 1553 * Set up disk label with parameters from current disk type. 1554 * Then call the generic disklabel read routine which tries to 1555 * read a label from disk and insert it. If it doesn't exist use 1556 * our defaults. 1557 */ 1558 static void 1559 fdGetDiskLabel(fd_softc_t *fd, dev_t dev) 1560 { 1561 const char *msg; 1562 int fdType; 1563 struct disklabel *lp; 1564 struct cpu_disklabel *clp; 1565 1566 if (TRACE_IOCTL) 1567 printf("iwm: fdGetDiskLabel() for disk %" PRIu64 ".\n", 1568 (dev_t) (minor(dev) / MAXPARTITIONS)); 1569 fdType = minor(dev) % MAXPARTITIONS; 1570 lp = fd->diskInfo.dk_label; 1571 clp = fd->diskInfo.dk_cpulabel; 1572 memset(lp, 0, sizeof(struct disklabel)); 1573 memset(clp, 0, sizeof(struct cpu_disklabel)); 1574 /* 1575 * How to describe a drive with a variable # of sectors per 1576 * track (8..12) and variable rpm (300..550)? Apple came up 1577 * with ZBR in 1983! Un*x drive management sucks. 1578 */ 1579 lp->d_type = DKTYPE_FLOPPY; 1580 lp->d_rpm = 300; 1581 lp->d_secsize = fd->currentType->sectorSize; 1582 lp->d_ntracks = fd->currentType->heads; 1583 lp->d_ncylinders = fd->currentType->tracks; 1584 lp->d_nsectors = fd->currentType->secPerTrack; 1585 lp->d_secpercyl = fd->currentType->secPerCyl; 1586 lp->d_secperunit = fd->currentType->secPerDisk; 1587 lp->d_interleave = fd->currentType->interleave; 1588 lp->d_trkseek = fd->currentType->stepRate; 1589 1590 strcpy(lp->d_typename, dktypenames[DKTYPE_FLOPPY]); 1591 strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 1592 1593 lp->d_npartitions = fdType + 1; 1594 lp->d_partitions[fdType].p_offset = 0; 1595 lp->d_partitions[fdType].p_size = lp->d_secperunit; 1596 lp->d_partitions[fdType].p_fstype = FS_BSDFFS; 1597 lp->d_partitions[fdType].p_fsize = 512; 1598 lp->d_partitions[fdType].p_frag = 8; 1599 1600 lp->d_magic = DISKMAGIC; 1601 lp->d_magic2 = DISKMAGIC; 1602 lp->d_checksum = dkcksum(lp); 1603 /* 1604 * Call the generic disklabel extraction routine. If we don't 1605 * find a label on disk, keep our faked one. 1606 */ 1607 if (TRACE_OPEN) 1608 printf(" now calling readdisklabel()...\n"); 1609 1610 msg = readdisklabel(dev, fdstrategy, lp, clp); 1611 if (msg == NULL) { 1612 strncpy(lp->d_packname, "default label", 1613 sizeof(lp->d_packname)); /* XXX - ?? */ 1614 } 1615 #ifdef IWM_DEBUG 1616 else 1617 printf("iwm: %s.\n", msg); 1618 #endif 1619 if (TRACE_OPEN) 1620 fdPrintDiskLabel(lp); 1621 } 1622 1623 1624 1625 /* 1626 * initCylinderCache 1627 * 1628 * Allocate cylinder cache and set up pointers to sectors. 1629 */ 1630 static int 1631 initCylinderCache(fd_softc_t *fd) 1632 { 1633 int i; 1634 int err; 1635 int secsize; 1636 1637 err = 0; 1638 secsize = fd->currentType->sectorSize; 1639 fd->cachedSide = 0; 1640 1641 fd->cbuf = kmem_alloc(IWM_MAX_GCR_SECTORS * secsize, KM_SLEEP); 1642 if (NULL == fd->cbuf) 1643 err = ENOMEM; 1644 else 1645 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) { 1646 fd->w_slots[i].valid = 0; 1647 fd->w_slots[i].secbuf = NULL; 1648 1649 fd->r_slots[i].valid = 0; 1650 fd->r_slots[i].secbuf = fd->cbuf + i * secsize; 1651 } 1652 return err; 1653 } 1654 1655 1656 /* 1657 * invalidateCylinderCache 1658 * 1659 * Switching cylinders (tracks?) invalidates the read cache. 1660 */ 1661 static void 1662 invalidateCylinderCache(fd_softc_t *fd) 1663 { 1664 int i; 1665 1666 fd->cachedSide = 0; 1667 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) { 1668 fd->r_slots[i].valid = 0; 1669 } 1670 } 1671 1672 1673 /* 1674 * getFDType 1675 * 1676 * return pointer to disk format description 1677 */ 1678 static fdInfo_t * 1679 getFDType(short unit) 1680 { 1681 int driveFlags; 1682 fdInfo_t *thisType; 1683 extern fdInfo_t fdTypes[]; 1684 1685 driveFlags = iwmCheckDrive(unit); 1686 /* 1687 * Drive flags are: Bit 0 - 1 = Drive is double sided 1688 * 1 - 1 = No disk inserted 1689 * 2 - 1 = Motor is off 1690 * 3 - 1 = Disk is writable 1691 * 4 - 1 = Disk is DD (800/720K) 1692 * 31 - 1 = No drive / invalid drive # 1693 */ 1694 if (TRACE_CONFIG) { 1695 printf("iwm: Drive %d says 0x0%x (%d)\n", 1696 unit, driveFlags, driveFlags); 1697 } 1698 if (driveFlags < 0) 1699 thisType = NULL;/* no such drive */ 1700 else 1701 if (driveFlags & 0x01) 1702 thisType = &fdTypes[1]; /* double sided */ 1703 else 1704 thisType = &fdTypes[0]; /* single sided */ 1705 1706 return thisType; 1707 } 1708 1709 1710 /* 1711 * fdDeviceToType 1712 * 1713 * maps the minor device number (elsewhere: partition type) to 1714 * a corresponding disk format. 1715 * This is currently: 1716 * fdXa default (800K GCR) 1717 * fdXb 400K GCR 1718 * fdXc 800K GCR 1719 */ 1720 static fdInfo_t * 1721 fdDeviceToType(fd_softc_t *fd, dev_t dev) 1722 { 1723 int type; 1724 fdInfo_t *thisInfo; 1725 /* XXX This broke with egcs 1.0.2 */ 1726 /* extern fdInfo_t fdTypes[]; */ 1727 1728 type = minor(dev) % MAXPARTITIONS; /* 1,2,... */ 1729 if (type > sizeof(fdTypes) / sizeof(fdTypes[0])) 1730 thisInfo = NULL; 1731 else 1732 thisInfo = (type == 0) ? fd->defaultType : &fdTypes[type - 1]; 1733 return thisInfo; 1734 } 1735 1736 1737 /* 1738 * seek 1739 * 1740 * Step to given track; optionally restore to track zero before 1741 * and/or verify correct track. 1742 * Note that any necessary retries are done here. 1743 * We keep the current position on disk in a 'struct diskPosition'. 1744 */ 1745 static int 1746 seek(fd_softc_t *fd, int style) 1747 { 1748 int state, done; 1749 int err, ierr; 1750 int steps; 1751 1752 diskPosition_t *loc; 1753 sectorHdr_t hdr; 1754 char action[32]; 1755 iwm_softc_t *iwm = device_lookup_private(&iwm_cd, 0); /* XXX */ 1756 1757 const char *stateDesc[] = { 1758 "Init", 1759 "Seek", 1760 "Recalibrate", 1761 "Verify", 1762 "Exit" 1763 }; 1764 enum { 1765 seek_state_Init = 0, 1766 seek_state_Seek, 1767 seek_state_Recalibrate, 1768 seek_state_Verify, 1769 seek_state_Exit 1770 }; 1771 /* XXX egcs */ 1772 done = err = ierr = 0; 1773 fd->seekRetries = 0; 1774 fd->verifyRetries = 0; 1775 1776 loc = &fd->pos; 1777 1778 state = seek_state_Init; 1779 do { 1780 if (TRACE_STEP) 1781 printf(" seek state %d [%s].\n", 1782 state, stateDesc[state]); 1783 switch (state) { 1784 1785 case seek_state_Init: 1786 if (TRACE_STEP) 1787 printf("Current track is %d, new track %d.\n", 1788 loc->oldTrack, loc->track); 1789 memset(&hdr, 0, sizeof(hdr)); 1790 err = ierr = 0; 1791 fd->seekRetries = 0; 1792 fd->verifyRetries = 0; 1793 state = (style == IWM_SEEK_RECAL) 1794 ? seek_state_Recalibrate : seek_state_Seek; 1795 done = 0; 1796 break; 1797 1798 case seek_state_Recalibrate: 1799 ierr = iwmTrack00(); 1800 if (ierr == 0) { 1801 loc->oldTrack = 0; 1802 state = seek_state_Seek; 1803 } else { 1804 strncpy(action, "Recalibrate (track 0)", 1805 sizeof(action)); 1806 state = seek_state_Exit; 1807 } 1808 break; 1809 1810 case seek_state_Seek: 1811 ierr = 0; 1812 steps = loc->track - loc->oldTrack; 1813 1814 if (steps != 0) 1815 ierr = iwmSeek(steps); 1816 if (ierr == 0) { 1817 /* No error or nothing to do */ 1818 state = (style == IWM_SEEK_VERIFY) 1819 ? seek_state_Verify : seek_state_Exit; 1820 } else { 1821 if (fd->seekRetries++ < iwm->maxRetries) 1822 state = seek_state_Recalibrate; 1823 else { 1824 strncpy(action, "Seek retries", 1825 sizeof(action)); 1826 state = seek_state_Exit; 1827 } 1828 } 1829 break; 1830 1831 case seek_state_Verify: 1832 ierr = checkTrack(loc, TRACE_STEP); 1833 if (ierr == 0 && loc->track == hdr.track) 1834 state = seek_state_Exit; 1835 else { 1836 if (fd->verifyRetries++ < iwm->maxRetries) 1837 state = seek_state_Recalibrate; 1838 else { 1839 strncpy(action, "Verify retries", 1840 sizeof(action)); 1841 state = seek_state_Exit; 1842 } 1843 } 1844 break; 1845 1846 case seek_state_Exit: 1847 if (ierr == 0) { 1848 loc->oldTrack = loc->track; 1849 err = 0; 1850 /* Give the head some time to settle down */ 1851 delay(3000); 1852 } else { 1853 #ifdef DIAGNOSTIC 1854 printf(" seek() action \"%s\", err = %d.\n", 1855 action, ierr); 1856 #endif 1857 err = EIO; 1858 } 1859 done = 1; 1860 break; 1861 } 1862 } while (!done); 1863 return err; 1864 } 1865 1866 1867 /* 1868 * checkTrack 1869 * 1870 * After positioning, get a sector header for validation 1871 */ 1872 static int 1873 checkTrack(diskPosition_t *loc, int debugFlag) 1874 { 1875 int spl; 1876 int iwmErr; 1877 sectorHdr_t hdr; 1878 1879 spl = spl6(); 1880 iwmSelectSide(loc->side); 1881 iwmErr = iwmReadSectHdr(&hdr); 1882 splx(spl); 1883 if (debugFlag) { 1884 printf("Seeked for %d, got at %d, Hdr read err %d.\n", 1885 loc->track, hdr.track, iwmErr); 1886 } 1887 return iwmErr; 1888 } 1889 1890 1891 /* Debugging stuff */ 1892 1893 static void 1894 hexDump(u_char *buf, int len) 1895 { 1896 int i, j; 1897 u_char ch; 1898 1899 printf("\nDump %d from %p:\n", len, buf); 1900 i = j = 0; 1901 if (NULL != buf) do { 1902 printf("%04x: ", i); 1903 for (j = 0; j < 8; j++) 1904 printf("%02x ", buf[i + j]); 1905 printf(" "); 1906 for (j = 8; j < 16; j++) 1907 printf("%02x ", buf[i + j]); 1908 printf(" "); 1909 for (j = 0; j < 16; j++) { 1910 ch = buf[i + j]; 1911 if (ch > 31 && ch < 127) 1912 printf("%c", ch); 1913 else 1914 printf("."); 1915 } 1916 printf("\n"); 1917 i += 16; 1918 } while (len > i); 1919 } 1920 1921 1922 static void 1923 fdPrintDiskLabel(struct disklabel *lp) 1924 { 1925 int i; 1926 1927 printf("iwm: Disklabel entries of current floppy.\n"); 1928 printf("\t d_type:\t%d (%s)\n", lp->d_type, 1929 dktypenames[lp->d_type]); 1930 printf("\t d_typename:\t%s\n", lp->d_typename); 1931 printf("\t d_packname:\t%s\n", lp->d_packname); 1932 1933 printf("\t d_secsize:\t%d\n", lp->d_secsize); 1934 printf("\t d_nsectors:\t%d\n", lp->d_nsectors); 1935 printf("\t d_ntracks:\t%d\n", lp->d_ntracks); 1936 printf("\t d_ncylinders:\t%d\n", lp->d_ncylinders); 1937 printf("\t d_secpercyl:\t%d\n", lp->d_secpercyl); 1938 printf("\t d_secperunit:\t%d\n", lp->d_secperunit); 1939 1940 printf("\t d_rpm: \t%d\n", lp->d_rpm); 1941 printf("\t d_interleave:\t%d\n", lp->d_interleave); 1942 printf("\t d_trkseek:\t%d [ms]\n", lp->d_trkseek); 1943 1944 printf(" d_npartitions:\t%d\n", lp->d_npartitions); 1945 for (i = 0; i < lp->d_npartitions; i++) { 1946 printf("\t d_partitions[%d].p_offset:\t%d\n", i, 1947 lp->d_partitions[i].p_offset); 1948 printf("\t d_partitions[%d].p_size:\t%d\n", i, 1949 lp->d_partitions[i].p_size); 1950 printf("\t d_partitions[%d].p_fstype:\t%d (%s)\n", i, 1951 lp->d_partitions[i].p_fstype, 1952 fstypenames[lp->d_partitions[i].p_fstype]); 1953 printf("\t d_partitions[%d].p_frag:\t%d\n", i, 1954 lp->d_partitions[i].p_frag); 1955 printf("\n"); 1956 } 1957 } 1958