Skip to content

Commit

Permalink
hci: several improvements and fixes including:
Browse files Browse the repository at this point in the history
- add l2cap signaling support
- implement evtNumCompPkts to count in-flight packets
- correct implementation for WriteWithoutReponse
- speed up time waiting for hardware
- corrections to MTU exchange

Signed-off-by: deadprogram <[email protected]>
  • Loading branch information
deadprogram committed Jan 22, 2024
1 parent 1f78023 commit 17114fd
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 23 deletions.
5 changes: 4 additions & 1 deletion adapter_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ func newBLEStack(uart *machine.UART) (*hci, *att) {
a := newATT(h)
h.att = a

l := newL2CAP(h)
h.l2cap = l

return h, a
}

Expand Down Expand Up @@ -171,7 +174,7 @@ func (a *Adapter) startNotifications() {
}
}

time.Sleep(10 * time.Millisecond)
time.Sleep(5 * time.Millisecond)
}
}()

Expand Down
47 changes: 33 additions & 14 deletions att_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import (
)

const (
attCID = 0x0004
bleCTL = 0x0008

attOpError = 0x01
attOpMTUReq = 0x02
attOpMTUResponse = 0x03
Expand Down Expand Up @@ -261,6 +258,7 @@ type att struct {
lastErrorHandle uint16
lastErrorCode uint8
mtu uint16
maxMTU uint16
services []rawService
characteristics []rawCharacteristic
descriptors []rawDescriptor
Expand All @@ -284,6 +282,7 @@ func newATT(hci *hci) *att {
lastHandle: 0x0001,
attributes: []rawAttribute{},
localServices: []rawService{},
maxMTU: 248,
}
}

Expand Down Expand Up @@ -384,7 +383,7 @@ func (a *att) writeCmd(connectionHandle, valueHandle uint16, data []byte) error
return err
}

return a.waitUntilResponse()
return nil
}

func (a *att) writeReq(connectionHandle, valueHandle uint16, data []byte) error {
Expand All @@ -406,7 +405,7 @@ func (a *att) writeReq(connectionHandle, valueHandle uint16, data []byte) error
return a.waitUntilResponse()
}

func (a *att) mtuReq(connectionHandle, mtu uint16) error {
func (a *att) mtuReq(connectionHandle uint16) error {
if debug {
println("att.mtuReq:", connectionHandle)
}
Expand All @@ -416,7 +415,7 @@ func (a *att) mtuReq(connectionHandle, mtu uint16) error {

var b [3]byte
b[0] = attOpMTUReq
binary.LittleEndian.PutUint16(b[1:], mtu)
binary.LittleEndian.PutUint16(b[1:], a.mtu)

if err := a.sendReq(connectionHandle, b[:]); err != nil {
return err
Expand All @@ -425,6 +424,12 @@ func (a *att) mtuReq(connectionHandle, mtu uint16) error {
return a.waitUntilResponse()
}

func (a *att) setMaxMTU(mtu uint16) error {
a.maxMTU = mtu

return nil
}

func (a *att) sendReq(handle uint16, data []byte) error {
a.clearResponse()

Expand Down Expand Up @@ -504,11 +509,21 @@ func (a *att) handleData(handle uint16, buf []byte) error {

case attOpMTUReq:
if debug {
println("att.handleData: attOpMTUReq")
println("att.handleData: attOpMTUReq", hex.EncodeToString(buf))
}
a.mtu = binary.LittleEndian.Uint16(buf[1:])
response := [3]byte{attOpMTUResponse, buf[1], buf[2]}
if err := a.hci.sendAclPkt(handle, attCID, response[:]); err != nil {
mtu := binary.LittleEndian.Uint16(buf[1:])
if mtu > a.maxMTU {
mtu = a.maxMTU
}

// save mtu for connection
a.mtu = mtu

var b [3]byte
b[0] = attOpMTUResponse
binary.LittleEndian.PutUint16(b[1:], mtu)

if err := a.hci.sendAclPkt(handle, attCID, b[:]); err != nil {
return err
}

Expand Down Expand Up @@ -1032,7 +1047,7 @@ func (a *att) waitUntilResponse() error {
break
}

time.Sleep(100 * time.Millisecond)
time.Sleep(5 * time.Millisecond)
}
}

Expand All @@ -1050,17 +1065,21 @@ func (a *att) poll() error {
return nil
}

func (a *att) addConnection(handle uint16) {
func (a *att) addConnection(handle uint16) error {
a.connections = append(a.connections, handle)

return nil
}

func (a *att) removeConnection(handle uint16) {
func (a *att) removeConnection(handle uint16) error {
for i := range a.connections {
if a.connections[i] == handle {
a.connections = append(a.connections[:i], a.connections[i+1:]...)
return
break
}
}

return nil
}

func (a *att) addLocalAttribute(typ attributeType, parent uint16, uuid UUID, permissions CharacteristicPermissions, value []byte) uint16 {
Expand Down
8 changes: 4 additions & 4 deletions gap_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error {
})

a.hci.clearAdvData()
time.Sleep(10 * time.Millisecond)
time.Sleep(5 * time.Millisecond)

default:
if !a.scanning {
Expand All @@ -108,7 +108,7 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error {
lastUpdate = time.Now().UnixNano()
}

time.Sleep(10 * time.Millisecond)
time.Sleep(5 * time.Millisecond)
}
}

Expand Down Expand Up @@ -188,7 +188,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err
break
}

time.Sleep(10 * time.Millisecond)
time.Sleep(5 * time.Millisecond)
}
}

Expand Down Expand Up @@ -405,7 +405,7 @@ func (a *Advertisement) Start() error {
}
}

time.Sleep(10 * time.Millisecond)
time.Sleep(5 * time.Millisecond)
}
}()

Expand Down
16 changes: 13 additions & 3 deletions gattc_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ var (
)

const (
maxDefaultServicesToDiscover = 6
maxDefaultCharacteristicsToDiscover = 8
maxDefaultServicesToDiscover = 8
maxDefaultCharacteristicsToDiscover = 16
)

const (
Expand Down Expand Up @@ -94,6 +94,11 @@ func (d Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {

// reset raw services
d.adapter.att.services = []rawService{}

// did we find them all?
if len(foundServices) == len(uuids) {
break
}
}

switch {
Expand Down Expand Up @@ -191,6 +196,11 @@ func (s DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteri

// reset raw characteristics
s.device.adapter.att.characteristics = []rawCharacteristic{}

// did we find them all?
if len(foundCharacteristics) == len(uuids) {
break
}
}

switch {
Expand Down Expand Up @@ -274,7 +284,7 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err

// GetMTU returns the MTU for the characteristic.
func (c DeviceCharacteristic) GetMTU() (uint16, error) {
err := c.service.device.adapter.att.mtuReq(c.service.device.handle, c.service.device.mtu)
err := c.service.device.adapter.att.mtuReq(c.service.device.handle)
if err != nil {
return 0, err
}
Expand Down
90 changes: 89 additions & 1 deletion hci_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ const (
const (
hciACLLenPos = 4
hciEvtLenPos = 2

attCID = 0x0004
bleCTL = 0x0008
signalingCID = 0x0005
securityCID = 0x0006
)

var (
Expand All @@ -113,13 +118,16 @@ type leConnectData struct {
role uint8
peerBdaddrType uint8
peerBdaddr [6]uint8
interval uint16
timeout uint16
}

type hci struct {
uart *machine.UART
softCTS machine.Pin
softRTS machine.Pin
att *att
l2cap *l2cap
buf []byte
address [6]byte
cmdCompleteOpcode uint16
Expand All @@ -128,6 +136,8 @@ type hci struct {
scanning bool
advData leAdvertisingReport
connectData leConnectData
maxPkt uint16
pendingPkt uint16
}

func newHCI(uart *machine.UART) *hci {
Expand Down Expand Up @@ -263,6 +273,26 @@ func (h *hci) setLeEventMask(eventMask uint64) error {
return h.sendCommandWithParams(ogfLECtrl<<ogfCommandPos|0x01, b[:])
}

func (h *hci) readLeBufferSize() error {
if err := h.sendCommand(ogfLECtrl<<ogfCommandPos | ocfLEReadBufferSize); err != nil {
return err
}

pktLen := binary.LittleEndian.Uint16(h.buf[0:])
h.maxPkt = uint16(h.buf[2])

// pkt len must be at least 27 bytes
if pktLen < 27 {
pktLen = 27
}

if err := h.att.setMaxMTU(pktLen); err != nil {
return err
}

return nil
}

func (h *hci) leSetScanEnable(enabled, duplicates bool) error {
h.scanning = enabled

Expand Down Expand Up @@ -357,6 +387,21 @@ func (h *hci) leCancelConn() error {
return h.sendCommand(ogfLECtrl<<ogfCommandPos | ocfLECancelConn)
}

func (h *hci) leConnUpdate(handle uint16, minInterval, maxInterval,
latency, supervisionTimeout uint16) error {

var b [14]byte
binary.LittleEndian.PutUint16(b[0:], handle)
binary.LittleEndian.PutUint16(b[2:], minInterval)
binary.LittleEndian.PutUint16(b[4:], maxInterval)
binary.LittleEndian.PutUint16(b[6:], latency)
binary.LittleEndian.PutUint16(b[8:], supervisionTimeout)
binary.LittleEndian.PutUint16(b[10:], 0x0004)
binary.LittleEndian.PutUint16(b[12:], 0x0006)

return h.sendCommandWithParams(ogfLECtrl<<ogfCommandPos|ocfLEConnUpdate, b[:])
}

func (h *hci) disconnect(handle uint16) error {
var b [3]byte
binary.LittleEndian.PutUint16(b[0:], handle)
Expand Down Expand Up @@ -437,6 +482,8 @@ func (h *hci) sendAclPkt(handle uint16, cid uint8, data []byte) error {
return err
}

h.pendingPkt++

return nil
}

Expand Down Expand Up @@ -492,6 +539,13 @@ func (h *hci) handleACLData(buf []byte) error {
} else {
return h.att.handleData(aclHdr.handle&0x0fff, buf[8:aclHdr.len+8])
}
case signalingCID:
if debug {
println("signaling cid", aclHdr.cid, hex.EncodeToString(buf))
}

return h.l2cap.handleData(aclHdr.handle&0x0fff, buf[8:aclHdr.len+8])

default:
if debug {
println("unknown acl data cid", aclHdr.cid)
Expand All @@ -513,6 +567,7 @@ func (h *hci) handleEventData(buf []byte) error {

handle := binary.LittleEndian.Uint16(buf[3:])
h.att.removeConnection(handle)
h.l2cap.removeConnection(handle)

return h.leSetAdvertiseEnable(true)

Expand Down Expand Up @@ -549,8 +604,28 @@ func (h *hci) handleEventData(buf []byte) error {

case evtNumCompPkts:
if debug {
println("evtNumCompPkts")
println("evtNumCompPkts", hex.EncodeToString(buf))
}
// count of handles
c := buf[2]
pkts := uint16(0)

for i := byte(0); i < c; i++ {
pkts += binary.LittleEndian.Uint16(buf[5+i*4:])
}

if pkts > 0 && h.pendingPkt > pkts {
h.pendingPkt -= pkts
} else {
h.pendingPkt = 0
}

if debug {
println("evtNumCompPkts", pkts, h.pendingPkt)
}

return nil

case evtLEMetaEvent:
if debug {
println("evtLEMetaEvent")
Expand All @@ -569,7 +644,20 @@ func (h *hci) handleEventData(buf []byte) error {
h.connectData.peerBdaddrType = buf[7]
copy(h.connectData.peerBdaddr[0:], buf[8:])

switch buf[2] {
case leMetaEventConnComplete:
h.connectData.interval = binary.LittleEndian.Uint16(buf[14:])
h.connectData.timeout = binary.LittleEndian.Uint16(buf[16:])
case leMetaEventEnhancedConnectionComplete:
h.connectData.interval = binary.LittleEndian.Uint16(buf[26:])
h.connectData.timeout = binary.LittleEndian.Uint16(buf[28:])
}

h.att.addConnection(h.connectData.handle)
if err := h.l2cap.addConnection(h.connectData.handle, h.connectData.role,
h.connectData.interval, h.connectData.timeout); err != nil {
return err
}

return h.leSetAdvertiseEnable(false)

Expand Down
Loading

0 comments on commit 17114fd

Please sign in to comment.