Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added waits for UDEV rules (copy of @philfifi's pull) #28

Merged
merged 8 commits into from
Sep 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Contributors
* Simon Rowe (@srowe)
* Steven P. Goldsmith (@sgjava)
* Adrian Cuzman (@adriancuzman)
* Mathieu Pasquet (@winterscar)
18 changes: 18 additions & 0 deletions OPi/sysfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,46 @@
from OPi.constants import HIGH, LOW, IN, OUT, \
NONE, RISING, FALLING, BOTH

import os
import time

# Allow to wait up to 1 second for the file have the correct permissions
WAIT_PERMISSION_TIMEOUT = 1.


def await_permissions(path):
start_time = time.time()
while (not os.access(path, os.W_OK) and
time.time() - start_time < WAIT_PERMISSION_TIMEOUT):
time.sleep(0.1)


@contextmanager
def value_descriptor(pin, mode="r"):
path = "/sys/class/gpio/gpio{0}/value".format(pin)
await_permissions(path)
with open(path, mode) as fp:
yield fp


def export(pin):
path = "/sys/class/gpio/export"
await_permissions(path)
with open(path, "w") as fp:
fp.write(str(pin))


def unexport(pin):
path = "/sys/class/gpio/unexport"
await_permissions(path)
with open(path, "w") as fp:
fp.write(str(pin))


def direction(pin, dir):
assert dir in [IN, OUT]
path = "/sys/class/gpio/gpio{0}/direction".format(pin)
await_permissions(path)
with open(path, "w") as fp:
if dir == IN:
fp.write("in")
Expand All @@ -54,6 +71,7 @@ def output(pin, value):
def edge(pin, trigger):
assert trigger in [NONE, RISING, FALLING, BOTH]
path = "/sys/class/gpio/gpio{0}/edge".format(pin)
await_permissions(path)
opts = {
NONE: "none",
RISING: "rising",
Expand Down
23 changes: 23 additions & 0 deletions doc/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,26 @@ Install the latest version of the library directly from
`PyPI <https://pypi.python.org/pypi?:action=display&name=OPi.GPIO>`_::

$ sudo pip install --upgrade OPi.GPIO

Non Root Access
---------------
If you want to be able to use the library as a non root user, you will need to setup a `UDEV` rule to grant you permissions first.
This can be accomplished as follows:

``$ sudo usermod -aG gpio <current_user>``

``$ sudo nano /etc/udev/rules.d/99-gpio.rules``

That should add your user to the GPIO group, create a new ``UDEV`` rule, and open it in the Nano text editor.

Enter the following into Nano


SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"
SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"



press ``ctrl-x``, ``Y``, and ``ENTER`` to save and close the file.

Finally, reboot and you should be ready to use ``OPi.GPIO`` as a non root user.
20 changes: 19 additions & 1 deletion tests/test_sysfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,29 @@
Tests for the :py:mod:`OPi.sysfs` module.
"""
import pytest
import time
import threading
import os

from OPi.sysfs import export, unexport, direction, input, output, edge
from OPi.sysfs import export, unexport, direction, input, output,\
edge, await_permissions, WAIT_PERMISSION_TIMEOUT
from OPi.constants import IN, OUT, LOW, HIGH, NONE, RISING, FALLING, BOTH


@pytest.mark.parametrize("test_input,expected", [
(0.1, True),
(1.5, False),
])
def test_await_permissions(fs, test_input, expected):
path = "/sys/class/gpio/test"
fs.CreateFile(path)
os.chmod(path, 0o444) # revoke write permissions to the file
start_time = time.time()
threading.Timer(test_input, lambda: os.chmod(path, 0o666)).start()
await_permissions(path)
assert (time.time() - start_time < WAIT_PERMISSION_TIMEOUT) == expected


def test_export(fs):
fs.CreateFile("/sys/class/gpio/export")
export(19)
Expand Down