You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为自定义GPIO上升沿中断内核模块添加/dev/uio设备文件?

Adding a UIO Device to Your GPIO Interrupt Driver

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:

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=y or as a module).
  • The uio_info.irq_flags must match the flags used in request_irq.
  • Error handling in the init function is critical to avoid resource leaks if setup fails.

内容的提问来源于stack exchange,提问作者Khilav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:50:32