diff --git a/pyectool/__init__.py b/pyectool/__init__.py index e1bd152..e4e6df5 100644 --- a/pyectool/__init__.py +++ b/pyectool/__init__.py @@ -1,27 +1,9 @@ from __future__ import annotations -from .libectool_py import ( - __doc__, - __version__, - ascii_mode, - auto_fan_control, - get_max_non_battery_temperature, - get_max_temperature, - init, - is_on_ac, - release, - set_fan_duty, -) +from .libectool_py import __doc__, __version__, ECController __all__ = [ "__doc__", "__version__", - "ascii_mode", - "auto_fan_control", - "get_max_non_battery_temperature", - "get_max_temperature", - "init", - "is_on_ac", - "release", - "set_fan_duty", + "ECController", ] diff --git a/pyectool/__init__.pyi b/pyectool/__init__.pyi index 7337574..1d562ab 100644 --- a/pyectool/__init__.pyi +++ b/pyectool/__init__.pyi @@ -3,12 +3,10 @@ from __future__ import annotations __doc__: str __version__: str -def init() -> None: ... -def release() -> None: ... -def is_on_ac() -> bool: ... -def auto_fan_control() -> None: ... -def set_fan_duty(duty: int) -> None: ... -def get_max_temperature() -> float: ... -def get_max_non_battery_temperature() -> float: ... - -ascii_mode: bool +class ECController: + def __init__(self) -> None: ... + def is_on_ac(self) -> bool: ... + def auto_fan_control(self) -> None: ... + def set_fan_duty(self, duty: int) -> None: ... + def get_max_temperature(self) -> float: ... + def get_max_non_battery_temperature(self) -> float: ... \ No newline at end of file diff --git a/src/bindings/CMakeLists.txt b/src/bindings/CMakeLists.txt index 8b3dd75..788b69b 100644 --- a/src/bindings/CMakeLists.txt +++ b/src/bindings/CMakeLists.txt @@ -1,7 +1,8 @@ # Create the Python module -python_add_library(libectool_py MODULE libectool_py.cc WITH_SOABI) +python_add_library(libectool_py MODULE PyECController.cc ECController.cc + WITH_SOABI) # Link against required libraries target_link_libraries(libectool_py PRIVATE pybind11::headers libectool) -target_include_directories(libectool_py PUBLIC ../include) +target_include_directories(libectool_py PRIVATE . ../include) target_compile_definitions(libectool_py PUBLIC VERSION_INFO=${PROJECT_VERSION}) diff --git a/src/bindings/ECController.cc b/src/bindings/ECController.cc new file mode 100644 index 0000000..96e616a --- /dev/null +++ b/src/bindings/ECController.cc @@ -0,0 +1,49 @@ +#include "ECController.h" +#include "libectool.h" + +void ECController::handle_error(int code, const std::string &msg) { + if (code == 0) return; + + std::string reason; + switch (code) { + case EC_ERR_INIT: reason = "EC initialization failed"; break; + case EC_ERR_READMEM: reason = "EC memory read failed"; break; + case EC_ERR_EC_COMMAND: reason = "EC command failed"; break; + case EC_ERR_INVALID_PARAM: reason = "Invalid parameter"; break; + default: reason = "Unknown error"; break; + } + + throw std::runtime_error(msg + " (" + reason + ", code " + std::to_string(code) + ")"); +} + + +bool ECController::is_on_ac() { + int ac; + int ret = ec_is_on_ac(&ac); + handle_error(ret, "Failed to read AC status"); + return ac; +} + +void ECController::auto_fan_control() { + int ret = ec_auto_fan_control(); + handle_error(ret, "Failed to enable auto fan control"); +} + +void ECController::set_fan_duty(int duty) { + int ret = ec_set_fan_duty(duty); + handle_error(ret, "Failed to set fan duty"); +} + +float ECController::get_max_temperature() { + float t; + int ret = ec_get_max_temperature(&t); + handle_error(ret, "Failed to get max temperature"); + return t; +} + +float ECController::get_max_non_battery_temperature() { + float t; + int ret = ec_get_max_non_battery_temperature(&t); + handle_error(ret, "Failed to get non-battery temperature"); + return t; +} diff --git a/src/bindings/ECController.h b/src/bindings/ECController.h new file mode 100644 index 0000000..969ed54 --- /dev/null +++ b/src/bindings/ECController.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +class ECController { +public: + bool is_on_ac(); + void auto_fan_control(); + void set_fan_duty(int duty); + float get_max_temperature(); + float get_max_non_battery_temperature(); + +private: + void handle_error(int code, const std::string &msg); +}; diff --git a/src/bindings/PyECController.cc b/src/bindings/PyECController.cc new file mode 100644 index 0000000..e6d9898 --- /dev/null +++ b/src/bindings/PyECController.cc @@ -0,0 +1,29 @@ +#include +#include "ECController.h" + +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + +namespace py = pybind11; + +PYBIND11_MODULE(libectool_py, m) { + m.doc() = "Python bindings for ectool"; + + py::class_(m, "ECController") + .def(py::init<>()) + .def("is_on_ac", &ECController::is_on_ac, "Check if on AC power") + .def("auto_fan_control", &ECController::auto_fan_control, "Enable automatic fan control") + .def("set_fan_duty", &ECController::set_fan_duty, + "Set fan duty cycle (0-100)", py::arg("duty")) + .def("get_max_temperature", &ECController::get_max_temperature, + "Get max temperature") + .def("get_max_non_battery_temperature", + &ECController::get_max_non_battery_temperature, + "Get max non-battery temperature"); + +#ifdef VERSION_INFO + m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); +#else + m.attr("__version__") = "dev"; +#endif +} diff --git a/src/bindings/libectool_py.cc b/src/bindings/libectool_py.cc deleted file mode 100644 index f77272a..0000000 --- a/src/bindings/libectool_py.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include "libectool.h" - -#define STRINGIFY(x) #x -#define MACRO_STRINGIFY(x) STRINGIFY(x) - -namespace py = pybind11; - -PYBIND11_MODULE(libectool_py, m) { - m.doc() = "Python bindings for ectool"; - - // Optional: expose init/release explicitly - m.def("init", &libectool_init, "Initialize libectool"); - m.def("release", &libectool_release, "Release libectool"); - - // Expose API functions - m.def("is_on_ac", &is_on_ac, "Check if on AC power"); - m.def("auto_fan_control", &auto_fan_control, "Enable automatic fan control"); - m.def("set_fan_duty", &set_fan_duty, py::arg("duty"), "Set fan duty cycle (0-100)"); - m.def("get_max_temperature", &get_max_temperature, "Get max temperature"); - m.def("get_max_non_battery_temperature", &get_max_non_battery_temperature, "Get max non-battery temperature"); - - // Expose global variable ascii_mode - m.attr("ascii_mode") = py::cast(&ascii_mode, py::return_value_policy::reference); - - #ifdef VERSION_INFO - m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); - #else - m.attr("__version__") = "dev"; - #endif -} diff --git a/src/core/libectool.cc b/src/core/libectool.cc index 8c8c64f..849cfd3 100644 --- a/src/core/libectool.cc +++ b/src/core/libectool.cc @@ -49,21 +49,18 @@ int libectool_init() /* For non-USB alt interfaces, we need to acquire the GEC lock */ if (!(interfaces & COMM_USB) && acquire_gec_lock(GEC_LOCK_TIMEOUT_SECS) < 0) { - fprintf(stderr, "Could not acquire GEC lock.\n"); return -1; } /* If the interface is set to USB, try that (no lock needed) */ if (interfaces == COMM_USB) { #ifndef _WIN32 if (comm_init_usb(vid, pid)) { - fprintf(stderr, "Couldn't find EC on USB.\n"); /* Release the lock if it was acquired */ release_gec_lock(); return -1; } #endif } else if (comm_init_alt(interfaces, device_name, i2c_bus)) { - fprintf(stderr, "Couldn't find EC\n"); release_gec_lock(); return -1; } @@ -71,7 +68,6 @@ int libectool_init() /* Initialize ring buffers for sending/receiving EC commands */ if (comm_init_buffer()) { - fprintf(stderr, "Couldn't initialize buffers\n"); release_gec_lock(); return -1; } @@ -91,174 +87,158 @@ void libectool_release() #endif } -static uint8_t read_mapped_mem8(uint8_t offset) +int read_mapped_temperature(int id) { int ret; uint8_t val; - ret = ec_readmem(offset, sizeof(val), &val); - if (ret <= 0) { - fprintf(stderr, "failure in %s(): %d\n", __func__, ret); - exit(1); - } - return val; -} + ret = ec_readmem(EC_MEMMAP_THERMAL_VERSION, sizeof(val), &val); + if (ret <= 0 || val == 0) + return EC_TEMP_SENSOR_NOT_PRESENT; -int read_mapped_temperature(int id) -{ - int rv; - - if (!read_mapped_mem8(EC_MEMMAP_THERMAL_VERSION)) { - /* - * The temp_sensor_init() is not called, which implies no - * temp sensor is defined. - */ - rv = EC_TEMP_SENSOR_NOT_PRESENT; - } else if (id < EC_TEMP_SENSOR_ENTRIES) - rv = read_mapped_mem8(EC_MEMMAP_TEMP_SENSOR + id); - else if (read_mapped_mem8(EC_MEMMAP_THERMAL_VERSION) >= 2) - rv = read_mapped_mem8(EC_MEMMAP_TEMP_SENSOR_B + id - - EC_TEMP_SENSOR_ENTRIES); - else { - /* Sensor in second bank, but second bank isn't supported */ - rv = EC_TEMP_SENSOR_NOT_PRESENT; + if (id < EC_TEMP_SENSOR_ENTRIES) { + ret = ec_readmem(EC_MEMMAP_TEMP_SENSOR + id, sizeof(val), &val); + return (ret <= 0) ? EC_TEMP_SENSOR_ERROR : val; } - return rv; + + // Check if second bank is supported + if (val < 2) + return EC_TEMP_SENSOR_NOT_PRESENT; + + ret = ec_readmem( + EC_MEMMAP_TEMP_SENSOR_B + id - EC_TEMP_SENSOR_ENTRIES, + sizeof(val), &val); + return (ret <= 0) ? EC_TEMP_SENSOR_ERROR : val; } // ----------------------------------------------------------------------------- // Top-level endpoint functions // ----------------------------------------------------------------------------- -bool is_on_ac() { - if (libectool_init() < 0) - fprintf(stderr, "Failed initializing EC connection\n"); +int ec_is_on_ac(int *ac_present) { + int ret; + uint8_t flags; + + if (!ac_present) + return EC_ERR_INVALID_PARAM; - uint8_t flags = read_mapped_mem8(EC_MEMMAP_BATT_FLAG); - bool ac_present = (flags & EC_BATT_FLAG_AC_PRESENT); + ret = libectool_init(); + if (ret < 0) + return EC_ERR_INIT; - libectool_release(); + ret = ec_readmem(EC_MEMMAP_BATT_FLAG, sizeof(flags), &flags); - return ac_present; -} + if (ret <= 0) { + libectool_release(); + return EC_ERR_READMEM; + } -void auto_fan_control() { - if (libectool_init() < 0) - fprintf(stderr, "Failed initializing EC connection\n"); + *ac_present = !!(flags & EC_BATT_FLAG_AC_PRESENT); + libectool_release(); + return 0; +} - int rv = ec_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0, NULL, 0, NULL, 0); +int ec_auto_fan_control() { + int ret = libectool_init(); + if (ret < 0) + return EC_ERR_INIT; - if (rv < 0) - fprintf(stderr, "Failed to enable auto fan control\n"); + ret = ec_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0, NULL, 0, NULL, 0); libectool_release(); + if (ret < 0) + return EC_ERR_EC_COMMAND; + return 0; } -void set_fan_duty(int duty) { - if (libectool_init() < 0) - fprintf(stderr, "Failed initializing EC connection\n"); +int ec_set_fan_duty(int duty) { + if (duty < 0 || duty > 100) + return EC_ERR_INVALID_PARAM; - struct ec_params_pwm_set_fan_duty_v0 p_v0; - int rv; - - if (duty < 0 || duty > 100) { - fprintf(stderr, "Error: Fan duty cycle must be between 0 and 100.\n"); - return; - } + int ret = libectool_init(); + if (ret < 0) + return EC_ERR_INIT; + struct ec_params_pwm_set_fan_duty_v0 p_v0; p_v0.percent = duty; - rv = ec_command(EC_CMD_PWM_SET_FAN_DUTY, 0, &p_v0, sizeof(p_v0), + ret = ec_command(EC_CMD_PWM_SET_FAN_DUTY, 0, &p_v0, sizeof(p_v0), NULL, 0); - if (rv < 0) - fprintf(stderr, "Error: Can't set duty cycle\n"); libectool_release(); + if (ret < 0) + return EC_ERR_EC_COMMAND; + return 0; } -// Get the maximum temperature from all sensors -float get_max_temperature() { - if (libectool_init() < 0) - fprintf(stderr, "Failed initializing EC connection\n"); +int ec_get_max_temperature(float *max_temp) { + if (!max_temp) + return EC_ERR_INVALID_PARAM; - float max_temp = -1.0f; + int ret = libectool_init(); + if (ret < 0) + return EC_ERR_INIT; + + float t = -1.0f; int mtemp, temp; int id; for (id = 0; id < EC_MAX_TEMP_SENSOR_ENTRIES; id++) { mtemp = read_mapped_temperature(id); switch (mtemp) { - case EC_TEMP_SENSOR_NOT_PRESENT: - break; - case EC_TEMP_SENSOR_ERROR: - fprintf(stderr, "Sensor %d error\n", id); - break; - case EC_TEMP_SENSOR_NOT_POWERED: - fprintf(stderr, "Sensor %d disabled\n", id); - break; - case EC_TEMP_SENSOR_NOT_CALIBRATED: - fprintf(stderr, "Sensor %d not calibrated\n", - id); - break; - default: - temp = K_TO_C(mtemp + EC_TEMP_SENSOR_OFFSET); - } - - if (temp > max_temp) { - max_temp = temp; + case EC_TEMP_SENSOR_NOT_PRESENT: + case EC_TEMP_SENSOR_ERROR: + case EC_TEMP_SENSOR_NOT_POWERED: + case EC_TEMP_SENSOR_NOT_CALIBRATED: + continue; + default: + temp = K_TO_C(mtemp + EC_TEMP_SENSOR_OFFSET); + if (temp > t) + t = temp; } } + libectool_release(); - return max_temp; + + if (t < 0) + return EC_ERR_READMEM; + *max_temp = t; + return 0; } -float get_max_non_battery_temperature() +int ec_get_max_non_battery_temperature(float *max_temp) { - if (libectool_init() < 0) - fprintf(stderr, "Failed initializing EC connection\n"); + if (!max_temp) + return EC_ERR_INVALID_PARAM; + + int ret = libectool_init(); + if (ret < 0) + return EC_ERR_INIT; struct ec_params_temp_sensor_get_info p; struct ec_response_temp_sensor_get_info r; - int rv; - float max_temp = -1.0f; + float t = -1.0f; int mtemp, temp; - int id; for (p.id = 0; p.id < EC_MAX_TEMP_SENSOR_ENTRIES; p.id++) { - if (read_mapped_temperature(p.id) == EC_TEMP_SENSOR_NOT_PRESENT) + mtemp = read_mapped_temperature(p.id); + if (mtemp < 0) continue; - rv = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, &p, + ret = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, &p, sizeof(p), &r, sizeof(r)); - if (rv < 0) + if (ret < 0) continue; - printf("%d: %d %s\n", p.id, r.sensor_type, - r.sensor_name); - - if(strcmp(r.sensor_name, "Battery")){ // not eqaul to battery - mtemp = read_mapped_temperature(p.id); - switch (mtemp) { - case EC_TEMP_SENSOR_NOT_PRESENT: - break; - case EC_TEMP_SENSOR_ERROR: - fprintf(stderr, "Sensor %d error\n", id); - break; - case EC_TEMP_SENSOR_NOT_POWERED: - fprintf(stderr, "Sensor %d disabled\n", id); - break; - case EC_TEMP_SENSOR_NOT_CALIBRATED: - fprintf(stderr, "Sensor %d not calibrated\n", - id); - break; - default: - temp = K_TO_C(mtemp + EC_TEMP_SENSOR_OFFSET); - } + if(strcmp(r.sensor_name, "Battery")){ temp = K_TO_C(mtemp + EC_TEMP_SENSOR_OFFSET); - if (temp > max_temp) { - max_temp = temp; - } + if (temp > t) + t = temp; } } libectool_release(); - return max_temp; + + if (t < 0) + return EC_ERR_READMEM; + *max_temp = t; + return 0; } diff --git a/src/include/libectool.h b/src/include/libectool.h index 531bf8f..ebc6fbf 100644 --- a/src/include/libectool.h +++ b/src/include/libectool.h @@ -3,6 +3,12 @@ #include +// Standard error codes +#define EC_ERR_INIT -1 +#define EC_ERR_READMEM -2 +#define EC_ERR_EC_COMMAND -3 +#define EC_ERR_INVALID_PARAM -4 + #ifdef __cplusplus extern "C" { #endif @@ -12,11 +18,11 @@ int libectool_init(); void libectool_release(); // API functions to expose -bool is_on_ac(); -void auto_fan_control(); -void set_fan_duty(int duty); -float get_max_temperature(); -float get_max_non_battery_temperature(); +int ec_is_on_ac(int *ac_present); +int ec_auto_fan_control(); +int ec_set_fan_duty(int duty); +int ec_get_max_temperature(float *max_temp); +int ec_get_max_non_battery_temperature(float *max_temp); /* ASCII mode for printing, default off */ extern int ascii_mode;