|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.0 (the 'License'). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License." ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: /* ! 25: * Copyright 1997-1998 by Apple Computer, Inc., All rights reserved. ! 26: * Copyright 1994-1997 NeXT Software, Inc., All rights reserved. ! 27: * ! 28: * AtapiCntCmds.m - ATAPI command implementation for ATA interface. ! 29: * ! 30: * HISTORY ! 31: * ! 32: * 4-Jan-1998 Joe Liu at Apple ! 33: * Merged the various wait routines into a single routine. ! 34: * Wait routine now sleeps longer to generate a more accurate wait ! 35: * interval. ! 36: * Check the BSY bit before sending the packet command. ! 37: * Detect shadow/phantom drives and timeout more quickly. ! 38: * Sprinkled some IODelays before checking the BSY bit. Since according ! 39: * to the spec, a drive may take up to 400ns to set the BSY bit, and ! 40: * we don't want to read it before it has enough time to assert itself. ! 41: * ! 42: * 3-Sep-1996 Becky Divinski at NeXT ! 43: * Moved LBA, IORDY, and buffer capabilities out ! 44: * from under DEBUG statements, so they will ! 45: * always print out during startup. ! 46: * ! 47: * 13-Feb-1996 Rakesh Dubey at NeXT ! 48: * More resistant to broken firmwares. ! 49: * ! 50: * 13-Jul-1995 Rakesh Dubey at NeXT ! 51: * Improved device detection and log messages. ! 52: * ! 53: * 1-Sep-1994 Rakesh Dubey at NeXT ! 54: * Created. ! 55: */ ! 56: ! 57: #import "AtapiCntCmds.h" ! 58: #import "IdePIIX.h" ! 59: ! 60: //#define DEBUG ! 61: ! 62: /* ! 63: * Max timeout while waiting for BSY == 0. ! 64: */ ! 65: #define ATAPI_MAX_WAIT_FOR_NOTBUSY (5 * 1000) // 5 secs ! 66: ! 67: @implementation IdeController(ATAPI) ! 68: ! 69: /* ! 70: * Method: atapiWaitStatusBitsFor:on:off:alt: ! 71: * ! 72: * General purpose wait routine. Wait for BSY bit in the Status/AltStatus ! 73: * register to clear, then make sure that the "on" bits are set, and the ! 74: * "off" bits are cleared in the status register. ! 75: * ! 76: * The routine busy waits for an initial count, then it uses IOSleeps to ! 77: * do non-blocking delays. IOSleep() has a minimum delay of around 15ms. ! 78: * Anything smaller will yield a longer than expected delay. ! 79: * ! 80: * Arguments: ! 81: * timeout - How long to wait for BSY bit to clear in milliseconds. ! 82: * on - Set bit mask. ! 83: * off - Cleared bit mask (BSY bit is implicitly checked). ! 84: * alt - YES to poll using AltStatus instead of Status register. ! 85: * ! 86: * Returns: ! 87: * IDER_SUCCESS - successful completion. ! 88: * IDER_TIMEOUT - timeout or bits check failed. ! 89: * ! 90: */ ! 91: ! 92: #define ATAPI_WAIT_DELAYS 5 // how many initial 1ms busy waits ! 93: #define ATAPI_SLEEP_DURATION 20 // how long to sleep using IOSleep ! 94: ! 95: - (atapi_return_t) atapiWaitStatusBitsFor:(unsigned int)timeout ! 96: on:(unsigned char)on ! 97: off:(unsigned char)off ! 98: alt:(BOOL)alt ! 99: { ! 100: unsigned char status; ! 101: int delays = ATAPI_WAIT_DELAYS; ! 102: ! 103: if (timeout == 0) ! 104: return IDER_TIMEOUT; ! 105: ! 106: IODelay(1); /* give the drive 400ns to assert BSY bit */ ! 107: do { ! 108: status = alt ? inb(_ideRegsAddrs.altStatus) : ! 109: inb(_ideRegsAddrs.status); ! 110: if (!(status & BUSY)) ! 111: break; ! 112: if (delays-- > 0) { // use IODelay for initial wait ! 113: IODelay(1000); // busy-wait for 1ms ! 114: timeout--; ! 115: } ! 116: else { // too long, use IOSleep instead ! 117: int sleep_interval = ATAPI_SLEEP_DURATION; ! 118: if (timeout < sleep_interval) ! 119: sleep_interval = timeout; ! 120: IOSleep(sleep_interval); ! 121: timeout -= sleep_interval; ! 122: } ! 123: } while (timeout > 0); ! 124: if (timeout == 0) ! 125: return IDER_TIMEOUT; ! 126: ! 127: IODelay(1); /* give the drive another 400ns to update Status Register */ ! 128: if (((status & on) == on) && ((~status & off) == off)) ! 129: return IDER_SUCCESS; ! 130: ! 131: return IDER_TIMEOUT; ! 132: } ! 133: ! 134: - (void) printAtapiInfo:(ideIdentifyInfo_t *)ideIdentifyInfo ! 135: Device:(unsigned char)unit ! 136: { ! 137: int i; ! 138: char name[50]; ! 139: char firmware[9]; ! 140: atapiGenConfig_t *atapiGenConfig; ! 141: char *protocolTypeStr, *deviceTypeStr, *cmdDrqTypeStr; ! 142: char *removableStr, *cmdPacketSizeStr; ! 143: ! 144: // IOLog("Capability: %04x\n", ideIdentifyInfo->capabilities); ! 145: ! 146: /* ! 147: * Print drive name with firmware revision number after doing byte swaps. ! 148: */ ! 149: for (i = 0; i < 20; i++) { ! 150: name[2*i] = ideIdentifyInfo->modelNumber[2*i+1]; ! 151: name[2*i+1] = ideIdentifyInfo->modelNumber[2*i]; ! 152: } ! 153: name[40] = '\0'; ! 154: ! 155: for (i = 0; i < 4; i++) { ! 156: firmware[2*i] = ideIdentifyInfo->firmwareRevision[2*i+1]; ! 157: firmware[2*i+1] = ideIdentifyInfo->firmwareRevision[2*i]; ! 158: } ! 159: firmware[8] = '\0'; ! 160: ! 161: for (i = 38; i >= 0; i--) { ! 162: if (name[i] != ' ') ! 163: break; ! 164: } ! 165: strcpy(name+i+2, firmware); ! 166: ! 167: #ifdef DEBUG ! 168: IOLog("%s: %s\n", [self name], name); ! 169: #endif DEBUG ! 170: ! 171: /* ! 172: * This information should be printed since it is not duplicated ! 173: * elsewhere. ! 174: */ ! 175: atapiGenConfig = (atapiGenConfig_t *) &ideIdentifyInfo->genConfig; ! 176: ! 177: #ifdef undef ! 178: IOLog("%s: gen config: %x\n", [self name], ideIdentifyInfo->genConfig); ! 179: #endif undef ! 180: ! 181: if (atapiGenConfig->protocolType == 0 || atapiGenConfig->protocolType == 1) ! 182: protocolTypeStr = "ATA"; ! 183: else if (atapiGenConfig->protocolType == 2) ! 184: protocolTypeStr = "ATAPI"; ! 185: else ! 186: protocolTypeStr = "UNKNOWN PROTOCOL"; ! 187: ! 188: if (atapiGenConfig->deviceType == 0) ! 189: deviceTypeStr = "DIRECT ACCESS"; ! 190: else if (atapiGenConfig->deviceType == 5) ! 191: deviceTypeStr = "CD-ROM"; ! 192: else if (atapiGenConfig->deviceType == 7) ! 193: deviceTypeStr = "OPTICAL"; ! 194: else if (atapiGenConfig->deviceType == 1) ! 195: deviceTypeStr = "TAPE"; ! 196: else ! 197: deviceTypeStr = "UNKNOWN DEVICE TYPE"; ! 198: ! 199: if (atapiGenConfig->cmdDrqType == 0) ! 200: cmdDrqTypeStr = "SLOW DRQ"; ! 201: else if (atapiGenConfig->cmdDrqType == 1) ! 202: cmdDrqTypeStr = "INTR DRQ"; ! 203: else if (atapiGenConfig->cmdDrqType == 2) ! 204: cmdDrqTypeStr = "FAST DRQ"; ! 205: else ! 206: cmdDrqTypeStr = "UNKNOWN DRQ TYPE"; ! 207: ! 208: if (atapiGenConfig->removable == 0) ! 209: removableStr = "NOT REMOVABLE"; ! 210: else if (atapiGenConfig->removable == 1) ! 211: removableStr = "REMOVABLE"; ! 212: ! 213: if (atapiGenConfig->cmdPacketSize == 0) ! 214: cmdPacketSizeStr = "CMD PKT LEN=12"; ! 215: else if (atapiGenConfig->cmdPacketSize == 1) ! 216: cmdPacketSizeStr = "CMD PKT LEN=16"; ! 217: else ! 218: cmdPacketSizeStr = "CMD PKT LEN=UNKNOWN"; ! 219: ! 220: ! 221: IOLog("%s: Drive %d: %s %s (%s, %s, %s)\n", [self name], unit, ! 222: protocolTypeStr, ! 223: deviceTypeStr, cmdDrqTypeStr, ! 224: removableStr, cmdPacketSizeStr); ! 225: ! 226: #ifdef DEBUG ! 227: if (ideIdentifyInfo->capabilities & IDE_CAP_LBA_SUPPORTED) { ! 228: IOLog("%s: LBA supported.\n", [self name]); ! 229: } ! 230: if (ideIdentifyInfo->capabilities & IDE_CAP_IORDY_SUPPORTED) { ! 231: IOLog("%s: IORDY supported.\n", [self name]); ! 232: } ! 233: ! 234: if (ideIdentifyInfo->bufferType != 0) { ! 235: IOLog("%s: buffer type %d, %d sectors.\n", [self name], ! 236: ideIdentifyInfo->bufferType, ideIdentifyInfo->bufferSize); ! 237: } ! 238: ! 239: IOLog("%s: PIO timing cycle: %d ns.\n", [self name], ! 240: ideIdentifyInfo->pioDataTransferCyleTimingMode & ! 241: IDE_PIO_TIMING_MODE_MASK); ! 242: ! 243: if (ideIdentifyInfo->capabilities & IDE_CAP_DMA_SUPPORTED) { ! 244: IOLog("%s: DMA timing cycle: %d ns.\n", [self name], ! 245: ideIdentifyInfo->dmaDataTransferCyleTimingMode & ! 246: IDE_DMA_TIMING_MODE_MASK); ! 247: } ! 248: ! 249: #endif DEBUG ! 250: } ! 251: ! 252: /* ! 253: * There are lots of ATAPI CD-ROMs that shadow the task file register and ! 254: * hence show up as two devices. They also might issue a valid interrupt when ! 255: * ATAPI Identify Device command is sent so we need to really make sure ! 256: * whether the device has data for us. ! 257: */ ! 258: - (atapi_return_t) _atapiIdentifyDevice:(struct vm_map *)client ! 259: addr:(caddr_t)xferAddr ! 260: { ! 261: unsigned int cmd = ATAPI_IDENTIFY_DRIVE; ! 262: unsigned char dh = ADDRESS_MODE_LBA; /* ALWAYS */ ! 263: unsigned char status; ! 264: atapi_return_t rtn; ! 265: ! 266: unsigned int oldTimeout; ! 267: ! 268: #ifdef DEBUG ! 269: IOLog("ATAPI Identify Device\n"); ! 270: #endif DEBUG ! 271: ! 272: rtn = [self atapiWaitStatusBitsFor:ATAPI_MAX_WAIT_FOR_NOTBUSY ! 273: on:0 off:0 alt:NO]; ! 274: if (rtn != IDER_SUCCESS) ! 275: return (rtn); ! 276: ! 277: dh |= _driveNum ? SEL_DRIVE1 : SEL_DRIVE0; ! 278: outb(_ideRegsAddrs.driveSelect, dh); ! 279: ! 280: bzero(xferAddr, IDE_SECTOR_SIZE); ! 281: ! 282: [self enableInterrupts]; ! 283: [self clearInterrupts]; ! 284: ! 285: outb(_ideRegsAddrs.command, ATAPI_IDENTIFY_DRIVE); ! 286: ! 287: /* Wait for BSY bit to clear before reading from AltStatus. ! 288: * We do this to avoid a long interrupt timeout when probing ! 289: * "phantom" CD-ROM drives shadowed by another device on the same ! 290: * IDE connector. ! 291: /* Read from AltStatus instead of Status register to avoid ! 292: * ack'ing any pending interrupts. ! 293: */ ! 294: rtn = [self atapiWaitStatusBitsFor:ATAPI_MAX_WAIT_FOR_NOTBUSY ! 295: on:0 off:0 alt:YES]; ! 296: if (rtn != IDER_SUCCESS) { ! 297: IOLog("%s: Polling BSY bit timed-out\n", [self name]); ! 298: return (rtn); ! 299: } ! 300: ! 301: /* Shadow (false) devices should set the ABORT bit in the ATA ! 302: * Error register. We check for ABORT bit and return an error ! 303: * if that bit is set. ! 304: */ ! 305: if ((inb(_ideRegsAddrs.altStatus) & ERROR) && ! 306: (inb(_ideRegsAddrs.error) & CMD_ABORTED)) { ! 307: // Don't bother waiting for an interrupt, abort immediately. ! 308: #ifdef DEBUG ! 309: IOLog("%s: Phantom ATAPI drive detected. Status:0x%02x\n", ! 310: [self name], inb(_ideRegsAddrs.altStatus)); ! 311: #endif DEBUG ! 312: return IDER_ERROR; ! 313: } ! 314: ! 315: oldTimeout = [self interruptTimeOut]; ! 316: [self setInterruptTimeOut:4000]; // four seconds ! 317: rtn = [self ideWaitForInterrupt:cmd ideStatus:&status]; ! 318: [self setInterruptTimeOut:oldTimeout]; ! 319: ! 320: if (rtn != IDER_SUCCESS) { ! 321: [self getIdeRegisters:NULL Print:"Atapi Identify"]; ! 322: return (rtn); ! 323: } ! 324: ! 325: // Make sure DRQ is set ! 326: rtn = [self atapiWaitStatusBitsFor:ATAPI_MAX_WAIT_FOR_NOTBUSY ! 327: on:DREQUEST off:0 alt:NO]; ! 328: if (rtn != IDER_SUCCESS) ! 329: return (rtn); ! 330: ! 331: /* ! 332: * FIXME: We need to do some sanity check on this data to be really sure ! 333: * that there is an ATAPI device out here. ! 334: */ ! 335: [self xferData:xferAddr read:YES client:client length:IDE_SECTOR_SIZE]; ! 336: return IDER_SUCCESS; ! 337: } ! 338: ! 339: /* ! 340: * Some CD-ROMS (like SONY CDU-55D) fail the first request so we reset the ! 341: * device and retry. ! 342: */ ! 343: #define ATAPI_IDENTIFY_DEVICE_RETRIES 3 ! 344: ! 345: - (atapi_return_t) atapiIdentifyDevice:(struct vm_map *)client ! 346: addr:(caddr_t)xferAddr ! 347: unit:(unsigned char)unit ! 348: { ! 349: int i; ! 350: atapi_return_t ret; ! 351: ! 352: for (i = 0; i < ATAPI_IDENTIFY_DEVICE_RETRIES; i++) { ! 353: ! 354: ret = [self _atapiIdentifyDevice:client addr:xferAddr]; ! 355: if (ret == IDER_SUCCESS) ! 356: break; ! 357: ! 358: /* Reset hardware and try again */ ! 359: [self atapiSoftReset:unit]; ! 360: IOSleep(500); ! 361: if (_ide_debug) { ! 362: IOLog("%s: Drive %d: ATAPI Identify Device failed " ! 363: "(error %d). Retrying..\n", [self name], unit, ret); ! 364: } ! 365: } ! 366: #ifdef DEBUG ! 367: if (i == ATAPI_IDENTIFY_DEVICE_RETRIES) { ! 368: IOLog("%s: FATAL: Drive %d: ATAPI Identify Device.\n", ! 369: [self name], unit); ! 370: } ! 371: #endif DEBUG ! 372: return ret; ! 373: } ! 374: ! 375: - (void) atapiInitParameters:(ideIdentifyInfo_t *)infoPtr ! 376: Device:(unsigned char)unit ! 377: { ! 378: atapiGenConfig_t *atapiGenConfig = ! 379: (atapiGenConfig_t *) &infoPtr->genConfig; ! 380: ! 381: if (atapiGenConfig->cmdPacketSize == 0x01) ! 382: _drives[unit].atapiCmdLen = 16; ! 383: else ! 384: _drives[unit].atapiCmdLen = 12; ! 385: ! 386: /* ! 387: * The command len is 12. However some devices think it is 16 which is ! 388: * actually reserved for SAM compatibility. See page 58, SFF 8020, rev ! 389: * 1.2. This is a workaround. ! 390: */ ! 391: if (_drives[unit].atapiCmdLen != 12) { ! 392: IOLog("%s: ATAPI: command len changed to 12 from %d.\n", ! 393: [self name], _drives[unit].atapiCmdLen); ! 394: _drives[unit].atapiCmdLen = 12; ! 395: } ! 396: ! 397: _drives[unit].atapiCmdDrqType = atapiGenConfig->cmdDrqType; ! 398: } ! 399: ! 400: /* ! 401: * Identify ATAPI device must have been executed first before callling this ! 402: * method. ! 403: */ ! 404: - (unsigned char) atapiCommandPacketSize:(unsigned char)unit ! 405: { ! 406: if (unit < MAX_IDE_DRIVES) ! 407: return _drives[unit].atapiCmdLen; ! 408: else ! 409: return 0; ! 410: } ! 411: ! 412: ! 413: /* ! 414: * The amount of time in milliseconds it takes for an ATAPI device to post ! 415: * its signature after a reset. Some CD-ROMs take a long time to respond. Do ! 416: * not decrease it without testing with various makes of ATAPI devices. ! 417: */ ! 418: #define ATAPI_RESET_DELAY 2500 ! 419: ! 420: - (atapi_return_t) atapiSoftReset:(unsigned char)unit ! 421: { ! 422: unsigned char dh = ADDRESS_MODE_LBA; /* ALWAYS */ ! 423: ! 424: #ifdef DEBUG ! 425: IOLog("%s: atapiSoftReset:device %d\n", [self name], unit); ! 426: #endif DEBUG ! 427: ! 428: dh |= unit ? SEL_DRIVE1 : SEL_DRIVE0; ! 429: ! 430: outb(_ideRegsAddrs.driveSelect, dh); ! 431: outb(_ideRegsAddrs.command, ATAPI_SOFT_RESET); ! 432: ! 433: IOSleep(50); /* Enough time to assert busy */ ! 434: ! 435: if ([self atapiWaitStatusBitsFor:ATAPI_RESET_DELAY on:0 off:0 alt:NO] == ! 436: IDER_SUCCESS) ! 437: return IDER_SUCCESS; ! 438: ! 439: #ifdef DEBUG ! 440: IOLog("%s: atapiSoftReset: FAILED. Status = %x\n", [self name], status); ! 441: #endif DEBUG ! 442: ! 443: return IDER_ERROR; ! 444: } ! 445: ! 446: /* ! 447: * Method: issuePacketCommand ! 448: * ! 449: * This method will issue the Packet Command code (0xA0) to the command ! 450: * register. Then wait for an interrupt (INT DRQ devices only) and poll ! 451: * the Ireason/Status for: ! 452: * ! 453: * BSY = 0 ! 454: * CoD = 1 ! 455: * IO = 0 ! 456: * ! 457: * We assume that the task file has been initialized prior to calling ! 458: * this method. ! 459: * ! 460: * Returns: ! 461: * IDER_SUCCESS - DRQ is set and ready to send the 12-byte command. ! 462: * IDER_ERROR - Abort and send a ATAPI soft reset. ! 463: */ ! 464: #define MAX_DRQ_WAIT (100 * 1000 * 5) // 5 second wait ! 465: ! 466: - (atapi_return_t) issuePacketCommand ! 467: { ! 468: int i; ! 469: unsigned char interruptReason; ! 470: unsigned char status; ! 471: ! 472: #ifdef DEBUG ! 473: IOLog("%s: issuePacketCommand\n", [self name]); ! 474: #endif DEBUG ! 475: ! 476: /* ! 477: * Send the packet command. ! 478: */ ! 479: outb(_ideRegsAddrs.command, ATAPI_PACKET); ! 480: ! 481: /* ! 482: * Some devices might have interrupted so we should take care of that ! 483: * first. ! 484: */ ! 485: if (_drives[_driveNum].atapiCmdDrqType == ATAPI_CMD_DRQ_INT) { ! 486: atapi_return_t rtn; ! 487: rtn = [self ideWaitForInterrupt:ATAPI_PACKET ideStatus:&status]; ! 488: if (rtn != IDER_SUCCESS) ! 489: return rtn; ! 490: } ! 491: ! 492: /* ! 493: * Wait till we get okay from the device. ! 494: */ ! 495: IODelay(1); ! 496: for (i = 0; i < MAX_DRQ_WAIT; i++) { ! 497: interruptReason = inb(_ideRegsAddrs.interruptReason); ! 498: status = inb(_ideRegsAddrs.status); ! 499: ! 500: if (!(status & BUSY) && ! 501: (interruptReason & CMD_OR_DATA) && ! 502: !(interruptReason & IO_DIRECTION)) ! 503: break; ! 504: IODelay(10); ! 505: } ! 506: ! 507: if (i == MAX_DRQ_WAIT) { ! 508: IOLog("%s: ATAPI Drive %d: Invalid Interrupt Reason: %x.\n", ! 509: [self name], _driveNum, interruptReason); ! 510: return IDER_ERROR; ! 511: } ! 512: ! 513: /* ! 514: * Now DRQ should be set. ! 515: */ ! 516: status = inb(_ideRegsAddrs.status); ! 517: if (status & DREQUEST) { ! 518: return IDER_SUCCESS; ! 519: } ! 520: ! 521: IOLog("%s: ATAPI Drive %d: DRQ not set: %x\n", ! 522: [self name], _driveNum, status); ! 523: return IDER_ERROR; ! 524: } ! 525: ! 526: /* ! 527: * Method: sendAtapiCommand:cmdLen: ! 528: * ! 529: * Send the ATAPI command packet bytes. ! 530: * Note that the command is sent out as words in little endian format. ! 531: */ ! 532: - (void) sendAtapiCommand:(unsigned char *)atapiCmd ! 533: cmdLen:(unsigned char)len ! 534: { ! 535: int i; ! 536: ! 537: for (i = 0; i < len/2; i++) { ! 538: outw(_ideRegsAddrs.data, atapiCmd[2*i+1] << 8 | atapiCmd[2*i]); ! 539: } ! 540: } ! 541: ! 542: #define MAX_SENDPACKET_RETRIES 3 ! 543: #define MAX_BUSY_WAIT (1000*100) ! 544: ! 545: /* ! 546: * Transfer data between host and the ATAPI device through PIO. ! 547: * ! 548: * Before calling this method, the packet command and command data bytes ! 549: * have been issued to the drive. This method will transfer data until ! 550: * DRQ = 0, or if the maximum transfer count is reached, whichever ! 551: * occurs first. This method will require an interrupt for every iteration ! 552: * of the loop. ! 553: */ ! 554: - (sc_status_t) atapiPIODataTransfer:(atapiIoReq_t *)atapiIoReq ! 555: buffer:(void *)buffer ! 556: client:(struct vm_map *)client ! 557: { ! 558: int i; ! 559: atapi_return_t rtn; ! 560: unsigned char status; ! 561: unsigned int offset; ! 562: unsigned char cmd = atapiIoReq->atapiCmd[0]; ! 563: unsigned int bytes; ! 564: ! 565: /* ! 566: * Zero the counters. ! 567: */ ! 568: atapiIoReq->bytesTransferred = 0; ! 569: offset = 0; ! 570: ! 571: for (;;) { ! 572: ! 573: /* At this point, the drive is executing the packet command. ! 574: * For commands such as FORMAT_UNIT (0x04), the may take a very ! 575: * long time. Probably much longer than IDE_INTR_TIMEOUT. ! 576: * ! 577: * To guard against those cases, we take the maximum of ! 578: * IDE_INTR_TIMEOUT vs. scsiReq.timeout. ! 579: */ ! 580: if (atapiIoReq->timeout > IDE_INTR_TIMEOUT) { ! 581: u_int current_timeout = [self interruptTimeOut]; ! 582: ! 583: //IOLog("using SCSI timeout:%d\n", atapiIoReq->timeout); ! 584: [self setInterruptTimeOut:atapiIoReq->timeout]; ! 585: rtn = [self ideWaitForInterrupt:cmd ideStatus:&status]; ! 586: [self setInterruptTimeOut:current_timeout]; ! 587: } ! 588: else { ! 589: rtn = [self ideWaitForInterrupt:cmd ideStatus:&status]; ! 590: } ! 591: ! 592: if (rtn != IDER_SUCCESS) { ! 593: IOLog("%s: FATAL: ATAPI Drive: %d Command %x failed.\n", ! 594: [self name], _driveNum, atapiIoReq->atapiCmd[0]); ! 595: [self getIdeRegisters:NULL Print:"ATAPI Command"]; ! 596: [self atapiSoftReset:_driveNum]; ! 597: atapiIoReq->scsiStatus = STAT_CHECK; ! 598: return SR_IOST_CHKSNV; ! 599: } ! 600: ! 601: /* ! 602: * This is stupid but the Chinon drive fires off an interrupt first ! 603: * and then updates the status register. It appears that any drive ! 604: * based on Western Digital chipset will do this. At any rate, this ! 605: * code is harmless and should be left here. ! 606: */ ! 607: for (i = 0; i < MAX_BUSY_WAIT; i++) { ! 608: if (status & BUSY) ! 609: IODelay(10); ! 610: else ! 611: break; ! 612: status = inb(_ideRegsAddrs.status); ! 613: } ! 614: ! 615: /* ! 616: * If DRQ == 0 then host has terminated the command. ! 617: */ ! 618: if (!(status & DREQUEST)) { ! 619: /* ! 620: * Check for command completion status. ! 621: */ ! 622: if (status & ERROR) { ! 623: #ifdef DEBUG ! 624: // Don't complain for TEST UNIT READY command ! 625: if (cmd != 0x00) { ! 626: [self dumpStatus: atapiIoReq]; ! 627: } ! 628: IOLog("%s: ATAPI command %x failed. " ! 629: "Error: %x Status: %x\n", [self name], ! 630: atapiIoReq->atapiCmd[0], ! 631: inb(_ideRegsAddrs.error), status); ! 632: #endif DEBUG ! 633: ! 634: atapiIoReq->scsiStatus = STAT_CHECK; ! 635: return SR_IOST_CHKSNV; ! 636: } ! 637: else { ! 638: #ifdef DEBUG ! 639: IOLog("%s: Comamnd %x completed. status %x\n", ! 640: [self name], atapiIoReq->atapiCmd[0], status); ! 641: #endif DEBUG ! 642: atapiIoReq->scsiStatus = STAT_GOOD; ! 643: return SR_IOST_GOOD; ! 644: } ! 645: } ! 646: ! 647: /* ! 648: * Command is not completed. We need to transfer data as requested by ! 649: * the device. ! 650: */ ! 651: bytes = inb(_ideRegsAddrs.byteCountHigh) << 8 | ! 652: inb(_ideRegsAddrs.byteCountLow); ! 653: ! 654: #ifdef DEBUG ! 655: IOLog("%s: ATAPI Drive %d: completed: %x, request: %x max: %x\n", ! 656: [self name], _driveNum, atapiIoReq->bytesTransferred, ! 657: bytes, atapiIoReq->maxTransfer); ! 658: #endif DEBUG ! 659: ! 660: /* ! 661: * If the device requests more data to be transferred than required ! 662: * by the command protocol, then we should transfer null data. ! 663: */ ! 664: ! 665: if (atapiIoReq->bytesTransferred + bytes > atapiIoReq->maxTransfer) { ! 666: ! 667: IOLog("%s: ATAPI Drive %d: " ! 668: "ERROR: Transfer limit (%x bytes) exceeded\n", ! 669: [self name], _driveNum, atapiIoReq->maxTransfer); ! 670: ! 671: IOLog("%s: Bytes already transfered: %x, next request: %x\n", ! 672: [self name], atapiIoReq->bytesTransferred, bytes); ! 673: ! 674: [self dumpStatus: atapiIoReq]; ! 675: ! 676: /* ! 677: * This thing is probably hosed but let's do the best we can. ! 678: */ ! 679: { ! 680: int diff = atapiIoReq->maxTransfer-atapiIoReq->bytesTransferred; ! 681: IOLog("%s: Transferring %x bytes instead of %x\n", ! 682: [self name], diff, bytes); ! 683: [self xferData:buffer+offset read:atapiIoReq->read ! 684: client:client length:diff]; ! 685: atapiIoReq->bytesTransferred += diff; ! 686: offset += diff; ! 687: [self atapiSoftReset:_driveNum]; // reset hardware as well ! 688: } ! 689: ! 690: atapiIoReq->scsiStatus = STAT_GOOD; ! 691: return SR_IOST_GOOD; ! 692: #if 0 ! 693: /* this was old behavior */ ! 694: atapiIoReq->scsiStatus = STAT_CHECK; ! 695: return SR_IOST_CMDREJ; ! 696: #endif 0 ! 697: } ! 698: ! 699: /* ! 700: * Now transfer data between device and memory. ! 701: */ ! 702: [self xferData:buffer+offset read:atapiIoReq->read client:client ! 703: length:bytes]; ! 704: ! 705: atapiIoReq->bytesTransferred += bytes; ! 706: offset += bytes; ! 707: } ! 708: ! 709: /* ! 710: * Will never get here. ! 711: */ ! 712: atapiIoReq->scsiStatus = STAT_GOOD; ! 713: return SR_IOST_GOOD; ! 714: } ! 715: ! 716: /* ! 717: * Prints command and result in case of error. ! 718: */ ! 719: - (void) dumpStatus:(atapiIoReq_t *)atapiIoReq ! 720: { ! 721: int i; ! 722: ! 723: IOLog("%s: Failed command: drive: %d lun: %d len: %d read: %d\n", ! 724: [self name], ! 725: atapiIoReq->drive, atapiIoReq->lun, atapiIoReq->cmdLen, ! 726: atapiIoReq->read); ! 727: IOLog("%s: Failed command: ", [self name]); ! 728: for (i = 0; i < atapiIoReq->cmdLen; i++) ! 729: IOLog("%x ", atapiIoReq->atapiCmd[i]); ! 730: IOLog("\n"); ! 731: ! 732: [self getIdeRegisters:NULL Print:"dumpStatus"]; ! 733: } ! 734: ! 735: /* ! 736: * Execute the ATAPI command specified in 'atapiIoReq'. ! 737: */ ! 738: - (sc_status_t) atapiExecuteCmd:(atapiIoReq_t *)atapiIoReq ! 739: buffer : (void *)buffer ! 740: client : (struct vm_map *)client ! 741: { ! 742: int i; ! 743: atapi_return_t rtn; ! 744: unsigned char dh = ADDRESS_MODE_LBA; /* ALWAYS */ ! 745: sc_status_t sc_ret; ! 746: unsigned char cmd = atapiIoReq->atapiCmd[0]; ! 747: BOOL useDMA = NO; ! 748: ! 749: /* ! 750: * Reject command if there are no ATAPI devices detected. ! 751: */ ! 752: if ((atapiIoReq->drive >= MAX_IDE_DRIVES) || ! 753: (atapiIoReq->lun != 0) || ! 754: ([self isAtapiDevice:atapiIoReq->drive] == NO)) { ! 755: return SR_IOST_SELTO; ! 756: } ! 757: ! 758: /* Now execute the SCSI command. */ ! 759: ! 760: #ifdef DEBUG ! 761: /* Print out the command received. */ ! 762: IOLog("%s: atapiExecuteCmd: drive: %d lun: %d len: %d read %d\n", ! 763: [self name], atapiIoReq->drive, atapiIoReq->lun, atapiIoReq->cmdLen, ! 764: atapiIoReq->read); ! 765: IOLog("%s: Command: ", [self name]); ! 766: if (atapiIoReq->atapiCmd[4] == 0x41) ! 767: atapiIoReq->atapiCmd[4] = 0x24; ! 768: for (i = 0; i < atapiIoReq->cmdLen; i++) ! 769: IOLog("%x ", atapiIoReq->atapiCmd[i]); ! 770: IOLog("\n"); ! 771: //IOBreakToDebugger(); ! 772: #endif DEBUG ! 773: ! 774: _driveNum = atapiIoReq->drive; ! 775: ! 776: /* ! 777: * We are very particular on the type of commands that we allow ! 778: * DMA to be used. ! 779: * ! 780: * 1. It must be a Read/Write data command. ! 781: * 2. Buffer must be 4-byte aligned. ! 782: # 3. The drive must be DMA capable. ! 783: */ ! 784: useDMA = ((_drives[_driveNum].transferType != IDE_TRANSFER_PIO) && ! 785: (((vm_offset_t)buffer & (PIIX_BUF_ALIGN - 1)) == 0) && ! 786: ((cmd == 0x28) || (cmd == 0xa8) || // read 10 and read 12 ! 787: (cmd == 0x2a) || (cmd == 0xaa) || // write 10 and write 12 ! 788: (cmd == 0x2f) || (cmd == 0x2e))); // write and verify ! 789: ! 790: for (i = 0; i < MAX_SENDPACKET_RETRIES; i++) { ! 791: ! 792: /* ! 793: * Select the drive. Make sure the BSY bit is off before ! 794: * changing the drv bit. ! 795: */ ! 796: [self atapiWaitStatusBitsFor:ATAPI_MAX_WAIT_FOR_NOTBUSY ! 797: on:0 off:0 alt:NO]; ! 798: dh |= _driveNum ? SEL_DRIVE1 : SEL_DRIVE0; ! 799: outb(_ideRegsAddrs.driveSelect, dh); ! 800: ! 801: /* ! 802: * Wait for BSY = 0, DRQ = 0. ! 803: */ ! 804: rtn = [self atapiWaitStatusBitsFor:ATAPI_MAX_WAIT_FOR_NOTBUSY ! 805: on:0 off:DREQUEST alt:NO]; ! 806: if (rtn != IDER_SUCCESS) { ! 807: IOLog("%s: ATAPI Drive %d: Not Ready For Packet command.\n", ! 808: [self name], _driveNum); ! 809: [self atapiSoftReset:_driveNum]; ! 810: continue; ! 811: } ! 812: ! 813: /* ! 814: * Send our preferred data transfer size (2048 bytes). ! 815: */ ! 816: outb(_ideRegsAddrs.byteCountLow, 0x00); ! 817: outb(_ideRegsAddrs.byteCountHigh, 0x08); ! 818: ! 819: /* ! 820: * We will use PIO for data transfers. ! 821: */ ! 822: if (useDMA == YES) ! 823: outb(_ideRegsAddrs.features, 1); ! 824: else ! 825: outb(_ideRegsAddrs.features, 0); ! 826: ! 827: [self enableInterrupts]; ! 828: [self clearInterrupts]; ! 829: ! 830: /* ! 831: * First tell the ATAPI device that we are going to send it a packet ! 832: * command. If this command fails the ATAPI device needs to be reset. ! 833: */ ! 834: if ([self issuePacketCommand] == IDER_SUCCESS) { ! 835: break; ! 836: } ! 837: ! 838: IOLog("%s: ATAPI Drive %d: Packet command failed. Retrying...\n", ! 839: [self name], _driveNum); ! 840: [self atapiSoftReset:_driveNum]; ! 841: } ! 842: ! 843: /* ! 844: * Are we hosed? ! 845: */ ! 846: if (i == MAX_SENDPACKET_RETRIES) { ! 847: atapiIoReq->scsiStatus = STAT_CHECK; ! 848: IOLog("%s: ATAPI Drive %d: FATAL: Packet command.\n", ! 849: [self name], _driveNum); ! 850: return SR_IOST_CMDREJ; ! 851: } ! 852: ! 853: // [self clearInterrupts]; ! 854: ! 855: /* ! 856: * Send the ATAPI command packet bytes (usually 12-bytes) to the device. ! 857: */ ! 858: [self sendAtapiCommand:atapiIoReq->atapiCmd cmdLen:atapiIoReq->cmdLen]; ! 859: ! 860: /* ! 861: * Peform data transfer (if any) and return the result code. ! 862: */ ! 863: if (useDMA == YES) ! 864: sc_ret = [self performATAPIDMA:atapiIoReq buffer:buffer client:client]; ! 865: else ! 866: sc_ret = [self atapiPIODataTransfer:atapiIoReq buffer:buffer ! 867: client:client]; ! 868: ! 869: return sc_ret; ! 870: } ! 871: ! 872: #if 0 ! 873: #define ATAPI_SET_CDROM_SPEED 0xbb ! 874: ! 875: /* ! 876: * Doesn't seem to make any difference. ! 877: */ ! 878: - (void)setMaxSpeedForATAPICDROM:(unsigned char)unit ! 879: { ! 880: atapiIoReq_t atapiIoReq; ! 881: ! 882: bzero(&atapiIoReq, sizeof(atapiIoReq_t)); ! 883: ! 884: atapiIoReq.cmdLen = [self atapiCommandPacketSize:unit]; ! 885: atapiIoReq.drive = unit; ! 886: ! 887: atapiIoReq.atapiCmd[0] = ATAPI_SET_CDROM_SPEED; ! 888: atapiIoReq.atapiCmd[2] = 0xff; ! 889: atapiIoReq.atapiCmd[3] = 0xff; ! 890: ! 891: (void) [self atapiExecuteCmd:&atapiIoReq ! 892: buffer:NULL client:(struct vm_map *)IOVmTaskSelf()]; ! 893: } ! 894: #endif 0 ! 895: ! 896: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.