Post

Replies

Boosts

Views

Activity

Reply to How to setup DriverKit Timer Event with OSAction Callback Binding
After encountering issues with the timer firing incorrectly, I refactored the code for setting up a 1-second timer in macOS DriverKit. Below is the implementation: // Use the Mach Absolute Time clock uint64_t clockType = kIOTimerClockMachAbsoluteTime; // Convert Mach Absolute Time to nanoseconds mach_timebase_info_data_t timebaseInfo; mach_timebase_info(&timebaseInfo); // Get the timebase info // Get the current Mach Absolute Time uint64_t currentTime = mach_absolute_time(); // Conversion: Mach Absolute Time * (timebase.numer / timebase.denom) uint64_t currentTimeInNanoseconds = (currentTime * timebaseInfo.numer) / timebaseInfo.denom; // Calculate the deadline: 1 second from now in nanoseconds uint64_t oneSecondInNanoseconds = NSEC_PER_SEC; // 1 second uint64_t deadlineInNanoseconds = currentTimeInNanoseconds + oneSecondInNanoseconds; // Convert back to Mach Absolute Time units uint64_t deadline = (deadlineInNanoseconds * timebaseInfo.denom) / timebaseInfo.numer; // Leeway: Set to 0 for precise timing (can adjust for power efficiency) uint64_t leeway = 0; kern_return_t ret = ivars->m_zts_timer_event_source->WakeAtTime(clockType, deadline, leeway); if (ret != kIOReturnSuccess) { LogErr("Failed to schedule WakeAtTime: 0x%x", ret); return ret; } Log("Timer successfully scheduled to wake in 1 second"); Deadline Calculation: By calculating the deadline with the proper Mach time conversion, the timer behaves as expected.
Topic: App & System Services SubTopic: Drivers Tags:
Apr ’25
Reply to How to setup DriverKit Timer Event with OSAction Callback Binding
Dear Kevin, After tracing through the code, here’s my understanding of the steps involved: Initialization: In the init method, assign a work queue to m_work_queue using GetWorkQueue(). Then create a timer dispatch source (IOTimerDispatchSource) link with ZtsTimerOccurred_Impl, and associate it with the work queue. ivars->m_work_queue = GetWorkQueue(); error = IOTimerDispatchSource::Create(ivars->m_work_queue.get(), &zts_timer_event_source); ivars->m_zts_timer_event_source = OSSharedPtr(zts_timer_event_source, OSNoRetain); Next, create an action (OSAction) for handling the timer: error = CreateActionZtsTimerOccurred(sizeof(void*), &zts_timer_occurred_action); // How can this be done? ivars->m_zts_timer_occurred_action = OSSharedPtr(zts_timer_occurred_action, OSNoRetain); ivars->m_zts_timer_event_source->SetHandler(ivars->m_zts_timer_occurred_action.get()); Start I/O In the StartIO method, use DispatchSync to schedule tasks and call StartTimers(). ivars->m_work_queue->DispatchSync(^(){ ... StartTimers(); }); Start Timers Set up and enable the timer in StartTimers() to schedule an initial wake-up. ivars->m_zts_timer_event_source->WakeAtTime(kIOTimerClockMachAbsoluteTime, current_time + ivars->m_zts_host_ticks_per_buffer, 0); ivars->m_zts_timer_event_source->SetEnable(true); Timer Event Handler Handle the timer event in ZtsTimerOccurred_Impl and set the next wake-up. void SimpleAudioDevice::ZtsTimerOccurred_Impl(OSAction* action, uint64_t time) { ... ivars->m_zts_timer_event_source->WakeAtTime(kIOTimerClockMachAbsoluteTime, current_host_time + host_ticks_per_buffer, 0); ... } Question: The GetWorkQueue() & CreateActionZtsTimerOccurred methods seem to belong to the framework. How can I do if there’s no explicit API to call? In StartIO, is it necessary to use DispatchSync, or can another approach be used? Why are there two WakeAtTime calls—one in StartTimers() and another in ZtsTimerOccurred_Impl? What’s the purpose of this design? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Apr ’25
Reply to Issue Writing to BAR1 After BAR0 is Unavailable
I think I now understand what the memoryIndex should be set to. Here’s the updated log output: !!! ERROR : Failed to get BAR0 info (error: 0xe00002f0). !!! BAR1 - MemoryIndex: 0x00000000, Size: 0x00040000, Type: 0 !!! ERROR : Failed to get BAR2 info (error: 0xe00002f0). !!! And here’s the function I used to retrieve the BAR information: IOReturn GetBARInfo(IOPCIDevice* pciDevice, uint8_t barIndex, uint8_t* memoryIndex, uint64_t *barSize, uint8_t* barType) { IOReturn ret = pciDevice->GetBARInfo(barIndex, memoryIndex, barSize, barType); if (ret == kIOReturnSuccess) { Log("BAR%d - MemoryIndex: 0x%08x, Size: 0x%08llx, Type: %u", barIndex, *memoryIndex, *barSize, *barType); } else { LogErr("Failed to get BAR%d info (error: 0x%08x).", barIndex, ret); } return ret; } And then, use the memoryIndex obtained from GetBARInfo to perform memory read/write operations.
Topic: App & System Services SubTopic: Drivers Tags:
Mar ’25
Reply to DriverKit CppUserClient Searching for dext service but Failed opening service with error: 0xe00002c7
Don't say that, Smith. Valuable information about dext is almost impossible to find online, so any help you provide is incredibly significant to me. I'm truly grateful. This is my first time working on dext development, I always ask Copilot or ChatGPT for help, but their answers are mostly non-functional code, which is truly frustrating. For example, when I try to connect to this dext, their user space client code is not workable also. May I politely ask if you have a basic and functional Xcode project for both the dext and client sides that I could use as a reference?
Topic: App & System Services SubTopic: Drivers Tags:
Jan ’25
Reply to DriverKit CppUserClient Searching for dext service but Failed opening service with error: 0xe00002c7
Thanks, Smith, Thanks for your quick response. There are so few resource about DriverKit developing that I can find. So, thank you very much! I follow your suggestion, add the code for NewUserClient kern_return_t IMPL(testinfo, NewUserClient) { kern_return_t ret = kIOReturnSuccess; IOService* client = nullptr; Log("fredapp NewUserClient()"); ret = Create(this, "UserClientProperties", &client); if (ret != kIOReturnSuccess) { Log("fredapp NewUserClient() - Failed to create UserClientProperties with error: 0x%08x.", ret); goto Exit; } *userClient = OSDynamicCast(IOUserClient, client); if (*userClient == NULL) { Log("fredapp NewUserClient() - Failed to cast new client."); client->release(); ret = kIOReturnError; goto Exit; } Log("fredapp NewUserClient() - Finished."); Exit: return ret; } And change the CppUserClient ret = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching(dextIdentifier), &iterator); But still got the message: Failed to match to device. By the way, in CppUserClient.cpp ret = IOServiceOpen(service, mach_task_self_, kIOHIDServerConnectType, &connection); our dext is using the PCIDriverKit.framework, and include the headers #include <DriverKit/IOUserServer.h> #include <DriverKit/IOLib.h> #include <PCIDriverKit/PCIDriverKit.h> #include <DriverKit/IODMACommand.h> #include <DriverKit/IODispatchSource.h> #include <DriverKit/IOTimerDispatchSource.h> #include <DriverKit/IOInterruptDispatchSource.h> So I try to replace the kIOHIDServerConnectType to kIOPCIDeviceConnectType, but I do not know how to import the correct header file. I try to include #include <IOKit/PCIDriverKit.h> but Xcode report file not found. What's the next step to try? And I find a Github repository SamsungDS/MacVFN, in the file MacVFN/MacVFNUserClient.cpp, #include <DriverKit/IOUserServer.h> #include <DriverKit/IOLib.h> #include <DriverKit/OSData.h> #include <DriverKit/OSDictionary.h> #include <DriverKit/OSNumber.h> #include <PCIDriverKit/PCIDriverKit.h> If it is workable (because I was failed to include PCIDriverKit/PCIDriverKit.h), and I will try to download and inspect the code to find some information and try to figure out how to fix my problem. Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Jan ’25
Reply to How to setup DriverKit Timer Event with OSAction Callback Binding
After encountering issues with the timer firing incorrectly, I refactored the code for setting up a 1-second timer in macOS DriverKit. Below is the implementation: // Use the Mach Absolute Time clock uint64_t clockType = kIOTimerClockMachAbsoluteTime; // Convert Mach Absolute Time to nanoseconds mach_timebase_info_data_t timebaseInfo; mach_timebase_info(&timebaseInfo); // Get the timebase info // Get the current Mach Absolute Time uint64_t currentTime = mach_absolute_time(); // Conversion: Mach Absolute Time * (timebase.numer / timebase.denom) uint64_t currentTimeInNanoseconds = (currentTime * timebaseInfo.numer) / timebaseInfo.denom; // Calculate the deadline: 1 second from now in nanoseconds uint64_t oneSecondInNanoseconds = NSEC_PER_SEC; // 1 second uint64_t deadlineInNanoseconds = currentTimeInNanoseconds + oneSecondInNanoseconds; // Convert back to Mach Absolute Time units uint64_t deadline = (deadlineInNanoseconds * timebaseInfo.denom) / timebaseInfo.numer; // Leeway: Set to 0 for precise timing (can adjust for power efficiency) uint64_t leeway = 0; kern_return_t ret = ivars->m_zts_timer_event_source->WakeAtTime(clockType, deadline, leeway); if (ret != kIOReturnSuccess) { LogErr("Failed to schedule WakeAtTime: 0x%x", ret); return ret; } Log("Timer successfully scheduled to wake in 1 second"); Deadline Calculation: By calculating the deadline with the proper Mach time conversion, the timer behaves as expected.
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Apr ’25
Reply to How to setup DriverKit Timer Event with OSAction Callback Binding
Dear Kevin, After tracing through the code, here’s my understanding of the steps involved: Initialization: In the init method, assign a work queue to m_work_queue using GetWorkQueue(). Then create a timer dispatch source (IOTimerDispatchSource) link with ZtsTimerOccurred_Impl, and associate it with the work queue. ivars->m_work_queue = GetWorkQueue(); error = IOTimerDispatchSource::Create(ivars->m_work_queue.get(), &zts_timer_event_source); ivars->m_zts_timer_event_source = OSSharedPtr(zts_timer_event_source, OSNoRetain); Next, create an action (OSAction) for handling the timer: error = CreateActionZtsTimerOccurred(sizeof(void*), &zts_timer_occurred_action); // How can this be done? ivars->m_zts_timer_occurred_action = OSSharedPtr(zts_timer_occurred_action, OSNoRetain); ivars->m_zts_timer_event_source->SetHandler(ivars->m_zts_timer_occurred_action.get()); Start I/O In the StartIO method, use DispatchSync to schedule tasks and call StartTimers(). ivars->m_work_queue->DispatchSync(^(){ ... StartTimers(); }); Start Timers Set up and enable the timer in StartTimers() to schedule an initial wake-up. ivars->m_zts_timer_event_source->WakeAtTime(kIOTimerClockMachAbsoluteTime, current_time + ivars->m_zts_host_ticks_per_buffer, 0); ivars->m_zts_timer_event_source->SetEnable(true); Timer Event Handler Handle the timer event in ZtsTimerOccurred_Impl and set the next wake-up. void SimpleAudioDevice::ZtsTimerOccurred_Impl(OSAction* action, uint64_t time) { ... ivars->m_zts_timer_event_source->WakeAtTime(kIOTimerClockMachAbsoluteTime, current_host_time + host_ticks_per_buffer, 0); ... } Question: The GetWorkQueue() & CreateActionZtsTimerOccurred methods seem to belong to the framework. How can I do if there’s no explicit API to call? In StartIO, is it necessary to use DispatchSync, or can another approach be used? Why are there two WakeAtTime calls—one in StartTimers() and another in ZtsTimerOccurred_Impl? What’s the purpose of this design? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Apr ’25
Reply to Issue Writing to BAR1 After BAR0 is Unavailable
I think I now understand what the memoryIndex should be set to. Here’s the updated log output: !!! ERROR : Failed to get BAR0 info (error: 0xe00002f0). !!! BAR1 - MemoryIndex: 0x00000000, Size: 0x00040000, Type: 0 !!! ERROR : Failed to get BAR2 info (error: 0xe00002f0). !!! And here’s the function I used to retrieve the BAR information: IOReturn GetBARInfo(IOPCIDevice* pciDevice, uint8_t barIndex, uint8_t* memoryIndex, uint64_t *barSize, uint8_t* barType) { IOReturn ret = pciDevice->GetBARInfo(barIndex, memoryIndex, barSize, barType); if (ret == kIOReturnSuccess) { Log("BAR%d - MemoryIndex: 0x%08x, Size: 0x%08llx, Type: %u", barIndex, *memoryIndex, *barSize, *barType); } else { LogErr("Failed to get BAR%d info (error: 0x%08x).", barIndex, ret); } return ret; } And then, use the memoryIndex obtained from GetBARInfo to perform memory read/write operations.
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Mar ’25
Reply to DriverKit CppUserClient Searching for dext service but Failed opening service with error: 0xe00002c7
Don't say that, Smith. Valuable information about dext is almost impossible to find online, so any help you provide is incredibly significant to me. I'm truly grateful. This is my first time working on dext development, I always ask Copilot or ChatGPT for help, but their answers are mostly non-functional code, which is truly frustrating. For example, when I try to connect to this dext, their user space client code is not workable also. May I politely ask if you have a basic and functional Xcode project for both the dext and client sides that I could use as a reference?
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Jan ’25
Reply to DriverKit CppUserClient Searching for dext service but Failed opening service with error: 0xe00002c7
Thanks, Smith, Thanks for your quick response. There are so few resource about DriverKit developing that I can find. So, thank you very much! I follow your suggestion, add the code for NewUserClient kern_return_t IMPL(testinfo, NewUserClient) { kern_return_t ret = kIOReturnSuccess; IOService* client = nullptr; Log("fredapp NewUserClient()"); ret = Create(this, "UserClientProperties", &client); if (ret != kIOReturnSuccess) { Log("fredapp NewUserClient() - Failed to create UserClientProperties with error: 0x%08x.", ret); goto Exit; } *userClient = OSDynamicCast(IOUserClient, client); if (*userClient == NULL) { Log("fredapp NewUserClient() - Failed to cast new client."); client->release(); ret = kIOReturnError; goto Exit; } Log("fredapp NewUserClient() - Finished."); Exit: return ret; } And change the CppUserClient ret = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching(dextIdentifier), &iterator); But still got the message: Failed to match to device. By the way, in CppUserClient.cpp ret = IOServiceOpen(service, mach_task_self_, kIOHIDServerConnectType, &connection); our dext is using the PCIDriverKit.framework, and include the headers #include <DriverKit/IOUserServer.h> #include <DriverKit/IOLib.h> #include <PCIDriverKit/PCIDriverKit.h> #include <DriverKit/IODMACommand.h> #include <DriverKit/IODispatchSource.h> #include <DriverKit/IOTimerDispatchSource.h> #include <DriverKit/IOInterruptDispatchSource.h> So I try to replace the kIOHIDServerConnectType to kIOPCIDeviceConnectType, but I do not know how to import the correct header file. I try to include #include <IOKit/PCIDriverKit.h> but Xcode report file not found. What's the next step to try? And I find a Github repository SamsungDS/MacVFN, in the file MacVFN/MacVFNUserClient.cpp, #include <DriverKit/IOUserServer.h> #include <DriverKit/IOLib.h> #include <DriverKit/OSData.h> #include <DriverKit/OSDictionary.h> #include <DriverKit/OSNumber.h> #include <PCIDriverKit/PCIDriverKit.h> If it is workable (because I was failed to include PCIDriverKit/PCIDriverKit.h), and I will try to download and inspect the code to find some information and try to figure out how to fix my problem. Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Jan ’25