mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-22 18:52:30 +00:00
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
This commit is contained in:
@@ -21,9 +21,12 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "platform/portduino/USBHal.h"
|
||||
|
||||
std::map<configNames, int> settingsMap;
|
||||
std::map<configNames, std::string> 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<int>(RADIOLIB_NC);
|
||||
settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as<int>(RADIOLIB_NC);
|
||||
settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0);
|
||||
settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as<bool>(false);
|
||||
settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
|
||||
settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as<std::string>("");
|
||||
settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as<int>(0x5512);
|
||||
settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as<int>(0x1A86);
|
||||
|
||||
settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as<std::string>("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[spidev] = yamlConfig["Lora"]["spidev"].as<std::string>("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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
#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<configNames, int> settingsMap;
|
||||
extern std::map<configNames, std::string> 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);
|
||||
bool MAC_from_string(std::string mac_str, uint8_t *dmac);
|
||||
|
||||
194
src/platform/portduino/USBHal.h
Normal file
194
src/platform/portduino/USBHal.h
Normal file
@@ -0,0 +1,194 @@
|
||||
#ifndef PI_HAL_LGPIO_H
|
||||
#define PI_HAL_LGPIO_H
|
||||
|
||||
// include RadioLib
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include <RadioLib.h>
|
||||
#include <csignal>
|
||||
#include <libpinedio-usb.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user