cyw43439: HCI implementation

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram 2024-05-17 19:28:42 +02:00 committed by Ron Evans
parent 9905abd00e
commit 457af7571a
14 changed files with 201 additions and 21 deletions

View file

@ -44,6 +44,10 @@ smoketest-tinygo:
@md5sum test.hex @md5sum test.hex
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-express -tags="hci hci_uart" ./examples/advertisement $(TINYGO) build -o test.uf2 -size=short -target=circuitplay-express -tags="hci hci_uart" ./examples/advertisement
@md5sum test.hex @md5sum test.hex
$(TINYGO) build -o test.uf2 -size=short -target=pico-w ./examples/discover
@md5sum test.hex
$(TINYGO) build -o test.uf2 -size=short -target=badger2040-w ./examples/advertisement
@md5sum test.hex
smoketest-linux: smoketest-linux:
# Test on Linux. # Test on Linux.

116
adapter_cyw43439.go Normal file
View file

@ -0,0 +1,116 @@
//go:build cyw43439
package bluetooth
import (
"machine"
"log/slog"
"github.com/soypat/cyw43439"
)
const maxConnections = 1
// Adapter represents a SPI connection to the HCI controller on an attached CYW4349 module.
type Adapter struct {
hciAdapter
}
// DefaultAdapter is the default adapter on the current system.
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
hciAdapter: hciAdapter{
isDefault: true,
connectHandler: func(device Device, connected bool) {
return
},
connectedDevices: make([]Device, 0, maxConnections),
},
}
// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error {
if debug {
println("Initializing CYW43439 device")
}
dev := cyw43439.NewPicoWDevice()
cfg := cyw43439.DefaultBluetoothConfig()
if debug {
cfg.Logger = slog.New(slog.NewTextHandler(machine.USBCDC, &slog.HandlerOptions{
Level: slog.LevelDebug - 2,
}))
}
err := dev.Init(cfg)
if err != nil {
if debug {
println("Error initializing CYW43439 device", err.Error())
}
return err
}
transport := &hciSPI{dev: dev}
a.hci, a.att = newBLEStack(transport)
if debug {
println("Enabling CYW43439 device")
}
a.enable()
if debug {
println("Enabled CYW43439 device")
}
return nil
}
type hciSPI struct {
dev *cyw43439.Device
}
func (h *hciSPI) startRead() {
}
func (h *hciSPI) endRead() {
}
func (h *hciSPI) Buffered() int {
return h.dev.BufferedHCI()
}
func (h *hciSPI) ReadByte() (byte, error) {
var buf [1]byte
r, err := h.dev.HCIReadWriter()
if err != nil {
return 0, err
}
if _, err := r.Read(buf[:]); err != nil {
return 0, err
}
return buf[0], nil
}
func (h *hciSPI) Read(buf []byte) (int, error) {
r, err := h.dev.HCIReadWriter()
if err != nil {
return 0, err
}
return r.Read(buf)
}
func (h *hciSPI) Write(buf []byte) (int, error) {
w, err := h.dev.HCIReadWriter()
if err != nil {
return 0, err
}
return w.Write(buf)
}

View file

@ -1,4 +1,4 @@
//go:build hci || ninafw //go:build hci || ninafw || cyw43439
package bluetooth package bluetooth
@ -25,9 +25,18 @@ type hciAdapter struct {
} }
func (a *hciAdapter) enable() error { func (a *hciAdapter) enable() error {
a.hci.start() if err := a.hci.start(); err != nil {
if debug {
println("error starting HCI:", err.Error())
}
return err
}
if err := a.hci.reset(); err != nil { if err := a.hci.reset(); err != nil {
if debug {
println("error resetting HCI:", err.Error())
}
return err return err
} }

View file

@ -96,6 +96,10 @@ func (h *hciUART) ReadByte() (byte, error) {
return h.uart.ReadByte() return h.uart.ReadByte()
} }
func (h *hciUART) Read(buf []byte) (int, error) {
return h.uart.Read(buf)
}
const writeAttempts = 200 const writeAttempts = 200
func (h *hciUART) Write(buf []byte) (int, error) { func (h *hciUART) Write(buf []byte) (int, error) {

View file

@ -108,6 +108,10 @@ func (h *hciUART) ReadByte() (byte, error) {
return h.uart.ReadByte() return h.uart.ReadByte()
} }
func (h *hciUART) Read(buf []byte) (int, error) {
return h.uart.Read(buf)
}
const writeAttempts = 200 const writeAttempts = 200
func (h *hciUART) Write(buf []byte) (int, error) { func (h *hciUART) Write(buf []byte) (int, error) {

View file

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

View file

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

View file

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

View file

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

6
go.mod
View file

@ -1,11 +1,12 @@
module tinygo.org/x/bluetooth module tinygo.org/x/bluetooth
go 1.18 go 1.20
require ( require (
github.com/go-ole/go-ole v1.2.6 github.com/go-ole/go-ole v1.2.6
github.com/godbus/dbus/v5 v5.1.0 github.com/godbus/dbus/v5 v5.1.0
github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b
github.com/soypat/cyw43439 v0.0.0-20240521202811-13f2f2d46d64
github.com/tinygo-org/cbgo v0.0.4 github.com/tinygo-org/cbgo v0.0.4
golang.org/x/crypto v0.12.0 golang.org/x/crypto v0.12.0
tinygo.org/x/drivers v0.26.1-0.20230922160320-ed51435c2ef6 tinygo.org/x/drivers v0.26.1-0.20230922160320-ed51435c2ef6
@ -16,6 +17,9 @@ require (
require ( require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/soypat/seqs v0.0.0-20240509190925-a6350cee83a7 // indirect
github.com/tinygo-org/pio v0.0.0-20231216154340-cd888eb58899 // indirect
golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 // indirect
golang.org/x/sys v0.11.0 // indirect golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect golang.org/x/term v0.11.0 // indirect
) )

8
go.sum
View file

@ -15,14 +15,22 @@ github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b/go.mod h1:CI
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soypat/cyw43439 v0.0.0-20240521202811-13f2f2d46d64 h1:dNmjx0DMk6H1RdVhmfQudggSDjlayX0G0fOC8SxfAW8=
github.com/soypat/cyw43439 v0.0.0-20240521202811-13f2f2d46d64/go.mod h1:2c95aUfGi2f0oDceIIeozc8ZtVz4NJiZMwkyEGv6q/Y=
github.com/soypat/seqs v0.0.0-20240509190925-a6350cee83a7 h1:0AQR/zVYjgF5TPrjc/bfDdydWApSUyUgbM08kbm+FdQ=
github.com/soypat/seqs v0.0.0-20240509190925-a6350cee83a7/go.mod h1:oCVCNGCHMKoBj97Zp9znLbQ1nHxpkmOY9X+UAGzOxc8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
github.com/tinygo-org/cbgo v0.0.4 h1:3D76CRYbH03Rudi8sEgs/YO0x3JIMdyq8jlQtk/44fU= github.com/tinygo-org/cbgo v0.0.4 h1:3D76CRYbH03Rudi8sEgs/YO0x3JIMdyq8jlQtk/44fU=
github.com/tinygo-org/cbgo v0.0.4/go.mod h1:7+HgWIHd4nbAz0ESjGlJ1/v9LDU1Ox8MGzP9mah/fLk= github.com/tinygo-org/cbgo v0.0.4/go.mod h1:7+HgWIHd4nbAz0ESjGlJ1/v9LDU1Ox8MGzP9mah/fLk=
github.com/tinygo-org/pio v0.0.0-20231216154340-cd888eb58899 h1:/DyaXDEWMqoVUVEJVJIlNk1bXTbFs8s3Q4GdPInSKTQ=
github.com/tinygo-org/pio v0.0.0-20231216154340-cd888eb58899/go.mod h1:LU7Dw00NJ+N86QkeTGjMLNkYcEYMor6wTDpTCu0EaH8=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw=
golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

47
hci.go
View file

@ -1,4 +1,4 @@
//go:build ninafw || hci //go:build ninafw || hci || cyw43439
package bluetooth package bluetooth
@ -68,6 +68,7 @@ const (
hciCommandPkt = 0x01 hciCommandPkt = 0x01
hciACLDataPkt = 0x02 hciACLDataPkt = 0x02
hciSynchronousDataPkt = 0x03
hciEventPkt = 0x04 hciEventPkt = 0x04
hciSecurityPkt = 0x06 hciSecurityPkt = 0x06
@ -126,6 +127,7 @@ type hciTransport interface {
endRead() endRead()
Buffered() int Buffered() int
ReadByte() (byte, error) ReadByte() (byte, error)
Read(buf []byte) (int, error)
Write(buf []byte) (int, error) Write(buf []byte) (int, error)
} }
@ -156,8 +158,19 @@ func (h *hci) start() error {
h.transport.startRead() h.transport.startRead()
defer h.transport.endRead() defer h.transport.endRead()
for h.transport.Buffered() > 0 { var data [32]byte
h.transport.ReadByte() for {
if i := h.transport.Buffered(); i > 0 {
if i > len(data) {
i = len(data)
}
if _, err := h.transport.Read(data[:i]); err != nil {
return err
}
continue
}
return nil
} }
return nil return nil
@ -177,14 +190,19 @@ func (h *hci) poll() error {
i := 0 i := 0
for h.transport.Buffered() > 0 { for h.transport.Buffered() > 0 {
data, _ := h.transport.ReadByte() sz := h.transport.Buffered()
h.buf[i] = data c := sz + 4 - (sz % 4)
_, err := h.transport.Read(h.buf[i : i+c])
if err != nil {
return err
}
i += sz
done, err := h.processPacket(i) done, err := h.processPacket(i)
switch { switch {
case err == ErrHCIUnknown || err == ErrHCIInvalidPacket || err == ErrHCIUnknownEvent: case err == ErrHCIUnknown || err == ErrHCIInvalidPacket || err == ErrHCIUnknownEvent:
if debug { if debug {
println("hci error:", err.Error()) println("hci error:", err.Error(), hex.EncodeToString(h.buf[:i]))
} }
i = 0 i = 0
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
@ -199,7 +217,6 @@ func (h *hci) poll() error {
i = 0 i = 0
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
default: default:
i++
time.Sleep(1 * time.Millisecond) time.Sleep(1 * time.Millisecond)
} }
} }
@ -238,9 +255,19 @@ func (h *hci) processPacket(i int) (bool, error) {
} }
} }
case hciSynchronousDataPkt:
// not supported by BLE, so ignore
if i > 3 {
pktlen := int(h.buf[3])
if debug {
println("hci synchronous data:", i, pktlen, hex.EncodeToString(h.buf[:1+3+pktlen]))
}
return true, nil
}
default: default:
if debug { if debug {
println("unknown packet data:", h.buf[0]) println("unknown packet data:", hex.EncodeToString(h.buf[0:i]))
} }
return true, ErrHCIUnknown return true, ErrHCIUnknown
} }
@ -725,6 +752,10 @@ func (h *hci) handleEventData(buf []byte) error {
return ErrHCIUnknownEvent return ErrHCIUnknownEvent
} }
case evtHardwareError: case evtHardwareError:
if debug {
println("evtHardwareError", hex.EncodeToString(buf))
}
return ErrHCIUnknownEvent return ErrHCIUnknownEvent
} }

View file

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

View file

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