####三:Sensor SLPI层代码分析 ####
在学习SLPI侧代码前我们先了解下SEE的registry&config。
registry 放在/persist/sensors/registry/registry中,它是通过config生成的,是给SLPI解析的文件。
config 放在/persist/sensors/registry/config中,它需要RD修改更新,用来生成register以便SLPI使用。每次config update后,即会更新registry。每次reboot后,会重新加载registry。
config都是以.json为后缀的文件,每个物理sensor会有两个json文件,一个是包含所有平台的特殊配置文件,另一个是sensor driver的特殊配置文件。
如果config文件不存在并且sensor driver支持默认参数,则sensor library会将默认参数填充到registry中。
sensor driver可以通过发送request给registry sensor来随时更新registry。
下面来详细介绍下json文件:以高通给的demo文件为例。
/persist/sensors/registry/config/sdm845_lsm6dsm_0.json
{
"config":{
"hw_platform": ["HDK"],
"soc_id": ["341"]
},
"lsm6dso_0_platform":{
"owner": "lsm6dso",
".config":{
"owner": "lsm6dso",
"bus_type":{ "type": "int", "ver": "0",
"data": "1"
},
"bus_instance":{ "type": "int", "ver": "0",
"data": "2"
},
"slave_config":{ "type": "int", "ver": "0",
"data": "0"
},
"min_bus_speed_khz":{ "type": "int", "ver": "0",
"data": "0"
},
"max_bus_speed_khz":{ "type": "int", "ver": "0",
"data": "3300"
},
...
}
上面config为platform-specific configuration, 格式为:target _ sensor_name _ hadware_id
上图说明了platform-specific config中每个元素的含义。
上图为可以用作SPI or I2C的GPIO,这些GPIO是可以复用的,
举个栗子:
bus_type:1,bus_instance:2,slave_config:1
意思为:使用SPI bus,QUP为2,即使用SSC_6、SSC_7、SSC_8、SSC_9、SSC_10、SSC_11这6组GPIO。slave_config为0,即设备连在SSC_6(QUP2 lane4)上。
若bus_type :0 ,其他不变的话。
意思为:使用I2C bus,QUP为2,即使用SSC_2、SSC_3,I2C2这组I2C。slave address为0x01。
上图为sensor中断GPIO。高通强烈建议用户使用中断GPIO时与上图一一对应,所以accel的中断pin为117,mag的中断pin为119。
//sdm845_icm206xx_0.json
"dri_irq_num":{ "type": "int", "ver": "0",
"data": "117"
},
//sdm845_ak0991x_0.json
"dri_irq_num":{ "type": "int", "ver": "0",
"data": "119"
},
下面说下driver-specific configuration
/persist/sensors/registry/config/lsm6dsm_0.json
{
"config":
{
"hw_platform": ["QRD", "MTP", "Dragon", "Surf", "HDK"],
"soc_id": ["336", "341"]
},
"lsm6dso_0":{
"owner": "lsm6dso",
".accel":{
"owner": "lsm6dso",
".config":{
"owner": "lsm6dso",
"is_dri":{ "type": "int", "ver": "0",
"data": "1"
},
"hw_id":{ "type": "int", "ver": "0",
"data": "0"
},
"res_idx":{ "type": "int", "ver": "0",
"data": "2"
},
"sync_stream":{ "type": "int", "ver": "0",
"data": "0"
}
}
},
".gyro":{
"owner": "lsm6dso",
".config":{
"owner": "lsm6dso",
"is_dri":{ "type": "int", "ver": "0",
"data": "1"
},
"hw_id":{ "type": "int", "ver": "0",
"data": "0"
},
"res_idx":{ "type": "int", "ver": "0",
"data": "4"
},
"sync_stream":{ "type": "int", "ver": "0",
"data": "0"
}
}
},
...
}
格式为: sensor_name_hadware_id
上图说明了driver-specific config中每个元素的含义。
了解完registry & config,下面开始学习SLPI层Sensor。
/slpi/ssc/utils/osa/中为整个slpi的入口函数,分析build下osa.scons。可以看到user部分初始化函数为sns_user_pd_init。
env.AddRCInitFunc(
['SSC_SLPI_USER','MODEM_MODEM','SSC_ADSP_USER'],
{
'sequence_group' : 'RCINIT_GROUP_7', # required
'init_name' : 'sns', # required
'init_function' : 'sns_user_pd_init', # required
'dependencies' : ['uTimetick','i2cbsp_init','adsppm_client','pram_mgr_clnt']
})
该init函数为高通开放给custormer的入口函数,可以理解为main函数。
sns_rc sns_user_pd_init()
{
if(false == sns_init_done)
{
/* If enabled, this will delay the framework initialization by 7 seconds.
This is to easily capture init messages when SSC boots up */
#if defined(SNS_DELAY_INIT)
const sns_time one_second_in_ticks = 19200000ULL;
for(int i = 7; i > 0; i--)
{
MSG_1(MSG_SSID_SNS, DBG_MED_PRIO, "init countdown %d ", i);
/* sns_busy_wait is implemented as a sleep() */
sns_busy_wait(one_second_in_ticks);
}
#endif
...
sns_fw_init();
...
}
这里我们不关心其他init,只研究sns_fw_init。我们开始进入SEE的framework层。
1.framework层
code放在/slpi/ssc/framework/中。
另外还要注意一下,SNS_DELAY_INIT这个宏,当定义后,会delay 7s后再进行framework 初始化。一般在debug时会加上该宏,用来抓取SSC boots up时的log。
//sns_fw_init.c
int sns_fw_init(void)
{
...
rc = sns_sensor_init_fw(); //No.1
...
rc = sns_sensor_instance_init_fw(); //No.2
...
rc = register_static_sensors(); //No.3
...
return 0;
}
//sns_sensor.c
sns_rc
sns_sensor_init_fw(void)
{
...
sensor_cb = (sns_sensor_cb)
{
.struct_len = sizeof(sensor_cb),
.get_service_manager = &get_service_manager,
.get_sensor_instance = &get_sensor_instance,
.create_instance = &sns_sensor_instance_init,
.remove_instance = &sns_sensor_instance_deinit,
.get_library_sensor = &get_library_sensor,
.get_registration_index = &get_registration_index,
};
return SNS_RC_SUCCESS;
}
//sns_sensor_instance.c
sns_rc
sns_sensor_instance_init_fw(void)
{
instance_cb = (sns_sensor_instance_cb)
{
.struct_len = sizeof(instance_cb),
.get_service_manager = &get_service_manager,
.get_client_request = &get_client_request,
.remove_client_request = &remove_client_request,
.add_client_request = &add_client_request
};
return SNS_RC_SUCCESS;
}
在sns_fw_init函数中我们着重分析上面三个函数,
No.1中是sns_sensor_cb的回调函数,这里需要注意下,后面分析sensor driver时会非常频繁的用到这些回调函数。
No.2中是sns_sensor_instance_cb的回到函数,同样需要注意下,后面使用也很频繁。
No.3中是所有sensor的静态注册函数。需要说明下,这个静态注册非常有意思。为了方便添加和移除sensor,高通SDM845中将注册函数写到build文件中,每次build image时,会动态的将build中注册函数写到特定的sensor注册文件中,以便register_static_sensors()使用。
举个例子,以accel的driver icm206xx为例,进入/slpi/ssc/sensors/icm206xx/中,看下build脚本sns_icm206xx.scons。
if 'USES_SSC_STATIC_LIB_BUILDER' in env:
if 'SSC_TARGET_HEXAGON' in env['CPPDEFINES']:
env.AddSSCSU(inspect.getfile(inspect.currentframe()),
flavor = ["hexagon"],
register_func_name = "sns_register_icm206xx",
binary_lib = False,
add_island_files = icm206xx_island_enable)
上面的含义是,若USES_SSC_STATIC_LIB_BUILDER在环境中,则往环境中添加如下数据。。。。可以看到register_func_name = “sns_register_icm206xx”,后面解析可以知道sns_register_icm206xx为accel sensor的入口函数,这里仅说明一下。
那么build环境中是否有USES_SSC_STATIC_LIB_BUILDER呢?
在SLPI build脚本ssc_static_lib_builder.py中中可以看到有加入该flag。
env.AddUsesFlags('USES_SSC_STATIC_LIB_BUILDER')
env.AddMethod(add_ssc_su, 'AddSSCSU')
那如何使用sensor入口函数sns_register_icm206xx呢?
还是在ssc_static_lib_builder.py中,有个函数generate_static_sensor_list()
if "sns_register_suid_sensor" == register_func_name:
static_sensors.insert(0, (register_func_name, registration_cnt))
else:
static_sensors.append((register_func_name, registration_cnt))
#==============================================================================
# Generates sns_static_sensors.c
#==============================================================================
def generate_static_sensor_list(env, tags):
global static_sensors
if env.IsKeyEnable(tags) is True:
logger.info("generate_static_sensor_list() called with %d sensors" % len(static_sensors))
#dest = os.path.join(env.subst('${SSC_ROOT}'), 'framework', 'src')
#if not os.path.isdir(dest) or not os.listdir(dest):
# return None
if len(static_sensors) == 0:
logger.error("There are no static sensors?!!!")
return None
static_sensors_file = os.path.join(env.subst('${SSC_ROOT}'),
'framework', 'src', 'sns_static_sensors.c')
fo = open(static_sensors_file, "w")
fo.write("/* Autogenerated file. Manual modification is pointless. */\n\n")
fo.write("#include \"sns_rc.h\"\n")
fo.write("#include \"sns_register.h\"\n")
fo.write("#include \"sns_types.h\"\n")
fo.write("\n")
for reg_func,reg_cnt in static_sensors:
fo.write("sns_rc %s(sns_register_cb const *register_api);\n" % reg_func)
fo.write("\nconst sns_register_entry sns_register_sensor_list[] =\n{\n")
for reg_func,reg_cnt in static_sensors:
fo.write(" { %s, %i},\n" % (reg_func, reg_cnt))
fo.write("};\n\n")
fo.write("const uint32_t sns_register_sensor_list_len = ARR_SIZE(sns_register_sensor_list);\n\n")
fo.close()
这个函数的作用是根据所有sensor的build脚本xxx.scons中入口函数,生成一个新的文件sns_static_sensors.c。
ok,编译完后git diff下,看到了生成的sns_static_sensors.c中新加了sns_register_icm206xx。
sns_rc sns_gyro_cal_register(sns_register_cb const *register_api);
sns_rc sns_gyro_rot_matrix_register(sns_register_cb const *register_api);
+sns_rc sns_register_icm206xx(sns_register_cb const *register_api);
sns_rc sns_register_interrupt(sns_register_cb const *register_api);
const sns_register_entry sns_register_sensor_list[] =
{
{ sns_gyro_rot_matrix_register, 1},
+ { sns_register_icm206xx, 1},
{ sns_register_interrupt, 1},
}
我想大家应该都了解了吧,这样做的目的就是很快捷的添加或删除driver。
回到No.3,register_static_sensors函数中,
static sns_rc register_static_sensors(void)
{
sns_register_cb reg_cb = (sns_register_cb) //No.1
{
.struct_len = sizeof(reg_cb),
.init_sensor = &sns_sensor_init
};
for(int i = 0; i < sns_register_sensor_list_len; i++)
{
for(int j = 0; j < sns_register_sensor_list[i].cnt; j++) //No.2
{
...
sns_register_sensor_list[i].func(®_cb);
sns_sensor_library_start(library); //No.3
...
}
}
return SNS_RC_SUCCESS;
}
No.1中,sns_register_cb的回调函数,每个sensor driver入口函数都会调用该数据结构中.init_sensor函数。该函数的主要作用是,(1)判断是否是island mode,后面会将什么是island mode。(2)将相关数据结构加入到链表。比如library、sensors等等。
No.2中,上面生成sns_static_sensors.c文件中的sns_register_sensor_list.func,即sensor注册的入口函数;然后执行。执行后就进入的sensor driver的世界。
No.3中,sns_sensor_library_start,主要调用sensor_api->init函数和sensor_api->get_sensor_uid函数,至于sensor_api是什么,后面会讲,这里不懂先略过。
Ok,framework层初始化流程部分讲了一部分,下面开始讲sensor driver层,在sensor driver层讲解中顺带会说下相关的framework层,这样可以更深入的了解SEE框架。
2.sensor driver层
code放在/slpi/ssc/sensors/中
我们研究高通提供的demo sensor driver code:lsm6dso。
进入qcom_firware->slpi_proc->ssc->sensors->lsm6dso目录后,首先下看下build脚本。
####lsm6dso.scons######
Import('env')
import os,inspect
if ('SSC_TARGET_HEXAGON_MDSP' in env['CPPDEFINES']):
Return()
lsm6dso_island_enable = False
if 'SNS_ISLAND_INCLUDE_LSM6DSO' in env: #No.1
lsm6dso_island_enable = True
if ('SSC_TARGET_HEXAGON' in env['CPPDEFINES']) and ('SENSORS_DD_DEV_FLAG' not in env):
env.AddSSCSU(inspect.getfile(inspect.currentframe()), #No.2
register_func_name = "sns_register_lsm6dso",
binary_lib = False,
add_island_files = lsm6dso_island_enable)
if 'SENSORS_DD_DEV_FLAG' in env: #No.3
ME = inspect.getfile(inspect.currentframe())
MY_ROOT = os.path.dirname(os.path.dirname(ME))
REMOVE_FILES = env.FindFiles(['*.*'], MY_ROOT)
env.CleanPack(env['SSC_BUILD_TAGS'], REMOVE_FILES)
No.1中若存在flag=SNS_ISLAND_INCLUDE_LSM6DSO,则lsm6dso_island_enable=true,即lsm6dso被设置成island mode。何为Island mode,高通解释island有着很低的功耗。
如何设置成为island mode呢?
在build脚本上,我们需要设置flag,在build/ssc.scons中加入。
env.AddUsesFlags(['SNS_ISLAND_INCLUDE_LSM6DSO'])
在sensor driver code上,我们我要
(1) 把sensor中这些API放到sns_< drv_name >_sensor_island.c中实现
//本例为sns_lsm6dso_sensor_island.c
sns_sensor_api 内容
get_sensor_uid()
set_client_request() only for accel driver libraries
(2)把sensor instance中这些API放到sns_< drv_name >_sensor_instance_island.c中实现
//本例为sns_lsm6dso_sensor_instance_island.c
sns_sensor_instance_api内容
notify_event()
set_client_config() only for accel driver libraries
(3)把所有sensor & sensor instance island中调用的函数放到sns_< drv_name >_hal_island.c中实现:
//本例为sns_lsm6dso_hal_island.c
lsm6dso_com_write_wrapper()
lsm6dso_start_fifo_streaming()
and so on...
Normal情况哪些API放在哪些文件中呢?
(1) 把sensor中这些API放到sns_< drv_name >_sensor.c中实现
init()
deinit()
set_client_request() for non-accel driver libraries
notify_event()
(2) 把sensor instance中这些API放到sns_< drv_name >_sensor_instance.c中实现
init()
deinit()
set_client_config() only for non-accel driver libraries
(3)所有sensor & sensor instance 非island中调用的函数放到sns_< drv_name >_hal.c中实现。
No.2中设置flag=SSC_TARGET_HEXAGON是动态注册,registry_func_name="sns_register_lsm6dso"为sensor driver的入口函数。binary_lib为是否是二进制lib,高通的一些虚拟sensor比如计步器、amd、smd等等都是以lib形式提供给customer的。customer只要调用API使用即可,不需要知道如何实现。
No.3中设置flag=SENSORS_DD_DEV_FLAG是静态注册,在SDM845上使用的均为动态注册。
接着来到入口函数中:
//sns_lsm6dso.c
sns_rc sns_register_lsm6dso(sns_register_cb const *register_api)
{
int i = 0;
/** Register Sensors */
for(i = 0; i< ARR_SIZE(lsm6dso_supported_sensors) ; i++) {
register_api->init_sensor(sizeof(lsm6dso_state), lsm6dso_supported_sensors[i].sensor_api,
lsm6dso_supported_sensors[i].instance_api);
}
return SNS_RC_SUCCESS;
}
//sns_lsm6dso_sensor_island.c
const lsm6dso_sensors lsm6dso_supported_sensors[ MAX_SUPPORTED_SENSORS ] = {
{LSM6DSO_ACCEL, &lsm6dso_accel_sensor_api, &lsm6dso_sensor_instance_api},
{LSM6DSO_GYRO, &lsm6dso_gyro_sensor_api, &lsm6dso_sensor_instance_api},
{LSM6DSO_MOTION_DETECT , &lsm6dso_motion_detect_sensor_api, &lsm6dso_sensor_instance_api},
{LSM6DSO_SENSOR_TEMP, &lsm6dso_sensor_temp_sensor_api, &lsm6dso_sensor_instance_api}
};
上面入口函数中注册四组api,每组api包含sns_sensor_api 和 sns_sensor_instance_api。
sns_sensor_api数据结构放在sns_lsm6dso_sensor_island.c中;该部分主要是为了sensor的初始化。
sns_sensor_instance_api数据结构放在sns_lsm6dso_sensor_instance_island.c中;该部分主要是为了sensor对应的操作。
以LSM6DSO_ACCEL为例:
1: sns_sensor_api定义在sns_sensor.h中,结构如下:
typedef struct sns_sensor_api
{
uint32_t struct_len;
/**
* Initialize a Sensor to its hard-coded/default state. Generate
* requests for any other necessary data (e.g. Registry data). A call to
* sns_sensor_api::deinit will precede any subsequent calls to this function.
*
* @param[i] this Sensor reference
*
* @return
* SNS_RC_INVALID_STATE - Requisite hardware not available
* SNS_RC_POLICY - Required services not available
* SNS_RC_SUCCESS
*/
sns_rc (*init)(
sns_sensor *const this);
/**
* Release all hardware and software resources associated with this Sensor
*
* @param[i] this Sensor reference
*
* @return
* SNS_RC_INVALID_STATE - Error occurred: some resource could not be released
* SNS_RC_SUCCESS
*/
sns_rc (*deinit)(
sns_sensor *const this);
/**
* Each Sensor must have a globally unique identifier; each algorithm
* and driver will define their own. If a Sensor may be loaded twice on the
* system, it is responsible for returning two unique values. These must
* not change across device reboots.
*
* @param[i] this Sensor reference
*
* @return The unique identifier for this Sensor
*/
sns_sensor_uid const* (*get_sensor_uid)(
sns_sensor const *const this);
/**
* Notification to the client that some data has been received.
*
* The client must use the sns_event_service to obtain this data
* for processing.
*
* @return
* SNS_RC_INVALID_STATE - A client error occurred; Framework shall destroy
* client
* SNS_RC_NOT_AVAILABLE - A transitory error occurred; Framework shall
* remove all outstanding input
* SNS_RC_INVALID_LIBRARY_STATE - A permanent error occurred; Framework shall
* destroy all sensors present in the client library
* SNS_RC_SUCCESS
*/
sns_rc (*notify_event)(
sns_sensor *const this);
/**
* Add, remove, or update a client's request to this Sensor.
*
* For each new request sent by a client, the Sensor (via this function)
* will receive the new_request. If the client has an active request
* (which is to be replaced), it will be specified in exist_request.
*
* If 'remove' is false:
* A client has sent a new request to this Sensor. Determine if any
* active Sensor Instance in sns_sensor_cb::get_sensor_instance()
* will handle this request. If yes, use add_client_request to associate
* this new request with that existing Instance.
*
* If not, instantiate and initialize a new Sensor Instance with the
* appropriate configuration, and similarly use add_client_request.
*
* In either case, if exist_request is provided and new_request provides
* a superceding configuration, exist_request must be removed via
* remove_client_request.
*
* If 'remove' is true:
* Remove this client request by sns_sensor_instance_cb::remove_client_request;
* re-arrange any remaining client requests/sensor instances.
*
* In all cases, if the result of the operation is a Sensor Instance with
* zero clients, sns_sensor_cb::remove_instance must be called.
*
* @param[i] this Sensor reference
* @param[i] exist_request If this request comes-in over an existing stream,
* this is the existing request.
* @param[i] new_request New request just received
* @param[i] remove If the client no longer requires this data
*
* @return
* The Sensor Instance chosen to handle this new client. NULL if an error
* occurred during processing; or if "remove" was true.
* Or sns_instance_no_error (see above).
*/
struct sns_sensor_instance* (*set_client_request)(
sns_sensor *const this,
struct sns_request const *exist_request,
struct sns_request const *new_request,
bool remove);
} sns_sensor_api;
上面每个函数都有注释,这里不再解释。
//sns_lsm6dso_sensor_island.c , sns_sensor_api放在island文件中,上面island介绍中有解释。
sns_sensor_api lsm6dso_accel_sensor_api =
{
.struct_len = sizeof(sns_sensor_api),
.init = &lsm6dso_accel_init,
.deinit = &lsm6dso_accel_deinit,
.get_sensor_uid = &lsm6dso_get_sensor_uid,
.set_client_request = &lsm6dso_set_client_request,
.notify_event = &lsm6dso_sensor_notify_event,
};
Initialization
上面说到sns_sensor_library_start,主要调用sensor_api->init函数和sensor_api->get_sensor_uid函数,下面分别介绍.init和.get_sensor_uid函数。
(1)lsm6dso_accel_i