From 63ec27d6e2eea95a30ee58a8801e20d1c111fec5 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sat, 7 Feb 2026 23:28:25 +0000 Subject: [PATCH] ble: add bonding support --- src/ble.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 11 deletions(-) diff --git a/src/ble.c b/src/ble.c index 8c6ce77..ea201ba 100644 --- a/src/ble.c +++ b/src/ble.c @@ -5,10 +5,70 @@ #include "CH58x_sys.h" #include "CH58xBLE_LIB.h" #include "led.h" +#include "screen.h" #include "wang.h" + +// Digits are from Spleen, under BSD-3-Clause. +const uint8_t digit_font[10 * 4] = { + 0x1e,0x29,0x25,0x1e, + 0x00,0x22,0x3f,0x20, + 0x32,0x29,0x29,0x26, + 0x12,0x21,0x25,0x1a, + 0x0f,0x08,0x3e,0x08, + 0x27,0x25,0x25,0x19, + 0x1e,0x25,0x25,0x18, + 0x03,0x31,0x09,0x07, + 0x1a,0x25,0x25,0x1a, + 0x06,0x29,0x29,0x1e +}; + +static const uint16_t magic = 0x201; +static const uint16_t magic2 = 0x1FE; + +static uint32_t ble_pin = 0; + +static void ble_popup_render(struct row_buf *buf) { + uint16_t fb[44] = {0}; + int num = ble_pin; + + fb[6] = magic2; + fb[7] = magic; + fb[37] = magic; + fb[38] = magic2; + + for (int i = 5; i >= 0; i--) { + int x = 8 + (i * 5); + int digit = (num % 10); + + for (int j = 0; j < 4; j++) { + fb[x + j] = magic | (((uint16_t) digit_font[digit * 4 + j]) << 2); + } + + fb[x + 4] = magic; + + num = num / 10; + } + display_make_buf_all(fb, buf); +} + +static void ble_popup_event(struct screen_event event) { + if (event.type == SE_BUTTON_PRESS) { + screen_pop(); + } +} + +struct screen ble_screen = { + ble_popup_render, + ble_popup_event, +}; + int ble_on = 0; int ble_connected = 0; + +// Whether the device is bonded or was previously bonded and recovered the keys successfully. +int ble_paired = 0; + void ble_toggle(void) { uint8_t ble_mode = ble_on ? FALSE : TRUE; GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(ble_mode), &ble_mode); @@ -23,6 +83,27 @@ static void ble_calib_cb(void) { Calibration_LSI(Level_128); } + +static void passcode_callback(uint8_t *device_addr, uint16_t handle, uint8_t uiInputs, uint8_t uiOutputs) { + ble_pin = tmos_rand() % 1000000; + screen_dirty = 1; + + GAPBondMgr_PasscodeRsp(handle, SUCCESS, ble_pin); +} + +static void pair_state_callback(uint16_t handle, uint8_t state, uint8_t status) { + if (state == GAPBOND_PAIRING_STATE_STARTED) { + screen_push(&ble_screen); + } else { + if (current_screen == &ble_screen) screen_pop(); + } + + if (status != SUCCESS) return; + if (state != GAPBOND_PAIRING_STATE_BONDED && state != GAPBOND_PAIRING_STATE_COMPLETE) return; + + ble_paired = 1; +} + static uint8_t periph_task = INVALID_TASK_ID; static uint16_t peripheral_task(uint8_t task_id, uint16_t events) { @@ -41,7 +122,24 @@ static uint16_t peripheral_task(uint8_t task_id, uint16_t events) { static void gap_onParamUpdate(uint16_t a, uint16_t b, uint16_t c, uint16_t d) { } -static void gap_onStateChange(gapRole_States_t a, gapRoleEvent_t *b) { +static gapRole_States_t previous_state = GAPROLE_INIT; + +static void gap_onStateChange(gapRole_States_t new_state, gapRoleEvent_t *event) { + if (previous_state == GAPROLE_CONNECTED && (new_state & GAPROLE_STATE_ADV_MASK) != GAPROLE_CONNECTED) { + ble_paired = 0; + ble_connected = 0; + } + + if ((new_state & GAPROLE_STATE_ADV_MASK) == GAPROLE_ADVERTISING && event->gap.opcode == GAP_LINK_TERMINATED_EVENT) { + uint8_t ble_mode = TRUE; + GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(ble_mode), &ble_mode); + } + + if (new_state == GAPROLE_CONNECTED) { + ble_connected = 1; + } + + previous_state = new_state & GAPROLE_STATE_ADV_MASK; } uint8_t advertData[] = { @@ -85,8 +183,8 @@ static gapRolesCBs_t gap_handlers = { }; static gapBondCBs_t bond_managers = { - 0, - 0, + passcode_callback, + pair_state_callback, }; static const uint8_t service_uuid[2] = {LO_UINT16(0xFEE0), HI_UINT16(0xFEE0)}; @@ -102,10 +200,11 @@ static gattAttribute_t attr_table[] = { {{2, rx_char_uuid}, GATT_PERMIT_WRITE, 0, (uint8_t *)&rx_char_val}, }; -uint16_t blefb[44] = {0}; - static bStatus_t write_handler(uint16_t conn_handle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method) { - if (gattPermitAuthorWrite(pAttr->permissions)) return ATT_ERR_INSUFFICIENT_AUTHOR; + uint8_t bonded_count = 0; + GAPBondMgr_GetParameter(GAPBOND_BOND_COUNT, &bonded_count); + + if (gattPermitAuthorWrite(pAttr->permissions) || (bonded_count && !ble_paired)) return ATT_ERR_INSUFFICIENT_AUTHOR; uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]); if (uuid != 0xFEE1) return ATT_ERR_ATTR_NOT_FOUND; @@ -121,6 +220,17 @@ static gattServiceCBs_t service_handlers = { 0, }; +uint32_t ble_read_flash(uint32_t addr, uint32_t num, uint32_t *buf) { + EEPROM_READ(addr, buf, num * 4); + return 0; +} + +uint32_t ble_write_flash(uint32_t addr, uint32_t num, uint32_t *buf) { + EEPROM_ERASE(addr, num * 4); + EEPROM_WRITE(addr, buf, num * 4); + return 0; +} + void ble_init(void) { bleConfig_t cfg = {0}; @@ -128,8 +238,11 @@ void ble_init(void) { cfg.MEMAddr = (uint32_t) BLE_BUF; cfg.MEMLen = (uint32_t) sizeof(BLE_BUF); - // not using the bonding information, as we do not support _bonding_. - // This should be changed. but. you know. + cfg.SNVAddr = (uint32_t) EEPROM_MAX_SIZE - 0x200; + cfg.SNVBlock = 256; + cfg.SNVNum = 1; + cfg.readFlashCB = ble_read_flash; + cfg.writeFlashCB = ble_write_flash; // TODO: magic numbers // amount of buffered packets @@ -177,10 +290,10 @@ void ble_init(void) { GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, 200); SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_DEFAULT_PASSCODE, uint32_t, 0); - SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_PAIRING_MODE, uint8_t, GAPBOND_PAIRING_MODE_NO_PAIRING); - SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_MITM_PROTECTION, uint8_t, FALSE); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_PAIRING_MODE, uint8_t, GAPBOND_PAIRING_MODE_WAIT_FOR_REQ); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_MITM_PROTECTION, uint8_t, TRUE); SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_IO_CAPABILITIES, uint8_t, GAPBOND_IO_CAP_DISPLAY_ONLY); - SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_BONDING_ENABLED, uint8_t, FALSE); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_BONDING_ENABLED, uint8_t, TRUE); GAPRole_BroadcasterSetCB(&broadcast_handlers);