Android sensor 系统框架 (一)
这几天深入学习了Android sensor框架,以此博客记录和分享分析过程,其中难免会有错误的地方,欢迎指出!
这里主要分析KERNEL->HAL->JNI这3层的流程。主要从以下几方面开始着手:
(A) 驱动层向上提供了那些文件节点给系统层读写?
(B) 系统层通过什么方式访问驱动层?
(C) 如何统一各种sensors的接口,生成.so库?
(D) 如何加载访问.so库
(E) 实现sensor service (未写,待续)
(F) JNI接口 (未写,待续)
(A) 驱动层向上提供了那些文件节点给系统层读写?
关于linux基本驱动框架,不在本文范围,这里不再分析。学习过Linux驱动的都知道应用层要访问内核驱动,都是通过open、
read、write等系统函数操作驱动注册的文件节点进行的。这里以n2dm g-sensor的驱动为例,找出这些文件节点。文件路径:
./kernel/drivers/input/misc/n2dm.c
static int n2dm_acc_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
......
acc->input_dev->name = "accelerometer";
err = input_register_device(acc->input_dev); /* 生成 /dev/input/event2 */
......
err = misc_register(&n2dm_acc_misc_device); /* 生成 /sys/class/n2dm_acc */
......
gsensor_class = class_create(THIS_MODULE,"xr-gsensor");
gsensor_cmd_dev = device_create(gsensor_class, NULL, , NULL, "device");
device_create_file(gsensor_cmd_dev, &dev_attr_gsensor); /* 生成 /sys/class/xr-gsensor/device/gsensor */
device_create_file(gsensor_cmd_dev, &dev_attr_delay_acc); /* 生成 /sys/class/xr-gsensor/device/delay_acc */ ......
}
由以上代码可知,这个驱动生成了/dev/input/event2、/sys/class/n2dm_acc、/sys/class/xr-gsensor/device/gsensor、
/sys/class/xr-gsensor/device/delay_acc这4个文件节点,其中/sys/class/n2dm_acc文件节点用不到,可以不用理会。其余3
个文件节点的作用分别是:
1. /dev/input/event2:驱动通过这个文件节点上报输入事件,如这个是g-sensor传感器,会上报x,y,z三轴事件,数据格式:
input_report_abs(acc->input_dev, ABS_X, xyz[]);
input_report_abs(acc->input_dev, ABS_Y, xyz[]);
input_report_abs(acc->input_dev, ABS_Z, xyz[]);
input_sync(acc->input_dev);
2. /sys/class/xr-gsensor/device/gsensor数据格式:
"" /* 使能g-sensor */
"" /* 禁止g-sensor */
3. /sys/class/xr-gsensor/device/delay_acc数据格式:
十进制数值 /* g-sensor 数据刷新频率,时间ms */
(B) 系统层通过什么方式访问驱动层?
系统层肯定是通过open、read、write、ioctl等函数读写文件节点的方式访问驱动层,读写这些文件节点的代码路径在:
vendor/sprd/open-source/libs/libsensors_sprd/
这个目录下是各种传感器,如:g-sensor、光感、陀螺仪等的系统访问驱动的接口文件,这里以g-sensor为例来分析,
文件路径:
vendor/sprd/open-source/libs/libsensors_sprd/Acc_Xr.cpp
vendor/sprd/open-source/libs/libsensors_sprd/AccSensor.h
vendor/sprd/open-source/libs/libsensors_sprd/InputEventReader.cpp
vendor/sprd/open-source/libs/libsensors_sprd/InputEventReader.h
vendor/sprd/open-source/libs/libsensors_sprd/sensors.cpp
vendor/sprd/open-source/libs/libsensors_sprd/sensors.h
其中,vendor/sprd/open-source/libs/libsensors_sprd/InputEventReader.cpp文件的作用是为事件读取提供一个环形
缓冲区,vendor/sprd/open-source/libs/libsensors_sprd/sensors.cpp的作用是抽象所有的sensor为一个sensor hal。
AccSensor.h
class AccSensor : public SensorBase {
public:
AccSensor();
virtual ~AccSensor(); #ifndef ACC_NULL
enum {
Accelerometer = ,
numSensors
};
#else
static const int numSensors = ;
#endif virtual int readEvents(sensors_event_t* data, int count);
virtual bool hasPendingEvents() const;
virtual int setDelay(int32_t handle, int64_t ns);
virtual int setEnable(int32_t handle, int enabled);
virtual int64_t getDelay(int32_t handle);
virtual int getEnable(int32_t handle);
virtual int populateSensorList(struct sensor_t *list);
private:
int mEnabled;
int64_t mDelay;
int mLayout;
InputEventCircularReader mInputReader;
sensors_event_t mPendingEvent;
bool mHasPendingEvent;
char input_sysfs_path[PATH_MAX];
int input_sysfs_path_len; int setInitialState();
SensorCoordinate mSensorCoordinate;
};
AccSensor.h 文件定义了一个AccSensor类,派生于SensorBase,而Acc_Xr.cpp就是实现了这个类里面的方法
Acc_Xr.cpp
AccSensor::AccSensor() :
SensorBase(NULL, XR_ACC_INPUT_NAME),
mEnabled(), mDelay(-), mInputReader(), mHasPendingEvent(false),
mSensorCoordinate()
{
......
mPendingEvent.sensor = ID_A;
...... strcpy(input_sysfs_path, XR_GSENSOR_DEVICE_NAME);
input_sysfs_path_len = strlen(input_sysfs_path);
...... /* AccSensor的构造函数主要做了:
* 1. 构造SensorBase,扫描所有的/dev/input/event*,并判断该文件节点下是否存在一个名为XR_ACC_INPUT_NAME
* (accelerometer)的输入设备,我的设备是/dev/input/event2,如果存在,则返回该文件节点的fd
* 2. 获得input_sysfs_path文件节点为XR_GSENSOR_DEVICE_NAME(/sys/class/xr-gsensor/device/)
* 3. 初始化一个环形缓冲区
* 4. 设置sensor类型ID_A
*/
} int AccSensor::setEnable(int32_t handle, int enabled)
{
......
if(enabled==)
strcpy(enable,"");
else if(enabled==)
strcpy(enable,"");
else
strcpy(enable,"");
...... /* 设置sensor状态,1:使能,0:禁止
* 把状态值写到文件节点:/sys/class/xr-gsensor/device/gsensor
*/
strcpy(&input_sysfs_path[input_sysfs_path_len], "gsensor");
err = write_sys_attribute(input_sysfs_path, enable, );
......
mEnabled &= ~();
mEnabled |= uint32_t(newState); ......
} int AccSensor::setDelay(int32_t handle, int64_t delay_ns)
{
......
ms = delay_ns / ;
strcpy(&input_sysfs_path[input_sysfs_path_len], "delay_acc");
bytes = sprintf(buffer, "%d", ms);
write_sys_attribute(input_sysfs_path, buffer, bytes);
......
} int AccSensor::readEvents(sensors_event_t * data, int count)
{
......
/* 从文件节点/dev/input/event2中读取驱动层提交的事件,
* 并写入环形队列
*/
ssize_t n = mInputReader.fill(data_fd);
......
/* 从队列中取出一个事件 */
while (count && mInputReader.readEvent(&event)) {
......
/* 写入到sensors_event_t缓冲区 */
*data++ = mPendingEvent;
count--;
numEventReceived++;
......
/* 指向下一个事件 */
mInputReader.next();
} return numEventReceived;
} int AccSensor::populateSensorList(struct sensor_t *list)
{
/* 把该sensor加入list链表
* 所有的sensor都要加入到这个链表,以便统一管理
*/
memcpy(list, sSensorList, sizeof(struct sensor_t) * numSensors);
return numSensors;
}
(C) 如何统一各种sensors的接口,生成.so库?
通过vendor/sprd/open-source/libs/libsensors_sprd/sensors.cpp 和
vendor/sprd/open-source/libs/libsensors_sprd/sensors.h 统一管理所有
sensor,抽象所有接口
sensors.cpp
static int sensors__get_sensors_list(struct sensors_module_t *module,
struct sensor_t const **list)
{
/* 获取sensor链表首指针 */
*list = sSensorList;
/* 返回该sensor总数 */
return numSensors;
} static struct hw_module_methods_t sensors_module_methods = {
open: open_sensors
}; /* sensors.cpp最终的目的就是要构造这个结构体 */
struct sensors_module_t HAL_MODULE_INFO_SYM = {
common:{
tag: HARDWARE_MODULE_TAG,
version_major: ,
version_minor: ,
/* 加载so库时,是通过SENSORS_HARDWARE_MODULE_ID
* 找到这个结构体
*/
id: SENSORS_HARDWARE_MODULE_ID,
name: "SPRD Sensor module",
author: "Spreadtrum",
methods: &sensors_module_methods,
dso: ,
reserved:{},
},
get_sensors_list:sensors__get_sensors_list,
}; sensors_poll_context_t::sensors_poll_context_t()
{
/* 实例化一个AccSensor类的对象
* 这个对象代表一个g-sensor
*/
mSensors[acc] = new AccSensor();
/* 获得sensor索引 */
numSensors +=
mSensors[acc]->populateSensorList(sSensorList + numSensors);
/* 读取/dev/input/event2节点的fd */
mPollFds[acc].fd = mSensors[acc]->getFd();
/* 有数据输入就产生POLLIN事件 */
mPollFds[acc].events = POLLIN;
mPollFds[acc].revents = ;
...... /* 实例化一个磁力计对象 */
mSensors[ori] = new OriSensor();
numSensors +=
mSensors[ori]->populateSensorList(sSensorList + numSensors);
mPollFds[ori].fd = mSensors[ori]->getFd();
mPollFds[ori].events = POLLIN;
mPollFds[ori].revents = ; /* 实例化一个光感对象 */
...... /* 以下创建的管道,感觉没啥用,多余的
* 因为在activate中写管道,在pollEvent中度管道,
* 读到消息后并没有做什么相关处理,有知道的兄弟
* 麻烦指教一下,非常感谢!
*/
int wakeFds[];
/* 创建一个管道 */
int result = pipe(wakeFds);
ALOGE_IF(result < , "error creating wake pipe (%s)", strerror(errno));
fcntl(wakeFds[], F_SETFL, O_NONBLOCK);
fcntl(wakeFds[], F_SETFL, O_NONBLOCK);
/* 写管道的fd */
mWritePipeFd = wakeFds[]; /* 读管道的fd */
mPollFds[wake].fd = wakeFds[];
mPollFds[wake].events = POLLIN;
mPollFds[wake].revents = ;
} int sensors_poll_context_t::activate(int handle, int enabled)
{
/* 获得sensor类型 */
int drv = handleToDriver(handle);
......
/* 使能该sensor,如:如果这是g-sensor,则调用的是
* AccSensor::setEnable(int32_t handle, int enabled)
*/
err = mSensors[drv]->setEnable(handle, enabled);
/* 往管道写一个WAKE_MESSAGE消息 */
const char wakeMessage(WAKE_MESSAGE);
int result = write(mWritePipeFd, &wakeMessage, );
} int sensors_poll_context_t::setDelay(int handle, int64_t ns)
{
......
return setDelay_sub(handle, ns);
} int sensors_poll_context_t::setDelay_sub(int handle, int64_t ns)
{
/* 获得sensor类型 */
int drv = handleToDriver(handle); /* 获取sensor的状态,使能还是禁止
* 如:如果这是g-sensor,则调用的是
* AccSensor::getEnable(int32_t handle)
*/
int en = mSensors[drv]->getEnable(handle);
/* 获取sensor的输入事件频率
* 如:如果这是g-sensor,则调用的是
* AccSensor::getDelay(int32_t handle)
*/
int64_t cur = mSensors[drv]->getDelay(handle);
...... /* 设置sensor的输入事件频率
* 如:如果这是g-sensor,则调用的是
* AccSensor::setDelay(int32_t handle, int64_t delay_ns)
*/
err = mSensors[drv]->setDelay(handle, ns);
......
} int sensors_poll_context_t::pollEvents(sensors_event_t * data, int count)
{
......
do {
/* 轮询读各个sensor的输入事件 */
for (int i = ; count && i < numSensorDrivers; i++) {
SensorBase *const sensor(mSensors[i]);
......
/* 读事件 */
int nb = sensor->readEvents(data, count);
....
count -= nb;
nbEvents += nb;
data += nb;
}
/* 还有事件没读完 */
if (count) {
do {
/* 休眠等待事件输入,如果超时则退出 */
n = poll(mPollFds, numFds,
nbEvents ? : polltime);
} while (n < && errno == EINTR); if (mPollFds[wake].revents & POLLIN) {
......
/* 从管道读出msg,然而什么都没干,why? */
int result = read(mPollFds[wake].fd, &msg, );
......
}
}
} while (n && count); return nbEvents;
} static int poll__activate(struct sensors_poll_device_t *dev,
int handle, int enabled)
{
sensors_poll_context_t *ctx = (sensors_poll_context_t *) dev;
return ctx->activate(handle, enabled);
} static int poll__setDelay(struct sensors_poll_device_t *dev,
int handle, int64_t ns)
{
sensors_poll_context_t *ctx = (sensors_poll_context_t *) dev;
return ctx->setDelay(handle, ns);
} static int poll__poll(struct sensors_poll_device_t *dev,
sensors_event_t * data, int count)
{
sensors_poll_context_t *ctx = (sensors_poll_context_t *) dev;
return ctx->pollEvents(data, count);
} /** Open a new instance of a sensor device using name */
static int open_sensors(const struct hw_module_t *module, const char *id,
struct hw_device_t **device)
{
int status = -EINVAL;
sensors_poll_context_t *dev = new sensors_poll_context_t(); memset(&dev->device, , sizeof(sensors_poll_device_t)); /* 构造sensors_poll_context_t结构体 */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = ;
dev->device.common.module = const_cast < hw_module_t * >(module);
dev->device.common.close = poll__close;
dev->device.activate = poll__activate;
dev->device.setDelay = poll__setDelay;
dev->device.poll = poll__poll; *device = &dev->device.common;
status = ; return status;
}
Android.mk
......
LOCAL_MODULE := sensors.$(TARGET_BOARD_PLATFORM) #这里的TARGET_BOARD_PLATFORM是sc8830
......
mmm ./vendor/sprd/open-source/libs/libsensors_sprd -B
生成sensors.sc8830.so
这样就实现了硬件和应用的分离,应用要访问硬件是只要加载这个so库就行了
Android sensor 系统框架 (一)的更多相关文章
- Android sensor 系统框架 (二)
连载上一篇http://www.cnblogs.com/hackfun/p/7327320.html (D) 如何加载访问.so库 在前一篇博客http://www.cnblogs.com/hackf ...
- Android动画学习(一)——Android动画系统框架简介
2015-11-09补充:Drawable Animation极有可能是Frame Animation 这几天在找工作,面试的时候被问到了Android动画,之前完全没接触过这部分,直接给懵了,当然其 ...
- 11.1 Android显示系统框架_framebuffer原理及改进
1. Android显示系统框架Android Graphic UI with GPU Hardware Accelerationhttps://community.nxp.com/docs/DOC- ...
- Android的系统框架的深入认识
Android采用层次化系统架构,官方公布的标准架构如下图所示.Android由底层往上分为4个主要功能层,分别是linux内核层(Linux Kernel),系统运行时库层(Libraries和An ...
- Android的系统框架
Android的系统架构采用了分层架构的思想,如图1所示.从上层到底层共包括四层,分别是应用程序程序层.应用框架层.系统库和Android运行时和Linux内核. 图1:Android系统架构图 每 ...
- [置顶] Android Sensor系统剖析(4.0)(下)
Author:Harish_hu@qq.com 由于现在电脑上只有4.0的代码,考虑到代码差别也不大,所以下部分,就基于4.0来分析. 3:SensorManager 上一部分说过,开机后,syst ...
- 11.5 Android显示系统框架_Vsync机制_黄油计划_三个方法改进显示系统
5. Vsync机制5.1 黄油计划_三个方法改进显示系统vsync, triple buffering, vsync虚拟化 参考文档:林学森 <深入理解Android内核设计思想>第2版 ...
- 11.2 Android显示系统框架_android源码禁用hwc和GPU
2. 修改tiny4412_Android源码禁用hwc和gpu(厂家不会提供hwc和gpu的源代码,没有源代码就没法分析了,因此在这里禁用该功能并用软件库实现)最终源码: git clone htt ...
- 11.5 Android显示系统框架_Vsync机制_代码分析
5.5 surfaceflinger对vsync的处理buffer状态图画得不错:http://ju.outofmemory.cn/entry/146313 android设备可能连有多个显示器,AP ...
随机推荐
- 【页面传值6种方式】- 【JSP 页面传值方法总结:4种】 - 【跨页面传值的几种简单方式3种】
阅读目录 1. URL 链接后追加参数 2. Form 3. 设置 Cookie 4. 设置 Session JSP 页面间传递参数是项目中经常需要的,这应该算是 web 基本功吧. 试着将各种方式总 ...
- Ubuntu 16.04安装WPS
1.下载 http://community.wps.cn/download/ 根据需要对应版本进行下载deb包. 2.安装 sudo dpkg -i wps-office_10.1.0.5672~a2 ...
- Ubuntu 16.04粘贴板增强工具Parcellite
默认在Ubuntu已经有粘贴板增强工具,比如保存最近五个的复制文本历史. 1.系统默认 使用:[Ctrl]+[;] 然后用数字进行选择即可,但是只支持文本,只能保存最近5次 2.安装Parcellit ...
- FreeMarker与Spring MVC 4结合错误:Caused by: java.lang.NoClassDefFoundError: org/springframework/ui/freemarker/FreeMarkerConfiguration
添加spring-context-support的依赖到POM: <!-- spring-context-support --> <!-- https://mvnrepository ...
- 转:Redis 缓存策略
转:http://api.crap.cn/index.do#/web/article/detail/web/ARTICLE/7754a002-6400-442d-8dc8-e76e72d948ac 目 ...
- ubuntu tweak Install
ubuntu tweak 1:增加PPA源 sudo add-apt-repository ppa:tualatrix/ppa 2:編輯源列表sudo gedit /etc/apt/sources.l ...
- Solidworks如果有两个相似的图纸如何快速复制第二份图纸
如下图所示,我有两个零件,只有四个孔从螺纹孔改成了通孔(孔的尺寸改大了一点) 我已经画好了带螺纹的图纸 直接另存为,但是不要勾选另存为副本,改一下另存为的名字即可 然后打开这个另存为的工程图, ...
- 总是有人想在android上直连mysql,是猴子请来的逗比吗?
总是有人想在android上直连mysql,都是是猴子请来的逗比吗?
- SyntaxError:identifier starts immediately after numeric literal
1.错误描写叙述 2.错误原因 因为在改动方法传參的过程,须要传个id,可是这个id是字符串类型,传入的是数值型 3.解决的方法 在传參时,须要加入"",变成字符串类型 User. ...
- unable to execute dex: multiple dex files Cocos2dxAccelerometer
原文转载:http://discuss.cocos2d-x.org/t/conversion-to-dalvik-format-failed-unable-to-execute-dex-multipl ...