Sensor系列之aDSP端Sensor Driver流程
--所有的存在都是为了走向毁灭而设计的。
--我们被囚禁在这....
--生与死的无尽螺旋里。
--这是诅咒?
--还是惩罚?
--面对着将无解谜题交予我们的神明,我们会有反抗的一天吗?
本系列导航:
Android简单的计步器应用实现
Android Native到HAL源码剖析,以sensor为例
Android Sensor HAL层初始化流程(基于Qualcomm平台)
Sensor系列之Sensor HAL层数据获取,基于Qualcomm平台
Sensor系列之SensorDaemon服务端流程解析
Sensor系列之dsps架构分析
Sensor系列之SensorDaemon与DSPS端通信过程
Sensor专栏:Android Sensor架构全解析
Sensor在最初的时候都是直接挂在处理器上处理的,其驱动都是和linux或android标准的驱动一样,都是生成对应的设备节点给上层提供数据。但是,由于sensor可能需要一直处于工作状态,产生了功耗的问题,故而各个芯片厂商才推出了自己的解决方案。而高通则将sensor的处理放到了application digital signal processor(aDSP)中,这样待机时主处理器休眠以降低功耗,由这个aDSP在处理音频数据的间隙捎带着就能把sensor的数据处理了,真是高明。
今天我们就开始窥探一下高通是怎样具体实现的,先看一下高通给出的架构图。
上面两张图完整的展示了高通SSC的架构,其中上半部分为AP,下半部分则是aDSP,下半部分图我们在之前的dsps架构分析中已经列举了各个组成模块以及相应的功能特点。我们之前的文章已经分析到SMR/QMI发送消息的流程,接下来我们就要进入Sensor Manager(SMGR),深入驱动程序了。
MAIN
aDSP模块的启动从main函数开始执行,该函数定义在sns_pd.c中,路径为adsp_proc/Sensors/dsps/src/common/。
int main (void){ /* Core Init for user PD */ coremain_main(); printf("Core Init for sensors image done\n"); /* Sensors Initialization */ sns_init(); return 0; /* never reaches, no user exit handling yet */}
这里的coremain_main方法是定义在modem端的,在modem_proc/中,我们暂不关注,而sns_init则是对sensor的初始化过程,其方法主体就是调用sns_init_once方法执行one-time的初始化过程,它会调用各个模块的初始化方法。
static void sns_init_once( void ){ int i; INT8U err; OS_FLAGS flags = 0; const sns_init_fcn init_ptrs[] = SNS_INIT_FUNCTIONS; if ( SNS_SUCCESS != sns_heap_init()) { MSG(MSG_SSID_SNS, DBG_ERROR_PRIO, "Sensors Heap Init failed, using Default heap ID"); sns_heap_id = QURT_ELITE_HEAP_DEFAULT; } sns_init_flag_grp = sns_os_sigs_create( SNS_INIT_FLAG_DONE, &err ); SNS_ASSERT(NULL != sns_init_flag_grp); for( i = 0; NULL != init_ptrs[i]; i++ ) { //MSG_1(MSG_SSID_QDSP6, DBG_HIGH_PRIO, "Sensors Init : %d", i); if( SNS_SUCCESS != init_ptrs[i]() ) { /* Handle error */ //MSG_1(MSG_SSID_QDSP6, DBG_HIGH_PRIO, "Sensors Init FAIL: %d", i); sns_init_done(); } while( !(SNS_INIT_FLAG_DONE & flags) ) { /* Continue polling for the flag until module init is done */ flags = sns_os_sigs_pend( sns_init_flag_grp, SNS_INIT_FLAG_DONE, OS_FLAG_WAIT_SET_ANY, 0, &err ); MSG_1(MSG_SSID_QDSP6, DBG_HIGH_PRIO, "Sensors Init : waiting(%x)", flags); } flags = 0; } MSG(MSG_SSID_QDSP6, DBG_HIGH_PRIO, "Sensors Init : ///////////init once completed///////////");}
我们又看到了类似的场景了,通过定义的全局SNS_INIT_FUNCTIONS函数指针,依次进行调用。当所有init方法执行完成,发送init done的信号。
我们这里以MSM8960板子的初始化函数定义列表为例,来分析aDSP的初始化流程,如下:
#ifdef FEATURE_MSM8960# define SNS_INIT_FUNCTIONS \ { sns_memmgr_init, \ // 内存管理器 sns_init_dsps, \ // 各种dsps服务的初始化 sns_em_init, \ // 事件管理器 sns_smr_init, \ // Message Router用于传递消息(resp/ind) sns_dl_init, \ // Dynamic Loading service sns_smgr_init, \ // Sensor Manager(核心部分) sns_scm_init, \ // 检验管理器 sns_sam_init, \ // 算法管理器 sns_pm_test_task_init, \ // dog_init, \ NULL }
其中最重要的当属SMGR的初始化了。
SMGR INIT
SMGR的init方法是直接启动了一个sns_smgr_task,所有的任务都放在task中完成的。
SNS_SMGR_UIMAGE_CODE sns_err_code_e sns_smgr_init(void){ sns_os_task_create_ext(sns_smgr_task, NULL, (OS_STK *)&sns_smgr_task_stack[SNS_MODULE_STK_SIZE_DSPS_SMGR-1], SNS_MODULE_PRI_DSPS_SMGR, SNS_MODULE_PRI_DSPS_SMGR, (OS_STK *)&sns_smgr_task_stack[0], SNS_MODULE_STK_SIZE_DSPS_SMGR, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_ISLAND, (uint8_t *)"SNS_SMGR"); return SNS_SUCCESS;}
这里我直接给出大致的流程图:
上图中,Communication Library通过I2C以及GPIO,SPI等,直接和sensor device通信了,通过下面的图可以了解这个流程:
I2C挂载图:
上图中,外围期间1,2等便可以是我们的sensor设备或其他可使用I2C通信的电子器件了。
ddf打开的过程如下:
sns_ddf_status_e sns_ddf_open_port( sns_ddf_handle_t* handle, const sns_ddf_port_config_s* cfg ){ sns_ddf_status_e status = SNS_DDF_SUCCESS; if ( cfg == NULL || handle == NULL ) { return SNS_DDF_EINVALID_PARAM; } *handle = NULL; status = sns_ddf_comm_malloc( (void **)handle, sizeof(sns_ddf_sensor_info_s) ); if ( SNS_DDF_SUCCESS != status ) { SNS_PRINTF_STRING_ERROR_1( SNS_DBG_MOD_DSPS_DDF, "Malloc fail, size = %d", sizeof(sns_ddf_sensor_info_s) ); return status; } switch( cfg->bus ) { // 根据设备配置的config bus,如果是I2C,则调用sns_ddf_comm_bus_i2c_open,如果是SPI,则调用sns_ddf_comm_bus_spi_open case SNS_DDF_BUS_I2C: status = sns_ddf_comm_bus_i2c_open( *handle, cfg ); break; case SNS_DDF_BUS_SPI: status = sns_ddf_comm_bus_spi_open( *handle, cfg ); break; default: status = SNS_DDF_EINVALID_PARAM; } if ( SNS_DDF_SUCCESS != status ) { SNS_PRINTF_STRING_ERROR_1(SNS_DBG_MOD_DSPS_DDF, "open_port, result = %d", status); sns_ddf_comm_mfree( *handle ); *handle = NULL; return SNS_DDF_EBUS; } return status;}
// initializes and configures SPI communication bus.static sns_ddf_status_e sns_ddf_comm_bus_spi_open( sns_ddf_handle_t handle, const sns_ddf_port_config_s* cfg){#if SNS_DDF_COMM_BUS_SPI_ENABLE_DRIVER static const spi_device_id_t spi_bus_instances[] = { 0, SPI_DEVICE_1, SPI_DEVICE_2, SPI_DEVICE_3, SPI_DEVICE_4, SPI_DEVICE_5, SPI_DEVICE_6, SPI_DEVICE_7, SPI_DEVICE_8, SPI_DEVICE_9, SPI_DEVICE_10, SPI_DEVICE_11, SPI_DEVICE_12, }; SPI_RESULT result; //spi_errors.h sns_ddf_sensor_info_s* sns_info = (sns_ddf_sensor_info_s*)handle; if ( cfg->bus_instance >= ARR_SIZE(spi_bus_instances) ) { return SNS_DDF_EINVALID_PARAM; } /* Initialize member params */ sns_info->bus = SNS_DDF_BUS_SPI; sns_info->spi_s.dev_id = spi_bus_instances[cfg->bus_instance]; sns_info->spi_s.cfg = cfg->bus_config.spi; if ( EnableSPI == false ) { return SNS_DDF_SUCCESS; } /* Open SPI port*/ result = spi_open(sns_info->spi_s.dev_id); if ( result != SPI_SUCCESS ) { SNS_PRINTF_STRING_ERROR_1( SNS_DBG_MOD_DSPS_DDF, "spi_open fail result=%d", result ); return SNS_DDF_EBUS; } //TODO: fake write switching sensor to SPI mode? ------------------------ /* Close device - this only turns the clocks off */ result = spi_close(sns_info->spi_s.dev_id); if ( result != SPI_SUCCESS ) { SNS_PRINTF_STRING_ERROR_1( SNS_DBG_MOD_DSPS_DDF, "spi_close fail result=%d", result ); return SNS_DDF_EBUS; }#endif return SNS_DDF_SUCCESS;}
// Initializes and configures I2C communication bus.static sns_ddf_status_e sns_ddf_comm_bus_i2c_open( sns_ddf_handle_t handle, const sns_ddf_port_config_s* cfg){ //TODO: table declared twice! Check sns_smgr_hw.c I2cDrv_I2cBusId sns_i2c_bus_table[] static const I2cDrv_I2cBusId i2c_bus_instances[] = { 0, I2CDRV_I2C_1, I2CDRV_I2C_2, I2CDRV_I2C_3, I2CDRV_I2C_4, I2CDRV_I2C_5, I2CDRV_I2C_6, I2CDRV_I2C_7, I2CDRV_I2C_8, I2CDRV_I2C_9, I2CDRV_I2C_10, I2CDRV_I2C_11, I2CDRV_I2C_12 }; int32 result; sns_ddf_sensor_info_s* sns_info = (sns_ddf_sensor_info_s*)handle; if ( cfg->bus_instance >= ARR_SIZE(i2c_bus_instances) ) { return SNS_DDF_EINVALID_PARAM; } /* Initialize member params */ sns_info->bus = SNS_DDF_BUS_I2C; sns_info->i2c_s.reg_addr_type = cfg->bus_config.i2c->reg_addr_type; sns_info->i2c_s.i2c_bus.clntCfg.uSlaveAddr = cfg->bus_config.i2c->slave_addr; sns_info->i2c_s.i2c_bus.clntCfg.uBusFreqKhz = SNS_DDF_DEFAULT_I2C_BUS_FREQ; sns_info->i2c_s.i2c_bus.clntCfg.uByteTransferTimeoutUs = SNS_DDF_DEFAULT_BYTE_XFER_TMO; if ( EnableI2C == false ) { return SNS_DDF_SUCCESS; }if (i2c_bus_instances[cfg->bus_instance] == I2CDRV_I2C_5) sns_info->i2c_s.i2c_bus.clntCfg.uBusFreqKhz = 100; /* Obtain the handle for the port. */ result = I2cDrv_Open(i2c_bus_instances[cfg->bus_instance], &sns_info->i2c_s.i2c_bus, 0); if ( I2C_RES_SUCCESS != result ) { SNS_PRINTF_STRING_ERROR_1( SNS_DBG_MOD_DSPS_DDF, "I2cDrv_Open, result = %d", result ); return SNS_DDF_EBUS; } return SNS_DDF_SUCCESS;}
由此可见,挂载SPI上的设备终会调用spi_open打开设备,而I2C上的则用I2cDrv_Open来进行处理。
以上便是整个aDSP的流程了,结合代码,相信你会很快掌握这个过程,RTFSC,Go!
App processor 与aDSP端数据流图
AP侧从libsensor1开始的数据流走向如下图所示,其中上层到libsensor1的调用逻辑已经在之前的文章中理清了。无非是通过SensorContext的poll从Queue中读取数据,请自行查找回顾,这里只贴出HAL层的框架图及相关的API供参考。
1.HAL层数据处理
2 aDSP层数据处理
Sensor数据获取方式
Sensor上报数据的三种方式:
1(Polling)0x00
调用一次get_data后启动timer,等到timer到时间后调用sns_ddf_driver_if_s中指定的handle_timer()函数上报一组传感器数据
2(DRI)0x80
调用enable_sched_data()启用DRI(Data ReadyInterrupt,数据完成中断),按照set_cycle_time指定的ODR(Output Data Rate,数据输出速率)进行数据采集,采集完成后调用sns_ddf_driver_if_s中指定的handle_irq()函数上报传感器数据。
3(FIFO)0xD0
调用trigger_fifo_data()函数启动FIFO模式,当数据量到达指定的阈值,触发sns_ddf_smgr_data_notify()函数上报一批数据。
更多相关文章
- “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
- Android中使用GSON包解析JSON数据(复杂JSON)
- Android(安卓)manifest中关于 sharedUserId 数据权限
- Linux-Android启动之Machine-Init函数
- Android(安卓)listview的使用
- 【String.xml】修改Android中strings.xml文件, 动态改变数据
- 赵雅智_名片夹(5)_Android中listview可折叠伸缩仿手风琴效果(动态)