linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(1)

来源:互联网 发布:首个中国网络作家村 编辑:程序博客网 时间:2024/06/01 20:55


Programming input drivers(摘于Documentation\input\input-programming.txt)

这篇文档说明的输入设备驱动的编写。

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;
}

module_init(button_init);

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;

}

/**
 * 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;


dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); 分配一个input_dev结构体
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);  增加模块引用计数
}


return dev;
}




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;

}

/**
 * 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;


__set_bit(EV_SYN, dev->evbit);设置input_dev所支持的事件类型。事件类型由input_dev的evbit成员来表示,在这里将其EV_SYN置位,表示设备支持所有的事件。常用的事件类型如下:

1. #define EV_SYN 0x00 /*表示设备支持所有的事件*/
2. #define EV_KEY 0x01 /*键盘或者按键,表示一个键码*/
3. #define EV_REL 0x02 /*鼠标设备,表示一个相对的光标位置结果*/
4. #define EV_ABS 0x03 /*手写板产生的值,其是一个绝对整数值*/
5. #define EV_MSC 0x04 /*其他类型*/
6. #define EV_LED 0x11 /*LED灯设备*/
7. #define EV_SND 0x12 /*蜂鸣器,输入声音*/
8. #define EV_REP 0x14 /*允许重复按键类型*/
9. #define EV_PWR 0x16 /*电源管理事件*/


/*
* 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);  初始化一个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;

检查getkeycode()函数和setkeycode()函数是否被定义,如果没定义,则使用默认的处理函数,这两个函数为 input_default_getkeycode()和input_default_setkeycode()。 input_default_getkeycode()函数用来得到指定位置的键值。input_default_setkeycode()函数用来设置键值。


snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);


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;
}


list_add_tail(&dev->node, &input_dev_list);调用此函数将input_dev加入全局量input_dev_list链表,  如下定义:static LIST_HEAD(input_dev_list);


list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); 此函数用来匹配input_dev和handler,只有匹配成功,才能进行下一步的关联操作。源码如下:

static int input_attach_handler(structinput_dev *dev, structinput_handler *handler)
{
const struct input_device_id *id; 输入设备的指针

struct input_device_id {

kernel_ulong_t flags;

__u16 bustype;  总线类型
__u16 vendor;   制造商ID
__u16 product;  产品ID
__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;
};
 

        int error;


if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;

首先判断handle的blacklist是否被赋值,如果被赋值,则匹配blacklist中的数据跟dev->id的数据是否匹配。blacklist是一个input_device_id*的类型,其指向input_device_ids的一个表,这个表中存放了驱动程序应该忽略的设备。即使在id_table中找到支持的项,也应该忽略这种设备。


id = input_match_device(handler->id_table, dev); 此函数数匹配handle->id_table和dev->id中的数据。handler->id_table也是一个input_device_id类型的指针,其表示驱动支持的设备列表。其中handler结构体在另一篇博客中有说明。这个函数还有后面的下篇再分析。
if (!id)
return -ENODEV;


error = handler->connect(handler, dev, id);
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;
}


input_wakeup_procfs_readers();


mutex_unlock(&input_mutex);


return 0;
}


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_exit(button_exit);


linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(2)的链接地址



原创粉丝点击