Annotation of driverkit/libDriver/Kernel/AudioStream.m, revision 1.1.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.