From 7384ef4c564d677138112fe6c2f105fa9e057250 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Mon, 1 Nov 2021 22:48:16 +0100 Subject: [PATCH] sd: handle system attibutes on reconnect --- adapter_nrf51.go | 33 +++++++++++++++++++++++---------- adapter_nrf528xx-full.go | 26 +++++++++++++++++--------- adapter_nrf528xx-peripheral.go | 27 ++++++++++++++++++--------- adapter_sd.go | 11 +++++++++++ 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/adapter_nrf51.go b/adapter_nrf51.go index 29cfb503..73923e9f 100644 --- a/adapter_nrf51.go +++ b/adapter_nrf51.go @@ -50,6 +50,19 @@ func handleEvent() { currentConnection.Reg = gapEvent.conn_handle DefaultAdapter.connectHandler(Address{}, true) case C.BLE_GAP_EVT_DISCONNECTED: + if debug { + println("evt: disconnected") + } + // Store system attributes data + dataLen := uint16(0) + C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, nil, &dataLen, 0) // get data length + if int(dataLen) <= cap(DefaultAdapter.systemAttributes) { // we can not allocate here, so ensure at least data fits the buffer + DefaultAdapter.systemAttributes = DefaultAdapter.systemAttributes[:dataLen] + C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, &DefaultAdapter.systemAttributes[0], &dataLen, 0) + } + // Clean up state for this connection. + currentConnection.Reg = C.BLE_CONN_HANDLE_INVALID + // Auto-restart advertisement if needed. if defaultAdvertisement.isAdvertising.Get() != 0 { // The advertisement was running but was automatically stopped // by the connection event. @@ -59,7 +72,6 @@ func handleEvent() { // necessary. defaultAdvertisement.start() } - currentConnection.Reg = C.BLE_CONN_HANDLE_INVALID DefaultAdapter.connectHandler(Address{}, false) case C.BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: // Respond with the default PPCP connection parameters by passing @@ -87,15 +99,16 @@ func handleEvent() { handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data) } case C.BLE_GATTS_EVT_SYS_ATTR_MISSING: - // This event is generated when reading the Generic Attribute - // service. It appears to be necessary for bonded devices. - // From the docs: - // > If the pointer is NULL, the system attribute info is - // > initialized, assuming that the application does not have any - // > previously saved system attribute data for this device. - // Maybe we should look at the error, but as there's not really a - // way to handle it, ignore it. - C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0) + if debug { + println("evt: sys attr missing") + } + // Try and restore system attributes data if we have any (from previous connections) + // Fallback to initialize them from scratch otherwise + if len(DefaultAdapter.systemAttributes) > 0 { + C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, &DefaultAdapter.systemAttributes[0], uint16(len(DefaultAdapter.systemAttributes)), 0) + } else { + C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0) + } default: if debug { println("unknown GATTS event:", id, id-C.BLE_GATTS_EVT_BASE) diff --git a/adapter_nrf528xx-full.go b/adapter_nrf528xx-full.go index a0be0a9a..fbe3bb42 100644 --- a/adapter_nrf528xx-full.go +++ b/adapter_nrf528xx-full.go @@ -49,6 +49,13 @@ func handleEvent() { if debug { println("evt: disconnected") } + // Store system attributes data + dataLen := uint16(0) + C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, nil, &dataLen, 0) // get data length + if int(dataLen) <= cap(DefaultAdapter.systemAttributes) { // we can not allocate here, so ensure at least data fits the buffer + DefaultAdapter.systemAttributes = DefaultAdapter.systemAttributes[:dataLen] + C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, &DefaultAdapter.systemAttributes[0], &dataLen, 0) + } // Clean up state for this connection. for i, cb := range gattcNotificationCallbacks { if cb.connectionHandle == currentConnection.Reg { @@ -116,15 +123,16 @@ func handleEvent() { handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data) } case C.BLE_GATTS_EVT_SYS_ATTR_MISSING: - // This event is generated when reading the Generic Attribute - // service. It appears to be necessary for bonded devices. - // From the docs: - // > If the pointer is NULL, the system attribute info is - // > initialized, assuming that the application does not have any - // > previously saved system attribute data for this device. - // Maybe we should look at the error, but as there's not really a - // way to handle it, ignore it. - C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0) + if debug { + println("evt: sys attr missing") + } + // Try and restore system attributes data if we have any (from previous connections) + // Fallback to initialize them from scratch otherwise + if len(DefaultAdapter.systemAttributes) > 0 { + C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, &DefaultAdapter.systemAttributes[0], uint16(len(DefaultAdapter.systemAttributes)), 0) + } else { + C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0) + } case C.BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: // This event is generated by some devices. While we could support // larger MTUs, this default MTU is supported everywhere. diff --git a/adapter_nrf528xx-peripheral.go b/adapter_nrf528xx-peripheral.go index 84e7c578..78ea2cf2 100644 --- a/adapter_nrf528xx-peripheral.go +++ b/adapter_nrf528xx-peripheral.go @@ -38,6 +38,14 @@ func handleEvent() { if debug { println("evt: disconnected") } + // Store system attributes data + dataLen := uint16(0) + C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, nil, &dataLen, 0) // get data length + if int(dataLen) <= cap(DefaultAdapter.systemAttributes) { // we can not allocate here, so ensure at least data fits the buffer + DefaultAdapter.systemAttributes = DefaultAdapter.systemAttributes[:dataLen] + C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, &DefaultAdapter.systemAttributes[0], &dataLen, 0) + } + // Clean up state for this connection. currentConnection.Reg = C.BLE_CONN_HANDLE_INVALID // Auto-restart advertisement if needed. if defaultAdvertisement.isAdvertising.Get() != 0 { @@ -71,15 +79,16 @@ func handleEvent() { handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data) } case C.BLE_GATTS_EVT_SYS_ATTR_MISSING: - // This event is generated when reading the Generic Attribute - // service. It appears to be necessary for bonded devices. - // From the docs: - // > If the pointer is NULL, the system attribute info is - // > initialized, assuming that the application does not have any - // > previously saved system attribute data for this device. - // Maybe we should look at the error, but as there's not really a - // way to handle it, ignore it. - C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0) + if debug { + println("evt: sys attr missing") + } + // Try and restore system attributes data if we have any (from previous connections) + // Fallback to initialize them from scratch otherwise + if len(DefaultAdapter.systemAttributes) > 0 { + C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, &DefaultAdapter.systemAttributes[0], uint16(len(DefaultAdapter.systemAttributes)), 0) + } else { + C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0) + } case C.BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: // This event is generated by some devices. While we could support // larger MTUs, this default MTU is supported everywhere. diff --git a/adapter_sd.go b/adapter_sd.go index 6f2bd03b..86744a0b 100644 --- a/adapter_sd.go +++ b/adapter_sd.go @@ -50,6 +50,16 @@ type Adapter struct { charWriteHandlers []charWriteHandler connectHandler func(device Address, connected bool) + + // System attributes in context of SoftDevice primarily mean Client Characteristic Configuration Descriptors (CCCD). + // It is mandated by Bluetooth specification that CCCD values for a bonded peer should be stored between connections. + // + // Our bluetooth stack stores system attributes on disconnect + // and provides them back to SoftDevice on BLE_GATTS_EVT_SYS_ATTR_MISSING event. + // + // Note: CCCD values can be altered only by the peer, so you cannot change them from the application. + // Treat this data as a blob of unknown format, i.e. store it as is and provide it back as is, without changing it. + systemAttributes []byte } // DefaultAdapter is the default adapter on the current system. On Nordic chips, @@ -57,6 +67,7 @@ type Adapter struct { // // Make sure to call Enable() before using it to initialize the adapter. var DefaultAdapter = &Adapter{isDefault: true, + systemAttributes: make([]byte, 64)[:0], // capacity 64, length 0 connectHandler: func(device Address, connected bool) { return }}