-----欢迎点赞,收藏和关注------
alsps架构分析
以ams_tcs3707色温为例
1.sensor驱动添加
int alsps_driver_add(struct alsps_init_info *obj)
所有的sensor驱动都是通过上述函数添加到alsps框架中,注册的结构体自行定义,注意在结构struct alsps_init_info的成员中完成添加的sensor的驱动注册。
alsps_driver_add函数会把结构体struct alsps_init_info保存在一个全局的结构体数组中,如下:
struct alsps_init_info *alsps_init_list[MAX_CHOOSE_ALSPS_NUM];
完成sensor驱动添加的流程是:定义一个struct alsps_init_info结构体,完善结构体成员init,在init中注册sensor驱动,示例:
static int tcs3707_remove(void)
{
printk("tcs3707_remove\n");
i2c_del_driver(&ams_driver);
return 0;
}
static int tcs3707_local_init(void)
{
printk("tcs3707_local_init\n");
if (i2c_add_driver(&ams_driver)) {
APS_ERR("add driver error\n");
return -1;
}
return 0;
}
static struct alsps_init_info tcs3707_init_info = {
.name = "tcs3707",
.init = tcs3707_local_init,
.uninit = tcs3707_remove,
};
static int __init tcs3707_init(void)
{
printk("tcs3707_init\n");
if( alsps_driver_add(&tcs3707_init_info) ){
pr_err("add driver error\n");
return -1;
}
return 0;
}
到此为止sensor驱动就添加进mtk的alsps框架中去了,但是还没有打通和上层的交互的数据流和控制流通道。
2.alsps框架运行流程
代码地址:kernel-4.14\drivers\misc\mediatek\sensors-1.0\alsps\alsps.c
alsps框架主要看两个函数:alsps_context_alloc_object()和alsps_real_driver_init()。
alsps框架运行起来时,主要执行上述两个函数,先执行alsps_context_alloc_object(),再执行alsps_real_driver_init()。
alsps_context_alloc_object()函数主要是实现数据上报的工作队列和数据轮询定时器。在alsps_context_alloc_object函数中已经有了光距感的als和ps的数据上报工作队列和数据轮询定时器,但是没有色温的,那么参照als的添加色温的数据上报工作队列、数据轮询定时器和其他数据即可。
alsps_real_driver_init()函数主要是通过前面提到的全局结构体数组调用struct alsps_init_info结构体的init成员,完成sensor驱动的注册。
到此整个alsps的整体框架就跑完了,接下来就要了解alsps框架的数据流和控制流。
3.sensor控制流通道和数据流通道
控制流
首先要知道上层是通过什么接口来通知驱动使能sensor上报数。
可以通过追踪前面提到的alsps_context_alloc_object()函数中的的数据上报工作队列在哪里被调用了。例如:
static struct alsps_context *alsps_context_alloc_object(void)
{
......
INIT_WORK(&obj->report_als, als_work_func);
INIT_WORK(&obj->report_ps, ps_work_func);
INIT_WORK(&obj->report_als_rgb, als_rgb_work_func);//自己添加的色温数据上报工作队列
init_timer(&obj->timer_als);
init_timer(&obj->timer_ps);
init_timer(&obj->timer_als_rgb);//自己添加的色温数据轮询定时器
......
}
可以搜索report_als,找到als_poll函数里会调用als的数据上报工作队列,而als_poll是als的数据轮询定时器工作函数,那么可以继续查找als的定时器工作函数在哪里被调用,搜索timer_als,可以看到:
static int als_enable_and_batch(void){
......
if (cxt->is_als_polling_run == false) {
mod_timer(&cxt->timer_als,jiffies +atomic_read(&cxt->delay_als)
/(1000 / HZ));
cxt->is_als_polling_run = true;
cxt->is_als_first_data_after_enable = true;
}
......
}
再继续追查als_enable_and_batch函数们可以看到:
static ssize_t als_store_active(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
......
if (handle == ID_LIGHT) {
if (en) {
cxt->als_enable = 1;
last_als_report_data = -1;
} else if (!en) {
cxt->als_enable = 0;
} else {
pr_err("alsps_store_active error !!\n");
err = -1;
goto err_out;
}
#if defined(CONFIG_NANOHUB) && defined(CONFIG_MTK_ALSPSHUB)
......
#else
err = als_enable_and_batch();//als使能控制函数
#endif
} else if (handle == ID_RGBW) {
if (en)
cxt->rgbw_enable = 1;
else if (!en)
cxt->rgbw_enable = 0;
else {
pr_err("alsps_store_active error !!\n");
err = -1;
goto err_out;
}
#if defined(CONFIG_NANOHUB) && defined(CONFIG_MTK_ALSPSHUB)
......
#else
err = als_rgb_enable_and_batch();//模仿als_enable_and_batch添加的色温使能控制函数。
#endif
}
......
}
als_store_active函数为上层使能sensor的调用接口,在系统文件的位置为:
/sys/devices/virtual/sensor/m_als_misc/alsactive
alsactive节点的调用可在vendor\mediatek\proprietary\hardware\sensor\sensors-1.0\AmbienteLight.cpp文件中查看。
整个控制流为:向文件alsactive写入数据,使能sensor并轮询sensor数据。
添加的色温sensor需要去模仿als_enable_and_batch实现相同功能的函数als_rgb_enable_and_batch,在这个函数中要主要去实现sensor的使能,定时器实现步骤很简单(定时器按照前面alsps_context_alloc_object函数中的内容添加)。
sensor的控制有一个专门的结构体struct als_control_path去实现,als_control_path结构体中只有als的控制函数成员,这里添加色温的内容到其中:
struct als_control_path {
int (*open_report_data)(int open); /* open data rerport to HAL */
int (*enable_nodata)(int en); /* only enable not report event to HAL */
int (*set_delay)(u64 delay);
int (*batch)(int flag, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs);
int (*flush)(void); /* open data rerport to HAL */
int (*set_cali)(uint8_t *data, uint8_t count);
+ int (*rgbw_enable)(int en);
+ int (*rgbw_batch)(int flag, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs);
+ int (*rgbw_flush)(void);
int (*access_data_fifo)(void);
bool is_report_input_direct;
bool is_support_batch;
bool is_polling_mode;
bool is_use_common_factory;
+ bool is_rgbw_register;
};
此结构体的填充由函数als_register_control_path完成,同样我们要添加和色温相关的控制内容到其中:
int als_register_control_path(struct als_control_path *ctl)
{
struct alsps_context *cxt = NULL;
int err = 0;
cxt = alsps_context_obj;
+ if(ctl->is_rgbw_register){
+ cxt->als_ctl.rgbw_enable = ctl->rgbw_enable;
+ cxt->als_ctl.rgbw_batch = ctl->rgbw_batch;
+ cxt->als_ctl.rgbw_flush = ctl->rgbw_flush;
+ return 0;
+ }
cxt->als_ctl.set_delay = ctl->set_delay;
cxt->als_ctl.open_report_data = ctl->open_report_data;
cxt->als_ctl.enable_nodata = ctl->enable_nodata;
cxt->als_ctl.batch = ctl->batch;
cxt->als_ctl.flush = ctl->flush;
cxt->als_ctl.set_cali = ctl->set_cali;
cxt->als_ctl.is_support_batch = ctl->is_support_batch;
cxt->als_ctl.is_report_input_direct = ctl->is_report_input_direct;
cxt->als_ctl.is_use_common_factory = ctl->is_use_common_factory;
if (cxt->als_ctl.enable_nodata == NULL || cxt->als_ctl.batch == NULL ||
cxt->als_ctl.flush == NULL) {
printk("als register control path fail\n");
return -1;
}
/* add misc dev for sensor hal control cmd */
err = als_misc_init(alsps_context_obj);
if (err) {
pr_err("unable to register alsps misc device!!\n");
return -2;
}
err = sysfs_create_group(&alsps_context_obj->als_mdev.this_device->kobj,
&als_attribute_group);//创建文件节点
if (err < 0) {
pr_err("unable to create alsps attribute file\n");
return -3;
}
kobject_uevent(&alsps_context_obj->als_mdev.this_device->kobj,
KOBJ_ADD);
return 0;
}
可以看到als_register_control_path函数中创建了上层调用的节点,在本驱动中既要使用光距感也要使用色温,都会使用这个函数添加控制节点,因此添加了判断标识is_rgbw_register,这样就不会重复创建文件节点和设置其他属性导致出错。
在源码内搜索als_register_control_path函数,可以看到,在自带的源码sensor驱动中的probe中调用了该函数,添加自己要用到的控制函数和变量:
static int stk3a5x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
...
struct als_control_path als_ctl = { 0 };
...
als_ctl.open_report_data = als_open_report_data;
als_ctl.enable_nodata = als_enable_nodata;
als_ctl.set_delay = als_set_delay;
als_ctl.batch = als_batch;
als_ctl.flush = als_flush;
als_ctl.is_report_input_direct = false;
als_ctl.is_use_common_factory = false;
if (1 == obj->hw->polling_mode_als)
{
als_ctl.is_polling_mode = true;
}
else
{
als_ctl.is_polling_mode = false;
}
#ifdef CUSTOM_KERNEL_SENSORHUB
als_ctl.is_support_batch = obj->hw.is_batch_supported_als;
#else
als_ctl.is_support_batch = false;
#endif
APS_ERR("stk3a5x 24778451 als_register_control_path\n");
err = als_register_control_path(&als_ctl);
......
}
那么色温sensor的驱动也同样要按照这种格式添加自己的控制函数,实现如下:
int tcs3707_als_interface_init(void)
{
int err = 0;
struct als_control_path als_ctl = { 0 };
struct als_rgb_data_path als_rgb_data = { 0 };
/*控制流*/
als_ctl.is_rgbw_register = 1;
als_ctl.rgbw_enable = rgbw_enable;
als_ctl.rgbw_batch = rgbw_batch;
als_ctl.rgbw_flush = rgbw_flush;
err = als_register_control_path(&als_ctl);
if (err) {
APS_ERR("register fail = %d\n", err);
}
/*数据流*/
als_rgb_data.get_data = als_get_data;
als_rgb_data.als_rgb_get_raw_data = als_get_raw_data;
err = als_rgb_register_data_path(&als_rgb_data);
if (err) {
APS_ERR("als_rgb_register_data_path register fail = %d\n", err);
}
als_ctl.is_rgbw_register = 0;
return 0;
}
把该函数添加到色温驱动的probe函数中即可,然后实现三个函数(rgbw_enable、rgbw_batch和rgbw_flush)。
到此上层对底层的控制基本就完成了,重要的还是rgbw_enable函数,其他可以为空。
数据流
数据是通过输入sensor_input_event函数上报,前面已经提到数据的轮询,和数据上报工作队列。
重点就是要完成数据上报工作对列函数,数据轮询是每隔一段调用数据上报工作队列。
同样的模仿als的work_fun函数添加自己的rgb_work_func函数:
static void als_rgb_work_func(struct work_struct *work)
{
struct alsps_context *cxt = NULL;
static int32_t value[4];
int err = 0;
cxt = alsps_context_obj;
if (cxt->als_rgb_data.als_rgb_get_raw_data == NULL) {
ALSPS_PR_ERR("alsps driver not register als_rgb_get_raw_data path\n");
return;
}
err = cxt->als_rgb_data.als_rgb_get_raw_data((int32_t*)value);
if (err) {
ALSPS_PR_ERR("get alsps rgb data fails!!\n");
goto als_loop;
}
rgbw_data_report((int32_t*)value);//对sensor_input_event函数进行了封装
als_loop:
if (true == cxt->is_als_rgb_polling_run)
mod_timer(&cxt->timer_als_rgb, jiffies + atomic_read(&cxt->delay_als_rgb)/(1000/HZ));
}
可以看到有用到获取数据的结构体,同样模仿als_data,来添加自己的结构als_rgb_data:
struct als_data_path {
int (*get_data)(int *als_value, int *status);
int (*als_get_raw_data)(int *als_value);
int vender_div;
};
struct als_rgb_data_path {
int (*get_data)(int *rgb_value, int *status);
int (*als_rgb_get_raw_data)(int *rgb_value);
int vender_div;
};
struct alsps_context {
struct input_dev *idev;
struct sensor_attr_t als_mdev;
struct sensor_attr_t ps_mdev;
struct work_struct report_ps;
struct work_struct report_als;
+ struct work_struct report_als_rgb;
struct mutex alsps_op_mutex;
struct timer_list timer_als; /*als polling timer */
struct timer_list timer_ps; /* ps polling timer */
+ struct timer_list timer_als_rgb;
atomic_t trace;
atomic_t delay_als; /*als polling period for reporting input event*/
atomic_t delay_ps; /*ps polling period for reporting input event*/
+ atomic_t delay_als_rgb;
atomic_t wake; /*user-space request to wake-up, used with stop*/
atomic_t early_suspend;
struct alsps_data drv_data;
struct als_control_path als_ctl;
struct als_data_path als_data;
+ struct als_rgb_data_path als_rgb_data;
struct ps_control_path ps_ctl;
struct ps_data_path ps_data;
/* Active, but HAL don't need data sensor.such as orientation need */
bool is_als_active_nodata;
bool is_als_active_data; /* Active and HAL need data . */
/* Active, but HAL don't need data sensor.such as orientation need */
bool is_ps_active_nodata;
bool is_ps_active_data; /* Active and HAL need data . */
bool is_als_first_data_after_enable;
bool is_ps_first_data_after_enable;
bool is_als_polling_run;
bool is_ps_polling_run;
bool is_als_rgb_polling_run;
/* v2.judging whether sensor is in batch mode */
bool is_als_batch_enable;
bool is_ps_batch_enable;
bool is_get_valid_ps_data_after_enable;
bool is_get_valid_als_data_after_enable;
int als_power;
+ int rgbw_power;
int als_enable;
+ int rgbw_enable;
int64_t als_delay_ns;
int64_t als_latency_ns;
int64_t als_rgb_delay_ns;
int64_t als_rgb_latency_ns;
+ int64_t rgbw_delay_ns;
+ int64_t rgbw_latency_ns;
};
这里的struct alsps_context结构的成员添加,在前面的介绍中就应该已经添加完,这贴出来做参考。
数据结构也要通过一个函数来添加到全局结构体struct alsps_context中,模仿als_register_data_path函数添加als_rgb_register_data_path函数:
int als_rgb_register_data_path(struct als_rgb_data_path *data)
{
struct alsps_context *cxt = NULL;
cxt = alsps_context_obj;
cxt->als_rgb_data.als_rgb_get_raw_data = data->als_rgb_get_raw_data;
if (cxt->als_rgb_data.als_rgb_get_raw_data == NULL) {
ALSPS_LOG("als rgbw register data path fail\n");
return -1;
}
return 0;
}
然后继续完成struct als_rgb_data_path 结构体成员的添加(已经在控制流阶段贴出代码,这里删减后再贴出):
int tcs3707_als_interface_init(void)
{
int err = 0;
struct als_rgb_data_path als_rgb_data = { 0 };
...
/*数据流*/
als_rgb_data.get_data = als_get_data;
als_rgb_data.als_rgb_get_raw_data = als_get_raw_data;
err = als_rgb_register_data_path(&als_rgb_data);
if (err) {
APS_ERR("als_rgb_register_data_path register fail = %d\n", err);
}
...
return 0;
}
函数als_get_data可以为空,不进行调用。主要去完成als_get_raw_data函数,和色温驱动的数据获取函数对接,按照rgb数据格式上报数据即可。
到此alsps的框架就分析完了,了解上层是通过哪些节点(系统文件夹m_als_misc下)控制sensor的状态,了解数据是如何上报(通过定时器每隔一段时间,去调用工作队列获取sensor数据然后上报)。
其他的sensor框架和alsps类似,可做为参考。
-----欢迎点赞,收藏和关注------