-
Notifications
You must be signed in to change notification settings - Fork 4
/
dump-gotenna-flash
executable file
·162 lines (125 loc) · 5.55 KB
/
dump-gotenna-flash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/python
from datetime import timedelta
import sys
import time
import argparse
import re
import serial
def check_for_ongoing_process(port):
"""Check to see whether the goTenna is busy."""
bytecount = 0
end = time.time() + 5
# We can check for ongoing progress by seeing if we see more than 2k of bytes in five seconds.
while time.time() < end and bytecount <= 2048:
read = port.read(1)
bytecount = bytecount + len(read)
return bytecount > 2048
def read_eflash_output(port, progress, output, logout):
"""Perform the read loop over the serial port and save all read data."""
lastreadcounter = 10
lastblock = 0
bigbuffers = 0
blockpattern = re.compile(r"([0-9,a-f]{6}):\s")
# Initial progress: 0%
progress(0, 100)
# Issue the read_eflash command to the goTenna CLI. This will begin the dump.
port.write("\nread_eflash\n")
while port.isOpen():
# Read a block of data up to 4k bytes, or up to the one second timeout.
size = 4096
waiting = port.inWaiting()
if waiting > size:
size = waiting
bigbuffers = bigbuffers + 1
block = port.read(size)
# Write that block of data to our output file.
output.write(block)
# Keep the serial connection alive to the goTenna. Otherwise "USB Asleep".
port.write([0x00])
# The read_eflash command will send heaps of data our way.
# If we end up with a block smaller than expected we likely ran in to our timeout.
if block.__len__() < 2048:
lastreadcounter = lastreadcounter - 1
# If we're all out of timeout chances, abort reading since we don't have any more eflash to read.
if lastreadcounter == 0:
break
# Attempt to find the *last* block number in the output.
match = None
for match in blockpattern.finditer(block):
pass
if match:
lastblock = int(match.group(1), 16)
# 2093040 (0x1feff0) is what I've observed the last block to be dumped.
progress(lastblock, 2093040)
# Alert if we might not have a complete dump.
if lastblock != 2093040:
logout.write("\nRead last block {0}, which might not be the final block (expected {1}).\n".format(lastblock, 2093040))
# Something educational about how quickly data is being sent versus consumed.
if bigbuffers > 0:
logout.write("Found {0} bigbuffers while reading from the serial port.\n".format(bigbuffers))
def detect_gotenna(port):
"""Detect whether a goTenna is connected to the serial port."""
# We're looking for a message that looks like this:
# [2154445-7952] FW Version : 00.15.04
fwpattern = re.compile(r"FW Version\s*\:\s*(\d+\.\d+\.\d+)", re.IGNORECASE)
# We may have to take a few tries at this, if the goTenna is busy. Some times we may catch it printing diagnostic messages.
attempts = 3
while attempts > 0:
# Issue the version command to the cli.
port.write("\nversion\n")
lines = port.readlines()
for line in lines:
match = fwpattern.search(line)
if match:
return match.group(1)
attempts = attempts - 1
return None
def dump_eflash(serialport, logout, progress, output):
"""Check the serial port for readiness and execute the flash dump."""
port = serial.Serial(serialport, 9600, timeout=1)
logout.write("Connected to serial port {0}.\n".format(serialport))
logout.write("Waiting for idle on the goTenna... Please wait 5 seconds.\n")
if check_for_ongoing_process(port):
port.close()
message = "The goTenna currently busy. Cannot dump flash. Turn the goTenna off and on again."
logout.write(message + "\n")
raise Exception(message)
fwversion = detect_gotenna(port)
if fwversion is None:
port.close()
message = "Failed to find goTenna connected to {0}.".format(serialport)
logout.write(message + "\n")
raise Exception(message)
logout.write("Found goTenna running firmware {0}.\n".format(fwversion))
logout.write("The goTenna is available and ready. Sit back and relax, this will take a bit of time.\n")
logout.write("Dumping flash. Do not disconnect or turn the goTenna off!\n")
starttime = time.time()
read_eflash_output(port, progress, output, logout)
endtime = time.time()
# The progress_cli doesn't add \n, clean up after it.
logout.write("\n")
# Finally, note how long we've been running for.
logout.write("Flash read in {0}.\n".format(str(timedelta(seconds=(endtime - starttime)))))
port.close()
output.flush()
def progress_cli(currentblock, maximumblock):
"""Update the progress readout on the CLI during the dump operation."""
sys.stdout.write("\rSaving: {:3.2%}".format((float(currentblock) / float(maximumblock))))
sys.stdout.flush()
def main():
"""Main entrypoint for the flash dumping utility, handles CLI options and execution."""
parser = argparse.ArgumentParser()
parser.add_argument("device", type=str, help="Your goTenna TTY device, often /dev/ttyACM0.")
parser.add_argument("output", type=str, help="The output file to save the log to.")
args = parser.parse_args()
logout = sys.stdout
output = sys.stdout
if args.output != "-":
output = open(args.output, 'w')
try:
dump_eflash(args.device, logout, progress_cli, output)
except Exception as exception:
sys.stderr.write(str(exception) + "\n")
sys.exit(1)
if __name__ == "__main__":
main()