I have a C++/Objective-C command line application, running on MacOs (15.1.1 (24B91)), that communicates with a Bluetooth LE peripheral. The application is build with Apple clang 16.0.0 and CMake as build system using Boost.Asio.
I'm able to establish a L2CAP channel and after the channel is established, the peripheral sends a first (quite small) SDU on that channel to the application. The PSM is 0x80 and was chosen by the peripherals BLE stack. The application receives the PSM via GATT notification.
I can see the SDU being send in a single LL PDU with Wireshark. I can also see the SDU being received in Apples PacketLogger. But I miss the corresponding call to a stream event handler. For all other GATT related events, the corresponding delegates / callbacks are called.
The code that creates a dispatch queue and passes it to the CBCentralManager looks like this:
dispatch_queue = dispatch_queue_create("de.torrox.ble_event_queue", NULL);
manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_queue options:nil];
When the L2CAP channel is established, the didOpenL2CAPChannel callback gets called from a thread within the dispatch_queue (has been verified with lldb):
- (void)peripheral:(CBPeripheral *)peripheral
didOpenL2CAPChannel:(CBL2CAPChannel *)channel
error:(NSError *)error
{
[channel inputStream].delegate = self;
[channel outputStream].delegate = self;
[[channel inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[[channel outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[[channel inputStream] open];
[[channel outputStream] open];
...
// a reference to the channel is stored in the outside channel object
[channel retain];
...
}
Yet, not a single stream event is generated:
- (void)stream:(NSStream *)stream
handleEvent:(NSStreamEvent)event_code
{
Log( @"stream:handleEvent %@, %lu", stream, event_code );
...
}
When I add a functionality, to poll the input stream, the stream will report the expected L2CAP input. But no event is generated.
The main thread of execution is usually blocking on a boost::asio::io_context::run() call. The design is, to have the stream callback stream:handleEvent to post call back invocations on that io_context, and thus to wake up the main thread and get that callbacks being invoked on the main thread.
All asynchronous GATT delegate calls are working as expected. The only missing events, are the events from the L2CAP streams. The same code worked in an older project on an older version of MacOs and an older version of Boost.
How can I find out, why the stream delegates are not called?
IOKit
RSS for tagGain user-space access to hardware devices and drivers using IOKit.
Posts under IOKit tag
38 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
On a macOS machine running v15.0, I have a daemon run by launchd which subscribes to the sleep and wakeup notifications using the IORegisterForSystemPower method.
void PowerCallBack(void* refCon, io_service_t service, natural_t messageType, void* messageArgument)
{
switch (messageType)
{
case kIOMessageSystemWillSleep:
logger->Debug("Received sleep notification from macOS");
if (refCon)
{
//Handle Sleep
}
IOAllowPowerChange(root_port, (long)messageArgument);
break;
case kIOMessageSystemHasPoweredOn:
logger->Debug("Received wakeup notification from macOS");
if (refCon)
{
// Handle Wakeup
}
break;
default:
break;
}
}
void MacOSNotification::RegisterNotifications()
{
logger->Debug("Registering for notifications from macOS");
powerNotificationThread = [[NSThread alloc] initWithBlock:^{
// Notifier object, used to deregister later
root_port = IORegisterForSystemPower(this, ¬ifyPortRef, PowerCallBack, ¬ifierObject);
if (root_port == 0)
{
return;
}
logger->Debug("Registered for system power notifications from macOS");
// Add the notification port to the application runloop
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notifyPortRef),
kCFRunLoopCommonModes);
CFRunLoopRun();
}]; //END OF THREAD BLOCK
[powerNotificationThread start];
}
Using this mechanism, I am getting notifications for normal sleep and wakeup transitions like closing and opening the lid. I need these notifications to terminate/reconnect my connection to a cloud service when we go to sleep/wakeup respectively.
I have noticed from the power logs at /private/var/log/powermanagement that the after the sleep initiated by lid closing or clicking sleep in the top apple menu (both of which I can detect as they generate power notification), the macOS machine wakes up with the following message from powerd logs:
DarkWake from Deep Idle [CDNP] : due to SMC.OutboxNotEmpty smc.70070000 wifibt/
I do not get any notification for this wakeup and my application threads start running. This happens every 15 to 16 mins from my observation.
After this DarkWake, we go back to 'Maintenance' sleep in under a minute as can be seen by the following powerd log:
Entering Sleep state due to 'Maintenance Sleep':TCPKeepAlive=active
I do not get any notifications for this either.
Is there a way to track and get notified of these DarkWake -> Maintenance sleep cycles? At the very least I would like to log when we go into and come out of these states. Currently I just rely on seeing a 15 min window of no logs to know this must have a DarkWake -> Maintenance sleep cycle.
Also is there a way to make sure my application and its threads are not woken up by DarkWake (like an opt-out)? I would like to make it so that my application only runs when we are properly sleeping and waking.
Firstly, I realise that Kexts are deprecated. And for my needs, user-space IOKit from an application might be more than I’ll ever need, but I can’t help ensuring I’ve got all my ducks in a row while I’m designing an app.
I noticed in the above Kext deprecation notice that it’s more about specific APIs that are deprecated rather than the kext mechanism itself (no mention of IOKit kernel APIs there). Along with the fact that a reboot would be required and various other policy changes.
While reading up on System Extensions and the various newer tools, DriverKit, Endpoint Security etc, I’ve noticed there’s no mention of this “IOKit Driver” target/deliverable in the Xcode template chooser. It looks like, from the requirements, that DriverKit is aimed at hardware manufacturers, with a need to request the DriverKit entitlement for development.
With respect to Apple’s internal Kext deprecation roadmap, how safe is it to build a product that relies on using an IOKit Driver and are there any requirements similar to DriverKit? Would a developer need to request an entitlement?
If I developed an app using user-space IOKit and, for illustration purposes let’s say I also included an IOKit Driver in the app bundle. If I used the IOKit API, including header file constants only (no digging around in the IORegistryExplorer for non-public keys), as Apple intends them to be used, would this fall foul of any App Store rules that anyone is aware of?
Put another way, would making use of an IOKit Driver disqualify my app from being distributed via the App Store, similar to an Endpoint Security extension?
As an aside, there are a lot of API in the ES Framework that could be used to build apps that have nothing to do with Endpoint Security. File system related apps for example. It’s a shame there isn’t an enhanced middle ground between FSEvents and Endpoint Security framework.
Hi,
I am trying to develop MacOS application which will be connecting to USB devices and should be available in AppStore.
So it must be Sandbox and probably I've to use permission com.apple.security.device.usb.
I've following requirements:
I need to detect USB devices with file system
I need to have ability to upload & download files from this device
I need to read device serial number
I wonder if I can use IOKit for this and it will be compliant with AppStore rules or not?
Hi everyone,
Are there any new updates on communicating with iPhones via USB-C without needing an MFi chip, especially with the iPhone 15 or upcoming iPhone 16?
I'm developing a custom accessory and wondering if the switch to USB-C has introduced any new possibilities for data communication without going through the MFi program.
Thanks!
Hello,
I am facing with misunderstanding how to read usb device properties correctly.
The notification 'kIOTerminatedNotification' is delivered after an IOService has been terminated. Can I use IORegistryEntryCreateCFProperties() to get properties of terminated device?
I am asking because I/O Registry is a dynamic database that captures the connections of all driver and nub objects currently active. Howerver, can we say that terminated device is still active?
If IORegistryEntryCreateCFProperties() can not be used, are there any other way? (e.g. using Device Interface)
Thank you in advance!
Is there an option to create a macOS filter driver for the IONVMeFamily Driver that would enable sending NVMe Admin commands( likes security send , security receive, Firmware download)? This approach would allow us to reuse the IO path provided by the family driver.
Hello,
I'd like to find out if macOS Sequoia's MAC Address randomization affects the data (specifically, MAC addresses) we receive from I/O Kit.
For context, I'd like to find out if it affects my Mac App Store receipt validation code in any way.
Thank you,
– Matthias
Hi all,
I was wondering if anyone knew a way to change the brightness of your MacBook screen in Swift without using an overlay that changes the colours?
I want the effect of just pressing the F1 and F2 brightness controls but done without using system events/ Applescript popping up windows on the screen.
I think the UIScreen.brightness is something similar to what I want but it is not available for NSscreen. I can't figure out a way to do it with IOKit either.
Things like ddccl doesn't work as the screen is not an external monitor.
If there is a solution using Swift or terminal commands any help is much appreciated.
Thanks,
James
Hello,
I am working on app which must prevent attaching any USB devices to Mac due to security.
Unfortunately I have not found any direct way to implement such blocking:
Looks like IOKit does not allow to block USB (at least in user space)
ES_EVENT_TYPE_AUTH_IOKIT_OPEN (Endpoint Security) does not prevent using USB device if I send response ES_AUTH_RESULT_DENY for "AppleUSBHostDeviceUserClient"
I have found several similar problems on forum but no any solution:
https://developer.apple.com/forums/thread/671193
(https://developer.apple.com/forums/thread/756573
https://developer.apple.com/forums/thread/741051
What is the easiest way to implement such blocking?
Thank you in advance!
Investigating a kernel panic, I discovered that Apple Silicon Panic traces are not working with how I know to symbolicate the panic information. I have not found proper documentation that corrects this situation.
Attached file is an indentity-removed panic, received from causing an intentional panic (dereferencing nullptr), so that I know what functions to expect in the call stack. This is cut-and-pasted from the "Report To Apple" dialog that appears after the reboot:
panic_1_4_21_b.txt
To start, I download and install the matching KDK (in this case KDK_14.6.1_23G93.kdk), identified from this line:
OS version: 23G93
Kernel version: Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:04 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T8122
Then start lldb from Terminal, using this command:
bash_prompt % lldb -arch arm64e /Library/Developer/KDKs/KDK_14.6.1_23G93.kdk/System/Library/Kernels/kernel.release.t8122
Next I load the remaining scripts per the instructions from lldb:
(lldb) settings set target.load-script-from-symbol-file true
I need to know what address to load my kext symbols to, which I read from this line of the panic log, after the @ symbol:
com.company.product(1.4.21d119)[92BABD94-80A4-3F6D-857A-3240E4DA8009]@0xfffffe001203bfd0->0xfffffe00120533ab
I am using a debug build of my kext, so the DWARF symbols are part of the binary. I use this line to load the symbols into the lldb session:
(lldb) addkext -F /Library/Extensions/KextName.kext/Contents/MacOS/KextName 0xfffffe001203bfd0
And now I should be able to use lldb image lookup to identify pointers on the stack that land within my kext. For example, the current PC at the moment of the crash lands within the kext (expected, because it was intentional):
(lldb) image lookup -a 0xfffffe001203fe10
Which gives the following incorrect result:
Address: KextName[0x0000000000003e40] (KextName.__TEXT.__cstring + 14456)
Summary: "ffer has %d retains\n"
That's not even a program instruction - that's within a cstring. No, that cstring isn't involved in anything pertaining to the intentional panic I am expecting to see.
Can someone please explain what I'm doing wrong and provide instructions that will give symbol information from a panic trace on an Apple Silicon Mac?
Disclaimers:
Yes I know IOPCIFamily is deprecated, I am in process of transitioning to DriverKit Dext from IOKit kext. Until then I must maintain the kext.
Terminal command "atos" provides similar incorrect results, and seems to not work with debug-built-binaries (only dSYM files)
Yes this is an intentional panic so that I can verify the symbolicate process before I move on to investigating an unexpected panic
I have set nvram boot-args to include keepsyms=1
I have tried (lldb) command script import lldb.macosx but get a result of error: no images in crash log (after the nvram settings)
Hello,
I am trying to get value of InterfaceClass for particular USB Device. I modified matching dictionary and added the property locationID property.
CFMutableDictionaryRef matchingDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
if (!matchingDictionary)
{
return -1;
}
int32_t locationID = 0xffff;
CFNumberRef cfLocationID = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &locationID);
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBHostPropertyLocationID), cfLocationID);
CFRelease(cfLocationID);
io_service_t ioService = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDictionary);
if (!ioService)
{
return -1;
}
CFNumberRef cfInterfaceClass = (CFNumberRef)IORegistryEntrySearchCFProperty(ioService, kIOServicePlane, CFSTR(kUSBHostMatchingPropertyInterfaceClass), kCFAllocatorDefault, kNilOptions);
...
Unfortunately nothing is found and ioService is NULL. What can be wrong here?
Thank you in advance!
Many USB storage devices are NVMe devices accessed through a USB-NVMe bridge chip, such as those by JMicron and Asmedia. These chipsets do not forward SMART data requests to the devices, but do have the capability to forward raw NVMe commands to the devices using vendor specific requests.
How can we provide access to SMART data for NVMe devices accessed through a bridge chip like this?
Many people used the OSX SAT SMART kext driver to provide access to SMART data for devices using USB-SATA chips, but it is a kext and doesn't support NVMe. See https://binaryfruit.com/drivedx/usb-drive-support#install-instructions
Would we need to implement a kext like that to make this work? Is there a DriverKit way to do this?
Hi there,
I am working on a little application which processes cursor and graphics tablet data and adds some extra control to the output.
So far it makes use of...
if let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap, //.cghidEventTap
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: eventMask,
callback: handleTapEvent,
userInfo: userInfo)
... to modify existing events.
The issue that in some cases arises (it's a globally working app) - that some other applications pull and process pointer-data aside the event stream and therefor create conflicting values.
Would creating and posting events to a 'virtual pointing device' on a lower system level (kext) help?
Let's discuss. BR, E
Hello everyone!
I'm developing framework and app for macOS for PCI devices. For communication with driverkit, I'm verifying by giving userclient access entities of system extension to app.
However, the app is just a sample program, and our customer is trying to develop the app using a framework with PCI communication part.
Is there a way to build a framework with my company's signature, and to build and execute it without acquiring userclient access elements by any chance by a customer developer?
Moreover, userclient access is only available to developers who have subscribed to the Apple Developer Program, so I hope that client/developers do not need to obtain separate entries.
When using external NVMe devices on iOS / iPadOS I cannot tell how to access the disk SMART data.
On macOS I can use NVMeSMARTLibExternal.h to access this information but the same system does not seem to work on iPadOS (even with Thunderbolt NVMe devices).
When using M series iPads with professional Thunderbolt storage this woudlbe very useful.
Hello, forum, I'm trying to build connection between a non-MFi HID device (like keyboard) and iOS app with IOKit and Swift.
Thanks to this post, I have manage to import the IOKit into objc header.
IOKit on iOS/iPadOS 16.0+
#import <IOKit/IOKitLib.h>
However, I have this compiler error when I try to imitate same methods in the SerialPortSaple project from following article, but the IOKit can not be imported to Swift at first place.
Communicating with a Modem on a Serial Port
The screen shot of the sample project:
It looks like the complier unable to reach the io_object_t type somehow, is there any workaround or approach?
I confirmed that I had successfully turned on the device and registered the callback method,
but the callback method was not successfully invoked when moving with Magic mouse.
What method does the Magic Mouse and Magic Trackpad need to use to get the input value of the device?
IOReturn ioReturn = IOHIDDeviceOpen(deviceRef, kIOHIDOptionsTypeNone);
if ( kIOReturnSuccess == ioReturn ) {
IOHIDDeviceRegisterInputValueCallback(deviceRef, mouseInputValueCallback, NULL);
}
static void mouseInputValueCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {
NSLog(@"handle input value");
}
I passed the __IOHIDDeviceCopyMatchingInputElements get magic mouse input element
<__NSArrayM 0x6000012c4900>(
timestamp: 0 type: 1 usagePage: 1 usage: 48 reportID: 16 value: 0,
timestamp: 0 type: 1 usagePage: 1 usage: 49 reportID: 16 value: 0,
timestamp: 0 type: 2 usagePage: 9 usage: 1 reportID: 16 value: 0,
timestamp: 0 type: 2 usagePage: 9 usage: 2 reportID: 16 value: 0
)
(NSArray *)getHIDReprots {
if (!deviceRef) {
return nil;
}
NSMutableArray *reports = [[NSMutableArray alloc] init];
CFArrayRef elements;
CFIndex i;
elements = IOHIDDeviceCopyMatchingElements(deviceRef, NULL, kIOHIDOptionsTypeNone);
for (i = 0; i<CFArrayGetCount(elements); i++) {
const IOHIDElementRef element = (void*)CFArrayGetValueAtIndex(elements, i);
IOHIDElementType eleType = IOHIDElementGetType(element);
NSString *eleTypeStr = @"";
switch (eleType) {
case kIOHIDElementTypeInput_Misc:
eleTypeStr = @"kIOHIDElementTypeInput_Misc";
break;
case kIOHIDElementTypeInput_Button:
eleTypeStr = @"kIOHIDElementTypeInput_Button";
break;
case kIOHIDElementTypeInput_Axis:
eleTypeStr = @"kIOHIDElementTypeInput_Axis";
break;
case kIOHIDElementTypeInput_ScanCodes:
eleTypeStr = @"kIOHIDElementTypeInput_ScanCodes";
break;
case kIOHIDElementTypeInput_NULL:
eleTypeStr = @"kIOHIDElementTypeInput_NULL";
break;
case kIOHIDElementTypeOutput:
eleTypeStr = @"kIOHIDElementTypeOutput";
break;
case kIOHIDElementTypeFeature:
eleTypeStr = @"kIOHIDElementTypeFeature";
break;
case kIOHIDElementTypeCollection:
eleTypeStr = @"kIOHIDElementTypeCollection";
break;
default:
break;
}
uint32_t page = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
uint32_t reportID = IOHIDElementGetReportID(element);
uint32_t reportSize = IOHIDElementGetReportSize(element);
uint32_t reportCount = IOHIDElementGetReportCount(element);
NSString *elementStr = [[NSString alloc] initWithFormat:@" reportID:%d, reportSize:%d, type:%@, UsagePage:%d, usage:%d, reportCount:%d\n\n",reportID, reportSize, eleTypeStr, page, usage, reportCount];
[reports addObject:elementStr];
}
return reports;
}
reportID:0, reportSize:0, type:kIOHIDElementTypeCollection, UsagePage:1, usage:2, reportCount:1
reportID:0, reportSize:0, type:kIOHIDElementTypeCollection, UsagePage:1, usage:1, reportCount:1
reportID:16, reportSize:16, type:kIOHIDElementTypeInput_Misc, UsagePage:1, usage:48, reportCount:1
reportID:16, reportSize:16, type:kIOHIDElementTypeInput_Misc, UsagePage:1, usage:49, reportCount:1
reportID:16, reportSize:1, type:kIOHIDElementTypeInput_Button, UsagePage:9, usage:1, reportCount:1
reportID:16, reportSize:1, type:kIOHIDElementTypeInput_Button, UsagePage:9, usage:2, reportCount:1
reportID:71, reportSize:8, type:kIOHIDElementTypeFeature, UsagePage:6, usage:32, reportCount:1
reportID:85, reportSize:512, type:kIOHIDElementTypeFeature, UsagePage:65282, usage:85, reportCount:64
reportID:0, reportSize:0, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:-1, reportCount:1
reportID:0, reportSize:1, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:0, reportCount:1
reportID:16, reportSize:0, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:-1, reportCount:1
reportID:16, reportSize:1, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:0, reportCount:1
reportID:71, reportSize:0, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:-1, reportCount:1
reportID:71, reportSize:1, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:0, reportCount:1
reportID:85, reportSize:0, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:-1, reportCount:1
reportID:85, reportSize:1, type:kIOHIDElementTypeInput_NULL, UsagePage:0, usage:0, reportCount:1
Devices:
VendorID ProductID LocationID UsagePage Usage RegistryID Transport Class Product UserClass Built-In
0x4c 0x265 0x3bab3ec3 1 2 0x100001ec4 Bluetooth AppleHSBluetoothHIDDriver Magic Trackpad (null) 0
0x4c 0x265 0x3bab3ec3 0 1 0x100001eb2 Bluetooth AppleHSBluetoothDevice Magic Trackpad (null) 0
0x5ac 0x30d 0x5abbb32b 1 2 0x100001221 Bluetooth BNBMouseDevice Magic Mouse (null) 0
0x4c 0x265 0x3bab3ec3 65280 3 0x100001ed2 Bluetooth AppleHSBluetoothHIDDriver Magic Trackpad (null) 0
0x4c 0x265 0x3bab3ec3 65280 13 0x100001ecc Bluetooth AppleHSBluetoothHIDDriver Magic Trackpad (null) 0
0x4c 0x265 0x3bab3ec3 65280 11 0x100001eba Bluetooth AppleHSBluetoothHIDDriver Magic Trackpad (null) 0
Hello.
Is there a legal way to block iOS devices from being mounted on macOS?
I noticed, that when an iOS device is connected, it pretends to be like a storage device but it is not. It not even going through diskArbitration. It seems that some fileProvider is taking place there.
I know that it is possible to do via the MDM profile:
<key>PayloadContent</key>
<dict>
<key>.GlobalPreferences</key>
<dict>
<key>Forced</key>
<array>
<dict>
<key>mcx_preference_settings</key>
<dict>
<key>ignore-devices</key> <true/>
</dict>
</dict>
</array>
</dict>
</dict>
But is there some programmatic solution?
If I use EndpointSecurity and block file operations for the usbmuxd process on /var folder, it prevents iOS devices from being mounted. But wouldn't be there any negative side effects from such a solution?
Hi everyone,
first-time caller, long-ti... wait, no, I just got here. :)
I am relatively new to all things Apple, so apologies in advance if it takes me a few goes to properly explain things.
We have a framework, which includes an API, an XPC service, etc, and we have a device driver. We also have some sample apps that use the framework, and if they have the app sandbox capability, then we expect them to use the XPC Service instead of accessing our driver directly. This works fine on Monterey and presumably has worked fine on all previous versions of MacOS.
Something seems to have changed on Ventura, and we don't understand what.
When we build the same app on Ventura, it appears to be in the sandbox (according to the Sandbox column in Activity Monitor), but in the Console there is this line (twice):
default <time> <OurAppName> Revoking sandbox extension; key = 0
Which we suspect is linked to the fact that the app then does not use the XPC Service, and instead accesses the driver directly, much to our surprise.
Software built on developer's machines is "Automatically managed" and "Signed to Run Locally" in case that matters.
Do we need to change our code to support Ventura and onward? Or is it a bizarre bug?
Oh, I should say that I'm running the latest version of Ventura (13.6.7 as of writing) but not the latest Xcode (14.2 (14C18)) and CLI tools... can't remember how to find that version... Apple clang version 14.0.0 (clang-1400.0.29.202).
Any help would be appreciated, thanks.
Jeremy