Export BLE peripheral internals (GAP + BLE glue) to apps

Add a data-driven firmware-symbol export mechanism (EXPORT_ENABLE list in
build.sh, applied by the api_symbols.csv editor) and use it to enable all
gap_* functions, the safe ble_glue_* status/lifecycle calls, and
furi_hal_bt_init. Apps can now drive GAP directly, run a custom GATT
server, set the advertised name / extra-beacon config, and read radio-stack
status. The coprocessor FUS stack delete/install calls stay disabled to
avoid erasing the radio firmware. 19 symbols newly exported; firmware links.
This commit is contained in:
2026-09-11 13:07:37 +01:00
parent 999b20f378
commit 9fbbd1aeda
2 changed files with 36 additions and 2 deletions
+14
View File
@@ -165,6 +165,20 @@ Three facts about the Flipper Zero drive the design:
`stm32wb5x_BLE_Stack_full_extended_fw.bin` (BLE 5 extended advertising, `stm32wb5x_BLE_Stack_full_extended_fw.bin` (BLE 5 extended advertising,
Coded PHY), which loads at 0x080C5000; it is not selected by this script. Coded PHY), which loads at 0x080C5000; it is not selected by this script.
### Extra firmware APIs exported to apps
Beyond the ST BLE commands, `build.sh` enables a set of firmware functions that
stock firmware keeps internal, so any `.fap` on this firmware can use them. The
set is a list of name patterns in `build.sh` (`EXPORT_ENABLE`); each enabled
symbol costs one API-table entry.
* **BLE peripheral internals** (`gap_*`, safe `ble_glue_*`, `furi_hal_bt_init`):
drive GAP directly, run a custom GATT server, change the advertised name and
the extra-beacon config, read connection state and radio-stack status,
without going through the firmware's serial profile. The coprocessor
firmware-update calls (`ble_glue_fus_stack_delete/install`) are deliberately
left disabled, since they can erase the radio stack.
`build.sh` also widens the GAP roles the firmware initialises `build.sh` also widens the GAP roles the firmware initialises
(`GAP_PERIPHERAL_ROLE | GAP_CENTRAL_ROLE | GAP_OBSERVER_ROLE`) in (`GAP_PERIPHERAL_ROLE | GAP_CENTRAL_ROLE | GAP_OBSERVER_ROLE`) in
`targets/f7/ble_glue/gap.c`. The app itself uses HCI-level scan and connect `targets/f7/ble_glue/gap.c`. The app itself uses HCI-level scan and connect
+22 -2
View File
@@ -101,13 +101,25 @@ API_CSV=targets/f7/api_symbols.csv
# all also gap, hal, l2cap (~20 KB) # all also gap, hal, l2cap (~20 KB)
# min only the functions BT Inspector imports (~1.5 KB) # min only the functions BT Inspector imports (~1.5 KB)
BLE_API="${BLE_API:-hci,gatt}" BLE_API="${BLE_API:-hci,gatt}"
# --- Firmware API exported to .fap apps (in addition to the ST BLE commands) ---
# Each area is appended below; every enabled symbol stays in the firmware image.
# BLE peripheral internals: GAP control (advertised name, custom GATT server,
# extra-beacon config, connection state) and BLE-glue radio-stack status.
EXPORT_ENABLE="${EXPORT_ENABLE:-}"
EXPORT_ENABLE+='
^gap_
^ble_glue_(start|stop|get_c2_status|get_hardfault_info|force_c2_mode)$
^furi_hal_bt_init$
'
export EXPORT_ENABLE
if grep -q "^Function,?," "$API_CSV" || ! grep -qE "^Header,\+,.*ble_hci_le\.h" "$API_CSV"; then if grep -q "^Function,?," "$API_CSV" || ! grep -qE "^Header,\+,.*ble_hci_le\.h" "$API_CSV"; then
# fbt notices the new headers, rewrites the csv with '?' entries and stops. # fbt notices the new headers, rewrites the csv with '?' entries and stops.
echo ">> syncing API symbol table (expected to stop once)" echo ">> syncing API symbol table (expected to stop once)"
./fbt -j"${JOBS:-4}" "${FBT_ARGS[@]}" api_check || true ./fbt -j"${JOBS:-4}" "${FBT_ARGS[@]}" api_check || true
fi fi
python3 - "$API_CSV" "$BLE_API" "$APP_SRC" <<'PY' python3 - "$API_CSV" "$BLE_API" "$APP_SRC" <<'PY'
import re, sys, glob import os, re, sys, glob
csv_path, mode, app_src = sys.argv[1:4] csv_path, mode, app_src = sys.argv[1:4]
hdrs = {"hci": "ble_hci_le", "gap": "ble_gap_aci", "gatt": "ble_gatt_aci", "hal": "ble_hal_aci", "l2cap": "ble_l2cap_aci"} hdrs = {"hci": "ble_hci_le", "gap": "ble_gap_aci", "gatt": "ble_gatt_aci", "hal": "ble_hal_aci", "l2cap": "ble_l2cap_aci"}
owner = {} owner = {}
@@ -124,17 +136,25 @@ elif mode == "min":
else: else:
want = set(mode.split(",")) want = set(mode.split(","))
selected = {fn for fn, key in owner.items() if key in want} selected = {fn for fn, key in owner.items() if key in want}
out, n_on = [], 0 # Firmware symbols to force-enable, one regex per line, from $EXPORT_ENABLE.
enable_res = [re.compile(p) for p in os.environ.get("EXPORT_ENABLE", "").split() if p]
out, n_on, n_fw = [], 0, 0
for line in open(csv_path).read().splitlines(): for line in open(csv_path).read().splitlines():
parts = line.split(",") parts = line.split(",")
if len(parts) >= 3 and parts[0] == "Function" and parts[2] in owner: if len(parts) >= 3 and parts[0] == "Function" and parts[2] in owner:
parts[1] = "+" if parts[2] in selected else "-" parts[1] = "+" if parts[2] in selected else "-"
n_on += parts[1] == "+" n_on += parts[1] == "+"
elif len(parts) >= 3 and parts[0] in ("Function", "Variable") and any(r.search(parts[2]) for r in enable_res):
if parts[1] != "+":
n_fw += 1
parts[1] = "+"
elif len(parts) >= 2 and parts[1] == "?": elif len(parts) >= 2 and parts[1] == "?":
parts[1] = "-" if parts[0] == "Variable" else "+" parts[1] = "-" if parts[0] == "Variable" else "+"
out.append(",".join(parts)) out.append(",".join(parts))
open(csv_path, "w").write("\n".join(out) + "\n") open(csv_path, "w").write("\n".join(out) + "\n")
print(f">> BLE API export ({mode}): {n_on} of {len(owner)} aci_*/hci_* functions enabled") print(f">> BLE API export ({mode}): {n_on} of {len(owner)} aci_*/hci_* functions enabled")
if enable_res:
print(f">> firmware API export: {n_fw} extra symbol(s) newly enabled")
PY PY
if [ "${1:-}" = "fap" ]; then if [ "${1:-}" = "fap" ]; then