sensor驱动层 --- light sensor

时间:2021-08-31 00:49:50

概述:

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事件数。