Hello All,
Yay 1.8!
So in discussions with Alexandre, he expressed that he would really like there to be a Plug and Play system in wine to support the HID work I have been doing. Others at WineConf also expressed a desire for a plug and play system. So I have been doing research, reading and work to try to design out how this system will work. I promised him that while I would do research and testing I would wait until after the release before initiating this conversation.
We also discussed at WineConf that design processes like this should happen more out in the open so here is my proposed design, with my thinking, so that people who have interest and maybe more knowledge about parts of this than I can help improve the design. I have done some basic proof of concept coding on most of this so that I know it can work, but have flexibility.
Long discussion below.
The main thing I have learned is that this side of Plug and Play (The system side) is very complicated and pretty much completely undocumented. So this beginning design is going to be greatly simplified but hopefully in enough of a good framework state that it can be expanded as needed.
A few definitions, these are borrowed terms but may not be specifically defined exactly the same as on windows:
Bus driver : this is whatever lump of code that it talking to the underlying devices, this should be talking to platform native devices. For the purposes of our PnP the bus driver will be only really responsible for enumerating devices, detecting addition and removal of devices and being able to generate device IDs for these devices. This will all be wine code as far as I can determine.
Device Driver: This is the code that is actually trying to do something with the device enumerated but the bus driver. This too will likely be wine code. Think a hid minidriver or usb.sys. Generally any supported native drivers will have to sit above this particular device driver level. But that could maybe change, we will see
Plug and Play manager: This is the chunk of code that manages all the driver, devices and does the plug and play work. I have chosen to make the existing Programs/plugplay.exe be the plug and play manager.
No INF processing or install:
The most important part of this that is missing and that I am not addressing is anything about driver installation or inf files. Right now everything in this process is wine builtin code, so there is no need to have inf processing, and that is a seriously complicated part of this process. I have not fully figured out how we should manage the driver store and do the full driver on demand installation and such. But a basic framework can be put together without it.
The Plug and Play process, (As I see it for wine)
Ok, here is the meat of what is interesting. Starting at the bottom. Plug and Play bus drivers will be written for OS/X HID devices as well as udev hidraw devices. When I discussed this with Alexandre he did not feel like these need to be individual dlls, so they will be included in the plugplay.exe manager. However they will be designed in such a way that if a compelling reason why we need to pull them out becomes apparent it will not be difficult. These bus drivers will not be full device driver really, they just handle the Plug and play aspects of the bus, the device->DeviceExtension will be simply be a handle to the native device in question. The driver will only handle IRP_MJ_PNP ioctls. Having these be loaded into the plugplay.exe manager process space makes many things much easier.
When the bus driver detects a new device it will call PNP_IoInvalidateDeviceRelations, and internal function inside of plugplay.exe that mirrors IoInvalidateDeviceRelations. Because the bus drivers are living inside of the plugplay manager there is no need to implement and deal with the ntoskrnl.exe version of IoInvalidateDeviceRelations. Again this will be coded to be able to be pulled out if needed. If we decide to handle any bus drivers that live outside of the plugplay.exe process space then we will need to have this functionality, probably via RPC like the service manager.
Here is where the fun comes. Because we are not handling installing drivers (yet) we can just make use of the critical device store. These are drivers that, on windows, are automatically loaded on boot every time. This database is maintained in [CurrentControlSet/Control/CriticalDeviceDatabase] The keys in this registry key represent a HardwareID that will match a device. We query the IDs from the bus device’s BusQueryHardwareIDs and if we find a match (from most specific to most general) we look at that registry key. The CriticalDeviceDatabase entry will specify a [Service] value. All the first round builtin drivers will specify the [Service]. Further implementations when we start supporting inf files will also look at supporting the [ClassGUID] value where a driver inf can be specified.
That Service will specify a key in [CurrentControlSet\Services]. These services will need to be [type] of 0x1 (device driver) and [start] of 0x3 (on demand), finally they will have an [ImagePath] that points to the .sys file for this driver. The plug and play manager will determine if it has loaded that driver or not, and if not then it will load the driver into its process space and call the drivers DriverEntry. Part of this will be done via the advapi service APIs to query the service and then I was planing to reuse code from winedevice.exe for the actual loading of the driver.sys into the plugplay.exe process space.
With the loaded driver we then call the driver’s extension.AddDevice entry point with the new bus device. The drivers AddDevice function will be well behaved and call IoAttachDeviceToDeviceStack. The Plug and Play manager will look at the bus device after AddDevice and if there is an AttachedDevice then will proceed to call IRP_MN_START_DEVICE on the attached device. Then a IRP_MJ_POWER/IRP_MN_SET_POWER to PowerDeviceD0.
It is an important point that having the bus drivers and function drivers all loaded into the same process space means that they have a common process stack for sharing memory and handles and such. Driver will expect this so if we end up having to change this we will have to deal with the complexities of the memory management.
Device removal will be more simplistic. When the bus driver detects that a device has been removed then we again make a PNP_IoInvalidateDeviceRelations. Then the plug and play manager will send a IRP_MN_SURPRISE_REMOVAL followed by a IRP_MN_REMOVE_DEVICE ioctl to the AttachedDevice.
That will represent the entirety of the process, as I see it on the first pass, for plug and play. Some of the many things this does not do, it does not make use of or maintain a driver store (I think maintained as keys under the [CurrentControlSet/Control/Class/<GUID>] keys). It does not do any of the complicated driver INF, install on demand work and it does not allow for new drivers to be installed via any of the installation processes. But it does not prevent this from being expanded on for next passes.
Another big unanswered part of this is the integration with the Plug and Play configuration manager (cfgmgr32.dll) and all the CM_XXX functions.
I am only working on the HID bus and drivers, however I feel like we could expand this pretty easily into USB or even mountmgr bus and driver architectures as we move forward.
Comments? Thoughts? Anyone actually get to the end here?
-aric
On Sun, Dec 20, 2015 at 9:16 PM, Aric Stewart aric@codeweavers.com wrote:
Hello All,
Yay 1.8!
So in discussions with Alexandre, he expressed that he would really like there to be a Plug and Play system in wine to support the HID work I have been doing. Others at WineConf also expressed a desire for a plug and play system. So I have been doing research, reading and work to try to design out how this system will work. I promised him that while I would do research and testing I would wait until after the release before initiating this conversation.
We also discussed at WineConf that design processes like this should happen more out in the open so here is my proposed design, with my thinking, so that people who have interest and maybe more knowledge about parts of this than I can help improve the design. I have done some basic proof of concept coding on most of this so that I know it can work, but have flexibility.
Long discussion below.
The main thing I have learned is that this side of Plug and Play (The system side) is very complicated and pretty much completely undocumented. So this beginning design is going to be greatly simplified but hopefully in enough of a good framework state that it can be expanded as needed.
A few definitions, these are borrowed terms but may not be specifically defined exactly the same as on windows:
Bus driver : this is whatever lump of code that it talking to the underlying devices, this should be talking to platform native devices. For the purposes of our PnP the bus driver will be only really responsible for enumerating devices, detecting addition and removal of devices and being able to generate device IDs for these devices. This will all be wine code as far as I can determine.
Device Driver: This is the code that is actually trying to do something with the device enumerated but the bus driver. This too will likely be wine code. Think a hid minidriver or usb.sys. Generally any supported native drivers will have to sit above this particular device driver level. But that could maybe change, we will see
Plug and Play manager: This is the chunk of code that manages all the driver, devices and does the plug and play work. I have chosen to make the existing Programs/plugplay.exe be the plug and play manager.
No INF processing or install:
The most important part of this that is missing and that I am not addressing is anything about driver installation or inf files. Right now everything in this process is wine builtin code, so there is no need to have inf processing, and that is a seriously complicated part of this process. I have not fully figured out how we should manage the driver store and do the full driver on demand installation and such. But a basic framework can be put together without it.
The Plug and Play process, (As I see it for wine)
Ok, here is the meat of what is interesting. Starting at the bottom. Plug and Play bus drivers will be written for OS/X HID devices as well as udev hidraw devices. When I discussed this with Alexandre he did not feel like these need to be individual dlls, so they will be included in the plugplay.exe manager. However they will be designed in such a way that if a compelling reason why we need to pull them out becomes apparent it will not be difficult. These bus drivers will not be full device driver really, they just handle the Plug and play aspects of the bus, the device->DeviceExtension will be simply be a handle to the native device in question. The driver will only handle IRP_MJ_PNP ioctls. Having these be loaded into the plugplay.exe manager process space makes many things much easier.
When the bus driver detects a new device it will call PNP_IoInvalidateDeviceRelations, and internal function inside of plugplay.exe that mirrors IoInvalidateDeviceRelations. Because the bus drivers are living inside of the plugplay manager there is no need to implement and deal with the ntoskrnl.exe version of IoInvalidateDeviceRelations. Again this will be coded to be able to be pulled out if needed. If we decide to handle any bus drivers that live outside of the plugplay.exe process space then we will need to have this functionality, probably via RPC like the service manager.
Here is where the fun comes. Because we are not handling installing drivers (yet) we can just make use of the critical device store. These are drivers that, on windows, are automatically loaded on boot every time. This database is maintained in [CurrentControlSet/Control/CriticalDeviceDatabase] The keys in this registry key represent a HardwareID that will match a device. We query the IDs from the bus device’s BusQueryHardwareIDs and if we find a match (from most specific to most general) we look at that registry key. The CriticalDeviceDatabase entry will specify a [Service] value. All the first round builtin drivers will specify the [Service]. Further implementations when we start supporting inf files will also look at supporting the [ClassGUID] value where a driver inf can be specified.
That Service will specify a key in [CurrentControlSet\Services]. These services will need to be [type] of 0x1 (device driver) and [start] of 0x3 (on demand), finally they will have an [ImagePath] that points to the .sys file for this driver. The plug and play manager will determine if it has loaded that driver or not, and if not then it will load the driver into its process space and call the drivers DriverEntry. Part of this will be done via the advapi service APIs to query the service and then I was planing to reuse code from winedevice.exe for the actual loading of the driver.sys into the plugplay.exe process space.
I've already done that. Attached are 2 patches that make driver loading independent of global variables, and move driver loading code from winedevice.exe to ntoskrnl.exe where it can be reused to load drivers from anything that links to ntoskrnl.exe.
With the loaded driver we then call the driver’s extension.AddDevice entry point with the new bus device. The drivers AddDevice function will be well behaved and call IoAttachDeviceToDeviceStack. The Plug and Play manager will look at the bus device after AddDevice and if there is an AttachedDevice then will proceed to call IRP_MN_START_DEVICE on the attached device. Then a IRP_MJ_POWER/IRP_MN_SET_POWER to PowerDeviceD0.
It is an important point that having the bus drivers and function drivers all loaded into the same process space means that they have a common process stack for sharing memory and handles and such. Driver will expect this so if we end up having to change this we will have to deal with the complexities of the memory management.
My work on USB uses a similar idea.
In my design, usbhub.sys (my "bus driver") is loaded at Wine start-up. It is loaded by winedevice.exe, in its own separate winedevice.exe process just like every driver currently is, but it is able to (using my previously mentioned patches) load USB device drivers into its own process, and leading to a hybrid driver architecture where copy protection drivers should continue to work as they get loaded into their own winedevice processes and crashes won't affect other drivers, while usbhub.sys can load USB drivers into its own process as needed to provide hardware support.
Also I planned to detect device additions and removals by listing all USB devices every second, and detecting what changed since the previous listing. This is because there is no other portable way to read device addition/removal events: there used to be HAL but now we've degenerated to the Linux-only udev :-(. I was planning to hack the plug and play side of things ;-). A background thread in usbhub.sys would do this polling for USB devices and build device objects and load drivers.
We should probably develop the plug and play system before USB though?
Device removal will be more simplistic. When the bus driver detects that a device has been removed then we again make a PNP_IoInvalidateDeviceRelations. Then the plug and play manager will send a IRP_MN_SURPRISE_REMOVAL followed by a IRP_MN_REMOVE_DEVICE ioctl to the AttachedDevice.
That will represent the entirety of the process, as I see it on the first pass, for plug and play. Some of the many things this does not do, it does not make use of or maintain a driver store (I think maintained as keys under the [CurrentControlSet/Control/Class/<GUID>] keys). It does not do any of the complicated driver INF, install on demand work and it does not allow for new drivers to be installed via any of the installation processes. But it does not prevent this from being expanded on for next passes.
Another big unanswered part of this is the integration with the Plug and Play configuration manager (cfgmgr32.dll) and all the CM_XXX functions.
I am only working on the HID bus and drivers, however I feel like we could expand this pretty easily into USB or even mountmgr bus and driver architectures as we move forward.
Comments? Thoughts? Anyone actually get to the end here?
-aric
Damjan
Thanks for the reply! Sorry for my delay I am on vacation so e-mail and think about complicated things like this time is more scarce.
On 12/22/15 12:13 PM, Damjan Jovanovic wrote:
I've already done that. Attached are 2 patches that make driver loading independent of global variables, and move driver loading code from winedevice.exe to ntoskrnl.exe where it can be reused to load drivers from anything that links to ntoskrnl.exe.
That is pretty cool! Have you submitted these for review and comment? It would be nice to move the driver loading somewhere like ntoskrnl
My work on USB uses a similar idea.
In my design, usbhub.sys (my "bus driver") is loaded at Wine start-up. It is loaded by winedevice.exe, in its own separate winedevice.exe process just like every driver currently is, but it is able to (using my previously mentioned patches) load USB device drivers into its own process, and leading to a hybrid driver architecture where copy protection drivers should continue to work as they get loaded into their own winedevice processes and crashes won't affect other drivers, while usbhub.sys can load USB drivers into its own process as needed to provide hardware support.
That should plug into this structure pretty easily, that is good to know. the usbhub.sys will no longer be loaded by winedevice.exe but instead it will be integrated into plugplay.exe, still in driver style for later pull out if we want.
Also I planned to detect device additions and removals by listing all USB devices every second, and detecting what changed since the previous listing. This is because there is no other portable way to read device addition/removal events: there used to be HAL but now we've degenerated to the Linux-only udev :-(. I was planning to hack the plug and play side of things ;-). A background thread in usbhub.sys would do this polling for USB devices and build device objects and load drivers.
If you are using udev then you should be able to to listen for device removal and insertion without having to poll. In my proof of concept Plug and Play setup I have a udev setup that does this for hidraw devices and it quite straight forward.
We should probably develop the plug and play system before USB though?
I agree. So far I am looking at the lack of much comment as people not having any problem with this, and since it works with your concept and artecheture I will start cleaning it up and try to submit patches soon after the new year.
-aric