Tmd27711 三合一传感器流程
云科世纪 戴楊一如
参考http://blog.sina.com.cn/s/blog_89f592f5010132qy.html
零、編譯與燒寫
编内核与android
./buildT20WGRETAIL
烧system.2knand.bin
编内核
./buildT20WGRETAIL kernel
烧boot.2knand.bin
编单个android模块
trunk下
source build/envsetup.sh
choosecombo 1(release),9(QHD4500),3(eng)
进模块目录
如:device/cct/common/libsku7sensors
执行mm
烧 out/.../system/lib/hw/sensor...(see Makefile)
壹、Kernel層:
源文件:kernel/drivers/input/misc/ Tmd27711.c
一、名词:
als:ambient light sensor
ps:proximity sensor
ailt: als interrupt lower threshold
aiht: als interrupt higher threshold
pilt: ps interrupt lower threshold
piht:ps interrupt higher threshold
二、数据采集方式:
Tmd27711 默认通过中断方式采集数据。驱动的主要工作是为传感器设置上下阀值,当传感器侦测到光线/距离高于上阀值或低于下阀值时,就产生一个硬件中断,然后驱动扑捉这个中断,并将此时的报值处理后通过input子系统报给上层。
三、sysfs接口
Tmd27711通过sysfs接口向上层提供获取/设置sensor enable、获取/设置sensor delay的方法。
774行:static DEVICE_ATTR(proximity_poll_delay, S_IRUGO | S_IWUGO,
proximity_poll_delay_show, proximity_poll_delay_store);
static DEVICE_ATTR(p_data, S_IRUGO | S_IWUGO,
p_data_show, NULL);
754行:static DEVICE_ATTR(light_poll_delay, S_IRUGO | S_IWUGO,
light_poll_delay_show, light_poll_delay_store);
这个宏原型是:#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
主要实现了proximity_poll_delay, proximity_poll_delay_show, proximity_poll_delay_store, light_poll_delay,light_poll_delay_show, light_poll_delay_store这几个回调函数。
四、流程
从模块初始化开始tmd27711_init()-> i2c_add_driver()-> i2c_register_driver() -> driver_register() -> bus_add_driver() -> driver_attach() -> __driver_attach() -> driver_probe_device() -> really_probe() 其中有这样一句:
Dd.c 270行:ret = drv->probe(dev);
在这里调用了驱动的探针函数bma020_probe()
接下来看探针函数。
测试适配器是否支持I2C_FUNC_SMBUS_BYTE:
925行:i2c_check_functionality(client->adapter, I2C_FUNC_I2C)
给client添加name,给sharp_dev的g_client成员赋值:
932行:if (!strcmp(client->name, "tmd27711"))
{
sharp_dev.g_client_ps = client;
sharp_dev.g_client_als = client;
}
取出平台数据,并用它初始化中断队列:
937行:p_data = client->dev.platform_data;
if( NULL != p_data->init_irq ){
p_data->init_irq();
}
客户端初始化:
943:i2c_set_clientdata(client, &sharp_dev);
该句最终将调用Core.c下的函数device_private_init(),其中有这句:
dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
它将给主数据结构体分配空间。
下面开始初始化设备状态(并非硬件实际状态,而是记录的状态):
947:mutex_init(&sharp_dev.update_lock); //加持自旋锁
949:sharp_dev.enable = 0; /* default mode is standard */
sharp_dev.h_threshold = 0;
sharp_dev.l_threshold = 0;
sharp_dev.ps_detection = 0; /* default to no detection */
sharp_dev.power_state = 0;
下面初始化外围设备(对应软件概念client)
959:err = tmd27711_init_client(client);
这个函数主要是调用下面这两句,并在后面做一些验证。
837:struct tmd27711_data *data = i2c_get_clientdata(client);//得到client的data结构。
841:err=tmd27711_init_register(client);//初始化寄存器,即把设备中一些可写的寄存器写入初始值。
回到probe中,接下来两行分别注册距离和光线传感器
962:ret = ps_register_device(sharp_dev.g_client_ps);
965:ret = als_register_device(sharp_dev.g_client_als);
这两个函数的内容是一致的,注意全局变量sharp_dev,这里实现的是sharp_dev.g_ps / sharp_dev.g_als. 我猜g_的意思是get_
其中注意这句:
sharp_dev.g_ps = input_allocate_device(); / sharp_dev.g_als = input_allocate_device();
它们完成I/O空间注册,为输入子系统申请 input_dev 结构。
再回到probe,接下来几句是重点,因为这东西本项目的acceleration sensor里面没有,这是区别:
969:wake_lock_init(&sharp_dev.tmd_wake_lock, WAKE_LOCK_SUSPEND, "tmd-wake-lock");
使用Wakelock机制,开始持有一个wakelock注意第二个实参WAKE_LOCK_SUSPEND,初始化休眠锁
然后初始化工作队列:
970行: INIT_DELAYED_WORK(&sharp_dev.dwork, tmd27711_work_handler);
上面这句往宏里传入回调函数tmd27711_work_handler,这个回调函数将是设备硬件中断的处理函数。来具体看这个函数:
它先读取tmd的状态寄存器0x13,目的是对中断来源做判断,而后分别对两种中断(Proximity/light)作不同的处理:
status = i2c_smbus_read_byte_data(g_client, CMD_BYTE|TMD27711_STATUS_REG);
……
if ((status & data->enable & 0x20) == 0x20)
{
。。。
}
else if ((status & data->enable & 0x10) == 0x10)
。。。
下面分别来看处理方法。
首先,如果状态寄存器显示proximity sensor产生了中断,则先读取光感数据,目的是做一次判断,把它与atime(als time)乘以系数后的值相比,若光感值更大,则认为此次psensor中断是由light sensor产生的噪声,从而将它过滤;若光感值小于乘积,则暂时认可这个中断,调用tmd27711_change_ps_threshold。
cdata = i2c_smbus_read_word_data(g_client, CMD_WORD|TMD27711_CDATAL_REG);
if (cdata < (75*(1024*(256-data->atime)))/100)
tmd27711_change_ps_threshold(g_client);
else {
pr_debug("Triggered by background ambient noise\n");
}
函数tmd27711_change_ps_threshold定义在本源文件,是距离传感器中断处理的核心函数。在函数中,首先读取数据传感:186:Proximity Data Register (0x18 − 0x19h),将之与上下阀值作比较,若此次中断是第一次中断,阀值就是初始化时设置的:
190:if ( (data->ps_data > data->pilt) && (data->ps_data >= data->piht) ) {
若所报值高于上阀值与下阀值(说明该中断是由于硬件检测到反射红外线强度高于设置的“上阀值”而产生的),那么认为有物体接近了psensor,将侦测状态置零,表示far-to-near detected:
192:data->ps_detection = 0;
然后改写阀值寄存器,低阀值写为初始值,高阀值写为1023;这样设置之后,只有侦测到1023这个值才会产生中断。
194:i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PILTL_REG, data->l_threshold);
i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PIHTL_REG, 1023);//1023
然后改固件中记录的阀值(并非硬件改动,硬件改动为上面2行):
197: data->pilt = data->l_threshold;
data->piht = 1023;
上面是ps报值高于上下阀值导致的中断这种情况,下面是报值低于上下阀值的情况,判断条件如下:
202:else if ( (data->ps_data <= data->pilt) && (data->ps_data < data->piht) ) {
步骤与上面一一对应,因为检测到低强度红外反射,故认为有物体远离传感器,将ps_detection置1,表near-to-far,改写外设的阀值寄存器,高值写为初始,低值写为0,这样只有检测到0这个值时才会产生中断。代码如下:
/* near-to-far detected */
data->ps_detection = 1;
i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PILTL_REG, 0);//0
i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PIHTL_REG, data->h_threshold);
data->pilt = 0;
data->piht = data->h_threshold;
//set_irq_wake(client->irq,0);
pr_debug("near-to-far detected data->ps_detection=%d\n",data->ps_detection);
然后将ps_detection通过input子系统上报给上层,注意上报的只是0或1,即near-to-far/far-to-near
214: input_report_abs(sharp_dev.g_ps, ABS_DISTANCE, data->ps_detection);
input_sync(sharp_dev.g_ps);
上面第二句是每次上报结束时,再上报一个结束标志,就像对讲机的“over”
以上是tmd27711_change_ps_threshold处理的全过程。
下面回到tmd27711_work_handler,第一种中断情况(即:硬件状态显示产生了ps中断,且不是als的噪声)处理还没有结束,还有下面两句:
365 i2c_smbus_write_byte(g_client, CMD_CLR_PS_INT);
i2c_smbus_write_byte(g_client, CMD_CLR_PS_ALS_INT);
写入0xe5和0xe7,清除中断信号。
接下来是第二种情况。这里与上面的分支有些许不同。当检测到状态寄存器的0x10这个值的时候,直接认为als引起了中断,而不再去排除噪声的情况:
tmd27711_change_als_threshold(g_client);
i2c_smbus_write_byte(g_client, CMD_CLR_ALS_INT);
操作是与上面对应的,先调用als中断处理的核心函数,后清除中断信号。下面来看这个核心函数tmd27711_change_als_threshold。
先读取当前光感值(0x14,0x15):
365:data->als_data = i2c_smbus_read_word_data(client, CMD_WORD|TMD27711_CDATAL_REG);
再分别取这个值的0.8倍与1.2倍作为上下阀值,其中这个系数是由TMD27711_ALS_THRESHOLD_HSYTERESIS这个宏设定的:
308:data->als_threshold_l = (data->als_data * (100-TMD27711_ALS_THRESHOLD_HSYTERESIS) ) /100;
data->als_threshold_h = (data->als_data * (100+TMD27711_ALS_THRESHOLD_HSYTERESIS) ) /100;
将这两个值写入als阀值寄存器:
313:i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_AILTL_REG, data->als_threshold_l);
i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_AIHTL_REG, data->als_threshold_h);
刚刚读的光强度来自于0x14与0x15,这两个寄存器的报值反映的是可见光与红外光的强度,还有0x16与0x17这两个寄存器,它们只响应红外线。这在硬件的datasheet中表述为:
Each device combines one photodiode (CH0), which is responsive to both visible and infrared light, and a second photodiode (CH1), which is responsive primarily to infrared light.
下面要报给上层的值,就引入了ch1,也就是0x16与0x17的值,将它们与ch0做一番处理:
317:if ((lux_val = taos_get_lux()) < 0)
printk(KERN_ERR "TAOS: call to taos_get_lux() returned error %d in ioctl als_data\n", lux_val);
下面来看taos_get_lux()这个处理光线强度值的函数。在一番声明之后,通过一个for循环获取四个寄存器(ch0,ch1)的值:
for (i = 0; i < 4; i++) {
if ((ret = (i2c_smbus_write_byte(sharp_dev.g_client_als, (CMD_BYTE | (TMD27711_CDATAL_REG + i))))) < 0) {
printk(KERN_ERR "TAOS: i2c_smbus_write_byte() to chan0/1/lo/hi reg failed in taos_get_lux()\n");
return (ret);
}
chdata[i] = i2c_smbus_read_byte(sharp_dev.g_client_als);
}
用raw_clear表示ch0,raw_ir表示ch1:
raw_clear = chdata[1];
raw_clear <<= 8;
raw_clear |= chdata[0];
raw_ir = chdata[3];
raw_ir <<= 8;
raw_ir |= chdata[2];
接下来把这两个值自乘一些系数,然后做一些判断,然后我们迎来了这两句,它们的目的其实是要确定两个系数:
ratio = (raw_ir<<15)/raw_clear;
for (p = lux_tablep; p->ratio && p->ratio < ratio; p++);
if(!p->ratio)
return 0;
上面是将raw_ir(红外光)乘以2的15次方除以raw_clear(红外+可见),把商赋予ratio。然后用一个for循环去检索lux_tablep,目的是从lux_tablep中找到一组数据,这组数据里的ratio从左侧最接近(小于且差最小)局部变量ratio。lux_tablep结构如下:
struct lux_data {
u16 ratio;
u16 clear;
u16 ir;
};
struct lux_data TritonFN_lux_data[] = {
{ 9830, 8320, 15360 },
{ 12452, 10554, 22797 },
{ 14746, 6234, 11430 },
{ 17695, 3968, 6400 },
{ 0, 0, 0 }
};
很明显,这个结构中,ratio是用来确定“我需要的是哪一组”,clear和ir是真正要用作下一句的系数的。
现在,来看本函数中最重要的一句:
lux = ((raw_clear*(p->clear)) - (raw_ir*(p->ir)));
现在知道了,原来这个函数的主要目的是把红外线的读数(ch1)从红外线+可见光(ch0)中剥离。
后面几句只是对lux这个值做一些增益处理和最大值修正。然后返回lux。
以上是处理光感数值的函数taos_get_lux的全部过程,现在回到调用者tmd27711_change_als_threshold。从上个函数中拿到剥离了红外线的光感值后,将它除以1000:
lux_val=(int)(lux_val/1000);
然后通过input子系统上报这个值,同样的,报完后报一个“over”:
input_report_abs(sharp_dev.g_als, ABS_MISC, lux_val);
input_sync(sharp_dev.g_als);
至此,光感中断的核心处理函数tmd27711_change_als_threshold也全部走完了,回到调用者tmd27711_work_handler。上面说过,调用完处理函数后, 将清理中断信号。最后唤醒休眠锁:
wake_unlock(&sharp_dev.tmd_wake_lock);
总的中断处理函数tmd27711_work_handler也走完,回到probe。刚刚进入处理函数是因为在probe中将它注册为工作队列:
INIT_DELAYED_WORK(&sharp_dev.dwork, tmd27711_work_handler);
最后注册中断处理函数:
if (request_irq(client->irq, tmd27711_interrupt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
client->name, &sharp_dev)) {
dev_info(&client->dev, "tmd27711 : Unable to acquire irq!\n");
goto exit_kfree;
}
关注回调函数tmd27711_interrupt()
384:tmd27711_reschedule_work(data, 0);
在跟下去,执行
__cancel_delayed_work(&data->dwork);
schedule_delayed_work(&data->dwork, delay);
延迟工作队列。
以上是probe全部流程。
贰、HAL層
源文件:device/qcom/common/libsku7sensors/ ProximitySensor.cpp , LightSensor.cpp
一、构造函数
ProximitySensor::ProximitySensor()
首先给事件变量mPendingEvent的成员赋值,分别给这些成员赋值version、sensor、type、acceleration.status。注意到距离传感器的type是SENSOR_TYPE_PROXIMITY。
然后将input_sysfs_path写为/sys/class/input/$input_name/device,
并调用setEnable(0, 1);
立刻来看setEnable。
函数的原型是virtual int setEnable(int32_t handle, int enabled);
但定义时却是int ProximitySensor::setEnable(int32_t, int en) {
所以忽略了第一个参数,不论传什么进来,都没有影响。
这个函数用来开/关Psensor,开还是关,由其第二个参数控制。主要做的事就是根据第二个参数,将0或者1写入/sys/class/input/$input_name/device/enable:
78: strcpy(&input_sysfs_path[input_sysfs_path_len], "enable");
fd = open(input_sysfs_path, O_RDWR);
…
88: if(write(fd, buf, sizeof(buf)) < 0)
这与驱动程序中的proximity_enable_store()函数以下几行对应:
Tmd27711.c 692行 if (sysfs_streq(buf, "1"))
new_value = true;
else if (sysfs_streq(buf, "0"))
new_value = false;
于是实现了enable设备。
然后关闭sysfs文件,改变本类中的状态变量:
close(fd);
mEnabled = flags;
接下来一句调用本源文件中的函数:
92: setInitialState();
看这个函数:
65: if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_PROXIMITY), &absinfo)) {
// make sure to report an event immediately
mHasPendingEvent = true;
mPendingEvent.distance = indexToValue(2);
}
return 0;
使用 EVIOCGABS ioctl提供绝对坐标轴设备的状态信息,若得到0,说明设备响应正常,则将mHasPendingEvent置为真,并设置距离为indexToValue(2);
函数indexToValue()在本源文件定义,只是简单地将值乘以一个系数,这个系数是宏PROXIMITY_THRESHOLD_GP2A:5.0f。
然后setInitialState函数跑完,返回0。
setEnable函数也跑完,返回0.
构造函数到此为止。
二、轮询函数
reandEvents()
这个函数是上层轮询设备的桥梁。上层通过策略,在某段时间里主动读取input缓冲区,查看驱动报上来的值。
首先检查mHasPendingEvent的值,若为真,则表示还有已读取但未处理的事件,则,将mHasPendingEvent值假,然后给事件重新盖时间戳,再把传进来的数据指针指向mPendingEvent,最后返回传感器时能状态:
if (mHasPendingEvent) {
mHasPendingEvent = false;
mPendingEvent.timestamp = getTimestamp();
*data = mPendingEvent;
return mEnabled ? 1 : 0;
}
下一句调用mInputReader.fill,传入的文件描述符是data_fd,fill会用系统调用read读取这个文件,将内容读入input_event:
116:ssize_t n = mInputReader.fill(data_fd);
下面在一个while循环中,使用mInputReader.readEvent从input_event队列读值,先判断事件类型,
while (count && mInputReader.readEvent(&event)) {
int type = event->type;
125:if (type == EV_ABS) {
if (event->code == EVENT_TYPE_PROXIMITY) {
if (event->value != -1) {
// FIXME: not sure why we're getting -1 sometimes
mPendingEvent.distance = indexToValue(event->value);
}
}
若是坐标绝对值(type==EV_ABS),则通过刚刚提到的indexToValue将值保存在mPendingEvent.distance中;
132:} else if (type == EV_SYN) {
mPendingEvent.timestamp = timevalToNano(event->time);// 给mPendingEvent盖时间戳
if (mEnabled) {
*data++ = mPendingEvent;// 将上层传来取值的指针赋上数据
count--;
numEventReceived++;
}
}
若是报值结束标志(EV_SYN),则认为这次读值完成,给mPendingEvent盖时间戳,将上层传来取值的指针赋上数据,计数器count自减,numEventReceived自加。
143:mInputReader.next();
在while循环的结尾,input_reader指向下一条事件。
146:return numEventReceived;
函数最后返回已读取的事件数目。
三、轮询函数
光线传感器lightSensor比Psensor在HAL层多了一个函数setDelay,它用来向文件/sys/class/input/$input_name/device/poll_delay写入64位的超长整型ns
叁、JNI層(本章从服务端角度出发,与Framework层的界限模糊)
附图一张:
源文件:frameworks/base/services/sensorservice/SensorService.cpp
frameworks/native/libs/gui/ SensorEventQueue.cpp, BitTube.cpp等
//frameworks/base/libs/gui/Sensor.cpp
//frameworks/base/services/sensorservice/SensorDevice.cpp
//frameworks/base/core/jni/android_hardware_SensorManager.cpp 为了叙述连贯性,将这个源文件的内容放在下面一起讲
下面用使能函数举例。这个情景是客户端响应顶层应用的enable lightSensor动作,并且已经调用到了服务端的enableDisnable,现在服务端要把开启/关闭的动作传递给HAL层:
看SensorService.cpp的enableDisable()
简单地调用类SensorService的enable和disable
if (enabled) {
err = mService->enable(this, handle);
} else {
err = mService->disable(this, handle);
}
return err;
先看enable()这个函数
它最终调用的是
432:connection->sendEvents(&event, 1);
这个函数也定义在本源文件,它将调用:
600:ssize_t size = SensorEventQueue::write(mChannel,
reinterpret_cast<ASensorEvent const*>(scratch), count);
这个函数定义在frameworks/native/libs/gui/SensorEventQueue.cpp
ssize_t SensorEventQueue::write(const sp<BitTube>& tube,
ASensorEvent const* events, size_t numEvents) {
return BitTube::sendObjects(tube, events, numEvents);
}
于是BitTube.cpp BitTube::sendObjects() –> BitTube::write()
到此,服务端就完成了往管道的写端写入数据。
肆、Framework層(这章从客户端角度出发,与JNI层的界限模糊)
附图一张:
源文件:frameworks/base/core/java/android/hardware/ SensorManager.java(声明native函数,在android_hardware_SensorManager.cpp中实现,实现java与c++交互),SystemSensorManager.cpp等
frameworks/base/core/jni/android_hardware_SensorManager.cpp
frameworks/native/libs/gui/ SensorEventQueue.cpp, BitTube.cpp等
ISensorEventConnection.cpp
下面的情景是应用层发出请求后,客户端怎样将请求传递给服务端。
看SensorManager.java 中的registerListener()函數,注意到源文件里有四個registerListener(),仔細分辨函數原型,應用層調用的是boolean registerListener(SensorEventListener listener, Sensor sensor, int rate)
找到這個函數,只有一句:return registerListener(listener, sensor, rate, null);
繼續trace,
628: int delay = -1;
switch (rate) {
case SENSOR_DELAY_FASTEST:
delay = 0;
break;
case SENSOR_DELAY_GAME:
delay = 20000;
break;
case SENSOR_DELAY_UI:
delay = 66667;
break;
case SENSOR_DELAY_NORMAL:
delay = 200000;
break;
default:
delay = rate;
break;
}
原來第三個參數是延遲。
646: return registerListenerImpl(listener, sensor, delay, handler);
再trace進,這個函數的定義在同文件夾下的SystemSensorManager.cpp里找到。
該函數首先搜索sListeners這個列表,若列表中有這個listener,再查看這個listener中是否有sensor,沒有的話就加入sensor,然後調用enableSensorLocked對這個sensor使能、設置採樣時間:
349:} else if (!l.hasSensor(sensor)) {
l.addSensor(sensor);
if (!enableSensorLocked(sensor, delay)) {
// oops. there was an error
l.removeSensor(sensor);
result = false;
如果監聽器列表sListeners中沒有這個listener,則新建一個ListenerDelegate,并把它加入sListeners。此時再判斷一次sListeners是否為空,不為空,則調用sSensorThread.startLocked()開啟主線程,調用enableSensorLocked對這個sensor使能、設置採樣時間:
333:if (!sListeners.isEmpty()) {
if (sSensorThread.startLocked()) {
if (!enableSensorLocked(sensor, delay)) {
// oops. there was an error
sListeners.remove(l);
result = false;
}
開啟主線程的方法sSensorThread.startLocked()定義在本源文件,在裏面會去構造一個SensorThreadRunnable并調用thread.start()使之開始運行:
76:SensorThreadRunnable runnable = new SensorThreadRunnable();
Thread thread = new Thread(runnable, SensorThread.class.getName());
thread.start();
再看類SensorThreadRunnable,也定義在本源文件。根據直覺,我覺得當上面運行到thread.start()的時候,會調用SensorThreadRunnable:run()。看run方法,裏面有一個無限循環:
120行: while (true) {
// wait for an event
final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
。。。。。。
listener.onSensorChangedLocked(sensorObject, values, timestamp, accuracy);
以上死循環第一句,sensors_data_poll函数等待硬件传感器的事件;
这个poll函数在本源文件最后一行有声明static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);,但没有定义,native关键字说明是本地库中的函数,事实上,这几个native函数全部定义在frameworks/base/core/jni/android_hardware_SensorManager.cpp里
sensors_data_poll函数128行: res = queue->read(&event, 1) -> BitTube::recvObjects(mSensorChannel, events, numEvents) -> recvObjects -> BitTube::read -> recv
到此客户端便完成了向管道读取端读数据
回到类SensorThreadRunnable,死循环的最後一句,調用到監聽者函數listener.onSensorChangedLocked,即應用層的監聽者接口。
現在回到監聽器註冊函數registerListenerImpl,上面提到它在開啟主線程(startLocked)之後會調用enableSensorLocked。
追蹤enableSensorLocked函數,它仍定義在本源文件,它會去遍歷每一個listener,并判斷該監聽器是否含有形參sensor,若有,則調用sensors_enable_sensor:
290行:for (ListenerDelegate i : sListeners) {
if (i.hasSensor(sensor)) {
String name = sensor.getName();
int handle = sensor.getHandle();
result = sensors_enable_sensor(sQueue, name, handle, delay);
break;
}
static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable)方法与poll方法一样,定位到frameworks/base/core/jni/android_hardware_SensorManager.cpp的sensors_enable_sensor函数
static jboolean sensors_enable_sensor –> enableSensor() -> SensorEventConnection.enableDisable() 这个函数定义在ISensorEventConnection.cpp (frameworks\native\libs\gui) -> Binder: transact()
上面红字部分,我一直找不到类ISensorEventConnection下的enableDisable方法,直到看到这句:
class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
搜了下cpp中这种冒号的用法:类名后的冒号是用来定义类的继承,即:class 派生类名 : 继承方式基类名
原来BpSensorEventConnection是派生类名,它的基类正是SensorEventConnection
最终通过IBinder的transact把使能sensor这个请求发送给服务端。
从log中得到验证,上述猜测是错误的,SensorEventQueue.cpp中的enableSensor()调用的ISensorEventConnection->enableDisable,它并不会调用ISensorEventConnection.cpp中的enableDisable方法,而是直接调用sensorService.cpp中的 SensorService::SensorEventConnection::enableDisable 方法。Log如下:
E/Sensors ( 452): dyyr-To enable sensor in client, what's the next?
E/SensorService( 452): dyyr-it's here! sensorservice_enableDisable
E/Sensors ( 452): activate enable=1 handle=3
即,并不通过binder,直接通过函数调用将这个使能设备的请求传递到服务端。
伍、應用層
phone应用太复杂,所以先分析硬件测试的应用
源文件: Vendor\Cct\Cct_factory_kit\Src\Com\Cct\Factory\PSensor\PSensor.java
一、实现方式
整个源文件实现一个PSensor类,它继承于Activity,重写父类的onCreate、onDestroy、finish等方法。在这个类中定义了一个内部类PSensorListener,它是距离传感器的专属listener,继承于SensorEventListener,并重写父类的onSensorChanged()方法,在这个方法里实现对硬件中断的应用层响应。当距离传感器的硬件中断产生时,SensorService就会自动调用这个子类的onSensorChanged方法。
二、函數
1、onCreate()
在某个时机里sensorService会调用这个onCreate方法。在这里面,会去建立显示psensor报值的窗口,最主要的,会去调用本源文件的getService()方法。然后会进行第一次输出。
2、getService()
这个函数的最终目的是注册那个内部类PSensorListener,在这之前做了很多准备工作:取得SensorManager实例,取得PSensor实例,取得PSensorListener实例。
69: mPSensor = mSensorManager.getDefaultSensor(SENSOR_TYPE);
注意取得proximity sensor是在这里,最顶层,所以在后面的sensorservice中看不到具体的sensor名称(proximity),而不同的sensor在HAL层也提供统一的接口,所以service层可以完全忽略具体的sensor名。
74:mPSensorListener = new PSensorListener(this);
构造一个PSensorListener,这个PSensorListener主要重写父类SensorEventListener的onSensorChanged()方法,即当有报值时,做独特的处理,在这个应用里,是将距离传感器所上报的值通过updateView()写到一个textbox里面。
75 if (!mSensorManager.registerListener(mPSensorListener, mPSensor,
SensorManager.SENSOR_DELAY_FASTEST)) {
关键步骤,创建sensorListener,把刚才得到的sensor注册到sensorManager上。
3、onSensorChanged()
这个是内部类PSensorListener的方法,实现扑捉到中断后的处理方法。
141:updateView(value);
调用updateView()把值显示出来。
143:if (value != INIT_VALUE && pre_value != INIT_VALUE && value < pre_value) {
pass();
}
pre_value = value;
Pass即测试通过。由于距离传感器只报接近和远离两种状态,根据上面的逻辑,若两次报值为1,0,则pass;或三次报值为010,也pass。