Files
uv-k1-k5v3-firmware-custom/serialtool/msg.py
T
2025-10-22 12:38:54 +08:00

186 lines
4.5 KiB
Python

# Copyright (c) 2025 muzkr
#
# https://github.com/muzkr
#
# Licensed under the MIT License (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at the root of this repository.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""
Serial message functionalities
"""
MSG_NOTIFY_DEV_INFO = 0x0518
MSG_NOTIFY_BL_VER = 0x0530
MSG_PROG_FW = 0x0519
MSG_PROG_FW_RESP = 0x051A
MSG_PROG_f80 = 0x0516
MSG_PROG_f80_RESP = 0x0517
# _MSG_LOG = 0x4C4C # 'L' 'L'
# _MSG_LOG_OBFUSCATED = 0x205A
class Msg:
def make(msg_type: int, data_len: int):
obj = Msg(4 + data_len)
obj.set_msg_type(msg_type)
return obj
def __init__(self, buf: bytearray | int):
if isinstance(buf, int):
buf = bytearray(buf)
self.buf = buf
self._set_data_len(len(buf) - 4)
def get_msg_type(self) -> int:
return _get_hw_LE(self.buf)
def set_msg_type(self, msg_type: int):
_put_hw_LE(msg_type, self.buf)
def get_data_len(self) -> int:
return _get_hw_LE(self.buf, 2)
def _set_data_len(self, data_len: int):
_put_hw_LE(data_len, self.buf, 2)
def get_hw_LE(self, off: int) -> int:
return _get_hw_LE(self.buf, off)
def get_word_LE(self, off: int) -> int:
return _get_word_LE(self.buf, off)
def set_hw_LE(self, off: int, n: int):
_put_hw_LE(n, self.buf, off)
def set_word_LE(self, off: int, n: int):
_put_word_LE(n, self.buf, off)
def fetch(buf: bytearray) -> Msg | None:
if len(buf) < 8:
return None
pack_begin = buf.find(b"\xab\xcd")
if -1 == pack_begin:
if buf.endswith(b"\xab"):
del buf[:-1]
else:
del buf[:]
return None
if len(buf) - pack_begin < 8:
return None
msg_len = _get_hw_LE(buf, pack_begin + 2)
pack_end = pack_begin + 6 + msg_len
if not buf.startswith(b"\xdc\xba", pack_end):
# We've got wrong beginning
del buf[: pack_begin + 2]
return None
# --------------
# Packet complete
# Message type 0x4c4c (obfuscated to 0x205a) is for logging
# msg_type = _get_hw_LE(buf, pack_begin + 4)
# if _MSG_LOG == msg_type or _MSG_LOG_OBFUSCATED == msg_type:
# # Log messages are not obfuscated
# pass
# else:
# _obfus(buf, pack_begin + 4, msg_len + 2)
_obfus(buf, pack_begin + 4, msg_len + 2)
crc = _get_hw_LE(buf, pack_end - 2)
msg = Msg(buf[pack_begin + 4 : pack_end - 2])
del buf[: pack_end + 2]
# Validate CRC: don't. Messages from device do not apply correct CRC
crc += 0
return msg
def make_packet(msg: bytes) -> bytes:
msg_len = len(msg)
if 0 != (msg_len % 2):
msg_len += 1
buf = bytearray(8 + msg_len)
_put_hw_LE(0xCDAB, buf)
_put_hw_LE(msg_len, buf, 2)
_put_hw_LE(0xBADC, buf, 6 + msg_len)
with memoryview(buf) as view:
view[4 : 4 + len(msg)] = msg
crc = calc_CRC(buf, 4, msg_len)
_put_hw_LE(crc, buf, 4 + msg_len)
_obfus(buf, 4, 2 + msg_len)
return buf
def calc_CRC(buf: bytes, off: int = 0, size: int = 0) -> int:
CRC = 0
for i in range(size):
b = 0xFF & buf[off + i]
CRC ^= b << 8
for j in range(8):
# Check bit [15]
if 1 & (CRC >> 15):
CRC = (CRC << 1) ^ 0x1021
else:
CRC = CRC << 1
CRC = 0xFFFF & CRC
return CRC
_OBFUS_TBL = b"\x16\x6c\x14\xe6\x2e\x91\x0d\x40\x21\x35\xd5\x40\x13\x03\xe9\x80"
def _obfus(buf: bytearray, off: int = 0, size: int = 0):
N = len(_OBFUS_TBL)
for i in range(size):
buf[off + i] ^= _OBFUS_TBL[i % N]
def _get_hw_LE(buf: bytes, off: int = 0) -> int:
return (buf[off + 1] << 8) | buf[off]
def _get_word_LE(buf: bytes, off: int = 0) -> int:
return (buf[off + 3] << 24) | (buf[off + 2] << 16) | (buf[off + 1] << 8) | buf[off]
def _put_hw_LE(n: int, buf: bytes, off: int = 0):
buf[off] = 0xFF & n
buf[off + 1] = 0xFF & (n >> 8)
def _put_word_LE(n: int, buf: bytes, off: int = 0):
buf[off] = 0xFF & n
buf[off + 1] = 0xFF & (n >> 8)
buf[off + 2] = 0xFF & (n >> 16)
buf[off + 3] = 0xFF & (n >> 24)