-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First, semi-working ARM Mac support version
- Loading branch information
Showing
10 changed files
with
446 additions
and
199 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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/target | ||
**/*.rs.bk | ||
Cargo.lock | ||
rust-toolchain |
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,99 @@ | ||
use crate::error::Error; | ||
use crate::error::Error::{DisplayLocationNotFound, ServiceNotFound}; | ||
use crate::iokit::CoreDisplay_DisplayCreateInfoDictionary; | ||
use crate::iokit::IoIterator; | ||
use crate::kern_try; | ||
use core_foundation::base::{CFType, TCFType}; | ||
use core_foundation::dictionary::CFDictionary; | ||
use core_foundation::string::CFString; | ||
use core_foundation_sys::base::{kCFAllocatorDefault, CFAllocatorRef, CFTypeRef, OSStatus}; | ||
use core_graphics::display::CGDisplay; | ||
use io_kit_sys::keys::kIOServicePlane; | ||
use io_kit_sys::types::{io_object_t, io_registry_entry_t}; | ||
use io_kit_sys::{ | ||
kIORegistryIterateRecursively, IORegistryEntryCreateCFProperty, IORegistryEntryGetName, IORegistryEntryGetPath, | ||
}; | ||
use std::ffi::CStr; | ||
use std::os::raw::{c_uint, c_void}; | ||
|
||
pub type IOAVService = CFTypeRef; | ||
|
||
#[link(name = "CoreDisplay", kind = "framework")] | ||
extern "C" { | ||
// Creates an IOAVService from an existing I/O Kit service | ||
pub fn IOAVServiceCreateWithService(allocator: CFAllocatorRef, service: io_object_t) -> IOAVService; | ||
|
||
// Reads data over I2C from the specified IOAVService | ||
pub fn IOAVServiceReadI2C( | ||
service: IOAVService, | ||
chip_address: c_uint, | ||
offset: c_uint, | ||
output_buffer: *mut c_void, | ||
output_buffer_size: c_uint, | ||
) -> OSStatus; | ||
|
||
// Writes data over I2C to the specified IOAVService | ||
pub fn IOAVServiceWriteI2C( | ||
service: IOAVService, | ||
chip_address: c_uint, | ||
data_address: c_uint, | ||
input_buffer: *const c_void, | ||
input_buffer_size: c_uint, | ||
) -> OSStatus; | ||
} | ||
|
||
pub fn get_display_av_service(display: CGDisplay) -> Result<IOAVService, Error> { | ||
if display.is_builtin() { | ||
return Err(ServiceNotFound); | ||
} | ||
let display_infos: CFDictionary<CFString, CFType> = | ||
unsafe { CFDictionary::wrap_under_create_rule(CoreDisplay_DisplayCreateInfoDictionary(display.id)) }; | ||
let location = display_infos | ||
.find(CFString::from_static_string("IODisplayLocation")) | ||
.ok_or(DisplayLocationNotFound)? | ||
.downcast::<CFString>() | ||
.ok_or(DisplayLocationNotFound)? | ||
.to_string(); | ||
let external_location = CFString::from_static_string("External").into_CFType(); | ||
|
||
let mut iter = IoIterator::root()?; | ||
while let Some(service) = iter.next() { | ||
if let Ok(registry_location) = get_service_registry_entry_path(service.as_raw()) { | ||
if registry_location == location { | ||
while let Some(service) = iter.next() { | ||
if get_service_registry_entry_name(service.as_raw())? == "DCPAVServiceProxy" { | ||
let av_service = unsafe { IOAVServiceCreateWithService(kCFAllocatorDefault, service.as_raw()) }; | ||
let loc_ref = unsafe { | ||
CFType::wrap_under_create_rule(IORegistryEntryCreateCFProperty( | ||
service.as_raw(), | ||
CFString::from_static_string("Location").as_concrete_TypeRef(), | ||
kCFAllocatorDefault, | ||
kIORegistryIterateRecursively, | ||
)) | ||
}; | ||
if !av_service.is_null() && loc_ref == external_location { | ||
return Ok(av_service); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
Err(ServiceNotFound) | ||
} | ||
|
||
fn get_service_registry_entry_path(entry: io_registry_entry_t) -> Result<String, Error> { | ||
let mut path_buffer = [0_i8; 1024]; | ||
unsafe { | ||
kern_try!(IORegistryEntryGetPath(entry, kIOServicePlane, path_buffer.as_mut_ptr())); | ||
Ok(CStr::from_ptr(path_buffer.as_ptr()).to_string_lossy().into_owned()) | ||
} | ||
} | ||
|
||
fn get_service_registry_entry_name(entry: io_registry_entry_t) -> Result<String, Error> { | ||
let mut name = [0; 128]; | ||
unsafe { | ||
kern_try!(IORegistryEntryGetName(entry, name.as_mut_ptr())); | ||
Ok(CStr::from_ptr(name.as_ptr()).to_string_lossy().into_owned()) | ||
} | ||
} |
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,51 @@ | ||
use core_graphics::base::CGError; | ||
use ddc::ErrorCode; | ||
use io_kit_sys::ret::kIOReturnSuccess; | ||
use mach::kern_return::{kern_return_t, KERN_FAILURE}; | ||
use thiserror::Error; | ||
|
||
/// An error that can occur during DDC/CI communication with a monitor | ||
#[derive(Error, Debug)] | ||
pub enum Error { | ||
/// Core Graphics errors | ||
#[error("Core Graphics error: {0}")] | ||
CoreGraphics(CGError), | ||
/// Kernel I/O errors | ||
#[error("MacOS kernel I/O error: {0}")] | ||
Io(kern_return_t), | ||
/// DDC/CI errors | ||
#[error("DDC/CI error: {0}")] | ||
Ddc(ErrorCode), | ||
/// Service not found | ||
#[error("Service not found")] | ||
ServiceNotFound, | ||
/// Display location not found | ||
#[error("Service not found")] | ||
DisplayLocationNotFound, | ||
} | ||
|
||
pub fn verify_io(result: kern_return_t) -> Result<(), Error> { | ||
if result == kIOReturnSuccess { | ||
Ok(()) | ||
} else { | ||
Err(Error::Io(result)) | ||
} | ||
} | ||
|
||
impl From<std::io::Error> for Error { | ||
fn from(error: std::io::Error) -> Self { | ||
Error::Io(error.raw_os_error().unwrap_or(KERN_FAILURE)) | ||
} | ||
} | ||
|
||
impl From<ErrorCode> for Error { | ||
fn from(error: ErrorCode) -> Self { | ||
Error::Ddc(error) | ||
} | ||
} | ||
|
||
impl From<CGError> for Error { | ||
fn from(error: CGError) -> Self { | ||
Error::CoreGraphics(error) | ||
} | ||
} |
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,130 @@ | ||
use crate::error::{verify_io, Error}; | ||
use crate::iokit::{kIODisplayOnlyPreferredName, IODisplayCreateInfoDictionary}; | ||
use crate::iokit::{ | ||
kIOI2CDDCciReplyTransactionType, kIOI2CNoTransactionType, kIOI2CSimpleTransactionType, IOFBCopyI2CInterfaceForBus, | ||
IOFBGetI2CInterfaceCount, IOI2CRequest, IoI2CInterfaceConnection, | ||
}; | ||
use crate::iokit::{IoIterator, IoObject}; | ||
use core_foundation::base::{CFType, TCFType}; | ||
use core_foundation::dictionary::CFDictionary; | ||
use core_foundation::number::CFNumber; | ||
use core_foundation::string::CFString; | ||
use core_foundation_sys::base::kCFAllocatorDefault; | ||
use core_graphics::display::CGDisplay; | ||
use ddc::{Command, CommandResult}; | ||
use io_kit_sys::ret::kIOReturnSuccess; | ||
use io_kit_sys::types::{io_service_t, IOItemCount}; | ||
use io_kit_sys::IORegistryEntryCreateCFProperties; | ||
use mach::kern_return::KERN_FAILURE; | ||
|
||
pub fn display_info_dict(frame_buffer: &IoObject) -> Option<CFDictionary<CFString, CFType>> { | ||
unsafe { | ||
let info = IODisplayCreateInfoDictionary(frame_buffer.into(), kIODisplayOnlyPreferredName).as_ref()?; | ||
Some(CFDictionary::<CFString, CFType>::wrap_under_create_rule(info)) | ||
} | ||
} | ||
|
||
/// Get supported I2C / DDC transaction types | ||
/// DDCciReply is what we want, but Simple will also work | ||
pub unsafe fn get_supported_transaction_type() -> Option<u32> { | ||
let transaction_types_key = CFString::from_static_string("IOI2CTransactionTypes"); | ||
|
||
for io_service in IoIterator::for_service_names("IOFramebufferI2CInterface")? { | ||
let mut service_properties = std::ptr::null_mut(); | ||
if IORegistryEntryCreateCFProperties( | ||
(&io_service).into(), | ||
&mut service_properties, | ||
kCFAllocatorDefault as _, | ||
0, | ||
) == kIOReturnSuccess | ||
{ | ||
let info = CFDictionary::<CFString, CFType>::wrap_under_create_rule(service_properties as _); | ||
let transaction_types = info.find(&transaction_types_key)?.downcast::<CFNumber>()?.to_i64()?; | ||
if ((1 << kIOI2CDDCciReplyTransactionType) & transaction_types) != 0 { | ||
return Some(kIOI2CDDCciReplyTransactionType); | ||
} else if ((1 << kIOI2CSimpleTransactionType) & transaction_types) != 0 { | ||
return Some(kIOI2CSimpleTransactionType); | ||
} | ||
} | ||
} | ||
None | ||
} | ||
|
||
/// Finds if a framebuffer that matches display | ||
fn framebuffer_port_matches_display(port: &IoObject, display: CGDisplay) -> Option<()> { | ||
let mut bus_count: IOItemCount = 0; | ||
unsafe { | ||
IOFBGetI2CInterfaceCount(port.into(), &mut bus_count); | ||
} | ||
if bus_count == 0 { | ||
return None; | ||
}; | ||
|
||
let info = display_info_dict(port)?; | ||
|
||
let display_vendor_key = CFString::from_static_string("DisplayVendorID"); | ||
let display_product_key = CFString::from_static_string("DisplayProductID"); | ||
let display_serial_key = CFString::from_static_string("DisplaySerialNumber"); | ||
|
||
let display_vendor = info.find(&display_vendor_key)?.downcast::<CFNumber>()?.to_i64()? as u32; | ||
let display_product = info.find(&display_product_key)?.downcast::<CFNumber>()?.to_i64()? as u32; | ||
// Display serial number is not always present. If it's not there, default to zero | ||
// (to match what CGDisplay.serial_number() returns | ||
let display_serial = info | ||
.find(&display_serial_key) | ||
.and_then(|x| x.downcast::<CFNumber>()) | ||
.and_then(|x| x.to_i32()) | ||
.map(|x| x as u32) | ||
.unwrap_or(0); | ||
|
||
if display_vendor == display.vendor_number() | ||
&& display_product == display.model_number() | ||
&& display_serial == display.serial_number() | ||
{ | ||
Some(()) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Gets the framebuffer port for a display | ||
pub fn get_io_framebuffer_port(display: CGDisplay) -> Option<IoObject> { | ||
if display.is_builtin() { | ||
return None; | ||
} | ||
IoIterator::for_services("IOFramebuffer")? | ||
.find(|framebuffer| framebuffer_port_matches_display(framebuffer, display).is_some()) | ||
} | ||
|
||
/// send an I2C request to a display | ||
pub(crate) unsafe fn send_request( | ||
service: &IoObject, | ||
request: &mut IOI2CRequest, | ||
post_request_delay: u32, | ||
) -> Result<(), Error> { | ||
let mut bus_count = 0; | ||
let mut result: Result<(), Error> = Err(Error::Io(KERN_FAILURE)); | ||
verify_io(IOFBGetI2CInterfaceCount(service.into(), &mut bus_count))?; | ||
for bus in 0..bus_count { | ||
let mut interface: io_service_t = 0; | ||
if IOFBCopyI2CInterfaceForBus(service.into(), bus, &mut interface) == kIOReturnSuccess { | ||
let interface = IoObject::from(interface); | ||
result = IoI2CInterfaceConnection::new(&interface) | ||
.and_then(|connection| connection.send_request(request)) | ||
.map_err(From::from); | ||
if result.is_ok() { | ||
break; | ||
} | ||
} | ||
} | ||
std::thread::sleep(std::time::Duration::from_millis(post_request_delay as u64)); | ||
result | ||
} | ||
|
||
pub(crate) fn get_response_transaction_type<C: Command>() -> u32 { | ||
if C::Ok::MAX_LEN == 0 { | ||
kIOI2CNoTransactionType | ||
} else { | ||
unsafe { get_supported_transaction_type().unwrap_or(kIOI2CNoTransactionType) } | ||
} | ||
} |
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
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
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 |
---|---|---|
@@ -1,5 +1,9 @@ | ||
#[macro_use] | ||
pub(crate) mod errors; | ||
pub(crate) mod display; | ||
pub(crate) mod io2c_interface; | ||
pub(crate) mod wrappers; | ||
mod display; | ||
mod io2c_interface; | ||
mod wrappers; | ||
|
||
pub(crate) use display::*; | ||
pub(crate) use io2c_interface::*; | ||
pub(crate) use wrappers::*; |
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
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 |
---|---|---|
|
@@ -19,7 +19,11 @@ | |
//! # } | ||
//! ``` | ||
|
||
mod arm; | ||
mod error; | ||
mod intel; | ||
mod iokit; | ||
mod monitor; | ||
|
||
pub use error::*; | ||
pub use monitor::*; |
Oops, something went wrong.