sensor

参考:框架讲的好http://blog.csdn.net/cs_lht/article/details/8173232

 

==================================================================================================================

第4章 Android Sensors HAL层分析

4.1 Sensor简要介绍

Sensor是很常见的重要器件,比如在生活常见到的声控开关,电子温度计等。手机与平板领域已经广泛使用sensor,大家在使用这些设备的时候也许没有意识到sensor的存在,但确实已经和sensor打交道了。Android 1.5开始为开发人员提供了关于sensorAPI,这样开发人员可以开发出更加优秀的应用程序。

4.1.1 Android Sensors 种类

android系统里sensor的种类比较多。在最新的Android4.0里支持的sensor种类多达13种,其中有两种是Android4.0新增,下面会有介绍。具体类型如表4- 1

4- 1

ID

Sensor类型

说明

1

SENSOR_TYPE_ACCELEROMETER

加速度传感器

加速度传感器又叫G-sensor,返回xyz三轴的加速度数值。

该数值包含地心引力的影响,单位是m/s^2

2

SENSOR_TYPE_MAGNETIC_FIELD

磁力传感器

磁力传感器简称为M-sensor,返回xyz三轴的环境磁场数据。

该数值的单位是微特斯拉(micro-Tesla),用uT表示。

硬件上一般没有独立的磁力传感器,磁力数据由电子罗盘传感器提供(E-compass)。

3

SENSOR_TYPE_ORIENTATION

方向传感器

方向传感器简称为O-sensor,返回三轴的角度数据,方向数据的单位是角度。

为了得到精确的角度数据,E-compass需要获取G-sensor的数据,

经过计算生产O-sensor数据,否则只能获取水平方向的角度。

方向传感器提供三个数据,分别为azimuthpitchroll

4

SENSOR_TYPE_GYROSCOPE

陀螺仪

陀螺仪传感器叫做Gyro-sensor,返回xyz三轴的角速度数据。

角速度的单位是radians/second

5

SENSOR_TYPE_LIGHT

光线传感器

光线感应传感器检测实时的光线强度,光强单位是lux,其物理意义是照射到单位面积上的光通量。

光线感应传感器主要用于Android系统的LCD自动亮度功能。

可以根据采样到的光强数值实时调整LCD的亮度。

6

SENSOR_TYPE_PRESSURE

压力传感器

压力传感器返回当前的压强,单位是百帕斯卡hectopascalhPa)。

7

SENSOR_TYPE_TEMPERATURE

温度传感器

温度传感器返回当前的温度。

8

SENSOR_TYPE_PROXIMITY

近场传感器

接近传感器检测物体与手机的距离,单位是厘米。

接近传感器可用于接听电话时自动关闭LCD屏幕以节省电量。

9

SENSOR_TYPE_GRAVITY

重力传感器

重力传感器简称GV-sensor,输出重力数据。在地球上,重力数值为9.8,单位是m/s^2。坐标系统与加速度传感器相同。

当设备复位时,重力传感器的输出与加速度传感器相同。

10

SENSOR_TYPE_LINEAR_ACCELERATION

线性加速度传感器

线性加速度传感器简称LA-sensor

线性加速度传感器是加速度传感器减去重力影响获取的数据。

单位是m/s^2,坐标系统与加速度传感器相同。

加速度传感器、重力传感器和线性加速度传感器的计算公式如下:

加速度 重力 线性加速度

11

SENSOR_TYPE_ROTATION_VECTOR

旋转矢量传感器

旋转矢量传感器简称RV-sensor。旋转矢量代表设备的方向,是一个将坐标轴和角度混合计算得到的数据。

12

SENSOR_TYPE_RELATIVE_HUMIDITY

湿度传感器

湿度传感器测量周围空气湿度,传感器返回的只是一下湿度的百分比,要确定具体的湿度还需要其他sensor的配合。.

13

SENSOR_TYPE_AMBIENT_TEMPERATURE

环境温度传感器

The ambient (room) temperature in degree Celsius.

4.1.2 SensorAndroid中的层次结构

用户空间

Java应用程序                  Sensor APIs

Java Framewo框架  /frameworks/base/core/java/android/hardware/     

   SensorManager.java   Sensor.java

JNI:  路径android4.0\frameworks\base\core\jni

        android_hardware_SensorManager.cpp

本地库 :  /frameworks/base/services/sensorservice/

    SensorService.cpp  SensorDevice.cpp

HAL(硬件抽象层)         

      sensors.cpp   SensorBase.cpp 

内核空间

Sensor设备驱动程序           driver.c

硬件

传感器                       重力传感器,陀螺仪等

                                          2.lichee\linux-3.0\drivers\gsensor\bma250.c

4.2 Android Sensor Framework的实现

SensorAndroid framework 层次中最主要的结构是SensorManagerSensorManagersensor的事件监听器注册、采样频率、事件采集都有关系。下面会仔细分析SensorManager是如何工作的。这里说的SensorManager指的是framework  java层的,如果是通过JNI调用非java层次的逻辑,这里会特别指出来。

4.2.1 Sensor相应文件介绍

4.2.2 Sensor注册事件监听及采样频率

Sensor方面的应用程序比较简单,只要实现一个listener的类,实现其中的方法,然后利用SensorManager将其某个具体的Sensor注册到服务中,之后的主要工作就是对获得传感器数据进行处理了。下面会对应用程序如何注册sensor事件监听器以及如何设置采样频率进行分析。

下面这段程序是应用程序注册sensor事件监听器的语句。

SensorManager mySensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

mySensorManager.registerListener(mySensorListener, 

      mySensorManager.getDefaultSensor(SensorManager.SENSOR_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);

应用程序是如何确定系统中存在哪些可以使用的sensor呢?仔细分析注册senso r事件监听器的语句可以发现在注册事件监听器时调用了SensorManagergetDefaultSensor()方法,那这一定和SensorManager有关系了。分析SensorManager之前先看一下它的构造函数。

SensorManager的构造函数中主要代码

sensors_module_init();

final ArrayLIst<Sensor> fullList = sFullSensorsLIst;

int i = 0;

do {

    Sensor sensor = new Sensor();

    i = sensors_module_get_next_sensor(sensor,i);

    if(i>=0) {

      sensor.setLegacyType(getLegacySensorType(sensor.getType()));

      fullList.add(sensor);

      sHandleToSensor.append(sensor.getHandle(),sensor);

    }

}while(i>0);

sPool = new SensorEventPool(sFullSensorsList.Size()*2);

sSensorThread = new SensorThread();

构造函数中的while循环调用sensors_module_get_next_sensor() 函数,这个函数会通过JNI读取设备中具体有哪些sensor并初始化sFullSensorsList ArrayList。这个JNI函数里还是要仔细分析一下

    const SensorOffsets& sensorOffsets(gSensorOffsets);

    jstring name = env->NewStringUTF(list->getName().string());

    jstring vendor = env->NewStringUTF(list->getVendor().string());

    env->SetObjectField(sensor, sensorOffsets.name, name);

    env->SetObjectField(sensor, sensorOffsets.vendor, vendor);

    env->SetIntField(sensor, sensorOffsets.version, 1);

    env->SetIntField(sensor, sensorOffsets.handle, list->getHandle());

    env->SetIntField(sensor, sensorOffsets.type, list->getType());

    env->SetFloatField(sensor, sensorOffsets.range, list->getMaxValue());

    env->SetFloatField(sensor, sensorOffsets.resolution, list->getResolution());

    env->SetFloatField(sensor, sensorOffsets.power, list->getPowerUsage());

    env->SetIntField(sensor, sensorOffsets.minDelay, list->getMinDelay());

代码中有gSensorOffsets这个变量,这个变量具体是什么呢,下面代码是变量的定义。

struct SensorOffsets

{

    jfieldID    name;

    jfieldID    vendor;

    jfieldID    version;

    jfieldID    handle;

    jfieldID    type;

    jfieldID    range;

    jfieldID    resolution;

    jfieldID    power;

    jfieldID    minDelay;

} gSensorOffsets;

结构体SensorOffsets主要定义了sensor共有的特性。这个结构体中的变量与Sensor.java中变量是对应起来的。sensors_module_get_next_sensor()中调用SetIntField等函数时通过gSensorOffsets对应的参数来指定修改java中相应参数的值。这个结构体很重要,在SensorHAL层还会看到很类似的结构体,学习HAL层时可以对比一下。

上面说的SensorManagergetDefaultSensor()方法就是通过查找sFullSensorsList确定系统是否存在某个特定的sensor。确定设备是否存在sensor后,下面就可以分析应用程序是如何注册事件监听器的

应用程序调用SensorManagerregisterListener() 传递的三个参数分别是listenersensor种类,最后一个参数就是采样频率。在注册事件监听器的同时,采样频率也被指定了。先看事件监听的注册。

     ListenerDelegate l = null;

     …..

     l = new ListenerDelegate(listener, sensor, handler);

     sListeners.add(l);

     if (!sListeners.isEmpty()) {

          if (sSensorThread.startLocked()) {

              if (!enableSensorLocked(sensor, delay)) {

                  sListeners.remove(l);

                  result = false;

              }

          } else {

             sListeners.remove(l);

             result = false;

          }

     }

上面这段代码是registerListener()函数里注册listener的主要语句,当然在registerListener()里有着复杂的逻辑结构,由于只截取其中的一部分代码,逻辑关系不是很清晰,如果要仔细分析这一块的逻辑关系,需要仔细阅读源码。在registerListener函数中根据参数传进来的listener新建一个ListenerDelegate变量,然后加入到sListeners中,sListenersArrayList变量,存储ListenerDelegate变量。在这里只是将listener加入到sListeners里,并不能看出系统是如何实现事件监听的。在上面的构造函数的代码的最下面new SensorThread()新建一个类,从类的名字上看出这个类和线程有很大关系。在这个类里面就可以很明显的看出事件监听器是如何工作的。下面会对SensorThread类进行分析。

SensorThread类中有这样一个函数

boolean startLocked(){

try{

      if(mThread == null){

        mSensorReady = false;

        SensorThreadRunnable runnable = new SensorThreadRunnable();

        Thread thread = new Thread(runnable,SensorThread.class.getName());

        thread.start();

        ……

      }

      mThread = thread;

}catch(…){ … }

return mThread == null?false:true;

}

mThreadThread线程类型变量,startLocked函数的作用主要是确定mThread变量是否已经初始化,如果没有初始化,就新建一个thread并对mThread进行初始化,如果已经初始化,则直接返回true。这里的mThread线程就是sensor事件监听的主要执行者。所以mThread是否初始化关系到事件监听能否正常进行。startLocked至少被调用一次,mThread才会被初始化。在上面registerListener函数代码中有if (sSensorThread.startLocked())一条语句,这条语句就是调用startLocked函数的地方。也就是说SensorManager注册第一个事件监听器的时候就会启动监听线程。当mThread已经启动了以后,SensorManager注册sensor事件监听器,只是在sListener中添加监听器。mThread线程的run方法就是事件监听器真正工作的地方了。

           final Sensor sensorObject = sHandleToSensor.get(sensor);

           if (sensorObject != null) {

              final int size = sListeners.size();

              for (int i=0 ; i<size ; i++) {

                 ListenerDelegate listener = sListeners.get(i);

                 if (listener.hasSensor(sensorObject)) {

                    listener.onSensorChangedLocked(sensorObject,

                              values, timestamp, accuracy);

                 }

              }

          }

上面的代码是从run方法截取出来的一部分代码。从代码中的for循环可以看出事件监听器是如何工作的了。for循环会遍历所有的listener,如果当前的listener监听的sensor类型与当前产生事件的sensor的类型相同,listener则会调用相应的事件处理方法,对sensor事件进行相应的处理,同时应用程序作出相应的响应。

在上面registerListener函数的代码中,还有if (!enableSensorLocked(sensor, delay))这样的语句,其中enableSensorLocked(sensor, delay)的语句就是设置采样频率的地方。该方法通过JNI调用到android_hardware_SensorManager.cpp文件中的sensor_enable_locked()方法,该方法最后通过SensorService的内部类调用SensorServicesetEventRate方法,该方法会通过SensorInterfaceSensor HAL层联系起来。

4.3 Android Sensor HAL分析

Android HAL部分抽象出硬件的统一接口。不同的硬件厂商只要根据这些接口实现相应的硬件抽象及驱动,则相应的硬件就可以很好的工作。Android上层只会调用这些标准接口,所以HAL层屏蔽掉了硬件的差别,对Androidappframework来说,所有的硬件都有统一的接口,这样Android在移植起来的时候会简单很多。

4.3.1 Sensor HAL 接口分析

Sensor模块相对于Android的其他模块要简单一些,有关sensor HAL 的接口定于主要集中在“hardware/libhardware/include/hardware/sensor.h”的文件中。在文件的开始可以看到“#define SENSORS_HARDWARE_MODULE_ID "sensors"”定义了sensor模块的id。在sensor.h中定义了Android_ics所支持的所有sensor种类:

#define SENSOR_TYPE_ACCELEROMETER        1

#define SENSOR_TYPE_MAGNETIC_FIELD        2

#define SENSOR_TYPE_ORIENTATION            3

#define SENSOR_TYPE_GYROSCOPE             4

#define SENSOR_TYPE_LIGHT                  5

#define SENSOR_TYPE_PRESSURE               6

#define SENSOR_TYPE_TEMPERATURE           7

#define SENSOR_TYPE_PROXIMITY              8

#define SENSOR_TYPE_GRAVITY                9

#define SENSOR_TYPE_LINEAR_ACCELERATION    10

#define SENSOR_TYPE_ROTATION_VECTOR       11

#define SENSOR_TYPE_RELATIVE_HUMIDITY      12

#define SENSOR_TYPE_AMBIENT_TEMPERATURE  13

Sensor module的具体定义是sensors_module_t的结构体。

struct sensors_module_t {

    struct hw_module_t common;

    int (*get_sensors_list)(struct sensors_module_t* module,

            struct sensor_t const** list);

};

可以在代码中看到sensor模块的是在硬件通用模块hw_module_t的基础上添加了函数get_sensors_list而形成的。添加的函数主要是获取sensor列表的。

HAL里,Sensor数据的轮询也有相对应的结构体。

struct sensors_poll_device_t {

    struct hw_device_t common;

    int (*activate)(struct sensors_poll_device_t *dev,

            int handle, int enabled);

    int (*setDelay)(struct sensors_poll_device_t *dev,

            int handle, int64_t ns);

    int (*poll)(struct sensors_poll_device_t *dev,

            sensors_event_t* data, int count);

};

在这里可以看到sensor设备除了硬件设备共有的一些特性外,还有一些特有的方法。这些特有的方法的实现需要和厂商提供的驱动配套起来,这样才能很好的完成数据的轮询。其中的函数指针会在打开sensor设备是进行初始化。

Sensor类型在HAL里对应一个结构体,结构体里变量描述了sensor的相关特性。在HAL里这个结构体的定义如下:

struct sensor_t {

    const char*      name;

    const char*      vendor;

    int             version;

    int             handle;

    int             type;

    float           maxRange;

    float           resolution;

    float           power;

    int32_t         minDelay;

    void*          reserved[8];

};

这个结构体里定义了sensorname、厂商、版本等信息。每个sensor都会有这样的信息。回顾4.2.2节里的SensorOffsets的结构体,发现这两个结构体的变量名基本都是一样的。只是sensor_t里多了个保留变量。SensorOffsets的变量与sensor_t的变量是对应起来的,当framework层需要sensor的类型信息时,就对根据HAL这边的变量的值对SensorOffsets进行相应的初始化。

下面的代码是sensors_event_t的定义:

typedef struct sensors_event_t {

    int32_t version;

    int32_t sensor;

    int32_t type;

    int32_t reserved0;

    int64_t timestamp;

    union {

        float           data[16];

        sensors_vec_t   acceleration;

        sensors_vec_t   magnetic;

        sensors_vec_t   orientation;

        sensors_vec_t   gyro;

        float           temperature;

        float           distance;

        float           light;

        float           pressure;

        float           relative_humidity;

    };

    uint32_t        reserved1[4];

} sensors_event_t;

HAL并没有为每种sensor单独定义sensorevent,这里用sensors_event_t一个结构体覆盖了所有的sensor可能产生的sensorevent。所有的sensorevent有一些共有的特性,比如产生事件的sensor的版本、类型的信息。虽然sensor产生的事件有一些相同的信息,但每种sensor事件的数据肯定不相同。在sensors_event_t中用union解决了事件数据不相同的问题。在union中可以看见sensors_vec_t的数据类型,具体的类型定义如下:

typedef struct {

    union {

        float v[3];

        struct {

            float x;

            float y;

            float z;

        };

        struct {

            float azimuth;

            float pitch;

            float roll;

        };

    };

    int8_t status;

    uint8_t reserved[3];

} sensors_vec_t;

不同的sensor会有不同的数据样式,sensors_vec_t中的unionsensor可能产生数据格式统一起来,这样的不同的sensor数据可以用同一个结构体表示。

4.3.2 Sensor HAL 与 framework的联系

Framework层要获得sensor数据,首先必须先打开sensor设备。Android是什么时候打开sensor设备,又是如何打开sensor设备的。通过分析SensorSeriver.cpp,这些问题都能得到解决。对SensorService.cpp先从onFirstRef()函数开始。

void SensorService::onFirstRef()

{

    SensorDevice& dev(SensorDevice::getInstance());

    if (dev.initCheck() == NO_ERROR) {

        sensor_t const* list;

        ssize_t count = dev.getSensorList(&list);

        ...

    }

}

上面的是onFirstRef函数刚开始的代码,从这里可以看出SensorService一开始就去获取SensorDeviceSensorDevice::getInstance()这句代码反应了SensorDevice 是单例模式,即如果已经存在了SensorDevice的实例,则直接返回该实例,如果不存在SensorDevice的实例,则创建一个并返回。下面看一下SensorDevice的构造函数。

SensorDevice::SensorDevice() : mSensorDevice(0), mSensorModule(0)

{

    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,

            (hw_module_t const**)&mSensorModule);

    if (mSensorModule) {

        err = sensors_open(&mSensorModule->common, &mSensorDevice);

        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);

            }

        }

    }

}

这段代码里可以看到sensor是如何被打开的了。hw_get_module()函数时获取模块信息,加载相应的动态库。具体的实现在hardware/libhardware/hardware.c里,有兴趣可以自己研究一下。调用sensors_open()则是真正打开sensor设备。

static inline int sensors_open(const struct hw_module_t* module,

        struct sensors_poll_device_t** device) {

    return module->methods->open(module,

            SENSORS_HARDWARE_POLL, (struct hw_device_t**)device);

}

上面的代码是sensors_open()的具体代码,在代码中看到,sensors_open函数实际上是调用sensor module里的相应的open()函数。而这里需要的模块信息已经通过调用hw_get_module()函数得到了。到这里sensor设备已经打开了。继续分析SensorDevice的构造函数。mSensorModule->get_sensors_list(mSensorModule, &list);这条语句也是非常重要的。执行这条语句将获得系统里都有哪些。应用程序会根据list中包含的sensor来确定有哪些sensor可以正常使用。get_sensors_list()函数也来自hw_get_module()函数取得的模块。由于sensor的制造商很多,为了配合厂家提供的驱动,HAL相应的函数里的内容需要根据具体的sensor设备进行改写。但HAL层的函数接口是不会改变的。

sensor设备已经打开,应用程序便可以不断读取sensor的数据。下面会分析framework是如何通过HAL取得sensor的数据。在4.2.2中分析过SensorThread类,这里还需要继续分析一下。SensorThread中的内部类SensorThreadRunnable()实现了线程的run方法。在run方法体中存在一个while循环。

while (true) {

      final int sensor = sensors_data_poll(sQueue, values, status, timestamp);

int accuracy = status[0];

      ...

}

在上面的代码中可以看到while循环的第一条语句便是调用sensors_data_poll()函数。这个函数的作用就是不断的读取sensor设备上报的数据信息。这个函数是native方法,方法的实现在frameworks/base/core/jni/android_hardware_SensorManager.cpp中。

    ASensorEvent event;

    res = queue->read(&event, 1);

    if (res == -EAGAIN) {

        res = queue->waitForEvent();

        if (res != NO_ERROR)

            return -1;

        res = queue->read(&event, 1);

    }

这段代码是sensors_data_poll()函数实现中的部分代码。res = queue->read(&event, 1)的操作时读取sensor上报的数据。当执行后返回的值为-EAGAIN,即重新再试的意思时,则会调用SensorEventQueue的waitForEvent()函数,这个函数的代码就不仔细看了,这个函数里存在一个while循环,函数的作用就是等待事件的发生。SensorEventQueue的read函数只是读取相应设备节点的事件,并且和sensorHAL没有关系。那么函数读取的事件来自哪呢。这个问题就和HAL有关系了。

SensorServicethreadLoop()方法中有一些重要操作,下面看一下threadLoop的代码:

bool SensorService::threadLoop()

{

    const size_t numEventMax = 16 * (1 + mVirtualSensorList.size());

    sensors_event_t buffer[numEventMax];

    sensors_event_t scratch[numEventMax];

    SensorDevice& device(SensorDevice::getInstance());

    const size_t vcount = mVirtualSensorList.size();

    ssize_t count;

    do {

        count = device.poll(buffer, numEventMax);

        if (count<0) {

            LOGE("sensor poll failed (%s)", strerror(-count));

            break;

        }

        recordLastValue(buffer, count);

        if (count && vcount) {

            sensors_event_t const * const event = buffer;

            const DefaultKeyedVector<int, SensorInterface*> virtualSensors(

                    getActiveVirtualSensors());

            const size_t activeVirtualSensorCount = virtualSensors.size();

            if (activeVirtualSensorCount) {

                size_t k = 0;

                SensorFusion& fusion(SensorFusion::getInstance());

                if (fusion.isEnabled()) {

                    for (size_t i=0 ; i<size_t(count) ; i++) {

                        fusion.process(event[i]);

                    }

                }

                for (size_t i=0 ; i<size_t(count) ; i++) {

                    for (size_t j=0 ; j<activeVirtualSensorCount ; j++) {

                        sensors_event_t out;

                        if (virtualSensors.valueAt(j)->process(&out, event[i])) {

                            buffer[count + k] = out;

                            k++;

                        }

                    }

                }

                if (k) {

                    // record the last synthesized values

                    recordLastValue(&buffer[count], k);

                    count += k;

                    // sort the buffer by time-stamps

                    sortEventBuffer(buffer, count);

                }

            }

        }

    } while (count >= 0 || Thread::exitPending());

    abort();

    return false;

}

上面代码中存在一个while循环,在这个循环中有这么一条语句device.poll(buffer, numEventMax),语句中deviceSensorDevice类型变量。那么poll()函数具体是做什么的呢。

ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {

    if (!mSensorDevice) return NO_INIT;

    ssize_t c;

    do {

        c = mSensorDevice->poll(mSensorDevice, buffer, count);

    } while (c == -EINTR);

    return c;

}

这是poll函数的内容,可以看到函数也存在一个while循环,具体循环的意思是,如果mSensorDevice的poll函数被打断,则重新调用poll函数,直到成功为止。这里的mSensorDevice是sensors_poll_device_t类型的变量,这里就很明显的和HAL联系起来了。HAL层又会和驱动进行联系,这里就不再往下看了。

 

===================================================================

1.SensorBase.cpp分析

int SensorBase::openInput(const char* inputName) { //inputName == proximity_sensor
    int fd = -1;
    const char *dirname = "/dev/input";
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);                        //devname = "/dev/input"
    filename = devname + strlen(devname);    // filename = ----------|  指向devname的结尾
    *filename++ = '/';            // filename = '/' 所以 devname = "dev/input/", filename继续指向devname的最后
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
                (de->d_name[1] == '\0' ||
                        (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;

 //de->d_name == "event5"时, filename = "event5", 所以devname = "dev/input/event5"
        strcpy(filename, de->d_name);
        fd = open(devname, O_RDONLY);
        if (fd>=0) {
            char name[80];
   
  //   /dev/input/enent5 这个设备的名字是 name = proximity_sensor
            if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {  //通过ioctl命令EVIOCGNAME,能获取dev/input/event*对应的Device Name
                name[0] = '\0';
            }
            if (!strcmp(name, inputName)) { // 此时name == inputname == proximity_sensor
                strcpy(input_name, filename); // filename == event5 , 所以 input_name = event5
                break;
            } else {
                close(fd);
                fd = -1;
            }
        }
    }
    closedir(dir);
    LOGE_IF(fd<0, "couldn't find '%s' input device", inputName);
    return fd;
}

更多 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值