1/*
2 * Copyright (c) 1998-2014 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include "IOAudioDebug.h"
24#include "IOAudioControl.h"
25#include "IOAudioControlUserClient.h"
26#include "IOAudioTypes.h"
27#include "IOAudioDefines.h"
28#include "AudioTracepoints.h"
29
30#include <IOKit/IOLib.h>
31#include <IOKit/IOWorkLoop.h>
32#include <IOKit/IOCommandGate.h>
33
34// <rdar://8518215>
35enum
36{
37	kCommandGateStatus_Normal				= 0,
38	kCommandGateStatus_RemovalPending,
39	kCommandGateStatus_Invalid
40};
41
42#define super IOService
43
44OSDefineMetaClassAndStructors(IOAudioControl, IOService)
45OSMetaClassDefineReservedUsed(IOAudioControl, 0);
46OSMetaClassDefineReservedUsed(IOAudioControl, 1);
47OSMetaClassDefineReservedUsed(IOAudioControl, 2);
48OSMetaClassDefineReservedUsed(IOAudioControl, 3);
49
50OSMetaClassDefineReservedUnused(IOAudioControl, 4);
51OSMetaClassDefineReservedUnused(IOAudioControl, 5);
52OSMetaClassDefineReservedUnused(IOAudioControl, 6);
53OSMetaClassDefineReservedUnused(IOAudioControl, 7);
54OSMetaClassDefineReservedUnused(IOAudioControl, 8);
55OSMetaClassDefineReservedUnused(IOAudioControl, 9);
56OSMetaClassDefineReservedUnused(IOAudioControl, 10);
57OSMetaClassDefineReservedUnused(IOAudioControl, 11);
58OSMetaClassDefineReservedUnused(IOAudioControl, 12);
59OSMetaClassDefineReservedUnused(IOAudioControl, 13);
60OSMetaClassDefineReservedUnused(IOAudioControl, 14);
61OSMetaClassDefineReservedUnused(IOAudioControl, 15);
62OSMetaClassDefineReservedUnused(IOAudioControl, 16);
63OSMetaClassDefineReservedUnused(IOAudioControl, 17);
64OSMetaClassDefineReservedUnused(IOAudioControl, 18);
65OSMetaClassDefineReservedUnused(IOAudioControl, 19);
66OSMetaClassDefineReservedUnused(IOAudioControl, 20);
67OSMetaClassDefineReservedUnused(IOAudioControl, 21);
68OSMetaClassDefineReservedUnused(IOAudioControl, 22);
69OSMetaClassDefineReservedUnused(IOAudioControl, 23);
70
71// New code
72
73// OSMetaClassDefineReservedUsed(IOAudioControl, 3);
74IOReturn IOAudioControl::createUserClient(task_t task, void *securityID, UInt32 taskType, IOAudioControlUserClient **newUserClient, OSDictionary *properties)
75{
76    IOReturn result = kIOReturnSuccess;
77    IOAudioControlUserClient *userClient;
78
79    userClient = IOAudioControlUserClient::withAudioControl(this, task, securityID, taskType, properties);
80
81    if (userClient) {
82        *newUserClient = userClient;
83    } else {
84        result = kIOReturnNoMemory;
85    }
86
87    return result;
88}
89
90void IOAudioControl::sendChangeNotification(UInt32 notificationType)
91{
92    OSCollectionIterator *iterator;
93    IOAudioControlUserClient *client;
94
95    if (!userClients || !isStarted) {
96        return;
97    }
98
99	// If we're doing a config change, just queue the notification for later.
100	if (reserved->providerEngine->configurationChangeInProgress) {
101		OSNumber *notificationNumber;
102		UInt32		i, count;
103		bool		dupe = FALSE;
104
105		if (!reserved->notificationQueue) {
106			reserved->notificationQueue = OSArray::withCapacity (1);
107			if (!reserved->notificationQueue) {
108				return;
109			}
110		}
111
112		notificationNumber = OSNumber::withNumber (notificationType, sizeof (notificationType) * 8);
113		if (!notificationNumber)
114			return;
115
116		// Check to see if this is a unique notification, there is no need to send dupes.
117		count = reserved->notificationQueue->getCount ();
118		for (i = 0; i < count; i++) {
119			if (notificationNumber->isEqualTo ((OSNumber *)reserved->notificationQueue->getObject (i))) {
120				dupe = TRUE;
121				break;		// no need to send duplicate notifications
122			}
123		}
124		if (!dupe) {
125			reserved->notificationQueue->setObject (notificationNumber);
126		}
127		notificationNumber->release ();
128	} else {
129		iterator = OSCollectionIterator::withCollection(userClients);
130		if (iterator) {
131			while ( (client = (IOAudioControlUserClient *)iterator->getNextObject()) ) {
132				client->sendChangeNotification(notificationType);
133			}
134
135			iterator->release();
136		}
137	}
138}
139
140void IOAudioControl::sendQueuedNotifications(void)
141{
142	UInt32				i;
143	UInt32				count;
144
145	// Send our the queued notications and release the queue.
146	if (reserved && reserved->notificationQueue) {
147		count = reserved->notificationQueue->getCount ();
148		for (i = 0; i < count; i++) {
149			if (!isInactive()) {		// <radr://9320521,9040208>
150				sendChangeNotification(((OSNumber *)reserved->notificationQueue->getObject(i))->unsigned32BitValue());
151			}
152		}
153		reserved->notificationQueue->release();
154		reserved->notificationQueue = NULL;
155	}
156}
157
158// Original code here...
159IOAudioControl *IOAudioControl::withAttributes(UInt32 type,
160                                               OSObject *initialValue,
161                                               UInt32 channelID,
162                                               const char *channelName,
163                                               UInt32 cntrlID,
164                                               UInt32 subType,
165                                               UInt32 usage)
166{
167    IOAudioControl *control;
168
169    control = new IOAudioControl;
170
171    if (control) {
172        if (!control->init(type, initialValue, channelID, channelName, cntrlID, subType, usage)) {
173            control->release();
174            control = 0;
175        }
176    }
177
178    return control;
179}
180
181bool IOAudioControl::init(UInt32 _type,
182                          OSObject *initialValue,
183                          UInt32 newChannelID,
184                          const char *channelName,
185                          UInt32 cntrlID,
186                          UInt32 _subType,
187                          UInt32 _usage,
188                          OSDictionary *properties)
189{
190    if (!super::init(properties)) {
191        return false;
192    }
193
194    if (initialValue == NULL) {
195        return false;
196    }
197
198    if (_type == 0) {
199        return false;
200    }
201
202    setType(_type);
203
204    setChannelID(newChannelID);
205    setControlID(cntrlID);
206
207	setSubType(_subType);
208
209    if (channelName) {
210        setChannelName(channelName);
211    }
212
213    if (_usage != 0) {
214        setUsage(_usage);
215    }
216
217    _setValue(initialValue);
218
219    userClients = OSSet::withCapacity(1);
220    if (!userClients) {
221        return false;
222    }
223
224	reserved = (ExpansionData *)IOMalloc (sizeof(struct ExpansionData));
225	if (!reserved) {
226		return false;
227	}
228
229	reserved->providerEngine = NULL;
230	reserved->notificationQueue = NULL;
231	reserved->commandGateStatus = kCommandGateStatus_Normal;	// <rdar://8518215>
232	reserved->commandGateUsage = 0;								// <rdar://8518215>
233    isStarted = false;
234
235    return true;
236}
237
238void IOAudioControl::setType(UInt32 _type)
239{
240    this->type = _type;
241    setProperty(kIOAudioControlTypeKey, type, sizeof(UInt32)*8);
242}
243
244void IOAudioControl::setSubType(UInt32 _subType)
245{
246    this->subType = _subType;
247    setProperty(kIOAudioControlSubTypeKey, subType, sizeof(UInt32)*8);
248}
249
250void IOAudioControl::setChannelName(const char *channelName)
251{
252    setProperty(kIOAudioControlChannelNameKey, channelName);
253}
254
255void IOAudioControl::setUsage(UInt32 _usage)
256{
257    this->usage = _usage;
258    setProperty(kIOAudioControlUsageKey, usage, sizeof(UInt32)*8);
259}
260
261void IOAudioControl::setCoreAudioPropertyID(UInt32 propertyID)
262{
263    setProperty(kIOAudioControlCoreAudioPropertyIDKey, propertyID, sizeof(UInt32)*8);
264    setUsage(kIOAudioControlUsageCoreAudioProperty);
265}
266
267void IOAudioControl::setReadOnlyFlag()
268{
269    setProperty(kIOAudioControlValueIsReadOnlyKey, (bool)true);
270}
271
272UInt32 IOAudioControl::getType()
273{
274    return type;
275}
276
277UInt32 IOAudioControl::getSubType()
278{
279    return subType;
280}
281
282UInt32 IOAudioControl::getUsage()
283{
284    return usage;
285}
286
287void IOAudioControl::free()
288{
289    audioDebugIOLog(3, "+ IOAudioControl[%p]::free()\n", this);
290
291    if (userClients) {
292        // should we do some sort of notification here?
293        userClients->release();
294        userClients = NULL;
295    }
296
297    if (valueChangeTarget) {
298        valueChangeTarget->release();
299        valueChangeTarget = NULL;
300    }
301
302    if (commandGate) {
303        if (workLoop) {
304            workLoop->removeEventSource(commandGate);
305        }
306
307        commandGate->release();
308        commandGate = NULL;
309    }
310
311    if (workLoop) {
312        workLoop->release();
313        workLoop = NULL;
314    }
315
316	if (reserved) {
317		if (reserved->notificationQueue) {
318			reserved->notificationQueue->release();
319			reserved->notificationQueue = NULL;
320		}
321
322		IOFree (reserved, sizeof (struct ExpansionData));
323		reserved = NULL;
324	}
325
326    super::free();
327    audioDebugIOLog(3, "- IOAudioControl[%p]::free()\n", this);
328}
329
330bool IOAudioControl::start(IOService *provider)
331{
332	AudioTrace_Start(kAudioTIOAudioControl, kTPIOAudioControlStart, (uintptr_t)this, (uintptr_t)provider, 0, 0);
333    if (!super::start(provider)) {
334        return false;
335    }
336
337    isStarted = true;
338	reserved->providerEngine = OSDynamicCast (IOAudioEngine, provider);
339
340	AudioTrace_End(kAudioTIOAudioControl, kTPIOAudioControlStart, (uintptr_t)this, (uintptr_t)provider, true, 0);
341    return true;
342}
343
344bool IOAudioControl::attachAndStart(IOService *provider)
345{
346    bool result = true;
347
348    if (attach(provider)) {
349        if (!isStarted) {
350            result = start(provider);
351            if (!result) {
352                detach(provider);
353            }
354        }
355    } else {
356        result = false;
357    }
358
359    return result;
360}
361
362void IOAudioControl::stop(IOService *provider)
363{
364    audioDebugIOLog(3, "+ IOAudioControl[%p]::stop(%p)\n", this, provider);
365
366    if (userClients && (userClients->getCount() > 0)) {
367        IOCommandGate *cg;
368
369        cg = getCommandGate();
370
371		if (cg) {
372			cg->runAction(detachUserClientsAction);
373		}
374    }
375
376    if (valueChangeTarget) {
377        valueChangeTarget->release();
378        valueChangeTarget = NULL;
379        valueChangeHandler.intHandler = NULL;
380    }
381
382	// <rdar://7233118>, <rdar://7029696> Remove the event source here as performing heavy workloop operation in free() could lead
383	// to deadlock since the context which free() is called is not known. stop() is called on the workloop, so it is safe to remove
384	// the event source here.
385	if (reserved->commandGateUsage == 0) {							// <rdar://8518215>
386		reserved->commandGateStatus = kCommandGateStatus_Invalid;	// <rdar://8518215>
387
388		if (commandGate) {
389			if (workLoop) {
390				workLoop->removeEventSource(commandGate);
391			}
392
393			commandGate->release();
394			commandGate = NULL;
395		}
396	}
397	else {	// <rdar://8518215>
398		reserved->commandGateStatus = kCommandGateStatus_RemovalPending;
399	}
400
401    super::stop(provider);
402
403    isStarted = false;
404
405    audioDebugIOLog(3, "- IOAudioControl[%p]::stop(%p)\n", this, provider);
406}
407
408bool IOAudioControl::getIsStarted()
409{
410    return isStarted;
411}
412
413IOWorkLoop *IOAudioControl::getWorkLoop()
414{
415    return workLoop;
416}
417
418void IOAudioControl::setWorkLoop(IOWorkLoop *wl)
419{
420	if (!workLoop) {
421		workLoop = wl;
422
423		if (workLoop) {
424			workLoop->retain();
425
426			commandGate = IOCommandGate::commandGate(this);
427
428			if (commandGate) {
429				workLoop->addEventSource(commandGate);
430			}
431		}
432	}
433}
434
435IOCommandGate *IOAudioControl::getCommandGate()
436{
437    return commandGate;
438}
439
440// <rdar://7529580>
441IOReturn IOAudioControl::_setValueAction(OSObject *target, void *arg0, void *arg1, void *arg2, void *arg3)
442{
443    IOReturn result = kIOReturnBadArgument;
444
445    if (target) {
446        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, target);
447        if (audioControl) {
448            IOCommandGate *cg;
449
450            cg = audioControl->getCommandGate();
451
452            if (cg) {
453				setCommandGateUsage(audioControl, true);	// <rdar://8518215>
454                result = cg->runAction(setValueAction, arg0, arg1, arg2, arg3);
455				setCommandGateUsage(audioControl, false);	// <rdar://8518215>
456            } else {
457                result = kIOReturnError;
458            }
459        }
460    }
461
462    return result;
463}
464
465IOReturn IOAudioControl::setValueAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
466{
467    IOReturn result = kIOReturnBadArgument;
468
469    if (owner) {
470        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, owner);
471        if (audioControl) {
472            result = audioControl->setValue((OSObject *)arg1);
473        }
474    }
475
476    return result;
477}
478
479IOReturn IOAudioControl::setValue(OSObject *newValue)
480{
481    IOReturn result = kIOReturnSuccess;
482
483    if (OSDynamicCast(OSNumber, newValue)) {
484        audioDebugIOLog(3, "+ IOAudioControl[%p]::setValue(int = %d)\n", this, ((OSNumber *)newValue)->unsigned32BitValue());
485    } else {
486        audioDebugIOLog(3, "+ IOAudioControl[%p]::setValue(%p)\n", this, newValue);
487    }
488
489    if (newValue) {
490        if (!value || !value->isEqualTo(newValue)) {
491            result = validateValue(newValue);
492            if (result == kIOReturnSuccess) {
493                result = performValueChange(newValue);
494                if (result == kIOReturnSuccess) {
495                    result = updateValue(newValue);
496                } else {
497                    audioDebugIOLog(2, "  Error 0x%x received from driver - value not set!\n", result);
498                }
499            } else {
500                audioDebugIOLog(2, "  Error 0x%x - invalid value.\n", result);
501            }
502        }
503    } else {
504        result = kIOReturnBadArgument;
505    }
506
507    if (OSDynamicCast(OSNumber, newValue)) {
508        audioDebugIOLog(3, "- IOAudioControl[%p]::setValue(int = %d) returns 0x%lX\n", this, ((OSNumber *)newValue)->unsigned32BitValue(), (long unsigned int)result );
509    } else {
510        audioDebugIOLog(3, "- IOAudioControl[%p]::setValue(%p) returns 0x%lX\n", this, newValue, (long unsigned int)result );
511    }
512
513    return result;
514}
515
516IOReturn IOAudioControl::setValue(SInt32 intValue)
517{
518    IOReturn result = kIOReturnError;
519    OSNumber *number;
520
521    number = OSNumber::withNumber(intValue, sizeof(SInt32)*8);
522    if (number) {
523        result = setValue(number);
524        number->release();
525    }
526
527    return result;
528}
529
530IOReturn IOAudioControl::validateValue(OSObject *_value)
531{
532    return kIOReturnSuccess;
533}
534
535IOReturn IOAudioControl::updateValue(OSObject *newValue)
536{
537    IOReturn result;
538
539    result = _setValue(newValue);
540    if (result == kIOReturnSuccess) {
541        sendValueChangeNotification();
542    }
543
544    return result;
545}
546
547IOReturn IOAudioControl::_setValue(OSObject *newValue)
548{
549    if (value != newValue) {
550        if (value) {
551            value->release();
552        }
553        value = newValue;
554        value->retain();
555
556        setProperty(kIOAudioControlValueKey, value);
557    }
558
559    return kIOReturnSuccess;
560}
561
562IOReturn IOAudioControl::hardwareValueChanged(OSObject *newValue)
563{
564    IOReturn result = kIOReturnSuccess;
565
566    audioDebugIOLog(3, "+ IOAudioControl[%p]::hardwareValueChanged(%p)\n", this, newValue);
567
568    if (newValue) {
569        if (!value || !value->isEqualTo(newValue)) {
570            result = validateValue(newValue);
571            if (result == kIOReturnSuccess) {
572                result = updateValue(newValue);
573            } else {
574                IOLog("IOAudioControl[%p]::hardwareValueChanged(%p) - Error 0x%x - invalid value.\n", this, newValue, result);
575            }
576        }
577    } else {
578        result = kIOReturnBadArgument;
579    }
580
581    audioDebugIOLog(3, "- IOAudioControl[%p]::hardwareValueChanged(%p) returns 0x%lX\n", this, newValue, (long unsigned int)result );
582    return result;
583}
584
585void IOAudioControl::setValueChangeHandler(IntValueChangeHandler intValueChangeHandler, OSObject *target)
586{
587    valueChangeHandlerType = kIntValueChangeHandler;
588    valueChangeHandler.intHandler = intValueChangeHandler;
589    setValueChangeTarget(target);
590}
591
592void IOAudioControl::setValueChangeHandler(DataValueChangeHandler dataValueChangeHandler, OSObject *target)
593{
594    valueChangeHandlerType = kDataValueChangeHandler;
595    valueChangeHandler.dataHandler = dataValueChangeHandler;
596    setValueChangeTarget(target);
597}
598
599void IOAudioControl::setValueChangeHandler(ObjectValueChangeHandler objectValueChangeHandler, OSObject *target)
600{
601    valueChangeHandlerType = kObjectValueChangeHandler;
602    valueChangeHandler.objectHandler = objectValueChangeHandler;
603    setValueChangeTarget(target);
604}
605
606void IOAudioControl::setValueChangeTarget(OSObject *target)
607{
608    if (target) {
609        target->retain();
610    }
611
612    if (valueChangeTarget) {
613        valueChangeTarget->release();
614    }
615
616    valueChangeTarget = target;
617}
618
619IOReturn IOAudioControl::performValueChange(OSObject *newValue)
620{
621    IOReturn result = kIOReturnError;
622
623    audioDebugIOLog(3, "+ IOAudioControl[%p]::performValueChange(%p)\n", this, newValue);
624
625    if (valueChangeHandler.intHandler != NULL) {
626        switch(valueChangeHandlerType) {
627            case kIntValueChangeHandler:
628                OSNumber *oldNumber, *newNumber;
629
630                if ((oldNumber = OSDynamicCast(OSNumber, getValue())) == NULL) {
631                    IOLog("IOAudioControl[%p]::performValueChange(%p) - Error: can't call handler - int handler set and old value is not an OSNumber.\n", this, newValue);
632                    break;
633                }
634
635                if ((newNumber = OSDynamicCast(OSNumber, newValue)) == NULL) {
636                    IOLog("IOAudioControl[%p]::performValueChange(%p) - Error: can't call handler - int handler set and new value is not an OSNumber.\n", this, newValue);
637                    break;
638                }
639
640                result = valueChangeHandler.intHandler(valueChangeTarget, this, oldNumber->unsigned32BitValue(), newNumber->unsigned32BitValue());
641
642                break;
643            case kDataValueChangeHandler:
644                OSData *oldData, *newData;
645                const void *oldBytes, *newBytes;
646                UInt32 oldSize, newSize;
647
648                if (getValue()) {
649                    if ((oldData = OSDynamicCast(OSData, getValue())) == NULL) {
650                        IOLog("IOAudioControl[%p]::performValueChange(%p) - Error: can't call handler - data handler set and old value is not an OSData.\n", this, newValue);
651                        break;
652                    }
653
654                    oldBytes = oldData->getBytesNoCopy();
655                    oldSize = oldData->getLength();
656                } else {
657                    oldBytes = NULL;
658                    oldSize = 0;
659                }
660
661                if (newValue) {
662                    if ((newData = OSDynamicCast(OSData, newValue)) == NULL) {
663                        IOLog("IOAudioControl[%p]::performValueChange(%p) - Error: can't call handler - data handler set and new value is not an OSData.\n", this, newValue);
664                        break;
665                    }
666
667                    newBytes = newData->getBytesNoCopy();
668                    newSize = newData->getLength();
669                } else {
670                    newBytes = NULL;
671                    newSize = 0;
672                }
673
674                result = valueChangeHandler.dataHandler(valueChangeTarget, this, oldBytes, oldSize, newBytes, newSize);
675
676                break;
677            case kObjectValueChangeHandler:
678                result = valueChangeHandler.objectHandler(valueChangeTarget, this, getValue(), newValue);
679                break;
680        }
681    }
682
683    audioDebugIOLog(3, "- IOAudioControl[%p]::performValueChange(%p) returns 0x%lX\n", this, newValue, (long unsigned int)result );
684    return result;
685}
686
687IOReturn IOAudioControl::flushValue()
688{
689    return performValueChange(getValue());
690}
691
692OSObject *IOAudioControl::getValue()
693{
694    return value;
695}
696
697SInt32 IOAudioControl::getIntValue()
698{
699    OSNumber *number;
700    SInt32 intValue = 0;
701
702    number = OSDynamicCast(OSNumber, getValue());
703    if (number) {
704        intValue = (SInt32)number->unsigned32BitValue();
705    }
706
707    return intValue;
708}
709
710const void *IOAudioControl::getDataBytes()
711{
712    const void *bytes = NULL;
713    OSData *data;
714
715    data = OSDynamicCast(OSData, getValue());
716    if (data) {
717        bytes = data->getBytesNoCopy();
718    }
719
720    return bytes;
721}
722
723UInt32 IOAudioControl::getDataLength()
724{
725    UInt32 length = 0;
726    OSData *data;
727
728    data = OSDynamicCast(OSData, getValue());
729    if (data) {
730        length = data->getLength();
731    }
732
733    return length;
734}
735
736void IOAudioControl::sendValueChangeNotification()
737{
738    OSCollectionIterator *iterator;
739    IOAudioControlUserClient *client;
740
741    if (!userClients) {
742        return;
743    }
744
745    iterator = OSCollectionIterator::withCollection(userClients);
746    if (iterator) {
747        while ( (client = (IOAudioControlUserClient *)iterator->getNextObject()) ) {
748            client->sendValueChangeNotification();
749        }
750
751        iterator->release();
752    }
753}
754
755void IOAudioControl::setControlID(UInt32 newControlID)
756{
757    controlID = newControlID;
758    setProperty(kIOAudioControlIDKey, newControlID, sizeof(UInt32)*8);
759}
760
761UInt32 IOAudioControl::getControlID()
762{
763    return controlID;
764}
765
766void IOAudioControl::setChannelID(UInt32 newChannelID)
767{
768    channelID = newChannelID;
769    setProperty(kIOAudioControlChannelIDKey, newChannelID, sizeof(UInt32)*8);
770}
771
772UInt32 IOAudioControl::getChannelID()
773{
774    return channelID;
775}
776
777void IOAudioControl::setChannelNumber(SInt32 channelNumber)
778{
779    setProperty(kIOAudioControlChannelNumberKey, channelNumber, sizeof(SInt32)*8);
780}
781
782IOReturn IOAudioControl::createUserClient(task_t task, void *securityID, UInt32 taskType, IOAudioControlUserClient **newUserClient)
783{
784    IOReturn result = kIOReturnSuccess;
785    IOAudioControlUserClient *userClient;
786
787    userClient = IOAudioControlUserClient::withAudioControl(this, task, securityID, taskType);
788
789    if (userClient) {
790        *newUserClient = userClient;
791    } else {
792        result = kIOReturnNoMemory;
793    }
794
795    return result;
796}
797
798IOReturn IOAudioControl::newUserClient(task_t task, void *securityID, UInt32 taskType, IOUserClient **handler)
799{
800#if __i386__ || __x86_64__
801	return kIOReturnUnsupported;
802#else
803    IOReturn result = kIOReturnSuccess;
804    IOAudioControlUserClient *client = NULL;
805
806    audioDebugIOLog(3, "+ IOAudioControl[%p]::newUserClient()\n", this);
807
808	*handler = NULL;		// <rdar://8370885>
809
810    if (!isInactive()) {	// <rdar://7324947>
811		result = createUserClient(task, securityID, taskType, &client);
812
813		if ((result == kIOReturnSuccess) && (client != NULL)) {
814			if (workLoop) {		// <rdar://7324947>
815				result = workLoop->runAction(_addUserClientAction, this, client);	// <rdar://7324947>, <rdar://7529580>
816
817				if (result == kIOReturnSuccess) {
818					*handler = client;
819				} else {
820					client->release();	// <rdar://8370885>
821				}
822
823			} else {
824				client->release();		// <rdar://8370885>
825				result = kIOReturnError;
826			}
827		}
828    } else {	// <rdar://7324947>
829        result = kIOReturnNoDevice;
830    }
831
832    audioDebugIOLog(3, "- IOAudioControl[%p]::newUserClient() returns 0x%lX\n", this, (long unsigned int)result );
833    return result;
834#endif
835}
836
837//	<rdar://8121989>	Restructured for single point of entry and single point of exit so that
838//	the indentifier post processing tool can properly insert scope when post processing a log file
839//	obtained via fwkpfv.
840
841IOReturn IOAudioControl::newUserClient(task_t task, void *securityID, UInt32 taskType, OSDictionary *properties, IOUserClient **handler)
842{
843    IOReturn					result = kIOReturnSuccess;
844    IOAudioControlUserClient *	client = NULL;
845
846    audioDebugIOLog(3, "+ IOAudioControl[%p]::newUserClient()\n", this);
847
848	*handler = NULL;		// <rdar://8370885>
849
850    if ( !isInactive () )	// <rdar://7324947>
851	{
852		if ( kIOReturnSuccess != newUserClient ( task, securityID, taskType, handler ) )
853		{
854			result = createUserClient ( task, securityID, taskType, &client, properties );
855
856			if ( ( kIOReturnSuccess == result ) && ( NULL != client ) )
857			{
858				if ( workLoop )	// <rdar://7324947>
859				{
860					result = workLoop->runAction ( _addUserClientAction, this, client );	// <rdar://7324947>, <rdar://7529580>
861
862					if ( result == kIOReturnSuccess )
863					{
864						*handler = client;
865					}
866					else
867					{
868						client->release();		// <rdar://8370885>
869					}
870				}
871				else
872				{
873					client->release();			// <rdar://8370885>
874					result = kIOReturnError;
875				}
876			}
877		}
878    }
879    else	// <rdar://7324947>
880    {
881        result = kIOReturnNoDevice;
882    }
883
884    audioDebugIOLog(3, "- IOAudioControl[%p]::newUserClient() returns 0x%lX\n", this, (long unsigned int)result );
885    return result;
886}
887
888void IOAudioControl::clientClosed(IOAudioControlUserClient *client)
889{
890    audioDebugIOLog(3, "+ IOAudioControl[%p]::clientClosed(%p)\n", this, client);
891
892    if (client) {
893		if (workLoop) {														// <rdar://7529580>
894			workLoop->runAction(_removeUserClientAction, this, client);		// <rdar://7529580>
895		}
896    }
897    audioDebugIOLog(3, "- IOAudioControl[%p]::clientClosed(%p)\n", this, client);
898}
899
900// <rdar://7529580>
901IOReturn IOAudioControl::_addUserClientAction(OSObject *target, void *arg0, void *arg1, void *arg2, void *arg3)
902{
903    IOReturn result = kIOReturnBadArgument;
904
905    if (target) {
906        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, target);
907        if (audioControl) {
908            IOCommandGate *cg;
909
910            cg = audioControl->getCommandGate();
911
912            if (cg) {
913				setCommandGateUsage(audioControl, true);	// <rdar://8518215>
914                result = cg->runAction(addUserClientAction, arg0, arg1, arg2, arg3);
915				setCommandGateUsage(audioControl, false);	// <rdar://8518215>
916            } else {
917                result = kIOReturnError;
918            }
919        }
920    }
921
922    return result;
923}
924
925IOReturn IOAudioControl::addUserClientAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
926{
927    IOReturn result = kIOReturnBadArgument;
928
929    if (owner) {
930        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, owner);
931        if (audioControl) {
932            result = audioControl->addUserClient((IOAudioControlUserClient *)arg1);
933        }
934    }
935
936    return result;
937}
938
939// <rdar://7529580>
940IOReturn IOAudioControl::_removeUserClientAction(OSObject *target, void *arg0, void *arg1, void *arg2, void *arg3)
941{
942    IOReturn result = kIOReturnBadArgument;
943
944    if (target) {
945        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, target);
946        if (audioControl) {
947            IOCommandGate *cg;
948
949            cg = audioControl->getCommandGate();
950
951            if (cg) {
952				setCommandGateUsage(audioControl, true);	// <rdar://8518215>
953                result = cg->runAction(removeUserClientAction, arg0, arg1, arg2, arg3);
954				setCommandGateUsage(audioControl, false);	// <rdar://8518215>
955            } else {
956                result = kIOReturnError;
957            }
958        }
959    }
960
961    return result;
962}
963
964IOReturn IOAudioControl::removeUserClientAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
965{
966    IOReturn result = kIOReturnBadArgument;
967
968    if (owner) {
969        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, owner);
970        if (audioControl) {
971            result = audioControl->removeUserClient((IOAudioControlUserClient *)arg1);
972        }
973    }
974
975    return result;
976}
977
978IOReturn IOAudioControl::detachUserClientsAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
979{
980    IOReturn result = kIOReturnBadArgument;
981
982    if (owner) {
983        IOAudioControl *audioControl = OSDynamicCast(IOAudioControl, owner);
984        if (audioControl) {
985            result = audioControl->detachUserClients();
986        }
987    }
988
989    return result;
990}
991
992IOReturn IOAudioControl::addUserClient(IOAudioControlUserClient *newUserClient)
993{
994    IOReturn					result = kIOReturnSuccess;		// <rdar://8370885>
995
996    audioDebugIOLog(3, "+ IOAudioControl[%p]::addUserClient(%p)\n", this, newUserClient);
997
998    assert(userClients);
999
1000	// <rdar://8370885>
1001	if (!isInactive()) {
1002		if (!newUserClient->attach(this)) {
1003			result = kIOReturnError;
1004		} else if (!newUserClient->start(this) || !userClients) {
1005			newUserClient->detach(this);
1006			result = kIOReturnError;
1007		} else {
1008			userClients->setObject(newUserClient);
1009		}
1010	} else {
1011        result = kIOReturnNoDevice;
1012    }
1013
1014    audioDebugIOLog(3, "- IOAudioControl[%p]::addUserClient(%p) returns 0x%lX\n", this, newUserClient, (long unsigned int)kIOReturnSuccess );
1015    return result;	// <rdar://8370885>
1016}
1017
1018IOReturn IOAudioControl::removeUserClient(IOAudioControlUserClient *userClient)
1019{
1020    audioDebugIOLog(3, "+ IOAudioControl[%p]::removeUserClient(%p)\n", this, userClient);
1021
1022    assert(userClients);
1023
1024    userClient->retain();
1025
1026    userClients->removeObject(userClient);
1027
1028    if (!isInactive()) {
1029        userClient->terminate();
1030    }
1031
1032    userClient->release();
1033
1034    audioDebugIOLog(3, "- IOAudioControl[%p]::removeUserClient(%p) returns 0x%lX\n", this, userClient, (long unsigned int)kIOReturnSuccess );
1035    return kIOReturnSuccess;
1036}
1037
1038IOReturn IOAudioControl::detachUserClients()
1039{
1040    IOReturn result = kIOReturnSuccess;
1041
1042    audioDebugIOLog(3, "+ IOAudioControl[%p]::detachUserClients()\n", this);
1043
1044    assert(userClients);
1045
1046    if (!isInactive()) {
1047        OSIterator *iterator;
1048
1049        iterator = OSCollectionIterator::withCollection(userClients);
1050
1051        if (iterator) {
1052            IOAudioControlUserClient *userClient;
1053
1054            while ( (userClient = (IOAudioControlUserClient *)iterator->getNextObject()) ) {
1055                userClient->terminate();
1056            }
1057
1058            iterator->release();
1059        }
1060    }
1061
1062    userClients->flushCollection();
1063
1064    audioDebugIOLog(3, "- IOAudioControl[%p]::detachUserClients() returns 0x%lX\n", this, (long unsigned int)result );
1065    return result;
1066}
1067
1068// <rdar://8518215>
1069void IOAudioControl::setCommandGateUsage(IOAudioControl *control, bool increment)
1070{
1071	if (control->reserved) {
1072		if (increment) {
1073			switch (control->reserved->commandGateStatus)
1074			{
1075				case kCommandGateStatus_Normal:
1076				case kCommandGateStatus_RemovalPending:
1077					control->reserved->commandGateUsage++;
1078					break;
1079				case kCommandGateStatus_Invalid:
1080					// Should never be here. If so, something went bad...
1081					break;
1082			}
1083		}
1084		else {
1085			switch (control->reserved->commandGateStatus)
1086			{
1087				case kCommandGateStatus_Normal:
1088					if (control->reserved->commandGateUsage > 0) {
1089						control->reserved->commandGateUsage--;
1090					}
1091					break;
1092				case kCommandGateStatus_RemovalPending:
1093					if (control->reserved->commandGateUsage > 0) {
1094						control->reserved->commandGateUsage--;
1095
1096						if (control->reserved->commandGateUsage == 0) {
1097							control->reserved->commandGateStatus = kCommandGateStatus_Invalid;
1098
1099							if (control->commandGate) {
1100								if (control->workLoop) {
1101									control->workLoop->removeEventSource(control->commandGate);
1102								}
1103
1104								control->commandGate->release();
1105								control->commandGate = NULL;
1106							}
1107						}
1108					}
1109					break;
1110				case kCommandGateStatus_Invalid:
1111					// Should never be here. If so, something went bad...
1112					break;
1113			}
1114		}
1115	}
1116}
1117
1118IOReturn IOAudioControl::setProperties(OSObject *properties)
1119{
1120    OSDictionary *props;
1121    IOReturn result = kIOReturnSuccess;
1122
1123    if (properties && (props = OSDynamicCast(OSDictionary, properties))) {
1124        OSNumber *number = OSDynamicCast(OSNumber, props->getObject(kIOAudioControlValueKey));
1125
1126        if (number) {
1127        	if (workLoop) {																// <rdar://7529580>
1128				result = workLoop->runAction(_setValueAction, this, (void *)number);	// <rdar://7529580>
1129            } else {
1130                result = kIOReturnError;
1131            }
1132        }
1133    } else {
1134        result = kIOReturnBadArgument;
1135    }
1136
1137    return result;
1138}
1139