Skip to content

Commit

Permalink
ninafw: can read characteristic values
Browse files Browse the repository at this point in the history
Signed-off-by: deadprogram <[email protected]>
  • Loading branch information
deadprogram committed Dec 30, 2023
1 parent 539631c commit 8fb540e
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 10 deletions.
47 changes: 42 additions & 5 deletions att_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,15 @@ type att struct {
lastErrorCode uint8
services []rawService
characteristics []rawCharacteristic
value []byte
}

func newATT(hci *hci) *att {
return &att{
hci: hci,
services: []rawService{},
characteristics: []rawCharacteristic{},
value: []byte{},
}
}

Expand All @@ -134,8 +136,6 @@ func (a *att) readByGroupReq(connectionHandle, startHandle, endHandle uint16, uu

switch {
case a.responded:
a.clearResponse()

return nil

default:
Expand Down Expand Up @@ -174,8 +174,42 @@ func (a *att) readByTypeReq(connectionHandle, startHandle, endHandle uint16, typ

switch {
case a.responded:
a.clearResponse()
return nil

default:
// check for timeout
if (time.Now().UnixNano()-start)/int64(time.Second) > 3 {
break
}

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

return ErrATTTimeout
}

func (a *att) readReq(connectionHandle, valueHandle uint16) error {
if _debug {
println("att.readReq:", connectionHandle, valueHandle)
}

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

if err := a.sendReq(connectionHandle, b[:]); err != nil {
return err
}

start := time.Now().UnixNano()
for {
if err := a.hci.poll(); err != nil {
return err
}

switch {
case a.responded:
return nil

default:
Expand Down Expand Up @@ -325,7 +359,7 @@ func (a *att) handleData(handle uint16, buf []byte) error {

case attOpReadReq:
if _debug {
println("att.handleData: attOpREADReq")
println("att.handleData: attOpReadReq")
}

case attOpReadBlobReq:
Expand All @@ -335,8 +369,10 @@ func (a *att) handleData(handle uint16, buf []byte) error {

case attOpReadResponse:
if _debug {
println("att.handleData: attOpREADResponse")
println("att.handleData: attOpReadResponse")
}
a.responded = true
a.value = append(a.value, buf[1:]...)

case attOpWriteReq:
if _debug {
Expand Down Expand Up @@ -403,4 +439,5 @@ func (a *att) clearResponse() {
a.lastErrorOpcode = 0
a.lastErrorHandle = 0
a.lastErrorCode = 0
a.value = []byte{}
}
68 changes: 63 additions & 5 deletions gattc_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@ package bluetooth
import "errors"

var (
errNotYetImplemented = errors.New("not yet implemented")
errNotYetImplemented = errors.New("bluetooth: not yet implemented")
errNoWrite = errors.New("bluetooth: write not permitted")
errNoWriteWithoutResponse = errors.New("bluetooth: write without response not permitted")
errWriteFailed = errors.New("bluetooth: write failed")
errNoRead = errors.New("bluetooth: read not permitted")
errReadFailed = errors.New("bluetooth: read failed")
errNoNotify = errors.New("bluetooth: notify/indicate not permitted")
errEnableNotificationsFailed = errors.New("bluetooth: enable notifications failed")
)

const (
charPropertyBroadcast = 0x01
charPropertyRead = 0x02
charPropertyWriteWithoutResponse = 0x04
charPropertyWrite = 0x08
charPropertyNotify = 0x10
charPropertyIndicate = 0x20
)

// DeviceService is a BLE service on a connected peripheral device.
Expand Down Expand Up @@ -79,7 +95,10 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
type DeviceCharacteristic struct {
uuid UUID

service *DeviceService
service *DeviceService
permissions CharacteristicPermissions
handle uint16
properties uint8
}

// UUID returns the UUID for this DeviceCharacteristic.
Expand Down Expand Up @@ -124,10 +143,32 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter

if len(s.device.adapter.att.characteristics) > 0 {
for _, rawCharacteristic := range s.device.adapter.att.characteristics {
permissions := CharacteristicPermissions(0)
if rawCharacteristic.properties&charPropertyBroadcast != 0 {
permissions |= CharacteristicBroadcastPermission
}
if rawCharacteristic.properties&charPropertyRead != 0 {
permissions |= CharacteristicReadPermission
}
if rawCharacteristic.properties&charPropertyWriteWithoutResponse != 0 {
permissions |= CharacteristicWriteWithoutResponsePermission
}
if rawCharacteristic.properties&charPropertyWrite != 0 {
permissions |= CharacteristicWritePermission
}
if rawCharacteristic.properties&charPropertyNotify != 0 {
permissions |= CharacteristicNotifyPermission
}
if rawCharacteristic.properties&charPropertyIndicate != 0 {
permissions |= CharacteristicIndicatePermission
}
s.characteristics = append(s.characteristics,
DeviceCharacteristic{
service: s,
uuid: rawCharacteristic.uuid,
service: s,
uuid: rawCharacteristic.uuid,
handle: rawCharacteristic.valueHandle,
properties: rawCharacteristic.properties,
permissions: permissions,
})
startHandle = rawCharacteristic.valueHandle + 1
}
Expand Down Expand Up @@ -167,5 +208,22 @@ func (c DeviceCharacteristic) GetMTU() (uint16, error) {

// Read reads the current characteristic value.
func (c *DeviceCharacteristic) Read(data []byte) (int, error) {
return 0, errNotYetImplemented
if !c.permissions.Read() {
return 0, errNoRead
}

defer c.service.device.adapter.att.clearResponse()

err := c.service.device.adapter.att.readReq(c.service.device.handle, c.handle)
if err != nil {
return 0, err
}

if len(c.service.device.adapter.att.value) == 0 {
return 0, errReadFailed
}

copy(data, c.service.device.adapter.att.value)

return len(c.service.device.adapter.att.value), nil
}

0 comments on commit 8fb540e

Please sign in to comment.