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 <ron@hybridgroup.com>
This commit is contained in:
deadprogram 2024-01-16 20:45:45 +01:00 committed by Ron Evans
parent 564b0ba58f
commit bf647ecd57
4 changed files with 71 additions and 44 deletions

View file

@ -171,7 +171,7 @@ func (a *Adapter) startNotifications() {
}
}
time.Sleep(250 * time.Millisecond)
time.Sleep(10 * time.Millisecond)
}
}()

View file

@ -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
}
}

View file

@ -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,14 +280,31 @@ type Advertisement struct {
func (a *Adapter) DefaultAdvertisement() *Advertisement {
if defaultAdvertisement.adapter == nil {
defaultAdvertisement.adapter = a
}
a.AddService(
return &defaultAdvertisement
}
// Configure this advertisement.
func (a *Advertisement) Configure(options AdvertisementOptions) error {
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,
@ -291,7 +312,7 @@ func (a *Adapter) DefaultAdvertisement() *Advertisement {
},
},
})
a.AddService(
a.adapter.AddService(
&Service{
UUID: ServiceUUIDGenericAttribute,
Characteristics: []CharacteristicConfig{
@ -301,17 +322,16 @@ func (a *Adapter) DefaultAdvertisement() *Advertisement {
},
},
})
}
return &defaultAdvertisement
return nil
}
// Configure this advertisement.
func (a *Advertisement) Configure(options AdvertisementOptions) error {
// 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
}

View file

@ -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 {
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
}