prototooth/gap_nrf528xx.go
Ayke van Laethem f91f73ede2
Add S110 version 8.0.0 SoftDevice for nrf51822
The nrf51822 chip is still widely available, for example in the BBC
micro:bit. Therefore it's a good idea to support it too.

Unfortunately, Nordic decided to change the API in some significant ways
so many parts are not compatible between S110 for nrf51 and the other
nrf52* SoftDevices.
2020-05-31 15:40:57 +02:00

131 lines
3.7 KiB
Go

// +build softdevice,!s110v8
package bluetooth
import (
"device/arm"
"runtime/volatile"
)
/*
// Define SoftDevice functions as regular function declarations (not inline
// static functions).
#define SVCALL_AS_NORMAL_FUNCTION
#include "ble_gap.h"
*/
import "C"
// Memory buffers needed by sd_ble_gap_scan_start.
var (
scanReportBuffer rawAdvertisementPayload
gotScanReport volatile.Register8
globalScanResult ScanResult
)
// Advertisement encapsulates a single advertisement instance.
type Advertisement struct {
handle uint8
}
// NewAdvertisement creates a new advertisement instance but does not configure
// it. It can be called before the SoftDevice has been initialized.
func (a *Adapter) NewAdvertisement() *Advertisement {
return &Advertisement{
handle: C.BLE_GAP_ADV_SET_HANDLE_NOT_SET,
}
}
// Configure this advertisement. Must be called after SoftDevice initialization.
func (a *Advertisement) Configure(broadcastData, scanResponseData []byte, options *AdvertiseOptions) error {
data := C.ble_gap_adv_data_t{}
if broadcastData != nil {
data.adv_data = C.ble_data_t{
p_data: &broadcastData[0],
len: uint16(len(broadcastData)),
}
}
if scanResponseData != nil {
data.scan_rsp_data = C.ble_data_t{
p_data: &scanResponseData[0],
len: uint16(len(scanResponseData)),
}
}
params := C.ble_gap_adv_params_t{
properties: C.ble_gap_adv_properties_t{
_type: C.BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED,
},
interval: uint32(options.Interval),
}
errCode := C.sd_ble_gap_adv_set_configure(&a.handle, &data, &params)
return makeError(errCode)
}
// Start advertisement. May only be called after it has been configured.
func (a *Advertisement) Start() error {
errCode := C.sd_ble_gap_adv_start(a.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
return makeError(errCode)
}
// Scan starts a BLE scan. It is stopped by a call to StopScan. A common pattern
// is to cancel the scan when a particular device has been found.
func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error {
if a.scanning {
// There is a possible race condition here if Scan() is called from a
// different goroutine, but that is not allowed (and will likely result
// in an error below anyway).
return errScanning
}
a.scanning = true
scanParams := C.ble_gap_scan_params_t{}
scanParams.set_bitfield_extended(0)
scanParams.set_bitfield_active(0)
scanParams.interval = 100 * 1000 / 625 // 100ms in 625µs units
scanParams.window = 100 * 1000 / 625 // 100ms in 625µs units
scanParams.timeout = C.BLE_GAP_SCAN_TIMEOUT_UNLIMITED
scanReportBufferInfo := C.ble_data_t{
p_data: &scanReportBuffer.data[0],
len: uint16(len(scanReportBuffer.data)),
}
errCode := C.sd_ble_gap_scan_start(&scanParams, &scanReportBufferInfo)
if errCode != 0 {
return Error(errCode)
}
// Wait for received scan reports.
for a.scanning {
// Wait for the next advertisement packet to arrive.
// TODO: use some sort of condition variable once the scheduler supports
// them.
arm.Asm("wfe")
if gotScanReport.Get() == 0 {
// Spurious event. Continue waiting.
continue
}
gotScanReport.Set(0)
// Call the callback with the scan result.
callback(a, globalScanResult)
// Restart the advertisement. This is needed, because advertisements are
// automatically stopped when the first packet arrives.
errCode := C.sd_ble_gap_scan_start(nil, &scanReportBufferInfo)
if errCode != 0 {
return Error(errCode)
}
}
return nil
}
// StopScan stops any in-progress scan. It can be called from within a Scan
// callback to stop the current scan. If no scan is in progress, an error will
// be returned.
func (a *Adapter) StopScan() error {
if !a.scanning {
return errNotScanning
}
a.scanning = false
return nil
}