Userspace linux что это
The Userspace I/O HOWTOВ¶
About this documentВ¶
TranslationsВ¶
PrefaceВ¶
For many types of devices, creating a Linux kernel driver is overkill. All that is really needed is some way to handle an interrupt and provide access to the memory space of the device. The logic of controlling the device does not necessarily have to be within the kernel, as the device does not need to take advantage of any of other resources that the kernel provides. One such common class of devices that are like this are for industrial I/O cards.
To address this situation, the userspace I/O system (UIO) was designed. For typical industrial I/O cards, only a very small kernel module is needed. The main part of the driver will run in user space. This simplifies development and reduces the risk of serious bugs within a kernel module.
Please note that UIO is not an universal driver interface. Devices that are already handled well by other kernel subsystems (like networking or serial or USB) are no candidates for an UIO driver. Hardware that is ideally suited for an UIO driver fulfills all of the following:
AcknowledgmentsВ¶
I’d like to thank Thomas Gleixner and Benedikt Spranger of Linutronix, who have not only written most of the UIO code, but also helped greatly writing this HOWTO by giving me all kinds of background information.
FeedbackВ¶
About UIOВ¶
If you use UIO for your card’s driver, here’s what you get:
How UIO worksВ¶
/dev/uioX is used to access the address space of the card. Just use mmap() to access registers or RAM locations of your card.
For some hardware that has more than one interrupt source internally, but not separate IRQ mask and status registers, there might be situations where userspace cannot determine what the interrupt source was if the kernel handler disables them by writing to the chip’s IRQ register. In such a case, the kernel has to disable the IRQ completely to leave the chip’s register untouched. Now the userspace part can determine the cause of the interrupt, but it cannot re-enable interrupts. Another cornercase is chips where re-enabling interrupts is a read-modify-write operation to a combined IRQ status/acknowledge register. This would be racy if a new interrupt occurred simultaneously.
To handle interrupts properly, your custom kernel module can provide its own interrupt handler. It will automatically be called by the built-in handler.
For cards that don’t generate interrupts but need to be polled, there is the possibility to set up a timer that triggers the interrupt handler at configurable time intervals. This interrupt simulation is done by calling uio_event_notify() from the timer’s event handler.
Each driver provides attributes that are used to read or write variables. These attributes are accessible through sysfs files. A custom kernel driver module can add its own attributes to the device owned by the uio driver, but not added to the UIO device itself at this time. This might change in the future if it would be found to be useful.
The following standard attributes are provided by the UIO framework:
These attributes appear under the /sys/class/uio/uioX directory. Please note that this directory might be a symlink, and not a real directory. Any userspace code that accesses it must be able to handle this.
Each UIO device can make one or more memory regions available for memory mapping. This is necessary because some industrial I/O cards require access to more than one PCI memory region in a driver.
Each mapX/ directory contains four read-only files that show attributes of the memory:
From userspace, the different mappings are distinguished by adjusting the offset parameter of the mmap() call. To map the memory of mapping N, you have to use N times the page size as your offset:
Since these ioport regions can not be mapped, they will not appear under /sys/class/uio/uioX/maps/ like the normal memory described above. Without information about the port regions a hardware has to offer, it becomes difficult for the userspace part of the driver to find out which ports belong to which UIO device.
Each portX/ directory contains four read-only files that show name, start, size, and type of the port region:
Writing your own kernel moduleВ¶
Please have a look at uio_cif.c as an example. The following paragraphs explain the different sections of this file.
struct uio_infoВ¶
This structure tells the framework the details of your driver, Some of the members are required, others are optional.
Usually, your device will have one or more memory regions that can be mapped to user space. For each region, you have to set up a struct uio_mem in the mem[] array. Here’s a description of the fields of struct uio_mem :
Sometimes, your device can have one or more port regions which can not be mapped to userspace. But if there are other possibilities for userspace to access these ports, it makes sense to make information about the ports available in sysfs. For each region, you have to set up a struct uio_port in the port[] array. Here’s a description of the fields of struct uio_port :
Adding an interrupt handlerВ¶
What you need to do in your interrupt handler depends on your hardware and on how you want to handle it. You should try to keep the amount of code in your kernel interrupt handler low. If your hardware requires no action that you have to perform after each interrupt, then your handler can be empty.
If, on the other hand, your hardware needs some action to be performed after each interrupt, then you must do it in your kernel module. Note that you cannot rely on the userspace part of your driver. Your userspace program can terminate at any time, possibly leaving your hardware in a state where proper interrupt handling is still required.
There might also be applications where you want to read data from your hardware at each interrupt and buffer it in a piece of kernel memory you’ve allocated for that purpose. With this technique you could avoid loss of data if your userspace program misses an interrupt.
A note on shared interrupts: Your driver should support interrupt sharing whenever this is possible. It is possible if and only if your driver can detect whether your hardware has triggered the interrupt or not. This is usually done by looking at an interrupt status register. If your driver sees that the IRQ bit is actually set, it will perform its actions, and the handler returns IRQ_HANDLED. If the driver detects that it was not your hardware that caused the interrupt, it will do nothing and return IRQ_NONE, allowing the kernel to call the next possible interrupt handler.
If you decide not to support shared interrupts, your card won’t work in computers with no free interrupts. As this frequently happens on the PC platform, you can save yourself a lot of trouble by supporting interrupt sharing.
Using uio_pdrv for platform devicesВ¶
The advantage of this approach is that you only have to edit a file you need to edit anyway. You do not have to create an extra driver.
Using uio_pdrv_genirq for platform devicesВ¶
Especially in embedded devices, you frequently find chips where the irq pin is tied to its own dedicated interrupt line. In such cases, where you can be really sure the interrupt is not shared, we can take the concept of uio_pdrv one step further and use a generic interrupt handler. That’s what uio_pdrv_genirq does.
Using uio_pdrv_genirq not only saves a few lines of interrupt handler code. You also do not need to know anything about the chip’s internal registers to create the kernel part of the driver. All you need to know is the irq number of the pin the chip is connected to.
Using uio_dmem_genirq for platform devicesВ¶
In addition to statically allocated memory ranges, they may also be a desire to use dynamically allocated regions in a user space driver. In particular, being able to access memory made available through the dma-mapping API, may be particularly useful. The uio_dmem_genirq driver provides a way to accomplish this.
This driver is used in a similar manner to the «uio_pdrv_genirq» driver with respect to interrupt configuration and handling.
Writing a driver in userspaceВ¶
Once you have a working kernel module for your hardware, you can write the userspace part of your driver. You don’t need any special libraries, your driver can be written in any reasonable language, you can use floating point numbers and so on. In short, you can use all the tools and libraries you’d normally use for writing a userspace application.
Getting information about your UIO deviceВ¶
Information about all UIO devices is available in sysfs. The first thing you should do in your driver is check name and version to make sure your talking to the right device and that its kernel driver has the version you expect.
You should also make sure that the memory mapping you need exists and has the size you expect.
There is a tool called lsuio that lists UIO devices and their attributes. It is available here:
With lsuio you can quickly check if your kernel module is loaded and which attributes it exports. Have a look at the manpage for details.
The source code of lsuio can serve as an example for getting information about an UIO device. The file uio_helper.c contains a lot of functions you could use in your userspace driver code.
mmap() device memoryВ¶
After you made sure you’ve got the right device with the memory mappings you need, all you have to do is to call mmap() to map the device’s memory to userspace.
The parameter offset of the mmap() call has a special meaning for UIO devices: It is used to select which mapping of your device you want to map. To map the memory of mapping N, you have to use N times the page size as your offset:
Waiting for interruptsВ¶
After you successfully mapped your devices memory, you can access it like an ordinary array. Usually, you will perform some initialization. After that, your hardware starts working and will generate an interrupt as soon as it’s finished, has some data available, or needs your attention because an error occurred.
Generic PCI UIO driverВ¶
The generic driver is a kernel module named uio_pci_generic. It can work with any device compliant to PCI 2.3 (circa 2002) and any compliant PCI Express device. Using this, you only need to write the userspace driver, removing the need to write a hardware-specific kernel module.
Making the driver recognize the deviceВ¶
Since the driver does not declare any device ids, it will not get loaded automatically and will not automatically bind to any devices, you must load it and allocate id to the driver yourself. For example:
If there already is a hardware specific kernel driver for your device, the generic driver still won’t bind to it, in this case if you want to use the generic driver (why would you?) you’ll have to manually unbind the hardware specific driver and bind the generic driver, like this:
You can verify that the device has been bound to the driver by looking for it in sysfs, for example like the following:
Which if successful should print:
Note that the generic driver will not bind to old PCI 2.2 devices. If binding the device failed, run the following command:
and look in the output for failure reasons.
Things to know about uio_pci_genericВ¶
Interrupts are handled using the Interrupt Disable bit in the PCI command register and Interrupt Status bit in the PCI status register. All devices compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should support these bits. uio_pci_generic detects this support, and won’t bind to devices which do not support the Interrupt Disable Bit in the command register.
On each interrupt, uio_pci_generic sets the Interrupt Disable bit. This prevents the device from generating further interrupts until the bit is cleared. The userspace driver should clear this bit before blocking and waiting for more interrupts.
Writing userspace driver using uio_pci_genericВ¶
Userspace driver can use pci sysfs interface, or the libpci library that wraps it, to talk to the device and to re-enable interrupts by writing to the command register.
Example code using uio_pci_genericВ¶
Here is some sample userspace driver code using uio_pci_generic:
Generic Hyper-V UIO driverВ¶
The generic driver is a kernel module named uio_hv_generic. It supports devices on the Hyper-V VMBus similar to uio_pci_generic on PCI bus.
Making the driver recognize the deviceВ¶
Since the driver does not declare any device GUID’s, it will not get loaded automatically and will not automatically bind to any devices, you must load it and allocate id to the driver yourself. For example, to use the network device GUID:
If there already is a hardware specific kernel driver for the device, the generic driver still won’t bind to it, in this case if you want to use the generic driver (why would you?) you’ll have to manually unbind the hardware specific driver and bind the generic driver, like this:
You can verify that the device has been bound to the driver by looking for it in sysfs, for example like the following:
Which if successful should print:
Things to know about uio_hv_genericВ¶
On each interrupt, uio_hv_generic sets the Interrupt Disable bit. This prevents the device from generating further interrupts until the bit is cleared. The userspace driver should clear this bit before blocking and waiting for more interrupts.
SPI userspace APIВ¶
SPI devices have a limited userspace API, supporting basic half-duplex read() and write() access to SPI slave devices. Using ioctl() requests, full duplex transfers and device I/O configuration are also available.
Some reasons you might want to use this programming interface include:
Prototyping in an environment that’s not crash-prone; stray pointers in userspace won’t normally bring down any Linux system.
Developing simple protocols used to talk to microcontrollers acting as SPI slaves, which you may need to change quite often.
Of course there are drivers that can never be written in userspace, because they need to access kernel interfaces (such as IRQ handlers or other layers of the driver stack) that are not accessible to userspace.
DEVICE CREATION, DRIVER BINDINGВ¶
The simplest way to arrange to use this driver is to just list it in the spi_board_info for a device as the driver it should use: the “modalias” entry is “spidev”, matching the name of the driver exposing this API. Set up the other device characteristics (bits per word, SPI clocking, chipselect polarity, etc) as usual, so you won’t always need to override them later.
(Sysfs also supports userspace driven binding/unbinding of drivers to devices. That mechanism might be supported here in the future.)
When you do that, the sysfs node for the SPI device will include a child device node with a “dev” attribute that will be understood by udev or mdev. (Larger systems will have “udev”. Smaller ones may configure “mdev” into busybox; it’s less featureful, but often enough.) For a SPI device with chipselect C on bus B, you should see:
character special device, major number 153 with a dynamically chosen minor device number. This is the node that userspace programs will open, created by “udev” or “mdev”.
as usual, the SPI device node will be a child of its SPI master controller.
created when the “spidev” driver binds to that device. (Directory or symlink, based on whether or not you enabled the “deprecated sysfs files” Kconfig option.)
Do not try to manage the /dev character device special file nodes by hand. That’s error prone, and you’d need to pay careful attention to system security issues; udev/mdev should already be configured securely.
If you unbind the “spidev” driver from that device, those two “spidev” nodes (in sysfs and in /dev) should automatically be removed (respectively by the kernel and by udev/mdev). You can unbind by removing the “spidev” driver module, which will affect all devices using this driver. You can also unbind by having kernel code remove the SPI device, probably by removing the driver for its SPI controller (so its spi_master vanishes).
Since this is a standard Linux device driver – even though it just happens to expose a low level API to userspace – it can be associated with any number of devices at a time. Just provide one spi_board_info record for each such SPI device, and you’ll get a /dev device node for each device.
BASIC CHARACTER DEVICE APIВ¶
Normal open() and close() operations on /dev/spidevB.D files work as you would expect.
Standard read() and write() operations are obviously only half-duplex, and the chipselect is deactivated between those operations. Full-duplex access, and composite operation without chipselect de-activation, is available using the SPI_IOC_MESSAGE(N) request.
Several ioctl() requests let your driver read or override the device’s current settings for data transfer parameters:
pass a pointer to a byte which will return (RD) or assign (WR) the SPI transfer mode. Use the constants SPI_MODE_0..SPI_MODE_3; or if you prefer you can combine SPI_CPOL (clock polarity, idle high iff this is set) or SPI_CPHA (clock phase, sample on trailing edge iff this is set) flags. Note that this request is limited to SPI mode flags that fit in a single byte.
SPI_IOC_RD_MODE32, SPI_IOC_WR_MODE32 …
pass a pointer to a uin32_t which will return (RD) or assign (WR) the full SPI transfer mode, not limited to the bits that fit in one byte.
SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST …
pass a pointer to a byte which will return (RD) or assign (WR) the bit justification used to transfer SPI words. Zero indicates MSB-first; other values indicate the less common LSB-first encoding. In both cases the specified value is right-justified in each word, so that unused (TX) or undefined (RX) bits are in the MSBs.
SPI_IOC_RD_BITS_PER_WORD, SPI_IOC_WR_BITS_PER_WORD …
pass a pointer to a byte which will return (RD) or assign (WR) the number of bits in each SPI transfer word. The value zero signifies eight bits.
SPI_IOC_RD_MAX_SPEED_HZ, SPI_IOC_WR_MAX_SPEED_HZ …
pass a pointer to a u32 which will return (RD) or assign (WR) the maximum SPI transfer speed, in Hz. The controller can’t necessarily assign that specific clock speed.
At this time there is no async I/O support; everything is purely synchronous.
There’s currently no way to report the actual bit rate used to shift data to/from a given device.
From userspace, you can’t currently change the chip select polarity; that could corrupt transfers to other devices sharing the SPI bus. Each SPI device is deselected when it’s not in active use, allowing other drivers to talk to other devices.
There’s a limit on the number of bytes each I/O request can transfer to the SPI device. It defaults to one page, but that can be changed using a module parameter.
Because SPI has no low-level transfer acknowledgement, you usually won’t see any I/O errors when talking to a non-existent device.
FULL DUPLEX CHARACTER DEVICE APIВ¶
See the spidev_fdx.c sample program for one example showing the use of the full duplex programming interface. (Although it doesn’t perform a full duplex transfer.) The model is the same as that used in the kernel spi_sync() request; the individual transfers offer the same capabilities as are available to kernel drivers (except that it’s not asynchronous).
The example shows one half-duplex RPC-style request and response message. These requests commonly require that the chip not be deselected between the request and response. Several such requests could be chained into a single kernel request, even allowing the chip to be deselected after each response. (Other protocol options include changing the word size and bitrate for each transfer segment.)
To make a full duplex request, provide both rx_buf and tx_buf for the same transfer. It’s even OK if those are the same buffer.
© Copyright The kernel development community.
The Userspace I/O HOWTOВ¶
About this documentВ¶
TranslationsВ¶
PrefaceВ¶
For many types of devices, creating a Linux kernel driver is overkill. All that is really needed is some way to handle an interrupt and provide access to the memory space of the device. The logic of controlling the device does not necessarily have to be within the kernel, as the device does not need to take advantage of any of other resources that the kernel provides. One such common class of devices that are like this are for industrial I/O cards.
To address this situation, the userspace I/O system (UIO) was designed. For typical industrial I/O cards, only a very small kernel module is needed. The main part of the driver will run in user space. This simplifies development and reduces the risk of serious bugs within a kernel module.
Please note that UIO is not an universal driver interface. Devices that are already handled well by other kernel subsystems (like networking or serial or USB) are no candidates for an UIO driver. Hardware that is ideally suited for an UIO driver fulfills all of the following:
AcknowledgmentsВ¶
I’d like to thank Thomas Gleixner and Benedikt Spranger of Linutronix, who have not only written most of the UIO code, but also helped greatly writing this HOWTO by giving me all kinds of background information.
FeedbackВ¶
About UIOВ¶
If you use UIO for your card’s driver, here’s what you get:
How UIO worksВ¶
/dev/uioX is used to access the address space of the card. Just use mmap() to access registers or RAM locations of your card.
For some hardware that has more than one interrupt source internally, but not separate IRQ mask and status registers, there might be situations where userspace cannot determine what the interrupt source was if the kernel handler disables them by writing to the chip’s IRQ register. In such a case, the kernel has to disable the IRQ completely to leave the chip’s register untouched. Now the userspace part can determine the cause of the interrupt, but it cannot re-enable interrupts. Another cornercase is chips where re-enabling interrupts is a read-modify-write operation to a combined IRQ status/acknowledge register. This would be racy if a new interrupt occurred simultaneously.
To handle interrupts properly, your custom kernel module can provide its own interrupt handler. It will automatically be called by the built-in handler.
For cards that don’t generate interrupts but need to be polled, there is the possibility to set up a timer that triggers the interrupt handler at configurable time intervals. This interrupt simulation is done by calling uio_event_notify() from the timer’s event handler.
Each driver provides attributes that are used to read or write variables. These attributes are accessible through sysfs files. A custom kernel driver module can add its own attributes to the device owned by the uio driver, but not added to the UIO device itself at this time. This might change in the future if it would be found to be useful.
The following standard attributes are provided by the UIO framework:
These attributes appear under the /sys/class/uio/uioX directory. Please note that this directory might be a symlink, and not a real directory. Any userspace code that accesses it must be able to handle this.
Each UIO device can make one or more memory regions available for memory mapping. This is necessary because some industrial I/O cards require access to more than one PCI memory region in a driver.
Each mapX/ directory contains four read-only files that show attributes of the memory:
From userspace, the different mappings are distinguished by adjusting the offset parameter of the mmap() call. To map the memory of mapping N, you have to use N times the page size as your offset:
Since these ioport regions can not be mapped, they will not appear under /sys/class/uio/uioX/maps/ like the normal memory described above. Without information about the port regions a hardware has to offer, it becomes difficult for the userspace part of the driver to find out which ports belong to which UIO device.
Each portX/ directory contains four read-only files that show name, start, size, and type of the port region:
Writing your own kernel moduleВ¶
Please have a look at uio_cif.c as an example. The following paragraphs explain the different sections of this file.
struct uio_infoВ¶
This structure tells the framework the details of your driver, Some of the members are required, others are optional.
Usually, your device will have one or more memory regions that can be mapped to user space. For each region, you have to set up a struct uio_mem in the mem[] array. Here’s a description of the fields of struct uio_mem :
Sometimes, your device can have one or more port regions which can not be mapped to userspace. But if there are other possibilities for userspace to access these ports, it makes sense to make information about the ports available in sysfs. For each region, you have to set up a struct uio_port in the port[] array. Here’s a description of the fields of struct uio_port :
Adding an interrupt handlerВ¶
What you need to do in your interrupt handler depends on your hardware and on how you want to handle it. You should try to keep the amount of code in your kernel interrupt handler low. If your hardware requires no action that you have to perform after each interrupt, then your handler can be empty.
If, on the other hand, your hardware needs some action to be performed after each interrupt, then you must do it in your kernel module. Note that you cannot rely on the userspace part of your driver. Your userspace program can terminate at any time, possibly leaving your hardware in a state where proper interrupt handling is still required.
There might also be applications where you want to read data from your hardware at each interrupt and buffer it in a piece of kernel memory you’ve allocated for that purpose. With this technique you could avoid loss of data if your userspace program misses an interrupt.
A note on shared interrupts: Your driver should support interrupt sharing whenever this is possible. It is possible if and only if your driver can detect whether your hardware has triggered the interrupt or not. This is usually done by looking at an interrupt status register. If your driver sees that the IRQ bit is actually set, it will perform its actions, and the handler returns IRQ_HANDLED. If the driver detects that it was not your hardware that caused the interrupt, it will do nothing and return IRQ_NONE, allowing the kernel to call the next possible interrupt handler.
If you decide not to support shared interrupts, your card won’t work in computers with no free interrupts. As this frequently happens on the PC platform, you can save yourself a lot of trouble by supporting interrupt sharing.
Using uio_pdrv for platform devicesВ¶
The advantage of this approach is that you only have to edit a file you need to edit anyway. You do not have to create an extra driver.
Using uio_pdrv_genirq for platform devicesВ¶
Especially in embedded devices, you frequently find chips where the irq pin is tied to its own dedicated interrupt line. In such cases, where you can be really sure the interrupt is not shared, we can take the concept of uio_pdrv one step further and use a generic interrupt handler. That’s what uio_pdrv_genirq does.
Using uio_pdrv_genirq not only saves a few lines of interrupt handler code. You also do not need to know anything about the chip’s internal registers to create the kernel part of the driver. All you need to know is the irq number of the pin the chip is connected to.
Using uio_dmem_genirq for platform devicesВ¶
In addition to statically allocated memory ranges, they may also be a desire to use dynamically allocated regions in a user space driver. In particular, being able to access memory made available through the dma-mapping API, may be particularly useful. The uio_dmem_genirq driver provides a way to accomplish this.
This driver is used in a similar manner to the «uio_pdrv_genirq» driver with respect to interrupt configuration and handling.
Writing a driver in userspaceВ¶
Once you have a working kernel module for your hardware, you can write the userspace part of your driver. You don’t need any special libraries, your driver can be written in any reasonable language, you can use floating point numbers and so on. In short, you can use all the tools and libraries you’d normally use for writing a userspace application.
Getting information about your UIO deviceВ¶
Information about all UIO devices is available in sysfs. The first thing you should do in your driver is check name and version to make sure your talking to the right device and that its kernel driver has the version you expect.
You should also make sure that the memory mapping you need exists and has the size you expect.
There is a tool called lsuio that lists UIO devices and their attributes. It is available here:
With lsuio you can quickly check if your kernel module is loaded and which attributes it exports. Have a look at the manpage for details.
The source code of lsuio can serve as an example for getting information about an UIO device. The file uio_helper.c contains a lot of functions you could use in your userspace driver code.
mmap() device memoryВ¶
After you made sure you’ve got the right device with the memory mappings you need, all you have to do is to call mmap() to map the device’s memory to userspace.
The parameter offset of the mmap() call has a special meaning for UIO devices: It is used to select which mapping of your device you want to map. To map the memory of mapping N, you have to use N times the page size as your offset:
Waiting for interruptsВ¶
After you successfully mapped your devices memory, you can access it like an ordinary array. Usually, you will perform some initialization. After that, your hardware starts working and will generate an interrupt as soon as it’s finished, has some data available, or needs your attention because an error occurred.
Generic PCI UIO driverВ¶
The generic driver is a kernel module named uio_pci_generic. It can work with any device compliant to PCI 2.3 (circa 2002) and any compliant PCI Express device. Using this, you only need to write the userspace driver, removing the need to write a hardware-specific kernel module.
Making the driver recognize the deviceВ¶
Since the driver does not declare any device ids, it will not get loaded automatically and will not automatically bind to any devices, you must load it and allocate id to the driver yourself. For example:
If there already is a hardware specific kernel driver for your device, the generic driver still won’t bind to it, in this case if you want to use the generic driver (why would you?) you’ll have to manually unbind the hardware specific driver and bind the generic driver, like this:
You can verify that the device has been bound to the driver by looking for it in sysfs, for example like the following:
Which if successful should print:
Note that the generic driver will not bind to old PCI 2.2 devices. If binding the device failed, run the following command:
and look in the output for failure reasons.
Things to know about uio_pci_genericВ¶
Interrupts are handled using the Interrupt Disable bit in the PCI command register and Interrupt Status bit in the PCI status register. All devices compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should support these bits. uio_pci_generic detects this support, and won’t bind to devices which do not support the Interrupt Disable Bit in the command register.
On each interrupt, uio_pci_generic sets the Interrupt Disable bit. This prevents the device from generating further interrupts until the bit is cleared. The userspace driver should clear this bit before blocking and waiting for more interrupts.
Writing userspace driver using uio_pci_genericВ¶
Userspace driver can use pci sysfs interface, or the libpci library that wraps it, to talk to the device and to re-enable interrupts by writing to the command register.
Example code using uio_pci_genericВ¶
Here is some sample userspace driver code using uio_pci_generic:
Generic Hyper-V UIO driverВ¶
The generic driver is a kernel module named uio_hv_generic. It supports devices on the Hyper-V VMBus similar to uio_pci_generic on PCI bus.
Making the driver recognize the deviceВ¶
Since the driver does not declare any device GUID’s, it will not get loaded automatically and will not automatically bind to any devices, you must load it and allocate id to the driver yourself. For example, to use the network device GUID:
If there already is a hardware specific kernel driver for the device, the generic driver still won’t bind to it, in this case if you want to use the generic driver (why would you?) you’ll have to manually unbind the hardware specific driver and bind the generic driver, like this:
You can verify that the device has been bound to the driver by looking for it in sysfs, for example like the following:
Which if successful should print:
Things to know about uio_hv_genericВ¶
On each interrupt, uio_hv_generic sets the Interrupt Disable bit. This prevents the device from generating further interrupts until the bit is cleared. The userspace driver should clear this bit before blocking and waiting for more interrupts.