Skip to content

Commit

Permalink
Separate ARM/Intel functions into their own modules
Browse files Browse the repository at this point in the history
  • Loading branch information
haimgel committed Nov 5, 2024
1 parent 7e28fcf commit 93d7602
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 113 deletions.
2 changes: 1 addition & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
max_width = 120
edition = "2021"
style_edition = "2021"
use_try_shorthand = true
86 changes: 61 additions & 25 deletions src/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use crate::error::Error;
use crate::error::Error::{DisplayLocationNotFound, ServiceNotFound};
use crate::iokit::IoIterator;
use crate::iokit::{CoreDisplay_DisplayCreateInfoDictionary, IoObject};
use crate::kern_try;
use crate::{kern_try, verify_io};
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 ddc::I2C_ADDRESS_DDC_CI;
use ddc::{I2C_ADDRESS_DDC_CI, SUB_ADDRESS_DDC_CI};
use io_kit_sys::keys::kIOServicePlane;
use io_kit_sys::types::{io_object_t, io_registry_entry_t};
use io_kit_sys::{
Expand All @@ -18,35 +18,46 @@ use io_kit_sys::{
use mach::kern_return::KERN_SUCCESS;
use std::ffi::CStr;
use std::os::raw::{c_uint, c_void};
use std::time::Duration;

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(crate) fn execute<'a>(
service: &IOAVService,
i2c_address: u16,
request_data: &[u8],
out: &'a mut [u8],
response_delay: Duration,
) -> Result<&'a mut [u8], crate::error::Error> {
unsafe {
verify_io(IOAVServiceWriteI2C(
*service,
i2c_address as _, // I2C_ADDRESS_DDC_CI as u32,
SUB_ADDRESS_DDC_CI as _,
// Skip the first byte, which is the I2C address, which this API does not need
request_data[1..].as_ptr() as _,
(request_data.len() - 1) as _, // command_length as u32 + 3,
))?;
};
if !out.is_empty() {
std::thread::sleep(response_delay);
unsafe {
verify_io(IOAVServiceReadI2C(
*service,
i2c_address as _, // I2C_ADDRESS_DDC_CI as u32,
0,
out.as_ptr() as _,
out.len() as u32,
))?;
};
Ok(out)
} else {
Ok(&mut [0u8; 0])
}
}

/// Returns an AVService and its DDC I2C address for a given display
pub fn get_display_av_service(display: CGDisplay) -> Result<(IOAVService, u16), Error> {
pub(crate) fn get_display_av_service(display: CGDisplay) -> Result<(IOAVService, u16), Error> {
if display.is_builtin() {
return Err(ServiceNotFound);
}
Expand Down Expand Up @@ -137,3 +148,28 @@ fn get_service_registry_entry_name(entry: io_registry_entry_t) -> Result<String,
Ok(CStr::from_ptr(name.as_ptr()).to_string_lossy().into_owned())
}
}

#[link(name = "CoreDisplay", kind = "framework")]
extern "C" {
// Creates an IOAVService from an existing I/O Kit service
fn IOAVServiceCreateWithService(allocator: CFAllocatorRef, service: io_object_t) -> IOAVService;

// Reads data over I2C from the specified IOAVService
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
fn IOAVServiceWriteI2C(
service: IOAVService,
chip_address: c_uint,
data_address: c_uint,
input_buffer: *const c_void,
input_buffer_size: c_uint,
) -> OSStatus;
}

51 changes: 45 additions & 6 deletions src/intel.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::{verify_io, Error};
use crate::iokit::{kIODisplayOnlyPreferredName, IODisplayCreateInfoDictionary};
use crate::iokit::{kIODisplayOnlyPreferredName, kIOI2CNoTransactionType, IODisplayCreateInfoDictionary};
use crate::iokit::{
kIOI2CDDCciReplyTransactionType, kIOI2CSimpleTransactionType, IOFBCopyI2CInterfaceForBus, IOFBGetI2CInterfaceCount,
IOI2CRequest, IoI2CInterfaceConnection,
Expand All @@ -11,12 +11,52 @@ use core_foundation::number::CFNumber;
use core_foundation::string::CFString;
use core_foundation_sys::base::kCFAllocatorDefault;
use core_graphics::display::CGDisplay;
use ddc::SUB_ADDRESS_DDC_CI;
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;
use std::time::Duration;

pub fn display_info_dict(frame_buffer: &IoObject) -> Option<CFDictionary<CFString, CFType>> {
pub(crate) fn execute<'a>(
service: &IoObject,
i2c_address: u16,
request_data: &[u8],
out: &'a mut [u8],
response_delay: Duration,
) -> Result<&'a mut [u8], crate::error::Error> {
let mut request: IOI2CRequest = unsafe { std::mem::zeroed() };

request.commFlags = 0;
request.sendAddress = (i2c_address << 1) as u32;
request.sendTransactionType = kIOI2CSimpleTransactionType;
request.sendBuffer = &request_data as *const _ as usize;
request.sendBytes = request_data.len() as u32;
request.minReplyDelay = response_delay.as_nanos() as u64;
request.result = -1;

request.replyTransactionType = if out.is_empty() {
kIOI2CNoTransactionType
} else {
unsafe { get_supported_transaction_type().unwrap_or(kIOI2CNoTransactionType) }
};
request.replyAddress = ((i2c_address << 1) | 1) as u32;
request.replySubAddress = SUB_ADDRESS_DDC_CI;

request.replyBuffer = &out as *const _ as usize;
request.replyBytes = out.len() as u32;

unsafe {
send_request(service, &mut request)?;
}
if request.replyTransactionType != kIOI2CNoTransactionType {
Ok(&mut [0u8; 0])
} else {
Ok(out)
}
}

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))
Expand All @@ -25,7 +65,7 @@ pub fn display_info_dict(frame_buffer: &IoObject) -> Option<CFDictionary<CFStrin

/// 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> {
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")? {
Expand Down Expand Up @@ -87,7 +127,7 @@ fn framebuffer_port_matches_display(port: &IoObject, display: CGDisplay) -> Opti
}

/// Gets the framebuffer port for a display
pub fn get_io_framebuffer_port(display: CGDisplay) -> Option<IoObject> {
pub(crate) fn get_io_framebuffer_port(display: CGDisplay) -> Option<IoObject> {
if display.is_builtin() {
return None;
}
Expand All @@ -96,7 +136,7 @@ pub fn get_io_framebuffer_port(display: CGDisplay) -> Option<IoObject> {
}

/// send an I2C request to a display
pub(crate) unsafe fn send_request(
unsafe fn send_request(
service: &IoObject,
request: &mut IOI2CRequest,
// post_request_delay: u32,
Expand All @@ -116,6 +156,5 @@ pub(crate) unsafe fn send_request(
}
}
}
// std::thread::sleep(std::time::Duration::from_millis(post_request_delay as u64));
result
}
85 changes: 4 additions & 81 deletions src/monitor.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
#![deny(missing_docs)]

use crate::arm::{IOAVService, IOAVServiceReadI2C, IOAVServiceWriteI2C};
use crate::error::Error;
use crate::intel::get_supported_transaction_type;
use crate::iokit::CoreDisplay_DisplayCreateInfoDictionary;
use crate::iokit::IoObject;
use crate::iokit::*;
use crate::{arm, intel, verify_io};
use crate::{arm, intel};
use core_foundation::base::{CFType, TCFType};
use core_foundation::data::CFData;
use core_foundation::dictionary::CFDictionary;
Expand All @@ -23,7 +20,7 @@ use std::{fmt, iter};
#[derive(Debug)]
enum MonitorService {
Intel(IoObject),
Arm(IOAVService),
Arm(arm::IOAVService),
}

/// A handle to an attached monitor that allows the use of DDC/CI operations.
Expand Down Expand Up @@ -117,78 +114,6 @@ impl Monitor {
self.monitor
}

fn execute_intel<'a>(
service: &IoObject,
i2c_address: u16,
request_data: &[u8],
out: &'a mut [u8],
response_delay: Duration,
) -> Result<&'a mut [u8], crate::error::Error> {
let mut request: IOI2CRequest = unsafe { std::mem::zeroed() };

request.commFlags = 0;
request.sendAddress = (i2c_address << 1) as u32;
request.sendTransactionType = kIOI2CSimpleTransactionType;
request.sendBuffer = &request_data as *const _ as usize;
request.sendBytes = request_data.len() as u32;
request.minReplyDelay = response_delay.as_nanos() as u64;
request.result = -1;

request.replyTransactionType = if out.is_empty() {
kIOI2CNoTransactionType
} else {
unsafe { get_supported_transaction_type().unwrap_or(kIOI2CNoTransactionType) }
};
request.replyAddress = ((i2c_address << 1) | 1) as u32;
request.replySubAddress = SUB_ADDRESS_DDC_CI;

request.replyBuffer = &out as *const _ as usize;
request.replyBytes = out.len() as u32;

unsafe {
intel::send_request(service, &mut request)?;
}
if request.replyTransactionType != kIOI2CNoTransactionType {
Ok(&mut [0u8; 0])
} else {
Ok(out)
}
}

fn execute_arm<'a>(
service: &IOAVService,
i2c_address: u16,
request_data: &[u8],
out: &'a mut [u8],
response_delay: Duration,
) -> Result<&'a mut [u8], crate::error::Error> {
unsafe {
verify_io(IOAVServiceWriteI2C(
*service,
i2c_address as _, // I2C_ADDRESS_DDC_CI as u32,
SUB_ADDRESS_DDC_CI as _,
// Skip the first byte, which is the I2C address, which this API does not need
request_data[1..].as_ptr() as _,
(request_data.len() - 1) as _, // command_length as u32 + 3,
))?;
};
if !out.is_empty() {
std::thread::sleep(response_delay);
unsafe {
verify_io(IOAVServiceReadI2C(
*service,
i2c_address as _, // I2C_ADDRESS_DDC_CI as u32,
0,
out.as_ptr() as _,
out.len() as u32,
))?;
};
Ok(out)
} else {
Ok(&mut [0u8; 0])
}
}

fn encode_command<'a>(&self, data: &[u8], packet: &'a mut [u8]) -> &'a [u8] {
packet[0] = SUB_ADDRESS_DDC_CI;
packet[1] = 0x80 | data.len() as u8;
Expand Down Expand Up @@ -237,10 +162,8 @@ impl DdcCommandRaw for Monitor {
let mut packet = [0u8; 36 + 3];
let packet = self.encode_command(data, &mut packet);
let response = match &self.service {
MonitorService::Intel(service) => {
Self::execute_intel(service, self.i2c_address, packet, out, response_delay)
}
MonitorService::Arm(service) => Self::execute_arm(service, self.i2c_address, packet, out, response_delay),
MonitorService::Intel(service) => intel::execute(service, self.i2c_address, packet, out, response_delay),
MonitorService::Arm(service) => arm::execute(service, self.i2c_address, packet, out, response_delay),
}?;
self.decode_response(response)
}
Expand Down

0 comments on commit 93d7602

Please sign in to comment.