Linux Input Subsystem (I) _Driver Coding

Keywords: Linux Programming

Input devices have common features: interrupt driver + character IO. Based on the hierarchical idea, Linux kernel extracts the public parts of these devices. Based on the interface provided by cdev, an input subsystem is designed. All devices constructed by the input subsystem use the main device number 13. At the same time, the input subsystem also supports the automatic creation of device files. These files use blocked IO reader and writer. Form, created under "/dev/input/". As shown in the figure below. The input subsystem in the kernel is divided into device driver layer, input core layer and event processing layer from bottom to top. Because the events reported by each input device are different, in order for the application layer to recognize the reported events well, the kernel also encapsulates a standard interface for the application layer to describe an event, which is in "/include/upai/linux/input".

  • Device driver layer is a hardware-related implementation and the main part of driver development.
  • The input core layer mainly provides some APIs for device driver layer to call. The data reported by these API device driver layer can be transferred to event processing layer.
  • Event processing layer is responsible for creating device files and delivering reported events to user space.

The Use of input

The input object describes an input device, including the events it may report. These events are described by bitmaps. The corresponding tools provided by the kernel help us build an input object. You can refer to the kernel document "Documentation/input/input-programming.txt", which describes the use of the input subsystem in detail.

//input device object
121 struct input_dev {
122         const char *name;
129         unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
130         unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
131         unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
132         unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
133         unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
134         unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
135         unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
136         unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
137         unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
155 
162         unsigned long key[BITS_TO_LONGS(KEY_CNT)];
163         unsigned long led[BITS_TO_LONGS(LED_CNT)];
164         unsigned long snd[BITS_TO_LONGS(SND_CNT)];
165         unsigned long sw[BITS_TO_LONGS(SW_CNT)];
166 
172         struct input_handle __rcu *grab;
179 
180         struct device dev;
181 
182         struct list_head        h_list;
183         struct list_head        node;
190 };

struct input_dev
--122--> this name Not the device name. input The device name of the subsystem is specified in the subsystem source code, not here.
--129--> Device-supported input event bitmaps, EV_KEY,EV_REL, etc
--130--> For keystroke events, device-supported input sub-event bitmaps
--132--> For relative coordinate events, device-supported relative coordinate sub-event bitmaps
--133--> For absolute coordinate events, device-supported absolute coordinate sub-event bitmaps
--134--> Subevent Bitmaps Supported by Hybrid Devices
--180-->It means that this is a device.
--182-->h_list Is used to link correlation handle Linked list
--183-->node Used to link to others input_dev Linked list

Distribution/Release

//drivers/input/input.c
//Create an input object
struct input_dev *input_allocate_device(void);

//Release an input object
void input_free_device(struct input_dev *dev);

Initialization

Initialization of an input object is the main task of writing drivers using the input subsystem. The kernel specifies some common input events of common input devices in the header file "include/uapi/linux/input.h". These macros and arrays are our tools for initializing input objects. These macros are used for event parsing and event registration in user space at the same time. They can be regarded as communication protocols between driver and user space. Therefore, it is very important to understand the significance of these macros. In the input subsystem, the occurrence of each event is described by event (type) - > sub-event (code) - > value (value). For example, key event - > key F1 sub-event - > key F1 sub-event triggered value is high level 1. Note that events and sub-events and values are mutually reinforcing. Only when event EV_KEY is registered can sub-event BTN_0 be registered, which is meaningful.
Here is the event type agreed upon by the kernel, which corresponds to the type field of the event object in the application layer

Here are the types of keystroke events, and you can see the definition of PC keys.

In addition to describing common events, the kernel also provides tools to properly populate these events into bitmaps describing events in input objects.

//First kind
//This is a great way to register multiple events when a contract is in place.
button_dev->evbit[0] = BIT_MASK(EV_KEY);            
button_dev->keybit[BIT_WORD(BTN_0|BTN_1)] = BIT_MASK(BTN_0|BTN_1); 
//Second kinds
//Usually used to register only one event
set_bit(EV_KEY,button_dev.evbit);
set_bit(BTN_0,button_dev.keybit);

Registration/cancellation

After initializing an input object, you need to register it with the kernel

//Register input objects to the kernel
int input_register_device(struct input_dev *dev);

//Log off an input object from the kernel
void input_unregister_device(struct input_dev *dev);

Driver layer reports events

At the right time (because the input is eventually represented by interrupts, so it is usually in the driver's interrupt handler) the driver can report registered events and multiple events at the same time. Here is the API provided by the kernel.

//Report the specified event + sub-event + value
void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value);

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

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

//Reporting synchronization events
void input_report_rel(struct input_dev *dev,unsigned int code,int value);

//Synchronize all reports
void input_sync(struct input_dev *dev);

There are two points to note in reporting incidents:

  1. The report functions don't really report, but just prepare to report, sync will really report the event just reported to the input core.
  2. The input core will decide and then report the event processing layer, so for key events, we must first report 1 and then report 0 (or vice versa), not only report 1 or 0, so the core will think that an event has been triggered many times and reported only once, although we really pressed many times.

Application Layer Analysis

Event processing layer will eventually organize all report-driven events into a struct input_value[] and report them to the application layer. When the application layer retrieves the reported events from the corresponding device files, it should pay attention to:

  1. The number of elements received from the array is one more empty element than the underlying one, similar to the last empty element when writing of_device_id[], which the application layer should pay attention to when parsing.
  2. Event processing layer does not cache received events. If new events come, even if the old events are not read, they will be overwritten, so the application needs to read them in time.

As mentioned earlier, the macro in "include/uapi/linux/input.h" is a communication protocol shared by application layer and driver layer, so when the application layer parses the received struct input_value object, it only needs "include < linux/input.h>" to use the macro.

/*
 * The event structure itself
 */

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

Template

The following template first uses the input subsystem to report key events, and then reads them at the application level.

input key device driver

/{
           key@26{
                      compatible = "xj4412,key";
                      interrupt-parent = <&gpx1>;
                      interrupts = <2 2>;
           };
};

static struct input_dev *button_dev;
static int button_irq;
static int irqflags;

static irqreturn_t button_interrupt(int irq, void *dummy)
{
    input_report_key(button_dev, BTN_0, 0);
    input_report_key(button_dev, BTN_0, 1);
    input_sync(button_dev);
    return IRQ_HANDLED;
}
 
static int button_init(void)
{
    request_irq(button_irq, button_interrupt,irqflags, "button", NULL)) ;
    
    button_dev = input_allocate_device();
    button_dev->name = "button";
    button_dev->evbit[0] = BIT_MASK(EV_KEY);
    button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
    
    input_register_device(button_dev);
    return 0;
}

static int button_exit(void)
{
    input_free_device(button_dev);
    free_irq(button_irq, button_interrupt);
    return 0;   
}
static int key_probe(struct platform_device *pdev)
{
    struct resource *irq_res;
    irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if(irq_res){
        button_irq = irq_res->start;
        irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
    }else{
        return -EINVAL;     
    }
    return button_init();
}

static int key_remove(struct platform_device *dev)
{
    return button_exit();
}

struct of_device_id of_tbl[] = {
    {.compatible = "xj4412,key",},
    {},
};
MODULE_DEVICE_TABLE(of, of_tbl);
struct platform_driver key_drv = {
    .probe = key_probe,
    .remove = key_remove,
    .driver.name = "keydrv",
    .driver.of_match_table = of_tbl,
};
module_platform_driver_register(key_drv);
MODULE_LICENSE("GPL");

Application Layer Gets Key Values

#include <linux/input.h>
struct input_event {
    struct timeval time;
    unsigned short type;
    unsigned short code;
    int value;
};
int main(int argc, char * const argv[])
{
    int fd = 0;
    struct input_event event[3] = {0};      //3!!! The driver uploads two events, and the third is used to empty the element 
    int ret = 0;
    fd = open(argv[1],O_RDONLY);
    while(1){
        ret = read(fd,&event,sizeof(event));
        printf("ret:%d,val0:%d,val1:%d,val12:%d\n",ret,event[0].value,event[1].value,event[2].value);          //2!!! The last one is empty
        sleep(1);
    }
    return 0;
}

Posted by guymrodgers on Sun, 31 Mar 2019 02:57:30 -0700