Create PWM driver initial version - #12
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an initial F Prime Linux sysfs PWM driver component, defining its ports, status enum, events, C++ implementation, and local CMake target.
Changes:
- Defines the
Drv.LinuxPwmDriverpassive component and PWM control ports. - Implements sysfs-based period, duty cycle, enable, and export handling.
- Adds a component-level CMake library target, with unit tests left disabled.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.
| File | Description |
|---|---|
FlightComputer/Components/PwmDriver/LinuxPwmDriver.hpp |
Declares the PWM driver component implementation and internal state. |
FlightComputer/Components/PwmDriver/LinuxPwmDriver.cpp |
Implements sysfs PWM export and control handlers. |
FlightComputer/Components/PwmDriver/Driver.fpp |
Defines the FPP component, ports, events, interface, and status enum. |
FlightComputer/Components/PwmDriver/CMakeLists.txt |
Registers the PWM driver library and includes a commented-out unit test target. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| register_fprime_library( | ||
| AUTOCODER_INPUTS | ||
| "${CMAKE_CURRENT_LIST_DIR}/Driver.fpp" | ||
| SOURCES | ||
| "${CMAKE_CURRENT_LIST_DIR}/LinuxPwmDriver.cpp" |
| #ifndef Drv_LinuxPwmDriver_HPP | ||
| #define Drv_LinuxPwmDriver_HPP | ||
|
|
||
| #include "LinuxPwmDrv/Components/Driver/LinuxPwmDriverComponentAc.hpp" |
| // \brief cpp file for LinuxPwmDriver component implementation class | ||
| // ====================================================================== | ||
|
|
||
| #include "LinuxPwmDrv/Components/Driver/LinuxPwmDriver.hpp" |
| # register_fprime_ut( | ||
| # AUTOCODER_INPUTS | ||
| # "${CMAKE_CURRENT_LIST_DIR}/Driver.fpp" | ||
| # SOURCES | ||
| # "${CMAKE_CURRENT_LIST_DIR}/test/ut/DriverTestMain.cpp" | ||
| # "${CMAKE_CURRENT_LIST_DIR}/test/ut/DriverTester.cpp" | ||
| # DEPENDS | ||
| # STest # For rules-based testing | ||
| # UT_AUTO_HELPERS | ||
| # ) No newline at end of file |
| int fd = ::open(exportPath, O_WRONLY); | ||
| if (fd < 0) { | ||
| // Log the error event using errno to know exactly why it failed (e.g., EACCES) | ||
| this->log_WARNING_HI_OpenChannelError(chipNum, channelNum, errno); | ||
| return false; |
| // 2. Save the base path for our handlers to use later | ||
| snprintf(this->m_basePath, sizeof(this->m_basePath), "/sys/class/pwm/pwmchip%u/pwm%u", chipNum, channelNum); | ||
|
|
||
| // 3. Mark as opened and log success | ||
| this->m_opened = true; |
| if (bytesWritten != len) { | ||
| // The file opened, but the write failed (e.g., channel already exported or invalid) | ||
| this->log_WARNING_HI_OpenChannelError(chipNum, channelNum, errno); | ||
| return false; | ||
| } | ||
|
|
||
| // 2. Save the base path for our handlers to use later | ||
| snprintf(this->m_basePath, sizeof(this->m_basePath), "/sys/class/pwm/pwmchip%u/pwm%u", chipNum, channelNum); | ||
|
|
| // ---------------------------------------------------------------------- | ||
| // Component construction and destruction | ||
| // ---------------------------------------------------------------------- | ||
|
|
||
| LinuxPwmDriver ::LinuxPwmDriver(const char* const compName) : LinuxPwmDriverComponentBase(compName) {} | ||
|
|
||
| LinuxPwmDriver ::~LinuxPwmDriver() {} |
| // Write the string and immediately close the file | ||
| ssize_t bytesWritten = ::write(fd, channelStr, len); | ||
| ::close(fd); | ||
|
|
||
| if (bytesWritten != len) { | ||
| // The file opened, but the write failed (e.g., channel already exported or invalid) | ||
| this->log_WARNING_HI_OpenChannelError(chipNum, channelNum, errno); |
| @ A passive driver for Linux sysfs PWM | ||
| passive component LinuxPwmDriver { |
|
if you could fix the failing CI checks and review the Copilot suggestions (in particular, making sure that the OS-level driver opens and closes correctly), this would be great. Adding @alexisgmm as a reviewer since they are writing the ADCS software that will actually use this driver. |
| Drv::PwmStatus LinuxPwmDriver::open(U32 chipNum, U32 channelNum) { | ||
| // 1. Export the channel (Tell Linux we want to use it) | ||
| char exportPath[256]; | ||
| snprintf(exportPath, sizeof(exportPath), "/sys/class/pwm/pwmchip%u/export", chipNum); |
There was a problem hiding this comment.
Return value should be checked, system calls always have a chance of failing.
|
|
||
| Drv::PwmStatus LinuxPwmDriver::open(U32 chipNum, U32 channelNum) { | ||
| // 1. Export the channel (Tell Linux we want to use it) | ||
| char exportPath[256]; |
There was a problem hiding this comment.
Magic number, define size as a #define or constexpr U8 somewhere in the header
| } | ||
|
|
||
| // Convert the channel number to a string to write to the file | ||
| char channelStr[32]; |
| int len = snprintf(channelStr, sizeof(channelStr), "%u", channelNum); | ||
|
|
||
| // Write the string and immediately close the file | ||
| ssize_t bytesWritten = ::write(fd, channelStr, len); | ||
| ::close(fd); |
There was a problem hiding this comment.
Return values should be checked and logged for errors
| } | ||
|
|
||
| // 2. Save the base path for our handlers to use later | ||
| snprintf(this->m_basePath, sizeof(this->m_basePath), "/sys/class/pwm/pwmchip%u/pwm%u", chipNum, channelNum); |
There was a problem hiding this comment.
Check return code please
| } | ||
|
|
||
| Drv::PwmStatus LinuxPwmDriver::writeSysfsInt(const char* file, U32 value) { | ||
| char fullPath[512]; |
There was a problem hiding this comment.
Define size as constant please
|
|
||
| Drv::PwmStatus LinuxPwmDriver::writeSysfsInt(const char* file, U32 value) { | ||
| char fullPath[512]; | ||
| snprintf(fullPath, sizeof(fullPath), "%s/%s", this->m_basePath, file); |
| int len = snprintf(valStr, sizeof(valStr), "%u", value); | ||
|
|
||
| // Write the buffer directly | ||
| ssize_t bytesWritten = ::write(fd, valStr, len); | ||
|
|
||
| ::close(fd); |
There was a problem hiding this comment.
These lines seem to be repeated, any chance we can have these put into a function somehow?
| U32 m_periodNs = 0; | ||
|
|
||
| // Buffer to hold the base path (e.g., "/sys/class/pwm/pwmchip0/pwm1") | ||
| char m_basePath[256]; |
There was a problem hiding this comment.
Ditto from previous comments, change size to constant defined somewhere in this header
| // Handler implementations for typed input ports | ||
| // ---------------------------------------------------------------------- | ||
|
|
||
| //! Handler implementation for pwmEnable |
There was a problem hiding this comment.
@MahirEmran @h-badams @alexisgmm I see that F prime is writing comments inline with the parameters, should we change these to be doxygen (as in, above the function)? My vote is yes considering our pipelines ask for this formatting.
Description
Our first draft of the PWM driver, does not have unit tests.
Artifacts for PR #12 (DO NOT CHANGE)