2021-05-17 05:27:15 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2020-04-17 10:54:06 +02:00
|
|
|
import sys, os
|
|
|
|
import time
|
|
|
|
import array
|
|
|
|
import math
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
import usb.core
|
|
|
|
import usb.util
|
|
|
|
|
|
|
|
#======= Some C-like static constants =======
|
|
|
|
DFU_ID_VENDOR = 0x4348
|
|
|
|
DFU_ID_PRODUCT = 0x55e0
|
|
|
|
|
|
|
|
EP_OUT_ADDR = 0x02
|
|
|
|
EP_IN_ADDR = 0x82
|
|
|
|
|
|
|
|
USB_MAX_TIMEOUT = 2000
|
|
|
|
|
|
|
|
DETECT_CHIP_CMD_V2 = [0xa1, 0x12, 0x00, 0x52, 0x11, 0x4d, 0x43, 0x55, 0x20, 0x49, 0x53,
|
|
|
|
0x50, 0x20, 0x26, 0x20, 0x57, 0x43, 0x48, 0x2e, 0x43, 0x4e]
|
|
|
|
END_FLASH_CMD_V2 = [0xa2, 0x01, 0x00, 0x00]
|
|
|
|
RESET_RUN_CMD_V2 = [0xa2, 0x01, 0x00, 0x01]
|
|
|
|
SEND_KEY_CMD_V20 = [0xa3, 0x30, 0x00]
|
|
|
|
SEND_KEY_CMD_V23 = [0xa3, 0x38, 0x00] + [0x00] * (0x38)
|
|
|
|
ERASE_CHIP_CMD_V2 = [0xa4, 0x01, 0x00, 0x08]
|
|
|
|
WRITE_CMD_V2 = [0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
|
|
|
VERIFY_CMD_V2 = [0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
|
|
|
READ_CFG_CMD_V2 = [0xa7, 0x02, 0x00, 0x1f, 0x00]
|
|
|
|
|
|
|
|
CH55X_IC_REF = {}
|
|
|
|
CH55X_IC_REF[0x51] = {'device_name': 'CH551', 'device_flash_size': 10240, 'device_dataflash_size': 128, 'chip_id': 0x51}
|
|
|
|
CH55X_IC_REF[0x52] = {'device_name': 'CH552', 'device_flash_size': 16384, 'device_dataflash_size': 128, 'chip_id': 0x52}
|
|
|
|
CH55X_IC_REF[0x53] = {'device_name': 'CH553', 'device_flash_size': 10240, 'device_dataflash_size': 128, 'chip_id': 0x53}
|
|
|
|
CH55X_IC_REF[0x54] = {'device_name': 'CH554', 'device_flash_size': 14336, 'device_dataflash_size': 128, 'chip_id': 0x54}
|
|
|
|
CH55X_IC_REF[0x59] = {'device_name': 'CH559', 'device_flash_size': 61440, 'device_dataflash_size': 128, 'chip_id': 0x59}
|
|
|
|
#=============================================
|
|
|
|
|
|
|
|
def __get_dfu_device(idVendor=DFU_ID_VENDOR, idProduct=DFU_ID_PRODUCT):
|
|
|
|
dev = usb.core.find(idVendor=idVendor, idProduct=idProduct)
|
|
|
|
if dev is None:
|
|
|
|
return (None, 'NO_DEV_FOUND')
|
|
|
|
try:
|
|
|
|
if dev.is_kernel_driver_active(0):
|
|
|
|
try:
|
|
|
|
dev.detach_kernel_driver(0)
|
|
|
|
except usb.core.USBError:
|
|
|
|
return (None, 'USB_ERROR_CANNOT_DETACH_KERNEL_DRIVER')
|
|
|
|
except:
|
|
|
|
pass #Windows dont need detach
|
|
|
|
|
|
|
|
try:
|
|
|
|
dev.set_configuration()
|
|
|
|
except usb.core.USBError:
|
|
|
|
return (None, 'USB_ERROR_CANNOT_SET_CONFIG')
|
|
|
|
|
|
|
|
try:
|
|
|
|
usb.util.claim_interface(dev, 0)
|
|
|
|
except usb.core.USBError:
|
|
|
|
return (None, 'USB_ERROR_CANNOT_CLAIM_INTERFACE')
|
|
|
|
|
|
|
|
return (dev, '')
|
|
|
|
|
|
|
|
def __detect_ch55x_v2(dev):
|
|
|
|
dev.write(EP_OUT_ADDR, DETECT_CHIP_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
try:
|
|
|
|
return CH55X_IC_REF[ret[4]]
|
|
|
|
except KeyError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def __read_cfg_ch55x_v2(dev):
|
|
|
|
dev.write(EP_OUT_ADDR, READ_CFG_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 30, USB_MAX_TIMEOUT)
|
|
|
|
|
|
|
|
ver_str = 'V%d.%d%d' % (ret[19], ret[20], ret[21])
|
|
|
|
chk_sum = (ret[22] + ret[23] + ret[24] + ret[25]) % 256
|
|
|
|
|
|
|
|
return (ver_str, chk_sum)
|
|
|
|
|
|
|
|
def __write_key_ch55x_v20(dev, chk_sum):
|
|
|
|
SEND_KEY_CMD_V20 = SEND_KEY_CMD_V20 + [chk_sum] * 0x30
|
|
|
|
|
|
|
|
dev.write(EP_OUT_ADDR, SEND_KEY_CMD_V20)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
if ret[3] == 0:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def __write_key_ch55x_v23(dev):
|
|
|
|
dev.write(EP_OUT_ADDR, SEND_KEY_CMD_V23)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
if ret[3] == 0:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def __erase_chip_ch55x_v2(dev):
|
|
|
|
dev.write(EP_OUT_ADDR, ERASE_CHIP_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
if ret[3] == 0:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def __write_flash_ch55x_v20(dev, chk_sum, chip_id, payload):
|
|
|
|
payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number
|
|
|
|
file_length = len(payload)
|
|
|
|
|
|
|
|
for index in range(file_length):
|
|
|
|
if index % 8 == 7:
|
|
|
|
payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256
|
|
|
|
|
|
|
|
left_len = file_length
|
|
|
|
curr_addr = 0
|
|
|
|
|
|
|
|
while curr_addr < file_length:
|
|
|
|
|
|
|
|
if left_len >= 56:
|
|
|
|
pkt_length = 56
|
|
|
|
else:
|
|
|
|
pkt_length = left_len
|
|
|
|
|
|
|
|
__WRITE_CMD_V2 = WRITE_CMD_V2
|
|
|
|
__WRITE_CMD_V2[1] = pkt_length + 5
|
|
|
|
__WRITE_CMD_V2[3] = curr_addr % 256
|
|
|
|
__WRITE_CMD_V2[4] = (curr_addr >> 8) % 256
|
|
|
|
__WRITE_CMD_V2[7] = left_len % 256
|
|
|
|
__WRITE_CMD_V2 = __WRITE_CMD_V2 + payload[curr_addr:curr_addr+pkt_length]
|
|
|
|
|
|
|
|
dev.write(EP_OUT_ADDR, __WRITE_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
|
|
|
|
curr_addr = curr_addr + pkt_length
|
|
|
|
left_len = left_len - pkt_length
|
|
|
|
|
|
|
|
if ret[4] != 0x00:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return file_length
|
|
|
|
|
|
|
|
def __write_flash_ch55x_v23(dev, chk_sum, chip_id, payload):
|
|
|
|
payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number
|
|
|
|
file_length = len(payload)
|
|
|
|
|
|
|
|
for index in range(file_length):
|
|
|
|
if index % 8 == 7:
|
|
|
|
payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256
|
|
|
|
else:
|
|
|
|
payload[index] = (payload[index] ^ chk_sum) % 256
|
|
|
|
|
|
|
|
left_len = file_length
|
|
|
|
curr_addr = 0
|
|
|
|
|
|
|
|
while curr_addr < file_length:
|
|
|
|
|
|
|
|
if left_len >= 56:
|
|
|
|
pkt_length = 56
|
|
|
|
else:
|
|
|
|
pkt_length = left_len
|
|
|
|
|
|
|
|
__WRITE_CMD_V2 = WRITE_CMD_V2
|
|
|
|
__WRITE_CMD_V2[1] = pkt_length + 5
|
|
|
|
__WRITE_CMD_V2[3] = curr_addr % 256
|
|
|
|
__WRITE_CMD_V2[4] = (curr_addr >> 8) % 256
|
|
|
|
__WRITE_CMD_V2[7] = left_len % 256
|
|
|
|
__WRITE_CMD_V2 = __WRITE_CMD_V2 + payload[curr_addr:curr_addr+pkt_length]
|
|
|
|
|
|
|
|
dev.write(EP_OUT_ADDR, __WRITE_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
|
|
|
|
curr_addr = curr_addr + pkt_length
|
|
|
|
left_len = left_len - pkt_length
|
|
|
|
|
|
|
|
if ret[4] != 0x00:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return file_length
|
|
|
|
|
|
|
|
def __verify_flash_ch55x_v20(dev, chk_sum, chip_id, payload):
|
|
|
|
payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number
|
|
|
|
file_length = len(payload)
|
|
|
|
|
|
|
|
for index in range(file_length):
|
|
|
|
if index % 8 == 7:
|
|
|
|
payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256
|
|
|
|
|
|
|
|
left_len = file_length
|
|
|
|
curr_addr = 0
|
|
|
|
|
|
|
|
while curr_addr < file_length:
|
|
|
|
|
|
|
|
if left_len >= 56:
|
|
|
|
pkt_length = 56
|
|
|
|
else:
|
|
|
|
pkt_length = left_len
|
|
|
|
|
|
|
|
__VERIFY_CMD_V2 = VERIFY_CMD_V2
|
|
|
|
__VERIFY_CMD_V2[1] = pkt_length + 5
|
|
|
|
__VERIFY_CMD_V2[3] = curr_addr % 256
|
|
|
|
__VERIFY_CMD_V2[4] = (curr_addr >> 8) % 256
|
|
|
|
__VERIFY_CMD_V2[7] = left_len % 256
|
|
|
|
__VERIFY_CMD_V2 = __VERIFY_CMD_V2 + payload[curr_addr:curr_addr+pkt_length]
|
|
|
|
|
|
|
|
dev.write(EP_OUT_ADDR, __VERIFY_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
|
|
|
|
curr_addr = curr_addr + pkt_length
|
|
|
|
left_len = left_len - pkt_length
|
|
|
|
|
|
|
|
if ret[4] != 0x00:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return file_length
|
|
|
|
|
|
|
|
def __verify_flash_ch55x_v23(dev, chk_sum, chip_id, payload):
|
|
|
|
payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number
|
|
|
|
file_length = len(payload)
|
|
|
|
|
|
|
|
for index in range(file_length):
|
|
|
|
if index % 8 == 7:
|
|
|
|
payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256
|
|
|
|
else:
|
|
|
|
payload[index] = (payload[index] ^ chk_sum) % 256
|
|
|
|
|
|
|
|
left_len = file_length
|
|
|
|
curr_addr = 0
|
|
|
|
|
|
|
|
while curr_addr < file_length:
|
|
|
|
|
|
|
|
if left_len >= 56:
|
|
|
|
pkt_length = 56
|
|
|
|
else:
|
|
|
|
pkt_length = left_len
|
|
|
|
|
|
|
|
__VERIFY_CMD_V2 = VERIFY_CMD_V2
|
|
|
|
__VERIFY_CMD_V2[1] = pkt_length + 5
|
|
|
|
__VERIFY_CMD_V2[3] = curr_addr % 256
|
|
|
|
__VERIFY_CMD_V2[4] = (curr_addr >> 8) % 256
|
|
|
|
__VERIFY_CMD_V2[7] = left_len % 256
|
|
|
|
__VERIFY_CMD_V2 = __VERIFY_CMD_V2 + payload[curr_addr:curr_addr+pkt_length]
|
|
|
|
|
|
|
|
dev.write(EP_OUT_ADDR, __VERIFY_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
|
|
|
|
curr_addr = curr_addr + pkt_length
|
|
|
|
left_len = left_len - pkt_length
|
|
|
|
|
|
|
|
if ret[4] != 0x00:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return file_length
|
|
|
|
|
|
|
|
def __end_flash_ch55x_v2(dev):
|
|
|
|
dev.write(EP_OUT_ADDR, END_FLASH_CMD_V2)
|
|
|
|
ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT)
|
|
|
|
if ret[4] != 0x00:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
def __restart_run_ch55x_v2(dev):
|
|
|
|
dev.write(EP_OUT_ADDR, RESET_RUN_CMD_V2)
|
|
|
|
|
|
|
|
def __main():
|
|
|
|
parser = argparse.ArgumentParser(description="USBISP Tool For WinChipHead CH55x.")
|
|
|
|
parser.add_argument('-f', '--file', type=str, default='', help="The target file to be flashed.")
|
|
|
|
parser.add_argument('-r', '--reset_after_flash', action='store_true', default=False, help="Reset after finsh flash.")
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
ret = __get_dfu_device()
|
|
|
|
if ret[0] is None:
|
|
|
|
sys.exit(ret[1])
|
|
|
|
dev = ret[0]
|
|
|
|
|
|
|
|
ret = __detect_ch55x_v2(dev)
|
|
|
|
if ret is None:
|
|
|
|
print('Unable to detect CH55x.')
|
|
|
|
print('Found %s.' % ret['device_name'])
|
|
|
|
chip_id = ret['chip_id']
|
|
|
|
|
|
|
|
ret = __read_cfg_ch55x_v2(dev)
|
|
|
|
chk_sum = ret[1]
|
|
|
|
|
|
|
|
print('BTVER: %s.' % ret[0])
|
|
|
|
|
|
|
|
if args.file != '':
|
|
|
|
payload = list(open(args.file, 'rb').read())
|
2021-05-17 05:27:15 +02:00
|
|
|
if ret[0] in ['V2.30']:
|
2020-04-17 10:54:06 +02:00
|
|
|
ret = __write_key_ch55x_v20(dev, chk_sum)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to write key to CH55x.')
|
|
|
|
|
|
|
|
ret = __erase_chip_ch55x_v2(dev)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to erase CH55x.')
|
|
|
|
|
|
|
|
ret = __write_flash_ch55x_v20(dev, chk_sum, chip_id, payload)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to flash firmware of CH55x.')
|
|
|
|
|
|
|
|
ret = __verify_flash_ch55x_v20(dev, chk_sum, chip_id, payload)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to verify firmware of CH55x.')
|
|
|
|
else:
|
2021-05-17 05:27:15 +02:00
|
|
|
if ret[0] in ['V2.31', 'V2.40']:
|
2020-04-17 10:54:06 +02:00
|
|
|
ret = __write_key_ch55x_v23(dev)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to write key to CH55x.')
|
|
|
|
|
|
|
|
ret = __erase_chip_ch55x_v2(dev)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to erase CH55x.')
|
|
|
|
|
|
|
|
ret = __write_flash_ch55x_v23(dev, chk_sum, chip_id, payload)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to flash firmware of CH55x.')
|
|
|
|
|
|
|
|
ret = __verify_flash_ch55x_v23(dev, chk_sum, chip_id, payload)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to verify firmware of CH55x.')
|
|
|
|
else:
|
|
|
|
sys.exit('Bootloader version not supported.')
|
|
|
|
|
|
|
|
ret = __end_flash_ch55x_v2(dev)
|
|
|
|
if ret is None:
|
|
|
|
sys.exit('Failed to end flash process.')
|
|
|
|
|
|
|
|
print('Flash done.')
|
|
|
|
|
|
|
|
if args.reset_after_flash:
|
|
|
|
__restart_run_ch55x_v2(dev)
|
|
|
|
print('Restart and run.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
__main()
|