kopia lustrzana https://github.com/OpenRTX/OpenRTX
620 wiersze
24 KiB
Python
620 wiersze
24 KiB
Python
|
#!/usr/bin/env python3
|
||
|
# -*- coding: utf-8 -*-
|
||
|
################################################################################################################################################
|
||
|
#
|
||
|
# GD-77 Firmware uploader. By Roger VK3KYY
|
||
|
#
|
||
|
#
|
||
|
# This script has only been tested on Windows and Linux, it may or may not work on OSX
|
||
|
#
|
||
|
# On Windows,..
|
||
|
# the driver the system installs for the GD-77, which is the HID driver, needs to be replaced by the LibUSB-win32 using Zadig
|
||
|
# for USB device with idVendor=0x15a2, idProduct=0x0073
|
||
|
# Once this driver is installed the CPS and official firmware loader will no longer work as they can't find the device
|
||
|
# To use the CPS etc again, use the DeviceManager to uninstall the driver associated with idVendor=0x15a2, idProduct=0x0073 (this will appear as a libusb-win32 device)
|
||
|
# Then unplug the GD-77 and reconnect, and the HID driver will be re-installed
|
||
|
#
|
||
|
#
|
||
|
# On Linux, depending of you distro, you need to install a special udev rule to automatically unbind the USB HID device to usbhid driver.
|
||
|
#
|
||
|
#
|
||
|
# You also need python3-usb, enum34 and urllib3
|
||
|
#
|
||
|
################################################################################################################################################
|
||
|
|
||
|
######################### Error codes #########################
|
||
|
#
|
||
|
# -1: Missing firmware file
|
||
|
# -2: Wrong SGL file format
|
||
|
# -3: Unencrypted firmware
|
||
|
# -4: Firmware file is too large
|
||
|
# -5: Unknown HT model type
|
||
|
# -6: Command line parsing error
|
||
|
# -7: Online download firmware location error
|
||
|
# -8: Online download firmware binary error
|
||
|
# -9: Online firmware download failure
|
||
|
# -10: Firmare/HT mismatch
|
||
|
# -99: Unsupported GD-77S (will be removed in the futur)
|
||
|
#
|
||
|
###############################################################
|
||
|
|
||
|
import usb
|
||
|
import getopt, sys
|
||
|
import ntpath
|
||
|
import os.path
|
||
|
from array import array
|
||
|
import enum
|
||
|
import urllib3
|
||
|
import re
|
||
|
import tempfile
|
||
|
|
||
|
class SGLFormatOutput(enum.Enum):
|
||
|
GD_77 = 0
|
||
|
GD_77S = 1
|
||
|
DM_1801 = 2
|
||
|
RD_5R = 3
|
||
|
UNKNOWN = 4
|
||
|
|
||
|
def __int__(self):
|
||
|
return self.value
|
||
|
|
||
|
|
||
|
# Globals
|
||
|
responseOK = [0x41]
|
||
|
outputModes = ["GD-77", "GD-77S", "DM-1801", "RD-5R", "Unknown"]
|
||
|
outputFormat = SGLFormatOutput.GD_77
|
||
|
downloadedFW = ""
|
||
|
|
||
|
########################################################################
|
||
|
# Utilities to dump hex for testing
|
||
|
########################################################################
|
||
|
def hexdump(buf):
|
||
|
cbuf = ""
|
||
|
for b in buf:
|
||
|
cbuf = cbuf + "0x%0.2X " % ord(b)
|
||
|
return cbuf
|
||
|
|
||
|
def hexdumpArray(buf):
|
||
|
cbuf = ""
|
||
|
for b in buf:
|
||
|
cbuf = cbuf + "0x%0.2X " % b
|
||
|
return cbuf
|
||
|
|
||
|
def hexdumpArray2(buf):
|
||
|
cbuf = ""
|
||
|
for b in buf:
|
||
|
cbuf = cbuf + "%0.2X-" % b
|
||
|
return cbuf[:-1]
|
||
|
|
||
|
def strdumpArray(buf):
|
||
|
cbuf = ""
|
||
|
for b in buf:
|
||
|
cbuf = cbuf + chr(b)
|
||
|
return cbuf
|
||
|
|
||
|
def downloadFirmware(downloadStable):
|
||
|
url = "https://github.com/rogerclarkmelbourne/OpenGD77/releases"
|
||
|
urlBase = "http://github.com"
|
||
|
httpPool = urllib3.PoolManager()
|
||
|
pattern = ""
|
||
|
fwVersion = "UNKNOWN"
|
||
|
fwVersionPatternFormat = r'/{}([0-9\.]+)/'
|
||
|
urlFW = ""
|
||
|
webContent = ""
|
||
|
|
||
|
print(" - " + "Try to download the firmware for your {} from the project page".format(outputModes[int(outputFormat)]))
|
||
|
print(" - " + "Retrieve firmware location");
|
||
|
|
||
|
try:
|
||
|
response = httpPool.request('GET', url)
|
||
|
except urllib3.URLError as e:
|
||
|
print("".format(e.reason))
|
||
|
sys.exit(-7)
|
||
|
|
||
|
webContent = str(response.data)
|
||
|
|
||
|
if (outputFormat == SGLFormatOutput.GD_77):
|
||
|
patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenGD77\.sgl'
|
||
|
elif (outputFormat == SGLFormatOutput.GD_77S):
|
||
|
patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenGD77S\.sgl'
|
||
|
elif (outputFormat == SGLFormatOutput.DM_1801):
|
||
|
patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenDM1801\.sgl'
|
||
|
elif (outputFormat == SGLFormatOutput.RD_5R):
|
||
|
patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenDM5R\.sgl'
|
||
|
|
||
|
pattern = patternFormat.format("R" if downloadStable == True else "D")
|
||
|
fwVersionPattern = fwVersionPatternFormat.format("R" if downloadStable == True else "D")
|
||
|
contentArray = webContent.split("\n")
|
||
|
|
||
|
for l in contentArray:
|
||
|
m = re.search(pattern, l)
|
||
|
if (m != None):
|
||
|
urlFW = urlBase + m.group(0)
|
||
|
|
||
|
m = re.search(fwVersionPattern, urlFW)
|
||
|
if (m != None):
|
||
|
fwVersion = m.group(0).strip('/')
|
||
|
|
||
|
break
|
||
|
|
||
|
if (len(urlFW)):
|
||
|
global downloadedFW
|
||
|
downloadedFW = os.path.join(tempfile.gettempdir(), next(tempfile._get_candidate_names()) + '.sgl')
|
||
|
|
||
|
print(" - " + "Downloading the firmware version {}, please wait".format(fwVersion));
|
||
|
|
||
|
try:
|
||
|
response = httpPool.request('GET', urlFW, preload_content=False)
|
||
|
except urllib3.URLError as e:
|
||
|
print("".format(e.reason))
|
||
|
sys.exit(-8)
|
||
|
|
||
|
length = response.getheader('content-length')
|
||
|
|
||
|
if (length != None):
|
||
|
length = int(length)
|
||
|
blocksize = max(4096, (length//100))
|
||
|
else:
|
||
|
blocksize = 4096
|
||
|
|
||
|
# Download data
|
||
|
with open(downloadedFW, "w+b") as f:
|
||
|
while True:
|
||
|
data = response.read(blocksize)
|
||
|
|
||
|
if not data:
|
||
|
break
|
||
|
|
||
|
f.write(data)
|
||
|
f.close()
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
########################################################################
|
||
|
# Send the data packet to the GD-77 and return response
|
||
|
########################################################################
|
||
|
def sendAndGetResponse(dev, cmd):
|
||
|
USB_WRITE_ENDPOINT = 0x02
|
||
|
USB_READ_ENDPOINT = 0x81
|
||
|
TRANSFER_LENGTH = 38
|
||
|
headerData = [0x0] * 4
|
||
|
|
||
|
headerData[0] = 1
|
||
|
headerData[1] = 0
|
||
|
headerData[2] = ((len(cmd) >> 0) & 0xff)
|
||
|
headerData[3] = ((len(cmd) >> 8) & 0xff)
|
||
|
|
||
|
cmd = headerData + cmd
|
||
|
|
||
|
#print("TX: " + hexdumpArray2(cmd))
|
||
|
#print("TX: '{}'".format(strdumpArray(cmd[4:])))
|
||
|
|
||
|
ret = dev.write(USB_WRITE_ENDPOINT, cmd)
|
||
|
ret = dev.read(USB_READ_ENDPOINT, TRANSFER_LENGTH + 4, 5000)
|
||
|
|
||
|
#print("RX: " + hexdumpArray2(ret[4:]))
|
||
|
#print("RX: '{}'".format(strdumpArray(ret[4:])))
|
||
|
|
||
|
return ret[4:]
|
||
|
|
||
|
########################################################################
|
||
|
# Send the data packet to the GD-77 and confirm the response is correct
|
||
|
########################################################################
|
||
|
def sendAndCheckResponse(dev, cmd, resp):
|
||
|
USB_WRITE_ENDPOINT = 0x02
|
||
|
USB_READ_ENDPOINT = 0x81
|
||
|
TRANSFER_LENGTH = 38
|
||
|
zeroPad = [0x0] * TRANSFER_LENGTH
|
||
|
headerData = [0x0] * 4
|
||
|
|
||
|
headerData[0] = 1
|
||
|
headerData[1] = 0
|
||
|
headerData[2] = ((len(cmd) >> 0) & 0xff)
|
||
|
headerData[3] = ((len(cmd) >> 8) & 0xff)
|
||
|
|
||
|
if (len(resp) < TRANSFER_LENGTH):
|
||
|
resp = resp + zeroPad[0:TRANSFER_LENGTH - len(resp)]
|
||
|
|
||
|
cmd = headerData + cmd
|
||
|
|
||
|
#print("TX: " + hexdumpArray2(cmd))
|
||
|
#print("TX: '{}'".format(strdumpArray(cmd[4:])))
|
||
|
|
||
|
ret = dev.write(USB_WRITE_ENDPOINT, cmd)
|
||
|
ret = dev.read(USB_READ_ENDPOINT, TRANSFER_LENGTH + 4, 5000)
|
||
|
expected = array("B", resp)
|
||
|
|
||
|
#print("RX: " + hexdumpArray2(ret[4:]))
|
||
|
#print("RX: '{}'".format(strdumpArray(ret[4:])))
|
||
|
|
||
|
if (expected == ret[4:]):
|
||
|
return True
|
||
|
else:
|
||
|
print("Error. Read returned: " + str(ret))
|
||
|
return False
|
||
|
|
||
|
|
||
|
##############################
|
||
|
# Create checksum data packet
|
||
|
##############################
|
||
|
def createChecksumData(buf, startAddress, endAddress):
|
||
|
#checksum data starts with a small header, followed by the 32 bit checksum value, least significant byte first
|
||
|
checkSumData = [ 0x45, 0x4e, 0x44, 0xff, 0xDE, 0xAD, 0xBE, 0xEF ]
|
||
|
cs = 0
|
||
|
|
||
|
for i in range(startAddress, endAddress):
|
||
|
cs = cs + buf[i]
|
||
|
|
||
|
checkSumData[4] = (cs % 256) & 0xff
|
||
|
checkSumData[5] = ((cs >> 8) % 256) & 0xff
|
||
|
checkSumData[6] = ((cs >> 16) % 256) & 0xff
|
||
|
checkSumData[7] = ((cs >> 24) % 256) & 0xff
|
||
|
return checkSumData
|
||
|
|
||
|
|
||
|
def updateBlockAddressAndLength(buf, address, length):
|
||
|
buf[5] = ((length) % 256) & 0xff
|
||
|
buf[4] = ((length >> 8) % 256) & 0xff
|
||
|
buf[3] = ((address) % 256) & 0xff
|
||
|
buf[2] = ((address >> 8) % 256) & 0xff
|
||
|
buf[1] = ((address >> 16) % 256) & 0xff
|
||
|
buf[0] = ((address >> 24) % 256) & 0xff
|
||
|
return buf
|
||
|
|
||
|
|
||
|
#####################################################
|
||
|
# Open firmware file on disk and sent it to the GD-77
|
||
|
###########################################b##########
|
||
|
def sendFileData(fileBuf, dev):
|
||
|
dataHeader = [0x00] * (0x20 + 0x06)
|
||
|
BLOCK_LENGTH = 1024 #1k
|
||
|
DATA_TRANSFER_SIZE = 0x20
|
||
|
checksumStartAddress = 0
|
||
|
address = 0
|
||
|
|
||
|
fileLength = len(fileBuf)
|
||
|
totalBlocks = (fileLength // BLOCK_LENGTH) + 1
|
||
|
|
||
|
while address < fileLength:
|
||
|
if ((address % BLOCK_LENGTH) == 0):
|
||
|
checksumStartAddress = address
|
||
|
|
||
|
dataHeader = updateBlockAddressAndLength(dataHeader, address, DATA_TRANSFER_SIZE)
|
||
|
|
||
|
if ((address + DATA_TRANSFER_SIZE) < fileLength):
|
||
|
|
||
|
for i in range(DATA_TRANSFER_SIZE):
|
||
|
dataHeader[6 + i] = fileBuf[address + i]
|
||
|
|
||
|
if (sendAndCheckResponse(dev, dataHeader, responseOK) == False):
|
||
|
print("Error sending data")
|
||
|
return False
|
||
|
break
|
||
|
|
||
|
address = address + DATA_TRANSFER_SIZE
|
||
|
|
||
|
if ((address % 0x400) == 0):
|
||
|
print("\r - Sent block " + str(address // BLOCK_LENGTH) + " of "+ str(totalBlocks), end='')
|
||
|
sys.stdout.flush()
|
||
|
|
||
|
if (sendAndCheckResponse(dev, createChecksumData(fileBuf, checksumStartAddress, address), responseOK) == False):
|
||
|
print("Error sending checksum")
|
||
|
return False
|
||
|
break
|
||
|
|
||
|
else:
|
||
|
print("\r - Sending last block ", end='')
|
||
|
sys.stdout.flush()
|
||
|
|
||
|
DATA_TRANSFER_SIZE = fileLength - address
|
||
|
|
||
|
dataHeader = updateBlockAddressAndLength(dataHeader, address, DATA_TRANSFER_SIZE)
|
||
|
|
||
|
for i in range(DATA_TRANSFER_SIZE):
|
||
|
dataHeader[6 + i] = fileBuf[address + i]
|
||
|
|
||
|
if (sendAndCheckResponse(dev, dataHeader, responseOK) == False):
|
||
|
print("Error sending data")
|
||
|
return False
|
||
|
break
|
||
|
|
||
|
address = address + DATA_TRANSFER_SIZE
|
||
|
|
||
|
if (sendAndCheckResponse(dev, createChecksumData(fileBuf, checksumStartAddress, address), responseOK) == False):
|
||
|
print("Error sending checksum")
|
||
|
return False
|
||
|
break
|
||
|
|
||
|
print("")
|
||
|
return True
|
||
|
|
||
|
#####################################################
|
||
|
# Probe connected model
|
||
|
###########################################b##########
|
||
|
def probeModel(dev):
|
||
|
commandLetterA = [ 0x41 ] # 'A'
|
||
|
command0 = [[ 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44 ], [ 0x23, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x3f ]] # 'DOWNLOAD'
|
||
|
command1 = [ commandLetterA, responseOK ]
|
||
|
commandDummy = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ]
|
||
|
#commandMOD = [ 0x46, 0x2D, 0x4D, 0x4F, 0x44, 0xff, 0xff, 0xff ] # F-MOD...
|
||
|
#commandEND = [ 0x45, 0x4E, 0x44, 0xFF ] # END.
|
||
|
commandID = [ command0, command1 ]
|
||
|
models = [[ 'DV01', SGLFormatOutput.GD_77 ], [ 'DV02', SGLFormatOutput.GD_77S ], [ 'DV03', SGLFormatOutput.DM_1801 ]]
|
||
|
# RD-5R also have "DV02" id
|
||
|
|
||
|
commandNumber = 0
|
||
|
while commandNumber < len(commandID):
|
||
|
if sendAndCheckResponse(dev, commandID[commandNumber][0], commandID[commandNumber][1]) == False:
|
||
|
return SGLFormatOutput.UNKNOWN
|
||
|
commandNumber = commandNumber + 1
|
||
|
|
||
|
resp = sendAndGetResponse(dev, commandDummy)
|
||
|
##dummy = sendAndGetResponse(dev, command0[0])
|
||
|
|
||
|
for x in models:
|
||
|
if (x[0] == str(resp[:4].tobytes().decode("ascii"))):
|
||
|
return x[1]
|
||
|
|
||
|
return SGLFormatOutput.UNKNOWN
|
||
|
|
||
|
###########################################################################################################################################
|
||
|
# Send commands to the GD-77 to verify we are the updater, prepare to program including erasing the internal program flash memory
|
||
|
###########################################################################################################################################
|
||
|
def sendInitialCommands(dev, encodeKey):
|
||
|
commandLetterA =[ 0x41] #A
|
||
|
command0 =[[0x44,0x4f,0x57,0x4e,0x4c,0x4f,0x41,0x44],[0x23,0x55,0x50,0x44,0x41,0x54,0x45,0x3f]] # DOWNLOAD
|
||
|
command1 =[commandLetterA,responseOK]
|
||
|
command3 =[[0x46, 0x2d, 0x50, 0x52, 0x4f, 0x47, 0xff, 0xff],responseOK] #... F-PROG..
|
||
|
|
||
|
if (outputFormat == SGLFormatOutput.GD_77):
|
||
|
command2 =[[0x44, 0x56, 0x30, 0x31, (0x61 + 0x00), (0x61 + 0x0C), (0x61 + 0x0D), (0x61 + 0x01)],[0x44, 0x56, 0x30, 0x31]] #.... DV01enhi (DV01enhi comes from deobfuscated sgl file)
|
||
|
command4 =[[0x53, 0x47, 0x2d, 0x4d, 0x44, 0x2d, 0x37, 0x36, 0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #SG-MD-760
|
||
|
command5 =[[0x4d, 0x44, 0x2d, 0x37, 0x36, 0x30, 0xff, 0xff],responseOK] #MD-760..
|
||
|
elif (outputFormat == SGLFormatOutput.GD_77S):
|
||
|
command2 =[[0x44, 0x56, 0x30, 0x32, 0x6D, 0x40, 0x7D, 0x63],[0x44, 0x56, 0x30, 0x32]] #.... DV02Gpmj (thanks Wireshark)
|
||
|
command4 =[[0x53, 0x47, 0x2d, 0x4d, 0x44, 0x2d, 0x37, 0x33, 0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #SG-MD-730
|
||
|
command5 =[[0x4d, 0x44, 0x2d, 0x37, 0x33, 0x30, 0xff, 0xff],responseOK] #MD-730..
|
||
|
elif (outputFormat == SGLFormatOutput.DM_1801):
|
||
|
command2 =[[0x44, 0x56, 0x30, 0x33, 0x74, 0x21, 0x44, 0x39],[0x44, 0x56, 0x30, 0x33]] #.... last 4 bytes of the command are the offset encoded as letters a - p (hard coded fr
|
||
|
command4 =[[0x42, 0x46, 0x2d, 0x44, 0x4d, 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #BF-DMR
|
||
|
command5 =[[0x31, 0x38, 0x30, 0x31, 0xff, 0xff, 0xff, 0xff],responseOK] # MD-1801
|
||
|
elif (outputFormat == SGLFormatOutput.RD_5R):
|
||
|
command2 =[[0x44, 0x56, 0x30, 0x32, 0x53, 0x36, 0x37, 0x62],[0x44, 0x56, 0x30, 0x32]] #.... last 4 bytes of the command are the offset encoded as letters a - p (hard coded fr
|
||
|
command4 =[[0x42, 0x46, 0x2D, 0x35, 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #BF-5R
|
||
|
command5 =[[0x42, 0x46, 0x2D, 0x35, 0x52, 0xff, 0xff, 0xff],responseOK] # BF-5R
|
||
|
|
||
|
command6 =[[0x56, 0x31, 0x2e, 0x30, 0x30, 0x2e, 0x30, 0x31],responseOK] #V1.00.01
|
||
|
commandErase =[[0x46, 0x2d, 0x45, 0x52, 0x41, 0x53, 0x45, 0xff],responseOK] #F-ERASE
|
||
|
commandPostErase =[commandLetterA,responseOK]
|
||
|
commandProgram =[[0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0xf],responseOK] #PROGRAM
|
||
|
commands =[command0,command1,command2,command3,command4,command5,command6,commandErase,commandPostErase,commandProgram]
|
||
|
commandNames =["Sending Download command", "Sending ACK", "Sending encryption key", "Sending F-PROG command", "Sending radio modem number", "Sending radio modem number 2", "Sending version", "Sending erase command", "Send post erase command", "Sending Program command"]
|
||
|
commandNumber = 0
|
||
|
|
||
|
# Buffer.BlockCopy(encodeKey, 0, command2[0], 4, 4);
|
||
|
command2[0] = command2[0][0:4] + encodeKey
|
||
|
|
||
|
# Send the commands which the GD-77 expects before the start of the data
|
||
|
while commandNumber < len(commands):
|
||
|
print(" - " + commandNames[commandNumber])
|
||
|
|
||
|
if sendAndCheckResponse(dev, commands[commandNumber][0], commands[commandNumber][1]) == False:
|
||
|
print("Error sending command")
|
||
|
return False
|
||
|
break
|
||
|
commandNumber = commandNumber + 1
|
||
|
return True
|
||
|
|
||
|
###########################################################################################################################################
|
||
|
#
|
||
|
###########################################################################################################################################
|
||
|
def checkForSGLAndReturnEncryptedData(fileBuf):
|
||
|
header_tag = list("SGL!")
|
||
|
headerModel = []
|
||
|
|
||
|
buf_in_4 = list("".join(map(chr, fileBuf[0:4])))
|
||
|
headerModel.append(fileBuf[11])
|
||
|
|
||
|
if buf_in_4 == header_tag:
|
||
|
# read and decode offset and xor tag
|
||
|
buf_in_4 = list(fileBuf[0x000C : 0x000C + 4])
|
||
|
|
||
|
for i in range(0, 4):
|
||
|
buf_in_4[i] = buf_in_4[i] ^ ord(header_tag[i])
|
||
|
|
||
|
offset = buf_in_4[0] + 256 * buf_in_4[1]
|
||
|
xor_data = [ buf_in_4[2], buf_in_4[3] ]
|
||
|
|
||
|
# read and decode part of the header
|
||
|
buf_in_512 = list(fileBuf[offset + 0x0006 : offset + 0x0006 + 512])
|
||
|
|
||
|
xor_idx = 0;
|
||
|
for i in range(0, 512):
|
||
|
buf_in_512[i] = buf_in_512[i] ^ xor_data[xor_idx]
|
||
|
|
||
|
xor_idx += 1
|
||
|
if xor_idx == 2:
|
||
|
xor_idx = 0
|
||
|
|
||
|
#
|
||
|
encodeKey = buf_in_512[0x005D : 0x005D + 4]
|
||
|
|
||
|
# extract length
|
||
|
length1 = buf_in_512[0x0000]
|
||
|
length2 = buf_in_512[0x0001]
|
||
|
length3 = buf_in_512[0x0002]
|
||
|
length4 = buf_in_512[0x0003]
|
||
|
length = (length4 << 24) + (length3 << 16) + (length2 << 8) + length1
|
||
|
|
||
|
# extract encoded raw firmware
|
||
|
retBuf = [0x00] * length;
|
||
|
retBuf = fileBuf[len(fileBuf) - length : len(fileBuf) - length + len(retBuf) ]
|
||
|
|
||
|
return retBuf, encodeKey, headerModel
|
||
|
|
||
|
print("ERROR: SGL! header is missing.")
|
||
|
return None, None, None
|
||
|
|
||
|
###########################################################################################################################################
|
||
|
#
|
||
|
###########################################################################################################################################
|
||
|
def usage():
|
||
|
print("Usage:")
|
||
|
print(" " + ntpath.basename(sys.argv[0]) + " [OPTION]")
|
||
|
print("")
|
||
|
print(" -h, --help : Display this help text")
|
||
|
print(" -f, --firmware=<filename.sgl> : Flash <filename.sgl> instead of default file \"firmware.sgl\"")
|
||
|
print(" -m, --model=<type> : Select transceiver model. Models are: {}".format(", ".join(str(x) for x in outputModes[:-1])) + ".")
|
||
|
print(" -d, --download : Download firmware from the project website")
|
||
|
print(" -S, --stable : Select the stable version while downloading from the project page")
|
||
|
print(" -U, --unstable : Select the development version while downloading from the project page")
|
||
|
print("")
|
||
|
|
||
|
#####################################################
|
||
|
# Main function.
|
||
|
#####################################################
|
||
|
def main():
|
||
|
global outputFormat
|
||
|
sglFile = "firmware.sgl"
|
||
|
downloadStable = True
|
||
|
doDownload = False
|
||
|
doForce = False
|
||
|
|
||
|
# Command line argument parsing
|
||
|
try:
|
||
|
opts, args = getopt.getopt(sys.argv[1:], "hf:m:dSUF", ["help", "firmware=", "model=", "download", "stable", "unstable", "force"])
|
||
|
except getopt.GetoptError as err:
|
||
|
print(str(err))
|
||
|
usage()
|
||
|
sys.exit(-6)
|
||
|
|
||
|
for opt, arg in opts:
|
||
|
if opt in ("-h", "--help"):
|
||
|
usage()
|
||
|
sys.exit(0)
|
||
|
elif opt in ("-f", "--firmware"):
|
||
|
sglFile = arg
|
||
|
elif opt in ("-m", "--model"):
|
||
|
try:
|
||
|
index = outputModes.index(arg)
|
||
|
except ValueError as e:
|
||
|
print("Model \"{}\" is unknown".format(arg))
|
||
|
sys.exit(-5)
|
||
|
|
||
|
outputFormat = SGLFormatOutput(index)
|
||
|
|
||
|
if (outputFormat == SGLFormatOutput.UNKNOWN):
|
||
|
print("Unsupported model")
|
||
|
sys.exit(-5)
|
||
|
|
||
|
elif opt in ["-d", "--download"]:
|
||
|
doDownload = True
|
||
|
elif opt in ["-S", "--stable"]:
|
||
|
downloadStable = True
|
||
|
elif opt in ["-U", "--unstable"]:
|
||
|
downloadStable = False
|
||
|
elif opt in ["-F", "--force"]:
|
||
|
doForce = True
|
||
|
else:
|
||
|
assert False, "Unhandled option"
|
||
|
|
||
|
# Try to connect USB device
|
||
|
dev = usb.core.find(idVendor=0x15a2, idProduct=0x0073)
|
||
|
if (dev):
|
||
|
# Needed on Linux
|
||
|
try:
|
||
|
if dev.is_kernel_driver_active(0):
|
||
|
dev.detach_kernel_driver(0)
|
||
|
except NotImplementedError as e:
|
||
|
pass
|
||
|
|
||
|
#seems to be needed for the usb to work !
|
||
|
dev.set_configuration()
|
||
|
|
||
|
if (outputFormat == SGLFormatOutput.UNKNOWN):
|
||
|
outputFormat = probeModel(dev)
|
||
|
if (outputFormat == SGLFormatOutput.UNKNOWN):
|
||
|
print("Error. Failed to detect you transceiver model.")
|
||
|
sys.exit(-5)
|
||
|
print(" - Detected model: {}".format(outputModes[int(outputFormat)]))
|
||
|
|
||
|
# Try to download the firmware
|
||
|
if (doDownload == True):
|
||
|
if (downloadFirmware(downloadStable) == True):
|
||
|
sglFile = downloadedFW
|
||
|
else:
|
||
|
print("Firmware download failed")
|
||
|
sys.exit(-9)
|
||
|
|
||
|
if (os.path.isfile(sglFile) == False):
|
||
|
print("Firmware file \"" + sglFile + "\" is missing.")
|
||
|
sys.exit(-1)
|
||
|
|
||
|
print(" - " + "Now flashing your {} with \"{}\"".format(outputModes[int(outputFormat)], sglFile))
|
||
|
|
||
|
with open(sglFile, 'rb') as f:
|
||
|
fileBuf = f.read()
|
||
|
|
||
|
# Check firmware
|
||
|
filename, file_extension = os.path.splitext(sglFile)
|
||
|
|
||
|
# Define encodeKey according to HT model
|
||
|
if (outputFormat == SGLFormatOutput.GD_77):
|
||
|
encodeKey = [ (0x61 + 0x00), (0x61 + 0x0C), (0x61 + 0x0D), (0x61 + 0x01) ]
|
||
|
elif (outputFormat == SGLFormatOutput.GD_77S):
|
||
|
encodeKey = [ (0x6D), (0x40), (0x7D), (0x63) ] ## Original header (smaller filelength): was (0x47), (0x70), (0x6d), (0x4a)
|
||
|
elif (outputFormat == SGLFormatOutput.DM_1801):
|
||
|
encodeKey = [ (0x74), (0x21), (0x44), (0x39) ]
|
||
|
elif (outputFormat == SGLFormatOutput.RD_5R):
|
||
|
encodeKey = [ (0x53), (0x36), (0x37), (0x62) ]
|
||
|
|
||
|
if (file_extension == ".sgl"):
|
||
|
firmwareModelTag = { SGLFormatOutput.GD_77: 0x1B , SGLFormatOutput.GD_77S: 0x70, SGLFormatOutput.DM_1801: 0x4F, SGLFormatOutput.RD_5R: 0x5C}
|
||
|
|
||
|
## Could be a SGL file !
|
||
|
fileBuf, encodeKey, headerModel = checkForSGLAndReturnEncryptedData(fileBuf)
|
||
|
|
||
|
if (fileBuf == None):
|
||
|
print("Error. Missing SGL in .sgl file header")
|
||
|
sys.exit(-2)
|
||
|
|
||
|
print(" - " + "Firmware file confirmed as SGL")
|
||
|
|
||
|
if (doForce == False):
|
||
|
# Check if the firmware matches the transceiver model
|
||
|
if (headerModel[0] != firmwareModelTag[outputFormat]):
|
||
|
print("Error. The firmware doesn't match the transceiver model.")
|
||
|
sys.exit(-10)
|
||
|
|
||
|
else:
|
||
|
print("Firmware file is an unencrypted binary. Exiting")
|
||
|
sys.exit(-3)
|
||
|
|
||
|
if len(fileBuf) > 0x7b000:
|
||
|
print("Error. Firmware file too large.")
|
||
|
sys.exit(-4)
|
||
|
|
||
|
if (sendInitialCommands(dev, encodeKey) == True):
|
||
|
if (sendFileData(fileBuf, dev) == True):
|
||
|
print("Firmware update complete. Please reboot the {}.".format(outputModes[int(outputFormat)]))
|
||
|
else:
|
||
|
print("Error while sending data")
|
||
|
else:
|
||
|
print("Error while sending initial commands")
|
||
|
|
||
|
usb.util.dispose_resources(dev) #free up the USB device
|
||
|
|
||
|
else:
|
||
|
print("Error. Can't find your transceiver.")
|
||
|
|
||
|
# Remove downloaded firmware, if any
|
||
|
if (len(downloadedFW)):
|
||
|
if (os.path.isfile(downloadedFW)):
|
||
|
os.remove(downloadedFW)
|
||
|
|
||
|
|
||
|
## Run the program
|
||
|
main()
|
||
|
sys.exit(0)
|