mirror of
https://github.com/bkerler/edl
synced 2024-11-13 14:49:21 +00:00
296 lines
11 KiB
Python
Executable File
296 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# (c) B.Kerler 2018-2024 under GPLv3 license
|
|
# If you use my code, make sure you refer to my name
|
|
#
|
|
# !!!!! If you use this code in commercial products, your product is automatically
|
|
# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
|
|
import time
|
|
import serial
|
|
import serial.tools.list_ports
|
|
import argparse
|
|
import requests
|
|
from Exscript.protocols.telnetlib import Telnet
|
|
import usb.core
|
|
from enum import Enum
|
|
|
|
import os, sys, inspect
|
|
|
|
current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
|
sys.path.insert(0, current_dir)
|
|
|
|
try:
|
|
from edlclient.Tools.qc_diag import qcdiag
|
|
except ImportError as e:
|
|
print(current_dir)
|
|
from qc_diag import qcdiag
|
|
|
|
pass
|
|
|
|
try:
|
|
from edlclient.Library.utils import LogBase
|
|
except ImportError as e:
|
|
from Library.utils import LogBase
|
|
|
|
|
|
class vendor(Enum):
|
|
sierra = 0x1199
|
|
quectel = 0x2c7c
|
|
zte = 0x19d2
|
|
netgear = 0x0846
|
|
telit = 0x413c
|
|
|
|
|
|
class deviceclass:
|
|
vid = 0
|
|
pid = 0
|
|
|
|
def __init__(self, vid, pid):
|
|
self.vid = vid
|
|
self.pid = pid
|
|
|
|
|
|
class connection(metaclass=LogBase):
|
|
def __init__(self, port=""):
|
|
self.serial = None
|
|
self.tn = None
|
|
self.connected = False
|
|
if port == "":
|
|
port = self.detect(port)
|
|
if port == "":
|
|
try:
|
|
self.tn = Telnet("192.168.1.1", 5510)
|
|
self.connected = True
|
|
except:
|
|
self.connected = False
|
|
if port != "":
|
|
self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1)
|
|
self.connected = self.serial.is_open
|
|
|
|
def waitforusb(self, vid, pid):
|
|
timeout = 0
|
|
while timeout < 10:
|
|
for device in self.detectusbdevices():
|
|
if device.vid == vid:
|
|
if device.pid == pid:
|
|
return True
|
|
time.sleep(1)
|
|
timeout += 1
|
|
return False
|
|
|
|
def websend(self, url):
|
|
headers = {'Referer': 'http://192.168.0.1/index.html', 'Accept-Charset': 'UTF-8'}
|
|
r = requests.get(url, headers=headers)
|
|
if b"FACTORY:ok" in r.content or b"success" in r.content:
|
|
print(
|
|
f"Detected a ZTE in web mode .... switching mode success (convert back by sending \"AT+ZCDRUN=F\" via AT port)")
|
|
return self.waitforusb(vendor.zte.value, 0x0016)
|
|
return False
|
|
|
|
def getserialports(self):
|
|
return [port for port in serial.tools.list_ports.comports()]
|
|
|
|
def detectusbdevices(self):
|
|
dev = usb.core.find(find_all=True)
|
|
ids = [deviceclass(cfg.idVendor, cfg.idProduct) for cfg in dev]
|
|
return ids
|
|
|
|
def detect(self, port):
|
|
vendortable = {
|
|
0x1199: ["Sierra Wireless", 3],
|
|
0x2c7c: ["Quectel", 3],
|
|
0x19d2: ["ZTE", 2],
|
|
0x0846: ["Netgear", 2],
|
|
0x413c: ["Telit", 0]
|
|
}
|
|
mode = "Unknown"
|
|
for device in self.detectusbdevices():
|
|
if device.vid == vendor.zte.value:
|
|
if device.pid == 0x0016:
|
|
print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Diag mode")
|
|
mode = "AT"
|
|
break
|
|
elif device.pid == 0x1403:
|
|
print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Web mode")
|
|
mode = "Web"
|
|
# url = 'http://192.168.0.1/goform/goform_set_cmd_process?goformId=USB_MODE_SWITCH&usb_mode=1' #adb
|
|
url = 'http://192.168.0.1/goform/goform_process?goformId=MODE_SWITCH&switchCmd=FACTORY'
|
|
if self.websend(url):
|
|
mode = "AT"
|
|
break
|
|
elif device.vid == vendor.telit.value:
|
|
if device.pid == 0x81d7:
|
|
print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Diag mode")
|
|
print("Sending download mode command")
|
|
interface = 5
|
|
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x413c, 0x81d7, interface]])
|
|
if diag.connect():
|
|
data = diag.hdlc.receive_reply()
|
|
res = diag.send(b"\x4b\x65\x01\x00")
|
|
if res[0] == 0x4B:
|
|
print("Sending download mode succeeded")
|
|
diag.disconnect()
|
|
break
|
|
if mode == "AT" or mode == "Unknown":
|
|
for port in self.getserialports():
|
|
if port.vid in vendortable:
|
|
portid = port.location[-1:]
|
|
if int(portid) == vendortable[port.vid][1]:
|
|
print(f"Detected a {vendortable[port.vid][0]} at interface at: " + port.device)
|
|
return port.device
|
|
return ""
|
|
|
|
def readreply(self):
|
|
info = []
|
|
timeout = 0
|
|
if self.serial is not None:
|
|
while True:
|
|
tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '')
|
|
if "OK" in tmp:
|
|
info.append(tmp)
|
|
return info
|
|
elif "ERROR" in tmp:
|
|
return -1
|
|
if tmp != "":
|
|
info.append(tmp)
|
|
else:
|
|
timeout += 1
|
|
if timeout == 20:
|
|
break
|
|
return info
|
|
|
|
def send(self, cmd):
|
|
if self.tn is not None:
|
|
self.tn.write(bytes(cmd + "\r", 'utf-8'))
|
|
time.sleep(0.05)
|
|
data = ""
|
|
while True:
|
|
tmp = self.tn.read_eager()
|
|
if tmp != b"":
|
|
data += tmp.strip().decode('utf-8')
|
|
else:
|
|
break
|
|
return data.split("\r\n")
|
|
elif self.serial is not None:
|
|
self.serial.write(bytes(cmd + "\r", 'utf-8'))
|
|
time.sleep(0.05)
|
|
return self.readreply()
|
|
|
|
def close(self):
|
|
if self.tn is not None:
|
|
self.tn.close()
|
|
self.connected = False
|
|
if self.serial is not None:
|
|
self.serial.close()
|
|
self.connected = False
|
|
|
|
def ati(self):
|
|
data = {}
|
|
info = self.send("ATI")
|
|
if info != -1:
|
|
for line in info:
|
|
if "Revision" in line:
|
|
data["revision"] = line.split(":")[1].strip()
|
|
if "Model" in line:
|
|
data["model"] = line.split(":")[1].strip()
|
|
if "Quectel" in line:
|
|
data["vendor"] = "Quectel"
|
|
if "Manufacturer" in line:
|
|
data["manufacturer"] = line.split(":")[1].strip()
|
|
if "Sierra Wireless" in data["manufacturer"]:
|
|
data["vendor"] = "Sierra Wireless"
|
|
elif "ZTE CORPORATION" in data["manufacturer"]:
|
|
data["vendor"] = "ZTE"
|
|
elif "Netgear" in data["manufacturer"]:
|
|
data["vendor"] = "Netgear"
|
|
elif "Telit" in data["manufacturer"]:
|
|
data["vendor"] = "Telit"
|
|
return data
|
|
|
|
|
|
class dwnloadtools(metaclass=LogBase):
|
|
def sendcmd(self, tn, cmd):
|
|
tn.write(bytes(cmd, 'utf-8') + b"\n")
|
|
time.sleep(0.05)
|
|
return tn.read_eager().strip().decode('utf-8')
|
|
|
|
def run(self, args):
|
|
port = args.port
|
|
cn = connection(port)
|
|
if cn.connected:
|
|
info = cn.ati()
|
|
if "vendor" in info:
|
|
if info["vendor"] == "Sierra Wireless" or info["vendor"] == "Netgear":
|
|
print("Sending download mode command")
|
|
print(cn.send("AT!BOOTHOLD\r"))
|
|
print(cn.send('AT!QPSTDLOAD\r'))
|
|
print("Done switching to download mode")
|
|
elif info["vendor"] == "Quectel":
|
|
print("Sending download mode command")
|
|
interface = 0
|
|
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x2c7c, 0x0125, interface]])
|
|
if diag.connect():
|
|
diag.hdlc.receive_reply()
|
|
res = diag.send(b"\x4b\x65\x01\x00")
|
|
diag.disconnect()
|
|
print("Done switching to download mode")
|
|
elif info["vendor"] == "Telit":
|
|
print("Sending download mode command")
|
|
interface = 0
|
|
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x2c7c, 0x0125, interface]])
|
|
if diag.connect():
|
|
diag.hdlc.receive_reply()
|
|
res = diag.send(b"\x4b\x65\x01\x00")
|
|
diag.disconnect()
|
|
print("Done switching to download mode")
|
|
elif info["vendor"] == "ZTE":
|
|
print("Sending download mode command")
|
|
interface = 0
|
|
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2, 0x0016, interface]])
|
|
if diag.connect():
|
|
diag.hdlc.receive_reply()
|
|
res = diag.send(b"\x4b\x65\x01\x00")
|
|
if res[0] == 0x4B:
|
|
print("Done switching to ENANDPRG mode")
|
|
else:
|
|
res = diag.send(b"\x3a")
|
|
if res[0] == 0x3A:
|
|
while True:
|
|
state = cn.waitforusb(vendor.zte.value, 0x0076)
|
|
if not state:
|
|
diag.disconnect()
|
|
if diag.connect():
|
|
res = diag.send(b"\x3a")
|
|
else:
|
|
break
|
|
if state:
|
|
print("Done switching to NANDPRG mode")
|
|
else:
|
|
print("Failed switching to download mode")
|
|
diag.disconnect()
|
|
cn.close()
|
|
|
|
|
|
def main():
|
|
version = "1.1"
|
|
info = 'Modem Gimme-EDL ' + version + ' (c) B. Kerler 2020-2021'
|
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info)
|
|
parser.add_argument(
|
|
'-port', '-p',
|
|
help='use com port for auto unlock',
|
|
default="")
|
|
parser.add_argument(
|
|
'-logfile', '-l',
|
|
help='use logfile for debug log',
|
|
default="")
|
|
args = parser.parse_args()
|
|
if not args.port:
|
|
parser.print_help()
|
|
return
|
|
dw = dwnloadtools()
|
|
dw.run(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|