Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added VESA support to DDC for some more modern monitors #18

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions NativeDisplayBrightness/DDC.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#define AUTO_SIZE_CENTER 0x1E
#define WIDTH 0x22
#define HEIGHT 0x32
#define VERTICAL_POS 0x30
#define VERTICAL_POS 0x30
#define HORIZONTAL_POS 0x20
#define PINCUSHION_AMP 0x24
#define PINCUSHION_PHASE 0x42
Expand All @@ -45,7 +45,6 @@
#define RED_BLACK_LEVEL 0x6C
#define GREEN_BLACK_LEVEL 0x6E
#define BLUE_BLACK_LEVEL 0x70
#define ORIENTATION 0xAA
#define AUDIO_MUTE 0x8D
#define SETTINGS 0xB0 //unsure on this one
#define ON_SCREEN_DISPLAY 0xCA
Expand Down Expand Up @@ -85,6 +84,7 @@ struct EDID {
UInt8 versionmajor : 8;
UInt8 versionminor : 8;
UInt8 digitalinput : 1;
union inputbitmap {
struct digitalinput {
UInt8 : 6;
UInt8 dfp : 1;
Expand All @@ -97,6 +97,7 @@ struct EDID {
UInt8 green : 1;
UInt8 serrated : 1;
} analog;
};
UInt8 maxh : 8;
UInt8 maxv : 8;
UInt8 gamma : 8;
Expand Down Expand Up @@ -181,6 +182,7 @@ struct EDID {
UInt8 interlaced : 1;
UInt8 stereo : 2;
UInt8 synctype : 2;
union sync {
struct analogsync {
UInt8 serrated : 1;
UInt8 syncall : 1;
Expand All @@ -189,6 +191,7 @@ struct EDID {
UInt8 vsync : 1;
UInt8 hsync : 1;
} digital;
};
UInt8 twowaystereo : 1;
} timing;
struct text {
Expand Down
173 changes: 29 additions & 144 deletions NativeDisplayBrightness/DDC.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,102 +7,16 @@
//

#include <IOKit/IOKitLib.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <ApplicationServices/ApplicationServices.h>
#include "DDC.h"
#define kDelayBase 100

static io_service_t IOFramebufferPortFromCGDisplayID(CGDirectDisplayID displayID)
// iterate IOreg's device tree to find the IOFramebuffer mach service port that corresponds to a given CGDisplayID
// replaces CGDisplayIOServicePort: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/index.html#//apple_ref/c/func/CGDisplayIOServicePort
// based on: https://github.com/glfw/glfw/pull/192/files
{
io_iterator_t iter;
io_service_t serv, servicePort = 0;

kern_return_t err = IOServiceGetMatchingServices( kIOMasterPortDefault,
IOServiceMatching(IOFRAMEBUFFER_CONFORMSTO), // IOFramebufferI2CInterface
&iter);

if (err != KERN_SUCCESS)
return 0;

// now recurse the IOReg tree
while ((serv = IOIteratorNext(iter)) != MACH_PORT_NULL)
{
CFDictionaryRef info;
io_name_t name;
CFIndex vendorID = 0, productID = 0, serialNumber = 0;
CFNumberRef vendorIDRef, productIDRef, serialNumberRef;
CFStringRef location = CFSTR("");
//CFStringRef serial = CFSTR("");
Boolean success = 0;

// get metadata from IOreg node
IORegistryEntryGetName(serv, name);
info = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName);

/* When assigning a display ID, Quartz considers the following parameters:Vendor, Model, Serial Number and Position in the I/O Kit registry */
// http://opensource.apple.com//source/IOGraphics/IOGraphics-179.2/IOGraphicsFamily/IOKit/graphics/IOGraphicsTypes.h
CFStringRef locationRef = CFDictionaryGetValue(info, CFSTR(kIODisplayLocationKey));
location = CFStringCreateCopy(NULL, locationRef);
//CFStringRef serialRef = CFDictionaryGetValue(info, CFSTR(kDisplaySerialString));
//serial = CFStringCreateCopy(NULL, serialRef);

if(CFDictionaryGetValueIfPresent(info, CFSTR(kDisplayVendorID), (const void**)&vendorIDRef))
success = CFNumberGetValue(vendorIDRef, kCFNumberCFIndexType, &vendorID);

if(CFDictionaryGetValueIfPresent(info, CFSTR(kDisplayProductID), (const void**)&productIDRef))
success &= CFNumberGetValue(productIDRef, kCFNumberCFIndexType, &productID);

IOItemCount busCount;
IOFBGetI2CInterfaceCount(serv, &busCount);

if (!success || busCount < 1) {
// this does not seem to be a DDC-enabled display, skip it
CFRelease(info);
continue;
} else {
// MacBook built-in screens have IOFBI2CInterfaceIDs=(0) but do not respond to DDC comms
// they also do not have a BusType: IOFBI2CInterfaceInfo = ({"IOI2CBusType"=1 .. })
// if (framebuffer.hasDDCConnect(0)) // https://developer.apple.com/reference/kernel/ioframebuffer/1813510-hasddcconnect?language=objc
// kDisplayBundleKey
// kAppleDisplayTypeKey -- if this is an Apple display, can use IODisplay func to change brightness: http://stackoverflow.com/a/32691700/3878712
}

if(CFDictionaryGetValueIfPresent(info, CFSTR(kDisplaySerialNumber), (const void**)&serialNumberRef))
CFNumberGetValue(serialNumberRef, kCFNumberCFIndexType, &serialNumber);

// compare IOreg's metadata to CGDisplay's metadata to infer if the IOReg's I2C monitor is the display for the given NSScreen.displayID
if (CGDisplayVendorNumber(displayID) != vendorID ||
CGDisplayModelNumber(displayID) != productID ||
CGDisplaySerialNumber(displayID) != serialNumber ) // SN is zero in lots of cases, so duplicate-monitors can confuse us :-/
{
CFRelease(info);
continue;
}

// considering this IOFramebuffer as the match for the CGDisplay, dump out its information
// printf("VN:%ld PN:%ld SN:%ld", vendorID, productID, serialNumber);
// printf(" UN:%d", CGDisplayUnitNumber(displayID));
// printf(" IN:%d", iter);
//printf(" Serial:%s\n", CFStringGetCStringPtr(serial, kCFStringEncodingUTF8));
// printf(" %s %s\n", name, CFStringGetCStringPtr(location, kCFStringEncodingUTF8));
servicePort = serv;
CFRelease(info);
break;
}

IOObjectRelease(iter);
return servicePort;
}
#define kDelayBase 45

dispatch_semaphore_t DisplayQueue(CGDirectDisplayID displayID) {
static UInt64 queueCount = 0;
static struct DDCQueue {CGDirectDisplayID id; dispatch_semaphore_t queue;} *queues = NULL;
dispatch_semaphore_t queue = NULL;
if (!queues)
queues = calloc(50, sizeof(*queues)); //FIXME: specify
queues = calloc(50, sizeof(*queues));//FIXME: specify
UInt64 i = 0;
while (i < queueCount)
if (queues[i].id == displayID)
Expand All @@ -120,9 +34,8 @@ bool DisplayRequest(CGDirectDisplayID displayID, IOI2CRequest *request) {
dispatch_semaphore_t queue = DisplayQueue(displayID);
dispatch_semaphore_wait(queue, DISPATCH_TIME_FOREVER);
bool result = false;
io_service_t framebuffer; // https://developer.apple.com/reference/kernel/ioframebuffer
//if ((framebuffer = CGDisplayIOServicePort(displayID))) { // Deprecated in OSX 10.9
if ((framebuffer = IOFramebufferPortFromCGDisplayID(displayID))) {
io_service_t framebuffer;
if ((framebuffer = CGDisplayIOServicePort(displayID))) {
IOItemCount busCount;
if (IOFBGetI2CInterfaceCount(framebuffer, &busCount) == KERN_SUCCESS) {
IOOptionBits bus = 0;
Expand Down Expand Up @@ -154,74 +67,46 @@ bool DisplayRequest(CGDirectDisplayID displayID, IOI2CRequest *request) {
dispatch_semaphore_signal(queue);
return result && request->result == KERN_SUCCESS;
}

bool DDCWrite(CGDirectDisplayID displayID, struct DDCWriteCommand *write) {
IOI2CRequest request;
UInt8 data[128];

bzero( &request, sizeof(request));

request.commFlags = 0;

request.sendAddress = 0x6E;
request.sendTransactionType = kIOI2CSimpleTransactionType;
request.sendBuffer = (vm_address_t) &data[0];
request.sendBytes = 7;

data[0] = 0x51;
data[1] = 0x84;
data[2] = 0x03;
data[3] = write->control_id;
data[4] = (write->new_value) >> 8;
data[5] = write->new_value & 255;
data[6] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3]^ data[4] ^ data[5];


request.replyTransactionType = kIOI2CNoTransactionType;
request.replyBytes = 0;


IOI2CRequest request = {};
request.commFlags = kIOI2CUseSubAddressCommFlag;
request.sendAddress = 0x6E;
request.sendSubAddress = 0x51;
request.sendTransactionType = kIOI2CSimpleTransactionType;
UInt8 data[6] = {0x80, 0x03, write->control_id, 0x0, write->new_value};
data[0]+=sizeof(data)-2;
data[5] = request.sendAddress ^ request.sendSubAddress ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
request.sendBuffer = (vm_address_t) data;
request.sendBytes = sizeof(data);
request.replyTransactionType = kIOI2CNoTransactionType;
bool result = DisplayRequest(displayID, &request);
return result;
}

bool DDCRead(CGDirectDisplayID displayID, struct DDCReadCommand *read) {
IOI2CRequest request;
IOI2CRequest request = {};
UInt8 reply_data[11] = {};
bool result = false;
UInt8 data[128];


bzero( &request, sizeof(request));

request.commFlags = 0;

request.sendAddress = 0x6E;
request.sendTransactionType = kIOI2CSimpleTransactionType;
request.sendBuffer = (vm_address_t) &data[0];
request.sendBytes = 5;
request.minReplyDelay = kDelayBase;

data[0] = 0x51;
data[1] = 0x82;
data[2] = 0x01;
data[3] = read->control_id;
data[4] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3];

request.replyTransactionType = kIOI2CDDCciReplyTransactionType;
request.replyAddress = 0x6F;
request.replySubAddress = 0x51;

request.commFlags = kIOI2CUseSubAddressCommFlag;
request.sendAddress = 0x6E;
request.sendSubAddress = 0x51;
request.sendTransactionType = kIOI2CSimpleTransactionType;
UInt8 data[4] = {0x80, 0x1, read->control_id};
data[0]+=sizeof(data)-2;
data[3] = request.sendAddress ^ request.sendSubAddress ^ data[0] ^ data[1] ^ data[2];
request.sendBuffer = (vm_address_t) data;
request.sendBytes = sizeof(data);
request.replyAddress = 0x6F;
request.replySubAddress = request.sendSubAddress;
request.replyTransactionType = kIOI2CDDCciReplyTransactionType;
request.replyBuffer = (vm_address_t) reply_data;
request.replyBytes = sizeof(reply_data);

request.minReplyDelay = kDelayBase;
result = DisplayRequest(displayID, &request);
result = (result && reply_data[0] == request.sendAddress && reply_data[2] == 0x2 && reply_data[4] == read->control_id && reply_data[10] == (request.replyAddress ^ request.replySubAddress ^ reply_data[1] ^ reply_data[2] ^ reply_data[3] ^ reply_data[4] ^ reply_data[5] ^ reply_data[6] ^ reply_data[7] ^ reply_data[8] ^ reply_data[9]));
read->max_value = reply_data[7];
read->current_value = reply_data[9];
return result;
}

bool EDIDTest(CGDirectDisplayID displayID, struct EDID *edid) {
IOI2CRequest request = {};
UInt8 data[128] = {};
Expand Down