Raspberry Pi GPIO /value file appears with wrong permissions momentarily

David I. McIntosh

My son is implementing a server on a Raspberry Pi that allows control of the GPIO pins via a network connection. He has discovered some strange behaviour, which at first seemed like a bug (but see answer below).

First, the OS being used is Raspbian, a version of Debian Linux. He is using the standard system file to control the GPIO ports.

We start with a GPIO pin, e.g. pin 17, in a non-exported state. For example,

echo "17" > /sys/class/gpio/unexport

Now, if the server is asked to turn on pin 17, it does the following:

  1. Opens the /sys/class/gpio/export, writes "17" to it, and closes the export file
  2. Open the /sys/class/gpio/gpio17/direction file for read, examines it to see if it is set as input or output. Closes the file. Then, if necessary, re-opens the file for write and writes "out" to the file, to set the pin as an output pin, and closes the direction file.

At this point, we should be able to open /sys/class/gpio/gpio17/value for write, and write a "1" to it.

However, the permissions on the /sys/class/gpio/gpio17/value file exists but the group permissions is read-only. If we put in a "sleep" in order to wait for a fraction of a second, the permissions change so the group permission has write permissions.

I would have expected that the OS should not return from the write to the direction file until it had set the permissions on the value file correctly.

Why is this happening? It seems like a bug. Where should I report this (with more detail...)? See answer below.

What follows are the relevant bits of code. The code has been edited and paraphrased a bit, but it is essentially what is being used. (Keep in mind it's the code of a grade 12 student trying to learn C++ and Unix concepts):

class GpioFileOut
{
    private:
        const string m_fName;
        fstream m_fs;

    public:
        GpioFileOut(const string& sName)
        : m_fName(("/sys/class/gpio/" + sName).c_str())
        {
            m_fs.open(m_fName.c_str());
            if (m_fs.fail())
            {
                cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl << endl;
            }
            else
            {
                cout << m_fName << " opened" << endl;
            }
        }

        ~GpioFileOut()
        {
            m_fs.close();
            cout << m_fName << " closed" << endl << endl;
        }

        void reOpen()
        {
            m_fs.close();
            m_fs.open(m_fName);
            if (m_fs.fail())
            {
                cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl << endl;
            }
            else
            {
                cout << m_fName << " re-opened" << endl;
            }
        }

        GpioFileOut& operator<<(const string &s)
        {
            m_fs << s << endl;
            cout << s << " sent to " << m_fName << endl;
            return *this;
        }

        GpioFileOut& operator<<(int n)
        {
            return *this << to_string(n); //ostringstream
        }

        bool fail()
        {
            return m_fs.fail();
        }
};

class GpioFileIn
{
    private:
        ifstream m_fs;
        string m_fName;

    public:
        GpioFileIn(const string& sName)
        : m_fs( ("/sys/class/gpio/" + sName).c_str())
        , m_fName(("/sys/class/gpio/" + sName).c_str())
        {
            if (m_fs <= 0 || m_fs.fail())
            {
                cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl;
            }
            else
            {
                cout << m_fName << " opened" << endl;
            }
        }

        ~GpioFileIn()
        {
            m_fs.close();
            cout << m_fName << " closed" << endl << endl;
        }

        void reOpen()
        {
            m_fs.close();
            m_fs.open(m_fName);
            if (m_fs <= 0 || m_fs.fail())
            {
                cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl;
            }
            else
            {
                cout << m_fName << " re-opened" << endl;
            }
        }

        GpioFileIn& operator>>(string &s)
        {
            m_fs >> s;
            cout << s << " read from " << m_fName << endl;
            return *this;
        }
        bool fail()
        {
            return m_fs.fail();
        }
};

class Gpio
{
    public:
        static const bool OUT = true;
        static const bool IN = false;
        static const bool ON = true;
        static const bool OFF = false;

        static bool setPinDirection(const int pinId, const bool direction)
        {
            GpioFileOut dirFOut(("gpio" + to_string(pinId) + "/direction").c_str());
            if (dirFOut.fail())
            {
                if (!openPin(pinId))
                {
                    cout << "ERROR! Pin direction not set: Failed to export pin" << endl;
                    return false;
                }
                dirFOut.reOpen();
            }
            dirFOut << (direction == OUT ? "out" : "in");
        }


        static bool setPinValue(const int pinId, const bool pinValue)
        {
            string s;
            {
                GpioFileIn dirFIn(("gpio" + to_string(pinId) + "/direction").c_str());
                if (dirFIn.fail())
                {
                    if (!openPin(pinId))
                    {
                        cout << "ERROR! Pin not set: Failed to export pin"<<endl;
                        return false;
                    }
                    dirFIn.reOpen();
                }
                dirFIn >> s;
            }

            if (strncmp(s.c_str(), "out", 3) == 0)
            {
                struct stat _stat;
                int nTries = 0;
                string fname("/sys/class/gpio/gpio"+to_string(pinId)+"/value");

                for(;;)
                {
                    if (stat(fname.c_str(), &_stat) == 0)
                    {
                        cout << _stat.st_mode << endl;
                        if (_stat.st_mode & 020 )
                            break;
                    }
                    else
                    {
                        cout << "stat failed. (Did the pin get exported successfully?)" << endl;
                    }

                    cout << "sleeping until value file appears with correct permissions." << endl;
                    if (++nTries > 10)
                    {
                        cout << "giving up!";
                        return false;
                    }
                    usleep(100*1000);
                };
                GpioFileOut(("gpio" + to_string(pinId) + "/value").c_str()) << pinValue;
                return true;
            }
            return false;
        }

        static bool openPin(const int pinId)
        {
            GpioFileOut fOut("export");
            if (fOut.fail())
                return false;
            fOut << to_string(pinId);
            return true;
        }
}

int main()
{
    Gpio::openPin(17);
    Gpio::setPinDirection(17, Gpio::OUT)
    Gpio::setPinValue(17, Gpio::ON);
}

The key point is this: without the for(;;) loop that stat's the file, the execution fails, and we can see the permissions change on the file within 100ms.

harmic

From a kernel perspective, the 'value' files for each GPIO pin that has been exported are created with mode 0644 and ownership root:root. The kernel does not do anything to change this when you write to the 'direction' file.

The behavior you are describing is due to the operation of the systemd udev service. This service listens for events from the kernel about changes in device state, and applies rules accordingly.

When I tested on my own Pi, I did not experience the behavior you described - the gpio files in /sys are all owned by root:root and have mode 0644, and did not change regardless of direction. However I am running Pidora, and I could not find any udev rules in my system relating to this. I am assuming that Raspbian (or maybe some package you have added to your system) has added such rules.

I did find this thread where some suggested rules are mentioned. In particular this rule which would have the effect you describe:

SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c 'chown -R root:gpio /sys/class/gpio; chmod -R 770 /sys/class/gpio; chown -R root:gpio /sys/devices/virtual/gpio; chmod -R 770 /sys/devices/virtual/gpio'"

You can search in /lib/udev/rules.d, /usr/lib/udev/rules.d and /etc/udev/rules.d for any files containing the text 'gpio' to confirm if you have such rules. By the way, I would be surprised if the change was triggered by changing direction on the pin, more likely by the action of exporting the pin to userspace.

The reason you need to sleep for a while after exporting the device is that until your process sleeps, the systemd service may not get a chance to run and action the rules.

The reason it is done like this, rather than just having the kernel take care of it, is to push policy implementation to userspace in order to provide maximum flexibility without overly complicating the kernel itself.

See: systemd-udevd.service man page and udev man page.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Raspberry Pi GPIO /value file appears with wrong permissions momentarily

From Dev

Raspberry Pi B+ GPIO input value is changing without connecting

From Dev

Python Raspberry Pi GPIO error

From Dev

Raspberry Pi PHP GPIO read

From Dev

Raspberry Pi PHP GPIO read

From Dev

Unable to write to a GPIO pin despite file permissions on /sys/class/gpio/gpio18/value

From Dev

Unable to write to a GPIO pin despite file permissions on /sys/class/gpio/gpio18/value

From Dev

Default file permissions for FAT32 USB stick on Raspberry Pi

From Dev

Raspberry Pi camera writing permissions

From Dev

Raspberry Pi 2 Some GPIO pins not working

From Dev

How to detect change in GPIO input raspberry pi

From Dev

python+raspberry pi gpio invalid syntax

From Dev

Using GPIO Pins of Raspberry Pi from scratch?

From Dev

PPS GPIO with Buildroot image on Raspberry Pi 3

From Dev

Raspberry pi GPIO pins wont switch

From Dev

Accessing raspberry pi gpio ports through unity

From Dev

Raspberry Pi - More GPIO pins or/and more leds

From Dev

Can't manage to update raspberry pi, gpio-header html file

From Dev

Copy file to Raspberry Pi

From Dev

Why can't the jvm write to gpio on raspberry pi?

From Dev

BCM2835 gpio device tree raspberry pi

From Dev

Raspberry Pi RPi.GPIO error with threated callback

From Dev

Connect Raspberry Pi GPIO to 5V with only 1 resistor

From Dev

Raspberry Pi GPIO: Change Duty Cycle By Console Commands

From Dev

Raspberry Pi camera GPIO...close statement causing error

From Dev

Executing a python script including GPIO commands by cron in Raspberry Pi 2

From Dev

Raspberry Pi RuntimeError: Conflicting edge detection already enabled for this GPIO channel

From Dev

BCM2835 gpio device tree raspberry pi

From Dev

Can GPIO.output be assigned to a variable? Raspberry PI

Related Related

  1. 1

    Raspberry Pi GPIO /value file appears with wrong permissions momentarily

  2. 2

    Raspberry Pi B+ GPIO input value is changing without connecting

  3. 3

    Python Raspberry Pi GPIO error

  4. 4

    Raspberry Pi PHP GPIO read

  5. 5

    Raspberry Pi PHP GPIO read

  6. 6

    Unable to write to a GPIO pin despite file permissions on /sys/class/gpio/gpio18/value

  7. 7

    Unable to write to a GPIO pin despite file permissions on /sys/class/gpio/gpio18/value

  8. 8

    Default file permissions for FAT32 USB stick on Raspberry Pi

  9. 9

    Raspberry Pi camera writing permissions

  10. 10

    Raspberry Pi 2 Some GPIO pins not working

  11. 11

    How to detect change in GPIO input raspberry pi

  12. 12

    python+raspberry pi gpio invalid syntax

  13. 13

    Using GPIO Pins of Raspberry Pi from scratch?

  14. 14

    PPS GPIO with Buildroot image on Raspberry Pi 3

  15. 15

    Raspberry pi GPIO pins wont switch

  16. 16

    Accessing raspberry pi gpio ports through unity

  17. 17

    Raspberry Pi - More GPIO pins or/and more leds

  18. 18

    Can't manage to update raspberry pi, gpio-header html file

  19. 19

    Copy file to Raspberry Pi

  20. 20

    Why can't the jvm write to gpio on raspberry pi?

  21. 21

    BCM2835 gpio device tree raspberry pi

  22. 22

    Raspberry Pi RPi.GPIO error with threated callback

  23. 23

    Connect Raspberry Pi GPIO to 5V with only 1 resistor

  24. 24

    Raspberry Pi GPIO: Change Duty Cycle By Console Commands

  25. 25

    Raspberry Pi camera GPIO...close statement causing error

  26. 26

    Executing a python script including GPIO commands by cron in Raspberry Pi 2

  27. 27

    Raspberry Pi RuntimeError: Conflicting edge detection already enabled for this GPIO channel

  28. 28

    BCM2835 gpio device tree raspberry pi

  29. 29

    Can GPIO.output be assigned to a variable? Raspberry PI

HotTag

Archive