struct list_head node; //该链表头用于将设备链接到input_dev_list
};
Input_dev是一个很强大的结构体,它把所有的input设备(触摸屏、键盘、鼠标等)的信息都考虑到了,对于触摸屏来说只用到它里面的一部分而已,尤其是加粗的部分,注意该结构体中最后两行定义的两个list_head结构体,list_head在/linux/list.h中有定义,深入跟踪
struct list_head {
struct list_head *next, *prev;
};
该结构体内部并没有定义数据而只定义了两个指向本身结构体的指针,预先说明一下,所有的input device在注册后会加入一个input_dev_list(输入设备链表),所有的eventhandler在注册后会加入一个input_handler_list(输入处理程序链表),这里的list_head主要的作用是作为input_dev_list和input_handler_list的一个节点来保存地址。Input_dev_list和input_handler_list之间的对应关系由input_handle结构体桥接,具体后面说明。
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
const struct file_operations *fops; //提供给用户对设备操作的函数指针
int minor;
char *name;
struct input_device_id *id_table; //与input_dev匹配用的id
struct input_device_id *blacklist; //标记的黑名单
struct list_head h_list; //用于链接和该handler相关的handle
struct list_head node; //用于将该handler链入input_handler_list
};
input_handler顾名思义,它是用来处理input_dev的一个结构体,相关的处理函数在结构里内部都有定义,最后两行定义的list_head结构体作用同input_dev所定义的一样,这里不再说明。
注:input_device_id结构体在/linux/mod_devicetable.h中有定义
struct input_handle {
void *private;
int open; //记录设备打开次数
char *name;
struct input_dev *dev; //指向所属的input_dev
struct input_handler *handler; //指向所属的input_handler
struct list_head d_node; //用于链入所指向的input_dev的handle链表
struct list_head h_node; //用于链入所指向的input_handler的handle链表
};
可以看到input_handle中拥有指向input_dev和input_handler的指针,即input_handle是用来关联input_dev和input_handler。为什么用input_handle来关联input_dev和input_handler而不将input_dev和input_handler直接对应呢?因为一个device可以对应多个handler,而一个handler也可处理多个device。就如一个触摸屏设备可以对应event handler也可以对应tseve handler。
input_dev、input_handler、input_handle的关系如下图2所示。
图2 input_dev,input_handler,input_handle关系图
第二章、input device的注册
Input device的注册实际上仅仅只有几行代码,因为在input.c中已经将大量的代码封装好了,主需要调用几个关键的函数就能完成对input device的注册。
在xxx_ts.c中预先定义全局变量struct input_dev tsdev;然后进入到初始化函数
static int __init xxx_probe(struct platform_device *pdev)
{
…
if (!(tsdev = input_allocate_device()))
{
printk(KERN_ERR "tsdev: not enough memoryn");
err = -ENOMEM;
goto fail;
}
…
tsdev->name = "xxx TouchScreen"; //xxx为芯片型号
tsdev ->phys = "xxx/event0";
tsdev ->id.bustype = BUS_HOST; //设备id,用于匹配handler的id
tsdev ->id.vendor = 0x0005;
tsdev ->id.product = 0x0001;
tsdev ->id.version = 0x0100;
tsdev ->open = xxx_open;
tsdev ->close =xxx_close;
tsdev ->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN); //设置支持的事件类型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(tsdev, ABS_X, 0, 0x400, 0, 0); //限定绝对坐标X的取值范围
input_set_abs_params(tsdev, ABS_Y, 0, 0x400, 0, 0); //同上
input_set_abs_params(tsdev, ABS_PRESSURE, 0, 1000, 0, 0); //触摸屏压力值范围
…
If(input_register_device(tsdev) == error) //注册设备
goto fail;
…
fail:
input_free_device(tsdev);
printk(“ts probe failedn”);
return err;
}
先看该函数中的tsdev = input_allocate_device(),深入最终,该函数在input.c中有定义
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->dynalloc = 1;
dev->cdev.class = &input_class;
class_device_initialize(&dev->cdev);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
}
return dev;
}
学过C语言应该都知道malloc函数(开辟内存空间),而这里的kzalloc也类似于C语言中的malloc一样,是linux内核空间分配内存函数,后面的GFP_KERNEL标志意为常规的内存分配,更多的分配标志可参阅先关资料(我也不懂)。再回到前面的函数中来,接着后面的代码为对tsdev结构体中的成员进行赋值初始化,赋值完成后就要开始进入主题:注册设备了,进入到函数input_register_device(tsdev),该函数在input.c中有定义。
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0); //定义原子变量,禁止线程并发访问
struct input_handle *handle; //定义一些变量备后文使用
struct input_handler *handler;
struct input_device_id *id;
const char *path;
int error;
if (!dev->dynalloc) {
printk(KERN_WARNING "input: device %s is statically allocated, will not registern"
"Please convert to input_allocate_device() or contact dtor_core@ameritech.netn",
dev->name ? dev->name : "");
return -EINVAL;
}
mutex_init(&dev->mutex); //互斥锁初始化,防止临界区代码被并发访问
set_bit(EV_SYN, dev->evbit); //设置支持同步事件,input设备全部默认支持同步事件
/*
* 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;
}
[1].. [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]