From bf647ecd579468ce42d91b584bae202486f04fcf Mon Sep 17 00:00:00 2001 From: deadprogram Date: Tue, 16 Jan 2024 20:45:45 +0100 Subject: [PATCH] ninafw: this PR contains several fixes and improvements for the NINAFW implementation including: - correctly return from read requests instead of returning spurious error - move some steps previously being done during Configure() into Start() where they more correctly belonged. - use advertising display name as the correct default value for the generic access characteristic. - speed up the polling for new notifications for Centrals Signed-off-by: deadprogram --- adapter_ninafw.go | 2 +- att_ninafw.go | 4 ++ gap_ninafw.go | 97 ++++++++++++++++++++++++++++------------------- gatts_ninafw.go | 12 ++++-- 4 files changed, 71 insertions(+), 44 deletions(-) diff --git a/adapter_ninafw.go b/adapter_ninafw.go index 5f9b33e..688e568 100644 --- a/adapter_ninafw.go +++ b/adapter_ninafw.go @@ -171,7 +171,7 @@ func (a *Adapter) startNotifications() { } } - time.Sleep(250 * time.Millisecond) + time.Sleep(10 * time.Millisecond) } }() diff --git a/att_ninafw.go b/att_ninafw.go index bfdaa07..b09e228 100644 --- a/att_ninafw.go +++ b/att_ninafw.go @@ -925,6 +925,8 @@ func (a *att) handleReadReq(handle, attrHandle uint16) error { if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil { return err } + + return nil } case attributeTypeDescriptor: @@ -945,6 +947,8 @@ func (a *att) handleReadReq(handle, attrHandle uint16) error { if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil { return err } + + return nil } } diff --git a/gap_ninafw.go b/gap_ninafw.go index 47cc2ba..a964eaa 100644 --- a/gap_ninafw.go +++ b/gap_ninafw.go @@ -269,6 +269,10 @@ var defaultAdvertisement Advertisement // Advertisement encapsulates a single advertisement instance. type Advertisement struct { adapter *Adapter + + localName []byte + serviceUUIDs []UUID + interval uint16 } // DefaultAdvertisement returns the default advertisement instance but does not @@ -276,31 +280,6 @@ type Advertisement struct { func (a *Adapter) DefaultAdvertisement() *Advertisement { if defaultAdvertisement.adapter == nil { defaultAdvertisement.adapter = a - - a.AddService( - &Service{ - UUID: ServiceUUIDGenericAccess, - Characteristics: []CharacteristicConfig{ - { - UUID: CharacteristicUUIDDeviceName, - Flags: CharacteristicReadPermission, - }, - { - UUID: CharacteristicUUIDAppearance, - Flags: CharacteristicReadPermission, - }, - }, - }) - a.AddService( - &Service{ - UUID: ServiceUUIDGenericAttribute, - Characteristics: []CharacteristicConfig{ - { - UUID: CharacteristicUUIDServiceChanged, - Flags: CharacteristicIndicatePermission, - }, - }, - }) } return &defaultAdvertisement @@ -308,10 +287,51 @@ func (a *Adapter) DefaultAdvertisement() *Advertisement { // Configure this advertisement. func (a *Advertisement) Configure(options AdvertisementOptions) error { - // uint8_t type = (_connectable) ? 0x00 : (_localName ? 0x02 : 0x03); + switch { + case options.LocalName != "": + a.localName = []byte(options.LocalName) + default: + a.localName = []byte("TinyGo") + } + + a.serviceUUIDs = append([]UUID{}, options.ServiceUUIDs...) + a.interval = uint16(options.Interval) + + a.adapter.AddService( + &Service{ + UUID: ServiceUUIDGenericAccess, + Characteristics: []CharacteristicConfig{ + { + UUID: CharacteristicUUIDDeviceName, + Flags: CharacteristicReadPermission, + Value: a.localName, + }, + { + UUID: CharacteristicUUIDAppearance, + Flags: CharacteristicReadPermission, + }, + }, + }) + a.adapter.AddService( + &Service{ + UUID: ServiceUUIDGenericAttribute, + Characteristics: []CharacteristicConfig{ + { + UUID: CharacteristicUUIDServiceChanged, + Flags: CharacteristicIndicatePermission, + }, + }, + }) + + return nil +} + +// Start advertisement. May only be called after it has been configured. +func (a *Advertisement) Start() error { + // uint8_t type = (_connectable) ? 0x00 : (_localName ? 0x02 : 0x03); typ := uint8(0x00) - if err := a.adapter.hci.leSetAdvertisingParameters(uint16(options.Interval), uint16(options.Interval), + if err := a.adapter.hci.leSetAdvertisingParameters(a.interval, a.interval, typ, 0x00, 0x00, [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x07, 0); err != nil { return err } @@ -325,8 +345,8 @@ func (a *Advertisement) Configure(options AdvertisementOptions) error { advertisingDataLen += 3 // TODO: handle multiple service UUIDs - if len(options.ServiceUUIDs) > 0 { - uuid := options.ServiceUUIDs[0] + if len(a.serviceUUIDs) > 0 { + uuid := a.serviceUUIDs[0] var sz uint8 switch { @@ -355,23 +375,22 @@ func (a *Advertisement) Configure(options AdvertisementOptions) error { scanResponseDataLen := uint8(0) switch { - case len(options.LocalName) > 29: + case len(a.localName) > 29: scanResponseData[1] = 0x08 scanResponseData[0] = 1 + 29 - copy(scanResponseData[2:], options.LocalName[:29]) + copy(scanResponseData[2:], a.localName[:29]) scanResponseDataLen = 31 - case len(options.LocalName) > 0: + case len(a.localName) > 0: scanResponseData[1] = 0x09 - scanResponseData[0] = uint8(1 + len(options.LocalName)) - copy(scanResponseData[2:], options.LocalName) - scanResponseDataLen = uint8(2 + len(options.LocalName)) + scanResponseData[0] = uint8(1 + len(a.localName)) + copy(scanResponseData[2:], a.localName) + scanResponseDataLen = uint8(2 + len(a.localName)) } - return a.adapter.hci.leSetScanResponseData(scanResponseData[:scanResponseDataLen]) -} + if err := a.adapter.hci.leSetScanResponseData(scanResponseData[:scanResponseDataLen]); err != nil { + return err + } -// Start advertisement. May only be called after it has been configured. -func (a *Advertisement) Start() error { if err := a.adapter.hci.leSetAdvertiseEnable(true); err != nil { return err } diff --git a/gatts_ninafw.go b/gatts_ninafw.go index 5b83786..3bf454a 100644 --- a/gatts_ninafw.go +++ b/gatts_ninafw.go @@ -42,10 +42,14 @@ func (a *Adapter) AddService(service *Service) error { endHandle = a.att.addLocalAttribute(attributeTypeDescriptor, charHandle, shortUUID(gattClientCharacteristicConfigUUID).UUID(), CharacteristicReadPermission|CharacteristicWritePermission, []byte{0, 0}) } - if service.Characteristics[i].Handle != nil { - service.Characteristics[i].Handle.adapter = a - service.Characteristics[i].Handle.handle = valueHandle - service.Characteristics[i].Handle.permissions = service.Characteristics[i].Flags + if service.Characteristics[i].Handle == nil { + service.Characteristics[i].Handle = &Characteristic{} + } + + service.Characteristics[i].Handle.adapter = a + service.Characteristics[i].Handle.handle = valueHandle + service.Characteristics[i].Handle.permissions = service.Characteristics[i].Flags + if len(service.Characteristics[i].Value) > 0 { service.Characteristics[i].Handle.value = service.Characteristics[i].Value }