上一章写了一篇LED驱动,里面填充了file_operations结构体的.write .open .ioctl函数,现在为了继续填充这个结构体部分,填充下.read函数。所以想到了按键属于输入设备 需要用.read函数。
整个框架和上一篇的一样,只是加了一个.read函数,这里先采用查询法来实现。在下一篇时采用中断法,不多说,上代码。
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
/*#define definition*/
#define DEVICE_NAME "button_drv" //设备名称 和文件系统里设备节点名称区分开
//#define BUTTON_MAJOR 0 //主设备号
static unsigned long led_table[] =
{
S3C2410_GPF0,
S3C2410_GPF1,
S3C2410_GPF2,
S3C2410_GPF4,
};
static unsigned int led_cfg_table[] =
{
S3C2410_GPF0_INP,
S3C2410_GPF1_INP,
S3C2410_GPF2_INP,
S3C2410_GPF4_INP ,
};
//.open func 打开设备时 将四个按键置于输入状态
static int button_drv_open(struct inode *inode, struct file *filp)
{
int i;
for(i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
}
printk("button_drv_open\n");
/*配置GPIF0 1 2 4 为输入*/
return 0;
}
//系统.read函数,read要做的就是返回数据到内核空间,这里是按键,那么肯定返回按键状态到用户空间
ssize_t button_read_drv(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
//int regval; //接收GPIO的数据
unsigned char key_vals[4]; //保存要传递给用户空间的4个按键口
if(size != sizeof(key_vals)) //size为用户空间返回的字长度
{
return -EINVAL;
}
key_vals[0] = s3c2410_gpio_getpin(S3C2410_GPF0) & (1 << 0) ? 1:0;
key_vals[1] = s3c2410_gpio_getpin(S3C2410_GPF1) & (1 << 1) ? 1:0;
key_vals[2] = s3c2410_gpio_getpin(S3C2410_GPF2) & (1 << 2) ? 1:0;
key_vals[3] = s3c2410_gpio_getpin(S3C2410_GPF4) & (1 << 4) ? 1:0;
copy_to_user(buf, key_vals, sizeof(key_vals)); //返回key_vals数组数据到用户空间
return (sizeof(key_vals));
}
//define the file_operations
static struct file_operations button_drv_fops = {
.owner = THIS_MODULE,
.open = button_drv_open,
.read = button_read_drv,
};
static struct class *button_drv_class; //定义一个类 方便在/dev目录下建立一个节点
int major; //自动分配主设备号
int button_drv_init(void)
{
//int ret;
//ret = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);
major = register_chrdev(0, DEVICE_NAME, &button_drv_fops);
//if(ret < 0)//
if(major < 0)
{
printk("button_drv can not register major number!\n");
return major;
}
/*注册一个类 使得mdev可以在/dev目录下自动建立设备节点 不需要手动mknod dev c 224 0这样的操作*/
button_drv_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(button_drv_class))
{
printk("error, fail to init button_drv_class");
return -1;
}
/*如果上面成功注册进mdev的话 下面自动在/dev目录下创建一个设备节点*/
device_create(button_drv_class, NULL, MKDEV(major, 0), NULL, "BUTTON_TEST"); //这个就是在/dev下创建的BUTTON_TEST 设备节点了 应用程序调用
printk("BUTTON_DRV initialized! \n");
return 0;
}
void button_drv_exit(void)
{
unregister_chrdev(major, DEVICE_NAME); //卸载驱动
device_destroy(button_drv_class, MKDEV(major, 0)); //删掉设备节点
class_destroy(button_drv_class); //注销类
printk("rm -rf BUTTON_DRV! \n");
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL"); //获得授权
/*以后的makefile文件都不用贴出来了,因为和上一篇的都一样 只是函数名改了下而已*/
/*用户测试程序*/
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
static int fd; //用于存放打开设备时返回的文件句柄
unsigned char key_vals[4];
int cnt = 0;
int main(int argc, char **argv)
{
fd = open("/dev/BUTTON_TEST", O_RDWR); //打开设备 并读写权限
if(fd < 0)
{
perror("open device failed");
exit(1);
}
while(1)
{
read(fd, key_vals, sizeof(key_vals)); //执行读函数,将内核数据交互到用户空间
if(!key_vals[0] || !key_vals[1] || !key_vals[2] || !key_vals[3])
{
printf("%d key pressed %d %d %d %d \n", cnt++, key_vals[0] , key_vals[1], key_vals[2], key_vals[3]);
}
}
return 0;
}
我们将测试程序和驱动拷到文件系统 执行 发现每按下一个按键 相应的数据显示为0
但是查询法最大的弊端就是:这个应用程序将一直占用cpu资源 我们将程序运行于后台./button_test & 而用采用top命令查看 这个进程占用cpu资源90%以上 在top下按q退出监视状态,所以下一篇我们就开始着手基于中断法的按键驱动了