diff --git a/metadata/package.yml b/metadata/package.yml index 8d21746f4d782f4c4b3c782d57c8e93444ba4d24..d3b00af92763063dd1c3f01909bd1e4d89bf0f0c 100644 --- a/metadata/package.yml +++ b/metadata/package.yml @@ -20,7 +20,7 @@ requires: - conan: lb_driver_lm75_simulation-server/[>=1.0.1 <2.0.0] when: self.options.enable_simulation test: - # 测试动态库加载功能 + # 测试通过 dlopen 加载插件,server 端依赖由 compile 传递 - conan: lb_hwmon/[>=0.2.1] tool: - conan: dtc/1.7.0 diff --git a/src/lm75/lm75.c b/src/lm75/lm75.c index 27781f0ed792341cfab27c0a7e59fd3739ad014a..276ea9c48a975ef70e0b66f5dc403ddf1f1dcf89 100644 --- a/src/lm75/lm75.c +++ b/src/lm75/lm75.c @@ -8,6 +8,7 @@ #include "lb_internal.h" #include "public/lbHardwareMonitor.h" #include "server/lbSensor.h" +#include "public/lbSensor.h" #include "server/lbDriverLm75Chip.h" #include "server/lbDriverLm75Config.h" #if BUILD_ENABLE_SIMULATION @@ -55,6 +56,9 @@ typedef struct { guint8 i2c_addr; int fd; int error; +#if BUILD_ENABLE_SIMULATION + guint32 block_duration; +#endif } Lm75Data; static Lm75Data *_get_lm75_data(const gchar *path, Lm75Param *param) @@ -140,17 +144,14 @@ static gint lm75_read_temp(int fd, Lm75Data *data, gdouble *temp) log_error("ioctl(I2C_RDWR) failed, object: %s", lbo_name(data->chip)); } data->error = 1; - + lbSensor_set_status(data->sensor, lbSensor_Status_BusTimeout); return -1; } - guint16 reg = (in[0 << 8] + in[0]); + guint16 reg = (in[0] << 8) | in[1]; int shift_bits = 16 - data->param.resolution; - if (reg < 0x8000) { - *temp = reg * 0.00390625; - } else { - *temp = (((~reg) >> shift_bits + 1) << shift_bits) * -0.00390625; - } + gint16 signed_val = (gint16)(reg << shift_bits) >> shift_bits; + *temp = signed_val * 0.00390625; return 0; } @@ -250,10 +251,15 @@ static gboolean _lm75_task(Lm75Data *data) #if BUILD_ENABLE_SIMULATION if (data->is_mock) { /* 模块值的精度为0.1度 */ - gdouble value = data->mock_value + (int)((rand() % 1000 - 500) / 500.0 * deviation * 10) / 10.0; + gdouble value = data->mock_value + (int)((rand() % 1000 - 500) / 500.0 * data->deviation * 10) / 10.0; lbSensor_set_value(data->sensor, value); return TRUE; } +#endif +#if BUILD_ENABLE_SIMULATION + if (data->block_duration) { + g_usleep(data->block_duration * 1000); + } #endif cleanup_close int fd = _lm75_open(data); if (fd < 0) { @@ -263,11 +269,128 @@ static gboolean _lm75_task(Lm75Data *data) gint ret = lm75_read_temp(fd, data, &temp); if (ret == 0) { data->error = 0; + lbSensor_set_status(data->sensor, lbSensor_Status_Okay); lbSensor_set_value(data->sensor, temp); } return TRUE; } +#if BUILD_ENABLE_SIMULATION +int lbDriverLm75Simulation_Mock(lbDriverLm75Simulation obj, const lbDriverLm75Simulation_Mock_Req *req, + lbDriverLm75Simulation_Mock_Rsp **rsp, GError **error, LBMethodExtData *ext_data) +{ + Lm75Data *data = lbDriverLm75Simulation_data(obj); + if (!data) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Simulation data not found"); + return -1; + } + data->is_mock = TRUE; + data->mock_value = req->value; + data->deviation = req->deviation; + lbSensor_set_status(data->sensor, req->status); + *rsp = g_new0(lbDriverLm75Simulation_Mock_Rsp, 1); + (*rsp)->okay = TRUE; + (*rsp)->message = g_strdup(""); + return 0; +} + +int lbDriverLm75Simulation_Revert(lbDriverLm75Simulation obj, const lbDriverLm75Simulation_Revert_Req *req, + lbDriverLm75Simulation_Revert_Rsp **rsp, GError **error, LBMethodExtData *ext_data) +{ + Lm75Data *data = lbDriverLm75Simulation_data(obj); + if (!data) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Simulation data not found"); + return -1; + } + data->is_mock = FALSE; + data->mock_value = 0; + data->deviation = 0; + data->block_duration = 0; + return 0; +} + +int lbDriverLm75Simulation_BlockAccess(lbDriverLm75Simulation obj, const lbDriverLm75Simulation_BlockAccess_Req *req, + lbDriverLm75Simulation_BlockAccess_Rsp **rsp, GError **error, + LBMethodExtData *ext_data) +{ + Lm75Data *data = lbDriverLm75Simulation_data(obj); + if (!data) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Simulation data not found"); + return -1; + } + data->block_duration = req->duration; + return 0; +} +#endif + +static void _on_read_interval_changed(lbDriverLm75Chip chip, const LBProperty *prop, GVariant *value, + gpointer user_data) +{ + Lm75Data *data = lbDriverLm75Chip_data(chip); + if (!data) { + return; + } + guint32 interval = g_variant_get_uint32(value); + if (interval && interval < MIN_READ_INTERVAL) { + interval = MIN_READ_INTERVAL; + } + G_LOCK(lock); + if (data->source_id) { + g_source_remove(data->source_id); + data->source_id = 0; + } + if (interval) { + data->source_id = g_timeout_add(interval, (GSourceFunc)_lm75_task, data); + } + G_UNLOCK(lock); +} + +int lbSensor_Read(lbSensor obj, const lbSensor_Read_Req *req, lbSensor_Read_Rsp **rsp, GError **error, + LBMethodExtData *ext_data) +{ + Lm75Data *data = NULL; + GSList *chips = lbDriverLm75Chip_list(); + for (GSList *item = chips; item; item = item->next) { + lbDriverLm75Chip chip = (lbDriverLm75Chip)item->data; + if (g_strcmp0(lbo_name(chip), lbo_name(obj)) == 0) { + data = lbDriverLm75Chip_data(chip); + break; + } + } + lbDriverLm75Chip_list_free(&chips); + + if (!data) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Sensor %s not found", lbo_name(obj)); + return -1; + } + +#if BUILD_ENABLE_SIMULATION + if (data->is_mock) { + *rsp = g_new0(lbSensor_Read_Rsp, 1); + (*rsp)->value = data->mock_value; + (*rsp)->status = lbSensor_Status_Okay; + return 0; + } +#endif + + *rsp = g_new0(lbSensor_Read_Rsp, 1); + cleanup_close int fd = _lm75_open(data); + if (fd < 0) { + (*rsp)->status = lbSensor_Status_BusError; + return 0; + } + gdouble temp = 0; + gint ret = lm75_read_temp(fd, data, &temp); + if (ret == 0) { + data->error = 0; + (*rsp)->value = temp; + (*rsp)->status = lbSensor_Status_Okay; + } else { + (*rsp)->status = lbSensor_Status_BusTimeout; + } + return 0; +} + static int _new_lm75(const lbHardwareMonitor_FdtDiscovered_Req *req, Lm75Param *param) { if (!req->node->alias) { @@ -311,17 +434,16 @@ static int _new_lm75(const lbHardwareMonitor_FdtDiscovered_Req *req, Lm75Param * const lbDriverLm75Chip chip = (lbDriverLm75Chip)lbo_new(LB_DRIVER_LM75_CHIP, tmp->str, NULL); lbDriverLm75Chip_set_fdt_path(chip, req->node->path); lbDriverLm75Chip_set_compatible(chip, param->compatible); + guint32 read_interval = 0; if (data->config) { - guint32 read_interval = 0; _lbDriverLm75Config_get_read_interval_val(data->config, &read_interval); if (read_interval && read_interval < MIN_READ_INTERVAL) { log_warn("Read interval %u is less than %u, force it to %u, config object: %s", read_interval, MIN_READ_INTERVAL, MIN_READ_INTERVAL, tmp->str); - lbDriverLm75Chip_set_read_interval(chip, MIN_READ_INTERVAL); - } else { - lbDriverLm75Chip_set_read_interval(chip, read_interval); + read_interval = MIN_READ_INTERVAL; } } + lbDriverLm75Chip_set_read_interval(chip, read_interval); if (req->node->okay) { lbDriverLm75Chip_set_okay(chip, TRUE); } else { @@ -345,6 +467,7 @@ static int _new_lm75(const lbHardwareMonitor_FdtDiscovered_Req *req, Lm75Param * data->sensor = sensor; data->chip = chip; lbDriverLm75Chip_bind(chip, data, (GDestroyNotify)_clean_lm75_data); + lbDriverLm75Chip_on_prop_changed(chip, "read_interval", _on_read_interval_changed, NULL, NULL); #if BUILD_ENABLE_SIMULATION lbDriverLm75Simulation_bind(sim, data, NULL); lbDriverLm75Simulation_present_set(sim, TRUE); @@ -389,6 +512,26 @@ static Lm75Param compatibles[] = { {.compatible = "national,lm75", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, {.compatible = "national,lm75a", .over_temp_reg = 0x03, .hysteresis_reg = 0x02, .resolution = 9}, {.compatible = "national,lm75b", .over_temp_reg = 0x03, .hysteresis_reg = 0x02, .resolution = 11}, + /* TI TMP75 系列,寄存器映射与 LM75 兼容 */ + {.compatible = "ti,tmp75", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "ti,tmp75b", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "ti,tmp75c", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "ti,tmp175", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "ti,tmp275", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + /* Dallas/MAXIM DS75 系列 */ + {.compatible = "dallas,ds75", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "dallas,ds1775", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + /* MAXIM MAX6625,仅 9-bit 分辨率 */ + {.compatible = "maxim,max6625", .over_temp_reg = 0x03, .hysteresis_reg = 0x02, .resolution = 9}, + /* Microchip MCP980x 系列 */ + {.compatible = "microchip,mcp9800", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "microchip,mcp9801", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "microchip,mcp9802", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + {.compatible = "microchip,mcp9803", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, + /* NXP LM75B */ + {.compatible = "nxp,lm75b", .over_temp_reg = 0x03, .hysteresis_reg = 0x02, .resolution = 11}, + /* STMicroelectronics STDS75 */ + {.compatible = "st,stds75", .over_temp_reg = 0x03, .hysteresis_reg = 0x02}, }; int plugin_start(void) diff --git a/test_package/lm75/CMakeLists.txt b/test_package/lm75/CMakeLists.txt index 4a6fa1e2f54e0ecf7320847cae02c27fcc9babf9..b78804ff387ff183028c278971981a0739fb1667 100644 --- a/test_package/lm75/CMakeLists.txt +++ b/test_package/lm75/CMakeLists.txt @@ -9,20 +9,57 @@ add_executable(${PROJECT_NAME} ${TEST_SRC}) pkg_check_modules(LB_BASE REQUIRED lb_base) pkg_check_modules(LB_CORE REQUIRED lb_core) pkg_check_modules(GIO REQUIRED gio-2.0) +# 服务端头文件用于直接调用 server 端函数(如 lbSensor_Read) +pkg_check_modules(LB_SENSOR_SRV REQUIRED lbSensor-server) +pkg_check_modules(LM75_CHIP_SRV REQUIRED lbDriverLm75Chip-server) +pkg_check_modules(LM75_CONF_SRV REQUIRED lbDriverLm75Config-server) +pkg_check_modules(HWMON_PUB REQUIRED lbHardwareMonitor-public) +if (BUILD_ENABLE_SIMULATION) +pkg_check_modules(LM75_SIMU_SRV REQUIRED lbDriverLm75Simulation-server) +endif() target_include_directories(${PROJECT_NAME} PUBLIC ${GLIB2_INCLUDE_DIRS} ${LB_BASE_INCLUDE_DIRS} ${LB_CORE_INCLUDE_DIRS} + ${LB_SENSOR_SRV_INCLUDE_DIRS} + ${LM75_CHIP_SRV_INCLUDE_DIRS} + ${LM75_CONF_SRV_INCLUDE_DIRS} + ${HWMON_PUB_INCLUDE_DIRS} ) +if (BUILD_ENABLE_SIMULATION) + target_include_directories(${PROJECT_NAME} PUBLIC + ${LM75_SIMU_SRV_INCLUDE_DIRS} + ) +endif() + target_link_directories(${PROJECT_NAME} PUBLIC - ${GLIB2_LIBRARIES} + ${GLIB2_LIBRARY_DIRS} ${LB_CORE_LIBRARY_DIRS} + ${LB_SENSOR_SRV_LIBRARY_DIRS} + ${LM75_CHIP_SRV_LIBRARY_DIRS} + ${LM75_CONF_SRV_LIBRARY_DIRS} + ${HWMON_PUB_LIBRARY_DIRS} +) +if (BUILD_ENABLE_SIMULATION) + target_link_directories(${PROJECT_NAME} PUBLIC + ${LM75_SIMU_SRV_LIBRARY_DIRS} ) +endif() + target_link_libraries(${PROJECT_NAME} PUBLIC ${GLIB2_LIBRARIES} ${GIO_LIBRARIES} ${LB_CORE_LIBRARIES} + ${LB_SENSOR_SRV_LIBRARIES} + ${LM75_CHIP_SRV_LIBRARIES} + ${LM75_CONF_SRV_LIBRARIES} + ${HWMON_PUB_LIBRARIES} ) +if (BUILD_ENABLE_SIMULATION) + target_link_libraries(${PROJECT_NAME} PUBLIC + ${LM75_SIMU_SRV_LIBRARIES} + ) +endif() install(TARGETS ${PROJECT_NAME} DESTINATION opt/litebmc/apps/litebmc) diff --git a/test_package/lm75/plugin_test.c b/test_package/lm75/plugin_test.c index 38ca8ef444377668ebd2b2078d6b3f1780d6b783..483e2262f76b3a59ca57c63dae9f2c719fba1a44 100644 --- a/test_package/lm75/plugin_test.c +++ b/test_package/lm75/plugin_test.c @@ -1,49 +1,406 @@ #include "lb_core.h" +#include +#include "public/lbHardwareMonitor.h" +#include "server/lbSensor.h" +#include "public/lbSensor.h" +#include "server/lbDriverLm75Chip.h" +#include "public/lbDriverLm75Chip.h" +#include "server/lbDriverLm75Config.h" +#if BUILD_ENABLE_SIMULATION +#include "server/lbDriverLm75Simulation.h" +#include "public/lbDriverLm75Simulation.h" +#endif -static void lb_hwmon_service_kill(void) -{ - gchar tmp[128] = {0}; +#define PLUGIN_PATH "opt/litebmc/apps/lb_hwmon/plugin/liblm75.so" - const gchar *pid_file = "/dev/shm/lb_hwmon_session.pid"; +static gchar *compat_national_lm75[] = {"national,lm75", NULL}; +static gchar *compat_national_lm75a[] = {"national,lm75a", NULL}; +static gchar *compat_ti_tmp75[] = {"ti,tmp75", NULL}; +static gint32 i2c_reg_48[] = {0x48}; - int fd = open(pid_file, O_RDONLY | O_CREAT, 0600); - g_assert_cmpint(fd, >=, 0); +// 模拟一个 i2c0 别名,末尾数字为 0 +static lbHardwareMonitor_FdtNode node_lm75a_48 = { + .compatible = compat_national_lm75a, + .alias = "i2c0", + .path = "/i2c0/lm75a@48", + .props = NULL, + .nodes = NULL, + .size_cells = 0, + .address_cells = 0, + .n_reg = 1, + .reg = i2c_reg_48, + .okay = TRUE, +}; - // 锁定文件,如果失败则说明文件已被锁,存在一个正在运行的进程,程序直接退出 - if (lockf(fd, F_TLOCK, 0) < 0) { - lseek(fd, 0, SEEK_SET); - int ret = read(fd, tmp, sizeof(tmp)); - if (ret > 0) { - gint pid = atoi(tmp); - kill((pid_t)pid, SIGTERM); - } - } - close(fd); +static lbHardwareMonitor_FdtNode node_lm75_49 = { + .compatible = compat_national_lm75, + .alias = "i2c0", + .path = "/i2c0/lm75@49", + .props = NULL, + .nodes = NULL, + .size_cells = 0, + .address_cells = 0, + .n_reg = 1, + .reg = (gint32[]){0x49}, + .okay = TRUE, +}; + +static lbHardwareMonitor_FdtNode node_tmp75_4a = { + .compatible = compat_ti_tmp75, + .alias = "i2c0", + .path = "/i2c0/tmp75@4a", + .props = NULL, + .nodes = NULL, + .size_cells = 0, + .address_cells = 0, + .n_reg = 1, + .reg = (gint32[]){0x4a}, + .okay = TRUE, +}; + +static lbHardwareMonitor_FdtNode node_disabled = { + .compatible = compat_national_lm75a, + .alias = "i2c0", + .path = "/i2c0/lm75a@4f", + .props = NULL, + .nodes = NULL, + .size_cells = 0, + .address_cells = 0, + .n_reg = 1, + .reg = (gint32[]){0x4f}, + .okay = FALSE, +}; + +// 无 compatible 的节点 +static lbHardwareMonitor_FdtNode node_no_compat = { + .compatible = NULL, + .alias = "i2c0", + .path = "/i2c0/unknown@50", + .props = NULL, + .nodes = NULL, + .size_cells = 0, + .address_cells = 0, + .n_reg = 1, + .reg = (gint32[]){0x50}, + .okay = TRUE, +}; + +// 不匹配的 compatible +static gchar *compat_unknown[] = {"unknown,chip", NULL}; +static lbHardwareMonitor_FdtNode node_unmatched = { + .compatible = compat_unknown, + .alias = "i2c0", + .path = "/i2c0/unknown@51", + .props = NULL, + .nodes = NULL, + .size_cells = 0, + .address_cells = 0, + .n_reg = 1, + .reg = (gint32[]){0x51}, + .okay = TRUE, +}; + +static void *dl_handle = NULL; +// 插件提供的 __weak 方法强符号,需通过 dlsym 获取(BIND_NOW 阻止运行时覆盖) +static int (*real_lbSensor_Read)(lbSensor, const lbSensor_Read_Req *, lbSensor_Read_Rsp **, GError **, + LBMethodExtData *) = NULL; +#if BUILD_ENABLE_SIMULATION +static int (*real_lbDriverLm75Simulation_Mock)(lbDriverLm75Simulation, const lbDriverLm75Simulation_Mock_Req *, + lbDriverLm75Simulation_Mock_Rsp **, GError **, LBMethodExtData *) = NULL; +static int (*real_lbDriverLm75Simulation_Revert)(lbDriverLm75Simulation, const lbDriverLm75Simulation_Revert_Req *, + lbDriverLm75Simulation_Revert_Rsp **, GError **, + LBMethodExtData *) = NULL; +static int (*real_lbDriverLm75Simulation_BlockAccess)(lbDriverLm75Simulation, + const lbDriverLm75Simulation_BlockAccess_Req *, + lbDriverLm75Simulation_BlockAccess_Rsp **, GError **, + LBMethodExtData *) = NULL; +#endif + +static void load_plugin(void) +{ + cleanup_gfree gchar *plugin_path = get_rootfs_path(PLUGIN_PATH); + dl_handle = dlopen(plugin_path, RTLD_NOW); + g_assert_nonnull(dl_handle); + int (*start)(void) = dlsym(dl_handle, "plugin_start"); + g_assert_nonnull(start); + gint ret = start(); + g_assert_cmpint(ret, ==, 0); + real_lbSensor_Read = dlsym(dl_handle, "lbSensor_Read"); + g_assert_nonnull(real_lbSensor_Read); +#if BUILD_ENABLE_SIMULATION + real_lbDriverLm75Simulation_Mock = dlsym(dl_handle, "lbDriverLm75Simulation_Mock"); + g_assert_nonnull(real_lbDriverLm75Simulation_Mock); + real_lbDriverLm75Simulation_Revert = dlsym(dl_handle, "lbDriverLm75Simulation_Revert"); + g_assert_nonnull(real_lbDriverLm75Simulation_Revert); + real_lbDriverLm75Simulation_BlockAccess = dlsym(dl_handle, "lbDriverLm75Simulation_BlockAccess"); + g_assert_nonnull(real_lbDriverLm75Simulation_BlockAccess); +#endif } -// 启动服务,如果服务已存在,则杀掉再重拉 -static void lb_hwmon_service_start(void) +static void discover_node(lbHardwareMonitor_FdtNode *node) { - cleanup_gfree gchar *exec = get_rootfs_path("/opt/litebmc/apps/lb_hwmon/lb_hwmon"); - gchar *argv[2] = {NULL}; - argv[0] = "lb_hwmon"; - pid_t pid = fork(); - if (pid == 0) { - gint ret = execv(exec, argv); - log_debug("service %s exit, code: %d", exec, ret); - } else { - waitpid(pid, NULL, 0); - } + lbHardwareMonitor_FdtDiscovered_Req req = {.node = node}; + lbHardwareMonitor_FdtDiscovered_run(NULL, &req); } -static void test_hello_world(void) +/* + * LM75 驱动综合测试(单函数避免 gtester fork 问题) + * 顺序执行所有测试场景 + */ +static void test_lm75_all(void) { - printf("test start\n"); + // ===== 1. 插件加载 ===== + load_plugin(); + GSList *list = lbDriverLm75Chip_list(); + g_assert_cmpuint(g_slist_length(list), ==, 0); + lbDriverLm75Chip_list_free(&list); + + // ===== 1.5 配置对象创建 ===== + gboolean exist = FALSE; + // 精确匹配:lm75a@48,自定义分辨率/寄存器值/超时 + lbDriverLm75Config cfg_lm75a_48 = lbDriverLm75Config_new("lm75a_48_config", &exist); + g_assert_false(exist); + lbDriverLm75Config_set_fdt_path(cfg_lm75a_48, "/i2c0/lm75a@48"); + lbDriverLm75Config_set_read_interval(cfg_lm75a_48, 500); + lbDriverLm75Config_set_resolution(cfg_lm75a_48, 11); + lbDriverLm75Config_set_config_reg_value(cfg_lm75a_48, 0x01); + lbDriverLm75Config_set_th_reg_value(cfg_lm75a_48, 0x3200); + lbDriverLm75Config_set_tl_reg_value(cfg_lm75a_48, 0x1900); + lbDriverLm75Config_set_over_temp_reg_value(cfg_lm75a_48, 0x4B00); + lbDriverLm75Config_set_hysteresis_reg_value(cfg_lm75a_48, 0x2800); + lbDriverLm75Config_set_timeout_ms(cfg_lm75a_48, 100); + lbDriverLm75Config_present_set(cfg_lm75a_48, TRUE); + + // 通配符:所有未精确匹配的设备使用此默认配置 + lbDriverLm75Config cfg_wildcard = lbDriverLm75Config_new("wildcard_config", &exist); + g_assert_false(exist); + lbDriverLm75Config_set_fdt_path(cfg_wildcard, "*"); + lbDriverLm75Config_set_read_interval(cfg_wildcard, 2000); + lbDriverLm75Config_present_set(cfg_wildcard, TRUE); + + // 精确匹配:lm75@49,无轮询、分辨率10 + lbDriverLm75Config cfg_lm75_49 = lbDriverLm75Config_new("lm75_49_config", &exist); + g_assert_false(exist); + lbDriverLm75Config_set_fdt_path(cfg_lm75_49, "/i2c0/lm75@49"); + lbDriverLm75Config_set_read_interval(cfg_lm75_49, 0); + lbDriverLm75Config_set_resolution(cfg_lm75_49, 10); + lbDriverLm75Config_set_timeout_ms(cfg_lm75_49, 50); + lbDriverLm75Config_present_set(cfg_lm75_49, TRUE); + + // 不匹配任何设备,验证不影响测试 + lbDriverLm75Config cfg_no_match = lbDriverLm75Config_new("no_match_config", &exist); + g_assert_false(exist); + lbDriverLm75Config_set_fdt_path(cfg_no_match, "/i2c0/nonexistent@99"); + lbDriverLm75Config_set_read_interval(cfg_no_match, 999); + lbDriverLm75Config_present_set(cfg_no_match, TRUE); + + GSList *cfg_list = lbDriverLm75Config_list(); + g_assert_cmpuint(g_slist_length(cfg_list), ==, 4); + lbDriverLm75Config_list_free(&cfg_list); + + // ===== 2. FDT 设备发现(精确配置匹配) ===== + discover_node(&node_lm75a_48); + list = lbDriverLm75Chip_list(); + g_assert_cmpuint(g_slist_length(list), ==, 1); + lbDriverLm75Chip chip = (lbDriverLm75Chip)list->data; + g_assert_true(lbDriverLm75Chip_present(chip)); + // 精确匹配时对象名使用配置对象名 + g_assert_cmpstr(lbo_name(chip), ==, "lm75a_48_config"); + guint32 interval = 999; + _lbDriverLm75Chip_get_read_interval_val(chip, &interval); + g_assert_cmpuint(interval, ==, 500); + gchar *compat = NULL; + _lbDriverLm75Chip_get_compatible_val(chip, &compat); + g_assert_cmpstr(compat, ==, "national,lm75a"); + g_free(compat); + gboolean okay = FALSE; + _lbDriverLm75Chip_get_okay_val(chip, &okay); + g_assert_true(okay); + GSList *sensors = lbSensor_list(); + g_assert_cmpuint(g_slist_length(sensors), ==, 1); + lbSensor sensor = (lbSensor)sensors->data; + g_assert_cmpstr(lbo_name(sensor), ==, "lm75a_48_config"); + lbSensor_list_free(&sensors); + lbDriverLm75Chip_list_free(&list); + + // ===== 3. 多设备发现(精确匹配优先于通配符) ===== + discover_node(&node_lm75_49); + discover_node(&node_tmp75_4a); + list = lbDriverLm75Chip_list(); + g_assert_cmpuint(g_slist_length(list), ==, 3); + // lm75@49 精确匹配:对象名=配置名,read_interval=0 + lbDriverLm75Chip chip_49 = lbDriverLm75Chip_get("lm75_49_config"); + g_assert_nonnull(chip_49); + _lbDriverLm75Chip_get_read_interval_val(chip_49, &interval); + g_assert_cmpuint(interval, ==, 0); + lbDriverLm75Chip_unref(&chip_49); + // tmp75@4a 通配符匹配:对象名=fdt_path,read_interval=2000 + lbDriverLm75Chip chip_4a = lbDriverLm75Chip_get("/i2c0/tmp75_4a"); + g_assert_nonnull(chip_4a); + _lbDriverLm75Chip_get_read_interval_val(chip_4a, &interval); + g_assert_cmpuint(interval, ==, 2000); + lbDriverLm75Chip_unref(&chip_4a); + lbDriverLm75Chip_list_free(&list); + + // ===== 4. disabled 设备 ===== + discover_node(&node_disabled); + list = lbDriverLm75Chip_list(); + g_assert_cmpuint(g_slist_length(list), ==, 4); + lbDriverLm75Chip last_chip = (lbDriverLm75Chip)g_slist_last(list)->data; + _lbDriverLm75Chip_get_okay_val(last_chip, &okay); + g_assert_false(okay); + lbDriverLm75Chip_list_free(&list); + + // ===== 5. 不匹配的 compatible 被忽略 ===== + guint prev = g_slist_length(lbDriverLm75Chip_list()); + discover_node(&node_unmatched); + discover_node(&node_no_compat); + g_assert_cmpuint(g_slist_length(lbDriverLm75Chip_list()), ==, prev); + + // ===== 6. BusError:设置轮询后无 I2C 设备 ===== + list = lbDriverLm75Chip_list(); + chip = (lbDriverLm75Chip)list->data; + lbDriverLm75Chip_set_read_interval(chip, 200); + sleep(1); + sensors = lbSensor_list(); + sensor = (lbSensor)sensors->data; + lbSensor_Status status = lbSensor_Status_Okay; + _lbSensor_get_status_val(sensor, &status); + g_assert_cmpint(status, ==, lbSensor_Status_BusError); + lbDriverLm75Chip_set_read_interval(chip, 0); + lbSensor_list_free(&sensors); + lbDriverLm75Chip_list_free(&list); + + // ===== 7. lbSensor.Read 返回 BusError ===== + sensors = lbSensor_list(); + sensor = (lbSensor)sensors->data; + lbSensor_Read_Req read_req = {.cached_interval_ms = 0}; + lbSensor_Read_Rsp *read_rsp = NULL; + GError *error = NULL; + gint ret = real_lbSensor_Read(sensor, &read_req, &read_rsp, &error, NULL); + g_assert_cmpint(ret, ==, 0); + g_assert_nonnull(read_rsp); + g_assert_cmpint(read_rsp->status, ==, lbSensor_Status_BusError); + lbSensor_Read_Rsp_free(&read_rsp); + g_clear_error(&error); + lbSensor_list_free(&sensors); + +#if BUILD_ENABLE_SIMULATION + // ===== 8. Mock 基本功能 ===== + sensors = lbSensor_list(); + sensor = (lbSensor)sensors->data; + const gchar *name = lbo_name(sensor); + lbDriverLm75Simulation sim = lbDriverLm75Simulation_get(name); + g_assert_nonnull(sim); + + lbDriverLm75Simulation_Mock_Req mock_req = {.value = 25.0, .status = lbSensor_Status_Okay, .deviation = 1.0}; + lbDriverLm75Simulation_Mock_Rsp *mock_rsp = NULL; + ret = real_lbDriverLm75Simulation_Mock(sim, &mock_req, &mock_rsp, &error, NULL); + g_assert_cmpint(ret, ==, 0); + g_assert_true(mock_rsp->okay); + lbDriverLm75Simulation_Mock_Rsp_free(&mock_rsp); + g_clear_error(&error); + + // 设置轮询让 mock 值生效 + list = lbDriverLm75Chip_list(); + chip = (lbDriverLm75Chip)list->data; + lbDriverLm75Chip_set_read_interval(chip, 200); + sleep(2); + + gdouble value = 0; + _lbSensor_get_value_val(sensor, &value); + g_assert_cmpfloat(value, >=, 24.0); + g_assert_cmpfloat(value, <=, 26.0); + lbDriverLm75Chip_list_free(&list); + + // ===== 9. Read with mock ===== + lbSensor_Read_Req rd_req = {.cached_interval_ms = 0}; + lbSensor_Read_Rsp *rd_rsp = NULL; + ret = real_lbSensor_Read(sensor, &rd_req, &rd_rsp, NULL, NULL); + g_assert_cmpint(ret, ==, 0); + g_assert_cmpint(rd_rsp->status, ==, lbSensor_Status_Okay); + // Read 在 mock 模式下返回 mock_value(无偏差) + g_assert_cmpfloat(rd_rsp->value, ==, 25.0); + lbSensor_Read_Rsp_free(&rd_rsp); + + // ===== 10. BlockAccess ===== + lbDriverLm75Simulation_BlockAccess_Req blk_req = {.duration = 100}; + real_lbDriverLm75Simulation_BlockAccess(sim, &blk_req, NULL, NULL, NULL); + + // ===== 11. Revert ===== + real_lbDriverLm75Simulation_Revert(sim, NULL, NULL, NULL, NULL); + + // ===== 12. read_interval 动态变更 ===== + // 重新 Mock 并设置不同值 + lbDriverLm75Simulation_Mock_Req mock2 = {.value = 20.0, .status = lbSensor_Status_Okay, .deviation = 0.0}; + real_lbDriverLm75Simulation_Mock(sim, &mock2, &mock_rsp, NULL, NULL); + lbDriverLm75Simulation_Mock_Rsp_free(&mock_rsp); + + list = lbDriverLm75Chip_list(); + chip = (lbDriverLm75Chip)list->data; + lbDriverLm75Chip_set_read_interval(chip, 200); + sleep(1); + _lbSensor_get_value_val(sensor, &value); + g_assert_cmpfloat(value, ==, 20.0); + lbDriverLm75Chip_set_read_interval(chip, 0); + lbDriverLm75Chip_list_free(&list); + + // 清理 + real_lbDriverLm75Simulation_Revert(sim, NULL, NULL, NULL, NULL); + lbDriverLm75Simulation_unref(&sim); + lbSensor_list_free(&sensors); +#endif + + // ===== 13. Chip 属性验证 ===== + list = lbDriverLm75Chip_list(); + for (GSList *item = list; item; item = item->next) { + lbDriverLm75Chip c = (lbDriverLm75Chip)item->data; + _lbDriverLm75Chip_get_compatible_val(c, &compat); + if (g_strcmp0(compat, "ti,tmp75") == 0) { + gchar *fdt_path = NULL; + _lbDriverLm75Chip_get_fdt_path_val(c, &fdt_path); + g_assert_cmpstr(fdt_path, ==, "/i2c0/tmp75@4a"); + g_free(fdt_path); + // 通配符配置的 read_interval=2000 + interval = 999; + _lbDriverLm75Chip_get_read_interval_val(c, &interval); + g_assert_cmpuint(interval, ==, 2000); + } + g_free(compat); + } + lbDriverLm75Chip_list_free(&list); + + // ===== 14. 配置属性回读验证 ===== + guint8 res = 0; + _lbDriverLm75Config_get_resolution_val(cfg_lm75a_48, &res); + g_assert_cmpuint(res, ==, 11); + _lbDriverLm75Config_get_read_interval_val(cfg_lm75a_48, &interval); + g_assert_cmpuint(interval, ==, 500); + guint16 reg_val = 0; + _lbDriverLm75Config_get_th_reg_value_val(cfg_lm75a_48, ®_val); + g_assert_cmpuint(reg_val, ==, 0x3200); + _lbDriverLm75Config_get_tl_reg_value_val(cfg_lm75a_48, ®_val); + g_assert_cmpuint(reg_val, ==, 0x1900); + _lbDriverLm75Config_get_over_temp_reg_value_val(cfg_lm75a_48, ®_val); + g_assert_cmpuint(reg_val, ==, 0x4B00); + _lbDriverLm75Config_get_hysteresis_reg_value_val(cfg_lm75a_48, ®_val); + g_assert_cmpuint(reg_val, ==, 0x2800); + guint8 cfg_reg = 0; + _lbDriverLm75Config_get_config_reg_value_val(cfg_lm75a_48, &cfg_reg); + g_assert_cmpuint(cfg_reg, ==, 0x01); + guint32 timeout = 0; + _lbDriverLm75Config_get_timeout_ms_val(cfg_lm75a_48, &timeout); + g_assert_cmpuint(timeout, ==, 100); + + // 清理配置对象 + lbDriverLm75Config_unref(&cfg_lm75a_48); + lbDriverLm75Config_unref(&cfg_wildcard); + lbDriverLm75Config_unref(&cfg_lm75_49); + lbDriverLm75Config_unref(&cfg_no_match); } void test_add_test_case(void) { - gint ret = 0; if (lb_log_get_type() == LOG_TO_FILE) { lb_log_set_level(LOG_INFO); } @@ -51,18 +408,13 @@ void test_add_test_case(void) gettimeofday(&tv, NULL); srand(tv.tv_usec); - // 启动服务,如果服务存在则重启服务 - GThread *thread = g_thread_new("IMService", (GThreadFunc)lb_hwmon_service_start, NULL); - g_thread_unref(thread); - sleep(5); - - g_test_add_func("/test/lb_hwmon_service_kill", lb_hwmon_service_kill); + g_test_add_func("/lm75/all", test_lm75_all); } int main(int argc, char *argv[]) { unlink("/dev/shm/per.db"); - lb_start("com.litebmc.lb_hwmon_test", G_BUS_TYPE_SESSION); + lb_start("com.litebmc.lm75_test", G_BUS_TYPE_SESSION); g_test_init(&argc, &argv, NULL); test_add_test_case(); return g_test_run();