diff --git a/lib/io/hardware_gpio.cpp b/lib/io/hardware_gpio.cpp index f62184f9..415b7d84 100644 --- a/lib/io/hardware_gpio.cpp +++ b/lib/io/hardware_gpio.cpp @@ -1,36 +1,125 @@ #include "hardware_gpio.hpp" +#include +#include + +#include + namespace hyped::io { std::optional HardwareGpioReader::read() { - // TODOLater: implement - throw -1; + // pinMap just 0000.... with 1 flipped in pin number. + // So AND with readAddr to extract the specific pin + const std::uint8_t out = 0; + if (*gpio_readAddr & pin_map) { + return core::DigitalSignal::kHigh; + } else { + return core::DigitalSignal::kLow; + } } core::Result HardwareGpioWriter::write(const core::DigitalSignal state) { - // TODOLater: implement - throw -1; + // Set and Clear are seperate registers. + if (state == core::DigitalSignal::kHigh) { + *gpio_setAddr = pin_map; + } else { + *gpio_clearAddr = pin_map; + } + return core::Result::kSuccess; } HardwareGpio::HardwareGpio(core::ILogger &log) : log_(log) { - // TODOLater: implement } std::optional> HardwareGpio::getReader(const std::uint8_t pin) { - // TODOLater: implement - log_.log(core::LogLevel::kFatal, "GPIO reader not implemented"); - return std::nullopt; + // What happens if all shared pointers are removed and this is called? + // Map solves this, GPIO will always hold one pointer so it dosen't get destroyed. + if (initialized_readers_.count(pin) != 0) { + std::shared_ptr reader = initialized_readers_[pin]; + return reader; + } + + // There are 4 Bank Numbers, 0 1 2 3 + // Integer divison to get bank number + const std::uint32_t bank = pin / 32; + // Modulo by 32 gets us the ID of the pin relative to the bank + const std::uint32_t pin_id = pin % 32; + // Gpio addresses contain 32 pins, so we use pinmap to specify specific pin. + const std::uint32_t pin_map = (1 << pin_id); + if (bank > 3) { + log_.log(core::LogLevel::kFatal, "invalid pin number"); + return std::nullopt; + } + + // Get memory address from bank address + const off_t pin_address = bank_addresses[bank]; + volatile void *gpio_addr; + volatile std::uint32_t *gpio_read; + + // /dev/mem and mmap is related to making the registry available as a virtual memory address. + int fd = open("/dev/mem", O_RDWR); + if (fd < 0) { + log_.log(core::LogLevel::kFatal, "opening /dev/mem failed"); + return std::nullopt; + } + + // Get the memory mapping of the GPIO pin. + gpio_addr = mmap(0, pin_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, pin_address); + if (gpio_addr == MAP_FAILED) { + log_.log(core::LogLevel::kFatal, "mmap failed"); + return std::nullopt; + } + // Type conversion for address adding from void + const std::uint64_t base = reinterpret_cast(gpio_addr); + // pinRead is the hardware specified address for reading. + gpio_read = reinterpret_cast(base + pin_read); + + // Keep track of intialized pins + initialized_readers_[pin] + = std::shared_ptr(new HardwareGpioReader(pin_map, gpio_read)); + std::shared_ptr reader = initialized_readers_[pin]; + return reader; } std::optional> HardwareGpio::getWriter(const std::uint8_t pin) { - // TODOLater: implement - log_.log(core::LogLevel::kFatal, "GPIO writer not implemented"); - return std::nullopt; + // Check if pin is already initialized + if (initialized_writers_.count(pin) != 0) { + std::shared_ptr writer = initialized_writers_[pin]; + return writer; + } + + const std::uint32_t bank = pin / 32; + const std::uint32_t pin_id = pin % 32; + const std::uint32_t pin_map = (1 << pin_id); + + const off_t pin_address = bank_addresses[bank]; + volatile void *gpio_addr; + volatile std::uint32_t *gpio_set; + volatile std::uint32_t *gpio_clear; + + int fd = open("/dev/mem", O_RDWR); + if (fd < 0) { return std::nullopt; } + + gpio_addr = mmap(0, pin_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, pin_address); + if (gpio_addr == MAP_FAILED) { return std::nullopt; } + + const std::uint64_t base = reinterpret_cast(gpio_addr); + + // pinset is the hardware specified address for reading. + gpio_set = reinterpret_cast(base + pin_set); + + // pinclear is the hardware specified address for reading. + gpio_clear = reinterpret_cast(base + pin_clear); + + initialized_writers_[pin] + = std::shared_ptr(new HardwareGpioWriter(pin_map, gpio_set, gpio_clear)); + std::shared_ptr writer = initialized_writers_[pin]; + return writer; } } // namespace hyped::io \ No newline at end of file diff --git a/lib/io/hardware_gpio.hpp b/lib/io/hardware_gpio.hpp index 9cd7e66d..e246c0fd 100644 --- a/lib/io/hardware_gpio.hpp +++ b/lib/io/hardware_gpio.hpp @@ -2,31 +2,63 @@ #include "gpio.hpp" +#include + #include namespace hyped::io { +// GPIO hardware specified addresses and sizes for read, clear, set, size, etc. +static constexpr std::uint32_t pin_size = 0x1000; +static constexpr std::uint32_t pin_read = 0x138; +static constexpr std::uint32_t pin_clear = 0x190; +static constexpr std::uint32_t pin_set = 0x194; + class HardwareGpioReader : public IGpioReader { public: + /** + * @brief Read a high or low from the GPIO pin. + */ virtual std::optional read(); private: - HardwareGpioReader(); + HardwareGpioReader(const std::uint8_t pin, volatile std::uint32_t *read) + : pin_map(pin), + gpio_readAddr(read){}; + + const std::uint8_t pin_map; + volatile std::uint32_t *gpio_readAddr; + friend class HardwareGpio; }; class HardwareGpioWriter : public IGpioWriter { public: + /** + * @brief Writes a high or low to the GPIO pin. + * @param state The digital signal to write to the pin. + */ virtual core::Result write(const core::DigitalSignal state); private: - HardwareGpioWriter(const std::uint8_t pin); + HardwareGpioWriter(const std::uint8_t pin, + volatile std::uint32_t *set, + volatile std::uint32_t *clear) + : pin_map(pin), + gpio_setAddr(set), + gpio_clearAddr(clear){}; + + const std::uint8_t pin_map; + volatile std::uint32_t *gpio_setAddr; + volatile std::uint32_t *gpio_clearAddr; + friend class HardwareGpio; }; /** * Hardware GPIO interface, requires physical GPIO pins to be present. This should only * be instantiated at the top level and then provided to users through the IGpio interface. + * Ensure inputted pins are defined as pin = 32*X + Y (GPIOX_Y) */ class HardwareGpio { public: @@ -37,6 +69,13 @@ class HardwareGpio { private: core::ILogger &log_; + // Bank Addresses are header base addresses. + // Page 211-213 Figure 6-7/8 P8 Header Pins Beaglebone Bible + + const std::array bank_addresses = {0x44e07000, 0x4804c000, 0x481ac000, 0x481ae000}; + std::unordered_map> initialized_writers_; + std::unordered_map> initialized_readers_; + // Also hardware specified addresses and sizes for read, clear, set, size, etc. }; } // namespace hyped::io \ No newline at end of file diff --git a/scripts/hooks/pre-commit b/scripts/hooks/pre-commit old mode 100755 new mode 100644 diff --git a/scripts/setup b/scripts/setup old mode 100755 new mode 100644