ekfsm 1.1.0a15.post1__py3-none-any.whl → 1.3.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/boards/oem/hitron/hdrc-300s.yaml +1 -1
- ekfsm/cli.py +6 -8
- ekfsm/config.py +14 -6
- ekfsm/core/__init__.py +5 -5
- ekfsm/core/components.py +4 -4
- ekfsm/core/slots.py +6 -13
- ekfsm/core/sysfs.py +183 -13
- ekfsm/core/utils.py +104 -64
- ekfsm/devices/__init__.py +8 -7
- ekfsm/devices/coretemp.py +11 -10
- ekfsm/devices/eeprom.py +69 -39
- ekfsm/devices/ekf_ccu_uc.py +58 -47
- ekfsm/devices/ekf_sur_led.py +6 -2
- ekfsm/devices/generic.py +141 -88
- ekfsm/devices/gpio.py +33 -25
- ekfsm/devices/iio.py +15 -31
- ekfsm/devices/iio_thermal_humidity.py +12 -13
- ekfsm/devices/mux.py +11 -6
- ekfsm/devices/pmbus.py +62 -70
- ekfsm/devices/smbios.py +10 -8
- ekfsm/devices/smbus.py +1 -1
- ekfsm/devices/utils.py +0 -9
- ekfsm/exceptions.py +28 -7
- ekfsm/lock.py +48 -21
- ekfsm/simctrl.py +37 -83
- ekfsm/system.py +45 -102
- ekfsm/utils.py +44 -0
- {ekfsm-1.1.0a15.post1.dist-info → ekfsm-1.3.0.dist-info}/METADATA +8 -5
- ekfsm-1.3.0.dist-info/RECORD +46 -0
- ekfsm-1.1.0a15.post1.dist-info/RECORD +0 -45
- {ekfsm-1.1.0a15.post1.dist-info → ekfsm-1.3.0.dist-info}/WHEEL +0 -0
- {ekfsm-1.1.0a15.post1.dist-info → ekfsm-1.3.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,29 +54,85 @@ 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
|
-
raise ConfigError(
|
|
63
|
-
|
|
64
|
-
)
|
|
66
|
+
raise ConfigError(f"{self.name}: No function given for interface {name}.")
|
|
67
|
+
|
|
65
68
|
if not hasattr(self, func):
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
if abort:
|
|
70
|
+
raise NotImplementedError(
|
|
71
|
+
f"{self.name}: Function {func} for interface {name} not implemented."
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
|
|
69
75
|
provides[key].update({name: getattr(self, func)})
|
|
70
76
|
else:
|
|
71
|
-
if not hasattr(self,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
if not hasattr(self, interface):
|
|
78
|
+
if abort:
|
|
79
|
+
raise NotImplementedError(
|
|
80
|
+
f"{self.name}: Function {interface} for provider {key} not implemented."
|
|
81
|
+
)
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
provides[key].update({interface: getattr(self, interface)})
|
|
76
85
|
|
|
77
86
|
return provides
|
|
78
87
|
|
|
88
|
+
@property
|
|
89
|
+
def sysfs(self):
|
|
90
|
+
"""
|
|
91
|
+
Access sysfs device for device
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
Sysfs device for device
|
|
96
|
+
|
|
97
|
+
Raises
|
|
98
|
+
------
|
|
99
|
+
SysFSError
|
|
100
|
+
If the SysFSDevice does not exist
|
|
101
|
+
"""
|
|
102
|
+
if self.sysfs_device is None:
|
|
103
|
+
raise SysFSError(f"No sysfs device attached to device {self.name}")
|
|
104
|
+
|
|
105
|
+
return self.sysfs_device
|
|
106
|
+
|
|
107
|
+
def read_attr(self, attr: str, mode: str = "utf", strip: bool = True):
|
|
108
|
+
match mode:
|
|
109
|
+
case "utf":
|
|
110
|
+
return self.sysfs.read_utf8(attr, strip)
|
|
111
|
+
case "bytes":
|
|
112
|
+
return self.sysfs.read_bytes(attr)
|
|
113
|
+
case "float":
|
|
114
|
+
return self.sysfs.read_float(attr)
|
|
115
|
+
case "int":
|
|
116
|
+
return self.sysfs.read_int(attr)
|
|
117
|
+
case _:
|
|
118
|
+
raise UnsupportedModeError(f"Mode {mode} is not supported")
|
|
119
|
+
|
|
120
|
+
def read_attr_or_default(self, attr: str, mode: str = "utf", strip: bool = True, default=None):
|
|
121
|
+
try:
|
|
122
|
+
return self.read_attr(attr, mode, strip)
|
|
123
|
+
except UnsupportedModeError:
|
|
124
|
+
raise
|
|
125
|
+
except Exception:
|
|
126
|
+
if default is not None:
|
|
127
|
+
return default
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
def read_sysfs_bytes(self, attr) -> bytes:
|
|
131
|
+
if len(attr) <= 0:
|
|
132
|
+
raise SysFSError("sysfs attribute name too short")
|
|
133
|
+
|
|
134
|
+
return self.sysfs.read_bytes(attr)
|
|
135
|
+
|
|
79
136
|
def read_sysfs_attr_bytes(self, attr: str) -> bytes | None:
|
|
80
137
|
"""
|
|
81
138
|
Read a sysfs attribute as bytes.
|
|
@@ -92,7 +149,7 @@ class Device(SysTree):
|
|
|
92
149
|
None:
|
|
93
150
|
If the sysfs device is not set or the attribute does not exist.
|
|
94
151
|
"""
|
|
95
|
-
if self.sysfs_device and len(attr) != 0:
|
|
152
|
+
if self.sysfs_device is not None and len(attr) != 0 and attr in [x.name for x in self.sysfs_device.attributes]:
|
|
96
153
|
return self.sysfs_device.read_attr_bytes(attr)
|
|
97
154
|
return None
|
|
98
155
|
|
|
@@ -112,9 +169,10 @@ class Device(SysTree):
|
|
|
112
169
|
None:
|
|
113
170
|
If the sysfs device is not set or the attribute does not exist.
|
|
114
171
|
"""
|
|
115
|
-
|
|
116
|
-
return self.
|
|
117
|
-
|
|
172
|
+
try:
|
|
173
|
+
return self.sysfs.read_utf8(attr)
|
|
174
|
+
except Exception:
|
|
175
|
+
return None
|
|
118
176
|
|
|
119
177
|
def write_sysfs_attr(self, attr: str, data: str | bytes) -> None:
|
|
120
178
|
"""
|
|
@@ -132,31 +190,31 @@ class Device(SysTree):
|
|
|
132
190
|
return None
|
|
133
191
|
|
|
134
192
|
@property
|
|
135
|
-
def hw_module(self) -> "
|
|
193
|
+
def hw_module(self) -> "HWModule":
|
|
136
194
|
"""
|
|
137
|
-
Get or set the
|
|
195
|
+
Get or set the HWModule instance that this device belongs to.
|
|
138
196
|
|
|
139
197
|
Parameters
|
|
140
198
|
----------
|
|
141
199
|
hw_module: optional
|
|
142
|
-
The
|
|
200
|
+
The HWModule instance to set.
|
|
143
201
|
|
|
144
202
|
Returns
|
|
145
203
|
-------
|
|
146
|
-
|
|
147
|
-
The
|
|
204
|
+
HWModule
|
|
205
|
+
The HWModule instance that this device belongs to.
|
|
148
206
|
None
|
|
149
207
|
If used as a setter.
|
|
150
208
|
"""
|
|
151
|
-
from ekfsm.core.components import
|
|
209
|
+
from ekfsm.core.components import HWModule
|
|
152
210
|
|
|
153
|
-
if isinstance(self._hw_module,
|
|
211
|
+
if isinstance(self._hw_module, HWModule):
|
|
154
212
|
return self._hw_module
|
|
155
213
|
else:
|
|
156
|
-
raise RuntimeError("Device is not a child of
|
|
214
|
+
raise RuntimeError("Device is not a child of HWModule")
|
|
157
215
|
|
|
158
216
|
@hw_module.setter
|
|
159
|
-
def hw_module(self, hw_module: "
|
|
217
|
+
def hw_module(self, hw_module: "HWModule") -> None:
|
|
160
218
|
self._hw_module = hw_module
|
|
161
219
|
|
|
162
220
|
def get_i2c_chip_addr(self) -> int:
|
|
@@ -164,9 +222,7 @@ class Device(SysTree):
|
|
|
164
222
|
|
|
165
223
|
chip_addr = self.device_args.get("addr")
|
|
166
224
|
if chip_addr is None:
|
|
167
|
-
raise ConfigError(
|
|
168
|
-
f"{self.name}: Chip address not provided in board definition"
|
|
169
|
-
)
|
|
225
|
+
raise ConfigError(f"{self.name}: Chip address not provided in board definition")
|
|
170
226
|
|
|
171
227
|
if not hasattr(self.parent, "sysfs_device") or self.parent.sysfs_device is None:
|
|
172
228
|
# our device is the top level device of the slot
|
|
@@ -174,16 +230,12 @@ class Device(SysTree):
|
|
|
174
230
|
slot_attributes = self.hw_module.slot.attributes
|
|
175
231
|
|
|
176
232
|
if slot_attributes is None:
|
|
177
|
-
raise ConfigError(
|
|
178
|
-
f"{self.name}: Slot attributes not provided in system configuration"
|
|
179
|
-
)
|
|
233
|
+
raise ConfigError(f"{self.name}: Slot attributes not provided in system configuration")
|
|
180
234
|
|
|
181
235
|
if not self.hw_module.is_master:
|
|
182
236
|
# slot coding is only used for non-master devices
|
|
183
237
|
if not hasattr(slot_attributes, "slot_coding"):
|
|
184
|
-
raise ConfigError(
|
|
185
|
-
f"{self.name}: Slot coding not provided in slot attributes"
|
|
186
|
-
)
|
|
238
|
+
raise ConfigError(f"{self.name}: Slot coding not provided in slot attributes")
|
|
187
239
|
|
|
188
240
|
slot_coding_mask = 0xFF
|
|
189
241
|
|
|
@@ -194,15 +246,16 @@ class Device(SysTree):
|
|
|
194
246
|
|
|
195
247
|
return chip_addr
|
|
196
248
|
|
|
197
|
-
def get_i2c_sysfs_device(self, addr: int) ->
|
|
198
|
-
from ekfsm.core.components import
|
|
249
|
+
def get_i2c_sysfs_device(self, addr: int, driver_required=True, find_driver: Callable | None = None) -> SysfsDevice:
|
|
250
|
+
from ekfsm.core.components import HWModule
|
|
199
251
|
|
|
200
252
|
parent = self.parent
|
|
201
253
|
assert parent is not None
|
|
202
254
|
|
|
203
|
-
#
|
|
204
|
-
|
|
205
|
-
|
|
255
|
+
# If parent is a HWModule, we can get the i2c bus from the master device
|
|
256
|
+
# XXX: Does this still hold true after refactoring?
|
|
257
|
+
if isinstance(parent, HWModule):
|
|
258
|
+
i2c_bus_path = self.__master_i2c_bus()
|
|
206
259
|
else:
|
|
207
260
|
# otherwise the parent must be a MuxChannel
|
|
208
261
|
from ekfsm.devices.mux import MuxChannel
|
|
@@ -218,67 +271,64 @@ class Device(SysTree):
|
|
|
218
271
|
and not (entry / "new_device").exists() # skip bus entries
|
|
219
272
|
and (entry / "name").exists()
|
|
220
273
|
):
|
|
221
|
-
#
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
if
|
|
230
|
-
return
|
|
231
|
-
|
|
232
|
-
#
|
|
274
|
+
# PRP devices unfortunately do not readily expose the underlying I2C address of the device like
|
|
275
|
+
# regular I2C devices that follow the `${I2C_BUS}-${ADDR}` pattern. To address this issue, we
|
|
276
|
+
# initialize the ACPI _STR object for each PRP device with the necessary information, which is
|
|
277
|
+
# accessible in the `${DEVICE_SYSFS_PATH}/firmware_node/description` file.
|
|
278
|
+
if (entry / "firmware_node").exists() and (entry / "firmware_node" / "description").exists():
|
|
279
|
+
description = (entry / "firmware_node/description").read_text().strip()
|
|
280
|
+
acpi_addr = int(description.split(" - ")[0], 16)
|
|
281
|
+
|
|
282
|
+
if acpi_addr == addr:
|
|
283
|
+
return SysfsDevice(entry, driver_required, find_driver)
|
|
284
|
+
|
|
285
|
+
# For regular non-PRP devices, the address is contained in the directory name (e.g. 2-0018).
|
|
233
286
|
else:
|
|
234
|
-
|
|
235
|
-
if got_addr == addr:
|
|
236
|
-
return SysFSDevice(entry)
|
|
287
|
+
acpi_addr = int(entry.name.split("-")[1], 16)
|
|
237
288
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
289
|
+
if acpi_addr == addr:
|
|
290
|
+
return SysfsDevice(entry, driver_required, find_driver)
|
|
291
|
+
|
|
292
|
+
raise FileNotFoundError(f"Device with address 0x{addr:x} not found in {i2c_bus_path}")
|
|
241
293
|
|
|
242
294
|
@staticmethod
|
|
243
|
-
def
|
|
244
|
-
if (
|
|
245
|
-
master.config.get("bus_masters") is not None
|
|
246
|
-
and master.config["bus_masters"].get("i2c") is not None
|
|
247
|
-
):
|
|
295
|
+
def __master_i2c_get_config(master: "HWModule") -> dict:
|
|
296
|
+
if master.config.get("bus_masters") is not None and master.config["bus_masters"].get("i2c") is not None:
|
|
248
297
|
return master.config["bus_masters"]["i2c"]
|
|
249
298
|
else:
|
|
250
299
|
raise ConfigError("Master definition incomplete")
|
|
251
300
|
|
|
252
|
-
def
|
|
301
|
+
def __master_i2c_bus(self) -> Path:
|
|
253
302
|
if self.hw_module.is_master:
|
|
254
303
|
# we are the master
|
|
255
304
|
master = self.hw_module
|
|
256
305
|
master_key = "MASTER_LOCAL_DEFAULT"
|
|
257
306
|
override_master_key = self.device_args.get("i2c_master", None)
|
|
307
|
+
|
|
258
308
|
if override_master_key is not None:
|
|
259
309
|
master_key = override_master_key
|
|
260
310
|
else:
|
|
261
311
|
# another board is the master
|
|
262
312
|
if self.hw_module.slot.master is None:
|
|
263
|
-
raise ConfigError(
|
|
264
|
-
f"{self.name}: Master board not found in slot attributes"
|
|
265
|
-
)
|
|
313
|
+
raise ConfigError(f"{self.name}: Master board not found in slot attributes")
|
|
266
314
|
|
|
267
315
|
master = self.hw_module.slot.master
|
|
268
316
|
master_key = self.hw_module.slot.slot_type.name
|
|
269
317
|
|
|
270
|
-
i2c_masters = self.
|
|
318
|
+
i2c_masters = self.__master_i2c_get_config(master)
|
|
271
319
|
|
|
272
320
|
if i2c_masters.get(master_key) is not None:
|
|
273
321
|
dir = sysfs_root() / Path(i2c_masters[master_key])
|
|
274
322
|
bus_dirs = list(dir.glob("i2c-*"))
|
|
323
|
+
|
|
275
324
|
if len(bus_dirs) == 1:
|
|
276
325
|
return bus_dirs[0]
|
|
277
326
|
elif len(bus_dirs) > 1:
|
|
278
|
-
raise ConfigError(f"Multiple master
|
|
279
|
-
|
|
327
|
+
raise ConfigError(f"Multiple master I2C buses found for {master_key}")
|
|
328
|
+
|
|
329
|
+
raise ConfigError(f"No master I2C bus found for {master_key}")
|
|
280
330
|
else:
|
|
281
|
-
raise ConfigError(f"Master
|
|
331
|
+
raise ConfigError(f"Master I2C bus not found for {master_key}")
|
|
282
332
|
|
|
283
333
|
def get_i2c_bus_number(self) -> int:
|
|
284
334
|
"""
|
|
@@ -295,11 +345,14 @@ class Device(SysTree):
|
|
|
295
345
|
parent_path = self.parent.sysfs_device.path
|
|
296
346
|
else:
|
|
297
347
|
parent_path = self.sysfs_device.path.parent
|
|
348
|
+
|
|
298
349
|
if parent_path.is_symlink():
|
|
299
350
|
parent_path = parent_path.readlink()
|
|
351
|
+
|
|
300
352
|
bus_number = parent_path.name.split("-")[1]
|
|
353
|
+
|
|
301
354
|
return int(bus_number)
|
|
302
355
|
|
|
303
356
|
def __repr__(self) -> str:
|
|
304
357
|
sysfs_path = getattr(self.sysfs_device, "path", "")
|
|
305
|
-
return f"{self.name};
|
|
358
|
+
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
12
|
from ekfsm.exceptions import 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
|
)
|
|
@@ -72,21 +80,15 @@ class GPIO(Device):
|
|
|
72
80
|
|
|
73
81
|
assert self.gpio is not None
|
|
74
82
|
match = re.search(r"\d+", self.gpio.name)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
83
|
+
|
|
84
|
+
if not match:
|
|
85
|
+
raise GPIOError(GPIOError.ErrorType.NO_MATCHING_DEVICE, "Failed to find matching device")
|
|
86
|
+
|
|
87
|
+
self.number: int = int(match.group().strip())
|
|
81
88
|
|
|
82
89
|
self.init_dev()
|
|
83
90
|
|
|
84
|
-
def _find_gpio_dev(
|
|
85
|
-
self,
|
|
86
|
-
parent: SysTree | None = None,
|
|
87
|
-
*args,
|
|
88
|
-
**kwargs,
|
|
89
|
-
) -> tuple[int, int]:
|
|
91
|
+
def _find_gpio_dev(self, parent: SysTree | None = None, *args, **kwargs) -> tuple[int, int]:
|
|
90
92
|
self.addr = self.get_i2c_chip_addr()
|
|
91
93
|
self.logger.debug(f"GPIO: {self.addr}")
|
|
92
94
|
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
@@ -97,9 +99,13 @@ class GPIO(Device):
|
|
|
97
99
|
try:
|
|
98
100
|
self.dev = gpiod.Chip(str(self.gpio))
|
|
99
101
|
self.initialized = True
|
|
102
|
+
return
|
|
100
103
|
except FileNotFoundError:
|
|
104
|
+
self.initialized = False
|
|
101
105
|
raise FileNotFoundError(f"{self.gpio} does not exist")
|
|
102
106
|
|
|
107
|
+
self.initialized = False
|
|
108
|
+
|
|
103
109
|
def num_lines(self) -> int:
|
|
104
110
|
"""
|
|
105
111
|
Get number of GPIO lines available on the device.
|
|
@@ -153,9 +159,7 @@ class GPIO(Device):
|
|
|
153
159
|
default=None,
|
|
154
160
|
)
|
|
155
161
|
) is not None:
|
|
156
|
-
raise GPIOError(
|
|
157
|
-
GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid."
|
|
158
|
-
)
|
|
162
|
+
raise GPIOError(GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid.")
|
|
159
163
|
|
|
160
164
|
def set_direction(self, pin: int, direction: bool) -> None:
|
|
161
165
|
"""
|
|
@@ -187,10 +191,12 @@ class GPIOExpander(GPIO):
|
|
|
187
191
|
self,
|
|
188
192
|
name: str,
|
|
189
193
|
parent: SysTree | None,
|
|
194
|
+
children: Device | None = None,
|
|
195
|
+
abort: bool = False,
|
|
190
196
|
*args,
|
|
191
197
|
**kwargs,
|
|
192
198
|
):
|
|
193
|
-
super().__init__(name, parent, None, *args, **kwargs)
|
|
199
|
+
super().__init__(name, parent, None, abort, *args, **kwargs)
|
|
194
200
|
|
|
195
201
|
def __str__(self) -> str:
|
|
196
202
|
return (
|
|
@@ -204,15 +210,17 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
|
204
210
|
self,
|
|
205
211
|
name: str,
|
|
206
212
|
parent: SysTree | None,
|
|
213
|
+
children: Device | None = None,
|
|
214
|
+
abort: bool = False,
|
|
207
215
|
*args,
|
|
208
216
|
**kwargs,
|
|
209
217
|
):
|
|
210
|
-
super().__init__(name, parent, None, *args, **kwargs)
|
|
218
|
+
super().__init__(name, parent, None, abort, *args, **kwargs)
|
|
211
219
|
|
|
212
220
|
def probe(self, *args, **kwargs) -> bool:
|
|
213
|
-
from ekfsm.core import
|
|
221
|
+
from ekfsm.core import HWModule
|
|
214
222
|
|
|
215
|
-
assert isinstance(self.hw_module,
|
|
223
|
+
assert isinstance(self.hw_module, HWModule)
|
|
216
224
|
id, _ = self.read_board_id_rev()
|
|
217
225
|
self.logger.debug(f"Probing EKFIdentificationIOExpander: {id}")
|
|
218
226
|
|
|
@@ -245,7 +253,7 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
|
245
253
|
)
|
|
246
254
|
|
|
247
255
|
def revision(self) -> str:
|
|
248
|
-
|
|
256
|
+
_, rev = self.read_board_id_rev()
|
|
249
257
|
return str(rev)
|
|
250
258
|
|
|
251
259
|
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
|
|
|
@@ -28,22 +26,23 @@ class IIOThermalHumidity(Device):
|
|
|
28
26
|
name: str,
|
|
29
27
|
parent: SysTree | None = None,
|
|
30
28
|
children: list[Device] | None = None,
|
|
29
|
+
abort: bool = False,
|
|
31
30
|
*args,
|
|
32
31
|
**kwargs,
|
|
33
32
|
):
|
|
34
33
|
self.logger = ekfsm_logger("IIOThermalHumidity:" + name)
|
|
35
|
-
super().__init__(name, parent, children, *args, **kwargs)
|
|
34
|
+
super().__init__(name, parent, children, abort, *args, **kwargs)
|
|
36
35
|
self.addr = self.get_i2c_chip_addr()
|
|
37
|
-
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"))
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
if len(dir) == 0:
|
|
41
|
-
raise FileNotFoundError("iio entry not found")
|
|
42
|
-
self.iio_sysfs = SysFSDevice(dir[0])
|
|
43
|
-
self.logger.debug(f"iio: {self.iio_sysfs.path}")
|
|
42
|
+
self.sysfs_device.extend_attributes(list_sysfs_attributes(attrs))
|
|
44
43
|
|
|
45
44
|
def temperature(self) -> float:
|
|
46
|
-
return iio_get_in_value(self.
|
|
45
|
+
return iio_get_in_value(self.sysfs, "in_temp") / 1000.0
|
|
47
46
|
|
|
48
47
|
def humidity(self) -> float:
|
|
49
|
-
return iio_get_in_value(self.
|
|
48
|
+
return iio_get_in_value(self.sysfs, "in_humidityrelative") / 1000.0
|