如何为自定义GPIO上升沿中断内核模块添加/dev/uio设备文件?
Got it, let's break down exactly what you need to do to create a UIO device for your existing GPIO interrupt driver. UIO is perfect here because it lets user-space applications handle interrupt events without needing to mess with kernel space beyond this setup.
Key Steps & Required Structures
The core structure you’ll need is struct uio_info — this tells the kernel how to manage your UIO device. Here’s the step-by-step implementation:
1. Add UIO-Related Header Files
First, include these headers to your existing code:
#include <linux/uio_driver.h> #include <linux/platform_device.h>
2. Define UIO Device Variables
Add these static variables outside your init/exit functions to hold the UIO device and its configuration:
static struct platform_device *uio_plat_dev; static struct uio_info uio_info = { .name = "gpio-switch-uio", // Name shown in /sys/class/uio/ .version = "1.0", .irq = IRQ_SHARED, // Match your interrupt sharing setting .irq_flags = IRQF_TRIGGER_RISING, // Same as your request_irq trigger };
3. Update the Interrupt Handler to Notify User-Space
Modify your interrupt handler to signal user-space when an interrupt occurs using uio_event_notify:
static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){ static unsigned int numberPresses = 0; printk(KERN_INFO "GPIO_TEST: Interrupt! (button state is %d)\n", gpio_get_value(gpio_switch)); printk(KERN_INFO "GPIO_TEST: The button was pressed %d times\n", ++numberPresses); // Notify user-space that an interrupt triggered uio_event_notify(&uio_info); return (irq_handler_t) IRQ_HANDLED; }
4. Register the UIO Device in Module Initialization
Update your ebbgpio_init function to set up and register the UIO device after your existing GPIO/IRQ setup:
static int __init ebbgpio_init(void){ int result = 0; printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n"); // Existing GPIO setup code gpio_request(gpio_switch, "sysfs"); gpio_direction_input(gpio_switch); gpio_set_debounce(gpio_switch, 100); printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpio_switch)); irq_number = gpio_to_irq(gpio_switch); printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d\n", irq_number); // Request IRQ (keep your existing line) result = request_irq(irq_number, (irq_handler_t) ebbgpio_irq_handler, IRQF_TRIGGER_RISING, "switch-event", NULL); if (result != 0) { printk(KERN_ERR "GPIO_TEST: Failed to request IRQ\n"); goto cleanup_gpio; } // Link UIO info to our IRQ uio_info.irq = irq_number; // Create platform device for UIO uio_plat_dev = platform_device_alloc("uio", -1); if (!uio_plat_dev) { result = -ENOMEM; printk(KERN_ERR "GPIO_TEST: Failed to alloc platform device\n"); goto cleanup_irq; } // Register platform device result = platform_device_add(uio_plat_dev); if (result != 0) { printk(KERN_ERR "GPIO_TEST: Failed to add platform device\n"); goto cleanup_plat_dev; } // Register UIO device with the kernel result = uio_register_device(&uio_plat_dev->dev, &uio_info); if (result != 0) { printk(KERN_ERR "GPIO_TEST: Failed to register UIO device\n"); goto cleanup_plat_dev_add; } printk(KERN_INFO "GPIO_TEST: UIO device registered successfully\n"); return result; // Cleanup paths for error handling cleanup_plat_dev_add: platform_device_del(uio_plat_dev); cleanup_plat_dev: platform_device_put(uio_plat_dev); cleanup_irq: free_irq(irq_number, NULL); cleanup_gpio: gpio_free(gpio_switch); return result; }
5. Clean Up UIO Resources in Module Exit
Update ebbgpio_exit to unregister the UIO device before cleaning up GPIO/IRQ resources:
static void __exit ebbgpio_exit(void){ // Unregister UIO device first uio_unregister_device(&uio_info); platform_device_del(uio_plat_dev); platform_device_put(uio_plat_dev); // Existing cleanup code free_irq(irq_number, NULL); gpio_free(gpio_switch); printk(KERN_INFO "GPIO_TEST: Cleanup complete\n"); }
User-Space Testing Example
Once the module is loaded, you’ll find a /dev/uioX device (check with ls /dev/uio*). Here’s a simple C program to read interrupt events:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <poll.h> int main() { int fd = open("/dev/uio0", O_RDWR); if (fd < 0) { perror("Failed to open UIO device"); return 1; } struct pollfd fds = { .fd = fd, .events = POLLIN, }; printf("Waiting for GPIO interrupts...\n"); while (1) { int ret = poll(&fds, 1, -1); if (ret < 0) { perror("Poll failed"); break; } if (fds.revents & POLLIN) { unsigned long count; read(fd, &count, sizeof(count)); printf("Interrupt received! Count: %lu\n", count); } } close(fd); return 0; }
Compile and run it with:
gcc uio_test.c -o uio_test ./uio_test
Important Notes
- Ensure your kernel has UIO support enabled (
CONFIG_UIO=yor as a module). - The
uio_info.irq_flagsmust match the flags used inrequest_irq. - Error handling in the init function is critical to avoid resource leaks if setup fails.
内容的提问来源于stack exchange,提问作者Khilav

