锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Android Sensor Input类型 (二) Device Driver

时间:2023-12-09 15:37:02 l6传感器

SENSORS 设备驱动

1.1 Device tree 配置

在msm根据硬件原理图设计8909平台sensors 是挂载在BLSP1 QUP1 所以需要在 i2c1 在节点下配置磁传感器mmc3416 为例;

  mpu6050@68 {     compatible = "invn,mpu6050";     reg = <0x68>;     pinctrl-names = "mpu_default","mpu_sleep";     pinctrl-0 = <&mpu6050_default>;     pinctrl-1 = <&mpu6050_sleep>;     interrupt-parent = <&msm_gpio>;     interrupts = <96 0x1>;     vdd-supply = <&pm8909_l17>;     vlogic-supply = <&pm8909_l6>;     invn,gpio-int = <&msm_gpio 96 0x1>;     invn,place = "Portrait Down";    };   mmc3416x@30 { /* Magnetic field sensor */     compatible = "memsic,mmc3416x";     reg = <0x30>;     vdd-supply = <&pm8909_l17>;     vio-supply = <&pm8909_l6>;     memsic,dir = "obverse-x-axis-forward";     memsic,auto-report;    };

从以上两棵设备树的信息可以看出 在sensors device tree 配置以配置为主,ic 的供电,i2c 从设备地址, 中断gpio 脚,以及独特的sensor 属性等,具体功能,待分析设备驱动再做简要说明。

1.2 设备驱动编译

在msm8909平台上,sensors 存储目录通常选择以下路径: msm8909/code/kernel/drivers/input/misc/ 以mpu6050 和 mmc3416 为例,需要在 msm8909/code/kernel/arch/arm/configs/msm8909-1gb_defconfig 打开中将编译的宏控,配置如下:

CONFIG_SENSORS_MPU6050=y CONFIG_SENSORS_MMC3416X=y

编译完成后,查看out 是否生成对应的目录.o 文件。

1.3 设备驱动分析

以mmc3416 例如,分析驱动

1.3.1 设备驱动注册

static struct of_device_id mmc3416x_match_table[] = {    { .compatible = "memsic,mmc3416x", },    { }, }; static struct i2c_driver mmc3416x_driver = {    .probe   = mmc3416x_probe,    .remove   = mmc3416x_remove,    .id_table  = mmc3416x_id,    .driver   = {      .owner = THIS_MODULE,      .name  = MMC3416X_I2C_NAME,      .of_match_table = mmc3416x_match_table,      .pm = &mmc3416x_pm_ops,    }, }; module_i2c_driver(mmc3416x_driver);

在mmc3416x.c在驱动中,首先使用module_i2c_driver将其注册i2c在设备总线上, 这个接口是moudle_init 和 i2c_add_driver 结合二次封装,注册i2c_driver是mmc3416x_driver

这里主要关注注册信息 of_match_table 属性;mmc3416x_match_table首个元素的compatible与device tree中配置的compatible相同,则mmc3416x_probe将被调用。

1.3.2 probe 流程分析

这里以mmc3416x.c 驱动 例如,它的逻辑相对最简单,但对它来说msm8909 平台的sensors 核心结构在驱动架构方面。 整个probe 函数 简化如下:

static int mmc3416x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mmc3416x_data *memsic;  ///定义设备结构,以后将介绍其内容 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ;  //检测申请i2c是否可用 memsic = devm_kzalloc(&client->dev, sizeof(struct mmc3416x_data), GFP_KERNEL); /// if (client->dev.of_node) mmc3416x_parse_dt(client, memsic);  //node,分析设备树的信息。     ///这里用of函数族 获取了memsic,dir及 memsic,auto-report在设备结构中填充。 else   memsic->dir = 0;   memsic->auto_report = 1; //node,使用默认值。    memsic->i2c = client;     //填充i2c client 在设备结构中。    dev_set_drvdata(&client->dev, memsic); //设置device私有数据即将填充设备结构 device去私有数据。   mutex_init(&memsic->ecompass_lock);   mutex_init(&memsic->ops_lock); ///初始化两个互斥 memsic->regmap = devm_regmap_init_i2c(client, &mmc3416x_regmap_config); ///申请后 regmap,i2c相关的读写都是通过的 regmap完成相关接口。   res = mmc3416x_power_init(memsic);  ///获取设备树配置 vdd,vio。   res = mmc3416x_check_device(memsic); //通过regmap_read读取设备id,判断设备. memsic->idev = mmc3416x_init_input(client); // 注册输入子系统   memsic->data_wq = NULL; if (memsic->auto_report) {  //如果支持自动报告,注册等待队列     dev_dbg(&client->dev, "auto report is enabled\n");     INIT_DELAYED_WORK(&memsic->dwork, mmc3416x_poll);     memsic->data_wq =     create_freezable_workqueue("mmc3416_data_work");    } memsic->cdev = sensors_cdev;  //定义好的sensors_classdev填充设备结构cdev中 memsic->cdev.sensors_enable = mmc3416x_set_enable;  //填充sensors_enable memsic->cdev.sensors_poll_delay = mmc3416x_set_poll_delay;  //填充sensors_poll_delay res = sensors_classdev_register(&memsc->idev->dev, &memsic->cdev);
//绑定device注册填充好的sensors_classde到 sensors class中。
    res = mmc3416x_power_set(memsic, false); //失能电
memsic->poll_interval = MMC3416X_DEFAULT_INTERVAL_MS; 
//设置轮询时间间隔
}

probe总结:

平台上的sensors驱动结构,probe主要做且都会做的一个步骤主要就是以下几步:

  • 封装设备结构,主要是获取电,gpio等信息,初始化用到的数据结构并填充到device私有数据中。

  • 注册输入子系统

  • 填充并注册sensors_classdev到 sensor class中去

  • 设置延时工作队列,probe结束,然后就是等待调度

1.3.3 驱动driver 分析

根据上小节的总结,我们对这几个主要的地方做更深一步的分析。 首先先来看下sensor 设备结构的构成,这里还是以mmc3416x 为例。

struct mmc3416x_data {
struct mutex ecompass_lock;
struct mutex ops_lock; //互斥锁
struct workqueue_struct *data_wq; //工作队列
struct delayed_work dwork; //延时执行的work
struct sensors_classdev cdev; //sensors_class 设备结构
struct mmc3416x_vec last;
struct i2c_client *i2c; //i2c client
struct input_dev *idev; //input device
struct regulator *vdd; //2.8v 电
struct regulator *vio; //1.8v 电
struct regmap *regmap; //获取regmap 对i2c 通讯接口的封装
int dir; //获取的dir
int auto_report; //获取的是否自动上报的配置
int enable;
int poll_interval; //轮询时间间隔的设置
int power_enabled; //上电的状态
unsigned long timeout; //超时时间
};

相关输入子系统的注册:

static struct input_dev *mmc3416x_init_input(struct i2c_client *client)
{
struct input_dev *input = NULL;    
input = devm_input_allocate_device(&client->dev);  
//申请一个 input device
    input->name = "compass";
    input->phys = "mmc3416x/input0";
    input->id.bustype = BUS_I2C;
    //填充input相关的设备信息
    __set_bit(EV_ABS, input->evbit);
    //设置输入事件为ABS类,即绝对坐标类
    input_set_abs_params(input, ABS_X, -2047, 2047, 0, 0); 
    input_set_abs_params(input, ABS_Y, -2047, 2047, 0, 0);
    input_set_abs_params(input, ABS_Z, -2047, 2047, 0, 0);
    //设置事件代码为ABS_X, ABS_Y, ABS_Z ,并设置了abs相关的坐标范围
    input_set_capability(input, EV_REL, REL_X);
    input_set_capability(input, EV_REL, REL_Y);
    input_set_capability(input, EV_REL, REL_Z);
// 设置了 输入事件为REL类,即相对坐标类,支持事件代码分别为 REL_X, REL_Y, REL_Z。
    status = input_register_device(input);  //注册这个input设备到输入子系统。
    return input;
}

接下来是填充sensors_classdev ,并注册到 sensor_class中, 这里的核心就是根据sensor_class的要求,将所有信息通过sensors_classdev传递上去, 如果是设备信息直接赋值,如果是操作函数则通过函数指针回调。关于msm8909使用的sensor_class的架构留在下一章详细分析,这里主要分析被传递的设备信息和回调的函数接口。

以下是mmc3416x用到的sensors_classdev设备信息,更多的内容请查看sensors_classdev的结构体类型定义。

static struct sensors_classdev sensors_cdev = {
    .name = "mmc3416x-mag",                     //sensor name
    .vendor = "MEMSIC, Inc",                    //厂商信息
    .version = 1,                                //版本号
    .handle = SENSORS_MAGNETIC_FIELD_HANDLE,  //
    .type = SENSOR_TYPE_MAGNETIC_FIELD,        //2表示type为 磁力传感器
    .max_range = "1228.8",                         
    .resolution = "0.0488228125",
    .sensor_power = "0.35",
    .min_delay = 10000,
    .max_delay = 10000,
    .fifo_reserved_event_count = 0,
    .fifo_max_event_count = 0,
    .enabled = 0,
    .delay_msec = MMC3416X_DEFAULT_INTERVAL_MS,
    .sensors_enable = NULL,                      
    .sensors_poll_delay = NULL,                  
};

在probe中设置了两个函数的回调:

    memsic->cdev.sensors_enable = mmc3416x_set_enable;
    memsic->cdev.sensors_poll_delay = mmc3416x_set_poll_delay;

这两个函数最终的目的最终都是为例调用工作队列的处理函数,就轮询读取sensor获取的坐标信息。

下面是这两个回调的实现:

static int mmc3416x_set_enable(struct sensors_classdev *sensors_cdev,
        unsigned int enable)
{
    struct mmc3416x_data *memsic = container_of(sensors_cdev,
            struct mmc3416x_data, cdev);
    //通过cdev获取到sensor设备结构体。
mutex_lock(&memsic->ops_lock);
if (enable && (!memsic->enable)) {
rc = mmc3416x_power_set(memsic, true); //上电
      rc = regmap_write(memsic->regmap, MMC3416X_REG_CTRL,MMC3416X_CTRL_TM);
      // 发送TM命令 在读数据之前
        memsic->timeout = jiffies;
        if (memsic->auto_report)
            queue_delayed_work(memsic->data_wq,
                &memsic->dwork,
                msecs_to_jiffies(memsic->poll_interval)); //调用延时工作队列
} 
else if ((!enable) && memsic->enable) {
        if (memsic->auto_report)
            cancel_delayed_work_sync(&memsic->dwork);
        if (mmc3416x_power_set(memsic, false)) 
            //接收到enable = 0,轮训结束,下电
    }
    memsic->enable = enable;
}
​
static int mmc3416x_set_poll_delay(struct sensors_classdev *sensors_cdev,
        unsigned int delay_msec)
{
    struct mmc3416x_data *memsic = container_of(sensors_cdev,
            struct mmc3416x_data, cdev);
    //通过cdev获取到sensor设备结构体。
    mutex_lock(&memsic->ops_lock);
    if (memsic->poll_interval != delay_msec)
        memsic->poll_interval = delay_msec;
     //根据传入的时间参数更新 延时执行的时间,就轮询间隔。
    if (memsic->auto_report && memsic->enable)
        mod_delayed_work(system_wq, &memsic->dwork,
                msecs_to_jiffies(delay_msec));
    mutex_unlock(&memsic->ops_lock);
​
    return 0;
}

最后就来分析下工作队列的处理函数是如何调用工作的,根据初始化的延时执行work的处理函数INIT_DELAYED_WORK(&memsic->dwork, mmc3416x_poll); 这个work被假如到data_wq工作队列中。

mmc3416x_poll

static void mmc3416x_poll(struct work_struct *work)
{
    int ret;
    s8 *tmp;
    struct mmc3416x_vec vec;
    struct mmc3416x_vec report;
    struct mmc3416x_data *memsic = container_of((struct delayed_work *)work,
            struct mmc3416x_data, dwork);
//通过cdev获取到sensor设备结构体。
    ktime_t timestamp;
    vec.x = vec.y = vec.z = 0;
    ret = mmc3416x_read_xyz(memsic, &vec);
​
    tmp = &mmc3416x_rotation_matrix[memsic->dir][0];
    report.x = tmp[0] * vec.x + tmp[1] * vec.y + tmp[2] * vec.z;
    report.y = tmp[3] * vec.x + tmp[4] * vec.y + tmp[5] * vec.z;
    report.z = tmp[6] * vec.x + tmp[7] * vec.y + tmp[8] * vec.z;
​
    timestamp = ktime_get_boottime();
    input_report_abs(memsic->idev, ABS_X, report.x);
    input_report_abs(memsic->idev, ABS_Y, report.y);
    input_report_abs(memsic->idev, ABS_Z, report.z);
    input_event(memsic->idev,
            EV_SYN, SYN_TIME_SEC,
            ktime_to_timespec(timestamp).tv_sec);
    input_event(memsic->idev,
        EV_SYN, SYN_TIME_NSEC,
        ktime_to_timespec(timestamp).tv_nsec);
    input_sync(memsic->idev);
    //上报信息
exit:
    queue_delayed_work(memsic->data_wq,
            &memsic->dwork,
            msecs_to_jiffies(memsic->poll_interval));
    //再次调度work,延时时间为poll_interval
}

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章