blob: 435d3117a69177323b322cd84f2ba90e2404b88e [file] [log] [blame]
Kévin Redone88be9e2020-01-14 18:19:48 +01001#!/usr/bin/env python
2# encoding: utf-8
3# python: 3.8.1
4
5# library to enumerate USB devices
6import usb.core
7from usb.util import *
8# more elegant structure
9from typing import NamedTuple
10# regular expressions utilities
11import re
12# open utilities to handle files
13import os, sys
14# to download the firmwares
15import urllib.request
16# to flash using DFU-util
17import subprocess
18
19# SIMtrace 2 device information
20class Device(NamedTuple):
21 usb_vendor_id: int
22 usb_product_id: int
23 name: str
24 url: dict # 1: sniff/trace firmware, 2: card emulation firmware
25
26# SIMtrace 2 devices definitions
27DEVICE_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"})
28DEVICE_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"})
29DEVICE_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"})
30DEVICES = [DEVICE_SIMTRACE, DEVICE_QMOD]
31
32# which firmware does the SIMtrace USN interface subclass correspond
33FIRMWARE_SUBCLASS = {1: "trace", 2: "cardem"}
34
35def print_help():
36 print("this script will flash SIMtrace 2 - based devices")
37 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")
38 print("to flash a specific firmware, provide the name as argument")
39 print("the possible firmwares are: trace, cardem")
40 print("to list all devices connected to USB, provide the argument \"list\"")
41
42# the firmware to flash
43to_flash = None
44
45# parse command line argument
46if len(sys.argv) == 2:
47 to_flash = sys.argv[1]
48if to_flash not in ["list", "trace", "cardem"] and len(sys.argv) > 1:
49 print_help()
50 exit(0)
51
52# get all USB devices
53devices = []
54devices_nb = 0
55updated_nb = 0
56usb_devices = usb.core.find(find_all=True)
57for usb_device in usb_devices:
58 # find SIMtrace devices
59 definitions = list(filter(lambda x: x.usb_vendor_id == usb_device.idVendor and x.usb_product_id == usb_device.idProduct, DEVICES))
60 if 1 != len(definitions):
61 continue
62 devices_nb += 1
63 definition = definitions[0]
64 serial = usb_device.serial_number or "unknown"
65 usb_path = str(usb_device.bus) + "-" + ".".join(map(str, usb_device.port_numbers))
66 print("found " + definition.name + " device (chip ID " + serial + ") at USB path " + usb_path)
67 # determine if we are running DFU (in most cases the bootloader, but could also be the application)
68 dfu_interface = None
69 for configuration in usb_device:
70 # get DFU interface descriptor
71 dfu_interface = dfu_interface or find_descriptor(configuration, bInterfaceClass=254, bInterfaceSubClass=1)
72 if (None == dfu_interface):
73 print("no DFU USB interface found")
74 continue
75 dfu_mode = (2 == dfu_interface.bInterfaceProtocol) # InterfaceProtocol 1 is runtime mode, 2 is DFU mode
76 # determine firmware type (when not in DFU mode)
77 firmware = None
78 simtrace_interface = None
79 for configuration in usb_device:
80 simtrace_interface = simtrace_interface or find_descriptor(configuration, bInterfaceClass=255)
81 if simtrace_interface and simtrace_interface.bInterfaceSubClass in FIRMWARE_SUBCLASS:
82 firmware = firmware or FIRMWARE_SUBCLASS[simtrace_interface.bInterfaceSubClass]
83 if dfu_mode:
84 firmware = 'dfu'
85 if firmware:
86 print("installed firmware: " + firmware)
87 else:
88 print("unknown installed firmware")
89 continue
90 # determine version of the application/bootloader firmware
91 version = None
92 version_interface = None
93 for configuration in usb_device:
94 # get custom interface with string
95 version_interface = version_interface or find_descriptor(configuration, bInterfaceClass=255, bInterfaceSubClass=255)
96 if version_interface and version_interface.iInterface and version_interface.iInterface > 0 and get_string(usb_device, version_interface.iInterface):
97 version = get_string(usb_device, version_interface.iInterface)
98 if not version:
99 # the USB serial is set (in the application) since version 0.5.1.34-e026 from 2019-08-06
100 # https://git.osmocom.org/simtrace2/commit/?id=e0265462d8c05ebfa133db2039c2fbe3ebbd286e
101 # the USB serial is set (in the bootloader) since version 0.5.1.45-ac7e from 2019-11-18
102 # https://git.osmocom.org/simtrace2/commit/?id=5db9402a5f346e30288db228157f71c29aefce5a
103 # the firmware version is set (in the application) since version 0.5.1.37-ede8 from 2019-08-13
104 # https://git.osmocom.org/simtrace2/commit/?id=ede87e067dadd07119f24e96261b66ac92b3af6f
105 # the firmware version is set (in the bootloader) since version 0.5.1.45-ac7e from 2019-11-18
106 # https://git.osmocom.org/simtrace2/commit/?id=5db9402a5f346e30288db228157f71c29aefce5a
107 if dfu_mode:
108 if serial:
109 version = "< 0.5.1.45-ac7e"
110 else:
111 versoin = "< 0.5.1.45-ac7e"
112 else:
113 if serial:
114 version = "< 0.5.1.37-ede8"
115 else:
116 versoin = "< 0.5.1.34-e026"
117 print("device firmware version: " + version)
118 # flash latest firmware
119 if to_flash == "list": # we just want to list the devices, not flash them
120 continue
121 # check the firmware exists
122 if firmware == "dfu" and to_flash is None:
123 print("device is currently in DFU mode. you need to specify which firmware to flash")
124 continue
125 to_flash = to_flash or firmware
126 if to_flash not in definition.url.keys():
127 print("no firmware image available for " + firmware + " firmware")
128 continue
129 # download firmware
130 try:
131 dl_path, header = urllib.request.urlretrieve(definition.url[to_flash])
132 except:
133 print("could not download firmware " + definition.url[to_flash])
134 continue
135 dl_file = open(dl_path, "rb")
136 dl_data = dl_file.read()
137 dl_file.close()
138 # compare versions
139 dl_version = re.search(b'firmware \d+\.\d+\.\d+\.\d+-[0-9a-fA-F]{4}', dl_data)
140 if dl_version is None:
141 print("could not get version from downloaded firmware image")
142 os.remove(dl_path)
143 continue
144 dl_version = dl_version.group(0).decode("utf-8").split(" ")[1]
145 print("latest firmware version: " + dl_version)
146 versions = list(map(lambda x: int(x), version.split(" ")[-1].split("-")[0].split(".")))
147 dl_versions = list(map(lambda x: int(x), dl_version.split("-")[0].split(".")))
148 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]))
149 if not dl_newer:
150 print("no need to flash latest version")
151 os.remove(dl_path)
152 continue
153 print("flashing latest version")
154 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])
155 os.remove(dl_path)
156 if 0 != dfu_result.returncode:
157 printf("flashing firmware using dfu-util failed. ensure dfu-util is installed and you have the permissions to access this USB device")
158 continue
159 updated_nb += 1
160
161print(str(devices_nb)+ " SIMtrace 2 device(s) found")
162print(str(updated_nb)+ " SIMtrace 2 device(s) updated")