代码示例_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------------\n",__FUNCTION__);

    //获取当前触发中断的按键信息
    p = (struct buttons *)dev;
    
    //获取产生中断的gpio口的值
    value = gpio_get_value(p->gpio);
    //判断是按下还是松开
    if(value){
        //松开
        printk("kernel:%s up!\n",p->name);
        button_dev->event.code = p->code;
        button_dev->event.value = 0;
    }else{
        //按下
        printk("kenel:%s pressed!\n",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------------\n",__FUNCTION__);
    
    return 0;
}
ssize_t button_read(struct file *filp , char __user *buf , size_t size, loff_t *flags)
{
    int ret;
    printk("--------^_^ %s------------\n",__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!\n");
        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------------\n",__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------------\n",__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!\n");
    }
    
    return 0;
}

int button_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned int addr;
    printk("--------^_^ %s------------\n",__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\n",__func__);
        return -EAGAIN;
    }

    return 0;
}

unsigned int button_poll(struct file *filp, struct poll_table_struct *pts)
{
    unsigned int mask = 0;
    printk("--------^_^ %s------------\n",__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------------\n",__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------------\n",__FUNCTION__);
    //0,实例化设备对象
    //参数1 ---- 要申请的空间的大小
    //参数2 ---- 申请的空间的标识
    button_dev = kzalloc(sizeof(struct s5pv210_button),GFP_KERNEL);
    if(IS_ERR(button_dev)){
        printk("kzalloc error!\n");
        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!\n");
        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!\n");
        ret =  -EINVAL;
        goto err_kfree;
    }
#endif

    //创建cdev

    //申请cdev的空间
    button_dev->cdev = cdev_alloc();
    if(IS_ERR(button_dev->cdev)){        
        printk("button_dev->cdev error!\n");
        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!\n");
        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!\n");
        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!\n");
            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!\n");
        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------------\n",__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\n",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("按下了上键!\n");
            else
                printf("松开了上键!\n");
            break;
            case KEY_DOWN:
            if(event.value)
                printf("按下了下键!\n");
            else
                printf("松开了下键!\n");
            break;
            case KEY_LEFT:
            if(event.value)
                printf("按下了左键!\n");
            else
                printf("松开了左键!\n");
            break;
            case KEY_RIGHT:
            if(event.value)
                printf("按下了右键!\n");
            else
                printf("松开了右键!\n");
            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

 

posted @ 2019-06-09 13:11  panda_w  阅读(1070)  评论(0编辑  收藏  举报