Linux设备驱动之字符设备\interrupt\wait_queue实现

本文介绍了一个简单的字符设备驱动程序,该程序集成了中断处理和等待队列功能,并演示了如何通过cat命令读取设备文件来获取中断触发的信息。此外,还提供了一个用于测试的示例应用程序。

这篇文章是前几篇文章的总结,同时实现了中断,等待队列,都放在了字符设备的read中。
可以使用cat /dev/char_interrupt 类似于getevent
代码就不添加注释了,比较简单,下篇文章会在此基础上添加poll机制。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/fs.h>

#include <linux/interrupt.h>
#include <linux/irq.h>

#include <asm/io.h>
#include <asm/irq.h>

#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/sched.h>


#define IMX_GPIO_NR(bank, nr)               (((bank) - 1) * 32 + (nr))
#define CYNO_GPIO_BEEP_NUM                  IMX_GPIO_NR(6,10)

int char_interrupt_major = 0;
dev_t char_devno;

struct char_interrupt_dev{
    struct cdev cdev;
};

static struct pin_desc{
    int irq;
    unsigned char *name;
    unsigned int pin;
};

static struct pin_desc beep_desc = {
    0,
    "beep_num",
    CYNO_GPIO_BEEP_NUM
};

struct char_interrupt_dev *char_interrupt_devp;
struct class *char_interrupt_class;
DECLARE_WAIT_QUEUE_HEAD(wq);  
static int condition = 0; 



static irqreturn_t beep_interrupt_handler(int irq, void *dev_id)
{
    //printk("%s\n", __func__);
    condition = 1;
    wake_up_interruptible(&wq);
    return IRQ_HANDLED;
}


static int char_interrupt_open (struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "%s\n", __func__);
    return 0;
}

static ssize_t char_interrupt_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    char *data = "button falling\n\n\n";
    condition = 0;
    //printk(KERN_INFO "%s\n", __func__);
    wait_event_interruptible(wq,condition);
    //printk(KERN_INFO "%s : %s\n", __func__, data);
    copy_to_user(buf,data,strlen(data));
    return strlen(data);
}

static ssize_t char_interrupt_write (struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    char data[30];
    //printk(KERN_INFO "%s\n", __func__);
    memset(data, 0, 30);
    copy_from_user(data, buf, count);
    printk(KERN_INFO "%s : data is %s\n", __func__, data);
    return strlen(data);
}

static struct file_operations char_interrupt_fops = {
    .owner = THIS_MODULE,
    .open  = char_interrupt_open,
    .read  = char_interrupt_read,
    .write = char_interrupt_write,

};

static int char_interrupt_init(void)
{
    int err, ret = -ENODEV;
    struct device *dev_temp;


    printk(KERN_INFO "%s\n", __func__);


    ret = alloc_chrdev_region(&char_devno, 0, 1, "char_interrupt");
    char_interrupt_major = MAJOR(char_devno);

    if(ret){
        printk(KERN_ERR "%s : chrdev_region fail\n", __func__);
        goto chrdev_region_fail;
    }

    char_interrupt_devp = kmalloc(sizeof(struct char_interrupt_dev), GFP_KERNEL);
    if(char_interrupt_devp == NULL){
        printk(KERN_ERR "%s : kmalloc is fail\n", __func__);
        goto kmalloc_fail;
    }

    memset(char_interrupt_devp, 0, sizeof(struct char_interrupt_dev));

    cdev_init(&char_interrupt_devp->cdev, &char_interrupt_fops);
    char_interrupt_devp->cdev.owner = THIS_MODULE;
    char_interrupt_devp->cdev.ops = &char_interrupt_fops;
    err = cdev_add(&char_interrupt_devp->cdev, char_devno, 1);
    if(err){
        printk(KERN_ERR "%s : cdev_add fail\n", __func__);
        goto cdev_add_fail;
    }

    //start create class point
    char_interrupt_class = class_create(THIS_MODULE, "char_interrupt");
    if(IS_ERR(char_interrupt_class)){
        printk(KERN_ERR "%s : class_create fail\n", __func__);
        goto class_create_fail;
    }

    dev_temp = device_create(char_interrupt_class, NULL, char_devno, NULL, "char_interrupt");
    if(IS_ERR(dev_temp)){
        printk(KERN_ERR "%s : device_create fail\n", __func__);
        goto device_create_fail;
    }

    //interrupt init
    if(gpio_request(beep_desc.pin ,beep_desc.name)){
        printk(KERN_ERR "%s : request gpio %d error\n", __func__, beep_desc.pin);
        goto err_gpio_request;
    }
    gpio_direction_input(beep_desc.pin);
    beep_desc.irq = gpio_to_irq(beep_desc.pin);
    printk(KERN_INFO "%s : the irq num is %d\n", __func__, beep_desc.irq);
    ret = request_threaded_irq(beep_desc.irq, NULL, beep_interrupt_handler , IRQF_ONESHOT | IRQF_TRIGGER_FALLING, beep_desc.name , &beep_desc);
    if(ret){
        printk(KERN_ERR "%s : request_irq is error\n", __func__);
        goto err_request_irq;
    }

    printk(KERN_INFO "%s : init end\n", __func__);

    return 0;

err_request_irq:
    free_irq(beep_desc.irq, &beep_desc);

err_gpio_request:
    gpio_free(beep_desc.pin);

device_create_fail:
    class_destroy(char_interrupt_class);

class_create_fail:
    cdev_del(&char_interrupt_devp->cdev);

cdev_add_fail:
    kfree(char_interrupt_devp);

kmalloc_fail:

chrdev_region_fail:
    unregister_chrdev_region(char_devno,1);
    return -1;

}

static void char_interrupt_exit(void)
{
    printk(KERN_INFO "%s\n", __func__);
    free_irq(beep_desc.irq, &beep_desc);
    gpio_free(beep_desc.pin);
    device_destroy(char_interrupt_class, char_devno);
    class_destroy(char_interrupt_class);
    cdev_del(&char_interrupt_devp->cdev);
    kfree(char_interrupt_devp);
    unregister_chrdev_region(char_devno,1);
}

module_init(char_interrupt_init);
module_exit(char_interrupt_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("linux char driver base");
MODULE_AUTHOR("xiaolei");

测试:

cat /dev/char_interrutp
这里写图片描述

使用应用程序: char_interrupt_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main(void)
{
    int fd, ret;
    char data[20];

    fd = open("/dev/char_interrupt", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }


    write(fd, "xiaolei_write", strlen("xiaolei_write"));

    ret = read(fd, data, 1);
    printf("ret = %d\n", ret);
    printf("read data is %s", data);

    close(fd);
    return 0;
}
以下是一个使用 epoll 和 SPI 通信的字符设备驱动代码示例: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/spi/spi.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/slab.h> #define DRIVER_NAME "my_spi_driver" #define DEVICE_NAME "my_spi_device" #define MAX_BUF_LEN 256 static struct spi_device *spi_dev; static struct cdev my_cdev; static dev_t my_dev; static struct class *my_class; static struct device *my_device; static char spi_buf[MAX_BUF_LEN]; static int buf_len; static int irq_gpio = -1; static volatile int irq_flag = 0; static struct fasync_struct *async_queue; static DECLARE_WAIT_QUEUE_HEAD(irq_wait_queue); static irqreturn_t my_spi_irq_handler(int irq, void *dev_id) { irq_flag = 1; wake_up_interruptible(&irq_wait_queue); kill_fasync(&async_queue, SIGIO, POLL_IN); return IRQ_HANDLED; } static int my_spi_open(struct inode *inode, struct file *filp) { int ret = 0; printk(KERN_INFO "my_spi: opening device\n"); ret = spi_sync(spi_dev, NULL, 0); if (ret < 0) { printk(KERN_ERR "my_spi: SPI sync failed\n"); return ret; } return 0; } static int my_spi_release(struct inode *inode, struct file *filp) { printk(KERN_INFO "my_spi: closing device\n"); return 0; } static ssize_t my_spi_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; if (count > buf_len) count = buf_len; ret = copy_to_user(buf, spi_buf, count); if (ret < 0) { printk(KERN_ERR "my_spi: copy_to_user failed\n"); return ret; } return count; } static ssize_t my_spi_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; if (count > MAX_BUF_LEN) count = MAX_BUF_LEN; ret = copy_from_user(spi_buf, buf, count); if (ret < 0) { printk(KERN_ERR "my_spi: copy_from_user failed\n"); return ret; } buf_len = count; return count; } static unsigned int my_spi_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; poll_wait(filp, &irq_wait_queue, wait); if (irq_flag != 0) mask |= POLLIN | POLLRDNORM; return mask; } static int my_spi_fasync(int fd, struct file *filp, int mode) { return fasync_helper(fd, filp, mode, &async_queue); } static struct file_operations my_spi_fops = { .owner = THIS_MODULE, .open = my_spi_open, .release = my_spi_release, .read = my_spi_read, .write = my_spi_write, .poll = my_spi_poll, .fasync = my_spi_fasync, }; static int my_spi_probe(struct spi_device *spi) { int ret = 0; printk(KERN_INFO "my_spi: probing device\n"); spi_dev = spi; ret = gpio_request(spi->irq, "my_spi_irq"); if (ret < 0) { printk(KERN_ERR "my_spi: request irq gpio failed\n"); return ret; } irq_gpio = spi->irq; ret = gpio_direction_input(irq_gpio); if (ret < 0) { printk(KERN_ERR "my_spi: set irq gpio direction failed\n"); goto fail; } ret = request_irq(gpio_to_irq(irq_gpio), my_spi_irq_handler, IRQF_TRIGGER_RISING, "my_spi_irq_handler", NULL); if (ret < 0) { printk(KERN_ERR "my_spi: request irq failed\n"); goto fail; } ret = alloc_chrdev_region(&my_dev, 0, 1, DRIVER_NAME); if (ret < 0) { printk(KERN_ERR "my_spi: alloc_chrdev_region failed\n"); goto fail; } cdev_init(&my_cdev, &my_spi_fops); my_cdev.owner = THIS_MODULE; ret = cdev_add(&my_cdev, my_dev, 1); if (ret < 0) { printk(KERN_ERR "my_spi: cdev_add failed\n"); goto fail; } my_class = class_create(THIS_MODULE, DRIVER_NAME); if (IS_ERR(my_class)) { printk(KERN_ERR "my_spi: class_create failed\n"); ret = PTR_ERR(my_class); goto fail; } my_device = device_create(my_class, NULL, my_dev, NULL, DEVICE_NAME); if (IS_ERR(my_device)) { printk(KERN_ERR "my_spi: device_create failed\n"); ret = PTR_ERR(my_device); goto fail; } return 0; fail: if (irq_gpio != -1) gpio_free(spi->irq); return ret; } static int my_spi_remove(struct spi_device *spi) { printk(KERN_INFO "my_spi: removing device\n"); device_destroy(my_class, my_dev); class_destroy(my_class); cdev_del(&my_cdev); unregister_chrdev_region(my_dev, 1); free_irq(gpio_to_irq(irq_gpio), NULL); gpio_free(irq_gpio); return 0; } static struct spi_driver my_spi_driver = { .driver = { .name = "my_spi_driver", .owner = THIS_MODULE, }, .probe = my_spi_probe, .remove = my_spi_remove, }; static int __init my_spi_init(void) { int ret = 0; printk(KERN_INFO "my_spi: initializing driver\n"); ret = spi_register_driver(&my_spi_driver); if (ret < 0) { printk(KERN_ERR "my_spi: spi_register_driver failed\n"); return ret; } return 0; } static void __exit my_spi_exit(void) { printk(KERN_INFO "my_spi: exiting driver\n"); spi_unregister_driver(&my_spi_driver); } module_init(my_spi_init); module_exit(my_spi_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("My SPI Driver"); ``` 该驱动程序使用 SPI 通信与外部设备通信,并使用字符设备接口提供用户空间访问。它使用 epoll 和 fasync 机制来实现非阻塞 I/O 和异步通知。它还使用了一个 GPIO 中断实现异步通知。该驱动程序在初始化时注册了一个 SPI 设备驱动程序,当 SPI 总线上有新设备被探测到时,驱动程序的 probe 函数将被调用。在 probe 函数中,驱动程序会为该设备分配一个字符设备号,并创建一个字符设备文件,以便用户空间程序可以打开和读写该设备。当 SPI 设备从系统中移除时,驱动程序的 remove 函数将被调用,该函数将执行清理操作,以确保系统在设备被移除时不会出现问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值