Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sd: MTU exchange #295

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions adapter_nrf528xx-full.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func handleEvent() {
}
connectionAttempt.connectionHandle = gapEvent.conn_handle
connectionAttempt.state.Set(2) // connection was successful
mtuExchangeAttempt.state.Set(0)
DefaultAdapter.connectHandler(device, true)
}
case C.BLE_GAP_EVT_DISCONNECTED:
Expand All @@ -63,7 +64,7 @@ func handleEvent() {
// because it would need to be reconfigured as a non-connectable
// advertisement. That's left as a future addition, if
// necessary.
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, connCfgTag)
}
device := Device{
connectionHandle: gapEvent.conn_handle,
Expand Down Expand Up @@ -157,9 +158,16 @@ func handleEvent() {
// way to handle it, ignore it.
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.
C.sd_ble_gatts_exchange_mtu_reply(gattsEvent.conn_handle, C.BLE_GATT_ATT_MTU_DEFAULT)
rsp := gattsEvent.params.unionfield_exchange_mtu_request()
effectiveMtu := min(DefaultAdapter.cfg.Gatt.AttMtu, uint16(rsp.client_rx_mtu))
if debug {
println("mtu exchange requested. self:", DefaultAdapter.cfg.Gatt.AttMtu, ", peer:", rsp.client_rx_mtu, ", effective:", effectiveMtu)
}

var errCode = C.sd_ble_gatts_exchange_mtu_reply(gattsEvent.conn_handle, C.uint16_t(effectiveMtu))
if debug {
println("mtu exchange replied, err:", Error(errCode).Error())
}
case C.BLE_GATTS_EVT_HVN_TX_COMPLETE:
// ignore confirmation of a notification successfully sent
default:
Expand Down Expand Up @@ -255,6 +263,36 @@ func handleEvent() {
}
}
}
case C.BLE_GATTC_EVT_EXCHANGE_MTU_RSP:
rsp := gattcEvent.params.unionfield_exchange_mtu_rsp()
if debug {
println("mtu exchanged, effective mtu:", rsp.server_rx_mtu)
}

mtuExchangeAttempt.effectiveMtu = uint16(rsp.server_rx_mtu)
mtuExchangeAttempt.state.Set(2) // mtu exchange was successful

if debug {
rsp := gattcEvent.params.unionfield_exchange_mtu_rsp()
println("mtu exchanged, effective mtu:", rsp.server_rx_mtu)
}
case C.BLE_GATTC_EVT_TIMEOUT:
timeoutEvt := gattcEvent.params.unionfield_timeout()
switch timeoutEvt.src {
case C.BLE_GATT_TIMEOUT_SRC_PROTOCOL:
// Failed to connect to a peripheral.
if debug {
println("gattc timeout: src protocol")
}
mtuExchangeAttempt.state.Set(3) // mtu exchange timed out
default:
// For example a scan timeout.
if debug {
println("gattc timeout: other")
}
}
case C.BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE:
// no op
default:
if debug {
println("unknown GATTC event:", id, id-C.BLE_GATTC_EVT_BASE)
Expand All @@ -266,3 +304,10 @@ func handleEvent() {
}
}
}

func min(a, b uint16) uint16 {
if a < b {
return a
}
return b
}
22 changes: 18 additions & 4 deletions adapter_nrf528xx-peripheral.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func handleEvent() {
// because it would need to be reconfigured as a non-connectable
// advertisement. That's left as a future addition, if
// necessary.
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, connCfgTag)
}
device := Device{
connectionHandle: gapEvent.conn_handle,
Expand Down Expand Up @@ -91,9 +91,16 @@ func handleEvent() {
// way to handle it, ignore it.
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.
C.sd_ble_gatts_exchange_mtu_reply(gattsEvent.conn_handle, C.BLE_GATT_ATT_MTU_DEFAULT)
rsp := gattsEvent.params.unionfield_exchange_mtu_request()
effectiveMtu := min(DefaultAdapter.cfg.Gatt.AttMtu, uint16(rsp.client_rx_mtu))
if debug {
println("mtu exchange requested. self:", DefaultAdapter.cfg.Gatt.AttMtu, ", peer:", rsp.client_rx_mtu, ", effective:", effectiveMtu)
}

var errCode = C.sd_ble_gatts_exchange_mtu_reply(gattsEvent.conn_handle, C.uint16_t(effectiveMtu))
if debug {
println("mtu exchange replied, err:", Error(errCode).Error())
}
case C.BLE_GATTS_EVT_HVN_TX_COMPLETE:
// ignore confirmation of a notification successfully sent
default:
Expand All @@ -107,3 +114,10 @@ func handleEvent() {
}
}
}

func min(a, b uint16) uint16 {
if a < b {
return a
}
return b
}
33 changes: 33 additions & 0 deletions adapter_nrf528xx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ var clockConfigXtal C.nrf_clock_lf_cfg_t = C.nrf_clock_lf_cfg_t{
accuracy: C.NRF_CLOCK_LF_ACCURACY_250_PPM,
}

const connCfgTag uint8 = 1

//go:extern __app_ram_base
var appRAMBase [0]uint32

Expand All @@ -47,6 +49,37 @@ func (a *Adapter) enable() error {

// Enable the BLE stack.
appRAMBase := C.uint32_t(uintptr(unsafe.Pointer(&appRAMBase)))

bleCfg := C.ble_cfg_t{}

connCfg := bleCfg.unionfield_conn_cfg()
connCfg.conn_cfg_tag = connCfgTag

if a.cfg.Gap.EventLength == 0 {
a.cfg.Gap.EventLength = 2
}

gapCfg := connCfg.params.unionfield_gap_conn_cfg()
gapCfg.conn_count = 1
gapCfg.event_length = a.cfg.Gap.EventLength

errCode = C.sd_ble_cfg_set(C.uint32_t(C.BLE_CONN_CFG_GAP), &bleCfg, appRAMBase)
if errCode != 0 {
return Error(errCode)
}

if a.cfg.Gatt.AttMtu == 0 {
a.cfg.Gatt.AttMtu = 23
}

gattCfg := connCfg.params.unionfield_gatt_conn_cfg()
gattCfg.att_mtu = a.cfg.Gatt.AttMtu

errCode = C.sd_ble_cfg_set(C.uint32_t(C.BLE_CONN_CFG_GATT), &bleCfg, appRAMBase)
if errCode != 0 {
return Error(errCode)
}

errCode = C.sd_ble_enable(&appRAMBase)
return makeError(errCode)
}
Expand Down
40 changes: 39 additions & 1 deletion adapter_sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var currentConnection = volatileHandle{handle: volatile.Register16{C.BLE_CONN_HA
// Globally allocated buffer for incoming SoftDevice events.
var eventBuf struct {
C.ble_evt_t
buf [23]byte
buf [244]byte
}

func init() {
Expand All @@ -49,6 +49,8 @@ type Adapter struct {
charWriteHandlers []charWriteHandler

connectHandler func(device Device, connected bool)

cfg Config
}

// DefaultAdapter is the default adapter on the current system. On Nordic chips,
Expand All @@ -62,6 +64,42 @@ var DefaultAdapter = &Adapter{isDefault: true,

var eventBufLen C.uint16_t

// Config represents the settings that will be configured to the connection
// '1' of the SoftDevice.
type Config struct {
Gap GapConfig
Gatt GattConfig
}

type GapConfig struct {
// EventLength is the time set aside for this connection on every
// connection interval in 1.25 ms units. The minimum value is 2.
EventLength uint16
}

type GattConfig struct {
// AttMtu is the maximum size of ATT packet the SoftDevice can send or
// receive. The default and minimum value is 23. The maximum value is 247.
// Using ATT_MTU sizes that are multiples of 23 is ideal.
AttMtu uint16
}

// Configure sets the configuration for this adapter.
// The configuration will be applied when the adapter is enabled.
func (a *Adapter) Configure(cfg Config) error {
if cfg.Gap.EventLength != 0 && cfg.Gap.EventLength < 2 {
return errors.New("invalid event length")
}

if cfg.Gatt.AttMtu != 0 && (cfg.Gatt.AttMtu < 23 || cfg.Gatt.AttMtu > 247) {
return errors.New("invalid ATT MTU")
}

a.cfg = cfg

return nil
}

// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error {
Expand Down
2 changes: 1 addition & 1 deletion gap_nrf528xx-advertisement.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (a *Advertisement) Configure(options AdvertisementOptions) error {
// Start advertisement. May only be called after it has been configured.
func (a *Advertisement) Start() error {
a.isAdvertising.Set(1)
errCode := C.sd_ble_gap_adv_start(a.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
errCode := C.sd_ble_gap_adv_start(a.handle, connCfgTag)
return makeError(errCode)
}

Expand Down
44 changes: 43 additions & 1 deletion gap_nrf528xx-central.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

/*
#include "ble_gap.h"
#include "ble_gattc.h"
*/
import "C"

Expand Down Expand Up @@ -99,6 +100,11 @@ var connectionAttempt struct {
connectionHandle C.uint16_t
}

var mtuExchangeAttempt struct {
state volatile.Register8 // 0 means unused, 1 means in progress, 2 means exchanged, 3 means timeout
effectiveMtu uint16
}

// Connect starts a connection attempt to the given peripheral device address.
//
// Limitations on Nordic SoftDevices inclue that you cannot do more than one
Expand Down Expand Up @@ -162,7 +168,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err
connectionAttempt.state.Set(1)

// Start the connection attempt. We'll get a signal in the event handler.
errCode := C.sd_ble_gap_connect(&addr, &scanParams, &connectionParams, C.BLE_CONN_CFG_TAG_DEFAULT)
errCode := C.sd_ble_gap_connect(&addr, &scanParams, &connectionParams, connCfgTag)
if errCode != 0 {
connectionAttempt.state.Set(0)
return Device{}, Error(errCode)
Expand All @@ -175,6 +181,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err
// Successfully connected.
connectionAttempt.state.Set(0)
connectionHandle := connectionAttempt.connectionHandle

return Device{
connectionHandle: connectionHandle,
}, nil
Expand All @@ -190,6 +197,41 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err
}
}

// ExchangeMTU starts an MTU exchange request procedure. The effective MTU
// (the maximum value supported by both parties) is returned.
func (d Device) ExchangeMTU(mtu uint16) (uint16, error) {
if mtuExchangeAttempt.state.Get() == 2 {
return 0, errors.New("mtu already exchanged")
}

if mtuExchangeAttempt.state.Get() == 1 {
return 0, errors.New("mtu exchange in progress")
}

mtuExchangeAttempt.state.Set(1)

errCode := C.sd_ble_gattc_exchange_mtu_request(d.connectionHandle, C.uint16_t(mtu))
if errCode != 0 {
mtuExchangeAttempt.state.Set(0)
return 0, Error(errCode)
}

for {
state := mtuExchangeAttempt.state.Get()
if state == 2 {
return mtuExchangeAttempt.effectiveMtu, nil
} else if state == 3 {
// Timeout while exchanging mtu
mtuExchangeAttempt.state.Set(0)
return 0, errors.New("mtu exchange timeout")
} else {
// TODO: use some sort of condition variable once the scheduler
// supports them.
arm.Asm("wfe")
}
}
}

// Disconnect from the BLE device.
func (d Device) Disconnect() error {
errCode := C.sd_ble_gap_disconnect(d.connectionHandle, C.BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)
Expand Down
2 changes: 1 addition & 1 deletion gatts_sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (a *Adapter) AddService(service *Service) error {
},
init_len: C.uint16_t(len(char.Value)),
init_offs: 0,
max_len: 20, // This is a conservative maximum length.
max_len: C.BLE_GATTS_FIX_ATTR_LEN_MAX,
}
if len(char.Value) != 0 {
value.p_value = (*C.uint8_t)(unsafe.Pointer(&char.Value[0]))
Expand Down
Loading