[plain text]
#include <sys/cdefs.h>
__BEGIN_DECLS
#include <kern/thread_call.h>
__END_DECLS
#include "AppleUSBAudioLevelControl.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 <IOKit/IOPlatformExpert.h>
#include <libkern/OSByteOrder.h>
#define super IOAudioLevelControl
OSDefineMetaClassAndStructors(AppleUSBAudioLevelControl, IOAudioLevelControl)
AppleUSBAudioLevelControl *AppleUSBAudioLevelControl::create (UInt8 theUnitID, UInt8 theInterfaceNumber, UInt8 theControlSelector, UInt8 theChannelNumber, Boolean shouldUpdatePRAM, USBDeviceRequest theUSBDeviceRequest, void *theCallerRefCon, UInt32 subType, UInt32 usage) {
AppleUSBAudioLevelControl * control;
debug8IOLog ("+AppleUSBAudioLevelControl::create (%d, %d, %d, %d, %p, %lX, %lX)\n", theUnitID, theInterfaceNumber, theControlSelector, theChannelNumber, theUSBDeviceRequest, subType, usage);
control = new AppleUSBAudioLevelControl;
FailIf (NULL == control, Exit);
if (FALSE == control->init (theUnitID, theInterfaceNumber, theControlSelector, theChannelNumber, shouldUpdatePRAM, theUSBDeviceRequest, theCallerRefCon, subType, usage)) {
control->release ();
control = NULL;
}
Exit:
debug8IOLog ("-AppleUSBAudioLevelControl::create(%d, %d, %d, %d, %p, %lX, %lX)\n", theUnitID, theInterfaceNumber, theControlSelector, theChannelNumber, theUSBDeviceRequest, subType, usage);
return control;
}
bool AppleUSBAudioLevelControl::init (UInt8 theUnitID, UInt8 theInterfaceNumber, UInt8 theControlSelector, UInt8 theChannelNumber, Boolean shouldUpdatePRAM, USBDeviceRequest theUSBDeviceRequest, void *theCallerRefCon, UInt32 subType, UInt32 usage, OSDictionary *properties) {
const char * channelName = NULL;
SInt16 currentValue;
SInt16 deviceMin;
SInt16 deviceMax;
IOFixed deviceMinDB;
IOFixed deviceMaxDB;
IOFixed resolutionDB;
IOReturn ret;
Boolean result;
Boolean arePRAMControl;
debug8IOLog ("+AppleUSBAudioLevelControl[%p]::init (%d, %d, %d, %d, %p, %p)\n", this, theUnitID, theInterfaceNumber, theControlSelector, theChannelNumber, theUSBDeviceRequest, properties);
result = FALSE;
arePRAMControl = FALSE;
FailIf (NULL == theUSBDeviceRequest, Exit);
setValueThreadCall = thread_call_allocate ((thread_call_func_t)updateValueCallback, this);
FailIf (NULL == setValueThreadCall, Exit);
if (theControlSelector == 0xff) {
theControlSelector = VOLUME_CONTROL;
arePRAMControl = TRUE;
}
if (kIOAudioLevelControlSubTypeLFEVolume == subType) {
theChannelNumber = 1;
}
unitID = theUnitID;
interfaceNumber = theInterfaceNumber;
controlSelector = theControlSelector;
channelNumber = theChannelNumber;
callerRefCon = theCallerRefCon;
usbDeviceRequest = theUSBDeviceRequest;
fShouldUpdatePRAM = shouldUpdatePRAM;
switch (channelNumber) {
case kIOAudioControlChannelIDAll:
channelName = kIOAudioControlChannelNameAll;
break;
case kIOAudioControlChannelIDDefaultLeft:
channelName = kIOAudioControlChannelNameLeft;
break;
case kIOAudioControlChannelIDDefaultRight:
channelName = kIOAudioControlChannelNameRight;
break;
case 0xff:
debugIOLog ("++AppleUSBAudioLevelControl: Does not support channel number 0xff.\n");
return FALSE;
default:
channelName = "Unknown";
break;
}
currentValue = GetCurVolume (interfaceNumber, channelNumber, &ret);
FailIf (kIOReturnSuccess != ret, Exit);
debug3IOLog ("channelNumber %d, currentValue = 0x%X, ", channelNumber, currentValue);
volRes = GetVolumeResolution (interfaceNumber, channelNumber, &ret);
FailIf (kIOReturnSuccess != ret, Exit);
debug2IOLog ("vol res = %d, ", volRes);
deviceMin = GetMinVolume (interfaceNumber, channelNumber, &ret);
FailIf (kIOReturnSuccess != ret, Exit);
debug2IOLog ("deviceMin = 0x%X, ", deviceMin);
deviceMax = GetMaxVolume (interfaceNumber, channelNumber, &ret);
FailIf (kIOReturnSuccess != ret, Exit);
debug2IOLog ("deviceMax = 0x%X\n", deviceMax);
if ((SInt16)0x8000 == deviceMin) {
deviceMin = (SInt16)0x8001;
debug2IOLog ("deviceMin adjusted to = %d\n", deviceMin);
}
deviceMinDB = ConvertUSBVolumeTodB (deviceMin);
deviceMaxDB = ConvertUSBVolumeTodB (deviceMax);
resolutionDB = ConvertUSBVolumeTodB (volRes);
offset = -deviceMin;
debug2IOLog ("offset = %d\n", offset);
currentValue = (currentValue + offset) / volRes;
if (deviceMin < 0 && deviceMax > 0) {
deviceMax += volRes;
debug2IOLog ("deviceMax adjusted to = 0x%X\n", deviceMax);
}
deviceMax = ((deviceMin + offset) + (deviceMax + offset)) / volRes;
if (kIOAudioLevelControlSubTypeLFEVolume == subType) {
currentValue = currentValue / 2;
updateUSBValue (currentValue);
}
fMaxVolume = deviceMax;
fMinVolume = deviceMin + offset;
deviceMin = -1;
if (arePRAMControl) {
UInt8 curPRAMVol;
IODTPlatformExpert * platform = NULL;
curPRAMVol = 0;
platform = OSDynamicCast (IODTPlatformExpert, getPlatform ());
if (NULL != platform) {
platform->readXPRAM ((IOByteCount)kPRamVolumeAddr, &curPRAMVol, (IOByteCount)1);
curPRAMVol = (curPRAMVol & 0xF8);
}
currentValue = curPRAMVol;
deviceMin = 0;
deviceMax = 7;
subType = kIOAudioLevelControlSubTypePRAMVolume;
channelName = kIOAudioControlChannelNameAll;
theChannelNumber = 0; }
if (kIOAudioLevelControlSubTypeLFEVolume == subType) {
theChannelNumber = 0;
channelName = kIOAudioControlChannelNameAll;
}
debug4IOLog ("min = %d, max = %d, current = %d\n", deviceMin, deviceMax, currentValue);
FailIf (FALSE == super::init (currentValue, deviceMin, deviceMax, deviceMinDB, deviceMaxDB, theChannelNumber, channelName, 0, subType, usage), Exit);
result = TRUE;
Exit:
debug8IOLog ("-AppleUSBAudioLevelControl[%p]::init (%d, %d, %d, %d, %p, %p)\n", this, theUnitID, theInterfaceNumber, theControlSelector, theChannelNumber, theUSBDeviceRequest, properties);
return result;
}
void AppleUSBAudioLevelControl::free () {
debug2IOLog ("+AppleUSBAudioLevelControl[%p]::free ()\n", this);
if (setValueThreadCall) {
thread_call_free (setValueThreadCall);
setValueThreadCall = NULL;
}
super::free ();
debug2IOLog ("-AppleUSBAudioLevelControl[%p]::free ()\n", this);
}
IOReturn AppleUSBAudioLevelControl::performValueChange (OSObject * newValue) {
OSNumber * newValueAsNumber;
SInt32 newValueAsSInt32;
debug3IOLog ("+AppleUSBAudioLevelControl[%p]::performValueChange (%d)\n", this, newValue);
newValueAsNumber = OSDynamicCast (OSNumber, newValue);
FailIf (NULL == newValueAsNumber, Exit);
newValueAsSInt32 = newValueAsNumber->unsigned32BitValue ();
debug3IOLog ("++AppleUSBAudioLevelControl[%p]::performValueChange (%ld)\n", this, newValueAsSInt32);
if (NULL != setValueThreadCall) {
thread_call_enter1 (setValueThreadCall, (thread_call_param_t)newValueAsSInt32);
}
debug3IOLog ("-AppleUSBAudioLevelControl[%p]::performValueChange (%d)\n", this, newValueAsSInt32);
Exit:
return kIOReturnSuccess;
}
void AppleUSBAudioLevelControl::updateUSBValue () {
updateUSBValue (getIntValue ());
}
void AppleUSBAudioLevelControl::updateUSBValue (SInt32 newValue) {
SInt16 theValue;
SInt16 newVolume;
IOReturn ret;
debug3IOLog ("+AppleUSBAudioLevelControl[%p]::updateUSBValue (%d)\n", this, newValue);
if (newValue < 0) {
newVolume = 0x8000;
} else {
if (newValue > 0) {
newVolume = ((newValue - 1) * volRes) - offset;
} else {
newVolume = (newValue * volRes) - offset;
}
}
theValue = HostToUSBWord (newVolume);
debug2IOLog ("setting volume to 0x%X\n", newVolume);
ret = SetCurVolume (interfaceNumber, channelNumber, theValue);
if (getSubType () == kIOAudioLevelControlSubTypeLFEVolume) {
ret = SetCurVolume (interfaceNumber, 2, theValue);
}
if (TRUE == fShouldUpdatePRAM && FALSE == gExpertMode) { WritePRAMVol (newValue, newValue);
}
#if 0
if (getSubType () != kIOAudioLevelControlSubTypeLFEVolume && getUsage () == kIOAudioControlUsageOutput) {
IOAudioLevelControl * iSubVolume;
IORegistryEntry * start;
IORegistryEntry * parent;
IORegistryEntry * engine;
iSubVolume = NULL;
debugIOLog ("Looking for iSub volume 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);
iSubVolume = (IOAudioLevelControl *)FindEntryByNameAndProperty (engine, "AppleUSBAudioLevelControl", kIOAudioControlSubTypeKey, kIOAudioLevelControlSubTypeLFEVolume);
engine->release ();
if (NULL != iSubVolume) {
newiSubVolume = ((newValue * kiSubMaxVolume) / getMaxValue ()) * kiSubVolumePercent / 100;
debugIOLog ("Setting the iSub volume control\n");
iSubVolume->setValue (newiSubVolume);
iSubVolume->release ();
}
}
#endif
if (ret != kIOReturnSuccess) {
debug4IOLog ("++AppleUSBAudioLevelContol:updateUSBValue () - set current value for %d:%d failed: 0x%X\n", controlSelector, channelNumber, ret);
}
debug3IOLog ("-AppleUSBAudioLevelControl[%p]::updateUSBValue (%d)\n", this, newValue);
}
SInt16 AppleUSBAudioLevelControl::GetCurVolume (UInt8 interfaceNumber, UInt8 channelNumber, IOReturn * error) {
IOUSBDevRequest devReq;
SInt16 theVolume;
devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
devReq.bRequest = GET_CUR;
devReq.wValue = (controlSelector << 8) | channelNumber;
devReq.wIndex = (unitID << 8) | interfaceNumber;
devReq.wLength = 2;
devReq.pData = &theVolume;
*error = usbDeviceRequest (&devReq, callerRefCon);
FailIf (kIOReturnSuccess != *error, Error);
Exit:
return USBToHostWord (theVolume);
Error:
theVolume = 0;
goto Exit;
}
SInt16 AppleUSBAudioLevelControl::GetMaxVolume (UInt8 interfaceNumber, UInt8 channelNumber, IOReturn * error) {
IOUSBDevRequest devReq;
SInt16 theVolume;
devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
devReq.bRequest = GET_MAX;
devReq.wValue = (controlSelector << 8) | channelNumber;
devReq.wIndex = (unitID << 8) | interfaceNumber;
devReq.wLength = 2;
devReq.pData = &theVolume;
*error = usbDeviceRequest (&devReq, callerRefCon);
FailIf (kIOReturnSuccess != *error, Error);
Exit:
return USBToHostWord (theVolume);
Error:
theVolume = 0;
goto Exit;
}
SInt16 AppleUSBAudioLevelControl::GetMinVolume (UInt8 interfaceNumber, UInt8 channelNumber, IOReturn * error) {
IOUSBDevRequest devReq;
SInt16 theVolume;
devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
devReq.bRequest = GET_MIN;
devReq.wValue = (controlSelector << 8) | channelNumber;
devReq.wIndex = (unitID << 8) | interfaceNumber;
devReq.wLength = 2;
devReq.pData = &theVolume;
*error = usbDeviceRequest (&devReq, callerRefCon);
FailIf (kIOReturnSuccess != *error, Error);
Exit:
return USBToHostWord (theVolume);
Error:
theVolume = 0;
goto Exit;
}
UInt16 AppleUSBAudioLevelControl::GetVolumeResolution (UInt8 interfaceNumber, UInt8 channelNumber, IOReturn * error) {
IOUSBDevRequest devReq;
UInt16 theResolution;
devReq.bmRequestType = USBmakebmRequestType (kUSBIn, kUSBClass, kUSBInterface);
devReq.bRequest = GET_RES;
devReq.wValue = (controlSelector << 8) | channelNumber;
devReq.wIndex = (unitID << 8) | interfaceNumber;
devReq.wLength = 2;
devReq.pData = &theResolution;
*error = usbDeviceRequest (&devReq, callerRefCon);
FailIf (kIOReturnSuccess != *error, Error);
Exit:
return USBToHostWord (theResolution);
Error:
theResolution = 0;
goto Exit;
}
IOReturn AppleUSBAudioLevelControl::SetCurVolume (UInt8 interfaceNumber, UInt8 channelNumber, SInt16 volume) {
IOUSBDevRequest devReq;
IOReturn error;
devReq.bmRequestType = USBmakebmRequestType (kUSBOut, kUSBClass, kUSBInterface);
devReq.bRequest = SET_CUR;
devReq.wValue = (controlSelector << 8) | channelNumber;
devReq.wIndex = (unitID << 8) | interfaceNumber;
devReq.wLength = 2;
devReq.pData = &volume;
FailIf ((TRUE == isInactive()), DeviceInactive); error = usbDeviceRequest (&devReq, callerRefCon);
FailIf (kIOReturnSuccess != error, Exit);
Exit:
return error;
DeviceInactive:
debugIOLog("AppleUSBAudioLevelControl::SetCurVolume ERROR attempt to send a device request to and inactive device\n");
error = kIOReturnError;
goto Exit;
}
void AppleUSBAudioLevelControl::updateValueCallback (void *arg1, void *arg2) {
AppleUSBAudioLevelControl *levelControl;
SInt32 value;
UInt32 subType;
debug3IOLog ("+AppleUSBAudioLevelControl::updateValueCallback (%p, %p)\n", (UInt32*)arg1, (UInt32*)arg2);
levelControl = OSDynamicCast (AppleUSBAudioLevelControl, (OSObject*)arg1);
value = (SInt32)arg2;
if (levelControl) {
subType = levelControl->getSubType ();
if (kIOAudioLevelControlSubTypePRAMVolume == subType) {
UInt8 curPRAMVol;
IODTPlatformExpert * platform = NULL;
platform = OSDynamicCast (IODTPlatformExpert, getPlatform ());
if (NULL != platform) {
platform->readXPRAM ((IOByteCount)kPRamVolumeAddr, &curPRAMVol, (IOByteCount)1);
curPRAMVol = (curPRAMVol & 0xF8) | value;
platform->writeXPRAM ((IOByteCount)kPRamVolumeAddr, &curPRAMVol, (IOByteCount)1);
}
} else {
levelControl->updateUSBValue (value);
}
}
debug3IOLog ("-AppleUSBAudioLevelControl::updateValueCallback (%p, %p)\n", (UInt32*)arg1, (UInt32*)arg2);
}
IOFixed AppleUSBAudioLevelControl::ConvertUSBVolumeTodB (SInt16 volume) {
IOFixed dBVolumeFixed;
if (volume == (SInt16)0x8000) {
dBVolumeFixed = ((SInt16)0x8000 * 256) << 8; } else {
dBVolumeFixed = volume * 256;
}
debug3IOLog ("volume = %d, dBVolumeFixed = 0x%X\n", volume, dBVolumeFixed);
return dBVolumeFixed;
}
#if 0
IORegistryEntry * AppleUSBAudioLevelControl::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
#pragma mark +PRAM VOLUME
UInt8 AppleUSBAudioLevelControl::VolumeToPRAMValue (SInt32 leftVol, SInt32 rightVol) {
UInt32 pramVolume; UInt32 averageVolume; const UInt32 volumeRange = (fMaxVolume - fMinVolume + 1);
UInt32 volumeSteps;
if (leftVol < 0)
leftVol = 0;
if (rightVol < 0)
rightVol = 0;
averageVolume = (leftVol + rightVol) >> 1; volumeSteps = volumeRange / kMaximumPRAMVolume; pramVolume = averageVolume / volumeSteps;
if ((pramVolume == 0) && (leftVol != 0 || rightVol !=0 )) {
pramVolume = 1;
}
return (pramVolume & 0x07);
}
void AppleUSBAudioLevelControl::WritePRAMVol (SInt32 leftVol, SInt32 rightVol) {
UInt8 pramVolume;
UInt8 curPRAMVol;
IODTPlatformExpert * platform = NULL;
platform = OSDynamicCast (IODTPlatformExpert, getPlatform ());
debug3IOLog ("AppleUSBTrinityAudioDevice::WritePRAMVol leftVol=%lu, rightVol=%lu\n",leftVol, rightVol);
if (NULL != platform) {
pramVolume = VolumeToPRAMValue (leftVol, rightVol);
platform->readXPRAM ((IOByteCount)kPRamVolumeAddr, &curPRAMVol, (IOByteCount)1);
if (pramVolume != (curPRAMVol & 0x07)) {
curPRAMVol = (curPRAMVol & 0xF8) | pramVolume;
debug2IOLog ("AppleUSBAudioLevelControl::WritePRAMVol curPRAMVol=0x%x\n", curPRAMVol);
platform->writeXPRAM ((IOByteCount)kPRamVolumeAddr, &curPRAMVol, (IOByteCount) 1);
}
}
}
Generated by GNU enscript 1.6.4.