|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * Copyright (c) 1998 Apple Computer, Inc. All rights reserved. ! 24: * ! 25: * Hardware independent (relatively) code for the AudioBus ! 26: * ! 27: * HISTORY ! 28: * ! 29: * ! 30: */ ! 31: ! 32: #include <IOKit/audio/IOAudioBus.h> ! 33: #include <IOKit/IOLib.h> ! 34: #include <IOKit/IOWorkLoop.h> ! 35: #include <IOKit/IOFilterInterruptEventSource.h> ! 36: ! 37: #undef super ! 38: #define super IOAudioController ! 39: ! 40: //************************************************************************ ! 41: // Implementation of protocol clas. ! 42: //************************************************************************ ! 43: OSDefineMetaClass( IOAudioBus, IOAudioController ) ! 44: OSDefineAbstractStructors( IOAudioBus, IOAudioController ) ! 45: ! 46: #ifdef __ppc__ ! 47: ! 48: //************************************************************************ ! 49: // Begin implementation of IOAudioBus class. ! 50: //************************************************************************ ! 51: ! 52: #define kNumberOfBuffers 4 ! 53: #define kNumberOfSamples 8192 ! 54: #define kNumberOfChannels 2 // left and right ! 55: #define kSampleSize 2 // 16 bit channels ! 56: #define kBlockSize 256 ! 57: ! 58: const int IOAudioBus::kAudioDMAdeviceInt = 0; ! 59: const int IOAudioBus::kAudioDMAtxInt = 1; ! 60: const int IOAudioBus::kAudioDMArxInt = 2; ! 61: ! 62: const int IOAudioBus::kAudioDMAOutputStream = 0; ! 63: const int IOAudioBus::kAudioDMAInputStream = 1; ! 64: ! 65: // Constructs an empty audio bus: ! 66: bool ! 67: IOAudioBus::init(OSDictionary * properties) ! 68: { ! 69: if (!super::init(properties)) ! 70: return false; ! 71: ! 72: ioAudioStreamsDMA = NULL; ! 73: numDMAStreams = NULL; ! 74: ! 75: // Initialize my ivars. ! 76: fBufMax = kNumberOfBuffers * kNumberOfSamples * kNumberOfChannels * kSampleSize; ! 77: fBlockSize = kBlockSize; ! 78: ! 79: return true; ! 80: } ! 81: ! 82: // This should free everything ! 83: void ! 84: IOAudioBus::free() ! 85: { ! 86: FreeStreams(); ! 87: super::free(); ! 88: } ! 89: ! 90: void IOAudioBus::startWorkLoop() ! 91: { ! 92: super::startWorkLoop(); ! 93: registerInterrupts(); ! 94: } ! 95: ! 96: void IOAudioBus::registerInterrupts() ! 97: { ! 98: fTxInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(this, ! 99: IOAudioBus::AudioBusInterruptHandler, ! 100: IOAudioBus::AudioBusInterruptFilter, ! 101: fDevice, ! 102: kAudioDMAtxInt); ! 103: fWorkLoop->addEventSource(fTxInterruptSource); ! 104: ! 105: fRxInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(this, ! 106: IOAudioBus::AudioBusInterruptHandler, ! 107: IOAudioBus::AudioBusInterruptFilter, ! 108: fDevice, ! 109: kAudioDMArxInt); ! 110: fWorkLoop->addEventSource(fRxInterruptSource); ! 111: ! 112: fTxInterruptSource->enable(); ! 113: fRxInterruptSource->enable(); ! 114: } ! 115: ! 116: bool IOAudioBus::AudioBusInterruptFilter(OSObject *owner, ! 117: IOFilterInterruptEventSource *source) ! 118: { ! 119: register IOAudioBus *bus = (IOAudioBus *)owner; ! 120: bool result = true; ! 121: ! 122: if (bus) { ! 123: result = bus->filterInterrupt(source->getIntIndex()); ! 124: } ! 125: ! 126: return result; ! 127: } ! 128: ! 129: bool IOAudioBus::filterInterrupt(int index) ! 130: { ! 131: IOAudioStreamStatus *status = getSharedStatus(getStreamForInterrupt(index)); ! 132: ! 133: if (status) { ! 134: clock_get_uptime(&status->fLastLoopTime); ! 135: ++status->fCurrentLoopCount; ! 136: } ! 137: ! 138: return false; ! 139: } ! 140: ! 141: void IOAudioBus::AudioBusInterruptHandler(OSObject *owner, ! 142: IOInterruptEventSource * /*source*/, ! 143: int /*count*/) ! 144: { ! 145: return; ! 146: } ! 147: ! 148: AudioStreamIndex IOAudioBus::getStreamForInterrupt(int index) ! 149: { ! 150: AudioStreamIndex result = kNoStream; ! 151: ! 152: switch (index) { ! 153: case kAudioDMAtxInt: ! 154: result = kAudioDMAOutputStream; ! 155: break; ! 156: case kAudioDMArxInt: ! 157: result = kAudioDMAInputStream; ! 158: break; ! 159: } ! 160: return result; ! 161: } ! 162: ! 163: // Creates n empty streams (also a method to free them) ! 164: bool ! 165: IOAudioBus::AllocateStreams(int n) ! 166: { ! 167: numDMAStreams = n; ! 168: ! 169: if (numDMAStreams > 0) { ! 170: // Allocates as many StreamDMAInfo as needed: ! 171: ioAudioStreamsDMA = (StreamDMAInfo*)IOMalloc(sizeof(StreamDMAInfo) * numDMAStreams); ! 172: if (ioAudioStreamsDMA == NULL) { ! 173: numDMAStreams = 0; ! 174: return false; ! 175: } ! 176: for (n =0 ; n < numDMAStreams; n++) { ! 177: ioAudioStreamsDMA[n].streamProperty = NULL; ! 178: ioAudioStreamsDMA[n].fIOBaseDMA = NULL; ! 179: ioAudioStreamsDMA[n].fSharedStatus = NULL; ! 180: } ! 181: ! 182: return (true); ! 183: } ! 184: ! 185: return (false); ! 186: } ! 187: ! 188: bool ! 189: IOAudioBus::FreeStreams() ! 190: { ! 191: int n; ! 192: ! 193: if (ioAudioStreamsDMA != NULL) { ! 194: // First stops all the streams: ! 195: for (n =0 ; n < numDMAStreams; n++) ! 196: if (ioAudioStreamsDMA[n].fIOBaseDMA != NULL) { ! 197: stopStream(n); ! 198: ! 199: // If we ever created a stream property fpr the stream, delete it: ! 200: IOFree(ioAudioStreamsDMA[n].streamProperty, 128); ! 201: ioAudioStreamsDMA[n].streamProperty = NULL; ! 202: } ! 203: ! 204: IOFree(ioAudioStreamsDMA, sizeof(StreamDMAInfo) * numDMAStreams); ! 205: ioAudioStreamsDMA = NULL; ! 206: ! 207: return true; ! 208: } ! 209: ! 210: return false; ! 211: } ! 212: ! 213: // For each stream defines its properties: ! 214: bool ! 215: IOAudioBus::DefineStream(AudioStreamIndex i, int direction, UInt32 rate, IODBDMAChannelRegisters *base) ! 216: { ! 217: if ((ioAudioStreamsDMA != NULL) && (i < numDMAStreams) && (base != NULL)) { ! 218: ioAudioStreamsDMA[i].fIOBaseDMA = base; ! 219: ! 220: // The stream direction is "hardwired" so that ecah channel ! 221: // can go in one direction. ! 222: if (direction == kInput) { ! 223: ioAudioStreamsDMA[i].fDmaCmd = kdbdmaInputMore; ! 224: ioAudioStreamsDMA[i].fNeedsErase = false; ! 225: ioAudioStreamsDMA[i].fIsInput = true; ! 226: } ! 227: else { ! 228: ioAudioStreamsDMA[i].fDmaCmd = kdbdmaOutputMore; ! 229: ioAudioStreamsDMA[i].fNeedsErase = true; ! 230: ioAudioStreamsDMA[i].fIsInput = false; ! 231: } ! 232: ioAudioStreamsDMA[i].fSampleRate = rate; ! 233: ! 234: #ifdef DEBUGMODE ! 235: IOLog("IOAudioBus::DefineStream(%d, %s, %ld, 0x%08lx)\n",i, (direction == kInput ? "kInput" : "kOutput"),rate, (UInt32)base); ! 236: #endif ! 237: } ! 238: ! 239: return false; ! 240: } ! 241: ! 242: // Returns the first stream in the given direction after the given ! 243: // index. (this is useful if we have to handle more than one stream ! 244: // for input and output). (afterIndex is inclusive) ! 245: ! 246: AudioStreamIndex ! 247: IOAudioBus::firstStreamAfter(int inDirection, AudioStreamIndex afterIndex) ! 248: { ! 249: if ((ioAudioStreamsDMA != NULL) && (afterIndex < numDMAStreams)) { ! 250: for (;afterIndex < numDMAStreams; afterIndex++) { ! 251: if ((inDirection == kInput) && (ioAudioStreamsDMA[afterIndex].fIsInput)) ! 252: return (afterIndex); ! 253: else if ((inDirection == kOutput) && (!ioAudioStreamsDMA[afterIndex].fIsInput)) ! 254: return (afterIndex); ! 255: } ! 256: } ! 257: ! 258: // We did not find a stream with the wanted properties ! 259: return (kInvalidStreamIndex); ! 260: } ! 261: ! 262: OSDictionary* ! 263: IOAudioBus::getStreamProperties(AudioStreamIndex i) ! 264: { ! 265: OSDictionary *dict = NULL; ! 266: OSString *errorString = NULL; ! 267: ! 268: if ((ioAudioStreamsDMA != NULL) && (i < numDMAStreams)) ! 269: if (ioAudioStreamsDMA[i].fIOBaseDMA != NULL) { ! 270: ! 271: ioAudioStreamsDMA[i].streamProperty = (char*)IOMalloc(128); ! 272: if (ioAudioStreamsDMA[i].streamProperty != NULL) { ! 273: // Depending from the direction of the stream this builds the property ! 274: // string: ! 275: if (ioAudioStreamsDMA[i].fIsInput) { ! 276: sprintf(ioAudioStreamsDMA[i].streamProperty, "{'In'=%d:8;'Out'=%d:8;'Channels'=%d:8;'Rate'=%ld:32;}", ! 277: (UInt8)1, (UInt8)0, (UInt8)2, ! 278: ioAudioStreamsDMA[i].fSampleRate); ! 279: } ! 280: else{ ! 281: sprintf(ioAudioStreamsDMA[i].streamProperty, "{'In'=%d:8;'Out'=%d:8;'Channels'=%d:8;'Rate'=%ld:32;}", ! 282: (UInt8)0, (UInt8)1, (UInt8)2, ! 283: ioAudioStreamsDMA[i].fSampleRate); ! 284: } ! 285: ! 286: dict = OSDynamicCast(OSDictionary, OSUnserialize(ioAudioStreamsDMA[i].streamProperty, &errorString)); ! 287: } ! 288: } ! 289: else ! 290: IOLog("IOAudioBus::getStreamProperties: bad index %d\n", i); ! 291: ! 292: if (dict == NULL) { ! 293: if (errorString != NULL) { ! 294: IOLog("IOAudioBus::getStreamProperties %s (\"%s\")\n", errorString->getCStringNoCopy(), ioAudioStreamsDMA[i].streamProperty); ! 295: errorString->release(); ! 296: } ! 297: } ! 298: ! 299: return dict; ! 300: } ! 301: ! 302: /* ! 303: * Map stream data for caller. ! 304: */ ! 305: ! 306: IOAudioStream * ! 307: IOAudioBus::createAudioStream(AudioStreamIndex index) ! 308: { ! 309: assert(index < numDMAStreams); ! 310: IOAudioStream * stream = super::createAudioStream(index); ! 311: return stream; ! 312: } ! 313: ! 314: int IOAudioBus::probeStreams() ! 315: { ! 316: return numDMAStreams; ! 317: } ! 318: ! 319: IOAudioStreamStatus * IOAudioBus::startStream(AudioStreamIndex index) ! 320: { ! 321: assert(index < numDMAStreams); ! 322: ! 323: int numBlocks, bufSize, i, cmdSize; ! 324: u_int32_t cmdPhys, bufPhys, seqPhys, offset; ! 325: IOAudioStreamStatus *status; ! 326: char *bufs; ! 327: bool doInterrupt = false; ! 328: ! 329: // Calculate size and allocate dbdma command area, sample buffer and shared status. ! 330: numBlocks = fBufMax/fBlockSize; ! 331: bufSize = fBlockSize * numBlocks; ! 332: cmdSize = (numBlocks * 2 + 1) * sizeof(IODBDMADescriptor); ! 333: IODBDMADescriptor *cmds = (IODBDMADescriptor *)IOMallocAligned(cmdSize, 4); ! 334: bufs = (char *)IOMallocAligned(round_page(bufSize), PAGE_SIZE); ! 335: ! 336: // This makes sure we get an entire page for the status buffer ! 337: // to prevent the problem of other memory being allocated ! 338: // in the same page that we're sharing read-only with user space. ! 339: // Since we don't need an entire page for each status struct, ! 340: // we could keep track of how much of the page that we've used and ! 341: // assign chunks of it for each stream... ! 342: ! 343: status = (IOAudioStreamStatus *)IOMallocAligned(round_page(sizeof(IOAudioStreamStatus)), PAGE_SIZE); ! 344: ! 345: // Everything after this check should always succeed. ! 346: if(!cmds || !bufs || !status) ! 347: return NULL; ! 348: ! 349: bzero(bufs, bufSize); ! 350: ! 351: // get physical addresses of everything for the DBDMA controller. ! 352: seqPhys = pmap_extract(kernel_pmap, (vm_address_t) &(status->fCurrentBlock)); ! 353: cmdPhys = pmap_extract(kernel_pmap, (vm_address_t) cmds); ! 354: bufPhys = pmap_extract(kernel_pmap, (vm_address_t) bufs); ! 355: offset = 0; ! 356: ! 357: // The address of the stop command: ! 358: u_int32_t cmdStopPys = pmap_extract(kernel_pmap, (vm_address_t) (&cmds[numBlocks * 2])); ! 359: ! 360: for(i=0; i<numBlocks; i++) { ! 361: u_int32_t cmdDest; ! 362: ! 363: if(offset >= PAGE_SIZE) { ! 364: bufPhys = pmap_extract(kernel_pmap, (vm_address_t) (bufs + i*fBlockSize)); ! 365: offset = 0; ! 366: } ! 367: ! 368: // Need a DBDMA branch if the next command is on a different page or ! 369: // if we have to loop back to the first DBDMA command. ! 370: if(i == numBlocks-1) { ! 371: cmdDest = cmdPhys; ! 372: doInterrupt = true; ! 373: } else if( ((2*(i+1)*sizeof(IODBDMADescriptor)) % PAGE_SIZE) == 0) ! 374: cmdDest = pmap_extract(kernel_pmap, (vm_address_t) (cmds+2*(i+1))); ! 375: else ! 376: cmdDest = 0; ! 377: ! 378: IOMakeDBDMADescriptorDep( &cmds[2*i], ! 379: kdbdmaStoreQuad, ! 380: kdbdmaKeyStream0, ! 381: kdbdmaIntNever, ! 382: kdbdmaBranchNever, ! 383: kdbdmaWaitNever, ! 384: sizeof(u_int32_t), ! 385: seqPhys, ! 386: OSReadLittleInt32(&i, 0) ); ! 387: if(cmdDest) { ! 388: IOMakeDBDMADescriptorDep( &cmds[2*i+1], ! 389: ioAudioStreamsDMA[index].fDmaCmd, ! 390: kdbdmaKeyStream0, ! 391: doInterrupt ? kdbdmaIntAlways : kdbdmaIntNever, ! 392: kdbdmaBranchAlways, ! 393: kdbdmaWaitNever, ! 394: fBlockSize, ! 395: bufPhys+offset, ! 396: cmdDest); ! 397: } ! 398: else { ! 399: IOMakeDBDMADescriptorDep( &cmds[2*i+1], ! 400: ioAudioStreamsDMA[index].fDmaCmd, ! 401: kdbdmaKeyStream0, ! 402: kdbdmaIntNever, ! 403: kdbdmaBranchIfTrue, ! 404: kdbdmaWaitNever, ! 405: fBlockSize, ! 406: bufPhys+offset, ! 407: cmdStopPys); ! 408: ! 409: } ! 410: offset += fBlockSize; ! 411: } ! 412: ! 413: // Add a STOP: ! 414: IOMakeDBDMADescriptor( &cmds[2*i], ! 415: kdbdmaStop, ! 416: kdbdmaKeyStream0, ! 417: kdbdmaIntNever, ! 418: kdbdmaBranchNever, ! 419: kdbdmaWaitNever, ! 420: 0, ! 421: NULL); ! 422: ! 423: ioAudioStreamsDMA[index].fSharedStatus = status; ! 424: ioAudioStreamsDMA[index].fCmds = cmds; ! 425: ioAudioStreamsDMA[index].fCmdSize = cmdSize; ! 426: ioAudioStreamsDMA[index].fSampleBuffer = (int16_t *)bufs; ! 427: status->fVersion = 1; ! 428: status->fErases = ioAudioStreamsDMA[index].fNeedsErase; ! 429: status->fRunning = 0; ! 430: status->fConnections = 0; ! 431: ! 432: status->fBufSize = bufSize; ! 433: status->fBlockSize = fBlockSize; ! 434: status->fNumBlocks = numBlocks; ! 435: status->fSampleSize = 2; ! 436: status->fChannels = 2; ! 437: status->fDataRate = ioAudioStreamsDMA[index].fSampleRate * status->fSampleSize * status->fChannels; ! 438: status->fCurrentBlock = 0; ! 439: status->fEraseHeadBlock = 0; ! 440: status->fCurrentLoopCount = 0; ! 441: status->fMixBufferInUse = false; ! 442: ! 443: #ifdef DEBUGMODE ! 444: IOLog("DMA commands at 0x%x, %d blocks, seq at 0x%x\n", cmds, numBlocks, seqVirt); ! 445: IOLog("Block size %d, total size %d\n", fBlockSize, bufSize); ! 446: #endif ! 447: ! 448: flush_dcache((vm_offset_t) cmds, cmdSize, false ); ! 449: ! 450: clock_get_uptime(&status->fLastLoopTime); ! 451: ! 452: IOSetDBDMAChannelControl( ioAudioStreamsDMA[index].fIOBaseDMA, IOClearDBDMAChannelControlBits(kdbdmaS0)); ! 453: IOSetDBDMABranchSelect( ioAudioStreamsDMA[index].fIOBaseDMA, IOSetDBDMAChannelControlBits(kdbdmaS0)); ! 454: IODBDMAStart( ioAudioStreamsDMA[index].fIOBaseDMA, cmdPhys ); ! 455: ! 456: return status; ! 457: } ! 458: ! 459: void IOAudioBus::stopStream(AudioStreamIndex index) ! 460: { ! 461: IOFilterInterruptEventSource *interruptEventSource; ! 462: UInt8 attemptsToStop = 100; ! 463: ! 464: assert(!ioAudioStreamsDMA[index].fSharedStatus->fRunning); ! 465: ! 466: if (index == kAudioDMAOutputStream) { ! 467: interruptEventSource = fTxInterruptSource; ! 468: } else { // index == kAudioDMAInputStream ! 469: interruptEventSource = fRxInterruptSource; ! 470: } ! 471: interruptEventSource->disable(); ! 472: ! 473: IOSetDBDMAChannelControl( ioAudioStreamsDMA[index].fIOBaseDMA, IOSetDBDMAChannelControlBits(kdbdmaS0)); ! 474: while ((IOGetDBDMAChannelStatus(ioAudioStreamsDMA[index].fIOBaseDMA) & kdbdmaActive ) && (attemptsToStop--)) { ! 475: eieio(); ! 476: IOSleep(10); ! 477: } ! 478: ! 479: IODBDMAStop( ioAudioStreamsDMA[index].fIOBaseDMA ); ! 480: IODBDMAReset( ioAudioStreamsDMA[index].fIOBaseDMA ); ! 481: ! 482: IOFreeAligned(ioAudioStreamsDMA[index].fCmds, ioAudioStreamsDMA[index].fCmdSize); ! 483: IOFreeAligned(ioAudioStreamsDMA[index].fSampleBuffer, round_page(ioAudioStreamsDMA[index].fSharedStatus->fBufSize)); ! 484: IOFreeAligned(ioAudioStreamsDMA[index].fSharedStatus, round_page(sizeof(IOAudioStreamStatus))); ! 485: ! 486: ioAudioStreamsDMA[index].fCmds = NULL; ! 487: ioAudioStreamsDMA[index].fSampleBuffer = NULL; ! 488: ioAudioStreamsDMA[index].fSharedStatus = NULL; ! 489: ! 490: interruptEventSource->enable(); ! 491: } ! 492: ! 493: void IOAudioBus::pauseStream(AudioStreamIndex index) ! 494: { ! 495: assert(index < numDMAStreams); ! 496: IODBDMAPause( ioAudioStreamsDMA[index].fIOBaseDMA ); ! 497: } ! 498: ! 499: void IOAudioBus::resumeStream(AudioStreamIndex index) ! 500: { ! 501: assert(index < numDMAStreams); ! 502: ! 503: IODBDMAContinue( ioAudioStreamsDMA[index].fIOBaseDMA ); ! 504: } ! 505: ! 506: IOAudioStreamStatus * IOAudioBus::getSharedStatus(AudioStreamIndex index) ! 507: { ! 508: assert(index < numDMAStreams); ! 509: return ioAudioStreamsDMA[index].fSharedStatus; ! 510: } ! 511: ! 512: void * IOAudioBus::getSampleBuffer(AudioStreamIndex index) ! 513: { ! 514: assert(index < numDMAStreams); ! 515: return ioAudioStreamsDMA[index].fSampleBuffer; ! 516: } ! 517: ! 518: #endif // __ppc__
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.