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