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函数在探测到设备时,将被调用。