前言:
Input子系统包括标准Linux,Android核心驱动,Android相关设备驱动,G-sensor的设备驱动程序。传感器作为
一种输入设备,也是通过input系统把其数据上报给系统,或者通过input系统得到用户的配置信息。这里以传感器
为例学习input driver层。
Sensor驱动从通过I2C从寄存器中读取sensor值,然后写入/dev/input/目录下对应的文件。HAL层通过读取该文件的
值进一步传递给Framework层。每个厂商使用的具体的输入驱动硬件不同,因此软件也不尽相同,但是具体的原理都
是一样的。
代码路径: kernel\drivers\input。
Sensor模块的module_i2c_driver方法会向i2c总线挂载该驱动。
module_i2c_driver(mpu6880_i2c_driver);
mpu6880_i2c_driver结构体如下,
static struct i2c_driver mpu6880_i2c_driver = {
.driver = {
.name = "mpu6880",
.owner = THIS_MODULE,
.pm = &mpu6880_pm,
.of_match_table = mpu6880_of_match,
},
.probe = mpu6880_probe,
.remove = mpu6880_remove,
.id_table = mpu6880_ids,
};
module_i2c_driver(mpu6880_i2c_driver);
该驱动加载时的probe方法是mpu6880_probe。
mpu6880_probe方法有2个参数,
static int mpu6880_probe(struct i2c_client *client, const struct i2c_device_id *id){
第一个参数client是I2C寄存器在sensor驱动中的客户端,
第二个参数id是I2C寄存器在sensor驱动中的id,
通过这2个参数,sensor就可以从寄存器中读出sensor数据。
mpu6880_probe方法主要如下,
1,sensor模块初始化,
sensor = devm_kzalloc(&client->dev, sizeof(struct mpu6880_sensor), GFP_KERNEL);//申请内存
•••
sensor->client = client;//为mpu6880_sensor结构体赋值
sensor->dev = &client->dev;
•••
sensor->accel_dev->name = MPU6880_DEV_NAME_ACCEL;
sensor->gyro_dev->name = MPU6880_DEV_NAME_GYRO;
sensor->accel_dev->id.bustype = BUS_I2C;
sensor->gyro_dev->id.bustype = BUS_I2C;
sensor->gyro_poll_ms = pdata->gyro_poll_ms;
sensor->accel_poll_ms = pdata->accel_poll_ms;
mpu6880_sensor定义如下,
struct mpu6880_sensor {
struct i2c_client *client;//i2c客户端
struct device *dev; //设备
struct input_dev *accel_dev;//加速度sensor
struct input_dev *gyro_dev;
struct sensors_classdev accel_cdev;
struct sensors_classdev gyro_cdev;
struct mpu6880_platform_data *pdata;
•••
2,注册2个sensor,
ret = input_register_device(sensor->accel_dev);
if (ret) {
dev_err(&client->dev, "Failed to register input device\n");
goto err_free_irq;
}
ret = input_register_device(sensor->gyro_dev);
if (ret) {
dev_err(&client->dev, "Failed to register input device\n");
goto err_unregister_accel;
}
3,利用定时中断读取sensor数据,
INIT_DELAYED_WORK(&sensor->gyro_poll_work, mpu6880_gyro_work_fn);
INIT_DELAYED_WORK(&sensor->accel_poll_work, mpu6880_accel_work_fn);
新建一个内核线程,入口方法为mpu6880_accel_work_fn,重力感应器在此以后就不啰嗦了,整个和加速度完全一样。
Sensor事件在驱动中的主要分为3个部分,
1,驱动层,从I2C寄存器中读取sensor数据。
2,核心层,统一发送给input子系统处理。
3,事件层,将处理后的event写入缓存,供HAL调用。
调用流程如下,
mpu6880_accel_work_fn方法主要分为2个步骤,
1,首先调用mpu6880_read_accel_data方法读取寄存器中的sensor数据
mpu6880_read_accel_data(sensor, &sensor->axis);
2,然后调用mpu6880_accel_report方法进行处理。
mpu6880_accel_report(sensor);
mpu6880_read_accel_data方法如下,
static void mpu6880_read_accel_data(struct mpu6880_sensor *sensor,
struct axis_data *data)
{
u16 buffer[3]; //缓存
mpu6880_read_reg(sensor->client, sensor->reg.raw_accel,
(u8 *)buffer, MPU6880_RAW_ACCEL_DATA_LEN); //读取sensor数据
data->x = be16_to_cpu(buffer[0]);
data->y = be16_to_cpu(buffer[1]);
data->z = be16_to_cpu(buffer[2]);
}
首先调用mpu6880_read_reg方法从寄存器中读取数据,然后将数据写入data结构体中。
mpu6880_read_reg方法如下,
static int mpu6880_read_reg(struct i2c_client *client, u8 start_addr,
u8 *buffer, int length)
{
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &start_addr,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = buffer,
},
};
return i2c_transfer(client->adapter, msg, 2);
}
首先构造一个i2c_msg结构体,然后直接调用i2c_transfer方法读取数据, i2c_transfer是系统方法。
这样,寄存器的数据终于写入mpu6880_sensor结构体的axis_data结构体。
mpu6880_accel_report方法的主要逻辑如下,
input_report_abs(sensor->accel_dev, ABS_X, (sensor->axis.x )); //发送x轴加速度
input_report_abs(sensor->accel_dev, ABS_Y, (sensor->axis.y )); //发送y轴加速度
input_report_abs(sensor->accel_dev, ABS_Z, (sensor->axis.z )); //发送z轴加速度
input_event(sensor->accel_dev, EV_SYN, SYN_TIME_SEC, ktime_to_timespec(ts).tv_sec);
input_event(sensor->accel_dev, EV_SYN, SYN_TIME_NSEC, ktime_to_timespec(ts).tv_nsec);
input_sync(sensor->accel_dev);
首先发送三个方向上的加速度,然后发送系统的当前时间,获取系统当前时间如下,
ktime_t ts;
ts = ktime_get_boottime();
注意ktime_t结构体, tv_sec变量单位是s, tv_nsec单位是ns。这2个值同一单位相加就是1970年1月1日
到当前系统sensor数据上报的时间。
input.h的input_report_abs方法也是调用input.c的input_event方法进行处理。
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
接下来的事就交由input.c 来做了。