• 代码示例_中断下半部



      1 //头文件
      2 #include <linux/init.h>
      3 #include <linux/module.h>
      4 #include <linux/fs.h>
      5 #include <linux/device.h>
      6 #include <linux/slab.h>
      7 #include <linux/gpio.h>
      8 #include <linux/cdev.h>
      9 #include <linux/interrupt.h>
     10 #include <linux/input.h>
     11 #include <linux/sched.h>
     12 #include <linux/poll.h>
     13 #include <linux/mm.h>
     14 
     15 
     16 #include <asm/io.h>
     17 #include <asm/page.h>
     18 #include <asm/string.h>
     19 #include <asm/uaccess.h>
     20 #include <asm-generic/ioctl.h>
     21 
     22 #define BUTTON_iOC_GET_DATA 0x4321
     23 struct mem_data{
     24     char buf[128];
     25 };
     26 
     27 //定义一个按键的数据包
     28 struct button_event{
     29     int code;         //按键的名称---键值:KEY_DOWN
     30     int value;        //按键的状态---按下:1,松开:0
     31 };
     32 
     33 //设计一个描述按键的结构体类型
     34 struct buttons{
     35     char *name;        //名称
     36     unsigned int irqno;        //中断号
     37     int gpio;                //按键对应的gpio口
     38     int code;                //键值
     39     unsigned long flags;    //触发方式
     40 };
     41 
     42 //定义一个数组来保存多个按键的数据
     43 struct buttons buttons_set[] = {
     44     [0] = {
     45         .name = "key1_up",
     46         .irqno = IRQ_EINT(0),
     47         .gpio = S5PV210_GPH0(0),
     48         .code = KEY_UP,
     49         .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
     50     },
     51     [1] = {
     52         .name = "key2_down",
     53         .irqno = IRQ_EINT(1),
     54         .gpio = S5PV210_GPH0(1),
     55         .code = KEY_DOWN,
     56         .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
     57     },
     58     [2] = {
     59         .name = "key3_left",
     60         .irqno = IRQ_EINT(2),
     61         .gpio = S5PV210_GPH0(2),
     62         .code = KEY_LEFT,
     63         .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
     64     },
     65     [3] = {
     66         .name = "key4_right",
     67         .irqno = IRQ_EINT(3),
     68         .gpio = S5PV210_GPH0(3),
     69         .code = KEY_RIGHT,
     70         .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
     71     },
     72 };
     73 
     74 //面向对象编程----设计设备的类型
     75 struct s5pv210_button{
     76     //unsigned int major;
     77     dev_t  devno;
     78     struct class * cls;
     79     struct device * dev;
     80     struct cdev  *cdev;
     81     unsigned int irqno;
     82     struct button_event event;
     83     wait_queue_head_t wq_head;
     84     
     85     int have_data;        //表示当前是否有数据可读,可读--1,不可读--0
     86 
     87     void * virt_mem;
     88     struct tasklet_struct tasklet;
     89 };
     90 struct s5pv210_button *button_dev;
     91 
     92 //中断下半部的处理函数-----该函数会被内核线程执行
     93 void button_irq_tasklet(unsigned long data)
     94 {
     95     printk("--------^_^ %s------------
    ",__FUNCTION__);
     96 
     97     //此时有数据可读
     98     button_dev->have_data = 1;
     99 
    100     //从等待队列中唤醒阻塞的进程
    101     wake_up_interruptible(&button_dev->wq_head);
    102     
    103 }
    104 
    105 
    106 //实现中断处理函数--------当触发中断时会被执行
    107 irqreturn_t button_irq_svc(int irqno, void *dev)
    108 {
    109     int value;
    110     struct buttons *p;
    111     printk("--------^_^ %s------------
    ",__FUNCTION__);
    112 
    113     //获取当前触发中断的按键信息
    114     p = (struct buttons *)dev;
    115     
    116     //获取产生中断的gpio口的值
    117     value = gpio_get_value(p->gpio);
    118     //判断是按下还是松开
    119     if(value){
    120         //松开
    121         printk("kernel:%s up!
    ",p->name);
    122         button_dev->event.code = p->code;
    123         button_dev->event.value = 0;
    124     }else{
    125         //按下
    126         printk("kenel:%s pressed!
    ",p->name);
    127         button_dev->event.code = p->code;
    128         button_dev->event.value = 1;
    129     }
    130 
    131     //将tasklet对象加入到内核线程中
    132     tasklet_schedule(&button_dev->tasklet);
    133     
    134     return IRQ_HANDLED;
    135 }
    136 
    137 //实现设备操作接口
    138 int button_open(struct inode *inode, struct file *filp)
    139 {
    140 
    141     printk("--------^_^ %s------------
    ",__FUNCTION__);
    142     
    143     return 0;
    144 }
    145 ssize_t button_read(struct file *filp , char __user *buf , size_t size, loff_t *flags)
    146 {
    147     int ret;
    148     printk("--------^_^ %s------------
    ",__FUNCTION__);
    149     //判读open时,有没有设置flags为NONBLOCK
    150     if(filp->f_flags & O_NONBLOCK && !button_dev->have_data)
    151         return -EAGAIN;
    152         
    153     //判断此时是否有数据可读
    154     wait_event_interruptible(button_dev->wq_head,button_dev->have_data);
    155     
    156     //将内核数据转换为用户空间数据
    157     ret = copy_to_user(buf,&button_dev->event,size);
    158     if(ret > 0){
    159         printk("copy_to_user error!
    ");
    160         return -EFAULT;
    161     }
    162 
    163     //将数据返回给应用空间后,清空数据包,同时将hava_data置零
    164     memset(&button_dev->event,0,sizeof(button_dev->event));
    165     button_dev->have_data = 0;
    166     return size;
    167 }
    168 
    169 ssize_t button_write(struct file *filp, const char __user *buf, size_t size, loff_t *flags)
    170 {
    171 
    172     printk("--------^_^ %s------------
    ",__FUNCTION__);
    173 
    174     return size;
    175 }
    176 
    177 long button_ioctl(struct file *filp, unsigned int cmd , unsigned long args)
    178 {
    179     void __user *argp;
    180     struct mem_data data;
    181     int ret;
    182     printk("--------^_^ %s------------
    ",__FUNCTION__);
    183     argp = (void __user *)args;
    184 
    185     switch(cmd){
    186         case BUTTON_iOC_GET_DATA:
    187                 memset(data.buf,0,sizeof(data.buf));
    188                 memcpy(data.buf, button_dev->virt_mem,sizeof(data.buf));
    189                 ret = copy_to_user(argp,&data,sizeof(data));
    190                 if(ret > 0){
    191                     return -EFAULT;
    192                 }
    193             break;
    194         default:
    195             printk("unkown cmd!
    ");
    196     }
    197     
    198     return 0;
    199 }
    200 
    201 int button_mmap(struct file *filp, struct vm_area_struct *vma)
    202 {
    203     unsigned int addr;
    204     printk("--------^_^ %s------------
    ",__FUNCTION__);
    205 
    206     //1,获得一块物理内存空间-----将申请的虚拟空间转换为对应的物理空间
    207     addr = virt_to_phys(button_dev->virt_mem);    
    208 
    209     //2,将物理内存映射到虚拟空间---应用空间
    210     
    211     vma->vm_flags |= VM_IO;
    212     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    213 
    214     if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
    215                     PAGE_SIZE, vma->vm_page_prot)) {
    216         printk(KERN_ERR "%s: io_remap_pfn_range failed
    ",__func__);
    217         return -EAGAIN;
    218     }
    219 
    220     return 0;
    221 }
    222 
    223 unsigned int button_poll(struct file *filp, struct poll_table_struct *pts)
    224 {
    225     unsigned int mask = 0;
    226     printk("--------^_^ %s------------
    ",__FUNCTION__);
    227 
    228     //1,将等待队列头注册到系统中(VFS)
    229     poll_wait(filp,&button_dev->wq_head,pts);
    230 
    231     //2,如果产生按键中断-有数据可读,此时返回POLLIN,如果没有数据返回0
    232     if(button_dev->have_data)
    233         mask |= POLLIN;
    234 
    235     return mask;
    236     
    237 }
    238 
    239 int button_close(struct inode *inode, struct file *filp)
    240 {
    241     printk("--------^_^ %s------------
    ",__FUNCTION__);
    242     
    243     return 0;
    244 }
    245 
    246 
    247 static struct file_operations fops = {
    248     .open = button_open,
    249     .read = button_read,
    250     .write = button_write,
    251     .poll = button_poll,
    252     .mmap = button_mmap,
    253     .unlocked_ioctl = button_ioctl,
    254     .release = button_close,
    255 };
    256 
    257 
    258 //加载函数和卸载函数
    259 static int __init button_init(void)   //加载函数-----在驱动被加载时执行
    260 {
    261     int ret,i;
    262     printk("--------^_^ %s------------
    ",__FUNCTION__);
    263     //0,实例化设备对象
    264     //参数1 ---- 要申请的空间的大小
    265     //参数2 ---- 申请的空间的标识
    266     button_dev = kzalloc(sizeof(struct s5pv210_button),GFP_KERNEL);
    267     if(IS_ERR(button_dev)){
    268         printk("kzalloc error!
    ");
    269         ret = PTR_ERR(button_dev);
    270         return -ENOMEM;
    271     }
    272     
    273     //1,申请设备号-----新方法
    274 #if 0
    275     //静态申请设备号
    276     button_dev->major = 256;
    277     ret = register_chrdev_region(MKDEV(button_dev->major,0),1,"button_drv");
    278     if(ret < 0){
    279         printk("register_chrdev_region error!
    ");
    280         ret =  -EINVAL;
    281         goto err_kfree;
    282     }
    283 #else
    284     //动态申请设备号
    285     ret = alloc_chrdev_region(&button_dev->devno,0,1,"button_drv");
    286     if(ret < 0){
    287         printk("register_chrdev_region error!
    ");
    288         ret =  -EINVAL;
    289         goto err_kfree;
    290     }
    291 #endif
    292 
    293     //创建cdev
    294 
    295     //申请cdev的空间
    296     button_dev->cdev = cdev_alloc();
    297     if(IS_ERR(button_dev->cdev)){        
    298         printk("button_dev->cdev error!
    ");
    299         ret = PTR_ERR(button_dev->cdev);
    300         goto err_unregister;
    301     }
    302 
    303     //初始化cdev的成员
    304     cdev_init(button_dev->cdev,&fops);
    305 
    306     //将cdev加入到内核中----链表
    307     ret = cdev_add(button_dev->cdev,button_dev->devno,1);
    308 
    309 
    310     
    311     //2,创建设备文件-----/dev/button
    312     button_dev->cls = class_create(THIS_MODULE,"button_cls");
    313     if(IS_ERR(button_dev->cls)){
    314         printk("class_create error!
    ");
    315         ret = PTR_ERR(button_dev->cls);
    316         goto err_cdev_del;
    317     }
    318     
    319     button_dev->dev = device_create(button_dev->cls,NULL,button_dev->devno,NULL,"button");
    320     if(IS_ERR(button_dev->dev)){
    321         printk("device_create error!
    ");
    322         ret = PTR_ERR(button_dev->dev);
    323         goto err_class;
    324     }
    325 
    326 
    327     //3,硬件初始化---申请中断
    328     for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
    329         ret = request_irq(buttons_set[i].irqno,button_irq_svc,buttons_set[i].flags,buttons_set[i].name,&buttons_set[i]);
    330         if(ret != 0){
    331             printk("request_irq error!
    ");
    332             ret = -EBUSY;
    333             goto err_device;
    334         }
    335     }
    336     //初始化等待队列头
    337     init_waitqueue_head(&button_dev->wq_head);
    338     
    339     //获取一块虚拟的内存空间---内核中
    340     button_dev->virt_mem = kzalloc(PAGE_SIZE,GFP_KERNEL);
    341     if(IS_ERR(button_dev->virt_mem)){
    342         printk("kzalloc error!
    ");
    343         ret = -EBUSY;
    344         goto err_free_irq;
    345     }
    346 
    347     //中断下半部---初始化: struct tasklet_struct
    348     tasklet_init(&button_dev->tasklet,button_irq_tasklet,123);
    349     
    350     return 0;
    351 err_free_irq:
    352     for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
    353         free_irq(buttons_set[i].irqno,&buttons_set[i]);
    354     }
    355 err_device:
    356     device_destroy(button_dev->cls,button_dev->devno);
    357 err_class:
    358     class_destroy(button_dev->cls);
    359     
    360 err_cdev_del:
    361     cdev_del(button_dev->cdev);
    362     
    363 err_unregister:
    364     unregister_chrdev_region(button_dev->devno,1);
    365     
    366 err_kfree:
    367     kfree(button_dev);
    368     return ret;
    369 
    370     
    371 }
    372 
    373 static void __exit button_exit(void)   //卸载函数-----在驱动被卸载时执行
    374 {
    375     int i;
    376     printk("--------^_^ %s------------
    ",__FUNCTION__);
    377     kfree(button_dev->virt_mem);    
    378     for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
    379         free_irq(buttons_set[i].irqno,&buttons_set[i]);
    380     }
    381     device_destroy(button_dev->cls,button_dev->devno);
    382     class_destroy(button_dev->cls);
    383     cdev_del(button_dev->cdev);
    384     unregister_chrdev_region(button_dev->devno,1);
    385     kfree(button_dev);
    386 }
    387 
    388 //声明和认证
    389 module_init(button_init);
    390 module_exit(button_exit);
    391 MODULE_LICENSE("GPL");

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <poll.h>
    #include <sys/mman.h>
    #include <linux/input.h>
    
    //定义一个按键的数据包
    struct button_event{
        int code;    //按键的名称---键值:KEY_DOWN
        int value;        //按键的状态---按下:1,松开:0
    };
    
    #define BUTTON_iOC_GET_DATA 0x4321
    #define PAGE_SIZE 1UL<<12
    struct mem_data{
        char buf[128];
    };
    int main(void)
    {
    
        int fd;
        int ret;
        struct button_event event;
        struct pollfd pfds[2];
        char buf[128];
        char *str = "hello kernel";
        struct mem_data data;
    
        fd = open("/dev/button",O_RDWR);
        if(fd < 0){
        perror("open");
        exit(1);
        }
        
        //测试mmap的功能
        char *addr = mmap(NULL,PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if(addr == NULL){
        perror("mmap");
        exit(1);
        }
        //向映射的物理空间中写数据
        strcpy(addr,str);
        sleep(1);
    
        //验证数据是否写入到映射的物理空间----通过ioctl读取数据
        ret = ioctl(fd,BUTTON_iOC_GET_DATA,&data);
        if(ret < 0){
        perror("ioctl");
        exit(1);
        }
        printf("data.buf = %s
    ",data.buf); //将读到的数据打印出来
        sleep(1);
    
        pfds[0].fd = 0;        //标准输入文件描述符
        pfds[0].events = POLLIN;    //是否可读
    
        pfds[1].fd = fd;        //开发板中的键盘
        pfds[1].events = POLLIN;    //按键是否触发中断
    
    
        while(1){
    
        ret = poll(pfds,2,-1);
        if(ret < 0){
            perror("poll");
            exit(1);
        }
        if(ret > 0){
    
            //标准输入可读
            if(pfds[0].revents & POLLIN){
            fgets(buf,sizeof(buf),stdin);
            printf("%s",buf);
            }
            //开发板中的按键触发了中断 
            if(pfds[1].revents & POLLIN){
    
            bzero(&event,sizeof(event));
            ret = read(fd,&event,sizeof(event));
            if(ret < 0){
                perror("read");
                exit(1);
            }
    
            switch(event.code){
                case KEY_UP:
                if(event.value)
                    printf("按下了上键!
    ");
                else
                    printf("松开了上键!
    ");
                break;
                case KEY_DOWN:
                if(event.value)
                    printf("按下了下键!
    ");
                else
                    printf("松开了下键!
    ");
                break;
                case KEY_LEFT:
                if(event.value)
                    printf("按下了左键!
    ");
                else
                    printf("松开了左键!
    ");
                break;
                case KEY_RIGHT:
                if(event.value)
                    printf("按下了右键!
    ");
                else
                    printf("松开了右键!
    ");
                break;
            }
            }
        }
        }
    
        close(fd);
        return 0;
    }

     1 #指定内核源码路径
     2 KERNEL_DIR = /home/farsight/s5pv210/kernel/linux-3.0.8
     3 CUR_DIR = $(shell pwd)
     4 MYAPP = test
     5 
     6 all:
     7     #让make进入内核源码编译,同时将当前目录中的c程序作为内核模块一起编译
     8     make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
     9     arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
    10 
    11 clean:
    12     #删除上面编译生成的文件
    13     make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
    14     rm -rf $(MYAPP)
    15 
    16 install:
    17     cp *.ko $(MYAPP) /opt/rootfs/drv_module
    18 
    19 #指定当前目录下哪个文件作为内核模块编
    20 obj-m = button_drv.o
    Stay hungry, stay foolish 待续。。。
  • 相关阅读:
    Python面试题目之Python函数默认参数陷阱
    Python面试题目之列表取值超出范围
    Python面试题目之打乱打乱有序列表
    Python面试题目之列表去重并维持原来顺序
    Python面试题目之字典排序
    前端学习笔记之JavaScript
    前端学习笔记之CSS后代选择器、子元素选择器、相邻兄弟选择器区别与详解
    前端学习笔记之CSS知识汇总
    SQL学习笔记八之ORM框架SQLAlchemy
    高效循环算法
  • 原文地址:https://www.cnblogs.com/panda-w/p/10991450.html
Copyright © 2020-2023  润新知