GVKun编程网logo

Linux输入子系统详解(linux input子系统)

11

在本文中,您将会了解到关于Linux输入子系统详解的新资讯,同时我们还将为您解释linuxinput子系统的相关在本文中,我们将带你探索Linux输入子系统详解的奥秘,分析linuxinput子系统的

在本文中,您将会了解到关于Linux输入子系统详解的新资讯,同时我们还将为您解释linux input子系统的相关在本文中,我们将带你探索Linux输入子系统详解的奥秘,分析linux input子系统的特点,并给出一些关于02_03_Linux根文件系统详解、7.Linux 输入子系统分析、8. 输入子系统 ------ 键盘按键驱动程序、INPUT 输入子系统 —— 按键的实用技巧。

本文目录一览:

Linux输入子系统详解(linux input子系统)

Linux输入子系统详解(linux input子系统)

input输入子系统框架 

linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。

【注意】keyboard.c不会在/dev/input下产生节点,而是作为ttyn终端(不包括串口终端)的输入。

 

驱动层

对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。将底层的硬件输入转化为统一事件形式,想输入核心(Input Core)汇报。

输入子系统核心层

对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。它承上启下为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息。

事件处理层

对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。主要是和用户空间交互(Linux中在用户空间将所有的设备都当作文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)。

 

/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。

事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。

输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。

 

 

 

由上图可知输入子系统核心层提供的支持以及如何上报事件到input event drivers。

 

作为输入设备的驱动开发者,需要做以下几步:

  • 在驱动加载模块中,设置你的input设备支持的事件类型
  •  注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等)
  •  将输入设备注册到输入子系统中

 ///////////////////////////////////////////////////////////////////分割线/////////////////////////////////////////////////////////////////////////////////

输入核心提供了底层输入设备驱动程序所需的API,如分配/释放一个输入设备:

struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev);

 

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
struct input_dev *input_allocate_device(void)
{
    struct input_dev *dev;
         /*分配一个input_dev结构体,并初始化为0*/ 
    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
    if (dev) {
        dev->dev.type = &input_dev_type;/*初始化设备的类型*/  
        dev->dev.class = &input_class; /*设置为输入设备类*/  
        device_initialize(&dev->dev);/*初始化device结构*/ 
        mutex_init(&dev->mutex); /*初始化互斥锁*/ 
        spin_lock_init(&dev->event_lock); /*初始化事件自旋锁*/  
        INIT_LIST_HEAD(&dev->h_list);/*初始化链表*/ 
        INIT_LIST_HEAD(&dev->node); /*初始化链表*/  

        __module_get(THIS_MODULE);/*模块引用技术加1*/  
    }

    return dev;
}    
View Code

 

 

注册/注销输入设备用的接口如下:

int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);

 

/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it''s capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 */
int input_register_device(struct input_dev *dev)
{
       //定义一些函数中将用到的局部变量
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;
    const char *path;
    int error;
    //设置 input_dev 所支持的事件类型,由 evbit 成员来表示。具体类型在后面归纳。
    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);

    /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
    input_cleanse_bitmasks(dev);

     //初始化 timer 定时器,用来处理重复点击按键。(去抖)
    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don''t do it in input.c.
     */
    init_timer(&dev->timer);
       //如果 rep[REP_DELAY] 和 [REP_PERIOD] 没有设值,则赋默认值。为了去抖。
    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
        dev->timer.data = (long) dev;
        dev->timer.function = input_repeat_key;
        dev->rep[REP_DELAY] = 250;
        dev->rep[REP_PERIOD] = 33;
    }
     //检查下列两个函数是否被定义,没有被定义则赋默认值。
    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;//得到指定位置键值

    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;//设置指定位置键值
       //设置 input_dev 中 device 的名字为 inputN
       //将如 input0 input1 input2 出现在 sysfs 文件系统中
    dev_set_name(&dev->dev, "input%ld",
             (unsigned long) atomic_inc_return(&input_no) - 1);
       //将 input->dev 包含的 device 结构注册到 Linux 设备模型中。
    error = device_add(&dev->dev);
    if (error)
        return error;
    //打印设备的路径并输出调试信息
    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    printk(KERN_INFO "input: %s as %s\n",
        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    kfree(path);

    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }
       //将 input_dev 加入 input_dev_list 链表中(这个链表中包含有所有 input 设备)
    list_add_tail(&dev->node, &input_dev_list);

    list_for_each_entry(handler, &input_handler_list, node)
      //调用 input_attatch_handler()函数匹配 handler 和 input_dev。
       //这个函数很重要,在后面单独分析。
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    mutex_unlock(&input_mutex);

    return 0;
}
View Code

 

 

而对于所有的输入事件,内核都用统一的数据结构来描述,这个数据结构是input_event

/*
 * The event structure itself
 */

struct input_event {
    struct timeval time; //<输入事件发生的时间
    __u16 type;          //<输入事件的类型
    __u16 code;          //<在输入事件类型下的编码
    __s32 value;         //<code的值
};

 

输入事件的类型--input_event.type

/*
 * Event types
 */

#define EV_SYN            0x00 //< 同步事件
#define EV_KEY            0x01 //< 按键事件
#define EV_REL            0x02 //<相对坐标(如:鼠标移动,报告相对最后一次位置的偏移) 
#define EV_ABS            0x03 //< 绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置) 
#define EV_MSC            0x04 //< 其它 
#define EV_SW             0x05 //<开关 
#define EV_LED            0x11 //<按键/设备灯 
#define EV_SND            0x12 //<声音/警报 
#define EV_REP            0x14 //<重复 
#define EV_FF             0x15 //<力反馈 
#define EV_PWR            0x16 //<电源 
#define EV_FF_STATUS      0x17 //<力反馈状态 
#define EV_MAX            0x1f //< 事件类型最大个数和提供位掩码支持 
#define EV_CNT            (EV_MAX+1)

 

 Linux输入子系统提供了设备驱动层上报输入事件的函数

报告输入事件用的接口如下:

/* 报告指定type、code的输入事件 */
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
/* 报告键值 */
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}
/* 报告相对坐标 */
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_REL, code, value);
}
/* 报告绝对坐标 */
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_ABS, code, value);
}
...

当提交输入设备产生的输入事件之后,需要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:

void input_sync(struct input_dev *dev);

【例子】驱动实现——报告结束input_sync()同步用于告诉input core子系统报告结束,触摸屏设备驱动中,一次点击的整个报告过程如下:

input_reprot_abs(input_dev,ABS_X,x);  //x坐标

input_reprot_abs(input_dev,ABS_Y,y);  // y坐标

input_reprot_abs(input_dev,ABS_PRESSURE,1);

input_sync(input_dev);//同步结束

【例子】按键中断程序

//按键初始化
static int __init button_init(void)
{//申请中断
    if(request_irq(BUTTON_IRQ,button_interrupt,0,”button”,NUll))
        return –EBUSY;
    set_bit(EV_KEY,button_dev.evbit); //支持EV_KEY事件
    set_bit(BTN_0,button_dev.keybit); //支持设备两个键
    set_bit(BTN_1,button_dev.keybit); //
    input_register_device(&button_dev);//注册input设备
}
/*在按键中断中报告事件*/
Static void button_interrupt(int irq,void *dummy,struct pt_regs *fp)
{
    input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));//读取寄存器BUTTON_PORT0的值
    input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1));
    input_sync(&button_dev);
}

【小结】input子系统仍然是字符设备驱动程序,但是代码量减少很多,input子系统只需要完成两个工作:初始化和事件报告(这里在linux中是通过中断来实现的)。

 

Event Handler层解析

Input输入子系统数据结构关系图

 

 

 

 

 

 

 

input_handler结构体

 

struct input_handle;

/**
 * struct input_handler - implements one of interfaces for input devices
 * @private: driver-specific data
 * @event: event handler. This method is being called by input core with
 *    interrupts disabled and dev->event_lock spinlock held and so
 *    it may not sleep
 * @filter: similar to @event; separates normal event handlers from
 *    "filters".
 * @match: called after comparing device''s id with handler''s id_table
 *    to perform fine-grained matching between device and handler
 * @connect: called when attaching a handler to an input device
 * @disconnect: disconnects a handler from input device
 * @start: starts handler for given handle. This function is called by
 *    input core right after connect() method and also when a process
 *    that "grabbed" a device releases it
 * @fops: file operations this driver implements
 * @minor: beginning of range of 32 minors for devices this driver
 *    can provide
 * @name: name of the handler, to be shown in /proc/bus/input/handlers
 * @id_table: pointer to a table of input_device_ids this driver can
 *    handle
 * @h_list: list of input handles associated with the handler
 * @node: for placing the driver onto input_handler_list
 *
 * Input handlers attach to input devices and create input handles. There
 * are likely several handlers attached to any given input device at the
 * same time. All of them will get their copy of input event generated by
 * the device.
 *
 * The very same structure is used to implement input filters. Input core
 * allows filters to run first and will not pass event to regular handlers
 * if any of the filters indicate that the event should be filtered (by
 * returning %true from their filter() method).
 *
 * Note that input core serializes calls to connect() and disconnect()
 * methods.
 */
struct input_handler {

    void *private;

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);

    const struct file_operations *fops;
    int minor;
    const char *name;

    const struct input_device_id *id_table;

    struct list_head    h_list;
    struct list_head    node;
};
View Code

 

【例子】以evdev.c中的evdev_handler为例:

static struct input_handler evdev_handler = {
                .event = evdev_event, //<向系统报告input事件,系统通过read方法读取
                .connect = evdev_connect, //<和input_dev匹配后调用connect构建
                .disconnect = evdev_disconnect,
                .fops = &evdev_fops, //<event设备文件的操作方法
                .minor = EVDEV_MINOR_BASE, //<次设备号基准值
                .name = "evdev",
                .id_table = evdev_ids, //<匹配规则
        };

 

 

 

输入设备驱动的简单案例

documentation/input/input-programming.txt文件,讲解了编写输入设备驱动程序的核心步骤。

Programming input drivers
~~~~~~~~~~~~~~~~~~~~~~~~~

1. Creating an input device driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1.0 The simplest example
~~~~~~~~~~~~~~~~~~~~~~~~

Here comes a very simple example of an input device driver. The device has
just one button and the button is accessible at i/o port BUTTON_PORT. When
pressed or released a BUTTON_IRQ happens. The driver could look like:

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

static struct input_dev *button_dev;

static irqreturn_t button_interrupt(int irq, void *dummy)
{
    input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
    input_sync(button_dev);
    return IRQ_HANDLED;
}

static int __init button_init(void)
{
    int error;

    if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
                printk(KERN_ERR "button.c: Can''t allocate irq %d\n", button_irq);
                return -EBUSY;
        }

    button_dev = input_allocate_device();
    if (!button_dev) {
        printk(KERN_ERR "button.c: Not enough memory\n");
        error = -ENOMEM;
        goto err_free_irq;
    }

    button_dev->evbit[0] = BIT_MASK(EV_KEY);
    button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);

    error = input_register_device(button_dev);
    if (error) {
        printk(KERN_ERR "button.c: Failed to register device\n");
        goto err_free_dev;
    }

    return 0;

 err_free_dev:
    input_free_device(button_dev);
 err_free_irq:
    free_irq(BUTTON_IRQ, button_interrupt);
    return error;
}

static void __exit button_exit(void)
{
        input_unregister_device(button_dev);
    free_irq(BUTTON_IRQ, button_interrupt);
}

module_init(button_init);
module_exit(button_exit);

1.1 What the example does
~~~~~~~~~~~~~~~~~~~~~~~~~

First it has to include the <linux/input.h> file, which interfaces to the
input subsystem. This provides all the definitions needed.

In the _init function, which is called either upon module load or when
booting the kernel, it grabs the required resources (it should also check
for the presence of the device).

Then it allocates a new input device structure with input_allocate_device()
and sets up input bitfields. This way the device driver tells the other
parts of the input systems what it is - what events can be generated or
accepted by this input device. Our example device can only generate EV_KEY
type events, and from those only BTN_0 event code. Thus we only set these
two bits. We could have used

    set_bit(EV_KEY, button_dev.evbit);
    set_bit(BTN_0, button_dev.keybit);

as well, but with more than single bits the first approach tends to be
shorter.

Then the example driver registers the input device structure by calling

    input_register_device(&button_dev);

This adds the button_dev structure to linked lists of the input driver and
calls device handler modules _connect functions to tell them a new input
device has appeared. input_register_device() may sleep and therefore must
not be called from an interrupt or with a spinlock held.

While in use, the only used function of the driver is

    button_interrupt()

which upon every interrupt from the button checks its state and reports it
via the

    input_report_key()

call to the input system. There is no need to check whether the interrupt
routine isn''t reporting two same value events (press, press for example) to
the input system, because the input_report_* functions check that
themselves.

Then there is the

    input_sync()

call to tell those who receive the events that we''ve sent a complete report.
This doesn''t seem important in the one button case, but is quite important
for for example mouse movement, where you don''t want the X and Y values
to be interpreted separately, because that''d result in a different movement.

1.2 dev->open() and dev->close()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In case the driver has to repeatedly poll the device, because it doesn''t
have an interrupt coming from it and the polling is too expensive to be done
all the time, or if the device uses a valuable resource (eg. interrupt), it
can use the open and close callback to know when it can stop polling or
release the interrupt and when it must resume polling or grab the interrupt
again. To do that, we would add this to our example driver:

static int button_open(struct input_dev *dev)
{
    if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
                printk(KERN_ERR "button.c: Can''t allocate irq %d\n", button_irq);
                return -EBUSY;
        }

        return 0;
}

static void button_close(struct input_dev *dev)
{
        free_irq(IRQ_AMIGA_VERTB, button_interrupt);
}

static int __init button_init(void)
{
    ...
    button_dev->open = button_open;
    button_dev->close = button_close;
    ...
}

Note that input core keeps track of number of users for the device and
makes sure that dev->open() is called only when the first user connects
to the device and that dev->close() is called when the very last user
disconnects. Calls to both callbacks are serialized.

The open() callback should return a 0 in case of success or any nonzero value
in case of failure. The close() callback (which is void) must always succeed.

1.3 Basic event types
~~~~~~~~~~~~~~~~~~~~~

The most simple event type is EV_KEY, which is used for keys and buttons.
It''s reported to the input system via:

    input_report_key(struct input_dev *dev, int code, int value)

See linux/input.h for the allowable values of code (from 0 to KEY_MAX).
Value is interpreted as a truth value, ie any nonzero value means key
pressed, zero value means key released. The input code generates events only
in case the value is different from before.

In addition to EV_KEY, there are two more basic event types: EV_REL and
EV_ABS. They are used for relative and absolute values supplied by the
device. A relative value may be for example a mouse movement in the X axis.
The mouse reports it as a relative difference from the last position,
because it doesn''t have any absolute coordinate system to work in. Absolute
events are namely for joysticks and digitizers - devices that do work in an
absolute coordinate systems.

Having the device report EV_REL buttons is as simple as with EV_KEY, simply
set the corresponding bits and call the

    input_report_rel(struct input_dev *dev, int code, int value)

function. Events are generated only for nonzero value.

However EV_ABS requires a little special care. Before calling
input_register_device, you have to fill additional fields in the input_dev
struct for each absolute axis your device has. If our button device had also
the ABS_X axis:

    button_dev.absmin[ABS_X] = 0;
    button_dev.absmax[ABS_X] = 255;
    button_dev.absfuzz[ABS_X] = 4;
    button_dev.absflat[ABS_X] = 8;

Or, you can just say:

    input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8);

This setting would be appropriate for a joystick X axis, with the minimum of
0, maximum of 255 (which the joystick *must* be able to reach, no problem if
it sometimes reports more, but it must be able to always reach the min and
max values), with noise in the data up to +- 4, and with a center flat
position of size 8.

If you don''t need absfuzz and absflat, you can set them to zero, which mean
that the thing is precise and always returns to exactly the center position
(if it has any).

1.4 BITS_TO_LONGS(), BIT_WORD(), BIT_MASK()
~~~~~~~~~~~~~~~~~~~~~~~~~~

These three macros from bitops.h help some bitfield computations:

    BITS_TO_LONGS(x) - returns the length of a bitfield array in longs for
               x bits
    BIT_WORD(x)     - returns the index in the array in longs for bit x
    BIT_MASK(x)     - returns the index in a long for bit x

1.5 The id* and name fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The dev->name should be set before registering the input device by the input
device driver. It''s a string like ''Generic button device'' containing a
user friendly name of the device.

The id* fields contain the bus ID (PCI, USB, ...), vendor ID and device ID
of the device. The bus IDs are defined in input.h. The vendor and device ids
are defined in pci_ids.h, usb_ids.h and similar include files. These fields
should be set by the input device driver before registering it.

The idtype field can be used for specific information for the input device
driver.

The id and name fields can be passed to userland via the evdev interface.

1.6 The keycode, keycodemax, keycodesize fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These three fields should be used by input devices that have dense keymaps.
The keycode is an array used to map from scancodes to input system keycodes.
The keycode max should contain the size of the array and keycodesize the
size of each entry in it (in bytes).

Userspace can query and alter current scancode to keycode mappings using
EVIOCGKEYCODE and EVIOCSKEYCODE ioctls on corresponding evdev interface.
When a device has all 3 aforementioned fields filled in, the driver may
rely on kernel''s default implementation of setting and querying keycode
mappings.

1.7 dev->getkeycode() and dev->setkeycode()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
getkeycode() and setkeycode() callbacks allow drivers to override default
keycode/keycodesize/keycodemax mapping mechanism provided by input core
and implement sparse keycode maps.

1.8 Key autorepeat
~~~~~~~~~~~~~~~~~~

... is simple. It is handled by the input.c module. Hardware autorepeat is
not used, because it''s not present in many devices and even where it is
present, it is broken sometimes (at keyboards: Toshiba notebooks). To enable
autorepeat for your device, just set EV_REP in dev->evbit. All will be
handled by the input system.

1.9 Other event types, handling output events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The other event types up to now are:

EV_LED - used for the keyboard LEDs.
EV_SND - used for keyboard beeps.

They are very similar to for example key events, but they go in the other
direction - from the system to the input device driver. If your input device
driver can handle these events, it has to set the respective bits in evbit,
*and* also the callback routine:

    button_dev->event = button_event;

int button_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
{
    if (type == EV_SND && code == SND_BELL) {
        outb(value, BUTTON_BELL);
        return 0;
    }
    return -1;
}

This callback routine can be called from an interrupt or a BH (although that
isn''t a rule), and thus must not sleep, and must not take too long to finish.
input-programming.txt

该例子提供的案例代码描述了一个button设备,产生的事件通过BUTTON_PORT引脚获取,当有按下/释放发生时,BUTTON_IRQ被触发,以下是驱动的源代码:

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

static struct input_dev *button_dev;  /*输入设备结构体*/ 
/*中断处理函数*/  
static irqreturn_t button_interrupt(int irq, void *dummy)
{
    /*向输入子系统报告产生按键事件*/  
    input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
    /*通知接收者,一个报告发送完毕*/  
    input_sync(button_dev);
    return IRQ_HANDLED;
}
/*加载函数*/ 
static int __init button_init(void)
{
    int error;
    /*申请中断处理函数*/ //返回0表示成功,返回-INVAL表示无效
    if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
                /*申请失败,则打印出错信息*/  
                printk(KERN_ERR "button.c: Can''t allocate irq %d\n", button_irq);
                return -EBUSY;
        }
    /*分配一个设备结构体*/ 
    //将在 sys/class/input/input-n 下面创建设备属性文件
    button_dev = input_allocate_device();
    if (!button_dev) {     /*判断分配是否成功*/  
        printk(KERN_ERR "button.c: Not enough memory\n");
        error = -ENOMEM;
        goto err_free_irq;
    }

    button_dev->evbit[0] = BIT_MASK(EV_KEY);  /*设置按键信息*/ 
    button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);

    error = input_register_device(button_dev);  /*注册一个输入设备*/  
    if (error) {
        printk(KERN_ERR "button.c: Failed to register device\n");
        goto err_free_dev;
    }

    return 0;
 /*以下是错误处理*/  
 err_free_dev:
    input_free_device(button_dev);
 err_free_irq:
    free_irq(BUTTON_IRQ, button_interrupt);
    return error;
}
 /*卸载函数*/  
static void __exit button_exit(void)
{
    input_unregister_device(button_dev); /*注销按键设备*/  
    free_irq(BUTTON_IRQ, button_interrupt);/*释放按键占用的中断线*/  
}

module_init(button_init);
module_exit(button_exit);

 从这个简单的例子中可以看到。
在初始化函数 button_init() 中注册了一个中断处理函数,然后调用 input_allocate_device() 函数分配了一个 input_dev 结构体,并调用 input_register_device() 对其进行注册。
在中断处理函数 button_interrupt() 中,实例将接收到的按键信息上报给 input 子系统,从而通过 input子系统,向用户态程序提供按键输入信息。

 

02_03_Linux根文件系统详解

02_03_Linux根文件系统详解

/boot:系统启动相关的文件,如内核initrd,以及grub(bootloader)
/dev:设备文件

  • 块设备:随机访问,数据块
  • 字符设备:线性访问,按字符为单位,如鼠标,显示器(在显存中按位显示)
  • 设备号:主设备号(major)和次设备号(minor)

/etc:配置文件
/home:用户的家目录,每一个用户的家目录通常默认为 /home/USERNAME
/root:管理员的家目录
/lib:库文件和内核模块文件

  • 静态库,.a
  • 动态库,.dll,.so(shared object)
  • /lib/modules:内核模块文件

/media:挂载点目录,移动设备
/mnt:挂载点目录,额外的临时文件系统
/opt:可选目录,第三方程序的安装目录
/proc:伪文件系统,内核映射文件
/sys:伪文件系统,跟硬件设备相关的属性映射文件
/tmp:临时文件,var/tmp
/var:可变化的文件
/bin:可执行文件,用户命令
/sbin:管理命令
/usr:universal,shared,read-only全局共享只读文件

  • /usr/bin
  • /usr/sbin
  • /usr/lib
  • /usr/local
    • /usr/local/bin
    • /usr/local/sbin
    • /usr/local/lib

7.Linux 输入子系统分析

7.Linux 输入子系统分析

  • 为什么要引入输入子系统?

在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢?

一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons

 1 .....
 2 int main(int argc, char **argv)
 3 {
 4     unsigned char key_val;
 5     int ret;
 6     fd = open("/dev/buttons", O_RDWR); //默认为阻塞操作
 7     if (fd < 0)
 8     {
 9         printf("can''t open!\n");
10         return -1;
11     }
12 ......

但是实际上,一般的应用程序不会去打开这样设备文件“/dev/buttons”。一般打开的都是系统原有的文件,如“ dev/tty* ” ,还有可能是不需要打开什么tty,
而是直接“scanf()”就去获得了按键的输入。

以前我们写一些输入设备(键盘、鼠标等)的驱动都是采用字符设备、混杂设备处理的。问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可以实现一种机制,可以对分散的、不同类别的输入设备进行统一的驱动,所以才出现了输入子系统。

 输入子系统引入的好处:

(1)统一了物理形态各异的相似的输入设备的处理功能。例如,各种鼠标,不论PS/2、USB、还是蓝牙,都被同样处理。

(2)提供了用于分发输入报告给用户应用程序的简单的事件(event)接口。你的驱动不必创建、管理/dev节点以及相关的访问方法。因此它能够很方便的调用输入API以发送鼠标移动、键盘按键,或触摸事件给用户空间。X windows这样的应用程序能够无缝地运行于输入子系统提供的event接口之上。

(3)抽取出了输入驱动的通用部分,简化了驱动,并提供了一致性。例如,输入子系统提供了一个底层驱动(成为serio)的集合,支持对串口和键盘控制器等硬件输入的访问
  详见《精通Linux设备驱动程序开发》


 

 

1.Linux输入子系统框架

linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:

  输入子系统事件处理层(EventHandler)

  输入子系统核心层(InputCore)

  输入子系统设备驱动层(input driver)

1.输入子系统设备驱动层:主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

2.核心层:承上启下。为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息。

    设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。

3.事件处理层:是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

    (Linux中在用户空间将所有的设备都当做文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)

 

输入子系统中有两个类型的驱动,当我们要为一个输入设备(如触摸屏)的编写驱动的时候,我们是要编写两个驱动:输入设备驱动和输入事件驱动?

       答案是否定的。在子系统中,事件驱动是标准的,对所有的输入类都是可以用的,所以你更可能的是实现输入设备驱动而不是输入事件驱动。你的设备可以利用一个已经存在的,合适的输入事件驱动通过输入核心和用户应用程序接口。

输入设备都各有不同,那么输入子系统也就只能实现他们的共性,差异性则由设备驱动来实现。差异性又体现在哪里?

最直观体现在设备功能上的不同。对于驱动编写者来说,在设备驱动中就只要使用输入子系统提供的工具(也就是函数)来完成这些“差异”就行了,其他的则是输入子系统的工作。这个思想不仅存在于输入子系统,其他子系统也是一样(比如:usb子系统、video子系统等)

 先分析核心层的代码 Input.c

 以下转载自Linux之输入子系统分析(详解)

Input.c

 在最后有一下两段:

subsys_initcall(input_init);   //修饰入口函数

module_exit(input_exit);     //修饰出口函数

可知,子系统是作为一个模块存在的。

1.先来分析入口函数input_init

 1 static int __init input_init(void)
 2 {
 3     int err;
 4 
 5     err = class_register(&input_class); //注册类,放在/sys/class
 6     if (err) {
 7         printk(KERN_ERR "input: unable to register input_dev class\n");
 8         return err;
 9     }
10 
11     err = input_proc_init(); //在/proc下建立相关文件
12     if (err)
13         goto fail1;
14 
15     err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册驱动
16     if (err) {
17         printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
18         goto fail2;
19     }
20 
21     return 0;
22 
23  fail2:    input_proc_exit();
24  fail1:    class_unregister(&input_class);
25     return err;
26 }

 

(1)上面第5行,class_register(&input_class),是在/sys/class中创建一个input类,input_class类结构如下:

struct class input_class = {
    .name            = "input",
    .release            = input_dev_release,
    .uevent            = input_dev_uevent,
};

如下图,我们启动内核,再启动一个input子系统的驱动后,也可以看到创建了个"input"类 :

疑问:为什么在此只创建了类,却没有使用class_dev_create()函数在类下面创建驱动设备?

  当注册了input子系统的驱动后,才会有设备驱动,此处代码没有驱动。以下会详细解释。

(2)上面第15行通过register_chrdev创建驱动设备,其中变量INPUT_MAJOR =13,所以创建了一个主设备为13的"input"设备。

然后我们来看看它的操作结构体input_fops,如下

static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
};

只有一个.open函数,比如当我们挂载一个新的input驱动,则内核便会调用该.open函数,接下来分析该.open函数

 

2.进入input_open_file函数(drivers/input/input.c)

 

 1 static int input_open_file(struct inode *inode, struct file *file)
 2  {
 3      struct input_handler *handler = input_table[iminor(inode) >> 5]; // (1)
 4      const struct file_operations *old_fops, *new_fops = NULL;
 5      int err;
 6 
 7      if (!handler || !(new_fops = fops_get(handler->fops)))  //(2)
 8           return -ENODEV; 
 9 
10     if (!new_fops->open) {
11            fops_put(new_fops);
12            return -ENODEV;
13     }
14 
15     old_fops = file->f_op;
16     file->f_op = new_fops;     //(3)
17 
18     err = new_fops->open(inode, file);   //(4)
19     if (err) {
20           fops_put(file->f_op);
21            file->f_op = fops_get(old_fops);
22    }
23 
24    fops_put(old_fops);
25 
26     return err;
27 }

(1)第3行中,其中iminor (inode)函数调用了MINOR(inode->i_rdev);读取子设备号,然后将子设备除以32,找到新挂载的input驱动的数组号,然后放在input_handler 驱动处理函数handler中 

(2)第7行中,若handler有值,说明挂载有这个驱动,就将handler结构体里的成员file_operations * fops赋到新的file_operations *old_fops里面

(3)第16行中, 再将新的file_operations *old_fops赋到file-> file_operations  *f_op里, 此时input子系统的file_operations就等于新挂载的input驱动的file_operations结构体,实现一个偷天换日的效果.

(4)第18行中,然后调用新挂载的input驱动的*old_fops里面的成员.open函数

 

3.上面代码的input_table[]数组在初始时是没有值的,

所以我们来看看input_table数组里面的数据又是在哪个函数里被赋值

 在input.c函数(drivers/input/input.c)中搜索input_table,找到它在input_register_handler()函数中被赋值,代码如下:

int input_register_handler(struct input_handler *handler)
{
... ...
input_table[handler->minor >> 5] = handler;    //input_table[]被赋值
... ...
list_add_tail(&handler->node, &input_handler_list); //然后将这个input_handler放到input_handler_list链表中  
... ...

 list_for_each_entry(dev, &input_dev_list, node) //对于每一个input_dev,调用input_attach_handler
   input_attach_handler(dev, handler);  //根据input_handler的id_table判断能否支持这个input_dev

}

就是将驱动处理程序input_handler注册到input_table[]中,然后放在input_handler_list链表中,后面会讲这个链表

 

 4.继续来搜索input_register_handler,看看这个函数被谁来调用

 如下图所示,有evdev.c(事件设备),tsdev.c(触摸屏设备),joydev.c(joystick操作杆设备),keyboard.c(键盘设备),mousedev.c(鼠标设备) 这5个内核自带的设备处理函数注册到input子系统中

以evdev.c为例,它在evdev_ini()函数中注册:

static int __init evdev_init(void)
{
       return input_register_handler(&evdev_handler);  //注册
}

 

5.我们来看看这个evdev_handler变量是什么结构体,:

1 static struct input_handler evdev_handler = {
2        .event =  evdev_event,    
3        .connect =      evdev_connect,  //(4)
4        .disconnect = evdev_disconnect,
5        .fops =           &evdev_fops,    //(1)
6        .minor =  EVDEV_MINOR_BASE, //(2)
7        .name =         "evdev",
8        .id_table =      evdev_ids, //(3)
9 };

就是我们之前看的input_handler驱动处理结构体

(1) 第5行中.fops:文件操作结构体,其中evdev_fops函数就是自己的写的操作函数,然后赋到.fops中

(2)第6行中 .minor:用来存放次设备号

其中EVDEV_MINOR_BASE=64, 然后调用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中

 所以当open打开这个input设备,就会进入 input_open_file()函数,执行evdev_handler-> evdev_fops -> .open函数,如下所示:

 1 static const struct file_operations evdev_fops = {
 2     .owner =    THIS_MODULE,
 3     .read  =    evdev_read,
 4     .write =    evdev_write,
 5     .poll  =    evdev_poll,
 6     .open  =    evdev_open,
 7     .release =  evdev_release,
 8     .unlocked_ioctl = evdev_ioctl,
 9 #ifdef CONFIG_COMPAT
10     .compat_ioctl =    evdev_ioctl_compat,
11 #endif
12     .fasync =    evdev_fasync,
13     .flush  =    evdev_flush
14 };

(3)第8行中.id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev->的id和某个input_handler的id_table相匹配,就会调用.connect连接函数,如下图

(4)第3行中.connect:连接函数,将设备input_dev和某个input_handler建立连接,如下图

6.我们先来看看上图的input_register_device()函数,如何创建驱动设备的

 搜索input_register_device,发现内核自己就已经注册了很多驱动设备

 6.1然后进入input_register_device()函数,代码如下:

1 int input_register_device(struct input_dev *dev)   //*dev:要注册的驱动设备
2 {
3  ... ...
4        list_add_tail(&dev->node, &input_dev_list);   //(1)放入链表中
5  ... ...
6        list_for_each_entry(handler, &input_handler_list, node)  //(2)
7          input_attach_handler(dev, handler); 
8  ... ...
9 }

(1)第4行中,将要注册的input_dev驱动设备放在input_dev_list链表中

(2)第6行中,其中input_handler_list在前面讲过,就是存放每个input_handle驱动处理结构体,

然后list_for_each_entry()函数会将每个input_handle从链表中取出,放到handler中

最后会调用input_attach_handler()函数,将每个input_handle的id_table进行判断,若两者支持便进行连接。

  6.2然后我们在回过头来看注册input_handler的input_register_handler()函数,如下图所示

 在input.c中

 1 int input_register_handler(struct input_handler *handler)
 2 {
 3     struct input_dev *dev;
 4 
 5     INIT_LIST_HEAD(&handler->h_list);
 6 
 7     if (handler->fops != NULL) {
 8         if (input_table[handler->minor >> 5])
 9             return -EBUSY;
10 
11         input_table[handler->minor >> 5] = handler; //放入数组
12     }
13 
14     list_add_tail(&handler->node, &input_handler_list); //放入链表
15     
16     list_for_each_entry(dev, &input_dev_list, node)  //对于每一个input_dev,调用input_attach_handler
17         input_attach_handler(dev, handler);
18 
19     input_wakeup_procfs_readers();
20     return 0;

所以,不管新添加input_dev还是input_handler,都会进入input_attach_handler()判断两者id是否有支持, 若两者支持便进行连接

 

  6.3我们来看看input_attach_handler()如何实现匹配两者id的:

 1 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
 2 {
 3 ... ...
 4 id = input_match_device(handler->id_table, dev);  //匹配两者
 5 
 6 if (!id)                                     //若不匹配,return退出
 7 return -ENODEV; 
 8 
 9 error = handler->connect(handler, dev, id);  //调用input_handler ->connect函数建立连接
10 ... ...
11 
12 }

根据input_handler的id_table判断能否支持这个input_dev
如果能支持,则调用input_handler的connect函数建立“连接”

7.以evdev.c(事件驱动) 的evdev_handler->connect函数来分析是怎样建立连接的,如下:

1 static struct input_handler evdev_handler = {
2        .event   =    evdev_event,    
3        .connect =    evdev_connect,  
4        .disconnect = evdev_disconnect,
5        .fops  =          &evdev_fops,    
6        .minor =  EVDEV_MINOR_BASE, 
7        .name  =         "evdev",
8        .id_table =       evdev_ids, 
9 };

 

  7.1 evdev_handler的.connect函数是evdev_connect(),代码如下:

 1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)     
 2 {
 3 ... ... 
 4 for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驱动设备的子设备号
 5     if (minor == EVDEV_MINORS) {  // EVDEV_MINORS=32,所以该事件下的驱动设备最多存32个,
 6         printk(KERN_ERR "evdev: no more free evdev devices\n");
 7         return -ENFILE;                //没找到驱动设备
 8     }
 9  ... ...
10  evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //分配一个input_handle全局结构体(没有r)
11  ... ...
12  evdev->handle.dev = dev;            //指向参数input_dev驱动设备
13 evdev->handle.name = evdev->name;
14 evdev->handle.handler = handler;    //指向参数 input_handler驱动处理结构体
15 evdev->handle.private = evdev;
16 sprintf(evdev->name, "event%d", minor);    //(1)保存驱动设备名字, event%d
17 ... ...
18 devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),  //(2) 将主设备号和次设备号转换成dev_t类型
19 cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); 
20                                                            // (3)在input类下创建驱动设备
21 
22 ... ...
23 error = input_register_handle(&evdev->handle); //(4)注册这个input_handle结构体
24 
25 ... ...
26 }

(1) 第16行中,是在保存驱动设备名字,名为event%d, 比如下图(键盘驱动)event1: 因为没有设置子设备号,默认从小到大排列,其中event0是表示这个input子系统,所以这个键盘驱动名字就是event1

(2)第18行中,是在保存驱动设备的主次设备号,其中主设备号INPUT_MAJOR=13,因为EVDEV_MINOR_BASE=64,所以此设备号=64+驱动程序本事子设备号, 比如下图(键盘驱动)event1:  主次设备号就是13,65

(3)在之前在2小结里就分析了input_class类结构,所以第19行中,会在/sys/class/input类下创建驱动设备event%d,比如下图(键盘驱动)event1:

(4)最终会进入input_register_handle()函数来注册,代码在下面

  

 7.2 input_register_handle()函数如下:

 1 int input_register_handle(struct input_handle *handle)
 2 {
 3       struct input_handler *handler = handle->handler; //handler= input_handler驱动处理结构体 
 4 
 5       list_add_tail(&handle->d_node, &handle->dev->h_list); //(1)
 6       list_add_tail(&handle->h_node, &handler->h_list);    // (2)
 7  
 8       if (handler->start)
 9              handler->start(handle);
10       return 0;
11 }

(1)在第5行中, 因为handle->dev指向input_dev驱动设备,所以就是将handle->d_node放入到input_dev驱动设备的h_list链表中,

即input_dev驱动设备的h_list链表就指向handle->d_node

(2) 在第6行中, 同样, input_handler驱动处理结构体的h_list也指向了handle->h_node

最终如下图所示:

两者的.h_list都指向了同一个handle结构体,然后通过.h_list 来找到handle的成员.dev和handler,便能找到对方,便建立了连接

 

8.建立了连接后,又如何读取evdev.c(事件驱动) 的evdev_handler->.fops->.read函数?

事件驱动的.read函数是evdev_read()函数,我们来分析下:

 1 static ssize_t evdev_read(struct file *file, char __user *      buffer, size_t count, loff_t *ppos)
 2 {
 3  ... ...
 4 /*判断应用层要读取的数据是否正确*/
 5 if (count < evdev_event_size())
 6 return -EINVAL;
 7 
 8 /*在非阻塞操作情况下,若client->head == client->tail|| evdev->exist时(没有数据),则return返回*/
 9  if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
10 return -EAGAIN;
11  
12 /*若client->head == client->tail|| evdev->exist时(没有数据),等待中断进入睡眠状态  */
13   retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);
14 
15   ... ...           //上传数据
16 
17 }

 

9.若read函数进入了休眠状态,又是谁来唤醒?

 我们搜索这个evdev->wait这个等待队列变量,找到evdev_event函数里唤醒:

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
... ...
 wake_up_interruptible(&evdev->wait);   //有事件触发,便唤醒等待中断
}

其中evdev_event()是evdev.c(事件驱动) 的evdev_handler->.event成员,如下所示:

1 static struct input_handler evdev_handler = {
2        .event   =    evdev_event,    
3        .connect =    evdev_connect,  
4        .disconnect = evdev_disconnect,
5        .fops  =          &evdev_fops,    
6        .minor =  EVDEV_MINOR_BASE, 
7        .name  =         "evdev",
8        .id_table =       evdev_ids, 
9 };

当有事件发生了,比如对于按键驱动,当有按键按下时,就会进入.event函数中处理事件

 

 10.分析下,是调用evdev_event()这个.event事件驱动函数

 猜测:硬件相关的代码(input_dev那层)--中断处理函数

 来看看内核 gpio_keys_isr()函数代码例子就知道了 (driver/input/keyboard/gpio_key.c)

1 static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
2 {
3  /*获取按键值,赋到state里*/
4  ... ...
5 
6 /*上报事件*/
7 input_event(input, type, button->code, !!state);  
8 input_sync(input);                        //同步信号通知,表示事件发送完毕
9 }

显然就是通过input_event()来调用.event事件函数,我们来看看:

 1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 2 {
 3 struct input_handle *handle;
 4 ... ...
 5 
 6 /* 通过input_dev ->h_list链表找到input_handle驱动处理结构体*/
 7 list_for_each_entry(handle, &dev->h_list, d_node)    
 8 if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体
 9     handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数 
10 
11 }

若之前驱动input_dev和处理input_handler已经通过input_handler 的.connect函数建立起了连接,那么就调用evdev_event()的.event事件函数,如下图所示:

 

 

 11.本节总结分析:

1.注册输入子系统,进入input_init():

1)创建主设备号为13的"input"字符设备

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

 

2.open打开驱动,进入input_open_file():

1)更新设备的file_oprations

file->f_op=fops_get(handler->fops);

2)执行file_oprations->open函数

err = new_fops->open(inode, file);

 

3.注册input_handler,进入input_register_handler():

1)添加到input_table[]处理数组中

input_table[handler->minor >> 5] = handler;

 2)添加到input_handler_list链表中

list_add_tail(&handler->node, &input_handler_list);

3)判断input_dev的id,是否有支持这个驱动的设备

list_for_each_entry(dev, &input_dev_list, node)   //遍历查找input_dev_list链表里所有input_dev

 input_attach_handler(dev, handler);             //判断两者id,若两者支持便进行连接。

 

4.注册input_dev,进入input_register_device():

1)放在input_dev_list链表中

list_add_tail(&dev->node, &input_dev_list);

2)判断input_handler的id,是否有支持这个设备的驱动

list_for_each_entry(handler, &input_handler_list, node)  //遍历查找input_handler_list链表里所有input_handler
input_attach_handler(dev, handler);                      //判断两者id,若两者支持便进行连接。

 

5.判断input_handlerinput_devid,进入input_attach_handler():

 1)匹配两者id,

input_match_device(handler->id_table, dev);        //匹配input_handler和dev的id,不成功退出函数

2)匹配成功调用input_handler ->connect

handler->connect(handler, dev, id);              //建立连接

 

6.建立input_handlerinput_dev的连接,进入input_handler->connect():

 1)创建全局结构体,通过input_handle结构体连接双方

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    //创建两者连接的input_handle全局结构体
list_add_tail(&handle->d_node, &handle->dev->h_list); //连接input_dev->h_list
list_add_tail(&handle->h_node, &handler->h_list);    // 连接input_handle->h_list

 

7.有事件发生时,比如按键中断,在中断函数中需要进入input_event()上报事件:

 1)找到驱动处理结构体,然后执行input_handler->event()

list_for_each_entry(handle, &dev->h_list, d_node)     // 通过input_dev ->h_list链表找到input_handle驱动处理结构体
if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体(有可能一个驱动设备在不同情况下有不同的驱动处理方式)
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数

 

8. 输入子系统 ------ 键盘按键驱动程序

8. 输入子系统 ------ 键盘按键驱动程序

  • 由上一节的输入子系统的框架分析可知,其分三层:设备驱动层,核心层,事件驱动层

我们在为某种设备的编写驱动层,只需要关心设备驱动层,即如何驱动设备并获得硬件数据(如按下的按键数据),然后调用核心层提供的接口,核心层就会自动把数据提交给事件处理层。在输入子系统中,事件驱动是标准的,适用于所有输入类的。我们的设备可以利用一个已经存在的,合适的输入事件驱动,通过输入核心,和用户应用程序接口。

 

一、编写设备驱动层的流程

1. 分配一个 input——dev 结构体

2. 设置 input_dev 的成员

3. 注册 input_dev 驱动设备

4. 硬件相关代码

  1)初始化定时器和中断

  2)写中断服务函数

  3)写定时器超时函数

  4)在出口函数中 释放中断函数,删除定时器,卸载释放驱动

 

二、相关结构体及函数

input_dev 驱动设备结构体中常用成员及相关函数如下:(include/linux/Input.h)

 1 struct input_dev {      
 2 
 3        void *private;
 4        const char *name;  //设备名字
 5        const char *phys;  //文件路径,比如 input/buttons
 6        const char *uniq;   
 7        struct input_id id;
 8 
 9  
10        unsigned long evbit[NBITS(EV_MAX)];  //表示支持哪事件,常用有以下几种事件(可以多选)
11        //EV_SYN      同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
12        //EV_KEY       键盘事件  这些都是宏定义
13        //EV_REL       (relative)相对坐标事件,比如鼠标
14        //EV_ABS       (absolute)绝对坐标事件,比如摇杆、触摸屏感应
15        //EV_MSC      其他事件,功能
16        //EV_LED       LED灯事件
17        //EV_SND      (sound)声音事件
18 
19        //EV_REP       重复键盘按键事件
20   //(内部会定义一个定时器,若有键盘按键事件一直按下/松开,就重复定时,时间一到就上报事件)   
21 
22        //EV_FF         受力事件
23        //EV_PWR      电源事件
24        //EV_FF_STATUS  受力状态事件
25 
26        unsigned long keybit[NBITS(KEY_MAX)];   //存放支持的键盘按键值,即能产生哪些按键
27                                     //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)
28 
29        unsigned long relbit[NBITS(REL_MAX)];    //存放支持的相对坐标值,如x,y,滚轮
30        unsigned long absbit[NBITS(ABS_MAX)];   //存放支持的绝对坐标值
31        unsigned long mscbit[NBITS(MSC_MAX)];   //存放支持的其它事件,也就是功能
32        unsigned long ledbit[NBITS(LED_MAX)];    //存放支持的各种状态LED
33        unsigned long sndbit[NBITS(SND_MAX)];    //存放支持的各种声音
34        unsigned long ffbit[NBITS(FF_MAX)];       //存放支持的受力设备
35        unsigned long swbit[NBITS(SW_MAX)];     //存放支持的开关功能

2) 函数如下:

 1 struct input_dev *input_allocate_device(void);  //向内核中申请一个input_dev设备,然后返回这个设备
 2   
 3 input_unregister_device(struct input_dev *dev);  //卸载/sys/class/input目录下的input_dev这个类设备, 一般在驱动出口函数写
 4  
 5 input_free_device(struct input_dev *dev);   //释放input_dev这个结构体, 一般在驱动出口函数写
 6  
 7  
 8 
 9 set_bit(nr,p);                  //设置某个结构体成员p里面的某位等于nr,支持这个功能
10 /* 比如:
11 set_bit(EV_KEY,buttons_dev->evbit);   //设置input_dev结构体buttons_dev->evbit支持EV_KEY
12 set_bit(KEY_S,buttons_dev->keybit);  //设置input_dev结构体buttons_dev->keybit支持按键”S”
13 */
14 
15 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);  //上报事件
16  // input_dev *dev :要上报哪个input_dev驱动设备的事件
17  // type : 要上报哪类事件, 比如按键事件,则填入: EV_KEY
18  // code: 对应的事件里支持的哪个变量,比如按下按键L则填入: KEY_L
19  //value:对应的变量里的数值,比如松开按键则填入1,松开按键则填入0

 

input_sync(struct input_dev *dev); //同步事件通知

为什么使用了 input_event () 上报事件函数,就要使用这个函数?

因为 input_event () 函数只是个事件函数,所以需要这个 input_sync () 同步事件函数来通知系统,然后系统才会知道

input_sync () 代码如下

static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0); //就是上报同步事件,告诉内核:input_event()事件执行完毕
}

 

三、编写设备驱动程序

 

  1 //参考:linux-2.6.22.6\linux-2.6.22.6\drivers\input\keyboard\Gpio_keys.c
  2 //pre1.包含头文件
  3 #include <linux/module.h>
  4 #include <linux/version.h>
  5 
  6 #include <linux/init.h>
  7 #include <linux/fs.h>
  8 #include <linux/interrupt.h>
  9 #include <linux/irq.h>
 10 #include <linux/sched.h>
 11 #include <linux/pm.h>
 12 #include <linux/sysctl.h>
 13 #include <linux/proc_fs.h>
 14 #include <linux/delay.h>
 15 #include <linux/platform_device.h>
 16 #include <linux/input.h>
 17 #include <linux/irq.h>
 18 #include <asm/gpio.h>
 19 #include <asm/io.h>
 20 #include <asm/arch/regs-gpio.h>
 21 
 22 
 23 struct pin_desc{
 24     int irq;    //按键的外部中断标志位
 25     char *name;    //中断设备名称
 26     unsigned int pin;    //引脚
 27     unsigned int key_val;  //dev_id,对应键盘的 L ,  S,  空格,  enter  
 28 };
 29 
 30 /* 定义四个按键 */
 31 struct pin_desc pins_desc[4] = {
 32     {IRQ_EINT0,  "S2", S3C2410_GPF0,  KEY_L},
 33     {IRQ_EINT2,  "S3", S3C2410_GPF2,  KEY_S},
 34     {IRQ_EINT11, "S4", S3C2410_GPG3,  KEY_ENTER},
 35     {IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT},
 36 };
 37 
 38 static struct pin_desc *irq_pd;             //指针irq_pd用来保存dev_id
 39 static struct timer_list buttons_timer;    //定时器结构体
 40 static struct input_dev *buttons_dev;    //定义一个input_dev结构体指针 
 41 
 42 /* 中断服务函数 */
 43 static irqreturn_t buttons_irq(int irq, void *dev_id)
 44 {
 45     /* 10ms后启动定时器 */
 46     irq_pd = (struct pin_desc *)dev_id;    //保存当前的dev_id
 47     mod_timer(&buttons_timer, jiffies+HZ/100);  //更新定时器10ms
 48     return IRQ_RETVAL(IRQ_HANDLED);
 49 }
 50 
 51 /* 定时器超时函数 */
 52 static void buttons_timer_function(unsigned long data)
 53 {
 54     //超时处理函数只需要将事件上报即可
 55     
 56     struct pin_desc * pindesc = irq_pd;
 57     unsigned int pinval;
 58     
 59     if (!pindesc)
 60         return;
 61     
 62     pinval = s3c2410_gpio_getpin(pindesc->pin);
 63 
 64     if (pinval)
 65     {
 66         /* 松开 :上报EV_KEY类型,button按键,0(没按下)*/
 67         input_event(buttons_dev, EV_KEY, pindesc->key_val, 0); 
 68         input_sync(buttons_dev); //上传同步事件,告诉系统有事件出现 
 69     }
 70     else
 71     {
 72         /* 按下:上报EV_KEY类型,button按键,1(按下) */
 73         input_event(buttons_dev, EV_KEY, pindesc->key_val, 1); 
 74         input_sync(buttons_dev);
 75     }
 76 }
 77 
 78 
 79 
 80 //pre2.写入口函数
 81 static int buttons_init(void)
 82 {
 83     int i;
 84     /* 1.分配一个input_dev结构体 */
 85     //向内核中申请一个input_dev设备,然后返回这个设备,此处省略判断返回值
 86     buttons_dev = input_allocate_device();
 87     
 88     /* 2.设置input_dev的成员 */
 89     /* 2.1先设置能产生哪一类事件 */
 90     set_bit(EV_KEY, buttons_dev->evbit); //此处表示能产生键盘事件
 91    set_bit(EV_REP, buttons_dev->evbit); //支持键盘重复按事件
 92     /* 2.2能产生这类操作里的哪些事件 eg: L,S,ENTER,LEFTSHIFT */
 93     set_bit(KEY_L, buttons_dev->keybit);       //#define KEY_L    38
 94     set_bit(KEY_S, buttons_dev->keybit);    //这些宏都在Input.h中定义
 95     set_bit(KEY_ENTER, buttons_dev->keybit); //支持按键回车
 96     set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);
 97 
 98     /* 3.注册input_dev 驱动设备 */
 99     input_register_device(buttons_dev);
100 
101     /* 4.硬件相关代码 */
102     init_timer(&buttons_timer);
103     buttons_timer.function = buttons_timer_function;
104     add_timer(&buttons_timer); 
105     
106     for (i = 0; i < 4; i++)
107     {
108         request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
109     }
110 
111     return 0;
112 }
113 
114 //pre3.写出口函数
115 static void buttons_exit(void)
116 {
117     int i;
118     for (i = 0; i < 4; i++)
119     {
120         free_irq(pins_desc[i].irq, &pins_desc[i]); //清中断
121     }
122     del_timer(&buttons_timer);  //删除定时器
123     input_unregister_device(buttons_dev);  //卸载设备
124     input_free_device(buttons_dev);  //释放分配给input_dev设备的空间
125 }
126 
127 
128 
129 
130 //pre4.修饰,添加属性
131 module_init(buttons_init);
132 module_exit(buttons_exit);
133 
134 MODULE_LICENSE("GPL");

 

四、测试

 1. 挂载

挂载键盘驱动后,如下图,可以通过  ls -l /dev/event*   命令查看已挂载的设备节点:

 加载了驱动之后,多出事件 event1,代表我们的按键驱动

 其中主设备号 13,次设备号是 65,

在事件处理驱动的函数中,如 Evdev.c 中的 evdev_connect 函数中,

for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);

, 其中 event 驱动本身的此设备号是从 64 开始的,如上图,内核启动时,会加载自带触摸屏驱动,所以我们的键盘驱动的次设备号 = 64+1

2. 运行

测试运行有两种,一种是直接打开 /dev/tyy1, 第二种是使用 exec 命令

方法 1:

cat /dev/tty1     //tty1:LCD 终端 , 就会通过 tty_io.c 来访问键盘驱动 , 然后打印在 tty1 终端上

方法 2:

exec 0</dev/tty1    // 将 /dev/tty1 挂载到 -sh 进程描述符 0 下 , 此时的键盘驱动就会直接打印在 tty1 终端上

3. 调试:

若测试不成功,板子又在 QT 下进行的:

1) 可以使用 vi 命令,在板子上打开记事本,按按键测试

2) 或者删除 /etc/init.d/rcS 里面有关 QT 自启动的命令,然后重启

若板子没在 QT 下进行,也无法测试成功:

1) 可以使用 hexdump 命令来调试代码

详见 NQian 博主的文章

(exec 命令详解入口地址: http://www.cnblogs.com/lifexy/p/7553228.html)

(hexdump 命令调试代码详解地址:http://www.cnblogs.com/lifexy/p/7553550.html)

 

参考:13.Linux 键盘按键驱动 (详解)

 

INPUT 输入子系统 —— 按键

INPUT 输入子系统 —— 按键

一、什么是 input 输入子系统?

    1.1. Linux 系统支持的输入设备繁多,例如键盘、鼠标、触摸屏、手柄或者是一些输入设备像体感输入等等,Linux 系统是如何管理如此之多的不同类型、不同原理、不同的输入信息的输入设备的呢?其实就是通过 input 输入子系统这套软件体系来完成的。从整体上来说,input 输入子系统分为 3 层:上层(输入事件驱动层)、中层(输入核心层)、下层(输入设备驱动层),如下图所示:

    1.2. 图中 Drivers 对应的就是下层设备驱动层,对应各种各样不同的输入设备,Input Core 对应的就是中层核心层,Handlers 对应的就是上层输入事件驱动层,最右边的代表的是用户空间

    1.3. 上层中的各个 handler(Keyboard/Mouse/Joystick/Event)是属于平行关系。由于历史原因,一种设备可能连接到多个 handler 层中,由于 event 是后出的。所有 event 实现大一统,所有输入设备都可以连接到 event handler 中

    1.4. 官方查看文档

            文档 1:kernel\Documentation\input\input.txt

            文档 2:kernel\Documentation\input\input-programming.txt

二。输入子系统框架分析

    2.1. 重要结构体

        2.1.1.  input_dev 结构体

            a. 该结构体是所有输入设备的抽象,只要是输入设备都可以用此结构体描述

struct input_dev {
    const char *name;             //  input设备的名字
    const char *phys;              //  
    const char *uniq;              //
    struct input_id id;             //  

//  这些是用来表示该input设备能够上报的事件类型有哪些   是用位的方式来表示的
    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

    unsigned int keycodemax;
    unsigned int keycodesize;
    void *keycode;
    int (*setkeycode)(struct input_dev *dev,
              unsigned int scancode, unsigned int keycode);
    int (*getkeycode)(struct input_dev *dev,
              unsigned int scancode, unsigned int *keycode);

    struct ff_device *ff;

    unsigned int repeat_key;
    struct timer_list timer;

    int sync;

    int abs[ABS_CNT];
    int rep[REP_MAX + 1];

    unsigned long key[BITS_TO_LONGS(KEY_CNT)];
    unsigned long led[BITS_TO_LONGS(LED_CNT)];
    unsigned long snd[BITS_TO_LONGS(SND_CNT)];
    unsigned long sw[BITS_TO_LONGS(SW_CNT)];

    int absmax[ABS_CNT];
    int absmin[ABS_CNT];
    int absfuzz[ABS_CNT];
    int absflat[ABS_CNT];
    int absres[ABS_CNT];

    int (*open)(struct input_dev *dev);              //    设备的open函数
    void (*close)(struct input_dev *dev);          
    int (*flush)(struct input_dev *dev, struct file *file);
    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);     //  上报事件

    struct input_handle *grab;

    spinlock_t event_lock;
    struct mutex mutex;

    unsigned int users;
    bool going_away;

    struct device dev;                 //  内置的device结构体变量

    struct list_head    h_list;    //  用来挂接input_dev 设备连接的所有handle 的一个链表头
    struct list_head    node;    //  作为链表节点挂接到  input_dev_list 链表上  (input_dev_list链表是input核心层维护的一个用来挂接所有input设备的一个链表头)
};
View Code

            2.1.2. input_handler 结构体

struct input_handler {

    void *private;            //  私有数据

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);   //  handler用于向上层上报输入事件的函数
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);            //   match 函数用来匹配handler 与 input_dev 设备
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);   //  当handler 与 input_dev 匹配成功之后用来连接
    void (*disconnect)(struct input_handle *handle);          //  断开handler 与 input_dev 之间的连接
    void (*start)(struct input_handle *handle);                    

    const struct file_operations *fops;             //  一个file_operations 指针
    int minor;                                      //  该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构
    const char *name;                               //  handler的名字

    const struct input_device_id *id_table;      //  指向一个 input_device_id  类型的数组,用来进行与input设备匹配时用到的信息
 
    struct list_head    h_list;       //  用来挂接handler 上连接的所有handle 的一个链表头
    struct list_head    node;        //  作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头)
};
View Code

            2.1.3. input_device_id 结构体

struct input_device_id {

    kernel_ulong_t flags;    //  这个flag 表示我们的这个 input_device_id 是用来匹配下面的4个情况的哪一项
                                    //  flag == 1表示匹配总线  2表示匹配供应商   4表示匹配产品  8表示匹配版本
    __u16 bustype;
    __u16 vendor;
    __u16 product;
    __u16 version;

    kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

    kernel_ulong_t driver_info;
};
View Code

    2.2. input 框架中重要函数

        2.2.1、核心模块注册 input_init

            a. 函数位于 drivers\input\input.c

            b. class_register

            c. input_proc_init

            d. register_chrdev

static int __init input_init(void)
{
    int err;

    input_init_abs_bypass();

    err = class_register(&input_class);                //  创建设备类    /sys/class/input
    if (err) {
        printk(KERN_ERR "input: unable to register input_dev class\n");
        return err;
    }

    err = input_proc_init();           //    proc文件系统相关的初始化
    if (err)
        goto fail1;

    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);       //   注册字符设备驱动   主设备号13   input_fops 中只实现了open函数,所以他的原理其实和misc其实是一样的
    if (err) {
        printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2:    input_proc_exit();
 fail1:    class_unregister(&input_class);
    return err;
}
View Code

        2.2.2. 核心层提供给设备驱动层的接口函数

            2.2.2.1. input 设备驱动框架留给设备驱动层的接口函数主要有 3 个: 

                a. input_allocate_device。分配一块 input_dev 结构体类型大小的内存 

struct input_dev *input_allocate_device(void)
{
    struct input_dev *dev;                 //   定义一个 input_dev  指针

    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);   //  申请分配内存
    if (dev) {
        dev->dev.type = &input_dev_type;          //  确定input设备的 设备类型     input_dev_type
        dev->dev.class = &input_class;                //  确定input设备所属的设备类   class
        device_initialize(&dev->dev);                   //  input设备的初始化
        mutex_init(&dev->mutex);                        //  互斥锁初始化
        spin_lock_init(&dev->event_lock);            //  自旋锁初始化
        INIT_LIST_HEAD(&dev->h_list);                 //  input_dev -> h_list 链表初始化
        INIT_LIST_HEAD(&dev->node);                 //  input_dev -> node 链表初始化

        __module_get(THIS_MODULE);
    }

    return dev;
}
View Code

                b. input_set_capability。设置输入设备可以上报哪些输入事件 

                    函数原型:input_set_capability (struct input_dev *dev, unsigned int type, unsigned int code) 

                        参数:dev 就是设备的 input_dev 结构体变量

                                  type 表示设备可以上报的事件类型 

                                  code 表示上报这类事件中的那个事件 

                                  注意:input_set_capability 函数一次只能设置一个具体事件,如果设备可以上报多个事件,则需要重复调用这个函数来进行设置,

                    例如:  input_set_capability (dev, EV_KEY, KEY_Q); // 至于函数内部是怎么设置的,将会在后面进行分析。 

                                input_set_capability(dev, EV_KEY, KEY_W); 

                                input_set_capability(dev, EV_KEY, KEY_E);

                c. input_register_device。向 input 核心层注册设备

int input_register_device(struct input_dev *dev)      //  注册input输入设备
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;                          //  定义一个  input_handler 结构体指针
    const char *path;
    int error;

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);                  //   每一个input输入设备都会发生这个事件

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);  //  清除KEY_RESERVED 事件对应的bit位,也就是不传输这种类型的事件

    /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
    input_cleanse_bitmasks(dev);           //   确保input_dev中的用来记录事件的变量中没有提到的位掩码是干净的。

    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don''t do it in input.c.
     */
    init_timer(&dev->timer);
    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
        dev->timer.data = (long) dev;
        dev->timer.function = input_repeat_key;
        dev->rep[REP_DELAY] = 250;
        dev->rep[REP_PERIOD] = 33;
    }

    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;

    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    dev_set_name(&dev->dev, "input%ld",                                  //   设置input设备对象的名字    input+数字
             (unsigned long) atomic_inc_return(&input_no) - 1);

    error = device_add(&dev->dev);         //   添加设备       例如:          /sys/devices/virtual/input/input0     
    if (error)
        return error;

    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  //  获取input设备对象所在的路径      /sys/devices/virtual/input/input_xxx   
    printk(KERN_INFO "input: %s as %s\n",
        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    kfree(path);

    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }

    list_add_tail(&dev->node, &input_dev_list);             //   链表挂接:    将 input_dev->node 作为节点挂接到 input_dev_list  链表上

    list_for_each_entry(handler, &input_handler_list, node)  //  遍历input_handler_list 链表上的所有handler
        input_attach_handler(dev, handler);                        //  将handler与input设备进行匹配

    input_wakeup_procfs_readers();                //  更新proc 文件系统

    mutex_unlock(&input_mutex);

    return 0;
}
View Code

                d. input_attach_handler 函数:

                    input_attach_handler 就是 input_register_device 函数中用来对下层的设备驱动和上层的 handler 进行匹配的一个函数,只有匹配成功之后就会调用上层 handler 中的 connect 函数

 

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;                //   定义一个input_device_id 的指针
    int error;

    id = input_match_device(handler, dev);   //  通过这个函数进行handler与input设备的匹配工作
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);  //  匹配成功则调用 handler 中的 connect 函数进行连接
    if (error && error != -ENODEV)
        printk(KERN_ERR
            "input: failed to attach handler %s to device %s, "
            "error: %d\n",
            handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}



static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;            //   定义一个 input_device_id  指针
    int i;

    for (id = handler->id_table; id->flags || id->driver_info; id++) {  //  依次遍历handler->id_table 所指向的input_device_id 数组中的各个元素
                                                                                                                    //  依次进行下面的匹配过程
        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)         //    匹配总线
            if (id->bustype != dev->id.bustype)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  //  匹配供应商
            if (id->vendor != dev->id.vendor)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)  //  匹配产品
            if (id->product != dev->id.product)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)  //  匹配版本
            if (id->version != dev->id.version)
                continue;

    //    下面的这些是匹配我们上传的事件是否属实
        MATCH_BIT(evbit,  EV_MAX);
        MATCH_BIT(keybit, KEY_MAX);
        MATCH_BIT(relbit, REL_MAX);
        MATCH_BIT(absbit, ABS_MAX);
        MATCH_BIT(mscbit, MSC_MAX);
        MATCH_BIT(ledbit, LED_MAX);
        MATCH_BIT(sndbit, SND_MAX);
        MATCH_BIT(ffbit,  FF_MAX);
        MATCH_BIT(swbit,  SW_MAX);

        if (!handler->match || handler->match(handler, dev))
            return id;        //    如果数组中的某个匹配成功了就返回他的地址
    }

    return NULL;
}
View Code

三。驱动开发者开发流程

    3.1. 内核提供的函数

        3.1.1. input_allocate_device

        3.1.2. input_register_device

        3.1.3. input_register_device

    3.2. 开发代码

        3.2.1. button_device.c

#include <linux/init.h>            // __init   __exit
#include <linux/module.h>      // module_init  module_exit
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <mach/gpio.h> 
#include <linux/leds.h>
#include <asm/string.h> 

#include <linux/platform_device.h>

#include <linux/input.h> 
#include "button_device_driver.h"

#define BUTTON_GPIO_CONFIG 0xFF

/* KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT
    * LEFT     -> EINT2    -> GPH0_2
    * DOWN     -> EINT3    -> GPH0_3
    * UP     -> KP_COL0 -> GPH2_0
    * RIGHT  -> KP_COL1 -> GPH2_1
    * MENU     -> KP_COL3 -> GPH2_3 (KEY_A)
    * BACK     -> KP_COL2 -> GPH2_2 (KEY_B)
    */

void s5pv210_button_release(struct device *dev);

struct gpioIRQ button_irq[] = 
{
    {
        "button_Left_IRQ",
        BUTTON_LEFT_IRQ,
        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
    },
    {
        "button_down_IRQ",
        BUTTON_DOWN_IRQ,
        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
    }
};
static struct s5pv210_button_platdata x210_button_pdata[] = {
    [0]={
        .name        = "button_Left",
        .gpio        = S5PV210_GPH0(2),
        .gpio_cfg    = BUTTON_GPIO_CONFIG,
        .button_code= KEY_LEFT,
        .button_irq    = &button_irq[0],
        },
    [1]={
        .name        = "button_down",
        .gpio        =  S5PV210_GPH0(3),
        .gpio_cfg    = BUTTON_GPIO_CONFIG,
        .button_code= KEY_DOWN,
        .button_irq    = &button_irq[1],
        }
};


static struct platform_device s5pv210_device_button = {
    .name    = "s5pv210-button",
    .id        = -1,
    .dev    = {
            .platform_data    = &x210_button_pdata,
            .release = s5pv210_button_release,
        },
};
void s5pv210_button_release(struct device *dev)
{
    printk(KERN_WARNING "s5pv210_button_release successful \n");
}

static int __init s5pv210_button_init(void)
{
    int ret = -1;
    printk(KERN_INFO "device.c : s5pv210_button_init successful \n");    
    ret = platform_device_register(&s5pv210_device_button);
    if (ret < 0) 
    {
        printk(KERN_WARNING "platform_add_devices fail \n");
    }
    return ret;
}

static void __exit s5pv210_button_exit(void)
{    
    printk(KERN_INFO "device.c : s5pv210_button_exit successful \n");

    platform_device_unregister(&s5pv210_device_button); 
}




module_init(s5pv210_button_init);
module_exit(s5pv210_button_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");                // 描述模块的许可证
MODULE_AUTHOR("musk");                // 描述模块的作者
MODULE_DESCRIPTION("x210 button device");    // 描述模块的介绍信息
MODULE_ALIAS("button_device");            // 描述模块的别名信息
        3.2.2. button_driver.c

#include <linux/input.h> 
#include <linux/module.h> 
#include <linux/init.h>
#include <asm/irq.h> 
#include <asm/io.h>

#include <mach/irqs.h>

#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>

#include "button_device_driver.h"
#include <linux/kernel.h> 
/*
 * POWER  -> EINT1   -> GPH0_1
 * LEFT   -> EINT2   -> GPH0_2
 * DOWN   -> EINT3   -> GPH0_3
 * UP     -> KP_COL0 -> GPH2_0
 * RIGHT  -> KP_COL1 -> GPH2_1
 * MENU   -> KP_COL3 -> GPH2_3 (KEY_A)
 * BACK   -> KP_COL2 -> GPH2_2 (KEY_B)
 */

static struct input_dev *button_dev;


static irqreturn_t button_interrupt(int irq, void *dummy) 
{ 
    
    struct platform_device *pdev = (struct platform_device *)dummy;
    struct s5pv210_button_platdata *pdata = pdev->dev.platform_data;
    if(dummy == NULL)
    {
        printk("\n button_interrupt fail;irq = %d\n",irq);
        return -1;
    }
    printk("\n irq = %d\n",irq);
    if(irq == pdata[0].button_irq->irq)
    {
        s3c_gpio_cfgpin(pdata[0].gpio, S3C_GPIO_SFN(0x00));
        input_report_key(button_dev, pdata[0].button_code,!gpio_get_value(pdata[0].gpio));
        s3c_gpio_cfgpin(pdata[0].gpio, S3C_GPIO_SFN(0x0f));
    }
    if(irq == pdata[1].button_irq->irq)
    {
        s3c_gpio_cfgpin(pdata[1].gpio, S3C_GPIO_SFN(0x00));
        input_report_key(button_dev, pdata[1].button_code,!gpio_get_value(pdata[1].gpio));
        s3c_gpio_cfgpin(pdata[1].gpio, S3C_GPIO_SFN(0x0f));
    }
    input_sync(button_dev);

    return IRQ_HANDLED; 
}


static int s5pv210_button_probe(struct platform_device *pdev)
{
    int error,ret;
    struct s5pv210_button_platdata *pdata = pdev->dev.platform_data;
    printk(KERN_INFO "button_driver.c: s5pv210_button_probe successful\n");
    if (request_irq(pdata[0].button_irq->irq, button_interrupt, pdata[0].button_irq->irq_trigger, pdata[0].button_irq->name, pdev)) 
    { 
        printk(KERN_ERR "key-s5pv210.c: Can''t allocate irq %d\n", pdata[0].button_irq->irq);
        return -EBUSY;
    }
    ret = gpio_request(pdata[0].gpio, pdata[0].name);
    if(ret)
        printk("button-driver: request %s fail", pdata[0].name);
    s3c_gpio_cfgpin(pdata[0].gpio, S3C_GPIO_SFN(pdata[0].gpio_cfg));
    
    
    
    if (request_irq(pdata[1].button_irq->irq, button_interrupt, pdata[1].button_irq->irq_trigger, pdata[1].button_irq->name, pdev)) 
    { 
        printk(KERN_ERR "key-s5pv210.c: Can''t allocate irq %d\n", pdata[1].button_irq->irq);
        return -EBUSY;
    }
        ret = gpio_request(pdata[1].gpio, pdata[1].name);
    if(ret)
        printk(KERN_ERR "button-driver: request %s fail", pdata[1].name);
    s3c_gpio_cfgpin(pdata[1].gpio, S3C_GPIO_SFN(pdata[1].gpio_cfg));
    
    
    button_dev = input_allocate_device();
    if (!button_dev)    
    { 
        printk(KERN_ERR "button.c: Not enough memory\n");
        error = -ENOMEM;
        goto err_free_irq; 
    }
    
    set_bit(EV_KEY, button_dev->evbit);

    set_bit(pdata[0].button_code, button_dev->keybit);
    set_bit(pdata[1].button_code, button_dev->keybit);

    error = input_register_device(button_dev);
    if (error) 
    { 
        printk(KERN_ERR "button_driver.c: Failed to register device\n");
        goto err_free_dev; 
    }
    return 0;
 
 err_free_dev:
    input_free_device(button_dev);
    
 err_free_irq:
    free_irq(pdata[0].button_irq->irq, pdev);
    free_irq(pdata[1].button_irq->irq, pdev);
    gpio_free(pdata[0].gpio);
    gpio_free(pdata[1].gpio);
    
    return error;
}

static int s5pv210_button_remove(struct platform_device *pdev)
{
    struct s5pv210_button_platdata *pdata = pdev->dev.platform_data;
    printk(KERN_INFO "button_driver.c: s5pv210_button_remove successful\n");
    
    free_irq(IRQ_EINT2, pdev);
    free_irq(IRQ_EINT3, pdev);
    gpio_free(pdata[0].gpio);    
    gpio_free(pdata[1].gpio);    
    input_unregister_device(button_dev);

    
    return  0;
}

static struct platform_driver s5pv210_button_driver = {
    .probe        = s5pv210_button_probe,
    .remove        = s5pv210_button_remove,
    .driver        = {
        .name    = "s5pv210-button",
        .owner    = THIS_MODULE,
    }
};

static int __init s5pv210_button_init(void) 
{ 
    return platform_driver_register(&s5pv210_button_driver);
}
static void __exit s5pv210_button_exit(void)
{
    platform_driver_unregister(&s5pv210_button_driver);
}

module_init(s5pv210_button_init); 
module_exit(s5pv210_button_exit);

MODULE_AUTHOR("musk");
MODULE_DESCRIPTION("s5pv210 button driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("button-s5pv210");
        3.2.3. button_device_driver.h

#ifndef __BUTTON_DEVICE_DRIVER_H
#define __BUTTON_DEVICE_DRIVER_H 

#define BUTTON_LEFT_IRQ  IRQ_EINT2
#define BUTTON_DOWN_IRQ  IRQ_EINT3

#include <mach/irqs.h>
#include <linux/interrupt.h>

struct gpioIRQ {
    char            *name;
    unsigned int     irq;
    unsigned long    irq_trigger;

};
struct s5pv210_button_platdata {
    char            *name;

    unsigned int    gpio;
    unsigned int     gpio_cfg;
    unsigned int    button_code;
    struct gpioIRQ     *button_irq;
};

#endif /* __ASM_ARCH_LEDSGPIO_H */
View Code

 

索引文献:https://www.cnblogs.com/deng-tao/p/6094049.html

今天关于Linux输入子系统详解linux input子系统的讲解已经结束,谢谢您的阅读,如果想了解更多关于02_03_Linux根文件系统详解、7.Linux 输入子系统分析、8. 输入子系统 ------ 键盘按键驱动程序、INPUT 输入子系统 —— 按键的相关知识,请在本站搜索。

本文标签: