diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-07-03 15:41:02 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-07-03 15:41:02 +0200 |
commit | 85b380fcb5ac42d74bede9baa9b94c31b375208e (patch) | |
tree | 8d3fff87484bb21934ed3410c4311fe468fed5d9 /utils | |
parent | 97abf15a58d06ff9170f728cc4eeefb55055e648 (diff) | |
download | external_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-x | utils/qmidb/Entities.py | 111 | ||||
-rwxr-xr-x | utils/qmidb/Enums.py | 95 | ||||
-rwxr-xr-x | utils/qmidb/Fields.py | 158 | ||||
-rwxr-xr-x | utils/qmidb/Structs.py | 350 | ||||
-rwxr-xr-x | utils/qmidb/qmidb.py | 51 | ||||
-rwxr-xr-x | utils/qmidb/utils.py | 116 | ||||
-rw-r--r-- | utils/qmitest.c | 818 |
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; +} + |