对于android模拟器开发环境的搭建这里我就不多说了,网上google下一大堆,还有就是android 模拟器的kernel使用的是goldfish的kernel,可以使用git得到源码,然后就可以编译了,大家还是可以参考罗老师的博客。。。
在这里我准备编写一个温度传感器的虚拟driver,之前写过g-sensor和light sensor,所以不想写了,换个新鲜的,其实驱动架构都是一样的,OK 分化不多说,下面就介绍一下这个驱动。
在这里,我比较偷懒的使用了linux的一个iio子系统,这是一个不成熟的子系统,所以被放到源码陌路下面的/drvers/staging中,对于这个子系统,我也只是粗略的看过它的驱动模型,好吧 ^0^,不过个人觉得这个子系统还是蛮简单使用的,而却里面的api不是很多,相信大家随便分析下就能搞懂了。
相信大家看到了这个头文件就差不多知道驱动是怎么写的了吧,我选用的是platform device driver,driver layer向user space传送数据时通过input sybsystem传送的,这也是android sensor驱动比较主流的做法,还有一些做法是直接往自己创建的sysfs中写数据,这也是一中比较简单的做法,事件的触控方式我选用的是poll方式,因为这里我写的驱动是一个虚拟的设备,一般出发方式会选用中断触发,而我这个驱动选择每隔一段时间往user space上报数据,时间间隔就是这里的POLL_INTERVAL这个宏设定的。
这边就是定义了probe和remove,真实的设备的话还有会suspend,resume,early_suspend,late_resume等回调函数,在适当的时间会回调到这些函数(犀利的读者可能看到了这边remove我没有去实现,哈哈,我比较懒,不过大家要有一个良好的习惯,不要学我)。但是在这边注册了platform的驱动,是去找哪的platform设备呢?当然是我们自己要去实现啦,通常device端我们都会在板级的文件中去定义,我们这里是:
大家注意。这边的name和driver中platform_driver中name用该一样,不然他们怎么可以绑定在一起呢,不然他们怎么会找到对方呢,有缘千里来相会嘛,对不?
这边做的都是一些初始化的事情,我们这边首先给我们的机构体分配内存,然后给iio device分配空间,然后注册iio device,然后注册input_polled_device这里可以参考input)poll的源码,主要就是内嵌了一个工作队列来poll数据,这里不多说读者可以自行去分析。
之前我们已经写好了自己的driver,现在我们要在android下测试我们的tool。
这里我使用extern下面去编译生成一个tool,在adb shell中可以执行的,来抓取我们的温度值。
这一步相对简单,可以看做是linux的应用程序,附代码:
/external/temperature/temperature.c
-
"color:#cc33cc;">#include
-
#include
-
#include
-
#include
-
-
-
-
-
-
-
-
-
-
-
int main(void)
-
{
-
struct input_event ev_temp;
-
int fd = open("/dev/input/event2", O_RDWR);
-
-
while(1)
-
{
-
int count = read(fd, &ev_temp, sizeof(struct input_event));
-
if(EV_ABS == ev_temp.type && ABS_PRESSURE == ev_temp.code)
-
{
-
printf("time : %ld, %d", ev_temp.time.tv_sec, ev_temp.time.tv_usec);
-
printf(" Current Temperature: %d \n", ev_temp.value);
-
}
-
}
-
return 0;
-
}
-
这边我就不多说了,大家都能看懂,接下来是android的makefile,
-
/external/temperature/Android.mk
-
"code" class="cpp">"color:#cc33cc;">LOCAL_PATH := $(call my-dir)
-
-
include $(CLEAR_VARS)
-
-
LOCAL_MODULE_TAGS := optional
-
-
LOCAL_MODULE := temperature
-
-
LOCAL_SRC_FILES := $(call all-subdir-c-files)
-
-
include $(BUILD_EXECUTABLE)
然后编译一下之后会在out/.../generic/system/bin下生成这个tool,
-
"code" class="cpp">"color:#cc33cc;">root@jay:/home/jay# adb shell
-
#
-
#
-
#
-
#
-
# temperature
-
time : 81, 292520 Current Temperature: 1
-
time : 84, 40953 Current Temperature: 2
-
time : 86, 61726 Current Temperature: 3
-
time : 88, 42323 Current Temperature: 4
-
time : 90, 61805 Current Temperature: 5
-
^C
-
#
这样就完成了,接下来是我们temperature 的HAL,敬请期待。。。
前面,带着大家一起写了一个temperature sensor的驱动,已经一个测试tool来测试这个驱动,基本功能已经ok,若还有问题的可以参考前面2篇文章,在这里我们要在HAL层中添加我们的设备,来跟framework中的代码连接起来。
在开始摆代码之前我觉得有必要啰嗦几句,HAL层我个人觉得是个比较重要的东西,虽然这边现在还不是很成熟,还不是很规范,但是google还是做了很大力气针对HAL的。
首先来介绍一下android HAL 层,
Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。下图为整个android 架构:
这个是google定义的android整个系统的架构,大家可以看到HAL层起了一个承上启下的作用,HAL层主要是针对一些传感器和一些个硬件而存在的,其实也可以绕过HAL层直接使用JNI来实现从驱动到framework这个过程,但是我们这里主要是讲android中的sensor,针对标准的android而言,sensor都是在HAL中添加的,个人理解还是呼应硬件厂商的建议吧,毕竟这部分代码还可以不开源的。
好了,开源不开源什么的,我是不去关心,对我影响不大,我们还是接着看HAL。
Android硬件抽象层,从下到上涉及到了Android系统的硬件驱动层、硬件抽象层、运行时库和应用程序框架层等等,下图描述了硬件抽象层在Android系统中的位置,以及它和其它层的关系:
现在的 libhardware 作法,使用了stub的概念。stub 虽然仍是以 *.so 檔的形式存在,但 HAL 已经将 *.so 档隐藏起来了。Stub 向 HAL提供操作函数(operations),而 runtime 则是向 HAL 取得特定模块(stub)的 operations,再 callback 这些操作函数。HAL的实现主要在hardware.c和hardware.h文件中。实质也是通过加载 *.so 库。从而呼叫 *.so 里的符号(symbol)实现。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
到此,都是些理论的东西,实在是不想讲这些条条杠杠的,从小语文就不好的我,最讨厌的就是这些理论的东西了,实践才是真理呢。。。
好了接下来轮到我们的sensor的HAL层了,这里先列出来主要用到的代码:
。。。。。。
讲漏了,这边sensor的HAL层代码我参考的是freescale的BSP,因为个人感觉比较容易懂,哈哈。
先给下载链接,不然大家看不到完整的代码。
代码目录如下:
sensor/├── AccelSensor.cpp├── AccelSensor.h├── Android.mk├── InputEventReader.cpp├── InputEventReader.h├── LightSensor.cpp├── LightSensor.h├── SensorBase.cpp├── SensorBase.h├── sensors.cpp├── sensors.h├── TemperatureSensor.cpp└── TemperatureSensor.h
这里我们要修改以及添加的是三个文件:
sensor.cpp TemperatureSensor.cpp TemperatureSensor.h
好了,接下来就带大家分析下android sensor的HAL层!!!
先看一下/hardware/libhareware/hardware.c和/hardware/libhardware/include/hardware/hardware.h
这2个文件是android hal层最重要的两个文件,其中定义了hal层主要要实现的3个结构体,
-
struct hw_module_t;
-
struct hw_module_methods_t;
-
struct hw_device_t;
-
-
-
-
-
-
-
typedef struct hw_module_t {
-
-
uint32_t tag;
-
-
-
uint16_t version_major;
-
-
-
uint16_t version_minor;
-
-
-
const char *id;
-
-
-
const char *name;
-
-
-
const char *author;
-
-
-
struct hw_module_methods_t* methods;
-
-
-
void* dso;
-
-
-
uint32_t reserved[32-7];
-
-
} hw_module_t;
-
-
typedef struct hw_module_methods_t {
-
-
int (*open)(const struct hw_module_t* module, const char* id,
-
struct hw_device_t** device);
-
-
} hw_module_methods_t;
-
-
-
-
-
-
typedef struct hw_device_t {
-
-
uint32_t tag;
-
-
-
uint32_t version;
-
-
-
struct hw_module_t* module;
-
-
-
uint32_t reserved[12];
-
-
-
int (*close)(struct hw_device_t* device);
-
-
} hw_device_t;
其实标准的android hal就是实现这3个结构体,然后设置一些回调函数,最后把数据poll到framework中。
好,接下来看下sensor中是怎么封装的:
在/hardware/libhardware/include/hardware/sensors.h 中定义了几个结构体,其中都包含了上面的3个结构体,所以我们sensor module只需要去实现sensor.h中的这几个就OK了。
下面提一下如何添加我们的temperature sensor,首先是hardware/libhardware/modules/sensor/sensors.cpp 中sensor list中添加我们的sensor的信息,
-
-
static const struct sensor_t sSensorList[] = {
-
{ "Analog Devices ADXL345/6 3-axis Accelerometer",
-
"ADI",
-
1, SENSORS_ACCELERATION_HANDLE,
-
SENSOR_TYPE_ACCELEROMETER, RANGE_A, CONVERT_A, 0.145f, 200, { } },
-
{ "Android Light sensor",
-
"Android",
-
1, SENSORS_LIGHT_HANDLE,
-
SENSOR_TYPE_LIGHT, 16000.0f, 1.0f, 0.35f, 0, { } },
-
-
{ "Android Temperature Sensor",
-
"Android",
-
1, SENSORS_TEMPERATURE_HANDLE,
-
SENSOR_TYPE_TEMPERATURE, 100.0f, 1.0f, 1.0f,0, { }},
-
-
};
这里面的Android Temperature Sensor就是我们自己添加的sensor,然后我们去实现我们的temperature sensor的功能,也就是填充一些结构体和实现一些回调函数,这里我挑重要的讲。
-
int TemperatureSensor::readEvents(sensors_event_t* data, int count)
-
{
-
if(count<1)
-
return -EINVAL;
-
-
if(mHasPendingEvent){
-
mHasPendingEvent = false;
-
mPendingEvent.timestamp = getTimestamp();
-
*data = mPendingEvent;
-
return mEnabled ? 1:0;
-
}
-
ssize_t n = mInputReader.fill(data_fd);
-
if(n<0)
-
return n;
-
-
int numEventReceived = 0;
-
input_event const* event;
-
DEBUG("count: %d\n",count);
-
while(count && mInputReader.readEvent(&event)){
-
int fd=open("/dev/input/event1", O_RDONLY);
-
if(fd<0){
-
DEBUG("readEvents: open event2 failed...\n");
-
return fd;
-
}
-
int ret=read(fd,&event,sizeof(event));
-
if(ret<sizeof(event)){
-
DEBUG("readEvent read failed....\n");
-
return ret;
-
}
-
close(fd);
-
int type=event->type;
-
if(type == EV_ABS){
-
DEBUG("Current Temp: %d\n",event->value);
-
mPendingEvent.temperature = (float)(event->value);
-
}else if(type==EV_SYN){
-
mPendingEvent.timestamp = timevalToNano(event->time);
-
if( (mPendingEvent.temperature != mPreviousTemperature)){
-
*data++ = mPendingEvent;
-
count--;
-
numEventReceived++;
-
mPreviousTemperature = mPendingEvent.temperature;
-
DEBUG("Current Temp: %d\n",(int)mPendingEvent.temperature);
-
}
-
}else
-
DEBUG("temperature : unknow event...\n");
-
mInputReader.next();
-
}
-
return numEventReceived;
-
}
大家看了Temperature.cpp就应该知道我这里实现HAL层poll数据最主要的就是上面这个函数,这边其实就是linux的应用层的写法,open input 节点,然后read data,在poll给framework层。
这里我要提醒大家,如果对自己不够有信心的话第一次写hal层代码的时候最好多加点debug message,因为在hal层的调试比较麻烦,出现的错误会直接导致系统不断重启,不会告诉你错哪,所以,最好自己加debug message来调试。
代码方面大家可以看下Temperature.h和sensor.cpp这两个文件里面要实现的一些类和结构体,按照规范写好回调函数就ok了,大家自行分析绰绰有余。
还有就是这里的makefile,会把module编译成sensor.goldfish.so,给framework调用,ok来讲一下framework是如何调用HAL层里面的API的,
大家可以看下android源码下面的frameworks/base/services/sensorservice/SensorDevice.cpp
-
SensorDevice::SensorDevice()
-
: mSensorDevice(0),
-
mSensorModule(0)
-
{
-
status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
-
(hw_module_t const**)&mSensorModule);
-
-
LOGE_IF(err, "couldn't load %s module (%s)",
-
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-
if (mSensorModule) {
-
err = sensors_open(&mSensorModule->common, &mSensorDevice);
-
-
LOGE_IF(err, "couldn't open device for module %s (%s)",
-
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-
if (mSensorDevice) {
-
sensor_t const* list;
-
ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
-
mActivationCount.setCapacity(count);
-
Info model;
-
for (size_t i=0 ; i<size_t(count) ; i++) {
-
mActivationCount.add(list[i].handle, model);
-
mSensorDevice->activate(mSensorDevice, list[i].handle, 0);
-
}
-
}
-
}
-
}
这里调用了
-
status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
-
(hw_module_t const**)&mSensorModule);
这个函数在哪呢?在这:
hardware/libhardware/hardware.c里的
-
int hw_get_module(const char *id, const struct hw_module_t **module)
-
{
-
int status;
-
int i;
-
const struct hw_module_t *hmi = NULL;
-
char prop[PATH_MAX];
-
char path[PATH_MAX];
-
-
-
-
-
-
-
-
-
-
-
-
for (i=0 ; i
-
if (i < HAL_VARIANT_KEYS_COUNT) {
-
if (property_get(variant_keys[i], prop, NULL) == 0) {
-
continue;
-
}
-
snprintf(path, sizeof(path), "%s/%s.%s.so",
-
HAL_LIBRARY_PATH1, id, prop);
-
if (access(path, R_OK) == 0) break;
-
-
-
snprintf(path, sizeof(path), "%s/%s.%s.so",
-
HAL_LIBRARY_PATH2, id, prop);
-
if (access(path, R_OK) == 0) break;
-
} else {
-
snprintf(path, sizeof(path), "%s/%s.default.so",
-
HAL_LIBRARY_PATH1, id);
-
if (access(path, R_OK) == 0) break;
-
}
-
}
-
-
-
status = -ENOENT;
-
if (i < HAL_VARIANT_KEYS_COUNT+1) {
-
-
-
status = load(id, path, module);
-
}
-
-
-
return status;
-
}
这里首先把要get的module的名字传进去,然后找到sensor.goldfish.so,但是怎么去加载这个binary呢?
看下这里的load函数:
-
-
-
-
-
-
static int load(const char *id,
-
const char *path,
-
const struct hw_module_t **pHmi)
-
{
-
int status;
-
void *handle;
-
struct hw_module_t *hmi;
-
-
-
-
-
-
-
handle = dlopen(path, RTLD_NOW);
-
if (handle == NULL) {
-
char const *err_str = dlerror();
-
LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
-
status = -EINVAL;
-
goto done;
-
}
-
-
-
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
-
hmi = (struct hw_module_t *)dlsym(handle, sym);
-
if (hmi == NULL) {
-
LOGE("load: couldn't find symbol %s", sym);
-
status = -EINVAL;
-
goto done;
-
}
-
-
-
if (strcmp(id, hmi->id) != 0) {
-
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
-
status = -EINVAL;
-
goto done;
-
}
-
-
hmi->dso = handle;
-
-
-
status = 0;
-
-
done:
-
if (status != 0) {
-
hmi = NULL;
-
if (handle != NULL) {
-
dlclose(handle);
-
handle = NULL;
-
}
-
} else {
-
LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
-
id, path, *pHmi, handle);
-
}
-
-
*pHmi = hmi;
-
-
return status;
-
}
-
handle = dlopen(path, RTLD_NOW);这个函数是用来打开找到的sensor.goldfish.so这个动态库的,然后找到这个库里的一些回调函数,怎么找呢?
-
"code" class="cpp" style="background-color: rgb(255, 255, 255); ">"code" class="cpp">
-
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
-
HAL_MODULE_INFO_SYM_AS_STR;是一个宏,被定义在hardware.h中:
-
"code" class="cpp">#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
好了,就是找HMI这个字串,我们可以用readelf命令查看下sensor.glodfish.so里面的symbol:
大家可以看到这里的第25行,OK 找到了,这里找到了这个动态库的地址,然后就可以一次找到这个动态库中的函数,最终可以被调用。结束语:
-
这里我只是大致分析了一下这个流程,对于代码是如何编写的,大家可以参考我提供的下载,其实android hal层就是填充一些“规则”。
-
下面一节我们来编写一个测试apk来抓我们的温度,之后再来分析framework层,因为到这里我们sensor的移植就结束了,framework层中android已经帮我们把api都写好了,也就是说sensor是android的标准api,我们不需要自己去搭建。
上面已经介绍了,android temperature sensor的移植过程,代码页已经贴给大家了,现在我们写一个APK来进行测试,代码很简单,界面也很简单很丑陋,哈哈,大家不要介意,这个aok只是用作测试,这里我就不多做介绍了,直接贴代码。
java代码:
-
package com.android.jay.sensor1;
-
-
import android.app.Activity;
-
import android.hardware.Sensor;
-
import android.hardware.SensorEvent;
-
import android.hardware.SensorEventListener;
-
import android.hardware.SensorManager;
-
import android.os.Bundle;
-
import android.widget.TextView;
-
-
public class MySensor1Activity extends Activity implements SensorEventListener {
-
-
private TextView tView;
-
SensorManager sensorManager = null;
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
tView=(TextView)findViewById(R.id.tv);
-
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
-
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE),sensorManager.SENSOR_DELAY_GAME);
-
}
-
-
public void onSensorChanged(SensorEvent event) {
-
if(event.sensor.getType()==Sensor.TYPE_TEMPERATURE)
-
tView.setText("value: "+Float.toString(event.values[0]));
-
-
}
-
-
public void onAccuracyChanged(Sensor sensor, int accuracy) {
-
-
-
}
-
}
最后用adb install xxx.apk安装程序,然后再模拟器上运行,结果如下:
OK,看到value在不断的变化了吧,完工,下面一节我们会介绍framework层如何处理的。。。
之前的几篇文章重点介绍了android中传感器模块的标准移植方法,这篇文章我主要跟大家介绍下android framework中对传感器的处理以及管理,涉及到的代码有:
/frameworks/base/services/sensorservice/SensorService.cpp
/frameworks/base/services/sensorservice/SensorDevice.cpp
/frameworks/base/services/sensorservice/SensorInterface.cpp
/frameworks/base/core/jni/android/hardware/jni/android_hardware_SensorManager.cpp
/frameworks/base/core/java/android/hardware/SensorManager.java
首先在这里我先声明下,网上关于这部分的资料很多,不过都大同小异,而且大部分是android2.2的分析,但是到了2.3之后sensor这边改了很多代码,一开始我也看的很迷糊的。这里我只是阐述了我的理解,可能是有问题的,希望有识之士可以指出,这边我也只能粗略的介绍了,能力有限。
在上一篇文章中介绍了在SensorDevice.cpp中使用hw_get_module来获得HAL层编译出来的sensor.goldfish.so:
-
SensorDevice::SensorDevice()
-
: mSensorDevice(0),
-
mSensorModule(0)
-
{
-
status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
-
(hw_module_t const**)&mSensorModule);
-
-
LOGE_IF(err, "couldn't load %s module (%s)",
-
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-
if (mSensorModule) {
-
err = sensors_open(&mSensorModule->common, &mSensorDevice);
-
-
LOGE_IF(err, "couldn't open device for module %s (%s)",
-
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-
if (mSensorDevice) {
-
sensor_t const* list;
-
ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
-
mActivationCount.setCapacity(count);
-
Info model;
-
for (size_t i=0 ; i<size_t(count) ; i++) {
-
mActivationCount.add(list[i].handle, model);
-
mSensorDevice->activate(mSensorDevice, list[i].handle, 0);
-
}
-
}
-
}
-
}
这就是framework调用到HAL的最最重要的一个接口,接下来的工作可以说就是根据得到的这个地址来找到相应的HAL中定义的hw_device_t结构体中的回调函数作为framework中的api,然后进行“封装”,因为hal中只是poll数据,framework需要对数据处理,以及封装API给android app开发者使用。
-
-
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, 0, sizeof(sensors_poll_device_t));
-
-
dev->device.common.tag = HARDWARE_DEVICE_TAG;
-
dev->device.common.version = 0;
-
dev->device.common.module = const_cast(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 = 0;
-
return status;
-
}
最主要的还是open,close,activate,setDelay,poll等回调函数。
得到module之后再open,然后就是得到activate,poll等函数进行封装到SensorDevice这个类当中去。最后,service这层是通过SensorInterface.cpp把接口都封装到HardwareSensor这个类中,传送到/frameworks/base/core/jni/android_hardware_SensorManager.cpp中被使用。
大家可以看到其实/frameworks/base/core/jni/android_hardware_SensorManager.cpp就是/frameworks/base/core/java/android/hardware/SensorManager.java的原生代码,其中封装了一些native function给java文件调用,其中对于我们来说最重要的也就是poll函数。
下面是core中的jni函数:
-
static jint
-
sensors_data_poll(JNIEnv *env, jclass clazz, jint nativeQueue,
-
jfloatArray values, jintArray status, jlongArray timestamp)
-
{
-
sp queue(reinterpret_cast(nativeQueue));
-
if (queue == 0) {return -1;}
-
-
status_t res;
-
ASensorEvent event;
-
-
res = queue->read(&event, 1);
-
if (res == -EAGAIN) {
-
res = queue->waitForEvent();
-
if (res != NO_ERROR)
-
return -1;
-
res = queue->read(&event, 1);
-
}
-
if (res < 0)
-
return -1;
-
-
jint accuracy = event.vector.status;
-
env->SetFloatArrayRegion(values, 0, 3, event.vector.v);
-
env->SetIntArrayRegion(status, 0, 1, &accuracy);
-
env->SetLongArrayRegion(timestamp, 0, 1, &event.timestamp);
-
return event.sensor;
-
}
java层就是调用了这边的这个函数来得到底层的数据,其中比较重要的其实就是
-
env->SetFloatArrayRegion(values, 0, 3, event.vector.v);
-
env->SetIntArrayRegion(status, 0, 1, &accuracy);
-
env->SetLongArrayRegion(timestamp, 0, 1, &event.timestamp);
设置了value,status,timestamp这三个变量,java和应用层也就是用到了这3个参数来写android app的。
-
while (true) {
-
-
-
Log.d(TAG, "sensor data poll......");
-
final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
-
Log.d(TAG, "sensor data poll......value:"+values[0]+values[1]+values[2]);
-
-
int accuracy = status[0];
-
synchronized (sListeners) {
-
if (sensor == -1 || sListeners.isEmpty()) {
-
-
-
t;span> 。。。
接下去我就不分析了,下面就是一些封装,封装,再封装,然后设置sensor的监听器,一般我们修改和跟踪代码就是通过以上介绍的函数中加。
ok,这边android sensor 的framework就粗略的介绍到这边。
下面介绍另外一种方法从driver打通到android framework层,其中会涉及到android server jni和driver中的uevent设置以及监听。
敬请期待。。。
之前,我们已经实现了android HAL层,在android模拟器上移植了一个虚拟的temperature sensor,我之前在模拟器上也移植了backlight,RTC等驱动,都能在应用层得到需要的数据,其实自己想学点东西,给自己布置点任务还是不错的,通过模拟器也可以来学习linux 中的device driver,这部分在今后的博客中我会涉及到,这篇blog我主要是想在之前所做的东西的基础上来实现android 的另外一种架构层的实现。
driver--->framework JNI(server)---->framework JAVA(server)
我们的任务分为如下几个步骤:
=====================================================================
|| 1.在temperature sensor驱动中添加sysfs中添加一个只读的文件让JNI 读数据。
|| 2.在temperature sensor驱动中添加uevent通知user space数据发生了改变需要读取。
|| 3.在android framework中添加jni server读取底层数据,封装操作函数。
|| 4.在android framework中添加uevent observer来监听uevent事件的变化。
|| 5.(可选)在java server中添加系统ACTION,然后编写android apk来接收ACTION。
======================================================================
下面会根据这个步骤来展开我们的学习!!!!!!!
之前的代码可以参照之前的文章。
1.在temperature sensor驱动中添加sysfs中添加一个只读的文件让JNI 读数据。
首先来看的还是驱动,我们在之前的驱动中加入如下代码:
首先是添加value文件节点,可以参照name是如何做的
-
static int tempValue;
-
-
static ssize_t temperature_show_value(struct device *dev,
-
struct device_attribute *attr, char *buf)
-
{
-
return sprintf(buf, "%d\n", tempValue);
-
}
-
-
static IIO_DEVICE_ATTR(name, S_IRUGO, temperature_show_name, NULL,0);
-
static IIO_DEVICE_ATTR(value, S_IRUGO, temperature_show_value, NULL,0);
-
-
static struct attribute *temperature_attributes[] = {
-
&iio_dev_attr_name.dev_attr.attr,
-
&iio_dev_attr_value.dev_attr.attr,
-
NULL
-
};
这样的话会生成/sys/bus/iio/devices/device0/value 可以read 这个节点来得到温度值。
2.在temperature sensor驱动中添加uevent通知user space数据发生了改变需要读取。
接下来是uevent的添加,这边先把代码贴上,我们在分析uvent的代码。
-
"code" class="cpp">static void temperature_dev_poll(struct input_polled_dev *dev)
-
{
-
-
char *buf;
-
char *envp[3];
-
sysfs_notify(&dev->input->dev.kobj,NULL, "value");
-
buf = kmalloc(32,GFP_ATOMIC);
-
if(!buf){
-
printk(KERN_ERR "%s kmalloc failed\n", __func__);
-
return;
-
}
-
envp[0] = "NAME=temperature";
-
snprintf(buf , 32 , "TEMPERATURE=%d",tempValue);
-
envp[1] = buf;
-
envp[2] =NULL;
-
kobject_uevent_env(&dev->input->dev.kobj,KOBJ_CHANGE,envp);
-
kfree(buf);
-
-
printk(KERN_INFO "Current Temperature: %d\n",tempValue);
-
if((tempValue++)==100)
-
tempValue=0;
-
input_event(dev->input,EV_ABS,ABS_PRESSURE,tempValue);
-
input_sync(dev->input);
-
}
其实大家可以看到这边最主要的就是kobject_uevent_env函数,这个函数会通知user space 环境变量发生了变化,然后user space可以通过监听uevent来知道这个时候需要读数据,好,来分析下这个函数:
-
-
-
-
-
-
-
-
-
-
-
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
-
char *envp_ext[])
-
{
-
struct kobj_uevent_env *env;
-
const char *action_string = kobject_actions[action];
-
const char *devpath = NULL;
-
const char *subsystem;
-
struct kobject *top_kobj;
-
struct kset *kset;
-
struct kset_uevent_ops *uevent_ops;
-
u64 seq;
-
int i = 0;
-
int retval = 0;
-
-
pr_debug("kobject: '%s' (%p): %s\n",
-
kobject_name(kobj), kobj, __func__);
-
-
-
top_kobj = kobj;
-
-
while (!top_kobj->kset && top_kobj->parent)
-
top_kobj = top_kobj->parent;
-
-
if (!top_kobj->kset) {
-
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
-
"without kset!\n", kobject_name(kobj), kobj,
-
__func__);
-
return -EINVAL;
-
}
-
-
kset = top_kobj->kset;
-
uevent_ops = kset->uevent_ops;
-
-
-
if (uevent_ops && uevent_ops->filter)
-
if (!uevent_ops->filter(kset, kobj)) {
-
pr_debug("kobject: '%s' (%p): %s: filter function "
-
"caused the event to drop!\n",
-
kobject_name(kobj), kobj, __func__);
-
return 0;
-
}
-
-
-
if (uevent_ops && uevent_ops->name)
-
subsystem = uevent_ops->name(kset, kobj);
-
else
-
subsystem = kobject_name(&kset->kobj);
-
if (!subsystem) {
-
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
-
"event to drop!\n", kobject_name(kobj), kobj,
-
__func__);
-
return 0;
-
}
-
-
-
-
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
-
if (!env)
-
return -ENOMEM;
-
-
-
-
devpath = kobject_get_path(kobj, GFP_KERNEL);
-
if (!devpath) {
-
retval = -ENOENT;
-
goto exit;
-
}
-
-
-
-
retval = add_uevent_var(env, "ACTION=%s", action_string);
-
if (retval)
-
goto exit;
-
-
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
-
if (retval)
-
goto exit;
-
-
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
-
if (retval)
-
goto exit;
-
-
-
if (envp_ext) {
-
for (i = 0; envp_ext[i]; i++) {
-
retval = add_uevent_var(env, "%s", envp_ext[i]);
-
if (retval)
-
goto exit;
-
}
-
}
-
-
-
-
if (uevent_ops && uevent_ops->uevent) {
-
retval = uevent_ops->uevent(kset, kobj, env);
-
if (retval) {
-
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
-
"%d\n", kobject_name(kobj), kobj,
-
__func__, retval);
-
goto exit;
-
}
-
}
-
-
-
-
-
-
-
-
if (action == KOBJ_ADD)
-
kobj->state_add_uevent_sent = 1;
-
else if (action == KOBJ_REMOVE)
-
kobj->state_remove_uevent_sent = 1;
-
-
-
-
spin_lock(&sequence_lock);
-
seq = ++uevent_seqnum;
-
spin_unlock(&sequence_lock);
-
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
-
if (retval)
-
goto exit;
-
-
#if defined(CONFIG_NET)
-
-
if (uevent_sock) {
-
struct sk_buff *skb;
-
size_t len;
-
-
-
len = strlen(action_string) + strlen(devpath) + 2;
-
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
-
if (skb) {
-
char *scratch;
-
-
-
scratch = skb_put(skb, len);
-
sprintf(scratch, "%s@%s", action_string, devpath);
-
-
-
for (i = 0; i < env->envp_idx; i++) {
-
len = strlen(env->envp[i]) + 1;
-
scratch = skb_put(skb, len);
-
strcpy(scratch, env->envp[i]);
-
}
-
-
NETLINK_CB(skb).dst_group = 1;
-
retval = netlink_broadcast(uevent_sock, skb, 0, 1,
-
GFP_KERNEL);
-
} else
-
retval = -ENOMEM;
-
}
-
#endif
-
-
-
-
if (uevent_helper[0]) {
-
char *argv [3];
-
-
argv [0] = uevent_helper;
-
argv [1] = (char *)subsystem;
-
argv [2] = NULL;
-
retval = add_uevent_var(env, "HOME=/");
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env,
-
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
-
if (retval)
-
goto exit;
-
-
-
retval = call_usermodehelper(argv[0], argv,
-
env->envp, UMH_WAIT_EXEC);
-
}
-
-
exit:
-
kfree(devpath);
-
kfree(env);
-
return retval;
-
}
-
EXPORT_SYMBOL_GPL(kobject_uevent_env);
这边我们要关注的就是ACTION还有就是环境变量参数,注意,我们这边使用的kobject是input->dev.kobj,所以这边的DEVPATH和SUBSYSTEM都是跟input subsystem有关的。
这里把uevent传到user space了,下面来看user space是怎么做的,涉及的代码有:
hardware/libhardware_legacy/uevent frameworks/base/core/jni/android_os_UEventObserver.cpp frameworks/base/core/java/android/os/UEventObserver.java frameworks/base/services/java/com/android/server/SystemServer.java
这边我提最重要的,就是hardware/libhardware_legacy/uevent/uevent.c 发送一个socket广播把这个uevent事件发送出去
-
s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
-
if(s < 0)
-
return 0;
-
-
setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
-
-
if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-
close(s);
-
return 0;
-
}
然后就是在framework中通过jni进行封装,使得java中也能通过sokect来监听uevent。
3.在android framework中添加jni server读取底层数据,封装操作函数。
下面我们来实现在jni中读取文件系统中的节点来获得温度的值。
下面是2个文件的下载地址,一个是jni一个是java(com_android_server_TemperatureObserver.cpp,TemperatureObserver.java)
其实在这边用到的东西也不是很多,最多就是jni中的一些规则:
在jni中获得java中的域,在jni中注册method函数,在jni中注册等方法,这边很简单,就是open节点,然后读数据,获得java中的变量的fieldID 然后把数据传给java中的变量,最后封装native函数,然后再java中调用。
1.获得java中的域:
-
jclass clazz = env->FindClass("com/android/server/TemperatureObserver");
-
if(clazz == NULL)
-
{
-
LOGE("Can't find com/android/server/TemperatureObserver");
-
return -1;
-
}
-
-
mTemperatureValueID =
-
env->GetFieldID(clazz , "mTemperatureValue" , "I");
2.open节点读数据,再把数据传到java的变量中:
-
jclass clazz = env->FindClass("com/android/server/TemperatureObserver");
-
if(clazz == NULL)
-
{
-
LOGE("Can't find com/android/server/TemperatureObserver");
-
return -1;
-
}
-
-
mTemperatureValueID =
-
env->GetFieldID(clazz , "mTemperatureValue" , "I");
4.在android framework中添加uevent observer来监听uevent事件的变化。
3.在java中注册uevent监听
-
public TemperatureObserver(Context context) {
-
mContext = context;
-
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TemperatureObserver");
-
mWakeLock.setReferenceCounted(false);
-
-
Slog.v(TAG,"------Struct function....");
-
/ startObserving("EVENT=temperature");
-
startObserving(TEMPERATURE_UEVENT_MATCH);
-
/ init();
-
}
4.在java中实现onUEvent,并且调用java原生接口
-
@Override
-
public void onUEvent(UEventObserver.UEvent event) {
-
if (LOG) Slog.v(TAG, "Temperature UEVENT: " + event.toString());
-
-
try {
-
update(event.get("NAME"), Integer.parseInt(event.get("TEMPERATURE")));
-
} catch (NumberFormatException e) {
-
Slog.e(TAG, "Could not parse switch state from event " + event);
-
}
-
}
-
-
private native void native_temperature_update();
-
-
private synchronized final void update(String newName, int newValue){
-
if(LOG)
-
Slog.v(TAG,"NAME: "+newName+" VALUE: "+newValue);
-
native_temperature_update();
-
Slog.v(TAG,"-----value-----"+mTemperatureValue);
-
}
至此,我们的分析就结束了,关于jni的用法我就不多说了,提供一些链接,大家可以拿作参考:
http://blog.csdn.net/thl789/article/details/7212822
http://blog.csdn.net/furongkang/article/details/6857610
这一部分到此结束,希望写的这些对大家有点帮助。