2023-12-21 01:29:23 +03:00
|
|
|
//go:build ninafw
|
|
|
|
|
|
|
|
package bluetooth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"slices"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
attOpError = 0x01
|
|
|
|
attOpMTUReq = 0x02
|
|
|
|
attOpMTUResponse = 0x03
|
|
|
|
attOpFindInfoReq = 0x04
|
|
|
|
attOpFindInfoResponse = 0x05
|
|
|
|
attOpFindByTypeReq = 0x06
|
|
|
|
attOpFindByTypeResponse = 0x07
|
|
|
|
attOpReadByTypeReq = 0x08
|
|
|
|
attOpReadByTypeResponse = 0x09
|
|
|
|
attOpReadReq = 0x0a
|
|
|
|
attOpReadResponse = 0x0b
|
|
|
|
attOpReadBlobReq = 0x0c
|
|
|
|
attOpReadBlobResponse = 0x0d
|
|
|
|
attOpReadMultiReq = 0x0e
|
|
|
|
attOpReadMultiResponse = 0x0f
|
|
|
|
attOpReadByGroupReq = 0x10
|
|
|
|
attOpReadByGroupResponse = 0x11
|
|
|
|
attOpWriteReq = 0x12
|
|
|
|
attOpWriteResponse = 0x13
|
|
|
|
attOpWriteCmd = 0x52
|
|
|
|
attOpPrepWriteReq = 0x16
|
|
|
|
attOpPrepWriteResponse = 0x17
|
|
|
|
attOpExecWriteReq = 0x18
|
|
|
|
attOpExecWriteResponse = 0x19
|
|
|
|
attOpHandleNotify = 0x1b
|
|
|
|
attOpHandleInd = 0x1d
|
|
|
|
attOpHandleCNF = 0x1e
|
|
|
|
attOpSignedWriteCmd = 0xd2
|
|
|
|
|
|
|
|
attErrorInvalidHandle = 0x01
|
|
|
|
attErrorReadNotPermitted = 0x02
|
|
|
|
attErrorWriteNotPermitted = 0x03
|
|
|
|
attErrorInvalidPDU = 0x04
|
|
|
|
attErrorAuthentication = 0x05
|
|
|
|
attErrorRequestNotSupported = 0x06
|
|
|
|
attErrorInvalidOffset = 0x07
|
|
|
|
attErrorAuthorization = 0x08
|
|
|
|
attErrorPreQueueFull = 0x09
|
|
|
|
attErrorAttrNotFound = 0x0a
|
|
|
|
attErrorAttrNotLong = 0x0b
|
|
|
|
attErrorInsuffEncrKeySize = 0x0c
|
|
|
|
attErrorInvalidAttrValueLength = 0x0d
|
|
|
|
attErrorUnlikely = 0x0e
|
|
|
|
attErrorInsuffEnc = 0x0f
|
|
|
|
attErrorUnsupportedGroupType = 0x10
|
|
|
|
attErrorInsufficientResources = 0x11
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
gattUnknownUUID = 0x0000
|
|
|
|
gattServiceUUID = 0x2800
|
|
|
|
gattCharacteristicUUID = 0x2803
|
|
|
|
gattDescriptorUUID = 0x2900
|
|
|
|
gattClientCharacteristicConfigUUID = 0x2902
|
2023-12-21 01:29:23 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrATTTimeout = errors.New("bluetooth: ATT timeout")
|
|
|
|
ErrATTUnknownEvent = errors.New("bluetooth: ATT unknown event")
|
|
|
|
ErrATTUnknown = errors.New("bluetooth: ATT unknown error")
|
|
|
|
ErrATTOp = errors.New("bluetooth: ATT OP error")
|
|
|
|
)
|
|
|
|
|
|
|
|
type rawService struct {
|
|
|
|
startHandle uint16
|
|
|
|
endHandle uint16
|
|
|
|
uuid UUID
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
func (s *rawService) Write(buf []byte) (int, error) {
|
|
|
|
s.startHandle = binary.LittleEndian.Uint16(buf[0:])
|
|
|
|
s.endHandle = binary.LittleEndian.Uint16(buf[2:])
|
|
|
|
|
|
|
|
sz := 4
|
|
|
|
switch len(buf) - 4 {
|
|
|
|
case 2:
|
|
|
|
s.uuid = New16BitUUID(binary.LittleEndian.Uint16(buf[4:]))
|
|
|
|
sz += 2
|
|
|
|
case 16:
|
|
|
|
var uuid [16]byte
|
|
|
|
copy(uuid[:], buf[4:])
|
|
|
|
slices.Reverse(uuid[:])
|
|
|
|
s.uuid = NewUUID(uuid)
|
|
|
|
sz += 16
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *rawService) Read(p []byte) (int, error) {
|
|
|
|
binary.LittleEndian.PutUint16(p[0:], s.startHandle)
|
|
|
|
binary.LittleEndian.PutUint16(p[2:], s.endHandle)
|
|
|
|
|
|
|
|
sz := 4
|
|
|
|
switch {
|
|
|
|
case s.uuid.Is16Bit():
|
|
|
|
binary.LittleEndian.PutUint16(p[4:], s.uuid.Get16Bit())
|
|
|
|
sz += 2
|
|
|
|
default:
|
|
|
|
uuid := s.uuid.Bytes()
|
|
|
|
copy(p[4:], uuid[:])
|
|
|
|
sz += 16
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz, nil
|
|
|
|
}
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
type rawCharacteristic struct {
|
|
|
|
startHandle uint16
|
|
|
|
properties uint8
|
|
|
|
valueHandle uint16
|
|
|
|
uuid UUID
|
2024-01-13 17:38:46 +03:00
|
|
|
chr *Characteristic
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *rawCharacteristic) Write(buf []byte) (int, error) {
|
|
|
|
c.startHandle = binary.LittleEndian.Uint16(buf[0:])
|
|
|
|
c.properties = buf[2]
|
|
|
|
c.valueHandle = binary.LittleEndian.Uint16(buf[3:])
|
|
|
|
|
|
|
|
sz := 5
|
|
|
|
switch len(buf) - 5 {
|
|
|
|
case 2:
|
|
|
|
c.uuid = New16BitUUID(binary.LittleEndian.Uint16(buf[5:]))
|
|
|
|
sz += 2
|
|
|
|
case 16:
|
|
|
|
var uuid [16]byte
|
|
|
|
copy(uuid[:], buf[5:])
|
|
|
|
slices.Reverse(uuid[:])
|
|
|
|
c.uuid = NewUUID(uuid)
|
|
|
|
sz += 16
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *rawCharacteristic) Read(p []byte) (int, error) {
|
|
|
|
binary.LittleEndian.PutUint16(p[0:], c.startHandle)
|
|
|
|
p[2] = c.properties
|
|
|
|
binary.LittleEndian.PutUint16(p[3:], c.valueHandle)
|
|
|
|
|
|
|
|
sz := 5
|
|
|
|
switch {
|
|
|
|
case c.uuid.Is16Bit():
|
|
|
|
binary.LittleEndian.PutUint16(p[5:], c.uuid.Get16Bit())
|
|
|
|
sz += 2
|
|
|
|
default:
|
|
|
|
uuid := c.uuid.Bytes()
|
|
|
|
copy(p[5:], uuid[:])
|
|
|
|
sz += 16
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz, nil
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type rawDescriptor struct {
|
|
|
|
handle uint16
|
2024-01-13 17:38:46 +03:00
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *rawDescriptor) Write(buf []byte) (int, error) {
|
|
|
|
d.handle = binary.LittleEndian.Uint16(buf[0:])
|
|
|
|
d.data = append(d.data, buf[2:]...)
|
|
|
|
|
|
|
|
return len(d.data) + 2, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *rawDescriptor) Read(p []byte) (int, error) {
|
|
|
|
binary.LittleEndian.PutUint16(p[0:], d.handle)
|
|
|
|
copy(p[2:], d.data)
|
|
|
|
|
|
|
|
return len(d.data) + 2, nil
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type rawNotification struct {
|
|
|
|
connectionHandle uint16
|
|
|
|
handle uint16
|
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
type attributeType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
attributeTypeService attributeType = iota
|
|
|
|
attributeTypeCharacteristic
|
|
|
|
attributeTypeCharacteristicValue
|
|
|
|
attributeTypeDescriptor
|
|
|
|
)
|
|
|
|
|
|
|
|
type rawAttribute struct {
|
|
|
|
typ attributeType
|
|
|
|
parent uint16
|
|
|
|
handle uint16
|
|
|
|
uuid UUID
|
|
|
|
permissions CharacteristicPermissions
|
|
|
|
value []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *rawAttribute) Write(buf []byte) (int, error) {
|
|
|
|
return 0, errNotYetImplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *rawAttribute) Read(p []byte) (int, error) {
|
|
|
|
binary.LittleEndian.PutUint16(p[0:], a.handle)
|
|
|
|
sz := 2
|
|
|
|
|
|
|
|
switch a.typ {
|
|
|
|
case attributeTypeCharacteristicValue, attributeTypeDescriptor:
|
|
|
|
switch {
|
|
|
|
case a.uuid.Is16Bit():
|
|
|
|
binary.LittleEndian.PutUint16(p[sz:], a.uuid.Get16Bit())
|
|
|
|
sz += 2
|
|
|
|
default:
|
|
|
|
uuid := a.uuid.Bytes()
|
|
|
|
copy(p[sz:], uuid[:])
|
|
|
|
sz += 16
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
copy(p[sz:], a.value)
|
|
|
|
sz += len(a.value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *rawAttribute) length() int {
|
|
|
|
switch a.typ {
|
|
|
|
case attributeTypeCharacteristicValue, attributeTypeDescriptor:
|
|
|
|
switch {
|
|
|
|
case a.uuid.Is16Bit():
|
|
|
|
return 2
|
|
|
|
default:
|
|
|
|
return 16
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return len(a.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
type att struct {
|
|
|
|
hci *hci
|
|
|
|
busy sync.Mutex
|
|
|
|
responded bool
|
|
|
|
errored bool
|
|
|
|
lastErrorOpcode uint8
|
|
|
|
lastErrorHandle uint16
|
|
|
|
lastErrorCode uint8
|
2024-01-04 00:14:24 +03:00
|
|
|
mtu uint16
|
2024-01-21 15:04:26 +03:00
|
|
|
maxMTU uint16
|
2023-12-21 01:29:23 +03:00
|
|
|
services []rawService
|
|
|
|
characteristics []rawCharacteristic
|
|
|
|
descriptors []rawDescriptor
|
|
|
|
value []byte
|
|
|
|
notifications chan rawNotification
|
2024-01-13 17:38:46 +03:00
|
|
|
|
|
|
|
connections []uint16
|
|
|
|
lastHandle uint16
|
|
|
|
attributes []rawAttribute
|
|
|
|
localServices []rawService
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func newATT(hci *hci) *att {
|
|
|
|
return &att{
|
|
|
|
hci: hci,
|
|
|
|
services: []rawService{},
|
|
|
|
characteristics: []rawCharacteristic{},
|
|
|
|
value: []byte{},
|
|
|
|
notifications: make(chan rawNotification, 32),
|
2024-01-13 17:38:46 +03:00
|
|
|
connections: []uint16{},
|
|
|
|
lastHandle: 0x0001,
|
|
|
|
attributes: []rawAttribute{},
|
|
|
|
localServices: []rawService{},
|
2024-01-21 15:04:26 +03:00
|
|
|
maxMTU: 248,
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) readByGroupReq(connectionHandle, startHandle, endHandle uint16, uuid shortUUID) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.readByGroupReq:", connectionHandle, startHandle, endHandle, uuid)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [7]byte
|
|
|
|
b[0] = attOpReadByGroupReq
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], startHandle)
|
|
|
|
binary.LittleEndian.PutUint16(b[3:], endHandle)
|
|
|
|
binary.LittleEndian.PutUint16(b[5:], uint16(uuid))
|
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.waitUntilResponse()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) readByTypeReq(connectionHandle, startHandle, endHandle uint16, typ uint16) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.readByTypeReq:", connectionHandle, startHandle, endHandle, typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [7]byte
|
|
|
|
b[0] = attOpReadByTypeReq
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], startHandle)
|
|
|
|
binary.LittleEndian.PutUint16(b[3:], endHandle)
|
|
|
|
binary.LittleEndian.PutUint16(b[5:], typ)
|
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.waitUntilResponse()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) findInfoReq(connectionHandle, startHandle, endHandle uint16) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.findInfoReq:", connectionHandle, startHandle, endHandle)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [5]byte
|
|
|
|
b[0] = attOpFindInfoReq
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], startHandle)
|
|
|
|
binary.LittleEndian.PutUint16(b[3:], endHandle)
|
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.waitUntilResponse()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) readReq(connectionHandle, valueHandle uint16) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.readReq:", connectionHandle, valueHandle)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [3]byte
|
|
|
|
b[0] = attOpReadReq
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], valueHandle)
|
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.waitUntilResponse()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) writeCmd(connectionHandle, valueHandle uint16, data []byte) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.writeCmd:", connectionHandle, valueHandle, hex.EncodeToString(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [3]byte
|
|
|
|
b[0] = attOpWriteCmd
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], valueHandle)
|
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, append(b[:], data...)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-01-21 15:04:26 +03:00
|
|
|
return nil
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) writeReq(connectionHandle, valueHandle uint16, data []byte) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.writeReq:", connectionHandle, valueHandle, hex.EncodeToString(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [3]byte
|
|
|
|
b[0] = attOpWriteReq
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], valueHandle)
|
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, append(b[:], data...)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.waitUntilResponse()
|
|
|
|
}
|
|
|
|
|
2024-01-21 15:04:26 +03:00
|
|
|
func (a *att) mtuReq(connectionHandle uint16) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-04 00:14:24 +03:00
|
|
|
println("att.mtuReq:", connectionHandle)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [3]byte
|
|
|
|
b[0] = attOpMTUReq
|
2024-01-21 15:04:26 +03:00
|
|
|
binary.LittleEndian.PutUint16(b[1:], a.mtu)
|
2024-01-04 00:14:24 +03:00
|
|
|
|
|
|
|
if err := a.sendReq(connectionHandle, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.waitUntilResponse()
|
|
|
|
}
|
|
|
|
|
2024-01-21 15:04:26 +03:00
|
|
|
func (a *att) setMaxMTU(mtu uint16) error {
|
|
|
|
a.maxMTU = mtu
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
func (a *att) sendReq(handle uint16, data []byte) error {
|
|
|
|
a.clearResponse()
|
|
|
|
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.sendReq:", handle, "data:", hex.EncodeToString(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
func (a *att) sendNotification(handle uint16, data []byte) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.sendNotifications:", handle, "data:", hex.EncodeToString(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
var b [3]byte
|
|
|
|
b[0] = attOpHandleNotify
|
|
|
|
binary.LittleEndian.PutUint16(b[1:], handle)
|
|
|
|
|
|
|
|
for connection := range a.connections {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.sendNotifications: sending to", connection)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(uint16(connection), attCID, append(b[:], data...)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) sendError(handle uint16, opcode uint8, hdl uint16, code uint8) error {
|
|
|
|
a.clearResponse()
|
|
|
|
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.sendError:", handle, "data:", opcode, hdl, code)
|
|
|
|
}
|
|
|
|
|
|
|
|
var b [5]byte
|
|
|
|
b[0] = attOpError
|
|
|
|
b[1] = opcode
|
|
|
|
binary.LittleEndian.PutUint16(b[2:], hdl)
|
|
|
|
b[4] = code
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, b[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
func (a *att) handleData(handle uint16, buf []byte) error {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData:", handle, "data:", hex.EncodeToString(buf))
|
|
|
|
}
|
|
|
|
|
|
|
|
switch buf[0] {
|
|
|
|
case attOpError:
|
|
|
|
a.errored = true
|
|
|
|
a.lastErrorOpcode = buf[1]
|
|
|
|
a.lastErrorHandle = binary.LittleEndian.Uint16(buf[2:])
|
|
|
|
a.lastErrorCode = buf[4]
|
|
|
|
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpERROR", a.lastErrorOpcode, a.lastErrorCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ErrATTOp
|
|
|
|
|
|
|
|
case attOpMTUReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-21 15:04:26 +03:00
|
|
|
println("att.handleData: attOpMTUReq", hex.EncodeToString(buf))
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
2024-01-21 15:04:26 +03:00
|
|
|
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 {
|
2024-01-13 17:38:46 +03:00
|
|
|
return err
|
|
|
|
}
|
2023-12-21 01:29:23 +03:00
|
|
|
|
|
|
|
case attOpMTUResponse:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpMTUResponse")
|
|
|
|
}
|
2024-01-04 00:14:24 +03:00
|
|
|
a.responded = true
|
|
|
|
a.mtu = binary.LittleEndian.Uint16(buf[1:])
|
2023-12-21 01:29:23 +03:00
|
|
|
|
|
|
|
case attOpFindInfoReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpFindInfoReq")
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
startHandle := binary.LittleEndian.Uint16(buf[1:])
|
|
|
|
endHandle := binary.LittleEndian.Uint16(buf[3:])
|
|
|
|
|
|
|
|
return a.handleFindInfoReq(handle, startHandle, endHandle)
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
case attOpFindInfoResponse:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpFindInfoResponse")
|
|
|
|
}
|
|
|
|
a.responded = true
|
|
|
|
|
|
|
|
lengthPerDescriptor := int(buf[1])
|
|
|
|
|
|
|
|
for i := 2; i < len(buf); i += lengthPerDescriptor {
|
2024-01-13 17:38:46 +03:00
|
|
|
d := rawDescriptor{}
|
|
|
|
d.Write(buf[i : i+lengthPerDescriptor])
|
2023-12-21 01:29:23 +03:00
|
|
|
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleData: descriptor", d.handle, hex.EncodeToString(d.data))
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
a.descriptors = append(a.descriptors, d)
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpFindByTypeReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpFindByTypeReq")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpReadByTypeReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadByTypeReq")
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
startHandle := binary.LittleEndian.Uint16(buf[1:])
|
|
|
|
endHandle := binary.LittleEndian.Uint16(buf[3:])
|
|
|
|
uuid := shortUUID(binary.LittleEndian.Uint16(buf[5:]))
|
|
|
|
|
|
|
|
return a.handleReadByTypeReq(handle, startHandle, endHandle, uuid)
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
case attOpReadByTypeResponse:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadByTypeResponse")
|
|
|
|
}
|
|
|
|
a.responded = true
|
|
|
|
|
|
|
|
lengthPerCharacteristic := int(buf[1])
|
|
|
|
|
|
|
|
for i := 2; i < len(buf); i += lengthPerCharacteristic {
|
2024-01-13 17:38:46 +03:00
|
|
|
c := rawCharacteristic{}
|
|
|
|
c.Write(buf[i : i+lengthPerCharacteristic])
|
2023-12-21 01:29:23 +03:00
|
|
|
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: characteristic", c.startHandle, c.properties, c.valueHandle, c.uuid.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
a.characteristics = append(a.characteristics, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
case attOpReadByGroupReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadByGroupReq")
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
startHandle := binary.LittleEndian.Uint16(buf[1:])
|
|
|
|
endHandle := binary.LittleEndian.Uint16(buf[3:])
|
|
|
|
uuid := shortUUID(binary.LittleEndian.Uint16(buf[5:]))
|
2023-12-21 01:29:23 +03:00
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
return a.handleReadByGroupReq(handle, startHandle, endHandle, uuid)
|
2023-12-21 01:29:23 +03:00
|
|
|
|
|
|
|
case attOpReadByGroupResponse:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadByGroupResponse")
|
|
|
|
}
|
|
|
|
a.responded = true
|
|
|
|
|
|
|
|
lengthPerService := int(buf[1])
|
|
|
|
|
|
|
|
for i := 2; i < len(buf); i += lengthPerService {
|
2024-01-13 17:38:46 +03:00
|
|
|
service := rawService{}
|
|
|
|
service.Write(buf[i : i+lengthPerService])
|
2023-12-21 01:29:23 +03:00
|
|
|
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: service", service.startHandle, service.endHandle, service.uuid.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
a.services = append(a.services, service)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
case attOpReadReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadReq")
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
attrHandle := binary.LittleEndian.Uint16(buf[1:])
|
|
|
|
return a.handleReadReq(handle, attrHandle)
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
case attOpReadBlobReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadBlobReq")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpReadResponse:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadResponse")
|
|
|
|
}
|
|
|
|
a.responded = true
|
|
|
|
a.value = append(a.value, buf[1:]...)
|
|
|
|
|
|
|
|
case attOpWriteReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpWriteReq")
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
attrHandle := binary.LittleEndian.Uint16(buf[1:])
|
|
|
|
return a.handleWriteReq(handle, attrHandle, buf[3:])
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
case attOpWriteCmd:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpWriteCmd")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpWriteResponse:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpWriteResponse")
|
|
|
|
}
|
|
|
|
a.responded = true
|
|
|
|
|
|
|
|
case attOpPrepWriteReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpPrepWriteReq")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpExecWriteReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpExecWriteReq")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpHandleNotify:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpHandleNotify")
|
|
|
|
}
|
|
|
|
|
|
|
|
not := rawNotification{
|
|
|
|
connectionHandle: handle,
|
|
|
|
handle: binary.LittleEndian.Uint16(buf[1:]),
|
|
|
|
data: []byte{},
|
|
|
|
}
|
|
|
|
not.data = append(not.data, buf[3:]...)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case a.notifications <- not:
|
|
|
|
default:
|
|
|
|
// out of space, drop notification :(
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpHandleInd:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpHandleInd")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpHandleCNF:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpHandleCNF")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpReadMultiReq:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpReadMultiReq")
|
|
|
|
}
|
|
|
|
|
|
|
|
case attOpSignedWriteCmd:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: attOpSignedWriteCmd")
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2023-12-21 01:29:23 +03:00
|
|
|
println("att.handleData: unknown")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-13 17:38:46 +03:00
|
|
|
func (a *att) handleReadByGroupReq(handle, start, end uint16, uuid shortUUID) error {
|
|
|
|
var response [64]byte
|
|
|
|
response[0] = attOpReadByGroupResponse
|
|
|
|
response[1] = 0x0 // length per service
|
|
|
|
pos := 2
|
|
|
|
|
|
|
|
switch uuid {
|
|
|
|
case shortUUID(gattServiceUUID):
|
|
|
|
for _, s := range a.localServices {
|
|
|
|
if s.startHandle >= start && s.endHandle <= end {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("attOpReadByGroupReq: replying with service", s.startHandle, s.endHandle, s.uuid.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
length := 20
|
|
|
|
if s.uuid.Is16Bit() {
|
|
|
|
length = 6
|
|
|
|
}
|
|
|
|
|
|
|
|
if response[1] == 0 {
|
|
|
|
response[1] = byte(length)
|
|
|
|
} else if response[1] != byte(length) {
|
|
|
|
// change of UUID size
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Read(response[pos : pos+length])
|
|
|
|
pos += length
|
|
|
|
|
|
|
|
if uint16(pos+length) > a.mtu {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case pos > 2:
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if err := a.sendError(handle, attOpReadByGroupReq, start, attErrorAttrNotFound); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
default:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("handleReadByGroupReq: unknown uuid", New16BitUUID(uint16(uuid)).String())
|
|
|
|
}
|
|
|
|
if err := a.sendError(handle, attOpReadByGroupReq, start, attErrorAttrNotFound); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) handleReadByTypeReq(handle, start, end uint16, uuid shortUUID) error {
|
|
|
|
var response [64]byte
|
|
|
|
response[0] = attOpReadByTypeResponse
|
|
|
|
pos := 0
|
|
|
|
|
|
|
|
switch uuid {
|
|
|
|
case shortUUID(gattCharacteristicUUID):
|
|
|
|
pos = 2
|
|
|
|
response[1] = 0
|
|
|
|
|
|
|
|
for _, c := range a.characteristics {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("handleReadByTypeReq: looking at characteristic", c.startHandle, c.uuid.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.startHandle >= start && c.valueHandle <= end {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("handleReadByTypeReq: replying with characteristic", c.startHandle, c.uuid.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
length := 21
|
|
|
|
if c.uuid.Is16Bit() {
|
|
|
|
length = 7
|
|
|
|
}
|
|
|
|
|
|
|
|
if response[1] == 0 {
|
|
|
|
response[1] = byte(length)
|
|
|
|
} else if response[1] != byte(length) {
|
|
|
|
// change of UUID size
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Read(response[pos : pos+length])
|
|
|
|
pos += length
|
|
|
|
|
|
|
|
if uint16(pos+length) > a.mtu {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case pos > 2:
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if err := a.sendError(handle, attOpReadByTypeReq, start, attErrorAttrNotFound); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
default:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("handleReadByTypeReq: unknown uuid", New16BitUUID(uint16(uuid)).String())
|
|
|
|
}
|
|
|
|
if err := a.sendError(handle, attOpReadByTypeReq, start, attErrorAttrNotFound); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) handleFindInfoReq(handle, start, end uint16) error {
|
|
|
|
var response [64]byte
|
|
|
|
response[0] = attOpFindInfoResponse
|
|
|
|
pos := 0
|
|
|
|
|
|
|
|
pos = 2
|
|
|
|
infoType := 0
|
|
|
|
response[1] = 0
|
|
|
|
|
|
|
|
for _, attr := range a.attributes {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("handleFindInfoReq: looking at attribute")
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr.handle >= start && attr.handle <= end {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("handleFindInfoReq: replying with attribute", attr.handle, attr.uuid.String(), attr.typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr.typ == attributeTypeCharacteristicValue || attr.typ == attributeTypeDescriptor {
|
|
|
|
infoType = 1
|
|
|
|
} else {
|
|
|
|
infoType = 2
|
|
|
|
}
|
|
|
|
|
|
|
|
length := attr.length() + 2
|
|
|
|
if response[1] == 0 {
|
|
|
|
response[1] = byte(infoType)
|
|
|
|
} else if response[1] != byte(infoType) {
|
|
|
|
// change of info type
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
attr.Read(response[pos : pos+length])
|
|
|
|
pos += length
|
|
|
|
|
|
|
|
if uint16(pos+length) >= a.mtu {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case pos > 2:
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if err := a.sendError(handle, attOpFindInfoReq, start, attErrorAttrNotFound); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) handleReadReq(handle, attrHandle uint16) error {
|
|
|
|
attr := a.findAttribute(attrHandle)
|
|
|
|
if attr == nil {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleReadReq: attribute not found", attrHandle)
|
|
|
|
}
|
|
|
|
return a.sendError(handle, attOpReadReq, attrHandle, attErrorAttrNotFound)
|
|
|
|
}
|
|
|
|
|
|
|
|
var response [64]byte
|
|
|
|
response[0] = attOpReadResponse
|
|
|
|
pos := 1
|
|
|
|
|
|
|
|
switch attr.typ {
|
|
|
|
case attributeTypeCharacteristicValue:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleReadReq: reading characteristic value", attrHandle)
|
|
|
|
}
|
|
|
|
|
|
|
|
c := a.findCharacteristic(attr.parent)
|
|
|
|
if c != nil && c.chr != nil {
|
|
|
|
value, err := c.chr.readValue()
|
|
|
|
if err != nil {
|
|
|
|
return a.sendError(handle, attOpReadReq, attrHandle, attErrorReadNotPermitted)
|
|
|
|
}
|
|
|
|
|
|
|
|
copy(response[pos:], value)
|
|
|
|
pos += len(value)
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-16 22:45:45 +03:00
|
|
|
|
|
|
|
return nil
|
2024-01-13 17:38:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
case attributeTypeDescriptor:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleReadReq: reading descriptor", attrHandle)
|
|
|
|
}
|
|
|
|
|
|
|
|
c := a.findCharacteristic(attr.parent)
|
|
|
|
if c != nil && c.chr != nil {
|
|
|
|
cccd, err := c.chr.readCCCD()
|
|
|
|
if err != nil {
|
|
|
|
return a.sendError(handle, attOpReadReq, attrHandle, attErrorReadNotPermitted)
|
|
|
|
}
|
|
|
|
|
|
|
|
binary.LittleEndian.PutUint16(response[pos:], cccd)
|
|
|
|
pos += 2
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, response[:pos]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-16 22:45:45 +03:00
|
|
|
|
|
|
|
return nil
|
2024-01-13 17:38:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.sendError(handle, attOpReadReq, attrHandle, attErrorReadNotPermitted)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) handleWriteReq(handle, attrHandle uint16, data []byte) error {
|
|
|
|
attr := a.findAttribute(attrHandle)
|
|
|
|
if attr == nil {
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleWriteReq: attribute not found", attrHandle)
|
|
|
|
}
|
|
|
|
return a.sendError(handle, attOpWriteReq, attrHandle, attErrorAttrNotFound)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch attr.typ {
|
|
|
|
case attributeTypeCharacteristicValue:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleWriteReq: writing characteristic value", attrHandle, hex.EncodeToString(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
c := a.findCharacteristic(attr.parent)
|
|
|
|
if c != nil && c.chr != nil {
|
|
|
|
if _, err := c.chr.Write(data); err != nil {
|
|
|
|
return a.sendError(handle, attOpWriteReq, attrHandle, attErrorWriteNotPermitted)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, []byte{attOpWriteResponse}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
case attributeTypeDescriptor:
|
2024-01-16 19:18:32 +03:00
|
|
|
if debug {
|
2024-01-13 17:38:46 +03:00
|
|
|
println("att.handleWriteReq: writing descriptor", attrHandle, hex.EncodeToString(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
c := a.findCharacteristic(attr.parent)
|
|
|
|
if c != nil && c.chr != nil {
|
|
|
|
if err := c.chr.writeCCCD(binary.LittleEndian.Uint16(data)); err != nil {
|
|
|
|
return a.sendError(handle, attOpWriteReq, attrHandle, attErrorWriteNotPermitted)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.hci.sendAclPkt(handle, attCID, []byte{attOpWriteResponse}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.sendError(handle, attOpWriteReq, attrHandle, attErrorWriteNotPermitted)
|
|
|
|
}
|
|
|
|
|
2023-12-21 01:29:23 +03:00
|
|
|
func (a *att) clearResponse() {
|
|
|
|
a.responded = false
|
|
|
|
a.errored = false
|
|
|
|
a.lastErrorOpcode = 0
|
|
|
|
a.lastErrorHandle = 0
|
|
|
|
a.lastErrorCode = 0
|
|
|
|
a.value = []byte{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) waitUntilResponse() error {
|
|
|
|
start := time.Now().UnixNano()
|
|
|
|
for {
|
|
|
|
if err := a.hci.poll(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case a.responded:
|
|
|
|
return nil
|
|
|
|
|
|
|
|
default:
|
|
|
|
// check for timeout
|
|
|
|
if (time.Now().UnixNano()-start)/int64(time.Second) > 3 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-01-21 15:04:26 +03:00
|
|
|
time.Sleep(5 * time.Millisecond)
|
2023-12-21 01:29:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ErrATTTimeout
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) poll() error {
|
|
|
|
a.busy.Lock()
|
|
|
|
defer a.busy.Unlock()
|
|
|
|
|
|
|
|
if err := a.hci.poll(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-01-13 17:38:46 +03:00
|
|
|
|
2024-01-21 15:04:26 +03:00
|
|
|
func (a *att) addConnection(handle uint16) error {
|
2024-01-13 17:38:46 +03:00
|
|
|
a.connections = append(a.connections, handle)
|
2024-01-21 15:04:26 +03:00
|
|
|
|
|
|
|
return nil
|
2024-01-13 17:38:46 +03:00
|
|
|
}
|
|
|
|
|
2024-01-21 15:04:26 +03:00
|
|
|
func (a *att) removeConnection(handle uint16) error {
|
2024-01-13 17:38:46 +03:00
|
|
|
for i := range a.connections {
|
|
|
|
if a.connections[i] == handle {
|
|
|
|
a.connections = append(a.connections[:i], a.connections[i+1:]...)
|
2024-01-21 15:04:26 +03:00
|
|
|
break
|
2024-01-13 17:38:46 +03:00
|
|
|
}
|
|
|
|
}
|
2024-01-21 15:04:26 +03:00
|
|
|
|
|
|
|
return nil
|
2024-01-13 17:38:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) addLocalAttribute(typ attributeType, parent uint16, uuid UUID, permissions CharacteristicPermissions, value []byte) uint16 {
|
|
|
|
handle := a.lastHandle
|
|
|
|
a.attributes = append(a.attributes,
|
|
|
|
rawAttribute{
|
|
|
|
typ: typ,
|
|
|
|
parent: parent,
|
|
|
|
handle: handle,
|
|
|
|
uuid: uuid,
|
|
|
|
permissions: permissions,
|
|
|
|
value: append([]byte{}, value...),
|
|
|
|
})
|
|
|
|
a.lastHandle++
|
|
|
|
|
|
|
|
return handle
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) addLocalService(start, end uint16, uuid UUID) {
|
|
|
|
a.localServices = append(a.localServices, rawService{
|
|
|
|
startHandle: start,
|
|
|
|
endHandle: end,
|
|
|
|
uuid: uuid,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) addLocalCharacteristic(startHandle uint16, properties CharacteristicPermissions, valueHandle uint16, uuid UUID, chr *Characteristic) {
|
|
|
|
a.characteristics = append(a.characteristics,
|
|
|
|
rawCharacteristic{
|
|
|
|
startHandle: startHandle,
|
|
|
|
properties: uint8(properties),
|
|
|
|
valueHandle: valueHandle,
|
|
|
|
uuid: uuid,
|
|
|
|
chr: chr,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) findAttribute(hdl uint16) *rawAttribute {
|
|
|
|
for i := range a.attributes {
|
|
|
|
if a.attributes[i].handle == hdl {
|
|
|
|
return &a.attributes[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *att) findCharacteristic(hdl uint16) *rawCharacteristic {
|
|
|
|
for i := range a.characteristics {
|
|
|
|
if a.characteristics[i].startHandle == hdl {
|
|
|
|
return &a.characteristics[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|