cyw43439: HCI implementation
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
parent
9905abd00e
commit
457af7571a
14 changed files with 201 additions and 21 deletions
4
Makefile
4
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.
|
||||
|
|
116
adapter_cyw43439.go
Normal file
116
adapter_cyw43439.go
Normal 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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build hci || ninafw
|
||||
//go:build hci || ninafw || cyw43439
|
||||
|
||||
package bluetooth
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build hci || ninafw
|
||||
//go:build hci || ninafw || cyw43439
|
||||
|
||||
package bluetooth
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build hci || ninafw
|
||||
//go:build hci || ninafw || cyw43439
|
||||
|
||||
package bluetooth
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build hci || ninafw
|
||||
//go:build hci || ninafw || cyw43439
|
||||
|
||||
package bluetooth
|
||||
|
||||
|
|
6
go.mod
6
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
|
||||
)
|
||||
|
|
8
go.sum
8
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=
|
||||
|
|
55
hci.go
55
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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build ninafw || hci
|
||||
//go:build ninafw || hci || cyw43439
|
||||
|
||||
package bluetooth
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build hci || ninafw
|
||||
//go:build hci || ninafw || cyw43439
|
||||
|
||||
package bluetooth
|
||||
|
||||
|
|
Loading…
Reference in a new issue