物联网之内核及驱动开发初级四(中断编程)

发布时间:2024-11-25 07:26

在中国四川的四姑娘山,适合初级和中级徒步者 #生活乐趣# #旅行建议# #户外徒步线路#

Linux驱动开发之中断编程

1,中断号--就是一个号码,需要通过一定的方式去获取到
    在3.14.0内核中,从设备树中获取

    获取中断号的方法:
        1, 宏定义
                IRQ_EINT(号码)
        2,设备树文件中
            arch/arm/boot/dts/exynos4412-fs4412.dts

    硬件连接:
            key ---- gpx1_2--- EINT10


    打开设备树文件:arch/arm/boot/dts/exynos4x12-pinctrl.dtsi
    找到gpx1:(该代码已经编译好,无需人工编写,此处作为分析源码)
         gpx1: gpx1 {
                        gpio-controller;//描述信息:这是一个gpio口控制器
                        #gpio-cells = <2>;//

                        interrupt-controller;//描述信息:这是一个中断控制器
                        interrupt-parent = <&gic>;//中断继承于gic(gpio具有中断功能,继承了一些中断特性)
                        interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
                                     <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;//中断号的一些表述(见芯片手册中断部分的说明,
                                                                                                     //上图右下角)
                        #interrupt-cells = <2>;
                };

    在编程过程中,需要定义自己的节点--描述当前设备用的中断号(此处需要人工编写完成)
         arch/arm/boot/dts/exynos4412-fs4412.dts  +51

                          key_int_node{
                compatible = "test_key";//可以通过compatible 来搜索这个节点
                interrupt-parent = <&gpx1>;//继承gpx1
                interrupts = <2 4>;//这里设置gpx1中的<0 26 0>,该成员位于gpx1中interrupts 的第2个,所以第一个位置
                                             //填写2第二个位置填写的是触发方式(0:上升沿   2:下降沿   4:高电平   8:低电平,
                                             //具体待查,可以不用管)
            };

    编译设备树文件:
        make dtbs
    更新dtbs文件:
        cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb  /tftpboot/

     2,在驱动中去通过代码获取到中断号,并且申请中断(实现中断处理方法)

    a,获取到中断号码:
        int get_irqno_from_node(void)
        {
            // 获取到设备树中的节点
            struct device_node *np = of_find_node_by_path("/key_int_node");
            if(np){
                printk("find node ok\n");
            }else{
                printk("find node failed\n");
            }

            // 通过节点去获取到中断号码
            int irqno = irq_of_parse_and_map(np, 0);
            printk("irqno = %d\n", irqno);

                         return irqno;
        }
    b,申请中断
    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev)
        参数1: 设备对应的中断号
        参数2: 中断的处理函数
                typedef irqreturn_t (*irq_handler_t)(int, void *);
        参数3:触发方式
                #define IRQF_TRIGGER_NONE    0x00000000  //内部控制器触发中断的时候的标志
                #define IRQF_TRIGGER_RISING    0x00000001 //上升沿
                #define IRQF_TRIGGER_FALLING    0x00000002 //下降沿
                #define IRQF_TRIGGER_HIGH    0x00000004  // 高点平
                #define IRQF_TRIGGER_LOW    0x00000008 //低电平触发
        参数4:中断的描述,自定义,主要是给用户查看的
                /proc/interrupts
        参数5:传递给参数2中函数指针的值
        返回值: 正确为0,错误非0


        参数2的赋值:
        irqreturn_t key_irq_handler(int irqno, void *devid)
        {
            return IRQ_HANDLED;
        }


        释放中断:
            void free_irq(unsigned int irq, void *dev_id)
            参数1: 设备对应的中断号
            参数2:与request_irq中第5个参数保持一致

3,实现字符设备驱动的框架
    // 1,设定一个全局的设备对象
    key_dev = kzalloc(sizeof(struct key_desc),  GFP_KERNEL);

         // 2,申请主设备号
    key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

    // 3,创建设备节点文件
    key_dev->cls = class_create(THIS_MODULE, "key_cls");
    key_dev->dev = device_create(key_dev->cls, NULL,MKDEV(key_dev->dev_major,0), NULL, "key0");

4,驱动中将硬件所产生的数据传递给用户
    a,硬件如何获取数据
        key: 按下和抬起: 1/0
        读取key对应的gpio的状态,可以判断按下还是抬起

                 读取key对应gpio的寄存器--数据寄存器

                 //读取数据寄存器
        int value = readl(key_dev->reg_base + 4) & (1<<2);

    b,驱动如何传递给用户
        在中断处理中填充数据:
            key_dev->event.code = KEY_ENTER;
            key_dev->event.value = 0;//按键的值(0/1)


        在xxx_read中将数据传递给用户
            ret = copy_to_user(buf, &key_dev->event,  count);

    c,用户如何拿到--编写应用程序
        while(1)
        {
            read(fd, &event, sizeof(struct key_event));

            if(event.code == KEY_ENTER)
            {
                if(event.value)
                {
                    printf("APP__ key enter pressed\n");
                }else{
                    printf("APP__ key enter up\n");
                }
            }
        }

5,实现文件IO模型之一阻塞,等同于休眠
    文件io模型:
            1,非阻塞
            2,阻塞
            3,多路复用--select/poll
            4, 异步信号通知faync

    阻塞: 当进程在读取外部设备的资源(数据),资源没有准备好,进程就会休眠
        linux应用中,大部分的函数接口都是阻塞
            scanf();
            read();
            write();
            accept();
    驱动中需要调用
        1,将当前进程加入到等待队列头中
            add_wait_queue(wait_queue_head_t * q, wait_queue_t * wait)
        2,将当前进程状态设置成TASK_INTERRUPTIBLE
            set_current_state(TASK_INTERRUPTIBLE)
        3,让出调度--休眠
            schedule(void)

         更加智能的接口,等同于上面的三个接口:
        wait_event_interruptible(wq, condition)

    驱动如何去写代码(参考:https://blog.csdn.net/yikai2009/article/details/8653578)
        1,等待队列头
                wait_queue_head_t   wq_head;//定义等待队列

                init_waitqueue_head(&key_dev->wq_head);//初始化等待队列。函数原型:init_waitqueue_head(wait_queue_head_t *q);

struct key_disc

{

unsigned int dev_major;

struct class * cls;

struct device * dev;

int irqno;

void * reg_base;

struct key_event event;

wait_queue_head_t wq_head;

int key_state;

}* key_dev;

static int __init key_drv_init(void)

{

int ret;

key_dev = kzalloc(sizeof(struct key_disc), GFP_KERNEL);

key_dev->dev_major = register_chrdev(0, "key_dev_test", &my_fops);

key_dev->cls = class_create(THIS_MODULE, "key_cls");

key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key%d",0);

key_dev->irqno = get_irqno_from_node();

key_dev->reg_base = ioremap(GPXCON_REG,8);

ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key3_eint10", NULL);

if(ret != 0)

{

printk("request_irq error\n");

return -EBUSY;

}

init_waitqueue_head(&key_dev->wq_head);

return 0;

}

        2,在需要等待(没有数据)的时候,进行休眠
            wait_event_interruptible(wait_queue_head_t wq, condition) // 内部会构建一个等待队列项/节点wait_queue_t
            参数1: 等待队列头
            参数2: 条件,如果是为假,就会等待(进程进入 TASK_INTERRUPTIBLE 模式睡眠,并挂在 queue 参数所指定的等待队列上),如果为真,就不会等待
                    可以用一标志位,来表示是否有数据

ssize_t key_drv_read (struct file *filp, char __user *buf, size_t count, loff_t *fpos)

{

int ret;

wait_event_interruptible(key_dev->wq_head, key_dev->key_state);

ret = copy_to_user(buf, &key_dev->event, count);

if(ret > 0)

{

printk("copy_to_user error\n");

return -EFAULT;

}

memset(&key_dev->event, 0, sizeof(key_dev->event));

key_dev->key_state = 0;

flag = 1;

return count;

}

        3,在一个合适的时候(有数据),会将进程唤醒
            wake_up_interruptible(wait_queue_head_t *q)// 从等待队列 q 中唤醒状态为 TASK_INTERRUPTIBLE 的进程

            用法:
                wake_up_interruptible(&key_dev->wq_head);
                //同时设置标志位
                key_dev->key_state  = 1;

irqreturn_t key_irq_handler(int irqno, void * devid)

{

if(flag){

int value = readl(key_dev->reg_base + 4) & (0x1<<2);

if(value)

{

printk("k3 up\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 0;

}

else

{

printk("k3 pressed\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 1;

}

wake_up_interruptible(&key_dev->wq_head);

key_dev->key_state = 1;}

return IRQ_HANDLED;

}

6, 非阻塞: 在读写的时候,如果没有数据,立刻返回,并且返回一个出错码
        用的会比较少,因为比较耗资源

    open("/dev/key0", O_RDWR|O_NONBLOCK);
    ------------------------------------
    驱动中需要去区分,当前模式是阻塞还是非阻塞
    //如果当前是非阻塞模式,并且没有数据,立马返回一个出错码
    if(filp->f_flags & O_NONBLOCK && !key_dev->key_state)
        return -EAGAIN;

7,多路复用--select和poll
    poll的应用:
    1, 需要打开多个文件(多个设备)

    2, 利用poll来实现监控fd的读,写,出错
        #include <poll.h>

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);
       参数1: 表示多个文件描述符集合
            struct pollfd描述的是文件描述符的信息
            struct pollfd {
               int   fd;  //文件描述符
               short events;   //希望监控fd的什么事件:读,写,出错
                            POLLIN 读,
                            POLLOUT 写,
                            POLLERR出错
               short revents;    //结果描述,表示当前的fd是否有读,写,出错
                            //用于判断,是内核自动赋值
                            POLLIN 读,
                            POLLOUT 写,
                            POLLERR出错
            };
        参数2:被监控的fd的个数
        参数3: 监控的时间:
                    正: 表示监控多少ms
                    负数: 无限的时间去监控
                    0: 等待0ms,类似于非阻赛
        返回值: 负数:出错
                大于0,表示fd中有数据
                等于0: 时间到

    

         8,如果应用中使用poll对设备文件进行了监控,那么设备驱动就必须实现poll接口
    unsigned int key_drv_poll(struct file *filp, struct poll_table_struct *pts)
    {

                 // 返回一个mask值
        unsigned int mask;
        // 调用poll_wait,将当前的等待队列注册到系统中
        poll_wait(filp, &key_dev->wq_head, pts);

                 // 1,当没有数据的时候返回一个0
        if(!key_dev->key_state)
            mask = 0;

        // 2,有数据返回一个POLLIN
        if(key_dev->key_state)
            mask |= POLLIN;

        return mask;
    }

    const struct file_operations key_fops = {
        .poll = key_drv_poll,
    };

struct key_desc{

unsigned int dev_major;

struct class *cls;

struct device *dev;

int irqno;

void *reg_base;

struct key_event event;

wait_queue_head_t wq_head;

int key_state;

};

unsigned int key_drv_poll(struct file *filp, struct poll_table_struct *pts)

{

unsigned int mask;

poll_wait(filp, &key_dev->wq_head, pts);

if(!key_dev->key_state)

mask = 0;

if(key_dev->key_state)

mask |= POLLIN;

return mask;

}

const struct file_operations key_fops = {

.open = key_drv_open,

.read = key_drv_read,

.write = key_drv_write,

.release = key_drv_close,

.poll = key_drv_poll,

};

static int __init key_drv_init(void)

{

int ret;

key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);

key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

key_dev->cls = class_create(THIS_MODULE, "key_cls");

key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0");

key_dev->irqno = get_irqno_from_node();

ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,

"key3_eint10", NULL);

if(ret != 0)

{

printk("request_irq error\n");

return ret;

}

key_dev->reg_base = ioremap(GPXCON_REG, 8);

init_waitqueue_head(&key_dev->wq_head);

return 0;

}

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <poll.h>

struct key_event{

int code;

int value;

};

#define KEY_ENTER28

int main(int argc, char *argv[])

{

int ret;

struct key_event event;

char in_buf[128];

int fd = open("/dev/key0", O_RDWR);

if(fd < 0)

{

perror("open");

exit(1);

}

struct pollfd pfd[2];

pfd[0].fd = fd;

pfd[0].events = POLLIN;

pfd[1].fd = 0;

pfd[1].events = POLLIN;

while(1)

{

ret = poll(pfd, 2, -1);

printf("ret = %d\n", ret);

if(ret > 0)

{

if(pfd[0].revents & POLLIN)

{

read(pfd[0].fd, &event, sizeof(struct key_event));

if(event.code == KEY_ENTER)

{

if(event.value)

{

printf("APP__ key enter pressed\n");

}else

{

printf("APP__ key enter up\n");

}

}

}

if(pfd[1].revents & POLLIN)

{

fgets(in_buf, 128, stdin);

printf("in_buf = %s\n", in_buf);

}

}else{

perror("poll");

exit(1);

}

}

close(pfd[0].fd);

return 0;

}

 9,异步信号通知: 当有数据到时候,驱动会发送信号(SIGIO)给应用,就可以异步去读写数据,不用主动去读写
    a,应用--处理信号,主要是读写数据
         void catch_signale(int signo)
        {
            if(signo == SIGIO)
            {
                printf("we got sigal SIGIO");
                // 读取数据
                read(fd, &event, sizeof(struct key_event));
                if(event.code == KEY_ENTER)
                {
                    if(event.value)
                    {
                        printf("APP__ key enter pressed\n");
                    }else
                    {
                        printf("APP__ key enter up\n");
                    }
                }
            }
        }

        // 1,设置信号处理方法
        signal(SIGIO,catch_signale);
        // 2,将当前进程设置成SIGIO的属主进程
        fcntl(fd, F_SETOWN, getpid());

        // 3,将io模式设置成异步模式
        int flags  = fcntl(fd, F_GETFL);
        fcntl(fd, F_SETFL, flags | FASYNC );

    b,驱动--发送信号
        1,需要和进程进行关联--记录信号该发送给谁
            实现一个fasync的接口

            int key_drv_fasync(int fd, struct file *filp, int on)
            {
                //只需要调用一个函数记录信号该发送给谁
                return fasync_helper(fd, filp, on,  &key_dev->faysnc);

            }
        2,在某个特定的时候去发送信号,在有数据的时候
            //发送信号
            kill_fasync(&key_dev->faysnc, SIGIO, POLLIN);

struct key_desc{

unsigned int dev_major;

struct class *cls;

struct device *dev;

int irqno;

void *reg_base;

struct key_event event;

wait_queue_head_t wq_head;

int key_state;

struct fasync_struct *faysnc;

};

struct key_desc *key_dev;

irqreturn_t key_irq_handler(int irqno, void *devid)

{

printk("-------%s-------------\n", __FUNCTION__);

int value = readl(key_dev->reg_base + 4) & (1<<2);

if(value){

printk("key3 up\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 0;

}else{

printk("key3 pressed\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 1;

}

wake_up_interruptible(&key_dev->wq_head);

key_dev->key_state = 1;

kill_fasync(&key_dev->faysnc, SIGIO, POLLIN);

return IRQ_HANDLED;

}

ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)

{

if(filp->f_flags & O_NONBLOCK && !key_dev->key_state)

return -EAGAIN;

int ret;

wait_event_interruptible(key_dev->wq_head, key_dev->key_state);

ret = copy_to_user(buf, &key_dev->event, count);

if(ret > 0)

{

printk("copy_to_user error\n");

return -EFAULT;

}

memset(&key_dev->event, 0, sizeof(key_dev->event));

key_dev->key_state = 0;

return count;

}

unsigned int key_drv_poll(struct file *filp, struct poll_table_struct *pts)

{

unsigned int mask;

poll_wait(filp, &key_dev->wq_head, pts);

if(!key_dev->key_state)

mask = 0;

if(key_dev->key_state)

mask |= POLLIN;

return mask;

}

int key_drv_fasync(int fd, struct file *filp, int on)

{

return fasync_helper(fd, filp, on, &key_dev->faysnc);

}

const struct file_operations key_fops = {

.open = key_drv_open,

.read = key_drv_read,

.write = key_drv_write,

.release = key_drv_close,

.poll = key_drv_poll,

.fasync = key_drv_fasync,

};

static int __init key_drv_init(void)

{

int ret;

key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);

key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

key_dev->cls = class_create(THIS_MODULE, "key_cls");

key_dev->dev = device_create(key_dev->cls, NULL,

MKDEV(key_dev->dev_major,0), NULL, "key0");

key_dev->irqno = get_irqno_from_node();

ret = request_irq(key_dev->irqno,key_irq_handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key3_eint10", NULL);

if(ret != 0)

{

printk("request_irq error\n");

return ret;

}

key_dev->reg_base = ioremap(GPXCON_REG, 8);

init_waitqueue_head(&key_dev->wq_head);

return 0;

}

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <poll.h>

#include <signal.h>

struct key_event{

int code;

int value;

};

#define KEY_ENTER28

static int fd;

static struct key_event event;

void catch_signale(int signo)

{

if(signo == SIGIO)

{

printf("we got sigal SIGIO\n");

read(fd, &event, sizeof(struct key_event));

if(event.code == KEY_ENTER)

{

if(event.value)

{

printf("APP__ key enter pressed\n");

}else

{

printf("APP__ key enter up\n");

}

}

}

}

int main(int argc, char *argv[])

{

int ret;

fd = open("/dev/key0", O_RDWR);

if(fd < 0)

{

perror("open");

exit(1);

}

signal(SIGIO,catch_signale);

fcntl(fd, F_SETOWN, getpid());

int flags = fcntl(fd, F_GETFL);

fcntl(fd, F_SETFL, flags | FASYNC );

while(1)

{

printf("I am waiting......\n");

sleep(1);

}

close(fd);

return 0;

}

10-中断的下半部
    1,softirq: 处理比较快,但是内核级别的机制,需要修改整个内核源码,不推荐也不常用
    2,tasklet: 内部实现实际调用了softirq
    3, workqueue: 工作队列
    

    1,tasklet:
        struct tasklet_struct
        {
            struct tasklet_struct *next;
            unsigned long state;
            atomic_t count;
            void (*func)(unsigned long); // 下半部的实现逻辑
            unsigned long data; // 传递给func的参数
        };

        a, 初始化
            struct tasklet_struct mytasklet;

            tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data)

            例子:
            void key_tasklet_half_irq(unsigned long data)
            {
                // 表示有数据,需要去唤醒整个进程/等待队列
                wake_up_interruptible(&key_dev->wq_head);
                //同时设置标志位
                key_dev->key_state  = 1;

                //发送信号
                kill_fasync(&key_dev->faysnc, SIGIO, POLLIN);
            }

            tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 45);


        b,在上半部中放入到内核线程中--启动
            // 启动下半步
            tasklet_schedule(&key_dev->mytasklet);

        c,模块卸载的时候:
            tasklet_kill(&key_dev->mytasklet);

struct key_desc{

unsigned int dev_major;

struct class *cls;

struct device *dev;

int irqno;

void *reg_base;

struct key_event event;

wait_queue_head_t wq_head;

int key_state;

struct fasync_struct *faysnc;

struct tasklet_struct mytasklet;

};

struct key_desc *key_dev;

void key_tasklet_half_irq(unsigned long data)

{

printk("-------%s-------------\n", __FUNCTION__);

wake_up_interruptible(&key_dev->wq_head);

key_dev->key_state = 1;

kill_fasync(&key_dev->faysnc, SIGIO, POLLIN);

}

irqreturn_t key_irq_handler(int irqno, void *devid)

{

printk("-------%s-------------\n", __FUNCTION__);

int value = readl(key_dev->reg_base + 4) & (1<<2);

if(value){

printk("key3 up\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 0;

}else{

printk("key3 pressed\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 1;

}

tasklet_schedule(&key_dev->mytasklet);

return IRQ_HANDLED;

}

ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)

{

if(filp->f_flags & O_NONBLOCK && !key_dev->key_state)

return -EAGAIN;

int ret;

wait_event_interruptible(key_dev->wq_head, key_dev->key_state);

ret = copy_to_user(buf, &key_dev->event, count);

if(ret > 0)

{

printk("copy_to_user error\n");

return -EFAULT;

}

memset(&key_dev->event, 0, sizeof(key_dev->event));

key_dev->key_state = 0;

return count;

}

static int __init key_drv_init(void)

{

int ret;

key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);

key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

key_dev->cls = class_create(THIS_MODULE, "key_cls");

key_dev->dev = device_create(key_dev->cls, NULL,

MKDEV(key_dev->dev_major,0), NULL, "key0");

key_dev->irqno = get_irqno_from_node();

ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,

"key3_eint10", NULL);

if(ret != 0)

{

printk("request_irq error\n");

return ret;

}

key_dev->reg_base = ioremap(GPXCON_REG, 8);

init_waitqueue_head(&key_dev->wq_head);

tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 45);

return 0;

}

   2,工作队列和工作
        typedef void (*work_func_t)(struct work_struct *work);

        struct work_struct {
            atomic_long_t data;
            struct list_head entry;
            work_func_t func;
        };

        a, 初始化

                         void work_irq_half(struct work_struct *work)
            {
                printk("-------%s-------------\n", __FUNCTION__);
                // 表示有数据,需要去唤醒整个进程/等待队列
                wake_up_interruptible(&key_dev->wq_head);
                //同时设置标志位
                key_dev->key_state  = 1;

                //发送信号
                kill_fasync(&key_dev->faysnc, SIGIO, POLLIN);

                             }
            struct work_struct mywork;

            INIT_WORK(struct work_struct *work, work_func_t func);

        b, 在上半部中放入到内核线程中--启动

            schedule_work(&key_dev->mywork);

struct key_desc{

unsigned int dev_major;

struct class *cls;

struct device *dev;

int irqno;

void *reg_base;

struct key_event event;

wait_queue_head_t wq_head;

int key_state;

struct fasync_struct *faysnc;

struct work_struct mywork;

};

struct key_desc *key_dev;

void work_irq_half(struct work_struct *work)

{

printk("-------%s-------------\n", __FUNCTION__);

wake_up_interruptible(&key_dev->wq_head);

key_dev->key_state = 1;

kill_fasync(&key_dev->faysnc, SIGIO, POLLIN);

}

irqreturn_t key_irq_handler(int irqno, void *devid)

{

printk("-------%s-------------\n", __FUNCTION__);

int value = readl(key_dev->reg_base + 4) & (1<<2);

if(value){

printk("key3 up\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 0;

}else{

printk("key3 pressed\n");

key_dev->event.code = KEY_ENTER;

key_dev->event.value = 1;

}

schedule_work(&key_dev->mywork);

return IRQ_HANDLED;

}

ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)

{

if(filp->f_flags & O_NONBLOCK && !key_dev->key_state)

return -EAGAIN;

int ret;

wait_event_interruptible(key_dev->wq_head, key_dev->key_state);

ret = copy_to_user(buf, &key_dev->event, count);

if(ret > 0)

{

printk("copy_to_user error\n");

return -EFAULT;

}

memset(&key_dev->event, 0, sizeof(key_dev->event));

key_dev->key_state = 0;

return count;

}

static int __init key_drv_init(void)

{

int ret;

key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);

key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

key_dev->cls = class_create(THIS_MODULE, "key_cls");

key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0");

key_dev->irqno = get_irqno_from_node();

ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key3_eint10", NULL);

if(ret != 0)

{

printk("request_irq error\n");

return ret;

}

key_dev->reg_base = ioremap(GPXCON_REG, 8);

init_waitqueue_head(&key_dev->wq_head);

INIT_WORK(&key_dev->mywork, work_irq_half);

return 0;

}

网址:物联网之内核及驱动开发初级四(中断编程) https://www.yuejiaxmz.com/news/view/253646

相关内容

IOT物联网中台:开启智慧生活新篇章 物联网平台系统
阿里云物联网平台学习(二)之场景联动
电机驱动软件开发工程师
IOT物联网中台:开启智慧生活新篇章 物联网平台系统,构建智慧城市数据生态 智慧城镇、物联网、数据中台
​“互联网+精益物流”=物流管理换代升级
初中部生活教师考核方案..doc
物联网关键技术及应用
智能互联:驱动未来科技变革的核心力量
生活中物联网的应用范围
万物互联:软件与硬件的协同之道

随便看看