aboutsummaryrefslogtreecommitdiffstats
path: root/utils/qmidb/Structs.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/qmidb/Structs.py')
-rwxr-xr-xutils/qmidb/Structs.py350
1 files changed, 350 insertions, 0 deletions
diff --git a/utils/qmidb/Structs.py b/utils/qmidb/Structs.py
new file mode 100755
index 0000000..34e001e
--- /dev/null
+++ b/utils/qmidb/Structs.py
@@ -0,0 +1,350 @@
+#!/usr/bin/env python
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# 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, version 2.
+#
+# 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, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2011 - 2012 Red Hat, Inc.
+#
+
+import utils
+
+# eDB2FragmentType
+TYPE_FIELD = 0 # Simple field fragment
+TYPE_STRUCT = 1 # Structure fragment
+TYPE_CONSTANT_PAD = 2 # Pad fragment, fixed length (bits)
+TYPE_VARIABLE_PAD_BITS = 3 # Pad fragment, variable (bits)
+TYPE_VARIABLE_PAD_BYTES = 4 # Pad fragment, variable (bytes)
+TYPE_FULL_BYTE_PAD = 5 # Pad fragment, pad to a full byte
+TYPE_MSB_2_LSB = 6 # Switch to MSB -> LSB order
+TYPE_LSB_2_MSB = 7 # Switch to LSB -> MSB order
+
+def is_pad_type(t):
+ if t == TYPE_CONSTANT_PAD or t == TYPE_VARIABLE_PAD_BITS or \
+ t == TYPE_VARIABLE_PAD_BYTES or t == TYPE_FULL_BYTE_PAD:
+ return True
+ return False
+
+# eDB2ModifierType
+MOD_NONE = 0 # Modifier is not used
+MOD_CONSTANT_ARRAY = 1 # Constant (elements) array
+MOD_VARIABLE_ARRAY = 2 # Variable (elements) array (modval gives #elements field)
+MOD_OBSOLETE_3 = 3 # Constant (bits) array [OBS]
+MOD_OBSOLETE_4 = 4 # Variable (bits) array [OBS]
+MOD_OPTIONAL = 5 # Fragment is optional
+MOD_VARIABLE_ARRAY2 = 6 # Variable (elements) array, start/stop given
+MOD_VARIABLE_ARRAY3 = 7 # Variable (elements) array, simple expression
+MOD_VARIABLE_STRING1 = 8 # Variable length string (bit length)
+MOD_VARIABLE_STRING2 = 9 # Variable length string (byte length)
+MOD_VARIABLE_STRING3 = 10 # Variable length string (character length)
+
+
+class FragmentBase:
+ # Struct fragments (ie, each line in Struct.txt describe each member of
+ # a struct. The format is as follows:
+ #
+ # id^order^type^fieldid^name^offset^modtype^modval
+ #
+ # 50409^1^0^54024^""^-1^2^"54023"
+ #
+ # This describes the second element (ie, element #1) of struct 50409,
+ # which is of type FIELD (0) and the value is described by Field #54024.
+ # Additionally, it's "modtype" is VARIABLE_ARRAY which is described by
+ # Field #54023. Basically, this fragment is a variable array of
+ # "Medium Preference" values, each element of which is one of the
+ # "QMI PDS Mediums" (Enum.txt, #50407 given by Field.txt #54024). The
+ # number of elements in the array is given by the "modtype" and "modval"
+ # pointers, which say that Field #54023 (which is also the first member
+ # of this struct) specifies the number of elements in this variable array.
+
+ def __init__(self, line):
+ parts = line.split('^')
+ if len(parts) < 8:
+ raise Exception("Invalid struct fragment '%s'" % line)
+ self.id = int(parts[0])
+ self.order = int(parts[1])
+ self.type = int(parts[2]) # eDB2FragmentType
+ self.value = int(parts[3]) # id of field in Fields.txt
+ self.name = parts[4].replace('"', '')
+ self.offset = int(parts[5])
+ self.modtype = int(parts[6]) # eDB2ModifierType
+ self.modval = parts[7].replace('"', '')
+
+ #Struct.txt:50021^0^0^50118^""^-1^1^"4"
+
+ def validate(self, fields, structs):
+ raise Exception("Surprising struct %d:%d field: %d" % (self.id, self.order, self.type))
+
+ # Should return size in *bits* of this struct fragment, including all
+ # sub-fragments
+ def emit(self, do_print, entity_name, indent, reserved_bits, fields, structs, enums, cur_structsize):
+ return 0
+
+class Msb2LsbFragment(FragmentBase):
+ # Subclass for TYPE_MSB_2_LSB
+ def validate(self, fields, structs):
+ pass
+
+class FieldFragment(FragmentBase):
+ # Subclass for TYPE_FIELD
+ def validate(self, fields, structs):
+ self.field = fields.get_child(self.value)
+
+ def emit(self, do_print, entity_name, indent, reserved_bits, fields, structs, enums, cur_structsize):
+ # modify the field like cProtocolEntityNav::ProcessFragment()
+ num_elements = 0
+ comment = ""
+ isarray = False
+
+ if self.modtype == MOD_CONSTANT_ARRAY:
+ # modval is number of elements
+ num_elements = int(self.modval)
+ elif self.modtype == MOD_VARIABLE_ARRAY or \
+ self.modtype == MOD_VARIABLE_STRING1 or \
+ self.modtype == MOD_VARIABLE_STRING2 or \
+ self.modtype == MOD_VARIABLE_STRING3:
+ # The "modval" is the field id that gives the # of elements
+ # of this variable array. That field is usually the immediately
+ # previous fragment of this struct. ie it's something like:
+ #
+ # struct foobar {
+ # u8 arraylen; <- specified by modval
+ # char array[]; <- current struct fragment
+ # }
+ #
+ fdesc = fields.get_child(int(self.modval))
+ comment = "size given by %s" % utils.nicename(fdesc.name)
+ isarray = True
+
+ return self.field.emit(do_print, indent, enums, num_elements, comment, isarray)
+
+
+class StructFragment(FragmentBase):
+ # Subclass for TYPE_STRUCT
+ def validate(self, fields, structs):
+ self.struct = structs.get_child(self.value)
+
+ def emit(self, do_print, entity_name, indent, reserved_bits, fields, structs, enums, cur_structsize):
+ # embedded structs often won't have a name of their own
+ structname = "a_item"
+ if self.name:
+ structname = "%s_item" % utils.nicename(self.name)
+
+ bits = self.struct.emit(structname, indent, reserved_bits, fields, structs, enums)
+
+ # Ignore the condition on some structs' modvals
+ fdesc_id = 0
+ if len(self.modval) and self.modval.find("=") < 0:
+ fdesc_id = int(self.modval)
+
+ comment = ""
+ arraybits = ""
+ try:
+ fdesc = fields.get_child(fdesc_id)
+ comment = "\t /* size given by %s */" % utils.nicename(fdesc.name)
+ arraybits = "[0]"
+ except KeyError:
+ pass
+
+ if do_print:
+ varname = utils.nicename(self.name)
+ if not varname:
+ varname = "item"
+ print "%sstruct %s %s%s;%s" % ("\t" * indent, structname, utils.nicename(self.name), arraybits, comment)
+
+ return bits
+
+class ConstantPadFragment(FragmentBase):
+ # Subclass for TYPE_CONSTANT_PAD
+ def __init__(self, line):
+ FragmentBase.__init__(self, line)
+ # padsize is total struct size (in bits) including this fragment; ie
+ # given the current struct size, pad the struct out to padsize
+ self.padsize = self.value
+
+ def validate(self, fields, structs):
+ if self.value < 0 or self.value > 1000:
+ raise Exception("Invalid constant pad size %d" % self.value)
+
+ def emit(self, do_print, entity_name, indent, reserved_bits, fields, structs, enums, cur_structsize):
+ # cur_structsize is in bits
+ if cur_structsize > self.padsize:
+ raise ValueError("Current structure size (%d) is larger than pad size (%d)!")
+
+ padbits = self.padsize - cur_structsize
+ padtype = "uint8"
+ if padbits > 8 and padbits <= 16:
+ padtype = "uint16"
+ elif padbits > 16 and padbits <= 32:
+ padtype = "uint32"
+ elif padbits > 32 and padbits <= 64:
+ padtype = "uint64"
+ else:
+ raise ValueError("FIXME: handle multi-long padding")
+
+ if do_print:
+ if padbits in [8, 16, 32]:
+ print "%s%s padding;" % ("\t" * indent, padtype)
+ else:
+ print "%s%s padding:%d;" % ("\t" * indent, padtype, padbits)
+ return padbits
+
+class Struct:
+ def __init__(self, sid):
+ self.id = sid
+ self.fragments = []
+ self.name = "struct_%d" % sid
+
+ def add_fragment(self, fragment):
+ for f in self.fragments:
+ if fragment.order == f.order:
+ raise Exception("Struct %d already has fragment order %d" % (self.id, fragment.order))
+ self.fragments.append(fragment)
+ self.fragments.sort(lambda x, y: cmp(x.order, y.order))
+
+ def validate(self, fields, structs):
+ for f in self.fragments:
+ f.validate(fields, structs)
+
+ def emit_header(self, entity_name, cmdno, tlvno):
+ if not entity_name:
+ entity_name = self.name
+ svcname = "Unknown"
+ cmdname = "Unknown"
+ tlvname = "Unknown"
+ else:
+ parts = entity_name.split("/")
+ svcname = parts[0].upper()
+ cmdname = parts[1].upper().replace(" ", "_")
+ tlvname = parts[2]
+
+ print '/**'
+ print ' * SVC: %s' % svcname
+ print ' * CMD: 0x%04x (%s)' % (cmdno, cmdname)
+ print ' * TLV: 0x%02x (%s)' % (tlvno, tlvname)
+ print ' * ID: %d' % self.id
+ print ' */'
+
+ def need_union(self, frag_idx):
+ # scan to see if there is any pad fragment in this struct; if there is,
+ # and if there's a struct fragment before the pad fragment, we'll need
+ # to make a union
+ found_struct = False
+ for i in range(frag_idx, len(self.fragments)):
+ if self.fragments[i].type == TYPE_STRUCT:
+ found_struct = True
+ if is_pad_type(self.fragments[i].type):
+ if found_struct:
+ return True
+ break
+ return False
+
+ def emit(self, entity_name, indent, pad_bits, fields, structs, enums):
+ print '%sstruct %s {' % ("\t" * indent, utils.nicename(entity_name))
+
+ # current structure size in *bits*
+ size_in_bits = 0
+
+ # if there was a previous member we're supposed to add some padding
+ # for (ie, this struct is in a union and something is before it) do
+ # that now
+ if pad_bits:
+ print '%suint8 padding:%d;' % ("\t" * (indent + 1), pad_bits)
+ size_in_bits += pad_bits
+
+ union_frag = None
+ reserved_bits = 0
+
+ for i in range(0, len(self.fragments)):
+ frag = self.fragments[i]
+
+ if not union_frag:
+ if self.need_union(i):
+ union_frag = frag
+ indent += 1
+ print '%sunion u_%s {' % ("\t" * indent, utils.nicename(frag.field.name))
+
+ bits = frag.emit(True, entity_name, indent + 1, reserved_bits, fields, structs, enums, size_in_bits)
+ if frag == union_frag:
+ # Track the first union fragment's reserved bits; we'll ignore
+ # them for total struct size since the following struct(s) will
+ # include it's size
+ reserved_bits = bits
+ elif is_pad_type(frag.type):
+ # When we reach the pad fragment, the union terminates so
+ # clear out union-specific state
+ reserved_bits = 0
+ if union_frag:
+ print '%s};' % ("\t" * indent)
+ indent -= 1
+ union_frag = None
+ size_in_bits += bits
+ else:
+ size_in_bits += bits
+
+ print "%s};\n" % ("\t" * indent)
+ return size_in_bits
+
+class Structs(utils.DbFile):
+ def __init__(self, path):
+ self.structs = {}
+ f = file(path + "Struct.txt")
+ for line in f:
+ if len(line.strip()) == 0:
+ continue
+
+ # parse enough of the line to get struct ID and type
+ parts = line.split('^')
+ if len(parts) < 3:
+ raise Exception("Invalid struct fragment '%s'" % line)
+
+ # make a new struct if we don't know about this one already
+ struct_id = int(parts[0])
+ try:
+ struct = self.structs[struct_id]
+ except KeyError:
+ struct = Struct(struct_id)
+ self.structs[struct.id] = struct
+
+ frag_type = int(parts[2])
+ try:
+ frag_class = { TYPE_FIELD: FieldFragment,
+ TYPE_STRUCT: StructFragment,
+ TYPE_CONSTANT_PAD: ConstantPadFragment,
+ TYPE_MSB_2_LSB: Msb2LsbFragment
+ }[frag_type]
+ except KeyError:
+ # fall back to base class
+ frag_class = FragmentBase
+
+ frag = frag_class(line.strip())
+ struct.add_fragment(frag)
+ f.close()
+
+ def validate(self, fields):
+ for s in self.structs.values():
+ s.validate(fields, self)
+
+ def has_child(self, sid):
+ return self.structs.has_key(sid)
+
+ def get_child(self, sid):
+ return self.structs[sid]
+
+ def emit_unused(self, used, fields, enums):
+ print '/**** UNKNOWN TLVs ****/\n'
+ for s in self.structs.values():
+ if not s.id in used:
+ s.emit_header(None, 0, 0)
+ s.emit(None, 0, fields, self, enums)
+