/*
 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
 
#include "AppleUSBAudioMuteControl.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;

    debugIOLog ("+ AppleUSBAudioMuteControl::create (%d, %d, %d, 0x%x, %lX, %lX)", 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;
        }
    }

    debugIOLog ("- AppleUSBAudioMuteControl::create (%d, %d, %d, 0x%x, %lX, %lX)", 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;
    UInt8								theValue;
    
    debugIOLog ("+ AppleUSBAudioMuteControl[%p]::init (%d, %d, %d, 0x%x, 0x%x, %lX)", this, theUnitID, theInterfaceNumber, theChannelNumber, theUSBDeviceRequest, usage, properties);
    result = FALSE;
    FailIf (NULL == theUSBDeviceRequest, Exit);

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

    mUnitID = theUnitID;
    mInterfaceNumber = theInterfaceNumber;
    mChannelNumber = theChannelNumber;
	mCallerRefCon = theCallerRefCon;
    mUSBDeviceRequest = theUSBDeviceRequest;

    switch (mChannelNumber) 
	{
        case kIOAudioControlChannelIDAll:
            channelName = kIOAudioControlChannelNameAll;
            break;
        case kIOAudioControlChannelIDDefaultLeft:
            channelName = kIOAudioControlChannelNameLeft;
            break;
        case kIOAudioControlChannelIDDefaultRight:
            channelName = kIOAudioControlChannelNameRight;
            break;
        case 0xff:
            debugIOLog ("! AppleUSBAudioMuteControl::init () - Does not support channel number 0xFF.");
            return FALSE;
        default:
            channelName = "Unknown";
            break;
    }
	
	if (kIOAudioToggleControlSubTypeLFEMute == subType) 
	{
		theValue = 0; // 0 = un-mute
		ret = SetCurMute (mInterfaceNumber, mChannelNumber, theValue);
	}

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

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

void AppleUSBAudioMuteControl::free () {
    debugIOLog ("+ AppleUSBAudioMuteControl[%p]::free ()", this);

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

    debugIOLog ("- AppleUSBAudioMuteControl[%p]::free ()", this);
    super::free ();
}

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

    debugIOLog ("+ AppleUSBAudioMuteControl[%p]::performValueChange (%d)", this, newValue); 

	newValueAsNumber = OSDynamicCast (OSNumber, newValue);
	FailIf (NULL == newValueAsNumber, Exit);
	newValueAsSInt32 = newValueAsNumber->unsigned32BitValue ();
	debugIOLog ("? AppleUSBAudioMuteControl[%p]::performValueChange (%ld) [converted]", 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 (mSetValueThreadCall);
    thread_call_enter1 (mSetValueThreadCall, (thread_call_param_t)newValueAsSInt32);
    
    debugIOLog ("- AppleUSBAudioMuteControl::performValueChange (%d)", 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 = (mUnitID << 8) | interfaceNumber;
    devReq.wLength = 1;
    devReq.pData = &theMuteState;

	*error = mUSBDeviceRequest (&devReq, mCallerRefCon, NULL);
    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 = (mUnitID << 8) | interfaceNumber;
    devReq.wLength = 1;
    devReq.pData = &theMuteState;

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

Exit:
    return error;

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

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

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

    debugIOLog ("+ AppleUSBAudioMuteControl::updateUSBValue (%d)", newValue);

    theValue = (newValue != 0);
	ret = SetCurMute (mInterfaceNumber, mChannelNumber, theValue);

    if (ret != kIOReturnSuccess) 
	{
        debugIOLog ("! AppleUSBAudioMuteContol:updateUSBValue () - set current value for channel %d failed: 0x%x", mChannelNumber, ret);
    }

    debugIOLog ("- AppleUSBAudioMuteControl::updateUSBValue (%d)", newValue);
}

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

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

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

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