I am developing a DriverKit driver for single port USB-serial adapter. I have a simple driver code as following.
class MyDriver: public IOUserUSBSerial
{
public:
virtual bool init() override;
virtual kern_return_t Start(IOService *provider) override;
virtual kern_return_t Stop(IOService *provider) override;
virtual void free() override;
};
bool
MyDriver::init()
{
Log("Entered %s", __FUNCTION__);
return super::init();
}
void
MyDriver::free()
{
Log("Entered %s", __FUNCTION__);
super::free();
}
kern_return_t
IMPL(MyDriver, Start)
{
kern_return_t ret;
Log("Entered %s", __FUNCTION__);
ret = Start(provider, SUPERDISPATCH);
if( ret!=kIOReturnSuccess ){
Log("Start returns Error: 0x%X\n", ret);
return ret;
}
ret = RegisterService();
if( ret!=kIOReturnSuccess ){
Log("IOReturn Error: 0x%X @ %d, %s\n", ret, __LINE__, __FUNCTION__);
return ret;
}
Log("Hello MyDriver is started...");
return ret;
}
kern_return_t
IMPL(MyDriver, Stop)
{
kern_return_t ret = kIOReturnSuccess;
Log("Entered %s", __FUNCTION__);
ret = Stop(provider, SUPERDISPATCH);
if( ret!=kIOReturnSuccess ){
Log("IOReturn Error: 0x%X @ %d, %s\n", ret, __LINE__, __FUNCTION__);
}
return ret;
}
<key>IOKitPersonalities</key>
<dict>
<key>MyDriver</key>
<dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleIdentifierKernel</key>
<string>com.apple.driver.driverkit.serial</string>
<key>IOClass</key>
<string>IOUserSerial</string>
<key>IOMatchCategory</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOProviderClass</key>
<string>IOUSBHostDevice</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOUserClass</key>
<string>MyDriver</string>
<key>IOUserServerName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>idVendor</key>
<integer>1234</integer>
<key>idProduct</key>
<integer>5678</integer>
<key>IOTTYBaseName</key>
<string>USB</string>
<key>IOTTYSuffix</key>
<string>4</string>
</dict>
</dict>
My USB device was detected by system, the init and Start were called successfully. However, while I call Start(provider, SUPERDISPATCH)
, it returns error code 0xE00002C7
(kIOReturnUnsupported
).
I have referred this thread. If I change to inherit IOUserSerial
class, The call to Start returns success. I guess the problem is caused by CFBundleIdentifierKernel
but I can't find any information about that.
Could any expert kindly tell me why the Start returns error?
Can I ignore the call to super::Start
?
Can I use IOUserSerial
subclass if I prefer to develop some specific features?
I have tried to read all articles online and feel frustrated. IOUserUSBSerial
example code is also not available online.
Disclaimer: I've not used IOUserUSBSerial
myself yet, but I have years of experience with other parts of DriverKit and IOKit and the kernel before that. With Apple's documentation you always have to read between the lines to work out what it's implying even if it's not stating it explicitly.
IOUserUSBSerial
:In short, I suspect the issue is this:
You have a non class-compliant (non-ACM) device for which you'd like to develop a driver to make it act as a serial port to macOS. I believe IOUserUSBSerial
is specifically for implementing vendor-specific behaviour on ACM devices, however.
I deduce this from the phrase "This class automatically manages the serial communications with the device." How would IOUserUSBSerial
know how to "automatically manage the serial communications" if your device implements a custom protocol? It must surely be implementing the ACM USB protocol.
So the fact that your device is USB is irrelevant for choosing IOUserUSBSerial
versus IOUserSerial
, what actually matters is whether or not it's an ACM compliant device. I agree with you that the solution appears to be to use the lower-level IOUserSerial
if your device is USB based but doesn't follow ACM.
<key>IOMatchCategory</key> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
Don't use IOMatchCategory when matching real hardware. (Except in very rare cases where you want multiple drivers attaching to the same node.)
Can I ignore the call to
super::Start
?
Generally: definitely not.
I guess the problem is caused by CFBundleIdentifierKernel but I can't find any information about that.
IOUserUSBSerial
is a bit of a weird one because it seems to be implemented entirely in the framework, within the DriverKit runtime. Unlike most DriverKit classes, there is no kernel class backing it. So com.apple.driver.driverkit.serial
actually appears to be the correct CFBundleIdentifierKernel
, and IOUserSerial
appears to be the correct IOClass
, which is unusual.