Восстановлено из транскрипта сессии после потери рабочего дерева на сервере сборки. Что внутри: - четыре новых модуля прошивки (приём, сборка исходящего, меню Messages); - правки существующих файлов в виде скриптов, с порядком применения; - инструменты стенда: снятие экрана рации по USB, дамп кадров, эталонная сборка пакета; - NOTES.md: что проверено в эфире, что не доделано, где грабли. Работало в эфире в августе 2026: данные ходят в обе стороны в режиме хотспота, рация принимает SMS и показывает на экране. Отправка собрана и сверена с эталоном сервера побайтово, но в эфире не проверялась. Claude-Session: https://claude.ai/code/session_01GJtvG5WwqYNe1b7s9LLtgy
57 строки
2.4 KiB
Python
57 строки
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Leave modem mode when the USB host goes away, instead of after a fixed timeout.
|
|
|
|
Stock firmware dropped out of hotspot mode if the host was quiet for 20 seconds, which
|
|
made the radio unusable as a permanently attached modem - so that timeout was disabled
|
|
on request. The side effect was that the radio then stayed a modem forever and had to be
|
|
power cycled by hand after every test.
|
|
|
|
This ties the exit to the actual USB connection state instead: while the host is present
|
|
the radio stays a modem no matter how quiet it is, and as soon as the cable is pulled (or
|
|
the host stops enumerating it) the radio returns to normal operation by itself.
|
|
"""
|
|
W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application"
|
|
p = W + "/source/functions/hotspot.c"
|
|
s = open(p).read()
|
|
|
|
if "usbHostIsAttached" in s:
|
|
print("already patched")
|
|
raise SystemExit(0)
|
|
|
|
# a small helper next to the other statics
|
|
anchor = "static const uint32_t MMDVMHOST_TIMEOUT = MMDVMHOST_TIMEOUT_DISABLED;\n"
|
|
assert anchor in s, "timeout constant not found"
|
|
|
|
helper = anchor + """
|
|
// The USB device handle tells us whether a host is actually enumerating us. Losing that
|
|
// is what "the computer went away" really means; host silence alone is not.
|
|
extern USBD_HandleTypeDef hUsbDeviceFS;
|
|
|
|
static bool usbHostIsAttached(void)
|
|
{
|
|
return (hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED);
|
|
}
|
|
"""
|
|
s = s.replace(anchor, helper, 1)
|
|
|
|
# make sure the USB device header is included
|
|
if '#include "usb_device.h"' not in s:
|
|
inc_anchor = '#include "functions/ticks.h"\n'
|
|
assert inc_anchor in s, "include anchor missing"
|
|
s = s.replace(inc_anchor, inc_anchor + '#include "usb_device.h"\n', 1)
|
|
|
|
# both inactivity checks now also fire when the host has disconnected
|
|
old = """ if ((MMDVMHOST_TIMEOUT != 0) && (nonVolatileSettings.hotspotType == HOTSPOT_TYPE_MMDVM) &&
|
|
((ticksGetMillis() - mmdvmHostLastActiveTime) > MMDVMHOST_TIMEOUT))
|
|
"""
|
|
new = """ if ((nonVolatileSettings.hotspotType == HOTSPOT_TYPE_MMDVM) &&
|
|
((usbHostIsAttached() == false) ||
|
|
((MMDVMHOST_TIMEOUT != 0) && ((ticksGetMillis() - mmdvmHostLastActiveTime) > MMDVMHOST_TIMEOUT))))
|
|
"""
|
|
count = s.count(old)
|
|
assert count == 2, "expected 2 inactivity checks, found %d" % count
|
|
s = s.replace(old, new)
|
|
|
|
open(p, "w").write(s)
|
|
print("hotspot.c: modem mode now ends when the USB host disconnects (%d checks)" % count)
|