Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Commit

Permalink
modern linux gpio api almost works
Browse files Browse the repository at this point in the history
  • Loading branch information
geeksville committed Apr 22, 2021
1 parent 7973624 commit 008e0f6
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 61 deletions.
4 changes: 2 additions & 2 deletions cores/portduino/PortduinoGPIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void gpioInit() {
void gpioIdle() {
// log(SysGPIO, LogDebug, "doing idle refresh");
for(size_t i = 0; i < NUM_GPIOS; i++)
pins[i]->refreshState();
pins[i]->refreshIfNeeded();
}

void gpioBind(GPIOPinIf *p) {
Expand Down Expand Up @@ -65,7 +65,7 @@ PinStatus digitalRead(pin_size_t pinNumber)
auto p = getGPIO(pinNumber);
auto r = p->readPin();

log(SysGPIO, LogDebug, "digitalRead(%d) -> %d", pinNumber, r);
// log(SysGPIO, LogDebug, "digitalRead(%d) -> %d", pinNumber, r);
return r;
}

Expand Down
105 changes: 53 additions & 52 deletions cores/portduino/PortduinoGPIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class GPIOPinIf
* the mirrored copy of state that is returned for calls to readPin().
*
* If an ISR is attached to this GPIO, call if the speicifed PinStatus matches */
virtual void refreshState() = 0;
virtual void refreshIfNeeded() = 0;

/** Convience function for the common case of mapping to only one GPIO */
virtual PinStatus readPin() = 0;
Expand Down Expand Up @@ -57,8 +57,13 @@ class GPIOPin : public GPIOPinIf

voidFuncPtr isr = NULL;

bool silent = false; // If silent, we won't emit logs

protected:

/// What sorts of edges do we want to invoke the ISR for?
int8_t isrPinStatus = -1; // -1 or PinStatus

public:
GPIOPin(pin_size_t n, String _name) : number(n), name(_name) {}
virtual ~GPIOPin() {}
Expand All @@ -67,31 +72,25 @@ class GPIOPin : public GPIOPinIf

const char *getName() const { return name.c_str(); }

/** Called to read from a pin and if the pin has changed state possibly call an ISR, also changes
* the mirrored copy of state that is returned for calls to readPin().
*
* If an ISR is attached to this GPIO, call if the speicifed PinStatus matches */
void refreshState()
{
if (mode != OUTPUT)
{
auto newState = readPinHardware();
auto oldState = status;
status = newState;
callISR(oldState, newState);
}
}

/** Convience function for the common case of mapping to only one GPIO */
virtual PinStatus readPin()
PinStatus readPin()
{
refreshState(); // Get current hw pin values (might also cause an ISR to run)

// if (!silent) log(SysGPIO, LogInfo, "readPin(%s, %d, %d)", getName(), getPinNum(), status);

return status;
}

/** Convience function for the common case of mapping to only one GPIO */
virtual void writePin(PinStatus s)
{
// log(SysGPIO, LogDebug, "writePin(%s, %d)", getName(), s);
assert(s == HIGH || s == LOW); // Don't let user set invalid values
status = s;

if (!silent)
log(SysGPIO, LogInfo, "writePin(%s, %d, %d)", getName(), getPinNum(), s);
}

virtual int analogRead()
Expand All @@ -117,6 +116,25 @@ class GPIOPin : public GPIOPinIf
virtual void setPinMode(PinMode m)
{
mode = m;
if (!silent)
log(SysGPIO, LogInfo, "setPinMode(%s, %d, %d)", getName(), getPinNum(), m);
}

/** CALLED ONLY BY PORTDUINO special thread
* If this pin has an ISR attached, poll it and call the ISR if necessary
*/
void refreshIfNeeded() {
if(isr)
refreshState();
}

/** Set silent mode
* @return this for easy chaining
*/
GPIOPin *setSilent(bool s = true)
{
silent = s;
return this;
}

private:
Expand All @@ -138,8 +156,25 @@ class GPIOPin : public GPIOPinIf
}
}

/** Called to read from a pin and if the pin has changed state possibly call an ISR, also changes
* the mirrored copy of state that is returned for calls to readPin().
*
* If an ISR is attached to this GPIO, call if the speicifed PinStatus matches */
void refreshState()
{
if (mode != OUTPUT)
{
auto newState = readPinHardware();
if(!silent)
log(SysGPIO, LogDebug, "refreshState(%s, %d)", getName(), newState);
auto oldState = status;
status = newState;
callISR(oldState, newState);
}
}

protected:
/// Read the low level hardware for this pin
/// Return the current the low level hardware for this pin, used to set pin status and (later) trigger ISRs
virtual PinStatus readPinHardware()
{
// default to assume no change
Expand All @@ -149,42 +184,8 @@ class GPIOPin : public GPIOPinIf

class SimGPIOPin : public GPIOPin
{
bool silent = false; // If silent, we won't emit logs

public:
SimGPIOPin(pin_size_t n, String _name) : GPIOPin(n, _name) {}

/** Set silent mode
* @return this for easy chaining
*/
SimGPIOPin *setSilent(bool s = true)
{
silent = s;
return this;
}

protected:
virtual void writePin(PinStatus s)
{
if (!silent)
log(SysGPIO, LogInfo, "SimGPIOPin::writePin(%s, %d, %d)", getName(), getPinNum(), s);
GPIOPin::writePin(s);
}

virtual PinStatus readPin()
{
auto r = GPIOPin::readPin();
if (!silent)
log(SysGPIO, LogInfo, "SimGPIOPin::readPin(%s, %d, %d)", getName(), getPinNum(), r);
return r;
}

virtual void setPinMode(PinMode m)
{
if (!silent)
log(SysGPIO, LogInfo, "SimGPIOPin::setPinMode(%s, %d, %d)", getName(), getPinNum(), m);
GPIOPin::setPinMode(m);
}
};

void gpioInit();
Expand Down
16 changes: 14 additions & 2 deletions cores/portduino/linux/LinuxHardwareSPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ class SimSPIChip : public SPIChip
class LinuxSPIChip : public SPIChip, private PosixFile
{
public:
LinuxSPIChip(const char *name = "/dev/spidev0.0") : PosixFile(name) {}
LinuxSPIChip(const char *name = "/dev/spidev0.0") : PosixFile(name) {
uint8_t mode = SPI_MODE_0;
uint8_t lsb = false;
int status = ioctl(SPI_IOC_WR_MODE, &mode);
assert(status >= 0);
status = ioctl(SPI_IOC_WR_LSB_FIRST, &lsb);
assert(status >= 0);
}

/**
* Do a SPI transaction to the selected device
Expand Down Expand Up @@ -142,12 +149,17 @@ namespace arduino
{
// Do nothing
//printf("beginTransaction\n");
assert(settings.bitOrder == MSBFIRST); // we don't support changing yet
assert(settings.dataMode == SPI_MODE0);
}

void HardwareSPI::endTransaction(void)
{
assert(spiChip);
spiChip->transfer(NULL, NULL, 0, true); // turn off chip select

// FIXME - for the time being I'm not using automatic ship select management
// spiChip->transfer(NULL, NULL, 0, true); // turn off chip select

//printf("endTransaction\n");
}

Expand Down
117 changes: 117 additions & 0 deletions cores/portduino/linux/gpio/LinuxGPIOPin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include "linux/gpio/LinuxGPIOPin.h"
#include <assert.h>
#include <dirent.h>

const char *consumer = "portduino";

static int chip_dir_filter(const struct dirent *entry)
{
bool is_chip;
char *path;
int ret;

ret = asprintf(&path, "/dev/%s", entry->d_name);
if (ret < 0)
return 0;

is_chip = gpiod_is_gpiochip_device(path);
free(path);
return !!is_chip;
}

static struct gpiod_chip *chip_open_by_name(const char *name)
{
struct gpiod_chip *chip;
char *path;
int ret;

ret = asprintf(&path, "/dev/%s", name);
if (ret < 0)
return NULL;

chip = gpiod_chip_open(path);
free(path);

return chip;
}

/**
* Try to find the specified linux gpio line, throw exception if not found
*/
static gpiod_line *getLine(const char *chipLabel, const char *linuxPinName) {

struct dirent **entries;
int num_chips = scandir("/dev/", &entries, chip_dir_filter, alphasort);
assert(num_chips > 0); // FIXME, throw exception

log(SysGPIO, LogDebug, "getLine(%s, %s)", chipLabel, linuxPinName);
for (int i = 0; i < num_chips; i++) {
auto chip = chip_open_by_name(entries[i]->d_name);
if (!chip) {
if (errno == EACCES)
continue; // skip chips we don't have access to

assert(0); // die_perror("unable to open %s", entries[i]->d_name);
} else {
auto label = gpiod_chip_label(chip);
if (strcmp(label, chipLabel) == 0) {
auto offset = gpiod_chip_find_line(chip, linuxPinName);
assert(offset >= 0); // fixme throw
auto line = gpiod_chip_get_line(chip, offset);
assert(line); // fixme throw

struct gpiod_line_request_config request = {
consumer, GPIOD_LINE_REQUEST_DIRECTION_AS_IS, 0};
auto result = gpiod_line_request(line, &request, 0);
assert(result == 0); // fixme throw
return line;
}
}
}
assert(0); // FIXME throw
}

/**
* Create a pin given a linux chip label and pin name
*/
LinuxGPIOPin::LinuxGPIOPin(pin_size_t n, const char *chipLabel,
const char *linuxPinName,
const char *portduinoPinName)
: GPIOPin(n, portduinoPinName ? portduinoPinName : linuxPinName) {
line = getLine(chipLabel, linuxPinName);
}

LinuxGPIOPin::~LinuxGPIOPin() {
gpiod_line_release(line);
// FIXME must call gpiod_chip_unref(chip);
}

/// Read the low level hardware for this pin
PinStatus LinuxGPIOPin::readPinHardware() {
int res = gpiod_line_get_value(line);
assert(res == 0 || res == 1); // FIXME throw instead

// log(SysGPIO, LogDebug, "readPinHardware(%s, %d)", getName(), res);
return (PinStatus) res;
}

void LinuxGPIOPin::writePin(PinStatus s) {
GPIOPin::writePin(s); // update status

int res = gpiod_line_set_value(line, s);
assert(res == 0);
}

void LinuxGPIOPin::setPinMode(PinMode m) {
GPIOPin::setPinMode(m);

// Set direction, if output use the current pinstate as the output value
int res =
gpiod_line_set_config(line,
(m == OUTPUT) ? GPIOD_LINE_REQUEST_DIRECTION_OUTPUT
: GPIOD_LINE_REQUEST_DIRECTION_INPUT,
0, readPin());
assert(res == 0);
}


Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

#include <assert.h>
#include <stdlib.h>
#include <gpiod.h>



namespace modern_linux_gpio {

/**
* Adapts the modern linux GPIO API for use by Portduino
*/
class LinuxGPIOPin : public GPIOPin {
/// Our GPIO line
struct gpiod_line *line;

public:
Expand All @@ -32,8 +32,8 @@ class LinuxGPIOPin : public GPIOPin {
* Constructor
* @param l is the low level linux GPIO pin object
*/
LinuxGPIOPin(pin_size_t n, String _name, struct gpiod_line *l)
: GPIOPin(n, _name), line(l) {}
// LinuxGPIOPin(pin_size_t n, String _name, struct gpiod_line *l)
// : GPIOPin(n, _name), line(l) {}

~LinuxGPIOPin();

Expand All @@ -44,4 +44,4 @@ class LinuxGPIOPin : public GPIOPin {
virtual void setPinMode(PinMode m);
};

} // namespace classic_linux_gpio

0 comments on commit 008e0f6

Please sign in to comment.