Android板级支持与硬件相关子系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

6.4 传感器BSP的实现

传感器的BSP实现由非标准的驱动程序和硬件抽象层组成。仿真器的实现使用特定的输入模拟传感器的输入,而Nexus One和Nexus S的传感器硬件抽象层则使用了类似的实现结构。

↘6.4.1 仿真器的实现

Android仿真环境中,传感器的实现方式是通过一个硬件抽象层读取文件系统中的文件来获取传感器信息。这样,就不需要驱动程序,只需要实现一个传感器的硬件抽象层。Android为仿真器提供了一个传感器硬件抽象层的示例实现。

仿真器Sensor硬件抽象层代码的路径为:sdk/emulator/sensors/

这里包含了一个Android.mk文件和一个源文件sensors_qemu.c,经过编译将形成一个单独的模块,即动态库sensors.goldfish.so(中间的goldfish表示产品名)。它将被放置在目标文件系统的system/lib/hw/目录中。

此处的实现复用了Android2.3以前的传感器系统的实现使用数据设备和控制设备的模式,并进行了封装。

sensors_qemu.c表示模块的打开函数如下所示:

    static int open_sensors(const struct hw_module_t* module,
                const char*   name, struct hw_device_t* *device) {
        int  status = -EINVAL;
        if (!strcmp(name, SENSORS_HARDWARE_POLL)) {
            SensorPoll *dev = malloc(sizeof(*dev));
            memset(dev, 0, sizeof(*dev));
            dev->device.common.tag     = HARDWARE_DEVICE_TAG;
            dev->device.common.version = 0;
            dev->device.common.module  = (struct hw_module_t*) module;
            dev->device.common.close   = poll__close;
            dev->device.poll           = poll__poll;
            dev->device.activate       = poll__activate;
            dev->device.setDelay       = poll__setDelay;
            dev->events_fd             = -l;
            dev->fd                    = -l;
            *device = &dev->device.common;
            status  = 0;
        }
        return status;
    }

sensors_qemu.c表示模块的核心结构定义如下所示:

    static struct hw_module_methods_t sensors_module_methods = {
        .open = open_sensors
    };
    struct sensors_module_t HAL_MODULE_INFO_SYM = {
        .common = {
            .tag = HARDWARE_MODULE_TAG,
            .version_major = l,                              // 主版本号和次版本号
            .version_minor = 0,
            .id = SENSORS_HARDWARE_MODULE_ID,                // 传感器硬件模块
            .name = "Goldfish SENSORS Module",
            .author = "The Android Open Source Project",
            .methods = &sensors_module_methods,
        },
        .get_sensors_list = sensors__get_sensors_list        // 返回传感器列表的函数
    };

其中sensors__get_sensors_list()函数返回传感器列表。sensor_t类型的sSensorListInit数组共定义了5个传感器:加速度、磁场、方向、温度和接近。

poll__poll()函数就是sensors_poll_device_t结构中poll的实现,调用了data__poll()函数完成功能,其中实现的核心部分如下所示:

    static int data_poll(struct sensors_data_device_t *dev, sensors_data_t* values) {
        SensorData*  data = (void*)dev;
        while (l) {
            char     buff[256];
            int      len = qemud_channel_recv(data->events_fd, buff, sizeof buff-l);
            float    params[3];
            int64_t  event_time;
            buff[len] = 0;       // 读取传感器信息
    // 如果字符串的数值是"wake"则表示唤醒
            if (!strcmp((const char*)data, "wake")) {  return 0x7FFFFFFF; }
            // "acceleration:<x>:<y>:<z>" corresponds to an acceleration event
            if (sscanf(buff, "acceleration:%g:%g:%g",
                        params+0, params+l, params+2) == 3) {
                new_sensors |= SENSORS_ACCELERATION;
                data->sensors[ID_ACCELERATION].acceleration.x = params[0];
                data->sensors[ID_ACCELERATION].acceleration.y = params[l];
                data->sensors[ID_ACCELERATION].acceleration.z = params[2];
                continue;
            }
    // 省略方向、磁场、温度、接近传感器的处理
            // 方向传感器"orientation:<azimuth>:<pitch>:<roll>"
            // "magnetic:<x>:<y>:<z>"
            // "temperature:<celsius>"
            // "proximity:<value>"
            if (sscanf(buff, "sync:%lld", &event_time) == l) {
                if (new_sensors) {
        data->pendingSensors = new_sensors;
        int64_t t = event_time * l000LL;               // 转换成nano-seconds单位
        if (data->timeStart == 0) {                     // 首次同步的时候做特殊处理
            data->timeStart  = data__now_ns();
            data->timeOffset = data->timeStart - t;
        }
        t += data->timeOffset;
        while (new_sensors) {
            uint32_t i = 3l - __builtin_clz(new_sensors);
            new_sensors &= ~(l<<i);
            data->sensors[i].time = t;                  // 获得时间信息
        }
        return pick_sensor(data, values);
                } else { // 省略错误处理的内容   }
                continue;
            }
        }
    }

处理的流程是:分类型读取传感器数据,给对应的数据结构结构赋值,由于本例是软件仿真示例,因此其取出信息的内容来自软件的Buffer,设置结果通过设置sensors_event_t数据结构来体现。

提示:不同Android版本的仿真器硬件抽象层实现的传感器个数有差别。

传感器数据的Buffer来自于qemud_channel_recv获取的信息。qemud_channel_recv可以和qemud_channel_send使用/dev/socket目录中的套接字qemud来实现通信。

↘6.4.2 Nexus One系统实现

Nexus One系统具有以下几个传感器硬件。

·BMA150:三个方向的加速度(Accelerometer)传感器。

·AK8973:三个方向的磁场(Magnetic field)传感器和方向(Orientation)传感器。

·CM3602:接近(Proximity)传感器和光亮度(Light)传感器。

1.驱动程序

Nexus One系统实现传感器的设备驱动程序由MISC字符设备和input设备构成,有以下几个设备节点。

·/dev/bma150:BMA150的MISC设备节点。

·/dev/akm8973_aot和/dev/akm8973_daemon:AK8973的MISC设备节点。

·/dev/cm3602:CM3602接近传感器的MISC设备节点。

·/dev/lightsensor:CM3602光亮度传感器的MISC设备节点。

·/dev/input/event6:名为compass,磁场和方向传感器的Event设备节点。

·/dev/input/event2:名为proximity,接近传感器的Event设备节点。

·/dev/input/event7:名为lightsensor-level,光亮度传感器的Event设备节点。

Linux内核中的源代码涉及以下几个部分。

·drivers/misc/akm8973.c:AK8973(compass)的MISC和Event驱动,I2C的总线设备。

·drivers/input/misc/capella_cm3602.c:CM3602的MISC字符驱动和proximity的Input驱动。

·arch/arm/mach-msm/board-mahimahi-microp.c:lightsensor的MISC的设备节点和lightsensor-level的Event设备节点,这是I2C设备。

2.硬件抽象层部分

Nexus One系统传感器的硬件抽象层包含了5个传感器,BMA150(AK8973)加速度、AK8973磁场、AK8973方向、CM3602接近和CM3602光亮度。硬件抽象层中包含的代码路径为:device/htc/passion-common/libsensors/,包括以下文件。

·sensors.c:C语言的主入口文件。

·nusensors.cpp:传感器的硬件抽象层的结构实现。

·SensorBase.*:传感器实现的基类。

·InputEventReader.*:通过Input驱动读取事件的工具。

·AkmSensor.*:操作/dev/akm8973_aot设备,名称为"compass",磁场和方向传感器的的实现。

·ProximitySensor.*:操作/dev/cm3602设备,名称为"proximity",接近传感器的实现。

·LightSensor.*:操作/dev/lightsensor设备,名称为"lightsensor-level",光亮度传感器的实现。

nusensors.cpp结构的内容如下所示:

        enum {                                          // 表示不同的传感器的枚举值,内部使用
            light          = 0,
            proximity      = l,
            akm            = 2,
            numSensorDrivers,
            numFds,
        };
        struct pollfd mPollFds[numFds];
        SensorBase* mSensors[numSensorDrivers];
    sensors_poll_context_t::sensors_poll_context_t() {
        mSensors[light] = new LightSensor();            // 建立传感器的真正实现,并赋值
        mPollFds[light].fd = mSensors[light]->getFd();  // 传感器使用的文件描述符
        mPollFds[light].events = POLLIN;
        mPollFds[light].revents = 0;
    // 省略部分内容
    }

nusensors的内容实际上是一个传感器的代理实现。它实现了传感器的硬件抽象层所需要的结构,其中调用的LightSensor等内容则是传感器的真正实现。此处每种传感器的实现作为独立模块,都继承自SensorBase类。

↘6.4.3 Nexus S系统实现

Nexus S系统具有以下几个传感器硬件。

·KR3DM:三个方向的加速度传感器。

·AK8973:三个方向的磁场传感器和方向传感器。

·GP2A:接近和光亮度传感器。

·K3G:螺旋仪(Gyroscope)传感器。

1.驱动程序

Nexus S系统传感器有以下几个设备节点。

·/dev/accelerometer:KR3DM的MISC设备节点。

·/dev/akm8973:AK8973的MISC设备节点。

·/dev/input/event6:名为compass,磁场和方向传感器的Event设备节点。

·/dev/input/event3:名为proximity,接近传感器的Event设备节点。

·/dev/input/event4:名为lightsensor-level,光亮度传感器的Event设备节点。

·/dev/input/event1:名为gyro,螺旋仪传感器的Event设备节点。

Linux内核中的源代码涉及以下部分。

·drivers/misc/kr3dm.c:实现了KR3DM的MISC设备节点,I2C总线的地址为1-0009。

·drivers/misc/ak8973.c:实现了AK8973(compass)的MISC和Event驱动,I2C线的地址为1-001c。

·drivers/input/misc/gp2a.c:实现了GP2A的lightsensor-level和proximity部分的Input驱动,gpio控制的设备,I2C总线的地址为11-0044。

·drivers/input/misc/k3g.c:实现了K3G的GP2A部分的Input驱动,I2C总线的地址为0-0069。

2.硬件抽象层部分

Nexus S系统传感器的硬件抽象层包含了6个传感器:KR3DM(AK8973)加速度、AK8973磁场、AK8973方向、GP2A接近、GP2A光亮度和K3G螺旋仪。硬件抽象层包含代码路径为:samsung/crespo/libsensors/,包括以下文件。

·sensors.*:C语言的主入口文件。

·nusensors.cpp:传感器的硬件抽象层的结构实现。

·SensorBase.*:传感器实现的基类。

·InputEventReader.*:通过Input驱动读取事件的工具。

·AkmSensor.*:AK8973磁场和方向传感器的实现,调用libakm.so。

·ProximitySensor.*:GP2A接近传感器的实现。

·LightSensor.*:GP2A光亮度传感器的实现。

·GypoSensor.*:K3G螺旋仪传感器的实现。

提示:Nexus One和Nexus S的传感器实现结构,已经成为典型和通用的方法。

↘6.4.4 Galaxy Nexus系统实现

Galaxy Nexus系统包括了众多的传感器,这些传感器都是OMAP处理器外板级硬件,使用I2C的方式与OMAP相连。包括了Sharp的GP2A的接近和光线传感器,Bosch的BMP180气压计,以及InvenSense的若干个运动相关的传感器。

1.驱动程序

Galaxy Nexus的传感器都是连接于I2C总线的,内核中板级定义的传感器相关的文件为:arch/arm/mach-omap2/board-tuna-sensors.c。其中的i2c_board_info类型的结构tuna_sensors_i2c4_boardinfo中定义了5个I2C的设备,如下所示:

    static struct i2c_board_info __initdata tuna_sensors_i2c4_boardinfo[] = {
      {    I2C_BOARD_INFO("mpu3050", 0x68),
          .irq = OMAP_GPIO_IRQ(GPIO_GYRO_INT), .platform_data = &mpu_data,   },
      {    I2C_BOARD_INFO("bma250", 0xl8),
          .irq = OMAP_GPIO_IRQ(GPIO_ACC_INT),.platform_data = &mpu_data.accel,},
      {    I2C_BOARD_INFO("yas530", 0x2e), .irq = OMAP_GPIO_IRQ(GPIO_MAG_INT),
          .platform_data = &mpu_data.compass,},
      {    I2C_BOARD_INFO("gp2a",   0x44), .platform_data = &gp2a_pdata,},
      {    I2C_BOARD_INFO("bmpl80", 0x77), },
    };

传感器的硬件连接方式都是I2C,因此各自有不同的地址,但是它们在用户空间有的是input设备,有的是MISC字符设备。

几个传感器相关的input设备节点如下所示。

·/dev/input/event3:Sharp的GP2A的接近传感器("proximity")。

·/dev/input/event4:Sharp的GP2A的光线传感器("lightsensor-level")。

·/dev/input/event0:Bosch的BMP180气压计(" barometer ")。

这几个内容都是I2C的设备,在运行时sys文件系统的/sys/bus/i2c/devices/目录中,它们分别为I2C的第4个控制器上面的0044和0077两个地址的设备,如下所示:

    shell@android:/ $ cat /sys/bus/i2c/devices/4-0044/name
    gp2a
    shell@android:/ $ cat /sys/bus/i2c/devices/4-0077/name
    bmpl80

这几个传感器的实现,都是通过I2C总线注册了input的设备节点,其代码路径为如下。

·drivers/input/misc/gp2a.c:GP2A接近和光线传感器的驱动。

·drivers/misc/bmp180.c:BMP180的驱动。

加速度传感器由InvenSense的各个传感器提供,MPU3050是一个三轴传感器。其代码路径为:drivers/misc/inv_mpu/,包括mpu-dev.c、mldl_cfg.c、mlsl-kernel.c等几个源文件。accel目录中的提供了bm250的加速度传感器的实现,其I2C的地址为4-0018;compass中提供了yas530磁力传感器,其I2C的地址为4-0028。

以上的实现在/dev/中提供了几个MISC的设备节点,包括mpu、accelirq、compassirq、mpuirq和timerirq。

2.硬件抽象层

Galaxy Nexus的系统传感器部分的硬件抽象层使用Tuna板的通用实现,代码路径为:device/samsung/tuna/libsensors/,生成sensors.tuna.so包括以下几个文件。

·sensors.*:传感器硬件抽象层的主入口文件。

·SumsungSensorBase.*:三星传感器实现的基类。

·InputEventReader.*:通过Input驱动读取事件的工具。

·LightSensor.*:名称为"lightsensor-level",光亮度传感器的实现。

·ProximitySensor.*:名称为" proximity",接近传感器的实现。

·PressureSensor.*:名称为" barometer ",气压计传感器的实现。

其代码结构是典型的传感器硬件抽象层的结构。SumsungSensorBase.*根据sys文件系统的路径/sys/class/input/读取各种设备信息。这里源代码中LOCAL_SENSORS宏的值为3,此处的实现只负责了驱动中3个input设备对应的3个传感器。

InvenSense的几个传感器的内容在二进制的库libinvensense_hal.so中实现,并被这里调用。硬件抽象层sensors .cpp的sensors_poll_context_t()函数调用了MPLSensor的内容,代码的片段如下所示:

    sensors_poll_context_t::sensors_poll_context_t(){
        FUNC_LOG;
        MPLSensor* p_mplsen = new MPLSensor();    // 调用libinvensense_hal.so库中的内容
        setCallbackObject(p_mplsen);               // 建立回调函数处理MPLSensor的事件
        numSensors =
            LOCAL_SENSORS + p_mplsen->populateSensorList(sSensorList + LOCAL_SENSORS,
                  sizeof(sSensorList[0]) * (ARRAY_SIZE(sSensorList) - LOCAL_SENSORS));
        mSensors[mpl] = p_mplsen;
        mPollFds[mpl].fd = mSensors[mpl]->getFd();
        mPollFds[mpl].events = POLLIN;
        mPollFds[mpl].revents = 0;
        mSensors[mpl_accel] = mSensors[mpl];
        mPollFds[mpl_accel].fd = ((MPLSensor*)mSensors[mpl])->getAccelFd();
        mPollFds[mpl_accel].events = POLLIN;
        mPollFds[mpl_accel].revents = 0;
        mSensors[mpl_timer] = mSensors[mpl];
        mPollFds[mpl_timer].fd = ((MPLSensor*)mSensors[mpl])->getTimerFd();
        mPollFds[mpl_timer].events = POLLIN;
        mPollFds[mpl_timer].revents = 0;
        //  省略部分内容
    }

在InvenSense的几个传感器的处理流程中,需要首先根据libinvensense_hal.so库提供接口得到各个传感器的文件描述符fd,将它们保存,之后的处理流程就和其他传感器基本相同了。