In the previous section, we explained how to automatically create device nodes and implement lighting LEDs in the "stupidest" way possible.
Link to the article in the previous section: http://blog.csdn.net/xiaoxiaopengbo/article/details/78779897
In this section, we make a slight change based on the previous section to implement a key-driven query method.
Q: Since it is based on the foundation of the previous section, it has only been slightly modified. What have been changed?
Answer: The frame is the same, or the character device frame, and the hardware operation is slightly changed. In the previous section, the GPIO of the LED is set as the output mode. In this section, the GPIO of the KEY is set as the input mode; in the previous section, The core function of LED driver implements led_open and led_write. In this section, the core function of KEY driver implements key_open and key_read. The biggest difference lies in the write function and the read function, and nothing else is different.
Q: How does the kernel pass data to applications in the application space?
Answer: As mentioned in the previous section, use the copy_to_user function.
For details, please refer to the driver source code:
#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 * Configure GPF1, GPF4, GPF2, GPF0 as input pins */ *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; // used to receive key value if (size != sizeof(key_vals)) return -EINVAL; /* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0 * Read GPF1, GPF4, GPF2, GPF0 pin values */ 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; /* After reading the value, pass the data to the application */ 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, }; /* Driver entry function */ static int second_drv_init(void) { /* If the main device number is set to 0, the system will automatically assign the main device number */ major = register_chrdev(0, "second_drv", &second_drv_fops); /* Create seconddrv class */ seconddrv_class = class_create(THIS_MODULE, "seconddrv"); /* Create the buttons device under the seconddrv class for the application to open the device */ seconddrv_device = device_create(seconddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* Map physical addresses to virtual addresses */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; return 0; } /* driver exit function */ static void second_drv_exit(void) { unregister_chrdev(major, "second_drv"); device_unregister(seconddrv_device); //Uninstall the device under the class class_destroy(seconddrv_class); //Uninstall the class iounmap(gpfcon); //Unmap } module_init(second_drv_init); //Used to decorate the entry function module_exit(second_drv_exit); //Used to decorate the exit function MODULE_AUTHOR ("LWJ"); MODULE_DESCRIPTION("Just for Demon"); MODULE_LICENSE("GPL"); //Follow GPL agreement
Application source code
#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; // Develop a good habit, when used for counting, it is generally initialized to 0 fd = open("/dev/buttons",O_RDWR); if (fd < 0) { printf("open error\n"); } /* Query mode reads in an infinite loop */ 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; }
test step 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
test step 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]
It can be seen from test step 2 that when the second_test process runs in the background, it occupies nearly 99% of the CPU utilization. Obviously, this query-driven drive is unreasonable and will be replaced.
Reprinted from: http://blog.csdn.net/lwj103862095/article/details/17484041