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