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:
parent
d46f2cc206
commit
926aeb43f6
5 changed files with 144 additions and 67 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
64
hci.go
|
@ -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 {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build ninafw
|
//go:build ninafw || hci
|
||||||
|
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue