③The input subsystem of tiny4412 Linux driver development

This article introduces the driver development of the Linux input subsystem.

The input subsystem of the Linux kernel provides a driver framework for input devices such as mouse, keyboard, touch screen, and joystick. When programmers want to write drivers for their own input devices, they only need to implement getting input events from the device. As for how to handle input events and how to implement user interface, it is all done by the input subsystem. This greatly reduces the coding effort of the input driver and also improves the robustness of the driver.  

At the same time, the input subsystem provides a standard interface for the application layer for all input devices, which greatly improves the usability of the driver.  

The driver code for the input subsystem is in the kernel's <drivers/input/> directory.

The implementation of the input subsystem composing  
   the input subsystem needs to meet the following requirements:  
   (1) The input subsystem needs to generate a device file in the /dev/ directory for each input device, so as to facilitate the application program to read the events generated by the specified input device ;  
   (2) For each input device, the input subsystem only needs to realize its event acquisition, and it does not need to consider how to process the event and how to reach the device file;  

   (3) Input devices in Linux can be divided into event classes (such as USB mouse, USB keyboard, touch screen, etc.), MOUSE class (specifically PS/2 interface input devices), joysticks and other types, and are implemented for these input devices. The interface of the device file must be different. Therefore the input subsystem needs to implement the correct device file interface for different types of input devices.

This time, we will learn about the input subsystem by developing a simple input subsystem code. This time, we will develop it in combination with the platform bus + input subsystem. The following is the relevant code:

First define a header file:

#ifndef __PLAT_INPUT_H__
#define __PLAT_INPUT_H__



struct key_info {
    char    *name;
    int gpio;
    int     code;
    int     flags;
};

struct key_platdata {
    struct key_info    *key_desc;
    int num;
};



#endif

Enter the Subsystem Platform Device Information Code:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>


#include "key_info.h"



struct key_info key_pdesc[4] = {
    [0] = {
        .name = "KEY_UP",
        .gpio = EXYNOS4_GPX3(2),
        .code = KEY_UP,
        .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    },
    
    [1] = {
        .name = "KEY_DOWN",
        .gpio = EXYNOS4_GPX3(3),
        .code = KEY_DOWN,
        .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    },
    
    [2] = {
        .name = "KEY_LEFT",
        .gpio = EXYNOS4_GPX3(4),
        .code = KEY_LEFT,
        .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    },
    
    [3] = {
        .name = "KEY_RIGHT",
        .gpio = EXYNOS4_GPX3(5),
        .code = KEY_RIGHT,
        .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    },
};

struct key_platdata key_pdev_info = {
    .key_desc = key_pdesc,
    .num = ARRAY_SIZE(key_pdesc),
};

struct platform_device key_input_pdev = {
    .name = "EXYNOS4_KEY",
    .id = -1,
    .dev = {
        .platform_data = &key_pdev_info,
    },
};

static void __exit
key_platdev_exit(void)
{
    platform_device_unregister(&key_input_pdev);
}

static int __init
key_platdev_init(void)
{
    return platform_device_register(&key_input_pdev);
}

module_init(key_platdev_init);
module_exit(key_platdev_exit);

MODULE_LICENSE("Dual BSD/GPL");

Enter the subsystem platform device driver code:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>


#include "key_info.h"



struct key_platdata *key_plat_info = NULL;
struct input_dev *key_inputdev = NULL;


irqreturn_t
key_input_irq(int irqno, void *devid)
{
    int i = 0;

    udelay (25);

    for(i = 0; i < key_plat_info->num; i++){
        if(gpio_get_value(key_plat_info->key_desc[i].gpio)){
            input_report_key(key_inputdev, key_plat_info->key_desc[i].code, 0);
        }
        else{
            printk("<KERNEL> %s PRESSED\n", key_plat_info->key_desc[i].name);
            input_report_key(key_inputdev, key_plat_info->key_desc[i].code, 1);
        }
    }

    input_sync(key_inputdev);

    return IRQ_HANDLED;
}

int
key_input_remove(struct platform_device *pdev)
{
    int i;

    for(i = 0; i < key_plat_info->num; i++)
        free_irq(gpio_to_irq(key_plat_info->key_desc[i].gpio), &key_plat_info->key_desc[i]);

    input_unregister_device(key_inputdev);
    input_free_device(key_inputdev);
}

int
key_input_probe(struct platform_device *pdev)
{
    int ret = -1, i;
    // 0, get platform data
    key_plat_info = pdev->dev.platform_data;

    // 1, construct an input_devvice object
    key_inputdev = input_allocate_device();
    if(NULL == key_inputdev){
        printk("input alloc failed!\n");
        return -EINVAL;
    }

    // 2, initialize the input_device object
    // 2.1, set what types of data can be generated
    __set_bit(EV_KEY, key_inputdev->evbit);

    // 2.2, set which key values ​​can be generated
    __set_bit(KEY_UP,   key_inputdev->keybit);
    __set_bit(KEY_DOWN, key_inputdev->keybit);
    __set_bit(KEY_LEFT, key_inputdev->keybit);
    __set_bit(KEY_RIGHT,key_inputdev->keybit);

    // 3, register the input device object
    ret = input_register_device(key_inputdev);
    if (0! = ret) {
        printk("input register failed!\n");
        goto err1;
    }

    // 4, hardware initialization --> apply for interrupt
    for(i = 0; i < key_plat_info->num; i++){
        ret = request_irq(gpio_to_irq(key_plat_info->key_desc[i].gpio), key_input_irq, key_plat_info->key_desc[i].flags,
                                key_plat_info->key_desc[i].name, &key_plat_info->key_desc[i]);
        if (0! = ret) {
            printk("request irq failed!\n");
            goto err2;
        }
    }

    return 0;

err2:
    input_unregister_device(key_inputdev);
err1:
    input_free_device(key_inputdev);
    return ret;
}

const struct platform_device_id key_id_table[] = {
    {"S5PV210_KEY", 0x5555},
    {"EXYNOS4_KEY", 0x6665},
};

struct platform_driver key_input_pdrv = {
    .probe = key_input_probe,
    .remove = key_input_remove,
    .driver = {
        .name = "Samsung_KEY",
    },
    .id_table = key_id_table,
};

static void __exit
key_platdrv_exit(void)
{
    platform_driver_unregister(&key_input_pdrv);
}

static int __init
key_platdrv_init(void)
{
    return platform_driver_register(&key_input_pdrv);
}

module_init(key_platdrv_init);
module_exit(key_platdrv_exit);

MODULE_LICENSE("Dual BSD/GPL");

User layer test code:

#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/input.h>


int main(void)
{
    int ret = -1,
	fd  = -1;

    struct input_event event;

    fd = open("/dev/input/event2", O_RDWR);
		if(fd < 0){
		perror("open failed!\n");
		exit(1);
    }

    for(;;){
		ret = read(fd, &event, sizeof(struct input_event));
		if(ret < 0){
			perror("read failed!\n");
			exit(1);
		}

		if(EV_KEY == event.type){
			switch(event.code)
			{
				case KEY_UP:
					if(event.value)
					printf("KEY_UP pressed!\n");
					else
					printf("KEY_UP UP!\n");
					break;

				case KEY_DOWN:
					if(event.value)
					printf("KEY_DOWN pressed!\n");
					else
					printf("KEY_DOWN UP!\n");
					break;

				case KEY_LEFT:
					if(event.value)
					printf("KEY_LEFT pressed!\n");
					else
					printf("KEY_LEFT UP!\n");
					break;

				case KEY_RIGHT:
					if(event.value)
					printf("KEY_RIGHT pressed!\n");
					else
					printf("KEY_RIGHT UP!\n");
					break;

				default:
					break;
			}
		}
	}

    if(close(fd) < 0){
		perror("close");
		exit(1);
    }

    return 0;
}

And finally the Makefile:

#Linux source code path
KERNEL_DIR = /home/george/1702/exynos/linux-3.5

#Specify the current path
CUR_DIR = $ (shell pwd)


MYAPP = key_test
MODULE = plat_input_dev
MODULE2 = plat_input_drv

all:
	make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
	arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
clean:
	make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
	$ (RM) $ (MYAPP)
install:
	cp -raf *.ko $(MYAPP) /home/george/1702/exynos/filesystem/1702

#Specify which source file to compile in the current directory
obj-m = $(MODULE).o
obj-m += $(MODULE2).o

That's all.

Guess you like

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