输入设备是与系统交互的设备,这些设备包括按钮、键盘、触摸屏、鼠标等。他们的工作方式是发送事件、输入内核在系统上捕获和传播。
一、输入设备结构
输入设备在内核中表示为struct input_dev{}实例:
struct input_dev {
const char *name; // 设备的名称
const char *phys; // 系统层次结构中设备的路径
ulong evbit[BITS_TO_LONGS(EV_CNT)];
/* evbit是设备支持的位图,有EV_KEY(键盘), EV_REL(鼠标), EV_ABS(操纵杆),
set_bit(EV_KEY, my_input_dev->evbit);
set_bit(EV_REL, my_input_dev->evbit);
set_bit(EV_ABS, my_input_dev->evbit);
*/
ulong keybit[BITS_TO_LONGS(KEY_CNT)]; // 支持KEY_0, KEY_A, KEY_B
ulong relbit[BITS_TO_LONGS(REL_CNT)]; // 支持REL_X, REL_Y, REL_Y,REL_RX等
ulong absbit[BITS_TO_LONGS(ABS_CNT)]; // 支持ABS_X, ABS_Y等
ulong mscbit[BITS_TO_LONGS(MSC_CNT)]; // 用于支持EV_MSC类型的设备,支持其他位图
uint repeat_kesy; // 存储最后一次安静的建码
int rep[REP_CNT]; // 自动重复参数的当前值
struct input_absinfo *absinfo;
/* absinfo中存储关于绝对坐标轴的信息(当前值、最小值、最大值、平坦值、模糊度和分辨率)
应使用input_set_abs_params()函数设置这些值
void input_set_abs_params(struct input_dev *dev, uint axis, int min,
int max, int fuzz, int flat);min和max是指下限值
和上限值,fuzz是指输入设备的指定通道上预期噪声。
#define ABSMAX_ACC_VAL 0x01ff
#define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL)
[...]
set_bit(EV_ABS, idev->evbit);
input_set_abs_param(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_abs_param(idev, ABS_y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_abs_param(idev, ABS_z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
*/
ulong key[BITS_TO_LONGS(KEY_CNT)]; // 反应设备按键/按钮的状态
void (*open)(struct input_dev *dev); // 第一个用户调用input_open_device时使用的方法
void (*close)(struct input_dev *dev);// 最后一个用户调用input_close_device时使用的方法
uint users; // 存储打开此设备的用户数量
struct device dev;
uint num_vals; // 当前帧中的排队的值的数量
uint max_vals; // 帧中排队的值的最大数量
struct input_value *vals; // 当前帧中排队的值的数组
bool devres_managed; // 表示当前设备使用devres框架管理
};
二、分配和注册输入设备
1. input_dev设备的分配和注册
// 分配、注册、注销和释放设备内存的函数
struct input_dev *input_alloc_device(void);
struct input_dev *devm_input_alloc_device(struct device *dev);
void input_free_device(struct input_dev *);
int input_register_device(struct input_dev *dev);
void input_unregister_device(struct input_dev *dev);
// 下面是i2c设备上输入设备的probe函数示例
struct input_dev *idev;
int error;
idev = input_alloc_device();
if (!idev) return -ENOMEM;
idev->name = BMA150_DRIVER;
idev->phys = BMA150_DRIVER "/input0";
idev->id.bustype = BUS_I2C;
idev->dev.parent = &client->dev;
set_bit(EV_ABS, idev->evbit);
input_set_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
error = input_register_device(idev);
if (error) goto err_free_mem;
error = request_threaded_irq(&client->irq, NULL, my_irq_thread,
IRQ_TRIGGER_RISING | IRQF_ONESHOT, NULL);
if (error) toto err_unregister_dev;
[...]
}
2. input_polled_dev设备的分配和注册
轮询输入设备时特殊类型的输入设备,它依靠轮询来感知设备状态的变化。而一般的输入设备类型则依靠IRQ来检测变化,并将事件发送到输入内核。轮询输入设备在input内核中描述为struct input_pooled_dev,其定义如下:
struct input_polled_dev {
void *private;
void (*open)(struct input_polled_dev *dev); // 用于启用轮询设备
void (*close)(struct input_polled_dev *dev); // 不再轮询时使用,使设备进入低功耗状态
void (*poll)(struct input_polled_dev *dev); // 每当设备被轮询时调用。
uint poll_interval; // msec,不设置时其默认值为500ms
uint poll_interval_min;
uint poll_interval_max;
struct input_dev *input;
bool devres_managed;
};
// 结构体的分配和释放,在分配后主要要设置其input字段
struct input_polled_dev *input_allocate_polled_device(void);
struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev);
void input_free_polled_device(struct input_polled_dev *dev);
// 注册和释放该结构
int input_register_polled_device(struct input_polled_dev *dev);
void input_unregister_polled_device(struct input_polled_dev *dev);
轮询输入设备的分配和使用
struct my_struct {
struct gpio_desc *reset_btn_desc;
struct input_polled_dev *poll_dev;
};
static int button_probe(struct platform_device *pdev) {
struct my_struct *myst;
struct input_dev *idev;
int retval;
myst = dev_kzalloc(&pdev->dev, sizeof(*myst), GFP_KERNEL);
if (!my_st) return -ENOMEM;
myst->poll_dev = devm_input_allocate_polled_device(&pdev->dev);
if (!my->poll_dev) return -ENOMEM;
myst->reset_btn_desc = gpiod_get(&pdev->dev, "reset", GPIOD_IN);
myst->poll_dev->private = ms;
myst->poll_dev->poll = my_btn_poll;
myst->poll_dev->poll_interval = 200;
myst->poll_dev->open = my_btn_poll;
input_dev = myst->poll_dev->input;
input_dev->name = "System Reset Btn";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &pdev->dev;
set_bit(EV_KEY, input_dev->evbit);
set_bit(BTN_0, input_dev->keybit);
retval = devm_input_register_polled_device(&pdev->dev, myst->poll_dev);
return retval;
}
三、产生和报告输入事件
输入设备根据可支持事件的不同,内核提供相应的API将其报告给input内核。
// 报告事件函数input_report_xxx
void input_report_abs(struct input_dev *dev, uint code, int val);
void input_report_rel(struct input_dev *dev, uint code, int val);
void input_report_key(struct input_dev *dev, uint code, int val);
// 报告完事件后,应调用input_sync()进行同步
void input_sync(struct input_dev *dev);
// 示例,报告EV_KEY事件
input_report_key(poll_dev->input, BTN_0, gpiod_get_Value(ms->rest_btn_desc)&1);
// 示例,报告EV_ABS事件
input_report_abs(nunchunk->input, ABS_X, x_value);
input_report_abs(nunchunk->input, ABS_Y, y_value);
input_report_abs(nunchunk->input, ABS_Z, z_value);
// 示例,报告EV_REL事件
input_report_rel(nunchunk->input, REL_X, (nunchunk->report.joy_x-128)/10);
bma150数字加速度传感器报告事件的驱动程序如下:
static void threaded_report_xyz(struct bma150_data *bma150) {
u8 data[DATA_SIZE];
s16 x,y,z;
s32 ret;
ret = i2c_smbus_read_i2c_block_data(bma150->client, BMA150_ACC_X_LSB_REG,
DATA_SIZE, data);
if (ret!= DATA_SIZE) return;
x = ((0xc0 & data[0]) >> 6) | (data[1]<<2);
y = ((0xc0 & data[2]) >> 6) | (data[3]<<2);
z = ((0xc0 & data[4]) >> 6) | (data[6]<<2);
x = (s16) (x<<6)>>6;
y = (s16) (y<<6)>>6;
z = (s16) (z<<6)>>6;
input_report_abs(bma150->input, ABS_X, x);
input_report_abs(bma150->input, ABS_Y, y);
input_report_abs(bma150->input, ABS_Z, z);
input_sync(bma150->input);
}
在轮询设备的poll函数中汇报事件的示例如下:
static void my_btn_poll(struct input_polled_dev *poll_dev) {
struct my_struct myst = poll_dev->private;
struct i2c_client = myst->client;
input_report_key(poll_dev->input, BTN_0, gpiod_get_value(
myst->reset_btn_desc)&1);
input_sync(poll_dev->input);
}
四、input设备的用户空间接口
每个注册的输入设备都有/dev/input/event<X>字符设备表示,从中可以读取来自内核的事件,读取该文件的应用程序会得到格式为struct input_event{}的事件record:
struct input_event {
struct timeval time;
u16 type; // 事件类型,EV_KEY, EV_ABS, EV_REL
u16 code; // 事件代码:REL_X, ABS_X, KEY_BACKSPACE, BTN_0等
s32 value;// EV_REL:其值相对变化;EV_ABS: 其值是新的绝对值;EV_KEY:按键按下为1,按键释放为0,按键自动重复设置为2
};