e0e261e166
Events are delivered using interrupts. But there was an accidental heap allocation in the interrupt. Fix it by using a global instead. This is safe (and doesn't need volatile accesses), as the global is only ever accessed from that particular interrupt.
131 lines
3.6 KiB
Go
131 lines
3.6 KiB
Go
//go:build softdevice
|
|
// +build softdevice
|
|
|
|
package bluetooth
|
|
|
|
import (
|
|
"device/nrf"
|
|
"errors"
|
|
"runtime/interrupt"
|
|
"runtime/volatile"
|
|
"unsafe"
|
|
)
|
|
|
|
// #include "ble.h"
|
|
// #ifdef NRF51
|
|
// #include "nrf_soc.h"
|
|
// #else
|
|
// #include "nrf_nvic.h"
|
|
// #endif
|
|
import "C"
|
|
|
|
var (
|
|
ErrNotDefaultAdapter = errors.New("bluetooth: not the default adapter")
|
|
)
|
|
|
|
var (
|
|
secModeOpen C.ble_gap_conn_sec_mode_t // No security is needed (aka open link).
|
|
defaultDeviceName = [6]byte{'T', 'i', 'n', 'y', 'G', 'o'}
|
|
)
|
|
|
|
// There can only be one connection at a time in the default configuration.
|
|
var currentConnection = volatile.Register16{C.BLE_CONN_HANDLE_INVALID}
|
|
|
|
// Globally allocated buffer for incoming SoftDevice events.
|
|
var eventBuf struct {
|
|
C.ble_evt_t
|
|
buf [23]byte
|
|
}
|
|
|
|
func init() {
|
|
secModeOpen.set_bitfield_sm(1)
|
|
secModeOpen.set_bitfield_lv(1)
|
|
}
|
|
|
|
// Adapter is a dummy adapter: it represents the connection to the (only)
|
|
// SoftDevice on the chip.
|
|
type Adapter struct {
|
|
isDefault bool
|
|
scanning bool
|
|
charWriteHandlers []charWriteHandler
|
|
|
|
connectHandler func(device Addresser, connected bool)
|
|
}
|
|
|
|
// DefaultAdapter is the default adapter on the current system. On Nordic chips,
|
|
// it will return the SoftDevice interface.
|
|
//
|
|
// Make sure to call Enable() before using it to initialize the adapter.
|
|
var DefaultAdapter = &Adapter{isDefault: true,
|
|
connectHandler: func(device Addresser, connected bool) {
|
|
return
|
|
}}
|
|
|
|
var eventBufLen uint16
|
|
|
|
// Enable configures the BLE stack. It must be called before any
|
|
// Bluetooth-related calls (unless otherwise indicated).
|
|
func (a *Adapter) Enable() error {
|
|
if !a.isDefault {
|
|
return ErrNotDefaultAdapter
|
|
}
|
|
|
|
// Enable the IRQ that handles all events.
|
|
intr := interrupt.New(nrf.IRQ_SWI2, func(interrupt.Interrupt) {
|
|
for {
|
|
eventBufLen = uint16(unsafe.Sizeof(eventBuf))
|
|
errCode := C.sd_ble_evt_get((*uint8)(unsafe.Pointer(&eventBuf)), &eventBufLen)
|
|
if errCode != 0 {
|
|
// Possible error conditions:
|
|
// * NRF_ERROR_NOT_FOUND: no events left, break
|
|
// * NRF_ERROR_DATA_SIZE: retry with a bigger data buffer
|
|
// (currently not handled, TODO)
|
|
// * NRF_ERROR_INVALID_ADDR: pointer is not aligned, should
|
|
// not happen.
|
|
// In all cases, it's best to simply stop now.
|
|
break
|
|
}
|
|
handleEvent()
|
|
}
|
|
})
|
|
intr.Enable()
|
|
intr.SetPriority(192)
|
|
|
|
// Do more specific initialization of this SoftDevice (split out for nrf52*
|
|
// and nrf51 chips because of the different API).
|
|
err := a.enable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
errCode := C.sd_ble_gap_device_name_set(&secModeOpen, &defaultDeviceName[0], uint16(len(defaultDeviceName)))
|
|
if errCode != 0 {
|
|
return Error(errCode)
|
|
}
|
|
|
|
var gapConnParams C.ble_gap_conn_params_t
|
|
gapConnParams.min_conn_interval = C.BLE_GAP_CP_MIN_CONN_INTVL_MIN
|
|
gapConnParams.max_conn_interval = C.BLE_GAP_CP_MIN_CONN_INTVL_MAX
|
|
gapConnParams.slave_latency = 0
|
|
gapConnParams.conn_sup_timeout = C.BLE_GAP_CP_CONN_SUP_TIMEOUT_NONE
|
|
|
|
errCode = C.sd_ble_gap_ppcp_set(&gapConnParams)
|
|
return makeError(errCode)
|
|
}
|
|
|
|
// DisableInterrupts must be used instead of disabling interrupts directly, to
|
|
// play well with the SoftDevice. Restore interrupts to the previous state with
|
|
// RestoreInterrupts.
|
|
func DisableInterrupts() uintptr {
|
|
var is_nested_critical_region uint8
|
|
C.sd_nvic_critical_region_enter(&is_nested_critical_region)
|
|
return uintptr(is_nested_critical_region)
|
|
}
|
|
|
|
// RestoreInterrupts restores interrupts to the state before calling
|
|
// DisableInterrupts. The mask parameter must be the value returned by
|
|
// DisableInterrupts.
|
|
func RestoreInterrupts(mask uintptr) {
|
|
C.sd_nvic_critical_region_exit(uint8(mask))
|
|
}
|