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