linux 输入子系统
input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.

###linux 输入子系统重要函数和结构体
####struct input_dev
/**
* struct input_dev - represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
* @uniq: unique identification code for the device (if device has it)
* @id: id of the device (struct input_id)
* @propbit: bitmap of device properties and quirks
* @evbit: bitmap of types of events supported by the device (EV_KEY,
* EV_REL, etc.)
* @keybit: bitmap of keys/buttons this device has
* @relbit: bitmap of relative axes for the device
* @absbit: bitmap of absolute axes for the device
* @mscbit: bitmap of miscellaneous events supported by the device
* @ledbit: bitmap of leds present on the device
* @sndbit: bitmap of sound effects supported by the device
* @ffbit: bitmap of force feedback effects supported by the device
* @swbit: bitmap of switches present on the device
* @hint_events_per_packet: average number of events generated by the
* device in a packet (between EV_SYN/SYN_REPORT events). Used by
* event handlers to estimate size of the buffer needed to hold
* events.
* @keycodemax: size of keycode table
* @keycodesize: size of elements in keycode table
* @keycode: map of scancodes to keycodes for this device
* @getkeycode: optional legacy method to retrieve current keymap.
* @setkeycode: optional method to alter current keymap, used to implement
* sparse keymaps. If not supplied default mechanism will be used.
* The method is being called while holding event_lock and thus must
* not sleep
* @ff: force feedback structure associated with the device if device
* supports force feedback effects
* @repeat_key: stores key code of the last key pressed; used to implement
* software autorepeat
* @timer: timer for software autorepeat
* @rep: current values for autorepeat parameters (delay, rate)
* @mt: pointer to multitouch state
* @absinfo: array of &struct input_absinfo elements holding information
* about absolute axes (current value, min, max, flat, fuzz,
* resolution)
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
* @sw: reflects current state of device's switches
* @open: this method is called when the very first user calls
* input_open_device(). The driver must prepare the device
* to start generating events (start polling thread,
* request an IRQ, submit URB, etc.)
* @close: this method is called when the very last user calls
* input_close_device().
* @flush: purges the device. Most commonly used to get rid of force
* feedback effects loaded into the device when disconnecting
* from it
* @event: event handler for events sent _to_ the device, like EV_LED
* or EV_SND. The device is expected to carry out the requested
* action (turn on a LED, play sound, etc.) The call is protected
* by @event_lock and must not sleep
* @grab: input handle that currently has the device grabbed (via
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
* recipient for all input events coming from the device
* @event_lock: this spinlock is is taken when input core receives
* and processes a new event for the device (in input_event()).
* Code that accesses and/or modifies parameters of a device
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
* has been registered with input core must take this lock.
* @mutex: serializes calls to open(), close() and flush() methods
* @users: stores number of users (input handlers) that opened this
* device. It is used by input_open_device() and input_close_device()
* to make sure that dev->open() is only called when the first
* user opens device and dev->close() is called when the very
* last user closes the device
* @going_away: marks devices that are in a middle of unregistering and
* causes input_open_device*() fail with -ENODEV.
* @dev: driver model's view of this device
* @h_list: list of input handles associated with the device. When
* accessing the list dev->mutex must be held
* @node: used to place the device onto input_dev_list
* @num_vals: number of values queued in the current frame
* @max_vals: maximum number of values queued in a frame
* @vals: array of values queued in the current frame
* @devres_managed: indicates that devices is managed with devres framework
* and needs not be explicitly unregistered or freed.
*/
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
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 hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int rep[REP_CNT];
struct input_mt *mt;
struct input_absinfo *absinfo;
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 (*open)(struct input_dev *dev);
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 __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
input_allocate_device()
input_allocate_device()给input device分配内存,并做一些默认初始化
/**
* 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;
//动态申请内存,使用GFP_KERNEL方式,注意GFP_KERNEL可能导致睡眠,不能在中断中调用这个函数
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); //类设备初始化,添加进input类设备模型中
mutex_init(&dev->mutex); //初始化互斥锁
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list); //初始化handle链表
INIT_LIST_HEAD(&dev->node); //初始化输入设备链表
__module_get(THIS_MODULE);
}
return dev;
}
input_register_device
input_register_device()注册input device,主要做下面几件事:
- 进一步初始化输入设备,例如连击事件;
- 注册输入设备到input类中;
- 把输入设备挂到输入设备链表input_dev_list中;
- 查找并匹配输入设备对应的事件处理层,通过input_handler_list链表
/**
* 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.
*
* Note that this function is also used to register managed input devices
* (ones allocated with devm_input_allocate_device()). Such managed input
* devices need not be explicitly unregistered or freed, their tear down
* is controlled by the devres infrastructure. It is also worth noting
* that tear down of managed input devices is internally a 2-step process:
* registered managed input device is first unregistered, but stays in
* memory and can still handle input_event() calls (although events will
* not be delivered anywhere). The freeing of managed input device will
* happen later, when devres stack is unwound to the point where device
* allocation was made.
*/
int input_register_device(struct input_dev *dev)
{
/* 用于记录输入设备名称的索引值 */
static atomic_t input_no = ATOMIC_INIT(0);
struct input_devres *devres = NULL;
/* 输入事件的处理接口指针,用于和设备的事件类型进行匹配 */
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(struct input_devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
devres->input = dev;
}
/* 默认所有的输入设备都支持EV_SYN/SYN_REPORT同步事件 */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED 不支持数据传输到应用层 */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;
dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}
/*
* 如果设备驱动没有指定重复按键(连击),系统默认提供以下的支持
* 其中init_timer为连击产生的定时器,时间到调用input_repeat_key函数
* 上报,REP_DELAY用于设置重复按键的键值,REP_PERIOD设置延时时间
*/
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",
(unsigned long) atomic_inc_return(&input_no) - 1);
/*添加设备*/
error = device_add(&dev->dev);
if (error)
goto err_free_vals;
/* 获取并打印设备的绝对路径名称 */
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;
/* 重要,把设备挂到全局的input子系统设备链表input_dev_list上 */
list_add_tail(&dev->node, &input_dev_list);
/* 核心重点,input设备在增加到input_dev_list链表上之后,会查找
* input_handler_list事件处理链表上的handler进行匹配,这里的匹配
* 方式与设备模型的device和driver匹配过程很相似,所有的input
* 都挂在input_dev_list上,所有类型的事件都挂在input_handler_list
* 上,进行“匹配相亲”*/
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
/* 如果这个值没有设置,系统把输入设备挂入设备链表 */
if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;
err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
input 事件上报相关
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件
EV_REL 0x02 相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
EV_ABS 0x03 绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
EV_MSC 0x04 其它
EV_SW 0x05 开关
EV_LED 0x11 按键/设备灯
EV_SND 0x12 声音/警报
EV_REP 0x14 重复
EV_FF 0x15 力反馈
EV_PWR 0x16 电源
EV_FF_STATUS 0x17 力反馈状态
EV_MAX 0x1f 事件类型最大个数和提供位掩码支持
Linux输入子系统提供了设备驱动层上报输入事件的函数
//上报按键事件
void input_report_key(struct input_dev *dev, unsigned int code, int value);
//上报相对坐标事件
void input_report_rel(struct input_dev *dev, unsigned int code, int value);
//上报绝对坐标事件
void input_report_abs(struct input_dev *dev, unsigned int code, int value);
//如果你觉得麻烦,你也可以只记住1个函数(因为上述函数都是通过它实现的)
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
linux触摸屏i2c设备驱动
接下来我们来看这个linux触摸屏驱动的最后一个代码,drv.c。他整合了i2c设备驱动和input设备驱动的整合. <pre class="prettyprint" id="c"> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/fs.h> #include <asm/uaccess.h>
include <linux/errno.h>
#include <linux/platform_device.h> #include <linux/clk.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/irq.h> #include <linux/interrupt.h>
define DOWN 1
#define UP 0
define SCREEN_MAX_X 800
#define SCREEN_MAX_Y 480 #define PRESS_MAX 255 #define FT5X0X_NAME “ft5x06”
static struct i2c_client ft5x06_client; static unsigned long count = 1; static struct timer_list timer_ts; /设置定时器:为了支持滑动*/ static struct input_dev *ts_test_dev; unsigned long *gph1con; unsigned int isdetect = 0;
struct work_struct ft5x06_wq; static unsigned int ts_status = DOWN;
/获取x坐标/ static unsigned int get_x_coordinate(void) { unsigned int xh,xl; xh = i2c_smbus_read_byte_data(ft5x06_client, 0x3); xl = i2c_smbus_read_byte_data(ft5x06_client, 0x4); return (((xh&0xf)«8)|xl); } /获得Y坐标/ static unsigned int get_y_coordinate(void) { unsigned int yh,yl; yh = i2c_smbus_read_byte_data(ft5x06_client, 0x5); yl = i2c_smbus_read_byte_data(ft5x06_client, 0x6); return (((yh&0xf)«8)|yl);
} /*up or down *up : 1 down : 0 */ static unsigned int get_ts_status(void) { unsigned int status; status = i2c_smbus_read_byte_data(ft5x06_client, 0x3); status = (status»6)&3; return status; } static void webee210_ft5x06_init(void) { i2c_smbus_write_byte_data(ft5x06_client, 0, 0); i2c_smbus_write_byte_data(ft5x06_client, 0xa4, 0); i2c_smbus_write_byte_data(ft5x06_client, 0xa0, 0); printk(“ft50x6 init …\n”); }
static void touch_detect(void) { unsigned int data_x, data_y; unsigned int x_data[5], y_data[5]; unsigned int i; while(1) { if(ts_status == UP) { printk(“\n up!!!!!!!!!!!!!!!!!!!!!\n”); input_report_abs(ts_test_dev, ABS_PRESSURE, 0); input_report_key(ts_test_dev, BTN_TOUCH, 0); input_sync(ts_test_dev);
return;
}
if(ts_status == DOWN )
{
printk("\n down !!!!!!!!!!!!!!!!!!!!!\n");
for(i = 0; i < 2; i++)
{
x_data[i] = get_x_coordinate();
y_data[i] = get_y_coordinate();
}
data_x = (x_data[0] + x_data[1])/2;
data_y = (y_data[0] + y_data[1])/2;
if(250< data_x && data_x <400)
data_x = data_x-40;
if(400< data_x && data_x <800)
data_x = data_x+30;
printk("x = %d\n",data_x);
printk("y = %d\n",data_y);
input_report_abs(ts_test_dev, ABS_X, data_x);
input_report_abs(ts_test_dev, ABS_Y, data_y);
input_report_abs(ts_test_dev, ABS_PRESSURE, PRESS_MAX);
input_report_key(ts_test_dev, BTN_TOUCH, 1);
input_sync(ts_test_dev);
}
printk("count %d\n",count);
if(count == 65535)
count = 1;
}
//printk("tasklet\n");
} /中断上半部分的工作/ static irqreturn_t pen_up_down_handler(int irq, void *dev_id) { if((count++)%2 == DOWN) { ts_status = DOWN; schedule_work(&ft5x06_wq);
}
else
{
ts_status = UP;
}
return IRQ_HANDLED; }
static int ft5x06_probe(struct i2c_client client, const struct i2c_device_id *id) { unsigned int ret; /探测到client,将其赋值给ft5x06_client*/ ft5x06_client = client;
/*1. 分配一个input_dev 结构体*/
ts_test_dev = input_allocate_device();
/*2. 设置支持哪些事件*/
set_bit(EV_KEY, ts_test_dev->evbit);
set_bit(EV_ABS, ts_test_dev->evbit);
/*3. 设置支持事件中的那种事件*/
set_bit(BTN_TOUCH, ts_test_dev->keybit);
/*设置触摸屏坐标的长宽*/
input_set_abs_params(ts_test_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(ts_test_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(ts_test_dev, ABS_PRESSURE, 0, PRESS_MAX, 0, 0);
/*注册*/
ret = input_register_device(ts_test_dev);
/*注册一个中断, 设为IRQ_TYPE_EDGE_BOTH,按下和放松都会触发中断*/
ret = request_irq(IRQ_EINT(14), pen_up_down_handler, IRQ_TYPE_EDGE_BOTH, "ts_test", NULL);
printk("ret_eint14_irq = %d\n", ret);
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/*将中断的下半部分添加到一个工作队列中*/
INIT_WORK(&ft5x06_wq, touch_detect);
webee210_ft5x06_init();
return 0; }
static int ft5x06_remove(struct i2c_client *client) { printk(“%s %s %d\n”, FILE, FUNCTION, LINE); free_irq(IRQ_EINT(14),NULL); return 0; }
static const struct i2c_device_id ft5x06_id_table[] = { { FT5X0X_NAME, 0 }, {} };
/* 1. 分配/设置i2c_driver */ static struct i2c_driver ft5x06_driver = { .driver = { .name = FT5X0X_NAME, .owner = THIS_MODULE, }, .probe = ft5x06_probe, .remove = ft5x06_remove,
.id_table = ft5x06_id_table, };
static int ft5x06_drv_init(void) { /* 2. 注册i2c_driver */ i2c_add_driver(&ft5x06_driver);
return 0; }
static void ft5x06_drv_exit(void) { i2c_del_driver(&ft5x06_driver); }
module_init(ft5x06_drv_init); module_exit(ft5x06_drv_exit); MODULE_LICENSE(“GPL”);
</pre>
关于i2c_driver的注册,关键在于实现.probe = ft5x06_probe,ft5x06_probe函数在探测到设备时,将被调用。