ekfsm 0.11.0b1.post3__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 +13 -0
- ekfsm/boards/oem/ekf/ccu.yaml +68 -0
- ekfsm/boards/oem/ekf/sc5-festival.yaml +30 -0
- ekfsm/boards/oem/ekf/sc9-toccata.yaml +31 -0
- ekfsm/boards/oem/ekf/spv-mystic.yaml +68 -0
- ekfsm/boards/oem/ekf/sq1-track.yaml +41 -0
- ekfsm/boards/oem/ekf/srf-fan.yaml +48 -0
- ekfsm/boards/oem/ekf/sur-uart.yaml +72 -0
- ekfsm/boards/oem/hitron/hdrc-300.yaml +20 -0
- ekfsm/cli.py +111 -0
- ekfsm/config.py +37 -0
- ekfsm/core/__init__.py +4 -0
- ekfsm/core/components.py +120 -0
- ekfsm/core/probe.py +10 -0
- ekfsm/core/slots.py +201 -0
- ekfsm/core/sysfs.py +91 -0
- ekfsm/core/utils.py +77 -0
- ekfsm/devices/__init__.py +28 -0
- ekfsm/devices/eeprom.py +1054 -0
- ekfsm/devices/ekf_ccu_uc.py +390 -0
- ekfsm/devices/ekf_sur_led.py +67 -0
- ekfsm/devices/generic.py +245 -0
- ekfsm/devices/gpio.py +340 -0
- ekfsm/devices/hwmon.py +71 -0
- ekfsm/devices/iio.py +58 -0
- ekfsm/devices/iio_thermal_humidity.py +41 -0
- ekfsm/devices/mux.py +39 -0
- ekfsm/devices/pmbus.py +65 -0
- ekfsm/devices/smbios.py +38 -0
- ekfsm/devices/utils.py +16 -0
- ekfsm/exceptions.py +58 -0
- ekfsm/log.py +28 -0
- ekfsm/py.typed +2 -0
- ekfsm/simctrl.py +241 -0
- ekfsm/system.py +326 -0
- ekfsm-0.11.0b1.post3.dist-info/METADATA +86 -0
- ekfsm-0.11.0b1.post3.dist-info/RECORD +39 -0
- ekfsm-0.11.0b1.post3.dist-info/WHEEL +4 -0
- ekfsm-0.11.0b1.post3.dist-info/entry_points.txt +2 -0
ekfsm/devices/gpio.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import gpiod
|
|
7
|
+
from gpiod.chip import LineSettings
|
|
8
|
+
from gpiod.line import Direction, Value
|
|
9
|
+
from more_itertools import first_true
|
|
10
|
+
|
|
11
|
+
from ekfsm.core.components import SystemComponent
|
|
12
|
+
from ekfsm.exceptions import GPIOError
|
|
13
|
+
from ekfsm.log import ekfsm_logger
|
|
14
|
+
|
|
15
|
+
from .generic import Device
|
|
16
|
+
from ..core.probe import ProbeableDevice
|
|
17
|
+
|
|
18
|
+
gpio_pat = re.compile(r"gpiochip\d+")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_gpio_major_minor(path: Path) -> tuple[int, int]:
|
|
22
|
+
for d in path.iterdir():
|
|
23
|
+
if gpio_pat.match(d.name):
|
|
24
|
+
dev = d / "dev"
|
|
25
|
+
if dev.exists():
|
|
26
|
+
content = dev.read_text().strip()
|
|
27
|
+
major, minor = map(int, content.split(":"))
|
|
28
|
+
return major, minor
|
|
29
|
+
|
|
30
|
+
raise GPIOError(
|
|
31
|
+
GPIOError.ErrorType.NO_MAJOR_MINOR,
|
|
32
|
+
f"No minor/major number found for GPIO device at {path}",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def find_gpio_dev_with_major_minor(major: int, minor: int) -> Path | None:
|
|
37
|
+
for dev in Path("/dev").iterdir():
|
|
38
|
+
if gpio_pat.match(dev.name):
|
|
39
|
+
stat_info = dev.stat()
|
|
40
|
+
|
|
41
|
+
cmaj = os.major(stat_info.st_rdev)
|
|
42
|
+
cmin = os.minor(stat_info.st_rdev)
|
|
43
|
+
|
|
44
|
+
if cmaj == major and cmin == minor:
|
|
45
|
+
return dev
|
|
46
|
+
|
|
47
|
+
raise GPIOError(
|
|
48
|
+
GPIOError.ErrorType.NO_MATCHING_DEVICE,
|
|
49
|
+
f"Failed to find GPIO device with major {major} and minor {minor}",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class GPIO(Device):
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
name: str,
|
|
58
|
+
parent: SystemComponent | None = None,
|
|
59
|
+
*args,
|
|
60
|
+
**kwargs,
|
|
61
|
+
):
|
|
62
|
+
super().__init__(
|
|
63
|
+
name,
|
|
64
|
+
parent,
|
|
65
|
+
None,
|
|
66
|
+
*args,
|
|
67
|
+
**kwargs,
|
|
68
|
+
)
|
|
69
|
+
self.logger = ekfsm_logger("GPIODevice" + name)
|
|
70
|
+
major, minor = self._find_gpio_dev(parent, *args, **kwargs)
|
|
71
|
+
self.gpio = find_gpio_dev_with_major_minor(major, minor)
|
|
72
|
+
|
|
73
|
+
assert self.gpio is not None
|
|
74
|
+
match = re.search(r"\d+", self.gpio.name)
|
|
75
|
+
if match:
|
|
76
|
+
self.number: int = int(match.group().strip())
|
|
77
|
+
else:
|
|
78
|
+
raise GPIOError(
|
|
79
|
+
GPIOError.ErrorType.NO_MATCHING_DEVICE, "Failed to find matching device"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
self.init_dev()
|
|
83
|
+
|
|
84
|
+
def _find_gpio_dev(
|
|
85
|
+
self,
|
|
86
|
+
parent: SystemComponent | None = None,
|
|
87
|
+
*args,
|
|
88
|
+
**kwargs,
|
|
89
|
+
) -> tuple[int, int]:
|
|
90
|
+
self.addr = self.get_i2c_chip_addr()
|
|
91
|
+
self.logger.debug(f"GPIO: {self.addr}")
|
|
92
|
+
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
93
|
+
return get_gpio_major_minor(self.sysfs_device.path)
|
|
94
|
+
|
|
95
|
+
def init_dev(self):
|
|
96
|
+
if self.gpio:
|
|
97
|
+
try:
|
|
98
|
+
self.dev = gpiod.Chip(str(self.gpio))
|
|
99
|
+
self.initialized = True
|
|
100
|
+
except FileNotFoundError:
|
|
101
|
+
raise FileNotFoundError(f"{self.gpio} does not exist")
|
|
102
|
+
|
|
103
|
+
def num_lines(self) -> int:
|
|
104
|
+
"""
|
|
105
|
+
Get number of GPIO lines available on the device.
|
|
106
|
+
"""
|
|
107
|
+
return self.dev.get_info().num_lines
|
|
108
|
+
|
|
109
|
+
def set_pin(self, pin: int, value: bool) -> None:
|
|
110
|
+
"""
|
|
111
|
+
Set the value of a GPIO pin.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
pin : int
|
|
116
|
+
The pin number.
|
|
117
|
+
value : bool
|
|
118
|
+
The value to set.
|
|
119
|
+
"""
|
|
120
|
+
v = Value.ACTIVE if value else Value.INACTIVE
|
|
121
|
+
with self.dev.request_lines(
|
|
122
|
+
consumer="set-pin",
|
|
123
|
+
config={pin: gpiod.LineSettings()},
|
|
124
|
+
) as request:
|
|
125
|
+
request.set_value(pin, v)
|
|
126
|
+
|
|
127
|
+
def get_pin(self, pin: int) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Get the value of a GPIO pin.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
pin : int
|
|
134
|
+
The pin number.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
bool
|
|
139
|
+
The value of the pin.
|
|
140
|
+
"""
|
|
141
|
+
with self.dev.request_lines(
|
|
142
|
+
consumer="get-pin",
|
|
143
|
+
config={pin: gpiod.LineSettings()},
|
|
144
|
+
) as req:
|
|
145
|
+
value = req.get_value(pin)
|
|
146
|
+
return value == Value.ACTIVE
|
|
147
|
+
|
|
148
|
+
def get_lines(self, lines: list[int]):
|
|
149
|
+
if (
|
|
150
|
+
invalid_pin := first_true(
|
|
151
|
+
lines,
|
|
152
|
+
pred=lambda line: line < 0 or line >= self.num_lines(),
|
|
153
|
+
default=None,
|
|
154
|
+
)
|
|
155
|
+
) is not None:
|
|
156
|
+
raise GPIOError(
|
|
157
|
+
GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def set_direction(self, pin: int, direction: bool) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Set the direction of a GPIO pin.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
pin : int
|
|
167
|
+
The pin number.
|
|
168
|
+
direction : bool
|
|
169
|
+
The direction to set. True for output, False for input.
|
|
170
|
+
"""
|
|
171
|
+
dir = Direction.OUTPUT if direction else Direction.INPUT
|
|
172
|
+
self.dev.request_lines(
|
|
173
|
+
consumer="set-direction",
|
|
174
|
+
config={pin: LineSettings(direction=dir)},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def __str__(self) -> str:
|
|
178
|
+
return (
|
|
179
|
+
f"GPIO - Number: {self.number}; "
|
|
180
|
+
f"sysfs_path: {self.sysfs_device.path if self.sysfs_device else ''} "
|
|
181
|
+
f"(dev: {self.gpio if self.gpio else 'No matching device found'})"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class GPIOExpander(GPIO):
|
|
186
|
+
def __init__(
|
|
187
|
+
self,
|
|
188
|
+
name: str,
|
|
189
|
+
parent: SystemComponent | None,
|
|
190
|
+
*args,
|
|
191
|
+
**kwargs,
|
|
192
|
+
):
|
|
193
|
+
super().__init__(name, parent, None, *args, **kwargs)
|
|
194
|
+
|
|
195
|
+
def __str__(self) -> str:
|
|
196
|
+
return (
|
|
197
|
+
f"GPIOExpander - Number: {self.number}; "
|
|
198
|
+
f"sysfs_path: {self.sysfs_device.path if self.sysfs_device else ''}"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
203
|
+
def __init__(
|
|
204
|
+
self,
|
|
205
|
+
name: str,
|
|
206
|
+
parent: SystemComponent | None,
|
|
207
|
+
*args,
|
|
208
|
+
**kwargs,
|
|
209
|
+
):
|
|
210
|
+
super().__init__(name, parent, None, *args, **kwargs)
|
|
211
|
+
|
|
212
|
+
def probe(self, *args, **kwargs) -> bool:
|
|
213
|
+
from ekfsm.core import HwModule
|
|
214
|
+
|
|
215
|
+
assert isinstance(self.root, HwModule)
|
|
216
|
+
id, _ = self.read_board_id_rev()
|
|
217
|
+
|
|
218
|
+
return self.root.id == id
|
|
219
|
+
|
|
220
|
+
def read_board_id_rev(self) -> tuple[int, int]:
|
|
221
|
+
for pin in range(6, 8):
|
|
222
|
+
self.set_direction(pin, True)
|
|
223
|
+
self.set_pin(pin, False)
|
|
224
|
+
for pin in range(0, 6):
|
|
225
|
+
self.set_direction(pin, False)
|
|
226
|
+
|
|
227
|
+
v_gnd = self.read_id_gpio_inputs()
|
|
228
|
+
self.set_pin(7, True)
|
|
229
|
+
v_7h = self.read_id_gpio_inputs()
|
|
230
|
+
self.set_pin(6, True)
|
|
231
|
+
v_6h = self.read_id_gpio_inputs()
|
|
232
|
+
|
|
233
|
+
readings = [v_gnd, v_7h, v_6h]
|
|
234
|
+
bit_sums = []
|
|
235
|
+
for bit in range(5, -1, -1):
|
|
236
|
+
s = 0
|
|
237
|
+
for reading in readings:
|
|
238
|
+
s += (reading >> bit) & 1
|
|
239
|
+
bit_sums.append(s)
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
sum(val * (4**i) for i, val in enumerate(reversed(bit_sums[2:]))),
|
|
243
|
+
self._get_board_rev(bit_sums[:2]), # board_rev
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def revision(self) -> str:
|
|
247
|
+
id, rev = self.read_board_id_rev()
|
|
248
|
+
return str(rev)
|
|
249
|
+
|
|
250
|
+
def read_id_gpio_inputs(self) -> int:
|
|
251
|
+
value = 0
|
|
252
|
+
for pin in range(6):
|
|
253
|
+
if self.get_pin(pin):
|
|
254
|
+
value |= 1 << pin
|
|
255
|
+
|
|
256
|
+
return value
|
|
257
|
+
|
|
258
|
+
@staticmethod
|
|
259
|
+
def _get_board_rev(bits: list[int]) -> int:
|
|
260
|
+
"""Convert 2-bit sum values to board revision number."""
|
|
261
|
+
rev_map = {
|
|
262
|
+
(0, 0): 0,
|
|
263
|
+
(0, 1): 1,
|
|
264
|
+
(0, 2): 2,
|
|
265
|
+
(1, 0): 3,
|
|
266
|
+
(1, 1): 4,
|
|
267
|
+
(1, 2): 5,
|
|
268
|
+
(2, 0): 6,
|
|
269
|
+
(2, 1): 7,
|
|
270
|
+
(2, 2): 8,
|
|
271
|
+
}
|
|
272
|
+
return rev_map.get((bits[0], bits[1]), -1)
|
|
273
|
+
|
|
274
|
+
def __str__(self) -> str:
|
|
275
|
+
return (
|
|
276
|
+
f"EKFIdentificationIOExpander - Number: {self.number}; "
|
|
277
|
+
f'sysfs_path: {self.sysfs_device.path if self.sysfs_device else ""}'
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class SimGpio(ABC):
|
|
282
|
+
@abstractmethod
|
|
283
|
+
def num_lines(self) -> int:
|
|
284
|
+
pass
|
|
285
|
+
|
|
286
|
+
@abstractmethod
|
|
287
|
+
def set_pin(self, pin: int, value: bool) -> None:
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
@abstractmethod
|
|
291
|
+
def get_pin(self, pin: int) -> bool:
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
@abstractmethod
|
|
295
|
+
def set_direction(self, pin: int, direction: bool) -> None:
|
|
296
|
+
pass
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class EKFIdSimGpio(SimGpio):
|
|
300
|
+
def __init__(self, coding_gnd, coding_vcc, coding_6, coding_7) -> None:
|
|
301
|
+
self._coding_gnd = coding_gnd
|
|
302
|
+
self._coding_vcc = coding_vcc
|
|
303
|
+
self._coding_6 = coding_6
|
|
304
|
+
self._coding_7 = coding_7
|
|
305
|
+
self._dir = 0
|
|
306
|
+
self._out = 0
|
|
307
|
+
self._in = 0
|
|
308
|
+
|
|
309
|
+
def num_lines(self) -> int:
|
|
310
|
+
return 8
|
|
311
|
+
|
|
312
|
+
def set_pin(self, pin: int, value: bool) -> None:
|
|
313
|
+
mask = 1 << pin
|
|
314
|
+
if value not in [0, 1]:
|
|
315
|
+
raise RuntimeError("value must be 0 or 1")
|
|
316
|
+
if self._dir & mask:
|
|
317
|
+
self._out = (self._out & ~mask) | (value << pin)
|
|
318
|
+
else:
|
|
319
|
+
raise RuntimeError("pin not set as output")
|
|
320
|
+
|
|
321
|
+
def get_pin(self, pin: int) -> bool:
|
|
322
|
+
mask = 1 << pin
|
|
323
|
+
if self._coding_gnd & mask:
|
|
324
|
+
return False
|
|
325
|
+
if self._coding_vcc & mask:
|
|
326
|
+
return True
|
|
327
|
+
if self._coding_6 & mask:
|
|
328
|
+
return True if self._out & (1 << 6) else False
|
|
329
|
+
if self._coding_7 & mask:
|
|
330
|
+
return True if self._out & (1 << 7) else False
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
def set_direction(self, pin: int, direction: bool) -> None:
|
|
334
|
+
if direction == 1 and (pin != 6 and pin != 7):
|
|
335
|
+
raise RuntimeError("only pins 6 and 7 supported as output")
|
|
336
|
+
mask = 1 << pin
|
|
337
|
+
if direction:
|
|
338
|
+
self._dir |= mask
|
|
339
|
+
else:
|
|
340
|
+
self._dir &= ~mask
|
ekfsm/devices/hwmon.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import os
|
|
3
|
+
import glob
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ekfsm.core.sysfs import SYSFS_ROOT
|
|
7
|
+
from ekfsm.devices.generic import Device
|
|
8
|
+
|
|
9
|
+
# Path to the root of the HWMON sysfs filesystem
|
|
10
|
+
HWMON_ROOT = SYSFS_ROOT / Path('class/hwmon')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def find_core_temp_dir(hwmon_dir) -> Path:
|
|
14
|
+
"""
|
|
15
|
+
Find the directory containing the coretemp hwmon device.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
hwmon_dir: Path to the hwmon directory
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Path to the directory containing the coretemp hwmon device
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
FileNotFoundError: If no coretemp directory is found
|
|
25
|
+
"""
|
|
26
|
+
# List all 'name' files in each subdirectory of hwmon_dir
|
|
27
|
+
name_files = glob.glob(os.path.join(hwmon_dir, '*', 'name'))
|
|
28
|
+
|
|
29
|
+
# Search for the file containing "coretemp"
|
|
30
|
+
for name_file in name_files:
|
|
31
|
+
with open(name_file, 'r') as file:
|
|
32
|
+
if file.readline().strip() == 'coretemp':
|
|
33
|
+
# Return the directory containing this file
|
|
34
|
+
return Path(os.path.dirname(name_file))
|
|
35
|
+
|
|
36
|
+
raise FileNotFoundError("No coretemp directory found")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class HWMON(Device):
|
|
40
|
+
"""
|
|
41
|
+
A class to represent the HWMON device.
|
|
42
|
+
|
|
43
|
+
A HWMON device is a virtual device that is used to read hardware monitoring values from the sysfs filesystem.
|
|
44
|
+
|
|
45
|
+
Note:
|
|
46
|
+
Currently, only the CPU temperature is read from the HWMON device.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
name: str,
|
|
52
|
+
parent: Device,
|
|
53
|
+
*args,
|
|
54
|
+
**kwargs,
|
|
55
|
+
):
|
|
56
|
+
from ekfsm.core.sysfs import SysFSDevice, SYSFS_ROOT
|
|
57
|
+
|
|
58
|
+
self.sysfs_device: SysFSDevice = SysFSDevice(find_core_temp_dir(SYSFS_ROOT / Path('class/hwmon')))
|
|
59
|
+
|
|
60
|
+
super().__init__(name, parent, None, *args, **kwargs)
|
|
61
|
+
|
|
62
|
+
def cputemp(self):
|
|
63
|
+
"""
|
|
64
|
+
Get the CPU temperature from the HWMON device.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
int
|
|
69
|
+
The CPU temperature in degrees Celsius.
|
|
70
|
+
"""
|
|
71
|
+
return int(self.sysfs_device.read_attr_utf8("temp1_input").strip()) / 1000
|
ekfsm/devices/iio.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from .generic import SysFSDevice
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def iio_get_in_value(dev: SysFSDevice, attrset: str) -> float:
|
|
5
|
+
"""
|
|
6
|
+
Calculate a value from an IIO in_* attribute set, using the _raw, _scale and _offset attributes (if present).
|
|
7
|
+
Typical name of attrset are "in_temp" or "in_voltage0".
|
|
8
|
+
|
|
9
|
+
Formula according to https://wiki.st.com/stm32mpu/wiki/How_to_use_the_IIO_user_space_interface
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
dev
|
|
14
|
+
sysfs device object pointing to the iio directory
|
|
15
|
+
attrset
|
|
16
|
+
name of the attribute set to read from (e.g. "in_temp")
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
float
|
|
21
|
+
calculated value from the attribute set (no unit conversion)
|
|
22
|
+
|
|
23
|
+
Raises
|
|
24
|
+
------
|
|
25
|
+
FileNotFoundError
|
|
26
|
+
if the neiter _input nor _raw attribute is found
|
|
27
|
+
|
|
28
|
+
"""
|
|
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
|
+
try:
|
|
38
|
+
content = dev.read_attr_utf8(f"{attrset}_raw")
|
|
39
|
+
except StopIteration:
|
|
40
|
+
raise FileNotFoundError(f"{attrset}_raw not found")
|
|
41
|
+
|
|
42
|
+
raw = float(content)
|
|
43
|
+
|
|
44
|
+
# use offset if present
|
|
45
|
+
try:
|
|
46
|
+
content = dev.read_attr_utf8(f"{attrset}_offset")
|
|
47
|
+
raw += float(content)
|
|
48
|
+
except StopIteration:
|
|
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
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from ekfsm.core.components import SystemComponent
|
|
4
|
+
from ekfsm.log import ekfsm_logger
|
|
5
|
+
|
|
6
|
+
from ..core.sysfs import SysFSDevice
|
|
7
|
+
|
|
8
|
+
from .generic import Device
|
|
9
|
+
from .iio import iio_get_in_value
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IIOThermalHumidity(Device):
|
|
13
|
+
"""
|
|
14
|
+
Device for IIO thermal and/or humidity sensors.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
name: str,
|
|
21
|
+
parent: SystemComponent | None = None,
|
|
22
|
+
children: list[Device] | None = None,
|
|
23
|
+
*args,
|
|
24
|
+
**kwargs,
|
|
25
|
+
):
|
|
26
|
+
self.logger = ekfsm_logger("IIOThermalHumidity:" + name)
|
|
27
|
+
super().__init__(name, parent, children, **kwargs)
|
|
28
|
+
self.addr = self.get_i2c_chip_addr()
|
|
29
|
+
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
30
|
+
|
|
31
|
+
dir = list(Path(self.sysfs_device.path).glob("iio:device*"))
|
|
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}")
|
|
36
|
+
|
|
37
|
+
def temperature(self) -> float:
|
|
38
|
+
return iio_get_in_value(self.iio_sysfs, "in_temp") / 1000.0
|
|
39
|
+
|
|
40
|
+
def humidity(self) -> float:
|
|
41
|
+
return iio_get_in_value(self.iio_sysfs, "in_humidityrelative") / 1000.0
|
ekfsm/devices/mux.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ekfsm.core.components import SystemComponent
|
|
2
|
+
|
|
3
|
+
from ..core.sysfs import SysFSDevice
|
|
4
|
+
from .generic import Device
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MuxChannel(Device):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
name: str,
|
|
11
|
+
channel_id: int,
|
|
12
|
+
parent: 'I2CMux',
|
|
13
|
+
children: list[Device] | None = None,
|
|
14
|
+
*args,
|
|
15
|
+
**kwargs,
|
|
16
|
+
) -> None:
|
|
17
|
+
super().__init__(name=name, parent=parent, children=children)
|
|
18
|
+
self.channel_id = channel_id
|
|
19
|
+
|
|
20
|
+
assert parent.sysfs_device is not None
|
|
21
|
+
assert isinstance(self.parent, I2CMux)
|
|
22
|
+
|
|
23
|
+
path = parent.sysfs_device.path / f"channel-{self.channel_id}"
|
|
24
|
+
self.sysfs_device = SysFSDevice(path)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class I2CMux(Device):
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
name: str,
|
|
31
|
+
parent: SystemComponent | None = None,
|
|
32
|
+
children: list[MuxChannel] | None = None,
|
|
33
|
+
*args,
|
|
34
|
+
**kwargs,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(name, parent, children, **kwargs)
|
|
37
|
+
|
|
38
|
+
self.addr = self.get_i2c_chip_addr()
|
|
39
|
+
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
ekfsm/devices/pmbus.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from ekfsm.core.components import SystemComponent
|
|
4
|
+
|
|
5
|
+
from ..core.sysfs import SysFSDevice
|
|
6
|
+
|
|
7
|
+
from .generic import Device
|
|
8
|
+
from ..core.probe import ProbeableDevice
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PmBus(Device, ProbeableDevice):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
name: str,
|
|
15
|
+
parent: SystemComponent | None = None,
|
|
16
|
+
children: list[Device] | None = None,
|
|
17
|
+
*args,
|
|
18
|
+
**kwargs,
|
|
19
|
+
):
|
|
20
|
+
super().__init__(name, parent, children, **kwargs)
|
|
21
|
+
self.addr = self.get_i2c_chip_addr()
|
|
22
|
+
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
23
|
+
|
|
24
|
+
files = list(Path(self.sysfs_device.path).rglob("hwmon/*/in1_input"))
|
|
25
|
+
if len(files) == 0:
|
|
26
|
+
raise FileNotFoundError("No HWMON entries found")
|
|
27
|
+
self.hwmon_sysfs = SysFSDevice(files[0].parent)
|
|
28
|
+
|
|
29
|
+
def probe(self, *args, **kwargs) -> bool:
|
|
30
|
+
from ekfsm.core import HwModule
|
|
31
|
+
|
|
32
|
+
assert isinstance(self.root, HwModule)
|
|
33
|
+
return self.root.id == self.model()
|
|
34
|
+
|
|
35
|
+
# Voltage and Current Interfaces
|
|
36
|
+
def _in_conversion(self, in_file: str) -> float:
|
|
37
|
+
return float(self.hwmon_sysfs.read_attr_utf8(in_file)) / 1000.0
|
|
38
|
+
|
|
39
|
+
def _current_conversion(self, in_file: str) -> float:
|
|
40
|
+
return float(self.hwmon_sysfs.read_attr_utf8(in_file)) / 1000.0
|
|
41
|
+
|
|
42
|
+
def in1_input(self) -> float:
|
|
43
|
+
return self._in_conversion("in1_input")
|
|
44
|
+
|
|
45
|
+
def in2_input(self) -> float:
|
|
46
|
+
return self._in_conversion("in2_input")
|
|
47
|
+
|
|
48
|
+
def curr1_input(self) -> float:
|
|
49
|
+
return self._current_conversion("curr1_input")
|
|
50
|
+
|
|
51
|
+
def curr2_input(self) -> float:
|
|
52
|
+
return self._current_conversion("curr2_input")
|
|
53
|
+
|
|
54
|
+
# Inventory Interface
|
|
55
|
+
def vendor(self) -> str:
|
|
56
|
+
return self.hwmon_sysfs.read_attr_utf8("vendor").strip()
|
|
57
|
+
|
|
58
|
+
def model(self) -> str:
|
|
59
|
+
return self.hwmon_sysfs.read_attr_utf8("model").strip()
|
|
60
|
+
|
|
61
|
+
def serial(self) -> str:
|
|
62
|
+
return self.hwmon_sysfs.read_attr_utf8("serial").strip()
|
|
63
|
+
|
|
64
|
+
def revision(self) -> str:
|
|
65
|
+
return self.hwmon_sysfs.read_attr_utf8("revision").strip()
|
ekfsm/devices/smbios.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from ekfsm.core.components import HwModule
|
|
3
|
+
from ekfsm.core.sysfs import SysFSDevice, SYSFS_ROOT
|
|
4
|
+
from .generic import Device
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SMBIOS(Device):
|
|
8
|
+
"""
|
|
9
|
+
A class to represent the SMBIOS device.
|
|
10
|
+
|
|
11
|
+
A SMBIOS device is a virtual device that is used to read system
|
|
12
|
+
configuration values from the DMI table.
|
|
13
|
+
|
|
14
|
+
Note:
|
|
15
|
+
Currently, only the board version / revision is read from the DMI table.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
name: str,
|
|
21
|
+
parent: HwModule | None = None,
|
|
22
|
+
*args,
|
|
23
|
+
**kwargs,
|
|
24
|
+
):
|
|
25
|
+
self.sysfs_device: SysFSDevice = SysFSDevice(SYSFS_ROOT / Path("devices/virtual/dmi/id"))
|
|
26
|
+
|
|
27
|
+
super().__init__(name, parent, None, *args, **kwargs)
|
|
28
|
+
|
|
29
|
+
def revision(self) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Get the board revision from the DMI table.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
str
|
|
36
|
+
The board revision.
|
|
37
|
+
"""
|
|
38
|
+
return self.sysfs_device.read_attr_utf8("board_version").strip()
|
ekfsm/devices/utils.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from crcmod.predefined import Crc
|
|
2
|
+
from typing import Sequence
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def compute_int_from_bytes(data: Sequence[int]) -> int:
|
|
6
|
+
# Combine the bytes into a single integer
|
|
7
|
+
result = 0
|
|
8
|
+
for num in data:
|
|
9
|
+
result = (result << 8) | num
|
|
10
|
+
return result
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_crc16_xmodem(data: bytes) -> int:
|
|
14
|
+
crc16_xmodem = Crc("xmodem")
|
|
15
|
+
crc16_xmodem.update(data)
|
|
16
|
+
return crc16_xmodem.crcValue
|