// Utility function for creating a list of uuids. func uuidList(u []ble.UUID, d []byte, w int) []ble.UUID { for len(d) > 0 { u = append(u, ble.UUID(d[:w])) d = d[w:] } return u }
// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1] // If filter is specified, only filtered services are returned. func (p *Client) DiscoverServices(filter []ble.UUID) ([]*ble.Service, error) { p.Lock() defer p.Unlock() if p.profile == nil { p.profile = &ble.Profile{} } start := uint16(0x0001) for { length, b, err := p.ac.ReadByGroupType(start, 0xFFFF, ble.PrimaryServiceUUID) if err == ble.ErrAttrNotFound { return p.profile.Services, nil } if err != nil { return nil, err } for len(b) != 0 { h := binary.LittleEndian.Uint16(b[:2]) endh := binary.LittleEndian.Uint16(b[2:4]) u := ble.UUID(b[4:length]) if filter == nil || ble.Contains(filter, u) { s := &ble.Service{ UUID: u, Handle: h, EndHandle: endh, } p.profile.Services = append(p.profile.Services, s) } if endh == 0xFFFF { return p.profile.Services, nil } start = endh + 1 b = b[length:] } } }
// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1] // If filter is specified, only filtered descriptors are returned. func (p *Client) DiscoverDescriptors(filter []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) { p.Lock() defer p.Unlock() start := c.ValueHandle + 1 for start <= c.EndHandle { fmt, b, err := p.ac.FindInformation(start, c.EndHandle) if err == ble.ErrAttrNotFound { break } else if err != nil { return nil, err } length := 2 + 2 if fmt == 0x02 { length = 2 + 16 } for len(b) != 0 { h := binary.LittleEndian.Uint16(b[:2]) u := ble.UUID(b[2:length]) d := &ble.Descriptor{UUID: u, Handle: h} if filter == nil || ble.Contains(filter, u) { c.Descriptors = append(c.Descriptors, d) } if u.Equal(ble.ClientCharacteristicConfigUUID) { c.CCCD = d } start = h + 1 b = b[length:] } } return c.Descriptors, nil }
// handle Read By Type request. [Vol 3, Part F, 3.4.4.1 & 3.4.4.2] func (s *Server) handleReadByTypeRequest(r ReadByTypeRequest) []byte { // Validate the request. switch { case len(r) != 7 && len(r) != 21: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } rsp := ReadByTypeResponse(s.txBuf) rsp.SetAttributeOpcode() buf := bytes.NewBuffer(rsp.AttributeDataList()) buf.Reset() // handle length (2 bytes) + value length. // Each response shall only contains values with the same size. dlen := 0 for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { if !a.typ.Equal(ble.UUID(r.AttributeType())) { continue } v := a.v if v == nil { buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-2)) if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess { // Return if the first value read cause an error. if dlen == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e) } // Otherwise, skip to the next one. break } v = buf2.Bytes() } if dlen == 0 { // Found the first value. dlen = 2 + len(v) if dlen > 255 { dlen = 255 } if dlen > buf.Cap() { dlen = buf.Cap() } rsp.SetLength(uint8(dlen)) } else if 2+len(v) != dlen { break } if buf.Len()+dlen > buf.Cap() { break } binary.Write(buf, binary.LittleEndian, a.h) binary.Write(buf, binary.LittleEndian, v[:dlen-2]) } if dlen == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) } return rsp[:2+buf.Len()] }
func serviceDataList(sd []ble.ServiceData, d []byte, w int) []ble.ServiceData { serviceData := ble.ServiceData{ UUID: ble.UUID(d[:w]), Data: make([]byte, len(d)-w), } copy(serviceData.Data, d[2:]) return append(sd, serviceData) }
// handle Find By Type Value request. [Vol 3, Part F, 3.4.3.3 & 3.4.3.4] func (s *Server) handleFindByTypeValueRequest(r FindByTypeValueRequest) []byte { // Validate the request. switch { case len(r) < 7: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } rsp := FindByTypeValueResponse(s.txBuf) rsp.SetAttributeOpcode() buf := bytes.NewBuffer(rsp.HandleInformationList()) buf.Reset() for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { v, starth, endh := a.v, a.h, a.endh if v == nil { // The value shall not exceed ATT_MTU - 7 bytes. // Since ResponseWriter caps the value at the capacity, // we allocate one extra byte, and the written length. buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-7+1)) e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)) if e != ble.ErrSuccess || buf2.Len() > len(s.txBuf)-7 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } endh = a.h } if !(ble.UUID(v).Equal(ble.UUID(r.AttributeValue()))) { continue } if buf.Len()+4 > buf.Cap() { break } binary.Write(buf, binary.LittleEndian, starth) binary.Write(buf, binary.LittleEndian, endh) } if buf.Len() == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) } return rsp[:1+buf.Len()] }
func (a *adv) Services() []ble.UUID { xUUIDs, ok := a.ad["kCBAdvDataServiceUUIDs"] if !ok { return nil } var uuids []ble.UUID for _, xUUID := range xUUIDs.(xpc.Array) { uuids = append(uuids, ble.UUID(ble.Reverse(xUUID.([]byte)))) } return uuids }
func (a *adv) ServiceData() []ble.ServiceData { xSDs, ok := a.ad["kCBAdvDataServiceData"] if !ok { return nil } xSD := xSDs.(xpc.Array) var sd []ble.ServiceData for i := 0; i < len(xSD); i += 2 { sd = append( sd, ble.ServiceData{ UUID: ble.UUID(xSD[i].([]byte)), Data: xSD[i+1].([]byte), }) } return sd }
// DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1] // If filter is specified, only filtered characteristics are returned. func (p *Client) DiscoverCharacteristics(filter []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) { p.Lock() defer p.Unlock() start := s.Handle var lastChar *ble.Characteristic for start <= s.EndHandle { length, b, err := p.ac.ReadByType(start, s.EndHandle, ble.CharacteristicUUID) if err == ble.ErrAttrNotFound { break } else if err != nil { return nil, err } for len(b) != 0 { h := binary.LittleEndian.Uint16(b[:2]) p := ble.Property(b[2]) vh := binary.LittleEndian.Uint16(b[3:5]) u := ble.UUID(b[5:length]) c := &ble.Characteristic{ UUID: u, Property: p, Handle: h, ValueHandle: vh, EndHandle: s.EndHandle, } if filter == nil || ble.Contains(filter, u) { s.Characteristics = append(s.Characteristics, c) } if lastChar != nil { lastChar.EndHandle = c.Handle - 1 } lastChar = c start = vh + 1 b = b[length:] } } return s.Characteristics, nil }