]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/adafruit_blinka/microcontroller/generic_linux/lgpio_pin.py
More changes and ran pre-commit
[Adafruit_Blinka-hackapet.git] / src / adafruit_blinka / microcontroller / generic_linux / lgpio_pin.py
1 # SPDX-FileCopyrightText: 2025 Melissa LeBlanc-Williams for Adafruit Industries
2 #
3 # SPDX-License-Identifier: MIT
4 """A Pin class for use with lgpio."""
5
6 from pathlib import Path
7 import lgpio
8
9
10 def _get_gpiochip():
11     """
12     Determines the handle of the GPIO chip device to access.
13
14     iterate through sysfs  to find a GPIO chip device with a driver known to be
15     used for userspace GPIO access.
16     """
17     for dev in Path("/sys/bus/gpio/devices").glob("gpiochip*"):
18         if Path(dev / "of_node/compatible").is_file():
19             drivers = set((dev / "of_node/compatible").read_text().split("\0"))
20             #   check if driver names are intended for userspace control
21             if drivers & {
22                 "raspberrypi,rp1-gpio",
23                 "raspberrypi,bcm2835-gpio",
24                 "raspberrypi,bcm2711-gpio",
25             }:
26                 return lgpio.gpiochip_open(int(dev.name[-1]))
27     # return chip0 as a fallback
28     return lgpio.gpiochip_open(0)
29
30
31 CHIP = _get_gpiochip()
32
33
34 class Pin:
35     """Pins dont exist in CPython so...lets make our own!"""
36
37     LOW = 0
38     HIGH = 1
39     OFF = LOW
40     ON = HIGH
41
42     # values of lg mode constants
43     PULL_NONE = 0x80
44     PULL_UP = 0x20
45     PULL_DOWN = 0x40
46     ACTIVE_LOW = 0x02
47
48     # drive mode lg constants
49     OPEN_DRAIN = 0x04
50     IN = 0x0100
51     OUT = 0x0200
52
53     # LG mode constants
54     _LG_ALERT = 0x400
55     _LG_GROUP = 0x800
56     _LG_MODES = IN | OUT | _LG_ALERT | _LG_GROUP
57     _LG_PULLS = PULL_NONE | PULL_UP | PULL_NONE | ACTIVE_LOW
58     _LG_DRIVES = OPEN_DRAIN
59
60     id = None
61     _value = LOW
62     _mode = IN
63
64     # we want exceptions
65     lgpio.exceptions = True
66
67     def __init__(self, bcm_number):
68         self.id = bcm_number
69
70     def __repr__(self):
71         return str(self.id)
72
73     def __eq__(self, other):
74         return self.id == other
75
76     def init(self, mode=IN, pull=None):
77         """Initialize the Pin"""
78         if mode is not None:
79             if mode == Pin.IN:
80                 self._mode = Pin.IN
81                 self._set_gpio_mode_in()
82             elif mode == self.OUT:
83                 self._mode = Pin.OUT
84                 Pin._check_result(lgpio.gpio_claim_output(CHIP, self.id, Pin.LOW))
85             else:
86                 raise RuntimeError(f"Invalid mode for pin: {self.id}")
87         if pull is not None:
88             if self._mode != Pin.IN:
89                 raise RuntimeError("Can only set pull resistor on input")
90             if pull in {Pin.PULL_UP, Pin.PULL_DOWN, Pin.PULL_NONE}:
91                 self._set_gpio_mode_in(lflags=pull)
92             else:
93                 raise RuntimeError(f"Invalid pull for pin: {self.id}")
94
95     def value(self, val=None):
96         """Set or return the Pin Value"""
97         if val is not None:
98             if val == Pin.LOW:
99                 self._value = val
100                 Pin._check_result(lgpio.gpio_write(CHIP, self.id, val))
101             elif val == Pin.HIGH:
102                 self._value = val
103                 Pin._check_result(lgpio.gpio_write(CHIP, self.id, val))
104             else:
105                 raise RuntimeError("Invalid value for pin")
106             return None
107         return Pin._check_result(lgpio.gpio_read(CHIP, self.id))
108
109     @staticmethod
110     def _check_result(result):
111         """
112         convert any result other than zero to a text message and pass it back
113         as a runtime exception.  Typical usage:  use the lgpio call as the
114         argument.
115         """
116         if result < 0:
117             raise RuntimeError(lgpio.error_text(result))
118         return result
119
120     def _set_gpio_mode_in(self, lflags=0):
121         """
122         claim a gpio as input, or modify the flags (PULL_UP, PULL_DOWN, ... )
123         """
124         # This gpio_free may seem redundant, but is required when
125         #  changing the line-flags of an already acquired input line
126         try:
127             lgpio.gpio_free(CHIP, self.id)
128         except lgpio.error:
129             pass
130         Pin._check_result(lgpio.gpio_claim_input(CHIP, self.id, lFlags=lflags))