Tmd27711 三合一傳感器流程


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

最終通過IBindertransact把使能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。

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM