-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
hci: allow for both ninafw and pure hci uart adapter implementations
Signed-off-by: deadprogram <[email protected]>
- Loading branch information
1 parent
07a9e1d
commit 8e8dd34
Showing
9 changed files
with
254 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
//go:build hci || ninafw | ||
|
||
package bluetooth | ||
|
||
import ( | ||
"machine" | ||
"runtime" | ||
|
||
"time" | ||
) | ||
|
||
// hciAdapter represents the implementation for the UART connection to the HCI controller. | ||
type hciAdapter struct { | ||
uart *machine.UART | ||
hci *hci | ||
att *att | ||
|
||
isDefault bool | ||
scanning bool | ||
|
||
connectHandler func(device Device, connected bool) | ||
|
||
connectedDevices []Device | ||
notificationsStarted bool | ||
} | ||
|
||
func (a *hciAdapter) enable() error { | ||
a.hci.start() | ||
|
||
if err := a.hci.reset(); err != nil { | ||
return err | ||
} | ||
|
||
time.Sleep(150 * time.Millisecond) | ||
|
||
if err := a.hci.setEventMask(0x3FFFFFFFFFFFFFFF); err != nil { | ||
return err | ||
} | ||
|
||
return a.hci.setLeEventMask(0x00000000000003FF) | ||
} | ||
|
||
func (a *hciAdapter) Address() (MACAddress, error) { | ||
if err := a.hci.readBdAddr(); err != nil { | ||
return MACAddress{}, err | ||
} | ||
|
||
return MACAddress{MAC: makeAddress(a.hci.address)}, nil | ||
} | ||
|
||
func newBLEStack(uart *machine.UART) (*hci, *att) { | ||
h := newHCI(uart) | ||
a := newATT(h) | ||
h.att = a | ||
|
||
l := newL2CAP(h) | ||
h.l2cap = l | ||
|
||
return h, a | ||
} | ||
|
||
// Convert a NINA MAC address into a Go MAC address. | ||
func makeAddress(mac [6]uint8) MAC { | ||
return MAC{ | ||
uint8(mac[0]), | ||
uint8(mac[1]), | ||
uint8(mac[2]), | ||
uint8(mac[3]), | ||
uint8(mac[4]), | ||
uint8(mac[5]), | ||
} | ||
} | ||
|
||
// Convert a Go MAC address into a NINA MAC Address. | ||
func makeNINAAddress(mac MAC) [6]uint8 { | ||
return [6]uint8{ | ||
uint8(mac[0]), | ||
uint8(mac[1]), | ||
uint8(mac[2]), | ||
uint8(mac[3]), | ||
uint8(mac[4]), | ||
uint8(mac[5]), | ||
} | ||
} | ||
|
||
func (a *hciAdapter) startNotifications() { | ||
if a.notificationsStarted { | ||
return | ||
} | ||
|
||
if debug { | ||
println("starting notifications...") | ||
} | ||
|
||
a.notificationsStarted = true | ||
|
||
// go routine to poll for HCI events for ATT notifications | ||
go func() { | ||
for { | ||
if err := a.att.poll(); err != nil { | ||
// TODO: handle error | ||
if debug { | ||
println("error polling for notifications:", err.Error()) | ||
} | ||
} | ||
|
||
time.Sleep(5 * time.Millisecond) | ||
} | ||
}() | ||
|
||
// go routine to handle characteristic notifications | ||
go func() { | ||
for { | ||
select { | ||
case not := <-a.att.notifications: | ||
if debug { | ||
println("notification received", not.connectionHandle, not.handle, not.data) | ||
} | ||
|
||
d := a.findConnection(not.connectionHandle) | ||
if d.deviceInternal == nil { | ||
if debug { | ||
println("no device found for handle", not.connectionHandle) | ||
} | ||
continue | ||
} | ||
|
||
n := d.findNotificationRegistration(not.handle) | ||
if n == nil { | ||
if debug { | ||
println("no notification registered for handle", not.handle) | ||
} | ||
continue | ||
} | ||
|
||
if n.callback != nil { | ||
n.callback(not.data) | ||
} | ||
|
||
default: | ||
} | ||
|
||
runtime.Gosched() | ||
} | ||
}() | ||
} | ||
|
||
func (a *hciAdapter) addConnection(d Device) { | ||
a.connectedDevices = append(a.connectedDevices, d) | ||
} | ||
|
||
func (a *hciAdapter) removeConnection(d Device) { | ||
for i := range a.connectedDevices { | ||
if d.handle == a.connectedDevices[i].handle { | ||
a.connectedDevices[i] = a.connectedDevices[len(a.connectedDevices)-1] | ||
a.connectedDevices[len(a.connectedDevices)-1] = Device{} | ||
a.connectedDevices = a.connectedDevices[:len(a.connectedDevices)-1] | ||
|
||
return | ||
} | ||
} | ||
} | ||
|
||
func (a *hciAdapter) findConnection(handle uint16) Device { | ||
for _, d := range a.connectedDevices { | ||
if d.handle == handle { | ||
if debug { | ||
println("found device", handle, d.Address.String(), "with notifications registered", len(d.notificationRegistrations)) | ||
} | ||
|
||
return d | ||
} | ||
} | ||
|
||
return Device{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
//go:build hci && hci_uart | ||
|
||
package bluetooth | ||
|
||
import ( | ||
"machine" | ||
) | ||
|
||
const maxConnections = 1 | ||
|
||
// Adapter represents the UART connection to the HCI controller. | ||
type Adapter struct { | ||
hciAdapter | ||
|
||
// used for software flow control | ||
cts, rts machine.Pin | ||
} | ||
|
||
// DefaultAdapter is the default adapter on the current system. | ||
// | ||
// Make sure to call Enable() before using it to initialize the adapter. | ||
var DefaultAdapter = &Adapter{ | ||
isDefault: true, | ||
connectHandler: func(device Device, connected bool) { | ||
return | ||
}, | ||
connectedDevices: make([]Device, 0, maxConnections), | ||
} | ||
|
||
// SetUART sets the UART to use for the HCI connection. | ||
// It must be called before calling Enable(). | ||
// Note that the UART must be configured with hardware flow control, or | ||
// SetSoftwareFlowControl() must be called. | ||
func (a *Adapter) SetUART(uart *machine.UART) error { | ||
a.uart = uart | ||
|
||
return nil | ||
} | ||
|
||
// SetSoftwareFlowControl sets the pins to use for software flow control, | ||
// if hardware flow control is not available. | ||
func (a *Adapter) SetSoftwareFlowControl(cts, rts machine.Pin) error { | ||
a.cts = cts | ||
a.rts = rts | ||
|
||
return nil | ||
} | ||
|
||
// 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) | ||
|
||
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() | ||
|
||
a.hci.softCTS = a.cts | ||
a.cts.Configure(machine.PinConfig{Mode: machine.PinInput}) | ||
} | ||
|
||
a.enable() | ||
} |
Oops, something went wrong.