input subsystem driver writing (key)

Posted by iceraider on Sun, 16 Jan 2022 13:57:33 +0100

We have analyzed and written a driver before, mainly including the following steps:

  1. The main equipment number of the driving equipment is set by yourself or automatically assigned by the system;
  2. Write equipment operation functions (drv_open, drv_read, drv_write, drv_close, etc.);
  3. Construct file_ The operations structure assigns the operation function written in the previous step to the operation function in the structure open,. read,. write,. poll,. fasync et al
  4. Register the driver and call register_chrdev(major, name, fops);
  5. Write entry and exit functions.

However, the input subsystem driver framework separates the above steps. It is composed of device layer, core layer and event layer. The core layer provides some functions common to the device layer and the event layer, such as registration function, anti registration function, event arrival processing function, etc., and completes steps 1-4 of driver programming. But in the input frame file_operations contains only one open function, which calls a specific input_handler structure, which contains * * mapped to different input device categories according to different secondary device numbers fops**; In fact, the event processing layer has helped us write a lot of related events in the Linux kernel; The device layer is the program related to the specific device we want to add to the input system.

When analyzing the input subsystem framework, we already know that different types of input devices have abstracted different handlers for processing, and the device layer realizes pure hardware operation. We can design the device layer according to the functions of the driver. The main content is to call input when detecting effective input transmission_ The event function reports the result to the handler layer.

1. Steps for writing drivers that conform to the input subsystem framework

  1. Assign an input_ Dev structure, call input_allocate_device() or devm_input_allocate_device(struct input_dev *);
  2. Write a function and set input_dev structure, select the event type of the input device (call the set_bit() function to implement);
  3. Register input_ Dev structure, call input_register_device(struct input_dev *) function implementation;
  4. Write hardware related codes: register interrupt processing functions. For example, keyboard devices need to write key lifting and lowering, touch screen devices need to write pressing, lifting and absolute movement, mouse devices need to write clicking, lifting and relative movement, and submit hardware data (key value / coordinate / status, etc.) when necessary, that is, report input events.
  5. After submitting the input event generated by the input device, you need to call void input_ The sync (struct input_dev * DEV) function notifies the input subsystem to handle the complete event generated by the device.

Event types supported by Linux input subsystem: (in include/linux/input.h)

EV_SYN     0x00    Synchronization event
EV_KEY     0x01    Key event
EV_REL     0x02    Relative coordinates(For example, when the mouse moves, the offset from the last position is reported)
EV_ABS     0x03    Absolute coordinates(For example, the touch screen or joystick reports the absolute coordinate position)
EV_MSC     0x04    other
EV_SW      0x05    switch
EV_LED     0x11    Key/Equipment lamp
EV_SND     0x12    voice/alert
EV_REP     0x14    repeat
EV_FF      0x15    Force feedback
EV_PWR    0x16    Power Supply
EV_FF_STATUS    0x17   Force feedback state
EV_MAX    0x1f    Maximum number of event types and bit mask support

The function of device driver layer reporting input events provided by Linux input subsystem: (in include/linux/input.h)

void input_event(struct input_dev* dev, unsigned int type, unsigned int code, int value); //Report the input event of the specified type and code

void input_report_key(struct input_dev *dev, unsigned int code, int value);	//Report key events

void input_report_rel(struct input_dev *dev, unsigned int code, int value);	//Report relative coordinate events

void input_report_abs(struct input_dev *dev, unsigned int code, int value);	//Report absolute coordinate events

2. Write the key driver according to the input system framework

Now we begin to use the input subsystem framework of the kernel, integrate our own drivers into it, and write more general key drivers. Here, the four keys on the JZ2440 development board are used as the keys of the input subsystem. The driver we write maps the four keys on the development board to different key values on the keyboard, representing the letters L, S and Enter on the keyboard. These values are in include \ Linux \ input Defined in H.

2.1 write entry function and exit function and build a good framework

/* Refer to 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>

static int buttons_init(void)
{
    /* 1.Assign an input_dev structure. */
    
    /* 2.Set input_dev structure. */
    
    /* 3.Register input_dev structure. */
    
    /* 4.Hardware related operations. */
}

static void buttons_exit(vod)
{
    
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");

2.1.1 allocate an input_dev structure

/* The following structure definitions are located outside the function and are global variables*/
static struct input_dev *buttons_dev;

/*The following statement is located in buttons_ In init function*/
buttons_dev = input_allocate_device();
if (!buttons_dev)
    return -ENOMEM;

2.1.2 setting input_dev structure

First look at the following input_ Definition of dev structure:

  • evbit: used to set the type of event that the device can generate (all event types supported by the input subsystem are described in Section 1). Here, we select the key event EV_KEY and EV_REP, code as follows:

    • set_bit(EV_KEY, buttons_input->evbit);	//Key event
      set_bit(EV_REP, buttons_input->evbit);	//Repeat event (long press the key will repeatedly enter the key value)
      
  • keybit: used to set which key values can be generated by the device. Here we set L, S, left shift and enter on the keyboard mentioned at the beginning of this section (so that the ls command can be executed on the development board). Key codes are defined in include \ Linux \ input H, intercept a small part:

    • #define KEY_ Enter 28 / / key code of enter
      #define KEY_ Key code of s 31 / / S
      #define KEY_ Key code of L 38 / / L
      #define KEY_ Leftshift 42 / / key code of leftshift
      

      Set the code of which key to enter the event type:

      set_bit(KEY_L, buttons_input->keybit);
      set_bit(KEY_S, buttons_input->keybit);
      set_bit(KEY_ENTER, buttons_input->keybit);
      set_bit(KEY_LEFTSHIFT, buttons_input->keybit);
      
  • relbit: indicates which relative displacement events can be generated, such as roller

  • absbit: indicates which absolute displacement events can be generated.

2.1.3 register input_dev structure

The function of registration is actually to set the current device buttons_dev input_dev in the linked list, and then with evdev in the event layer_ Handler compares one by one (by comparing ID and id.table []), and evdev is called if it matches_ In handler The connect function generates a handle structure (only containing. Handler and. dev, one pointing to the device and one pointing to the handler), establishes an association between the current device and the handler, and puts the current dev and its matching handler into their respective h_list. The code is as follows:

input_register_device(buttons_input);//Register device driver

2.1.4 hardware related operations

  • Define each key description structure
/* The following 2 structure definitions are located outside the function and belong to global variables*/
struct pin_desc{
  int irq;
  char *name;
  unsigned int pin;
  unsigned int key_val;
};
 
struct pin_desc pins_desc[4] = {
  {IRQ_EINT0,  "S2", S3C2410_GPF(0),   KEY_L},
  {IRQ_EINT2,  "S3", S3C2410_GPF(2),   KEY_S},
  {IRQ_EINT11, "S4", S3C2410_GPG(3),   KEY_ENTER},
};
  • Initialization timer (anti jitter)
/* The following structure definitions are located outside the function and are global variables*/
static struct timer_list buttons_timer;	//Define a timer

/*The following statement is located in buttons_ In init function*/
init_timer(&buttons_timer);	//Initialization timer
buttons_timer.function = buttons_timer_function;//Define timeout handling function
add_timer(&buttons_timer);	//Define timeout
  • Register interrupts for each key
/*The following statement is located in buttons_ In init function*/
for (i = 0; i < 3; i++)	Registration interruption
{
	request_irq(pins_desc[i].irq, buttons_irq, (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING), pins_desc[i].name, &pins_desc[i]);
}
  • So overall buttons_ The code of init function is as follows:
static int buttons_init(void)
{
	int i;
	
	/* 1. Assign an input_dev structure */
	buttons_dev = input_allocate_device();;

	/* 2. set up */
	/* 2.1 What kind of events can occur */
	set_bit(EV_KEY, buttons_dev->evbit);
	set_bit(EV_REP, buttons_dev->evbit);
	
	/* 2.2 What events in such operations can be generated: l, s, enter, leftship */
	set_bit(KEY_L, buttons_dev->keybit);
	set_bit(KEY_S, buttons_dev->keybit);
	set_bit(KEY_ENTER, buttons_dev->keybit);
	
	/* 3. register */
	input_register_device(buttons_dev);
	
	/* 4. Hardware related operations */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 3; i++)
	{
		request_irq(pins_desc[i].irq, buttons_irq, (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING), pins_desc[i].name, &pins_desc[i]);
	}
	
	return 0;
}

  • Write exit function
static void buttons_exit(void)
{
	int i;
	for (i = 0; i < 3; i++)
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}

	del_timer(&buttons_timer);
	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);	
}

2.2 write the interrupt processing function of the key

/* The following structure pointer definitions are located outside the function and belong to global variables*/
static struct pin_desc *irq_pd;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	/* 10ms Post start timer */
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies+HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

2.3 write timer timeout processing function

When key data is generated, call input_event function to report events, which will be from input_dev in the linked list h_list to find the corresponding handler and call the inside event function.

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)
	{
		/* Release: last parameter: 0-release, 1-press */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(buttons_dev);	//Reporting synchronization events
	}
	else
	{
		/* Press */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(buttons_dev);
	}
}

2.4 overall code buttons c

/* Refer to drivers\input\keyboard\gpio_keys.c */
/* Run on JZ2440 development board and Linux 3.0 4.2 kernel, gcc4 three point two*/
#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>

struct pin_desc{
	int irq;
	char *name;
	unsigned int pin;
	unsigned int key_val;
};

struct pin_desc pins_desc[4] = {
	{IRQ_EINT0,  "S2", S3C2410_GPF(0),   KEY_L},
	{IRQ_EINT2,  "S3", S3C2410_GPF(2),   KEY_S},
	{IRQ_EINT11, "S4", S3C2410_GPG(3),   KEY_ENTER},
//	{IRQ_EINT19, "S5",  S3C2410_GPG(11), KEY_LEFTSHIFT},
};

static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	/* 10ms Post start timer */
	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)
	{
		/* Release: last parameter: 0-release, 1-press */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(buttons_dev);
	}
	else
	{
		/* Press */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(buttons_dev);
	}
}

static int buttons_init(void)
{
	int i;
	
	/* 1. Assign an input_dev structure */
	buttons_dev = input_allocate_device();;

	/* 2. set up */
	/* 2.1 What kind of events can occur */
	set_bit(EV_KEY, buttons_dev->evbit);
	set_bit(EV_REP, buttons_dev->evbit);
	
	/* 2.2 What events in such operations can be generated: l, s, enter, leftship */
	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. register */
	input_register_device(buttons_dev);
	
	/* 4. Hardware related operations */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 3; i++)
	{
		request_irq(pins_desc[i].irq, buttons_irq, (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING), pins_desc[i].name, &pins_desc[i]);
	}
	
	return 0;
}

static void buttons_exit(void)
{
	int i;
	for (i = 0; i < 3; i++)
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}

	del_timer(&buttons_timer);
	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);	
}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

3. Testing

3.1 test with hexdump

hexdump /dev/event1	//Equivalent to executing ` open /dev/event1; read();`
     second        Microsecond    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

3.2 if QT is not started at present, follow the following method:

  • Execute cat /dev/tty1
  • Press S2, S3 and S4 on the development board
  • You can see: ls

perhaps

  • Execute exec 0 < / dev / tty1
  • You can use the keyboard on the development board to enter the ls command

3.3 if QT has been started

  • Open Notepad first
  • Then press the keys S2, S3 and S4
  • You can see ls displayed on Notepad

4. Summary

  • What happens when the input device is turned on?

    • drivers/input/input.c:
      • input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
    • static const struct file_operations input_fops = {
      .owner = THIS_MODULE,
      .open = input_open_file,
      };
  • How to read keys?

    • intput_open_file
      • struct input_handler *handler = input_table[iminor(inode) >> 5];
      • new_fops = fops_get(handler->fops) // =>&evdev_fops
      • file->f_op = new_fops;
      • err = new_fops->open(inode, file);
    • APP: read > ... >file->f_op->read
  • input_ Who constructs the table array?

    • input_register_handler
  • How to register input_handler?

    • input_register_handler
      • input_ table[handler->minor >> 5] = handler; // Put in array
      • list_ add_ tail(&handler->node, &input_handler_list); / / put in the linked list
      • //For each input_dev, call input_attach_handler
      • list_for_each_entry(dev, &input_dev_list, node)
        • input_attach_handler(dev, handler); // According to input_ ID of the handler_ Table determines whether this input can be supported_ dev
  • How to register an input device:

    • input_register_device

      • list_ add_ tail(&dev->node, &input_dev_list); // Put in linked list
      • //For each input_ All handlers call input_attach_handler
        list_for_each_entry(handler, &input_handler_list, node)
        • input_attach_handler(dev, handler); // According to input_ ID of the handler_ Table determines whether this input can be supported_ dev

      input_attach_handler

      • id = input_match_device(handler->id_table, dev);
      • error = handler->connect(handler, dev, id);

      Register input_dev or input_handler, the input on the left will be compared in pairs_ Dev and input on the right_ handler,
      According to input_ ID of the handler_ Table determines the input_ Can the handler support this input_dev,
      If so, call input_ The connect function of handler establishes a "connection"

  • How do I establish a connection?
  1. Assign an input_handle structure
  2. input_handle.dev = input_dev; // Input pointing to the left_ dev
    input_handle.handler = input_handler; // Input pointing to the right_ handler
  3. Registration:
    input_handler->h_list = &input_handle;
    inpu_dev->h_list = &input_handle;

evdev_connect

  1. evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // Assign an input_handle
  2. //Set
    evdev->handle. dev = dev; // Input pointing to the left_ dev
    evdev->handle.name = evdev->name;
    evdev->handle. handler = handler; // Input pointing to the right_ handler
    evdev->handle.private = evdev;
  3. //Register
    error = input_register_handle(&evdev->handle);
  • How to read keys?

    • APP application execution: read

    • . . . .

    • evdev_read

      • //If there is no data and it is opened in non blocking mode, it returns immediately
        if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;

        //Otherwise sleep
        retval = wait_event_interruptible(evdev->wait,
        client->head != client->tail || !evdev->exist);

    • Who will wake up?
      evdev_event
      wake_up_interruptible(&evdev->wait);

    • evdev_ Who called event?
      Guess: it should be hardware related code, input_ Called by the dev layer
      In the interrupt service routine of the device, determine what the event is, and then call the corresponding input_. Event handler
      gpio_keys_isr

      • //Report events
        input_event(input, type, button->code, !!state);
        input_sync(input);
      • input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
        • struct input_handle *handle;
        • list_for_each_entry(handle, &dev->h_list, d_node)
          if (handle->open)
          handle->handler->event(handle, type, code, value);