高通SDM845平台Sensor学习——3.SLPI(Physical Sensor)

####三: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

图1
上图说明了platform-specific config中每个元素的含义。

图2
上图为可以用作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。

图3
上图为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

图4
上图说明了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(&reg_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

  • 25
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值