Annotation of driverkit/libDriver/Kernel/AudioStream.m, revision 1.1

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(&regionQueue);
        !            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(&regionQueue)) {
        !           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(&regionQueue);
        !           177:     while (!queue_end(&regionQueue, (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(&region->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(&regionQueue)) {
        !           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(&regionQueue);
        !           288:     while (!queue_end(&regionQueue, (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 = &region->link;
        !           324:            queue_remove(&regionQueue, 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(&region->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(&regionQueue)) {
        !           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(&regionQueue);
        !           406:     while (!queue_end(&regionQueue, (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(&region->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(&region->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(&region->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(&regionQueue)) {
        !           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(&regionQueue);
        !           498:     while (!queue_end(&regionQueue, (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(&region->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(&regionQueue)) {
        !           744:        queue_remove_first(&regionQueue, 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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.