hci: allow for both ninafw and pure hci uart adapter implementations
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
parent
07a9e1d02e
commit
8e8dd34fc2
9 changed files with 254 additions and 174 deletions
176
adapter_hci.go
Normal file
176
adapter_hci.go
Normal file
|
@ -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
adapter_hci_uart.go
Normal file
64
adapter_hci_uart.go
Normal file
|
@ -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()
|
||||||
|
}
|
|
@ -4,36 +4,27 @@ package bluetooth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxConnections = 1
|
const maxConnections = 1
|
||||||
|
|
||||||
// Adapter represents the UART connection to the NINA fw.
|
// Adapter represents the HCI connection to the NINA fw.
|
||||||
type Adapter struct {
|
type Adapter struct {
|
||||||
hci *hci
|
hciAdapter
|
||||||
att *att
|
|
||||||
|
|
||||||
isDefault bool
|
|
||||||
scanning bool
|
|
||||||
|
|
||||||
connectHandler func(device Device, connected bool)
|
|
||||||
|
|
||||||
connectedDevices []Device
|
|
||||||
notificationsStarted bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAdapter is the default adapter on the current system.
|
// DefaultAdapter is the default adapter on the current system.
|
||||||
//
|
//
|
||||||
// Make sure to call Enable() before using it to initialize the adapter.
|
// Make sure to call Enable() before using it to initialize the adapter.
|
||||||
var DefaultAdapter = &Adapter{
|
var DefaultAdapter = &Adapter{
|
||||||
|
hciAdapter: hciAdapter{
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
connectHandler: func(device Device, connected bool) {
|
connectHandler: func(device Device, connected bool) {
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
connectedDevices: make([]Device, 0, maxConnections),
|
connectedDevices: make([]Device, 0, maxConnections),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable configures the BLE stack. It must be called before any
|
// Enable configures the BLE stack. It must be called before any
|
||||||
|
@ -73,66 +64,7 @@ func (a *Adapter) Enable() error {
|
||||||
machine.NINA_CTS.Configure(machine.PinConfig{Mode: machine.PinInput})
|
machine.NINA_CTS.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||||
}
|
}
|
||||||
|
|
||||||
a.hci.start()
|
return a.enable()
|
||||||
|
|
||||||
if err := a.hci.reset(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(150 * time.Millisecond)
|
|
||||||
|
|
||||||
if err := a.hci.setEventMask(0x3FFFFFFFFFFFFFFF); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := a.hci.setLeEventMask(0x00000000000003FF); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Adapter) 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 resetNINA() {
|
func resetNINA() {
|
||||||
|
@ -152,95 +84,3 @@ func resetNINAInverted() {
|
||||||
machine.NINA_RESETN.High()
|
machine.NINA_RESETN.High()
|
||||||
time.Sleep(1000 * time.Millisecond)
|
time.Sleep(1000 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adapter) 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 *Adapter) addConnection(d Device) {
|
|
||||||
a.connectedDevices = append(a.connectedDevices, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Adapter) 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 *Adapter) 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{}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build ninafw
|
//go:build hci || ninafw
|
||||||
|
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build ninafw
|
//go:build hci || ninafw
|
||||||
|
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build ninafw
|
//go:build hci || ninafw
|
||||||
|
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build ninafw
|
//go:build hci || ninafw
|
||||||
|
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build ninafw
|
//go:build hci || ninafw
|
||||||
|
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
Loading…
Reference in a new issue