6.3 传感器BSP的结构
Android传感器系统自传感器硬件抽象层接口以下的部分是非标准的,因此传感器系统移植包括传感器的驱动程序和硬件抽象层。
Android中Sensor的驱动程序是非标准的,只是为了满足硬件抽象层的需要。
↘6.3.1 驱动程序
从Linux操作系统中驱动程序的角度,Sensor的驱动程序没有标准架构。因此在Android中构建的Sensor驱动程序也是通过非标准的Linux驱动程序实现的。
Sensor驱动程序的主要的职责是从硬件中获得传感器信息,通过在用户空间调用接口将数据传送给上层。
Sensor驱动程序可以基于如下的接口来实现。
·Event设备
·Misc杂项字符设备
·直接实现一个字符设备的主设备
·使用sys文件系统
传感器需要实现与硬件相关的机制包括读取信息、阻塞、控制。这三者对应的经典接口分别是read、poll和ioctl,事实上只有读取功能是必须实现的。
由于传感器本身是一种获取信息的工具,因此将其实现为用于输入的Event设备是很自然的方式。Event设备可以实现用于阻塞的poll调用,在中断到来时将poll解除阻塞,然后实现read调用,将数据传递给用户空间。如果使用Event设备,显然可以使用Input驱动框架中定义的数据类型。
如果使用Misc杂项字符设备或者字符设备的主设备实现传感器的驱动程序,实际上和Event实现的驱动程序是很类似的。也可以直接实现file_operations中的read、poll和ioctl接口来实现对应的功能。当然,read读取信息的功能和poll实现阻塞的功能也可以通过ioctl来实现。
提示:如果使用ioctl实现功能,为了保证用户空间调用的灵活性,阻塞和读取信息最好不要在一个命令中实现。
使用sys文件系统可以实现基本的读、写功能,对应驱动中的show和store接口实现。显然,sys文件系统也可以实现阻塞,只是通常不这样做。
↘6.3.2 硬件抽象层的内容
1.Sensor硬件抽象层的接口
sensors.h是Android传感器系统硬件层的接口,这是一个标准的Android硬件模块之一。头文件中的各个SENSOR_TYPE_*常量表示各种传感器的类型。
Sensor模块sensors_module_t的定义如下所示:
struct sensors_module_t { struct hw_module_t common; int (*get_sensors_list)(struct sensors_module_t* module, struct sensor_t const** list); };
在标准的硬件模块(hw_module_t)的基础上增加了get_sensors_list()函数,用于获得传感器列表。
sensor_t结构用于描述传感器,如下所示:
struct sensor_t { const char* name; // 传感器的名称 const char* vendor; // 传感器的Vendor int version; // 传感器的版本 int handle; // 传感器的句柄 int type; // 传感器的类型 float maxRange; // 传感器的最大范围 float resolution; // 传感器的解析度 float power; // 传感器的耗能(估计值,单位为mA) void* reserved[9]; }
sensor_t表示单一传感器的信息,与Android Java层的传感器类对应。
sensors_vec_t结构表示的是一个传感器数据向量的结构体,内容如下所示:
typedef struct { union { float v[3]; // 使用3个浮点数据表示 struct { float x; float y; float z; }; // 使用轴坐标表示 struct { float azimuth; float pitch; float roll; }; // 使用极坐标表示 }; int8_t status; // 状态信息 uint8_t reserved[3]; } sensors_vec_t;
按照如上的定义,sensors_vec_t数据结构的大小为16字节,其中第1个成员是一个共用体,可以表示为3个单精度浮点数,或者轴坐标和极坐标的单精度浮点数的格式。最后的3个字节补足了这个结构体为16字节。
sensors_event_t数据结构表示传感器事件的数据,如下所示:
typedef struct { int sensor; // sensor标识符 int32_t type; // 类型 int32_t reserved0; // 保留 int64_t timestamp; // 时间戳(单位:nanosecond) union { sensors_vec_t vector; // x,y,z矢量 sensors_vec_t orientation; // 方向(单位:度) sensors_vec_t acceleration; // 加速度(单位:m/s2) sensors_vec_t magnetic; // 磁矢量(单位:uT) float temperature; // 温度(单位:摄氏度) float distance; // 距离(单位:米) float light; // 光度(单位:SI luminous flux) float pressure; // 压力(单位:hPa) }; uint32_t reservedl[4]; // 保留 } sensors_event_t;
在sensors_event_t中,使用一个共用体表示不同的传感器各自的数据类型,Sensor则是具体传感器的标识。
sensors_poll_device_t表示传感器的设备,其定义如下所示:
struct sensors_poll_device_t { struct hw_device_t common; int (*activate)(struct sensors_poll_device_t *dev, // 激活 int handle, int enabled); int (*setDelay)(struct sensors_poll_device_t *dev, // 设置延迟 int handle, int64_t ns); int (*poll)(struct sensors_poll_device_t *dev, // 获取数据 sensors_event_t* data, int count); };
activate、set_delay属于辅助功能,poll则提供了获取数据的功能,此调用将阻塞,直到有数据返回。
2.实现和调试Sensor硬件抽象层
Sensor硬件抽象层主要是sensors_poll_device_t中的几个函数指针。
poll函数指针是传感器的核心功能,其名称就是“poll”的原本含义,调用时被阻塞,直到传感器获得数据时返回。poll调用的实现在经典的状态中是“阻塞+读取”这两个环节,都可以通过调用驱动程序的相关接口(可能是非标准的ioctl)来完成。由于传感器设备通常耗费系统资源不会很多,因此阻塞可能不需要一定实现,取而代之的是使用固定的延迟。
activate函数指针用于激活—无效;setDelay函数指针用于设置延时,也就是设置了传感器的精度。
在Android系统中,支持多种类型的传感器,同一种类型的传感器也可以支持多个,因此在硬件抽象层中也需要处理这个内容。首先需要构建一个sensor_t类型的数组表示各个传感器,在阻塞方面,如果驱动程序中实现了标准的poll接口,在用户空间的调用中可以通过调用select实现多路选择的功能。各个传感器的驱动可能没有标准的实现,甚至可能接口都是不一致的,这时就需要使用轮循的方式来处理了,必要时可以使用多线程。
nusensors是用于传感器的测试工程,代码路径为:hardware/libhardware/tests/nusensors/。由C++的代码组成,其中的内容将生成test-nusensors可执行程序。这部分程序直接打开硬件抽象层的模块进行调用,其代码与传感器JNI部分类似。但是可以在命令行直接运行,不需要其他命令行参数,它将直接调用sensors_open打开几个传感器并使能,然后调用poll从传感器中取出数据,从命令行打印出结果。