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。