zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    .net开发环境的选择
    html头部的一些信息
    SQLHelper类
    C#实现文件下载
    js类
    Winform小知识点
    emacs 代码缩进
    自己喜欢的shell终端配置
    time_wait过多的优化
    Emacs 电子邮件组件RMAIL
  • 原文地址:https://www.cnblogs.com/yiru/p/2889503.html
Copyright © 2011-2022 走看看