二、设备树
Msm8909.dtsi Master
i2c_1: i2c@78b5000 { /* BLSP1 QUP1 */
compatible = "qcom,i2c-msm-v2";
#address-cells = <1>;
#size-cells = <0>;
reg-names = "qup_phys_addr";
reg = <0x78b5000 0x1000>;
interrupt-names = "qup_irq";
interrupts = <0 95 0>;
clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
<&clock_gcc clk_gcc_blsp1_qup1_i2c_apps_clk>;
clock-names = "iface_clk", "core_clk";
qcom,clk-freq-out = <100000>;
qcom,clk-freq-in = <19200000>;
pinctrl-names = "i2c_active", "i2c_sleep";
pinctrl-0 = <&i2c_1_active>;
pinctrl-1 = <&i2c_1_sleep>;
qcom,noise-rjct-scl = <0>;
qcom,noise-rjct-sda = <0>;
dmas = <&dma_blsp1 4 64 0x20000020 0x20>,
<&dma_blsp1 5 32 0x20000020 0x20>;
dma-names = "tx", "rx";
qcom,master-id = <86>;
};
Msm8909-pinctrl.dtsi
pmx_i2c_1 { /* CLK, DATA */
qcom,pins = <&gp 6>, <&gp 7>;
qcom,num-grp-pins = <2>;
qcom,pin-func = <3>;
label = "pmx_i2c_1";
i2c_1_active: i2c_1_active {
drive-strength = <2>; /* 2 MA */
bias-disable = <0>; /* No PULL */
};
i2c_1_sleep: i2c_1_sleep {
drive-strength = <2>; /* 2 MA */
bias-pull-down; /* PULL DOWN */
};
};
Msm8909-Q9-mtp.dtsi client
bosch@10 { /* Accelerometer sensor */
compatible = "bosch,bma2x2";
reg = <0x10>;
vdd-supply = <&pm8909_l17>;
vio-supply = <&pm8909_l6>;
};
bosch@12 { /* Magnetic field sensor */
compatible = "bosch,bmm050";
reg = <0x12>;
vdd-supply = <&pm8909_l17>;
vio-supply = <&pm8909_l6>;
};
三、 Makefile 文件
确保所有添加的驱动文件都包含在Makefile中,会被编译进去
obj-$(CONFIG_SENSORS_BMA2X2) += bma2x2_driver.o
ccflags-y += -DBMA2X2_SENSOR_IDENTIFICATION_ENABLE
obj-$(CONFIG_SENSORS_BMM050) += bmm050_driver.o bmm050.o
ccflags-y += -DBMM_USE_BASIC_I2C_FUNC -DCONFIG_BMM_USE_PLATFORM_DATA
四、 Config文件
config SENSORS_BMA2X2
tristate "BMA255/BMA250E/BMA222E/BMA280 acceleration sensor support"
depends on I2C
help
If you say yes here you get support for Bosch Sensortec's
acceleration sensors BMA255/BMA250E/BMA222E/BMA280.
config SENSORS_BMA2X2_ENABLE_INT1
tristate "BMA2X2 acceleration sensor interrupt INT1 support"
depends on SENSORS_BMA2X2 && !SENSORS_BMA2X2_ENABLE_INT2
help
If you say yes here you get INT1 support for Bosch Sensortec
acceleration sensors BMA255/BMA250E/BMA222E/BMA280.
Select it will disable interrupt INT2 support
config BOSCH_BMA2X2_ENABLE_INT2
tristate "BMA2X2 acceleration sensor interrupt INT2 support"
depends on SENSORS_BMA2X2 && !SENSORS_BMA2X2_ENABLE_INT1
help
If you say yes here you get INT2 support for Bosch Sensortec
acceleration sensors BMA255/BMA250E/BMA222E/BMA280.
Can only open if you do NOT open interrupt INT1 support
config SIG_MOTION
tristate "support significant motion sensor function"
depends on SENSORS_BMA2X2 && ( SENSORS_BMA2X2_ENABLE_INT1 || BOSCH_BMA2X2_ENABLE_INT2)
help
Say Y here if you want to support Bosch significant motion sensor function
config DOUBLE_TAP
tristate "support double tap sensor function"
depends on SENSORS_BMA2X2 && ( SENSORS_BMA2X2_ENABLE_INT1 || BOSCH_BMA2X2_ENABLE_INT2)
help
Say Y here if you want to support Bosch double tap sensor function
config SENSORS_BMM050
tristate "BMM050 Magnetic Sensor Driver"
depends on I2C
help
BMM050 Magnetic Sensor Driver implemented by Bosch-Sensortec.
五、 数据流程分析(kernel ->framework)
1、kernel部分
BMC156是acceleration和compass的结合,但是我们还是需要将它看作两个单独的sensor,但是软件原理相差不大,所以我们以compass为例进行叙述。(##此处I2C读出数据(无论对错)代表硬件没有问题且初始化成功)
1) Kernel/driver/bmm050.c bmm050_read_mdataXYZ_s32
这里是最初I2C从寄存器中读取xyz值的地方。XYZ的值存储在两个寄存器中(LSB、MSB),所以在读取寄存器中的值之后会进行位运算得出真正的XYZ值
BMM050_RETURN_FUNCTION_TYPE bmm050_read_mdataXYZ_s32(
struct bmm050_mdata_s32 *mdata)
{
BMM050_RETURN_FUNCTION_TYPE comres;
unsigned char a_data_u8r[8] = "";
struct {
BMM050_S16 raw_dataX;
BMM050_S16 raw_dataY;
BMM050_S16 raw_dataZ;
BMM050_U16 raw_dataR;
} raw_dataXYZ;
if (p_bmm050 == BMM050_NULL) {
comres = E_BMM050_NULL_PTR;
} else {
comres = p_bmm050->BMM050_BUS_READ_FUNC(p_bmm050->dev_addr,
BMM050_DATAX_LSB, a_data_u8r, 8);
/* Reading data for X axis */
a_data_u8r[0] = BMM050_GET_BITSLICE(a_data_u8r[0],
BMM050_DATAX_LSB_VALUEX);
raw_dataXYZ.raw_dataX = (BMM050_S16)((((BMM050_S16)
((signed char)a_data_u8r[1])) <<
SHIFT_LEFT_5_POSITION) | a_data_u8r[0]);
/* Reading data for Y axis */
a_data_u8r[2] = BMM050_GET_BITSLICE(a_data_u8r[2],
BMM050_DATAY_LSB_VALUEY);
raw_dataXYZ.raw_dataY = (BMM050_S16)((((BMM050_S16)
((signed char)a_data_u8r[3])) <<
SHIFT_LEFT_5_POSITION) | a_data_u8r[2]);
/* Reading data for Z axis */
a_data_u8r[4] = BMM050_GET_BITSLICE(a_data_u8r[4],
BMM050_DATAZ_LSB_VALUEZ);
raw_dataXYZ.raw_dataZ = (BMM050_S16)((((BMM050_S16)
((signed char)a_data_u8r[5])) <<
SHIFT_LEFT_7_POSITION) | a_data_u8r[4]);
PINFO("x = %d , y = %d ,z = %d \n",raw_dataXYZ.raw_dataX,raw_dataXYZ.raw_dataY,raw_dataXYZ.raw_dataZ); // 1、此处是寄存器中的xyz值
/* Reading data for Resistance*/
if (!comres)
mdata->drdy = BMM050_GET_BITSLICE(a_data_u8r[6],
BMM050_DATA_RDYSTAT);
a_data_u8r[6] = BMM050_GET_BITSLICE(a_data_u8r[6],
BMM050_R_LSB_VALUE);
raw_dataXYZ.raw_dataR = (BMM050_U16)((((BMM050_U16)
a_data_u8r[7]) <<
SHIFT_LEFT_6_POSITION) | a_data_u8r[6]);
/* Compensation for X axis */
mdata->datax = bmm050_compensate_X_s32(raw_dataXYZ.raw_dataX,
raw_dataXYZ.raw_dataR);
/* Compensation for Y axis */
mdata->datay = bmm050_compensate_Y_s32(raw_dataXYZ.raw_dataY,
raw_dataXYZ.raw_dataR);
/* Compensation for Z axis */
mdata->dataz = bmm050_compensate_Z_s32(raw_dataXYZ.raw_dataZ,
raw_dataXYZ.raw_dataR);
// 2、在观察到磁力值(x/y/z)正负不对称的时候,可以用excel将下方的log打出的xyz值制成散点图(x-y,x-z),观察图中偏差值,以校正xyz,保证圆心在圆点(文档最后会介绍)
/* Output raw resistance value */
mdata->resistance = raw_dataXYZ.raw_dataR;
PINFO("x = %d , y = %d ,z = %d \n",mdata->datax,mdata->datay,mdata->dataz);
// 3、此处读出的是对寄存器中的值进行校正、补偿之后的值
}
return comres;
}
2) Kernel/driver/bmm050_driver.c bmm_work_func
compass kernel对hardware的接口
static void bmm_work_func(struct work_struct *work)
{
struct bmm_client_data *client_data =
container_of((struct delayed_work *)work,
struct bmm_client_data, work);
struct i2c_client *client = client_data->client;
unsigned long delay =
msecs_to_jiffies(atomic_read(&client_data->delay));
mutex_lock(&client_data->mutex_value);
//使用FORCED_MODE,因为相较于NORMAL_MODE准确度更高,但一般使用NORMAL_MODE就够了
mutex_lock(&client_data->mutex_op_mode);
if (BMM_VAL_NAME(NORMAL_MODE) != client_data->op_mode)
bmm_set_forced_mode(client);
mutex_unlock(&client_data->mutex_op_mode);
//这里调用了上文中的函数,读取硬件数据
BMM_CALL_API(read_mdataXYZ_s32)(&client_data->value);
bmm_remap_sensor_data(&client_data->value, client_data);
//input_report_abs将kernel读到的数据都传输到hardware中
input_report_abs(client_data->input, ABS_X, client_data->value.datax);
input_report_abs(client_data->input, ABS_Y, client_data->value.datay);
input_report_abs(client_data->input, ABS_Z, client_data->value.dataz);
mutex_unlock(&client_data->mutex_value);
/*printk("=============x=%d y=%d z=%d\n",client_data->value.datax,
client_data->value.datay, client_data->value.dataz);*/
input_sync(client_data->input);
schedule_delayed_work(&client_data->work, delay);
}
2、 hardware 部分
1)hardware/sensors/CompassSensor.cpp CompassSensor::readEvents
这个文件是比较重要的,可以根据标志点调整xyz的方向(在kernel完成这部分调整比较好,层次清楚)。readEvents函数的作用是读取kernel上报的数据,并经过计算传递到上层。
打LOG:在打出时会发现data的log值会出现其他Sensor的值,例如Acceleration,这是正常的,因为几个Sensor是个union共用一个存储空间,这个可以看定义。data打出Compass的数据时会发现与result的值相同而可能与mPendingEvent有差异,这就需要看一下,这个差异是不是必要的。
int CompassSensor::readEvents(sensors_event_t* data, int count)
{
if (count < 1)
return -EINVAL;
if (mHasPendingEvent) {
mHasPendingEvent = false;
mPendingEvent.timestamp = getTimestamp();
*data = mPendingEvent;
return mEnabled ? 1 : 0;
}
if (mHasPendingMetadata) {
mHasPendingMetadata--;
meta_data.timestamp = getTimestamp();
*data = meta_data;
return mEnabled ? 1 : 0;
}
ssize_t n = mInputReader.fill(data_fd);
if (n < 0)
return n;
int numEventReceived = 0;
input_event const* event;
sensors_event_t raw, result;
#if FETCH_FULL_EVENT_BEFORE_RETURN
again:
#endif
while (count && mInputReader.readEvent(&event)) {
int type = event->type;
//Type == EV_ABS会循环3次,旨在将底层值赋给mPendingEvent.magnetic的x y z,在填充满之后type就会变成EV_SYN
if (type == EV_ABS) {
float value = event->value;
if (event->code == EVENT_TYPE_MAG_X) {
mPendingEvent.magnetic.x = value * res;// 此处的res一般为CONVERT_MAG ( 1/16 )
} else if (event->code == EVENT_TYPE_MAG_Y) {
mPendingEvent.magnetic.y = value * res;
} else if (event->code == EVENT_TYPE_MAG_Z) {
mPendingEvent.magnetic.z = value * res;
}
} else if (type == EV_SYN) {
switch (event->code) {
case SYN_TIME_SEC:
mUseAbsTimeStamp = true;
report_time = event->value*1000000000LL;
break;
case SYN_TIME_NSEC:
mUseAbsTimeStamp = true;
mPendingEvent.timestamp = report_time+event->value;
break;
//在SYN_REPORT中raw接了mPendingEvent的值,之后经过计算并赋给了result,最后传递给了data
case SYN_REPORT:
if (mUseAbsTimeStamp != true) {
mPendingEvent.timestamp = timevalToNano(event->time);
}
if (mEnabled) {
raw = mPendingEvent;
if (algo != NULL) {
if (algo->methods->convert(&raw, &result, NULL)) {
ALOGE("Calibration failed.");
result.magnetic.x = CALIBRATE_ERROR_MAGIC;
result.magnetic.y = CALIBRATE_ERROR_MAGIC;
result.magnetic.z = CALIBRATE_ERROR_MAGIC;
result.magnetic.status = 0;
}
} else {
result = raw;
}
//这里可添加ALOGE (”Compass x = %f , y = %f , z = %f celine\n ”, result.magnetic.x, result.magnetic.y, result.magnetic.z);
*data = result;
data->version = sizeof(sensors_event_t);
data->sensor = mPendingEvent.sensor;
data->type = SENSOR_TYPE_MAGNETIC_FIELD;
data->timestamp = mPendingEvent.timestamp;
/* The raw data is stored inside sensors_event_t.data after
* sensors_event_t.magnetic. Notice that the raw data is
* required to composite the virtual sensor uncalibrated
* magnetic field sensor.
*
* data[0~2]: calibrated magnetic field data.
* data[3]: magnetic field data accuracy.
* data[4~6]: uncalibrated magnetic field data.
*/
data->data[4] = mPendingEvent.data[0];
data->data[5] = mPendingEvent.data[1];
data->data[6] = mPendingEvent.data[2];
data++;
numEventReceived++;
count--;
}
break;
}
} else {
ALOGE("CompassSensor: unknown event (type=%d, code=%d)",
type, event->code);
}
ALOGE("Compass x = %f , y = %f , z = %f celine\n",mPendingEvent.magnetic.x,mPendingEvent.magnetic.y,mPendingEvent.magnetic.z);
ALOGE (”Compass x = %f , y = %f , z = %f celine\n ”, data.magnetic.x, data.magnetic.y, data.magnetic.z);
mInputReader.next();
}
#if FETCH_FULL_EVENT_BEFORE_RETURN
/* if we didn't read a complete event, see if we can fill and
try again instead of returning with nothing and redoing poll. */
if (numEventReceived == 0 && mEnabled == 1) {
n = mInputReader.fill(data_fd);
if (n)
goto again;
}
#endif
return numEventReceived;
}
(3) hardware/sensors/NativeSensorManager.cpp NativeSensorManager::readEvents
相当于将所有Sensor的readEvents或injectEvents做了一个汇总(例如CompassSensor::readEvents和AccelSensor::readEvents),在这可以对所有Sensor的数据进行遍历,但一次只能一个Sensor
int NativeSensorManager::readEvents(int handle, sensors_event_t* data, int count)
{
const SensorContext *list;
int i, j;
int number = getSensorCount();
int nb;
struct listnode *node;
struct SensorRefMap *item;
list = getInfoByHandle(handle);//根据handle确定Sensor,得到Sensor的list
if (list == NULL) {
ALOGE("Invalid handle(%d)", handle);
return -EINVAL;
}
do {
nb = list->driver->readEvents(data, count); //调用每个Sensor的readevents,获取数据存在data中
} while ((nb == -EAGAIN) || (nb == -EINTR));
for (j = 0; j < nb; j++) {
list_for_each(node, &list->listener) {
item = node_to_item(node, struct SensorRefMap, list);
if (item->ctx->enable && (item->ctx != list)) {
item->ctx->driver->injectEvents(&data[j], 1); //方向传感器之类的Sensor
}
}
}
if (list->enable)
return nb;
/* No need to report the events if the sensor is not enabled */
return 0;
}
(4)harware/sensors/sensors.cpp Sensors_poll_context_t::pollEvents
int sensors_poll_context_t::pollEvents(sensors_event_t* data, int count)
{
int nbEvents = 0;
int n = 0;
NativeSensorManager& sm(NativeSensorManager::getInstance());
const sensor_t *slist;
int number = sm.getSensorList(&slist);
do {
// 这是在根据Sensor设备的个数进行循环,相当于这个函数是在不停的对所有的Sensor进行轮询 poll()
for (int i = 0 ; count && i < number ; i++) {
if ((mPollFds[i].revents & POLLIN) || (sm.hasPendingEvents(slist[i].handle))) {
Mutex::Autolock _l(mLock);
int nb = sm.readEvents(slist[i].handle, data, count);
//ALOGE("x : %f , y = %f , z =%f celine compass\n",data->magnetic.x,data->magnetic.y,data->magnetic.z);
//ALOGE("azimuth : %f , pitch = %f , roll =%f celine compass\n",data->orientation.azimuth,data->orientation.pitch,data->orientation.roll);
//调用到NativeSensorManager的readevents,读取Sensor数据,下面的两个log打出的x y z值会发现是一样的,所以导致每次上报的值只能根据handle来确定,无法像结构体一样读取固定的存储地址。
if (nb < 0) {
ALOGE("readEvents failed.(%d)", errno);
return nb;
}
if (nb <= count) {
// no more data for this sensor
mPollFds[i].revents = 0;
}
count -= nb;
nbEvents += nb;
data += nb;
}
}
if (count) {
// we still have some room, so try to see if we can get
// some events immediately or just wait if we don't have
// anything to return
do {
n = poll(mPollFds, number + 1, nbEvents ? 0 : -1);
} while (n < 0 && errno == EINTR);
if (n<0) {
ALOGE("poll() failed (%s)", strerror(errno));
return -errno;
}
if (mPollFds[number].revents & POLLIN) {
char msg;
int result = read(mPollFds[number].fd, &msg, 1);
ALOGE_IF(result<0, "error reading from wake pipe (%s)", strerror(errno));
ALOGE_IF(msg != WAKE_MESSAGE, "unknown message on wake queue (0x%02x)", int(msg));
mPollFds[number].revents = 0;
}
}
// if we have events and space, go read them
} while (n && count);
return nbEvents;
}
(5)harware/sensors/sensors.cpp poll_poll
static int poll__poll(struct sensors_poll_device_t *dev,
sensors_event_t* data, int count) {
sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;
return ctx->pollEvents(data, count); //**
}
(6)harware/sensors/sensors.cpp open_sensors
static int open_sensors(const struct hw_module_t* module, const char*,
struct hw_device_t** device)
{
int status = -EINVAL;
sensors_poll_context_t *dev = new sensors_poll_context_t();
NativeSensorManager& sm(NativeSensorManager::getInstance());
memset(&dev->device, 0, sizeof(sensors_poll_device_1_ext_t));
dev->device.common.tag = HARDWARE_DEVICE_TAG;
#if defined(SENSORS_DEVICE_API_VERSION_1_3)
ALOGI("Sensors device API version 1.3 supported\n");
dev->device.common.version = SENSORS_DEVICE_API_VERSION_1_3;
#else
dev->device.common.version = SENSORS_DEVICE_API_VERSION_0_1;
#endif
dev->device.common.module = const_cast(module);
dev->device.common.close = poll__close;
dev->device.activate = poll__activate;
dev->device.setDelay = poll__setDelay;
dev->device.poll = poll__poll; //**
dev->device.calibrate = poll_calibrate;
#if defined(SENSORS_DEVICE_API_VERSION_1_3)
dev->device.batch = poll__batch;
dev->device.flush = poll__flush;
#endif
*device = &dev->device.common;
status = 0;
return status;
}
(7)harware/sensors/sensors.cpp
static struct hw_module_methods_t sensors_module_methods = {
open: open_sensors //**
};
这就是hardware对framework的接口HAL_MOUDLE_SYM,framework层根据id来选择模块
struct sensors_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: SENSORS_HARDWARE_MODULE_ID,
name: "Quic Sensor module",
author: "Quic",
methods: &sensors_module_methods, //**
dso: NULL,
reserved: {0},
},
get_sensors_list: sensors__get_sensors_list,
};
3、 framework 部分
(1)framework/native/services/sensorServices/SensorDevices.cpp SensorDevices::SensorDevice()
SensorDevice::SensorDevice()
: mSensorDevice(0),
mSensorModule(0)
{
status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
(hw_module_t const**)&mSensorModule); //获取sensor模块
ALOGE_IF(err, "couldn't load %s module (%s)",
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
if (mSensorModule) {
err = sensors_open_1(&mSensorModule->common, &mSensorDevice); //打开sensor设备
ALOGE_IF(err, "couldn't open device for module %s (%s)",
SENSORS_HARDWARE_MODULE_ID, strerror(-err));
if (mSensorDevice) {
if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
}
sensor_t const* list;
ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list); //获取sensor 列表
mActivationCount.setCapacity(count);
Info model;//Sensor<->handle<->info model,都是一一对应的关系,所以SensorDevices去读hal层的数据后根据handle放在对应的info中以便上层使用,在这里的时候每个Sensor才有独立的存储空间存放自己的数据,好处:类似acc、compass这类的传感器会产生大量的数据导致占用大量的存储空间,但可能我们并不需要那么频繁的刷新,那么高的精确度,这样就会造成资源的浪费
for (size_t i=0 ; ilist[i].handle, model);
mSensorDevice->activate(
reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
list[i].handle, 0);
}
}
}
}
1.通过SENSORS_HARDWARE_MODULE_ID获取Sensor模块,&mSensorModule
2.将该模块下的sensor设备公共部分打开,&mSensorDevice
3.获取该模块下的sensor list,&list(所有sensor),并记录下了sensor的个数count
4.for循环中,handle是用来识别sensor的,每一个sensor都会有一个独一无二的handle,这样用handle来标识 Info Model,相当于,每个sensor都有一个独立的Info,hal层传上来的数据就是放在这
5.对sensorDevice,根据handle逐个激活
六、 调试方法
1.Excel画图
上文中提到,在需要校准acceleration或者compass的数值时,我们可以使用excel制作散点图来观察数据是不是在以原点为圆心的圆上均匀分布的。校准x y z三个值的时候需要画两个散点图校准。
x – y
1) 在需要校准的地方(一般为kernel)加上打出 x y z的log,需有特殊词(如:picture)以便挑出log,在数据前后留出较大空格,以便之后导入数据( 如 x = %d , )。
2) 手机以x y 轴组成的面为平面旋转1~3周,确保在每个角度都打出了值,这一步可能需要重复多次(确认数据完整方法,观察数据例如从北顺时针转动手机时 x 值变化顺序是否为 正最大 –> 0 –> 负增大 -> 负减小 0 ->正增大)
3) 将以特殊词选出的log保存为txt格式,以便之后使用
4) 打开excel –> 数据 ->自文本 -> 选择 之前的txt文本 -> 按提示导入文本 ->选中列 x y –> 插入 ->散点图 down
5) 最终结果
2.打log
Kernel :#define DEBUG 打开PINFO
PINFO定义:#define PINFO( fmt , args…)
Printk (KERN_INFO “ \n ” “ [ I ] ” KERN_INFO MODULE_TAG “ < %s > < %d >” fmt “ \n ”,func , LINE ,##args) ;
< %s >:函数名
< %d >:行号
“##”的作用:如果可变参数部分(args …)被忽略或为空 , 那么 “##” 操作会使预处理器去掉它前面的逗号,使宏结束展开(补充完右边的括号)若在调用宏的时候确实提供了一些可变参数,GNU C 也会正常工作,它会把这些可变参数放在逗号后。
adb pull data/logger/kernel.log kernel.log
hardware : ALOGE
adb shell logcat –v time –b system –b event –b main –b radio > logcat_xxx.txt
kernel、hardware….都是单独编译,那么每个模块的log的定义必然都在该模块下,其中有些宏开关不是在代码中出现,而是在Makefile文件中,用来控制大量代码(可以编译出不同的版本),视情况改 (例如ALOGE)
3.调试顺序
1) 初始化是否正确,底层kernel是否可以读到数据,是否向hal层上报数据
2) Hal层是否可以从kernel读到数据(一致性),hal的数据是否上报给了framework
3) Framework是否接收到hal的数据(一致性)