在上一节中,我们讲解了如何自动创建设备节点,并用“最笨”的方法实现点亮LED。

上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17472455 

这一节里,我们基于上一节的基础上,稍微改动一下,来实现一个查询方式的按键驱动。


问:既然是基于上一节的基础,只是稍微改动,改动了哪些?

答:框架是不变的,还是字符设备框架,硬件操作有稍微变动,上一节里,LED的GPIO设置为输出方式,这一节里,KEY的GPIO设置为输入方式;上一节里,LED驱动的核心函数实现了led_open,led_write,这一节里,KEY驱动的核心函数实现了key_open,key_read;最大不同点在于write函数和read函数,其他没什么不一样。


问:内核如何将数据传递给应用空间的程序?

答:上一节已经讲过了,使用copy_to_user函数。


详细请参考驱动源码:

#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> 	//class_create

static struct class *seconddrv_class;
static struct device *seconddrv_device;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

int major;
static int second_drv_open(struct inode * inode, struct file * filp)
{
	/*  K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
  	 *  配置GPF1、GPF4、GPF2、GPF0为输入引脚
	 */
	 *gpfcon &= ~((0x3 << (1*2)) | (0x3 << (4*2)) | (0x3 << (2*2)) | (0x3 << (0*2)));
	return 0;
}

static ssize_t second_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
	unsigned char key_vals[4];
	unsigned long val;  	//用于接收按键值

	if (size != sizeof(key_vals))
			return -EINVAL;

	/*  K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
  	 *  读GPF1、GPF4、GPF2、GPF0引脚值
	 */
	val = *gpfdat;
	key_vals[0] = (val & (1<<1)) ? 1 : 0;
	key_vals[1] = (val & (1<<4)) ? 1 : 0;
	key_vals[2] = (val & (1<<2)) ? 1 : 0;
	key_vals[3] = (val & (1<<0)) ? 1 : 0;

	/* 读出值后,将数据传给应用程序 */
	copy_to_user(user, key_vals, sizeof(key_vals));

	return sizeof(key_vals);
	
}
/* File operations struct for character device */
static const struct file_operations second_drv_fops = {
	.owner		= THIS_MODULE,
	.open		= second_drv_open,
	.read		= second_drv_read,
};


/* 驱动入口函数 */
static int second_drv_init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, "second_drv", &second_drv_fops);

	/* 创建seconddrv类 */
	seconddrv_class = class_create(THIS_MODULE, "seconddrv");

	/* 在seconddrv类下创建buttons设备,供应用程序打开设备*/
	seconddrv_device = device_create(seconddrv_class, NULL, MKDEV(major, 0), NULL, "buttons");

	/* 将物理地址映射为虚拟地址 */
	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;
	
	return 0;
}

/* 驱动出口函数 */
static void second_drv_exit(void)
{
	unregister_chrdev(major, "second_drv");
	device_unregister(seconddrv_device);  //卸载类下的设备
	class_destroy(seconddrv_class);		//卸载类
	iounmap(gpfcon);					//解除映射
}

module_init(second_drv_init);  //用于修饰入口函数
module_exit(second_drv_exit);  //用于修饰出口函数	

MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL");  //遵循GPL协议


应用测试程序源码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


/* second_test
 */ 
int main(int argc ,char *argv[])

{
	int fd;
	unsigned char key_vals[4];
	int cnt = 0;  //养成好习惯,用于计数时,一般初始化为0
	
	fd = open("/dev/buttons",O_RDWR);
	if (fd < 0)
	{
		printf("open error\n");
	}

	/* 查询方式死循环地读 */
	while(1)
	{
		read(fd,key_vals,sizeof(key_vals));
		if(!key_vals[0] || !key_vals[1] || (!key_vals[2]) || (!key_vals[3]))
		{
			printf("%04d key pressed: %d %d %d %d\n",cnt++,key_vals[0],key_vals[1],key_vals[2],key_vals[3]);
		}
	}
	return 0;
}


测试步骤1:

[WJ2440]# ls
Qt             etc            mnt            second_drv.ko  var
TQLedtest      first_drv.ko   opt            second_test    web
app_test       first_test     proc           sys
bin            home           root           tmp
dev            lib            sbin           udisk
driver_test    linuxrc        sddisk         usr
[WJ2440]# ls /dev/buttons -l
ls: /dev/buttons: No such file or directory
[WJ2440]# insmod second_drv.ko 
[WJ2440]# lsmod 
second_drv 2184 0 - Live 0xbf009000
[WJ2440]# ls /dev/buttons -l
crw-rw----    1 root     root      252,   0 Jan  2 01:52 /dev/buttons
[WJ2440]# ls sys/class/seconddrv/ -l
lrwxrwxrwx    1 root     root             0 Jan  2 01:52 buttons -> ../../devices/virtual/seconddrv/buttons
[WJ2440]# ./second_test 
0000 key pressed: 0 1 1 1
0001 key pressed: 0 1 1 1
0002 key pressed: 0 1 1 1
0003 key pressed: 0 1 1 1
0004 key pressed: 0 1 1 1
....
0305 key pressed: 1 0 1 1
0306 key pressed: 1 0 1 1
0307 key pressed: 1 0 1 1
0308 key pressed: 1 0 1 1
0309 key pressed: 1 0 1 1
....
0460 key pressed: 1 1 0 1
0461 key pressed: 1 1 0 1
0462 key pressed: 1 1 0 1
0463 key pressed: 1 1 0 1
0464 key pressed: 1 1 0 1
....
0615 key pressed: 1 1 1 0
0616 key pressed: 1 1 1 0
0617 key pressed: 1 1 1 0
0618 key pressed: 1 1 1 0
0619 key pressed: 1 1 1 0

测试步骤2:

[WJ2440]# ./second_test  &
[WJ2440]# top
Mem: 9988K used, 50176K free, 0K shrd, 0K buff, 7168K cached
CPU: 14.9% usr 84.8% sys  0.0% nic  0.0% idle  0.0% io  0.0% irq  0.1% sirq
Load average: 0.71 0.22 0.07 2/23 603
  PID  PPID USER     STAT   VSZ %MEM CPU %CPU COMMAND
  602   592 root     R     1432  2.3   0 99.0 ./second_test
  603   592 root     R     2092  3.4   0  0.7 top
  592     1 root     S     2092  3.4   0  0.0 -/bin/sh
    1     0 root     S     2088  3.4   0  0.0 init
  589     1 root     S     2088  3.4   0  0.0 /usr/sbin/telnetd -l /bin/login
  587     1 root     S     1508  2.5   0  0.0 EmbedSky_wdg
  573     2 root     SW<      0  0.0   0  0.0 [rpciod/0]
    5     2 root     SW<      0  0.0   0  0.0 [khelper]
  329     2 root     SW<      0  0.0   0  0.0 [nfsiod]
    2     0 root     SW<      0  0.0   0  0.0 [kthreadd]
    3     2 root     SW<      0  0.0   0  0.0 [ksoftirqd/0]
    4     2 root     SW<      0  0.0   0  0.0 [events/0]
   11     2 root     SW<      0  0.0   0  0.0 [async/mgr]
  237     2 root     SW<      0  0.0   0  0.0 [kblockd/0]
  247     2 root     SW<      0  0.0   0  0.0 [khubd]
  254     2 root     SW<      0  0.0   0  0.0 [kmmcd]
  278     2 root     SW       0  0.0   0  0.0 [pdflush]
  279     2 root     SW       0  0.0   0  0.0 [pdflush]
  280     2 root     SW<      0  0.0   0  0.0 [kswapd0]
  325     2 root     SW<      0  0.0   0  0.0 [aio/0]

由测试步骤2可知,second_test进程在后台运行时,占用了将近99%的CPU利用率,显然,这种查询式驱动是不合理的,必将被取代。

上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17472455 



Logo

更多推荐