Keep BLE contact sync from blocking other companion transports

This commit is contained in:
mikecarper committed 2026-10-04 02:11:18 -07:00
1 parent bd5b79c1cb
commit dbcb400c74
11 files changed
+347 -53

No files matched your search

+29 -17
View File
@@ -369,7 +369,8 @@ void MyMesh::writeDisabledFrame() {
_serial->writeFrame(buf, 1);
}
bool MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) {
bool MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact,
BaseSerialInterface* route) {
int i = 0;
out_frame[i++] = code;
memcpy(&out_frame[i], contact.id.pub_key, PUB_KEY_SIZE);
@@ -389,7 +390,8 @@ bool MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) {
i += 4;
memcpy(&out_frame[i], &contact.lastmod, 4);
i += 4;
return _serial->writeFrame(out_frame, i) == (size_t)i;
return (route != NULL ? _serial->writeFrameToRoute(route, out_frame, i)
: _serial->writeFrame(out_frame, i)) == (size_t)i;
}
void MyMesh::stopContactsIterator() {
@@ -399,7 +401,7 @@ void MyMesh::stopContactsIterator() {
_iter_start_pending = false;
_iter_contact_pending = false;
_iter_next_frame_at = 0;
if (_serial != NULL) _serial->unlockReplyRoute();
_iter_reply_route = NULL;
}
static constexpr int CONTACT_UPDATE_FRAME_MIN_LEN =
@@ -2271,6 +2273,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_serial(NULL), _mota_source_control(NULL),
telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui), _iter(0) {
_iter_started = false;
_iter_reply_route = NULL;
_iter_start_pending = false;
_iter_contact_pending = false;
_iter_next_frame_at = 0;
@@ -5181,8 +5184,8 @@ void MyMesh::serviceSigningSession() {
}
}
void MyMesh::cancelSerialResponseStream() {
stopContactsIterator();
void MyMesh::cancelSerialResponseStream(BaseSerialInterface* route) {
if (route == NULL || _iter_reply_route == route) stopContactsIterator();
}
void MyMesh::cancelSerialOperationsForRoute(BaseSerialInterface* route) {
@@ -5268,7 +5271,7 @@ void MyMesh::handleCmdFrame(size_t len) {
MESH_DEBUG_PRINTLN("App %s connected", app_name);
BaseSerialInterface* app_route = _serial->captureReplyRoute();
stopContactsIterator(); // stop any left-over ContactsIterator
cancelSerialResponseStream(app_route);
cancelSerialOperationsForRoute(app_route);
int i = 0;
out_frame[i++] = RESP_CODE_SELF_INFO;
@@ -5535,9 +5538,10 @@ void MyMesh::handleCmdFrame(size_t len) {
_iter_filter_since = 0;
}
// CONTACTS_START, every CONTACT, and END_OF_CONTACTS are one response
// transaction. Keep them on the transport which requested the list.
_serial->lockReplyRoute();
// Retain this stream's destination independently of later commands.
// BLE's paced contact sync must not hold USB/WiFi command polling until
// the entire list finishes, or redirect their replies to the BLE app.
_iter_reply_route = _serial->captureReplyRoute();
_iter = startContactsIterator();
_iter_started = true;
_iter_start_pending = true;
@@ -5550,7 +5554,7 @@ void MyMesh::handleCmdFrame(size_t len) {
uint8_t reply[5];
reply[0] = RESP_CODE_CONTACTS_START;
memcpy(&reply[1], &_iter_total_count, 4); // total, NOT filtered count
if (_serial->writeFrame(reply, 5) == 5) {
if (_serial->writeFrameToRoute(_iter_reply_route, reply, 5) == 5) {
_iter_start_pending = false;
_iter_next_frame_at = futureMillis(CONTACT_STREAM_FRAME_INTERVAL_MS);
}
@@ -10123,6 +10127,9 @@ void MyMesh::checkCLIRescueCmd() {
void MyMesh::checkSerialInterface() {
size_t len = _serial->checkRecvFrame(cmd_frame);
if (_iter_started && !_serial->isReplyRouteAvailable(_iter_reply_route)) {
stopContactsIterator();
}
if (!_serial->isConnected()) {
stopContactsIterator();
return;
@@ -10130,11 +10137,12 @@ void MyMesh::checkSerialInterface() {
if (len > 0) {
handleCmdFrame(len);
} else if (_iter_started // check if our ContactsIterator is 'running'
}
if (_iter_started // check if our ContactsIterator is 'running'
&& (_iter_next_frame_at == 0
|| _iter_next_frame_at == _ms->getMillis()
|| millisHasNowPassed(_iter_next_frame_at))
&& !_serial->isWriteBusy() // don't spam the Serial Interface too quickly!
&& !_serial->isReplyRouteWriteBusy(_iter_reply_route)
) {
if (_iter_table_revision != getContactTableRevision()) {
// The paced stream walks the live compacting table. If an advert, app
@@ -10154,7 +10162,8 @@ void MyMesh::checkSerialInterface() {
uint8_t reply[5];
reply[0] = RESP_CODE_CONTACTS_START;
memcpy(&reply[1], &_iter_total_count, 4);
if (_serial->writeFrame(reply, sizeof(reply)) == sizeof(reply)) {
if (_serial->writeFrameToRoute(_iter_reply_route, reply, sizeof(reply))
== sizeof(reply)) {
_iter_start_pending = false;
_iter_next_frame_at = futureMillis(CONTACT_STREAM_FRAME_INTERVAL_MS);
}
@@ -10171,7 +10180,7 @@ void MyMesh::checkSerialInterface() {
out_frame[0] = RESP_CODE_END_OF_CONTACTS;
memcpy(&out_frame[1], &_most_recent_lastmod,
4); // include the most recent lastmod, so app can update their 'since'
if (_serial->writeFrame(out_frame, 5) == 5) {
if (_serial->writeFrameToRoute(_iter_reply_route, out_frame, 5) == 5) {
stopContactsIterator();
}
return;
@@ -10181,7 +10190,8 @@ void MyMesh::checkSerialInterface() {
// Do not advance past a contact until its complete frame has been accepted
// by the selected transport. A transiently full/lost route must not turn a
// successful CONTACTS_START/END transaction into a silently short list.
if (writeContactRespFrame(RESP_CODE_CONTACT, _iter_pending_contact)) {
if (writeContactRespFrame(RESP_CODE_CONTACT, _iter_pending_contact,
_iter_reply_route)) {
if (_iter_pending_contact.lastmod > _most_recent_lastmod) {
_most_recent_lastmod = _iter_pending_contact.lastmod;
}
@@ -10346,7 +10356,8 @@ bool MyMesh::canRecoverUsbLogging() const {
&& !_terminal_trace_pending
#endif
&& !(_serial && _serial->hasPendingIO())
&& !(_iter_started && _serial && _serial->isConnected());
&& !(_iter_started && _serial
&& _serial->isReplyRouteAvailable(_iter_reply_route));
}
bool MyMesh::hasPendingWork() const {
@@ -10370,7 +10381,8 @@ bool MyMesh::hasPendingWork() const {
const bool contact_write_needs_polling = isContactWriteDue();
#endif
return (_serial != NULL && _serial->hasPendingIO())
|| (_iter_started && _serial != NULL && _serial->isConnected())
|| (_iter_started && _serial != NULL
&& _serial->isReplyRouteAvailable(_iter_reply_route))
|| hasPendingOtaApply()
|| command_radio_apply_pending
|| _scheduled_reboot_at != 0
+4 -2
View File
@@ -188,7 +188,7 @@ public:
void activateRadio();
bool isRadioReady() const { return _radio_available; }
void startInterface(BaseSerialInterface &serial);
void cancelSerialResponseStream();
void cancelSerialResponseStream(BaseSerialInterface* route = NULL);
void cancelSerialOperationsForRoute(BaseSerialInterface* route);
bool hasFiniteDelayedReplyForRoute(BaseSerialInterface* route) const;
void resetUsbHostSessionInput();
@@ -481,7 +481,8 @@ private:
BaseSerialInterface* route = nullptr);
size_t writePendingSerialFrame(const uint8_t frame[], size_t len, uint32_t now);
void writeDisabledFrame();
bool writeContactRespFrame(uint8_t code, const ContactInfo &contact);
bool writeContactRespFrame(uint8_t code, const ContactInfo &contact,
BaseSerialInterface* route = NULL);
void stopContactsIterator();
bool updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, const uint8_t *frame, int len);
bool addToOfflineQueue(const uint8_t frame[], int len);
@@ -650,6 +651,7 @@ private:
AbstractUITask* _ui;
ContactsIterator _iter;
BaseSerialInterface* _iter_reply_route;
ContactInfo _iter_pending_contact;
uint32_t _iter_filter_since;
uint32_t _iter_next_frame_at;
+7 -15
View File
@@ -628,9 +628,7 @@ static void serviceCompanionPowerSaving(bool force = false) {
#if defined(ETHERNET_ENABLED)
static void cancelCompanionEthernetSession(void*) {
if (interface_manager.isReplyRouteFor(&ethernet_interface)) {
the_mesh.cancelSerialResponseStream();
}
the_mesh.cancelSerialResponseStream(&ethernet_interface);
the_mesh.cancelSerialOperationsForRoute(&ethernet_interface);
interface_manager.forgetReplyRouteForDisconnected(&ethernet_interface);
}
@@ -777,12 +775,9 @@ static void cancelUsbSerialOperations() {
// A cancelled reply must not enable diagnostics for the next USB host.
usb_logging_reply_pending = false;
#endif
// Contact enumeration uses the manager's pinned streaming route; delayed
// single replies capture their own route inside MyMesh. Cancel only USB's
// ownership so a simultaneous BLE/WiFi operation keeps running.
if (interface_manager.isReplyRouteFor(&usb_serial_interface)) {
the_mesh.cancelSerialResponseStream();
}
// Contact streams and delayed replies capture their own destinations.
// Cancel only USB's ownership so simultaneous BLE/WiFi work keeps running.
the_mesh.cancelSerialResponseStream(&usb_serial_interface);
the_mesh.cancelSerialOperationsForRoute(&usb_serial_interface);
interface_manager.forgetReplyRouteForDisconnected(&usb_serial_interface);
}
@@ -2308,9 +2303,7 @@ void halt() {
}
static void cancelCompanionWiFiSession(void*) {
if (interface_manager.isReplyRouteFor(&wifi_interface)) {
the_mesh.cancelSerialResponseStream();
}
the_mesh.cancelSerialResponseStream(&wifi_interface);
the_mesh.cancelSerialOperationsForRoute(&wifi_interface);
interface_manager.forgetReplyRouteForDisconnected(&wifi_interface);
}
@@ -2753,6 +2746,7 @@ static bool hasCompanionNonBluetoothClient() {
static void disableCompanionBluetoothForCli() {
companion_bluetooth_off_at = 0;
the_mesh.cancelSerialResponseStream(&bluetooth_interface);
the_mesh.cancelSerialOperationsForRoute(&bluetooth_interface);
interface_manager.disableBluetooth();
interface_manager.forgetReplyRouteForDisconnected(&bluetooth_interface);
@@ -3422,9 +3416,7 @@ void setup() {
// add usb interface
#if defined(RP2040_PLATFORM) && defined(ENABLE_WIFI_INTERFACE)
wifi_interface.setSessionChangedCallback([](void*) {
if (interface_manager.isReplyRouteFor(&wifi_interface)) {
the_mesh.cancelSerialResponseStream();
}
the_mesh.cancelSerialResponseStream(&wifi_interface);
the_mesh.cancelSerialOperationsForRoute(&wifi_interface);
interface_manager.forgetReplyRouteForDisconnected(&wifi_interface);
}, nullptr);