|
|
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: * AudioStream.m ! 26: * ! 27: * Copyright (c) 1991, NeXT Computer, Inc. All rights reserved. ! 28: * ! 29: * Audio driver channel object. ! 30: * ! 31: * HISTORY ! 32: * 07/14/92/mtm Original coding. ! 33: */ ! 34: ! 35: #import "AudioStream.h" ! 36: #import <driverkit/IOAudioPrivate.h> ! 37: #import "AudioChannel.h" ! 38: #import "audioLog.h" ! 39: #import "snd_reply.h" ! 40: #import <kernserv/prototypes.h> ! 41: #import <mach/mach_types.h> ! 42: #import "audioReply.h" ! 43: #import <bsd/sys/time.h> ! 44: #import <mach/mach_user_internal.h> ! 45: #import <mach/mach_interface.h> ! 46: #import <kernserv/kern_server_types.h> ! 47: #import <driverkit/kernelDriver.h> ! 48: #import <kernserv/i386/us_timer.h> // for microtime ! 49: #import <bsd/machine/limits.h> // ULONG_LONG_MAX ! 50: ! 51: /* Factory variables */ ! 52: ! 53: static void *threadArg = 0; ! 54: static NXLock *threadArgLock = nil; ! 55: ! 56: @implementation AudioStream ! 57: ! 58: /* ! 59: * Initialize. ! 60: */ ! 61: - initChannel:chan tag:(int)aTag user:(port_t *)user ! 62: owner:(port_t)owner type:(u_int)aType ! 63: { ! 64: kern_return_t krtn; ! 65: ! 66: xpr_audio_stream("AS: initChannel chan=0x%x tag=%d, user=0x%x owner=%d, " ! 67: "type=%d\n", chan, aTag, user, owner, aType); ! 68: [super init]; ! 69: channel = chan; ! 70: device = [chan audioDevice]; ! 71: tag = aTag; ! 72: samplingRate = 22050; ! 73: dataFormat = IOAudioDataFormatLinear16; ! 74: channelCount = 2; ! 75: queue_init(®ionQueue); ! 76: regionQueueLock = [[NXLock alloc] init]; ! 77: krtn = port_allocate(task_self(), user); ! 78: if (krtn) { ! 79: xpr_audio_stream("AS: initChannel: stream port_allocate error %d\n", ! 80: krtn,2,3,4,5); ! 81: log_error(("Audio: initChannel: stream port_allocate: %s\n", ! 82: mach_error_string(krtn))); ! 83: [self free]; ! 84: return nil; ! 85: } ! 86: userPort = *user; ! 87: kernUserPort = IOConvertPort(userPort, IO_CurrentTask, IO_Kernel); ! 88: ownerPort = owner; ! 89: type = aType; ! 90: ! 91: return self; ! 92: } ! 93: ! 94: /* ! 95: * Return instance variables. ! 96: */ ! 97: - (port_t)userPort ! 98: { ! 99: return userPort; ! 100: } ! 101: - (port_t)ownerPort ! 102: { ! 103: return ownerPort; ! 104: } ! 105: - channel ! 106: { ! 107: return channel; ! 108: } ! 109: - (ASType)type ! 110: { ! 111: return type; ! 112: } ! 113: ! 114: /* ! 115: * Init snd reply msg. ! 116: */ ! 117: - createSndReplyMsg ! 118: { ! 119: if (!sndReplyMsg) ! 120: sndReplyMsg = (msg_header_t *)IOMalloc(MSG_SIZE_MAX); ! 121: sndReplyMsg->msg_simple = TRUE; ! 122: sndReplyMsg->msg_size = sizeof(msg_header_t); ! 123: sndReplyMsg->msg_type = MSG_TYPE_NORMAL; ! 124: sndReplyMsg->msg_local_port = PORT_NULL; ! 125: sndReplyMsg->msg_remote_port = PORT_NULL; ! 126: sndReplyMsg->msg_id = 0; ! 127: return self; ! 128: } ! 129: ! 130: /* ! 131: * Send user stream control reply message. ! 132: */ ! 133: - sendControlMessage:(ASReplyMsg)status mask:(ASMsgRequest)statusMask ! 134: { ! 135: kern_return_t krtn; ! 136: region_t *region; ! 137: port_t ourReplyPort, ourUserPort; ! 138: ! 139: xpr_audio_stream("AS: sendControlMsg status=0x%x, mask=0x%x\n", status, ! 140: statusMask, 3,4,5); ! 141: ! 142: /* ! 143: * If new interface, send one message and return. ! 144: */ ! 145: if (type == AS_TypeUser) { ! 146: if (userReplyMessages & statusMask) { ! 147: ourReplyPort = IOConvertPort(userReplyPort, IO_Kernel, ! 148: IO_KernelIOTask); ! 149: ourUserPort = IOConvertPort(kernUserPort, IO_Kernel, ! 150: IO_KernelIOTask); ! 151: krtn = _NXAudioReplyStreamStatus(ourReplyPort, ourUserPort, ! 152: ourReplyPort, tag, ! 153: 0, status); ! 154: if (krtn != KERN_SUCCESS) { ! 155: xpr_audio_stream("AS: sendControlMsg: replyStreamStatus " ! 156: "error %s (%d)\n", ! 157: mach_error_string(krtn), krtn, 3,4,5); ! 158: } ! 159: } ! 160: return self; ! 161: } ! 162: ! 163: /* ! 164: * Old interface sends one per request, and may send none. ! 165: */ ! 166: xpr_audio_stream("AS: sendControlMsg: locking regionQueue\n", 1,2,3,4,5); ! 167: [regionQueueLock lock]; ! 168: if (queue_empty(®ionQueue)) { ! 169: [regionQueueLock unlock]; ! 170: xpr_audio_stream("AS: sendControlMsg: unlocked regionQueue\n", ! 171: 1,2,3,4,5); ! 172: return self; ! 173: } ! 174: [self createSndReplyMsg]; ! 175: ! 176: region = (region_t *)queue_first(®ionQueue); ! 177: while (!queue_end(®ionQueue, (queue_entry_t)region)) { ! 178: if (region->messages & statusMask) { ! 179: ourReplyPort = IOConvertPort(region->reply_port, IO_Kernel, ! 180: IO_KernelIOTask); ! 181: if (status == AS_ReplyPaused) ! 182: audio_snd_reply_paused(sndReplyMsg, ourReplyPort, region->tag); ! 183: else if (status == AS_ReplyResumed) ! 184: audio_snd_reply_resumed(sndReplyMsg, ourReplyPort, region->tag); ! 185: else if (status == AS_ReplyAborted) ! 186: audio_snd_reply_aborted(sndReplyMsg, ourReplyPort, region->tag); ! 187: krtn = msg_send(sndReplyMsg, STREAM_SEND_OPTIONS, ! 188: STREAM_SEND_TIMEOUT); ! 189: if (krtn != KERN_SUCCESS) { ! 190: xpr_audio_stream("AS: sendControlMsg msg_send error %s" ! 191: " (%d)\n", mach_error_string(krtn), ! 192: krtn, 3,4,5); ! 193: } ! 194: } ! 195: region = (region_t *)queue_next(®ion->link); ! 196: } ! 197: ! 198: [regionQueueLock unlock]; ! 199: xpr_audio_stream("AS: sendControlMsg: unlocked regionQueue\n", 1,2,3,4,5); ! 200: return self; ! 201: } ! 202: ! 203: /* ! 204: * Send user stream status message. ! 205: */ ! 206: - sendStatusMessage:(ASReplyMsg)status forRegion:(region_t *)region ! 207: { ! 208: kern_return_t krtn; ! 209: port_t ourReplyPort, ourUserPort; ! 210: ! 211: xpr_audio_stream("AS: sendStatusMsg %d for region 0x%x\n", status, ! 212: region, 3,4,5); ! 213: ! 214: ourReplyPort = IOConvertPort(region->reply_port, IO_Kernel, ! 215: IO_KernelIOTask); ! 216: ourUserPort = IOConvertPort(kernUserPort, IO_Kernel, IO_KernelIOTask); ! 217: ! 218: if (type == AS_TypeUser) { ! 219: if (status == AS_ReplyAborted && region->flags.excluded) ! 220: status = AS_ReplyExcluded; ! 221: krtn = _NXAudioReplyStreamStatus(ourReplyPort, ourUserPort, ! 222: ourReplyPort, tag, ! 223: region->tag, status); ! 224: if (krtn != KERN_SUCCESS) { ! 225: IOLog("AS: replyStreamStatus returns %d\n", krtn); ! 226: xpr_audio_stream("AS: replyStreamStatus error %s (%d)\n", ! 227: mach_error_string(krtn), krtn, 3,4,5); ! 228: } ! 229: } else { ! 230: [self createSndReplyMsg]; ! 231: if (status == _NXAUDIO_STATUS_STARTED) ! 232: audio_snd_reply_started(sndReplyMsg, ourReplyPort, region->tag); ! 233: else if (status == _NXAUDIO_STATUS_COMPLETED) ! 234: audio_snd_reply_completed(sndReplyMsg, ourReplyPort, region->tag); ! 235: else if (status == _NXAUDIO_STATUS_UNDERRUN) ! 236: audio_snd_reply_overflow(sndReplyMsg, ourReplyPort, region->tag); ! 237: krtn = msg_send(sndReplyMsg, STREAM_SEND_OPTIONS, STREAM_SEND_TIMEOUT); ! 238: if (krtn != KERN_SUCCESS) { ! 239: xpr_audio_stream("AS: sendStatusMsg msg_send error %s (%d)\n", ! 240: mach_error_string(krtn), krtn, 3,4,5); ! 241: } ! 242: } ! 243: return self; ! 244: } ! 245: ! 246: /* ! 247: * Free a region. ! 248: * Output stream subclass overrides to free region data. ! 249: */ ! 250: - freeRegion:(region_t *)region ! 251: { ! 252: IOFree((void *)region, sizeof(region_t)); ! 253: return self; ! 254: } ! 255: ! 256: /* ! 257: * Subclass can implement. ! 258: */ ! 259: - completeRegion:(region_t *)region descriptor:(dma_desc_t *)ddp ! 260: size:(u_int)xfer used:(u_int *)used ! 261: { ! 262: return self; ! 263: } ! 264: ! 265: /* ! 266: * Handle dma descriptor complete. ! 267: * Input streams overrides to check for recorded data to return. ! 268: */ ! 269: - dmaCompleteDescriptor:(dma_desc_t *)ddp transfered:(u_int)count ! 270: { ! 271: region_t *region; ! 272: queue_chain_t *region_link; ! 273: u_int used = 0; ! 274: BOOL sentAbort = NO; ! 275: ! 276: xpr_audio_stream("AS: dmaComp desc=0x%x, size=%d, xfer=%d\n", ! 277: ddp, ddp->size, count, 4,5); ! 278: ! 279: xpr_audio_stream("AS: dmaComp: locking regionQueue\n", 1,2,3,4,5); ! 280: [regionQueueLock lock]; ! 281: if (queue_empty(®ionQueue)) { ! 282: [regionQueueLock unlock]; ! 283: xpr_audio_stream("AS: dmaComp: unlocked regionQueue\n", 1,2,3,4,5); ! 284: return self; ! 285: } ! 286: ! 287: region = (region_t *)queue_first(®ionQueue); ! 288: while (!queue_end(®ionQueue, (queue_entry_t)region)) { ! 289: xpr_audio_stream("AS: dmaComplete region 0x%x\n", region, 2,3,4,5); ! 290: ! 291: if (region->start_ddp == ddp) { ! 292: xpr_audio_stream("AS: dmaComplete started region\n", 1,2,3,4,5); ! 293: if (region->messages & AS_RequestStarted) ! 294: [self sendStatusMessage:AS_ReplyStarted forRegion:region]; ! 295: region->start_ddp = 0; ! 296: } ! 297: ! 298: [self completeRegion:region descriptor:ddp size:count used:&used]; ! 299: ! 300: if ((region->end_ddp == ddp) || region->flags.aborted ! 301: || region->flags.split) { ! 302: xpr_audio_stream("AS: dmaComplete completed region\n", 1,2,3,4,5); ! 303: /* ! 304: * Old style (snd) streams get an abort message ! 305: * for every region. ! 306: */ ! 307: if (region->flags.aborted && ! 308: (region->messages & AS_RequestAborted) && ! 309: (!sentAbort || type == AS_TypeSnd)) { ! 310: [self sendStatusMessage:AS_ReplyAborted forRegion:region]; ! 311: sentAbort = YES; ! 312: } ! 313: /* ! 314: * Note snd_server interface does not allow completed messsage ! 315: * bit to be set if recording (for compatibility with old driver). ! 316: */ ! 317: if (!region->flags.aborted && !region->flags.split && ! 318: (region->messages & AS_RequestCompleted)) ! 319: [self sendStatusMessage:AS_ReplyCompleted forRegion:region]; ! 320: /* ! 321: * Free and remove this completed region. ! 322: */ ! 323: region_link = ®ion->link; ! 324: queue_remove(®ionQueue, region, region_t *, link); ! 325: [self freeRegion:region]; ! 326: region = (region_t *)queue_next(region_link); ! 327: continue; ! 328: } ! 329: if (used >= count) ! 330: break; ! 331: ! 332: region = (region_t *)queue_next(®ion->link); ! 333: } ! 334: ! 335: [regionQueueLock unlock]; ! 336: xpr_audio_stream("AS: dmaComp: unlocked regionQueue\n", 1,2,3,4,5); ! 337: return self; ! 338: } ! 339: ! 340: /* ! 341: * Subclass must implement. ! 342: */ ! 343: - (u_int)mixRegion:(region_t *)region descriptor:(dma_desc_t *)ddp ! 344: buffer:(vm_address_t)data maxCount:(u_int)max ! 345: virgin:(BOOL)isVirgin ! 346: rate:(u_int)srate format:(IOAudioDataFormat)format ! 347: channelCount:(u_int)chans ! 348: ! 349: { ! 350: log_error(("Audio: subclass does not implement mixRegion\n")); ! 351: return 0; ! 352: } ! 353: ! 354: /* ! 355: * Playback stream overrides this. ! 356: */ ! 357: - clearForMix:(char *)buf size:(u_int)count format:(IOAudioDataFormat)format ! 358: { ! 359: return self; ! 360: } ! 361: ! 362: /* ! 363: * Determine if data can be converted to current device parameters. ! 364: * Subclass overrides if it can do any conversions. ! 365: */ ! 366: - (BOOL)canConvertRegion:(region_t *)region ! 367: rate:(u_int)srate format:(IOAudioDataFormat)format ! 368: channelCount:(u_int)chans ! 369: ! 370: { ! 371: if (srate == samplingRate && ! 372: format == dataFormat && ! 373: chans == channelCount) ! 374: return YES; ! 375: else ! 376: return NO; ! 377: } ! 378: ! 379: /* ! 380: * Mix into a dma buffer. ! 381: */ ! 382: - (u_int)mixBuffer:(vm_address_t)data maxCount:(u_int)max ! 383: rate:(u_int *)srate format:(IOAudioDataFormat *)format ! 384: channelCount:(u_int *)chans descriptor:(dma_desc_t *)ddp ! 385: virgin:(BOOL)isVirgin streamCount:(u_int)streams ! 386: { ! 387: region_t *region; ! 388: u_int count = 0; ! 389: u_int mixCount; ! 390: u_int temp_align_buffer; ! 391: ! 392: if (isPaused) { ! 393: xpr_audio_stream("AS: mixBuffer: stream is paused\n", 1,2,3,4,5); ! 394: return 0; ! 395: } ! 396: ! 397: xpr_audio_stream("AS: mixBuffer: locking regionQueue\n", 1,2,3,4,5); ! 398: [regionQueueLock lock]; ! 399: if (queue_empty(®ionQueue)) { ! 400: [regionQueueLock unlock]; ! 401: xpr_audio_stream("AS: mixBuffer: unlocked regionQueue\n", 1,2,3,4,5); ! 402: return 0; ! 403: } ! 404: ! 405: region = (region_t *)queue_first(®ionQueue); ! 406: while (!queue_end(®ionQueue, (queue_entry_t)region)) { ! 407: xpr_audio_stream("AS: mixBuffer: mix region 0x%x\n", region, 2,3,4,5); ! 408: /* ! 409: * Skip completed or aborted region. ! 410: */ ! 411: if ((region->enq_ptr >= region->end) || region->flags.aborted) { ! 412: region = (region_t *)queue_next(®ion->link); ! 413: continue; ! 414: } ! 415: ! 416: if (*srate == 0) ! 417: *srate = samplingRate; ! 418: if (*format == IOAudioDataFormatUnset) ! 419: *format = dataFormat; ! 420: if (*chans == 0) ! 421: *chans = channelCount; ! 422: ! 423: /* ! 424: * FIXME: abort/deq if can't convert? ! 425: */ ! 426: if (![self canConvertRegion:region rate:*srate ! 427: format:*format channelCount: *chans]) { ! 428: xpr_audio_stream("AS: mixBuffer: mix region can not convert to " ! 429: "srate=%d, " ! 430: "fmt=%d chans=%d\n", *srate, *format, *chans, ! 431: 4,5); ! 432: region = (region_t *)queue_next(®ion->link); ! 433: continue; ! 434: } ! 435: ! 436: temp_align_buffer = (data + count); ! 437: ! 438: #if defined(sparc) || defined(hppa) ! 439: if ((dataFormat == IOAudioDataFormatLinear16) && (temp_align_buffer & 1)) ! 440: temp_align_buffer &= 0xfffffffe; ! 441: ! 442: #endif ! 443: ! 444: mixCount = [self mixRegion:region descriptor:ddp ! 445: buffer:(temp_align_buffer) ! 446: maxCount:(max - count) virgin:isVirgin ! 447: rate:*srate format:*format ! 448: channelCount:*chans]; ! 449: ! 450: if (!region->flags.started) { ! 451: region->start_ddp = ddp; ! 452: region->flags.started = TRUE; ! 453: } ! 454: if (region->enq_ptr >= region->end && !region->flags.ended) { ! 455: region->end_ddp = ddp; ! 456: region->flags.ended = TRUE; ! 457: } ! 458: ! 459: xpr_audio_stream("AS: mixBuffer: enq_ptr=0x%x\n", region->enq_ptr, ! 460: 2,3,4,5); ! 461: count += mixCount; ! 462: if (count >= max) ! 463: break; ! 464: region = (region_t *)queue_next(®ion->link); ! 465: } ! 466: ! 467: [regionQueueLock unlock]; ! 468: xpr_audio_stream("AS: mixBuffer: unlocked regionQueue\n", 1,2,3,4,5); ! 469: /* ! 470: * First playback mix needs to clear buffer to end. ! 471: */ ! 472: if (isVirgin && (count < max)) ! 473: [self clearForMix:(char *)(data + count) size:(max - count) ! 474: format: *format]; ! 475: ! 476: return count; ! 477: } ! 478: ! 479: /* ! 480: * Mark current regions as aborted. ! 481: * Return YES if any region marked. ! 482: */ ! 483: - (BOOL)markAbortionsExclude:(BOOL)excluded ! 484: { ! 485: region_t *region; ! 486: BOOL ret = NO; ! 487: ! 488: xpr_audio_stream("AS: markAbortions: locking regionQueue\n", 1,2,3,4,5); ! 489: [regionQueueLock lock]; ! 490: if (queue_empty(®ionQueue)) { ! 491: [regionQueueLock unlock]; ! 492: xpr_audio_stream("AS: markAbortions: unlocked regionQueue\n", ! 493: 1,2,3,4,5); ! 494: return ret; ! 495: } ! 496: ! 497: region = (region_t *)queue_first(®ionQueue); ! 498: while (!queue_end(®ionQueue, (queue_entry_t)region)) { ! 499: region->flags.aborted = TRUE; ! 500: region->flags.excluded = excluded; ! 501: ret = YES; ! 502: xpr_audio_stream("AS: markAbortions: region=0x%x, excl=%d\n", ! 503: region, excluded, 3,4,5); ! 504: region = (region_t *)queue_next(®ion->link); ! 505: } ! 506: ! 507: [regionQueueLock unlock]; ! 508: xpr_audio_stream("AS: markAbortions: unlocked regionQueue\n", 1,2,3,4,5); ! 509: return ret; ! 510: } ! 511: ! 512: /* ! 513: * Subtract timevals. ! 514: */ ! 515: static void subtract_times(struct timeval *t1, struct timeval *t2) ! 516: { ! 517: t1->tv_sec -= t2->tv_sec; ! 518: t1->tv_usec -= t2->tv_usec; ! 519: if (t1->tv_usec < 0) { ! 520: t1->tv_sec--; ! 521: t1->tv_usec += 1000000; ! 522: } ! 523: } ! 524: ! 525: /* ! 526: * Thread to send timed controls. ! 527: * One each launched for pause, resume, and abort. ! 528: */ ! 529: static void controlThread(void *arg) ! 530: { ! 531: port_t listenPort; ! 532: kern_return_t krtn; ! 533: control_msg_t *controlMsg = ! 534: (control_msg_t *)IOMalloc(sizeof(control_msg_t)); ! 535: int timeout = 0; ! 536: AudioStream *stream = nil; ! 537: ACStreamControl action = 0; ! 538: struct timeval now, when; ! 539: #ifndef KERNEL ! 540: struct timezone tz; ! 541: #endif KERNEL ! 542: ! 543: #ifdef KERNEL ! 544: listenPort = (port_t)threadArg; ! 545: [threadArgLock unlock]; ! 546: #else KERNEL ! 547: listenPort = (port_t)arg; ! 548: #endif KERNEL ! 549: ! 550: xpr_audio_stream("AS: controlThread: port=0x%x\n", listenPort, 2,3,4,5); ! 551: ! 552: while (1) { ! 553: controlMsg->header.msg_local_port = listenPort; ! 554: controlMsg->header.msg_size = sizeof(control_msg_t); ! 555: if (timeout > 0) ! 556: krtn = msg_receive(&controlMsg->header, RCV_TIMEOUT, timeout); ! 557: else ! 558: krtn = msg_receive(&controlMsg->header, MSG_OPTION_NONE, 0); ! 559: /* ! 560: * Timeout elapsed, perform stream control action. ! 561: */ ! 562: if (krtn == RCV_TIMED_OUT) { ! 563: [stream control:action]; ! 564: timeout = 0; ! 565: continue; ! 566: } ! 567: /* ! 568: * Exit when port goes stale. ! 569: */ ! 570: if (krtn != KERN_SUCCESS) { ! 571: xpr_audio_stream("AS: controlThread msg_rcv error %d\n", krtn, ! 572: 2,3,4,5); ! 573: break; ! 574: } ! 575: /* ! 576: * Request for action, calculate timeout. ! 577: */ ! 578: if (controlMsg->header.msg_id != AC_CONTROL_THREAD_MSG) { ! 579: xpr_audio_stream("AS: controlThread bad msg id=%d\n", ! 580: controlMsg->header.msg_id, 2,3,4,5); ! 581: break; ! 582: } ! 583: stream = (AudioStream *)controlMsg->obj; ! 584: action = controlMsg->action; ! 585: when = *controlMsg->when; ! 586: #ifdef KERNEL ! 587: microtime(&now); ! 588: #else KERNEL ! 589: gettimeofday(&now, &tz); ! 590: #endif KERNEL ! 591: subtract_times(&when, &now); ! 592: timeout = when.tv_sec * 1000; ! 593: timeout += when.tv_usec / 1000; ! 594: xpr_audio_stream("AS: controlThread timeout = %d\n", timeout, ! 595: 2,3,4,5); ! 596: if (timeout <= 0) { ! 597: [stream control:action]; ! 598: timeout = 0; ! 599: } ! 600: } ! 601: xpr_audio_stream("AS: controlThread: port=0x%x exits\n", listenPort, ! 602: 2,3,4,5); ! 603: IOExitThread(); ! 604: } ! 605: ! 606: /* ! 607: * Timed stream control. ! 608: */ ! 609: static void timedControl(port_t *threadPort, id self, ACStreamControl action, ! 610: struct timeval *when) ! 611: { ! 612: static const control_msg_t msg = { ! 613: { ! 614: 0, /* msg_unused */ ! 615: TRUE, /* msg_simple */ ! 616: sizeof(control_msg_t), /* msg_size */ ! 617: MSG_TYPE_NORMAL, /* msg_type */ ! 618: PORT_NULL, /* msg_local_port */ ! 619: PORT_NULL, /* msg_remote_port */ ! 620: AC_CONTROL_THREAD_MSG /* msg_id */ ! 621: }, ! 622: { ! 623: MSG_TYPE_INTEGER_32, /* msg_type_name */ ! 624: 32, /* msg_type_size */ ! 625: 3, /* msg_type_number */ ! 626: FALSE, /* msg_type_longform */ ! 627: FALSE /* msg_type_deallocate */ ! 628: }, ! 629: 0, ! 630: 0, ! 631: 0 ! 632: }; ! 633: control_msg_t *controlMsg = ! 634: (control_msg_t *)IOMalloc(sizeof(control_msg_t)); ! 635: kern_return_t krtn; ! 636: ! 637: if (!(*threadPort)) { ! 638: krtn = port_allocate(task_self(), threadPort); ! 639: if (krtn) { ! 640: log_error(("Audio: stream control thread port_allocate: %s\n", ! 641: mach_error_string(krtn))); ! 642: audio_panic(); ! 643: } ! 644: #ifdef KERNEL ! 645: if (threadArgLock == nil) ! 646: threadArgLock = [[NXLock alloc] init]; ! 647: [threadArgLock lock]; ! 648: threadArg = (void *)*threadPort; ! 649: #ifdef DEBUG ! 650: IOLog("threadArg = 0x%x\n", threadArg); ! 651: #endif DEBUG ! 652: kernel_thread(current_task_EXTERNAL(), (void *)controlThread); ! 653: #else KERNEL ! 654: (void)IOForkThread((IOThreadFunc)controlThread, (void *)*threadPort); ! 655: #endif ! 656: } ! 657: *controlMsg = msg; ! 658: controlMsg->header.msg_remote_port = *threadPort; ! 659: controlMsg->obj = self; ! 660: controlMsg->action = action; ! 661: controlMsg->when = when; ! 662: ! 663: krtn = msg_send(&controlMsg->header, SEND_TIMEOUT, 1000); ! 664: if (krtn != KERN_SUCCESS) { ! 665: xpr_audio_stream("AS: timedControl msg_send error %s (%d)\n", ! 666: mach_error_string(krtn), krtn, 3,4,5); ! 667: } ! 668: } ! 669: ! 670: /* ! 671: * Stream control. ! 672: */ ! 673: - control:(ACStreamControl)action atTime:(struct timeval)when ! 674: { ! 675: switch (action) { ! 676: case AC_ControlPause: ! 677: if (timerisset(&when)) { ! 678: pauseTime = when; ! 679: timedControl(&pausePort, self, action, &pauseTime); ! 680: } else { ! 681: isPaused = YES; ! 682: [self sendControlMessage:AS_ReplyPaused mask:AS_RequestPaused]; ! 683: } ! 684: break; ! 685: case AC_ControlResume: ! 686: if (timerisset(&when)) { ! 687: resumeTime = when; ! 688: timedControl(&resumePort, self, action, &resumeTime); ! 689: } else { ! 690: isPaused = NO; ! 691: [self sendControlMessage:AS_ReplyResumed mask:AS_RequestResumed]; ! 692: [device _dataPendingForChannel: [self channel]]; ! 693: } ! 694: break; ! 695: case AC_ControlAbort: ! 696: if (timerisset(&when)) { ! 697: abortTime = when; ! 698: timedControl(&abortPort, self, action, &abortTime); ! 699: } else if (![self markAbortionsExclude:NO]) { ! 700: /* ! 701: * Send reply messages now if no regions to abort. ! 702: */ ! 703: [self sendControlMessage:AS_ReplyAborted mask:AS_RequestAborted]; ! 704: } ! 705: break; ! 706: case AC_ControlExclude: ! 707: [self markAbortionsExclude:YES]; ! 708: break; ! 709: default: ! 710: log_error(("audio: unrecognized stream control %d\n", action)); ! 711: break; ! 712: } ! 713: return self; ! 714: } ! 715: ! 716: /* ! 717: * Stream control. ! 718: */ ! 719: - control:(ACStreamControl)action ! 720: { ! 721: struct timeval now; ! 722: ! 723: timerclear(&now); ! 724: return [self control:action atTime:now]; ! 725: } ! 726: ! 727: /* ! 728: * InputStream overrides. ! 729: */ ! 730: - returnRecordedData ! 731: { ! 732: return self; ! 733: } ! 734: ! 735: /* ! 736: * Free regions and region queue. ! 737: */ ! 738: - freeRegions ! 739: { ! 740: region_t *region; ! 741: ! 742: xpr_audio_stream("AS: free regions\n", 1,2,3,4,5); ! 743: while (!queue_empty(®ionQueue)) { ! 744: queue_remove_first(®ionQueue, region, region_t *, link); ! 745: [self freeRegion:region]; ! 746: } ! 747: return self; ! 748: } ! 749: ! 750: /* ! 751: * Allocate and init a region. ! 752: */ ! 753: - (region_t *)newRegion ! 754: { ! 755: region_t *region = (region_t *)IOMalloc(sizeof(region_t)); ! 756: ! 757: bzero((char *)region, sizeof(region_t)); ! 758: return region; ! 759: } ! 760: ! 761: /* ! 762: * Free resources and object. ! 763: */ ! 764: - free ! 765: { ! 766: kern_return_t krtn; ! 767: ! 768: if (pausePort) ! 769: port_deallocate(task_self(), pausePort); ! 770: if (resumePort) ! 771: port_deallocate(task_self(), resumePort); ! 772: if (abortPort) ! 773: port_deallocate(task_self(), abortPort); ! 774: [self freeRegions]; ! 775: krtn = port_deallocate(task_self(), userPort); ! 776: if (krtn) ! 777: log_error(("Audio: stream port_deallocate: %s\n", ! 778: mach_error_string(krtn))); ! 779: [regionQueueLock free]; ! 780: if (sndReplyMsg) ! 781: IOFree(sndReplyMsg, MSG_SIZE_MAX); ! 782: return [super free]; ! 783: } ! 784: ! 785: /* ! 786: * Set stream owner port. ! 787: */ ! 788: - setOwner:(port_t)newOwner ! 789: { ! 790: ownerPort = newOwner; ! 791: return self; ! 792: } ! 793: ! 794: /* ! 795: * This is dependent upon the machine representation of ns_time_t. ! 796: */ ! 797: ! 798: - (BOOL)bytesProcessed:(unsigned int *)num atTime:(unsigned int *)ts ! 799: { ! 800: ns_time_t interruptTime, startTime; ! 801: ! 802: startTime = [device _outputStartTime]; ! 803: interruptTime = [device _lastInterruptTimeStamp]; ! 804: ! 805: /* Almost certainly the DMA hasn't started yet. */ ! 806: if (interruptTime == 0) { ! 807: *num = *ts = 0; ! 808: return NO; ! 809: } ! 810: ! 811: interruptTime /= 1000; // microseconds ! 812: startTime /= 1000; ! 813: ! 814: xpr_audio_stream("AS: byteCount %d at %d\n", ! 815: bytesProcessed, interruptTime,3,4,5); ! 816: ! 817: if (ts != NULL) ! 818: *ts = (unsigned int) interruptTime; ! 819: *num = bytesProcessed; ! 820: ! 821: return YES; ! 822: } ! 823: ! 824: - (NXSoundParameterTag)dataEncoding ! 825: { ! 826: /* FIXME: change dataFormat to dataEncoding */ ! 827: ! 828: switch (dataFormat) { ! 829: case IOAudioDataFormatLinear16: ! 830: return NX_SoundStreamDataEncoding_Linear16; ! 831: case IOAudioDataFormatLinear8: ! 832: return NX_SoundStreamDataEncoding_Linear8; ! 833: case IOAudioDataFormatMulaw8: ! 834: return NX_SoundStreamDataEncoding_Mulaw8; ! 835: case IOAudioDataFormatAlaw8: ! 836: return NX_SoundStreamDataEncoding_Alaw8; ! 837: default: ! 838: IOLog("Audio: unrecognized data format: %d\n", dataFormat); ! 839: return -1; ! 840: } ! 841: } ! 842: - (void)setDataEncoding:(NXSoundParameterTag)encoding ! 843: { ! 844: /* FIXME: change dataFormat to dataEncoding */ ! 845: ! 846: switch (encoding) { ! 847: case NX_SoundStreamDataEncoding_Linear16: ! 848: dataFormat = IOAudioDataFormatLinear16; ! 849: break; ! 850: case NX_SoundStreamDataEncoding_Linear8: ! 851: dataFormat = IOAudioDataFormatLinear8; ! 852: break; ! 853: case NX_SoundStreamDataEncoding_Mulaw8: ! 854: dataFormat = IOAudioDataFormatMulaw8; ! 855: break; ! 856: case NX_SoundStreamDataEncoding_Alaw8: ! 857: dataFormat = IOAudioDataFormatAlaw8; ! 858: break; ! 859: case NX_SoundStreamDataEncoding_AES: ! 860: dataFormat = IOAudioDataFormatAES; ! 861: break; ! 862: default: ! 863: IOLog("Audio: supported encoding: %d\n", encoding); ! 864: break; ! 865: } ! 866: } ! 867: - (u_int)samplingRate ! 868: { ! 869: return samplingRate; ! 870: } ! 871: - (void)setSamplingRate:(u_int)rate ! 872: { ! 873: samplingRate = rate; ! 874: } ! 875: - (u_int)channelCount ! 876: { ! 877: return channelCount; ! 878: } ! 879: - (void)setChannelCount:(u_int)count ! 880: { ! 881: channelCount = count; ! 882: } ! 883: - (u_int)lowWaterMark ! 884: { ! 885: return 0; ! 886: } ! 887: - (void)setLowWaterMark:(u_int)count ! 888: { ! 889: return; ! 890: } ! 891: - (u_int)highWaterMark ! 892: { ! 893: return 0; ! 894: } ! 895: - (void)setHighWaterMark:(u_int)count ! 896: { ! 897: return; ! 898: } ! 899: ! 900: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.