一、等待队列(阻塞IO)

        临时存放任务的队练,Linux内核中可以通过等待队列来实现阻塞进程的唤醒。

        等待队列有如下函数接口

init_waitqueue_head
wait_event
wait_event_interruptible
wait_event_timeout
wait_event_interruptuble_timeout

        可以通过如下两个函数唤醒

wake_up
wake_up_interruptible

        接口原型

init_waitqueue_head(q);
wait_event(wq, condition);
wait_event_interruptible(wq, condition);
wait_event_timeout(wq, condition, timeout);
wait_event_interruptible_timeout(wq, condition, timeout);
wake_up(x);
wake_up_interruptible(x);

二、中断

        1.基本概念


        Linux有中断子系统,分为中断顶半部和中断底半部

        上半部:也称为中断顶半部,用于完成尽量少的比较紧急的功能,通常被设置为不可被新中断打断。
        下半部:也称为中断底半部,用来完成中断事件绝大多数任务,可以被新的中断打断,底半部相对来说并不是非常紧急,而且相对比较耗时。
        上半部的使用场景:
                1)要处理的内容不希望被其他中断打断
                2)要处理的任务对时间敏感
                3)要处理的任务与硬件有关
        下半部的使用场景:
                1)完成并不是非常紧急的耗时的任务
                2)可以被打断

        中断下半部有4中方式实现:
                1. 软中断
                        软中断是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候
                        软中断和tasklet运行于中断上下文,不允许睡眠
                2. tasklettasklet是利用软中断来实现的
                        tasklet不允许睡眠
                3. 工作队列
                        工作队列运行于进程上下文,允许睡眠
                4. 线程化irq
                        申请中断号时,内核会为相应的中断号分配一个对应的内核线程,中断handler结束后,返回值IRQ_WAKE_THREAD,内核调度对应线程执行thread_fun对应的函数

        可以通过request_irq来注册中断顶半部处理函数
        可以通过tasklet、workqueue、线程化的irq实现中断底半部
        中断顶半部完成中断对硬件必须完成的操作,实时性要求比较高的操作
        中断底半部完成比较费时的操作


        2. request_irq


        request_irq的中断编号可以通过gpio_to_irq由gpio编号获得中断编号
        中断编号也可以通过设备树中配置得到,驱动可以通过of_irq_get来获得中断编号

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


compatible = "pute-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_putekey>;
gpio-key = <&gpio1 18 GPIO_ACTIVE_LOW>;

interrupt-parent = <&gpio1>;

interrupts = <18 0>;


status = "okay";
};

 cat /proc/interrupts        查看当前系统中所有注册的中断号信息


        3. 中断涉及的函数接口

        request_irq       注册中断处理程序,绑定中断号与处理函数
        free_irq        释放已注册的中断资源
        enable_irq        使能中断
        disable_irq        禁止中断(立即返回不等待中断处理结束)

        原型

static inline int __must_check request_irq(unsigned int irq, irq_handler_t 
handler, unsigned long flags, const char *name, void *dev);
void free_irq(unsigned int irq, void *dev_id);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);

4. 中断资源竞争问题


        操作资源之前关中断,操作资源后开中断
        使用自旋锁对共享资源进行互斥操作保护


5. 中断底半部


        tasklet实现中断底半部:
                tasklet基于软中断
                是工作在中断上下文,中断底半部执行时仍处于中断发生过程中,不允许睡眠

接口:

DECLARE_TASKLET                定义TASKLET

tasklet_schedule                        使系统在适当的时候进行调度tasklet

原型:

DECLARE_TASKLET(name, func, data);
static inline void tasklet_schedule(struct tasklet_struct *t);


        workqueue实现中断底半部:
                workqueue基于内核线程
                是工作在进程上下文,中断底半部执行时中断已经结束了,所以进程中可以睡眠

接口:

INIT_WORK 初始化工作队列
schedule_work 使系统在适当的时候进行调度队列

原型:

INIT_WORK(_work, _func);
static inline bool schedule_work(struct work_struct *work);

三、linux四种IO模型

        IO模型包括:阻塞IO、非阻塞IO、异步IO(信号驱动IO)、多路复用IO
        1. 阻塞IO:事件没有到来时阻塞等待直到事件发生,继续向下执行,等待时间过程中,不需要占用CPU资源
        2. 非阻塞IO:事件没有发生继续向下执行,事件发生读取事件,继续向下执行
        3. 异步IO:当事件发生时,内核会向应用层发送通知,应用层捕捉到信号执行处理函数
        4. 多路复用IO:阻塞监听事件集合,事件发生时不再阻塞等待,判断发生的事件,继续向下执行

异步IO接口:

原型

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct 
**fapp);
void kill_fasync(struct fasync_struct **fp, int sig, int band);

band参数表示事件类型:

事件值 含义 应用场景
POLL_IN 设备有数据可读 串口接收缓冲区非空、网络数据包到达
POLL_OUT 设备可写入数据 发送缓冲区空闲、可继续发送数据
POLL_ERR 设备发生错误 硬件故障、通信超时
POLL_PRI 高优先级数据到达 TCP带外数据(如紧急指针数据)
POLL_HUP 设备断开连接 串口线拔出、Socket连接关闭

多路复用IO接口

原型:

static inline void poll_wait(struct file * filp, wait_queue_head_t * 
wait_address, poll_table *p);

四、代码

        1.非阻塞IO

key_drv.c

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>

static struct device_node *pkeynode = NULL;
static int gpiokey = 0;

static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
    int status = 0;
    unsigned long nret = 0;

    if (gpio_get_value(gpiokey)) {
        status = 0;
    } else {
        status = 1;
    }

    nret = copy_to_user(puser, &status, sizeof(status));
    if (nret) {
        pr_info("copy_to_user failed\n");
        return -1;
    }

    return nret;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = key_read,
};

static struct miscdevice misc_key = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_key",
    .fops = &fops,
};

static __init int key_drv_init(void)
{
    int ret = 0;
    
    ret = misc_register(&misc_key);
    if (ret) {
        pr_info("misc_register failed\n");
        return -1;
    }

    pkeynode = of_find_node_by_path("/putekey");
    if (NULL == pkeynode) {
        pr_info("of_find_node_by_path failed\n");
        return -1;
    }

    gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
    if (gpiokey < 0) {
        pr_info("of_get_named_gpio failed\n");
        return -1;
    }

    ret = gpio_request(gpiokey, "pute-key-gpio");
    if (ret) {
        pr_info("gpio_request failed\n");
        return -1;
    }

    ret = gpio_direction_input(gpiokey);
    if (ret) {
        pr_info("gpio_direction_input failed\n");
        return -1;
    }

    pr_info("key_drv_init success\n");

    return 0;
}

static __exit void key_drv_exit(void)
{
    int ret = 0;
    
    gpio_free(gpiokey);
    ret = misc_deregister(&misc_key);
    if (ret) {
        pr_info("misc_deregister failed\n");
        return;
    }

    pr_info("key_drv_exit success\n");

    return;
}

module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");

key_app.c

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

int main(void)
{
    int fd = 0;
    int stat = 0;

    fd = open("/dev/misc_key", O_RDWR);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    while (1)
    {
        read(fd, &stat, sizeof(stat));
        printf("stat = %d\n", stat);
    }

    close(fd);

    return 0;
}

        2.阻塞IO

key_drv.c

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>

static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;

static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
    unsigned long nret = 0;

    condition = 0;
    status = 0;
    wait_event_interruptible(wq, condition);

    nret = copy_to_user(puser, &status, sizeof(status));
    if (nret) {
        pr_info("copy_to_user failed\n");
        return -1;
    }

    return nret;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = key_read,
};

static struct miscdevice misc_key = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_key",
    .fops = &fops,
};

static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
    condition = 1;
    status = 1;
    wake_up_interruptible(&wq);

    return IRQ_HANDLED;
}

static __init int key_drv_init(void)
{
    int ret = 0;
    
    init_waitqueue_head(&wq);

    ret = misc_register(&misc_key);
    if (ret) {
        pr_info("misc_register failed\n");
        return -1;
    }

    pkeynode = of_find_node_by_path("/putekey");
    if (NULL == pkeynode) {
        pr_info("of_find_node_by_path failed\n");
        return -1;
    }

    gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
    if (gpiokey < 0) {
        pr_info("of_get_named_gpio failed\n");
        return -1;
    }

    ret = gpio_request(gpiokey, "pute-key-gpio");
    if (ret) {
        pr_info("gpio_request failed\n");
        return -1;
    }

    ret = gpio_direction_input(gpiokey);
    if (ret) {
        pr_info("gpio_direction_input failed\n");
        return -1;
    }
#if 0
    irqno = gpio_to_irq(gpiokey);
    if (irqno < 0) {
        pr_info("gpio_to_irq failed\n");
        return -1;
    }
#endif

    irqno = of_irq_get(pkeynode, 0);
    if (irqno < 0) {
        pr_info("of_irq_get failed\n");
        return -1;
    }

    ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
    if (ret) {
        pr_info("request_irq failed\n");
        return -1;
    }

    pr_info("key_drv_init success\n");

    return 0;
}

static __exit void key_drv_exit(void)
{
    int ret = 0;
    
    free_irq(irqno, NULL);
    gpio_free(gpiokey);
    ret = misc_deregister(&misc_key);
    if (ret) {
        pr_info("misc_deregister failed\n");
        return;
    }

    pr_info("key_drv_exit success\n");

    return;
}

module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");

key_app.c

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

int main(void)
{
    int fd = 0;
    int stat = 0;

    fd = open("/dev/misc_key", O_RDWR);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    while (1)
    {
        read(fd, &stat, sizeof(stat));
        printf("stat = %d\n", stat);
    }

    close(fd);

    return 0;
}

        3.异步IO

key_drv.c

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>

static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
struct fasync_struct *fapp = NULL;

static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
    unsigned long nret = 0;

    condition = 0;
    status = 0;
    wait_event_interruptible(wq, condition);

    nret = copy_to_user(puser, &status, sizeof(status));
    if (nret) {
        pr_info("copy_to_user failed\n");
        return -1;
    }

    return sizeof(status);
}

static int key_fasync(int fd, struct file *fp, int on)
{
    return fasync_helper(fd, fp, on, &fapp);
}


static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = key_read,
    .fasync = key_fasync,
};

static struct miscdevice misc_key = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_key",
    .fops = &fops,
};

static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
    if (fapp != NULL) {
        kill_fasync(&fapp, SIGIO, POLLIN);
    }

    status = 1;
    condition = 1;
    wake_up_interruptible(&wq);

    return IRQ_HANDLED;
}

static __init int key_drv_init(void)
{
    int ret = 0;
    
    init_waitqueue_head(&wq);

    ret = misc_register(&misc_key);
    if (ret) {
        pr_info("misc_register failed\n");
        return -1;
    }

    pkeynode = of_find_node_by_path("/putekey");
    if (NULL == pkeynode) {
        pr_info("of_find_node_by_path failed\n");
        return -1;
    }

    gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
    if (gpiokey < 0) {
        pr_info("of_get_named_gpio failed\n");
        return -1;
    }

    ret = gpio_request(gpiokey, "pute-key-gpio");
    if (ret) {
        pr_info("gpio_request failed\n");
        return -1;
    }

    ret = gpio_direction_input(gpiokey);
    if (ret) {
        pr_info("gpio_direction_input failed\n");
        return -1;
    }
#if 0
    irqno = gpio_to_irq(gpiokey);
    if (irqno < 0) {
        pr_info("gpio_to_irq failed\n");
        return -1;
    }
#endif

    irqno = of_irq_get(pkeynode, 0);
    if (irqno < 0) {
        pr_info("of_irq_get failed\n");
        return -1;
    }

    ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
    if (ret) {
        pr_info("request_irq failed\n");
        return -1;
    }

    pr_info("key_drv_init success\n");

    return 0;
}

static __exit void key_drv_exit(void)
{
    int ret = 0;
    
    free_irq(irqno, NULL);
    gpio_free(gpiokey);
    ret = misc_deregister(&misc_key);
    if (ret) {
        pr_info("misc_deregister failed\n");
        return;
    }

    pr_info("key_drv_exit success\n");

    return;
}

module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");

key_app.c

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

int fd = 0;

void handler(int signo)
{
    int stat = 0;

    read(fd, &stat, sizeof(stat));
    printf("stat = %d\n", stat);

    return;
}

int main(void)
{
    int flags = 0;

    signal(SIGIO, handler);

    fd = open("/dev/misc_key", O_RDWR);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    flags = fcntl(fd, F_GETFL);         //获得文件描述符属性
    flags |= O_ASYNC;                   //在文件描述符属性基础上加入异步IO属性
    fcntl(fd, F_SETFL, flags);          //重新设置文件描述符属性
    fcntl(fd, F_SETOWN, getpid());      //产生时间发送给当前进程任务



    while (1)
    {

    }

    close(fd);

    return 0;
}

        4.多路复用IO

key_drv.c

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
#include <asm-generic/poll.h>
#include <linux/poll.h>

static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
struct fasync_struct *fapp = NULL;

static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
    unsigned long nret = 0;

    condition = 0;
    status = 0;
    wait_event_interruptible(wq, condition);

    nret = copy_to_user(puser, &status, sizeof(status));
    if (nret) {
        pr_info("copy_to_user failed\n");
        return -1;
    }

    return sizeof(status);
}

static int key_fasync(int fd, struct file *fp, int on)
{
    return fasync_helper(fd, fp, on, &fapp);
}

static unsigned int key_poll(struct file *fp, struct poll_table_struct *ptable)
{
    unsigned int umask = 0;

    poll_wait(fp, &wq, ptable);

    if (0 == gpio_get_value(gpiokey)) {
        umask |= POLLIN;
    }

    return umask;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = key_read,
    .fasync = key_fasync,
    .poll = key_poll,
};

static struct miscdevice misc_key = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_key",
    .fops = &fops,
};

static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
    if (fapp != NULL) {
        kill_fasync(&fapp, SIGIO, POLLIN);
    }

    status = 1;
    condition = 1;
    wake_up_interruptible(&wq);

    return IRQ_HANDLED;
}

static __init int key_drv_init(void)
{
    int ret = 0;
    
    init_waitqueue_head(&wq);

    ret = misc_register(&misc_key);
    if (ret) {
        pr_info("misc_register failed\n");
        return -1;
    }

    pkeynode = of_find_node_by_path("/putekey");
    if (NULL == pkeynode) {
        pr_info("of_find_node_by_path failed\n");
        return -1;
    }

    gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
    if (gpiokey < 0) {
        pr_info("of_get_named_gpio failed\n");
        return -1;
    }

    ret = gpio_request(gpiokey, "pute-key-gpio");
    if (ret) {
        pr_info("gpio_request failed\n");
        return -1;
    }

    ret = gpio_direction_input(gpiokey);
    if (ret) {
        pr_info("gpio_direction_input failed\n");
        return -1;
    }
#if 0
    irqno = gpio_to_irq(gpiokey);
    if (irqno < 0) {
        pr_info("gpio_to_irq failed\n");
        return -1;
    }
#endif

    irqno = of_irq_get(pkeynode, 0);
    if (irqno < 0) {
        pr_info("of_irq_get failed\n");
        return -1;
    }

    ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
    if (ret) {
        pr_info("request_irq failed\n");
        return -1;
    }

    pr_info("key_drv_init success\n");

    return 0;
}

static __exit void key_drv_exit(void)
{
    int ret = 0;
    
    free_irq(irqno, NULL);
    gpio_free(gpiokey);
    ret = misc_deregister(&misc_key);
    if (ret) {
        pr_info("misc_deregister failed\n");
        return;
    }

    pr_info("key_drv_exit success\n");

    return;
}

module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");

key_app.c

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

int fd = 0;

void handler(int signo)
{
    int stat = 0;

    read(fd, &stat, sizeof(stat));
    printf("stat = %d\n", stat);

    return;
}

int main(void)
{
    int flags = 0;
    int nready = 0;
    int stat = 0;
    fd_set rdfds;
    fd_set tmpfds;

    signal(SIGIO, handler);

    fd = open("/dev/misc_key", O_RDWR);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

#if 0
    flags = fcntl(fd, F_GETFL);         //获得文件描述符属性
    flags |= O_ASYNC;                   //在文件描述符属性基础上加入异步IO属性
    fcntl(fd, F_SETFL, flags);          //重新设置文件描述符属性
    fcntl(fd, F_SETOWN, getpid());      //产生时间发送给当前进程任务
#endif

    FD_ZERO(&rdfds);
    FD_SET(fd, &rdfds);

    while (1)
    {
        tmpfds = rdfds;
        nready = select(fd+1, &tmpfds, NULL, NULL, NULL);
        if (-1 == nready) 
        {
            perror("fail to select");
            return -1;
        }

        if (FD_ISSET(fd, &tmpfds))
        {
            read(fd, &stat, sizeof(stat));
            printf("stat = %d\n", stat);
        }
    }

    close(fd);

    return 0;
}

        5.底半部

key_drv.c

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
#include <asm-generic/poll.h>
#include <linux/poll.h>
#include <linux/workqueue.h>
#include <linux/timer.h>

extern void msleep(unsigned int msecs);
static void tasklet_fun(unsigned long arg);

static struct device_node *pkeynode = NULL;
static int gpiokey = 0;
static int irqno = 0;
static wait_queue_head_t wq;
static int condition = 0;
static int status = 0;
struct fasync_struct *fapp = NULL;
static DECLARE_TASKLET(m_tasklet, tasklet_fun, 0);
static DEFINE_TIMER(m_timer, timeout_func, 0, 0);
static struct work_struct work_queue;


static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
    unsigned long nret = 0;

    condition = 0;
    status = 0;
    wait_event_interruptible(wq, condition);

    nret = copy_to_user(puser, &status, sizeof(status));
    if (nret) {
        pr_info("copy_to_user failed\n");
        return -1;
    }

    return sizeof(status);
}

static int key_fasync(int fd, struct file *fp, int on)
{
    return fasync_helper(fd, fp, on, &fapp);
}

static unsigned int key_poll(struct file *fp, struct poll_table_struct *ptable)
{
    unsigned int umask = 0;

    poll_wait(fp, &wq, ptable);

    if (0 == gpio_get_value(gpiokey)) {
        umask |= POLLIN;
    }

    return umask;
}

static void tasklet_fun(unsigned long arg)
{
    if (fapp != NULL) {
        kill_fasync(&fapp, SIGIO, POLLIN);
    }

    status = 1;
    condition = 1;
    wake_up_interruptible(&wq);

    return;
}

static void work_queue_fun(struct work_struct *work)
{
    
    if (fapp != NULL) {
        kill_fasync(&fapp, SIGIO, POLLIN);
    }

    status = 1;
    condition = 1;
    wake_up_interruptible(&wq);
    
    return;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = key_read,
    .fasync = key_fasync,
    .poll = key_poll,
};

static struct miscdevice misc_key = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_key",
    .fops = &fops,
};

static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
//    tasklet_schedule(&m_tasklet);
    schedule_work(&work_queue);  

    return IRQ_HANDLED;
}

static __init int key_drv_init(void)
{
    int ret = 0;
    
    init_waitqueue_head(&wq);
    INIT_WORK(&work_queue, work_queue_fun);

    ret = misc_register(&misc_key);
    if (ret) {
        pr_info("misc_register failed\n");
        return -1;
    }

    pkeynode = of_find_node_by_path("/putekey");
    if (NULL == pkeynode) {
        pr_info("of_find_node_by_path failed\n");
        return -1;
    }

    gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
    if (gpiokey < 0) {
        pr_info("of_get_named_gpio failed\n");
        return -1;
    }

    ret = gpio_request(gpiokey, "pute-key-gpio");
    if (ret) {
        pr_info("gpio_request failed\n");
        return -1;
    }

    ret = gpio_direction_input(gpiokey);
    if (ret) {
        pr_info("gpio_direction_input failed\n");
        return -1;
    }
#if 0
    irqno = gpio_to_irq(gpiokey);
    if (irqno < 0) {
        pr_info("gpio_to_irq failed\n");
        return -1;
    }
#endif

    irqno = of_irq_get(pkeynode, 0);
    if (irqno < 0) {
        pr_info("of_irq_get failed\n");
        return -1;
    }

    ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
    if (ret) {
        pr_info("request_irq failed\n");
        return -1;
    }

    pr_info("key_drv_init success\n");

    return 0;
}

static __exit void key_drv_exit(void)
{
    int ret = 0;
    
    free_irq(irqno, NULL);
    gpio_free(gpiokey);
    ret = misc_deregister(&misc_key);
    if (ret) {
        pr_info("misc_deregister failed\n");
        return;
    }

    pr_info("key_drv_exit success\n");

    return;
}

module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");

key_app.c

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

int fd = 0;

void handler(int signo)
{
    int stat = 0;

    read(fd, &stat, sizeof(stat));
    printf("stat = %d\n", stat);

    return;
}

int main(void)
{
    int flags = 0;
    int nready = 0;
    int stat = 0;
    fd_set rdfds;
    fd_set tmpfds;

    signal(SIGIO, handler);

    fd = open("/dev/misc_key", O_RDWR);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

#if 0
    flags = fcntl(fd, F_GETFL);         //获得文件描述符属性
    flags |= O_ASYNC;                   //在文件描述符属性基础上加入异步IO属性
    fcntl(fd, F_SETFL, flags);          //重新设置文件描述符属性
    fcntl(fd, F_SETOWN, getpid());      //产生时间发送给当前进程任务


    FD_ZERO(&rdfds);
    FD_SET(fd, &rdfds);

    while (1)
    {
        tmpfds = rdfds;
        nready = select(fd+1, &tmpfds, NULL, NULL, NULL);
        if (-1 == nready) 
        {
            perror("fail to select");
            return -1;
        }

        if (FD_ISSET(fd, &tmpfds))
        {
            read(fd, &stat, sizeof(stat));
            printf("stat = %d\n", stat);
        }
    }
#endif

    while (1)
    {
        read(fd, &stat, sizeof(stat));
        printf("stat = %d\n", stat);
    }


    close(fd);

    return 0;
}

五、总结

        1. 驱动中中断的使用?

                1)中断注册:驱动程序通过request_irq函数向内核注册中断处理程序,指定中断号、处理函数、触发方式(如边沿触发或电平触发)及设备标识。

                2)中断触发:硬件设备通过中断线向CPU发送信号,CPU暂停当前任务,保存上下文并跳转到中断处理程序。

                3)中断处理:中断处理函数(ISR)需快速完成关键操作(如读取设备状态),通常分为上半部(立即处理)和下半部(延迟处理,如通过tasklet或工作队列)。

                4)中断注销:驱动卸载时需释放中断资源。

        2.驱动中的四种IO模型怎么实现?

                1)阻塞式IO模型

                        等待队列实现阻塞IO,事件发生后中断,来唤醒事件,返回应用层

                2) 非阻塞IO

                        直接操作寄存器,应用层调用驱动层的read,不管什么情况,都会返回
                3)异步IO

                        重写file_oprations里的fasync函数,事件发生时向应用层发送通知
                4) 多路复用IO执行

                        重写file_oprations里的poll函数,应用层的select就会调用驱动层的poll,没有产生事件的时候会把他加入到监听表里并且关联一个等待队列,事件发生的时候,会把等待队列唤醒,那么表中的事件就会被会唤醒,只需要返回事件即可。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐