mirror of https://github.com/meshcore-dev/MeshCore
Browse Source
in preparation to switch to using our own fork of Adafruit NRF52 Arduino with the patch includedpull/2430/head
2 changed files with 1 additions and 201 deletions
@ -1,198 +0,0 @@ |
|||||
""" |
|
||||
Bluefruit BLE Patch Script |
|
||||
|
|
||||
Patches Bluefruit library to fix semaphore leak bug that causes device lockup |
|
||||
when BLE central disconnects unexpectedly (e.g., going out of range, supervision timeout). |
|
||||
|
|
||||
Patches applied: |
|
||||
1. BLEConnection.h: Add _hvn_qsize member to track semaphore queue size |
|
||||
2. BLEConnection.cpp: Store hvn_qsize and restore semaphore on disconnect |
|
||||
|
|
||||
Bug description: |
|
||||
- When a BLE central disconnects unexpectedly (reason=8 supervision timeout), |
|
||||
the BLE_GATTS_EVT_HVN_TX_COMPLETE event may never fire |
|
||||
- This leaves the _hvn_sem counting semaphore in a decremented state |
|
||||
- Since BLEConnection objects are reused (destructor never called), the |
|
||||
semaphore count is never restored |
|
||||
- Eventually all semaphore counts are exhausted and notify() blocks/fails |
|
||||
|
|
||||
""" |
|
||||
|
|
||||
from pathlib import Path |
|
||||
|
|
||||
Import("env") # pylint: disable=undefined-variable |
|
||||
|
|
||||
|
|
||||
def _patch_ble_connection_header(source: Path) -> bool: |
|
||||
""" |
|
||||
Add _hvn_qsize member variable to BLEConnection class. |
|
||||
|
|
||||
This is needed to restore the semaphore to its correct count on disconnect. |
|
||||
|
|
||||
Returns True if patch was applied or already applied, False on error. |
|
||||
""" |
|
||||
try: |
|
||||
content = source.read_text() |
|
||||
|
|
||||
# Check if already patched |
|
||||
if "_hvn_qsize" in content: |
|
||||
return True # Already patched |
|
||||
|
|
||||
# Find the location to insert - after _phy declaration |
|
||||
original_pattern = ''' uint8_t _phy; |
|
||||
|
|
||||
uint8_t _role;''' |
|
||||
|
|
||||
patched_pattern = ''' uint8_t _phy; |
|
||||
uint8_t _hvn_qsize; |
|
||||
|
|
||||
uint8_t _role;''' |
|
||||
|
|
||||
if original_pattern not in content: |
|
||||
print("Bluefruit patch: WARNING - BLEConnection.h pattern not found") |
|
||||
return False |
|
||||
|
|
||||
content = content.replace(original_pattern, patched_pattern) |
|
||||
source.write_text(content) |
|
||||
|
|
||||
# Verify |
|
||||
if "_hvn_qsize" not in source.read_text(): |
|
||||
return False |
|
||||
|
|
||||
return True |
|
||||
except Exception as e: |
|
||||
print(f"Bluefruit patch: ERROR patching BLEConnection.h: {e}") |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def _patch_ble_connection_source(source: Path) -> bool: |
|
||||
""" |
|
||||
Patch BLEConnection.cpp to: |
|
||||
1. Store hvn_qsize in constructor |
|
||||
2. Restore _hvn_sem semaphore to full count on disconnect |
|
||||
|
|
||||
Returns True if patch was applied or already applied, False on error. |
|
||||
""" |
|
||||
try: |
|
||||
content = source.read_text() |
|
||||
|
|
||||
# Check if already patched (look for the restore loop) |
|
||||
if "uxSemaphoreGetCount(_hvn_sem)" in content: |
|
||||
return True # Already patched |
|
||||
|
|
||||
# Patch 1: Store queue size in constructor |
|
||||
constructor_original = ''' _hvn_sem = xSemaphoreCreateCounting(hvn_qsize, hvn_qsize);''' |
|
||||
|
|
||||
constructor_patched = ''' _hvn_qsize = hvn_qsize; |
|
||||
_hvn_sem = xSemaphoreCreateCounting(hvn_qsize, hvn_qsize);''' |
|
||||
|
|
||||
if constructor_original not in content: |
|
||||
print("Bluefruit patch: WARNING - BLEConnection.cpp constructor pattern not found") |
|
||||
return False |
|
||||
|
|
||||
content = content.replace(constructor_original, constructor_patched) |
|
||||
|
|
||||
# Patch 2: Restore semaphore on disconnect |
|
||||
disconnect_original = ''' case BLE_GAP_EVT_DISCONNECTED: |
|
||||
// mark as disconnected |
|
||||
_connected = false; |
|
||||
break;''' |
|
||||
|
|
||||
disconnect_patched = ''' case BLE_GAP_EVT_DISCONNECTED: |
|
||||
// Restore notification semaphore to full count |
|
||||
// This fixes lockup when disconnect occurs with notifications in flight |
|
||||
while (uxSemaphoreGetCount(_hvn_sem) < _hvn_qsize) { |
|
||||
xSemaphoreGive(_hvn_sem); |
|
||||
} |
|
||||
// Release indication semaphore if waiting |
|
||||
if (_hvc_sem) { |
|
||||
_hvc_received = false; |
|
||||
xSemaphoreGive(_hvc_sem); |
|
||||
} |
|
||||
// mark as disconnected |
|
||||
_connected = false; |
|
||||
break;''' |
|
||||
|
|
||||
if disconnect_original not in content: |
|
||||
print("Bluefruit patch: WARNING - BLEConnection.cpp disconnect pattern not found") |
|
||||
return False |
|
||||
|
|
||||
content = content.replace(disconnect_original, disconnect_patched) |
|
||||
source.write_text(content) |
|
||||
|
|
||||
# Verify |
|
||||
verify_content = source.read_text() |
|
||||
if "uxSemaphoreGetCount(_hvn_sem)" not in verify_content: |
|
||||
return False |
|
||||
if "_hvn_qsize = hvn_qsize" not in verify_content: |
|
||||
return False |
|
||||
|
|
||||
return True |
|
||||
except Exception as e: |
|
||||
print(f"Bluefruit patch: ERROR patching BLEConnection.cpp: {e}") |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def _apply_bluefruit_patches(target, source, env): # pylint: disable=unused-argument |
|
||||
framework_path = env.get("PLATFORMFW_DIR") |
|
||||
if not framework_path: |
|
||||
framework_path = env.PioPlatform().get_package_dir("framework-arduinoadafruitnrf52") |
|
||||
|
|
||||
if not framework_path: |
|
||||
print("Bluefruit patch: ERROR - framework directory not found") |
|
||||
env.Exit(1) |
|
||||
return |
|
||||
|
|
||||
framework_dir = Path(framework_path) |
|
||||
bluefruit_lib = framework_dir / "libraries" / "Bluefruit52Lib" / "src" |
|
||||
patch_failed = False |
|
||||
|
|
||||
# Patch BLEConnection.h |
|
||||
conn_header = bluefruit_lib / "BLEConnection.h" |
|
||||
if conn_header.exists(): |
|
||||
before = conn_header.read_text() |
|
||||
success = _patch_ble_connection_header(conn_header) |
|
||||
after = conn_header.read_text() |
|
||||
|
|
||||
if success: |
|
||||
if before != after: |
|
||||
print("Bluefruit patch: OK - Applied BLEConnection.h fix (added _hvn_qsize member)") |
|
||||
else: |
|
||||
print("Bluefruit patch: OK - BLEConnection.h already patched") |
|
||||
else: |
|
||||
print("Bluefruit patch: FAILED - BLEConnection.h") |
|
||||
patch_failed = True |
|
||||
else: |
|
||||
print(f"Bluefruit patch: ERROR - BLEConnection.h not found at {conn_header}") |
|
||||
patch_failed = True |
|
||||
|
|
||||
# Patch BLEConnection.cpp |
|
||||
conn_source = bluefruit_lib / "BLEConnection.cpp" |
|
||||
if conn_source.exists(): |
|
||||
before = conn_source.read_text() |
|
||||
success = _patch_ble_connection_source(conn_source) |
|
||||
after = conn_source.read_text() |
|
||||
|
|
||||
if success: |
|
||||
if before != after: |
|
||||
print("Bluefruit patch: OK - Applied BLEConnection.cpp fix (restore semaphore on disconnect)") |
|
||||
else: |
|
||||
print("Bluefruit patch: OK - BLEConnection.cpp already patched") |
|
||||
else: |
|
||||
print("Bluefruit patch: FAILED - BLEConnection.cpp") |
|
||||
patch_failed = True |
|
||||
else: |
|
||||
print(f"Bluefruit patch: ERROR - BLEConnection.cpp not found at {conn_source}") |
|
||||
patch_failed = True |
|
||||
|
|
||||
if patch_failed: |
|
||||
print("Bluefruit patch: CRITICAL - Patch failed! Build aborted.") |
|
||||
env.Exit(1) |
|
||||
|
|
||||
|
|
||||
# Register the patch to run before build |
|
||||
bluefruit_action = env.VerboseAction(_apply_bluefruit_patches, "Applying Bluefruit BLE patches...") |
|
||||
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", bluefruit_action) |
|
||||
|
|
||||
# Also run immediately to patch before any compilation |
|
||||
_apply_bluefruit_patches(None, None, env) |
|
||||
Loading…
Reference in new issue