While going through the usb code in linux. I came across platform_device_add() api.
My question is when we create a platform device using the above api, how does the associated driver gets bind to it?
As per my understanding is that the platform drivers register itself and when a match occurs with the device tree compatible string and the one mentioned in the driver, the probe of the driver is called.
So does it use something like a "compatible string" as in the case of the device tree?
Does it use the "name" field present in the "struct platform_device" and match it with the "struct platform_driver" 's "name" field.
Please correct me if I am wrong. Also any text source to learn more on this will be helpful as I am new to this.
So, you have asked a few questions here.
We call platform_device_add() whenever we 100% sure that on the given platform we expect the device in question to be present and functioning.
In most cases ->probe() callback is being called synchronously either at the moment of device addition (if driver is already loaded) or at driver loading stage if device is present in the system.
It does not use Device Tree, it's purely board file based enumeration (device presence is identified by other means than ACPI or Device Tree).
Indeed it uses driver name to match. When the API is called it matches by device name. See implementation of platform_match_id() for the actual code.
Related
I've been exploring the MIDI APIs available on macOS and Windows recently (Core MIDI and Windows Multimedia, respectively) and noticed:
On MacOS, after querying for the number of available sources, each source can be uniquely identified by checking the value of the property kMIDIPropertyUniqueID.
On Win32, I can't find a clear alternative - it seems that an input device's "ID" is no more than the index into the list of connected devices, which could change at any time as new devices are connected and disconnected. Information in the capabilities structure one can request from a device is also not particularly distinctive, as using two of the same MIDI keyboard at once would result in two identical capability sets right down to the product name.
Is there some way to uniquely identify these devices in WinMM?
Short answer is no.
ID of a device in WinMM is indeed just an index in the devices list. So if you unplug one device and plug another one with the same properties (name, for example), you won't be able to determine whether the device you work with is the old one or new. Name, of course, can be the same, so that's not an option.
So from programming side there is no way to distinguish two MIDI devices with the same properties in Windows. In an app you can provide a button like "Refresh devices" or "Test device" to be up to date with current devices and to be able to determine which object in GUI corresponds to a MIDI device to.
As for macOS, kMIDIPropertyUniqueID is an option, BUT there are nuances. From the documentation on the constant:
The system assigns unique IDs to all objects. You may set this
property on virtual endpoints; however, doing so may fail if the ID
isn’t unique.
So in some cases you can get not unique value. In my opinion, the most reliable way to identify a device in macOS is to use its reference. For example, when you get a source device with MIDIGetSource you get MIDIEndpointRef as the result. In fact it's a UInt32 number which is truly unique in the scope of the system.
So I in my .NET library DryWetMIDI rely on MIDIEndpointRef to distinguish MIDI devices in macOS. Also it's possible to provide MIDI devices watching (adding/removing). But for Windows it's not possible unfortunately.
Unfortunately there is no equivalent in WinMM.
The best you can do is open the device to get a handle to it. The handle will be unique and will always refer to the opened device, even if other devices are added and removed.
Once you have the handle, you can use midiInGetID() or midiOutGetID() to get the ID in case it has changed.
I'm trying to have WDK7 Toaster's bus device and child devices share a same .sys driver file. I combine the busenum.sys and toaster.sys into a single Nls_ufcom.sys(bus.inf and toaster.inf both refer to the same Nls_ufcom.sys as service binary), but Windows reports error 38(CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD) for the child device after I assign toaster.inf as the driver from Device Manager. So, the AddDevice() callback is even not called for the child device.
I'm not sure whether I have done something wrong, or Windows really don't allow this. Wish to get some idea from you, Thank you.
I find out the reason. The reason is: I use different service name in bus.inf and toaster.inf . Apparent conclusion from my case, Windows does not allow two driver services to have [ the same service binary but difference service name ] .
The driver service name is the sub-key under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services .
Assigning the same service name in bus.inf and toaster.inf, then the driver is loaded successfully by both devices.
MSDN CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD's statement is so vague and misleading(since Vista era), which says:
Additionally, this message can appear if a driver is referenced by multiple INF AddService directives in one or more INF files.
-- It does not point out the key problem.
[2017-07-01] Ten months later, I come back to make a confirmation, this sharing sys trick work great from Windows XP SP2 to latest Windows 10.1703, no problem identified until now. I apply this trick in my UFCOM virtual COM port driver so successfully.
Is it possible to take a Windows driver such as a Ports class driver, then have it also set itself up as an NDIS filter (NetService class) driver by calling NdisFRegisterFilterDriver() in it's DriverEntry()? This would be essentially having the driver work double duty as a Ports and NetService class driver, but within a single code base and binary.
I'm attempting to do this and I'm seeing the call to register the NDIS driver fail, specifically with the following trace message:
[0][mp]<==ndisCreateFilterDriverRegistry, FilterServiceName 807EFA18 Status c0000001
[0][mp]==>NdisFRegisterFilterDriver: DriverObject 84C6C428
[0][mp]==>ndisCreateFilterDriverRegistry, FilterServiceName 807EFA18
[0][mp]<==ndisCreateFilterDriverRegistry, FilterServiceName 807EFA18 Status c0000001
I've looked around and it seems that the NDIS driver is heavily dependent on the values placed in the registry from the INF and the INF itself. I've tried to spoof the registry keys by adding the NetCfgInstanceId by hand and calling that value out in my code before trying to register the NDIS filter, but have hit a point where it just seems like the wrong way to go about it.
What is the recommended way to go about this? At this point I'd imagine that this would require a Ports class driver and NetService class driver separately, with some kind of composite driver to tie them together to be able to communicate, or have a way for one or the other to communicate through interprocess communication.
A stern warning
Do not attempt to "install" a filter by manually writing registry keys. As you've noticed, it's not easy, and even if you seem to get it working, it will all collapse when the OS tries to install the next LWF. Furthermore, I added some additional hardening features designed exactly to prevent people from doing this to Windows 10; you'll have to do some significant damage to the OS before you can hijack network bindings in Windows 10.
How to structure your driver package
Anyway, what you're describing is indeed possible. The way to do it is to provide the following in your driver package:
A PNP-style INF. This INF has:
The PORTS class
An AddService directive, that installs your driver service
A CopyFiles directive to bring in any files you need
Any other bits you need for the PNP device
A NetCfg-style INF. This INF has:
The NETSERVICE class
The usual LWF stuff: Characteristics=0x40000, FilterMediaTypes=xxx, FilterType=xxx, etc.
A reference to the service you installed in the other INF (HKR,Ndi,Service,,xxx)
Do not include an AddService or CopyFiles; that's already taken care of by the first INF
One .sys file. This driver does:
In DriverEntry, call NdisFRegisterFilterDriver, and pass the name of your service "xxx"
In DriverEntry, call WdfDriverCreate or fill out the DRIVER_OBJET dispatch table as you normally would for any other PNP driver
Implement FilterAttach and etc normally; implement your WDF EvtXxx or WDM IRP handlers normally
Don't forget to call NdisFDeregisterFilterDriver in EvtDriverUnload or DriverUnload, and also in the failure path for DriverEntry
How to install this fine mess
The good news is that, with these 2 INFs, you can meet your requirement of having 1 .sys file do two things. The bad news is that you've now got 2 INFs. Worse, one of the INFs is a NetCfg-style INF, so you can't just Include+Need it. The only way to install a NetCfg-style INF is to call INetCfgClassSetup::Install (or NetCfg.exe, its command-line wrapper). Windows Update only knows how to install PNP-style INFs, and PNP only knows how to Include other PNP-style INFs.
So the simplest solution is to ship an installer exe/msi that invokes the INetCfg API. If you can do that, it's simply a matter of a couple calls to SetupCopyOemInf and the INetCfg boilerplate that you can find in the bindview sample.
But, if you have to support a hardware-first installation, you need to bring out the big guns. You'll need to write a Co-Installer and include it with your driver package. The Co-Installer's job is to call the INetCfg APIs when your driver package is installed, and deregister when the package is uninstalled.
Co-Installers are generally discouraged, and are not supported for Universal drivers. So you should avoid a Co-Installer unless you've got no choice. Unfortunately I cannot think of any other way to register an NDIS LWF when a PNP device driver is installed through Windows Update. (This doesn't mean there isn't a crafty way to do it; I don't know everything.)
Note that you'd need a Co-Installer anyway even if you were shipping 2 .sys files. The need to call INetCfg doesn't change just because you merged the driver binaries.
Limitations
You'll have a full-fledged NDIS LWF driver, as well as a full-fledged PNP device driver. The only (minor) thing that doesn't work is that you cannot call NdisRegisterDeviceEx in this driver. The reason is that when you call NdisRegisterDeviceEx from a LWF, NDIS will attempt to co-opt your driver's dispatch table. But in this PNP+LWF dual driver, the dispatch table is owned by WDF or by you. This limitation is no problem, since you can call WdfDeviceCreate, and this routine is easier to use and has more features than the NDIS one anyway.
With the above configuration, the driver service is owned by PNP. That means the lifetime of your .sys file is owned by PNP. You cannot manually "net start" a PNP driver service; the only way to get your .sys file loaded is to actually enumerate your hardware. That means you can't have your NDIS LWF running when the hardware is not present. Typically this is what you'd want anyway. If it's not, you can try messing with the ServiceName directive, but there's some weird caveats with that, and I don't fully understand it myself.
I made a device driver. But my system crashed at the moment when it was deleting the device object. I think the symbolic link was deleted and it crashed after it was trying to delete the device as I can't see the symbolic link in the Global.
How do I delete this device now. It also gives me a error popup(system cannot find the file specified Device\Mydriver) when I try to open the listed Mydriver under devices from Winobj.
I tried starting the driver's service again. I do get a handle back when opening the service. But it wont start now. giving the error value of Cannot find the file specified. I was working fine, i mean starting the driver before this crash.
I am a beginner with drivers and doing this to learn, please guide.
I have taken this from : Programming microsoft windows driver 2nd edition by woney
I hope this helps.
Removability of devices in a Plug and Play environment is the ultimate source of the early-unload problem
mentioned in the text. it’s your responsibility to avoid sending an IRP to a driver that might no longer be in memory
and to prevent the PnP manager from unloading a driver that’s still processing an IRP you’ve sent to that driver.
One aspect of how you fulfill that responsibility is shown in the text: take an extra reference to the file object
returned by IoGetDeviceObjectPointer around the call to IoCallDriver. In most drivers, you’ll probably need the
extra reference only when you’re sending an asynchronous IRP. In that case, the code that ordinarily
dereferences the file object is likely to be in some other part of your driver that runs asynchronously with the
call to IoCallDriver—say, in the completion routine you’re obliged to install for an asynchronous IRP. If you send
a synchronous IRP, you’re much more likely to code your driver in such a way that you don’t dereference the file
object until the IRP completes.
Use the interactive boot option and don't load the troublesome driver. Then you can experiment perhaps by adding diagnostic instrumentation to the driver or other debugging techniques to determine the underlying problem.
Regarding guidInstance in DIDEVICEINSTANCE
Microsoft says:
Unique identifier for the instance of the device. An application can save the instance globally unique identifier (GUID) into a configuration file and use it at a later time. Instance GUIDs are specific to a particular computer. An instance GUID obtained from one computer is unrelated to instance GUIDs on another.
So, if I connect my device to the computer and my program does enumeration and finds the guid, do I ever have to enumerate again? Even if the user plugs and unplugs the device. If another device of the same type is plugged in, does it still recognize that the second device is not the same as the first and therefore requires a different guid? Should I just renumerate all the interfaces all the time my program runs to find my device or is once enough for a given pc?
Thanks.
I'm actually trying to solve a similar problem. According to the MSDN here, it looks like InstanceGUID is supposed to always be the same on the same computer. I've verified that if I unplug my USB device and plug it into a different port, it does indeed keep the same Instance GUID. However, if a different user logs into the same PC, DirectInput shows the same device having a different InstanceGUID!! I can't find any acknowledgement from Microsoft that this is a known problem.
So, I can partially answer your question. If you have two identical devices, you will get different InstanceGUIDs, and identical ProductGUIDs. Those InstanceGUIDs will stay consistent if you unplug your devices and move them to different USB ports. HOWEVER you will get different InstanceGUIDs if a different user logs in. At least I can verify that this is an issue on Windows 7 64bit.
The InstanceGuid will always be a unique identifier for every device plugged in - but if you remove the installation information (e.g. uninstalling a usb device) you also lose that InstanceGuid. The device will get some new unpredictable Guid when plugged in again.
The ProductGuid will always be the same for one device, since it's stored in the devices USB HID chip. It may happen though that two devices of the exact same type have the same ProductGuid. If they do, you can only identify them by their InstanceGuid (which may become invalid in some cases, as written above...).