虽然好几个月之前就对linux下的阻塞,非阻塞,select poll的实现,工作队列,tasklet等等做了较为深入的分析,但是在遇到实际的硬件驱动中,才真正去思考怎么将这些用到实际中,构建一个稳定高效的驱动。
板子上有四个按键,linux内核中有input子系统来很好的完成这个事情。先按照自己的思路一步步来,最后看下人家input子系统是如何实现的,肯定有不少值得借鉴的。
其实按键驱动的查询方式没什么实际价值,因为一直处于查询过程中,进程没有睡眠,占用cpu非常高,让其他应用程序没法很好的利用cpu。先看看驱动使用后的cpu状况:
top -d 5
看到这个cpu利用率,就可以明白这个驱动没什么用了,但是没关系,后面一步步用开头提到的方式来打造一个可以用的驱动程序。代码比较糟糕,还是贴一下,与将来的好代码做个对比:
驱动代码:
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/seq_file.h> #include <linux/cdev.h> #include <linux/ioport.h> #include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_*_user */ #include <asm/io.h> #define KEY_NUM 4 #define GPFCON 0x56000050 #define GPFDAT 0x56000054 #define GPFUP 0x56000058 struct key_dev { struct cdev dev; void __iomem *base; unsigned long offset; }; struct key_dev Key[4]; dev_t dev = 0; void __iomem *con; int key_open(struct inode *inode, struct file *filp) { struct key_dev *key; /* device information */ key = container_of(inode->i_cdev, struct key_dev, dev); filp->private_data = key; /* for other methods */ return 0; /* success */ } int key_release(struct inode *inode, struct file *filp) { return 0; } ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { char data; struct key_dev *key; u32 value; //printk(KERN_INFO "debug by baikal: key dev read\n"); key = (struct key_dev *)filp->private_data; value = ioread32(key->base); if(value & 1<<key->offset) data = '0'; else data = '1'; copy_to_user(buf,&data,count); return 0; } ssize_t key_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { return 0; } struct file_operations key_fops = { .owner = THIS_MODULE, .read = key_read, .write = key_write, //.ioctl = key_ioctl, .open = key_open, .release = key_release, }; static int key_init(void) { int result, i; u32 value; request_mem_region(GPFCON,0x4,"key"); con = ioremap(GPFCON,0x4); value = ioread32(con); iowrite32(value & 0xfcc0 ,con); //配置GPF0,1,2,4为input result = alloc_chrdev_region(&dev, 0, KEY_NUM,"key"); if (result < 0) { printk(KERN_WARNING "key: can't get major %d\n", MAJOR(dev)); return result; } for(i = 0; i < KEY_NUM; i++) { cdev_init( &Key[i].dev, &key_fops); request_mem_region(GPFDAT,0x4,"key"); Key[i].base = ioremap(GPFDAT,0x4); if(i < 3) Key[i].offset = i; else Key[i].offset = i+1; Key[i].dev.owner = THIS_MODULE; Key[i].dev.ops = &key_fops; result = cdev_add(&Key[i].dev,MKDEV(MAJOR(dev),i),1); if(result < 0) { printk(KERN_ERR "KEY: can't add key%d\n",i); return result; } } return 0; } static void key_exit(void) { int i; release_mem_region(GPFCON,0x4); iounmap(con); release_mem_region(GPFDAT,0x4); for( i = 0; i < KEY_NUM; i++) { iounmap(Key[i].base); cdev_del(&Key[i].dev); } unregister_chrdev_region(dev, KEY_NUM); } module_init(key_init); module_exit(key_exit); MODULE_AUTHOR("Baikal"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Simple Keyboard Driver");
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> int main(int argc, char **argv) { int fd[4]; char data; if(argc != 1) { printf("usage : ./key\n"); return -1; } fd[0] = open("/dev/key0",O_RDWR); fd[1] = open("/dev/key1",O_RDWR); fd[2] = open("/dev/key2",O_RDWR); fd[3] = open("/dev/key4",O_RDWR); if(fd[0] < 0) { perror("open key[0]"); return -1; } if(fd[1] < 0) { perror("open key[1]"); return -1; } if(fd[2] < 0) { perror("open key[2]"); return -1; } if(fd[3] < 0) { perror("open key[4]"); return -1; } while(1) { read(fd[0], &data, 1); //printf("data:%d\n",data); if(data == '1') printf("key 0 pressed\n"); else ; //printf("key 0 not pressed\n"); read(fd[1], &data, 1); if(data == '1') printf("key 1 pressed\n"); read(fd[2], &data, 1); if(data == '1') printf("key 2 pressed\n"); read(fd[3], &data, 1); if(data == '1') printf("key 4 pressed\n"); } return 0; }