Skip to content

Commit

Permalink
hci: allow for both ninafw and pure hci uart adapter implementations
Browse files Browse the repository at this point in the history
Signed-off-by: deadprogram <[email protected]>
  • Loading branch information
deadprogram authored and bgould committed Jan 26, 2024
1 parent 07a9e1d commit 8e8dd34
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 174 deletions.
176 changes: 176 additions & 0 deletions adapter_hci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//go:build hci || ninafw

package bluetooth

import (
"machine"
"runtime"

"time"
)

// hciAdapter represents the implementation for the UART connection to the HCI controller.
type hciAdapter struct {
uart *machine.UART
hci *hci
att *att

isDefault bool
scanning bool

connectHandler func(device Device, connected bool)

connectedDevices []Device
notificationsStarted bool
}

func (a *hciAdapter) enable() error {
a.hci.start()

if err := a.hci.reset(); err != nil {
return err
}

time.Sleep(150 * time.Millisecond)

if err := a.hci.setEventMask(0x3FFFFFFFFFFFFFFF); err != nil {
return err
}

return a.hci.setLeEventMask(0x00000000000003FF)
}

func (a *hciAdapter) Address() (MACAddress, error) {
if err := a.hci.readBdAddr(); err != nil {
return MACAddress{}, err
}

return MACAddress{MAC: makeAddress(a.hci.address)}, nil
}

func newBLEStack(uart *machine.UART) (*hci, *att) {
h := newHCI(uart)
a := newATT(h)
h.att = a

l := newL2CAP(h)
h.l2cap = l

return h, a
}

// Convert a NINA MAC address into a Go MAC address.
func makeAddress(mac [6]uint8) MAC {
return MAC{
uint8(mac[0]),
uint8(mac[1]),
uint8(mac[2]),
uint8(mac[3]),
uint8(mac[4]),
uint8(mac[5]),
}
}

// Convert a Go MAC address into a NINA MAC Address.
func makeNINAAddress(mac MAC) [6]uint8 {
return [6]uint8{
uint8(mac[0]),
uint8(mac[1]),
uint8(mac[2]),
uint8(mac[3]),
uint8(mac[4]),
uint8(mac[5]),
}
}

func (a *hciAdapter) startNotifications() {
if a.notificationsStarted {
return
}

if debug {
println("starting notifications...")
}

a.notificationsStarted = true

// go routine to poll for HCI events for ATT notifications
go func() {
for {
if err := a.att.poll(); err != nil {
// TODO: handle error
if debug {
println("error polling for notifications:", err.Error())
}
}

time.Sleep(5 * time.Millisecond)
}
}()

// go routine to handle characteristic notifications
go func() {
for {
select {
case not := <-a.att.notifications:
if debug {
println("notification received", not.connectionHandle, not.handle, not.data)
}

d := a.findConnection(not.connectionHandle)
if d.deviceInternal == nil {
if debug {
println("no device found for handle", not.connectionHandle)
}
continue
}

n := d.findNotificationRegistration(not.handle)
if n == nil {
if debug {
println("no notification registered for handle", not.handle)
}
continue
}

if n.callback != nil {
n.callback(not.data)
}

default:
}

runtime.Gosched()
}
}()
}

func (a *hciAdapter) addConnection(d Device) {
a.connectedDevices = append(a.connectedDevices, d)
}

func (a *hciAdapter) removeConnection(d Device) {
for i := range a.connectedDevices {
if d.handle == a.connectedDevices[i].handle {
a.connectedDevices[i] = a.connectedDevices[len(a.connectedDevices)-1]
a.connectedDevices[len(a.connectedDevices)-1] = Device{}
a.connectedDevices = a.connectedDevices[:len(a.connectedDevices)-1]

return
}
}
}

func (a *hciAdapter) findConnection(handle uint16) Device {
for _, d := range a.connectedDevices {
if d.handle == handle {
if debug {
println("found device", handle, d.Address.String(), "with notifications registered", len(d.notificationRegistrations))
}

return d
}
}

return Device{}
}
64 changes: 64 additions & 0 deletions adapter_hci_uart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//go:build hci && hci_uart

package bluetooth

import (
"machine"
)

const maxConnections = 1

// Adapter represents the UART connection to the HCI controller.
type Adapter struct {
hciAdapter

// used for software flow control
cts, rts machine.Pin
}

// DefaultAdapter is the default adapter on the current system.
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
isDefault: true,
connectHandler: func(device Device, connected bool) {
return
},
connectedDevices: make([]Device, 0, maxConnections),
}

// SetUART sets the UART to use for the HCI connection.
// It must be called before calling Enable().
// Note that the UART must be configured with hardware flow control, or
// SetSoftwareFlowControl() must be called.
func (a *Adapter) SetUART(uart *machine.UART) error {
a.uart = uart

return nil
}

// SetSoftwareFlowControl sets the pins to use for software flow control,
// if hardware flow control is not available.
func (a *Adapter) SetSoftwareFlowControl(cts, rts machine.Pin) error {
a.cts = cts
a.rts = rts

return nil
}

// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error {
a.hci, a.att = newBLEStack(a.uart)

if a.cts != 0 && a.rts != 0 {
a.hci.softRTS = a.rts
a.hci.softRTS.Configure(machine.PinConfig{Mode: machine.PinOutput})
a.hci.softRTS.High()

a.hci.softCTS = a.cts
a.cts.Configure(machine.PinConfig{Mode: machine.PinInput})
}

a.enable()
}
Loading

0 comments on commit 8e8dd34

Please sign in to comment.