The DriverKit problems to develop multiple serial USB device - macos

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.)

Related

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

How does GetCommState populate the DCB struct in Windows 10 when using usbser.sys CDC ACM driver

I am building a embedded device that will communicate to the outside world by virtual COM. I have the descriptor and all the callbacks for the USB set up correctly and COM is working - well kind of. The problem is that when I issue the GetCommState command for the COM I get a semi valid struct back and when one fixes only couple of parameters (like setting the speed and 8N1) and try to reconfigure the port by calling SetCommState the actions fails with: 'A device attached to the system is not functioning.'
If one continues to use the port it just work - all writes and reads - without a problem. But the issue is that most libraries try to reconfigure the port by first issuing the GetCommState and then SetCommState - pyserial and C# both do it in this way.
My question is where do the "default" configuration for COM comes form?
In the USB ACM CDC standard there are (optional) class requests for SET and GET COMM feature but I can see (from USB sniffer) that they are never called (I tried with capabilities for USB ACM CDC set to 0x06 (that is without SET/GET COMM) and 0x07 (with SET/GET COMM) but in no case I get a class request from the driver). So the driver must take the config from somewhere else, does anybody knows from where or how?
I am using an NXP LPC and Windows 10 with usbser.sys driver on other end.
What I already checked is:
compared the USB descriptor to the working one - they are the same
checked the USB traffic - the enumeration and communication looks the same
without doing GetCommState and SetCommState the COM is working without problem
I attached the content of the DCB struct for working sample (left) and my (right). I do not understand where do the marked values come from? Who sets them?
The settings should come from the port driver - you can view and set default values in the Windows Device Manager. In your case, it would seem that flow control with RTS/CTS is enabled (left picture), which might be something that your USB adapter uses internally. If it works, then leave those settings as they were.
I'd advise to do like this:
Always check the result of each API function you call!
Call CreateFile to get the port handle.
Optionally call GetCommTimeouts and store the result in a zero-initialized struct like COMMTIMEOUTS com_timeouts = {0};. Change members of the struct as needed, then call SetCommTimeouts.
Create an (almost) zero-initialized struct DCB dcb = { .DCBlength = sizeof(DCB) }.
Call GetCommState on this struct.
Set baudrate, parity, stop bits etc as required. Leave other members as they were.
Call SetCommState.

A driver has enumerated two child PDO's that returned identical Device ID's

I have a kmdf bus driver PCI\VEN_XXXX&DEV_XXXX that creates two statically enumerated PDOs with serial numbers: 217 and 218; one for each Ethernet port. The PDO hardware id is ROOT\MY_NIC_PORT so I can install a NDIS Miniport driver on them.
The bus driver passes SDV and Verifier; but, on reboot two more PDOs get enumerated. On the next reboot I get a duplicate pdo crash.
The toaster example used the device class guid as part of the hardware id. When I tried that my NIC ports no longer showed up in device manager.
Any debug suggestion or work around idea would be appreciated?
pnpCaps.LockSupported = WdfFalse;
pnpCaps.EjectSupported = WdfTrue;
pnpCaps.Removable = WdfTrue;
pnpCaps.DockDevice = WdfFalse;
pnpCaps.UniqueID = WdfTrue;
pnpCaps.SilentInstall = WdfTrue;
pnpCaps.SurpriseRemovalOK = WdfTrue;
pnpCaps.HardwareDisabled = WdfFalse;
pnpCaps.NoDisplayInUI = WdfFalse;
pnpCaps.Address = SerialNo;
pnpCaps.UINumber = SerialNo;
************************************************************
Driver Verifier detected violation:
A driver has enumerated two child PDO's that returned identical Device
ID's.
CulpritAddress = FFFFF8025ED309C4, DeviceObject1 = FFFFE3882FB2F300,
DeviceObject2 = FFFFE3882EBF88D0.
************************************************************
There are a few versions of the toaster bus sample -- assuming you started with this one, then note that it saves its list of child PDOs in the registry. My guess is that your driver is both loading PDOs from the registry, and trying to dynamically create some too.
Set a breakpoint on your driver's version of Bus_PlugInDevice, and see how often it's getting called. Make sure it's never getting called 2x with the same Instance ID.
To clear up a bit of the naming thing: a device setup class is a GUID that is totally unrelated to its hardware ID. For NICs that want to interoperate with the OS's networking stack, you must use the NET setup class, {4d36e972-e325-11ce-bfc1-08002be10318}. You can put anything you want into your hardware ID. I don't really encourage you to put "ROOT\" in there, since that could be confused with a root-enumerated device (which your devices are not). Instead, you can use "yourcompany_yourdevice\port1" as a hardware ID.
While you're thinking about naming things, there are a few things to note about hardware IDs:
Once you assign a HWID, it's rather difficult to change it in a future driver update, without breaking customers who had already installed your device. So get it right the first time.
Once you assign an Instance ID, don't change or reuse it for the lifetime of the device. Otherwise you'll cause this bugcheck, or cause IP addresses to bounce around / get reset. The OS ultimately uses the Instance ID to figure out which NIC port to bind an IP address to.
Think about what happens if someone plugs 2 of your device into a system. Make sure your Instance ID is unique across all ports. You can do this by encoding into the Instance ID the PCI device serial number (if it has one) or by falling back to the PCI bus:device:function.
Don't lump together different types of hardware under the same hardware ID. For example, if the deluxe version of your device supports checksum offload, but the regular version does not -- you should use 2 different hardware IDs for these two different devices. Otherwise it gets difficult to write a single INF that has keywords for both.

Difference between uart_register_driver and platform_driver_register?

I am studying UART Driver in kernel code and want to know, who first comes into picture, device_register() or driver_register() call?
For difference between them follow this.
and in UART probing, we call
uart_register_driver(struct uart_driver *drv)
and after successfully registration,
uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
Please explain this in details.
That's actually two questions, but I'll try to address both of them.
who first comes into picture, device_register() or driver_register() call?
As it stated in Documentation/driver-model/binding.txt, it doesn't matter in which particular order you call device_register() and driver_register().
device_register() adds device to device list and iterates over driver list to find the match
driver_register() adds driver to driver list and iterates over device list to find the match
Once match is found, matched device and driver are binded and corresponding probe function is called in driver code.
If you are still curious which one is called first (because it doesn't matter) -- usually it's device_register(), because devices are usually being registered on initcalls from core_initcall to arch_initcall, and drivers are usually being registered on device_initcall, which executed later.
See also:
[1] From where platform device gets it name
[2] Who calls the probe() of driver
[3] module_init() vs. core_initcall() vs. early_initcall()
Difference between uart_register_driver and platform_driver_register?
As you noticed there are 2 drivers (platform driver and UART driver) for one device. But don't let this confuse you: those are just two driver APIs used in one (in fact) driver. The explanation is simple: UART driver API just lacks some functionality we need, and this functionality is implemented in platform driver API. Here is responsibility of each API in regular tty driver:
platform driver API is used for 3 things:
Matching device (described in device tree file) with driver; this way probe function will be executed for us by platform driver framework
Obtaining device information (reading from device tree)
Handling Power Management (PM) operations (suspend/resume)
UART driver API: handling actual UART functionality: read, write, etc.
Let's use drivers/tty/serial/omap-serial.c for driver reference and arch/arm/boot/dts/omap5.dtsi for device reference. Let's say, for example, we have next device described in device tree:
uart1: serial#4806a000 {
compatible = "ti,omap4-uart";
reg = <0x4806a000 0x100>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart1";
clock-frequency = <48000000>;
};
It will be matched with platform driver in omap-serial.c by "ti,omap4-uart" string (you can find it in driver code). Then, using that platform driver, we can read properties from device tree node above, and use them for some platform stuff (setting up clocks, handling UART interrupt, etc.).
But in order to expose our device as standard TTY device we need to use UART framework (all those uart_* functions). Hence 2 different APIs: platform driver and UART driver.

How linux drive many network cards with the same driver?

I am learning linux network driver recently, and I wonder that if I have many network cards in same type on my board, how does the kernel drive them? Does the kernel need to load the same driver many times? I think it's not possible, insmod won't do that, so how can I make all same kind cards work at same time?
regards
The state of every card (I/O addresses, IRQs, ...) is stored into a driver-specific structure that is passed (directly or indirectly) to every entry point of the driver which can this way differenciate the cards. That way the very same code can control different cards (which means that yes, the kernel only keeps one instance of a driver's module no matter the number of devices it controls).
For instance, have a look at drivers/video/backlight/platform_lcd.c, which is a very simple LCD power driver. It contains a structure called platform_lcd that is private to this file and stores the state of the LCD (whether it is powered, and whether it is suspended). One instance of this structure is allocated in the probe function of the driver through kzalloc - that is, one per LCD device - and stored into the platform device representing the LCD using platform_set_drvdata. The instance that has been allocated for this device is then fetched back at the beginning of all other driver functions so that it knows which instance it is working on:
struct platform_lcd *plcd = to_our_lcd(lcd);
to_our_lcd expands to lcd_get_data which itself expands to dev_get_drvdata (a counterpart of platform_set_drvdata) if you look at include/linux/lcd.h. The function can then know the state of the device is has been invoked for.
This is a very simple example, and the platform_lcd driver does not directly control any device (this is deferred to a function pointer in the platform data), but add hardware-specific parameters (IRQ, I/O base, etc.) and you get how 99% of the drivers in Linux work.
The driver code is only loaded once, but it allocates a separate context structure for each card. Typically you will see a struct pci_driver with a .probe function pointer. The probe function is called once for each card by the PCI support code, and it calls alloc_etherdev to allocate a network interface with space for whatever private context it needs.

Resources