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