Android Sensor Input类型 (二) Device Driver

2024-04-18 10:58

本文主要是介绍Android Sensor Input类型 (二) Device Driver,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SENSORS 设备驱动

1.1 Device tree 配置

在msm8909平台上,根据硬件原理图设计得知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填充到设备结构中。
elsememsic->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(&memsic->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 deviceinput->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_pollstatic 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
}

这篇关于Android Sensor Input类型 (二) Device Driver的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/914585

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk