sensor驱动层 --- light sensor
Posted Achillisjack
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sensor驱动层 --- light sensor相关的知识,希望对你有一定的参考价值。
概述:
android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。知道,Linux 内核源代码版权遵循GNU License,而Android源代码版权遵循Apache License,前者在发布产品时,必须公布源代码,而后者无须发布源代码。如果把对硬件支持的所有代码都放在Linux驱动层,那就意味着发布时要公开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来说,损害是非常大的。因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android被踢出了Linux内核主线代码树中。大家想想,Android放在内核空间的驱动程序对硬件的支持是不完整的,把Linux内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android是开放系统而不是开源系统的原因。通平台sensor架构HAL层也必须满足Android系统HAL层规范,需要对它的上一层Framework层提供标准的接口,而这些接口的具体实现因平台而异;高通平台的整个Sensor HAL层代码分为了三层,分别是HAL接口层(标准的Android接口),Sensor中间层,和Sensor驱动层;
Sensor驱动层:提供了每个sensor访问底层sensor驱动的接口;
Sensor中间层:起承上启下的作用,对下负责组织和管理这些sensor,并通过物理sensor创建一些有实际功能的虚拟sensor,如指南针sensor则是由重力传感器和地磁传感器两颗实际的sensor 虚拟出来的,这些虚拟的sensor 与实际sensor对framework层而言都是一样,framework层会把这些sensor都当成独立的sensor;对上提供HAL接口层访问控制各个sensor的接口;HAL接口层:该层按照Android HAL层规范提供Framework层操作sensor的接口。
1, sensor驱动层
sensor驱动分为实际物理意义上的sensor以及由这些sensor模拟出来的virtual sensor,下面将以light sensor 和virtual sensor为例来对它们进行分析。
light sensor
所有sesnor驱动的基类SensorBase。
SensorBase代码如下,概述:
Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。知道,Linux 内核源代码版权遵循GNU License,而Android源代码版权遵循Apache License,前者在发布产品时,必须公布源代码,而后者无须发布源代码。如果把对硬件支持的所有代码都放在Linux驱动层,那就意味着发布时要公开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来说,损害是非常大的。因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android被踢出了Linux内核主线代码树中。大家想想,Android放在内核空间的驱动程序对硬件的支持是不完整的,把Linux内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android是开放系统而不是开源系统的原因。通平台sensor架构HAL层也必须满足Android系统HAL层规范,需要对它的上一层Framework层提供标准的接口,而这些接口的具体实现因平台而异;高通平台的整个Sensor HAL层代码分为了三层,分别是HAL接口层(标准的Android接口),Sensor中间层,和Sensor驱动层;Sensor驱动层:提供了每个sensor访问底层sensor驱动的接口;Sensor中间层:起承上启下的作用,对下负责组织和管理这些sensor,并通过物理sensor创建一些有实际功能的虚拟sensor,如指南针sensor则是由重力传感器和地磁传感器两颗实际的sensor 虚拟出来的,这些虚拟的sensor 与实际sensor对framework层而言都是一样,framework层会把这些sensor都当成独立的sensor;对上提供HAL接口层访问控制各个sensor的接口;
HAL接口层:该层按照Android HAL层规范提供Framework层操作sensor的接口。
1, sensor驱动层
sensor驱动分为实际物理意义上的sensor以及由这些sensor模拟出来的virtual sensor,下面将以light sensor 和virtual sensor为例来对它们进行分析。
light sensor
所有sesnor驱动的基类SensorBase。
SensorBase代码如下,
class SensorBase
protected:
const char* dev_name;
const char* data_name;
const sensor_cal_algo_t* algo;
char input_name[PATH_MAX];
int dev_fd;
int data_fd;
int64_t report_time;
bool mUseAbsTimeStamp;
sensors_meta_data_event_t meta_data;
char input_sysfs_path[PATH_MAX];•
int input_sysfs_path_len;
•••
这个类定义在hardware/qcom/sensors/SensorBase.h文件中, 是所有具体sensor驱动的基类.algo字段表示该sensor的校准算法,如果某个sensor的algo字段为非空,则底层读取的sensor的数据需要通过该算法的转换,才能上传到上层;input_name 表示sensor的event设备的文件节点路径,data_fd表示对应sensor的event设备的文件描述符,input_sysfs_path 字段表示对应sensor的设备节点路径。
它的构造函数定义在hardware/qcom/sensors/SensorBase.cpp文件中,其实现如下:
SensorBase::SensorBase(const char* dev_name,const char* data_name,
const struct SensorContext* context /* = NULL */)
: dev_name(dev_name), data_name(data_name), algo(NULL),
dev_fd(-1), data_fd(-1), mEnabled(0), mHasPendingMetadata(0)
if (context != NULL)
CalibrationManager&cm(CalibrationManager::getInstance());
algo = cm.getCalAlgo(context->sensor);
•••
一般具体sensor传入的dev_name和data_name均为NULL,data_fd会在派生类中的构造函数中赋值,该基类的其它接口都是虚拟函数,会在具体的派生类sensor中去实现,所以这里不作分析;下面来看看LightSensor类的定义:
class LightSensor : public SensorBase
InputEventCircularReader mInputReader;
sensors_event_t mPendingEvent;
bool mHasPendingEvent;
int sensor_index;
int setInitialState();
public:
LightSensor();
LightSensor(char *name);
•••
LightSensor是描述光感驱动的类,可以看到它继承于SensorBase这个基类;这里有个mInputReader变量,其类型为InputEventCircularReader,这个变量用来管理每个实际物理意义上的sensor的输入事件,下面来看看这个类的定义:
class InputEventCircularReader
struct input_event* const mBuffer;
struct input_event* const mBufferEnd;
struct input_event* mHead;
struct input_event* mCurr;
ssize_t mFreeSpace;
public:
InputEventCircularReader(size_t numEvents);
~InputEventCircularReader();
ssize_t fill(int fd);
ssize_t readEvent(input_event const** events);
void next();
;
mBuffer表示输入事件buffer,mBufferEnd指向这个buffer的结尾处,mHead指向buffer的空闲位置,mCurr指向当前读取buffer中的事件的位置,mFreeSpace表示buffer的剩余事件;
这个类的构造函数如下:
InputEventCircularReader::InputEventCircularReader(size_t numEvents)
: mBuffer(new input_event[numEvents * 2]),mBufferEnd(mBuffer + numEvents),
mHead(mBuffer),mCurr(mBuffer),mFreeSpace(numEvents)
这个构造函数很简单,就是申请输入事件buffer,和初始化指示buffer的各个指针函数fill表示从sensor对应的event字符设备中读取event事件,并用这些事件填充buffer,实现如下:
ssize_t InputEventCircularReader::fill(int fd)
size_t numEventsRead = 0;
if (mFreeSpace)
const ssize_t nread = read(fd, mHead, mFreeSpace *sizeof(input_event));
if (nread<0 || nread % sizeof(input_event))
•••
如果buffer未满,则从event设备中读取不超过空闲buffer数mFreeSpace的输入事件放入buffer中,并将mHead移动到空闲的buffer处,如果mHead超过了mBufferEnd的范围,则纠正mHead,并把超过mBufferEnd 范围的输入事件移到buffer的前面,从这里也可以看出为什么构造函数申请输入事件的buffer是2倍的最大输入事件数;如果buffer已满,则直接返回0;函数readEvent从buffer中读取事件,实现如下:
ssize_t InputEventCircularReader::readEvent(input_event const**events)
*events = mCurr;
ssize_t available = (mBufferEnd - mBuffer) - mFreeSpace;
return available ? 1 : 0;
该函数从buffer中读取mCurr指向的事件,并判断buffer中是否还有输入事件函数next的实现如下:
void InputEventCircularReader::next()
mCurr++;
mFreeSpace++;
if (mCurr >= mBufferEnd)
mCurr = mBuffer;
在readEvent调用之后,该函数会接着调用更新mCurr和mFreeSpace。LightSensor类中的方法比较简单,重点分析一些比较常用的方法;首先来看看它的构造函数
LightSensor::LightSensor(struct SensorContext *context)
: SensorBase(NULL, NULL, context),mInputReader(4),mHasPendingEvent(false),
sensor_index(GENERIC_LS)
mPendingEvent.version = sizeof(sensors_event_t);
mPendingEvent.sensor = context->sensor->handle;
mPendingEvent.type = SENSOR_TYPE_LIGHT;
memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));
data_fd = context->data_fd;
strlcpy(input_sysfs_path, context->enable_path,
sizeof(input_sysfs_path));
input_sysfs_path_len = strlen(input_sysfs_path);
mUseAbsTimeStamp = false;
这里将sensor对应的结构context中的data_fd(sensor对应event设备的文件描述符)赋值给sensor中的data_fd变量,把sensor设备路径赋值给变量input_sysfs_path, 这里有点要注意:Framework层通过PollEvent读取到输入事件后,正是通过mPendingEvent.sensor和mPendingEvent.type来判断事件的类型,后面也会讲到这一点。使能sensor的方法enable函数,其实现如下:
int LightSensor::enable(int32_t, int en)
int flags = en ? 1 : 0;
char propBuf[PROPERTY_VALUE_MAX];
property_get("sensors.light.loopback", propBuf, "0");
•••
上面是典型控制sensor驱动的方法,input_sysfs_path 存放着具体的sensor设备的文件节点路径,如LightSensor,驱动使用ltr559 ,input_sysfs_path 的值/sys/class/sensors/ltr559-light,则这里的fd就是/sys/class/sensors/ltr559-light/enable 的文件描述符对这个文件进行写操作,最终就会调用到底层驱动的sensors_enable_store函数,最终调用ltr559 的xxx_enable函数,这是属于底层驱动范畴,这里不进行深入分析,但建议先搞清楚底层驱动原理,会增加对代码理解力;HAL层控制底层驱基本上都采用的这种形式。当HAL接口层中的pollEvents层被调用时,readEvents方法就会被调用,这个函数最终会读取上层所需的输入事件信息,实现如下:
int LightSensor::readEvents(sensors_event_t* data, int count)
if (count < 1)
return -EINVAL;
//如果激活的sensor再次被激活,再上报最后一次事件
if (mHasPendingEvent)
mHasPendingEvent = false;
mPendingEvent.timestamp = getTimestamp();
•••
以ltr559 驱动为例,一般底层sensor驱动上报事件方式如下:
input_report_abs(LTR559->als_input_dev,ABS_MISC, value);
input_sync(LTR559->als_input_dev);//即 input_event(dev, EV_SYN, SYN_REPORT, 0);
该函数一方面从从sensor对应的event设备中读取input_event 事件,并将其保存到mInputReader的buffer 中,然后再从buffer 中读取input_event 事件转
换为sensors_event_t类型的事件,保存到参数data中,并返回读取到的sensors_event_t事件数。
以上是关于sensor驱动层 --- light sensor的主要内容,如果未能解决你的问题,请参考以下文章
Android Light Sensor 可检测显着的光线变化
OpenHarmony Sensor 模块Callback注册和回调全流程
自动驾驶 11-1: 光检测和测距传感器LIDAR Light Detection and Ranging Sensors