]>
Commit | Line | Data |
---|---|---|
4549e789 | 1 | /* SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause */ |
cf2064d7 SG |
2 | /* |
3 | * pylibfdt - Flat Device Tree manipulation in Python | |
4 | * Copyright (C) 2017 Google, Inc. | |
5 | * Written by Simon Glass <sjg@chromium.org> | |
cf2064d7 SG |
6 | */ |
7 | ||
8 | %module libfdt | |
9 | ||
c79d18c4 SG |
10 | %include <stdint.i> |
11 | ||
cf2064d7 SG |
12 | %{ |
13 | #define SWIG_FILE_WITH_INIT | |
14 | #include "libfdt.h" | |
15 | %} | |
16 | ||
17 | %pythoncode %{ | |
18 | ||
19 | import struct | |
20 | ||
21 | # Error codes, corresponding to FDT_ERR_... in libfdt.h | |
22 | (NOTFOUND, | |
23 | EXISTS, | |
24 | NOSPACE, | |
25 | BADOFFSET, | |
26 | BADPATH, | |
27 | BADPHANDLE, | |
28 | BADSTATE, | |
29 | TRUNCATED, | |
30 | BADMAGIC, | |
31 | BADVERSION, | |
32 | BADSTRUCTURE, | |
33 | BADLAYOUT, | |
34 | INTERNAL, | |
35 | BADNCELLS, | |
36 | BADVALUE, | |
37 | BADOVERLAY, | |
38 | NOPHANDLES) = QUIET_ALL = range(1, 18) | |
39 | # QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions | |
40 | # altogether. All # functions passed this value will return an error instead | |
41 | # of raising an exception. | |
42 | ||
43 | # Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors, | |
44 | # instead of raising an exception. | |
45 | QUIET_NOTFOUND = (NOTFOUND,) | |
46 | ||
47 | ||
48 | class FdtException(Exception): | |
49 | """An exception caused by an error such as one of the codes above""" | |
50 | def __init__(self, err): | |
51 | self.err = err | |
52 | ||
53 | def __str__(self): | |
54 | return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err)) | |
55 | ||
56 | def strerror(fdt_err): | |
57 | """Get the string for an error number | |
58 | ||
59 | Args: | |
60 | fdt_err: Error number (-ve) | |
61 | ||
62 | Returns: | |
63 | String containing the associated error | |
64 | """ | |
65 | return fdt_strerror(fdt_err) | |
66 | ||
67 | def check_err(val, quiet=()): | |
68 | """Raise an error if the return value is -ve | |
69 | ||
70 | This is used to check for errors returned by libfdt C functions. | |
71 | ||
72 | Args: | |
73 | val: Return value from a libfdt function | |
74 | quiet: Errors to ignore (empty to raise on all errors) | |
75 | ||
76 | Returns: | |
77 | val if val >= 0 | |
78 | ||
79 | Raises | |
80 | FdtException if val < 0 | |
81 | """ | |
82 | if val < 0: | |
83 | if -val not in quiet: | |
84 | raise FdtException(val) | |
85 | return val | |
86 | ||
87 | def check_err_null(val, quiet=()): | |
88 | """Raise an error if the return value is NULL | |
89 | ||
90 | This is used to check for a NULL return value from certain libfdt C | |
91 | functions | |
92 | ||
93 | Args: | |
94 | val: Return value from a libfdt function | |
95 | quiet: Errors to ignore (empty to raise on all errors) | |
96 | ||
97 | Returns: | |
98 | val if val is a list, None if not | |
99 | ||
100 | Raises | |
101 | FdtException if val indicates an error was reported and the error | |
102 | is not in @quiet. | |
103 | """ | |
104 | # Normally a list is returned which contains the data and its length. | |
105 | # If we get just an integer error code, it means the function failed. | |
106 | if not isinstance(val, list): | |
107 | if -val not in quiet: | |
108 | raise FdtException(val) | |
109 | return val | |
110 | ||
111 | class Fdt: | |
112 | """Device tree class, supporting all operations | |
113 | ||
114 | The Fdt object is created is created from a device tree binary file, | |
115 | e.g. with something like: | |
116 | ||
117 | fdt = Fdt(open("filename.dtb").read()) | |
118 | ||
119 | Operations can then be performed using the methods in this class. Each | |
120 | method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...). | |
121 | ||
122 | All methods raise an FdtException if an error occurs. To avoid this | |
123 | behaviour a 'quiet' parameter is provided for some functions. This | |
124 | defaults to empty, but you can pass a list of errors that you expect. | |
125 | If one of these errors occurs, the function will return an error number | |
126 | (e.g. -NOTFOUND). | |
127 | """ | |
128 | def __init__(self, data): | |
129 | self._fdt = bytearray(data) | |
130 | check_err(fdt_check_header(self._fdt)); | |
131 | ||
93c94b88 SG |
132 | def subnode_offset(self, parentoffset, name, quiet=()): |
133 | """Get the offset of a named subnode | |
134 | ||
135 | Args: | |
136 | parentoffset: Offset of the parent node to check | |
137 | name: Name of the required subnode, e.g. 'subnode@1' | |
138 | quiet: Errors to ignore (empty to raise on all errors) | |
139 | ||
140 | Returns: | |
141 | The node offset of the found node, if any | |
142 | ||
143 | Raises | |
144 | FdtException if there is no node with that name, or other error | |
145 | """ | |
146 | return check_err(fdt_subnode_offset(self._fdt, parentoffset, name), | |
147 | quiet) | |
148 | ||
cf2064d7 SG |
149 | def path_offset(self, path, quiet=()): |
150 | """Get the offset for a given path | |
151 | ||
152 | Args: | |
153 | path: Path to the required node, e.g. '/node@3/subnode@1' | |
154 | quiet: Errors to ignore (empty to raise on all errors) | |
155 | ||
156 | Returns: | |
157 | Node offset | |
158 | ||
159 | Raises | |
160 | FdtException if the path is not valid or not found | |
161 | """ | |
162 | return check_err(fdt_path_offset(self._fdt, path), quiet) | |
163 | ||
164 | def first_property_offset(self, nodeoffset, quiet=()): | |
165 | """Get the offset of the first property in a node offset | |
166 | ||
167 | Args: | |
168 | nodeoffset: Offset to the node to check | |
169 | quiet: Errors to ignore (empty to raise on all errors) | |
170 | ||
171 | Returns: | |
172 | Offset of the first property | |
173 | ||
174 | Raises | |
175 | FdtException if the associated node has no properties, or some | |
176 | other error occurred | |
177 | """ | |
178 | return check_err(fdt_first_property_offset(self._fdt, nodeoffset), | |
179 | quiet) | |
180 | ||
181 | def next_property_offset(self, prop_offset, quiet=()): | |
182 | """Get the next property in a node | |
183 | ||
184 | Args: | |
185 | prop_offset: Offset of the previous property | |
186 | quiet: Errors to ignore (empty to raise on all errors) | |
187 | ||
188 | Returns: | |
189 | Offset of the next property | |
190 | ||
191 | Raises: | |
192 | FdtException if the associated node has no more properties, or | |
193 | some other error occurred | |
194 | """ | |
195 | return check_err(fdt_next_property_offset(self._fdt, prop_offset), | |
196 | quiet) | |
197 | ||
198 | def get_name(self, nodeoffset): | |
199 | """Get the name of a node | |
200 | ||
201 | Args: | |
202 | nodeoffset: Offset of node to check | |
203 | ||
204 | Returns: | |
205 | Node name | |
206 | ||
207 | Raises: | |
208 | FdtException on error (e.g. nodeoffset is invalid) | |
209 | """ | |
210 | return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0] | |
211 | ||
212 | def get_property_by_offset(self, prop_offset, quiet=()): | |
213 | """Obtains a property that can be examined | |
214 | ||
215 | Args: | |
216 | prop_offset: Offset of property (e.g. from first_property_offset()) | |
217 | quiet: Errors to ignore (empty to raise on all errors) | |
218 | ||
219 | Returns: | |
220 | Property object, or None if not found | |
221 | ||
222 | Raises: | |
223 | FdtException on error (e.g. invalid prop_offset or device | |
224 | tree format) | |
225 | """ | |
226 | pdata = check_err_null( | |
227 | fdt_get_property_by_offset(self._fdt, prop_offset), quiet) | |
228 | if isinstance(pdata, (int)): | |
229 | return pdata | |
230 | return Property(pdata[0], pdata[1]) | |
231 | ||
232 | def first_subnode(self, nodeoffset, quiet=()): | |
233 | """Find the first subnode of a parent node | |
234 | ||
235 | Args: | |
236 | nodeoffset: Node offset of parent node | |
237 | quiet: Errors to ignore (empty to raise on all errors) | |
238 | ||
239 | Returns: | |
240 | The offset of the first subnode, if any | |
241 | ||
242 | Raises: | |
243 | FdtException if no subnode found or other error occurs | |
244 | """ | |
245 | return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet) | |
246 | ||
247 | def next_subnode(self, nodeoffset, quiet=()): | |
248 | """Find the next subnode | |
249 | ||
250 | Args: | |
251 | nodeoffset: Node offset of previous subnode | |
252 | quiet: Errors to ignore (empty to raise on all errors) | |
253 | ||
254 | Returns: | |
255 | The offset of the next subnode, if any | |
256 | ||
257 | Raises: | |
258 | FdtException if no more subnode found or other error occurs | |
259 | """ | |
260 | return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet) | |
261 | ||
262 | def totalsize(self): | |
263 | """Return the total size of the device tree | |
264 | ||
265 | Returns: | |
266 | Total tree size in bytes | |
267 | """ | |
268 | return check_err(fdt_totalsize(self._fdt)) | |
269 | ||
270 | def off_dt_struct(self): | |
271 | """Return the start of the device tree struct area | |
272 | ||
273 | Returns: | |
274 | Start offset of struct area | |
275 | """ | |
276 | return check_err(fdt_off_dt_struct(self._fdt)) | |
277 | ||
278 | def pack(self, quiet=()): | |
279 | """Pack the device tree to remove unused space | |
280 | ||
281 | This adjusts the tree in place. | |
282 | ||
283 | Args: | |
284 | quiet: Errors to ignore (empty to raise on all errors) | |
285 | ||
286 | Raises: | |
287 | FdtException if any error occurs | |
288 | """ | |
289 | return check_err(fdt_pack(self._fdt), quiet) | |
290 | ||
291 | def delprop(self, nodeoffset, prop_name): | |
292 | """Delete a property from a node | |
293 | ||
294 | Args: | |
295 | nodeoffset: Node offset containing property to delete | |
296 | prop_name: Name of property to delete | |
297 | ||
298 | Raises: | |
299 | FdtError if the property does not exist, or another error occurs | |
300 | """ | |
301 | return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name)) | |
302 | ||
303 | def getprop(self, nodeoffset, prop_name, quiet=()): | |
304 | """Get a property from a node | |
305 | ||
306 | Args: | |
307 | nodeoffset: Node offset containing property to get | |
308 | prop_name: Name of property to get | |
309 | quiet: Errors to ignore (empty to raise on all errors) | |
310 | ||
311 | Returns: | |
312 | Value of property as a bytearray, or -ve error number | |
313 | ||
314 | Raises: | |
315 | FdtError if any error occurs (e.g. the property is not found) | |
316 | """ | |
317 | pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name), | |
318 | quiet) | |
319 | if isinstance(pdata, (int)): | |
320 | return pdata | |
321 | return bytearray(pdata[0]) | |
322 | ||
93c94b88 SG |
323 | def get_phandle(self, nodeoffset): |
324 | """Get the phandle of a node | |
325 | ||
326 | Args: | |
327 | nodeoffset: Node offset to check | |
328 | ||
329 | Returns: | |
330 | phandle of node, or 0 if the node has no phandle or another error | |
331 | occurs | |
332 | """ | |
333 | return fdt_get_phandle(self._fdt, nodeoffset) | |
334 | ||
335 | def parent_offset(self, nodeoffset, quiet=()): | |
336 | """Get the offset of a node's parent | |
337 | ||
338 | Args: | |
339 | nodeoffset: Node offset to check | |
340 | quiet: Errors to ignore (empty to raise on all errors) | |
341 | ||
342 | Returns: | |
343 | The offset of the parent node, if any | |
344 | ||
345 | Raises: | |
346 | FdtException if no parent found or other error occurs | |
347 | """ | |
348 | return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet) | |
349 | ||
350 | def node_offset_by_phandle(self, phandle, quiet=()): | |
351 | """Get the offset of a node with the given phandle | |
352 | ||
353 | Args: | |
354 | phandle: Phandle to search for | |
355 | quiet: Errors to ignore (empty to raise on all errors) | |
356 | ||
357 | Returns: | |
358 | The offset of node with that phandle, if any | |
359 | ||
360 | Raises: | |
361 | FdtException if no node found or other error occurs | |
362 | """ | |
363 | return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet) | |
cf2064d7 SG |
364 | |
365 | class Property: | |
366 | """Holds a device tree property name and value. | |
367 | ||
368 | This holds a copy of a property taken from the device tree. It does not | |
369 | reference the device tree, so if anything changes in the device tree, | |
370 | a Property object will remain valid. | |
371 | ||
372 | Properties: | |
373 | name: Property name | |
374 | value: Proper value as a bytearray | |
375 | """ | |
376 | def __init__(self, name, value): | |
377 | self.name = name | |
378 | self.value = value | |
379 | %} | |
380 | ||
381 | %rename(fdt_property) fdt_property_func; | |
382 | ||
383 | typedef int fdt32_t; | |
384 | ||
385 | %include "libfdt/fdt.h" | |
386 | ||
387 | %include "typemaps.i" | |
388 | ||
389 | /* Most functions don't change the device tree, so use a const void * */ | |
390 | %typemap(in) (const void *)(const void *fdt) { | |
391 | if (!PyByteArray_Check($input)) { | |
392 | SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" | |
393 | "', argument " "$argnum"" of type '" "$type""'"); | |
394 | } | |
395 | $1 = (void *)PyByteArray_AsString($input); | |
396 | fdt = $1; | |
397 | fdt = fdt; /* avoid unused variable warning */ | |
398 | } | |
399 | ||
400 | /* Some functions do change the device tree, so use void * */ | |
401 | %typemap(in) (void *)(const void *fdt) { | |
402 | if (!PyByteArray_Check($input)) { | |
403 | SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" | |
404 | "', argument " "$argnum"" of type '" "$type""'"); | |
405 | } | |
406 | $1 = PyByteArray_AsString($input); | |
407 | fdt = $1; | |
408 | fdt = fdt; /* avoid unused variable warning */ | |
409 | } | |
410 | ||
411 | %typemap(out) (struct fdt_property *) { | |
412 | PyObject *buff; | |
413 | ||
414 | if ($1) { | |
415 | resultobj = PyString_FromString( | |
416 | fdt_string(fdt1, fdt32_to_cpu($1->nameoff))); | |
417 | buff = PyByteArray_FromStringAndSize( | |
418 | (const char *)($1 + 1), fdt32_to_cpu($1->len)); | |
419 | resultobj = SWIG_Python_AppendOutput(resultobj, buff); | |
420 | } | |
421 | } | |
422 | ||
423 | %apply int *OUTPUT { int *lenp }; | |
424 | ||
425 | /* typemap used for fdt_getprop() */ | |
426 | %typemap(out) (const void *) { | |
427 | if (!$1) | |
428 | $result = Py_None; | |
429 | else | |
430 | $result = Py_BuildValue("s#", $1, *arg4); | |
431 | } | |
432 | ||
433 | /* We have both struct fdt_property and a function fdt_property() */ | |
434 | %warnfilter(302) fdt_property; | |
435 | ||
436 | /* These are macros in the header so have to be redefined here */ | |
437 | int fdt_magic(const void *fdt); | |
438 | int fdt_totalsize(const void *fdt); | |
439 | int fdt_off_dt_struct(const void *fdt); | |
440 | int fdt_off_dt_strings(const void *fdt); | |
441 | int fdt_off_mem_rsvmap(const void *fdt); | |
442 | int fdt_version(const void *fdt); | |
443 | int fdt_last_comp_version(const void *fdt); | |
444 | int fdt_boot_cpuid_phys(const void *fdt); | |
445 | int fdt_size_dt_strings(const void *fdt); | |
446 | int fdt_size_dt_struct(const void *fdt); | |
447 | ||
448 | %include <../libfdt/libfdt.h> |