CH552/project/JTAG/ch55xtool.py
2021-05-17 11:27:15 +08:00

342 lines
11 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
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())
if ret[0] in ['V2.30']:
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:
if ret[0] in ['V2.31', 'V2.40']:
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()