aboutsummaryrefslogtreecommitdiff
path: root/chirp/bitwise.py
diff options
context:
space:
mode:
Diffstat (limited to 'chirp/bitwise.py')
-rw-r--r--chirp/bitwise.py1067
1 files changed, 1067 insertions, 0 deletions
diff --git a/chirp/bitwise.py b/chirp/bitwise.py
new file mode 100644
index 0000000..1559162
--- /dev/null
+++ b/chirp/bitwise.py
@@ -0,0 +1,1067 @@
+# Copyright 2010 Dan Smith <dsmith@danplanet.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Language:
+#
+# Example definitions:
+#
+# bit foo[8]; /* Eight single bit values */
+# u8 foo; /* Unsigned 8-bit value */
+# u16 foo; /* Unsigned 16-bit value */
+# ul16 foo; /* Unsigned 16-bit value (LE) */
+# u24 foo; /* Unsigned 24-bit value */
+# ul24 foo; /* Unsigned 24-bit value (LE) */
+# u32 foo; /* Unsigned 32-bit value */
+# ul32 foo; /* Unsigned 32-bit value (LE) */
+# i8 foo; /* Signed 8-bit value */
+# i16 foo; /* Signed 16-bit value */
+# il16 foo; /* Signed 16-bit value (LE) */
+# i24 foo; /* Signed 24-bit value */
+# il24 foo; /* Signed 24-bit value (LE) */
+# i32 foo; /* Signed 32-bit value */
+# il32 foo; /* Signed 32-bit value (LE) */
+# char foo; /* Character (single-byte */
+# lbcd foo; /* BCD-encoded byte (LE) */
+# bbcd foo; /* BCD-encoded byte (BE) */
+# char foo[8]; /* 8-char array */
+# struct {
+# u8 foo;
+# u16 bar;
+# } baz; /* Structure with u8 and u16 */
+#
+# Example directives:
+#
+# #seekto 0x1AB; /* Set the data offset to 0x1AB */
+# #seek 4; /* Set the data offset += 4 */
+# #printoffset "foobar" /* Echo the live data offset,
+# prefixed by string while parsing */
+#
+# Usage:
+#
+# Create a data definition in a string, and pass it and the data
+# to parse to the parse() function. The result is a structure with
+# dict-like objects for structures, indexed by name, and lists of
+# objects for arrays. The actual data elements can be interpreted
+# as integers directly (for int types). Strings and BCD arrays
+# behave as expected.
+
+import struct
+import os
+import logging
+
+import six
+from builtins import bytes
+
+from chirp import bitwise_grammar
+from chirp.memmap import MemoryMap
+
+LOG = logging.getLogger(__name__)
+
+
+class ParseError(Exception):
+ """Indicates an error parsing a definition"""
+ pass
+
+
+def byte_to_int(b):
+ if six.PY3 or isinstance(b, int):
+ return b
+ else:
+ return ord(b)
+
+
+def int_to_byte(i):
+ if six.PY3:
+ return bytes([i])
+ else:
+ return chr(i)
+
+
+def string_straight_encode(string):
+ """str -> bytes"""
+ # So, there are a lot of py2-thinking chirp drivers that
+ # will do something like this:
+ #
+ # memobj.name = 'foo\xff'
+ #
+ # because they need the string to be stored in radio memory
+ # with a 0xFF byte terminating the string. If they do that in
+ # py3 we will get the unicode equivalent, which is a non-ASCII
+ # character. Conventional unicode wisdom would tell us we need
+ # to encode() the string to get UTF-8 bytes, but that's not
+ # what we want here (the radio doesn't support UTF-8 and we need
+ # specific binary values in memory). Ideally we would have
+ # written all of chirp with bytes() for these values, but alas.
+ # We can get the intended string here by doing bytes([ord(char)]).
+ return bytes(b''.join(int_to_byte(ord(b)) for b in string))
+
+
+def string_straight_decode(string):
+ """bytes -> str"""
+ # Normally, we would want to decode bytes() to str() for py3.
+ # However...chirp drivers are currently using strings with
+ # hex escapes for setting binary byte values in memories.
+ # Technically, this is char, which is a little more like bytes()
+ # in py3, but until every driver has converted its strings to
+ # bytes(), this is massively simpler. Since set_value() is
+ # doing the inverse, we can do this here. If something sets
+ # a char to '\xFF' it will arrive below as the unicode character,
+ # and be converted to the integer/bytes value. When it is read out
+ # here, we will return that same unicode character and the driver
+ # will detect '\xFF' properly.
+ # FIXMEPY3: Remove this and the hack below when drivers convert to
+ # bytestrings.
+ return ''.join(chr(byte_to_int(b)) for b in string)
+
+
+def format_binary(nbits, value, pad=8):
+ s = ""
+ for i in range(0, nbits):
+ s = "%i%s" % (value & 0x01, s)
+ value >>= 1
+ return "%s%s" % ((pad - len(s)) * ".", s)
+
+
+def bits_between(start, end):
+ bits = (1 << (int(end) - int(start))) - 1
+ return bits << int(start)
+
+
+def pp(structure, level=0):
+ for i in structure:
+ if isinstance(i, list):
+ pp(i, level+2)
+ elif isinstance(i, tuple):
+ if isinstance(i[1], str):
+ print("%s%s: %s" % (" " * level, i[0], i[1]))
+ else:
+ print("%s%s:" % (" " * level, i[0]))
+ pp(i, level+2)
+ elif isinstance(i, str):
+ print("%s%s" % (" " * level, i))
+
+
+def array_copy(dst, src):
+ """Copy an array src into DataElement array dst"""
+ if len(dst) != len(src):
+ raise Exception("Arrays differ in size")
+
+ for i in range(0, len(dst)):
+ dst[i].set_value(src[i])
+
+
+def bcd_to_int(bcd_array):
+ """Convert an array of bcdDataElement like \x12\x34
+ into an int like 1234"""
+ value = 0
+ for bcd in bcd_array:
+ a, b = bcd.get_value()
+ value = (value * 100) + (a * 10) + b
+ return value
+
+
+def int_to_bcd(bcd_array, value):
+ """Convert an int like 1234 into bcdDataElements like "\x12\x34" """
+ for i in reversed(list(range(0, len(bcd_array)))):
+ bcd_array[i].set_value(value % 100)
+ value /= 100
+
+
+def get_string(char_array):
+ """Convert an array of charDataElements into a string"""
+ return "".join([x.get_value() for x in char_array])
+
+
+def set_string(char_array, string):
+ """Set an array of charDataElements from a string"""
+ array_copy(char_array, list(string))
+
+
+class DataElement:
+ _size = 1
+
+ def __init__(self, data, offset, count=1):
+ self._data = data
+ self._offset = int(offset)
+ self._count = count
+
+ def size(self):
+ return self._size * 8
+
+ def get_offset(self):
+ return self._offset
+
+ def _get_value(self, data):
+ raise Exception("Not implemented")
+
+ def get_value(self):
+ value = self._data[self._offset:self._offset + self._size]
+ return self._get_value(value)
+
+ def set_value(self, value):
+ raise Exception("Not implemented for %s" % self.__class__)
+
+ def get_raw(self, asbytes=False):
+ raw = self._data[self._offset:self._offset+self._size]
+ if asbytes:
+ return bytes(raw)
+ else:
+ return string_straight_decode(raw)
+
+ def set_raw(self, data):
+ if isinstance(data, str):
+ data = string_straight_encode(data)
+ self._data[self._offset] = data[:self._size]
+
+ def __getattr__(self, name):
+ raise AttributeError("Unknown attribute %s in %s" % (name,
+ self.__class__))
+
+ def __repr__(self):
+ return "(%s:%i bytes @ %04x)" % (self.__class__.__name__,
+ self._size,
+ self._offset)
+
+
+class arrayDataElement(DataElement):
+ def __repr__(self):
+ if isinstance(self.__items[0], bcdDataElement):
+ return "%i:[(%i)]" % (len(self.__items), int(self))
+
+ if isinstance(self.__items[0], charDataElement):
+ return "%i:[(%s)]" % (len(self.__items), repr(str(self))[1:-1])
+
+ s = "%i:[" % len(self.__items)
+ s += ",".join([repr(item) for item in self.__items])
+ s += "]"
+ return s
+
+ def __init__(self, offset):
+ self.__items = []
+ self._offset = offset
+
+ def append(self, item):
+ self.__items.append(item)
+
+ def get_value(self):
+ return list(self.__items)
+
+ def get_raw(self, asbytes=False):
+ raw = [item.get_raw(asbytes=asbytes) for item in self.__items]
+ if asbytes:
+ return bytes(b''.join(raw))
+ else:
+ return "".join(raw)
+
+ def __setitem__(self, index, val):
+ self.__items[index].set_value(val)
+
+ def __getitem__(self, index):
+ if isinstance(index, slice):
+ return self.__items[int(index.start):int(index.stop)]
+ else:
+ return self.__items[int(index)]
+
+ def __len__(self):
+ return len(self.__items)
+
+ def __str__(self):
+ if isinstance(self.__items[0], charDataElement):
+ # NOTE: All the values should be strings coming out of py3.
+ # and on py2 they could be a mix of unicode and str, the latter
+ # for non-ASCII values. On py2 we can just coerce all of these
+ # types to a string for compatbility.
+ return "".join([str(x.get_value()) for x in self.__items])
+ else:
+ return str(self.__items)
+
+ def __int__(self):
+ if isinstance(self.__items[0], bcdDataElement):
+ val = 0
+ if isinstance(self.__items[0], bbcdDataElement):
+ items = self.__items
+ else:
+ items = reversed(self.__items)
+ for i in items:
+ tens, ones = i.get_value()
+ val = (val * 100) + (tens * 10) + ones
+ return val
+ else:
+ raise ValueError("Cannot coerce this to int")
+
+ def __set_value_bbcd(self, value):
+ for i in reversed(self.__items):
+ twodigits = value % 100
+ value /= 100
+ i.set_value(twodigits)
+
+ def __set_value_lbcd(self, value):
+ for i in self.__items:
+ twodigits = value % 100
+ value /= 100
+ i.set_value(twodigits)
+
+ def __set_value_char(self, value):
+ if len(value) != len(self.__items):
+ raise ValueError("String expects exactly %i characters, not %i" % (
+ len(self.__items), len(value)))
+ for i in range(0, len(self.__items)):
+ self.__items[i].set_value(value[i])
+
+ def set_value(self, value):
+ if isinstance(self.__items[0], bbcdDataElement):
+ self.__set_value_bbcd(int(value))
+ elif isinstance(self.__items[0], lbcdDataElement):
+ self.__set_value_lbcd(int(value))
+ elif isinstance(self.__items[0], charDataElement):
+ self.__set_value_char(value)
+ elif len(value) != len(self.__items):
+ raise ValueError("Array cardinality mismatch")
+ else:
+ for i in range(0, len(value)):
+ self.__items[i].set_value(value[i])
+
+ def index(self, value):
+ index = 0
+ for i in self.__items:
+ if i.get_value() == value:
+ return index
+ index += 1
+ raise IndexError()
+
+ def __iter__(self):
+ return iter(self.__items)
+
+ def items(self):
+ index = 0
+ for item in self.__items:
+ yield (str(index), item)
+ index += 1
+
+ def size(self):
+ size = 0
+ for i in self.__items:
+ size += i.size()
+ return size
+
+
+class intDataElement(DataElement):
+ def __repr__(self):
+ fmt = "0x%%0%iX" % (self._size * 2)
+ return fmt % int(self)
+
+ def __int__(self):
+ return self.get_value()
+
+ def __nonzero__(self):
+ return int(self) != 0
+
+ def __invert__(self):
+ return ~self.get_value()
+
+ def __trunc__(self):
+ return self.get_value()
+
+ def __abs__(self):
+ return abs(self.get_value())
+
+ def __mod__(self, val):
+ return self.get_value() % val
+
+ def __mul__(self, val):
+ return self.get_value() * val
+
+ def __truediv__(self, val):
+ return self.get_value() / val
+
+ def __div__(self, val):
+ return self.get_value() / val
+
+ def __floordiv__(self, val):
+ return self.get_value() // val
+
+ def __add__(self, val):
+ return self.get_value() + val
+
+ def __sub__(self, val):
+ return self.get_value() - val
+
+ def __or__(self, val):
+ return self.get_value() | val
+
+ def __xor__(self, val):
+ return self.get_value() ^ val
+
+ def __and__(self, val):
+ return self.get_value() & val
+
+ def __radd__(self, val):
+ return val + self.get_value()
+
+ def __rsub__(self, val):
+ return val - self.get_value()
+
+ def __rmul__(self, val):
+ return val * self.get_value()
+
+ def __rdiv__(self, val):
+ return val / self.get_value()
+
+ def __rand__(self, val):
+ return val & self.get_value()
+
+ def __ror__(self, val):
+ return val | self.get_value()
+
+ def __rxor__(self, val):
+ return val ^ self.get_value()
+
+ def __rmod__(self, val):
+ return val % self.get_value()
+
+ def __lshift__(self, val):
+ return self.get_value() << val
+
+ def __rshift__(self, val):
+ return self.get_value() >> val
+
+ def __iadd__(self, val):
+ self.set_value(self.get_value() + val)
+ return self
+
+ def __isub__(self, val):
+ self.set_value(self.get_value() - val)
+ return self
+
+ def __imul__(self, val):
+ self.set_value(self.get_value() * val)
+ return self
+
+ def __idiv__(self, val):
+ self.set_value(self.get_value() / val)
+ return self
+
+ def __imod__(self, val):
+ self.set_value(self.get_value() % val)
+ return self
+
+ def __iand__(self, val):
+ self.set_value(self.get_value() & val)
+ return self
+
+ def __ior__(self, val):
+ self.set_value(self.get_value() | val)
+ return self
+
+ def __ixor__(self, val):
+ self.set_value(self.get_value() ^ val)
+ return self
+
+ def __index__(self):
+ return abs(self)
+
+ def __eq__(self, val):
+ return self.get_value() == val
+
+ def __ne__(self, val):
+ return self.get_value() != val
+
+ def __lt__(self, val):
+ return self.get_value() < val
+
+ def __le__(self, val):
+ return self.get_value() <= val
+
+ def __gt__(self, val):
+ return self.get_value() > val
+
+ def __ge__(self, val):
+ return self.get_value() >= val
+
+ def __bool__(self):
+ return self.get_value() != 0
+
+
+class u8DataElement(intDataElement):
+ _size = 1
+
+ def _get_value(self, data):
+ return ord(data)
+
+ def set_value(self, value):
+ self._data[self._offset] = (int(value) & 0xFF)
+
+
+class u16DataElement(intDataElement):
+ _size = 2
+ _endianess = ">"
+
+ def _get_value(self, data):
+ return struct.unpack(self._endianess + "H", data)[0]
+
+ def set_value(self, value):
+ self._data[self._offset] = struct.pack(self._endianess + "H",
+ int(value) & 0xFFFF)
+
+
+class ul16DataElement(u16DataElement):
+ _endianess = "<"
+
+
+class u24DataElement(intDataElement):
+ _size = 3
+ _endianess = ">"
+
+ def _get_value(self, data):
+ pre = self._endianess == ">" and b"\x00" or b""
+ post = self._endianess == "<" and b"\x00" or b""
+ return struct.unpack(self._endianess + "I", pre+data+post)[0]
+
+ def set_value(self, value):
+ if self._endianess == "<":
+ start = 0
+ end = 3
+ else:
+ start = 1
+ end = 4
+ packed = struct.pack(self._endianess + "I", int(value) & 0xFFFFFFFF)
+ self._data[self._offset] = packed[start:end]
+
+
+class ul24DataElement(u24DataElement):
+ _endianess = "<"
+
+
+class u32DataElement(intDataElement):
+ _size = 4
+ _endianess = ">"
+
+ def _get_value(self, data):
+ return struct.unpack(self._endianess + "I", data)[0]
+
+ def set_value(self, value):
+ self._data[self._offset] = struct.pack(self._endianess + "I",
+ int(value) & 0xFFFFFFFF)
+
+
+class ul32DataElement(u32DataElement):
+ _endianess = "<"
+
+
+class i8DataElement(u8DataElement):
+ _size = 1
+
+ def _get_value(self, data):
+ return struct.unpack("b", data)[0]
+
+ def set_value(self, value):
+ self._data[self._offset] = struct.pack("b", int(value))
+
+
+class i16DataElement(intDataElement):
+ _size = 2
+ _endianess = ">"
+
+ def _get_value(self, data):
+ return struct.unpack(self._endianess + "h", data)[0]
+
+ def set_value(self, value):
+ self._data[self._offset] = struct.pack(self._endianess + "h",
+ int(value))
+
+
+class il16DataElement(i16DataElement):
+ _endianess = "<"
+
+
+class i24DataElement(intDataElement):
+ _size = 3
+ _endianess = ">"
+
+ def _get_value(self, data):
+ pre = self._endianess == ">" and "\x00" or ""
+ post = self._endianess == "<" and "\x00" or ""
+ return struct.unpack(self._endianess + "i", pre+data+post)[0]
+
+ def set_value(self, value):
+ if self._endianess == "<":
+ start = 0
+ end = 3
+ else:
+ start = 1
+ end = 4
+ self._data[self._offset] = struct.pack(self._endianess + "i",
+ int(value))[start:end]
+
+
+class il24DataElement(i24DataElement):
+ _endianess = "<"
+
+
+class i32DataElement(intDataElement):
+ _size = 4
+ _endianess = ">"
+
+ def _get_value(self, data):
+ return struct.unpack(self._endianess + "i", data)[0]
+
+ def set_value(self, value):
+ self._data[self._offset] = struct.pack(self._endianess + "i",
+ int(value))
+
+
+class il32DataElement(i32DataElement):
+ _endianess = "<"
+
+
+class charDataElement(DataElement):
+ _size = 1
+
+ def __str__(self):
+ return str(self.get_value())
+
+ def __int__(self):
+ return ord(self.get_value())
+
+ def _get_value(self, data):
+ return string_straight_decode(data)
+
+ def set_value(self, value):
+ if isinstance(value, int):
+ # This is the case if bytes() are passed in to arrayDataElement
+ # to set a string
+ self._data[self._offset] = value
+ else:
+ self._data[self._offset] = string_straight_encode(value)
+
+
+class bcdDataElement(DataElement):
+ def __int__(self):
+ tens, ones = self.get_value()
+ return (tens * 10) + ones
+
+ def set_bits(self, mask):
+ self._data[self._offset] = ord(self._data[self._offset]) | int(mask)
+
+ def clr_bits(self, mask):
+ self._data[self._offset] = ord(self._data[self._offset]) & ~int(mask)
+
+ def get_bits(self, mask):
+ return ord(self._data[self._offset]) & int(mask)
+
+ def set_raw(self, data):
+ if isinstance(data, int):
+ self._data[self._offset] = data & 0xFF
+ elif isinstance(data, str):
+ self._data[self._offset] = string_straight_encode(data[0])
+ else:
+ raise TypeError("Unable to set bcdDataElement from type %s" %
+ type(data))
+
+ def set_value(self, value):
+ self._data[self._offset] = int("%02i" % value, 16)
+
+ def _get_value(self, data):
+ a = (ord(data) & 0xF0) >> 4
+ b = ord(data) & 0x0F
+ return (a, b)
+
+
+class lbcdDataElement(bcdDataElement):
+ _size = 1
+
+
+class bbcdDataElement(bcdDataElement):
+ _size = 1
+
+
+class bitDataElement(intDataElement):
+ _nbits = 0
+ _shift = 0
+ _subgen = u8DataElement # Default to a byte
+
+ def __repr__(self):
+ fmt = "0x%%0%iX (%%sb)" % (self._size * 2)
+ return fmt % (int(self), format_binary(self._nbits, self.get_value()))
+
+ def get_value(self):
+ data = self._subgen(self._data, self._offset).get_value()
+ mask = bits_between(self._shift-self._nbits, self._shift)
+ val = (data & mask) >> int(self._shift - self._nbits)
+ return val
+
+ def set_value(self, value):
+ mask = bits_between(self._shift-self._nbits, self._shift)
+
+ data = self._subgen(self._data, self._offset).get_value()
+ data &= ~mask
+
+ value = ((int(value) << int(self._shift-self._nbits)) & mask) | data
+
+ self._subgen(self._data, self._offset).set_value(value)
+
+ def size(self):
+ return self._nbits
+
+
+class structDataElement(DataElement):
+ def __repr__(self):
+ s = "struct {" + os.linesep
+ for prop in self._keys:
+ s += " %15s: %s%s" % (prop, repr(self._generators[prop]),
+ os.linesep)
+ s += "} %s (%i bytes at 0x%04X)%s" % (self._name,
+ self.size() / 8,
+ self._offset,
+ os.linesep)
+ return s
+
+ def __init__(self, *args, **kwargs):
+ self._generators = {}
+ self._keys = []
+ self._count = 1
+ if "name" in list(kwargs.keys()):
+ self._name = kwargs["name"]
+ del kwargs["name"]
+ else:
+ self._name = "(anonymous)"
+ DataElement.__init__(self, *args, **kwargs)
+ self.__init = True
+
+ def _value(self, data, generators):
+ result = {}
+ for name, gen in list(generators.items()):
+ result[name] = gen.get_value(data)
+ return result
+
+ def get_value(self):
+ result = []
+ for i in range(0, self._count):
+ result.append(self._value(self._data, self._generators[i]))
+
+ if self._count == 1:
+ return result[0]
+ else:
+ return result
+
+ def __getitem__(self, key):
+ return self._generators[key]
+
+ def __setitem__(self, key, value):
+ if key in self._generators:
+ self._generators[key].set_value(value)
+ else:
+ self._generators[key] = value
+ self._keys.append(key)
+
+ def __getattr__(self, name):
+ try:
+ return self._generators[name]
+ except KeyError:
+ raise AttributeError("No attribute %s in struct" % name)
+
+ def __setattr__(self, name, value):
+ if "_structDataElement__init" not in self.__dict__:
+ self.__dict__[name] = value
+ else:
+ self.__dict__["_generators"][name].set_value(value)
+
+ def size(self):
+ size = 0
+ for name, gen in list(self._generators.items()):
+ if not isinstance(gen, list):
+ gen = [gen]
+
+ i = 0
+ for el in gen:
+ i += 1
+ size += el.size()
+ return size
+
+ def get_raw(self, asbytes=False):
+ size = self.size() // 8
+ raw = self._data[self._offset:self._offset+size]
+ if asbytes:
+ return bytes(raw)
+ else:
+ return string_straight_decode(raw)
+
+ def set_raw(self, buffer):
+ if len(buffer) != (self.size() / 8):
+ raise ValueError("Struct size mismatch during set_raw()")
+ if isinstance(buffer, str):
+ buffer = string_straight_encode(buffer)
+ self._data[self._offset] = buffer
+
+ def __iter__(self):
+ for item in list(self._generators.values()):
+ yield item
+
+ def items(self):
+ for key in self._keys:
+ yield key, self._generators[key]
+
+
+class Processor:
+
+ _types = {
+ "u8": u8DataElement,
+ "u16": u16DataElement,
+ "ul16": ul16DataElement,
+ "u24": u24DataElement,
+ "ul24": ul24DataElement,
+ "u32": u32DataElement,
+ "ul32": ul32DataElement,
+ "i8": i8DataElement,
+ "i16": i16DataElement,
+ "il16": il16DataElement,
+ "i24": i24DataElement,
+ "il24": il24DataElement,
+ "i32": i32DataElement,
+ "char": charDataElement,
+ "lbcd": lbcdDataElement,
+ "bbcd": bbcdDataElement,
+ }
+
+ def __init__(self, data, offset):
+ if hasattr(data, 'get_byte_compatible'):
+ # bitwise uses the byte-compatible interface of MemoryMap,
+ # if that is what was passed in
+ data = data.get_byte_compatible()
+ self._data = data
+ self._offset = offset
+ self._obj = None
+ self._user_types = {}
+
+ def do_symbol(self, symdef, gen):
+ name = symdef[1]
+ self._generators[name] = gen
+
+ def do_bitfield(self, dtype, bitfield):
+ bytes = self._types[dtype](self._data, 0).size() / 8
+ bitsleft = bytes * 8
+
+ for _bitdef, defn in bitfield:
+ name = defn[0][1]
+ bits = int(defn[1][1])
+ if bitsleft < 0:
+ raise ParseError("Invalid bitfield spec")
+
+ class bitDE(bitDataElement):
+ _nbits = bits
+ _shift = bitsleft
+ _subgen = self._types[dtype]
+
+ self._generators[name] = bitDE(self._data, self._offset)
+ bitsleft -= bits
+
+ if bitsleft:
+ LOG.warn("WARNING: %i trailing bits unaccounted for in %s" %
+ (bitsleft, bitfield))
+
+ return bytes
+
+ def do_bitarray(self, i, count):
+ if count % 8 != 0:
+ raise ValueError("bit array must be divisible by 8.")
+
+ class bitDE(bitDataElement):
+ _nbits = 1
+ _shift = 8 - i % 8
+
+ return bitDE(self._data, self._offset)
+
+ def parse_defn(self, defn):
+ dtype = defn[0]
+
+ if defn[1][0] == "bitfield":
+ size = self.do_bitfield(dtype, defn[1][1])
+ count = 1
+ self._offset += size
+ else:
+ if defn[1][0] == "array":
+ sym = defn[1][1][0]
+ count = int(defn[1][1][1][1])
+ else:
+ count = 1
+ sym = defn[1]
+
+ name = sym[1]
+ res = arrayDataElement(self._offset)
+ size = 0
+ for i in range(0, count):
+ if dtype == "bit":
+ gen = self.do_bitarray(i, count)
+ self._offset += int((i+1) % 8 == 0)
+ else:
+ gen = self._types[dtype](self._data, self._offset)
+ self._offset += (gen.size() / 8)
+ res.append(gen)
+
+ if count == 1:
+ self._generators[name] = res[0]
+ else:
+ self._generators[name] = res
+
+ def parse_struct_decl(self, struct):
+ block = struct[:-1]
+ if block[0][0] == "symbol":
+ # This is a pre-defined struct
+ block = self._user_types[block[0][1]]
+ deftype = struct[-1]
+ if deftype[0] == "array":
+ name = deftype[1][0][1]
+ count = int(deftype[1][1][1])
+ elif deftype[0] == "symbol":
+ name = deftype[1]
+ count = 1
+
+ result = arrayDataElement(self._offset)
+ for i in range(0, count):
+ element = structDataElement(self._data, self._offset, count,
+ name=name)
+ result.append(element)
+ tmp = self._generators
+ self._generators = element
+ self.parse_block(block)
+ self._generators = tmp
+
+ if count == 1:
+ self._generators[name] = result[0]
+ else:
+ self._generators[name] = result
+
+ def parse_struct_defn(self, struct):
+ name = struct[0][1]
+ block = struct[1:]
+ self._user_types[name] = block
+
+ def parse_struct(self, struct):
+ if struct[0][0] == "struct_defn":
+ return self.parse_struct_defn(struct[0][1])
+ elif struct[0][0] == "struct_decl":
+ return self.parse_struct_decl(struct[0][1])
+ else:
+ raise Exception("Internal error: What is `%s'?" % struct[0][0])
+
+ def parse_directive(self, directive):
+ name = directive[0][0]
+ value = directive[0][1][0][1]
+ if name == "seekto":
+ self._offset = int(value, 0)
+ elif name == "seek":
+ self._offset += int(value, 0)
+ elif name == "printoffset":
+ LOG.debug("%s: %i (0x%08X)" %
+ (value[1:-1], self._offset, self._offset))
+
+ def parse_block(self, lang):
+ for t, d in lang:
+ if t == "struct":
+ self.parse_struct(d)
+ elif t == "definition":
+ self.parse_defn(d)
+ elif t == "directive":
+ self.parse_directive(d)
+
+ def parse(self, lang):
+ self._generators = structDataElement(self._data, self._offset)
+ self.parse_block(lang)
+ return self._generators
+
+
+def parse(spec, data, offset=0):
+ ast = bitwise_grammar.parse(spec)
+ p = Processor(data, offset)
+ return p.parse(ast)
+
+if __name__ == "__main__":
+ defn = """
+struct mytype { u8 foo; };
+struct mytype bar;
+struct {
+ u8 foo;
+ u8 highbit:1,
+ sixzeros:6,
+ lowbit:1;
+ char string[3];
+ bbcd fourdigits[2];
+} mystruct;
+"""
+ data = "\xab\x7F\x81abc\x12\x34"
+ tree = parse(defn, data)
+
+ print(repr(tree))
+
+ print("Foo %i" % tree.mystruct.foo)
+ print("Highbit: %i SixZeros: %i: Lowbit: %i" % (tree.mystruct.highbit,
+ tree.mystruct.sixzeros,
+ tree.mystruct.lowbit))
+ print("String: %s" % tree.mystruct.string)
+ print("Fourdigits: %i" % tree.mystruct.fourdigits)
+
+ import sys
+ sys.exit(0)
+
+ test = """
+ struct {
+ u16 bar;
+ u16 baz;
+ u8 one;
+ u8 upper:2,
+ twobit:1,
+ onebit:1,
+ lower:4;
+ u8 array[3];
+ char str[3];
+ bbcd bcdL[2];
+ } foo[2];
+ u8 tail;
+ """
+ data = "\xfe\x10\x00\x08\xFF\x23\x01\x02\x03abc\x34\x89"
+ data = (data * 2) + "\x12"
+ data = MemoryMap(data)
+
+ ast = bitwise_grammar.parse(test)
+
+ # Just for testing, pretty-print the tree
+ pp(ast)
+
+ # Mess with it a little
+ p = Processor(data, 0)
+ obj = p.parse(ast)
+ print("Object: %s" % obj)
+ print(obj["foo"][0]["bcdL"])
+ print(obj["tail"])
+ print(obj["foo"][0]["bar"])
+ obj["foo"][0]["bar"].set_value(255 << 8)
+ obj["foo"][0]["twobit"].set_value(0)
+ obj["foo"][0]["onebit"].set_value(1)
+ print("%i" % int(obj["foo"][0]["bar"]))
+
+ for i in obj["foo"][0]["array"]:
+ print(int(i))
+ obj["foo"][0]["array"][1].set_value(255)
+
+ for i in obj["foo"][0]["bcdL"]:
+ print(i.get_value())
+
+ int_to_bcd(obj["foo"][0]["bcdL"], 1234)
+ print(bcd_to_int(obj["foo"][0]["bcdL"]))
+
+ set_string(obj["foo"][0]["str"], "xyz")
+ print(get_string(obj["foo"][0]["str"]))
+
+ print(repr(data.get_packed()))