edl/edlclient/Tools/boottodwnload
2023-07-23 17:05:34 +02:00

284 lines
11 KiB
Python
Executable file

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# (c) B.Kerler 2018-2023 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()
dw=dwnloadtools()
dw.run(args)
if __name__=="__main__":
main()