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