Native driver: kernel module + Python library for sysfs-based PiPower5#5
Open
cavonlee wants to merge 94 commits into
Open
Native driver: kernel module + Python library for sysfs-based PiPower5#5cavonlee wants to merge 94 commits into
cavonlee wants to merge 94 commits into
Conversation
…r integration Add comprehensive driver for PiPower5 power management board including: - Main I2C driver framework with device tree support - SysFS interface for all power/battery parameters - UPower integration for battery status - Power button input device - Shutdown handling for low battery/button events - Build system with Makefile and install script - Documentation and test script - Remove old driver implementation and update dependencies
…d uninstall Core changes: - Rewrite PiPower5 class to read sensors via sysfs, write via smbus2 - Remove SPC dependency; add smbus2 instead - Remove BatteryDevice from service (kernel driver handles power_supply natively) Kernel driver improvements: - Add DKMS support (auto-rebuild on kernel updates) - Switch from HAT EEPROM auto-detect to dtoverlay in config.txt - Fix power_button_state sysfs: only accept 0 (reset) - Add missing power_supply properties (MODEL_NAME, MANUFACTURER, TECHNOLOGY, SCOPE, CAPACITY_LEVEL) - Comprehensive Makefile with dkms_install/uninstall/status targets New CLI features: - pipower5 doctor / pipower5 doctor --fix (health check + auto-repair) - pipower5 uninstall (remove driver, DKMS, overlay, config, package) - Bash completion script (bin/pipower5-completion.bash) Other: - Update device.py with config.txt management and health checks - Update install.sh to use Makefile + DKMS - Update CLAUDE.md and driver README
- Delete install.py (replaced by installer-scripts) - Delete tools/sf_installer.py (no longer needed) - Remove is_installed() from device.py (HAT EEPROM detection) - Simplify check_pipower5_connected() to sysfs-only - Remove get_device_tree_path/read_device_tree_file/get_part_number from utils.py - get_varient_id_and_version() defaults to V50
- One-liner via pironman5 plugin or variant ups - Manual: driver install.sh + pip install - Remove outdated install.py references
…ion-hat - Add power_supply_changed() call after status update so desktop UIs receive battery data changes (fixes empty battery display) - Fix STATUS priority: check FULL before CHARGING - Set ONLINE always to 1 for better desktop compatibility - Remove CURRENT_NOW/ENERGY_* from property list (align with fusion-hat's proven working configuration)
Previously config.json was stored in the Python package directory which requires root permissions to write. Move to user-writable ~/.config/pipower5/config.json with auto-migration from package template. - __init__.py: _USER_CONFIG as default, fallback to package template - pipower5_manager.py: same approach, auto-create config dir - Remove chmod calls (no longer needed for user-owned files)
- install target now auto-installs DKMS via apt if missing - status check searches all possible module locations (extra/, updates/, kernel/drivers/misc/, misc/)
Previously class was created in probe but never destroyed on rmmod, causing 'cannot create duplicate filename /class/pipower5' error on subsequent modprobe. This left sysfs attributes and power_supply unregistered, resulting in empty battery display. Fix: create the class once in module_init, destroy in module_exit. Remove class management from probe/remove entirely.
- status check: find .ko.xz (DKMS compressed) files - uninstall: also remove old pipower5_driver.ko and modules-load.d - DTS: add #address-cells/#size-cells to suppress dtc warnings on Pi 5
- Delete pipower5_manager.py, pipower5_service.py, pipower5_system.py (kernel driver now handles all hardware monitoring natively) - Delete battery_device.py (replaced by native power_supply driver) - Delete debounce.py, lazy_caller.py, logger.py (only used by service) - Keep email_sender.py (user plans to repurpose it) - Remove 'start'/'stop' CLI commands (no service to manage) - Simplify constants.py (remove PERIPHERALS list) - Simplify utils.py (remove unused merge_dict/log_error/is_included) - __init__.py: remove PiPower5Manager import, PERIPHERALS, is_included pipower5/ file count: 15 → 9
Problem: buzz_sequence() used smbus2 to directly write I2C registers (REG_WRITE_BUZZER_FEQ_L/H), conflicting with the native kernel driver that owns the I2C bus, causing 'OSError: [Errno 16] Device or resource busy'. Solution (Plan A): - driver: add buzzer_play write-only sysfs attribute that accepts 'freq,dur;freq,dur;...' sequence format, using delayed_work for kernel-side timer-based playback. Also supports single-frequency mode for backward compat with write_buzzer_freq(). - Python: rewrite buzz_sequence() to convert notes to frequencies and write to buzzer_play sysfs. Remove threading/queue cruft. write_buzzer_freq() now goes through sysfs too.
- Bump driver version from 1.0.0 to 2.1.0 (native driver milestone) - Bump Python package version from 1.2.2 to 2.1.0 - Add read_driver_version() to PiPower5 class - Add -dv / --driver-version CLI flag - Add info / status commands as aliases for -a - Show all 3 versions (python, driver, firmware) at bottom of -a output - Clarify -v and -fv help text
pipower5_write_byte_data() internally acquires pi_dev->lock, but shutdown_percentage_store, power_button_state_store, and buzzer_volume_store were also holding pi_dev->lock while calling it. Linux mutex is non-recursive — this causes a self-deadlock. Once deadlocked, any subsequent sysfs read (e.g. buzzer_volume_show) blocks forever on mutex_lock, causing the 'read hangs' symptom. Fix: do I2C writes outside the lock (pipower5_write_byte_data has its own internal locking), only use pi_dev->lock for cache updates.
…zzer timeout protection Root cause: sysfs store functions held pi_dev->lock while calling I2C functions that also acquired the same lock internally, causing mutex self-deadlock on non-recursive mutexes. Fix - restructure lock ownership: - Rename I2C functions to __pipower5_read_word/read_byte/write_byte (leading __ signals 'caller must hold pi_dev->lock') - pipower5_update_status() now locks once for the entire read cycle - All sysfs store/show functions explicitly manage the lock Buzzer timeout protection: - Single-frequency mode: auto-stop after 5000ms - Sequence mode: per-note duration capped at 30000ms - Prevents runaway buzzer
…root access - Replace DEVICE_ATTR_RW/WO with __ATTR(0664) for writable attributes - Add udev rule to chmod 755 dirs + chmod 664 files + chgrp gpio - Fix read_power_btn to use sysfs write instead of direct I2C - Fix PowerSource.BATTERY reference in __init__.py
…BATTERY ref - read_power_btn() now writes 0 via sysfs instead of direct I2C - __init__.py: fix pipower5.BATTERY -> PowerSource.BATTERY - Import PowerSource in CLI for -a display
…sion fix - Remove pipower5_button.c from build: KEY_POWER input events caused systemd-logind to trigger unwanted shutdown on button press. Shutdown is already handled via MCU -> I2C -> shutdown_request. Button state remains readable via power_button_state sysfs for pironman5. - Add pipower5_fix_sysfs_perms() using call_usermodehelper to chmod and chgrp sysfs files, enabling non-root gpio-group access. - Set __ATTR mode 0664 on writable sysfs attributes. - Remove input_dev, power_button_irq, last_power_button_state from struct.
…lass dir sysfs class directories (/sys/class/pipower5/pipower5) may return EACCES on stat() for non-root users even with correct group permissions. Switch is_connected() to use /sys/module/pipower5 which is always accessible when the module is loaded.
…mission issue The I2C core creates device directories (1-005c) without execute permission (drw-r--r--), blocking non-root users from traversing to sysfs attributes. Use NULL parent for device_create so the sysfs symlink resolves through /sys/devices/virtual/ instead.
…om sysfs/CLI - Add event ring buffer with timestamped logs (STARTUP, SHUTDOWN, BUTTON) - Expose events via /sys/class/pipower5/pipower5/events - Kernel driver now resets power button register (12) after reading - Remove shutdown_request from sysfs and CLI (handled internally) - Enhance shutdown log with battery percentage/voltage details - Restore button_check in poll work
- Button now polled at 50Hz (20ms interval) via separate work - Event detection moved from Python to kernel: POWER_DISCONNECTED, POWER_RESTORED, BATTERY_ACTIVATED, POWER_INSUFFICIENT, LOW_BATTERY - Shutdown events already handled (BATTERY_CRITICAL_SHUTDOWN, BATTERY_VOLTAGE_CRITICAL_SHUTDOWN) - All events logged to ring buffer + kernel log
- Module params: buzz_on, volume, shutdown_percentage (persisted via /etc/modprobe.d/pipower5.conf) - Events now emit kobject_uevent (KOBJ_CHANGE) for udev/systemd - Buzzer auto-triggers on events (controled by buzz_on bitmask) - buzz_on sysfs (RW), buzzer_volume & shutdown_percentage synced with module params - Default buzzer sequences hardcoded (matching old Python config) - Add udev rule + email notifier script in rules/
…mpat CLI - pipower5.py: remove all I2C methods (buzzer, ADV_CMD, smbus2) All reads/writes go through sysfs. Remove note.py. - pyproject.toml: remove smbus2 dependency - __init__.py: restore all old CLI flags for backward compat, implementations rewritten to use sysfs. Add send-email command. - buzzer-test writes to buzzer_play sysfs
…esidue, buzzer volume scale 0-10→0-100
… false trigger during power supply ramp-up
…lation at low settings
…mbus_write_word_data
…in same poll cycle
…se trigger during capacitor drain after unplug
…ain shows BATTERY, skip it
… cover capacitor drain (~1s)
…stemd" This reverts commit a112527.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Kernel Driver
Python Library
Config