kopia lustrzana https://github.com/sq5bpf/uvk5-reverse-engineering
Porównaj commity
4 Commity
943df0832d
...
9289ee6824
Autor | SHA1 | Data |
---|---|---|
sq5bpf | 9289ee6824 | |
sq5bpf | 1156789f09 | |
sq5bpf | bed08b1b13 | |
sq5bpf | acad7febb6 |
Plik binarny nie jest wyświetlany.
383
uvk5.py
383
uvk5.py
|
@ -47,7 +47,7 @@ DEBUG_SHOW_OBFUSCATED_COMMANDS = False
|
|||
# might be useful for someone debugging some obscure memory issue
|
||||
DEBUG_SHOW_MEMORY_ACTIONS = False
|
||||
|
||||
DRIVER_VERSION = "Quansheng UV-K5 driver v20230613 (c) Jacek Lipkowski SQ5BPF"
|
||||
DRIVER_VERSION = "Quansheng UV-K5 driver v20230619 (c) Jacek Lipkowski SQ5BPF"
|
||||
PRINT_CONSOLE = False
|
||||
|
||||
MEM_FORMAT = """
|
||||
|
@ -97,7 +97,10 @@ u8 vfo_open;
|
|||
|
||||
#seekto 0xe90;
|
||||
u8 beep_control;
|
||||
#seekto 0xe95;
|
||||
u8 key1_shortpress_action;
|
||||
u8 key1_longpress_action;
|
||||
u8 key2_shortpress_action;
|
||||
u8 key2_longpress_action;
|
||||
u8 scan_resume_mode;
|
||||
u8 auto_keypad_lock;
|
||||
u8 power_on_dispmode;
|
||||
|
@ -116,6 +119,33 @@ u8 repeater_tail_elimination;
|
|||
char logo_line1[16];
|
||||
char logo_line2[16];
|
||||
|
||||
#seekto 0xed0;
|
||||
struct {
|
||||
u8 side_tone;
|
||||
char separate_code;
|
||||
char group_call_code;
|
||||
u8 decode_response;
|
||||
u8 auto_reset_time;
|
||||
u8 preload_time;
|
||||
u8 first_code_persist_time;
|
||||
u8 hash_persist_time;
|
||||
u8 code_persist_time;
|
||||
u8 code_interval_time;
|
||||
u8 permit_remote_kill;
|
||||
} dtmf_settings;
|
||||
|
||||
#seekto 0xee0;
|
||||
struct {
|
||||
char dtmf_local_code[3];
|
||||
char unused1[5];
|
||||
char kill_code[5];
|
||||
char unused2[3];
|
||||
char revive_code[5];
|
||||
char unused3[3];
|
||||
char dtmf_up_code[16];
|
||||
char dtmf_down_code[16];
|
||||
} dtmf_settings_numbers;
|
||||
|
||||
#seekto 0xf18;
|
||||
u8 scanlist_default;
|
||||
u8 scanlist1_priority_scan;
|
||||
|
@ -141,6 +171,12 @@ struct {
|
|||
char name[16];
|
||||
} channelname[200];
|
||||
|
||||
#seekto 0x1c00;
|
||||
struct {
|
||||
char name[8];
|
||||
char number[3];
|
||||
char unused_00[5];
|
||||
} dtmfcontact[16];
|
||||
"""
|
||||
# bits that we will save from the channel structure (mostly unknown)
|
||||
SAVE_MASK_0A = 0b11001100
|
||||
|
@ -283,6 +319,17 @@ VFO_CHANNEL_NAMES = ["F1(50M-76M)A", "F1(50M-76M)B",
|
|||
|
||||
SCANLIST_LIST = ["None", "1", "2", "1+2"]
|
||||
|
||||
DTMF_CHARS = "0123456789ABCD*# "
|
||||
DTMF_CHARS_ID = "0123456789ABCDabcd"
|
||||
DTMF_CHARS_KILL = "0123456789ABCDabcd"
|
||||
DTMF_CHARS_UPDOWN = "0123456789ABCDabcd#* "
|
||||
DTMF_CODE_CHARS = "ABCD*# "
|
||||
DTMF_DECODE_RESPONSE_LIST = ["None", "Ring", "Reply", "Both"]
|
||||
|
||||
KEYACTIONS_LIST = ["None", "Flashlight on/off", "Power select",
|
||||
"Monitor", "Scan on/off", "VOX on/off",
|
||||
"Alarm on/off", "FM radio on/off", "Transmit 1750Hz"]
|
||||
|
||||
|
||||
# the communication is obfuscated using this fine mechanism
|
||||
def xorarr(data: bytes):
|
||||
|
@ -524,6 +571,11 @@ def _find_band(self, hz):
|
|||
B = BANDS_NOLIMITS
|
||||
else:
|
||||
B = BANDS
|
||||
|
||||
# currently the hacked firmware sets band=1 below 50MHz
|
||||
if self.FIRMWARE_NOLIMITS and mhz < 50.0:
|
||||
return 1
|
||||
|
||||
for a in B:
|
||||
if mhz >= B[a][0] and mhz <= B[a][1]:
|
||||
return a
|
||||
|
@ -549,7 +601,7 @@ class UVK5Radio(chirp_common.CloneModeRadio):
|
|||
'the memory image from the radio with chirp or k5prog '
|
||||
'and keep it. This can be later used to recover the '
|
||||
'original settings. \n\n'
|
||||
'DTMF settings and other details are not yet implemented')
|
||||
'some details are not yet implemented')
|
||||
rp.pre_download = _(
|
||||
"1. Turn radio on.\n"
|
||||
"2. Connect cable to mic/spkr connector.\n"
|
||||
|
@ -1024,6 +1076,81 @@ class UVK5Radio(chirp_common.CloneModeRadio):
|
|||
# "in the range %.1f - %.1f" % (FMMIN , FMMAX))
|
||||
_mem.fmfreq[i-1] = val2
|
||||
|
||||
# dtmf settings
|
||||
if element.get_name() == "dtmf_side_tone":
|
||||
_mem.dtmf_settings.side_tone = \
|
||||
element.value and 1 or 0
|
||||
|
||||
if element.get_name() == "dtmf_separate_code":
|
||||
_mem.dtmf_settings.separate_code = str(element.value)
|
||||
|
||||
if element.get_name() == "dtmf_group_call_code":
|
||||
_mem.dtmf_settings.group_call_code = element.value
|
||||
|
||||
if element.get_name() == "dtmf_decode_response":
|
||||
_mem.dtmf_settings.decode_response = \
|
||||
DTMF_DECODE_RESPONSE_LIST.index(str(element.value))
|
||||
|
||||
if element.get_name() == "dtmf_auto_reset_time":
|
||||
_mem.dtmf_settings.auto_reset_time = \
|
||||
int(int(element.value)/10)
|
||||
|
||||
if element.get_name() == "dtmf_preload_time":
|
||||
_mem.dtmf_settings.preload_time = \
|
||||
int(int(element.value)/10)
|
||||
|
||||
if element.get_name() == "dtmf_first_code_persist_time":
|
||||
_mem.dtmf_settings.first_code_persist_time = \
|
||||
int(int(element.value)/10)
|
||||
|
||||
if element.get_name() == "dtmf_hash_persist_time":
|
||||
_mem.dtmf_settings.hash_persist_time = \
|
||||
int(int(element.value)/10)
|
||||
|
||||
if element.get_name() == "dtmf_code_persist_time":
|
||||
_mem.dtmf_settings.code_persist_time = \
|
||||
int(int(element.value)/10)
|
||||
|
||||
if element.get_name() == "dtmf_code_interval_time":
|
||||
_mem.dtmf_settings.code_interval_time = \
|
||||
int(int(element.value)/10)
|
||||
|
||||
if element.get_name() == "dtmf_permit_remote_kill":
|
||||
_mem.dtmf_settings.permit_remote_kill = \
|
||||
element.value and 1 or 0
|
||||
|
||||
if element.get_name() == "dtmf_dtmf_local_code":
|
||||
k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*3
|
||||
_mem.dtmf_settings_numbers.dtmf_local_code = k[0:3]
|
||||
|
||||
if element.get_name() == "dtmf_dtmf_up_code":
|
||||
k = str(element.value).strip("\x20\xff\x00") + "\x00"*16
|
||||
_mem.dtmf_settings_numbers.dtmf_up_code = k[0:16]
|
||||
|
||||
if element.get_name() == "dtmf_dtmf_down_code":
|
||||
k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*16
|
||||
_mem.dtmf_settings_numbers.dtmf_down_code = k[0:16]
|
||||
|
||||
if element.get_name() == "dtmf_kill_code":
|
||||
k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
|
||||
_mem.dtmf_settings_numbers.kill_code = k[0:5]
|
||||
|
||||
if element.get_name() == "dtmf_revive_code":
|
||||
k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
|
||||
_mem.dtmf_settings_numbers.revive_code = k[0:5]
|
||||
|
||||
# dtmf contacts
|
||||
for i in range(1, 17):
|
||||
varname = "DTMF_" + str(i)
|
||||
if element.get_name() == varname:
|
||||
k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*8
|
||||
_mem.dtmfcontact[i-1].name = k[0:8]
|
||||
|
||||
varnumname = "DTMFNUM_" + str(i)
|
||||
if element.get_name() == varnumname:
|
||||
k = str(element.value).rstrip("\x20\xff\x00") + "\xff"*3
|
||||
_mem.dtmfcontact[i-1].number = k[0:3]
|
||||
|
||||
# scanlist stuff
|
||||
if element.get_name() == "scanlist_default":
|
||||
val = (int(element.value) == 2) and 1 or 0
|
||||
|
@ -1058,17 +1185,264 @@ class UVK5Radio(chirp_common.CloneModeRadio):
|
|||
if element.get_name() == "scanlist2_priority_ch2":
|
||||
_mem.scanlist2_priority_ch2 = val
|
||||
|
||||
if element.get_name() == "key1_shortpress_action":
|
||||
_mem.key1_shortpress_action = KEYACTIONS_LIST.index(
|
||||
str(element.value))
|
||||
|
||||
if element.get_name() == "key1_longpress_action":
|
||||
_mem.key1_longpress_action = KEYACTIONS_LIST.index(
|
||||
str(element.value))
|
||||
|
||||
if element.get_name() == "key2_shortpress_action":
|
||||
_mem.key2_shortpress_action = KEYACTIONS_LIST.index(
|
||||
str(element.value))
|
||||
|
||||
if element.get_name() == "key2_longpress_action":
|
||||
_mem.key2_longpress_action = KEYACTIONS_LIST.index(
|
||||
str(element.value))
|
||||
|
||||
def get_settings(self):
|
||||
_mem = self._memobj
|
||||
basic = RadioSettingGroup("basic", "Basic Settings")
|
||||
keya = RadioSettingGroup("keya", "Programmable keys")
|
||||
dtmf = RadioSettingGroup("dtmf", "DTMF Settings")
|
||||
dtmfc = RadioSettingGroup("dtmfc", "DTMF Contacts")
|
||||
scanl = RadioSettingGroup("scn", "Scan Lists")
|
||||
unlock = RadioSettingGroup("unlock", "Unlock Settings")
|
||||
fmradio = RadioSettingGroup("fmradio", "FM Radio")
|
||||
|
||||
roinfo = RadioSettingGroup("roinfo", "Driver information")
|
||||
|
||||
top = RadioSettings(basic, scanl, unlock, fmradio, roinfo)
|
||||
top = RadioSettings(
|
||||
basic, keya, dtmf, dtmfc, scanl, unlock, fmradio, roinfo)
|
||||
|
||||
# Programmable keys
|
||||
tmpval = int(_mem.key1_shortpress_action)
|
||||
if tmpval >= len(KEYACTIONS_LIST):
|
||||
tmpval = 0
|
||||
rs = RadioSetting("key1_shortpress_action", "Side key 1 short press",
|
||||
RadioSettingValueList(
|
||||
KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
|
||||
keya.append(rs)
|
||||
|
||||
tmpval = int(_mem.key1_longpress_action)
|
||||
if tmpval >= len(KEYACTIONS_LIST):
|
||||
tmpval = 0
|
||||
rs = RadioSetting("key1_longpress_action", "Side key 1 long press",
|
||||
RadioSettingValueList(
|
||||
KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
|
||||
keya.append(rs)
|
||||
|
||||
tmpval = int(_mem.key2_shortpress_action)
|
||||
if tmpval >= len(KEYACTIONS_LIST):
|
||||
tmpval = 0
|
||||
rs = RadioSetting("key2_shortpress_action", "Side key 2 short press",
|
||||
RadioSettingValueList(
|
||||
KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
|
||||
keya.append(rs)
|
||||
|
||||
tmpval = int(_mem.key2_longpress_action)
|
||||
if tmpval >= len(KEYACTIONS_LIST):
|
||||
tmpval = 0
|
||||
rs = RadioSetting("key2_longpress_action", "Side key 2 long press",
|
||||
RadioSettingValueList(
|
||||
KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
|
||||
keya.append(rs)
|
||||
|
||||
# DTMF settings
|
||||
tmppr = bool(_mem.dtmf_settings.side_tone > 0)
|
||||
rs = RadioSetting(
|
||||
"dtmf_side_tone",
|
||||
"DTMF Sidetone",
|
||||
RadioSettingValueBoolean(tmppr))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings.separate_code)
|
||||
if tmpval not in DTMF_CODE_CHARS:
|
||||
tmpval = '*'
|
||||
val = RadioSettingValueString(1, 1, tmpval)
|
||||
val.set_charset(DTMF_CODE_CHARS)
|
||||
rs = RadioSetting("dtmf_separate_code", "Separate Code", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings.group_call_code)
|
||||
if tmpval not in DTMF_CODE_CHARS:
|
||||
tmpval = '#'
|
||||
val = RadioSettingValueString(1, 1, tmpval)
|
||||
val.set_charset(DTMF_CODE_CHARS)
|
||||
rs = RadioSetting("dtmf_group_call_code", "Group Call Code", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = _mem.dtmf_settings.decode_response
|
||||
if tmpval >= len(DTMF_DECODE_RESPONSE_LIST):
|
||||
tmpval = 0
|
||||
rs = RadioSetting("dtmf_decode_response", "Decode Response",
|
||||
RadioSettingValueList(
|
||||
DTMF_DECODE_RESPONSE_LIST,
|
||||
DTMF_DECODE_RESPONSE_LIST[tmpval]))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = _mem.dtmf_settings.auto_reset_time
|
||||
if tmpval > 60 or tmpval < 5:
|
||||
tmpval = 5
|
||||
rs = RadioSetting("dtmf_auto_reset_time",
|
||||
"Auto reset time (s)",
|
||||
RadioSettingValueInteger(5, 60, tmpval))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = int(_mem.dtmf_settings.preload_time)
|
||||
if tmpval > 100 or tmpval < 3:
|
||||
tmpval = 30
|
||||
tmpval *= 10
|
||||
rs = RadioSetting("dtmf_preload_time",
|
||||
"Pre-load time (ms)",
|
||||
RadioSettingValueInteger(30, 1000, tmpval, 10))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = int(_mem.dtmf_settings.first_code_persist_time)
|
||||
if tmpval > 100 or tmpval < 3:
|
||||
tmpval = 30
|
||||
tmpval *= 10
|
||||
rs = RadioSetting("dtmf_first_code_persist_time",
|
||||
"First code persist time (ms)",
|
||||
RadioSettingValueInteger(30, 1000, tmpval, 10))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = int(_mem.dtmf_settings.hash_persist_time)
|
||||
if tmpval > 100 or tmpval < 3:
|
||||
tmpval = 30
|
||||
tmpval *= 10
|
||||
rs = RadioSetting("dtmf_hash_persist_time",
|
||||
"#/* persist time (ms)",
|
||||
RadioSettingValueInteger(30, 1000, tmpval, 10))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = int(_mem.dtmf_settings.code_persist_time)
|
||||
if tmpval > 100 or tmpval < 3:
|
||||
tmpval = 30
|
||||
tmpval *= 10
|
||||
rs = RadioSetting("dtmf_code_persist_time",
|
||||
"Code persist time (ms)",
|
||||
RadioSettingValueInteger(30, 1000, tmpval, 10))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = int(_mem.dtmf_settings.code_interval_time)
|
||||
if tmpval > 100 or tmpval < 3:
|
||||
tmpval = 30
|
||||
tmpval *= 10
|
||||
rs = RadioSetting("dtmf_code_interval_time",
|
||||
"Code interval time (ms)",
|
||||
RadioSettingValueInteger(30, 1000, tmpval, 10))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = bool(_mem.dtmf_settings.permit_remote_kill > 0)
|
||||
rs = RadioSetting(
|
||||
"dtmf_permit_remote_kill",
|
||||
"Permit remote kill",
|
||||
RadioSettingValueBoolean(tmpval))
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings_numbers.dtmf_local_code).upper().strip(
|
||||
"\x00\xff\x20")
|
||||
for i in tmpval:
|
||||
if i in DTMF_CHARS_ID:
|
||||
continue
|
||||
else:
|
||||
tmpval = "103"
|
||||
break
|
||||
val = RadioSettingValueString(3, 3, tmpval)
|
||||
val.set_charset(DTMF_CHARS_ID)
|
||||
rs = RadioSetting("dtmf_dtmf_local_code",
|
||||
"Local code (3 chars 0-9 ABCD)", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings_numbers.dtmf_up_code).upper().strip(
|
||||
"\x00\xff\x20")
|
||||
for i in tmpval:
|
||||
if i in DTMF_CHARS_UPDOWN or i == "":
|
||||
continue
|
||||
else:
|
||||
tmpval = "123"
|
||||
break
|
||||
val = RadioSettingValueString(1, 16, tmpval)
|
||||
val.set_charset(DTMF_CHARS_UPDOWN)
|
||||
rs = RadioSetting("dtmf_dtmf_up_code",
|
||||
"Up code (1-16 chars 0-9 ABCD*#)", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings_numbers.dtmf_down_code).upper().strip(
|
||||
"\x00\xff\x20")
|
||||
for i in tmpval:
|
||||
if i in DTMF_CHARS_UPDOWN:
|
||||
continue
|
||||
else:
|
||||
tmpval = "456"
|
||||
break
|
||||
val = RadioSettingValueString(1, 16, tmpval)
|
||||
val.set_charset(DTMF_CHARS_UPDOWN)
|
||||
rs = RadioSetting("dtmf_dtmf_down_code",
|
||||
"Down code (1-16 chars 0-9 ABCD*#)", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings_numbers.kill_code).upper().strip(
|
||||
"\x00\xff\x20")
|
||||
for i in tmpval:
|
||||
if i in DTMF_CHARS_KILL:
|
||||
continue
|
||||
else:
|
||||
tmpval = "77777"
|
||||
break
|
||||
if not len(tmpval) == 5:
|
||||
tmpval = "77777"
|
||||
val = RadioSettingValueString(5, 5, tmpval)
|
||||
val.set_charset(DTMF_CHARS_KILL)
|
||||
rs = RadioSetting("dtmf_kill_code",
|
||||
"Kill code (5 chars 0-9 ABCD)", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
tmpval = str(_mem.dtmf_settings_numbers.revive_code).upper().strip(
|
||||
"\x00\xff\x20")
|
||||
for i in tmpval:
|
||||
if i in DTMF_CHARS_KILL:
|
||||
continue
|
||||
else:
|
||||
tmpval = "88888"
|
||||
break
|
||||
if not len(tmpval) == 5:
|
||||
tmpval = "88888"
|
||||
val = RadioSettingValueString(5, 5, tmpval)
|
||||
val.set_charset(DTMF_CHARS_KILL)
|
||||
rs = RadioSetting("dtmf_revive_code",
|
||||
"Revive code (5 chars 0-9 ABCD)", val)
|
||||
dtmf.append(rs)
|
||||
|
||||
val = RadioSettingValueString(0, 80,
|
||||
"All DTMF Contacts are 3 codes "
|
||||
"(valid: 0-9 * # ABCD), "
|
||||
"or an empty string")
|
||||
val.set_mutable(False)
|
||||
rs = RadioSetting("dtmf_descr1", "DTMF Contacts", val)
|
||||
dtmfc.append(rs)
|
||||
|
||||
for i in range(1, 17):
|
||||
varname = "DTMF_"+str(i)
|
||||
varnumname = "DTMFNUM_"+str(i)
|
||||
vardescr = "DTMF Contact "+str(i)+" name"
|
||||
varinumdescr = "DTMF Contact "+str(i)+" number"
|
||||
|
||||
cntn = str(_mem.dtmfcontact[i-1].name).strip("\x20\x00\xff")
|
||||
cntnum = str(_mem.dtmfcontact[i-1].number).strip("\x20\x00\xff")
|
||||
|
||||
val = RadioSettingValueString(0, 8, cntn)
|
||||
rs = RadioSetting(varname, vardescr, val)
|
||||
dtmfc.append(rs)
|
||||
|
||||
val = RadioSettingValueString(0, 3, cntnum)
|
||||
val.set_charset(DTMF_CHARS)
|
||||
rs = RadioSetting(varnumname, varinumdescr, val)
|
||||
dtmfc.append(rs)
|
||||
|
||||
# scanlists
|
||||
if _mem.scanlist_default == 1:
|
||||
tmpsc = 2
|
||||
else:
|
||||
|
@ -1330,6 +1704,7 @@ class UVK5Radio(chirp_common.CloneModeRadio):
|
|||
RadioSettingValueString(0, 12, logo2))
|
||||
basic.append(rs)
|
||||
|
||||
# FM radio
|
||||
for i in range(1, 21):
|
||||
freqname = "FM_"+str(i)
|
||||
fmfreq = _mem.fmfreq[i-1]/10.0
|
||||
|
|
Ładowanie…
Reference in New Issue