#include <sys/cdefs.h>

__BEGIN_DECLS
#include <kern/thread_call.h>
__END_DECLS

#include "AppleUSBAudioMuteControl.h"
#include "AppleUSBAudioCommon.h"
#include "AppleUSBAudioDevice.h"

#include <IOKit/audio/IOAudioTypes.h>
#include <IOKit/audio/IOAudioDefines.h>
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/IOLib.h>

#include <libkern/OSByteOrder.h>

#define super IOAudioToggleControl
OSDefineMetaClassAndStructors(AppleUSBAudioMuteControl, IOAudioToggleControl)

AppleUSBAudioMuteControl *AppleUSBAudioMuteControl::create (UInt8 theUnitID, UInt8 theInterfaceNumber, UInt8 theChannelNumber, USBDeviceRequest theUSBDeviceRequest, void *theCallerRefCon, UInt32 usage, UInt32 subType, UInt32 controlID) {
    AppleUSBAudioMuteControl 			*control;

    debug7IOLog ("+AppleUSBAudioMuteControl::create (%d, %d, %d, 0x%x, %lX, %lX)\n", theUnitID, theInterfaceNumber, theChannelNumber, theUSBDeviceRequest, usage, controlID);
    control = new AppleUSBAudioMuteControl;

    if (control) {
        if (FALSE == control->init (theUnitID, theInterfaceNumber, theChannelNumber, theUSBDeviceRequest, theCallerRefCon, usage, subType, controlID)) {
            control->release ();
            control = NULL;
        }
    }

    debug7IOLog ("-AppleUSBAudioMuteControl[0x%x]::create (%d, %d, %d, 0x%x, %lX, %lX)\n", theUnitID, theInterfaceNumber, theChannelNumber, theUSBDeviceRequest, usage, controlID);
    return control;
}

bool AppleUSBAudioMuteControl::init (UInt8 theUnitID, UInt8 theInterfaceNumber, UInt8 theChannelNumber, USBDeviceRequest theUSBDeviceRequest, void *theCallerRefCon, UInt32 usage, UInt32 subType, UInt32 controlID, OSDictionary *properties) {
    const char *						channelName = NULL;
    UInt8								currentValue;
    IOReturn							ret;
    Boolean								result;
    
    debug8IOLog ("+AppleUSBAudioMuteControl[0x%x]::init (%d, %d, %d, 0x%x, 0x%x, %lX)\n", this, theUnitID, theInterfaceNumber, theChannelNumber, theUSBDeviceRequest, usage, properties);
    result = FALSE;
    FailIf (NULL == theUSBDeviceRequest, Exit);

    setValueThreadCall = thread_call_allocate ((thread_call_func_t)updateValueCallback, this);
    FailIf (NULL == setValueThreadCall, Exit);

    unitID = theUnitID;
    interfaceNumber = theInterfaceNumber;
    channelNumber = theChannelNumber;
	callerRefCon = theCallerRefCon;
    usbDeviceRequest = theUSBDeviceRequest;

    switch (channelNumber) {
        case kIOAudioControlChannelIDAll:
            channelName = kIOAudioControlChannelNameAll;
            break;
        case kIOAudioControlChannelIDDefaultLeft:
            channelName = kIOAudioControlChannelNameLeft;
            break;
        case kIOAudioControlChannelIDDefaultRight:
            channelName = kIOAudioControlChannelNameRight;
            break;
        case 0xff:
            debugIOLog ("++AppleUSBAudioMuteControl: Does not support channel number 0xff.\n");
            return FALSE;
        default:
            channelName = "Unknown";
            break;
    }

	currentValue = GetCurMute (interfaceNumber, channelNumber, &ret);
    FailIf (kIOReturnSuccess != ret, Exit);
    FailIf (FALSE == super::init (currentValue, theChannelNumber, channelName, controlID, subType, usage), Exit);

    result = TRUE;
    
Exit:
    debug8IOLog ("-AppleUSBAudioMuteControl[0x%x]::init (%d, %d, %d, 0x%x, 0x%x, %lX)\n", this, theUnitID, theInterfaceNumber, theChannelNumber, theUSBDeviceRequest, usage, properties);
    return result;
}

void AppleUSBAudioMuteControl::free () {
    debug2IOLog ("+AppleUSBAudioMuteControl[0x%x]::free ()\n", this);

    if (setValueThreadCall) {
        thread_call_free (setValueThreadCall);
        setValueThreadCall = NULL;
    }

    debug2IOLog ("-AppleUSBAudioMuteControl[0x%x]::free ()\n", this);
    super::free ();
}

IOReturn AppleUSBAudioMuteControl::performValueChange (OSObject * newValue) {
	OSNumber *					newValueAsNumber;
	SInt32						newValueAsSInt32;

    debug3IOLog ("+AppleUSBAudioMuteControl[0x%x]::performValueChange (%d)\n", this, newValue); 

	newValueAsNumber = OSDynamicCast (OSNumber, newValue);
	FailIf (NULL == newValueAsNumber, Exit);
	newValueAsSInt32 = newValueAsNumber->unsigned32BitValue ();
	debug3IOLog ("++AppleUSBAudioMuteControl[0x%x]::performValueChange (%ld)\n", this, newValueAsSInt32);

    // updateUSBValue ();
    // We should just be able to make an asynchronous deviceRequest, but for some reason that doesn't want to work
    // we get pipe stall errors and the change is never made

    assert (setValueThreadCall);
    thread_call_enter1 (setValueThreadCall, (thread_call_param_t)newValueAsSInt32);
    
    debug2IOLog ("-AppleUSBAudioMuteControl::performValueChange (%d)\n", newValueAsSInt32);

Exit:
    return kIOReturnSuccess;
}

UInt8 AppleUSBAudioMuteControl::GetCurMute (UInt8 interfaceNumber, UInt8 channelNumber, IOReturn * error) {
    IOUSBDevRequest				devReq;
    UInt8						theMuteState;

    devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
    devReq.bRequest = GET_CUR;
    devReq.wValue = (MUTE_CONTROL << 8) | channelNumber;
    devReq.wIndex = (unitID << 8) | interfaceNumber;
    devReq.wLength = 1;
    devReq.pData = &theMuteState;

	*error = usbDeviceRequest (&devReq, callerRefCon);
    FailIf (kIOReturnSuccess != *error, Error);

Exit:
    return theMuteState;
Error:
	theMuteState = 0;
	goto Exit;
}

IOReturn AppleUSBAudioMuteControl::SetCurMute (UInt8 interfaceNumber, UInt8 channelNumber, UInt8 theMuteState) {
    IOUSBDevRequest				devReq;
	IOReturn					error;

    devReq.bmRequestType = USBmakebmRequestType (kUSBOut, kUSBClass, kUSBInterface);
    devReq.bRequest = SET_CUR;
    devReq.wValue = (MUTE_CONTROL << 8) | channelNumber;
    devReq.wIndex = (unitID << 8) | interfaceNumber;
    devReq.wLength = 1;
    devReq.pData = &theMuteState;

	FailIf ((TRUE == isInactive()), DeviceInactive);	// In case we've been unplugged during sleep
	error = usbDeviceRequest (&devReq, callerRefCon);
    FailIf (kIOReturnSuccess != error, Exit);

Exit:
    return error;

DeviceInactive:
	debugIOLog("AppleUSBAudioMuteControl::SetCurMute ERROR attempt to send a device request to and inactive device\n");
	error = kIOReturnError;
	goto Exit;
}

void AppleUSBAudioMuteControl::updateUSBValue () {
    updateUSBValue (getIntValue ());
}

void AppleUSBAudioMuteControl::updateUSBValue (SInt32 newValue) {
    UInt8						theValue;
    IOReturn					ret;

    debug2IOLog ("+AppleUSBAudioMuteControl::updateUSBValue (%d)\n", newValue);

    theValue = (newValue != 0);
	ret = SetCurMute (interfaceNumber, channelNumber, theValue);

#if 0
	if (getSubType () != kIOAudioToggleControlSubTypeLFEMute && getUsage () == kIOAudioControlUsageOutput) {
		IOAudioToggleControl *	iSubMute;
		IORegistryEntry *		start;
		IORegistryEntry *		parent;
		IORegistryEntry *		engine;

		debugIOLog ("Looking for iSub mute control\n");
		start = getParentEntry (gIOServicePlane);
		FailIf (NULL == start, Exit);
		parent = start->getParentEntry (gIOServicePlane);
		FailIf (NULL == parent, Exit);
		engine = parent->childFromPath ("AppleUSBAudioEngine", gIOServicePlane);
		FailIf (NULL == engine, Exit);

		iSubMute = (IOAudioToggleControl *)FindEntryByNameAndProperty (engine, "AppleUSBAudioMuteControl", kIOAudioControlSubTypeKey, kIOAudioToggleControlSubTypeLFEMute);
		engine->release ();

		if (NULL != iSubMute) {
			debugIOLog ("Setting the iSub mute control\n");
			iSubMute->setValue (newValue);
			iSubMute->release ();
		}
	}
#endif
//Exit:
    if (ret != kIOReturnSuccess) {
        debug3IOLog ("++AppleUSBAudioMuteContol:updateUSBValue () - set current value for channel %d failed: 0x%x\n", channelNumber, ret);
    }

    debug2IOLog ("-AppleUSBAudioMuteControl::updateUSBValue (%d)\n", newValue);
}

void AppleUSBAudioMuteControl::updateValueCallback (void *arg1, void *arg2) {
    AppleUSBAudioMuteControl 	*muteControl;
    UInt32						value;

    debug3IOLog ("+AppleUSBAudioMuteControl::updateValueCallback (%d, %d)\n", (UInt32*)arg1, (UInt32*)arg2);
    muteControl = (AppleUSBAudioMuteControl *)arg1;
    value = (UInt32)arg2;

    if (muteControl && OSDynamicCast (AppleUSBAudioMuteControl, muteControl)) {
        muteControl->updateUSBValue (value);
    }

    debug3IOLog ("-AppleUSBAudioMuteControl::updateValueCallback (%d, %d)\n", (UInt32*)arg1, (UInt32*)arg2);
}

#if 0
IORegistryEntry * AppleUSBAudioMuteControl::FindEntryByNameAndProperty (const IORegistryEntry * start, const char * name, const char * key, UInt32 value) {
	OSIterator				*iterator;
	IORegistryEntry			*theEntry;
	IORegistryEntry			*tmpReg;
	OSNumber				*tmpNumber;

	theEntry = NULL;
	iterator = NULL;
	FailIf (NULL == start, Exit);

	iterator = start->getChildIterator (gIOServicePlane);
	FailIf (NULL == iterator, Exit);

	while (NULL == theEntry && (tmpReg = OSDynamicCast (IORegistryEntry, iterator->getNextObject ())) != NULL) {
		if (strcmp (tmpReg->getName (), name) == 0) {
			tmpNumber = OSDynamicCast (OSNumber, tmpReg->getProperty (key));
			if (NULL != tmpNumber && tmpNumber->unsigned32BitValue () == value) {
				theEntry = tmpReg;
				theEntry->retain ();
			}
		}
	}

Exit:
	if (NULL != iterator) {
		iterator->release ();
	}
	return theEntry;
}
#endif
