//头文件 #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