Wei Dongshan Phase 2 Driver Encyclopedia-002_Input Subsystem_Lesson 13 Section 2 Input Subsystem Driver Writing

1. How to write the driver?

First explain the driver framework, and then write the first_drv driver to print some information

Writing the first_drv driver requires the following steps:

(1) Write the driver first_drv_open first_drv_write

(2) Need to define the file_operations structure to encapsulate the driver function first_drv_open first_drv_write

For character devices, the following members of file_operations are commonly used:
Insert picture description here
(3) Module loading function, through the function register_chrdev(major, “first_drv”, &first_drv_fops) to register the character device

(4) Write the driver's first_drv_init entry function to call the register_chrdev() registration function,

(5) Modify the entry function through module_init() so that the kernel knows that there is this function

(6) Write the driver's first_drv_exit exit function, call this unregister_chrdev() function to uninstall,

(7) Modify the exit function through module_exit()

(8) Module license statement, the most common statement is MODULE_LICENSE ("GPL v2")

Two writing driver code

  1. head File
/* 参考drivers\input\keyboard\gpio_keys.c */

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>

#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
  1. Define input_dev structure
struct input_dev {
    
          

       void *private;
       const char *name;  //设备名字
       const char *phys;  //文件路径,比如 input/buttons
       const char *uniq;   
       struct input_id id;

 
       unsigned long evbit[NBITS(EV_MAX)];  //表示支持哪类事件,常用有以下几种事件(可以多选)
       //EV_SYN      同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
       //EV_KEY       键盘事件
       //EV_REL       (relative)相对坐标事件,比如鼠标
       //EV_ABS       (absolute)绝对坐标事件,比如摇杆、触摸屏感应

       unsigned long keybit[NBITS(KEY_MAX)];   //存放支持的键盘按键值
                                    //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)

       unsigned long relbit[NBITS(REL_MAX)];    //存放支持的相对坐标值
       unsigned long absbit[NBITS(ABS_MAX)];   //存放支持的绝对坐标值
       unsigned long mscbit[NBITS(MSC_MAX)];   //存放支持的其它事件,也就是功能
       unsigned long ledbit[NBITS(LED_MAX)];    //存放支持的各种状态LED
       unsigned long sndbit[NBITS(SND_MAX)];    //存放支持的各种声音
       unsigned long ffbit[NBITS(FF_MAX)];       //存放支持的受力设备
       unsigned long swbit[NBITS(SW_MAX)];     //存放支持的开关功能

 ... ...
  1. buttons_init function implementation
static struct input_dev *buttons_dev;

static int buttons_init(void)
{
    
    
	int i;
	
	/* 1. 分配一个input_dev结构体 */
	buttons_dev = input_allocate_device();

	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, buttons_dev->evbit);//按键类事件
	set_bit(EV_REP, buttons_dev->evbit);//重复类事件
	
	/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
	set_bit(KEY_L, buttons_dev->keybit);
	set_bit(KEY_S, buttons_dev->keybit);
	set_bit(KEY_ENTER, buttons_dev->keybit);
	set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

	/* 3. 注册 */
	input_register_device(buttons_dev);
	
	/* 4. 硬件相关的操作 */
	
	/*4.2 定时器初始化*/
	init_timer(&buttons_timer);//防抖动的
	buttons_timer.function = buttons_timer_function;//定义定时器服务函数
	add_timer(&buttons_timer);
	
	/*4.2 注册4个中断*/
	for (i = 0; i < 4; i++)
	{
    
    
		request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name,&pins_desc[i]);
	}
	
	return 0;
}

Analyze: When you call input_register_devicetimedev和handler两者是否匹配?

Corresponding buttons_exitfunction implementation

static void buttons_exit(void)
{
    
    
	int i;
	for (i = 0; i < 4; i++)
	{
    
    
		free_irq(pins_desc[i].irq, &pins_desc[i]);//对应request_irq
	}
	del_timer(&buttons_timer);//对应add_timer
	input_unregister_device(buttons_dev);//对应input_register_device
	
	input_free_device(buttons_dev);	//对应input_allocate_device
}
  1. Build a structure with four buttons
struct pin_desc{
    
    
	int irq;//中断号
	char *name;//名字
	unsigned int pin;//引脚号
	unsigned int key_val;//键值
};

struct pin_desc pins_desc[4] = {
    
    
	{
    
    IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},//S2按键对应L..
	{
    
    IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
	{
    
    IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
	{
    
    IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
};
  1. Interrupt service routine
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;//定时器相关结构体

/*按键中断服务函数*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    
    
	/* 10ms后启动定时器 */
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies+HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

/*定时器中断服务函数*/
static void buttons_timer_function(unsigned long data)
{
    
    
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;

	if (!pindesc)
		return;
	
	pinval = s3c2410_gpio_getpin(pindesc->pin);

	if (pinval)
	{
    
    
		/*
			struct input_dev *dev, 设备
			unsigned int type, 表示支持哪类事件,键盘事件
			unsigned int code, 表示事件编码,KEY_L,KEY_S,KEY_ENTER,KEY_LEFTSHIFT
			int value,松开 : 最后一个参数: 0-松开, 1-按下 自己定义即可
		*/
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
		/*
		*/
		input_sync(buttons_dev);
	}
	else
	{
    
    
		/* 按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(buttons_dev);
	}
}

Work flow:
initialization complete ->
key press ->
trigger interrupt program buttons_irq->
modify timer, trigger timer interrupt after 10ms ->
trigger timer interrupt program buttons_timer_function->
read pin value: press/ release- >
If pressed: report event input_event

4. Analyze the process behind the reported incident

  1. input_event function: (analyzed in the previous article)
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
    
    
	struct input_handle *handle;
	...
	/* 通过input_dev ->h_list链表找到input_handle驱动处理结构体*/
	list_for_each_entry(handle, &dev->h_list, d_node);
	if (handle->open)
		handle->handler->event(handle, type, code, value);
}
  1. Compared with the previous we need to construct the complete structure of the driver by ourselves, now the input subsystem has been constructed to complete file_operations and related functions. We only need to complete the hardware part!
static const struct file_operations evdev_fops = {
    
    
	.owner =	THIS_MODULE,
	.read =		evdev_read,
	.write =	evdev_write,
	.poll =		evdev_poll,
	.open =		evdev_open,
	.release =	evdev_release,
	.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl =	evdev_ioctl_compat,
#endif
	.fasync =	evdev_fasync,
	.flush =	evdev_flush
};

5. Test

  1. Modify the makefile:
  2. Load the driver:insmod buttons.ko
  3. ls -l /dev/event*
    Insert picture description here
    Major device number: 13; minor device number: 65; name: event1
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
    
    
	evdev->exist = 1;
	evdev->minor = minor;
	evdev->handle.dev = dev;
	evdev->handle.name = evdev->name;
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;
	sprintf(evdev->name, "event%d", minor);//设备名 event1

	evdev_table[minor] = evdev;

	//EVDEV_MINOR_BASE=64 次设备号 64+1=65
	//INPUT_MAJOR=13 主设备号13
	devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
	cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);
}	
  1. Test command-method 1 hexdump /dev/event1:;

Function: 1.open(/dev/event1); 2.read(); 3. Display the data read inside in hexadecimal

Line number------------seconds-----------microseconds-------class--
code --value 0000000-0bb2 0000-0e48 000c-0001- 0026-0001 0000
0000010-0bb2 0000-0e54 000c-0000-0000-0000 0000
0000020-0bb2 0000-5815 000e-0001-0026-0000 0000
0000030-0bb2 0000-581f 000e-0000-0000-0000 0000

static void buttons_timer_function(unsigned long data)
{
    
    
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;

	if (!pindesc)
		return;
	
	pinval = s3c2410_gpio_getpin(pindesc->pin);

	if (pinval)
	{
    
    
		/* 松开 : 最后一个参数: 0-松开, 1-按下 */
		/*
			type=EV_KEY=0x01;
			code=pindesc->key_val=38/31/28/42=0x26/0x...;
			value=0;
		*/
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(buttons_dev);
	}
	else
	{
    
    
		/* 按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(buttons_dev);
	}
}
  1. Test command-method 2

If QT is not started:

cat /dev/tty1 Press: s2, s3, s4 to get ls
or:
exec 0</dev/tty1

About exec 0</dev/tty1explained as follows:
on the standard error input and output:

# ls -l /proc/770/fd #770表示shell
lrwx------    1 0        0              64 Jan  1 00:03 0 -> /dev/s3c2410_serial0 #标准输入是串口
lrwx------    1 0        0              64 Jan  1 00:03 1 -> /dev/s3c2410_serial0 #标准输出是串口
lrwx------    1 0        0              64 Jan  1 00:03 10 -> /dev/tty
lrwx------    1 0        0              64 Jan  1 00:03 2 -> /dev/s3c2410_serial0 #标准错误是串口

exec 0</dev/tty1: Represents the input of /dev/tty1, that is, the input on the keyboard as standard input. It used to be input from the serial port, now it is input from the keyboard.

If QT has been started:

You can click on the notepad and press: s2, s3, s4

Why cat /dev/tty1can't the expected effect be achieved by using the command after starting QT ?
Because at this time, the above instructions are executed through the tty_io.c->keyboard.c program. keyboard.c also belongs to the input subsystem framework, not from buttons.c.
See "Wei Dongshan Phase 2 Driver Encyclopedia-002_Input Subsystem_Lesson 13, Section 2 keyboard.xmind"
Insert picture description here

  1. Test command-Method 3-Means exec 0</dev/tty1that the input of /dev/tty1, that is, the input on the keyboard, is used as standard input. The input
    was obtained from the serial port before, but now it is input from the keyboard.

  2. How to achieve repeated input when pressing the button without releasing it?

static int buttons_init(void)
{
    
    
	...
	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, buttons_dev->evbit);//按键类事件
	set_bit(EV_REP, buttons_dev->evbit);//重复类事件
	...
}

At this time, when the keyboard is continuously pressed, input_event() is called.
See "Wei Dongshan Phase 2 Driver Encyclopedia-002_Input Subsystem_Lesson 13, Section 2 Keyboard Repeat Input"

Guess you like

Origin blog.csdn.net/xiaoaojianghu09/article/details/104756084