edl/edlclient/Tools/boottodwnload
2024-06-10 01:25:50 +08:00

295 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()