From c9a42e2d1eaa1313365ff93c2dc13464021df4ae Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 23 Feb 2021 13:24:05 +0100 Subject: [PATCH] refactored GPIO to support callbacks --- bsp_rpi/gpio/GPIORPi.cpp | 2 +- linux/boardtest/LibgpiodTest.cpp | 2 +- linux/gpio/GpioCookie.cpp | 4 +- linux/gpio/GpioCookie.h | 2 +- linux/gpio/GpioIF.h | 2 +- linux/gpio/LinuxLibgpioIF.cpp | 292 ++++++++++++------ linux/gpio/LinuxLibgpioIF.h | 19 +- linux/gpio/gpioDefinitions.h | 68 +++- .../devices/SolarArrayDeploymentHandler.cpp | 2 +- 9 files changed, 268 insertions(+), 125 deletions(-) diff --git a/bsp_rpi/gpio/GPIORPi.cpp b/bsp_rpi/gpio/GPIORPi.cpp index 84a3365f..74b67a42 100644 --- a/bsp_rpi/gpio/GPIORPi.cpp +++ b/bsp_rpi/gpio/GPIORPi.cpp @@ -10,7 +10,7 @@ ReturnValue_t gpio::createRpiGpioConfig(GpioCookie* cookie, gpioId_t gpioId, int return HasReturnvaluesIF::RETURN_FAILED; } - GpioConfig_t config; + GpiodRegular config; /* Default chipname for Raspberry Pi. There is still gpiochip1 for expansion, but most users will not need this */ config.chipname = "gpiochip0"; diff --git a/linux/boardtest/LibgpiodTest.cpp b/linux/boardtest/LibgpiodTest.cpp index d88e5676..99602107 100644 --- a/linux/boardtest/LibgpiodTest.cpp +++ b/linux/boardtest/LibgpiodTest.cpp @@ -13,7 +13,7 @@ LibgpiodTest::LibgpiodTest(object_id_t objectId, object_id_t gpioIfobjectId, if (gpioInterface == nullptr) { sif::error << "LibgpiodTest::LibgpiodTest: Invalid Gpio interface." << std::endl; } - gpioInterface->initialize(gpioCookie); + gpioInterface->addGpios(gpioCookie); testCase = TestCases::LOOPBACK; } diff --git a/linux/gpio/GpioCookie.cpp b/linux/gpio/GpioCookie.cpp index cd73574c..f957d070 100644 --- a/linux/gpio/GpioCookie.cpp +++ b/linux/gpio/GpioCookie.cpp @@ -4,10 +4,10 @@ GpioCookie::GpioCookie() { } -ReturnValue_t GpioCookie::addGpio(gpioId_t gpioId, GpioConfig_t& gpioConfig){ +ReturnValue_t GpioCookie::addGpio(gpioId_t gpioId, GpiodRegular& gpioConfig){ auto gpioMapIter = gpioMap.find(gpioId); if(gpioMapIter == gpioMap.end()) { - auto statusPair = gpioMap.emplace(gpioId, gpioConfig); + auto statusPair = gpioMap.emplace(gpioId, new GpiodRegular(gpioConfig)); if (statusPair.second == false) { #if FSFW_VERBOSE_LEVEL >= 1 sif::error << "GpioCookie::addGpio: Failed to add GPIO " << gpioId << diff --git a/linux/gpio/GpioCookie.h b/linux/gpio/GpioCookie.h index e1436813..c7721a98 100644 --- a/linux/gpio/GpioCookie.h +++ b/linux/gpio/GpioCookie.h @@ -23,7 +23,7 @@ public: virtual ~GpioCookie(); - ReturnValue_t addGpio(gpioId_t gpioId, GpioConfig_t& gpioConfig); + ReturnValue_t addGpio(gpioId_t gpioId, GpiodRegular& gpioConfig); /** * @brief Get map with registered GPIOs. */ diff --git a/linux/gpio/GpioIF.h b/linux/gpio/GpioIF.h index 80d3a727..75feb3ce 100644 --- a/linux/gpio/GpioIF.h +++ b/linux/gpio/GpioIF.h @@ -22,7 +22,7 @@ public: * @param cookie Cookie specifying informations of the GPIOs required * by a object. */ - virtual ReturnValue_t initialize(GpioCookie* cookie) = 0; + virtual ReturnValue_t addGpios(GpioCookie* cookie) = 0; /** * @brief By implementing this function a child must provide the diff --git a/linux/gpio/LinuxLibgpioIF.cpp b/linux/gpio/LinuxLibgpioIF.cpp index 1b152a0f..0cf3858f 100644 --- a/linux/gpio/LinuxLibgpioIF.cpp +++ b/linux/gpio/LinuxLibgpioIF.cpp @@ -2,18 +2,20 @@ #include "GpioCookie.h" #include +#include #include #include #include + LinuxLibgpioIF::LinuxLibgpioIF(object_id_t objectId) : SystemObject(objectId) { } LinuxLibgpioIF::~LinuxLibgpioIF() { } -ReturnValue_t LinuxLibgpioIF::initialize(GpioCookie* gpioCookie){ +ReturnValue_t LinuxLibgpioIF::addGpios(GpioCookie* gpioCookie) { ReturnValue_t result; if(gpioCookie == nullptr) { sif::error << "LinuxLibgpioIF::initialize: Invalid cookie" << std::endl; @@ -32,101 +34,153 @@ ReturnValue_t LinuxLibgpioIF::initialize(GpioCookie* gpioCookie){ return RETURN_FAILED; } - /* Register new GPIOs in gpioMap*/ + /* Register new GPIOs in gpioMap */ gpioMap.insert(mapToAdd.begin(), mapToAdd.end()); return RETURN_OK; } ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) { + for(auto& gpioConfig: mapToAdd) { + switch(gpioConfig.second->gpioType) { + case(gpio::GpioTypes::NONE): { + return GPIO_INVALID_INSTANCE; + } + case(gpio::GpioTypes::GPIOD_REGULAR): { + GpiodRegular* regularGpio = dynamic_cast(gpioConfig.second); + configureRegularGpio(gpioConfig.first, regularGpio); + break; + } + case(gpio::GpioTypes::CALLBACK): { + auto gpioCallback = dynamic_cast(gpioMapIter->second); + if(gpioCallback->callback == nullptr) { + return GPIO_INVALID_INSTANCE; + } + gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::READ, + 0, gpioCallback->callbackArgs); + } + } + } + return RETURN_OK; +} + +ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, GpiodRegular *regularGpio) { std::string chipname; unsigned int lineNum; struct gpiod_chip *chip; gpio::Direction direction; std::string consumer; struct gpiod_line *lineHandle; - int result; + int result = 0; - for(auto& gpioConfig: mapToAdd) { - chipname = gpioConfig.second.chipname; - chip = gpiod_chip_open_by_name(chipname.c_str()); - if (!chip) { - sif::error << "LinuxLibgpioIF::configureGpios: Failed to open chip " - << chipname << ". Gpio ID: " << gpioConfig.first << std::endl; - return RETURN_FAILED; - } - - lineNum = gpioConfig.second.lineNum; - lineHandle = gpiod_chip_get_line(chip, lineNum); - if (!lineHandle) { - sif::error << "LinuxLibgpioIF::configureGpios: Failed to open line" << std::endl; - gpiod_chip_close(chip); - return RETURN_FAILED; - } - - direction = gpioConfig.second.direction; - consumer = gpioConfig.second.consumer; - /* Configure direction and add a description to the GPIO */ - switch (direction) { - case gpio::OUT: - result = gpiod_line_request_output(lineHandle, consumer.c_str(), - gpioConfig.second.initValue); - if (result < 0) { - sif::error << "LinuxLibgpioIF::configureGpios: Failed to request line " - << lineNum << " from GPIO instance with ID: " << gpioConfig.first - << std::endl; - gpiod_line_release(lineHandle); - return RETURN_FAILED; - } - break; - case gpio::IN: - result = gpiod_line_request_input(lineHandle, consumer.c_str()); - if (result < 0) { - sif::error << "LinuxLibgpioIF::configureGpios: Failed to request line " - << lineNum << " from GPIO instance with ID: " << gpioConfig.first - << std::endl; - gpiod_line_release(lineHandle); - return RETURN_FAILED; - } - break; - default: - sif::error << "LinuxLibgpioIF::configureGpios: Invalid direction specified" - << std::endl; - return RETURN_FAILED; - } - /** - * Write line handle to GPIO configuration instance so it can later be used to set or - * read states of GPIOs. - */ - gpioConfig.second.lineHandle = lineHandle; - } - return RETURN_OK; -} - -ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId){ - return driveGpio(gpioId, 1); -} - -ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId){ - return driveGpio(gpioId, 0); -} - -ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId, - unsigned int logiclevel) { - int result; - struct gpiod_line *lineHandle; - - gpioMapIter = gpioMap.find(gpioId); - if (gpioMapIter == gpioMap.end()){ - sif::debug << "LinuxLibgpioIF::driveGpio: Unknown gpio id " << gpioId << std::endl; + chipname = regularGpio->chipname; + chip = gpiod_chip_open_by_name(chipname.c_str()); + if (!chip) { + sif::error << "LinuxLibgpioIF::configureGpios: Failed to open chip " + << chipname << ". Gpio ID: " << gpioId << std::endl; return RETURN_FAILED; } - lineHandle = gpioMapIter->second.lineHandle; - result = gpiod_line_set_value(lineHandle, logiclevel); + lineNum = regularGpio->lineNum; + lineHandle = gpiod_chip_get_line(chip, lineNum); + if (!lineHandle) { + sif::error << "LinuxLibgpioIF::configureGpios: Failed to open line" << std::endl; + gpiod_chip_close(chip); + return RETURN_FAILED; + } + + direction = regularGpio->direction; + consumer = regularGpio->consumer; + /* Configure direction and add a description to the GPIO */ + switch (direction) { + case(gpio::OUT): { + result = gpiod_line_request_output(lineHandle, consumer.c_str(), + regularGpio->initValue); + if (result < 0) { + sif::error << "LinuxLibgpioIF::configureGpios: Failed to request line " << lineNum << + " from GPIO instance with ID: " << gpioId << std::endl; + gpiod_line_release(lineHandle); + return RETURN_FAILED; + } + break; + } + case(gpio::IN): { + result = gpiod_line_request_input(lineHandle, consumer.c_str()); + if (result < 0) { + sif::error << "LinuxLibgpioIF::configureGpios: Failed to request line " + << lineNum << " from GPIO instance with ID: " << gpioId << std::endl; + gpiod_line_release(lineHandle); + return RETURN_FAILED; + } + break; + } + default: { + sif::error << "LinuxLibgpioIF::configureGpios: Invalid direction specified" + << std::endl; + return GPIO_INVALID_INSTANCE; + } + + } + /** + * Write line handle to GPIO configuration instance so it can later be used to set or + * read states of GPIOs. + */ + regularGpio->lineHandle = lineHandle; + return RETURN_OK; +} + +ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) { + gpioMapIter = gpioMap.find(gpioId); + if (gpioMapIter == gpioMap.end()) { + sif::warning << "LinuxLibgpioIF::driveGpio: Unknown GPIOD ID " << gpioId << std::endl; + return UNKNOWN_GPIO_ID; + } + + if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) { + return driveGpio(gpioId, dynamic_cast(gpioMapIter->second), 1); + } + else { + auto gpioCallback = dynamic_cast(gpioMapIter->second); + if(gpioCallback->callback == nullptr) { + return GPIO_INVALID_INSTANCE; + } + gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE, + 1, gpioCallback->callbackArgs); + } + return GPIO_TYPE_FAILURE; +} + +ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) { + gpioMapIter = gpioMap.find(gpioId); + if (gpioMapIter == gpioMap.end()) { + sif::warning << "LinuxLibgpioIF::driveGpio: Unknown GPIOD ID " << gpioId << std::endl; + return UNKNOWN_GPIO_ID; + } + + if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) { + return driveGpio(gpioId, dynamic_cast(gpioMapIter->second), 0); + } + else { + auto gpioCallback = dynamic_cast(gpioMapIter->second); + if(gpioCallback->callback == nullptr) { + return GPIO_INVALID_INSTANCE; + } + gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE, + 0, gpioCallback->callbackArgs); + } + return GPIO_TYPE_FAILURE; +} + +ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId, + GpiodRegular* regularGpio, unsigned int logicLevel) { + if(regularGpio == nullptr) { + return GPIO_TYPE_FAILURE; + } + + int result = gpiod_line_set_value(regularGpio->lineHandle, logicLevel); if (result < 0) { - sif::error << "LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID " - << gpioId << " to logic level " << logiclevel << std::endl; + sif::warning << "LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID " << gpioId << + " to logic level " << logicLevel << std::endl; return DRIVE_GPIO_FAILURE; } @@ -134,38 +188,76 @@ ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId, } ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) { - struct gpiod_line *lineHandle; - gpioMapIter = gpioMap.find(gpioId); if (gpioMapIter == gpioMap.end()){ - sif::debug << "LinuxLibgpioIF::readGpio: Unknown gpio id " << gpioId << std::endl; - return RETURN_FAILED; + sif::warning << "LinuxLibgpioIF::readGpio: Unknown GPIOD ID " << gpioId << std::endl; + return UNKNOWN_GPIO_ID; + } + + if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) { + GpiodRegular* regularGpio = dynamic_cast(gpioMapIter->second); + if(regularGpio == nullptr) { + return GPIO_TYPE_FAILURE; + } + *gpioState = gpiod_line_get_value(regularGpio->lineHandle); + } + else { + } - lineHandle = gpioMapIter->second.lineHandle; - *gpioState = gpiod_line_get_value(lineHandle); return RETURN_OK; } ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){ - gpioId_t gpioId; + ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; for(auto& gpioConfig: mapToAdd) { - gpioId = gpioConfig.first; - /* Cross check with private map */ - gpioMapIter = gpioMap.find(gpioId); - if(gpioMapIter != mapToAdd.end()) { - /* An entry for this GPIO already exists. Check if configuration - * of direction is equivalent */ - if (gpioConfig.second.direction != gpioMapIter->second.direction){ - sif::error << "LinuxLibgpioIF::checkForConflicts: Detected conflict for GPIO " << - gpioConfig.first << std::endl; - return RETURN_FAILED; + switch(gpioConfig.second->gpioType) { + case(gpio::GpioTypes::GPIOD_REGULAR): { + auto regularGpio = dynamic_cast(gpioConfig.second); + if(regularGpio == nullptr) { + return GPIO_TYPE_FAILURE; } - /* Remove element from map to add because a entry for this GPIO - * already exists */ - mapToAdd.erase(gpioConfig.first); + result = checkForConflictsRegularGpio(gpioConfig.first, regularGpio); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + break; + } + default: { + + } } } - return RETURN_OK; + return status; +} + + +ReturnValue_t LinuxLibgpioIF::checkForConflictsRegularGpio(gpioId_t gpioIdToCheck, + GpiodRegular* gpioToCheck) { + /* Cross check with private map */ + gpioMapIter = gpioMap.find(gpioIdToCheck); + if(gpioMapIter != gpioMap.end()) { + if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) { + auto ownRegularGpio = dynamic_cast(gpioMapIter->second); + if(ownRegularGpio == nullptr) { + return GPIO_TYPE_FAILURE; + } + /* An entry for this GPIO already exists. Check if configuration + * of direction is equivalent */ + if (gpioToCheck->direction != ownRegularGpio->direction){ + sif::error << "LinuxLibgpioIF::checkForConflicts: Detected conflict for GPIO " << + gpioIdToCheck << std::endl; + return RETURN_FAILED; + + } + } + + /* Remove element from map to add because a entry for this GPIO + * already exists */ + gpioMap.erase(gpioIdToCheck); + + } + return HasReturnvaluesIF::RETURN_OK; } diff --git a/linux/gpio/LinuxLibgpioIF.h b/linux/gpio/LinuxLibgpioIF.h index 03c78fdb..287e157d 100644 --- a/linux/gpio/LinuxLibgpioIF.h +++ b/linux/gpio/LinuxLibgpioIF.h @@ -17,14 +17,21 @@ class GpioCookie; class LinuxLibgpioIF : public GpioIF, public SystemObject { public: - static const uint8_t INTERFACE_ID = CLASS_ID::LINUX_LIBGPIO_IF; + static const uint8_t gpioRetvalId = CLASS_ID::LINUX_LIBGPIO_IF; - static const ReturnValue_t DRIVE_GPIO_FAILURE = MAKE_RETURN_CODE(0x2); + static constexpr ReturnValue_t UNKNOWN_GPIO_ID = + HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 1); + static constexpr ReturnValue_t DRIVE_GPIO_FAILURE = + HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 2); + static constexpr ReturnValue_t GPIO_TYPE_FAILURE = + HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 3); + static constexpr ReturnValue_t GPIO_INVALID_INSTANCE = + HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4); LinuxLibgpioIF(object_id_t objectId); virtual ~LinuxLibgpioIF(); - ReturnValue_t initialize(GpioCookie* gpioCookie) override; + ReturnValue_t addGpios(GpioCookie* gpioCookie) override; ReturnValue_t pullHigh(gpioId_t gpioId) override; ReturnValue_t pullLow(gpioId_t gpioId) override; ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) override; @@ -40,7 +47,9 @@ private: * @param gpioId The GPIO ID of the GPIO to drive. * @param logiclevel The logic level to set. O or 1. */ - ReturnValue_t driveGpio(gpioId_t gpioId, unsigned int logiclevel); + ReturnValue_t driveGpio(gpioId_t gpioId, GpiodRegular* regularGpio, unsigned int logiclevel); + + ReturnValue_t configureRegularGpio(gpioId_t gpioId, GpiodRegular* regularGpio); /** * @brief This function checks if GPIOs are already registered and whether @@ -53,6 +62,8 @@ private: */ ReturnValue_t checkForConflicts(GpioMap& mapToAdd); + ReturnValue_t checkForConflictsRegularGpio(gpioId_t gpiodId, GpiodRegular* regularGpio); + /** * @brief Performs the initial configuration of all GPIOs specified in the GpioMap mapToAdd. */ diff --git a/linux/gpio/gpioDefinitions.h b/linux/gpio/gpioDefinitions.h index 28ad1239..e4fb89bc 100644 --- a/linux/gpio/gpioDefinitions.h +++ b/linux/gpio/gpioDefinitions.h @@ -12,6 +12,17 @@ enum Direction { OUT = 1 }; +enum GpioOperation { + READ, + WRITE +}; + +enum GpioTypes { + NONE, + GPIOD_REGULAR, + CALLBACK +}; + static constexpr gpioId_t NO_GPIO = -1; } @@ -29,23 +40,52 @@ static constexpr gpioId_t NO_GPIO = -1; * @param lineHandle The handle returned by gpiod_chip_get_line will be later written to this * pointer. */ -typedef struct GpioConfig { - GpioConfig(): chipname(), lineNum(0), consumer(), direction(gpio::Direction::OUT), - initValue(0) {}; +class GpioBase { +public: - GpioConfig(std::string chipname_, int lineNum_, std::string consumer_, - gpio::Direction direction_, int initValue_): - chipname(chipname_), lineNum(lineNum_), consumer(consumer_), - direction(direction_), initValue(initValue_) {} - std::string chipname; - int lineNum; + GpioBase() = default; + + GpioBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction, + int initValue): + gpioType(gpioType), consumer(consumer),direction(direction), initValue(initValue) {} + + virtual~ GpioBase() {}; + + /* Can be used to cast GpioBase to a concrete child implementation */ + gpio::GpioTypes gpioType = gpio::GpioTypes::NONE; std::string consumer; - gpio::Direction direction; - int initValue; - struct gpiod_line* lineHandle = nullptr; -} GpioConfig_t; + gpio::Direction direction = gpio::Direction::IN; + int initValue = 0; +}; -using GpioMap = std::unordered_map; +class GpiodRegular: public GpioBase { +public: + GpiodRegular(): GpioBase() {}; + + GpiodRegular(std::string chipname_, int lineNum_, std::string consumer_, + gpio::Direction direction_, int initValue_): + GpioBase(gpio::GpioTypes::GPIOD_REGULAR, consumer_, direction_, initValue_), + chipname(chipname_), lineNum(lineNum_) {} + std::string chipname; + int lineNum = 0; + struct gpiod_line* lineHandle = nullptr; +}; + +class GpioCallback: public GpioBase { +public: + GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_, + void (* callback) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args), + void* callbackArgs): + GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_), + callback(callback), callbackArgs(callbackArgs) {} + + void (* callback) (gpioId_t gpioId, gpio::GpioOperation gpioOp, + int value, void* args) = nullptr; + void* callbackArgs = nullptr; +}; + + +using GpioMap = std::unordered_map; using GpioMapIter = GpioMap::iterator; #endif /* LINUX_GPIO_GPIODEFINITIONS_H_ */ diff --git a/mission/devices/SolarArrayDeploymentHandler.cpp b/mission/devices/SolarArrayDeploymentHandler.cpp index a78478e5..5ce1558a 100644 --- a/mission/devices/SolarArrayDeploymentHandler.cpp +++ b/mission/devices/SolarArrayDeploymentHandler.cpp @@ -39,7 +39,7 @@ ReturnValue_t SolarArrayDeploymentHandler::initialize() { return ObjectManagerIF::CHILD_INIT_FAILED; } - result = gpioInterface->initialize(dynamic_cast(gpioCookie)); + result = gpioInterface->addGpios(dynamic_cast(gpioCookie)); if (result != RETURN_OK) { sif::error << "SolarArrayDeploymentHandler::initialize: Failed to initialize Gpio interface" << std::endl;