diff --git a/adapter_hci.go b/adapter_hci.go index fe5d57f..20bcdb4 100644 --- a/adapter_hci.go +++ b/adapter_hci.go @@ -3,17 +3,16 @@ package bluetooth import ( - "machine" "runtime" "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 { - uart *machine.UART - hci *hci - att *att + hciport hciTransport + hci *hci + att *att isDefault bool scanning bool @@ -49,8 +48,8 @@ func (a *hciAdapter) Address() (MACAddress, error) { return MACAddress{MAC: makeAddress(a.hci.address)}, nil } -func newBLEStack(uart *machine.UART) (*hci, *att) { - h := newHCI(uart) +func newBLEStack(port hciTransport) (*hci, *att) { + h := newHCI(port) a := newATT(h) h.att = a diff --git a/adapter_hci_uart.go b/adapter_hci_uart.go index ec0eb66..c45ac09 100644 --- a/adapter_hci_uart.go +++ b/adapter_hci_uart.go @@ -8,10 +8,12 @@ import ( 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 { hciAdapter + uart *machine.UART + // used for software flow control cts, rts machine.Pin } @@ -20,11 +22,13 @@ type Adapter struct { // // Make sure to call Enable() before using it to initialize the adapter. var DefaultAdapter = &Adapter{ - isDefault: true, - connectHandler: func(device Device, connected bool) { - return + hciAdapter: hciAdapter{ + isDefault: true, + 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. @@ -49,16 +53,66 @@ func (a *Adapter) SetSoftwareFlowControl(cts, rts machine.Pin) error { // 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) - + transport := &hciUART{uart: 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() + transport.rts = a.rts + a.rts.Configure(machine.PinConfig{Mode: machine.PinOutput}) + a.rts.High() - a.hci.softCTS = a.cts + transport.cts = a.cts a.cts.Configure(machine.PinConfig{Mode: machine.PinInput}) } + a.hci, a.att = newBLEStack(transport) 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 } diff --git a/adapter_ninafw.go b/adapter_ninafw.go index 2d06b21..9dd8901 100644 --- a/adapter_ninafw.go +++ b/adapter_ninafw.go @@ -9,7 +9,7 @@ import ( 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 { hciAdapter } @@ -54,16 +54,15 @@ func (a *Adapter) Enable() error { uart.Configure(cfg) - a.hci, a.att = newBLEStack(uart) + transport := &hciUART{uart: uart} if machine.NINA_SOFT_FLOWCONTROL { - a.hci.softRTS = machine.NINA_RTS - a.hci.softRTS.Configure(machine.PinConfig{Mode: machine.PinOutput}) - a.hci.softRTS.High() + machine.NINA_RTS.Configure(machine.PinConfig{Mode: machine.PinOutput}) + machine.NINA_RTS.High() - a.hci.softCTS = machine.NINA_CTS machine.NINA_CTS.Configure(machine.PinConfig{Mode: machine.PinInput}) } + a.hci, a.att = newBLEStack(transport) return a.enable() } @@ -84,3 +83,48 @@ func resetNINAInverted() { machine.NINA_RESETN.High() 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 +} diff --git a/hci.go b/hci.go index eea27fb..982f3d5 100644 --- a/hci.go +++ b/hci.go @@ -1,4 +1,4 @@ -//go:build ninafw +//go:build ninafw || hci package bluetooth @@ -6,7 +6,6 @@ import ( "encoding/binary" "encoding/hex" "errors" - "machine" "time" ) @@ -122,10 +121,16 @@ type leConnectData struct { timeout uint16 } +type hciTransport interface { + startRead() + endRead() + Buffered() int + ReadByte() (byte, error) + Write(buf []byte) (int, error) +} + type hci struct { - uart *machine.UART - softCTS machine.Pin - softRTS machine.Pin + transport hciTransport att *att l2cap *l2cap buf []byte @@ -140,24 +145,19 @@ type hci struct { pendingPkt uint16 } -func newHCI(uart *machine.UART) *hci { +func newHCI(t hciTransport) *hci { return &hci{ - uart: uart, - softCTS: machine.NoPin, - softRTS: machine.NoPin, - buf: make([]byte, 256), + transport: t, + buf: make([]byte, 256), } } func (h *hci) start() error { - if h.softRTS != machine.NoPin { - h.softRTS.Low() + h.transport.startRead() + defer h.transport.endRead() - defer h.softRTS.High() - } - - for h.uart.Buffered() > 0 { - h.uart.ReadByte() + for h.transport.Buffered() > 0 { + h.transport.ReadByte() } return nil @@ -172,15 +172,12 @@ func (h *hci) reset() error { } func (h *hci) poll() error { - if h.softRTS != machine.NoPin { - h.softRTS.Low() - - defer h.softRTS.High() - } + h.transport.startRead() + defer h.transport.endRead() i := 0 - for h.uart.Buffered() > 0 { - data, _ := h.uart.ReadByte() + for h.transport.Buffered() > 0 { + data, _ := h.transport.ReadByte() h.buf[i] = data done, err := h.processPacket(i) @@ -487,25 +484,8 @@ func (h *hci) sendAclPkt(handle uint16, cid uint8, data []byte) error { return nil } -const writeAttempts = 200 - func (h *hci) write(buf []byte) (int, error) { - if h.softCTS != machine.NoPin { - 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 + return h.transport.Write(buf) } type aclDataHeader struct { diff --git a/l2cap_hci.go b/l2cap_hci.go index 84ad644..b9d8d46 100644 --- a/l2cap_hci.go +++ b/l2cap_hci.go @@ -1,4 +1,4 @@ -//go:build ninafw +//go:build ninafw || hci package bluetooth