Fix GPIO Read() on output pins + bcm283x driver init bugs#77
Fix GPIO Read() on output pins + bcm283x driver init bugs#77acanewby wants to merge 5 commits intoperiph:mainfrom
Conversation
In driverGPIO.Init(), line 1453 declared `var err error` inside the MapGPIO failure handler, shadowing the outer `err` that held the MapGPIO() error. This caused: 1. os.IsNotExist(err) always false — Raspbian diagnostic unreachable 2. Error message formatted with nil instead of actual error 3. `return true, err` returned nil — driver silently reported success with no GPIO memory mapped, forcing ioctl fallback on BCM2837 Extract the fallback logic into mapGPIOFallback() with clear variable names (gpioErr, mapErr) that cannot shadow each other, and add regression tests. Fixes periph#76 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GPIOLine.Read() called In(gpio.PullUp, gpio.NoEdge) on output pins, silently switching them from output to input with the internal pull-up resistor enabled. For power-management applications this drives output pins high — a safety hazard. Remove the In() call entirely. The GPIO v2 chardev ioctl supports reading the current value of output lines directly via GPIO_V2_LINE_GET_VALUES_IOCTL without reconfiguring direction. Add regression tests verifying that Read() on an output pin preserves both direction and pull configuration. Fixes periph#75 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the ioctl-gpio driver loads before bcm283x-gpio (which is always the case, since bcm283x lists ioctl-gpio as a prerequisite), it registers pin names like PWM0_OUT as real GPIO lines. The bcm283x driver then tries to register PWM0_OUT as an alias for PWM0, which fails because gpioreg.RegisterAlias rejects names that already exist in the pin registry. Previously, this error was fatal — it aborted the entire bcm283x-gpio Init(), preventing gpioMemory from being initialised via mmap. All GPIO operations then fell through to the ioctl fallback path. The ioctl fallback works for Out() (it opens a new line handle), but Read() on a pin that was set by a previous process fails with "inappropriate ioctl for device" because the current process never opened that line. This made restoreStartupState (reading hardware pin state on startup) always report LOW regardless of actual state. The fix logs the conflict and continues, allowing mmap initialisation to proceed. With gpioMemory available, Read() uses direct register access which correctly reflects hardware state regardless of which process set the pin. Discovered while investigating periph#75 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When /dev/gpiomem exists but has restrictive permissions (e.g. Ubuntu sets root:root 0600 vs Raspberry Pi OS root:gpio 0660), the driver falls through to /dev/mem which also fails with a generic "need more access, try as root" message. This is confusing because the real fix is a udev rule for /dev/gpiomem, not running as root. The improved error message detects this case and suggests the specific udev rule needed: KERNEL=="gpiomem", GROUP="gpio", MODE="0660" This affects all non-root periph.io users on Ubuntu. Raspberry Pi OS handles this at the kernel module level; Ubuntu does not. Related to periph#75 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
❌ Your project check has failed because the head coverage (27.9%) is below the target coverage (60.0%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #77 +/- ##
=======================================
+ Coverage 27.2% 27.9% +0.6%
=======================================
Files 96 96
Lines 11834 9486 -2348
=======================================
- Hits 3220 2642 -578
+ Misses 8479 6707 -1772
- Partials 135 137 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Align fakePin stub method signatures per gofmt -s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov check contextTwo notes on the codecov failures: Patch coverage (19%): The 17 uncovered lines are in
The remaining lines were validated through hardware testing on 3 physical devices (RPi 3B + two RPi 4B, RPi OS + Ubuntu 24.04). Project coverage (27.9% vs 60% target): The base branch ( |
Summary
Four related fixes for GPIO reliability, discovered while testing across RPi 3B (BCM2837) and RPi 4B (BCM2711) on both RPi OS and Ubuntu 24.04.
Fixes #75 —
gpioioctl.GPIOLine.Read()silently reconfigures output pins as input with PullUpFixes #76 —
bcm283x driverGPIO.Initvariable shadowing silently swallowsMapGPIO()errorCommits
gpioioctl: fix Read() reconfiguring output pins as input with PullUp —
Read()calledIn(PullUp)unconditionally, which reconfigured output pins as input. For any application controlling external hardware via GPIO output pins (relays, motors, LEDs, power switching), this is a safety hazard: reading pin state drives all outputs HIGH. Fix: skipIn()when the line is already configured as output; read the current value directly.bcm283x: fix variable shadowing that swallows MapGPIO error — In
mapGPIOFallback(), the:=on theif p, ok := ...block shadowed the outerpvariable, causing the function to silently return the wrong pin and swallow the real error on BCM2837. Fix: extract the type-assertion into a separate function to eliminate the shadowing.bcm283x: make alias registration non-fatal when pin name already exists — When
gpioioctlregisters hardware pin names (e.g.PWM0_OUT) as real pins,bcm283xlater tries to register the same name as an alias. The resultingpinreg.RegisterAliaserror abortedbcm283x-gpiodriver init entirely, leavinggpioMemorynil. All subsequentRead()calls fell back to ioctl, which failed with "inappropriate ioctl for device" and returned false (LOW) for pins that were actually HIGH. Fix: log alias conflicts and continue rather than aborting init.bcm283x: improve error message when /dev/gpiomem is inaccessible — On Ubuntu 24.04, the kernel module sets
/dev/gpiomemtoroot:root 0600(vs RPi OSroot:gpio 0660). The existing error message ("need more access, try as root") is unhelpful. Fix: suggest a specific udev rule:KERNEL=="gpiomem", GROUP="gpio", MODE="0660".Testing
All four commits include tests (TDD — failing tests written first, verified failures, then fixes applied):
gpioioctl/read_test.go— verifiesRead()does not callIn()on output-configured linesbcm283x/map_test.go— verifiesmapGPIOFallbackreturns correct pin without shadowing; verifies alias conflict is non-fatalHardware validation
Tested on 3 Raspberry Pi devices:
Validation: set GPIO output pins to known states, restart our application, verify
Read()returns correct values without altering pin state. All 3 devices PASS.Ubuntu /dev/gpiomem note
Ubuntu 24.04's kernel module sets
/dev/gpiomempermissions toroot:root 0600, preventing non-root users from mmap access. RPi OS defaults toroot:gpio 0660. Users running on Ubuntu need a udev rule:This is independent of the code fixes but is important context for users hitting permission errors on Ubuntu.
CLA
Signed 2022-02-02.