ekfsm 0.13.0a183__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ekfsm might be problematic. Click here for more details.
- ekfsm/__init__.py +3 -14
- ekfsm/boards/oem/ekf/shu-shuttle.yaml +43 -0
- ekfsm/boards/oem/ekf/sq3-quartet.yaml +51 -37
- ekfsm/boards/oem/ekf/z1010.yaml +102 -0
- ekfsm/boards/oem/hitron/hdrc-300s.yaml +1 -1
- ekfsm/cli.py +32 -9
- ekfsm/config.py +14 -6
- ekfsm/core/__init__.py +13 -3
- ekfsm/core/components.py +7 -8
- ekfsm/core/connections.py +19 -0
- ekfsm/core/slots.py +6 -8
- ekfsm/core/sysfs.py +215 -25
- ekfsm/core/utils.py +128 -64
- ekfsm/devices/__init__.py +27 -7
- ekfsm/devices/buttons.py +251 -0
- ekfsm/devices/colorLed.py +110 -0
- ekfsm/devices/coretemp.py +35 -13
- ekfsm/devices/eeprom.py +73 -45
- ekfsm/devices/ekf_ccu_uc.py +76 -54
- ekfsm/devices/ekf_sur_led.py +6 -2
- ekfsm/devices/generic.py +200 -59
- ekfsm/devices/gpio.py +37 -27
- ekfsm/devices/iio.py +15 -31
- ekfsm/devices/iio_thermal_humidity.py +20 -13
- ekfsm/devices/imu.py +8 -4
- ekfsm/devices/io4edge.py +185 -0
- ekfsm/devices/ledArray.py +54 -0
- ekfsm/devices/mux.py +46 -8
- ekfsm/devices/pixelDisplay.py +141 -0
- ekfsm/devices/pmbus.py +74 -101
- ekfsm/devices/smbios.py +28 -8
- ekfsm/devices/smbus.py +1 -1
- ekfsm/devices/thermal_humidity.py +80 -0
- ekfsm/devices/toggles.py +90 -0
- ekfsm/devices/utils.py +52 -8
- ekfsm/devices/watchdog.py +79 -0
- ekfsm/exceptions.py +28 -7
- ekfsm/lock.py +48 -21
- ekfsm/simctrl.py +37 -83
- ekfsm/system.py +89 -73
- ekfsm/utils.py +44 -0
- {ekfsm-0.13.0a183.dist-info → ekfsm-1.5.0.dist-info}/METADATA +12 -6
- ekfsm-1.5.0.dist-info/RECORD +57 -0
- ekfsm-0.13.0a183.dist-info/RECORD +0 -45
- {ekfsm-0.13.0a183.dist-info → ekfsm-1.5.0.dist-info}/WHEEL +0 -0
- {ekfsm-0.13.0a183.dist-info → ekfsm-1.5.0.dist-info}/entry_points.txt +0 -0
ekfsm/devices/generic.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import TYPE_CHECKING, Callable
|
|
3
|
+
|
|
2
4
|
from munch import Munch
|
|
5
|
+
|
|
3
6
|
from ekfsm.core.components import SysTree
|
|
4
|
-
from ekfsm.core.sysfs import
|
|
5
|
-
from ekfsm.exceptions import ConfigError
|
|
6
|
-
from pathlib import Path
|
|
7
|
+
from ekfsm.core.sysfs import SysfsDevice, sysfs_root
|
|
8
|
+
from ekfsm.exceptions import ConfigError, SysFSError, UnsupportedModeError
|
|
7
9
|
|
|
8
10
|
if TYPE_CHECKING:
|
|
9
|
-
from ekfsm.core.components import
|
|
11
|
+
from ekfsm.core.components import HWModule
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class Device(SysTree):
|
|
@@ -26,26 +28,25 @@ class Device(SysTree):
|
|
|
26
28
|
super().__init__(name, abort=abort)
|
|
27
29
|
self.parent = parent
|
|
28
30
|
self.device_args = kwargs
|
|
29
|
-
self.logger.debug(f"Device: {name} {kwargs}")
|
|
30
31
|
|
|
31
32
|
if children:
|
|
32
33
|
self.children = children
|
|
33
34
|
|
|
34
|
-
#
|
|
35
|
-
self.hw_module = self.root
|
|
35
|
+
# Needs to be set during init because root will be changed after tree is complete
|
|
36
|
+
self.hw_module = self.root # pyright: ignore[reportAttributeAccessIssue]
|
|
36
37
|
|
|
37
|
-
#
|
|
38
|
+
# I2C initialization
|
|
38
39
|
if not hasattr(self, "sysfs_device") or self.sysfs_device is None:
|
|
39
|
-
self.sysfs_device:
|
|
40
|
+
self.sysfs_device: SysfsDevice | None = None
|
|
40
41
|
|
|
41
|
-
#
|
|
42
|
+
# Post-initialization steps
|
|
42
43
|
self._provides_attrs = kwargs.get("provides", {})
|
|
43
|
-
self.provides = self.__post_init__(Munch(self._provides_attrs))
|
|
44
|
+
self.provides = self.__post_init__(Munch(self._provides_attrs), abort)
|
|
44
45
|
|
|
45
|
-
def __post_init__(self, provides: Munch) -> Munch:
|
|
46
|
+
def __post_init__(self, provides: Munch, abort) -> Munch:
|
|
46
47
|
for key, fields in provides.items():
|
|
47
48
|
if isinstance(fields, dict):
|
|
48
|
-
provides[key] = self.__post_init__(Munch(fields))
|
|
49
|
+
provides[key] = self.__post_init__(Munch(fields), abort)
|
|
49
50
|
elif isinstance(fields, list | str):
|
|
50
51
|
provides[key] = Munch()
|
|
51
52
|
|
|
@@ -53,59 +54,182 @@ class Device(SysTree):
|
|
|
53
54
|
fields = [fields]
|
|
54
55
|
|
|
55
56
|
while fields:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
interface = fields.pop()
|
|
58
|
+
|
|
59
|
+
# TODO: Check if this is superfluous after schema validation
|
|
60
|
+
if isinstance(interface, dict):
|
|
61
|
+
name = list(interface.keys())[0]
|
|
62
|
+
|
|
59
63
|
try:
|
|
60
|
-
func = list(
|
|
64
|
+
func = list(interface.values())[0]
|
|
61
65
|
except IndexError:
|
|
62
66
|
raise ConfigError(
|
|
63
67
|
f"{self.name}: No function given for interface {name}."
|
|
64
68
|
)
|
|
69
|
+
|
|
65
70
|
if not hasattr(self, func):
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
if abort:
|
|
72
|
+
raise NotImplementedError(
|
|
73
|
+
f"{self.name}: Function {func} for interface {name} not implemented."
|
|
74
|
+
)
|
|
75
|
+
continue
|
|
76
|
+
|
|
69
77
|
provides[key].update({name: getattr(self, func)})
|
|
70
78
|
else:
|
|
71
|
-
if not hasattr(self,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
if not hasattr(self, interface):
|
|
80
|
+
if abort:
|
|
81
|
+
raise NotImplementedError(
|
|
82
|
+
f"{self.name}: Function {interface} for provider {key} not implemented."
|
|
83
|
+
)
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
provides[key].update({interface: getattr(self, interface)})
|
|
76
87
|
|
|
77
88
|
return provides
|
|
78
89
|
|
|
90
|
+
@property
|
|
91
|
+
def sysfs(self):
|
|
92
|
+
"""
|
|
93
|
+
Access sysfs device for device
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
Sysfs device for device
|
|
98
|
+
|
|
99
|
+
Raises
|
|
100
|
+
------
|
|
101
|
+
SysFSError
|
|
102
|
+
If the SysFSDevice does not exist
|
|
103
|
+
"""
|
|
104
|
+
if self.sysfs_device is None:
|
|
105
|
+
raise SysFSError(f"No sysfs device attached to device {self.name}")
|
|
106
|
+
|
|
107
|
+
return self.sysfs_device
|
|
108
|
+
|
|
109
|
+
def read_attr(self, attr: str, mode: str = "utf", strip: bool = True):
|
|
110
|
+
match mode:
|
|
111
|
+
case "utf":
|
|
112
|
+
return self.sysfs.read_utf8(attr, strip)
|
|
113
|
+
case "bytes":
|
|
114
|
+
return self.sysfs.read_bytes(attr)
|
|
115
|
+
case "float":
|
|
116
|
+
return self.sysfs.read_float(attr)
|
|
117
|
+
case "int":
|
|
118
|
+
return self.sysfs.read_int(attr)
|
|
119
|
+
case "hex":
|
|
120
|
+
return self.sysfs.read_hex(attr)
|
|
121
|
+
case _:
|
|
122
|
+
raise UnsupportedModeError(f"Mode {mode} is not supported")
|
|
123
|
+
|
|
124
|
+
def read_attr_or_default(
|
|
125
|
+
self, attr: str, mode: str = "utf", strip: bool = True, default=None
|
|
126
|
+
):
|
|
127
|
+
try:
|
|
128
|
+
return self.read_attr(attr, mode, strip)
|
|
129
|
+
except UnsupportedModeError:
|
|
130
|
+
raise
|
|
131
|
+
except Exception:
|
|
132
|
+
if default is not None:
|
|
133
|
+
return default
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
def read_sysfs_bytes(self, attr) -> bytes:
|
|
137
|
+
if len(attr) <= 0:
|
|
138
|
+
raise SysFSError("sysfs attribute name too short")
|
|
139
|
+
|
|
140
|
+
return self.sysfs.read_bytes(attr)
|
|
141
|
+
|
|
79
142
|
def read_sysfs_attr_bytes(self, attr: str) -> bytes | None:
|
|
80
|
-
|
|
143
|
+
"""
|
|
144
|
+
Read a sysfs attribute as bytes.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
attr
|
|
149
|
+
The sysfs attribute to read.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
content: bytes
|
|
154
|
+
The contents of the sysfs attribute as bytes.
|
|
155
|
+
None:
|
|
156
|
+
If the sysfs device is not set or the attribute does not exist.
|
|
157
|
+
"""
|
|
158
|
+
if (
|
|
159
|
+
self.sysfs_device is not None
|
|
160
|
+
and len(attr) != 0
|
|
161
|
+
and attr in [x.name for x in self.sysfs_device.attributes]
|
|
162
|
+
):
|
|
81
163
|
return self.sysfs_device.read_attr_bytes(attr)
|
|
82
164
|
return None
|
|
83
165
|
|
|
84
166
|
def read_sysfs_attr_utf8(self, attr: str) -> str | None:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
167
|
+
"""
|
|
168
|
+
Read a sysfs attribute as UTF-8 string.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
attr
|
|
173
|
+
The sysfs attribute to read.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
content: str
|
|
178
|
+
The contents of the sysfs attribute as UTF-8 string.
|
|
179
|
+
None:
|
|
180
|
+
If the sysfs device is not set or the attribute does not exist.
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
return self.sysfs.read_utf8(attr)
|
|
184
|
+
except Exception:
|
|
185
|
+
return None
|
|
88
186
|
|
|
89
187
|
def write_sysfs_attr(self, attr: str, data: str | bytes) -> None:
|
|
188
|
+
"""
|
|
189
|
+
Write data to a sysfs attribute.
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
attr
|
|
194
|
+
The sysfs attribute to write to.
|
|
195
|
+
data
|
|
196
|
+
The data to write to the sysfs attribute.
|
|
197
|
+
"""
|
|
90
198
|
if self.sysfs_device and len(attr) != 0:
|
|
91
199
|
return self.sysfs_device.write_attr(attr, data)
|
|
92
200
|
return None
|
|
93
201
|
|
|
94
202
|
@property
|
|
95
|
-
def hw_module(self) -> "
|
|
96
|
-
|
|
203
|
+
def hw_module(self) -> "HWModule":
|
|
204
|
+
"""
|
|
205
|
+
Get or set the HWModule instance that this device belongs to.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
hw_module: optional
|
|
210
|
+
The HWModule instance to set.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
HWModule
|
|
215
|
+
The HWModule instance that this device belongs to.
|
|
216
|
+
None
|
|
217
|
+
If used as a setter.
|
|
218
|
+
"""
|
|
219
|
+
from ekfsm.core.components import HWModule
|
|
97
220
|
|
|
98
|
-
if isinstance(self._hw_module,
|
|
221
|
+
if isinstance(self._hw_module, HWModule):
|
|
99
222
|
return self._hw_module
|
|
100
223
|
else:
|
|
101
|
-
raise RuntimeError("Device is not a child of
|
|
224
|
+
raise RuntimeError("Device is not a child of HWModule")
|
|
102
225
|
|
|
103
226
|
@hw_module.setter
|
|
104
|
-
def hw_module(self, hw_module: "
|
|
227
|
+
def hw_module(self, hw_module: "HWModule") -> None:
|
|
105
228
|
self._hw_module = hw_module
|
|
106
229
|
|
|
107
230
|
def get_i2c_chip_addr(self) -> int:
|
|
108
|
-
|
|
231
|
+
if self.parent is None:
|
|
232
|
+
raise ConfigError(f"{self.name}: Device must have a parent to get I2C chip address")
|
|
109
233
|
|
|
110
234
|
chip_addr = self.device_args.get("addr")
|
|
111
235
|
if chip_addr is None:
|
|
@@ -139,21 +263,27 @@ class Device(SysTree):
|
|
|
139
263
|
|
|
140
264
|
return chip_addr
|
|
141
265
|
|
|
142
|
-
def get_i2c_sysfs_device(
|
|
143
|
-
|
|
266
|
+
def get_i2c_sysfs_device(
|
|
267
|
+
self, addr: int, driver_required=True, find_driver: Callable | None = None
|
|
268
|
+
) -> SysfsDevice:
|
|
269
|
+
from ekfsm.core.components import HWModule
|
|
144
270
|
|
|
145
271
|
parent = self.parent
|
|
146
|
-
|
|
272
|
+
if parent is None:
|
|
273
|
+
raise ConfigError(f"{self.name}: Device must have a parent to get I2C sysfs device")
|
|
147
274
|
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
275
|
+
# If parent is a HWModule, we can get the i2c bus from the master device
|
|
276
|
+
# XXX: Does this still hold true after refactoring?
|
|
277
|
+
if isinstance(parent, HWModule):
|
|
278
|
+
i2c_bus_path = self.__master_i2c_bus()
|
|
151
279
|
else:
|
|
152
280
|
# otherwise the parent must be a MuxChannel
|
|
153
281
|
from ekfsm.devices.mux import MuxChannel
|
|
154
282
|
|
|
155
|
-
|
|
156
|
-
|
|
283
|
+
if not isinstance(parent, MuxChannel):
|
|
284
|
+
raise ConfigError(f"{self.name}: Parent must be MuxChannel when not a HWModule")
|
|
285
|
+
if parent.sysfs_device is None:
|
|
286
|
+
raise ConfigError(f"{self.name}: Parent MuxChannel must have a sysfs_device")
|
|
157
287
|
i2c_bus_path = parent.sysfs_device.path
|
|
158
288
|
|
|
159
289
|
# search for device with addr
|
|
@@ -163,29 +293,34 @@ class Device(SysTree):
|
|
|
163
293
|
and not (entry / "new_device").exists() # skip bus entries
|
|
164
294
|
and (entry / "name").exists()
|
|
165
295
|
):
|
|
166
|
-
#
|
|
296
|
+
# PRP devices unfortunately do not readily expose the underlying I2C address of the device like
|
|
297
|
+
# regular I2C devices that follow the `${I2C_BUS}-${ADDR}` pattern. To address this issue, we
|
|
298
|
+
# initialize the ACPI _STR object for each PRP device with the necessary information, which is
|
|
299
|
+
# accessible in the `${DEVICE_SYSFS_PATH}/firmware_node/description` file.
|
|
167
300
|
if (entry / "firmware_node").exists() and (
|
|
168
301
|
entry / "firmware_node" / "description"
|
|
169
302
|
).exists():
|
|
170
303
|
description = (
|
|
171
304
|
(entry / "firmware_node/description").read_text().strip()
|
|
172
305
|
)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
306
|
+
acpi_addr = int(description.split(" - ")[0], 16)
|
|
307
|
+
|
|
308
|
+
if acpi_addr == addr:
|
|
309
|
+
return SysfsDevice(entry, driver_required, find_driver)
|
|
176
310
|
|
|
177
|
-
#
|
|
311
|
+
# For regular non-PRP devices, the address is contained in the directory name (e.g. 2-0018).
|
|
178
312
|
else:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
313
|
+
acpi_addr = int(entry.name.split("-")[1], 16)
|
|
314
|
+
|
|
315
|
+
if acpi_addr == addr:
|
|
316
|
+
return SysfsDevice(entry, driver_required, find_driver)
|
|
182
317
|
|
|
183
318
|
raise FileNotFoundError(
|
|
184
319
|
f"Device with address 0x{addr:x} not found in {i2c_bus_path}"
|
|
185
320
|
)
|
|
186
321
|
|
|
187
322
|
@staticmethod
|
|
188
|
-
def
|
|
323
|
+
def __master_i2c_get_config(master: "HWModule") -> dict:
|
|
189
324
|
if (
|
|
190
325
|
master.config.get("bus_masters") is not None
|
|
191
326
|
and master.config["bus_masters"].get("i2c") is not None
|
|
@@ -194,12 +329,13 @@ class Device(SysTree):
|
|
|
194
329
|
else:
|
|
195
330
|
raise ConfigError("Master definition incomplete")
|
|
196
331
|
|
|
197
|
-
def
|
|
332
|
+
def __master_i2c_bus(self) -> Path:
|
|
198
333
|
if self.hw_module.is_master:
|
|
199
334
|
# we are the master
|
|
200
335
|
master = self.hw_module
|
|
201
336
|
master_key = "MASTER_LOCAL_DEFAULT"
|
|
202
337
|
override_master_key = self.device_args.get("i2c_master", None)
|
|
338
|
+
|
|
203
339
|
if override_master_key is not None:
|
|
204
340
|
master_key = override_master_key
|
|
205
341
|
else:
|
|
@@ -212,18 +348,20 @@ class Device(SysTree):
|
|
|
212
348
|
master = self.hw_module.slot.master
|
|
213
349
|
master_key = self.hw_module.slot.slot_type.name
|
|
214
350
|
|
|
215
|
-
i2c_masters = self.
|
|
351
|
+
i2c_masters = self.__master_i2c_get_config(master)
|
|
216
352
|
|
|
217
353
|
if i2c_masters.get(master_key) is not None:
|
|
218
354
|
dir = sysfs_root() / Path(i2c_masters[master_key])
|
|
219
355
|
bus_dirs = list(dir.glob("i2c-*"))
|
|
356
|
+
|
|
220
357
|
if len(bus_dirs) == 1:
|
|
221
358
|
return bus_dirs[0]
|
|
222
359
|
elif len(bus_dirs) > 1:
|
|
223
|
-
raise ConfigError(f"Multiple master
|
|
224
|
-
|
|
360
|
+
raise ConfigError(f"Multiple master I2C buses found for {master_key}")
|
|
361
|
+
|
|
362
|
+
raise ConfigError(f"No master I2C bus found for {master_key}")
|
|
225
363
|
else:
|
|
226
|
-
raise ConfigError(f"Master
|
|
364
|
+
raise ConfigError(f"Master I2C bus not found for {master_key}")
|
|
227
365
|
|
|
228
366
|
def get_i2c_bus_number(self) -> int:
|
|
229
367
|
"""
|
|
@@ -240,11 +378,14 @@ class Device(SysTree):
|
|
|
240
378
|
parent_path = self.parent.sysfs_device.path
|
|
241
379
|
else:
|
|
242
380
|
parent_path = self.sysfs_device.path.parent
|
|
381
|
+
|
|
243
382
|
if parent_path.is_symlink():
|
|
244
383
|
parent_path = parent_path.readlink()
|
|
384
|
+
|
|
245
385
|
bus_number = parent_path.name.split("-")[1]
|
|
386
|
+
|
|
246
387
|
return int(bus_number)
|
|
247
388
|
|
|
248
389
|
def __repr__(self) -> str:
|
|
249
390
|
sysfs_path = getattr(self.sysfs_device, "path", "")
|
|
250
|
-
return f"{self.name};
|
|
391
|
+
return f"{self.name}; Path: {sysfs_path}"
|
ekfsm/devices/gpio.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
1
|
import os
|
|
3
2
|
import re
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
import gpiod
|
|
7
|
-
from gpiod.chip import LineSettings
|
|
8
7
|
from gpiod.line import Direction, Value
|
|
8
|
+
from gpiod.line_settings import LineSettings
|
|
9
9
|
from more_itertools import first_true
|
|
10
10
|
|
|
11
11
|
from ekfsm.core.components import SysTree
|
|
12
|
-
from ekfsm.exceptions import GPIOError
|
|
12
|
+
from ekfsm.exceptions import ConfigError, GPIOError
|
|
13
13
|
from ekfsm.log import ekfsm_logger
|
|
14
14
|
|
|
15
|
-
from .generic import Device
|
|
16
15
|
from ..core.probe import ProbeableDevice
|
|
16
|
+
from .generic import Device
|
|
17
17
|
|
|
18
18
|
gpio_pat = re.compile(r"gpiochip\d+")
|
|
19
19
|
|
|
@@ -24,7 +24,13 @@ def get_gpio_major_minor(path: Path) -> tuple[int, int]:
|
|
|
24
24
|
dev = d / "dev"
|
|
25
25
|
if dev.exists():
|
|
26
26
|
content = dev.read_text().strip()
|
|
27
|
-
|
|
27
|
+
try:
|
|
28
|
+
major, minor = map(int, content.split(":"))
|
|
29
|
+
except Exception as e:
|
|
30
|
+
raise GPIOError(
|
|
31
|
+
GPIOError.ErrorType.NO_MAJOR_MINOR,
|
|
32
|
+
f"No minor/major number found for GPIO device at {path}",
|
|
33
|
+
) from e
|
|
28
34
|
return major, minor
|
|
29
35
|
|
|
30
36
|
raise GPIOError(
|
|
@@ -51,11 +57,12 @@ def find_gpio_dev_with_major_minor(major: int, minor: int) -> Path | None:
|
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
class GPIO(Device):
|
|
54
|
-
|
|
55
60
|
def __init__(
|
|
56
61
|
self,
|
|
57
62
|
name: str,
|
|
58
63
|
parent: SysTree | None = None,
|
|
64
|
+
children: Device | None = None,
|
|
65
|
+
abort: bool = False,
|
|
59
66
|
*args,
|
|
60
67
|
**kwargs,
|
|
61
68
|
):
|
|
@@ -63,6 +70,7 @@ class GPIO(Device):
|
|
|
63
70
|
name,
|
|
64
71
|
parent,
|
|
65
72
|
None,
|
|
73
|
+
abort,
|
|
66
74
|
*args,
|
|
67
75
|
**kwargs,
|
|
68
76
|
)
|
|
@@ -70,23 +78,18 @@ class GPIO(Device):
|
|
|
70
78
|
major, minor = self._find_gpio_dev(parent, *args, **kwargs)
|
|
71
79
|
self.gpio = find_gpio_dev_with_major_minor(major, minor)
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
if self.gpio is None:
|
|
82
|
+
raise GPIOError(GPIOError.ErrorType.NO_MATCHING_DEVICE, f"{self.name}: GPIO device not found")
|
|
74
83
|
match = re.search(r"\d+", self.gpio.name)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
84
|
+
|
|
85
|
+
if not match:
|
|
86
|
+
raise GPIOError(GPIOError.ErrorType.NO_MATCHING_DEVICE, "Failed to find matching device")
|
|
87
|
+
|
|
88
|
+
self.number: int = int(match.group().strip())
|
|
81
89
|
|
|
82
90
|
self.init_dev()
|
|
83
91
|
|
|
84
|
-
def _find_gpio_dev(
|
|
85
|
-
self,
|
|
86
|
-
parent: SysTree | None = None,
|
|
87
|
-
*args,
|
|
88
|
-
**kwargs,
|
|
89
|
-
) -> tuple[int, int]:
|
|
92
|
+
def _find_gpio_dev(self, parent: SysTree | None = None, *args, **kwargs) -> tuple[int, int]:
|
|
90
93
|
self.addr = self.get_i2c_chip_addr()
|
|
91
94
|
self.logger.debug(f"GPIO: {self.addr}")
|
|
92
95
|
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
@@ -97,9 +100,13 @@ class GPIO(Device):
|
|
|
97
100
|
try:
|
|
98
101
|
self.dev = gpiod.Chip(str(self.gpio))
|
|
99
102
|
self.initialized = True
|
|
103
|
+
return
|
|
100
104
|
except FileNotFoundError:
|
|
105
|
+
self.initialized = False
|
|
101
106
|
raise FileNotFoundError(f"{self.gpio} does not exist")
|
|
102
107
|
|
|
108
|
+
self.initialized = False
|
|
109
|
+
|
|
103
110
|
def num_lines(self) -> int:
|
|
104
111
|
"""
|
|
105
112
|
Get number of GPIO lines available on the device.
|
|
@@ -153,9 +160,7 @@ class GPIO(Device):
|
|
|
153
160
|
default=None,
|
|
154
161
|
)
|
|
155
162
|
) is not None:
|
|
156
|
-
raise GPIOError(
|
|
157
|
-
GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid."
|
|
158
|
-
)
|
|
163
|
+
raise GPIOError(GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid.")
|
|
159
164
|
|
|
160
165
|
def set_direction(self, pin: int, direction: bool) -> None:
|
|
161
166
|
"""
|
|
@@ -187,10 +192,12 @@ class GPIOExpander(GPIO):
|
|
|
187
192
|
self,
|
|
188
193
|
name: str,
|
|
189
194
|
parent: SysTree | None,
|
|
195
|
+
children: Device | None = None,
|
|
196
|
+
abort: bool = False,
|
|
190
197
|
*args,
|
|
191
198
|
**kwargs,
|
|
192
199
|
):
|
|
193
|
-
super().__init__(name, parent, None, *args, **kwargs)
|
|
200
|
+
super().__init__(name, parent, None, abort, *args, **kwargs)
|
|
194
201
|
|
|
195
202
|
def __str__(self) -> str:
|
|
196
203
|
return (
|
|
@@ -204,15 +211,18 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
|
204
211
|
self,
|
|
205
212
|
name: str,
|
|
206
213
|
parent: SysTree | None,
|
|
214
|
+
children: Device | None = None,
|
|
215
|
+
abort: bool = False,
|
|
207
216
|
*args,
|
|
208
217
|
**kwargs,
|
|
209
218
|
):
|
|
210
|
-
super().__init__(name, parent, None, *args, **kwargs)
|
|
219
|
+
super().__init__(name, parent, None, abort, *args, **kwargs)
|
|
211
220
|
|
|
212
221
|
def probe(self, *args, **kwargs) -> bool:
|
|
213
|
-
from ekfsm.core import
|
|
222
|
+
from ekfsm.core import HWModule
|
|
214
223
|
|
|
215
|
-
|
|
224
|
+
if not isinstance(self.hw_module, HWModule):
|
|
225
|
+
raise ConfigError(f"{self.name}: hw_module must be a HWModule instance")
|
|
216
226
|
id, _ = self.read_board_id_rev()
|
|
217
227
|
self.logger.debug(f"Probing EKFIdentificationIOExpander: {id}")
|
|
218
228
|
|
|
@@ -245,7 +255,7 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
|
245
255
|
)
|
|
246
256
|
|
|
247
257
|
def revision(self) -> str:
|
|
248
|
-
|
|
258
|
+
_, rev = self.read_board_id_rev()
|
|
249
259
|
return str(rev)
|
|
250
260
|
|
|
251
261
|
def read_id_gpio_inputs(self) -> int:
|
ekfsm/devices/iio.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from ekfsm.exceptions import ConversionError, SysFSError
|
|
2
2
|
|
|
3
|
+
from .generic import SysfsDevice
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
def iio_get_in_value(dev: SysfsDevice, attrset: str) -> float:
|
|
5
7
|
"""
|
|
6
8
|
Calculate a value from an IIO in_* attribute set, using the _raw, _scale and _offset attributes (if present).
|
|
7
9
|
Typical name of attrset are "in_temp" or "in_voltage0".
|
|
@@ -26,33 +28,15 @@ def iio_get_in_value(dev: SysFSDevice, attrset: str) -> float:
|
|
|
26
28
|
if the neiter _input nor _raw attribute is found
|
|
27
29
|
|
|
28
30
|
"""
|
|
29
|
-
# check if _input exists
|
|
30
|
-
try:
|
|
31
|
-
content = dev.read_attr_utf8(f"{attrset}_input")
|
|
32
|
-
return float(content)
|
|
33
|
-
except StopIteration:
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
# check if _raw exists
|
|
37
31
|
try:
|
|
38
|
-
content = dev.
|
|
39
|
-
except
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
pass
|
|
50
|
-
|
|
51
|
-
# use scale if present
|
|
52
|
-
try:
|
|
53
|
-
content = dev.read_attr_utf8(f"{attrset}_scale")
|
|
54
|
-
raw *= float(content)
|
|
55
|
-
except StopIteration:
|
|
56
|
-
pass
|
|
57
|
-
|
|
58
|
-
return raw
|
|
32
|
+
content = dev.read_float(f"{attrset}_input")
|
|
33
|
+
except (SysFSError, ConversionError):
|
|
34
|
+
try:
|
|
35
|
+
raw = dev.read_float(f"{attrset}_raw")
|
|
36
|
+
offset = dev.read_float(f"{attrset}_offset")
|
|
37
|
+
scale = dev.read_float(f"{attrset}_scale")
|
|
38
|
+
content = (raw + offset) * scale
|
|
39
|
+
except (SysFSError, ConversionError) as e:
|
|
40
|
+
raise FileNotFoundError from e
|
|
41
|
+
|
|
42
|
+
return content
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
1
|
from ekfsm.core.components import SysTree
|
|
4
2
|
from ekfsm.log import ekfsm_logger
|
|
3
|
+
from ekfsm.utils import next_or_raise
|
|
5
4
|
|
|
6
|
-
from ..core.sysfs import
|
|
7
|
-
|
|
5
|
+
from ..core.sysfs import list_sysfs_attributes
|
|
8
6
|
from .generic import Device
|
|
9
7
|
from .iio import iio_get_in_value
|
|
10
8
|
|
|
@@ -13,6 +11,14 @@ class IIOThermalHumidity(Device):
|
|
|
13
11
|
"""
|
|
14
12
|
Device for IIO thermal and/or humidity sensors.
|
|
15
13
|
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
name
|
|
17
|
+
The name of the device.
|
|
18
|
+
parent
|
|
19
|
+
The parent device of the IIOThermalHumidity device. If None, no parent is created.
|
|
20
|
+
children
|
|
21
|
+
The children of the IIOThermalHumidity device. If None, no children are created.
|
|
16
22
|
"""
|
|
17
23
|
|
|
18
24
|
def __init__(
|
|
@@ -20,22 +26,23 @@ class IIOThermalHumidity(Device):
|
|
|
20
26
|
name: str,
|
|
21
27
|
parent: SysTree | None = None,
|
|
22
28
|
children: list[Device] | None = None,
|
|
29
|
+
abort: bool = False,
|
|
23
30
|
*args,
|
|
24
31
|
**kwargs,
|
|
25
32
|
):
|
|
26
33
|
self.logger = ekfsm_logger("IIOThermalHumidity:" + name)
|
|
27
|
-
super().__init__(name, parent, children, **kwargs)
|
|
34
|
+
super().__init__(name, parent, children, abort, *args, **kwargs)
|
|
28
35
|
self.addr = self.get_i2c_chip_addr()
|
|
29
|
-
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
36
|
+
self.sysfs_device = self.get_i2c_sysfs_device(self.addr, False)
|
|
37
|
+
|
|
38
|
+
# TODO: We can just search the attributes directly
|
|
39
|
+
iio_dir = self.sysfs.path.glob("iio:device*")
|
|
40
|
+
attrs = next_or_raise(iio_dir, FileNotFoundError("IIO entry not found"))
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
if len(dir) == 0:
|
|
33
|
-
raise FileNotFoundError("iio entry not found")
|
|
34
|
-
self.iio_sysfs = SysFSDevice(dir[0])
|
|
35
|
-
self.logger.debug(f"iio: {self.iio_sysfs.path}")
|
|
42
|
+
self.sysfs_device.extend_attributes(list_sysfs_attributes(attrs))
|
|
36
43
|
|
|
37
44
|
def temperature(self) -> float:
|
|
38
|
-
return iio_get_in_value(self.
|
|
45
|
+
return iio_get_in_value(self.sysfs, "in_temp") / 1000.0
|
|
39
46
|
|
|
40
47
|
def humidity(self) -> float:
|
|
41
|
-
return iio_get_in_value(self.
|
|
48
|
+
return iio_get_in_value(self.sysfs, "in_humidityrelative") / 1000.0
|