aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/flash.py
blob: 435d3117a69177323b322cd84f2ba90e2404b88e (plain)
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/env python
# encoding: utf-8
# python: 3.8.1

# library to enumerate USB devices
import usb.core
from usb.util import *
# more elegant structure
from typing import NamedTuple
# regular expressions utilities
import re
# open utilities to handle files
import os, sys
# to download the firmwares
import urllib.request
# to flash using DFU-util
import subprocess

# SIMtrace 2 device information
class Device(NamedTuple):
	usb_vendor_id: int
	usb_product_id: int
	name: str
	url: dict # 1: sniff/trace firmware, 2: card emulation firmware

# SIMtrace 2 devices definitions
DEVICE_SIMTRACE = Device(usb_vendor_id=0x1d50, usb_product_id=0x60e3, name="SIMtrace 2", url={"trace": "https://ftp.osmocom.org/binaries/simtrace2/firmware/latest/simtrace-trace-dfu-latest.bin", "cardem": "https://osmocom.org/attachments/download/3868/simtrace-cardem-dfu.bin"})
DEVICE_QMOD = Device(usb_vendor_id=0x1d50, usb_product_id=0x4004, name="sysmoQMOD (Quad Modem)", url={"cardem": "https://ftp.osmocom.org/binaries/simtrace2/firmware/latest/qmod-cardem-dfu-latest.bin"})
DEVICE_OWHW = Device(usb_vendor_id=0x1d50, usb_product_id=0x4001, name="OWHW", url={"cardem": "https://ftp.osmocom.org/binaries/simtrace2/firmware/latest/owhw-cardem-dfu-latest.bin"})
DEVICES = [DEVICE_SIMTRACE, DEVICE_QMOD]

# which firmware does the SIMtrace USN interface subclass correspond
FIRMWARE_SUBCLASS = {1: "trace", 2: "cardem"}

def print_help():
	print("this script will flash SIMtrace 2 - based devices")
	print("when no argument is provided, it will try to flash the application firmware of all SIMtrace 2 devices connected to USB with the latest version")
	print("to flash a specific firmware, provide the name as argument")
	print("the possible firmwares are: trace, cardem")
	print("to list all devices connected to USB, provide the argument \"list\"")

# the firmware to flash
to_flash = None

# parse command line argument
if len(sys.argv) == 2:
  to_flash = sys.argv[1]
if to_flash not in ["list", "trace", "cardem"] and len(sys.argv) > 1:
	print_help()
	exit(0)

# get all USB devices
devices = []
devices_nb = 0
updated_nb = 0
usb_devices = usb.core.find(find_all=True)
for usb_device in usb_devices:
	# find SIMtrace devices
	definitions = list(filter(lambda x: x.usb_vendor_id == usb_device.idVendor and x.usb_product_id == usb_device.idProduct, DEVICES))
	if 1 != len(definitions):
		continue
	devices_nb += 1
	definition = definitions[0]
	serial = usb_device.serial_number or "unknown"
	usb_path = str(usb_device.bus) + "-" + ".".join(map(str, usb_device.port_numbers))
	print("found " + definition.name + " device (chip ID " + serial + ") at USB path " + usb_path)
	# determine if we are running DFU (in most cases the bootloader, but could also be the application)
	dfu_interface = None
	for configuration in usb_device:
		# get DFU interface descriptor
		dfu_interface = dfu_interface or find_descriptor(configuration, bInterfaceClass=254, bInterfaceSubClass=1)
	if (None == dfu_interface):
		print("no DFU USB interface found")
		continue
	dfu_mode = (2 == dfu_interface.bInterfaceProtocol) # InterfaceProtocol 1 is runtime mode, 2 is DFU mode
	# determine firmware type (when not in DFU mode)
	firmware = None
	simtrace_interface = None
	for configuration in usb_device:
		simtrace_interface = simtrace_interface or find_descriptor(configuration, bInterfaceClass=255)
	if simtrace_interface and simtrace_interface.bInterfaceSubClass in FIRMWARE_SUBCLASS:
		firmware = firmware or FIRMWARE_SUBCLASS[simtrace_interface.bInterfaceSubClass]
	if dfu_mode:
		firmware = 'dfu'
	if firmware:
		print("installed firmware: " + firmware)
	else:
		print("unknown installed firmware")
		continue
	# determine version of the application/bootloader firmware
	version = None
	version_interface = None
	for configuration in usb_device:
		# get custom interface with string
		version_interface = version_interface or find_descriptor(configuration, bInterfaceClass=255, bInterfaceSubClass=255)
	if version_interface and version_interface.iInterface and version_interface.iInterface > 0 and get_string(usb_device, version_interface.iInterface):
		version = get_string(usb_device, version_interface.iInterface)
	if not version:
		# the USB serial is set (in the application) since version 0.5.1.34-e026 from 2019-08-06
		# https://git.osmocom.org/simtrace2/commit/?id=e0265462d8c05ebfa133db2039c2fbe3ebbd286e
		# the USB serial is set (in the bootloader) since version 0.5.1.45-ac7e from 2019-11-18
		# https://git.osmocom.org/simtrace2/commit/?id=5db9402a5f346e30288db228157f71c29aefce5a
		# the firmware version is set (in the application) since version 0.5.1.37-ede8 from 2019-08-13
		# https://git.osmocom.org/simtrace2/commit/?id=ede87e067dadd07119f24e96261b66ac92b3af6f
		# the firmware version is set (in the bootloader) since version 0.5.1.45-ac7e from 2019-11-18
		# https://git.osmocom.org/simtrace2/commit/?id=5db9402a5f346e30288db228157f71c29aefce5a
		if dfu_mode:
			if serial:
				version = "< 0.5.1.45-ac7e"
			else:
				versoin = "< 0.5.1.45-ac7e"
		else:
			if serial:
				version = "< 0.5.1.37-ede8"
			else:
				versoin = "< 0.5.1.34-e026"
	print("device firmware version: " + version)
	# flash latest firmware
	if to_flash == "list": # we just want to list the devices, not flash them
		continue
	# check the firmware exists
	if firmware == "dfu" and to_flash is None:
		print("device is currently in DFU mode. you need to specify which firmware to flash")
		continue
	to_flash = to_flash or firmware
	if to_flash not in definition.url.keys():
		print("no firmware image available for " + firmware + " firmware")
		continue
	# download firmware
	try:
		dl_path, header = urllib.request.urlretrieve(definition.url[to_flash])
	except:
		print("could not download firmware " + definition.url[to_flash])
		continue
	dl_file = open(dl_path, "rb")
	dl_data = dl_file.read()
	dl_file.close()
	# compare versions
	dl_version = re.search(b'firmware \d+\.\d+\.\d+\.\d+-[0-9a-fA-F]{4}', dl_data)
	if dl_version is None:
		print("could not get version from downloaded firmware image")
		os.remove(dl_path)
		continue
	dl_version = dl_version.group(0).decode("utf-8").split(" ")[1]
	print("latest firmware version: " + dl_version)
	versions = list(map(lambda x: int(x), version.split(" ")[-1].split("-")[0].split(".")))
	dl_versions = list(map(lambda x: int(x), dl_version.split("-")[0].split(".")))
	dl_newer = (versions[0] < dl_versions[0] or (versions[0] == dl_versions[0] and versions[1] < dl_versions[1]) or (versions[0] == dl_versions[0] and versions[1] == dl_versions[1] and versions[2] < dl_versions[2]) or (versions[0] == dl_versions[0] and versions[1] == dl_versions[1] and versions[2] == dl_versions[2] and versions[3] < dl_versions[3]))
	if not dl_newer:
		print("no need to flash latest version")
		os.remove(dl_path)
		continue
	print("flashing latest version")
	dfu_result = subprocess.run(["dfu-util", "--device", hex(definition.usb_vendor_id) + ":" + hex(definition.usb_product_id), "--path", usb_path, "--cfg", "1", "--alt", "1", "--reset", "--download", dl_path])
	os.remove(dl_path)
	if 0 != dfu_result.returncode:
		printf("flashing firmware using dfu-util failed. ensure dfu-util is installed and you have the permissions to access this USB device")
		continue
	updated_nb += 1

print(str(devices_nb)+ " SIMtrace 2 device(s) found")
print(str(updated_nb)+ " SIMtrace 2 device(s) updated")