Heng30的博客
搜索 分类 关于 订阅

如何在Linux驱动中断函数中使用worker_struct?

2025-03-25

struct work_struct是Linux内核中用于实现延迟工作(deferred work)机制的重要数据结构,属于工作队列(workqueue)子系统的一部分。

主要特点

  • 延迟执行:允许将任务推迟到内核线程上下文中执行

  • 进程上下文:工作在进程上下文中运行,可以睡眠

  • 异步处理:常用于中断下半部(bottom half)处理

设备树

// test.dst

/dts-v1/;

/ {
    #address-cells = <1>;
    #size-cells = <1>;

    // 模拟数据
    gpioe:gpio@50006000 {
        #gpio-cells = <0x2>;
        reg =  <0x50006000 0x4 0x50006014 0x4 0x50000a28 0x4>;
    };

    // 模拟数据
    gpiof:gpio@50008000 {
        gpio-controller;
        #gpio-cells = <0x2>;
        interrupt-controller;
        #interrupt-cells = <0x2>;

        reg =  <0x50008000 0x4 0x50008014 0x4 0x50008a28 0x4>;
        gpio-ranges = <0x55 0x0 0x50 0x10>;
        status = "okay";
    };

    // 该节点会被转换为`struct platform_device`设备对象
    led {
        model = "This is LED";
        dev-type = "LED";

        // 参数:gpio对象引用,第几个引脚,是否使能
        led-gpios = <&gpioe 10 0>,<&gpiof 10 0>,<&gpioe 8 0>;

        // 与驱动匹配的关键设置
        compatible = "my_device_001";

        // 表示启用设备节点
        status = "okay";
    };

    key {
        model = "This is key";
        dev-type = "KEY";

        interrupt-parent = <&gpiof>;

        interrupts = <9 0>,<7 0>,<8 0>;

        key-gpios = <&gpiof 9 0>,<&gpiof 7 0>,<&gpiof 8 0>;

        // 与驱动匹配的关键设置
        compatible = "my_device_002";

        // 表示启用设备节点
        status = "okay";
    };
};

驱动代码

// simple.c

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>

#define PLATFORM_DEVICE_BASENAME "my_device"
#define PLATFORM_DEVICE_NAME_1 "my_device_001"
#define PLATFORM_DEVICE_NAME_2 "my_device_002"

static u32 led1_gpios;
static u32 key1_gpios;
static u32 key1_interrupts;
static int current_irq;
static struct work_struct work;

static irqreturn_t _key_ISR(int irq, void *dev) {
    pr_info("call key_ISR irq = %d\n", irq);

    current_irq = irq;

    // 中断上文执行完后,会执行中断下文,即`_work`函数
    schedule_work(&work);
    return IRQ_HANDLED;
}

static void _work(struct work_struct *work) {
    pr_info("call timeout\n");

    msleep(100); // 阻塞延时100毫秒, 会放弃cpu的使用权

    int irq = current_irq;

    if (irq == key1_interrupts) {
        if (gpio_get_value(key1_gpios) == 0) {
            pr_info("key1 pressed\n");
        } else {
            pr_info("key1 released\n");
        }
    }
}

static int _probe(struct platform_device *device) {
    pr_info("key_driver_probe\n");

    // 初始化gpio资源, 第二个参数要与test.dts中的资源名称要一致
    led1_gpios = of_get_named_gpio(device->dev.of_node, "led-gpios", 0);
    gpio_request(led1_gpios, "led1-gpios");
    gpio_direction_output(led1_gpios, 0);

    struct device_node *key_node = of_find_node_by_path("/key");
    if (!key_node) {
        pr_err("simple of_find_node_by_path('\\key') failed\n");
        return -1;
    }

    // 初始化gpio资源, 第二个参数要与test.dts中的资源名称要一致
    key1_gpios = of_get_named_gpio(key_node, "key-gpios", 0);
    gpio_request(key1_gpios, "key1-gpios");
    gpio_direction_input(key1_gpios);

    key1_interrupts = of_irq_get(key_node, 0);

    // 会创建`/proc/irq/key1-interrupts`目录
    int ret = request_irq(key1_interrupts, _key_ISR,
                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                          "key1-interrupts", NULL);
    if (ret) {
        pr_err("request_irq for key1 failed\n");
        return -1;
    }

    INIT_WORK(&work, _work);
    return 0;
}

// 当平台设备驱动卸载时,会自动调用该函数
static int _remove(struct platform_device *device) {
    pr_info("key_driver_remove\n");

    free_irq(key1_interrupts, NULL);
    gpio_free(key1_gpios);
    gpio_free(led1_gpios);

    cancel_work_sync(&work);
    return 0;
}

// 会与insmod安装的platform_device进行匹配
struct platform_device_id id_table_match[] = {
    // 名称要在20个字节以内
    [0] = {PLATFORM_DEVICE_NAME_1, 1},
    [1] = {PLATFORM_DEVICE_NAME_2, 2},
    [2] = {}, // 空元素代表结束
};

// 会与设备树安装的platform_device进行匹配
struct of_device_id of_node_match_table[] = {
    [0] = {.compatible = PLATFORM_DEVICE_NAME_1},
    [1] = {.compatible = PLATFORM_DEVICE_NAME_2},
    [2] = {}, // 空元素代表结束
};

struct platform_driver key_driver = {
    .probe = _probe,
    .remove = _remove,

    // 通过id_table匹配, 实现一个驱动对多个设备
    .driver =
        {
            .name = PLATFORM_DEVICE_BASENAME,

            // 设备树的匹配方式
            .of_match_table = of_node_match_table,
        },
    .id_table = id_table_match,
};

static int __init key_driver_init(void) {
    pr_info("key_driver_init\n");

    int ret = platform_driver_register(&key_driver);
    if (ret < 0) {
        pr_err("key_driver_register failed\n");
        return -1;
    }
    return 0;
}

static void __exit key_driver_exit(void) {
    pr_info("key_driver_exit\n");
    platform_driver_unregister(&key_driver);
}

module_init(key_driver_init);
module_exit(key_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("heng30");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple key_driver kernel module");

编译脚本


#!/bin/sh

top-dir = $(shell pwd)
kernel-version = $(shell uname -r)
kernel-dir ?= /lib/modules/$(kernel-version)/build

obj-m += simple.o

all: dt driver

driver:
        make -C $(kernel-dir) modules M=$(top-dir)

dt:
        dtc -@ -I dts -O dtb -o test.dtbo test.dts

clean:
        rm -f *.o *.ko *.mod *.mod.c *.order *.symvers *.dtbo
        make -C $(kernel-dir) clean m=$(top-dir)