We have analyzed and written a driver before, mainly including the following steps:
- The main equipment number of the driving equipment is set by yourself or automatically assigned by the system;
- Write equipment operation functions (drv_open, drv_read, drv_write, drv_close, etc.);
- 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
- Register the driver and call register_chrdev(major, name, fops);
- 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
- Assign an input_ Dev structure, call input_allocate_device() or devm_input_allocate_device(struct input_dev *);
- Write a function and set input_dev structure, select the event type of the input device (call the set_bit() function to implement);
- Register input_ Dev structure, call input_register_device(struct input_dev *) function implementation;
- 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.
- 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,
};
- drivers/input/input.c:
-
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
- intput_open_file
-
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
- input_register_handler
-
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?
- Assign an input_handle structure
- input_handle.dev = input_dev; // Input pointing to the left_ dev
input_handle.handler = input_handler; // Input pointing to the right_ handler - Registration:
input_handler->h_list = &input_handle;
inpu_dev->h_list = &input_handle;
evdev_connect
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // Assign an input_handle
- //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; - //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);
- //Report events
-