• 代码示例_mmap的实现



    //头文件
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/slab.h>
    #include <linux/gpio.h>
    #include <linux/cdev.h>
    #include <linux/interrupt.h>
    #include <linux/input.h>
    #include <linux/sched.h>
    #include <linux/poll.h>
    #include <linux/mm.h>
    
    
    
    
    #include <asm/io.h>
    #include <asm/page.h>
    #include <asm/string.h>
    #include <asm/uaccess.h>
    #include <asm-generic/ioctl.h>
    
    #define BUTTON_iOC_GET_DATA 0x4321
    struct mem_data{
        char buf[128];
    };
    
    //定义一个按键的数据包
    struct button_event{
        int code;         //按键的名称---键值:KEY_DOWN
        int value;        //按键的状态---按下:1,松开:0
    };
    
    //设计一个描述按键的结构体类型
    struct buttons{
        char *name;        //名称
        unsigned int irqno;        //中断号
        int gpio;                //按键对应的gpio口
        int code;                //键值
        unsigned long flags;    //触发方式
    };
    
    //定义一个数组来保存多个按键的数据
    struct buttons buttons_set[] = {
        [0] = {
            .name = "key1_up",
            .irqno = IRQ_EINT(0),
            .gpio = S5PV210_GPH0(0),
            .code = KEY_UP,
            .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
        },
        [1] = {
            .name = "key2_down",
            .irqno = IRQ_EINT(1),
            .gpio = S5PV210_GPH0(1),
            .code = KEY_DOWN,
            .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
        },
        [2] = {
            .name = "key3_left",
            .irqno = IRQ_EINT(2),
            .gpio = S5PV210_GPH0(2),
            .code = KEY_LEFT,
            .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
        },
        [3] = {
            .name = "key4_right",
            .irqno = IRQ_EINT(3),
            .gpio = S5PV210_GPH0(3),
            .code = KEY_RIGHT,
            .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
        },
    };
    
    //面向对象编程----设计设备的类型
    struct s5pv210_button{
        //unsigned int major;
        dev_t  devno;
        struct class * cls;
        struct device * dev;
        struct cdev  *cdev;
        unsigned int irqno;
        struct button_event event;
        wait_queue_head_t wq_head;
        
        int have_data;        //表示当前是否有数据可读,可读--1,不可读--0
    
        void * virt_mem;
    };
    struct s5pv210_button *button_dev;
    
    //实现中断处理函数--------当触发中断时会被执行
    irqreturn_t button_irq_svc(int irqno, void *dev)
    {
        int value;
        struct buttons *p;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
    
        //获取当前触发中断的按键信息
        p = (struct buttons *)dev;
        
        //获取产生中断的gpio口的值
        value = gpio_get_value(p->gpio);
        //判断是按下还是松开
        if(value){
            //松开
            printk("kernel:%s up!
    ",p->name);
            button_dev->event.code = p->code;
            button_dev->event.value = 0;
        }else{
            //按下
            printk("kenel:%s pressed!
    ",p->name);
            button_dev->event.code = p->code;
            button_dev->event.value = 1;
        }
    
        //此时有数据可读
        button_dev->have_data = 1;
    
        //从等待队列中唤醒阻塞的进程
        wake_up_interruptible(&button_dev->wq_head);
        
        return IRQ_HANDLED;
    }
    
    //实现设备操作接口
    int button_open(struct inode *inode, struct file *filp)
    {
    
        printk("--------^_^ %s------------
    ",__FUNCTION__);
        
        return 0;
    }
    ssize_t button_read(struct file *filp , char __user *buf , size_t size, loff_t *flags)
    {
        int ret;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
        //判读open时,有没有设置flags为NONBLOCK
        if(filp->f_flags & O_NONBLOCK && !button_dev->have_data)
            return -EAGAIN;
            
        //判断此时是否有数据可读
        wait_event_interruptible(button_dev->wq_head,button_dev->have_data);
        
        //将内核数据转换为用户空间数据
        ret = copy_to_user(buf,&button_dev->event,size);
        if(ret > 0){
            printk("copy_to_user error!
    ");
            return -EFAULT;
        }
    
        //将数据返回给应用空间后,清空数据包,同时将hava_data置零
        memset(&button_dev->event,0,sizeof(button_dev->event));
        button_dev->have_data = 0;
        return size;
    }
    
    ssize_t button_write(struct file *filp, const char __user *buf, size_t size, loff_t *flags)
    {
    
        printk("--------^_^ %s------------
    ",__FUNCTION__);
    
        return size;
    }
    
    long button_ioctl(struct file *filp, unsigned int cmd , unsigned long args)
    {
        void __user *argp;
        struct mem_data data;
        int ret;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
        argp = (void __user *)args;
    
        switch(cmd){
            case BUTTON_iOC_GET_DATA:
                    memset(data.buf,0,sizeof(data.buf));
                    memcpy(data.buf, button_dev->virt_mem,sizeof(data.buf));
                    ret = copy_to_user(argp,&data,sizeof(data));
                    if(ret > 0){
                        return -EFAULT;
                    }
                break;
            default:
                printk("unkown cmd!
    ");
        }
        
        return 0;
    }
    
    int button_mmap(struct file *filp, struct vm_area_struct *vma)
    {
        unsigned int addr;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
    
        //1,获得一块物理内存空间-----将申请的虚拟空间转换为对应的物理空间
        addr = virt_to_phys(button_dev->virt_mem);    
    
        //2,将物理内存映射到虚拟空间---应用空间
        
        vma->vm_flags |= VM_IO;
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    
        if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
                        PAGE_SIZE, vma->vm_page_prot)) {
            printk(KERN_ERR "%s: io_remap_pfn_range failed
    ",__func__);
            return -EAGAIN;
        }
    
        return 0;
    }
    
    unsigned int button_poll(struct file *filp, struct poll_table_struct *pts)
    {
        unsigned int mask = 0;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
    
        //1,将等待队列头注册到系统中(VFS)
        poll_wait(filp,&button_dev->wq_head,pts);
    
        //2,如果产生按键中断-有数据可读,此时返回POLLIN,如果没有数据返回0
        if(button_dev->have_data)
            mask |= POLLIN;
    
        return mask;
        
    }
    
    int button_close(struct inode *inode, struct file *filp)
    {
        printk("--------^_^ %s------------
    ",__FUNCTION__);
        
        return 0;
    }
    
    
    static struct file_operations fops = {
        .open = button_open,
        .read = button_read,
        .write = button_write,
        .poll = button_poll,
        .mmap = button_mmap,
        .unlocked_ioctl = button_ioctl,
        .release = button_close,
    };
    
    
    //加载函数和卸载函数
    static int __init button_init(void)   //加载函数-----在驱动被加载时执行
    {
        int ret,i;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
        //0,实例化设备对象
        //参数1 ---- 要申请的空间的大小
        //参数2 ---- 申请的空间的标识
        button_dev = kzalloc(sizeof(struct s5pv210_button),GFP_KERNEL);
        if(IS_ERR(button_dev)){
            printk("kzalloc error!
    ");
            ret = PTR_ERR(button_dev);
            return -ENOMEM;
        }
        
        //1,申请设备号-----新方法
    #if 0
        //静态申请设备号
        button_dev->major = 256;
        ret = register_chrdev_region(MKDEV(button_dev->major,0),1,"button_drv");
        if(ret < 0){
            printk("register_chrdev_region error!
    ");
            ret =  -EINVAL;
            goto err_kfree;
        }
    #else
        //动态申请设备号
        ret = alloc_chrdev_region(&button_dev->devno,0,1,"button_drv");
        if(ret < 0){
            printk("register_chrdev_region error!
    ");
            ret =  -EINVAL;
            goto err_kfree;
        }
    #endif
    
        //创建cdev
    
        //申请cdev的空间
        button_dev->cdev = cdev_alloc();
        if(IS_ERR(button_dev->cdev)){        
            printk("button_dev->cdev error!
    ");
            ret = PTR_ERR(button_dev->cdev);
            goto err_unregister;
        }
    
        //初始化cdev的成员
        cdev_init(button_dev->cdev,&fops);
    
        //将cdev加入到内核中----链表
        ret = cdev_add(button_dev->cdev,button_dev->devno,1);
    
    
        
        //2,创建设备文件-----/dev/button
        button_dev->cls = class_create(THIS_MODULE,"button_cls");
        if(IS_ERR(button_dev->cls)){
            printk("class_create error!
    ");
            ret = PTR_ERR(button_dev->cls);
            goto err_cdev_del;
        }
        
        button_dev->dev = device_create(button_dev->cls,NULL,button_dev->devno,NULL,"button");
        if(IS_ERR(button_dev->dev)){
            printk("device_create error!
    ");
            ret = PTR_ERR(button_dev->dev);
            goto err_class;
        }
    
    
        //3,硬件初始化---申请中断
        for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
            ret = request_irq(buttons_set[i].irqno,button_irq_svc,buttons_set[i].flags,buttons_set[i].name,&buttons_set[i]);
            if(ret != 0){
                printk("request_irq error!
    ");
                ret = -EBUSY;
                goto err_device;
            }
        }
        //初始化等待队列头
        init_waitqueue_head(&button_dev->wq_head);
        
        //获取一块虚拟的内存空间---内核中
        button_dev->virt_mem = kzalloc(PAGE_SIZE,GFP_KERNEL);
        if(IS_ERR(button_dev->virt_mem)){
            printk("kzalloc error!
    ");
            ret = -EBUSY;
            goto err_free_irq;
        }
    
        return 0;
    err_free_irq:
        for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
            free_irq(buttons_set[i].irqno,&buttons_set[i]);
        }
    err_device:
        device_destroy(button_dev->cls,button_dev->devno);
    err_class:
        class_destroy(button_dev->cls);
        
    err_cdev_del:
        cdev_del(button_dev->cdev);
        
    err_unregister:
        unregister_chrdev_region(button_dev->devno,1);
        
    err_kfree:
        kfree(button_dev);
        return ret;
    
        
    }
    
    static void __exit button_exit(void)   //卸载函数-----在驱动被卸载时执行
    {
        int i;
        printk("--------^_^ %s------------
    ",__FUNCTION__);
        kfree(button_dev->virt_mem);    
        for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
            free_irq(buttons_set[i].irqno,&buttons_set[i]);
        }
        device_destroy(button_dev->cls,button_dev->devno);
        class_destroy(button_dev->cls);
        cdev_del(button_dev->cdev);
        unregister_chrdev_region(button_dev->devno,1);
        kfree(button_dev);
    }
    
    //声明和认证
    module_init(button_init);
    module_exit(button_exit);
    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;
    }

    #指定内核源码路径
    KERNEL_DIR = /home/farsight/s5pv210/kernel/linux-3.0.8
    CUR_DIR = $(shell pwd)
    MYAPP = test
    
    all:
        #让make进入内核源码编译,同时将当前目录中的c程序作为内核模块一起编译
        make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
        arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
    
    clean:
        #删除上面编译生成的文件
        make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
        rm -rf $(MYAPP)
    
    install:
        cp *.ko $(MYAPP) /opt/rootfs/drv_module
    
    #指定当前目录下哪个文件作为内核模块编
    obj-m = button_drv.o
    Stay hungry, stay foolish 待续。。。
  • 相关阅读:
    ZOJ2334 Monkey King 并查集 STL
    ZOJ2286 Sum of Divisors 筛选式打表
    ZOJ2105 终于找到错误
    ZOJ-2091-Mean of Subsequence (反证法的运用!!)
    【9929】潜水员
    【9928】混合背包
    【t077】宝物筛选
    【9927】庆功会
    【9926】完全背包
    【9925】0/1背包
  • 原文地址:https://www.cnblogs.com/panda-w/p/10991438.html
Copyright © 2020-2023  润新知