aboutsummaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-07-03 15:41:02 +0200
committerAleksander Morgado <aleksander@lanedo.com>2012-07-03 15:41:02 +0200
commit85b380fcb5ac42d74bede9baa9b94c31b375208e (patch)
tree8d3fff87484bb21934ed3410c4311fe468fed5d9 /utils
parent97abf15a58d06ff9170f728cc4eeefb55055e648 (diff)
downloadexternal_libqmi-85b380fcb5ac42d74bede9baa9b94c31b375208e.zip
external_libqmi-85b380fcb5ac42d74bede9baa9b94c31b375208e.tar.gz
external_libqmi-85b380fcb5ac42d74bede9baa9b94c31b375208e.tar.bz2
core: moved Gobi API sources and utils to their own subdirectories
Diffstat (limited to 'utils')
-rwxr-xr-xutils/qmidb/Entities.py111
-rwxr-xr-xutils/qmidb/Enums.py95
-rwxr-xr-xutils/qmidb/Fields.py158
-rwxr-xr-xutils/qmidb/Structs.py350
-rwxr-xr-xutils/qmidb/qmidb.py51
-rwxr-xr-xutils/qmidb/utils.py116
-rw-r--r--utils/qmitest.c818
7 files changed, 1699 insertions, 0 deletions
diff --git a/utils/qmidb/Entities.py b/utils/qmidb/Entities.py
new file mode 100755
index 0000000..a55d7bf
--- /dev/null
+++ b/utils/qmidb/Entities.py
@@ -0,0 +1,111 @@
+#!/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
+
+class Entity:
+ def __init__(self, line):
+ # Each entity defines a TLV item in a QMI request or response. The
+ # entity's 'struct' field maps to the ID of a struct in Struct.txt,
+ # which describes the fields of that struct. For example:
+ #
+ # 33^"32,16"^"WDS/Start Network Interface Request/Primary DNS"^50021^-1
+ #
+ # The first field (33) indicates that this entity is valid for the QMI
+ # message eDB2_ET_QMI_WDS_REQ. The "32,16" is a tuple
+ # (eQMI_WDS_START_NET, 16), the first item of which (32) indicates the
+ # general QMI service the entity is associated with (WDS, CTL, NAS, etc)
+ # and the second item indicates the specific entity number (ie, the 'T'
+ # in TLV).
+ #
+ # The 'struct' field (50021) points to:
+ #
+ # Struct.txt:50021^0^0^50118^""^-1^1^"4"
+ #
+ # which indicates that the only member of this struct is field #50118:
+ #
+ # Field.txt:50118^"IP V4 Address"^8^0^2^0
+ #
+ # which should be pretty self-explanatory. Note that different entities
+ # can point to the same struct.
+
+
+ parts = line.split('^')
+ if len(parts) < 4:
+ raise Exception("Invalid entity line '%s'" % line)
+ self.uniqueid = parts[0] + '.' + parts[1].replace('"', '').replace(',', '.')
+ self.type = int(parts[0]) # eDB2EntityType
+
+ self.key = parts[1].replace('"','') # tuple of (eQMIMessageXXX, entity number)
+ self.cmdno = int(self.key.split(",")[0])
+ self.tlvno = int(self.key.split(",")[1])
+ self.name = parts[2].replace('"', '')
+ self.struct = int(parts[3])
+ self.format = None
+ self.internal = True
+ self.extformat = None
+ if len(parts) > 4:
+ self.format = int(parts[4])
+ if len(parts) > 5:
+ self.internal = int(parts[5]) != 0
+ if len(parts) > 6:
+ self.extformat = int(parts[6])
+
+ def validate(self, structs):
+ if not structs.has_child(self.struct):
+ raise Exception("Entity missing struct: %d" % self.struct)
+
+ def emit(self, fields, structs, enums):
+ if self.tlvno == 2 and self.name.find("/Result Code") > 0 and self.struct == 50000:
+ # ignore this entity if it's a standard QMI result code struct
+ return self.struct
+
+ # Tell the struct this value is for to emit itself
+ s = structs.get_child(self.struct)
+ s.emit_header(self.name, self.cmdno, self.tlvno)
+ s.emit(self.name, 0, 0, fields, structs, enums)
+ return self.struct
+
+
+class Entities(utils.DbFile):
+ def __init__(self, path):
+ self.byid = {}
+ f = file(path + "Entity.txt")
+ for line in f:
+ ent = Entity(line)
+ self.byid[ent.uniqueid] = ent
+
+ def validate(self, structs):
+ for e in self.byid.values():
+ e.validate(structs)
+
+ def emit(self, fields, structs, enums):
+ # emit the standard status TLV struct
+ print "struct qmi_result_code { /* QMI Result Code TLV (0x0002) */"
+ print "\tgobi_qmi_results qmi_result; /* QMI Result */"
+ print "\tgobi_qmi_errors qmi_error; /* QMI Error */"
+ print "};"
+ print ""
+
+ structs_used = []
+ for e in self.byid.values():
+ sused = e.emit(fields, structs, enums)
+ structs_used.append(sused)
+ return structs_used
+
diff --git a/utils/qmidb/Enums.py b/utils/qmidb/Enums.py
new file mode 100755
index 0000000..9d81c93
--- /dev/null
+++ b/utils/qmidb/Enums.py
@@ -0,0 +1,95 @@
+#!/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
+
+class EnumEntry:
+ def __init__(self, line):
+ parts = line.split('^')
+ if len(parts) < 3:
+ raise Exception("Invalid enum entry line '%s'" % line)
+ self.id = int(parts[0])
+ self.value = int(parts[1], 0)
+ self.name = parts[2].replace('"', '')
+ self.descid = None
+ if len(parts) > 3:
+ self.descid = int(parts[3])
+
+ def emit(self, enum_name):
+ print "\tGOBI_%s_%s\t\t= 0x%08x, /* %s */" % (
+ utils.constname(enum_name),
+ utils.constname(self.name),
+ self.value, self.name)
+
+class Enum:
+ def __init__(self, line):
+ parts = line.split('^')
+ if len(parts) < 4:
+ raise Exception("Invalid enum line '%s'" % line)
+ self.id = int(parts[0])
+ self.name = parts[1].replace('"', '')
+ self.descid = int(parts[2])
+ self.internal = int(parts[3]) != 0
+ self.values = [] # list of EnumEntry objects
+
+ def add_entry(self, entry):
+ for v in self.values:
+ if entry.value == v.value:
+ raise Exception("Enum %d already has value %d" % (self.id, v.value))
+ self.values.append(entry)
+ self.values.sort(lambda x, y: cmp(x.value, y.value))
+
+ def emit(self):
+ print 'typedef enum { /* %s */ ' % self.name
+ for en in self.values:
+ en.emit(self.name)
+ print "} gobi_%s;\n" % utils.nicename(self.name)
+
+
+class Enums(utils.DbFile):
+ def __init__(self, path):
+ self.enums = {}
+
+ # parse the enums
+ f = file(path + "Enum.txt")
+ for line in f:
+ try:
+ enum = Enum(line.strip())
+ self.enums[enum.id] = enum
+ except Exception(e):
+ pass
+ f.close()
+
+ # and now the enum entries
+ f = file(path + "EnumEntry.txt")
+ for line in f:
+ try:
+ entry = EnumEntry(line.strip())
+ self.enums[entry.id].add_entry(entry)
+ except Exception(e):
+ pass
+ f.close()
+
+ def emit(self):
+ for e in self.enums:
+ self.enums[e].emit()
+
+ def get_child(self, eid):
+ return self.enums[eid]
+
diff --git a/utils/qmidb/Fields.py b/utils/qmidb/Fields.py
new file mode 100755
index 0000000..9df70a2
--- /dev/null
+++ b/utils/qmidb/Fields.py
@@ -0,0 +1,158 @@
+#!/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
+
+
+# eDB2FieldType
+FIELD_TYPE_STD = 0 # Field is a standard type (see below)
+FIELD_TYPE_ENUM_UNSIGNED = 1 # Field is an unsigned enumerated type
+FIELD_TYPE_ENUM_SIGNED = 2 # Field is a signed enumerated type
+
+# eDB2StdFieldType
+FIELD_STD_BOOL = 0 # boolean (0/1, false/true)
+FIELD_STD_INT8 = 1 # 8-bit signed integer
+FIELD_STD_UINT8 = 2 # 8-bit unsigned integer
+FIELD_STD_INT16 = 3 # 16-bit signed integer
+FIELD_STD_UINT16 = 4 # 16-bit unsigned integer
+FIELD_STD_INT32 = 5 # 32-bit signed integer
+FIELD_STD_UINT32 = 6 # 32-bit unsigned integer
+FIELD_STD_INT64 = 7 # 64-bit signed integer
+FIELD_STD_UINT64 = 8 # 64-bit unsigned integer
+FIELD_STD_STRING_A = 9 # ANSI (ASCII?) fixed length string; size in bits
+FIELD_STD_STRING_U = 10 # UCS-2 fixed length string
+FIELD_STD_STRING_ANT = 11 # ANSI (ASCII?) NULL terminated string
+FIELD_STD_STRING_UNT = 12 # UCS-2 NULL terminated string
+FIELD_STD_FLOAT32 = 13 # 32-bit floating point value
+FIELD_STD_FLOAT64 = 14 # 64-bit floating point value
+FIELD_STD_STRING_U8 = 15 # UTF-8 encoded fixed length string
+FIELD_STD_STRING_U8NT = 16 # UTF-8 encoded NULL terminated string
+
+stdtypes = {
+ # Maps field type to [ <C type>, <is array>, <element size in bits> ]
+ FIELD_STD_BOOL: [ 'bool', False, 8 ],
+ FIELD_STD_INT8: [ 'int8', False, 8 ],
+ FIELD_STD_UINT8: [ 'uint8', False, 8 ],
+ FIELD_STD_INT16: [ 'int16', False, 16 ],
+ FIELD_STD_UINT16: [ 'uint16', False, 16 ],
+ FIELD_STD_INT32: [ 'int32', False, 32 ],
+ FIELD_STD_UINT32: [ 'uint32', False, 32 ],
+ FIELD_STD_INT64: [ 'int64', False, 64 ],
+ FIELD_STD_UINT64: [ 'uint64', False, 64 ],
+ FIELD_STD_STRING_A: [ 'char', True, 8 ],
+ FIELD_STD_STRING_U: [ 'uint16', True, 16 ],
+ FIELD_STD_STRING_ANT: [ 'char *', False, 8 ],
+ FIELD_STD_STRING_UNT: [ 'char *', False, 8 ],
+ FIELD_STD_FLOAT32: [ 'float32', False, 32 ],
+ FIELD_STD_FLOAT64: [ 'float64', False, 64 ],
+}
+
+def is_array_type(t):
+ return stdtypes[t][1]
+
+
+class Field:
+ def __init__(self, line):
+ parts = line.split('^')
+ if len(parts) < 6:
+ raise Exception("Invalid field line '%s'" % line)
+ self.id = int(parts[0])
+ self.name = parts[1].replace('"', '')
+ self.size = int(parts[2]) # in *bits*
+ self.type = int(parts[3]) # eDB2FieldType
+ self.typeval = int(parts[4]) # eDB2StdFieldType if 'type' == 0
+ self.hex = int(parts[5]) != 0
+ self.descid = None
+ self.internal = False
+ if len(parts) > 6:
+ self.descid = int(parts[6])
+ if len(parts) > 7:
+ self.internal = int(parts[7])
+
+ # Field.txt:50118^"IP V4 Address"^8^0^2^0
+
+ #Field.txt:50118^"IP V4 Address"^8^0^2^0
+
+ def get_charsize(self):
+ if self.type == FIELD_TYPE_STD:
+ if self.typeval == FIELD_STD_STRING_A or self.typeval == FIELD_STD_STRING_ANT or \
+ self.typeval == FIELD_STD_STRING_U or self.typeval == FIELD_STD_STRING_UNT:
+ return stdtypes[self.typeval][2]
+ raise Exception("Called for non-string type")
+
+ def emit(self, do_print, indent, enums, num_elements, comment, isarray):
+ ctype = ''
+ arraypart = ''
+
+ sizebits = 0
+ if self.type == FIELD_TYPE_STD: # eDB2_FIELD_STD
+ tinfo = stdtypes[self.typeval]
+ ctype = tinfo[0]
+
+ if is_array_type(self.typeval) or num_elements > 0:
+ if num_elements > 0:
+ arraypart = "[%d]" % num_elements
+ sizebits = num_elements * tinfo[2]
+ elif isarray:
+ # array with size given by previous fragment
+ arraypart = "[0]"
+ sizebits = 0
+ else:
+ arraypart = "[%d]" % (self.size / tinfo[2])
+ sizebits = self.size * tinfo[2]
+ elif self.type == FIELD_TYPE_ENUM_UNSIGNED or self.type == FIELD_TYPE_ENUM_SIGNED:
+ # It's a enum; find the enum
+ e = enums.get_child(self.typeval)
+ ctype = "gobi_%s" % utils.nicename(e.name)
+ if isarray:
+ if num_elements != 0:
+ raise Exception("Unhandled ENUM field type with size %d" % num_elements)
+ arraypart = "[0]";
+ sizebits = 0
+ else:
+ if self.size > 0:
+ # enum size is # of bits
+ arraypart = ":%d" % self.size
+ sizebits = self.size
+ else:
+ sizebits = 32
+ else:
+ raise ValueError("Unknown Field type")
+
+ if comment:
+ comment = " (%s)" % comment
+ if do_print:
+ print "%s%s %s%s; /* %s%s */" % ("\t" * indent, ctype, utils.nicename(self.name), arraypart, self.name, comment)
+ return sizebits
+
+class Fields:
+ def __init__(self, path):
+ self.byid = {}
+
+ f = file(path + "Field.txt")
+ for line in f:
+ field = Field(line.strip())
+ self.byid[field.id] = field
+
+ def has_child(self, fid):
+ return self.byid.has_key(fid)
+
+ def get_child(self, fid):
+ return self.byid[fid]
+
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)
+
diff --git a/utils/qmidb/qmidb.py b/utils/qmidb/qmidb.py
new file mode 100755
index 0000000..f970931
--- /dev/null
+++ b/utils/qmidb/qmidb.py
@@ -0,0 +1,51 @@
+#!/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 sys
+import Entities
+import Enums
+import Fields
+import Structs
+
+if len(sys.argv) > 2:
+ print "Usage: qmidb.py <path to Entity.txt>"
+ sys.exit(1)
+path = ""
+if len(sys.argv) == 2:
+ path = sys.argv[1] + "/"
+
+enums = Enums.Enums(path)
+entities = Entities.Entities(path)
+fields = Fields.Fields(path)
+structs = Structs.Structs(path)
+
+structs.validate(fields)
+entities.validate(structs)
+
+print '/* GENERATED CODE. DO NOT EDIT. */'
+print '\ntypedef uint8 bool;\n'
+enums.emit()
+
+print '\n\n'
+
+structs_used = entities.emit(fields, structs, enums)
+
+# emit structs that weren't associated with an entity
+structs.emit_unused(structs_used, fields, enums)
+
diff --git a/utils/qmidb/utils.py b/utils/qmidb/utils.py
new file mode 100755
index 0000000..a5bbd8a
--- /dev/null
+++ b/utils/qmidb/utils.py
@@ -0,0 +1,116 @@
+#!/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.
+#
+
+def constname(name):
+ rlist = { '(': '',
+ ')': '',
+ ' ': '_',
+ '/': '_',
+ '.': '',
+ ',': '' }
+ # Capture stuff like "-9 dB" correctly
+ if name.find("dB") == -1:
+ rlist['-'] = '_'
+ else:
+ rlist['-'] = 'NEG_'
+
+ sanitized = ""
+ for c in name:
+ try:
+ sanitized += rlist[c]
+ except KeyError:
+ sanitized += c
+
+ # Handle E_UTRA -> EUTRA and E_UTRAN -> EUTRAN
+ foo = sanitized.upper().strip('_')
+ foo.replace('E_UTRA', 'EUTRA')
+
+ # And HSDPA+ -> HSDPA_PLUS then strip all plusses
+ foo = foo.replace('HSDPA+', 'HSDPA_PLUS')
+ foo = foo.replace('+', '')
+
+ # 1xEV-DO -> 1x EVDO
+ foo = foo.replace("1XEV_DO", "1X_EVDO")
+ foo = foo.replace("EV_DO", "EVDO")
+
+ # Wi-Fi -> WiFi
+ foo = foo.replace("WI_FI", "WIFI")
+
+ # modify certain words
+ words = foo.split('_')
+ klist = [ 'UNSUPPORTED' ]
+ slist = [ 'STATES', 'REASONS', 'FORMATS', 'SELECTIONS', 'TYPES', 'TAGS',
+ 'PROTOCOLS', 'CLASSES', 'ACTIONS', 'VALUES', 'OPTIONS',
+ 'DOMAINS', 'DEVICES', 'MODES', 'MONTHS', 'PREFERENCES',
+ 'PREFS', 'DURATIONS', 'CAPABILITIES', 'INTERFACES',
+ 'TECHNOLOGIES', 'NETWORKS', 'DAYS', 'SYSTEMS', 'SCHEMES',
+ 'INDICATORS', 'ENCODINGS', 'INITIALS', 'BITS', 'MEDIUMS',
+ 'BASES', 'ERRORS', 'RESULTS', 'RATIOS', 'DELIVERIES',
+ 'FAMILIES', 'SETTINGS', 'SOURCES', 'ORDERS' ]
+ blist = { 'CLASSES': 'CLASS' }
+ final = ''
+ for word in words:
+ if word in klist or len(word) == 0:
+ continue
+ if len(final):
+ final += '_'
+ if word in blist:
+ final += blist[word]
+ elif word in slist:
+ if word.endswith("IES"):
+ final += word[:len(word) - 3] + "Y"
+ elif word.endswith("S"):
+ final += word[:len(word) - 1]
+ else:
+ final += word
+ return final
+
+def nicename(name):
+ name = name.lower()
+ name = name.replace("1xev-do", "1x evdo")
+ name = name.replace("ev-do", "evdo")
+ name = name.replace("wi-fi", "wifi")
+ name = name.replace("%", "pct")
+ name = name.replace(' ', '_').replace('/', '_').replace('-', '_').replace('.','').replace('(', '').replace(')', '')
+ name = name.replace("___", "_").replace("__", "_")
+ return name.strip('_')
+
+
+class DbFile:
+ # Base class for objects that handle reading a database file like
+ # Enum.txt or Struct.txt
+
+ def __init__(self, path):
+ raise Exception("init() method must be implemented")
+
+ def validate(self):
+ pass
+
+ def has_child(self, cid):
+ raise Exception("has_child() method must be implemented")
+
+ def get_child(self, cid):
+ raise Exception("get_child() method must be implemented")
+
+ def emit(self):
+ pass
+
+ def emit_unused(self, used, fields, enums):
+ pass
+
diff --git a/utils/qmitest.c b/utils/qmitest.c
new file mode 100644
index 0000000..7df896e
--- /dev/null
+++ b/utils/qmitest.c
@@ -0,0 +1,818 @@
+/* -*- Mode: C; 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ * Copyright (C) 2010 - 2012 Google, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+typedef u_int16_t u16;
+typedef u_int8_t u8;
+typedef u_int32_t u32;
+typedef u_int64_t u64;
+
+typedef unsigned int qbool;
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/***********************************************************/
+
+static void
+print_buf (const char *detail, const char *buf, size_t len)
+{
+ int i = 0, z;
+ qbool newline = FALSE, indent = FALSE;
+ char *f;
+ guint flen;
+
+ f = g_strdup_printf ("%s (%zu) ", detail, len);
+ flen = strlen (f);
+ g_print ("%s", f);
+ for (i = 0; i < len; i++) {
+ if (indent) {
+ z = flen;
+ while (z--)
+ g_print (" ");
+ indent = FALSE;
+ }
+ g_print ("%02x ", buf[i] & 0xFF);
+ if (((i + 1) % 16) == 0) {
+ g_print ("\n");
+ newline = TRUE;
+ indent = TRUE;
+ } else
+ newline = FALSE;
+ }
+
+ if (!newline)
+ g_print ("\n");
+}
+
+/**************************************************************/
+
+typedef enum {
+ QMI_SVC_CTL = 0x00,
+ QMI_SVC_WDS = 0x01,
+ QMI_SVC_DMS = 0x02,
+ QMI_SVC_NAS = 0x03,
+ QMI_SVC_QOS = 0x04,
+ QMI_SVC_WMS = 0x05,
+ QMI_SVC_PDS = 0x06,
+ QMI_SVC_AUTH = 0x07,
+ QMI_SVC_AT = 0x08,
+ QMI_SVC_VOICE = 0x09,
+ QMI_SVC_CAT2 = 0x0A,
+ QMI_SVC_UIM = 0x0B,
+ QMI_SVC_PBM = 0x0C,
+ QMI_SVC_LOC = 0x10,
+ QMI_SVC_SAR = 0x11,
+ QMI_SVC_RMTFS = 0x14,
+ QMI_SVC_CAT = 0xE0,
+ QMI_SVC_RMS = 0xE1,
+ QMI_SVC_OMA = 0xE2
+} qmi_service_type;
+
+struct qmux {
+ u8 tf; /* always 1 */
+ u16 len;
+ u8 ctrl;
+ u8 service;
+ u8 qmicid;
+} __attribute__((__packed__));
+
+struct qmi_tlv_status {
+ u16 status;
+ u16 error;
+} __attribute__((__packed__));
+
+struct getcid_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 service;
+ u16 size;
+ u8 qmisvc;
+} __attribute__((__packed__));
+
+struct releasecid_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 rlscid;
+ u16 size;
+ u16 cid;
+} __attribute__((__packed__));
+
+struct version_info_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct qmi_ctl_version_info_list_service {
+ u8 service_type; /* QMI_SVC_xxx */
+ u16 major_version;
+ u16 minor_version;
+} __attribute__((__packed__));
+
+struct qmi_tlv_ctl_version_info_list {
+ u8 count;
+ struct qmi_ctl_version_info_list_service services[0];
+}__attribute__((__packed__));
+
+struct seteventreport_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 reportchanrate;
+ u16 size;
+ u8 period;
+ u32 mask;
+} __attribute__((__packed__));
+
+struct getpkgsrvcstatus_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct getmeid_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct qmiwds_stats {
+ u32 txok;
+ u32 rxok;
+ u32 txerr;
+ u32 rxerr;
+ u32 txofl;
+ u32 rxofl;
+ u64 txbytesok;
+ u64 rxbytesok;
+ qbool linkstate;
+ qbool reconfigure;
+};
+
+struct nas_signal_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+const size_t qmux_size = sizeof(struct qmux);
+
+static void
+qmux_fill(struct qmux *qmux, u8 service, u16 cid, u16 size)
+{
+ qmux->tf = 1;
+ qmux->len = size - 1;
+ qmux->ctrl = 0;
+ qmux->service = cid & 0xff;
+ qmux->qmicid = cid >> 8;
+}
+
+static void *
+qmictl_new_getcid(u8 tid, u8 svctype, size_t *size)
+{
+ struct getcid_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0022;
+ req->tlvsize = 0x0004;
+ req->service = 0x01;
+ req->size = 0x0001;
+ req->qmisvc = svctype;
+ *size = sizeof(*req);
+ qmux_fill (&req->header, QMI_SVC_CTL, 0, *size);
+ return req;
+}
+
+static void *
+qmictl_new_releasecid(u8 tid, u16 cid, size_t *size)
+{
+ struct releasecid_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0023;
+ req->tlvsize = 0x05;
+ req->rlscid = 0x01;
+ req->size = 0x0002;
+ req->cid = cid;
+ *size = sizeof(*req);
+ qmux_fill (&req->header, QMI_SVC_CTL, 0, *size);
+ return req;
+}
+
+static void *
+qmictl_new_version_info(u8 tid, size_t *size)
+{
+ struct version_info_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x21;
+ req->tlvsize = 0;
+ *size = sizeof(*req);
+ qmux_fill (&req->header, QMI_SVC_CTL, 0, *size);
+ return req;
+}
+
+static void *
+qmiwds_new_seteventreport(u8 tid, size_t *size)
+{
+ struct seteventreport_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0001;
+ req->tlvsize = 0x0008;
+ req->reportchanrate = 0x11;
+ req->size = 0x0005;
+ req->period = 0x01;
+ req->mask = 0x000000ff;
+ *size = sizeof(*req);
+ return req;
+}
+
+static void *
+qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size)
+{
+ struct getpkgsrvcstatus_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x22;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ return req;
+}
+
+static void *
+qmidms_new_getmeid(u16 cid, u8 tid, size_t *size)
+{
+ struct getmeid_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x25;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ qmux_fill (&req->header, QMI_SVC_WDS, cid, *size);
+ return req;
+}
+
+static int
+qmux_parse(u16 *cid, void *buf, size_t size)
+{
+ struct qmux *qmux = buf;
+
+ if (!buf || size < 12)
+ return -ENOMEM;
+
+ if (qmux->tf != 1 || qmux->len != size - 1 || qmux->ctrl != 0x80)
+ return -EINVAL;
+
+ *cid = (qmux->qmicid << 8) + qmux->service;
+ return sizeof(*qmux);
+}
+
+static u16
+tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize)
+{
+ u16 pos;
+ u16 msize = 0;
+
+ if (!msg || !buf)
+ return -ENOMEM;
+
+ for (pos = 4; pos + 3 < msgsize; pos += msize + 3) {
+ msize = *(u16 *)(msg + pos + 1);
+ if (*(u8 *)(msg + pos) == type) {
+ if (bufsize < msize)
+ return -ENOMEM;
+
+ memcpy(buf, msg + pos + 3, msize);
+ return msize;
+ }
+ }
+
+ return -ENOMSG;
+}
+
+static int
+qmi_msgisvalid(void *msg, u16 size)
+{
+ struct qmi_tlv_status status;
+
+ if (tlv_get(msg, size, 2, &status, sizeof (status)) == sizeof (status)) {
+ if (le16toh (status.status != 0))
+ return le16toh (status.error);
+ else
+ return 0;
+ }
+ return -ENOMSG;
+}
+
+static int
+qmi_msgid(void *msg, u16 size)
+{
+ return size < 2 ? -ENODATA : *(u16 *)msg;
+}
+
+static int
+qmictl_version_info_resp(void *buf, u16 size)
+{
+ int result, i;
+ u8 offset = sizeof(struct qmux) + 2;
+ u8 svcbuf[100];
+ struct qmi_tlv_ctl_version_info_list *service_list;
+ struct qmi_ctl_version_info_list_service *svc;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x21)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ /* Get the services TLV */
+ result = tlv_get(buf, size, 0x01, svcbuf, sizeof (svcbuf));
+ if (result < 0)
+ return -EFAULT;
+
+ service_list = (struct qmi_tlv_ctl_version_info_list *) svcbuf;
+ if (result < (service_list->count * sizeof (struct qmi_ctl_version_info_list_service)))
+ return -EFAULT;
+
+ svc = &(service_list->services[0]);
+ for (i = 0; i < service_list->count; i++, svc++) {
+ g_message ("SVC: %d v%d.%d",
+ svc->service_type,
+ le16toh (svc->major_version),
+ le16toh (svc->minor_version));
+ }
+
+ return 0;
+}
+
+static int
+qmictl_getcid_resp(void *buf, u16 size, u16 *cid)
+{
+ int result;
+ u8 offset = sizeof(struct qmux) + 2;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x22)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x01, cid, 2);
+ if (result != 2)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+qmictl_releasecid_resp(void *buf, u16 size)
+{
+ int result;
+ u8 offset = sizeof(struct qmux) + 2;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x23)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats)
+{
+ int result;
+ u8 status[2];
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset || !stats)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result == 0x01) {
+ tlv_get(buf, size, 0x10, &stats->txok, 4);
+ tlv_get(buf, size, 0x11, &stats->rxok, 4);
+ tlv_get(buf, size, 0x12, &stats->txerr, 4);
+ tlv_get(buf, size, 0x13, &stats->rxerr, 4);
+ tlv_get(buf, size, 0x14, &stats->txofl, 4);
+ tlv_get(buf, size, 0x15, &stats->rxofl, 4);
+ tlv_get(buf, size, 0x19, &stats->txbytesok, 8);
+ tlv_get(buf, size, 0x1A, &stats->rxbytesok, 8);
+ } else if (result == 0x22) {
+ result = tlv_get(buf, size, 0x01, &status[0], 2);
+ if (result >= 1)
+ stats->linkstate = status[0] == 0x02;
+ if (result == 2)
+ stats->reconfigure = status[1] == 0x01;
+
+ if (result < 0)
+ return result;
+ } else {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int
+qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize)
+{
+ int result;
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset || meidsize < 14)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x25)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x12, meid, 14);
+ if (result != 14)
+ return -EFAULT;
+
+ return 0;
+}
+
+static void *
+qminas_new_signal(u16 cid, u8 tid, size_t *size)
+{
+ struct nas_signal_req *req;
+
+ req = g_malloc0 (sizeof (*req));
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x20;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ qmux_fill (&req->header, QMI_SVC_NAS, cid, *size);
+ return req;
+}
+
+/* NAS/Get Signal Strength TLV 0x01 */
+struct qminas_resp_signalstrength {
+ u8 dbm;
+ u8 act;
+} __attribute__((__packed__));
+
+static int
+qminas_signal_resp(void *buf, u16 size, u8 *dbm, u8 *act)
+{
+ int result;
+ struct qminas_resp_signalstrength signal;
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x20)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x01, &signal, sizeof (signal));
+ if (result != sizeof (signal))
+ return -EFAULT;
+
+ *dbm = signal.dbm;
+ *act = signal.act;
+ return 0;
+}
+
+/*****************************************************/
+
+static size_t
+send_and_wait_reply (int fd, void *b, size_t blen, char *reply, size_t rlen)
+{
+ ssize_t num;
+ fd_set in;
+ int result;
+ struct timeval timeout = { 1, 0 };
+
+ print_buf (">>>", b, blen);
+ num = write (fd, b, blen);
+ if (num != blen) {
+ g_warning ("Failed to write: wrote %zd err %d", num, errno);
+ return 0;
+ }
+
+ FD_ZERO (&in);
+ FD_SET (fd, &in);
+ result = select (fd + 1, &in, NULL, NULL, &timeout);
+ if (result != 1 || !FD_ISSET (fd, &in)) {
+ g_warning ("No data pending");
+ return 0;
+ }
+
+ errno = 0;
+ num = read (fd, reply, rlen);
+ if (num < 0) {
+ g_warning ("read error %d", errno);
+ return 0;
+ }
+ print_buf ("<<<", reply, num);
+ return num;
+}
+
+/* CTL service transaction ID */
+static u8 ctl_tid = 1;
+
+static int
+get_meid (int fd)
+{
+ void *b;
+ size_t blen;
+ u8 dms_tid = 1;
+ u16 dms_cid = 0;
+ char reply[2048];
+ size_t rlen;
+ char meid[16];
+ int err;
+
+ /* Allocate a DMS client ID */
+ b = qmictl_new_getcid (ctl_tid++, QMI_SVC_DMS, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ return 1;
+
+ err = qmictl_getcid_resp (reply, rlen, &dms_cid);
+ if (err < 0) {
+ g_warning ("Failed to get DMS client ID: %d", err);
+ return 1;
+ }
+
+ g_message ("DMS CID %d 0x%X", dms_cid, dms_cid);
+
+ /* Get the MEID */
+ b = qmidms_new_getmeid(dms_cid, dms_tid++, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ return 1;
+
+ memset (meid, 0, sizeof (meid));
+ err = qmidms_meid_resp (reply, rlen, meid, sizeof (meid) - 1);
+ if (err < 0)
+ g_warning ("Failed to get MEID: %d", err);
+ else
+ g_message ("MEID: %s", meid);
+
+ /* Relese the DMS client ID */
+ b = qmictl_new_releasecid (ctl_tid++, dms_cid, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ return 1;
+
+ err = qmictl_releasecid_resp (reply, rlen);
+ if (err < 0) {
+ g_warning ("Failed to release DMS client ID: %d", err);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const char *
+act_to_string (u8 act)
+{
+ switch (act) {
+ case 0:
+ return "no service";
+ case 1:
+ return "CDMA2000 1x";
+ case 2:
+ return "CDMA2000 HRPD/EVDO";
+ case 3:
+ return "AMPS";
+ case 4:
+ return "GSM";
+ case 5:
+ return "UMTS";
+ case 8:
+ return "LTE";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static int
+get_signal (int fd)
+{
+ void *b;
+ size_t blen;
+ u8 nas_tid = 1;
+ u16 nas_cid = 0;
+ char reply[2048];
+ size_t rlen;
+ int err;
+ u8 dbm = 0, act = 0;
+
+ /* Allocate a NAS client ID */
+ b = qmictl_new_getcid (ctl_tid++, QMI_SVC_NAS, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ return 1;
+
+ err = qmictl_getcid_resp (reply, rlen, &nas_cid);
+ if (err < 0) {
+ g_warning ("Failed to get NAS client ID: %d", err);
+ return 1;
+ }
+
+ g_message ("NAS CID %d 0x%X", nas_cid, nas_cid);
+
+ /* Get the signal strength */
+ b = qminas_new_signal(nas_cid, nas_tid++, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ return 1;
+
+ err = qminas_signal_resp (reply, rlen, &dbm, &act);
+ if (err < 0)
+ g_warning ("Failed to get signal: %d", err);
+ else {
+ g_message ("dBm: -%d", 0x100 - (dbm & 0xFF));
+ g_message ("AcT: %s", act_to_string (act));
+ }
+
+ /* Relese the NAS client ID */
+ b = qmictl_new_releasecid (ctl_tid++, nas_cid, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ return 1;
+
+ err = qmictl_releasecid_resp (reply, rlen);
+ if (err < 0) {
+ g_warning ("Failed to release NAS client ID: %d", err);
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ void *b;
+ size_t blen;
+ u8 ctl_tid = 1;
+ char reply[2048];
+ size_t rlen;
+ int err;
+
+ if (argc != 2) {
+ g_warning ("usage: %s <port>", argv[0]);
+ return 1;
+ }
+
+ errno = 0;
+ fd = open (argv[1], O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+ if (fd < 0) {
+ g_warning ("%s: open failed: %d", argv[1], errno);
+ return 1;
+ }
+
+ /* Send the ready request */
+ b = qmictl_new_version_info (ctl_tid++, &blen);
+ g_assert (b);
+
+ rlen = send_and_wait_reply (fd, b, blen, reply, sizeof (reply));
+ g_free (b);
+ if (rlen <= 0)
+ goto out;
+
+ err = qmictl_version_info_resp (reply, rlen);
+ if (err < 0) {
+ g_warning ("Failed to get version info: %d", err);
+ goto out;
+ }
+
+ get_meid (fd);
+ get_signal (fd);
+
+out:
+ close (fd);
+ return 0;
+}
+