dtoc: Support deleting a node
[platform/kernel/u-boot.git] / tools / dtoc / fdt.py
1 #!/usr/bin/python
2 # SPDX-License-Identifier: GPL-2.0+
3 #
4 # Copyright (C) 2016 Google, Inc
5 # Written by Simon Glass <sjg@chromium.org>
6 #
7
8 from enum import IntEnum
9 import struct
10 import sys
11
12 from dtoc import fdt_util
13 import libfdt
14 from libfdt import QUIET_NOTFOUND
15 from patman import tools
16
17 # This deals with a device tree, presenting it as an assortment of Node and
18 # Prop objects, representing nodes and properties, respectively. This file
19 # contains the base classes and defines the high-level API. You can use
20 # FdtScan() as a convenience function to create and scan an Fdt.
21
22 # This implementation uses a libfdt Python library to access the device tree,
23 # so it is fairly efficient.
24
25 # A list of types we support
26 class Type(IntEnum):
27     # Types in order from widest to narrowest
28     (BYTE, INT, STRING, BOOL, INT64) = range(5)
29
30     def needs_widening(self, other):
31         """Check if this type needs widening to hold a value from another type
32
33         A wider type is one that can hold a wider array of information than
34         another one, or is less restrictive, so it can hold the information of
35         another type as well as its own. This is similar to the concept of
36         type-widening in C.
37
38         This uses a simple arithmetic comparison, since type values are in order
39         from widest (BYTE) to narrowest (INT64).
40
41         Args:
42             other: Other type to compare against
43
44         Return:
45             True if the other type is wider
46         """
47         return self.value > other.value
48
49 def CheckErr(errnum, msg):
50     if errnum:
51         raise ValueError('Error %d: %s: %s' %
52             (errnum, libfdt.fdt_strerror(errnum), msg))
53
54
55 def BytesToValue(data):
56     """Converts a string of bytes into a type and value
57
58     Args:
59         A bytes value (which on Python 2 is an alias for str)
60
61     Return:
62         A tuple:
63             Type of data
64             Data, either a single element or a list of elements. Each element
65             is one of:
66                 Type.STRING: str/bytes value from the property
67                 Type.INT: a byte-swapped integer stored as a 4-byte str/bytes
68                 Type.BYTE: a byte stored as a single-byte str/bytes
69     """
70     data = bytes(data)
71     size = len(data)
72     strings = data.split(b'\0')
73     is_string = True
74     count = len(strings) - 1
75     if count > 0 and not len(strings[-1]):
76         for string in strings[:-1]:
77             if not string:
78                 is_string = False
79                 break
80             for ch in string:
81                 if ch < 32 or ch > 127:
82                     is_string = False
83                     break
84     else:
85         is_string = False
86     if is_string:
87         if count == 1: 
88             return Type.STRING, strings[0].decode()
89         else:
90             return Type.STRING, [s.decode() for s in strings[:-1]]
91     if size % 4:
92         if size == 1:
93             return Type.BYTE, chr(data[0])
94         else:
95             return Type.BYTE, [chr(ch) for ch in list(data)]
96     val = []
97     for i in range(0, size, 4):
98         val.append(data[i:i + 4])
99     if size == 4:
100         return Type.INT, val[0]
101     else:
102         return Type.INT, val
103
104
105 class Prop:
106     """A device tree property
107
108     Properties:
109         node: Node containing this property
110         offset: Offset of the property (None if still to be synced)
111         name: Property name (as per the device tree)
112         value: Property value as a string of bytes, or a list of strings of
113             bytes
114         type: Value type
115     """
116     def __init__(self, node, offset, name, data):
117         self._node = node
118         self._offset = offset
119         self.name = name
120         self.value = None
121         self.bytes = bytes(data)
122         self.dirty = offset is None
123         if not data:
124             self.type = Type.BOOL
125             self.value = True
126             return
127         self.type, self.value = BytesToValue(bytes(data))
128
129     def RefreshOffset(self, poffset):
130         self._offset = poffset
131
132     def Widen(self, newprop):
133         """Figure out which property type is more general
134
135         Given a current property and a new property, this function returns the
136         one that is less specific as to type. The less specific property will
137         be ble to represent the data in the more specific property. This is
138         used for things like:
139
140             node1 {
141                 compatible = "fred";
142                 value = <1>;
143             };
144             node1 {
145                 compatible = "fred";
146                 value = <1 2>;
147             };
148
149         He we want to use an int array for 'value'. The first property
150         suggests that a single int is enough, but the second one shows that
151         it is not. Calling this function with these two propertes would
152         update the current property to be like the second, since it is less
153         specific.
154         """
155         if self.type.needs_widening(newprop.type):
156
157             # A boolean has an empty value: if it exists it is True and if not
158             # it is False. So when widening we always start with an empty list
159             # since the only valid integer property would be an empty list of
160             # integers.
161             # e.g. this is a boolean:
162             #    some-prop;
163             # and it would be widened to int list by:
164             #    some-prop = <1 2>;
165             if self.type == Type.BOOL:
166                 self.type = Type.INT
167                 self.value = [self.GetEmpty(self.type)]
168             if self.type == Type.INT and newprop.type == Type.BYTE:
169                 if type(self.value) == list:
170                     new_value = []
171                     for val in self.value:
172                         new_value += [chr(by) for by in val]
173                 else:
174                     new_value = [chr(by) for by in self.value]
175                 self.value = new_value
176             self.type = newprop.type
177
178         if type(newprop.value) == list:
179             if type(self.value) != list:
180                 self.value = [self.value]
181
182             if len(newprop.value) > len(self.value):
183                 val = self.GetEmpty(self.type)
184                 while len(self.value) < len(newprop.value):
185                     self.value.append(val)
186
187     @classmethod
188     def GetEmpty(self, type):
189         """Get an empty / zero value of the given type
190
191         Returns:
192             A single value of the given type
193         """
194         if type == Type.BYTE:
195             return chr(0)
196         elif type == Type.INT:
197             return struct.pack('>I', 0);
198         elif type == Type.STRING:
199             return ''
200         else:
201             return True
202
203     def GetOffset(self):
204         """Get the offset of a property
205
206         Returns:
207             The offset of the property (struct fdt_property) within the file
208         """
209         self._node._fdt.CheckCache()
210         return self._node._fdt.GetStructOffset(self._offset)
211
212     def SetInt(self, val):
213         """Set the integer value of the property
214
215         The device tree is marked dirty so that the value will be written to
216         the block on the next sync.
217
218         Args:
219             val: Integer value (32-bit, single cell)
220         """
221         self.bytes = struct.pack('>I', val);
222         self.value = self.bytes
223         self.type = Type.INT
224         self.dirty = True
225
226     def SetData(self, bytes):
227         """Set the value of a property as bytes
228
229         Args:
230             bytes: New property value to set
231         """
232         self.bytes = bytes
233         self.type, self.value = BytesToValue(bytes)
234         self.dirty = True
235
236     def Sync(self, auto_resize=False):
237         """Sync property changes back to the device tree
238
239         This updates the device tree blob with any changes to this property
240         since the last sync.
241
242         Args:
243             auto_resize: Resize the device tree automatically if it does not
244                 have enough space for the update
245
246         Raises:
247             FdtException if auto_resize is False and there is not enough space
248         """
249         if self.dirty:
250             node = self._node
251             fdt_obj = node._fdt._fdt_obj
252             node_name = fdt_obj.get_name(node._offset)
253             if node_name and node_name != node.name:
254                 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
255                                  (node.path, node_name))
256
257             if auto_resize:
258                 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
259                                     (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
260                     fdt_obj.resize(fdt_obj.totalsize() + 1024 +
261                                    len(self.bytes))
262                     fdt_obj.setprop(node.Offset(), self.name, self.bytes)
263             else:
264                 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
265             self.dirty = False
266
267
268 class Node:
269     """A device tree node
270
271     Properties:
272         parent: Parent Node
273         offset: Integer offset in the device tree (None if to be synced)
274         name: Device tree node tname
275         path: Full path to node, along with the node name itself
276         _fdt: Device tree object
277         subnodes: A list of subnodes for this node, each a Node object
278         props: A dict of properties for this node, each a Prop object.
279             Keyed by property name
280     """
281     def __init__(self, fdt, parent, offset, name, path):
282         self._fdt = fdt
283         self.parent = parent
284         self._offset = offset
285         self.name = name
286         self.path = path
287         self.subnodes = []
288         self.props = {}
289
290     def GetFdt(self):
291         """Get the Fdt object for this node
292
293         Returns:
294             Fdt object
295         """
296         return self._fdt
297
298     def FindNode(self, name):
299         """Find a node given its name
300
301         Args:
302             name: Node name to look for
303         Returns:
304             Node object if found, else None
305         """
306         for subnode in self.subnodes:
307             if subnode.name == name:
308                 return subnode
309         return None
310
311     def Offset(self):
312         """Returns the offset of a node, after checking the cache
313
314         This should be used instead of self._offset directly, to ensure that
315         the cache does not contain invalid offsets.
316         """
317         self._fdt.CheckCache()
318         return self._offset
319
320     def Scan(self):
321         """Scan a node's properties and subnodes
322
323         This fills in the props and subnodes properties, recursively
324         searching into subnodes so that the entire tree is built.
325         """
326         fdt_obj = self._fdt._fdt_obj
327         self.props = self._fdt.GetProps(self)
328         phandle = fdt_obj.get_phandle(self.Offset())
329         if phandle:
330             self._fdt.phandle_to_node[phandle] = self
331
332         offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
333         while offset >= 0:
334             sep = '' if self.path[-1] == '/' else '/'
335             name = fdt_obj.get_name(offset)
336             path = self.path + sep + name
337             node = Node(self._fdt, self, offset, name, path)
338             self.subnodes.append(node)
339
340             node.Scan()
341             offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
342
343     def Refresh(self, my_offset):
344         """Fix up the _offset for each node, recursively
345
346         Note: This does not take account of property offsets - these will not
347         be updated.
348         """
349         fdt_obj = self._fdt._fdt_obj
350         if self._offset != my_offset:
351             self._offset = my_offset
352         name = fdt_obj.get_name(self._offset)
353         if name and self.name != name:
354             raise ValueError("Internal error, node '%s' name mismatch '%s'" %
355                              (self.path, name))
356
357         offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
358         for subnode in self.subnodes:
359             if subnode.name != fdt_obj.get_name(offset):
360                 raise ValueError('Internal error, node name mismatch %s != %s' %
361                                  (subnode.name, fdt_obj.get_name(offset)))
362             subnode.Refresh(offset)
363             offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
364         if offset != -libfdt.FDT_ERR_NOTFOUND:
365             raise ValueError('Internal error, offset == %d' % offset)
366
367         poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
368         while poffset >= 0:
369             p = fdt_obj.get_property_by_offset(poffset)
370             prop = self.props.get(p.name)
371             if not prop:
372                 raise ValueError("Internal error, node '%s' property '%s' missing, "
373                                  'offset %d' % (self.path, p.name, poffset))
374             prop.RefreshOffset(poffset)
375             poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
376
377     def DeleteProp(self, prop_name):
378         """Delete a property of a node
379
380         The property is deleted and the offset cache is invalidated.
381
382         Args:
383             prop_name: Name of the property to delete
384         Raises:
385             ValueError if the property does not exist
386         """
387         CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
388                  "Node '%s': delete property: '%s'" % (self.path, prop_name))
389         del self.props[prop_name]
390         self._fdt.Invalidate()
391
392     def AddZeroProp(self, prop_name):
393         """Add a new property to the device tree with an integer value of 0.
394
395         Args:
396             prop_name: Name of property
397         """
398         self.props[prop_name] = Prop(self, None, prop_name,
399                                      tools.get_bytes(0, 4))
400
401     def AddEmptyProp(self, prop_name, len):
402         """Add a property with a fixed data size, for filling in later
403
404         The device tree is marked dirty so that the value will be written to
405         the blob on the next sync.
406
407         Args:
408             prop_name: Name of property
409             len: Length of data in property
410         """
411         value = tools.get_bytes(0, len)
412         self.props[prop_name] = Prop(self, None, prop_name, value)
413
414     def _CheckProp(self, prop_name):
415         """Check if a property is present
416
417         Args:
418             prop_name: Name of property
419
420         Returns:
421             self
422
423         Raises:
424             ValueError if the property is missing
425         """
426         if prop_name not in self.props:
427             raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
428                              (self._fdt._fname, self.path, prop_name))
429         return self
430
431     def SetInt(self, prop_name, val):
432         """Update an integer property int the device tree.
433
434         This is not allowed to change the size of the FDT.
435
436         The device tree is marked dirty so that the value will be written to
437         the blob on the next sync.
438
439         Args:
440             prop_name: Name of property
441             val: Value to set
442         """
443         self._CheckProp(prop_name).props[prop_name].SetInt(val)
444
445     def SetData(self, prop_name, val):
446         """Set the data value of a property
447
448         The device tree is marked dirty so that the value will be written to
449         the blob on the next sync.
450
451         Args:
452             prop_name: Name of property to set
453             val: Data value to set
454         """
455         self._CheckProp(prop_name).props[prop_name].SetData(val)
456
457     def SetString(self, prop_name, val):
458         """Set the string value of a property
459
460         The device tree is marked dirty so that the value will be written to
461         the blob on the next sync.
462
463         Args:
464             prop_name: Name of property to set
465             val: String value to set (will be \0-terminated in DT)
466         """
467         if type(val) == str:
468             val = val.encode('utf-8')
469         self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
470
471     def AddData(self, prop_name, val):
472         """Add a new property to a node
473
474         The device tree is marked dirty so that the value will be written to
475         the blob on the next sync.
476
477         Args:
478             prop_name: Name of property to add
479             val: Bytes value of property
480
481         Returns:
482             Prop added
483         """
484         prop = Prop(self, None, prop_name, val)
485         self.props[prop_name] = prop
486         return prop
487
488     def AddString(self, prop_name, val):
489         """Add a new string property to a node
490
491         The device tree is marked dirty so that the value will be written to
492         the blob on the next sync.
493
494         Args:
495             prop_name: Name of property to add
496             val: String value of property
497
498         Returns:
499             Prop added
500         """
501         val = bytes(val, 'utf-8')
502         return self.AddData(prop_name, val + b'\0')
503
504     def AddStringList(self, prop_name, val):
505         """Add a new string-list property to a node
506
507         The device tree is marked dirty so that the value will be written to
508         the blob on the next sync.
509
510         Args:
511             prop_name: Name of property to add
512             val (list of str): List of strings to add
513
514         Returns:
515             Prop added
516         """
517         out = b''
518         for string in val:
519             out += bytes(string, 'utf-8') + b'\0'
520         return self.AddData(prop_name, out)
521
522     def AddInt(self, prop_name, val):
523         """Add a new integer property to a node
524
525         The device tree is marked dirty so that the value will be written to
526         the blob on the next sync.
527
528         Args:
529             prop_name: Name of property to add
530             val: Integer value of property
531
532         Returns:
533             Prop added
534         """
535         return self.AddData(prop_name, struct.pack('>I', val))
536
537     def AddSubnode(self, name):
538         """Add a new subnode to the node
539
540         Args:
541             name: name of node to add
542
543         Returns:
544             New subnode that was created
545         """
546         path = self.path + '/' + name
547         subnode = Node(self._fdt, self, None, name, path)
548         self.subnodes.append(subnode)
549         return subnode
550
551     def Delete(self):
552         """Delete a node
553
554         The node is deleted and the offset cache is invalidated.
555
556         Args:
557             node (Node): Node to delete
558
559         Raises:
560             ValueError if the node does not exist
561         """
562         CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
563                  "Node '%s': delete" % self.path)
564         parent = self.parent
565         self._fdt.Invalidate()
566         parent.subnodes.remove(self)
567
568     def Sync(self, auto_resize=False):
569         """Sync node changes back to the device tree
570
571         This updates the device tree blob with any changes to this node and its
572         subnodes since the last sync.
573
574         Args:
575             auto_resize: Resize the device tree automatically if it does not
576                 have enough space for the update
577
578         Returns:
579             True if the node had to be added, False if it already existed
580
581         Raises:
582             FdtException if auto_resize is False and there is not enough space
583         """
584         added = False
585         if self._offset is None:
586             # The subnode doesn't exist yet, so add it
587             fdt_obj = self._fdt._fdt_obj
588             if auto_resize:
589                 while True:
590                     offset = fdt_obj.add_subnode(self.parent._offset, self.name,
591                                                 (libfdt.NOSPACE,))
592                     if offset != -libfdt.NOSPACE:
593                         break
594                     fdt_obj.resize(fdt_obj.totalsize() + 1024)
595             else:
596                 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
597             self._offset = offset
598             added = True
599
600         # Sync the existing subnodes first, so that we can rely on the offsets
601         # being correct. As soon as we add new subnodes, it pushes all the
602         # existing subnodes up.
603         for node in reversed(self.subnodes):
604             if node._offset is not None:
605                 node.Sync(auto_resize)
606
607         # Sync subnodes in reverse so that we get the expected order. Each
608         # new node goes at the start of the subnode list. This avoids an O(n^2)
609         # rescan of node offsets.
610         num_added = 0
611         for node in reversed(self.subnodes):
612             if node.Sync(auto_resize):
613                 num_added += 1
614         if num_added:
615             # Reorder our list of nodes to put the new ones first, since that's
616             # what libfdt does
617             old_count = len(self.subnodes) - num_added
618             subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
619             self.subnodes = subnodes
620
621         # Sync properties now, whose offsets should not have been disturbed,
622         # since properties come before subnodes. This is done after all the
623         # subnode processing above, since updating properties can disturb the
624         # offsets of those subnodes.
625         # Properties are synced in reverse order, with new properties added
626         # before existing properties are synced. This ensures that the offsets
627         # of earlier properties are not disturbed.
628         # Note that new properties will have an offset of None here, which
629         # Python cannot sort against int. So use a large value instead so that
630         # new properties are added first.
631         prop_list = sorted(self.props.values(),
632                            key=lambda prop: prop._offset or 1 << 31,
633                            reverse=True)
634         for prop in prop_list:
635             prop.Sync(auto_resize)
636         return added
637
638
639 class Fdt:
640     """Provides simple access to a flat device tree blob using libfdts.
641
642     Properties:
643       fname: Filename of fdt
644       _root: Root of device tree (a Node object)
645       name: Helpful name for this Fdt for the user (useful when creating the
646         DT from data rather than a file)
647     """
648     def __init__(self, fname):
649         self._fname = fname
650         self._cached_offsets = False
651         self.phandle_to_node = {}
652         self.name = ''
653         if self._fname:
654             self.name = self._fname
655             self._fname = fdt_util.EnsureCompiled(self._fname)
656
657             with open(self._fname, 'rb') as fd:
658                 self._fdt_obj = libfdt.Fdt(fd.read())
659
660     @staticmethod
661     def FromData(data, name=''):
662         """Create a new Fdt object from the given data
663
664         Args:
665             data: Device-tree data blob
666             name: Helpful name for this Fdt for the user
667
668         Returns:
669             Fdt object containing the data
670         """
671         fdt = Fdt(None)
672         fdt._fdt_obj = libfdt.Fdt(bytes(data))
673         fdt.name = name
674         return fdt
675
676     def LookupPhandle(self, phandle):
677         """Look up a phandle
678
679         Args:
680             phandle: Phandle to look up (int)
681
682         Returns:
683             Node object the phandle points to
684         """
685         return self.phandle_to_node.get(phandle)
686
687     def Scan(self, root='/'):
688         """Scan a device tree, building up a tree of Node objects
689
690         This fills in the self._root property
691
692         Args:
693             root: Ignored
694
695         TODO(sjg@chromium.org): Implement the 'root' parameter
696         """
697         self._cached_offsets = True
698         self._root = self.Node(self, None, 0, '/', '/')
699         self._root.Scan()
700
701     def GetRoot(self):
702         """Get the root Node of the device tree
703
704         Returns:
705             The root Node object
706         """
707         return self._root
708
709     def GetNode(self, path):
710         """Look up a node from its path
711
712         Args:
713             path: Path to look up, e.g. '/microcode/update@0'
714         Returns:
715             Node object, or None if not found
716         """
717         node = self._root
718         parts = path.split('/')
719         if len(parts) < 2:
720             return None
721         if len(parts) == 2 and parts[1] == '':
722             return node
723         for part in parts[1:]:
724             node = node.FindNode(part)
725             if not node:
726                 return None
727         return node
728
729     def Flush(self):
730         """Flush device tree changes back to the file
731
732         If the device tree has changed in memory, write it back to the file.
733         """
734         with open(self._fname, 'wb') as fd:
735             fd.write(self._fdt_obj.as_bytearray())
736
737     def Sync(self, auto_resize=False):
738         """Make sure any DT changes are written to the blob
739
740         Args:
741             auto_resize: Resize the device tree automatically if it does not
742                 have enough space for the update
743
744         Raises:
745             FdtException if auto_resize is False and there is not enough space
746         """
747         self.CheckCache()
748         self._root.Sync(auto_resize)
749         self.Refresh()
750
751     def Pack(self):
752         """Pack the device tree down to its minimum size
753
754         When nodes and properties shrink or are deleted, wasted space can
755         build up in the device tree binary.
756         """
757         CheckErr(self._fdt_obj.pack(), 'pack')
758         self.Refresh()
759
760     def GetContents(self):
761         """Get the contents of the FDT
762
763         Returns:
764             The FDT contents as a string of bytes
765         """
766         return bytes(self._fdt_obj.as_bytearray())
767
768     def GetFdtObj(self):
769         """Get the contents of the FDT
770
771         Returns:
772             The FDT contents as a libfdt.Fdt object
773         """
774         return self._fdt_obj
775
776     def GetProps(self, node):
777         """Get all properties from a node.
778
779         Args:
780             node: Full path to node name to look in.
781
782         Returns:
783             A dictionary containing all the properties, indexed by node name.
784             The entries are Prop objects.
785
786         Raises:
787             ValueError: if the node does not exist.
788         """
789         props_dict = {}
790         poffset = self._fdt_obj.first_property_offset(node._offset,
791                                                       QUIET_NOTFOUND)
792         while poffset >= 0:
793             p = self._fdt_obj.get_property_by_offset(poffset)
794             prop = Prop(node, poffset, p.name, p)
795             props_dict[prop.name] = prop
796
797             poffset = self._fdt_obj.next_property_offset(poffset,
798                                                          QUIET_NOTFOUND)
799         return props_dict
800
801     def Invalidate(self):
802         """Mark our offset cache as invalid"""
803         self._cached_offsets = False
804
805     def CheckCache(self):
806         """Refresh the offset cache if needed"""
807         if self._cached_offsets:
808             return
809         self.Refresh()
810
811     def Refresh(self):
812         """Refresh the offset cache"""
813         self._root.Refresh(0)
814         self._cached_offsets = True
815
816     def GetStructOffset(self, offset):
817         """Get the file offset of a given struct offset
818
819         Args:
820             offset: Offset within the 'struct' region of the device tree
821         Returns:
822             Position of @offset within the device tree binary
823         """
824         return self._fdt_obj.off_dt_struct() + offset
825
826     @classmethod
827     def Node(self, fdt, parent, offset, name, path):
828         """Create a new node
829
830         This is used by Fdt.Scan() to create a new node using the correct
831         class.
832
833         Args:
834             fdt: Fdt object
835             parent: Parent node, or None if this is the root node
836             offset: Offset of node
837             name: Node name
838             path: Full path to node
839         """
840         node = Node(fdt, parent, offset, name, path)
841         return node
842
843     def GetFilename(self):
844         """Get the filename of the device tree
845
846         Returns:
847             String filename
848         """
849         return self._fname
850
851 def FdtScan(fname):
852     """Returns a new Fdt object"""
853     dtb = Fdt(fname)
854     dtb.Scan()
855     return dtb