From 457af7571ab5e7c3a9338cdd901a3b23b3c302d0 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Fri, 17 May 2024 19:28:42 +0200 Subject: [PATCH] cyw43439: HCI implementation Signed-off-by: deadprogram --- Makefile | 4 ++ adapter_cyw43439.go | 116 ++++++++++++++++++++++++++++++++++++++++++++ adapter_hci.go | 13 ++++- adapter_hci_uart.go | 4 ++ adapter_ninafw.go | 4 ++ att_hci.go | 2 +- gap_hci.go | 2 +- gattc_hci.go | 2 +- gatts_hci.go | 2 +- go.mod | 6 ++- go.sum | 8 +++ hci.go | 55 ++++++++++++++++----- l2cap_hci.go | 2 +- uuid_hci.go | 2 +- 14 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 adapter_cyw43439.go diff --git a/Makefile b/Makefile index 08844c8..247ec22 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,10 @@ smoketest-tinygo: @md5sum test.hex $(TINYGO) build -o test.uf2 -size=short -target=circuitplay-express -tags="hci hci_uart" ./examples/advertisement @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: # Test on Linux. diff --git a/adapter_cyw43439.go b/adapter_cyw43439.go new file mode 100644 index 0000000..07c1291 --- /dev/null +++ b/adapter_cyw43439.go @@ -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) +} diff --git a/adapter_hci.go b/adapter_hci.go index 20bcdb4..6c49c0d 100644 --- a/adapter_hci.go +++ b/adapter_hci.go @@ -1,4 +1,4 @@ -//go:build hci || ninafw +//go:build hci || ninafw || cyw43439 package bluetooth @@ -25,9 +25,18 @@ type hciAdapter struct { } 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 debug { + println("error resetting HCI:", err.Error()) + } + return err } diff --git a/adapter_hci_uart.go b/adapter_hci_uart.go index c45ac09..7ab2b14 100644 --- a/adapter_hci_uart.go +++ b/adapter_hci_uart.go @@ -96,6 +96,10 @@ func (h *hciUART) ReadByte() (byte, error) { return h.uart.ReadByte() } +func (h *hciUART) Read(buf []byte) (int, error) { + return h.uart.Read(buf) +} + const writeAttempts = 200 func (h *hciUART) Write(buf []byte) (int, error) { diff --git a/adapter_ninafw.go b/adapter_ninafw.go index 9dd8901..8686031 100644 --- a/adapter_ninafw.go +++ b/adapter_ninafw.go @@ -108,6 +108,10 @@ func (h *hciUART) ReadByte() (byte, error) { return h.uart.ReadByte() } +func (h *hciUART) Read(buf []byte) (int, error) { + return h.uart.Read(buf) +} + const writeAttempts = 200 func (h *hciUART) Write(buf []byte) (int, error) { diff --git a/att_hci.go b/att_hci.go index c69d367..44a6e03 100644 --- a/att_hci.go +++ b/att_hci.go @@ -1,4 +1,4 @@ -//go:build hci || ninafw +//go:build hci || ninafw || cyw43439 package bluetooth diff --git a/gap_hci.go b/gap_hci.go index 04716a2..4002011 100644 --- a/gap_hci.go +++ b/gap_hci.go @@ -1,4 +1,4 @@ -//go:build hci || ninafw +//go:build hci || ninafw || cyw43439 package bluetooth diff --git a/gattc_hci.go b/gattc_hci.go index 30bb251..df7b340 100644 --- a/gattc_hci.go +++ b/gattc_hci.go @@ -1,4 +1,4 @@ -//go:build hci || ninafw +//go:build hci || ninafw || cyw43439 package bluetooth diff --git a/gatts_hci.go b/gatts_hci.go index abc7bb7..f901990 100644 --- a/gatts_hci.go +++ b/gatts_hci.go @@ -1,4 +1,4 @@ -//go:build hci || ninafw +//go:build hci || ninafw || cyw43439 package bluetooth diff --git a/go.mod b/go.mod index 9944959..f6dad7c 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,12 @@ module tinygo.org/x/bluetooth -go 1.18 +go 1.20 require ( github.com/go-ole/go-ole v1.2.6 github.com/godbus/dbus/v5 v5.1.0 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 golang.org/x/crypto v0.12.0 tinygo.org/x/drivers v0.26.1-0.20230922160320-ed51435c2ef6 @@ -16,6 +17,9 @@ require ( require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // 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/term v0.11.0 // indirect ) diff --git a/go.sum b/go.sum index a652b02..2ff37bf 100644 --- a/go.sum +++ b/go.sum @@ -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.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 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/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.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= 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/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/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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/hci.go b/hci.go index 982f3d5..5a4edff 100644 --- a/hci.go +++ b/hci.go @@ -1,4 +1,4 @@ -//go:build ninafw || hci +//go:build ninafw || hci || cyw43439 package bluetooth @@ -66,10 +66,11 @@ const ( leMetaEventEnhancedConnectionComplete = 0x0A leMetaEventDirectAdvertisingReport = 0x0B - hciCommandPkt = 0x01 - hciACLDataPkt = 0x02 - hciEventPkt = 0x04 - hciSecurityPkt = 0x06 + hciCommandPkt = 0x01 + hciACLDataPkt = 0x02 + hciSynchronousDataPkt = 0x03 + hciEventPkt = 0x04 + hciSecurityPkt = 0x06 evtDisconnComplete = 0x05 evtEncryptionChange = 0x08 @@ -126,6 +127,7 @@ type hciTransport interface { endRead() Buffered() int ReadByte() (byte, error) + Read(buf []byte) (int, error) Write(buf []byte) (int, error) } @@ -156,8 +158,19 @@ func (h *hci) start() error { h.transport.startRead() defer h.transport.endRead() - for h.transport.Buffered() > 0 { - h.transport.ReadByte() + var data [32]byte + 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 @@ -177,14 +190,19 @@ func (h *hci) poll() error { i := 0 for h.transport.Buffered() > 0 { - data, _ := h.transport.ReadByte() - h.buf[i] = data + sz := h.transport.Buffered() + 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) switch { case err == ErrHCIUnknown || err == ErrHCIInvalidPacket || err == ErrHCIUnknownEvent: if debug { - println("hci error:", err.Error()) + println("hci error:", err.Error(), hex.EncodeToString(h.buf[:i])) } i = 0 time.Sleep(5 * time.Millisecond) @@ -199,7 +217,6 @@ func (h *hci) poll() error { i = 0 time.Sleep(5 * time.Millisecond) default: - i++ 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: if debug { - println("unknown packet data:", h.buf[0]) + println("unknown packet data:", hex.EncodeToString(h.buf[0:i])) } return true, ErrHCIUnknown } @@ -725,6 +752,10 @@ func (h *hci) handleEventData(buf []byte) error { return ErrHCIUnknownEvent } case evtHardwareError: + if debug { + println("evtHardwareError", hex.EncodeToString(buf)) + } + return ErrHCIUnknownEvent } diff --git a/l2cap_hci.go b/l2cap_hci.go index b9d8d46..8eeeaea 100644 --- a/l2cap_hci.go +++ b/l2cap_hci.go @@ -1,4 +1,4 @@ -//go:build ninafw || hci +//go:build ninafw || hci || cyw43439 package bluetooth diff --git a/uuid_hci.go b/uuid_hci.go index 9c3369d..2028706 100644 --- a/uuid_hci.go +++ b/uuid_hci.go @@ -1,4 +1,4 @@ -//go:build hci || ninafw +//go:build hci || ninafw || cyw43439 package bluetooth