MacOS custom Audio Driver doesn't override the default Driver - macos

We are developing a custom audio driver for a USB microphone in order to do simple processing (EQs) on the input audio stream (comparable to an APO for Windows).
After some help, we managed to assign our Driver (based on the SimpleAudioDriver) to the right audio device. But we now have two devices showing up : one is assigned with our driver and the other one is assigned to the default driver. How can we override the original one with ours, to just have one device showing up ?
We already tried to add more IOKitPersonalities to have a better probe score but it's the same.
We also read the logs and the original driver and our probe score are both at the maximum (100000).
Here is the current state of our info.plist file :
<plist version="1.0">
<dict>
<key>IOKitPersonalities</key>
<dict>
<key>SimpleAudioDriver</key>
<dict>
<key>idProduct</key>
<integer>49456</integer>
<key>idVendor</key>
<integer>1130</integer>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOMatchCategory</key>
<string>SimpleAudioDriver</string>
<key>IOProviderClass</key>
<string>IOUSBDevice</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOUserAudioDriverUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOUserClass</key>
<string>IOUserAudioDriverUserClient</string>
</dict>
<key>IOUserClass</key>
<string>SimpleAudioDriver</string>
<key>IOUserServerName</key>
<string>com.example.apple-samplecode.SimpleAudio.Driver</string>
<key>SimpleAudioDriverUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOUserClass</key>
<string>SimpleAudioDriverUserClient</string>
</dict>
</dict>
</dict>
</dict>
</plist>
If you have any hints, please get back to us.
Thanks.

The sample driver doesn't match any real hardware, but instead attaches itself to the dummy "resources" nub:
<key>IOProviderClass</key>
<string>IOUserResources</string>
This is a generic object that does nothing other than exist so that virtual drivers can attach. By default, objects can only be successfully be matched by one client (i.e. driver), so virtual drivers would fight over access to this dummy object. So when matching this object only, you need to use match categories:
<key>IOMatchCategory</key>
<string>SimpleAudioDriver</string>
However, you must absolutely not use this key when matching real hardware, unless you have a very good reason. If you keep this in, your driver's match category will be different from Apple's default driver, so both will match the device, leading to chaos.
To avoid multiple drivers trying to claim the same device, simply remove the entire IOMatchCategory key/value pair to use the default category, which is also what the standard macOS USB audio driver uses, and therefore only one driver will succeed at matching. Which one wins can be controlled via the different probe score levels for different USB matching patterns or, in unusual cases, using an explicit probe score.

Related

The DriverKit problems to develop multiple serial USB device

I am developing a DriverKit driver for a USB device which has multiple serial UARTs connected. Each UART will represent a cu.USBX port on Mac. My driver inherits IOUSBHostDevice class and it matches device ID well. Now, I am going to crate a new class inherits IOUserSerial to implement serial port. However, compiler said no new operator on base class. It seems the base OSObject class prevent to new the subclass as I did in IOKit driver. Since the similar IOUserSerial/IOUserUSBSerial examples are hard to find, I would like to ask if anyone can help me to solve this problem. Any feedback and clue is appreciated. Following are some snippets to show my situation.
My original IOKit port driver inherits IORS232SerialStreamSync.
class KextSerial : public IORS232SerialStreamSync
{
OSDeclareDefaultStructors( KextSerial ) ; // Constructor & Destructor stuff
:
}
My USB driver could create new KextSerials and initiate them as well.
KextSerial * newSerial = new KextSerial;
if( !newSerial->init(0, 0) ){
goto FailExit;
}
However, in my DriverKit port driver inherits IOUserSerial.
class DextSerial : public IOUserSerial
{
:
}
While I try to new the DextSerial as following.
DextSerial * newSerial = new DextSerial;
The compiler said "No matching function for call to 'operator new'"
Maybe I can't do this in DriverKit but I can't find documents from Apple's developing website.
Parallelly I have tried IOUserUSBSerial and OSObject, I got the same error message.
Your question isn't especially clear, but I'll do my best to go through the various options.
An instance of your main driver class is created by the system when your driver matches its provider device.
Creating instances explicitly in code mainly happens for user client objects if you're implementing NewUserClient yourself, or if you want to create client objects to attach below your main driver object instance.
Main driver object
When it comes to creating your main driver instance, this is where I/O Kit matching comes in, and it's all defined in the dext's Info.plist file. This part is very similar to kexts, apart from some changed/new IOKitPersonalities keys:
Key
Value Type
Description (dext)
Description (dext)
IOClass
string
DriverKit kernel-side IOService subclass, e.g. IOUserService, IOUserSCSIParallelInterfaceController, …
The driver's own IOService (or deeper) subclass
IOUserClass
string
Dext-side name of the driver's IOService (or deeper) subclass
N/A
CFBundleIdentifier
string
Bundle identifier of the dext
Bundle identifier of the kext
CFBundleIdentifierKernel
string
Bundle identifier of the kext hosting the DriverKit kernel-side class (IOClass), e.g. com.apple.kpi.iokit, com.apple.iokit.IOSCSIParallelFamily, …
N/A
IOUserServerName
string
Bundle identifier of the dext
N/A
Beyond that, the IOKitPersonalities dictionaries take the same form as for kexts.
So if your driver's main class is called KextSerial, you would put that as the value for IOUserClass.
Subordinate objects:
If you really need to create IOService object instances explicitly, you use IOService::Create(). The reason you can't just use standard C++ syntax is that the object needs to be mirrored in the kernel's view of the I/O Kit registry. So as with the main driver object, you need to specify the kernel and DriverKit side classes to instantiate.
The way Apple expects you to do this is by providing a dictionary property in your driver's IOKit personality, so this becomes a property on your main driver object instance. In the Create call you merely specify the property's name, the dictionary is specified in the Info.plist and looks something like this:
<key>MyDriverSubService</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOUserClass</key>
<string>MyDriverSubService</string>
</dict>
The keys and values have the same meanings as for the main personality.
My suggestion
My understanding of your situation is the following: you have a USB device which implements more than 1 serial port. How to proceed depends on the layout of the USB device I think.
1 USB Interface per serial port
If your device exposes 1 interface for each serial port it implements in its descriptor table, and their respective endpoints can be driven independently, then I'd directly subclass IOUserUSBSerial for one port, matching any of the interfaces. The system will create an instance of your driver for each port, and your driver just needs to worry about one port at a time.
If your device's interfaces aren't automatically enumerated by the system (e.g. if device class is 0xff), you may need an "umbrella" driver class and IOKit personality which matches the device itself and configures it, prompting the interfaces to be enumerated.
No 1:1 mapping between USB interfaces and serial ports.
If there isn't a 1:1 correspondence between USB interfaces and serial ports, for example just one interface ferrying the traffic from all serial ports, you'll want to have a main driver class which merely subclasses IOService.
This creates child objects, which will be instances of subclasses of IOUserSerial or IOUserUSBSerial. Depending on whether the SerialDriverKit permits multiple instances of these or not in one driver instance, you can either create these using IOService::Create, or you will need to run each port in its own driver instance, communicating with the USB device via an API you've defined on the main driver class. This is achieved by either directly matching your main driver class, or by creating instances of "nub" child objects in the main driver class and then matching those in the per-port personality. (The latter has the advantage of allowing the driver to dynamically enumerate the number of ports; when matching the main driver object directly, you'll need to use match categories to define the number of ports, and to disambiguate which instance is which.)
For example, see this question and answer about how to create a driver for a network adapter with multiple ports, as registering multiple IOUserNetworkEthernet objects from the same instance does not appear to work as you might expect. I do not know if SerialDriverKit has the same limitation, you'll have to try and find out.
Final notes
This may just be a misuse of terminology in your question, but: you do not want to inherit (subclass) from IOUSBHostDevice, you want to match existing instances of it representing the device to drive. (Use it or IOUSBHostInterface as the IOProviderClass.)

How to identify a HID after it's moved to a different port on Windows

I'm on Windows and am trying to store calibration data for game controllers connected via USB and am trying to find a value which uniquely identifies them in a port independent way.
There is the HidD_GetSerialNumberString function but i've read here that it's uncommon for devices to have serial numbers and indeed when i try to read one f.e. from a PS4 controller HidD_GetSerialNumberString returns FALSE and GetLastError returns ERROR_INVALID_PARAMETER.
Is there any other data available which can be accessed to achieve this?
You can try to use instance ID for your HID device (call CM_Get_Device_Interface_Property with device interface path and DEVPKEY_Device_InstanceId property and use string after last & char). It should be unique and persistent per system restarts. But it is not guaranteed if serial number is not provided (in this case instance ID will be different if device is plugged into different usb port)...
More info on this here: https://stackoverflow.com/a/56885175/1795050

What keys should I specify in the matching dictionary to only detect every storage USB device on mac os x?

I'm working on a cocoa app that detects any USB storage device (flash disk, Smartphone...) connected to the Mac and then use its files.When creating the matching dictionary I have to specify some keys and their values to only detect storage devices. I'm working on Mac os x.
Question : What keys should I specify in the matching dictionary to only detect every storage USB device? and what should be their values?
Is this I/O Kit matching or disk arbitration matching?
For the former, I don't think there's a clean way to match the IOMedia object, because that would require matching against more than one level of the I/O Kit device tree (with at least one level in between). So you have to do a passive match against the IOMedia object and then examine its provider's provider('s provider's provider...) to determine whether to actually match, e.g. match against:
<key>IOProviderClass</key>
<string>IOMedia</string>
<key>IOPropertyMatch</key>
<dict>
<key>Whole</key>
<true/>
</dict>
And then iterate your way up the tree until you find a node that contains a "Protocol Characteristics" dictionary and examine its "Physical Interconnect" property to see if it says "USB".
I'm not 100% certain that there's not a simpler way, but I don't know about it if it exists.
If you're using the Disk Arbitration framework, it is easy:
CFMutableDictionaryRef matchingDict =
CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(matchingDict,
kDADiskDescriptionDeviceProtocolKey,
CFSTR(kIOPropertyPhysicalInterconnectTypeUSB));
(quoted from the Disk Arbitration Programming Guide). From there, you can get the underlying IOMedia object by calling the DADiskCopyIOMedia function, which makes that probably the better API, assuming you're not doing in-kernel matching.
For other folks who might read this answer in the future, if you happen to be doing in-kernel matching for a kext that needs to attach to an IOMedia nub, just walk the provider chain in your probe method, and return either an instance of your class (to allow your driver to attach) or NULL (to reject that IOMedia object). Obviously that doesn't apply to a Cocoa app. Count your blessings. :-)

Getting USB data for OS X /dev/tty.* entry

I am trying to (programatically in Java, though running external scripts is a possibility) get the USB idProduct, idVendor and ideally the string descriptors for the manufacturer and product for a USB device.
The device has (and it is all I am provided with in the beginning) a /dev/tty.usbmodem entry, and I need some way to reliably map that back to the USB information, but I can't find anything that relates one to the other (or rather I don't know where to look).
The device in question has the /dev entry of:
crw-rw-rw- 1 root wheel 10, 2 18 Jul 15:08 /dev/tty.usbmodem9f31
system_profiler gives me the following USB information for the device:
<dict>
<key>_name</key>
<string>Stk500v2</string>
<key>a_product_id</key>
<string>0xa662</string>
<key>b_vendor_id</key>
<string>0x0403 (Future Technology Devices International Limited)</string>
<key>c_bcd_device</key>
<string> 1.80</string>
<key>e_device_speed</key>
<string>full_speed</string>
<key>f_manufacturer</key>
<string>www.cpustick.com</string>
<key>g_location_id</key>
<string>0x9f300000</string>
<key>h_bus_power</key>
<string>500</string>
<key>j_bus_power_used</key>
<string>500</string>
</dict>
I can see the prefix of 0x9f3 for the g_location_id matching the start of the dev entry's suffix of 9f31, but I don't know a) how reliable that would be, and b) what the "1" would represent at the end, and how that would be affected by having multiple of the same device on the same bus, and how you would differentiate between them.
I have other devices I need to do this with as well, and they don't all follow the same naming rules. One of them uses the USB serial number field for the suffix. Other potential ones (that I don't have control over at all) could do it differently again I guess.
So basically I need some way of mapping the textual device name /dev/tty.whatever to a physical USB device entry that I can then pull the data I need from. On Linux I do it by traversing the /sys tree from /sys/class/tty through to /sys/devices/pci...blahblah/usb[x]/wherever but I can find no similar facility on OS X.
You can use IOKit to get the USB vendor ID and product ID of a serial port on Mac OS X.
Also, there is a library called libserialport that you could use:
http://sigrok.org/wiki/Libserialport

OS X. How to get TISInputSourceRef (keyboard layout) of current active window of all system in daemon?

I want to get keyboard layout something like GetKeyboardLayout(threadId) in Windows. threadId is id of application with active window. I want to translate virtual key code to char of current language.
Now I use TISCopyCurrentKeyboardInputSource() function but it don't have parameters and it return only "U. S." language. I think this is layout of daemon.
Also I can't convert code with TISInputSourceRef of TISCopyInputSourceForLanguage(language) function. It return only US characters.
UPDATE:
Okey. I find solution of converting keyCodes. I jast call function UCKeyTranslate with parameter modifiers equal 0. But I cant't find how to get input source of active window or just any running application.
UPDATE 2: plist path is /Library/LaunchAgents
<?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>Label</key>
<string>my.keylogger</string>
<key>Program</key>
<string>/Users/Titan/Desktop/keylogger</string>
<key>ProgramArguments</key>
<array>
<string>keylogger</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
Whenever you want to access the state of user sessions from a daemon, You're Doing It Wrong™. It can't be made to work reliably.
The standard solution is to use user agents that get launched in each user session. Each agent can contact a central daemon and report about the state of the user session, if necessary.
See Technical Note TN2083: Daemons and Agents for more details.
Update:
The above technote is relevant for understanding why you can't do this from a daemon.
For the agent, it should use the distributed notification center to observe the kTISNotifySelectedKeyboardInputSourceChanged notification to learn when the selected keyboard input source changes. Then it can call TISCopyCurrentKeyboardInputSource() to learn what keyboard input source is current.
In order for the agent to receive the notification, it must run the run loop of its main thread in one of the common modes. In a normal Cocoa app, this is done for you as part of the main event loop. In a different kind of program, you have to do this yourself.
BOOL shouldKeepRunning = YES; // global
...
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMethod:)
name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged
object:nil
suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
/* do nothing */;
...
- (void) myMethod:(NSNotification*)note
{
TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource();
// do something with inputSource ...
}

Resources