blob: e8a34279abca97a6a3e1813b2563d2973b78b264 [file] [log] [blame]
package gatt
import (
"errors"
"log"
)
// MaxEIRPacketLength is the maximum allowed AdvertisingPacket
// and ScanResponsePacket length.
const MaxEIRPacketLength = 31
// ErrEIRPacketTooLong is the error returned when an AdvertisingPacket
// or ScanResponsePacket is too long.
var ErrEIRPacketTooLong = errors.New("max packet length is 31")
// Advertising data field types
const (
typeFlags = 0x01 // Flags
typeSomeUUID16 = 0x02 // Incomplete List of 16-bit Service Class UUIDs
typeAllUUID16 = 0x03 // Complete List of 16-bit Service Class UUIDs
typeSomeUUID32 = 0x04 // Incomplete List of 32-bit Service Class UUIDs
typeAllUUID32 = 0x05 // Complete List of 32-bit Service Class UUIDs
typeSomeUUID128 = 0x06 // Incomplete List of 128-bit Service Class UUIDs
typeAllUUID128 = 0x07 // Complete List of 128-bit Service Class UUIDs
typeShortName = 0x08 // Shortened Local Name
typeCompleteName = 0x09 // Complete Local Name
typeTxPower = 0x0A // Tx Power Level
typeClassOfDevice = 0x0D // Class of Device
typeSimplePairingC192 = 0x0E // Simple Pairing Hash C-192
typeSimplePairingR192 = 0x0F // Simple Pairing Randomizer R-192
typeSecManagerTK = 0x10 // Security Manager TK Value
typeSecManagerOOB = 0x11 // Security Manager Out of Band Flags
typeSlaveConnInt = 0x12 // Slave Connection Interval Range
typeServiceSol16 = 0x14 // List of 16-bit Service Solicitation UUIDs
typeServiceSol128 = 0x15 // List of 128-bit Service Solicitation UUIDs
typeServiceData16 = 0x16 // Service Data - 16-bit UUID
typePubTargetAddr = 0x17 // Public Target Address
typeRandTargetAddr = 0x18 // Random Target Address
typeAppearance = 0x19 // Appearance
typeAdvInterval = 0x1A // Advertising Interval
typeLEDeviceAddr = 0x1B // LE Bluetooth Device Address
typeLERole = 0x1C // LE Role
typeServiceSol32 = 0x1F // List of 32-bit Service Solicitation UUIDs
typeServiceData32 = 0x20 // Service Data - 32-bit UUID
typeServiceData128 = 0x21 // Service Data - 128-bit UUID
typeLESecConfirm = 0x22 // LE Secure Connections Confirmation Value
typeLESecRandom = 0x23 // LE Secure Connections Random Value
typeManufacturerData = 0xFF // Manufacturer Specific Data
)
// Advertising type flags
const (
flagLimitedDiscoverable = 0x01 // LE Limited Discoverable Mode
flagGeneralDiscoverable = 0x02 // LE General Discoverable Mode
flagLEOnly = 0x04 // BR/EDR Not Supported. Bit 37 of LMP Feature Mask Definitions (Page 0)
flagBothController = 0x08 // Simultaneous LE and BR/EDR to Same Device Capable (Controller).
flagBothHost = 0x10 // Simultaneous LE and BR/EDR to Same Device Capable (Host).
)
// FIXME: check the unmarshalling of this data structure.
type ServiceData struct {
UUID UUID
Data []byte
}
// This is borrowed from core bluetooth.
// Embedded/Linux folks might be interested in more details.
type Advertisement struct {
LocalName string
ManufacturerData []byte
ServiceData []ServiceData
Services []UUID
OverflowService []UUID
TxPowerLevel int
Connectable bool
SolicitedService []UUID
}
// This is only used in Linux port.
func (a *Advertisement) unmarshall(b []byte) error {
// Utility function for creating a list of uuids.
uuidList := func(u []UUID, d []byte, w int) []UUID {
for len(d) > 0 {
u = append(u, UUID{d[:w]})
d = d[w:]
}
return u
}
for len(b) > 0 {
if len(b) < 2 {
return errors.New("invalid advertise data")
}
l, t := b[0], b[1]
if len(b) < int(1+l) {
return errors.New("invalid advertise data")
}
d := b[2 : 1+l]
switch t {
case typeFlags:
// TODO: should we do anything about the discoverability here?
case typeSomeUUID16:
a.Services = uuidList(a.Services, d, 2)
case typeAllUUID16:
a.Services = uuidList(a.Services, d, 2)
case typeSomeUUID32:
a.Services = uuidList(a.Services, d, 4)
case typeAllUUID32:
a.Services = uuidList(a.Services, d, 4)
case typeSomeUUID128:
a.Services = uuidList(a.Services, d, 16)
case typeAllUUID128:
a.Services = uuidList(a.Services, d, 16)
case typeShortName:
a.LocalName = string(d)
case typeCompleteName:
a.LocalName = string(d)
case typeTxPower:
a.TxPowerLevel = int(d[0])
case typeServiceSol16:
a.SolicitedService = uuidList(a.SolicitedService, d, 2)
case typeServiceSol128:
a.SolicitedService = uuidList(a.SolicitedService, d, 16)
case typeServiceSol32:
a.SolicitedService = uuidList(a.SolicitedService, d, 4)
case typeManufacturerData:
a.ManufacturerData = make([]byte, len(d))
copy(a.ManufacturerData, d)
// case typeServiceData16,
// case typeServiceData32,
// case typeServiceData128:
default:
log.Printf("DATA: [ % X ]", d)
}
b = b[1+l:]
}
return nil
}
// AdvPacket is an utility to help crafting advertisment or scan response data.
type AdvPacket struct {
b []byte
}
// Bytes returns an 31-byte array, which contains up to 31 bytes of the packet.
func (a *AdvPacket) Bytes() [31]byte {
b := [31]byte{}
copy(b[:], a.b)
return b
}
// Len returns the length of the packets with a maximum of 31.
func (a *AdvPacket) Len() int {
if len(a.b) > 31 {
return 31
}
return len(a.b)
}
// AppendField appends a BLE advertising packet field.
// TODO: refuse to append field if it'd make the packet too long.
func (a *AdvPacket) AppendField(typ byte, b []byte) *AdvPacket {
// A field consists of len, typ, b.
// Len is 1 byte for typ plus len(b).
if len(a.b)+2+len(b) > MaxEIRPacketLength {
b = b[:MaxEIRPacketLength-len(a.b)-2]
}
a.b = append(a.b, byte(len(b)+1))
a.b = append(a.b, typ)
a.b = append(a.b, b...)
return a
}
// AppendFlags appends a flag field to the packet.
func (a *AdvPacket) AppendFlags(f byte) *AdvPacket {
return a.AppendField(typeFlags, []byte{f})
}
// AppendFlags appends a name field to the packet.
// If the name fits in the space, it will be append as a complete name field, otherwise a short name field.
func (a *AdvPacket) AppendName(n string) *AdvPacket {
typ := byte(typeCompleteName)
if len(a.b)+2+len(n) > MaxEIRPacketLength {
typ = byte(typeShortName)
}
return a.AppendField(typ, []byte(n))
}
// AppendManufacturerData appends a manufacturer data field to the packet.
func (a *AdvPacket) AppendManufacturerData(id uint16, b []byte) *AdvPacket {
d := append([]byte{uint8(id), uint8(id >> 8)}, b...)
return a.AppendField(typeManufacturerData, d)
}
// AppendUUIDFit appends a BLE advertised service UUID
// packet field if it fits in the packet, and reports whether the UUID fit.
func (a *AdvPacket) AppendUUIDFit(u UUID) bool {
if len(a.b)+2+u.Len() > MaxEIRPacketLength {
return false
}
// Err on the side of safety and assume that there might be
// other services available: Use typeSomeUUID instead
// of typeAllUUID.
// TODO: When we know the full set of services,
// calculate this exactly, instead of hedging.
switch u.Len() {
case 2:
a.AppendField(typeSomeUUID16, u.b)
case 16:
a.AppendField(typeSomeUUID128, u.b)
}
return true
}