-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jean THOMAS
committed
Feb 7, 2017
0 parents
commit 7ae0992
Showing
7 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
bin/* | ||
obj/* | ||
|
||
!bin/.gitkeep | ||
!obj/.gitkeep |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# project name (generate executable with this name) | ||
TARGET = trinityenabler | ||
|
||
# compiling flags here | ||
CFLAGS = -std=c99 -Wall -I. -pedantic -Werror -O3 | ||
|
||
# linking flags here | ||
LFLAGS = -Wall -I. -lm -framework IOKit -framework Foundation | ||
|
||
# change these to proper directories where each file should be | ||
SRCDIR = src | ||
OBJDIR = obj | ||
BINDIR = bin | ||
|
||
SOURCES := $(wildcard $(SRCDIR)/*.c) | ||
INCLUDES := $(wildcard $(SRCDIR)/*.h) | ||
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) | ||
rm = rm -f | ||
|
||
|
||
$(BINDIR)/$(TARGET): $(OBJECTS) | ||
$(CC) -o $@ $(LFLAGS) $(OBJECTS) | ||
|
||
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c | ||
$(CC) $(CFLAGS) -c $< -o $@ | ||
|
||
.PHONY: clean | ||
clean: | ||
$(rm) $(OBJECTS) | ||
|
||
.PHONY: remove | ||
remove: clean | ||
$(rm) $(BINDIR)/$(TARGET) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Trinity Enabler | ||
|
||
Apple Pro Speakers (codenamed "Trinity") feature a built-in Micronas UAC3552A USB DAC chip. This chip needs some firmware and some parameters (EQ settings) from the host computer to work properly. Apple's driver took care of this. Unfortunately, Apple Pro Speakers was dropped from Mac OS X when Snow Leopard came out. | ||
|
||
This utility provides Apple Pro Speakers support in recent macOS versions. | ||
|
||
## Howto ## | ||
|
||
### Using release builds ### | ||
|
||
Download the utility from the [release page](https://github.com/jeanthom/trinityenabler/releases). Drag'n'drop it into a terminal, and type in the matching power rating for your USB port (in most cases it will be `--power-500` or `--power-1500`). Hit the enter key, and your Apple Pro Speakers should be working :) | ||
|
||
### Compile it yourself ### | ||
|
||
``` | ||
make | ||
bin/trinityenabler --power-500 | ||
``` |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
Copyright (c) 2017 Jean THOMAS. | ||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the "Software"), | ||
to deal in the Software without restriction, including without limitation | ||
the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
and/or sell copies of the Software, and to permit persons to whom the Software | ||
is furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | ||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <string.h> | ||
#include <CoreFoundation/CoreFoundation.h> | ||
#include <libkern/OSByteOrder.h> | ||
#include <IOKit/IOKitLib.h> | ||
#include <IOKit/IOCFPlugIn.h> | ||
#include <IOKit/usb/IOUSBLib.h> | ||
#include <IOKit/usb/USBSpec.h> | ||
#include "main.h" | ||
|
||
UInt8 disableplugin_value= 0xba; | ||
|
||
int main(int argc, char *argv[]) { | ||
int i, ret; | ||
enum TrinityAvailablePower availablePower; | ||
IOUSBDeviceInterface300** deviceInterface; | ||
|
||
/* Reading power delivery capacity */ | ||
availablePower = POWER_NULL; | ||
for (i = 1; i < argc; i++) { | ||
if (strcmp(argv[i], "--power-500") == 0) { | ||
printf("Audio device set to 500mA\n"); | ||
availablePower = POWER_500MA; | ||
} else if (strcmp(argv[i], "--power-1500") == 0) { | ||
printf("Audio device set to 1500mA\n"); | ||
availablePower = POWER_1500MA; | ||
} else if (strcmp(argv[i], "--power-3000") == 0) { | ||
printf("Audio device set to 3000mA\n"); | ||
availablePower = POWER_3000MA; | ||
} else if (strcmp(argv[i], "--power-4000") == 0) { | ||
printf("Audio device set to 4000mA\n"); | ||
availablePower = POWER_4000MA; | ||
} | ||
} | ||
|
||
if (availablePower == POWER_NULL) { | ||
printf("Available power settings :\n"); | ||
printf("\t--power-500\t500mA\n"); | ||
printf("\t--power-1500\t1500mA\n"); | ||
printf("\t--power-3000\t3000mA\n"); | ||
printf("\t--power-4000\t4000mA\n"); | ||
|
||
return EXIT_SUCCESS; | ||
} | ||
|
||
/* Getting USB device interface */ | ||
deviceInterface = usbDeviceInterfaceFromVIDPID(0x05AC,0x1101); | ||
if (deviceInterface == NULL) { | ||
return EXIT_FAILURE; | ||
} | ||
|
||
/* Opening USB device interface */ | ||
ret = (*deviceInterface)->USBDeviceOpen(deviceInterface); | ||
if (ret == kIOReturnSuccess) { | ||
|
||
} else if (ret == kIOReturnExclusiveAccess) { | ||
printf("HID manager has already taken care of this device. Let's try anyway.\n"); | ||
} else { | ||
printf("Could not open device. Quitting…\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
if (disablePlugin(deviceInterface) != kIOReturnSuccess) { | ||
printf("Error while disabling plugin.\n"); | ||
return EXIT_FAILURE; | ||
} | ||
if (downloadEQ(deviceInterface, availablePower) != kIOReturnSuccess) { | ||
printf("Error while downloading EQ to Trinity audio device.\n"); | ||
return EXIT_FAILURE; | ||
} | ||
if (downloadPlugin(deviceInterface) != kIOReturnSuccess) { | ||
printf("Error while downloading plugin to Trinity audio device.\n"); | ||
return EXIT_FAILURE; | ||
} | ||
if (enablePlugin(deviceInterface) != kIOReturnSuccess) { | ||
printf("Error while enabling plugin.\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
/* Closing the USB device */ | ||
(*deviceInterface)->USBDeviceClose(deviceInterface); | ||
|
||
return EXIT_SUCCESS; | ||
} | ||
|
||
IOUSBDeviceInterface300** usbDeviceInterfaceFromVIDPID(SInt32 vid, SInt32 pid) { | ||
CFMutableDictionaryRef matchingDict; | ||
io_iterator_t usbRefIterator; | ||
io_service_t usbRef; | ||
IOCFPlugInInterface** plugin; | ||
IOUSBDeviceInterface300** deviceInterface; | ||
SInt32 score; | ||
|
||
/* Creating a matching dictionary to match the device's PID and VID */ | ||
matchingDict = IOServiceMatching(kIOUSBDeviceClassName); | ||
CFDictionaryAddValue(matchingDict, | ||
CFSTR(kUSBVendorID), | ||
CFNumberCreate(kCFAllocatorDefault, | ||
kCFNumberSInt32Type, | ||
&vid)); | ||
CFDictionaryAddValue(matchingDict, | ||
CFSTR(kUSBProductID), | ||
CFNumberCreate(kCFAllocatorDefault, | ||
kCFNumberSInt32Type, | ||
&pid)); | ||
|
||
/* Getting all the devices that are matched by the matching dictionary */ | ||
IOServiceGetMatchingServices(kIOMasterPortDefault, | ||
matchingDict, | ||
&usbRefIterator); | ||
|
||
/* We only use the first USB device */ | ||
usbRef = IOIteratorNext(usbRefIterator); | ||
IOObjectRelease(usbRefIterator); | ||
|
||
if (usbRef == 0) { | ||
printf("%s : Can't find suitable USB audio device\n", __PRETTY_FUNCTION__); | ||
return NULL; | ||
} else { | ||
IOCreatePlugInInterfaceForService(usbRef, | ||
kIOUSBDeviceUserClientTypeID, | ||
kIOCFPlugInInterfaceID, | ||
&plugin, | ||
&score); | ||
IOObjectRelease(usbRef); | ||
(*plugin)->QueryInterface(plugin, | ||
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID300), | ||
(LPVOID)&deviceInterface); | ||
(*plugin)->Release(plugin); | ||
|
||
return deviceInterface; | ||
} | ||
} | ||
|
||
IOReturn xdfpSetMem(IOUSBDeviceInterface300** deviceInterface, UInt8 *buf, UInt16 length, UInt16 xdfpAddr) { | ||
IOUSBDevRequest devReq; | ||
|
||
devReq.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice); | ||
devReq.bRequest = kMicronasSetMemReq; | ||
devReq.wValue = 0; | ||
devReq.wIndex = xdfpAddr; | ||
devReq.wLength = length; | ||
devReq.pData = buf; | ||
|
||
return (*deviceInterface)->DeviceRequest(deviceInterface, &devReq); | ||
} | ||
|
||
IOReturn xdfpWrite(IOUSBDeviceInterface300** deviceInterface, UInt16 xdfpAddr, SInt32 value) { | ||
static UInt8 xdfpData[5]; | ||
|
||
if (value < 0) value += 0x40000; | ||
xdfpData[0] = (value >> 10) & 0xff; | ||
xdfpData[1] = (value >> 2) & 0xff; | ||
xdfpData[2] = value & 0x03; | ||
xdfpData[3] = (xdfpAddr >> 8) & 0x03; | ||
xdfpData[4] = xdfpAddr & 0xff; | ||
|
||
return xdfpSetMem(deviceInterface, xdfpData, 5, V8_WRITE_START_ADDR); | ||
} | ||
|
||
IOReturn downloadEQ(IOUSBDeviceInterface300** deviceInterface, enum TrinityAvailablePower availablePower) { | ||
UInt16 xdfpAddr; | ||
UInt32 eqIndex; | ||
IOReturn ret; | ||
static SInt32 *eqSettings; | ||
|
||
switch (availablePower) { | ||
case POWER_4000MA: | ||
eqSettings = power4AEQSettings; | ||
break; | ||
case POWER_3000MA: | ||
eqSettings = power3AEQSettings; | ||
break; | ||
case POWER_1500MA: | ||
eqSettings = power1500mAEQSettings; | ||
break; | ||
default: | ||
case POWER_500MA: | ||
eqSettings = power500mAEQSettings; | ||
break; | ||
} | ||
|
||
for (eqIndex = 0, xdfpAddr = XDFP_STARTING_EQ_ADDR; eqIndex < EQ_TABLE_SIZE; eqIndex++, xdfpAddr++) { | ||
ret = xdfpWrite(deviceInterface, xdfpAddr, eqSettings[eqIndex]); | ||
if (ret != kIOReturnSuccess) { | ||
return ret; | ||
} | ||
|
||
nanosleep((const struct timespec[]){{0, 3000000L}}, NULL); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
IOReturn disablePlugin(IOUSBDeviceInterface300** deviceInterface) { | ||
return xdfpSetMem(deviceInterface, &disableplugin_value, 1, V8_PLUGIN_START_ADDR); | ||
} | ||
|
||
IOReturn enablePlugin(IOUSBDeviceInterface300** deviceInterface) { | ||
return xdfpSetMem(deviceInterface, pluginBinary, 1, V8_PLUGIN_START_ADDR); | ||
} | ||
|
||
IOReturn downloadPlugin(IOUSBDeviceInterface300** deviceInterface) { | ||
return xdfpSetMem(deviceInterface, &pluginBinary[1], sizeof(pluginBinary), V8_PLUGIN_START_ADDR+1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
Copyright (c) 2017 Jean THOMAS. | ||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the "Software"), | ||
to deal in the Software without restriction, including without limitation | ||
the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
and/or sell copies of the Software, and to permit persons to whom the Software | ||
is furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | ||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
/* 0xB042-0xB044 is XDFP (DSP interface) data in */ | ||
/* 0xB045-0xB046 is XDFP address/command interface */ | ||
/* 0xB05C-0xB05E is XDFP data out (unused here) */ | ||
#define V8_WRITE_START_ADDR 0xb042 | ||
|
||
/* This memory address is mapped to the RAM */ | ||
#define V8_PLUGIN_START_ADDR 0x8120 | ||
#define XDFP_STARTING_EQ_ADDR 0x50 | ||
|
||
#define EQ_TABLE_SIZE 16 | ||
|
||
#define kMicronasSetMemReq 4 | ||
#define kMicronasGetMemReq 5136 | ||
|
||
enum TrinityAvailablePower { | ||
POWER_NULL, POWER_500MA, POWER_1500MA, POWER_3000MA, POWER_4000MA | ||
}; | ||
|
||
/* These EQ come from AppleUSBTrinityAudioDevice.cpp */ | ||
static SInt32 power4AEQSettings[] = { | ||
228, -129968, 130513, | ||
-279, -125942, 128415, | ||
-1689, -123355, 126686, | ||
-5136, -95891, 109553, | ||
-18995, -993, 6924, | ||
-45000 | ||
}; | ||
|
||
static SInt32 power3AEQSettings[] = { | ||
228, -129968, 130513, | ||
-279, -125942, 128415, | ||
-1689, -123355, 126686, | ||
-5137, -95891, 109553, | ||
-18995, -993, 6924, | ||
-42000}; | ||
|
||
static SInt32 power1500mAEQSettings[] = { | ||
228, -129968, 130513, | ||
-279, -125942, 128415, | ||
-1689, -123355, 126686, | ||
-5137, -95891, 109553, | ||
-18995, -993, 6924, | ||
-20000}; | ||
|
||
static SInt32 power500mAEQSettings[] = { | ||
228, -129968, 130513, | ||
-279, -125942, 128415, | ||
-1689, -123355, 126686, | ||
-5137, -95891, 109553, | ||
-18995, -993, 6924, | ||
-8000}; | ||
|
||
/* Plugins are firmware extensions. The plugin is necessary to enable the amplifier chip. */ | ||
static UInt8 pluginBinary[] = { | ||
0xBF, 0x35, 0x81, 0xBA, 0x85, 0xEA, 0x7B, 0x80, 0xE1, 0x13, 0xBF, 0xDE, 0x0B, 0x8D, 0xB9, 0x85, | ||
0xBF, 0x1A, 0x0C, 0x8D, 0xB9, 0xE9, 0xF3, 0x81, 0xE8, 0x80, 0x80, 0x79, 0x90, 0x03, 0xBC, 0xEC, | ||
0x81, 0xEA, 0xA2, 0xB0, 0xE4, 0x00, 0xE5, 0x02, 0x44, 0x99, 0x01, 0x45, 0x15, 0x71, 0x14, 0x19, | ||
0x90, 0xF6, 0xE0, 0xF0, 0xC8, 0x87, 0x80, 0xC8, 0x51, 0xB0, 0x12, 0x63, 0x90, 0x03, 0x28, 0x98, | ||
0x02, 0xE0, 0x40, 0xC8, 0x89, 0x80, 0xC8, 0xA0, 0xB0, 0xE1, 0xFB, 0x12, 0x21, 0xC8, 0x88, 0x80, | ||
0xE8, 0x80, 0x80, 0x90, 0x09, 0xE8, 0x01, 0xA0, 0xC8, 0x80, 0x80, 0xBF, 0x2F, 0x81, 0xE8, 0x80, | ||
0x80, 0xC8, 0xF3, 0x81, 0xE1, 0x0C, 0x12, 0x21, 0x90, 0x25, 0xE9, 0xED, 0x81, 0xE8, 0x7B, 0x80, | ||
0x59, 0x49, 0x74, 0xE9, 0xF0, 0x81, 0xE2, 0x80, 0x2A, 0x73, 0x11, 0x2A, 0x7B, 0x99, 0x0A, 0xE8, | ||
0xF1, 0x81, 0x00, 0xC8, 0xF1, 0x81, 0xCC, 0x7B, 0x80, 0xE8, 0xEE, 0x81, 0xBC, 0xDA, 0x81, 0xE8, | ||
0xF2, 0x81, 0x90, 0x29, 0xE9, 0x7B, 0x80, 0xE8, 0xED, 0x81, 0x51, 0x72, 0xE8, 0xF1, 0x81, 0x98, | ||
0x16, 0x40, 0xC8, 0xF1, 0x81, 0x12, 0xE1, 0x80, 0x29, 0xE1, 0xB0, 0x79, 0x99, 0x04, 0x12, 0xBC, | ||
0xD4, 0x81, 0xE0, 0x30, 0xC8, 0x7B, 0x80, 0xE8, 0xEF, 0x81, 0xC8, 0xF2, 0x81, 0xE8, 0xF2, 0x81, | ||
0x90, 0x03, 0xBC, 0x24, 0x81, 0x40, 0xC8, 0xF2, 0x81, 0xBC, 0x24, 0x81, 0xB9, 0x01, 0x08, 0x0F, | ||
0xD0, 0x01, 0x01, 0x01 | ||
}; | ||
|
||
|
||
int main(int argc, char *argv[]); | ||
IOReturn xdfpSetMem(IOUSBDeviceInterface300** deviceInterface, UInt8 *buf, UInt16 length, UInt16 xdfpAddr); | ||
IOReturn xdfpWrite(IOUSBDeviceInterface300** deviceInterface, UInt16 xdfpAddr, SInt32 value); | ||
IOReturn downloadEQ(IOUSBDeviceInterface300** deviceInterface, enum TrinityAvailablePower availablePower); | ||
IOReturn downloadPlugin(IOUSBDeviceInterface300** deviceInterface); | ||
IOReturn disablePlugin(IOUSBDeviceInterface300** deviceInterface); | ||
IOReturn enablePlugin(IOUSBDeviceInterface300** deviceInterface); | ||
IOUSBDeviceInterface300** usbDeviceInterfaceFromVIDPID(SInt32 vid, SInt32 pid); |