From 7b3959bb069b80bcaebeb53902e974f4020ffc80 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sat, 7 Jun 2025 15:45:08 +0300 Subject: [PATCH 01/14] refactor: update submodule path; add pre-commit hooks --- .gitlab-ci.yml | 35 ----- .gitmodules | 6 +- .pre-commit-config.yaml | 59 ++++++++ pyproject.toml | 53 +++++++ src/core/CMakeLists.txt | 136 ++++++------------ .../{ectool_fanctrl.cc => ectool_wrapper.cc} | 74 +++++----- 6 files changed, 197 insertions(+), 166 deletions(-) delete mode 100644 .gitlab-ci.yml create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml rename src/core/{ectool_fanctrl.cc => ectool_wrapper.cc} (87%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 0049ac19..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,35 +0,0 @@ -stages: - - build - -build windows/x64: - stage: build - before_script: - - git submodule update --init --recursive - script: - - mkdir _build - - cd _build; - - '& "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -A x64 -T ClangCL ..' - - '& "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" --build . --config RelWithDebInfo --parallel' - artifacts: - expire_in: never - paths: - - _build/src/RelWithDebInfo/ectool.exe - - _build/src/RelWithDebInfo/ectool.pdb - tags: - - windows - -build linux/x64: - stage: build - image: debian:latest - before_script: - - apt update && apt install -yy cmake clang ninja-build git libftdi1-dev libusb-1.0-0-dev pkg-config - - git submodule update --init --recursive - script: - - mkdir _build - - cd _build; - - CC=clang CXX=clang++ cmake -GNinja .. - - cmake --build . - artifacts: - expire_in: never - paths: - - _build/src/ectool diff --git a/.gitmodules b/.gitmodules index bfd41a09..bbf88077 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "extern/FrameworkWindowsUtils"] - path = extern/FrameworkWindowsUtils - url = https://github.com/DHowett/FrameworkWindowsUtils +[submodule "src/extern/FrameworkWindowsUtils"] + path = src/extern/FrameworkWindowsUtils + url = https://github.com/DHowett/FrameworkWindowsUtils diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..7c086f3f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,59 @@ +# To use: +# +# pre-commit run -a +# +# Or: +# +# pre-commit install # (runs every time you commit in git) +# +# To update this file: +# +# pre-commit autoupdate +# +# See https://github.com/pre-commit/pre-commit + +ci: + autoupdate_commit_msg: "chore: update pre-commit hooks" + autofix_commit_msg: "style: pre-commit fixes" + +repos: +# Standard hooks +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml + exclude: ^conda\.recipe/meta\.yaml$ + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: requirements-txt-fixer + - id: trailing-whitespace + +# Check linting and style issues +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.11.13" + hooks: + - id: ruff + args: ["--fix", "--show-fixes"] + - id: ruff-format + exclude: ^(docs) + +# Changes tabs to spaces +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: remove-tabs + exclude: ^(docs) + +# CMake formatting +- repo: https://github.com/cheshirekow/cmake-format-precommit + rev: v0.6.13 + hooks: + - id: cmake-format + additional_dependencies: [pyyaml] + types: [file] + files: (\.cmake|CMakeLists.txt)(.in)?$ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..80287db0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,53 @@ +[build-system] +requires = ["scikit-build-core>=0.10", "pybind11"] +build-backend = "scikit_build_core.build" + + +[project] +name = "libectool" +version = "0.0.1" +description="A shared library (libectool) that exposes ectool functionalities for seamless integration with other applications" +readme = "README.md" +authors = [ + { name = "Ahmed Gamea", email = "ahmed.gamea@ejust.edu.eg" }, +] + +[tool.scikit-build] +wheel.expand-macos-universal-tags = true +minimum-version = "build-system.requires" + +[tool.cibuildwheel] +build-frontend = "build[uv]" + +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "I", # isort + "ARG", # flake8-unused-arguments + "C4", # flake8-comprehensions + "EM", # flake8-errmsg + "ICN", # flake8-import-conventions + "G", # flake8-logging-format + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib + "RET", # flake8-return + "RUF", # Ruff-specific + "SIM", # flake8-simplify + "T20", # flake8-print + "UP", # pyupgrade + "YTT", # flake8-2020 + "EXE", # flake8-executable + "NPY", # NumPy specific rules + "PD", # pandas-vet +] +ignore = [ + "PLR09", # Too many X + "PLR2004", # Magic comparison +] +isort.required-imports = ["from __future__ import annotations"] + +[tool.ruff.lint.per-file-ignores] +"tests/**" = ["T20"] diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ff2961a9..76dbb1f3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -9,127 +9,81 @@ set(ECTOOL_COMMON_SOURCES misc_util.cc crc.cc comm-host.cc - lock/gec_lock.cc -) + lock/gec_lock.cc) if(NOT WIN32) - list(APPEND ECTOOL_COMMON_SOURCES - comm-dev.cc - comm-i2c.cc - comm-lpc.cc - comm-servo-spi.cc - comm-usb.cc - lock/file_lock.cc - ) + list( + APPEND + ECTOOL_COMMON_SOURCES + comm-dev.cc + comm-i2c.cc + comm-lpc.cc + comm-servo-spi.cc + comm-usb.cc + lock/file_lock.cc) else() - list(APPEND ECTOOL_COMMON_SOURCES - comm-win32.cc - lock/win32_mutex_lock.cc - ) + list(APPEND ECTOOL_COMMON_SOURCES comm-win32.cc lock/win32_mutex_lock.cc) endif() - - # ============================ # Add Executable Target: ectool # ============================ add_executable(ectool ectool.cc ${ECTOOL_COMMON_SOURCES}) -target_include_directories(ectool PRIVATE - ../include - ${libusb_INCLUDE_DIRS} -) +target_include_directories(ectool PRIVATE ../include ${libusb_INCLUDE_DIRS}) -target_compile_definitions(ectool PRIVATE - CHROMIUM_EC - EXTERNAL_ECTOOL_BUILD -) +target_compile_definitions(ectool PRIVATE CHROMIUM_EC EXTERNAL_ECTOOL_BUILD) -target_compile_options(ectool PRIVATE - -Wno-c99-designator - -Wno-address-of-packed-member - -Wno-format-security -) +target_compile_options( + ectool PRIVATE -Wno-c99-designator -Wno-address-of-packed-member + -Wno-format-security) if(WIN32) - target_compile_definitions(ectool PRIVATE - _CRT_SECURE_NO_WARNINGS - ) - target_link_libraries(ectool PRIVATE - getopt - CrosECDriver - onecoreuap_apiset.lib - ) - if(MSVC) - target_compile_options(ectool PRIVATE - /FI"..\\include\\win32_shim.h" - ) - else() - target_compile_options(ectool PRIVATE - -include "..\\include\\win32_shim.h" - ) - endif() - target_include_directories(ectool PRIVATE - ../include/win32 - ) + target_compile_definitions(ectool PRIVATE _CRT_SECURE_NO_WARNINGS) + target_link_libraries(ectool PRIVATE getopt CrosECDriver + onecoreuap_apiset.lib) + if(MSVC) + target_compile_options(ectool PRIVATE /FI"..\\include\\win32_shim.h") + else() + target_compile_options(ectool PRIVATE -include "..\\include\\win32_shim.h") + endif() + target_include_directories(ectool PRIVATE ../include/win32) endif() target_link_libraries(ectool ${libusb_LIBRARIES} ${libftdi1_LIBRARIES}) - # ============================ # Add Shared Library : libectool.so # ============================ -set(LIBECTOOL_SOURCES ${ECTOOL_COMMON_SOURCES} ectool_fanctrl.cc) +set(LIBECTOOL_SOURCES ${ECTOOL_COMMON_SOURCES} ectool_wrapper.cc) add_library(libectool SHARED ${LIBECTOOL_SOURCES}) -target_include_directories(libectool PRIVATE - ../include - ${libusb_INCLUDE_DIRS} -) +target_include_directories(libectool PRIVATE ../include ${libusb_INCLUDE_DIRS}) -target_compile_definitions(libectool PRIVATE - CHROMIUM_EC - EXTERNAL_ECTOOL_BUILD -) +target_compile_definitions(libectool PRIVATE CHROMIUM_EC EXTERNAL_ECTOOL_BUILD) -target_compile_options(libectool PRIVATE - -Wno-c99-designator - -Wno-address-of-packed-member - -Wno-format-security -) +target_compile_options( + libectool PRIVATE -Wno-c99-designator -Wno-address-of-packed-member + -Wno-format-security) -target_link_libraries(libectool PRIVATE - ${libusb_LIBRARIES} - ${libftdi1_LIBRARIES} -) +target_link_libraries(libectool PRIVATE ${libusb_LIBRARIES} + ${libftdi1_LIBRARIES}) if(WIN32) - target_compile_definitions(ectool PRIVATE - _CRT_SECURE_NO_WARNINGS - ) - target_link_libraries(ectool PRIVATE - getopt - CrosECDriver - onecoreuap_apiset.lib - ) - if(MSVC) - target_compile_options(ectool PRIVATE - /FI"..\\include\\win32_shim.h" - ) - else() - target_compile_options(ectool PRIVATE - -include "..\\include\\win32_shim.h" - ) - endif() - target_include_directories(ectool PRIVATE - ../include/win32 - ) + target_compile_definitions(ectool PRIVATE _CRT_SECURE_NO_WARNINGS) + target_link_libraries(ectool PRIVATE getopt CrosECDriver + onecoreuap_apiset.lib) + if(MSVC) + target_compile_options(ectool PRIVATE /FI"..\\include\\win32_shim.h") + else() + target_compile_options(ectool PRIVATE -include "..\\include\\win32_shim.h") + endif() + target_include_directories(ectool PRIVATE ../include/win32) endif() -set_target_properties(libectool PROPERTIES - OUTPUT_NAME "ectool" # Generates `libectool.so` or `libectool.dll` +set_target_properties( + libectool PROPERTIES OUTPUT_NAME "ectool" # Generates `libectool.so` or + # `libectool.dll` ) - diff --git a/src/core/ectool_fanctrl.cc b/src/core/ectool_wrapper.cc similarity index 87% rename from src/core/ectool_fanctrl.cc rename to src/core/ectool_wrapper.cc index be7af241..273506e4 100644 --- a/src/core/ectool_fanctrl.cc +++ b/src/core/ectool_wrapper.cc @@ -65,12 +65,12 @@ bool is_on_ac() { void pause_fan_control() { if (libectool_init() < 0) fprintf(stderr, "Failed initializing EC connection\n"); - + int rv = ec_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0, NULL, 0, NULL, 0); if (rv < 0) fprintf(stderr, "Failed to enable auto fan control\n"); - + libectool_release(); } @@ -99,7 +99,7 @@ void set_fan_speed(int speed) { float get_max_temperature() { if (libectool_init() < 0) fprintf(stderr, "Failed initializing EC connection\n"); - + float max_temp = -1.0f; int mtemp, temp; int id; @@ -135,10 +135,10 @@ float get_max_non_battery_temperature() { if (libectool_init() < 0) fprintf(stderr, "Failed initializing EC connection\n"); - - struct ec_params_temp_sensor_get_info p; - struct ec_response_temp_sensor_get_info r; - int rv; + + struct ec_params_temp_sensor_get_info p; + struct ec_response_temp_sensor_get_info r; + int rv; float max_temp = -1.0f; int mtemp, temp; int id; @@ -153,7 +153,7 @@ float get_max_non_battery_temperature() 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) { @@ -178,7 +178,7 @@ float get_max_non_battery_temperature() } } } - + libectool_release(); return max_temp; } @@ -246,35 +246,35 @@ void libectool_release() 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; - } - return rv; + 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; + } + return rv; } static uint8_t read_mapped_mem8(uint8_t offset) { - 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; -} \ No newline at end of file + 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; +} From 9da0cdc93f195fe6bff919e5478020effefe0773 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sat, 7 Jun 2025 16:20:38 +0300 Subject: [PATCH 02/14] refactor: implement libectool library with core functionality and testing --- .github/workflows/build.yml | 4 +-- src/core/{ectool_wrapper.cc => libectool.cc} | 20 +++------------ src/core/libectool.h | 23 +++++++++++++++++ tests/test_libectool.c | 27 ++++++++++++++++++++ 4 files changed, 56 insertions(+), 18 deletions(-) rename src/core/{ectool_wrapper.cc => libectool.cc} (95%) create mode 100644 src/core/libectool.h create mode 100644 tests/test_libectool.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d5bac063..d2db5774 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Build ectool on: push: - branches: [ main ] + branches: [main, dev] pull_request: jobs: @@ -52,4 +52,4 @@ jobs: name: ectool-linux path: | _build/src/core/ectool - _build/src/core/libectool.so \ No newline at end of file + _build/src/core/libectool.so diff --git a/src/core/ectool_wrapper.cc b/src/core/libectool.cc similarity index 95% rename from src/core/ectool_wrapper.cc rename to src/core/libectool.cc index 273506e4..4900e93b 100644 --- a/src/core/ectool_wrapper.cc +++ b/src/core/libectool.cc @@ -2,6 +2,7 @@ #include #include +#include "libectool.h" #include "battery.h" #include "comm-host.h" #include "comm-usb.h" @@ -11,7 +12,6 @@ #include "ec_panicinfo.h" #include "ec_flash.h" #include "ec_version.h" -// #include "ectool.h" #include "i2c.h" #include "lightbar.h" #include "lock/gec_lock.h" @@ -30,21 +30,9 @@ #define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */ #define interfaces COMM_ALL -int libectool_init(); -void libectool_release(); -int read_mapped_temperature(int id); -static uint8_t read_mapped_mem8(uint8_t offset); - - -extern "C" { -int ascii_mode = 0; -bool is_on_ac(); -void pause_fan_control(); -void set_fan_speed(int speed); -float get_max_temperature(); -float get_max_non_battery_temperature(); - - +// int read_mapped_temperature(int id); +// static uint8_t read_mapped_mem8(uint8_t offset); +// int ascii_mode; // ----------------------------------------------------------------------------- // Top-level endpoint functions diff --git a/src/core/libectool.h b/src/core/libectool.h new file mode 100644 index 00000000..5c4a624f --- /dev/null +++ b/src/core/libectool.h @@ -0,0 +1,23 @@ +#ifndef LIBECTOOL_H +#define LIBECTOOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Library init/release +int libectool_init(); +void libectool_release(); + +// API functions to expose +bool is_on_ac(); +void pause_fan_control(); +void set_fan_speed(int speed); +float get_max_temperature(); +float get_max_non_battery_temperature(); + +#ifdef __cplusplus +} +#endif + +#endif // LIBECTOOL_H diff --git a/tests/test_libectool.c b/tests/test_libectool.c new file mode 100644 index 00000000..a2bca60c --- /dev/null +++ b/tests/test_libectool.c @@ -0,0 +1,27 @@ +#include +#include "libectool.h" + +int main() { + printf("Testing libectool...\n"); + + // Test is_on_ac + bool ac = is_on_ac(); + printf("is_on_ac() = %d\n", ac); + + // // Test fan control functions + // printf("Pausing fan control...\n"); + // pause_fan_control(); + + // printf("Setting fan speed to 50%%...\n"); + // set_fan_speed(50); + + // Test temperature functions + float max_temp = get_max_temperature(); + printf("Max temperature = %.2f C\n", max_temp); + + float max_non_batt_temp = get_max_non_battery_temperature(); + printf("Max non-battery temperature = %.2f C\n", max_non_batt_temp); + + printf("Test complete.\n"); + return 0; +} From d5444733b4e668ccea4776c6d2a08cc7b2761085 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sat, 7 Jun 2025 16:22:49 +0300 Subject: [PATCH 03/14] refactor: update libectool sources to include libectool.cc instead of ectool_wrapper.cc --- src/core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 76dbb1f3..2a2ff142 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -56,7 +56,7 @@ target_link_libraries(ectool ${libusb_LIBRARIES} ${libftdi1_LIBRARIES}) # Add Shared Library : libectool.so # ============================ -set(LIBECTOOL_SOURCES ${ECTOOL_COMMON_SOURCES} ectool_wrapper.cc) +set(LIBECTOOL_SOURCES ${ECTOOL_COMMON_SOURCES} libectool.cc) add_library(libectool SHARED ${LIBECTOOL_SOURCES}) From b4dcaa8c07aab80bb2c527b50fc0e879a9287520 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sat, 7 Jun 2025 16:36:54 +0300 Subject: [PATCH 04/14] refactor: enhance libectool initialization and cleanup functions --- src/core/libectool.cc | 193 ++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 99 deletions(-) diff --git a/src/core/libectool.cc b/src/core/libectool.cc index 4900e93b..9a40d9c3 100644 --- a/src/core/libectool.cc +++ b/src/core/libectool.cc @@ -30,9 +30,100 @@ #define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */ #define interfaces COMM_ALL -// int read_mapped_temperature(int id); -// static uint8_t read_mapped_mem8(uint8_t offset); -// int ascii_mode; +// ----------------------------------------------------------------------------- +// Helper functions +// ----------------------------------------------------------------------------- + +int libectool_init() +{ + char device_name[41] = CROS_EC_DEV_NAME; + uint16_t vid = USB_VID_GOOGLE, pid = USB_PID_HAMMER; + int i2c_bus = -1; + /* + * First try the preferred /dev interface (which has a built-in mutex). + * If the COMM_DEV flag is excluded or comm_init_dev() fails, + * then try alternative interfaces. + */ + if (!(interfaces & COMM_DEV) || comm_init_dev(device_name)) { + /* 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; + } + } + + /* Initialize ring buffers for sending/receiving EC commands */ + if (comm_init_buffer()) { + fprintf(stderr, "Couldn't initialize buffers\n"); + release_gec_lock(); + return -1; + } + + return 0; +} + +void libectool_release() +{ + /* Release the GEC lock. (This is safe even if no lock was acquired.) */ + release_gec_lock(); + +#ifndef _WIN32 + /* If the interface in use was USB, perform additional cleanup */ + if (interfaces == COMM_USB) + comm_usb_exit(); +#endif +} + +static uint8_t read_mapped_mem8(uint8_t offset) +{ + 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; +} + +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; + } + return rv; +} // ----------------------------------------------------------------------------- // Top-level endpoint functions @@ -170,99 +261,3 @@ float get_max_non_battery_temperature() libectool_release(); return max_temp; } -} - -// ----------------------------------------------------------------------------- -// Helper functions -// ----------------------------------------------------------------------------- - -int libectool_init() -{ - char device_name[41] = CROS_EC_DEV_NAME; - uint16_t vid = USB_VID_GOOGLE, pid = USB_PID_HAMMER; - int i2c_bus = -1; - /* - * First try the preferred /dev interface (which has a built-in mutex). - * If the COMM_DEV flag is excluded or comm_init_dev() fails, - * then try alternative interfaces. - */ - if (!(interfaces & COMM_DEV) || comm_init_dev(device_name)) { - /* 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; - } - } - - /* Initialize ring buffers for sending/receiving EC commands */ - if (comm_init_buffer()) { - fprintf(stderr, "Couldn't initialize buffers\n"); - release_gec_lock(); - return -1; - } - - return 0; -} - -void libectool_release() -{ - /* Release the GEC lock. (This is safe even if no lock was acquired.) */ - release_gec_lock(); - -#ifndef _WIN32 - /* If the interface in use was USB, perform additional cleanup */ - if (interfaces == COMM_USB) - comm_usb_exit(); -#endif -} - -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; - } - return rv; -} - -static uint8_t read_mapped_mem8(uint8_t offset) -{ - 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; -} From 6a1aa5438301bf84cfbeea4a008ae7e187ff4842 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sat, 7 Jun 2025 07:18:20 -0700 Subject: [PATCH 05/14] refactor: declare ascii_mode in libectool header --- src/core/libectool.cc | 1 + src/core/libectool.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/core/libectool.cc b/src/core/libectool.cc index 9a40d9c3..e3c5969b 100644 --- a/src/core/libectool.cc +++ b/src/core/libectool.cc @@ -30,6 +30,7 @@ #define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */ #define interfaces COMM_ALL +int ascii_mode; // ----------------------------------------------------------------------------- // Helper functions // ----------------------------------------------------------------------------- diff --git a/src/core/libectool.h b/src/core/libectool.h index 5c4a624f..d2d05fed 100644 --- a/src/core/libectool.h +++ b/src/core/libectool.h @@ -1,6 +1,8 @@ #ifndef LIBECTOOL_H #define LIBECTOOL_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -16,6 +18,9 @@ void set_fan_speed(int speed); float get_max_temperature(); float get_max_non_battery_temperature(); +/* ASCII mode for printing, default off */ +extern int ascii_mode; + #ifdef __cplusplus } #endif From d36c0503447255b3851b5e5e15c3b90c6b273a02 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sun, 8 Jun 2025 06:16:35 -0700 Subject: [PATCH 06/14] feat: implement interactive CLI for libectool testing --- tests/test_libectool.c | 81 +++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/tests/test_libectool.c b/tests/test_libectool.c index a2bca60c..8e9619c5 100644 --- a/tests/test_libectool.c +++ b/tests/test_libectool.c @@ -1,27 +1,68 @@ #include +#include #include "libectool.h" -int main() { - printf("Testing libectool...\n"); - - // Test is_on_ac - bool ac = is_on_ac(); - printf("is_on_ac() = %d\n", ac); - - // // Test fan control functions - // printf("Pausing fan control...\n"); - // pause_fan_control(); - - // printf("Setting fan speed to 50%%...\n"); - // set_fan_speed(50); +void print_menu() { + printf("\n=== libectool Testing CLI ===\n"); + printf("1. Check if on AC power\n"); + printf("2. Pause fan control\n"); + printf("3. Set fan speed\n"); + printf("4. Get max temperature\n"); + printf("5. Get max non-battery temperature\n"); + printf("0. Exit\n"); + printf("Choose an option: "); +} - // Test temperature functions - float max_temp = get_max_temperature(); - printf("Max temperature = %.2f C\n", max_temp); +int main() { + int choice; + int speed; - float max_non_batt_temp = get_max_non_battery_temperature(); - printf("Max non-battery temperature = %.2f C\n", max_non_batt_temp); + while (1) { + print_menu(); + if (scanf("%d", &choice) != 1) { + // clear invalid input + int c; + while ((c = getchar()) != '\n' && c != EOF); + printf("Invalid input. Try again.\n"); + continue; + } - printf("Test complete.\n"); - return 0; + switch (choice) { + case 1: { + bool ac = is_on_ac(); + printf("is_on_ac() = %d\n", ac); + break; + } + case 2: + printf("Pausing fan control...\n"); + pause_fan_control(); + break; + case 3: + printf("Enter fan speed (0-100): "); + if (scanf("%d", &speed) == 1) { + set_fan_speed(speed); + } else { + printf("Invalid speed.\n"); + // clear invalid input + int c; + while ((c = getchar()) != '\n' && c != EOF); + } + break; + case 4: { + float max_temp = get_max_temperature(); + printf("Max temperature = %.2f C\n", max_temp); + break; + } + case 5: { + float max_non_batt_temp = get_max_non_battery_temperature(); + printf("Max non-battery temperature = %.2f C\n", max_non_batt_temp); + break; + } + case 0: + printf("Exiting.\n"); + return 0; + default: + printf("Invalid choice. Try again.\n"); + } + } } From 481c85d845a1414267f590a653f18fa2019065b6 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sun, 8 Jun 2025 17:15:01 +0300 Subject: [PATCH 07/14] refactor: rename fan control functions for clarity --- src/core/libectool.cc | 12 ++++++------ src/core/libectool.h | 4 ++-- tests/test_libectool.c | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/libectool.cc b/src/core/libectool.cc index e3c5969b..8c8c64f0 100644 --- a/src/core/libectool.cc +++ b/src/core/libectool.cc @@ -142,7 +142,7 @@ bool is_on_ac() { return ac_present; } -void pause_fan_control() { +void auto_fan_control() { if (libectool_init() < 0) fprintf(stderr, "Failed initializing EC connection\n"); @@ -154,23 +154,23 @@ void pause_fan_control() { libectool_release(); } -void set_fan_speed(int speed) { +void set_fan_duty(int duty) { if (libectool_init() < 0) fprintf(stderr, "Failed initializing EC connection\n"); struct ec_params_pwm_set_fan_duty_v0 p_v0; int rv; - if (speed < 0 || speed > 100) { - fprintf(stderr, "Error: Fan speed must be between 0 and 100.\n"); + if (duty < 0 || duty > 100) { + fprintf(stderr, "Error: Fan duty cycle must be between 0 and 100.\n"); return; } - p_v0.percent = speed; + p_v0.percent = duty; rv = 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 speed\n"); + fprintf(stderr, "Error: Can't set duty cycle\n"); libectool_release(); } diff --git a/src/core/libectool.h b/src/core/libectool.h index d2d05fed..531bf8f3 100644 --- a/src/core/libectool.h +++ b/src/core/libectool.h @@ -13,8 +13,8 @@ void libectool_release(); // API functions to expose bool is_on_ac(); -void pause_fan_control(); -void set_fan_speed(int speed); +void auto_fan_control(); +void set_fan_duty(int duty); float get_max_temperature(); float get_max_non_battery_temperature(); diff --git a/tests/test_libectool.c b/tests/test_libectool.c index 8e9619c5..6acbb062 100644 --- a/tests/test_libectool.c +++ b/tests/test_libectool.c @@ -34,13 +34,13 @@ int main() { break; } case 2: - printf("Pausing fan control...\n"); - pause_fan_control(); + printf("Enable automatic fan control...\n"); + auto_fan_control(); break; case 3: printf("Enter fan speed (0-100): "); if (scanf("%d", &speed) == 1) { - set_fan_speed(speed); + set_fan_duty(speed); } else { printf("Invalid speed.\n"); // clear invalid input From 5fafe0a6be7ae5da655d15ac6aaa4434c84b2ba1 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sun, 8 Jun 2025 21:51:42 +0300 Subject: [PATCH 08/14] feat: Add pybind11 bindings, scikit-build-core setup, and CI workflow for Python package - Implemented initial pybind11 class bindings to expose C++ API to Python. - Integrated scikit-build-core by updating CMakeLists.txt and pyproject.toml. - Added GitHub Actions workflow to build and test the Python package. --- .github/workflows/pip.yml | 41 +++++++++++++++++++++++++++++++ .gitignore | 1 + CMakeLists.txt | 41 ++++++++++++++++++++++--------- pyectool/__init__.py | 27 ++++++++++++++++++++ pyectool/__init__.pyi | 14 +++++++++++ pyproject.toml | 5 ++-- src/bindings/CMakeLists.txt | 7 ++++++ src/bindings/libectool_py.cc | 31 +++++++++++++++++++++++ src/core/CMakeLists.txt | 40 ++++++++++++++---------------- src/{core => include}/libectool.h | 0 10 files changed, 170 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/pip.yml create mode 100644 pyectool/__init__.py create mode 100644 pyectool/__init__.pyi create mode 100644 src/bindings/CMakeLists.txt create mode 100644 src/bindings/libectool_py.cc rename src/{core => include}/libectool.h (100%) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml new file mode 100644 index 00000000..d7f24998 --- /dev/null +++ b/.github/workflows/pip.yml @@ -0,0 +1,41 @@ +name: Pip + +on: + workflow_dispatch: + pull_request: + push: + branches: + - dev_py_pkg_build + +jobs: + build: + name: Build on ${{ matrix.platform }} with Python ${{ matrix.python-version }} + runs-on: ${{ matrix.platform }} + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - name: Upgrade pip, build tools + run: | + python -m pip install --upgrade pip + pip install build scikit-build-core pybind11 + + - name: Build and install the package + run: | + pip install --verbose . + + - name: Test import + run: | + python -c "import pyectool; print('pyectool imported')" diff --git a/.gitignore b/.gitignore index a007feab..2480464a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build/* +*/__pycache__/* diff --git a/CMakeLists.txt b/CMakeLists.txt index ee34523b..9ffd5f2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,26 +1,43 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.15...3.27) -if(${CMAKE_VERSION} VERSION_LESS 3.12) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif() +project( + ${SKBUILD_PROJECT_NAME} + VERSION ${SKBUILD_PROJECT_VERSION} + LANGUAGES CXX) + +# Required for building Python extension modules via pybind11 +find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) -project(ECTool - VERSION 1.0.0 - DESCRIPTION "ChromeOS EC Tool" - LANGUAGES CXX) +# Find pybind11 (installed via pip/conda or system-wide) +find_package(pybind11 CONFIG REQUIRED) if(NOT WIN32) - find_package(PkgConfig REQUIRED) - pkg_check_modules(libusb REQUIRED libusb-1.0) - pkg_check_modules(libftdi1 REQUIRED libftdi1) + find_package(PkgConfig REQUIRED) + pkg_check_modules(libusb REQUIRED libusb-1.0) + pkg_check_modules(libftdi1 REQUIRED libftdi1) else() + endif() set(CMAKE_CXX_STANDARD 17) add_subdirectory(src/core) +add_subdirectory(src/bindings) add_subdirectory(src/extern) if(WIN32) - add_subdirectory(src/getopt) + add_subdirectory(src/getopt) endif() + +install( + TARGETS ectool libectool libectool_py + RUNTIME DESTINATION pyectool/bin # ectool CLI binary + LIBRARY DESTINATION pyectool # libectool_py.so (shared Python module) + ARCHIVE DESTINATION pyectool/lib # libectool.a (static lib) +) + +install( + DIRECTORY src/include/ + DESTINATION pyectool/include + FILES_MATCHING + PATTERN "libectool.h") diff --git a/pyectool/__init__.py b/pyectool/__init__.py new file mode 100644 index 00000000..e1bd1528 --- /dev/null +++ b/pyectool/__init__.py @@ -0,0 +1,27 @@ +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, +) + +__all__ = [ + "__doc__", + "__version__", + "ascii_mode", + "auto_fan_control", + "get_max_non_battery_temperature", + "get_max_temperature", + "init", + "is_on_ac", + "release", + "set_fan_duty", +] diff --git a/pyectool/__init__.pyi b/pyectool/__init__.pyi new file mode 100644 index 00000000..7337574e --- /dev/null +++ b/pyectool/__init__.pyi @@ -0,0 +1,14 @@ +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 diff --git a/pyproject.toml b/pyproject.toml index 80287db0..c527f0e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,16 +4,15 @@ build-backend = "scikit_build_core.build" [project] -name = "libectool" +name = "pyectool" version = "0.0.1" -description="A shared library (libectool) that exposes ectool functionalities for seamless integration with other applications" +description="Python bindings for ectool using pybind11, enabling seamless integration with other applications" readme = "README.md" authors = [ { name = "Ahmed Gamea", email = "ahmed.gamea@ejust.edu.eg" }, ] [tool.scikit-build] -wheel.expand-macos-universal-tags = true minimum-version = "build-system.requires" [tool.cibuildwheel] diff --git a/src/bindings/CMakeLists.txt b/src/bindings/CMakeLists.txt new file mode 100644 index 00000000..8b3dd757 --- /dev/null +++ b/src/bindings/CMakeLists.txt @@ -0,0 +1,7 @@ +# Create the Python module +python_add_library(libectool_py MODULE libectool_py.cc WITH_SOABI) + +# Link against required libraries +target_link_libraries(libectool_py PRIVATE pybind11::headers libectool) +target_include_directories(libectool_py PUBLIC ../include) +target_compile_definitions(libectool_py PUBLIC VERSION_INFO=${PROJECT_VERSION}) diff --git a/src/bindings/libectool_py.cc b/src/bindings/libectool_py.cc new file mode 100644 index 00000000..f77272aa --- /dev/null +++ b/src/bindings/libectool_py.cc @@ -0,0 +1,31 @@ +#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/CMakeLists.txt b/src/core/CMakeLists.txt index 2a2ff142..b66bfc3a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -26,7 +26,7 @@ else() endif() # ============================ -# Add Executable Target: ectool +# Add Executable Target: ectool (CLI) # ============================ add_executable(ectool ectool.cc ${ECTOOL_COMMON_SOURCES}) @@ -53,37 +53,33 @@ endif() target_link_libraries(ectool ${libusb_LIBRARIES} ${libftdi1_LIBRARIES}) # ============================ -# Add Shared Library : libectool.so +# Add STATIC Library : libectool # ============================ +add_library(libectool STATIC libectool.cc ${ECTOOL_COMMON_SOURCES}) -set(LIBECTOOL_SOURCES ${ECTOOL_COMMON_SOURCES} libectool.cc) +target_include_directories(libectool PUBLIC ../include ${libusb_INCLUDE_DIRS}) -add_library(libectool SHARED ${LIBECTOOL_SOURCES}) - -target_include_directories(libectool PRIVATE ../include ${libusb_INCLUDE_DIRS}) - -target_compile_definitions(libectool PRIVATE CHROMIUM_EC EXTERNAL_ECTOOL_BUILD) +target_compile_definitions(libectool PUBLIC CHROMIUM_EC EXTERNAL_ECTOOL_BUILD) target_compile_options( - libectool PRIVATE -Wno-c99-designator -Wno-address-of-packed-member - -Wno-format-security) + libectool PUBLIC -Wno-c99-designator -Wno-address-of-packed-member + -Wno-format-security) -target_link_libraries(libectool PRIVATE ${libusb_LIBRARIES} - ${libftdi1_LIBRARIES}) +target_link_libraries(libectool PUBLIC ${libusb_LIBRARIES} + ${libftdi1_LIBRARIES}) if(WIN32) - target_compile_definitions(ectool PRIVATE _CRT_SECURE_NO_WARNINGS) - target_link_libraries(ectool PRIVATE getopt CrosECDriver - onecoreuap_apiset.lib) + target_compile_definitions(libectool PUBLIC _CRT_SECURE_NO_WARNINGS) + target_link_libraries(libectool PUBLIC getopt CrosECDriver + onecoreuap_apiset.lib) if(MSVC) - target_compile_options(ectool PRIVATE /FI"..\\include\\win32_shim.h") + target_compile_options(libectool PUBLIC /FI"..\\include\\win32_shim.h") else() - target_compile_options(ectool PRIVATE -include "..\\include\\win32_shim.h") + target_compile_options(libectool PUBLIC -include + "..\\include\\win32_shim.h") endif() - target_include_directories(ectool PRIVATE ../include/win32) + target_include_directories(libectool PUBLIC ../include/win32) endif() -set_target_properties( - libectool PROPERTIES OUTPUT_NAME "ectool" # Generates `libectool.so` or - # `libectool.dll` -) +set_target_properties(libectool PROPERTIES OUTPUT_NAME ectool + POSITION_INDEPENDENT_CODE ON) diff --git a/src/core/libectool.h b/src/include/libectool.h similarity index 100% rename from src/core/libectool.h rename to src/include/libectool.h From 52f553b6d4daf29cbbedb6c7b92acafdf0cdcc90 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sun, 8 Jun 2025 21:56:38 +0300 Subject: [PATCH 09/14] feat: Add system dependencies installation step in CI workflow --- .github/workflows/pip.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index d7f24998..f62c4488 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -21,6 +21,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Install system dependencies + run: | + sudo apt update + sudo apt install -y libusb-1.0-0-dev libftdi1-dev pkg-config + - name: Set up Python uses: actions/setup-python@v5 with: From c28d4cc8c53c73450ea628ef2ab0efe96368710c Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sun, 8 Jun 2025 22:05:55 +0300 Subject: [PATCH 10/14] fix: import test in CI by avoiding local pyectool/ shadowing: - Prevented the local source directory from interfering with import test. - Changed working directory in GitHub Actions to ensure the installed package is used. --- .github/workflows/pip.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index f62c4488..dee845c1 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -43,4 +43,6 @@ jobs: - name: Test import run: | - python -c "import pyectool; print('pyectool imported')" + mkdir /tmp/testenv + cd /tmp/testenv + python -c "import pyectool; print('pyectool import successful')" From 1476401d3d28e2dff6595bc50a45f69a47a7c3d2 Mon Sep 17 00:00:00 2001 From: Yasser Date: Tue, 10 Jun 2025 01:31:51 +0300 Subject: [PATCH 11/14] Update README.md --- README.md | 57 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 80d0395b..e755b124 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,46 @@ -# libectool +# Pyectool -libectool is a shared library extracted from ectool, providing programmatic access to Embedded Controller (EC) functionalities on ChromeOS and compatible devices. +**Pyectool** is a Python package with C++ bindings for interacting with the Embedded Controller (EC) on ChromeOS and Framework devices. It is extracted from and based on [`ectool`](https://gitlab.howett.net/DHowett/ectool) utility, and exposes EC control functions directly to Python programs via a native extension. -## Features -- Exposes EC control functions via a shared library (`libectool.so`). -- Supports fan control, battery management, temperature monitoring, and more. -- Designed for integration into other applications. +## Features + +- Python bindings for EC functionality using `pybind11`. +- Supports fan duty control, temperature reading, AC power status, and more. +- Designed for integration with hardware management or fan control tools. +- Shared core logic with `libectool` for C/C++ integration. + +--- + +## 🛠️ Build & Install (Python Package) + +We use [`scikit-build-core`](https://scikit-build-core.readthedocs.io/en/latest/) to build the C++ extension via CMake. + +### Prerequisites + +Install the required system dependencies: -## Build Instructions ```sh -cd libectool -mkdir build && cd build -cmake .. -cmake --build . -``` -## Post Build Instructions -After building, you need to move `libectool.so` to a library directory where it can be found by your system: +sudo apt update +sudo apt install -y libusb-1.0-0-dev libftdi1-dev pkg-config +```` +### Clone the repository and switch to the Python package branch -### Option 1 — User-specific (Recommended for non-root users) ```sh -mkdir -p ~/.local/lib -cp src/core/libectool.so ~/.local/lib/libectool.so -export LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" +git clone --branch dev_py_pkg_build https://github.com/AhmedYasserrr/libectool.git +cd libectool ``` -To make it persistent across sessions, add the export to your shell configuration: +### Install the package ```sh -echo 'export LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH"' >> ~/.bashrc +python -m pip install --upgrade pip +pip install build scikit-build-core pybind11 +pip install . ``` -### Option 2 — Global installation + +After installing, **do not run Python from the `libectool/` directory**, since it contains a `pyectool/` folder that may shadow the installed package. + +Instead, test from another location, e.g.: + ```sh -sudo cp src/core/libectool.so /usr/local/lib/libectool.so +cd .. +python -c "import pyectool; print(pyectool.__version__)" ``` From ad9549deee3404a07e022a0fe1751132d63d28b7 Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Tue, 10 Jun 2025 17:02:51 +0300 Subject: [PATCH 12/14] refactor: remove pip upgrade and build tools installation from CI and README --- .github/workflows/pip.yml | 5 ----- README.md | 4 +--- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index dee845c1..6f51168d 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -32,11 +32,6 @@ jobs: python-version: ${{ matrix.python-version }} allow-prereleases: true - - name: Upgrade pip, build tools - run: | - python -m pip install --upgrade pip - pip install build scikit-build-core pybind11 - - name: Build and install the package run: | pip install --verbose . diff --git a/README.md b/README.md index e755b124..193bfe2f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ --- -## 🛠️ Build & Install (Python Package) +## Build & Install (Python Package) We use [`scikit-build-core`](https://scikit-build-core.readthedocs.io/en/latest/) to build the C++ extension via CMake. @@ -31,8 +31,6 @@ cd libectool ``` ### Install the package ```sh -python -m pip install --upgrade pip -pip install build scikit-build-core pybind11 pip install . ``` From 25d561ab514cb15ac30038fcd37c5749abfad05a Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Fri, 27 Jun 2025 14:37:37 +0300 Subject: [PATCH 13/14] fix: update push branch --- .github/workflows/pip.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 6f51168d..b84803dc 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -5,7 +5,7 @@ on: pull_request: push: branches: - - dev_py_pkg_build + - pyectool jobs: build: From 0e8ddb16fa3877330fad358b27f96e168e5e1e0e Mon Sep 17 00:00:00 2001 From: AhmedYasserrr Date: Sat, 28 Jun 2025 06:28:10 -0700 Subject: [PATCH 14/14] chore: update CI workflows and README, and bump version to 0.1.0 --- .github/workflows/build.yml | 55 ------------------------------------- .github/workflows/pip.yml | 4 +-- .gitignore | 1 + README.md | 50 ++++++++++++++++++++++++++++----- pyproject.toml | 2 +- 5 files changed, 46 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index d2db5774..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,55 +0,0 @@ -# .github/workflows/build.yml -name: Build ectool - -on: - push: - branches: [main, dev] - pull_request: - -jobs: - build-linux: - runs-on: ubuntu-latest - steps: - - name: Checkout repository (with submodules) - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install dependencies - run: | - sudo apt update - sudo apt install -y cmake clang ninja-build git libftdi1-dev libusb-1.0-0-dev pkg-config - - - name: Print environment info (for debugging) - run: | - echo "CC=$CC" - echo "CXX=$CXX" - clang --version - cmake --version - ninja --version - pwd && ls -al - - - name: Configure CMake - run: | - mkdir _build - cd _build - CC=clang CXX=clang++ cmake -GNinja .. - - - name: Build - run: | - cd _build - ninja - - - name: List build output (for debugging) - run: | - echo "::group::Build Output" - find _build -type f - echo "::endgroup::" - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: ectool-linux - path: | - _build/src/core/ectool - _build/src/core/libectool.so diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index b84803dc..ede59162 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -4,9 +4,7 @@ on: workflow_dispatch: pull_request: push: - branches: - - pyectool - + branches: [main, dev] jobs: build: name: Build on ${{ matrix.platform }} with Python ${{ matrix.python-version }} diff --git a/.gitignore b/.gitignore index 2480464a..f7fc85bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/* */__pycache__/* +dist/* diff --git a/README.md b/README.md index 193bfe2f..4f879bed 100644 --- a/README.md +++ b/README.md @@ -23,22 +23,58 @@ Install the required system dependencies: sudo apt update sudo apt install -y libusb-1.0-0-dev libftdi1-dev pkg-config ```` -### Clone the repository and switch to the Python package branch +### Clone the repository +## Install system-wide ```sh -git clone --branch dev_py_pkg_build https://github.com/AhmedYasserrr/libectool.git -cd libectool +sudo pip install . ``` -### Install the package -```sh -pip install . +Or: + +```bash +sudo env "PIP_BREAK_SYSTEM_PACKAGES=1" pip install . ``` +(Required on modern distros like Ubuntu 24.04 due to PEP 668.) +### Test from outside the repo dir After installing, **do not run Python from the `libectool/` directory**, since it contains a `pyectool/` folder that may shadow the installed package. Instead, test from another location, e.g.: ```sh cd .. -python -c "import pyectool; print(pyectool.__version__)" +sudo python -c "import pyectool; print(pyectool.is_on_ac())" +``` + +## VENV INSTALLATION + +If you **don’t** want to touch system Python: + +### Create venv + +```bash +python3 -m venv ~/.venv/pyectool +source ~/.venv/pyectool/bin/activate +``` + +### Install your package + +Inside the venv: +```bash +pip install . +``` +### Test from outside the repo dir +```bash +cd .. +sudo env "PATH=$PATH" python -c "import pyectool; print(pyectool.is_on_ac())" ``` + +### Available Functions + +| Function | Description | +| ------------------------------------------ | -------------------------------------------------------------------------------- | +| `auto_fan_control()` | Enables automatic fan control by the EC. | +| `get_max_non_battery_temperature() -> float` | Returns the highest temperature (in °C) from all sensors except the battery. | +| `get_max_temperature() -> float` | Returns the highest temperature (in °C) from all EC sensors including battery. | +| `is_on_ac() -> bool` | Checks whether the device is running on AC power. | +| `set_fan_duty(percent: int)` | Sets the fan duty cycle manually (0–100%). | \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index c527f0e1..d91681a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "scikit_build_core.build" [project] name = "pyectool" -version = "0.0.1" +version = "0.1.0" description="Python bindings for ectool using pybind11, enabling seamless integration with other applications" readme = "README.md" authors = [