虽然好几个月之前就对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;
}