Source to iokit/Families/IOAudio/IOAudioParts.cpp
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
*
* IOAudioDevice.cpp
*
* HISTORY
*
*/
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOCommandQueue.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/audio/IOAudioController.h>
//************************************************************************
// Implementation of protocol classes.
//************************************************************************
OSDefineMetaClass( IOAudioStream, IOUserClient )
OSDefineAbstractStructors( IOAudioStream, IOUserClient )
OSDefineMetaClass( IOAudioComponent, IOUserClient )
OSDefineAbstractStructors( IOAudioComponent, IOUserClient )
//************************************************************************
// Begin implementation of IOAudioStreamImpl class.
//************************************************************************
#undef super
#define super IOAudioStream
OSDefineMetaClassAndStructors( IOAudioStreamImpl, IOAudioStream )
bool
IOAudioStreamImpl::initWithPropsIndexQueue(OSDictionary *props,
AudioStreamIndex index, IOCommandQueue *queue)
{
if(!super::init(props))
return false;
if(!queue)
return false;
fIndex = index;
fCmdQueue = queue;
fMappedMem.fMixBuffer = NULL;
// make sure essential stuff is there
assert(OSDynamicCast(OSNumber, getProperty("In")) != NULL);
assert(OSDynamicCast(OSNumber, getProperty("Out")) != NULL);
fMethods[kCallSetFlow].object = this;
fMethods[kCallSetFlow].func = (IOMethod) &IOAudioStream::setFlow;
fMethods[kCallSetFlow].count0 = 1;
fMethods[kCallSetFlow].count1 = 0;
fMethods[kCallSetFlow].flags = kIOUCScalarIScalarO;
fMethods[kCallFlush].object = this;
fMethods[kCallFlush].func = (IOMethod) &IOAudioStream::Flush;
fMethods[kCallFlush].count0 = sizeof(IOAudioStreamPosition);
fMethods[kCallFlush].count1 = 0;
fMethods[kCallFlush].flags = kIOUCStructIStructO;
fMethods[kCallSetErase].object = this;
fMethods[kCallSetErase].func = (IOMethod) &IOAudioStream::setErase;
fMethods[kCallSetErase].count0 = 1;
fMethods[kCallSetErase].count1 = 1;
fMethods[kCallSetErase].flags = kIOUCScalarIScalarO;
return true;
}
void IOAudioStreamImpl::free()
{
if (fMappedMem.fMixBuffer) {
// Need to free mix buffer here - it will leak now
}
super::free();
}
/*
* Connect a client, possibly starting DMA for the stream if it's
* the first client.
*/
IOReturn
IOAudioStreamImpl::newUserClient( task_t owningTask, void * security_id,
UInt32 type, IOUserClient ** handler )
{
IOSyncer *syncWakeup;
*handler = this;
(*handler)->retain();
syncWakeup = IOSyncer::create();
fCmdQueue->enqueueCommand(true, syncWakeup, (void *)kConnect,
(void *)fIndex, &fMappedMem);
syncWakeup->wait();
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::clientDied()
{
/*
* Decrememt connection count, possibly freeing DMA buffers
*/
fCmdQueue->enqueueCommand(true, 0, (void *)kDetach, (void *)fIndex);
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::clientClose()
{
/*
* Decrememt connection count, possibly freeing DMA buffers
*/
//fCmdQueue->enqueueCommand(true, 0, (void *)kDetach, (void *)fIndex);
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::Flush(IOAudioStreamPosition *end)
{
// Tell device when it'll be safe to stop the stream
fCmdQueue->enqueueCommand(true, 0, (void *)kFlush, (void *)fIndex, (void *)end);
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::setFlow(bool flowing)
{
fCmdQueue->enqueueCommand(true, 0, (void *)kSetFlow, (void *)fIndex,
(void *)flowing);
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::isOutput(SInt32 *res) const
{
const OSNumber *obj;
obj = getOutputDescriptor();
if(obj)
*res = obj->unsigned8BitValue();
else
*res = 0;
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::getMixBuffer(void **mixBuffer)
{
*mixBuffer = fMappedMem.fMixBuffer;
return kIOReturnSuccess;
}
IOReturn IOAudioStreamImpl::setErase(bool erase, SInt32 *oldErase)
{
assert(fMappedMem.fStatus != NULL); // Must be a user attached.
*oldErase = fMappedMem.fStatus->fErases;
fMappedMem.fStatus->fErases = erase;
return kIOReturnSuccess;
}
IOReturn
IOAudioStreamImpl::setProperties( OSObject * properties )
{
return kIOReturnNotPrivileged;
}
IOReturn IOAudioStreamImpl::clientMemoryForType( UInt32 type,
UInt32 * flags, IOMemoryDescriptor ** memory )
{
// kprintf("IOAudioStream::clientMemoryForType(%d, %d, %d, %d)\n",
// type, flags, address, size);
switch(type) {
case kSampleBuffer:
*memory = IOMemoryDescriptor::withAddress(
(void *)fMappedMem.fSampleBuffer,
fMappedMem.fStatus->fBufSize,
kIODirectionNone);
*flags = 0;
break;
case kStatus:
*memory = IOMemoryDescriptor::withAddress(
(void *)fMappedMem.fStatus,
sizeof(IOAudioStreamStatus),
kIODirectionNone);
*flags = kIOMapReadOnly;
break;
case kMixBuffer:
if (fMappedMem.fMixBuffer == NULL) {
IOSyncer *syncWakeup;
syncWakeup = IOSyncer::create();
fCmdQueue->enqueueCommand(true, syncWakeup, (void *)kAllocMixBuffer,
(void *)fIndex, (void *)&fMappedMem.fMixBuffer);
syncWakeup->wait();
}
fMappedMem.fStatus->fMixBufferInUse = true;
*memory = IOMemoryDescriptor::withAddress(
(void *)fMappedMem.fMixBuffer,
(fMappedMem.fStatus->fBufSize / fMappedMem.fStatus->fSampleSize * sizeof(float)),
kIODirectionNone);
*flags = 0;
break;
default:
return kIOReturnUnsupported;
break;
}
return kIOReturnSuccess;
}
IOExternalMethod *
IOAudioStreamImpl::getExternalMethodForIndex( UInt32 index )
{
IOExternalMethod *method = NULL;
if(index < kNumCalls)
method = &fMethods[index];
return method;
}
//************************************************************************
// Begin implementation of IOAudioComponentImpl class.
//************************************************************************
#undef super
#define super IOAudioComponent
OSDefineMetaClassAndStructors( IOAudioComponentImpl, IOAudioComponent )
bool
IOAudioComponentImpl::initWithStuff(IOAudioController *owner, OSDictionary *props,
IOCommandQueue *queue)
{
if(!super::init(props))
return false;
if(!queue || !owner)
return false;
fCmdQueue = queue;
fOwner = owner;
return true;
}
void
IOAudioComponentImpl::free()
{
if(fNotifyMsg)
IOFree(fNotifyMsg, sizeof (struct _notifyMsg));
super::free();
}
IOReturn
IOAudioComponentImpl::updateVal(UInt32 val, OSDictionary *control, bool direct)
{
IOReturn ret = kIOReturnSuccess;
OSNumber * oldVal;
oldVal = (OSNumber *)control->getObject(IOAudioController::gValSym);
if(val != oldVal->unsigned32BitValue()) {
OSNumber * id;
OSNumber * newVal;
newVal = OSNumber::withNumber(val, oldVal->numberOfBits());
control->setObject(IOAudioController::gValSym, newVal);
id = (OSNumber *)control->getObject(IOAudioController::gIdSym);
if(id) {
if(direct)
fOwner->SetControl(id->unsigned16BitValue(), val);
else
fCmdQueue->enqueueCommand(true, 0, (void *)kSetVal,
(void *)id->unsigned16BitValue(), (void *)val);
}
}
return ret;
}
void
IOAudioComponentImpl::Set(const OSSymbol *type, const OSSymbol *name, int val)
{
OSDictionary *controls;
controls = OSDynamicCast(OSDictionary, getProperty(type));
if(controls) {
OSDictionary *valDict;
OSNumber * old;
OSNumber * newObj;
valDict = OSDynamicCast(OSDictionary,
controls->getObject(name));
if(valDict) {
old = OSDynamicCast(OSNumber, valDict->getObject(IOAudioController::gValSym));
if(old) {
newObj = OSNumber::withNumber(val, old->numberOfBits());
valDict->setObject(IOAudioController::gValSym, newObj);
newObj->release(); // XXX -- svail: added.
/* FIXME: Don't block */
if(fNotifyMsg)
mach_msg_send_from_kernel((mach_msg_header_t *)fNotifyMsg,
fNotifyMsg->h.msgh_size);
else if(type == IOAudioController::gInputsSym &&
name == IOAudioController::gJackSym &&
( GetType() == IOAudioController::gHeadphonesSym ||
GetType() == IOAudioController::gLineOutSym)) {
/*
* This is a headphone or line out jack, mute/unmute any speakers
* with the same IOAudio parent
*/
OSIterator * parents =
getParentIterator(IOAudioController::gIOAudioPlane);
IORegistryEntry *parent;
while((parent = OSDynamicCast(IORegistryEntry, parents->getNextObject()))) {
OSIterator * siblings =
parent->getChildIterator(IOAudioController::gIOAudioPlane);
IOAudioComponentImpl *sibling;
while((sibling = OSDynamicCast(IOAudioComponentImpl, siblings->getNextObject()))) {
if(sibling->GetType() == IOAudioController::gSpeakerSym) {
// Set all controls of type Mute to the new value
OSDictionary *spkrCtls =
OSDynamicCast(OSDictionary, sibling->getProperty(IOAudioController::gControlsSym));
OSCollectionIterator *iter = OSCollectionIterator::withCollection(spkrCtls);
OSSymbol *iterKey;
while((iterKey = OSDynamicCast(OSSymbol, iter->getNextObject()))){
OSObject *spkrCtlObj = spkrCtls->getObject(iterKey);
if(GetType(spkrCtlObj) == IOAudioController::gMuteSym) {
// Safe to just cast because GetType checks object type.
// Update hardware directly
updateVal(val, (OSDictionary *)spkrCtlObj, true);
}
}
}
}
}
}
}
}
}
}
IOReturn IOAudioComponentImpl::clientClose( void )
{
return kIOReturnSuccess;
}
IOReturn IOAudioComponentImpl::clientDied( void )
{
return kIOReturnSuccess;
}
/*
* Return a subclass of IOUserClient for User<->kernel comminication
* Since IOAudioStream is such a suclass, we can return the object
* itself (remembering to increment its retain count!).
*/
IOReturn IOAudioComponentImpl::newUserClient( task_t owningTask,
void * security_id, UInt32 type, IOUserClient ** handler )
{
//kprintf("task:%d, sec_id:0x%x, type:%d\n", owningTask, security_id, type);
*handler = this;
(*handler)->retain();
return kIOReturnSuccess;
}
IOReturn
IOAudioComponentImpl::setProperties( OSObject * properties )
{
OSDictionary *myControls;
OSDictionary *newControls;
OSDictionary *newDict;
OSCollectionIterator *iter;
OSObject *iterKey;
IOReturn ret = kIOReturnSuccess;
newDict = OSDynamicCast(OSDictionary, properties);
if(!newDict)
return kIOReturnBadArgument;
myControls = OSDynamicCast(OSDictionary, getProperty(IOAudioController::gControlsSym));
newControls = OSDynamicCast(OSDictionary, newDict->getObject(IOAudioController::gControlsSym));
if(!newControls)
return kIOReturnBadArgument;
/*
* Look through the Controls dictionary, for each one that has a new Val
* make a kSetVal command, updating the hardware.
*/
iter = OSCollectionIterator::withCollection(newControls);
while((iterKey = iter->getNextObject())) {
OSSymbol *key = OSDynamicCast(OSSymbol, iterKey);
OSDictionary *newCtrl;
OSNumber* newVal;
if(!key) {
ret = kIOReturnBadArgument;
break;
}
OSDictionary *myCtrl;
myCtrl = (OSDictionary *)myControls->getObject(key);
if(!myCtrl) {
ret = kIOReturnBadArgument;
break;
}
newCtrl = OSDynamicCast(OSDictionary, newControls->getObject(key));
if(!newCtrl) {
ret = kIOReturnBadArgument;
break;
}
newVal = OSDynamicCast(OSNumber, newCtrl->getObject(IOAudioController::gValSym));
if(!newVal) {
ret = kIOReturnBadArgument;
break;
}
// Update hardware via command queue
ret = updateVal(newVal->unsigned16BitValue(), myCtrl, false);
if(kIOReturnSuccess != ret)
break;
}
return ret;
}
IOReturn
IOAudioComponentImpl::registerNotificationPort(
mach_port_t port, UInt32 type, UInt32 refCon)
{
static IOAudioNotifyMsg notify_msg = {{
// mach_msg_bits_t msgh_bits;
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0),
// mach_msg_size_t msgh_size;
sizeof (IOAudioNotifyMsg),
// mach_port_t msgh_remote_port;
MACH_PORT_NULL,
// mach_port_t msgh_local_port;
MACH_PORT_NULL,
// mach_msg_size_t msgh_reserved;
0,
// mach_msg_id_t msgh_id;
type },
// UInt32 refCon
refCon
};
if( type != kIOAudioInputNotification)
return( kIOReturnUnsupported);
if ( fNotifyMsg == NULL )
fNotifyMsg = (struct _notifyMsg *)
IOMalloc( sizeof (IOAudioNotifyMsg) );
// Initialize the events available message.
*fNotifyMsg = notify_msg;
fNotifyMsg->h.msgh_remote_port = port;
return kIOReturnSuccess;
}
const OSSymbol *