I have a custom USB Audio Class 2 (UAC2) compatible device. When I connect this custom device to a MacBook with a configuration of up to 10 channels (16-bit), everything seems to work fine.
However, when I increase the channel count to 12, the MacBook does not recognize the 12 channels. It only shows the channel count as 0.
TN2274 is the only source where I found some information about Apple's Audio Class Drivers, but it doesn't mention any limitations regarding channel counts.
Could you let me know the current limitations of the Audio Class Drivers on the latest macOS versions? What configuration should I use to get 12 channels working?
P.S. I also found that a 12-channel, 8-bit configuration is detected by the MacBook, bit I want it to work with 16bits.
For more detail please check FB17098863
Drivers
RSS for tagUnderstand the role of drivers in bridging the gap between software and hardware, ensuring smooth hardware functionality.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi Apple,
We are working on a general USB device management solution on macOS for enterprise security. Our goal is to enforce policy-based restrictions on USB devices, such as:
For USB storage devices: block mount, read, or write access.
For other peripherals (e.g., USB headsets or microphones, raspberry pi, etc): block usage entirely.
We know in past, kernel extension would be the way to go, but as kext has been deprecated. And DriverKit is the new advertised framework.
At first, DriverKit looked like the right direction. However, after reviewing the documentation more closely, we noticed that using DriverKit for USB requires specific entitlements:
DriverKit USB Transport – VendorID
DriverKit USB Transport – VendorID and ProductID
This raises a challenge: if our solution is meant to cover all types of USB devices, we would theoretically need entitlements for every VendorID/ProductID in existence.
My questions are:
Is DriverKit actually the right framework for this kind of general-purpose USB device control?
If not, what framework or mechanism should we be looking at for enforcing these kinds of policies?
We also developed an Endpoint Security product, but so far we haven’t found a relevant Endpoint Security event type that would allow us to achieve this.
Any guidance on the correct technical approach would be much appreciated.
Thanks in advance for your help.
I've tried on multiple occasions to add a message on to the request form for a vendor ID, but it always comes back with just the one default.
I read in another post that they got it done through the Feedback Assistant, but that hasn't worked for me.
Does anyone have any other ideas?
Between us and our parent company, I need three vendor IDs.
Hi everyone,
I was following the Video Modernize PCI and SCSI drivers with DriverKit and the Document to implement UserMapHBAData(), and here’s my current implementation:
// kern_return_t DRV_MAIN_CLASS_NAME::UserMapHBAData_Impl(uint32_t *uniqueTaskID)
kern_return_t IMPL(DRV_MAIN_CLASS_NAME, UserMapHBAData)
{
Log("UserMapHBAData() - Start");
// Define the vm_page_size explicitly
const uint32_t vm_page_size = 4096;
kern_return_t ret;
IOBufferMemoryDescriptor *buffer = nullptr;
IOMemoryMap *memMap = nullptr;
void *taskData = nullptr;
// Create a buffer for HBA-specific task data
ret = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOutIn, ivars->fTaskDataSize, vm_page_size, &buffer);
__Require((kIOReturnSuccess == ret), Exit);
// Map memory to the driver extension's memory space
ret = buffer->CreateMapping(0, 0, 0, 0, 0, &memMap);
__Require((kIOReturnSuccess == ret), Exit);
// Retrieve mapped memory address
taskData = reinterpret_cast<void *>(memMap->GetAddress());
__Require(taskData, Exit);
// WARNING: Potential leak of an object stored into 'buffer'
// WARNING: Potential leak of an object stored into 'memMap'
// Assign a unique task ID
ivars->fTaskID++; // ERROR: No member named 'fTaskID' in 'DriverKitAcxxx_IVars'
ivars->fTaskArray[ivars->fTaskID] = taskData;
*uniqueTaskID = ivars->fTaskID;
Log("UserMapHBAData() - End");
return kIOReturnSuccess;
Exit:
// Cleanup in case of failure
if (memMap) {
memMap->free(); // Correct method for releasing memory maps
}
if (buffer) {
buffer->free(); // Correct method for releasing buffer memory
}
LogErr("ret = 0x%0x", ret);
Log("UserMapHBAData() - End");
return ret;
}
For reference, in KEXT, memory allocation is typically done using:
IOBufferMemoryDescriptor *buffer = IOBufferMemoryDescriptor::inTaskWithOptions(
kernel_task, // Task in which memory is allocated
kIODirectionOutIn, // Direction (read/write)
1024, // Size of the buffer in bytes
4); // Alignment requirements
However, after installing the dext, macOS hangs, and I have to do a hardware reset. After rebooting, the sysctl list output shows:
% sectl list
1 extension(s)
--- com.apple.system_extension.driver_extension
enabled active teamID bundleID (version) name [state]
* - com.accusys.DriverKitAcxxx (5.0/11) com.accusys.DriverKitAcxxx [activated waiting for user]
Questions:
What could be causing macOS to halt?
How should I approach debugging and resolving this issue?
Looking forward to your insights, any suggestions would be greatly appreciated!
Best regards, Charles
Dear Apple engineers and DriverKit developers,
We have developed a DriverKit (DEXT) driver for an HBA RAID controller.
The RAID controller is connected to hosts through Thunderbolt (PCIe port of the Thunderbolt controller).
We use an IO script to verify the developed driver. The test fails after a few (10-12) hours of running with an error:
“BUG IN CLIENT OF LIBDISPATCH: dispatch_sync called on queue already owned by current thread”.
We inspected the stack trace of the crash report. This error happens in the interrupt handler.
Thread 5 Crashed:
0 libdispatch.dylib 0x19671aa8c __DISPATCH_WAIT_FOR_QUEUE__ + 484
1 libdispatch.dylib 0x19671a5d0 _dispatch_sync_f_slow + 152
2 DriverKit 0x195d3fc1c IODispatchQueue::DispatchSync_f(void*, void (*)(void*)) + 296
3 DriverKit 0x195d40860 IOInterruptDispatchSourceThread(void*) + 380
4 libsystem_pthread.dylib 0x1968a3738 _pthread_start + 140
5 libsystem_pthread.dylib 0x1968ac6c8 thread_start + 8
On our side we created 5 DispatchQueue(s) for the interrupt processing and configured 5 interrupt handlers using ConfigureInterrupts(kIOInterruptTypePCIMessagedX, 1, 5). It gives 5 interrupts, as requested and it is not clear what is the reason for the issue.
Our code samples are below
uint32_t configureInterrupts(uint32_t requested, uint32_t required)
{
const kern_return_t ret = ioPCIdevice->ConfigureInterrupts(kIOInterruptTypePCIMessagedX, required, requested);
if (ret != kIOReturnSuccess)
return 0;
uint64_t interruptType = 0;
uint32_t interruptsCount = 0;
uint32_t interruptIndex = 0;
for ( ;; ++interruptIndex) {
if (IOInterruptDispatchSource::GetInterruptType(ioPCIdevice, interruptIndex, &interruptType) != kIOReturnSuccess)
break;
if ((interruptType & kIOInterruptTypePCIMessagedX) == 0)
continue;
++interruptsCount;
}
return interruptsCount;
}
.....
// Create DQs
for(int i = 0; i < maxInterrupts; ++i) {
ret = IODispatchQueue::Create(INTERRUPT_DQ_NAME, 0, 0, &ivars->interruptQueue[i]);
if (kIOReturnSuccess != ret || nullptr == ivars->interruptQueue[i]) {
GH_PRINT_ERR("Interrupts queue %d creation failed with error %d", i, ret);
return false;
}
}
.....
// Link DQ with interrupt
for(int index = 0; index < maxInterrupts; ++ index) {
kern_return_t ret = CreateActionHandleInterruptRequest(size, &ivars->interruptActions[index]);
if (kIOReturnSuccess != ret) {
GH_PRINT_ERR("Create action for interrupt handler %u failed.", index);
return ;
}
ret = IOInterruptDispatchSource::Create(ivars->PCI_io.dev, index, ivars->interruptQueue[index], &ivars->interruptSources[index]);
if (kIOReturnSuccess != ret || nullptr == ivars->interruptSources[index]) {
GH_PRINT_ERR("Creating interrupt source %u failed for interrupt index %u.", index, index);
return ;
}
ret = ivars->interruptSources[index]->SetHandler(ivars->interruptActions[index]);
if (kIOReturnSuccess != ret) {
GH_PRINT_ERR("Setting the handler for interrupt source %u failed.", index);
return ;
}
ivars->contexts[index] = ivars->interruptActions[index]->GetReference();
}
.....
// definition for interrupt handler
void HandleInterruptRequest ( OSAction *action, uint64_t count, uint64_t time) TYPE (IOInterruptDispatchSource::InterruptOccurred);
Do you have any clue how we can fix this error? Or directions and ways for investigation?
Please let us know if you need more details.
Topic:
App & System Services
SubTopic:
Drivers
First time working with DEXT, so I'm trying to make "Communicating between a DriverKit extension and a client app" sample project to work and I'm having trouble.
I'm following the steps for "Automatically manage signing". It seems that the step “Sign to Run Locally” is wrong now, so I let it to Apple Developer.
Now I have a Provisioning Profile error that says it doesn't include the com.apple.developer.driverkit.allow-any-userclient-access entitlement.
If I go to Certificates, Identifiers and Profiles for this AppID and go to Capability Requests and select DriverKit Allow Any UserClient Access, this bring me to a page where I select DriverKit Entitlement but Any UserClient Access is not a choice. I tried asking for UserClient Access but the request has been denied saying that the engineering team does not think these entitlements are necessary for testing locally. So I removed com.apple.developer.driverkit.allow-any-userclient-access from NullDriver.entitlements. I can now install the DEXT but when I try "Communicate with Dext" button, I received the error: Failed opening connection to dext with error: 0xe00002e2.
Looking in the console, I see this error message:
DK: NullDriverUserClient-0x10000ad6a:UC failed userclient-access check, needed bundle ID com.example.apple-samplecode.dext-to-user-client-2-TEST.driver
I tried many things, but I can't seem to be able to pass through the check to allow the application to communicate with the dext.
What am I missing?
Note that I'm focusing only on macOS, not iOS.
Thanks.
Hello,
I'm trying to make a DEXT for a thunderbolt device. I started from the DriverKit template that does a very simple Hello World. I added the DriverKit PCI (development) entitlement in the developer portal. The dext is installed and activated but when I connect my thunderbolt device this is what I see in the console log:
kernel DK: mydrv-0x100010a85 waiting for server com.mycompany.mydrvApp.mydrv-100010a85
kernel Driver com.mycompany.mydrvApp.mydrv has crashed 0 time(s)
kernelmanagerd Launching dext com.mycompany.mydrvApp.mydrv com.mycompany.mydrvApp.mydrv 0x100010a81 e675cb5ca6b6650163cc231c6af2f7e730b56b0bf394b857ce76f8e3105eb0f1
kernel DK: mydrv-0x100010a89 waiting for server com.mycompany.mydrvApp.mydrv-100010a89
kernelmanagerd Launching driver extension: Dext com.mycompany.mydrvApp.mydrv v1 in executable dext bundle com.mycompany.mydrvApp.mydrv at /Library/SystemExtensions/DC2F3964-043D-445E-A6CF-A9D7C529B39A/com.mycompany.mydrvApp.mydrv.dext
default 16:52:31.551867-0500 kernel /Library/SystemExtensions/DC2F3964-043D-445E-A6CF-A9D7C529B39A/com.mycompany.mydrvApp.mydrv.dext/com.mycompany.mydrvApp.mydrv[15788] ==> com.apple.dext
kernelmanagerd Found 1 dexts with bundle identifier com.mycompany.mydrvApp.mydrv
kernelmanagerd Using unique id e675cb5ca6b6650163cc231c6af2f7e730b56b0bf394b857ce76f8e3105eb0f1 to pick dext matching bundle identifier com.mycompany.mydrvApp.mydrv
kernelmanagerd Picked matching dext for bundle identifier com.mycompany.mydrvApp.mydrv: Dext com.mycompany.mydrvApp.mydrv v1 in executable dext bundle com.mycompany.mydrvApp.mydrv at /Library/SystemExtensions/DC2F3964-043D-445E-A6CF-A9D7C529B39A/com.mycompany.mydrvApp.mydrv.dext
kernelmanagerd Launching dext com.mycompany.mydrvApp.mydrv com.mycompany.mydrvApp.mydrv 0x100010a85 e675cb5ca6b6650163cc231c6af2f7e730b56b0bf394b857ce76f8e3105eb0f1
kernel DK: com.mycompany.mydrvApp.mydrv[15788] has team identifier L86BQ63GK2
kernelmanagerd Launching driver extension: Dext com.mycompany.mydrvApp.mydrv v1 in executable dext bundle com.mycompany.mydrvApp.mydrv at /Library/SystemExtensions/DC2F3964-043D-445E-A6CF-A9D7C529B39A/com.mycompany.mydrvApp.mydrv.dext
kernel
kernel DK: mydrv-0x100010a81: provider entitlements check failed
kernel DK: IOUserServer(com.mycompany.mydrvApp.mydrv-0x100010a81)-0x100010a8a::exit(Entitlements check failed)
What am I missing for the check to pass?
Here is my mydrv.entitlements file:
I tried adding IOPCIPrimaryMatch with my vendor id in info.plist, but with same result.
Developer mode is on and SIP is disabled.
Thanks
Hi,
We’re developing a DriverKit extension for iPadOS. In local Debug and Release builds, everything works as expected, but the same build uploaded to TestFlight fails at IOServiceOpen with the following errors:
-536870212 (0xE00002EC) kIOReturnUnsupported
-536870201 (0xE00002F7) kIOReturnNotPermitted
What we’ve verified so far
App entitlements
We checked our main app entitlements file, and it has the correct capabilities for the driverkit communication
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.driverkit.communicates-with-drivers</key>
<true/>
<key>com.apple.developer.driverkit.userclient-access</key>
<array>
<string>abc.def.ABCDriver</string>
</array>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>
we also checked the Provisioning profile (as shown on the portal) and the “Enabled Capabilities” seems to have the correct DriverKit Capabilities enabled.
Enabled Capabilities
Access Wi-Fi Information, DriverKit, DriverKit (development), DriverKit Communicates with Drivers, DriverKit USB Transport (development), DriverKit USB Transport - VendorID, DriverKit UserClient Access, iCloud, In-App Purchase, Sign In with Apple, System Extension
When we download and inspect the provisioning profile as plain text, we notice that some expected DriverKit entitlements appear to be missing from the section.
<key>Entitlements</key>
<dict>
<key>beta-reports-active</key>
<true/>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.communicates-with-drivers</key>
<true/>
<key>application-identifier</key>
<string>ABC123456.abc.def</string>
<key>keychain-access-groups</key>
<array>
<string>ABC123456.*</string>
<string>com.apple.token</string>
</array>
<key>get-task-allow</key>
<false/>
<key>com.apple.developer.team-identifier</key>
<string>ABC123456</string>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>ABC123456.*</string>
<key>com.apple.developer.icloud-services</key>
<string>*</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array></array>
<key>com.apple.developer.icloud-container-development-container-identifiers</key>
<array></array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array></array>
<key>com.apple.developer.driverkit.transport.usb</key>
<array>
<dict>
<key>idVendor</key>
<integer>1234</integer>
</dict>
</array>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
</dict>
We have a couple of questions:
Could the missing com.apple.developer.driverkit.userclient-access entitlement in the provisioning profile alone explain the kIOReturnUnsupported / kIOReturnNotPermitted failures from IOServiceOpen?
Why do some DriverKit capabilities appear in the Apple Developer portal UI but vanish from the actual profile we download? Is there an extra step we’re overlooking when regenerating profiles after toggling those capabilities?
Thanks
The device I am trying to develop a firmware updater for is an NVMe drive with a USB4 interface. It can connect in USB4 mode (tunneled NVMe), in USB 3 mode or in USB 2 mode.
In USB 2 and USB 3 mode, the device descriptor shows one interface with two alternates. Alternate 0 uses the bulk-only protocol, with one IN and one OUT pipe. Alternate 1 uses the UAS protocol, with two IN and two OUT pipes.
I use identical code in my driver to send custom CDBs. I can see using IORegistryExplorer that in USB 2 mode, macOS chooses alternate 0, the bulk-only protocol. My custom CDBs and their accompanying data pay loads are put on the bus, more or less as expected.
In USB 3 mode, macOS chooses alternate 1, the UAS protocol. My custom CDB is put on the bus, but no payload data is transferred.
Is this expected behavior?
If so, is there a way to force the OS to choose alternate 0 even when on USB 3, perhaps with another dext?
I'll file a bug about this when Feedback Assistant lets me.
=1) The situation:
1A) I make both a "DExt" and a "SDK" for still-imaging-USB-gadgets and MACOS>=14 ,iPADOS>=17
1B) One of the USB-gadgets needs warm_up after PlugIn (i.e End-User-App must know "now-TheMomentOfPlugIn" with precision ~1sec).
=2) The question is how to do "1B" rationally?
=3) My speculative guess: in BSD-descendant I expect existence (somewhere) of a "normal file" through "macports etc", which has normal "file creation time". Such a "file creation time" (accessible better via IORegistryEntry... at SDK-level; possibly via IOUSBHostInterface at DExt-level) is cognitive target of mine.
=4) Additional constraints: Technically absent. I freely modify code either DExt (descendant of IOUSBHostInterface) or SDK-level (IORegistryEntryGetRegistryEntryID, IORegistryEntry...)
Topic:
App & System Services
SubTopic:
Drivers
Hello everyone,
I am migrating a legacy KEXT to a DriverKit (DEXT) architecture. While the DEXT itself is working correctly, I am completely blocked by a code signing issue when trying to establish the UserClient connection from our SwiftUI management app.
Project Goal & Status:
Our DEXT (com.accusys.Acxxx.driver) activates successfully (systemextensionsctl list confirms [activated enabled]).
The core functionality is working (diskutil list shows the corresponding disk device node).
The Core Problem: The userclient-access Signing Error
To allow the app to connect to the DEXT, the com.apple.developer.driverkit.userclient-access entitlement is required in the app's .entitlements file.
However, as soon as this entitlement is added, the build fails.
Both automatic and manual signing fail with the same error:
`Provisioning profile ... doesn't match the entitlements file's value for the ... userclient-access entitlement.`
This build failure prevents the generation of an .app bundle, making it impossible to inspect the final entitlements with codesign.
What We've Confirmed:
The necessary capabilities (like DriverKit Communicates with Drivers) are visible and enabled for our App ID on the developer portal.
The issue persists on a clean system state and on the latest macOS Sequoia 15.7.1.
Our Research and Hypothesis:
We have reviewed the official documentation "Diagnosing issues with entitlements" (TN3125).
According to the documentation, a "doesn't match" error implies a discrepancy between the entitlements file and the provisioning profile.
Given that we have tried both automatic and manual profiles (after enabling the capability online), our hypothesis is that the provisioning profile generation process on Apple's backend is not correctly including the approved userclient-access entitlement into the profile file itself. The build fails because Xcode correctly detects this discrepancy.
Our Questions:
Did we misunderstand a step in the process, or is the issue not with the entitlement request at all? Alternatively, are there any other modifications we can make to successfully connect our App to the DEXT and trigger NewUserClient?
Thank you for any guidance.
Hello everyone,
We are migrating our KEXT for a Thunderbolt storage device to a DEXT based on IOUserSCSIParallelInterfaceController.
We've run into a fundamental issue where the driver's behavior splits based on the I/O source: high-level I/O from the file system (e.g., Finder, cp) is mostly functional (with a minor ls -al sorting issue for Traditional Chinese filenames), while low-level I/O directly to the block device (e.g., diskutil) fails or acts unreliably. Basic read/write with dd appears to be mostly functional.
We suspect that our DEXT is failing to correctly register its full device "personality" with the I/O Kit framework, unlike its KEXT counterpart. As a result, low-level I/O requests with special attributes (like cache synchronization) sent by diskutil are not being handled correctly by the IOUserSCSIParallelInterfaceController framework of our DEXT.
Actions Performed & Relevant Logs
1. Discrepancy: diskutil info Shows Different Device Identities for DEXT vs. KEXT
For the exact same hardware, the KEXT and DEXT are identified by the system as two different protocols.
KEXT Environment:
Device Identifier: disk5
Protocol: Fibre Channel Interface
...
Disk Size: 66.0 TB
Device Block Size: 512 Bytes
DEXT Environment:
Device Identifier: disk5
Protocol: SCSI
SCSI Domain ID: 2
SCSI Target ID: 0
...
Disk Size: 66.0 TB
Device Block Size: 512 Bytes
2. Divergent I/O Behavior: Partial Success with Finder/cp vs. Failure with diskutil
High-Level I/O (Partially Successful):
In the DEXT environment, if we operate on an existing volume (e.g., /Volumes/MyVolume), file copy operations using Finder or cp succeed. Furthermore, the logs we've placed in our single I/O entry point, UserProcessParallelTask_Impl, are triggered.
Side Effect: However, running ls -al on such a volume shows an incorrect sorting order for files with Traditional Chinese names (they appear before . and ..).
Low-Level I/O (Contradictory Behavior):
In the DEXT environment, when we operate directly on the raw block device (/dev/disk5):
diskutil partitionDisk ... -> Fails 100% of the time with the error: Error: -69825: Wiping volume data to prevent future accidental probing failed.
dd command -> Basic read/write operations appear to work correctly (a write can be immediately followed by a read within the same DEXT session, and the data is correct).
3. Evidence of Cache Synchronization Failure (Non-deterministic Behavior)
The success of the dd command is not deterministic. Cross-environment tests prove that its write operations are unreliable:
First Test:
In the DEXT environment, write a file with random data to /dev/disk5 using dd.
Reboot into the KEXT environment.
Read the data back from /dev/disk5 using dd. The result is a file filled with all zeros.
Conclusion: The write operation only went to the hardware cache, and the data was lost upon reboot.
Second Test:
In the DEXT environment, write the same random file to /dev/disk5 using dd.
Key Variable: Immediately after, still within the DEXT environment, read the data back once for verification. The content is correct!
Reboot into the KEXT environment.
Read the data back from /dev/disk5. This time, the content is correct!
Conclusion: The additional read operation in the second test unintentionally triggered a hardware cache flush. This proves that the dd (in our DEXT) write operation by itself does not guarantee synchronization, making its behavior unreliable.
Our Problem
Based on the observations above, we have the conclusion:
High-Level Path (triggered by Finder/cp):
When an I/O request originates from the high-level file system, the framework seems to enter a fully-featured mode. In this mode, all SCSI commands, including READ/WRITE, INQUIRY, and SYNCHRONIZE CACHE, are correctly packaged and dispatched to our UserProcessParallelTask_Impl entry point. Therefore, Finder operations are mostly functional.
Low-Level Path (triggered by dd/diskutil):
When an I/O request originates from the low-level raw block device layer:
The most basic READ/WRITE commands can be dispatched (which is why dd appears to work).
However, critical management commands, such as INQUIRY and SYNCHRONIZE CACHE, are not being correctly dispatched or handled. This leads to the incorrect device identification in diskutil info and the failure of diskutil partitionDisk due to its inability to confirm cache synchronization.
We would greatly appreciate any guidance, suggestions, or insights on how to resolve this discrepancy. Specifically, what is the recommended approach within DriverKit to ensure that a DEXT based on IOUserSCSIParallelInterfaceController can properly declare its capabilities and handle both high-level and low-level I/O requests uniformly?
Thank you.
Charles
Hi all,
We are migrating a SCSI HBA driver from KEXT to DriverKit (DEXT), with our DEXT inheriting from IOUserSCSIParallelInterfaceController. We've encountered a data corruption issue that is reliably reproducible under specific conditions and are hoping for some assistance from the community.
Hardware and Driver Configuration:
Controller: LSI 3108
DEXT Configuration: We are reporting our hardware limitations to the framework via the UserReportHBAConstraints function, with the following key settings:
// UserReportHBAConstraints...
addConstraint(kIOMaximumSegmentAddressableBitCountKey, 0x20); // 32-bit
addConstraint(kIOMaximumSegmentCountWriteKey, 129);
addConstraint(kIOMaximumByteCountWriteKey, 0x80000); // 512KB
Observed Behavior: Direct I/O vs. Buffered I/O
We've observed that the I/O behavior differs drastically depending on whether it goes through the system file cache:
1. Direct I/O (Bypassing System Cache) -> 100% Successful
When we use fio with the direct=1 flag, our read/write and data verification tests pass perfectly for all file sizes, including 20GB+.
2. Buffered I/O (Using System Cache) -> 100% Failure at >128MB
Whether we use the standard cp command or fio with the direct=1 option removed to simulate buffered I/O, we observe the exact same, clear failure threshold:
Test Results:
File sizes ≤ 128MB: Success. Data checksums match perfectly.
File sizes ≥ 256MB: Failure. Checksums do not match, and the destination file is corrupted.
Evidence of failure reproduced with fio (buffered_integrity_test.fio, with direct=1 removed):
fio --size=128M buffered_integrity_test.fio -> Test Succeeded (err=0).
fio --size=256M buffered_integrity_test.fio -> Test Failed (err=92), reporting the following error, which proves a data mismatch during the verification phase:
verify: bad header ... at file ... offset 1048576, length 1048576
fio: ... error=Illegal byte sequence
Our Analysis and Hypothesis
The phenomenon of "Direct I/O succeeding while Buffered I/O fails" suggests the problem may be related to the cache synchronization mechanism at the end of the I/O process:
Our UserProcessParallelTask_Impl function correctly handles READ and WRITE commands.
When cp or fio (buffered) runs, the WRITE commands are successfully written to the LSI 3108 controller's onboard DRAM cache, and success is reported up the stack.
At the end of the operation, to ensure data is flushed to disk, the macOS file system issues an fsync, which is ultimately translated into a SYNCHRONIZE CACHE SCSI command (Opcode 0x35 or 0x91) and sent to our UserProcessParallelTask_Impl.
We hypothesize that our code may not be correctly identifying or handling this SYNCHRONIZE CACHE opcode. It might be reporting "success" up the stack without actually commanding the hardware to flush its cache to the physical disk.
The OS receives this "success" status and assumes the operation is safely complete.
In reality, however, the last batch of data remains only in the controller's volatile DRAM cache and is eventually lost.
This results in an incomplete or incorrect file tail, and while the file size may be correct, the data checksum will inevitably fail.
Summary
Our DEXT driver performs correctly when handling Direct I/O but consistently fails with data corruption when handling Buffered I/O for files larger than 128MB. We can reliably reproduce this issue using fio with the direct=1 option removed.
The root cause is very likely the improper handling of the SYNCHRONIZE CACHE command within our UserProcessParallelTask. P.S. This issue did not exist in the original KEXT version of the driver.
We would appreciate any advice or guidance on this issue.
Thank you.
How does VMWare access USB devices without have any specifics of the USB device? Does it use the same profile/entitlement process or does it take a different approach?