hci: refactor to separate HCI transport implementation from interface to not always assume UART.

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram 2024-05-17 18:41:03 +02:00 committed by Ron Evans
parent d46f2cc206
commit 926aeb43f6
5 changed files with 144 additions and 67 deletions

View file

@ -3,17 +3,16 @@
package bluetooth package bluetooth
import ( import (
"machine"
"runtime" "runtime"
"time" "time"
) )
// hciAdapter represents the implementation for the UART connection to the HCI controller. // hciAdapter represents the implementation for the connection to the HCI controller.
type hciAdapter struct { type hciAdapter struct {
uart *machine.UART hciport hciTransport
hci *hci hci *hci
att *att att *att
isDefault bool isDefault bool
scanning bool scanning bool
@ -49,8 +48,8 @@ func (a *hciAdapter) Address() (MACAddress, error) {
return MACAddress{MAC: makeAddress(a.hci.address)}, nil return MACAddress{MAC: makeAddress(a.hci.address)}, nil
} }
func newBLEStack(uart *machine.UART) (*hci, *att) { func newBLEStack(port hciTransport) (*hci, *att) {
h := newHCI(uart) h := newHCI(port)
a := newATT(h) a := newATT(h)
h.att = a h.att = a

View file

@ -8,10 +8,12 @@ import (
const maxConnections = 1 const maxConnections = 1
// Adapter represents the UART connection to the HCI controller. // Adapter represents a "plain" UART connection to the HCI controller.
type Adapter struct { type Adapter struct {
hciAdapter hciAdapter
uart *machine.UART
// used for software flow control // used for software flow control
cts, rts machine.Pin cts, rts machine.Pin
} }
@ -20,11 +22,13 @@ type Adapter struct {
// //
// 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{
isDefault: true, hciAdapter: hciAdapter{
connectHandler: func(device Device, connected bool) { isDefault: true,
return connectHandler: func(device Device, connected bool) {
return
},
connectedDevices: make([]Device, 0, maxConnections),
}, },
connectedDevices: make([]Device, 0, maxConnections),
} }
// SetUART sets the UART to use for the HCI connection. // SetUART sets the UART to use for the HCI connection.
@ -49,16 +53,66 @@ func (a *Adapter) SetSoftwareFlowControl(cts, rts machine.Pin) error {
// Enable configures the BLE stack. It must be called before any // Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated). // Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error { func (a *Adapter) Enable() error {
a.hci, a.att = newBLEStack(a.uart) transport := &hciUART{uart: a.uart}
if a.cts != 0 && a.rts != 0 { if a.cts != 0 && a.rts != 0 {
a.hci.softRTS = a.rts transport.rts = a.rts
a.hci.softRTS.Configure(machine.PinConfig{Mode: machine.PinOutput}) a.rts.Configure(machine.PinConfig{Mode: machine.PinOutput})
a.hci.softRTS.High() a.rts.High()
a.hci.softCTS = a.cts transport.cts = a.cts
a.cts.Configure(machine.PinConfig{Mode: machine.PinInput}) a.cts.Configure(machine.PinConfig{Mode: machine.PinInput})
} }
a.hci, a.att = newBLEStack(transport)
a.enable() a.enable()
return nil
}
type hciUART struct {
uart *machine.UART
// used for software flow control
cts, rts machine.Pin
}
func (h *hciUART) startRead() {
if h.rts != machine.NoPin {
h.rts.Low()
}
}
func (h *hciUART) endRead() {
if h.rts != machine.NoPin {
h.rts.High()
}
}
func (h *hciUART) Buffered() int {
return h.uart.Buffered()
}
func (h *hciUART) ReadByte() (byte, error) {
return h.uart.ReadByte()
}
const writeAttempts = 200
func (h *hciUART) Write(buf []byte) (int, error) {
if h.cts != machine.NoPin {
retries := writeAttempts
for h.cts.Get() {
retries--
if retries == 0 {
return 0, ErrHCITimeout
}
}
}
n, err := h.uart.Write(buf)
if err != nil {
return 0, err
}
return n, nil
} }

View file

@ -9,7 +9,7 @@ import (
const maxConnections = 1 const maxConnections = 1
// Adapter represents the HCI connection to the NINA fw. // Adapter represents the HCI connection to the NINA fw using the hardware UART.
type Adapter struct { type Adapter struct {
hciAdapter hciAdapter
} }
@ -54,16 +54,15 @@ func (a *Adapter) Enable() error {
uart.Configure(cfg) uart.Configure(cfg)
a.hci, a.att = newBLEStack(uart) transport := &hciUART{uart: uart}
if machine.NINA_SOFT_FLOWCONTROL { if machine.NINA_SOFT_FLOWCONTROL {
a.hci.softRTS = machine.NINA_RTS machine.NINA_RTS.Configure(machine.PinConfig{Mode: machine.PinOutput})
a.hci.softRTS.Configure(machine.PinConfig{Mode: machine.PinOutput}) machine.NINA_RTS.High()
a.hci.softRTS.High()
a.hci.softCTS = machine.NINA_CTS
machine.NINA_CTS.Configure(machine.PinConfig{Mode: machine.PinInput}) machine.NINA_CTS.Configure(machine.PinConfig{Mode: machine.PinInput})
} }
a.hci, a.att = newBLEStack(transport)
return a.enable() return a.enable()
} }
@ -84,3 +83,48 @@ func resetNINAInverted() {
machine.NINA_RESETN.High() machine.NINA_RESETN.High()
time.Sleep(1000 * time.Millisecond) time.Sleep(1000 * time.Millisecond)
} }
type hciUART struct {
uart *machine.UART
}
func (h *hciUART) startRead() {
if machine.NINA_SOFT_FLOWCONTROL {
machine.NINA_RTS.Low()
}
}
func (h *hciUART) endRead() {
if machine.NINA_SOFT_FLOWCONTROL {
machine.NINA_RTS.High()
}
}
func (h *hciUART) Buffered() int {
return h.uart.Buffered()
}
func (h *hciUART) ReadByte() (byte, error) {
return h.uart.ReadByte()
}
const writeAttempts = 200
func (h *hciUART) Write(buf []byte) (int, error) {
if machine.NINA_SOFT_FLOWCONTROL {
retries := writeAttempts
for machine.NINA_CTS.Get() {
retries--
if retries == 0 {
return 0, ErrHCITimeout
}
}
}
n, err := h.uart.Write(buf)
if err != nil {
return 0, err
}
return n, nil
}

64
hci.go
View file

@ -1,4 +1,4 @@
//go:build ninafw //go:build ninafw || hci
package bluetooth package bluetooth
@ -6,7 +6,6 @@ import (
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"errors" "errors"
"machine"
"time" "time"
) )
@ -122,10 +121,16 @@ type leConnectData struct {
timeout uint16 timeout uint16
} }
type hciTransport interface {
startRead()
endRead()
Buffered() int
ReadByte() (byte, error)
Write(buf []byte) (int, error)
}
type hci struct { type hci struct {
uart *machine.UART transport hciTransport
softCTS machine.Pin
softRTS machine.Pin
att *att att *att
l2cap *l2cap l2cap *l2cap
buf []byte buf []byte
@ -140,24 +145,19 @@ type hci struct {
pendingPkt uint16 pendingPkt uint16
} }
func newHCI(uart *machine.UART) *hci { func newHCI(t hciTransport) *hci {
return &hci{ return &hci{
uart: uart, transport: t,
softCTS: machine.NoPin, buf: make([]byte, 256),
softRTS: machine.NoPin,
buf: make([]byte, 256),
} }
} }
func (h *hci) start() error { func (h *hci) start() error {
if h.softRTS != machine.NoPin { h.transport.startRead()
h.softRTS.Low() defer h.transport.endRead()
defer h.softRTS.High() for h.transport.Buffered() > 0 {
} h.transport.ReadByte()
for h.uart.Buffered() > 0 {
h.uart.ReadByte()
} }
return nil return nil
@ -172,15 +172,12 @@ func (h *hci) reset() error {
} }
func (h *hci) poll() error { func (h *hci) poll() error {
if h.softRTS != machine.NoPin { h.transport.startRead()
h.softRTS.Low() defer h.transport.endRead()
defer h.softRTS.High()
}
i := 0 i := 0
for h.uart.Buffered() > 0 { for h.transport.Buffered() > 0 {
data, _ := h.uart.ReadByte() data, _ := h.transport.ReadByte()
h.buf[i] = data h.buf[i] = data
done, err := h.processPacket(i) done, err := h.processPacket(i)
@ -487,25 +484,8 @@ func (h *hci) sendAclPkt(handle uint16, cid uint8, data []byte) error {
return nil return nil
} }
const writeAttempts = 200
func (h *hci) write(buf []byte) (int, error) { func (h *hci) write(buf []byte) (int, error) {
if h.softCTS != machine.NoPin { return h.transport.Write(buf)
retries := writeAttempts
for h.softCTS.Get() {
retries--
if retries == 0 {
return 0, ErrHCITimeout
}
}
}
n, err := h.uart.Write(buf)
if err != nil {
return 0, err
}
return n, nil
} }
type aclDataHeader struct { type aclDataHeader struct {

View file

@ -1,4 +1,4 @@
//go:build ninafw //go:build ninafw || hci
package bluetooth package bluetooth