From fad6633ec6f34db8f7e506557a6ff73f751d7cf5 Mon Sep 17 00:00:00 2001 From: Wojciech Porczyk Date: Fri, 13 Mar 2026 18:18:45 +0100 Subject: [PATCH] GPIO: Add support for active low MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “Active low” pins have inverted state, i.e. they're “true” or 1 when they're pulled to the ground and “false”/0 when left floating around VCC. This is very common configuration when building interfaces with semiconductors (like optocouplers), less so with relays where the polarity mostly doesn't matter. With varying polarization it is best to leave the choice of bias to the end user. Signed-off-by: Wojciech Porczyk --- plugin/gpio_linux.go | 35 ++++++++++++-- plugin/gpiobias.go | 11 +++++ plugin/gpiobias_enumer.go | 98 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 plugin/gpiobias.go create mode 100644 plugin/gpiobias_enumer.go diff --git a/plugin/gpio_linux.go b/plugin/gpio_linux.go index 404665f2ecb..5eaae53fe80 100644 --- a/plugin/gpio_linux.go +++ b/plugin/gpio_linux.go @@ -24,11 +24,15 @@ type gpio struct { // NewGpioPluginFromConfig creates a GPIO provider func NewGpioPluginFromConfig(ctx context.Context, other map[string]any) (Plugin, error) { cc := struct { - Function GpioType - Pin int - Chip string + Function GpioType + Pin int + ActiveLow bool + Bias GpioBias + Chip string }{ - Chip: "gpiochip0", + ActiveLow: false, + Bias: -1, + Chip: "gpiochip0", } if err := util.DecodeOther(other, &cc); err != nil { @@ -38,13 +42,34 @@ func NewGpioPluginFromConfig(ctx context.Context, other map[string]any) (Plugin, var opts []gpiocdev.LineReqOption switch cc.Function { case GpioTypeRead: - opts = append(opts, gpiocdev.AsInput, gpiocdev.WithPullUp) + opts = append(opts, gpiocdev.AsInput) case GpioTypeWrite: opts = append(opts, gpiocdev.AsOutput(0)) default: return nil, fmt.Errorf("invalid type: %s", cc.Function) } + switch cc.Bias { + case -1: + if cc.Function == GpioTypeRead { + opts = append(opts, gpiocdev.WithPullUp) + } + case GpioBiasAsIs: + opts = append(opts, gpiocdev.WithBiasAsIs) + case GpioBiasDisabled: + opts = append(opts, gpiocdev.WithBiasDisabled) + case GpioBiasPullUp: + opts = append(opts, gpiocdev.WithPullUp) + case GpioBiasPullDown: + opts = append(opts, gpiocdev.WithPullDown) + default: + return nil, fmt.Errorf("invalid bias: %s", cc.Bias) + } + + if cc.ActiveLow { + opts = append(opts, gpiocdev.AsActiveLow) + } + line, err := gpiocdev.RequestLine(cc.Chip, cc.Pin, opts...) if err != nil { return nil, fmt.Errorf("failed to open GPIO: %w", err) diff --git a/plugin/gpiobias.go b/plugin/gpiobias.go new file mode 100644 index 00000000000..c7b27f622af --- /dev/null +++ b/plugin/gpiobias.go @@ -0,0 +1,11 @@ +package plugin + +type GpioBias int + +//go:generate go tool enumer -type GpioBias -trimprefix GpioBias -transform=kebab -text +const ( + GpioBiasAsIs GpioBias = iota + GpioBiasDisabled + GpioBiasPullUp + GpioBiasPullDown +) diff --git a/plugin/gpiobias_enumer.go b/plugin/gpiobias_enumer.go new file mode 100644 index 00000000000..962c00432df --- /dev/null +++ b/plugin/gpiobias_enumer.go @@ -0,0 +1,98 @@ +// Code generated by "enumer -type GpioBias -trimprefix GpioBias -transform=kebab -text"; DO NOT EDIT. + +package plugin + +import ( + "fmt" + "strings" +) + +const _GpioBiasName = "as-isdisabledpull-uppull-down" + +var _GpioBiasIndex = [...]uint8{0, 5, 13, 20, 29} + +const _GpioBiasLowerName = "as-isdisabledpull-uppull-down" + +func (i GpioBias) String() string { + if i < 0 || i >= GpioBias(len(_GpioBiasIndex)-1) { + return fmt.Sprintf("GpioBias(%d)", i) + } + return _GpioBiasName[_GpioBiasIndex[i]:_GpioBiasIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _GpioBiasNoOp() { + var x [1]struct{} + _ = x[GpioBiasAsIs-(0)] + _ = x[GpioBiasDisabled-(1)] + _ = x[GpioBiasPullUp-(2)] + _ = x[GpioBiasPullDown-(3)] +} + +var _GpioBiasValues = []GpioBias{GpioBiasAsIs, GpioBiasDisabled, GpioBiasPullUp, GpioBiasPullDown} + +var _GpioBiasNameToValueMap = map[string]GpioBias{ + _GpioBiasName[0:5]: GpioBiasAsIs, + _GpioBiasLowerName[0:5]: GpioBiasAsIs, + _GpioBiasName[5:13]: GpioBiasDisabled, + _GpioBiasLowerName[5:13]: GpioBiasDisabled, + _GpioBiasName[13:20]: GpioBiasPullUp, + _GpioBiasLowerName[13:20]: GpioBiasPullUp, + _GpioBiasName[20:29]: GpioBiasPullDown, + _GpioBiasLowerName[20:29]: GpioBiasPullDown, +} + +var _GpioBiasNames = []string{ + _GpioBiasName[0:5], + _GpioBiasName[5:13], + _GpioBiasName[13:20], + _GpioBiasName[20:29], +} + +// GpioBiasString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func GpioBiasString(s string) (GpioBias, error) { + if val, ok := _GpioBiasNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _GpioBiasNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to GpioBias values", s) +} + +// GpioBiasValues returns all values of the enum +func GpioBiasValues() []GpioBias { + return _GpioBiasValues +} + +// GpioBiasStrings returns a slice of all String values of the enum +func GpioBiasStrings() []string { + strs := make([]string, len(_GpioBiasNames)) + copy(strs, _GpioBiasNames) + return strs +} + +// IsAGpioBias returns "true" if the value is listed in the enum definition. "false" otherwise +func (i GpioBias) IsAGpioBias() bool { + for _, v := range _GpioBiasValues { + if i == v { + return true + } + } + return false +} + +// MarshalText implements the encoding.TextMarshaler interface for GpioBias +func (i GpioBias) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for GpioBias +func (i *GpioBias) UnmarshalText(text []byte) error { + var err error + *i, err = GpioBiasString(string(text)) + return err +}