From a general standpoint, I am trying to figure out how to access a platform device from userspace. To be more specific, I have a EMIF controller on and SoC of which I have added to my device tree and I believe it is correctly bound to a pre-written EMIF platform device driver. Now I am trying to figure out how I can access this EMIF device from a userspace application. I have come accross a couple different topics that seem to have some connection to this issue but I cannot quite find out how they relate.
1) As I read it seems like most I/O is done through the use of device nodes which are created by mknod(), do I need to create a device node in order to access this device?
2) I have read a couple threads that talk about writting a Kernel module (Character?, Block?) that can interface with both userspace and the platform device driver, and use it as an intermediary.
3) I have read about the possibility of using mmap() to map the memory of my platform device into my virtual memory space. Is this possible?
4) It seems that when the EMIF driver is instantiated, it calls the probe() fucntion. What functions would a userpace application call in the driver?
It's not completely clear what you're needing to do (and I should caveat that I have no experience with EMIF or with "platform devices" specifically), but here's some overview to help you get started:
Yes, the usual way of providing access to a device is via a device node. Usually this access is provided by a character device driver unless there's some more specific way of providing it. Most of the time if an application is talking "directly" to your driver, it's a character device. Most other types of devices are used in interfacing with other kernel subsystems: for example, a block device is typically used to provide access from a file system driver (say) to an underlying disk drive; a network driver provides access to the network from the in-kernel TCP/IP stack, etc.
There are several char device methods or entry points that can be supported by your driver, but the most common are "read" (i.e. if a user-space program opens your device and does a read(2) from it), "write" (analogous for write(2)) and "ioctl" (often used for configuration/administrative tasks that don't fall naturally into either a read or write). Note that mknod(2) only creates the user-space side of the device. There needs to be a corresponding device driver in the kernel (the "major device number" given in the mknod call links the user-space node with the driver).
For actually creating the device node in the file system, this can be automated (i.e. the node will automatically show up in /dev) if you call the right kernel functions while setting up your device. There's a special daemon that gets notifications from the kernel and responds by executing the mknod(2) system call.
A kernel module is merely a dynamically loadable way of creating a driver or other kernel extension. It can create a character, block or network device (et al.), but then so can a statically linked module. There are some differences in capability mostly because not all kernel functions you might want to use are "exported" to (i.e. visible to) dynamically loaded modules.
It's possible to support mapping of the device memory into user virtual memory space. This would be implemented by yet another driver entry point (mmap). See struct file_operations for all the entry points a char driver can support.
This is pretty much up to you: it depends on what the application needs to be able to do. There are many drivers in the kernel that provide no direct function to user-space, only to other kernel code. As to "probe", there are many probe functions defined in various interfaces. In most cases, these are called by the kernel (or perhaps by a 'higher level "class" driver') to allow the specific driver to discover, identify and "claim" individual devices. They (probe functions) don't usually have anything directly to do with providing access from user-space but I might well be missing something in a particular interface.
You need to create a device node in order to access the device.
The probe function is called when the driver finds a matching device.
For information on platform device API, the following articles could be useful.
The platform device API
Platform devices and device trees
Related
I have tried extending IOUserNetworkEthernet and calling RegisterEthernetInterface(). This works perfectly for one ethernet interface, though the driver crashes when RegisterEthernetInterface is called a second time (doesn't return an error code). I have tried registering with separate queues.
Another approach was extending IOUserClient instead, and calling IOService::Create to create child IOUserNetworkEthernet instances. Everything about this approach works (the children appear within ioreg). However, once I call RegisterEthernetInterface on just one of the children, macOS crashes.
How would I go about creating a dext with multiple ethernet interfaces? Have I been approaching it the right way?
Appreciate any help.
I haven't yet implemented an ethernet dext myself, but based on my experience with using DriverKit for other types of drivers and knowing its design goals, I have an idea what the solution might be.
First off, let's clarify: you're implementing either a virtual ethernet device, or you're building a driver for hardware that unites multiple independent ports in one physical device. In the former case (I'm guessing this is what you're doing based on your IOUserClient comment), your driver will be matching IOUserResources and you create the ethernet driver instance when you receive the corresponding message from your user space component.
Now, DriverKit design: DriverKit is built around a goal of keeping the driver instance for each device in its own separate process. Sort of circling back to the microkernel idea. Specifically, this means that multiple devices that use the same driver will each create an independent instance of that driver in its own process. This very likely means that NetworkingDriverKit was never designed to support more than one instance of IOUserNetworkEthernet, because they are seen as separate devices. Hence the crashes.
OK, what do we do about it? Use DriverKit the way it was intended. Put each virtual ethernet adapter in its own driver instance. This gets a bit tricky. I think this should work:
Your dext has a "control" instance. This is the thing that matches IOUserResources. It's a simple IOService class which listens for the signal (presumably IOUserClient) to create or destroy a virtual ethernet device. When creating a virtual ethernet device, you need that to run in its own driver instance though. (For an actual device with multiple ports, this would match the USB/PCI device nub, manage bus communication with the device, and enumerate the ports.)
Instead of creating an instance of a IOUserNetworkEthernet subclass, create an instance of a "nub" class and attach it to your central control class instance. Call RegisterService() in its startup code, so it's considered for IOKit matching.
Set up a second IOKit matching dictionary in your Info.plist, which matches your new "nub" object. Here, use your IOUserNetworkEthernet-derived class to "drive" the nub. Once the virtual ethernet device is ready to use, call RegisterEthernetInterface().
2 extra things to note:
The virtual device will run in a separate process from the control object, so they can only communicate via DriverKit inter-process calls, i.e. user clients. Hopefully, they don't really need to communicate much though, and the control client can pass all the information required via properties on the nub. If you're implementing support for multi-port hardware, you probably won't be able to avoid this part though.
Your user space component (app, daemon) will need to open a new communication channel with the virtual device, so you'll probably need to implement user client support there too.
I'm not sure how you'd go about shutting down an individual virtual ethernet device, you could try calling Terminate() on either it or the nub it's matched on and see what happens.
I understand the basics behind a UIO driver, as described in the documentation. The part I'm missing is how to write data from the user space program back to the device driver. My guess is that you'd write this data to the mmap region, but then how to do you let the device driver know it should read said memory?
Ultimately I'm trying to write a block device that can be implemented by a user space program. I've got the block device code stubbed out and working: https://github.com/wspeirs/usbd. My thought was that UIO was the most efficient way to transfer blocks/sectors between the block device and the user space program. Is this the wrong way to go about communicating with the block device driver from user space? Should I be using sysfs or some other communication mechanism?
UIO is designed so that user space bypasses the kernel to communicate with a hardware device. That does not seem to fit your needs.
In a standard Linux block device, you can use mmap() to write data to your block and msync() to indicate to the driver which regions you have written.
I am studying windows device drivers on my own and I find it's very difficult to differentiate between PDO and FDO. Let me tell you the flow that I have in my head correct me if I am wrong !!
when system boots it loads the root bus driver that will create a FDO. Now it will enumerates its child devices and i guess some hot plug method of the bus driver will be called, when a new child will be found and that method will notify the PNP manager. PNP manager will call the AddDevice() routine of the root bus driver and that will intern create PDOs for new buses like one for PCI and etc. Please explain the whole flow in detail, this is just my imagination. And then it is documented that system will load the functional driver for the PCI bus that will create the FDO ??
what is this FDO?? and why would i need that?? According to me PCI bus driver should also follow the same as done by the root bus, enumerate its child and create PDOs for them, or by this FDO they mean PDO only ?? I am so much confused :( !!
What are you actually doing, or are you just trying to learn? I am just wondering how you ended up this low down in the stack.
PDO = Physical Device Object
FDO = Functional Device Object
A PDO acts as a physical device, but it does not necessarily have to be physical. It is essentially the interface between a device on a bus, and the bus itself. This is pretty well covered on MSDN.
Here is an example that uses USB sticks and this illustrates the difference quite well.
Here is a more in depth explanation and the important quote
If your point of reference is the PCI bus, then Pci.sys is the function driver. But if your point of reference is the Proseware Gizmo device, then Pci.sys is the bus driver. This dual role is typical in the PnP device tree. A driver that serves as function driver for a bus also serves as bus driver for a child device of the bus.
You also have filter drivers which allow you to sit between PDO's and FDO's and start doing naughty stuff like hiding files, POC rootkits etc. At this stage you can add extra functionality, or completely prevent access to the PDO.
And here is all the MSDN links.
http://msdn.microsoft.com/en-us/library/windows/hardware/hh439632(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff554721(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/hh439643(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff554731(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff564859(v=vs.85).aspx
http://technet.microsoft.com/en-us/library/cc776371(v=ws.10).aspx
If that doesn't clear it up for you, feel free to post back.
Here is the excerpt form "Programming the Microsoft Windows Driver Model", 2nd edition, Walter One:
- PDO stands for physical device object. The bus driver uses this
object to represent the connection between the device and the bus.
- FDO stands for function device object. The function driver uses
this object to manage the functionality of the device.
- FiDO stands
for filter device object. A filter driver uses this object as a place
to store the information it needs to keep about the hardware and its
filtering activities. (The early beta releases of the Windows 2000
DDK used the term FiDO, and I adopted it then. The DDK no longer
uses this term because, I guess, it was considered too frivolous.)
Hope this helps you out.
According to me PCI bus driver should also follow the same as done by the root bus, enumerate its child and create PDOs for them --WRONG!.
If you are talking about WDM, PnP Manager creates PDOs. Earlier to that, YOU had to create it(after detection of device) in DriverEntry().
I am new to Linux and I need to write a USB driver for a device with 2 interfaces. One interface is HID class (3/0/0) with one interrupt in endpoint and a report descriptor. The other interface is vendor defined with 3 bulk endpoints. In my usb_device_id table I have a USB_DEVICE entry with the VID and PID.
When I plug in the device, my xxx_probe function is called for the vendor defined interface but not for the HID interface. Instead, it appears that a built-in driver called 'generic-usb' is taking control of the HID interface.
Is there a way to ensure that my driver probe function is called first?
Why doesn't Linux make multiple passes looking for a more specific driver first (like Windows does)?
Alternatively, can the 'generic-usb' driver be used to receive data on the interrupt endpoint and to set reports and features on the control pipe?
It appears that libusb-1.0.8 allows an application to take control of interfaces on an attached device without the need of a custom driver. So far it appears to provide all the support that I need.
If a USB device driver is loaded, and at some time the device plugs in, then which part of the kernel will create struct device and register it?
When the driver is loaded, the system calls the function, which you assigned in module_init. You will want to call there usb_register(struct usb_driver skel_driver), where skel_driver is a struct with pointers to methods servicing the device, like the probe method.
The probe method is the one which is called, when a new usb device is introduced in to the system. In this place you can fill your struct (usb_skel) with what you will need in the future, initiate a char device or whatever you do, when the device is introduced.
The system mostly won't create anything by itself, it has to have most of the structs prepared and filled with device specific data.
Please see usb-skeleton in lxr for reference and learn to use it, besides read writing usb drivers from LDD.
Cheers