diff --git a/modules/network/ble/nimble/modBLEServer.c b/modules/network/ble/nimble/modBLEServer.c index e360e3a5..fcbbf765 100644 --- a/modules/network/ble/nimble/modBLEServer.c +++ b/modules/network/ble/nimble/modBLEServer.c @@ -2,17 +2,17 @@ * Copyright (c) 2016-2022 Moddable Tech, Inc. * * This file is part of the Moddable SDK Runtime. - * + * * The Moddable SDK Runtime is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * The Moddable SDK Runtime is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with the Moddable SDK Runtime. If not, see . * @@ -61,24 +61,24 @@ typedef struct { xsMachine *the; xsSlot obj; - + // server ble_addr_t bda; - + // services uint8_t deployServices; uint16_t handles[service_count][max_attribute_count]; - + // requests uint8_t requestPending; void *requestResult; - + // security uint8_t encryption; uint8_t bonding; uint8_t mitm; uint8_t iocap; - + // connection int16_t conn_id; ble_addr_t remote_bda; @@ -133,7 +133,7 @@ void xs_ble_server_initialize(xsMachine *the) gBLE->obj = xsThis; gBLE->conn_id = -1; xsRemember(gBLE->obj); - + xsmcVars(1); if (xsmcHas(xsArg(0), xsID_deployServices)) { xsmcGet(xsVar(0), xsArg(0), xsID_deployServices); @@ -155,8 +155,6 @@ void xs_ble_server_close(xsMachine *the) { modBLE ble = xsmcGetHostData(xsThis); if (!ble) return; - - gBLE = NULL; xsForget(ble->obj); xsmcSetHostData(xsThis, NULL); modMessagePostToMachine(ble->the, NULL, 0, bleServerCloseEvent, ble); @@ -165,8 +163,10 @@ void xs_ble_server_close(xsMachine *the) void xs_ble_server_destructor(void *data) { modBLE ble = data; + if (!ble) return; - + xsMachine *the = ble->the; + if (-1 != ble->conn_id) { modBLEConnection connection = modBLEConnectionFindByConnectionID(ble->conn_id); if (NULL != connection) @@ -176,7 +176,7 @@ void xs_ble_server_destructor(void *data) ble_gatts_reset(); c_free(ble); gBLE = NULL; - + modBLEPlatformTerminate(); } @@ -207,7 +207,7 @@ void xs_ble_server_start_advertising(xsMachine *the) uint8_t *scanResponseData = xsmcTest(xsArg(5)) ? (uint8_t*)xsmcToArrayBuffer(xsArg(5)) : NULL; uint32_t scanResponseDataLength = xsmcTest(xsArg(5)) ? xsmcGetArrayBufferLength(xsArg(5)) : 0; struct ble_gap_adv_params adv_params; - + switch(filterPolicy) { case kBLEAdvFilterPolicyWhitelistScans: filterPolicy = BLE_HCI_ADV_FILT_SCAN; @@ -235,7 +235,7 @@ void xs_ble_server_start_advertising(xsMachine *the) ble_gap_adv_rsp_set_data(scanResponseData, scanResponseDataLength); ble_gap_adv_start(gBLE->bda.type, NULL, BLE_HS_FOREVER, &adv_params, nimble_gap_event, NULL); } - + void xs_ble_server_stop_advertising(xsMachine *the) { ble_gap_adv_stop(); @@ -260,7 +260,7 @@ void xs_ble_server_set_security_parameters(xsMachine *the) uint8_t bonding = xsmcToBoolean(xsArg(1)); uint8_t mitm = xsmcToBoolean(xsArg(2)); uint8_t iocap = xsmcToInteger(xsArg(3)); - + gBLE->encryption = encryption; gBLE->bonding = bonding; gBLE->mitm = mitm; @@ -314,11 +314,11 @@ static void deployServices(xsMachine *the) if (0 == service_count) return; - + ctxt.chr = &chr_def; - + // Set device name and appearance from app GAP service when available - + for (int service_index = 0; service_index < service_count; ++service_index) { const struct ble_gatt_svc_def *service = &gatt_svr_svcs[service_index]; if (0 == ble_uuid_cmp((const ble_uuid_t*)service->uuid, &BT_UUID_GAP.u)) { @@ -349,7 +349,7 @@ static void deployServices(xsMachine *the) break; } } - + int rc = ble_gatts_reset(); if (0 == rc) @@ -367,7 +367,7 @@ static void deployServices(xsMachine *the) else ble_svc_gap_device_name_set(DEVICE_FRIENDLY_NAME); } - + if (0 != rc) xsUnknownError("failed to start services"); } @@ -375,9 +375,9 @@ static void deployServices(xsMachine *the) void modBLEServerBondingRemoved(char *address, uint8_t addressType) { ble_addr_t addr; - + if (!gBLE) return; - + addr.type = addressType; c_memmove(addr.val, address, 6); modMessagePostToMachine(gBLE->the, (void*)&addr, sizeof(addr), bondingRemovedEvent, NULL); @@ -386,7 +386,7 @@ void modBLEServerBondingRemoved(char *address, uint8_t addressType) static void readyEvent(void *the, void *refcon, uint8_t *message, uint16_t messageLength) { if (!gBLE) return; - + ble_hs_util_ensure_addr(0); ble_hs_id_infer_auto(0, &gBLE->bda.type); ble_hs_id_copy_addr(gBLE->bda.type, gBLE->bda.val, NULL); @@ -402,10 +402,10 @@ static void readyEvent(void *the, void *refcon, uint8_t *message, uint16_t messa static void connectEvent(void *the, void *refcon, uint8_t *message, uint16_t messageLength) { struct ble_gap_conn_desc *desc = (struct ble_gap_conn_desc *)message; - + if (!gBLE) return; - - xsBeginHost(gBLE->the); + + xsBeginHost(gBLE->the); if (-1 != desc->conn_handle) { if (-1 != gBLE->conn_id) { LOG_GAP_MSG("Ignoring duplicate connect event"); @@ -413,11 +413,11 @@ static void connectEvent(void *the, void *refcon, uint8_t *message, uint16_t mes } gBLE->conn_id = desc->conn_handle; gBLE->remote_bda = desc->peer_id_addr; - + modBLEConnection connection = c_calloc(sizeof(modBLEConnectionRecord), 1); if (!connection) xsUnknownError("out of memory"); - + connection->id = gBLE->conn_id; connection->type = kBLEConnectionTypeServer; connection->addressType = gBLE->remote_bda.type; @@ -436,7 +436,7 @@ static void connectEvent(void *the, void *refcon, uint8_t *message, uint16_t mes xsmcSetInteger(xsVar(1), desc->peer_id_addr.type); xsmcSet(xsVar(0), xsID_addressType, xsVar(1)); xsCall2(gBLE->obj, xsID_callback, -1 != desc->conn_handle ? xsStringX("onConnected") : xsStringX("onDisconnected"), xsVar(0)); - + bail: xsEndHost(gBLE->the); } @@ -446,15 +446,15 @@ static void disconnectEvent(void *the, void *refcon, uint8_t *message, uint16_t struct ble_gap_conn_desc *desc = (struct ble_gap_conn_desc *)message; if (!gBLE) return; - + xsBeginHost(gBLE->the); - + // ignore multiple disconnects on same connection if (-1 == gBLE->conn_id) { LOG_GAP_MSG("Ignoring duplicate disconnect event"); goto bail; - } - + } + modBLEConnection connection = modBLEConnectionFindByConnectionID(gBLE->conn_id); if (NULL != connection) modBLEConnectionRemove(connection); @@ -479,7 +479,7 @@ static void writeEvent(void *the, void *refcon, uint8_t *message, uint16_t messa uint8_t buffer[16]; uint16_t length; const char_name_table *char_name; - + if (!gBLE) goto bail; char_name = handleToCharName(value->handle); @@ -504,7 +504,7 @@ static void writeEvent(void *the, void *refcon, uint8_t *message, uint16_t messa xsCall2(gBLE->obj, xsID_callback, xsStringX("onCharacteristicWritten"), xsVar(0)); xsEndHost(gBLE->the); - + bail: c_free(value); } @@ -517,7 +517,7 @@ static void readEvent(void *the, void *refcon, uint8_t *message, uint16_t messag const char_name_table *char_name; uint8_t buffer[16]; uint16_t uuid_length; - + if (!gBLE) return; char_name = handleToCharName(*chr->val_handle); @@ -559,13 +559,13 @@ static void notificationStateEvent(void *the, void *refcon, uint8_t *message, ui if (state->conn_id != gBLE->conn_id) goto bail; - + xsBeginHost(gBLE->the); uint16_t uuid_length; uint8_t buffer[16]; char_name_table *char_name = (char_name_table *)handleToCharName(state->handle); const ble_uuid16_t *uuid = handleToUUID(state->handle); - + xsmcVars(6); xsmcSetNewObject(xsVar(0)); uuidToBuffer(buffer, (ble_uuid_any_t *)uuid, &uuid_length); @@ -592,14 +592,14 @@ static void passkeyEvent(void *the, void *refcon, uint8_t *message, uint16_t mes { struct ble_gap_event *event = (struct ble_gap_event *)message; struct ble_sm_io pkey = {0}; - + if (!gBLE) return; - + pkey.action = event->passkey.params.action; - + if (event->passkey.conn_handle != gBLE->conn_id) xsUnknownError("connection not found"); - + xsBeginHost(gBLE->the); xsmcVars(3); xsmcSetNewObject(xsVar(0)); @@ -631,7 +631,7 @@ static void passkeyEvent(void *the, void *refcon, uint8_t *message, uint16_t mes xsmcSet(xsVar(0), xsID_passkey, xsVar(2)); xsCall2(gBLE->obj, xsID_callback, xsStringX("onPasskeyConfirm"), xsVar(0)); } - + bail: xsEndHost(gBLE->the); } @@ -646,14 +646,14 @@ static void bleServerCloseEvent(void *the, void *refcon, uint8_t *message, uint1 static void encryptionChangeEvent(void *the, void *refcon, uint8_t *message, uint16_t messageLength) { struct ble_gap_event *event = (struct ble_gap_event *)message; - + if (!gBLE) return; - - + + LOG_GAP_MSG("Encryption change status="); LOG_GAP_INT(event->enc_change.status); - + if (0 == event->enc_change.status) { struct ble_gap_conn_desc desc; int rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); @@ -674,13 +674,13 @@ static void encryptionChangeEvent(void *the, void *refcon, uint8_t *message, uin static void mtuExchangedEvent(void *the, void *refcon, uint8_t *message, uint16_t messageLength) { struct ble_gap_event *event = (struct ble_gap_event *)message; - + if (!gBLE) return; if (event->mtu.conn_handle != gBLE->conn_id) return; - + xsBeginHost(gBLE->the); xsmcSetInteger(xsResult, event->mtu.value); xsCall2(gBLE->obj, xsID_callback, xsStringX("onMTUExchanged"), xsResult); @@ -712,7 +712,7 @@ void ble_server_on_reset(int reason) void nimble_on_sync(void) { if (!gBLE) return; - + ble_hs_util_ensure_addr(0); modMessagePostToMachine(gBLE->the, NULL, 0, readyEvent, NULL); } @@ -720,11 +720,11 @@ void nimble_on_sync(void) static void nimble_on_register(struct ble_gatt_register_ctxt *ctxt, void *arg) { int service_index, att_index, char_index, dsc_index; - + if (!gBLE) return; switch (ctxt->op) { - case BLE_GATT_REGISTER_OP_CHR: + case BLE_GATT_REGISTER_OP_CHR: case BLE_GATT_REGISTER_OP_DSC: { const struct ble_gatt_svc_def *svc_def = (ctxt->op == BLE_GATT_REGISTER_OP_CHR) ? ctxt->chr.svc_def : ctxt->dsc.svc_def; const struct ble_gatt_chr_def *chr_def = ctxt->chr.chr_def; @@ -738,7 +738,10 @@ static void nimble_on_register(struct ble_gatt_register_ctxt *ctxt, void *arg) const struct ble_gatt_chr_def *characteristic = &service->characteristics[char_index]; while (characteristic->uuid != 0) { if (ctxt->op == BLE_GATT_REGISTER_OP_CHR && chr_def == characteristic) { + if (!gBLE) return; + modCriticalSectionBegin(); gBLE->handles[service_index][att_index] = ctxt->chr.val_handle; + modCriticalSectionEnd(); return; } ++att_index; @@ -748,7 +751,10 @@ static void nimble_on_register(struct ble_gatt_register_ctxt *ctxt, void *arg) const struct ble_gatt_dsc_def *descriptor = &characteristic->descriptors[dsc_index]; while (descriptor->uuid != 0) { if (ctxt->op == BLE_GATT_REGISTER_OP_DSC && dsc_def == descriptor) { + if (!gBLE) return; + modCriticalSectionBegin(); gBLE->handles[service_index][att_index] = ctxt->dsc.handle; + modCriticalSectionEnd(); return; } descriptor = &characteristic->descriptors[++dsc_index]; @@ -774,23 +780,33 @@ static int nimble_gap_event(struct ble_gap_event *event, void *arg) LOG_GAP_EVENT(event); if (!gBLE) goto bail; + modCriticalSectionBegin(); + xsMachine *the = gBLE->the; + modCriticalSectionEnd(); switch (event->type) { case BLE_GAP_EVENT_CONNECT: if (event->connect.status == 0) { if (0 == ble_gap_conn_find(event->connect.conn_handle, &desc)) { - if (gBLE->mitm || gBLE->encryption) + + if (!gBLE) goto bail; + modCriticalSectionBegin(); + uint8_t mitm = gBLE->mitm; + uint8_t encryption = gBLE->encryption; + modCriticalSectionEnd(); + + if (mitm || encryption) ble_gap_security_initiate(desc.conn_handle); - modMessagePostToMachine(gBLE->the, (uint8_t*)&desc, sizeof(desc), connectEvent, NULL); + modMessagePostToMachine(the, (uint8_t*)&desc, sizeof(desc), connectEvent, NULL); goto bail; } } c_memset(&desc, 0, sizeof(desc)); desc.conn_handle = -1; - modMessagePostToMachine(gBLE->the, (uint8_t*)&desc, sizeof(desc), connectEvent, NULL); + modMessagePostToMachine(the, (uint8_t*)&desc, sizeof(desc), connectEvent, NULL); break; case BLE_GAP_EVENT_DISCONNECT: - modMessagePostToMachine(gBLE->the, (uint8_t*)&event->disconnect.conn, sizeof(event->disconnect.conn), disconnectEvent, NULL); + modMessagePostToMachine(the, (uint8_t*)&event->disconnect.conn, sizeof(event->disconnect.conn), disconnectEvent, NULL); break; case BLE_GAP_EVENT_SUBSCRIBE: { notificationState state = c_malloc(sizeof(notificationStateRecord)); @@ -798,12 +814,12 @@ static int nimble_gap_event(struct ble_gap_event *event, void *arg) state->notify = event->subscribe.cur_notify; state->conn_id = event->subscribe.conn_handle; state->handle = event->subscribe.attr_handle; - modMessagePostToMachine(gBLE->the, NULL, 0, notificationStateEvent, state); + modMessagePostToMachine(the, NULL, 0, notificationStateEvent, state); } break; } case BLE_GAP_EVENT_ENC_CHANGE: - modMessagePostToMachine(gBLE->the, (uint8_t*)event, sizeof(struct ble_gap_event), encryptionChangeEvent, NULL); + modMessagePostToMachine(the, (uint8_t*)event, sizeof(struct ble_gap_event), encryptionChangeEvent, NULL); break; case BLE_GAP_EVENT_REPEAT_PAIRING: // delete old bond and accept new link @@ -812,15 +828,15 @@ static int nimble_gap_event(struct ble_gap_event *event, void *arg) return BLE_GAP_REPEAT_PAIRING_RETRY; break; case BLE_GAP_EVENT_PASSKEY_ACTION: - modMessagePostToMachine(gBLE->the, (uint8_t*)event, sizeof(struct ble_gap_event), passkeyEvent, NULL); + modMessagePostToMachine(the, (uint8_t*)event, sizeof(struct ble_gap_event), passkeyEvent, NULL); break; case BLE_GAP_EVENT_MTU: - modMessagePostToMachine(gBLE->the, (uint8_t*)event, sizeof(struct ble_gap_event), mtuExchangedEvent, NULL); + modMessagePostToMachine(the, (uint8_t*)event, sizeof(struct ble_gap_event), mtuExchangedEvent, NULL); break; default: break; } - + bail: return rc; } @@ -873,30 +889,52 @@ const ble_uuid16_t *handleToUUID(uint16_t handle) { return NULL; } +static uint8_t is_request_pending() +{ + uint8_t is_pending = true; + + if (!gBLE) return false; + modCriticalSectionBegin(); + is_pending = gBLE->requestPending; + modCriticalSectionEnd(); + + return is_pending; +} + int gatt_svr_chr_dynamic_value_access_cb(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { LOG_GATT_EVENT(ctxt->op); - + if (!gBLE) goto bail; + modCriticalSectionBegin(); + xsMachine * the = gBLE->the; + modCriticalSectionEnd(); switch (ctxt->op) { case BLE_GATT_ACCESS_OP_READ_DSC: case BLE_GATT_ACCESS_OP_READ_CHR: { // The read request must be satisfied from this task. + if (!gBLE) goto bail; + modCriticalSectionBegin(); gBLE->requestPending = true; gBLE->requestResult = NULL; + modCriticalSectionEnd(); #if !USE_EVENT_TIMER - modMessagePostToMachine(gBLE->the, NULL, 0, readEvent, (void*)ctxt); - while (gBLE->requestPending) { + modMessagePostToMachine(the, NULL, 0, readEvent, (void*)ctxt); + while (is_request_pending() == true) { modDelayMilliseconds(5); } #else - readEvent(gBLE->the, ctxt, NULL, 0); + readEvent(the, ctxt, NULL, 0); #endif - if (NULL == gBLE->requestResult) + if (!gBLE) goto bail; + modCriticalSectionBegin(); + void * result = gBLE->requestResult; + modCriticalSectionEnd(); + if (NULL == result) return BLE_ATT_ERR_INSUFFICIENT_RES; else { - readDataRequest data = (readDataRequest)gBLE->requestResult; + readDataRequest data = (readDataRequest)result; os_mbuf_append(ctxt->om, data->data, data->length); c_free(data); } @@ -915,7 +953,7 @@ int gatt_svr_chr_dynamic_value_access_cb(uint16_t conn_handle, uint16_t attr_han value->length = ctxt->om->om_len; c_memmove(value->data, ctxt->om->om_data, ctxt->om->om_len); ble_uuid_copy(&value->uuid, ctxt->chr->uuid); - modMessagePostToMachine(gBLE->the, NULL, 0, writeEvent, (void*)value); + modMessagePostToMachine(the, NULL, 0, writeEvent, (void*)value); } break; }