aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2012-05-16 12:13:48 -0500
committerDan Williams <dcbw@redhat.com>2012-05-16 12:13:48 -0500
commit9bee743a9d193373df82a93ef4fd159504d0763e (patch)
treedd7f509495961c3c0cc99b88a2a3fee68fde9c61
parentdf1f21d17f6923145926fba5be38cff947254cae (diff)
vl600: add some reverse engineering docs and an AT com utility
-rwxr-xr-xvl600/atcom.py147
-rw-r--r--vl600/vl600.txt38
2 files changed, 185 insertions, 0 deletions
diff --git a/vl600/atcom.py b/vl600/atcom.py
new file mode 100755
index 00000000..5ca52b03
--- /dev/null
+++ b/vl600/atcom.py
@@ -0,0 +1,147 @@
+#! /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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details:
+#
+# Copyright (C) 2012 Red Hat, Inc.
+#
+
+import os
+import sys
+import select
+import struct
+import string
+from termios import *
+
+debug = False
+
+def lg_pack(data, seqno):
+ l = len(data)
+
+ fmt = "<"
+ fmt += "I" # magic
+ fmt += "I" # sequence number
+ fmt += "I" # data length
+ fmt += "H" # MUX channel
+ fmt += "%ds" % l # AT data
+
+ # Packets always padded to 4-byte boundaries
+ sz = struct.calcsize(fmt)
+ padding = 0
+ if sz % 4 > 0:
+ padding = 4 - (sz % 4)
+ fmt += "%ds" % padding
+
+ return struct.pack(fmt, 0xa512485a, seqno, l, 0xf011, data, "\0" * padding)
+
+def lg_unpack(data):
+ fmt = "<"
+ fmt += "I" # magic
+ fmt += "I" # sequence number
+ fmt += "I" # data length
+ fmt += "H" # MUX channel
+ fmt += "%ds" % (len(data) - 14) # AT data
+
+ (magic, seq, l, chan, resp) = struct.unpack(fmt, data)
+
+ if magic != 0xa512485a:
+ raise Exception("Bad magic: 0x%08x" % magic)
+ if chan != 0xf011:
+ print "Unhandled channel 0x%04x" % chan
+
+ # It appears that we're supposed to ignore any data after \r\n, or if
+ # we don't get a \r\n we ignore all of it. The modem adds random
+ # data to the end of the response, for example:
+ #
+ # > 5a 48 12 a5 08 00 00 00 0a 00 00 00 11 f0 41 54 2b 43 45 52 45 47 3f 0a
+ # < 5a 48 12 a5 4e 00 00 00 15 00 00 00 11 f0 2b 43 45 52 45 47 3a 20 30 2c 31 0d 0a 00 00 4f 4b 0d 0a 00 47 74
+ #
+ # where there's a trailing "00 47 74". The trailing bytes appear
+ # totally random in value and length.
+
+ crlf = resp.rfind("\r\n")
+ if crlf == -1:
+ return ""
+
+ return resp[:crlf + 2]
+
+def dump_raw(data, to_modem):
+ if debug:
+ line = ""
+ if to_modem:
+ line += "> "
+ else:
+ line += "< "
+ for c in data:
+ line += "%02x " % ord(c)
+ print line
+
+def make_printable(data):
+ p = ""
+ for c in data:
+ if c in string.printable and ord(c) >= 32 or c == '\n' or c == '\r':
+ p += c
+ else:
+ p += "<%2x>" % ord(c)
+ return p
+
+
+#########################################
+
+if len(sys.argv) != 2 and len(sys.argv) != 3:
+ print "Usage: %s <port> [--debug]" % sys.argv[0]
+ sys.exit(1)
+
+if len(sys.argv) > 2 and sys.argv[2] == "--debug":
+ debug = True
+
+fd = os.open(sys.argv[1], os.O_RDWR)
+
+# read existing port attributes and mask the ones we don't want
+attrs = tcgetattr(fd)
+attrs[0] = attrs[0] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON) # iflag
+attrs[1] = attrs[1] & ~OPOST # oflag
+attrs[2] = attrs[2] & ~(CSIZE | PARENB) # cflag
+attrs[3] = attrs[3] & ~(ECHO | ICANON | IEXTEN | ISIG) # lflag
+
+# Set up the attributes we do want
+attrs[2] = attrs[2] | CS8 # cflag
+attrs[4] = B115200 # ispeed
+attrs[5] = B115200 # ospeed
+attrs[6][VMIN] = 1 # cc
+attrs[6][VTIME] = 0 # cc
+tcsetattr(fd, TCSAFLUSH, attrs)
+
+infd = sys.stdin.fileno()
+seqno = 0
+while 1:
+ try:
+ rfd, wfd, xfd = select.select([ fd, infd ], [], [])
+ except KeyboardInterrupt:
+ print ""
+ break
+
+ if fd in rfd:
+ data = os.read(fd, 4096)
+ dump_raw(data, False)
+ line = lg_unpack(data)
+ if line:
+ print make_printable(line)
+
+ if infd in rfd:
+ line = os.read(infd, 512)
+ if line:
+ data = lg_pack(line, seqno)
+ seqno += 1
+ dump_raw(data, True)
+ os.write(fd, data)
+
+os.close(fd)
diff --git a/vl600/vl600.txt b/vl600/vl600.txt
new file mode 100644
index 00000000..f7f6719e
--- /dev/null
+++ b/vl600/vl600.txt
@@ -0,0 +1,38 @@
+Device uses an LG L2000 LTE chip and a Qualcomm MDM6800A for CDMA/EVDO.
+
+The firmware flasher tool speaks DIAG and includes a lot of LTE-related
+NV items.
+
+Device has two USB interfaces:
+
+0 - Proprietary ethernet interface
+1 - CDC-ACM serial port
+
+The ACM port speaks a proprietary protocol that MUX-es traffic from the
+following virtual interfaces (according to Windows):
+
+0: LGE LTE DM Port
+1: LGE USB Modem Port
+2: LGE LTE RF Serial Port (com)
+3: LGE CDMA USB Serial Port (com)
+4: LGE CDMA USB GPS NMEA Port (com)
+5: LGE CDMA LBS Serial Port (com)
+
+MUX Header Format
+-----------------
+
+u32: magic, always [ 0x5a 0x48 0x12 0xa5 ]
+u32: sequence number (unpaired; host and device use separate sequence numbers)
+u32: length (not including this header, but including any padding)
+u16: MUX channel (21 f0: CMD) (11 f0: AT)
+<data>
+
+
+Packets are 4-byte aligned with padding of zeros, and this padding is included
+in the length given in the header.
+
+AT commands may have trailing junk bytes. It appears that interpreters should
+simply ignore any data in AT packets after the last CRLF.
+
+CMD packets are terminated with a standard HDLC CRC-16 and 0x7E.
+