Skip to content

Commit bb87677

Browse files
aykevldeadprogram
authored andcommitted
nrf: add support for S113 SoftDevice
This SoftDevice is used by default on the BBC micro:bit v2 so it's a good idea to add support here. Unfortunately this SoftDevice does not support scanning and connecting to other devices. This means that I unfortunately had to duplicate the event handler. I managed to refactor most other code to avoid duplicating much more. (This is when macros would have been useful in Go...)
1 parent 340f698 commit bb87677

29 files changed

+15754
-280
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ smoketest-tinygo:
2828
@md5sum test.hex
2929
$(TINYGO) build -o test.hex -size=short -target=microbit-s110v8 ./examples/nusserver
3030
@md5sum test.hex
31+
$(TINYGO) build -o test.hex -size=short -target=microbit-v2-s113v7 ./examples/nusserver
32+
@md5sum test.hex
3133

3234
smoketest-linux:
3335
# Test on Linux.

adapter_nrf528xx-full.go

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
// +build softdevice,s132v6 softdevice,s140v6 softdevice,s140v7
2+
3+
package bluetooth
4+
5+
// This file implements the event handler for SoftDevices with full support:
6+
// both central and peripheral mode. This includes S132 and S140.
7+
8+
/*
9+
// Define SoftDevice functions as regular function declarations (not inline
10+
// static functions).
11+
#define SVCALL_AS_NORMAL_FUNCTION
12+
13+
#include "nrf_sdm.h"
14+
#include "nrf_nvic.h"
15+
#include "ble.h"
16+
#include "ble_gap.h"
17+
*/
18+
import "C"
19+
20+
import (
21+
"unsafe"
22+
)
23+
24+
func handleEvent() {
25+
id := eventBuf.header.evt_id
26+
switch {
27+
case id >= C.BLE_GAP_EVT_BASE && id <= C.BLE_GAP_EVT_LAST:
28+
gapEvent := eventBuf.evt.unionfield_gap_evt()
29+
switch id {
30+
case C.BLE_GAP_EVT_CONNECTED:
31+
connectEvent := gapEvent.params.unionfield_connected()
32+
switch connectEvent.role {
33+
case C.BLE_GAP_ROLE_PERIPH:
34+
if debug {
35+
println("evt: connected in peripheral role")
36+
}
37+
currentConnection.Reg = gapEvent.conn_handle
38+
DefaultAdapter.connectHandler(nil, true)
39+
case C.BLE_GAP_ROLE_CENTRAL:
40+
if debug {
41+
println("evt: connected in central role")
42+
}
43+
connectionAttempt.connectionHandle = gapEvent.conn_handle
44+
connectionAttempt.state.Set(2) // connection was successful
45+
DefaultAdapter.connectHandler(nil, true)
46+
}
47+
case C.BLE_GAP_EVT_DISCONNECTED:
48+
if debug {
49+
println("evt: disconnected")
50+
}
51+
// Clean up state for this connection.
52+
for i, cb := range gattcNotificationCallbacks {
53+
if cb.connectionHandle == currentConnection.Reg {
54+
gattcNotificationCallbacks[i].valueHandle = 0 // 0 means invalid
55+
}
56+
}
57+
currentConnection.Reg = C.BLE_CONN_HANDLE_INVALID
58+
// Auto-restart advertisement if needed.
59+
if defaultAdvertisement.isAdvertising.Get() != 0 {
60+
// The advertisement was running but was automatically stopped
61+
// by the connection event.
62+
// Note that it cannot be restarted during connect like this,
63+
// because it would need to be reconfigured as a non-connectable
64+
// advertisement. That's left as a future addition, if
65+
// necessary.
66+
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
67+
}
68+
DefaultAdapter.connectHandler(nil, false)
69+
case C.BLE_GAP_EVT_ADV_REPORT:
70+
advReport := gapEvent.params.unionfield_adv_report()
71+
if debug && &scanReportBuffer.data[0] != advReport.data.p_data {
72+
// Sanity check.
73+
panic("scanReportBuffer != advReport.p_data")
74+
}
75+
// Prepare the globalScanResult, which will be passed to the
76+
// callback.
77+
scanReportBuffer.len = byte(advReport.data.len)
78+
globalScanResult.RSSI = int16(advReport.rssi)
79+
globalScanResult.Address = Address{
80+
MACAddress{MAC: advReport.peer_addr.addr,
81+
isRandom: advReport.peer_addr.bitfield_addr_type() != 0},
82+
}
83+
globalScanResult.AdvertisementPayload = &scanReportBuffer
84+
// Signal to the main thread that there was a scan report.
85+
// Scanning will be resumed (from the main thread) once the scan
86+
// report has been processed.
87+
gotScanReport.Set(1)
88+
case C.BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
89+
// Respond with the default PPCP connection parameters by passing
90+
// nil:
91+
// > If NULL is provided on a peripheral role, the parameters in the
92+
// > PPCP characteristic of the GAP service will be used instead. If
93+
// > NULL is provided on a central role and in response to a
94+
// > BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST, the peripheral request
95+
// > will be rejected
96+
C.sd_ble_gap_conn_param_update(gapEvent.conn_handle, nil)
97+
case C.BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
98+
// We need to respond with sd_ble_gap_data_length_update. Setting
99+
// both parameters to nil will make sure we send the default values.
100+
C.sd_ble_gap_data_length_update(gapEvent.conn_handle, nil, nil)
101+
default:
102+
if debug {
103+
println("unknown GAP event:", id)
104+
}
105+
}
106+
case id >= C.BLE_GATTS_EVT_BASE && id <= C.BLE_GATTS_EVT_LAST:
107+
gattsEvent := eventBuf.evt.unionfield_gatts_evt()
108+
switch id {
109+
case C.BLE_GATTS_EVT_WRITE:
110+
writeEvent := gattsEvent.params.unionfield_write()
111+
len := writeEvent.len - writeEvent.offset
112+
data := (*[255]byte)(unsafe.Pointer(&writeEvent.data[0]))[:len:len]
113+
handler := DefaultAdapter.getCharWriteHandler(writeEvent.handle)
114+
if handler != nil {
115+
handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data)
116+
}
117+
case C.BLE_GATTS_EVT_SYS_ATTR_MISSING:
118+
// This event is generated when reading the Generic Attribute
119+
// service. It appears to be necessary for bonded devices.
120+
// From the docs:
121+
// > If the pointer is NULL, the system attribute info is
122+
// > initialized, assuming that the application does not have any
123+
// > previously saved system attribute data for this device.
124+
// Maybe we should look at the error, but as there's not really a
125+
// way to handle it, ignore it.
126+
C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0)
127+
case C.BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
128+
// This event is generated by some devices. While we could support
129+
// larger MTUs, this default MTU is supported everywhere.
130+
C.sd_ble_gatts_exchange_mtu_reply(gattsEvent.conn_handle, C.BLE_GATT_ATT_MTU_DEFAULT)
131+
default:
132+
if debug {
133+
println("unknown GATTS event:", id, id-C.BLE_GATTS_EVT_BASE)
134+
}
135+
}
136+
case id >= C.BLE_GATTC_EVT_BASE && id <= C.BLE_GATTC_EVT_LAST:
137+
gattcEvent := eventBuf.evt.unionfield_gattc_evt()
138+
switch id {
139+
case C.BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
140+
discoveryEvent := gattcEvent.params.unionfield_prim_srvc_disc_rsp()
141+
if debug {
142+
println("evt: discovered primary service", discoveryEvent.count)
143+
}
144+
discoveringService.state.Set(2) // signal there is a result
145+
if discoveryEvent.count >= 1 {
146+
// Theoretically there may be more, but as we're only using
147+
// sd_ble_gattc_primary_services_discover, there should only be
148+
// one discovered service. Use the first as a sensible fallback.
149+
discoveringService.startHandle.Set(discoveryEvent.services[0].handle_range.start_handle)
150+
discoveringService.endHandle.Set(discoveryEvent.services[0].handle_range.end_handle)
151+
discoveringService.uuid = discoveryEvent.services[0].uuid
152+
} else {
153+
// No service found.
154+
discoveringService.startHandle.Set(0)
155+
}
156+
case C.BLE_GATTC_EVT_CHAR_DISC_RSP:
157+
discoveryEvent := gattcEvent.params.unionfield_char_disc_rsp()
158+
if debug {
159+
println("evt: discovered characteristics", discoveryEvent.count)
160+
}
161+
if discoveryEvent.count >= 1 {
162+
// There may be more, but for ease of implementing we only
163+
// handle the first.
164+
discoveringCharacteristic.handle_value.Set(discoveryEvent.chars[0].handle_value)
165+
discoveringCharacteristic.char_props = discoveryEvent.chars[0].char_props
166+
discoveringCharacteristic.uuid = discoveryEvent.chars[0].uuid
167+
} else {
168+
// zero indicates we received no characteristic, set handle_value to last
169+
discoveringCharacteristic.handle_value.Set(0xffff)
170+
}
171+
case C.BLE_GATTC_EVT_DESC_DISC_RSP:
172+
discoveryEvent := gattcEvent.params.unionfield_desc_disc_rsp()
173+
if debug {
174+
println("evt: discovered descriptors", discoveryEvent.count)
175+
}
176+
if discoveryEvent.count >= 1 {
177+
// There may be more, but for ease of implementing we only
178+
// handle the first.
179+
uuid := discoveryEvent.descs[0].uuid
180+
if uuid._type == C.BLE_UUID_TYPE_BLE && uuid.uuid == 0x2902 {
181+
// Found a CCCD (Client Characteristic Configuration
182+
// Descriptor), which has a 16-bit UUID with value 0x2902).
183+
discoveringCharacteristic.handle_value.Set(discoveryEvent.descs[0].handle)
184+
} else {
185+
// Found something else?
186+
// TODO: handle this properly by continuing the scan. For
187+
// now, give up if we found something other than a CCCD.
188+
if debug {
189+
println(" found some other descriptor (unimplemented)")
190+
}
191+
}
192+
}
193+
case C.BLE_GATTC_EVT_READ_RSP:
194+
readEvent := gattcEvent.params.unionfield_read_rsp()
195+
if debug {
196+
println("evt: read response, data length", readEvent.len)
197+
}
198+
readingCharacteristic.handle_value.Set(readEvent.handle)
199+
readingCharacteristic.offset = readEvent.offset
200+
readingCharacteristic.length = readEvent.len
201+
202+
// copy read event data into Go slice
203+
copy(readingCharacteristic.value, (*[255]byte)(unsafe.Pointer(&readEvent.data[0]))[:readEvent.len:readEvent.len])
204+
case C.BLE_GATTC_EVT_HVX:
205+
hvxEvent := gattcEvent.params.unionfield_hvx()
206+
switch hvxEvent._type {
207+
case C.BLE_GATT_HVX_NOTIFICATION:
208+
if debug {
209+
println("evt: notification", hvxEvent.handle)
210+
}
211+
// Find the callback and call it (if there is any).
212+
for _, callbackInfo := range gattcNotificationCallbacks {
213+
if callbackInfo.valueHandle == hvxEvent.handle && callbackInfo.connectionHandle == gattcEvent.conn_handle {
214+
// Create a Go slice from the data, to pass to the
215+
// callback.
216+
data := (*[255]byte)(unsafe.Pointer(&hvxEvent.data[0]))[:hvxEvent.len:hvxEvent.len]
217+
if callbackInfo.callback != nil {
218+
callbackInfo.callback(data)
219+
}
220+
break
221+
}
222+
}
223+
}
224+
default:
225+
if debug {
226+
println("unknown GATTC event:", id, id-C.BLE_GATTC_EVT_BASE)
227+
}
228+
}
229+
default:
230+
if debug {
231+
println("unknown event:", id)
232+
}
233+
}
234+
}

adapter_nrf528xx-peripheral.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// +build softdevice,s113v7
2+
3+
package bluetooth
4+
5+
// This file implements the event handler for SoftDevices with only peripheral
6+
// mode support. This includes the S113.
7+
8+
/*
9+
// Define SoftDevice functions as regular function declarations (not inline
10+
// static functions).
11+
#define SVCALL_AS_NORMAL_FUNCTION
12+
13+
#include "nrf_sdm.h"
14+
#include "nrf_nvic.h"
15+
#include "ble.h"
16+
#include "ble_gap.h"
17+
*/
18+
import "C"
19+
20+
import (
21+
"unsafe"
22+
)
23+
24+
func handleEvent() {
25+
id := eventBuf.header.evt_id
26+
switch {
27+
case id >= C.BLE_GAP_EVT_BASE && id <= C.BLE_GAP_EVT_LAST:
28+
gapEvent := eventBuf.evt.unionfield_gap_evt()
29+
switch id {
30+
case C.BLE_GAP_EVT_CONNECTED:
31+
if debug {
32+
println("evt: connected in peripheral role")
33+
}
34+
currentConnection.Reg = gapEvent.conn_handle
35+
DefaultAdapter.connectHandler(nil, true)
36+
case C.BLE_GAP_EVT_DISCONNECTED:
37+
if debug {
38+
println("evt: disconnected")
39+
}
40+
currentConnection.Reg = C.BLE_CONN_HANDLE_INVALID
41+
// Auto-restart advertisement if needed.
42+
if defaultAdvertisement.isAdvertising.Get() != 0 {
43+
// The advertisement was running but was automatically stopped
44+
// by the connection event.
45+
// Note that it cannot be restarted during connect like this,
46+
// because it would need to be reconfigured as a non-connectable
47+
// advertisement. That's left as a future addition, if
48+
// necessary.
49+
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
50+
}
51+
DefaultAdapter.connectHandler(nil, false)
52+
case C.BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
53+
// We need to respond with sd_ble_gap_data_length_update. Setting
54+
// both parameters to nil will make sure we send the default values.
55+
C.sd_ble_gap_data_length_update(gapEvent.conn_handle, nil, nil)
56+
default:
57+
if debug {
58+
println("unknown GAP event:", id)
59+
}
60+
}
61+
case id >= C.BLE_GATTS_EVT_BASE && id <= C.BLE_GATTS_EVT_LAST:
62+
gattsEvent := eventBuf.evt.unionfield_gatts_evt()
63+
switch id {
64+
case C.BLE_GATTS_EVT_WRITE:
65+
writeEvent := gattsEvent.params.unionfield_write()
66+
len := writeEvent.len - writeEvent.offset
67+
data := (*[255]byte)(unsafe.Pointer(&writeEvent.data[0]))[:len:len]
68+
handler := DefaultAdapter.getCharWriteHandler(writeEvent.handle)
69+
if handler != nil {
70+
handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data)
71+
}
72+
case C.BLE_GATTS_EVT_SYS_ATTR_MISSING:
73+
// This event is generated when reading the Generic Attribute
74+
// service. It appears to be necessary for bonded devices.
75+
// From the docs:
76+
// > If the pointer is NULL, the system attribute info is
77+
// > initialized, assuming that the application does not have any
78+
// > previously saved system attribute data for this device.
79+
// Maybe we should look at the error, but as there's not really a
80+
// way to handle it, ignore it.
81+
C.sd_ble_gatts_sys_attr_set(gattsEvent.conn_handle, nil, 0, 0)
82+
case C.BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
83+
// This event is generated by some devices. While we could support
84+
// larger MTUs, this default MTU is supported everywhere.
85+
C.sd_ble_gatts_exchange_mtu_reply(gattsEvent.conn_handle, C.BLE_GATT_ATT_MTU_DEFAULT)
86+
default:
87+
if debug {
88+
println("unknown GATTS event:", id, id-C.BLE_GATTS_EVT_BASE)
89+
}
90+
}
91+
default:
92+
if debug {
93+
println("unknown event:", id)
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)