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