From 960626e498395d641d98f1430d53327b1cbf69a7 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 20 Dec 2024 17:34:02 -0600 Subject: [PATCH] Ch341 (#5474) * Very hacky first attempt at usermod ech341 * Fixes and debug printfs * Move to library version of libpinedio-usb * Add spidev: ch341 option in meshtasticd config.yaml * Only check settingsStrings on native * Use new CH341 code * Bump ch341 lib * Cleanup USBHal * Add ch341 config.d files * Remove ch341quirk * Bump to most recent spi-userspace driver * Add handling for ch341 serial, pid, and vid * Minor fixes from pio check * Trunk * Add include for musl compliance * Point to upstream libch341 --- arch/portduino/portduino.ini | 3 +- bin/config-dist.yaml | 9 - bin/config.d/lora-meshstick-1262.yaml | 11 ++ bin/config.d/lora-pinedio-usb-sx1262.yaml | 5 + src/main.cpp | 31 ++-- src/mesh/RadioLibInterface.cpp | 26 +-- src/mesh/RadioLibInterface.h | 9 +- src/platform/portduino/PortduinoGlue.cpp | 135 +++++++++------ src/platform/portduino/PortduinoGlue.h | 9 +- src/platform/portduino/USBHal.h | 194 ++++++++++++++++++++++ 10 files changed, 327 insertions(+), 105 deletions(-) create mode 100644 bin/config.d/lora-meshstick-1262.yaml create mode 100644 bin/config.d/lora-pinedio-usb-sx1262.yaml create mode 100644 src/platform/portduino/USBHal.h diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index bbafef4da1..34f0dd8c96 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -26,6 +26,7 @@ lib_deps = ${radiolib_base.lib_deps} rweather/Crypto@^0.4.0 https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805 + https://github.com/pine64/libch341-spi-userspace#8695637adeabf5abf5601d8e82cb0ba19ce9ec46 build_flags = ${arduino_base.build_flags} @@ -36,4 +37,4 @@ build_flags = -lstdc++fs -lbluetooth -lgpiod - -lyaml-cpp + -lyaml-cpp \ No newline at end of file diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index ec262536f6..49de1675be 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -12,13 +12,6 @@ Lora: # IRQ: 17 # Reset: 22 -# Module: sx1262 # pinedio -# CS: 0 -# IRQ: 10 -# Busy: 11 -# DIO2_AS_RF_SWITCH: true -# spidev: spidev0.1 - # Module: RF95 # Adafruit RFM9x # Reset: 25 # CS: 7 @@ -50,8 +43,6 @@ Lora: # TXen: x # TX and RX enable pins # RXen: x -# ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341 - # spiSpeed: 2000000 ### Set gpio chip to use in /dev/. Defaults to 0. diff --git a/bin/config.d/lora-meshstick-1262.yaml b/bin/config.d/lora-meshstick-1262.yaml new file mode 100644 index 0000000000..3f8d6c617a --- /dev/null +++ b/bin/config.d/lora-meshstick-1262.yaml @@ -0,0 +1,11 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 6 + Reset: 2 + Busy: 4 + spidev: ch341 + DIO3_TCXO_VOLTAGE: true +# USB_Serialnum: 12345678 + USB_PID: 0x5512 + USB_VID: 0x1A86 diff --git a/bin/config.d/lora-pinedio-usb-sx1262.yaml b/bin/config.d/lora-pinedio-usb-sx1262.yaml new file mode 100644 index 0000000000..6b8a9fc95d --- /dev/null +++ b/bin/config.d/lora-pinedio-usb-sx1262.yaml @@ -0,0 +1,5 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 10 + spidev: ch341 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index eb99f279a6..0409636b4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -90,6 +90,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "linux/LinuxHardwareI2C.h" #include "mesh/raspihttp/PiWebServer.h" #include "platform/portduino/PortduinoGlue.h" +#include "platform/portduino/USBHal.h" #include #include #include @@ -213,6 +214,9 @@ static OSThread *powerFSMthread; static OSThread *ambientLightingThread; RadioInterface *rIf = NULL; +#ifdef ARCH_PORTDUINO +RadioLibHal *RadioLibHAL = NULL; +#endif /** * Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep. @@ -241,7 +245,7 @@ void setup() // GPIO10 manages all peripheral power supplies // Turn on peripheral power immediately after MUC starts. // If some boards are turned on late, ESP32 will reset due to low voltage. - // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , + // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , // TF Card , Display backlight(AW9364DNR) , AN48841B(Trackball) , ES7210(Decoder) pinMode(KB_POWERON, OUTPUT); digitalWrite(KB_POWERON, HIGH); @@ -420,7 +424,6 @@ void setup() digitalWrite(AQ_SET_PIN, HIGH); #endif - // Currently only the tbeam has a PMU // PMU initialization needs to be placed before i2c scanning power = new Power(); @@ -706,12 +709,16 @@ void setup() pinMode(LORA_CS, OUTPUT); digitalWrite(LORA_CS, HIGH); SPI1.begin(false); -#else // HW_SPI1_DEVICE +#else // HW_SPI1_DEVICE SPI.setSCK(LORA_SCK); SPI.setTX(LORA_MOSI); SPI.setRX(LORA_MISO); SPI.begin(false); -#endif // HW_SPI1_DEVICE +#endif // HW_SPI1_DEVICE +#elif ARCH_PORTDUINO + if (settingsStrings[spidev] != "ch341") { + SPI.begin(); + } #elif !defined(ARCH_ESP32) // ARCH_RP2040 SPI.begin(); #else @@ -817,8 +824,11 @@ void setup() if (settingsMap[use_sx1262]) { if (!rIf) { LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = - new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); + if (settingsStrings[spidev] == "ch341") { + RadioLibHAL = ch341Hal; + } else { + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + } rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -832,8 +842,7 @@ void setup() } else if (settingsMap[use_rf95]) { if (!rIf) { LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = - new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -848,7 +857,7 @@ void setup() } else if (settingsMap[use_sx1280]) { if (!rIf) { LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -908,7 +917,7 @@ void setup() } else if (settingsMap[use_sx1268]) { if (!rIf) { LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -1265,4 +1274,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 5f82a41ced..e416160eb1 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -31,31 +31,7 @@ void LockingArduinoHal::spiEndTransaction() #if ARCH_PORTDUINO void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) { - if (busy == RADIOLIB_NC) { - spi->transfer(out, in, len); - } else { - uint16_t offset = 0; - - while (len) { - uint8_t block_size = (len < 20 ? len : 20); - spi->transfer((out != NULL ? out + offset : NULL), (in != NULL ? in + offset : NULL), block_size); - if (block_size == len) - return; - - // ensure GPIO is low - - uint32_t start = millis(); - while (digitalRead(busy)) { - if (!Throttle::isWithinTimespanMs(start, 2000)) { - LOG_ERROR("GPIO mid-transfer timeout, is it connected?"); - return; - } - } - - offset += block_size; - len -= block_size; - } - } + spi->transfer(out, in, len); } #endif diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index a5c2e30ddc..d6101ae376 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -22,18 +22,11 @@ class LockingArduinoHal : public ArduinoHal { public: - LockingArduinoHal(SPIClass &spi, SPISettings spiSettings, RADIOLIB_PIN_TYPE _busy = RADIOLIB_NC) - : ArduinoHal(spi, spiSettings) - { -#if ARCH_PORTDUINO - busy = _busy; -#endif - }; + LockingArduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; void spiBeginTransaction() override; void spiEndTransaction() override; #if ARCH_PORTDUINO - RADIOLIB_PIN_TYPE busy; void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override; #endif diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 750cc16309..82fd8de660 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -21,9 +21,12 @@ #include #include +#include "platform/portduino/USBHal.h" + std::map settingsMap; std::map settingsStrings; std::ofstream traceFile; +Ch341Hal *ch341Hal = nullptr; char *configPath = nullptr; char *optionMac = nullptr; @@ -104,7 +107,6 @@ void getMacAddr(uint8_t *dmac) struct hci_dev_info di; di.dev_id = 0; bdaddr_t bdaddr; - char addr[18]; int btsock; btsock = socket(AF_BLUETOOTH, SOCK_RAW, 1); if (btsock < 0) { // If anything fails, just return with the default value @@ -201,8 +203,36 @@ void portduinoSetup() } } } - + // if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address uint8_t dmac[6] = {0}; + if (settingsStrings[spidev] == "ch341") { + ch341Hal = new Ch341Hal(0); + if (settingsStrings[lora_usb_serial_num] != "") { + ch341Hal->serial = settingsStrings[lora_usb_serial_num]; + } + ch341Hal->vid = settingsMap[lora_usb_vid]; + ch341Hal->pid = settingsMap[lora_usb_pid]; + ch341Hal->init(); + if (!ch341Hal->isInit()) { + std::cout << "Could not initialize CH341 device!" << std::endl; + exit(EXIT_FAILURE); + } + char serial[9] = {0}; + ch341Hal->getSerialString(serial, 8); + std::cout << "Serial " << serial << std::endl; + if (strlen(serial) == 8 && settingsStrings[mac_address].length() < 12) { + uint8_t hash[32] = {0}; + memcpy(hash, serial, 8); + crypto->hash(hash, 8); + dmac[0] = (hash[0] << 4) | 2; + dmac[1] = hash[1]; + dmac[2] = hash[2]; + dmac[3] = hash[3]; + dmac[4] = hash[4]; + dmac[5] = hash[5]; + } + } + getMacAddr(dmac); if (dmac[0] == 0 && dmac[1] == 0 && dmac[2] == 0 && dmac[3] == 0 && dmac[4] == 0 && dmac[5] == 0) { std::cout << "*** Blank MAC Address not allowed!" << std::endl; @@ -225,47 +255,11 @@ void portduinoSetup() // Need to bind all the configured GPIO pins so they're not simulated // TODO: Can we do this in the for loop above? // TODO: If one of these fails, we should log and terminate - if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { - settingsMap[cs] = RADIOLIB_NC; - } - } - if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { - settingsMap[irq] = RADIOLIB_NC; - } - } - if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { - settingsMap[busy] = RADIOLIB_NC; - } - } - if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { - settingsMap[reset] = RADIOLIB_NC; - } - } - if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) { - settingsMap[sx126x_ant_sw] = RADIOLIB_NC; - } - } if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) { settingsMap[user] = RADIOLIB_NC; } } - if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { - settingsMap[rxen] = RADIOLIB_NC; - } - } - if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { - settingsMap[txen] = RADIOLIB_NC; - } - } - if (settingsMap[displayPanel] != no_screen) { if (settingsMap[displayCS] > 0) initGPIOPin(settingsMap[displayCS], gpioChipName); @@ -283,7 +277,43 @@ void portduinoSetup() initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); } - if (settingsStrings[spidev] != "") { + // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware + if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") { + if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { + settingsMap[cs] = RADIOLIB_NC; + } + } + if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { + settingsMap[irq] = RADIOLIB_NC; + } + } + if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { + settingsMap[busy] = RADIOLIB_NC; + } + } + if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { + settingsMap[reset] = RADIOLIB_NC; + } + } + if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) { + settingsMap[sx126x_ant_sw] = RADIOLIB_NC; + } + } + if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { + settingsMap[rxen] = RADIOLIB_NC; + } + } + if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { + settingsMap[txen] = RADIOLIB_NC; + } + } SPI.begin(settingsStrings[spidev].c_str()); } if (settingsStrings[traceFilename] != "") { @@ -378,17 +408,24 @@ bool loadConfig(const char *configPath) settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC); settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC); settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); - settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false); settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); - - settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0"); - if (settingsStrings[spidev].length() == 14) { - int x = settingsStrings[spidev].at(11) - '0'; - int y = settingsStrings[spidev].at(13) - '0'; - if (x >= 0 && x < 10 && y >= 0 && y < 10) { - settingsMap[spidev] = x + y << 4; - settingsMap[displayspidev] = settingsMap[spidev]; - settingsMap[touchscreenspidev] = settingsMap[spidev]; + settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as(""); + settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as(0x5512); + settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as(0x1A86); + + settingsStrings[spidev] = yamlConfig["Lora"]["spidev"].as("spidev0.0"); + if (settingsStrings[spidev] != "ch341") { + settingsStrings[spidev] = "/dev/" + settingsStrings[spidev]; + if (settingsStrings[spidev].length() == 14) { + int x = settingsStrings[spidev].at(11) - '0'; + int y = settingsStrings[spidev].at(13) - '0'; + // Pretty sure this is always true + if (x >= 0 && x < 10 && y >= 0 && y < 10) { + // I believe this bit of weirdness is specifically for the new GUI + settingsMap[spidev] = x + y << 4; + settingsMap[displayspidev] = settingsMap[spidev]; + settingsMap[touchscreenspidev] = settingsMap[spidev]; + } } } } diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 01541eeede..9cf9b66788 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -2,6 +2,8 @@ #include #include +#include "platform/portduino/USBHal.h" + enum configNames { use_sx1262, cs, @@ -13,13 +15,15 @@ enum configNames { rxen, dio2_as_rf_switch, dio3_tcxo_voltage, - ch341Quirk, use_rf95, use_sx1280, use_lr1110, use_lr1120, use_lr1121, use_sx1268, + lora_usb_serial_num, + lora_usb_pid, + lora_usb_vid, user, gpiochip, spidev, @@ -69,8 +73,9 @@ enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; +extern Ch341Hal *ch341Hal; int initGPIOPin(int pinNum, std::string gpioChipname); bool loadConfig(const char *configPath); static bool ends_with(std::string_view str, std::string_view suffix); void getMacAddr(uint8_t *dmac); -bool MAC_from_string(std::string mac_str, uint8_t *dmac); \ No newline at end of file +bool MAC_from_string(std::string mac_str, uint8_t *dmac); diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h new file mode 100644 index 0000000000..2b0302ced2 --- /dev/null +++ b/src/platform/portduino/USBHal.h @@ -0,0 +1,194 @@ +#ifndef PI_HAL_LGPIO_H +#define PI_HAL_LGPIO_H + +// include RadioLib +#include "platform/portduino/PortduinoGlue.h" +#include +#include +#include +#include + +// include the library for Raspberry GPIO pins + +#define PI_RISING (PINEDIO_INT_MODE_RISING) +#define PI_FALLING (PINEDIO_INT_MODE_FALLING) +#define PI_INPUT (0) +#define PI_OUTPUT (1) +#define PI_LOW (0) +#define PI_HIGH (1) + +#define CH341_PIN_CS (101) +#define CH341_PIN_IRQ (0) + +// the HAL must inherit from the base RadioLibHal class +// and implement all of its virtual methods +class Ch341Hal : public RadioLibHal +{ + public: + // default constructor - initializes the base HAL and any needed private members + Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0) + : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING) + { + } + + void getSerialString(char *_serial, size_t len) + { + if (!pinedio_is_init) { + return; + } + strncpy(_serial, pinedio.serial_number, len); + } + + void init() override + { + // now the SPI + spiBegin(); + } + + void term() override + { + // stop the SPI + spiEnd(); + } + + // GPIO-related methods (pinMode, digitalWrite etc.) should check + // RADIOLIB_NC as an alias for non-connected pins + void pinMode(uint32_t pin, uint32_t mode) override + { + if (pin == RADIOLIB_NC) { + return; + } + pinedio_set_pin_mode(&pinedio, pin, mode); + } + + void digitalWrite(uint32_t pin, uint32_t value) override + { + if (pin == RADIOLIB_NC) { + return; + } + pinedio_digital_write(&pinedio, pin, value); + } + + uint32_t digitalRead(uint32_t pin) override + { + if (pin == RADIOLIB_NC) { + return 0; + } + return pinedio_digital_read(&pinedio, pin); + } + + void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override + { + if ((interruptNum == RADIOLIB_NC)) { + return; + } + // LOG_DEBUG("Attach interrupt to pin %d", interruptNum); + pinedio_attach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum, (pinedio_int_mode)mode, interruptCb); + } + + void detachInterrupt(uint32_t interruptNum) override + { + if ((interruptNum == RADIOLIB_NC)) { + return; + } + // LOG_DEBUG("Detach interrupt from pin %d", interruptNum); + + pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum); + } + + void delay(unsigned long ms) override + { + if (ms == 0) { + sched_yield(); + return; + } + + usleep(ms * 1000); + } + + void delayMicroseconds(unsigned long us) override + { + if (us == 0) { + sched_yield(); + return; + } + usleep(us); + } + + void yield() override { sched_yield(); } + + unsigned long millis() override + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL); + } + + unsigned long micros() override + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000000ULL) + tv.tv_usec; + } + + long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override + { + fprintf(stderr, "pulseIn for pin %u is not supported!\n", pin); + return 0; + } + + void spiBegin() + { + if (!pinedio_is_init) { + if (serial != "") { + strncpy(pinedio.serial_number, serial.c_str(), 8); + pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1); + } + pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid); + pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid); + int32_t ret = pinedio_init(&pinedio, NULL); + if (ret != 0) { + fprintf(stderr, "Could not open SPI: %d\n", ret); + } else { + pinedio_is_init = true; + // LOG_INFO("USB Serial: %s", pinedio.serial_number); + pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0); + pinedio_set_pin_mode(&pinedio, 3, true); + pinedio_set_pin_mode(&pinedio, 5, true); + } + } + } + + void spiBeginTransaction() {} + + void spiTransfer(uint8_t *out, size_t len, uint8_t *in) + { + int32_t result = pinedio_transceive(&this->pinedio, out, in, len); + if (result < 0) { + fprintf(stderr, "Could not perform SPI transfer: %d\n", result); + } + } + + void spiEndTransaction() {} + + void spiEnd() + { + if (pinedio_is_init) { + pinedio_deinit(&pinedio); + pinedio_is_init = false; + } + } + + bool isInit() { return pinedio_is_init; } + + std::string serial = ""; + uint32_t pid = 0x5512; + uint32_t vid = 0x1A86; + + private: + // the HAL can contain any additional private members + pinedio_inst pinedio = {0}; + bool pinedio_is_init = false; +}; + +#endif \ No newline at end of file