Linux character-driven query button

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 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325472606&siteId=291194637