|
|
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: * InputStream.m ! 26: * ! 27: * Copyright (c) 1991, NeXT Computer, Inc. All rights reserved. ! 28: * ! 29: * Audio driver input stream object. ! 30: * ! 31: * HISTORY ! 32: * 07/14/92/mtm Original coding. ! 33: */ ! 34: ! 35: #import <mach/mach_types.h> ! 36: #import <kern/lock.h> ! 37: #import <mach/mach_user_internal.h> ! 38: #import <mach/mach_interface.h> ! 39: #import <kernserv/kern_server_types.h> ! 40: #import <kernserv/prototypes.h> ! 41: #import "InputStream.h" ! 42: #import <driverkit/IOAudioPrivate.h> ! 43: #import "snd_reply.h" ! 44: #import "audioLog.h" ! 45: #import "audioReply.h" ! 46: #import <mach/vm_param.h> // PAGE_SIZE ! 47: #import <architecture/byte_order.h> ! 48: #import <driverkit/kernelDriver.h> ! 49: ! 50: @implementation InputStream ! 51: ! 52: /* ! 53: * Enqueue buffer to region queue. ! 54: */ ! 55: - (BOOL)recordSize:(u_int)byteCount tag:(int)aTag ! 56: replyTo:(port_t)replyPort ! 57: replyMsgs:(ASMsgRequest)messages ! 58: { ! 59: region_t *region; ! 60: kern_return_t krtn; ! 61: port_t kernReplyPort; ! 62: vm_address_t kmem; ! 63: vm_task_t kernelTask = (vm_task_t)kern_serv_kernel_task_port(); ! 64: ! 65: kernReplyPort = IOConvertPort(replyPort, IO_CurrentTask, IO_Kernel); ! 66: ! 67: /* ! 68: * Truncate count if necessary. ! 69: */ ! 70: if (dataFormat == IOAudioDataFormatLinear16) ! 71: byteCount &= (channelCount == 1 ? ~(2-1) : ~(4-1)); ! 72: ! 73: /* ! 74: * Allocate memory in the kernel map. ! 75: */ ! 76: krtn = vm_allocate(kernelTask, &kmem, byteCount, TRUE); ! 77: if (krtn != KERN_SUCCESS) { ! 78: IOLog("Audio: record request (%d bytes) too large\n", byteCount); ! 79: return NO; ! 80: } ! 81: ! 82: region = [self newRegion]; ! 83: region->enq_ptr = region->record_ptr = region->data = kmem; ! 84: region->end = region->data + byteCount; ! 85: region->count = byteCount; ! 86: region->tag = aTag; ! 87: region->reply_port = kernReplyPort; ! 88: region->messages = messages; ! 89: ! 90: /* ! 91: * Some reply messages are per stream in NXSound interface. ! 92: * FIXME: these only get set if data sent to stream. ! 93: */ ! 94: userReplyMessages = messages; ! 95: userReplyPort = kernReplyPort; ! 96: ! 97: xpr_audio_stream("IS: locking regionQueue\n", 1,2,3,4,5); ! 98: [regionQueueLock lock]; ! 99: queue_enter(®ionQueue, region, region_t *, link); ! 100: [regionQueueLock unlock]; ! 101: xpr_audio_stream("IS: unlocked regionQueue\n", 1,2,3,4,5); ! 102: [device _dataPendingForChannel: [self channel]]; ! 103: return YES; ! 104: } ! 105: ! 106: /* ! 107: * Return count of bytes required for this region. ! 108: */ ! 109: - (u_int)mixRegion:(region_t *)region descriptor:(dma_desc_t *)ddp ! 110: buffer:(vm_address_t)data maxCount:(u_int)max ! 111: virgin:(BOOL)isVirgin rate:(u_int)srate format:(IOAudioDataFormat)format ! 112: channelCount:(u_int)chans ! 113: { ! 114: u_int count = region->end - region->enq_ptr; ! 115: ! 116: /* FIXME ! 117: * Crystal chip sends 4 bytes per sample, even in mono 8-bit mode. ! 118: */ ! 119: // max /= 4; ! 120: if (count > max) ! 121: count = max; ! 122: region->enq_ptr += count; ! 123: xpr_audio_stream("IS: requested %d bytes\n", count, 2,3,4,5); ! 124: return count; ! 125: } ! 126: ! 127: /* ! 128: * Read kernel memory into our task. ! 129: */ ! 130: static vm_address_t readFromKernel(vm_address_t kmem, u_int size) ! 131: { ! 132: vm_address_t data; ! 133: kern_return_t kerr; ! 134: unsigned int count; ! 135: vm_address_t kmem_page = trunc_page(kmem); ! 136: unsigned int offset = kmem - kmem_page; ! 137: vm_task_t kernelTask = (vm_task_t)kern_serv_kernel_task_port(); ! 138: ! 139: /* ! 140: * Note: kmem is normally on a page boundry because we get ! 141: * it from vm_allocate. This function, however, handles the ! 142: * more general case of an offset. ! 143: */ ! 144: ! 145: /* ! 146: * Read data from kernel map ! 147: * (vm_read allocates memory for us). ! 148: */ ! 149: kerr = vm_read(kernelTask, kmem_page, round_page(size + offset), ! 150: (pointer_t *)&data, &count); ! 151: if (kerr != KERN_SUCCESS) { ! 152: IOLog("Audio: vm_read returned %d\n", kerr); ! 153: //IOPanic("Audio: vm_read\n"); ! 154: } ! 155: /* ! 156: * Return offset of the data in our task. ! 157: */ ! 158: return data + offset; ! 159: } ! 160: ! 161: /* ! 162: * Send recorded data to user. ! 163: */ ! 164: - sendRecordedDataForRegion:(region_t *)region ! 165: { ! 166: kern_return_t krtn; ! 167: u_int count; ! 168: vm_address_t data; ! 169: port_t ourReplyPort, ourUserPort; ! 170: ! 171: count = region->record_ptr - region->data; ! 172: ! 173: data = readFromKernel(region->data, count); ! 174: xpr_audio_stream("IS: sendRecordedDataForRegion 0x%x, count=%d\n", ! 175: region, count, 3,4,5); ! 176: if (count == 0) { ! 177: log_debug(("Audio: no data recorded for region!\n")); ! 178: return self; ! 179: } ! 180: ! 181: ourReplyPort = IOConvertPort(region->reply_port, IO_Kernel, ! 182: IO_KernelIOTask); ! 183: ourUserPort = IOConvertPort(kernUserPort, IO_Kernel, IO_KernelIOTask); ! 184: ! 185: if (type == AS_TypeUser) { ! 186: krtn = _NXAudioReplyRecordedData(ourReplyPort, ourUserPort, ! 187: ourReplyPort, tag, ! 188: region->tag, ! 189: data, count); ! 190: if (krtn != KERN_SUCCESS) { ! 191: xpr_audio_stream("IS: sendRecData replyRecData error %d\n", ! 192: krtn, 2,3,4,5); ! 193: } ! 194: } else { ! 195: if (!sndReplyMsg) { ! 196: sndReplyMsg = (msg_header_t *)IOMalloc(MSG_SIZE_MAX); ! 197: sndReplyMsg->msg_simple = TRUE; ! 198: sndReplyMsg->msg_size = sizeof(msg_header_t); ! 199: sndReplyMsg->msg_type = MSG_TYPE_NORMAL; ! 200: sndReplyMsg->msg_local_port = PORT_NULL; ! 201: sndReplyMsg->msg_remote_port = PORT_NULL; ! 202: sndReplyMsg->msg_id = 0; ! 203: } ! 204: audio_snd_reply_recorded_data(sndReplyMsg, ourReplyPort, region->tag, ! 205: data, count); ! 206: krtn = msg_send(sndReplyMsg, STREAM_SEND_OPTIONS, STREAM_SEND_TIMEOUT); ! 207: if (krtn != KERN_SUCCESS) { ! 208: xpr_audio_stream("IS: sendRecData msg_send error %s (%d)\n", ! 209: mach_error_string(krtn), krtn, 3,4,5); ! 210: } ! 211: } ! 212: return self; ! 213: } ! 214: ! 215: /* ! 216: * Copy recorded data to region. ! 217: */ ! 218: - completeRegion:(region_t *)region descriptor:(dma_desc_t *)ddp ! 219: size:(u_int)xfer used:(u_int *)used ! 220: { ! 221: u_int needed = region->enq_ptr - region->record_ptr; ! 222: int i; ! 223: short *src, *dest; ! 224: ! 225: #ifdef DEBUG ! 226: if (xfer < ddp->size) { ! 227: xpr_audio_stream("IS: xfer (%d) < ddp->size (%d)\n", xfer, ddp->size, ! 228: 3,4,5); ! 229: log_debug(("Audio: recorded %d bytes, wanted %d\n", xfer, ddp->size)); ! 230: } ! 231: #endif DEBUG ! 232: ! 233: if ((xfer > 0) && (needed > 0)) { ! 234: xpr_audio_stream("IS: completeRegion needs %d bytes\n", needed, ! 235: 2,3,4,5); ! 236: if (*used + needed > xfer) ! 237: needed = xfer - *used; ! 238: xpr_audio_stream("IS: completeRegion copies %d bytes from 0x%x " ! 239: "to 0x%x\n", needed, ddp->mem + *used, ! 240: region->record_ptr, 4,5); ! 241: /* ! 242: * Swap in place. ! 243: */ ! 244: src = (short *)(ddp->mem + *used); ! 245: dest = (short *)region->record_ptr; ! 246: if (dataFormat == IOAudioDataFormatLinear16) { ! 247: for (i = 0; i < needed / 2; i++) ! 248: *dest++ = NXSwapHostShortToBig(*src++); ! 249: } else if (dataFormat == IOAudioDataFormatLinear8) { /* && isUnary */ ! 250: char *csrc = (char *)src; ! 251: char *cdest = (char *)dest; ! 252: for (i = 0; i < needed; i++) { ! 253: /* convert to 2's comp. by xor'ing the high bit */ ! 254: *cdest++ = (*csrc ^ 0x80) | (*csrc & 0x7f); ! 255: csrc++; ! 256: } ! 257: } else if (dataFormat == IOAudioDataFormatMulaw8) ! 258: bcopy ((char *)src, (char *)dest, needed); ! 259: *used += needed; ! 260: region->record_ptr += needed; ! 261: bytesProcessed += needed; ! 262: } ! 263: /* ! 264: * Return recorded data on last ddp even if not all data was recorded. ! 265: */ ! 266: if ((region->end_ddp == ddp) || region->flags.split) ! 267: [self sendRecordedDataForRegion:region]; ! 268: return self; ! 269: } ! 270: ! 271: /* ! 272: * Split current region and return data recorded so far. ! 273: */ ! 274: - returnRecordedData ! 275: { ! 276: region_t *region = 0, *newRegion; ! 277: BOOL found = NO; ! 278: kern_return_t krtn; ! 279: int completedSize, remainingSize, enqueuedSize; ! 280: vm_task_t kernelTask = (vm_task_t)kern_serv_kernel_task_port(); ! 281: ! 282: xpr_audio_stream("IS: locking regionQueue\n", 1,2,3,4,5); ! 283: [regionQueueLock lock]; ! 284: /* ! 285: * Find the first non-completed region. ! 286: */ ! 287: if (!queue_empty(®ionQueue)) { ! 288: region = (region_t *)queue_first(®ionQueue); ! 289: while (!queue_end(®ionQueue, (queue_entry_t)region)) { ! 290: if ((region->record_ptr > region->data) && ! 291: (region->record_ptr < region->end)) { ! 292: xpr_audio_stream("IS: returnRecData: region 0x%x\n", region, ! 293: 2,3,4,5); ! 294: found = YES; ! 295: break; ! 296: } ! 297: region = (region_t *)queue_next(®ion->link); ! 298: } ! 299: } ! 300: if (!found) { ! 301: [regionQueueLock unlock]; ! 302: xpr_audio_stream("IS: unlocked regionQueue\n", 1,2,3,4,5); ! 303: xpr_audio_stream("IS: returnRecData: no region to split\n", 1,2,3,4,5); ! 304: wantsRecordedData = YES; ! 305: return self; ! 306: } ! 307: ! 308: newRegion = (region_t *)IOMalloc(sizeof(region_t)); ! 309: *newRegion = *region; ! 310: completedSize = region->record_ptr - region->data; ! 311: remainingSize = region->count - completedSize; ! 312: enqueuedSize = region->enq_ptr - region->record_ptr; ! 313: xpr_audio_stream("IS: returnRecData: complete=%d, remain=%d " ! 314: "enq=%d\n", completedSize, remainingSize, ! 315: enqueuedSize, 4,5); ! 316: ! 317: /* ! 318: * Copy recorded data to a new region, free original data, ! 319: * and allocate new data for original region. ! 320: */ ! 321: krtn = vm_allocate(kernelTask, &newRegion->data, completedSize, TRUE); ! 322: if (krtn != KERN_SUCCESS) { ! 323: IOLog("Audio: cannot allocate record memory\n"); ! 324: IOFree(newRegion, sizeof(region_t)); ! 325: [regionQueueLock unlock]; ! 326: wantsRecordedData = YES; ! 327: return self; ! 328: } ! 329: ! 330: region->start_ddp = 0; ! 331: region->flags.started = FALSE; ! 332: ! 333: bcopy((char *)region->data, (char *)newRegion->data, completedSize); ! 334: ! 335: krtn = vm_deallocate(kernelTask, region->data, region->count); ! 336: if (krtn != KERN_SUCCESS) ! 337: log_error(("Audio: vm_deallocate: %s\n", mach_error_string(krtn))); ! 338: ! 339: krtn = vm_allocate(kernelTask, ®ion->data, remainingSize, TRUE); ! 340: if (krtn != KERN_SUCCESS) { ! 341: IOLog("Audio: cannot allocate record memory\n"); ! 342: remainingSize = enqueuedSize = 0; ! 343: } ! 344: ! 345: region->count = remainingSize; ! 346: region->end = region->data + region->count; ! 347: region->record_ptr = region->data; ! 348: region->enq_ptr = region->data + enqueuedSize; ! 349: ! 350: /* ! 351: * Enter the new, completed region at the front of the queue. ! 352: */ ! 353: newRegion->count = completedSize; ! 354: newRegion->end = newRegion->data + newRegion->count; ! 355: newRegion->record_ptr = newRegion->enq_ptr = newRegion->end; ! 356: newRegion->flags.ended = newRegion->flags.split = TRUE; ! 357: queue_enter_first(®ionQueue, newRegion, region_t *, link); ! 358: ! 359: [regionQueueLock unlock]; ! 360: xpr_audio_stream("IS: unlocked regionQueue\n", 1,2,3,4,5); ! 361: wantsRecordedData = NO; ! 362: return self; ! 363: } ! 364: ! 365: /* ! 366: * Override from superclass to see if we need to return recorded data. ! 367: */ ! 368: - dmaCompleteDescriptor:(dma_desc_t *)ddp transfered:(u_int)count ! 369: { ! 370: [super dmaCompleteDescriptor:ddp transfered:count]; ! 371: ! 372: if (wantsRecordedData) ! 373: [self returnRecordedData]; ! 374: return self; ! 375: } ! 376: ! 377: /* ! 378: * Free a region. ! 379: */ ! 380: - freeRegion:(region_t *)region ! 381: { ! 382: kern_return_t krtn; ! 383: ! 384: log_debug(("IS: freeRegion dealloc %d bytes at 0x%x\n", region->count, ! 385: region->data)); ! 386: /* ! 387: * Must get a fresh kernel task port here since ! 388: * we are in the IO task. ! 389: */ ! 390: krtn = vm_deallocate((vm_task_t)kern_serv_kernel_task_port(), ! 391: region->data, region->count); ! 392: if (krtn != KERN_SUCCESS) ! 393: log_error(("Audio: stream vm_deallocate error %s\n", ! 394: mach_error_string(krtn))); ! 395: return [super freeRegion:region]; ! 396: } ! 397: ! 398: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.