Using GPIOs without Using sudo

That’s the section heading on page 302 of Derek Molloy’s second edition “Exploring Beaglebone”

I found the section lacking in information on exactly how to do this. A web search showed up the following pages.

https://github.com/adafruit/adafruit-beaglebone-io-python/issues/137

https://linuxize.com/post/how-to-add-user-to-group-in-linux/

https://github.com/cnobile2012/RobotControl/tree/master/contrib

I’m still not sure of the actual process with this revision of the OS.

debian@ebb:~/lazarus$ uname -a

Linux ebb 4.14.108-ti-r136 #1stretch SMP PREEMPT Mon Jun 8 15:38:30 UTC 2020 armv7l GNU/Linux

Robert Nelson mentions 4.11+ as having support for “it” so is any of what is in the first link needed?

“it’s created after you export 0 (or 1) in the pwm system node. only v4.11.x+ has the udev patch for permissions. (didn’t want to break older userspace, we still need to fix bonescript before i backport that patch to v4.4.x/v4.9.x)”

I don’t have a

/etc/udev/rules.d/80-gpio-permissions.rules

So I’m thinking I do have to create that file with

KERNEL==“gpio*”, SUBSYSTEM==“gpio”, ACTION==“add”, PROGRAM="/usr/local/bin/udev-gpio-permissions.sh"

And then create the shell script as outlined but with user debian in group gpio?

Eg:

chown -R nick:digital /sys/devices/gpio

becomes

chown -R debian:gpio /sys/devices/gpio

That’s where it starts to fall apart for me as far as instructions go. There’s an assumption of knowledge about something except I don’t know what that is.

debian@ebb:~/lazarus$ ls -al /etc/udev/rules.d/

total 84

drwxr-xr-x 2 root root 4096 Apr 24 17:05 .

drwxr-xr-x 4 root root 4096 Jul 14 2020 …

-rw-r–r-- 1 root root 372 Jul 10 2020 10-of-symlink.rules

-rw-r–r-- 1 root root 48 Oct 7 2018 50-hidraw.rules

-rw-r–r-- 1 root root 44 Oct 7 2018 50-spi.rules

-rw-r–r-- 1 root root 142 Oct 7 2018 60-omap-tty.rules

-rw-r–r-- 1 root root 921 Jul 10 2020 80-eeprom-noroot.rules

-rw-r–r-- 1 root root 569 Feb 25 06:10 80-gpio-noroot.rules

-rw-r–r-- 1 root root 308 Feb 25 06:10 80-i2c-noroot.rules

-rw-r–r-- 1 root root 2113 Feb 25 06:10 81-pwm-noroot.rules

-rw-r–r-- 1 root root 339 Feb 1 2019 82-gpio-config-pin.rules

-rw-r–r-- 1 root root 359 Jul 10 2020 83-eqep-noroot.rules

-rw-r–r-- 1 root root 509 Aug 15 2018 84-gpio-noroot.rules

-rw-r–r-- 1 root root 188 Aug 15 2018 85-gpio-noroot.rules

-rw-r–r-- 1 root root 1414 Jul 10 2020 86-remoteproc-noroot.rules

-rw-r–r-- 1 root root 352 Jul 10 2020 86-rpmsg-noroot.rules

-rw-r–r-- 1 root root 218 Jul 10 2020 87-iio-noroot.rules

-rw-r–r-- 1 root root 308 Feb 25 06:10 88-leds-noroot.rules

-rw-r–r-- 1 root root 855 Oct 7 2018 beagle-tester.rules

-rw-r–r-- 1 root root 97 Oct 7 2018 tisdk.rules

-rw-r–r-- 1 root root 108 Oct 7 2018 uio.rules

debian@ebb:~/lazarus$

From

debian@ebb:~/lazarus$ more /etc/group

I get:

gpio:x:999:debian,node-red,john

What else is required so I don’t have to use sudo for C, Lazarus or python programs to access GPIO?

debian@ebb:~/lazarus$ cat /etc/udev/rules.d/80-gpio-noroot.rules

/etc/udev/rules.d/80-gpio-noroot.rules

Suspect I'm not really going to be of much help -- but a few
comments...

I'm still not sure of the actual process with this revision of the OS.
debian@ebb:~/lazarus$ uname -a
Linux ebb 4.14.108-ti-r136 #1stretch SMP PREEMPT Mon Jun 8 15:38:30 UTC 2020 armv7l GNU/Linux

  Stretch is getting a bit long in the tooth -- is there some reason you
need to stay on it (Debian organization is in the midst of finalizing the
replacement for Buster!).

  You also appear to have changed your Beagle's host name to match that
Malloy used for his -- the initials of "e/xploring b/eagleb/one". Just an
observation.

And then create the shell script as outlined but with user debian in group gpio?
Eg:
chown -R nick:digital /sys/devices/gpio

becomes
chown -R debian:gpio /sys/devices/gpio

  I'm somewhat surprised at that. My understanding is that adding the
"debian" user to the GROUP gpio should allow that user to access anything
that is part of the gpio group, using the group permissions rather than
owner permissions -- so should not need to be changed to debian as owner.

  Unfortunately, I can't state anything specific. Buster doesn't seem to
have the same layout.

debian@beaglebone:~$ uname -a
Linux beaglebone 4.19.94-ti-r48 #1buster SMP PREEMPT Wed Aug 19 17:38:55
UTC 2020 armv7l GNU/Linux
debian@beaglebone:~$ ls /sys/devices
armv7_cortex_a8 kprobe soc0 system uprobe
breakpoint platform software tracepoint virtual
debian@beaglebone:~$

NOTE: NO /sys/devices/gpio!

debian@beaglebone:~$ ls -l /dev/gpiochip*
crw-rw---- 1 root gpio 254, 0 Apr 30 20:46 /dev/gpiochip0
crw-rw---- 1 root gpio 254, 1 Apr 30 20:46 /dev/gpiochip1
crw-rw---- 1 root gpio 254, 2 May 10 14:51 /dev/gpiochip2
crw-rw---- 1 root gpio 254, 3 May 10 14:51 /dev/gpiochip3
debian@beaglebone:~$

  If I interpret the permissions, owner (root) and group (gpio) both have
R/W, so if debian is added to the group...

debian@beaglebone:~$ ls -l /sys/class/gpio
total 0
--w--w---- 1 root gpio 4096 Apr 30 20:46 export
lrwxrwxrwx 1 root gpio 0 Apr 30 20:46 gpio10 ->
../../devices/platform/ocp/44e07000.gpio/gpiochip0/gpio/gpio10
lrwxrwxrwx 1 root gpio 0 Apr 30 20:46 gpio11 ->
../../devices/platform/ocp/44e07000.gpio/gpiochip0/gpio/gpio11

... and those (links) are open to everyone, although...

debian@beaglebone:~$ ls -l
/sys/devices/platform/ocp/44e07000.gpio/gpiochip0/gpio/gpio10
total 0
-rwxrwx--- 1 root gpio 4096 Apr 30 20:46 active_low
lrwxrwxrwx 1 root gpio 0 Apr 30 20:46 device -> ../../../gpiochip0
-rwxrwx--- 1 root gpio 4096 Apr 30 20:46 direction
-rwxrwx--- 1 root gpio 4096 Apr 30 20:46 edge
-rwxrwx--- 1 root gpio 4096 Apr 30 20:46 label
drwxrwx--- 2 root gpio 0 Apr 30 20:46 power
lrwxrwxrwx 1 root gpio 0 Apr 30 20:46 subsystem ->
../../../../../../../class/gpio
-rwxrwx--- 1 root gpio 4096 Apr 30 20:46 uevent
-rwxrwx--- 1 root gpio 4096 Apr 30 20:46 value
debian@beaglebone:~$

... what they link to is back to root:gpio without everyone.

First of all thank you for responding. Comments below.

From: beagleboard@googlegroups.com [mailto:beagleboard@googlegroups.com] On Behalf Of Dennis Lee Bieber

  Suspect I'm not really going to be of much help -- but a few
comments...

>I'm still not sure of the actual process with this revision of the OS.
>debian@ebb:~/lazarus$ uname -a
>Linux ebb 4.14.108-ti-r136 #1stretch SMP PREEMPT Mon Jun 8 15:38:30 UTC 2020 armv7l GNU/Linux
>
  Stretch is getting a bit long in the tooth -- is there some reason you
need to stay on it (Debian organization is in the midst of finalizing the
replacement for Buster!).

It's been a love/hate relationship with the Beagle. I have about 5 books that are useless because the OS changed so the experiments don't work. Regardless of how wonderful the newest revision of the BBB OS is, if changes don't match the documentation that's out there then the changes are useless. It's why I had to buy the second edition of Derek Molloy's book. No more $SLOTS.

  You also appear to have changed your Beagle's host name to match that
Malloy used for his -- the initials of "e/xploring b/eagleb/one". Just an
observation.

Good observation. After fighting the various books, web pages etc. I decided I'd start at page 1 and work my way through the second edition so that what was written matched what I was doing. So if he's using 4.14 so am I.

>
>And then create the shell script as outlined but with user debian in group gpio?
>Eg:
>chown -R nick:digital /sys/devices/gpio
>
>becomes
>chown -R debian:gpio /sys/devices/gpio
>
  I'm somewhat surprised at that. My understanding is that adding the
"debian" user to the GROUP gpio should allow that user to access anything
that is part of the gpio group, using the group permissions rather than
owner permissions -- so should not need to be changed to debian as owner.

  Unfortunately, I can't state anything specific. Buster doesn't seem to
have the same layout.

And now we run into the real problem. The web pages out there are in many cases useless because they might refer to older versions of the OS or newer versions.

What started this particular stream of discovery was that I jumped ahead a bit rather than continue page by page. My desire is to create the same library of C++ functions for Lazarus (Free Pascal) and ultimately update the sockets.pp code to support socketCAN which it currently doesn't.

I've written a few utilities already that use the CANUSB from Lawicel and the same source code compiled on each host runs on Windows, LinuxCNC, Pi3 and the Beagle talking to the CANUSB and an active CANopen project.

To deal with CAN bus on the Pi3/Pi4 requires access to the SPI bus for the MCP2515. On the Beagle it's the on chip CAN device. And if I want to talk to sensors like I2C or One-Wire plus inexpensive LCD displays I need access to the hardware.

So I installed the PXL library and that's where I ran into the next roadblock. First you can't, from within the IDE and debugging, work with SPI bus without running Lazarus as root. Or it just won't work with the /sys/class/gpio folders. And that's the reason for wanting to free up access to the gpio.

The SPI bus ADAfruit application for a 320x240 display written in Python runs properly rendering LENNA.JPG onto the LCD display. The key outputs are the DC and RESET which are on gpio48 and gpio60. An "ls" of the /sys/class/gpio folder shows on start up those two are not visible.

It's possible to use:
$ sudo echo 48 > /sys/class/gpio/export
to create the gpio48 folder and as super user the VXP library can do that but then it fails on the write to SPI.

To deal with CAN bus on the Pi3/Pi4 requires access to the SPI bus for the MCP2515. On the Beagle it's the on chip CAN device. And if I want to talk to sensors like I2C or One-Wire plus inexpensive LCD displays I need access to the hardware.

So I installed the PXL library and that's where I ran into the next roadblock. First you can't, from within the IDE and debugging, work with SPI bus without running Lazarus as root. Or it just won't work with the /sys/class/gpio folders. And that's the reason for wanting to free up access to the gpio.

The SPI bus ADAfruit application for a 320x240 display written in Python runs properly rendering LENNA.JPG onto the LCD display. The key outputs are the DC and RESET which are on gpio48 and gpio60. An "ls" of the /sys/class/gpio folder shows on start up those two are not visible.

  SPI and I2C rely upon a different PINMUX configuration than GPIO...
GPIO is pretty much all "mode 7". SPI0 pins are "mode 0" and SPI1 pins are
"mode 3".
http://www.ofitselfso.com/BeagleNotes/BeagleboneBlackPinMuxModes.php

It's possible to use:
$ sudo echo 48 > /sys/class/gpio/export

  Really?

debian@beaglebone:~$ sudo echo 48 > /sys/class/gpio/export
[sudo] password for debian:
echo: write error: Operation not permitted

  To my knowledge, the redirection part is still done as the debian user,
only the echo is being done by sudo.

debian@beaglebone:~$ sudo su
root@beaglebone:/home/debian# echo 48 > /sys/class/gpio/export

to create the gpio48 folder and as super user the VXP library can do that but then it fails on the write to SPI.

  SPI0 appears on P9_17, _18, _21, and _22 (raw GPIO # 5, 4, 3, 2, aka
gpio0_5, ...). SPI1 are on P9_28, _29, _30, _31 (raw GPIO # 113, 111, 112,
110, aka gpio3_17, _15, _16, _14).

  GPIO # 48 (gpio1_16) on P9_15 has no modes for SPI.

debian@beaglebone:~$ ls -l /sys/bus/spi/devices
total 0
lrwxrwxrwx 1 root root 0 Dec 31 1999 spi0.0 ->
../../../devices/platform/ocp/48030000.spi/spi_master/spi0/spi0.0
lrwxrwxrwx 1 root root 0 Dec 31 1999 spi0.1 ->
../../../devices/platform/ocp/48030000.spi/spi_master/spi0/spi0.1
lrwxrwxrwx 1 root root 0 Dec 31 1999 spi1.0 ->
../../../devices/platform/ocp/481a0000.spi/spi_master/spi1/spi1.0
lrwxrwxrwx 1 root root 0 Dec 31 1999 spi1.1 ->
../../../devices/platform/ocp/481a0000.spi/spi_master/spi1/spi1.1
debian@beaglebone:~$

debian@beaglebone:~$ ls -l /sys/devices/platform/ocp/48030000.spi/
total 0
lrwxrwxrwx 1 root gpio 0 May 10 15:42 driver ->
../../../../bus/platform/drivers/omap2_mcspi
-rw-rw-r-- 1 root gpio 4096 May 10 15:42 driver_override
-r--r--r-- 1 root gpio 4096 May 10 15:42 modalias
lrwxrwxrwx 1 root gpio 0 May 10 15:42 of_node ->
../../../../firmware/devicetree/base/ocp/spi@48030000
drwxrwxr-x 2 root gpio 0 May 10 15:42 power
drwxrwxr-x 3 root gpio 0 May 10 15:42 spi_master
lrwxrwxrwx 1 root gpio 0 May 10 15:42 subsystem ->
../../../../bus/platform
-rw-rw-r-- 1 root gpio 4096 May 10 15:42 uevent
debian@beaglebone:~$

debian@beaglebone:~$ ls -l
/sys/devices/platform/ocp/48030000.spi/spi_master/spi0
total 0
lrwxrwxrwx 1 root gpio 0 May 10 15:42 device -> ../../../48030000.spi
lrwxrwxrwx 1 root gpio 0 May 10 15:42 of_node ->
../../../../../../firmware/devicetree/base/ocp/spi@48030000
drwxrwxr-x 2 root gpio 0 May 10 15:42 power
drwxrwxr-x 5 root gpio 0 May 10 15:42 spi0.0
drwxrwxr-x 5 root gpio 0 May 10 15:42 spi0.1
drwxrwxr-x 2 root gpio 0 May 10 15:42 statistics
lrwxrwxrwx 1 root gpio 0 May 10 15:42 subsystem ->
../../../../../../class/spi_master
-rw-rw-r-- 1 root gpio 4096 May 10 15:42 uevent
debian@beaglebone:~$

  NOTE: there are two config-pin in Buster, maybe even Stretch. The one
you get if you just enter "config-pin" is a compiled executable with some
limitations (if there is no pinmux file found it objects). I'm using the
older PERL script version which has a few more capabilities.

debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -i
p9_15
Pin name: P9_15
Function if no cape loaded: gpio
Function if cape loaded: default gpio gpio_pu gpio_pd gpio_input pwm
Function information: gpio1_16 default gpio1_16 gpio1_16 gpio1_16 gpio1_16
ehrpwm1_tripzone_input
Kernel GPIO id: 48
PRU GPIO id: 80
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -i
p9_17
Pin name: P9_17
Function if no cape loaded: gpio
Function if cape loaded: default gpio gpio_pu gpio_pd gpio_input spi_cs i2c
pwm pru_uart
Function information: gpio0_5 default gpio0_5 gpio0_5 gpio0_5 gpio0_5
spi0_cs0 i2c1_scl ehrpwm0_synci pru_uart
Kernel GPIO id: 5
PRU GPIO id: 37
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -q
p9_15
P9_15 Mode: default Direction: in Value: 1
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -q
p9_17
P9_17 Mode: default Direction: in Value: 1
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -l
p9_15
default gpio gpio_pu gpio_pd gpio_input pwm
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -l
p9_17
default gpio gpio_pu gpio_pd gpio_input spi_cs i2c pwm pru_uart
debian@beaglebone:~$

=============================================================================
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/DisplaySPI$ sudo ./DisplaySPI
[sudo] password for debian:
An unhandled exception occurred at $00032BB0:
                                            ESysfsSPITransfer: Cannot transfer <1> data byte(s) through SPI bus.
                                  $00032BB0 TSYSFSSPI__TRANSFER, line 247 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.SPI.pas
                                                  $00032AC4 TSYSFSSPI__WRITE, line 228 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.SPI.pas

  Since you haven't shown the code, I don't know what pins that call is
attempting to access. Especially as you earlier mention gpio 48 -- which
from what I can tell does not have SPI features. For the others, you may
need to pin-mux to activate SPI -- the default may be for them to be GPIO..

debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin p9_17
spi_cs
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin p9_18
spi
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin p9_22
spi_sclk
debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin -q
p9_18
P9_18 Mode: spi
debian@beaglebone:~$

  Using the other SPI device is trickier -- I believe a device tree
overlay needs to be specified in /boot/uEnv.txt. Config-pin fails

debian@beaglebone:~$
/opt/source/bb.org-overlays/tools/beaglebone-universal-io/config-pin p9_31
spi_sclk
P9_31 pinmux file not found!
bash: /sys/devices/platform/ocp/ocp*P9_31_pinmux/state: No such file or
directory
Cannot write pinmux file: /sys/devices/platform/ocp/ocp*P9_31_pinmux/state

debian@beaglebone:~$ ls /opt/source/bb.org-overlays/src/arm/BB*SPI*
/opt/source/bb.org-overlays/src/arm/BBBLUE-GP0-SPI-00A0.dts
/opt/source/bb.org-overlays/src/arm/BB-LCD-ADAFRUIT-18-SPI1-00A0.dts
/opt/source/bb.org-overlays/src/arm/BB-SPI0-ADS8688-0A00.dts
/opt/source/bb.org-overlays/src/arm/BB-SPI0-AT86RF233-00A0.dts
/opt/source/bb.org-overlays/src/arm/BB-SPI0-MCP23S08-00A0.dts
/opt/source/bb.org-overlays/src/arm/BB-SPI0-MCP3008-00A0.dts
/opt/source/bb.org-overlays/src/arm/BB-SPIDEV0-00A0.dts
/opt/source/bb.org-overlays/src/arm/BB-SPIDEV1-00A0.dts

debian@beaglebone:~$ sudo echo 48 > /sys/class/gpio/export
[sudo] password for debian:
echo: write error: Operation not permitted

To my knowledge, the redirection part is still done as the debian user,
only the echo is being done by sudo.

you're absolutely correct, but there's more than one way to do that.
for example,
    sudo sh -c "echo 48 > /sys/class/gpio/export"
would do all the stuff as root.

debian@beaglebone:~$ sudo su

*shudder* *twitch*

From: beagleboard@googlegroups.com [mailto:beagleboard@googlegroups.com] On Behalf Of Dennis Lee Bieber

>The SPI bus ADAfruit application for a 320x240 display written in Python runs properly rendering LENNA.JPG onto the LCD display.

The key outputs are the DC and RESET which are on gpio48 and gpio60. An "ls" of the /sys/class/gpio folder shows on start up those
two are not visible.
>

  SPI and I2C rely upon a different PINMUX configuration than GPIO...
GPIO is pretty much all "mode 7". SPI0 pins are "mode 0" and SPI1 pins are
"mode 3".
http://www.ofitselfso.com/BeagleNotes/BeagleboneBlackPinMuxModes.php

Hi Dennis,
I think you missed the above line in my initial posting where the signals DC and RESET are used for controlling the LCD display. They have absolutely nothing to do with SPI.

>to create the gpio48 folder and as super user the VXP library can do that but then it fails on the write to SPI.

In fact if I run the program without running it as super user the failure messages that occur happen because gpio48 and gpio60 cannot be created due to access rights.

The PXL library appears to be designed and tested with a BBB (based on the photos and wiring diagram) but because the library is older, the moving target (AKA BeagleBone OS) is likely the culprit for it not working.

Here's the constructor which shows I'm trying to get to spidev1.0 and the PinDC and PinRST are gpio48 and gpio60 respectively.

OK, I am not using a current kernel, so can’t really address the issues with group settings. But, here’s one thing that will work:
if you have a user program xyz, do :
sudo chown root:root xyz
sudo chmod u+s xyz

TYhis makes the program owned by rootm, and uses the privileges of root when that program is run. You DO need root privileges to MAKE these settings, but then the program will run and be able to access the GPIO without the user needing the privileges.

Making the user a member of the right group should also make any program run by that user have the necessary privilege. It depends on what is in the /etc/group file as to what group you need to be a member of.

Jon

Thanks.

It’s easy enough of course to sudo user_program and then enter password to run it but a pain. Perhaps this will fix that issue

But the more important issue is whether I can get the Lazarus IDE along with the debugger (separate program I believe) to run elevated as super user.

I need to be able to set break points and look at parameters before the system calls for sending data to the SPI port. But since the code doesn’t make it past the gpio48 and gpio60 control (due to permissions) I don’t even get a chance to see if the parameters are correctly formed for the system call.

If the attached zip comes through you can see a simple program that just toggles gpio48 attached to an LED. The memo component shows the results of the calls to the create/open/close links for the gpio access. This small application is the test for reconfiguring the debian user to be part of the gpio group for access. And once it runs from the command line or from the IDE without sudo then I know I’ve succeeded in that part.

It’s likely the same issue is happening with access to the SPI1.0 although you’d think running the bigger program that does SPI access elevated with sudo wouldn’t have that problem.

To try this program do sudo apt install lazarus.

John

io_test.zip (126 KB)

Okay... The following will be a bit of a rambling mess as I can't find
a clean way to separate all the file searching I'm doing... I tried to mark
what seem crucial points with

I think not... Not for an experiment at least. No offence intended, but
it wants to install over 1GB of stuff -- which is a 33% increase in the
content of my uSD card!, and would require me to attach a keyboard and
display to work with the GUI.

  Now, a simple command line only program would be a different matter
(does FPC support command line only? -- FPC may be small enough to justify
installing for testing via SSH, but not Lazarus... *** nope, just FPC is
most of the load, 900+MB)

  An example using GNAT (Ada). There is no (or I don't know of one)
library for GPIO access in GNAT Ada, so everything is SysFS I/O. I had to
set the pin number to string as the simple integer'image(pin) was
generating leading spaces, which resulted in invalid file name. Everything
is in one (117 line) file -- no nasty forms, etc.

debian@beaglebone:~/BBB_IO$ ls
main.adb
debian@beaglebone:~/BBB_IO$ cat main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Main is

   Sysfs_Path : constant String := "/sys/class/gpio";
   GPIO_Pin : constant String := "48";
   Pin_Value : Integer;

   procedure Export_Pin (Pin : in String) is

      Export_File : File_Type;
      Export_Path : constant String := Sysfs_Path & "/export";

   begin
      Put_Line ("Opening " & Export_Path);
      Open (Export_File, Mode => Out_File, Name => Export_Path);

      Put_Line ("Writing pin number: " & Pin);
      Put (Export_File, Pin);

      Put_Line ("Closing " & Export_Path);
      Close (Export_File);

      New_Line;

   end Export_Pin;

   procedure Set_Direction (Pin : in String; Direction : in String) is
      Pin_File : File_Type;
      Pin_Path : constant String := Sysfs_Path & "/gpio" & Pin &
"/direction";

   begin
      Put_Line ("Opening " & Pin_Path);
      Open (Pin_File, Mode => Out_File, Name => Pin_Path);

      Put_Line ("Writing direction: " & Direction);
      Put (Pin_File, Direction);

      Put_Line ("Closing " & Pin_Path);
      Close (Pin_File);

      New_Line;

   end Set_Direction;

   procedure Set_Value (Pin : in String; Value : in Integer) is

      Pin_File : File_Type;
      Pin_Path : constant String := Sysfs_Path & "/gpio" & Pin & "/value";

   begin
      Put_Line ("Opening " & Pin_Path);
      Open (Pin_File, Mode => Out_File, Name => Pin_Path);

      Put_Line ("Writing value: " & Integer'Image (Value));
      Put (Pin_File, Value, Width => 1);

      Put_Line ("Closing " & Pin_Path);
      Close (Pin_File);

      New_Line;

   end Set_Value;

   procedure Get_Value (Pin : in String; Value : out Integer) is

      Pin_File : File_Type;
      Pin_Path : constant String := Sysfs_Path & "/gpio" & Pin & "/value";
      In_Value : String := " ";

   begin
      Put_Line ("Opening " & Pin_Path);
      Open (Pin_File, Mode => In_File, Name => Pin_Path);

      Put_Line ("Reading value");
      Get (Pin_File, Value, Width => 0);

      Put_Line ("Closing " & Pin_Path);
      Close (Pin_File);

      New_Line;

   end Get_Value;

begin
   -- Believe the pins are already exported in Buster
   --Export_Pin (Gpio_Pin);

   Get_Value (GPIO_Pin, Pin_Value);
   Put_Line
     ("Current value of " & GPIO_Pin & " is " & Integer'Image (Pin_Value));
   New_Line;

   Set_Direction (GPIO_Pin, "out");

   New_Line;

   Get_Value (GPIO_Pin, Pin_Value);
   Put_Line
     ("Current value of " & GPIO_Pin & " is " & Integer'Image (Pin_Value));

   New_Line;

   Set_Value (GPIO_Pin, 0);
   Get_Value (GPIO_Pin, Pin_Value);
   Put_Line
     ("Current value of " & GPIO_Pin & " is " & Integer'Image (Pin_Value));

   New_Line;

   Set_Value (GPIO_Pin, 1);
   Get_Value (GPIO_Pin, Pin_Value);
   Put_Line
     ("Current value of " & GPIO_Pin & " is " & Integer'Image (Pin_Value));

end Main;
debian@beaglebone:~/BBB_IO$

debian@beaglebone:~/BBB_IO$ gnatmake main
arm-linux-gnueabihf-gcc-8 -c main.adb
arm-linux-gnueabihf-gnatbind-8 -x main.ali
arm-linux-gnueabihf-gnatlink-8 main.ali
debian@beaglebone:~/BBB_IO$
debian@beaglebone:~/BBB_IO$ ls
main main.adb main.ali main.o
debian@beaglebone:~/BBB_IO$
debian@beaglebone:~/BBB_IO$ ./main
Opening /sys/class/gpio/gpio48/value
Reading value
Closing /sys/class/gpio/gpio48/value

Current value of 48 is 1

Opening /sys/class/gpio/gpio48/direction
Writing direction: out
Closing /sys/class/gpio/gpio48/direction

Opening /sys/class/gpio/gpio48/value
Reading value
Closing /sys/class/gpio/gpio48/value

Current value of 48 is 0

Opening /sys/class/gpio/gpio48/value
Writing value: 0
Closing /sys/class/gpio/gpio48/value

Opening /sys/class/gpio/gpio48/value
Reading value
Closing /sys/class/gpio/gpio48/value

Current value of 48 is 0

Opening /sys/class/gpio/gpio48/value
Writing value: 1
Closing /sys/class/gpio/gpio48/value

Opening /sys/class/gpio/gpio48/value
Reading value
Closing /sys/class/gpio/gpio48/value

Current value of 48 is 1
debian@beaglebone:~/BBB_IO$

   Again, under a Buster IoT image (no X-Window overhead), NO SUDO NEEDED
FOR GPIO48 -- and it is already exported so I could even skip the export
call.

  I'm not going to try to configure a SPI device for testing.

Whoops -- artifact from earlier attempts, In_Value is no longer needed.

Actually I was suggesting the really simple way with apt install but there is another.

The fpupdeluxe is a windowing install application that allows you to create any version for any target on your PC. I did install it on my Beagle and yes, hours later it's done but that's because the beagle is a bit of a pig. Went much faster on a Pi3 and on both a LinuxCNC PC and WIN-7 PC lightning fast. And you can install which target you want to compile for.

The key here is that for the most part you write once, compile either anywhere or on the same machine for anywhere along with an IDE for much faster program development. The blinky.lpi project that I referred to is a command line program.

But I'm trying to keep this discussion pointed in the direction of the subject line. The problem with access to GPIO seems to exist in both the Pi and Beagle world. One posting mentioned how his software broke when the Pi OS was upgraded to 4.14.

The issues seem to be the same. Something with udev and creating the correct scripts and files.
https://stackoverflow.com/questions/41586162/access-gpio-on-beaglebone-as-non-root-user

The problem with the above link is that step 1 running 80-gpio-permissions.rules doesn't work for me because it doesn't exist on my system and the contents of that file aren't listed.

However they are in the reference but in that reference the revision of the kernel is 4.4.36
https://github.com/adafruit/adafruit-beaglebone-io-python/issues/137

My experience with changing Beagle versions is fraught with disaster where the change then breaks all sorts of stuff that was working. This is why I'm staying on 4.11 which is what the book uses and at least there's 1.25" of paper all pointing to the same OS.

So. Should I just blindly, in the command line way, type all that stuff with no idea of why I'm doing it, run it and if it works be happy?

As an example the second link uses:
chown -R nick:digital /sys/devices/gpio

The first link above uses
chown -R debian:root /sys/devices/gpio

I think debian:root is what I want to do but I don't understand why the nick:digital allows root access. Why not nick:root?

And the second link with nick's /usr/local/bin/udev-gpio-permissions.sh has a lot more lines in it.

As I said, this appears to have been an ongoing issue for more than 7 years. And perhaps the latest OS fixes it? But then I have to go through the process of rebuilding an entire system. Like going from WIN-7 to WIN-10. What a pain.

John

Followed what this user reported step by step.
https://stackoverflow.com/questions/41586162/access-gpio-on-beaglebone-as-non-root-user

No matter. ./Blinky still requires sudo as explained in the comments at the start of the program.

So after creating the
80-gpio-permissions.rules
And creating the
udev-gpio-permissions.sh

And then going through the
udevadm control --reload-rules
and reboot

What should be done next?

Thanks
John

Blinky.lpr (2.83 KB)

But I'm trying to keep this discussion pointed in the direction of the subject line. The problem with access to GPIO seems to exist in both the Pi and Beagle world. One posting mentioned how his software broke when the Pi OS was upgraded to 4.14.

pi@rpi3bplus-1:~/PI_GPIO$ uname -a
Linux rpi3bplus-1 5.10.17-v7+ #1403 SMP Mon Feb 22 11:29:51 GMT 2021 armv7l
GNU/Linux
pi@rpi3bplus-1:~/PI_GPIO$

  If I interpret that, the current R-Pi is up to kernel 5.10 vs 4.18 for
BBB

debian@beaglebone:~$ uname -a
Linux beaglebone 4.19.94-ti-r48 #1buster SMP PREEMPT Wed Aug 19 17:38:55
UTC 2020 armv7l GNU/Linux
debian@beaglebone:~$

  Well, there are some weird things on the R-Pi... My Ada program is able
to export a GPIO (they aren't exported by default, unlike the BBB). But it
fails when opening the direction file to change from "in" to "out". I even
tried changing from "out_file" to "append_file".

  Turns out to be a timing issue -- the OS hasn't finished creating the
pin files by the time it tried to write the direction. {This is
Raspberry-Pi -- I didn't have that problem on BBB, but wasn't exporting in
code either}. Putting in a 1 second delay after closing the export file let
the program run. I also had to add an exception handler on the export
operation in case the pin had already been exported (along with adding an
Unexport function called at the end). Other than using GPIO 18, and using
export (with delay), unexport -- this is the same program that ran on the
BBB. In fact, I just built and ran it on the BBB (Using GPIO 18). (Changed
to GPIO 48 -- and first run had the same timing error -- since it took the
exception branch; subsequent runs no problem).

pi@rpi3bplus-1:~/PI_GPIO$ gnatmake main
arm-linux-gnueabihf-gcc-8 -c main.adb
arm-linux-gnueabihf-gnatbind-8 -x main.ali
arm-linux-gnueabihf-gnatlink-8 main.ali
pi@rpi3bplus-1:~/PI_GPIO$ ./main
Opening /sys/class/gpio/export
Writing pin number: 18
Closing /sys/class/gpio/export
Delaying to allow pin control files to be set up

Opening /sys/class/gpio/gpio18/value
Reading value
Closing /sys/class/gpio/gpio18/value

Current value of 18 is 1

Opening /sys/class/gpio/gpio18/direction
Writing direction: out
Closing /sys/class/gpio/gpio18/direction

Opening /sys/class/gpio/gpio18/value
Reading value
Closing /sys/class/gpio/gpio18/value

Current value of 18 is 0

Opening /sys/class/gpio/gpio18/value
Writing value: 0
Closing /sys/class/gpio/gpio18/value

Opening /sys/class/gpio/gpio18/value
Reading value
Closing /sys/class/gpio/gpio18/value

Current value of 18 is 0

Opening /sys/class/gpio/gpio18/value
Writing value: 1
Closing /sys/class/gpio/gpio18/value

Opening /sys/class/gpio/gpio18/value
Reading value
Closing /sys/class/gpio/gpio18/value

Current value of 18 is 1

Opening /sys/class/gpio/gpio18/direction
Writing direction: in
Closing /sys/class/gpio/gpio18/direction

Opening /sys/class/gpio/unexport
Writing pin number: 18
Closing /sys/class/gpio/unexport

My experience with changing Beagle versions is fraught with disaster where the change then breaks all sorts of stuff that was working. This is why I'm staying on 4.11 which is what the book uses and at least there's 1.25" of paper all pointing to the same OS.

  As I mentioned, uSD cards are getting cheap... Write a current OS image
and try it on a fresh 8+GB card (don't forget to resize the partition after
first boot). If it works, great. If it doesn't you still have your original
image to mess with. I'd suggest
https://elinux.org/Beagleboard:Latest-images-testing#Debian_10_.28Buster.29_LXQt
at a minimum, or
https://rcn-ee.net/rootfs/bb.org/testing/2021-04-19/buster-lxqt/ (to save
time bringing the image up-to-date -- note that the latter is Buster 10-9
while the shipping image https://beagleboard.org/latest-images is 10-3 and
a year out-of-date).

So. Should I just blindly, in the command line way, type all that stuff with no idea of why I'm doing it, run it and if it works be happy?

As an example the second link uses:
chown -R nick:digital /sys/devices/gpio

The first link above uses
chown -R debian:root /sys/devices/gpio

I think debian:root is what I want to do but I don't understand why the nick:digital allows root access. Why not nick:root?

  It doesn't... It changes the OWNER of /sys/devices/gpio to BE the user
"nick" (note -- it isn't changing the files under that directory!), while
also putting them into a group called "digital". As owner, "nick" then has
full privileges to the file, and any user in the "digital" group has group
privileges.

  The second, again, would change the OWNER to "debian" but put the file
into a group called "root". And, again, it doesn't change what is below
that directory. That's one reason for all those nasty udev rules -- the
sysfs is created at boot time, and the pin files are created when the pin
is exported. Those files would be created using whatever the kernel is
configured to use for permissions (likely root:root) unless over-written by
a udev rule.

  In a proper system (current BBB and R-Pi), the GPIO should be
root:gpio, and the user "debian" ("pi") set as a member of the gpio group
-- owned by root, with access to any member of the "gpio" group, and group
permissions should be RWX

pi@rpi3bplus-1:~/PI_GPIO$ ls -l /sys/class/gpio
total 0
-rwxrwx--- 1 root gpio 4096 May 12 11:05 export
lrwxrwxrwx 1 root gpio 0 May 3 11:30 gpiochip0 ->
../../devices/platform/soc/3f200000.gpio/gpio/gpiochip0
lrwxrwxrwx 1 root gpio 0 May 3 11:30 gpiochip504 ->
../../devices/platform/soc/soc:firmware/soc:firmware:expgpio/gpio/gpiochip504
-rwxrwx--- 1 root gpio 4096 May 12 11:05 unexport

vs

debian@beaglebone:~/BBB_IO$ ls -l /sys/class/gpio
total 0
--w--w---- 1 root gpio 4096 May 12 11:20 export
lrwxrwxrwx 1 root gpio 0 May 11 20:40 gpio10 ->
../../devices/platform/ocp/44e07000.gpio/gpiochip0/gpio/gpio10
  <SNIP>
lrwxrwxrwx 1 root gpio 0 May 11 20:40 gpiochip0 ->
../../devices/platform/ocp/44e07000.gpio/gpio/gpiochip0
lrwxrwxrwx 1 root gpio 0 May 11 20:40 gpiochip32 ->
../../devices/platform/ocp/4804c000.gpio/gpio/gpiochip32
lrwxrwxrwx 1 root gpio 0 May 11 20:40 gpiochip64 ->
../../devices/platform/ocp/481ac000.gpio/gpio/gpiochip64
lrwxrwxrwx 1 root gpio 0 May 11 20:40 gpiochip96 ->
../../devices/platform/ocp/481ae000.gpio/gpio/gpiochip96
--w--w---- 1 root gpio 4096 May 12 11:20 unexport

pi@rpi3bplus-1:~/PI_GPIO$ groups pi
pi : pi adm dialout cdrom sudo audio video plugdev games users input netdev
spi i2c gpio lpadmin
pi@rpi3bplus-1:~/PI_GPIO$

  ... So, again... CAN you access GPIO 48 (from "debian", without using
sudo) from the SHELL itself?

Hi Dennis,
As you described below I have the permissions sets as below for both gpio48 and gpio60.

debian@ebb:/sys/class/gpio$ echo 48 > export
debian@ebb:/sys/class/gpio$ ls -l gpio48
lrwxrwxrwx 1 root gpio 0 May 12 10:22 gpio48 -> ../../devices/platform/ocp/4804c000.gpio/gpiochip1/gpio/gpio48
debian@ebb:/sys/class/gpio$

And user debian has belongs to these groups including 'gpio'

debian@ebb:/sys/class/gpio$ groups debian
debian : debian adm kmem dialout cdrom floppy sudo audio dip video plugdev users systemd-journal i2c bluetooth netdev cloud9ide gpio pwm eqep admin spi tisdk weston-launch xenomai
debian@ebb:/sys/class/gpio$

So now let's play with gpio48 which has an LED attached to it.

debian@ebb:/sys/class/gpio/gpio48$ ls
active_low device direction edge label power subsystem uevent value
debian@ebb:/sys/class/gpio/gpio48$ cat direction
in
debian@ebb:/sys/class/gpio/gpio48$ echo out > direction
debian@ebb:/sys/class/gpio/gpio48$ cat direction
out
debian@ebb:/sys/class/gpio/gpio48$ cat value
0

LED is currently OFF

debian@ebb:/sys/class/gpio/gpio48$ echo 1 > value
debian@ebb:/sys/class/gpio/gpio48$ cat value
1
debian@ebb:/sys/class/gpio/gpio48$

LED is ON.
No "sudo" required.

Now let's look at groups.
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ls -l Blinky
-rwxr-xr-x 1 debian debian 270880 May 11 15:08 Blinky

Hmmm. Not part of the gpio group

debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ./Blinky
An unhandled exception occurred at $000282E0:
                                             ESysfsFileOpenWrite: Cannot open file </sys/class/gpio/gpio48/direction> for writing.
                      $000282E0 WRITETEXTTOFILE, line 68 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.Types.pas
     $00021CB4 TSYSFSGPIO__SETPINMODE, line 200 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.GPIO.pas

So let’s add it to the gpio group:
                                                                                                 debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ chgrp gpio Blinky
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ls -l Blinky
-rwxr-xr-x 1 debian gpio 270880 May 11 15:08 Blinky

debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ./Blinky
An unhandled exception occurred at $000282E0:
                                             ESysfsFileOpenWrite: Cannot open file </sys/class/gpio/gpio48/direction> for writing.
                      $000282E0 WRITETEXTTOFILE, line 68 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.Types.pas
     $00021CB4 TSYSFSGPIO__SETPINMODE, line 200 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.GPIO.pas

So at the command line level I can work with gpio48 turning on and off the output (and the LED).

From the program I cannot since I don't have permission without using sudo to write to ...gpio48/direction

After every compile I could run the application from the command line with a sudo but that's not the solution I need. Nor to each time change the permissions at the command line level. If we can figure out what the attributes of the program are supposed to be so it runs without the sudo then I can potentially change something with the Lazarus IDE or Free Pascal Compiler to properly configure the executable. But at the moment I can't get the executable to run.

I recall reading that permissions of the gpio48 folder aren't enough. That the links may be a problem but each of the entries in gpio48 are also in the gpio group.

debian@ebb:/sys/class/gpio/gpio48$ ls -l
total 0
-rwxrwx--- 1 root gpio 4096 May 12 10:47 active_low
lrwxrwxrwx 1 root gpio 0 May 12 10:47 device -> ../../../gpiochip1
-rwxrwx--- 1 root gpio 4096 May 12 10:47 direction
-rwxrwx--- 1 root gpio 4096 May 12 10:47 edge
-rwxrwx--- 1 root gpio 4096 May 12 10:47 label
drwxrwx--- 2 root gpio 0 May 12 10:47 power
lrwxrwxrwx 1 root gpio 0 May 12 10:47 subsystem -> ../../../../../../../class/gpio
-rwxrwx--- 1 root gpio 4096 May 12 10:47 uevent
-rwxrwx--- 1 root gpio 4096 May 12 10:47 value
debian@ebb:/sys/class/gpio/gpio48$
  
So still basically lost. What sort of permissions does the executable "Blinky" need in order to be able to write to .../gpio/gpio48/direction?

Thanks
John

Oh, and one other thing with respect to the SPI. Here's another weird thing about the Beagle. Notice how the numbering is shifted from 0 relative to 1 relative. So I believe when the program tries to open spidev1.0 it's really still going for the physical spi0 port. But I could be wrong.

debian@ebb:/sys/class/gpio/gpio48$ ls /dev/spi*
/dev/spidev1.0 /dev/spidev1.1 /dev/spidev2.0 /dev/spidev2.1

/dev/spi:
0.0 0.1 1.0 1.1

Now let's look at groups.
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ls -l Blinky
-rwxr-xr-x 1 debian debian 270880 May 11 15:08 Blinky

Hmmm. Not part of the gpio group

  It shouldn't be... That line says the file "Blinky" is owned by user
"debian" and is part of the group "debian"... And group/other have
read/execute permission on the file.

  You /run/ the file from your account, and your account is a member of
"gpio" group.

debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ./Blinky
An unhandled exception occurred at $000282E0:
                                            ESysfsFileOpenWrite: Cannot open file </sys/class/gpio/gpio48/direction> for writing.
                     $000282E0 WRITETEXTTOFILE, line 68 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.Types.pas
    $00021CB4 TSYSFSGPIO__SETPINMODE, line 200 of /home/debian/lazarus/pxl/Source/PXL.Sysfs.GPIO.pas

So let’s add it to the gpio group:
                                                                                                debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ chgrp gpio Blinky

  Which only means users with group "gpio" membership now have access to
the file. It does nothing for what permissions the file itself is running
under -- those are defined by the user group membership.

  Unfortunately, the PXL source doesn't return the actual error code
which fpGetErrno would report; it raises that exception for any error code
(the help systems says "negative value if there was an error" whereas
fpwrite explicitly says -1 for an error; in either case, having fpgeterrno
value could help pin down what the real problem is).

  Handle := fpopen(FileName, O_WRONLY);
  if Handle < 0 then
    raise ESysfsFileOpenWrite.Create(Format(SCannotOpenFileForWriting,
[FileName]));

  I see that setpinmode does do an export of the pin if it is not already
exported... As my tests with Ada program show, I needed a delay (used one
second I believe) after doing an export or I'd get a similar problem with
opening the pin's files. However -- "not already exported" is PXL's
concept, not reality. PXL maintains a bit-map of "exported" pins, meaning
pins IT has done an export for...

function TSysfsGPIO.IsPinExported(const Pin: TPinIdentifier): Boolean;
begin
  Result := IsPinBitSet(Pin, ExportedBitmask);
end;

  This means the every time you run the program, it starts with the
assumption that the pin is NOT exported, and issues an export operation. I
suspect -- based on my timing issues -- that

procedure TSysfsGPIO.ExportPin(const Pin: TPinIdentifier);
begin
  TryWriteTextToFile(FExportFileName, IntToStr(Pin));
  SetPinBit(Pin, ExportedBitmask);
end;

needs to have some sleep/delay placed in it before it returns... say

  Sleep(1000);

for a 1 second delay (if that runs you could try fine-tuning downwards
until it fails, then add a safety margin to the last working value).

After every compile I could run the application from the command line with a sudo but that's not the solution I need. Nor to each time change the permissions at the command line level. If we can figure out what the attributes of the program are supposed to be so it runs without the sudo then I can potentially change something with the Lazarus IDE or Free Pascal Compiler to properly configure the executable. But at the moment I can't get the executable to run.

  The program attributes should only be a need to have eXecute permission
on thre file. Everything else should be defined by the /user account/
membership and the permissions of the target files.

  I don't know why "sudo" doesn't have the problem, unless, again, it is
a timing matter -- and immediately after the export the direction file
starts with root:root and needs time to be changed to root:gpio. If so,
then sudo would be owner of root and not tied to the group access... and if
so, adding a delay immediately after the export write in PXL may suffice.
After all, the program, as I understand the calling sequences and tests, IS
WRITING TO /sys/class/gpio/export without a problem.

debian@ebb:/sys/class/gpio/gpio48$ ls /dev/spi*
/dev/spidev1.0 /dev/spidev1.1 /dev/spidev2.0 /dev/spidev2.1

/dev/spi:
0.0 0.1 1.0 1.1

  Try ls -l This appears to be another change as mine map 1:1
(another reason to create an current Debian 10/buster uSD card). I wouldn't
be surprised if Stretch maps 0.0 to spidev2.0, while 1.0 goes to spidev1.0.

debian@beaglebone:~$ ls -l /dev/spi*
crw-rw---- 1 root spi 153, 0 May 12 12:46 /dev/spidev0.0
crw-rw---- 1 root spi 153, 1 May 12 12:46 /dev/spidev0.1
crw-rw---- 1 root spi 153, 2 May 12 15:58 /dev/spidev1.0
crw-rw---- 1 root spi 153, 3 May 12 15:58 /dev/spidev1.1

/dev/spi:
total 0
lrwxrwxrwx 1 root root 12 May 12 12:46 0.0 -> ../spidev0.0
lrwxrwxrwx 1 root root 12 May 12 12:46 0.1 -> ../spidev0.1
lrwxrwxrwx 1 root root 12 May 12 15:58 1.0 -> ../spidev1.0
lrwxrwxrwx 1 root root 12 May 12 15:58 1.1 -> ../spidev1.1
debian@beaglebone:~$

  A year old, so might be based on Stretch is
https://arcanesciencelab.wordpress.com/2020/01/14/the-correct-way-to-enable-spi-ports-on-the-beaglebone-black/

  On that page they state
"""
# For SPI1, /dev/spidev1.#

o/~ Talking to myself in public... o/~

  I'd strongly recommend editing this and doing whatever needs to be done
to rebuild the PXL library (if it doesn't just compile from sources
everytime) OR...

procedure TSysfsGPIO.ExportPin(const Pin: TPinIdentifier);
begin
TryWriteTextToFile(FExportFileName, IntToStr(Pin));

    Sleep(1000); <<<<<

SetPinBit(Pin, ExportedBitmask);
end;

... modifying the source program (for test: Blinky) to explicitly export
pins and delay before trying to do anything with them...

"""
begin
  GPIO := TSysfsGPIO.Create;

  GPIO.ExportPin(PinLED); <<<<<<<<
  Sleep(1000); <<<<<<<<

  try
    // Switch LED pin for output.
    GPIO.PinMode[PinLED] := TPinMode.Output;

    WriteLn('Blinking LED, press any key to exit...');
"""

  Modifying the library (and fine-tuning the sleep duration to the
minimum that is reliable) should ensure that any implicit pin export works
safely. Modifying the application source, OTOH, means if one exports ALL
NEEDED pins at the start, only one sleep would suffice for the batch,
rather than getting a sleep on each implicit export.

  *** WORRY ABOUT SPI AFTER THE GPIO PROBLEM IS SOLVED ***

Hi Dennis,
I've been playing with that yesterday.
I think the library needs to be changed in a couple of places. As you pointed out, it doesn't really know if the GPIO pin was already exported until it tries to do it and then flags that it's exported in the flags variable.

The problem is if the export fails it then not only checks an invalid flag setting and does the unexport but that ends up removing an exported pin that was already there when the program stops. I don't like that behaviour. I thought I saw that it only changes it to input from output but yet it's gone when the program finishes so that will take some more investigation.

What I found yesterday is by adding the delay you suggested down in the library after the TryWriteTextToFile() that I now get run from command line performance. Most of yesterday was a bit of a write-off dealing with a missing SIM card for my wife's new iPhone 12 and some running around to get other problems solved.

From puTTY I reliably get this now.
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ./Blinky
Blinking LED, press any key to exit...
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$

Today I want to change that low level code to:
1. Not set the exported flag if the pin is already there so it won't be unexported at the end but only set to what it was before the program ran. This means we also won't try to export it at the start.
2. If it was found to not be there then add the delay but within a repeat until with shorter sleep times and a loop counter.

I'll report back when it's done.

John

I've been playing with that yesterday.
I think the library needs to be changed in a couple of places. As you pointed out, it doesn't really know if the GPIO pin was already exported until it tries to do it and then flags that it's exported in the flags variable.

  On further scanning of the source -- it will all come down to the
library. I think the Export method is declared as private, so your
application couldn't call it directly without modifying the library spec.
And if one is modifying the library, might as well fix it all within the
library instead of still requiring applications to change also.

The problem is if the export fails it then not only checks an invalid flag setting and does the unexport but that ends up removing an exported pin that was already there when the program stops. I don't like that behaviour. I thought I saw that it only changes it to input from output but yet it's gone when the program finishes so that will take some more investigation.

  The change from in to out might be linked to the re-export if the
kernel tears down the pin configuration and rebuilds it (or -- it might
have been built into some overhead library operation, forgive me for not
doing a scan of the sources again).

What I found yesterday is by adding the delay you suggested down in the library after the TryWriteTextToFile() that I now get run from command line performance. Most of yesterday was a bit of a write-off dealing with a missing SIM card for my wife's new iPhone 12 and some running around to get other problems solved.

From puTTY I reliably get this now.
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$ ./Blinky
Blinking LED, press any key to exit...
debian@ebb:~/lazarus/pxl/Samples/FreePascal/SingleBoard/Generic/Blinky$

  Progress... <G>

Today I want to change that low level code to:
1. Not set the exported flag if the pin is already there so it won't be unexported at the end but only set to what it was before the program ran. This means we also won't try to export it at the start.

  That might add a bit more overhead than you want -- if you are talking
about adding a test to see if the .../gpioXX directory (or a file within
the directory) exists and is accessible. After all, it appears that every
access of a pin includes the test for is-pin-exported. You might want to
add a parallel bitmap similar to the one currently used in which you record
pins that were found to be externally exported.

  That would make the is-pin-exported something like

  <current pin bitmap test> OR <new bitmap test>

That way you only invoke the export function if the pin is in neither
bitmap. Export function could then be something like

  if <.../gpioXX directory exists> then
    update new bitmap
  else
    write the export command
    sleep
    update old bitmap

Hi Dennis,

I’ve got a pretty stable pxl gpio library working now.

Here is the original ExportPin function with the problem you identified where the OS needs time to create the files but fails when it tries to write out.

procedure TSysfsGPIO.ExportPin(const Pin: TPinIdentifier);
begin
TryWriteTextToFile(FExportFileName, IntToStr(Pin));
SetPinBit(Pin, ExportedBitmask);
end;

A bit of a mystery in how it works other than it creates an OS fault to signal issues. My new function is more complicated:

{
The ExportPin procedure has been expanded to test to see if it is already
exported. If it was already there then a new bit flag is set so that when
the application calls to unexport pins, the ones that were there are left alone.
}
procedure TSysfsGPIO.ExportPin(const Pin: TPinIdentifier);
var val : StdChar;
begin
// First check to see if we already exported this pin inside this application.
if (not IsPinBitSet(Pin, ExportedBitmask)) then begin
// Check to see if the pin was set before program started by reading from it.
// If the read fails then the /sys/class/gpio/gpio__ folder does not exist.
if (TryReadCharFromFile(FAccessFileName + IntToStr(Pin) + ‘/direction’,val)) then begin
// It’s already exported and folder exists so don’t bother trying again.
SetPinBit(Pin, ExportDefinedBitmask); // Set that it was already exported on start
SetPinBit(Pin, ExportedBitmask); // and that it’s exported.
end
else begin // File didn’t exist
// Wasn’t exported when program started so now let’s export it.
if TryWriteTextToFile(FExportFileName, IntToStr(Pin)) then
SetPinBit(Pin, ExportedBitmask);
end;
end;
end;

The only problem with the above procedure is that it’s not a function that returns a successful or not successful export. So we don’t know if the pin was actually exported yet.

So where does the delay suggestion you made come in? When we try and set the direction of the pin! Which is done in the user application.

// Switch LED pin for output.
GPIO.PinMode[PinLED] := TPinMode.Output;

The delay is not done in the original but should come after the call to export it or ExportPin needs to be changed to a function and the delay done there. Here’s the original.

procedure TSysfsGPIO.SetPinMode(const Pin: TPinIdentifier; const Mode: TPinMode);
begin
if Pin > MaximumSupportedPins then
raise EGPIOInvalidPin.Create(Format(SGPIOSpecifiedPinInvalid, [Pin]));

if not IsPinExported(Pin) then
ExportPin(Pin);

if Mode = TPinMode.Input then
begin
WriteTextToFile(FAccessFileName + IntToStr(Pin) + ‘/direction’, ‘in’);
ClearPinBit(Pin, DirectionBitmask);
end
else
begin
WriteTextToFile(FAccessFileName + IntToStr(Pin) + ‘/direction’, ‘out’);
SetPinBit(Pin, DirectionBitmask);
end;

SetPinBit(Pin, DirectionDefinedBitmask);
end;

Instead I’ve done as you suggested but let the program try with short intervals.

{
When we try and set the pin mode we first test to see if the pin was exported.
When the program first runs we don’t know yet if it existed as exported but after
that even if the pin mode is set again we will know because of the new alreadyexportedflag
and the exported flag.
}
procedure TSysfsGPIO.SetPinMode(const Pin: TPinIdentifier; const Mode: TPinMode);
var
escapeCounter : integer;
begin
if Pin > MaximumSupportedPins then
raise EGPIOInvalidPin.Create(Format(SGPIOSpecifiedPinInvalid, [Pin]));

if not IsPinExported(Pin) then begin // First time we don’t know if the pin was already exported.
ExportPin(Pin); // So we have to try which will set the proper flags.
if not IsPinAlreadyExported(Pin) then begin // If it wasn’t already exported then wait to see if it now is.
escapeCounter := 10; // Wait 5 seconds max for OS to create file structure.
repeat
Sleep(500); // Let the OS have time to create the files.
if (TryReadCharFromFile(FAccessFileName + IntToStr(Pin) + ‘/direction’,val)) then
escapeCounter := 0
else
dec(escapeCounter);
until escapeCounter <= 0;
end;
end;
// Finally set the direction.
if Mode = TPinMode.Input then
begin
WriteTextToFile(FAccessFileName + IntToStr(Pin) + ‘/direction’, ‘in’);
ClearPinBit(Pin, DirectionBitmask);
end
else
begin
WriteTextToFile(FAccessFileName + IntToStr(Pin) + ‘/direction’, ‘out’);
SetPinBit(Pin, DirectionBitmask);
end;

SetPinBit(Pin, DirectionDefinedBitmask);
end;

The only issue I’ve run into is the repeat until loop. Seems like the file is readable in about 100mS but fails with the write unless the delay is 500mS.

John