概述
转自:http://blog.csdn.net/lwj103862095/article/details/17484041
在上一节中,我们讲解了如何自动创建设备节点,并用“最笨”的方法实现点亮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 errorn");
- }
- /* 查询方式死循环地读 */
- 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 %dn",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
最后
以上就是无语冰淇淋为你收集整理的linux字符驱动之查询按键的全部内容,希望文章能够帮你解决linux字符驱动之查询按键所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复