|
|
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.1 (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 by Apple Computer, Inc., all rights reserved. ! 26: /* ! 27: * Copyright (c) 1995-1997 NeXT Software, Inc. All rights reserved. ! 28: * ! 29: * AtapiCnt.m - Implementation of ATAPI controller class. ! 30: * ! 31: * ! 32: * HISTORY ! 33: * 31-Aug-1994 Rakesh Dubey at NeXT ! 34: * Created. ! 35: */ ! 36: ! 37: #undef DEBUG ! 38: ! 39: #import <kern/assert.h> ! 40: #import <driverkit/kernelDriver.h> ! 41: #import <driverkit/interruptMsg.h> ! 42: #import <mach/mach_interface.h> ! 43: #import <driverkit/IODevice.h> ! 44: #import <driverkit/ppc/IOTreeDevice.h> ! 45: #import <driverkit/align.h> ! 46: #import <machkit/NXLock.h> ! 47: #import "io_inline.h" ! 48: ! 49: #import "IdeCnt.h" ! 50: #import "AtapiCnt.h" ! 51: #import "AtapiCntInternal.h" ! 52: ! 53: /* ! 54: * opcode groups ! 55: */ ! 56: #define SCSI_OPGROUP(opcode) ((opcode) & 0xe0) ! 57: ! 58: #define OPGROUP_0 0x00 /* six byte commands */ ! 59: #define OPGROUP_1 0x20 /* ten byte commands */ ! 60: #define OPGROUP_2 0x40 /* ten byte commands */ ! 61: #define OPGROUP_5 0xa0 /* twelve byte commands */ ! 62: #define OPGROUP_6 0xc0 /* six byte, vendor unique commands */ ! 63: #define OPGROUP_7 0xe0 /* ten byte, vendor unique commands */ ! 64: ! 65: /* ! 66: * List of SCSI commands that do not have a counterpart in ATAPI ! 67: * implementation. These commands will need to be mapped individually. ! 68: */ ! 69: static unsigned char mapToAtapi[] = { ! 70: C6OP_MODESELECT, ! 71: C6OP_MODESENSE ! 72: }; ! 73: ! 74: /* ! 75: * List of controllers that have been already probed. We need this since each ! 76: * Instance table lists IdeDisk as well as IdeCOntroller classes. And we need ! 77: * to create instances of disks attached to each controller only once. ! 78: */ ! 79: static int probedControllerCount = 0; ! 80: static id probedControllers[MAX_IDE_CONTROLLERS]; ! 81: ! 82: @implementation AtapiController ! 83: ! 84: + (BOOL)probe : deviceDescription ! 85: { ! 86: ! 87: int unit, i; ! 88: id direct; ! 89: id atapiCnt; ! 90: ! 91: direct = [deviceDescription directDevice]; ! 92: ! 93: for (i = 0; i < probedControllerCount; i++) ! 94: { ! 95: if (probedControllers[i] == direct) ! 96: { ! 97: return YES; ! 98: } ! 99: } ! 100: ! 101: probedControllers[probedControllerCount++] = direct; ! 102: ! 103: for (unit = 0; unit < [direct numDevices]; unit++) ! 104: { ! 105: if ([direct isAtapiDevice:unit]) ! 106: { ! 107: atapiCnt = [[self alloc] initFromDeviceDescription:deviceDescription]; ! 108: ! 109: if (atapiCnt == nil) ! 110: { ! 111: IOLog("ATAPI: failed to probe device %d.\n", unit); ! 112: continue; ! 113: } ! 114: ! 115: if ([atapiCnt initResources:direct] == nil) ! 116: { ! 117: IOLog("ATAPI: failed to initialize device %d.\n", unit); ! 118: [atapiCnt free]; ! 119: continue; ! 120: } ! 121: ! 122: if ([atapiCnt registerDevice] == nil) ! 123: { ! 124: IOLog("ATAPI: failed to register device %d.\n", unit); ! 125: [atapiCnt free]; ! 126: continue; ! 127: } ! 128: else ! 129: { ! 130: return YES; ! 131: } ! 132: } ! 133: } ! 134: return NO; ! 135: } ! 136: ! 137: /* ! 138: * We override IOSCSIController to make ourself look like an indirect device ! 139: * in order to get connected to IdeController. ! 140: */ ! 141: + (IODeviceStyle)deviceStyle ! 142: { ! 143: return IO_IndirectDevice; ! 144: } ! 145: ! 146: /* ! 147: * The protocol we need as an indirect device. ! 148: */ ! 149: static Protocol *protocols[] = { ! 150: @protocol(AtapiControllerPublic), ! 151: nil ! 152: }; ! 153: ! 154: + (Protocol **)requiredProtocols ! 155: { ! 156: return protocols; ! 157: } ! 158: ! 159: ! 160: - (unsigned short)scsiCmdLen:(IOSCSIRequest *) scsiReq ! 161: { ! 162: unsigned char cmdlen; ! 163: union cdb *cdbp = &scsiReq->cdb; ! 164: ! 165: switch (SCSI_OPGROUP(cdbp->cdb_opcode)) { ! 166: ! 167: case OPGROUP_0: ! 168: cmdlen = sizeof(struct cdb_6); ! 169: break; ! 170: ! 171: case OPGROUP_1: ! 172: case OPGROUP_2: ! 173: cmdlen = sizeof(struct cdb_10); ! 174: break; ! 175: ! 176: case OPGROUP_5: ! 177: cmdlen = sizeof(struct cdb_12); ! 178: break; ! 179: ! 180: case OPGROUP_6: ! 181: if (scsiReq->cdbLength) ! 182: cmdlen = scsiReq->cdbLength; ! 183: else ! 184: cmdlen = sizeof(struct cdb_6); ! 185: break; ! 186: ! 187: case OPGROUP_7: ! 188: if (scsiReq->cdbLength) ! 189: cmdlen = scsiReq->cdbLength; ! 190: else ! 191: cmdlen = sizeof(struct cdb_10); ! 192: break; ! 193: ! 194: default: ! 195: scsiReq->driverStatus = SR_IOST_CMDREJ; ! 196: return 0; ! 197: } ! 198: ! 199: return cmdlen; ! 200: } ! 201: ! 202: #define C10OP_MODESELECT 0x55 /* OPT: set device parameters */ ! 203: #define C10OP_MODESENSE 0x5a /* OPT: get device parameters */ ! 204: #define C10OP_READCAPACITY 0x25 /* read capacity */ ! 205: ! 206: /* ! 207: * Do a SCSI command, as specified by an IOSCSIRequest. ! 208: */ ! 209: - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq ! 210: buffer : (void *)buffer ! 211: client : (vm_task_t)client ! 212: { ! 213: int i; ! 214: atapiIoReq_t atapiIoReq; ! 215: atapiBuf_t *atapiBuf; ! 216: unsigned char *scsiCmd; ! 217: cdb_t my_cdb; ! 218: sc_status_t ret; ! 219: IOReturn driverStatus; ! 220: ! 221: [_ataController atapiCntrlrLock]; ! 222: ! 223: my_cdb = scsiReq->cdb; ! 224: ! 225: bzero(&atapiIoReq, sizeof(atapiIoReq_t)); ! 226: ! 227: atapiIoReq.cmdLen = [_ataController atapiCommandPacketSize:scsiReq->target]; ! 228: ! 229: atapiIoReq.read = scsiReq->read; ! 230: atapiIoReq.maxTransfer = scsiReq->maxTransfer; ! 231: atapiIoReq.drive = scsiReq->target; ! 232: atapiIoReq.lun = scsiReq->lun; ! 233: ! 234: scsiCmd = (unsigned char *) &(my_cdb.cdb_opcode); ! 235: ! 236: for (i = 0; i < [self scsiCmdLen:scsiReq]; i++) { ! 237: atapiIoReq.atapiCmd[i] = *scsiCmd; ! 238: scsiCmd++; ! 239: } ! 240: ! 241: /* ! 242: * Map to available ATAPI command if necessary. ! 243: */ ! 244: for (i = 0; i < sizeof(mapToAtapi)/sizeof(mapToAtapi[0]); i++) ! 245: { ! 246: if (atapiIoReq.atapiCmd[0] == mapToAtapi[i]) ! 247: { ! 248: if ([self maptoAtapiCmd: &atapiIoReq buffer:buffer] == NO) ! 249: { ! 250: [_ataController atapiCntrlrUnLock]; ! 251: return SR_IOST_CMDREJ; ! 252: } ! 253: break; ! 254: } ! 255: } ! 256: ! 257: /* ! 258: * If emulation is successful then return quickly. ! 259: */ ! 260: if ([self emulateSCSICmd: &atapiIoReq buffer:buffer] == YES) ! 261: { ! 262: [_ataController atapiCntrlrUnLock]; ! 263: return SR_IOST_GOOD; ! 264: } ! 265: ! 266: /* ! 267: * Send the command to the ioThread. ! 268: */ ! 269: atapiBuf = [self allocAtapiBuf]; ! 270: atapiBuf->atapiIoReq = &atapiIoReq; ! 271: atapiBuf->buffer = buffer; ! 272: atapiBuf->client = client; ! 273: atapiBuf->command = ATAPI_CNT_IOREQ; ! 274: driverStatus = [self enqueueAtapiBuf:atapiBuf]; ! 275: ret = atapiBuf->status; // SCSI status ! 276: [self freeAtapiBuf:atapiBuf]; ! 277: ! 278: /* ! 279: * This is a workaround for the Mitsumi Read CD-ROM capacity bug. It ! 280: * reports the block size as 2352 bytes instead of 2048 bytes. It is ! 281: * including preamble and other info. I have seen this on MITSUMI CD-ROM ! 282: * !B B02. The workaround is truncate the returned value. Remove this ! 283: * when it is no longer needed. ! 284: */ ! 285: { ! 286: unsigned int blockSize; ! 287: char *buf = (char *)buffer; ! 288: ! 289: if ((atapiIoReq.atapiCmd[0] == C10OP_READCAPACITY) && (atapiIoReq.lun == 0)) ! 290: { ! 291: blockSize = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; ! 292: ! 293: if ( blockSize == 2352 ) ! 294: { ! 295: blockSize = 2048; ! 296: buf[4] = (blockSize >> 24) & 0xff; ! 297: buf[5] = (blockSize >> 16) & 0xff; ! 298: buf[6] = (blockSize >> 8) & 0xff; ! 299: buf[7] = blockSize & 0xff; ! 300: } ! 301: } ! 302: } ! 303: ! 304: scsiReq->bytesTransferred = atapiIoReq.bytesTransferred; ! 305: scsiReq->scsiStatus = atapiIoReq.scsiStatus; ! 306: scsiReq->driverStatus = driverStatus; ! 307: ! 308: [_ataController atapiCntrlrUnLock]; ! 309: ! 310: return ret; ! 311: } ! 312: ! 313: - (BOOL) maptoAtapiCmd:(atapiIoReq_t *)atapiIoReq buffer:(void *)buffer ! 314: { ! 315: int i, pageLength; ! 316: unsigned char *data = (unsigned char *)buffer; ! 317: ! 318: if (atapiIoReq->atapiCmd[0] == C6OP_MODESENSE) ! 319: { ! 320: atapiIoReq->atapiCmd[0] = C10OP_MODESENSE; ! 321: atapiIoReq->atapiCmd[8] = atapiIoReq->atapiCmd[4]; ! 322: atapiIoReq->atapiCmd[4] = 0; ! 323: return YES; ! 324: } ! 325: ! 326: if (atapiIoReq->atapiCmd[0] == C6OP_MODESELECT) ! 327: { ! 328: atapiIoReq->atapiCmd[0] = C10OP_MODESELECT; ! 329: atapiIoReq->atapiCmd[8] = atapiIoReq->atapiCmd[4]; ! 330: atapiIoReq->atapiCmd[4] = 0; ! 331: ! 332: /* ! 333: * This is complicated since SCSI and ATAPI have very different ! 334: * data layouts for mode select. ! 335: */ ! 336: if ((atapiIoReq->atapiCmd[0] == C10OP_MODESELECT) && ! 337: (data[3] == 0x08)) { ! 338: data[0] = data[1] = 0; /* mode data length is reserved */ ! 339: data[2] = 0; /* always for CD-ROM. Tape? FIXME */ ! 340: data[3] = 0; /* reserved for ATAPI */ ! 341: ! 342: /* ! 343: * Move the data up by four bytes since there only four bytes of ! 344: * block descriptor in ATAPI while SCSI has eight. ! 345: */ ! 346: pageLength = data[13] & 0x01f; ! 347: for (i = 8; i < pageLength+8; i++) ! 348: { ! 349: data[i] = data[i+4]; ! 350: data[i+4] = 0; ! 351: } ! 352: ! 353: atapiIoReq->atapiCmd[8] = 0x14; /* 8 + 12 */ ! 354: data[9] = 0; ! 355: ! 356: return YES; ! 357: } ! 358: ! 359: /* ! 360: * Now handle the case where block descriptor length is zero. This is ! 361: * more messy since we are short of memory. FIXME later.. ! 362: */ ! 363: ! 364: return YES; /* this will fail now */ ! 365: } ! 366: ! 367: return YES; ! 368: } ! 369: ! 370: /* ! 371: * If this SCSI command is not supported by ATAPI then try to fake its ! 372: * execution if possible. ! 373: */ ! 374: - (BOOL) emulateSCSICmd:(atapiIoReq_t *)atapiIoReq buffer:(void *)buffer ! 375: { ! 376: unsigned char *data = (unsigned char *)buffer; ! 377: ! 378: /* ! 379: * We need to fake mode sense page 2. Workspace sends that and this page ! 380: * is reserved in ATAPI. In the SCSI world this page is for ! 381: * disconnect-reconnect and ATAPI doesn't support that now. ! 382: */ ! 383: if (atapiIoReq->atapiCmd[0] == C10OP_MODESENSE) ! 384: { ! 385: if ((atapiIoReq->atapiCmd[2] & 0x1f) == 0x02) ! 386: { ! 387: bzero(data, atapiIoReq->atapiCmd[4]); ! 388: data[0] = 0x02; ! 389: data[1] = atapiIoReq->atapiCmd[4]; ! 390: data[2] = 1; /* our preferred size: 2048 bytes */ ! 391: atapiIoReq->bytesTransferred = atapiIoReq->atapiCmd[4]; ! 392: atapiIoReq->scsiStatus = STAT_GOOD; ! 393: ! 394: return YES; ! 395: } ! 396: } ! 397: ! 398: return NO; /* default */ ! 399: } ! 400: ! 401: ! 402: /* ! 403: * Reset all ATAPI devices connected to this controller. Note that we have ! 404: * obtain a lock before resetting the controller. ! 405: */ ! 406: - (sc_status_t)resetSCSIBus ! 407: { ! 408: int unit; ! 409: atapi_return_t ret; ! 410: sc_status_t status = SR_IOST_GOOD; ! 411: ! 412: for (unit = 0; unit < [_ataController numDevices]; unit++) { ! 413: ! 414: if ([_ataController isAtapiDevice:unit]) ! 415: { ! 416: [_ataController atapiCntrlrLock]; ! 417: ret = [_ataController atapiSoftReset:unit]; ! 418: [_ataController atapiCntrlrUnLock]; ! 419: ! 420: if (ret != IDER_SUCCESS) ! 421: { ! 422: IOLog("%s: ATAPI reset failed.\n", [self name]); ! 423: status = SR_IOST_HW; ! 424: } ! 425: } ! 426: } ! 427: ! 428: return status; ! 429: } ! 430: ! 431: /* ! 432: * Note: ATAPI requires transfer byte counts be even. ! 433: * The alignment fields readLength and writeLength are set 2. ! 434: */ ! 435: ! 436: - (void)getDMAAlignment:(IODMAAlignment *)alignment ! 437: { ! 438: alignment->readStart = 1; /* XXX */ ! 439: alignment->writeStart = 1; /* XXX */ ! 440: alignment->readLength = 2; /* XXX */ ! 441: alignment->writeLength = 2; /* XXX */ ! 442: } ! 443: ! 444: /* ! 445: * Set maximum I/O length for SCSI framework. ! 446: * ! 447: * There are 256 DBDMA descriptors available. One is used for a DBDMA STOP command. Another is ! 448: * potentially used to handle odd-byte transfers. An additional one may be potentially used if ! 449: * the transfer address is not page-aligned, for ex: a 64KB unaligned transfer requires 17 descriptors. ! 450: */ ! 451: - (unsigned) maxTransfer ! 452: { ! 453: return (MAX_IDE_DESCRIPTORS - 3) * PAGE_SIZE; ! 454: } ! 455: ! 456: - property_IODeviceClass:(char *)classes length:(unsigned int *)maxLen ! 457: { ! 458: strcpy( classes, IOClassATAPIController); ! 459: return( self); ! 460: } ! 461: ! 462: - property_IODeviceType:(char *)types length:(unsigned int *)maxLen ! 463: { ! 464: strcat( types, " "IOTypeATAPI); ! 465: return( self); ! 466: } ! 467: ! 468: ! 469: @end ! 470:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.