Kernel driver - blocking driver

1. Define "waiting queue head"

    wait_queue_head_t key_q;

2. Initialize "waiting queue head"

    init_waitqueue_head(&key_q);

3. Wait for the event to happen

    wait_event(key_q, key_num);

4. Wake up and wait for an event

    wake_up(&key_q);

Query button status

key.c

#include <linux/module.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/sched.h>

#define	GPH0CON 0xE0200C00
#define	GPH0DAT 0xE0200C04
#define DEVICE_NAME	"tqkey"

#define	LEDCON 0xE0200060
#define LEDDAT 0xE0200064

volatile unsigned int *led_config;
volatile unsigned int *led_data;
volatile unsigned int *key_data;

unsigned int key_num;

//waiting queue
wait_queue_head_t key_q;

//1, define the work
struct work_struct *work;

//(Timer) 1. Define the timer structure
struct timer_list buttons_timer;

void work_func(struct work_struct *work)
{
	/*Start the timer*/ /*Delay 1s/10=100ms */
	mod_timer(&buttons_timer, jiffies + (HZ / 10));
}

//(timer) 5, function
static void buttons_timer_function(unsigned long data)
{
	unsigned int key_val;
	volatile unsigned short leddata;
	key_val = readw(key_data) & 0x03;  //GPH0_0 Key_1引角  //GPH0_0 Key_2引角
	if(key_val == 2) //The key is pressed to low level
	{
		leddata = 0xFF; // light up the LED
		key_num = 1;
	}
	else if(key_val == 1) //The key is pressed to low level
	{
		leddata = 0x00; // turn off the LED
		key_num = 2;
	}
	writel(leddata, led_data);

	// wake up the waiting queue
	wake_up(&key_q);
}

void timer_init(void)
{
	//(timer) 2, initialization
	init_timer(&buttons_timer);
	buttons_timer.function = &buttons_timer_function;

	//(Timer) 3. Register the timer with the kernel
	add_timer(&buttons_timer);
}

static irqreturn_t key_int(int irq, void *dev_id)
{
	//3. Submit the second half to the kernel default work queue keventd_wq
	schedule_work(work);
	return 0;
}

void key_hw_init(void)
{
	volatile unsigned short data;
	volatile unsigned int *gpio_config;
	gpio_config = (volatile unsigned int *)ioremap(GPH0CON, 4);
	data = readw(gpio_config); //Read the value in the original register
	data &= ~0xFF;
	data |= 0xFF;
	writew(data, gpio_config);
	printk("key_hw_init!\n");

	
	led_config = (volatile unsigned int *)ioremap(LEDCON, 4); //Convert physical address to virtual address
	writel(0x00011000, led_config);

	led_data = (volatile unsigned int *)ioremap(LEDDAT, 4);
	writel(0xFF, led_data);

	key_data = (volatile unsigned int *)ioremap(GPH0DAT, 4);
}

/*static long key_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	return -EINVAL;
}*/

static int key_open(struct inode *inode, struct file *file)
{
	return 0;
}

static int key_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	// enter the waiting queue to sleep
	wait_event(key_q, key_num); /*When key_num is true, that is, wake up when the key is pressed*/
	
	if (copy_to_user(buf, &key_num, 4))
	{
		key_num = 0;
		return 4;
	}
	else
	{
		key_num = 0;
		return 0;
	}
}


static struct file_operations key_fops =
{
	.owner = THIS_MODULE,
	//.unlocked_ioctl = key_ioctl,
	.open = key_open,
	.read = key_read,
	.release = NULL,
};

struct miscdevice key_miscdev =
{
	.minor = 200,
	.name = DEVICE_NAME,
	.fops = &key_fops,
};

//register function
static int __init button_init(void)
{
	int ret = 0;

	misc_register(&key_miscdev);

	//register interrupt handler
	ret = request_irq(IRQ_EINT0, key_int, IRQF_TRIGGER_FALLING, DEVICE_NAME, 0);
	ret = request_irq(IRQ_EINT1, key_int, IRQF_TRIGGER_FALLING, DEVICE_NAME, 0);

	//hardware initialization
	key_hw_init();

	//2, work initialization
	work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
	//(Note: GFP_KERNEL is the most commonly used in kernel memory allocation, it can cause hibernation when no memory is available)
	INIT_WORK(work, work_func); //Create work, associate work function

	// Kernel timer initialization
	timer_init();

	//Initialize the waiting queue
	init_waitqueue_head(&key_q);
	
	return 0;
}

//logout function
static void __exit button_exit(void)
{
	misc_deregister(&key_miscdev);

	// log out of the interrupt program
	free_irq(IRQ_EINT0, 0);
}

module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry.Gou");
MODULE_DESCRIPTION("TQ210 button driver");
key_app.c
/*********************************************
*File name :key_app.c
*Author    :JerryGou
*Date      :2017/10/23
*Function: Through the read function, get the key pressed in the kernel
*********************************************/
  
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

intmain()
{
	int fd;
	int buf = 0;
	fd = open("/dev/tqkey", 0);

	if (fd < 0)
		printf("open fail\n");

	read(fd, &buf, 4);
	printf("num is %d\n", buf);
	close(fd);
	return 0;
}


Guess you like

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