gobot
gobot copied to clipboard
Add support for GPIO Character devices on Linux (specifically raspberry pi)
The sysfs interface to gpio is deprecated and will be removed from the kernel sometime in 2020.
The kernel now has character device support for GPIO. Here's the output from some gpio commands on my Raspberry Pi 400:
pi@raspberrypi:/dev $ gpioinfo
gpiochip0 - 54 lines:
line 0: "ID_SDA" unused input active-high
line 1: "ID_SCL" unused input active-high
line 2: "SDA1" unused input active-high
line 3: "SCL1" unused input active-high
line 4: "GPIO_GCLK" unused input active-high
line 5: "GPIO5" unused input active-high
line 6: "GPIO6" unused input active-high
line 7: "SPI_CE1_N" unused input active-high
line 8: "SPI_CE0_N" unused input active-high
line 9: "SPI_MISO" unused input active-high
line 10: "SPI_MOSI" unused input active-high
line 11: "SPI_SCLK" unused input active-high
line 12: "GPIO12" unused input active-high
line 13: "GPIO13" unused input active-high
line 14: "TXD1" unused input active-high
line 15: "RXD1" unused input active-high
line 16: "GPIO16" unused input active-high
line 17: "GPIO17" unused input active-high
line 18: "GPIO18" unused input active-high
line 19: "GPIO19" unused input active-high
line 20: "GPIO20" unused input active-high
line 21: "GPIO21" unused input active-high
line 22: "GPIO22" unused input active-high
line 23: "GPIO23" unused input active-high
line 24: "GPIO24" unused input active-high
line 25: "GPIO25" unused input active-high
line 26: "GPIO26" unused input active-high
line 27: "GPIO27" unused input active-high
line 28: "RGMII_MDIO" unused input active-high
line 29: "RGMIO_MDC" unused input active-high
line 30: "CTS0" unused input active-high
line 31: "RTS0" unused input active-high
line 32: "TXD0" unused input active-high
line 33: "RXD0" unused input active-high
line 34: "SD1_CLK" unused input active-high
line 35: "SD1_CMD" unused input active-high
line 36: "SD1_DATA0" unused input active-high
line 37: "SD1_DATA1" unused input active-high
line 38: "SD1_DATA2" unused input active-high
line 39: "SD1_DATA3" unused input active-high
line 40: "PWM0_MISO" unused input active-high
line 41: "PWM1_MOSI" unused input active-high
line 42: "STATUS_LED_G_CLK" "led0" output active-high [used]
line 43: "SPIFLASH_CE_N" unused input active-high
line 44: "SDA0" unused input active-high
line 45: "SCL0" unused input active-high
line 46: "RGMII_RXCLK" unused input active-high
line 47: "RGMII_RXCTL" unused input active-high
line 48: "RGMII_RXD0" unused input active-high
line 49: "RGMII_RXD1" unused input active-high
line 50: "RGMII_RXD2" unused input active-high
line 51: "RGMII_RXD3" unused input active-high
line 52: "RGMII_TXCLK" unused input active-high
line 53: "RGMII_TXCTL" unused input active-high
gpiochip1 - 8 lines:
line 0: "BT_ON" unused output active-high
line 1: "WL_ON" unused output active-high
line 2: "PWR_LED_OFF" "led1" output active-low [used]
line 3: "GLOBAL_RESET" unused output active-high
line 4: "VDD_SD_IO_SEL" "vdd-sd-io" output active-high [used]
line 5: "CAM_GPIO" "power_ctrl" output active-high [used]
line 6: "SD_PWR_ON" "sd_vcc_reg" output active-high [used]
line 7: "SD_OC_N" unused input active-high
Adding support for the character device would also make it straightforward to support GPIO interrupts on linux
warthog's gpiod module is an example of a project using the character device.
Hi @joeblubaugh , thanks for this hint. It sounds like an important improvement.
The mentioned project looks like a good library which we could use. It seems it has some support for Pi mapping, which means it is expandable also for other devices, hopefully.
Before start implementing the interface we should consider some other libraries.
Found this project as an further example: https://github.com/sgjava/javauio
And some more information as a second start point besides the already mentioned embeddedbits.org by adafruit: https://blog.adafruit.com/2018/11/26/sysfs-is-dead-long-live-libgpiod-libgpiod-for-linux-circuitpython/
After some investigation we have at least this options to implement:
- cgo with direct kernel function calls
- cgo with ioctl
- golang with implementing read/write to character devices
- mixed cgo and golang with ioctl
- golang with CLI calls of "gpiod" package
- golang with full ioctl implementation
- golang with external go-module usage
I have started implementing the option 6, to be most aligned with existing I2C implementation. But the ioctl calls for GPIO's are much more complex. This means:
- "dynamic" calculation of the ioctl signal, depending on the payload size and some defines and macros at kernel level
- there is a deprecated V1 and a V2 version of ioctl
- there are many sub structures for using the various line parameters
The circumvention of the "dynamic ioctl signal calculation" would be possible by using some cgo code with included Kernel headers, but possibly leads to problems with cross compiling. Handling of two different versions and multiple structures is needed also for all the other options.
Using the CLI call in option 5 would add the dependency to "sudo apt install gpiod" and leads to implementing some parsing functionality, which most likely is not very stable and hard to maintain.
As far as I can tell, all those is still implemented in the provided warthog's gpiod module. Therefor we should not reinventing the wheel with our own implementation and just use it for option 7.
@deadprogram this is my conclusion, what do you think about?
@gen2thomas I think option 7 sounds like an excellent plan.
@deadprogram wonderful, then I have not wasted time in the last 3 hours :smile: . I will continue in this direction.
Hi @deadprogram I have finished the implementation and have done small interface changes:
Interface changes:
- setter function "Direction(string) error" removed from "gobot.DigitalPinner", now done by "WithDirection..." option
- getter function "DigitalPin(id string, dir string)" in "gobot.DigitalPinnerProvider" do not change the direction anymore, it provides the pin as is and if not exist it will be created as input, changes can be applied afterwards with "ApplyOptions()", to prevent accidentally direction changes, which could cause board damages. So it is now just "DigitalPin(id string)".
Therefore my suggestion is to increase the main-version on next merge to release branch, according to semver. Also the position of interfaces was changed in the previous PR #885 . Following the golang style - it should be defined at client side and not besides the implementation/provider. Unfortunately, because we check the fulfillment of the interfaces in the unit tests (which IMO is a good idea), the position needs to be at top level to prevent import cycles.
If there is something wrong with that in your opinion, of course I can revert/change this.
Some benchmarks from Tinkerboard testing with a loop for On/Off one pin:
TinkerOS-sysfs (Kernel 4.4.x):
- 40Khz @ 600Mhz
- 70Khz @ 1200Mhz
- 80Khz @ 1600Mhz
armbian-sysfs (Kernel 5.15.74):
- 50Khz @ 600Mhz
- 95Khz @ 1200Mhz
- 125Khz @ 1600Mhz
armbian-cdev (same system as above)
- 75Khz @ 600Mhz
- 125Khz @ 1200Mhz
- 160Khz @ 1600Mhz
Notes: The sysfs on cdev-Kernels is just a wrapper to cdev, so the result is more or less expected. Although this is an improvement, please have in mind, that there are unpredicted interruptions on output toggling for ~1ms (I have seen for Tinkerboard), due to this are no RT kernels. This happens independent of used gpio implementation and Kernel version.
part of release v2.0.0