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