前言
前面我們已經講解了sensor框架中的framework到vendor層,這篇文章我們將會講解kernel層的內容。不過不同的芯片平臺,kernel層中的sensor框架是不同的,這裏針對的是mt8167s平臺。不過這裏提醒一下,MTK平臺應該從kernel 3.x版本後就不支持溫溼度傳感器的框架了,不過幸好他們還保留了框架的雛形在,我們需要自行解決一下編譯問題。
正文
我們先看一下代碼的具體目錄:
drivers/misc/mediatek/sensors-1.0$ ls
accelerometer/ alsps/ dummy.c humidity/ magnetometer/ sensorHub/ accelgyro/
barometer/ geofence/ hwmon/ Makefile situation/ activity_sensor/
biometric/ gyroscope/ Kconfig sensorfusion/ step_counter/
目錄結構很清晰,不同的sensor都有單獨的目錄,這篇文章我們還是以溼度傳感器爲例,所以這裏單獨研究一下humidity。還是先看一下代碼目錄結構:
drivers/misc/mediatek/sensors-1.0/humidity$ ls
aht10/ hmdyhub/ humidity.c humidity_factory.c inc/ Kconfig Makefile
humidity.c文件爲不同型號的溼度傳感器驅動提供一些公共的接口,也可以說是MTK爲我們抽象一個有關humidity sensor的基本架構。在移植一個新型號的sensor時,只要將其通過公共接口註冊進系統就可以了。
1、初始化
static struct hmdy_init_info aht10_init_info = {
.name = "aht10",
.init = aht10_local_init,
.uninit = aht10_local_uninit,
};
static int __init aht10_init(void)
{
hmdy_driver_add(&aht10_init_info);
AHT_FUN();
return 0;
}
module_init(aht10_init);
在aht10驅動初始化的時候,通過hmdy_driver_add接口把我們的aht10驅動註冊進系統
int hmdy_driver_add(struct hmdy_init_info *obj)
{
int err = 0;
int i = 0;
HMDY_FUN();
if (!obj) {
HMDY_PR_ERR("HMDY driver add fail, hmdy_init_info is NULL\n");
return -1;
}
for (i = 0; i < MAX_CHOOSE_HMDY_NUM; i++) {
if (i == 0) {
HMDY_LOG("register humidity driver for the first time\n");
if (platform_driver_register(&humidity_driver))
HMDY_PR_ERR("failed to register gensor driver already exist\n");
}
if (humidity_init_list[i] == NULL) {
obj->platform_diver_addr = &humidity_driver;
humidity_init_list[i] = obj;
break;
}
}
if (i >= MAX_CHOOSE_HMDY_NUM) {
HMDY_PR_ERR("HMDY driver add err\n");
err = -1;
}
return err;
}
其實就是將我們自定義的struct hmdy_init_info aht10_init_info結構體保存到全局變量數組humidity_init_list中。然後在humidity驅動起來的時候,會通過hmdy_real_driver_init()接口調用已經註冊的sensor的init函數:
static int hmdy_real_driver_init(void)
{
int i = 0;
int err = 0;
for (i = 0; i < MAX_CHOOSE_HMDY_NUM; i++) {
if (humidity_init_list[i] != 0) {
err = humidity_init_list[i]->init();
if (err == 0) {
break;
}
}
}
if (i == MAX_CHOOSE_HMDY_NUM) {
err = -1;
}
return err;
}
這裏的init()函數對應到我們sensor的aht10_local_init函數:
static int aht10_local_init(void)
{
if (i2c_add_driver(&aht10_i2c_driver)) {
return -1;
}
if (-1 == aht10_init_flag) {
return -1;
}
return 0;
}
到這裏就是我們熟悉的I2C設備註冊函數了i2c_add_driver()。假設我們的sensor設備也正常加入到系統,調用我們自定義的probe函數,這裏面就需要我們進行三步重要的操作:
(1)設置設備資源
sensor框架爲我們提供了接口get_hmdy_dts_func()去解析我們的設備資源,包含I2C的地址,是否支持設置採樣率等等。
(2)struct hmdy_control_path
我們要設置自己的struct hmdy_control_path結構體初始值:
struct hmdy_control_path hmdy_control_path = {0};
hmdy_control_path.is_use_common_factory = false;
hmdy_control_path.open_report_data = aht10_open_report_data; /* 作用未知,一般直接返回就好 */
hmdy_control_path.enable_nodata = aht10_enable_nodata; /* 上層在打開sensor設備的時候,會調用到這個函數 */
hmdy_control_path.set_delay = aht10_set_delay; /* 字面上是用來設置延時,不過如果不需要可以直接返回 */
hmdy_control_path.is_report_input_direct = false;
hmdy_control_path.is_support_batch = dev_data->hw->is_batch_supported_hmdy; /* 是否支持設置採樣率 */
ret = hmdy_register_control_path(&hmdy_control_path); /* 將前面設置好的struct hmdy_control_path結構體通過公共接口註冊進系統 */
if (ret) {
AHT_INFO("register hmdy control path err\n");
goto exit_delete_attr;
}
(3)struct hmdy_data_path
struct hmdy_data_path hmdy_data_path = {0};
hmdy_data_path.get_data = aht10_get_humidity_data;
hmdy_data_path.vender_div = 10;
ret = hmdy_register_data_path(&hmdy_data_path);
if (ret) {
AHT_INFO("hmdy_register_data_path failed, ret = %d\n", ret);
goto exit_delete_attr;
}
這個結構體纔是重頭戲,其中get_data接口就是用來獲取sensor想要上報的數據:
int (*get_data)(int *value, int *status);
其中,value就是上報的數據值,同時通過status上報sensor的狀態。另外,上報的數據有時候需要調整一個百分比,那麼就會用到vender_div值了,在調試過程中自行調整即可。設置完畢就可以通過接口hmdy_register_data_path()將我們自定義的結構體註冊進系統了。
結語
kernel層框架的要點大概就這麼多,不同的sensor,基本的驅動流程都類似,讀完我這系列文章後應該就能一通百通了。