在linux内核gpio驱动中使用消息队列解耦原有的功能函数
时间: 2024-05-11 09:20:07 浏览: 167
在Linux内核GPIO驱动中使用消息队列可以解耦原有的功能函数,实现异步处理GPIO中断事件。下面是一个简单的示例代码:
```c
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#define MAX_QUEUE_LEN 32
struct gpio_event {
int gpio;
bool value;
struct list_head list;
};
static struct gpio_event *events;
static struct list_head event_list;
static DEFINE_MUTEX(event_lock);
static DECLARE_WAIT_QUEUE_HEAD(event_waitq);
static struct task_struct *event_task;
static struct task_struct *queue_task;
static int gpio_event_thread(void *data)
{
while (!kthread_should_stop()) {
wait_event_interruptible(event_waitq, !list_empty(&event_list));
mutex_lock(&event_lock);
while (!list_empty(&event_list)) {
struct gpio_event *event = list_first_entry(&event_list,
struct gpio_event,
list);
list_del(&event->list);
mutex_unlock(&event_lock);
// 处理 GPIO 中断事件
printk(KERN_INFO "GPIO %d value %d\n", event->gpio, event->value);
kfree(event);
mutex_lock(&event_lock);
}
mutex_unlock(&event_lock);
}
return 0;
}
static irqreturn_t gpio_event_handler(int irq, void *dev_id)
{
unsigned int gpio = (unsigned int)dev_id;
bool value = gpio_get_value(gpio);
struct gpio_event *event = kmalloc(sizeof(*event), GFP_KERNEL);
if (!event) {
printk(KERN_ERR "Failed to allocate gpio event\n");
return IRQ_HANDLED;
}
event->gpio = gpio;
event->value = value;
mutex_lock(&event_lock);
if (list_empty(&event_list)) {
wake_up_interruptible(&event_waitq);
}
if (list_is_singular(&event_list)) {
if (event_task) {
wake_up_process(event_task);
}
}
list_add_tail(&event->list, &event_list);
if (list_is_full(&event_list)) {
if (queue_task) {
wake_up_process(queue_task);
}
}
mutex_unlock(&event_lock);
return IRQ_HANDLED;
}
static int gpio_event_queue_thread(void *data)
{
while (!kthread_should_stop()) {
mutex_lock(&event_lock);
if (list_is_full(&event_list)) {
struct gpio_event *event = list_first_entry(&event_list,
struct gpio_event,
list);
list_del(&event->list);
// 处理 GPIO 中断事件
printk(KERN_INFO "Discarded GPIO %d value %d\n", event->gpio, event->value);
kfree(event);
}
mutex_unlock(&event_lock);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
return 0;
}
static int __init gpio_event_init(void)
{
int ret;
int i;
printk(KERN_INFO "GPIO event module loaded\n");
events = kmalloc_array(MAX_QUEUE_LEN, sizeof(*events), GFP_KERNEL);
if (!events) {
printk(KERN_ERR "Failed to allocate gpio event array\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&event_list);
event_task = kthread_run(gpio_event_thread, NULL, "gpio_event");
if (!event_task) {
printk(KERN_ERR "Failed to create gpio event thread\n");
ret = -ENOMEM;
goto err_free_events;
}
queue_task = kthread_run(gpio_event_queue_thread, NULL, "gpio_queue");
if (!queue_task) {
printk(KERN_ERR "Failed to create gpio queue thread\n");
ret = -ENOMEM;
goto err_stop_event_thread;
}
for (i = 0; i < MAX_QUEUE_LEN; i++) {
events[i].gpio = -1;
}
for (i = 0; i < NUM_GPIO_CHIPS; i++) {
struct gpio_chip *chip = &gpio_chip[i];
if (chip->base <= chip->ngpio) {
int j;
for (j = 0; j < chip->ngpio; j++) {
int gpio = chip->base + j;
if (gpio_is_valid(gpio)) {
ret = gpio_request_one(gpio, GPIOF_IN,
"gpio_event");
if (ret) {
printk(KERN_ERR "Failed to request gpio %d\n",
gpio);
goto err_free_gpio;
}
ret = request_irq(gpio_to_irq(gpio),
gpio_event_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"gpio_event",
(void *)(unsigned long)gpio);
if (ret) {
printk(KERN_ERR "Failed to request irq for gpio %d\n",
gpio);
gpio_free(gpio);
goto err_free_gpio;
}
}
}
}
}
return 0;
err_free_gpio:
for (i = 0; i < NUM_GPIO_CHIPS; i++) {
struct gpio_chip *chip = &gpio_chip[i];
if (chip->base <= chip->ngpio) {
int j;
for (j = 0; j < chip->ngpio; j++) {
int gpio = chip->base + j;
if (gpio_is_valid(gpio)) {
free_irq(gpio_to_irq(gpio), (void *)(unsigned long)gpio);
gpio_free(gpio);
}
}
}
}
kthread_stop(queue_task);
err_stop_event_thread:
kthread_stop(event_task);
err_free_events:
kfree(events);
return ret;
}
static void __exit gpio_event_exit(void)
{
int i;
printk(KERN_INFO "GPIO event module unloaded\n");
for (i = 0; i < NUM_GPIO_CHIPS; i++) {
struct gpio_chip *chip = &gpio_chip[i];
if (chip->base <= chip->ngpio) {
int j;
for (j = 0; j < chip->ngpio; j++) {
int gpio = chip->base + j;
if (gpio_is_valid(gpio)) {
free_irq(gpio_to_irq(gpio), (void *)(unsigned long)gpio);
gpio_free(gpio);
}
}
}
}
kthread_stop(queue_task);
kthread_stop(event_task);
kfree(events);
}
module_init(gpio_event_init);
module_exit(gpio_event_exit);
MODULE_LICENSE("GPL");
```
在上面的示例代码中,我们使用了一个事件队列 `event_list`,用于存储 GPIO 中断事件。当一个 GPIO 中断事件发生时,我们将事件信息保存到队列中,并唤醒等待事件的线程 `event_task`。如果队列已满,我们将丢弃最早的事件,并唤醒等待队列空闲的线程 `queue_task`。线程 `event_task` 会从队列中取出事件并处理,线程 `queue_task` 则负责丢弃队列中最早的事件。这样,我们就实现了 GPIO 中断事件的异步处理,并且可以通过消息队列解耦原有的功能函数。
阅读全文