blob: d1ae09d903a3313d50f06f0d72e264fd921425b3 [file] [log] [blame]
package gatt
import "log"
// attr is a BLE attribute. It is not exported;
// managing attributes is an implementation detail.
type attr struct {
h uint16 // attribute handle
typ UUID // attribute type in UUID
props Property // attripute property
secure Property // attribute secure (implementation specific usage)
value []byte // attribute value
pvt interface{} // point to the corresponsing Serveice/Characteristic/Descriptor
}
// A attrRange is a contiguous range of attributes.
type attrRange struct {
aa []attr
base uint16 // handle for first attr in aa
}
const (
tooSmall = -1
tooLarge = -2
)
// idx returns the index into aa corresponding to attr a.
// If h is too small, idx returns tooSmall (-1).
// If h is too large, idx returns tooLarge (-2).
func (r *attrRange) idx(h int) int {
if h < int(r.base) {
return tooSmall
}
if int(h) >= int(r.base)+len(r.aa) {
return tooLarge
}
return h - int(r.base)
}
// At returns attr a.
func (r *attrRange) At(h uint16) (a attr, ok bool) {
i := r.idx(int(h))
if i < 0 {
return attr{}, false
}
return r.aa[i], true
}
// Subrange returns attributes in range [start, end]; it may
// return an empty slice. Subrange does not panic for
// out-of-range start or end.
func (r *attrRange) Subrange(start, end uint16) []attr {
startidx := r.idx(int(start))
switch startidx {
case tooSmall:
startidx = 0
case tooLarge:
return []attr{}
}
endidx := r.idx(int(end) + 1) // [start, end] includes its upper bound!
switch endidx {
case tooSmall:
return []attr{}
case tooLarge:
endidx = len(r.aa)
}
return r.aa[startidx:endidx]
}
func dumpAttributes(aa []attr) {
log.Printf("Generating attribute table:")
log.Printf("handle\ttype\tprops\tsecure\tpvt\tvalue")
for _, a := range aa {
log.Printf("0x%04X\t0x%s\t0x%02X\t0x%02x\t%T\t[ % X ]",
a.h, a.typ, int(a.props), int(a.secure), a.pvt, a.value)
}
}
func generateAttributes(ss []*Service, base uint16) *attrRange {
var aa []attr
h := base
last := len(ss) - 1
for i, s := range ss {
var a []attr
h, a = generateServiceAttributes(s, h, i == last)
aa = append(aa, a...)
}
dumpAttributes(aa)
return &attrRange{aa: aa, base: base}
}
func generateServiceAttributes(s *Service, h uint16, last bool) (uint16, []attr) {
s.h = h
// endh set later
a := attr{
h: h,
typ: attrPrimaryServiceUUID,
value: s.uuid.b,
props: CharRead,
pvt: s,
}
aa := []attr{a}
h++
for _, c := range s.Characteristics() {
var a []attr
h, a = generateCharAttributes(c, h)
aa = append(aa, a...)
}
s.endh = h - 1
if last {
h = 0xFFFF
s.endh = h
}
return h, aa
}
func generateCharAttributes(c *Characteristic, h uint16) (uint16, []attr) {
c.h = h
c.vh = h + 1
ca := attr{
h: c.h,
typ: attrCharacteristicUUID,
value: append([]byte{byte(c.props), byte(c.vh), byte((c.vh) >> 8)}, c.uuid.b...),
props: c.props,
pvt: c,
}
va := attr{
h: c.vh,
typ: c.uuid,
value: c.value,
props: c.props,
pvt: c,
}
h += 2
aa := []attr{ca, va}
for _, d := range c.descs {
aa = append(aa, generateDescAttributes(d, h))
h++
}
return h, aa
}
func generateDescAttributes(d *Descriptor, h uint16) attr {
d.h = h
a := attr{
h: h,
typ: d.uuid,
value: d.value,
props: d.props,
pvt: d,
}
return a
}