blob: 5d9065b7ffc7f109f0b24b726eb9489c3816acc7 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package nmble
import (
"fmt"
"sync"
"time"
log "github.com/sirupsen/logrus"
. "mynewt.apache.org/newtmgr/nmxact/bledefs"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/sesn"
)
const WRITE_CMD_BASE_SZ = 3
const NOTIFY_CMD_BASE_SZ = 3
var nextSeq BleSeq = BLE_SEQ_MIN
var seqMtx sync.Mutex
func NextSeq() BleSeq {
seqMtx.Lock()
defer seqMtx.Unlock()
seq := nextSeq
nextSeq++
if nextSeq >= BLE_SEQ_EVT_MIN {
nextSeq = BLE_SEQ_MIN
}
return seq
}
func BhdTimeoutError(rspType MsgType, seq BleSeq) error {
str := fmt.Sprintf(
"Timeout waiting for blehostd to send %s response (seq=%d)",
MsgTypeToString(rspType), seq)
log.Debug(str)
return nmxutil.NewXportError(str)
}
func StatusError(op MsgOp, msgType MsgType, status int) error {
str := fmt.Sprintf("%s %s indicates error: %s (%d)",
MsgOpToString(op),
MsgTypeToString(msgType),
ErrCodeToString(status),
status)
log.Debug(str)
return nmxutil.NewBleHostError(status, str)
}
func BleDescFromConnFindRsp(r *BleConnFindRsp) BleConnDesc {
return BleConnDesc{
ConnHandle: r.ConnHandle,
OwnIdAddrType: r.OwnIdAddrType,
OwnIdAddr: r.OwnIdAddr,
OwnOtaAddrType: r.OwnOtaAddrType,
OwnOtaAddr: r.OwnOtaAddr,
PeerIdAddrType: r.PeerIdAddrType,
PeerIdAddr: r.PeerIdAddr,
PeerOtaAddrType: r.PeerOtaAddrType,
PeerOtaAddr: r.PeerOtaAddr,
Role: r.Role,
Encrypted: r.Encrypted,
Authenticated: r.Authenticated,
Bonded: r.Bonded,
KeySize: r.KeySize,
}
}
func BleAdvReportFromScanEvt(e *BleScanEvt) BleAdvReport {
return BleAdvReport{
EventType: e.EventType,
Sender: BleDev{
AddrType: e.AddrType,
Addr: e.Addr,
},
Rssi: e.Rssi,
Fields: BleAdvFields{
Data: e.Data.Bytes,
Flags: e.DataFlags,
Uuids16: e.DataUuids16,
Uuids16IsComplete: e.DataUuids16IsComplete,
Uuids32: e.DataUuids32,
Uuids32IsComplete: e.DataUuids32IsComplete,
Uuids128: e.DataUuids128,
Uuids128IsComplete: e.DataUuids128IsComplete,
Name: e.DataName,
NameIsComplete: e.DataNameIsComplete,
TxPwrLvl: e.DataTxPwrLvl,
SlaveItvlMin: e.DataSlaveItvlMin,
SlaveItvlMax: e.DataSlaveItvlMax,
SvcDataUuid16: e.DataSvcDataUuid16.Bytes,
PublicTgtAddrs: e.DataPublicTgtAddrs,
Appearance: e.DataAppearance,
AdvItvl: e.DataAdvItvl,
SvcDataUuid32: e.DataSvcDataUuid32.Bytes,
SvcDataUuid128: e.DataSvcDataUuid128.Bytes,
Uri: e.DataUri,
MfgData: e.DataMfgData.Bytes,
},
}
}
func NewBleConnectReq() *BleConnectReq {
return &BleConnectReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_CONNECT,
Seq: NextSeq(),
OwnAddrType: BLE_ADDR_TYPE_PUBLIC,
PeerAddrType: BLE_ADDR_TYPE_PUBLIC,
PeerAddr: BleAddr{},
DurationMs: 30000,
ScanItvl: 0x0010,
ScanWindow: 0x0010,
ItvlMin: 24,
ItvlMax: 40,
Latency: 0,
SupervisionTimeout: 0x0200,
MinCeLen: 0x0010,
MaxCeLen: 0x0300,
}
}
func NewBleTerminateReq() *BleTerminateReq {
return &BleTerminateReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_TERMINATE,
Seq: NextSeq(),
ConnHandle: 0,
HciReason: 0,
}
}
func NewBleConnCancelReq() *BleConnCancelReq {
return &BleConnCancelReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_CONN_CANCEL,
Seq: NextSeq(),
}
}
func NewBleDiscAllSvcsReq() *BleDiscAllSvcsReq {
return &BleDiscAllSvcsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_DISC_ALL_SVCS,
Seq: NextSeq(),
}
}
func NewBleDiscSvcUuidReq() *BleDiscSvcUuidReq {
return &BleDiscSvcUuidReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_DISC_SVC_UUID,
Seq: NextSeq(),
ConnHandle: 0,
Uuid: BleUuid{},
}
}
func NewBleDiscAllChrsReq() *BleDiscAllChrsReq {
return &BleDiscAllChrsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_DISC_ALL_CHRS,
Seq: NextSeq(),
}
}
func NewBleDiscAllDscsReq() *BleDiscAllDscsReq {
return &BleDiscAllDscsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_DISC_ALL_DSCS,
Seq: NextSeq(),
}
}
func NewBleExchangeMtuReq() *BleExchangeMtuReq {
return &BleExchangeMtuReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_EXCHANGE_MTU,
Seq: NextSeq(),
ConnHandle: 0,
}
}
func NewBleGenRandAddrReq() *BleGenRandAddrReq {
return &BleGenRandAddrReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_GEN_RAND_ADDR,
Seq: NextSeq(),
}
}
func NewBleSetRandAddrReq() *BleSetRandAddrReq {
return &BleSetRandAddrReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SET_RAND_ADDR,
Seq: NextSeq(),
}
}
func NewBleWriteCmdReq() *BleWriteCmdReq {
return &BleWriteCmdReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_WRITE_CMD,
Seq: NextSeq(),
}
}
func NewBleWriteReq() *BleWriteReq {
return &BleWriteReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_WRITE,
Seq: NextSeq(),
}
}
func NewBleScanReq() *BleScanReq {
return &BleScanReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SCAN,
Seq: NextSeq(),
}
}
func NewBleScanCancelReq() *BleScanCancelReq {
return &BleScanCancelReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SCAN_CANCEL,
Seq: NextSeq(),
}
}
func NewBleSetPreferredMtuReq() *BleSetPreferredMtuReq {
return &BleSetPreferredMtuReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SET_PREFERRED_MTU,
Seq: NextSeq(),
}
}
func NewBleConnFindReq() *BleConnFindReq {
return &BleConnFindReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_CONN_FIND,
Seq: NextSeq(),
}
}
func NewResetReq() *BleResetReq {
return &BleResetReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_RESET,
Seq: NextSeq(),
}
}
func NewBleSecurityInitiateReq() *BleSecurityInitiateReq {
return &BleSecurityInitiateReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SECURITY_INITIATE,
Seq: NextSeq(),
}
}
func NewBleAdvFieldsReq() *BleAdvFieldsReq {
return &BleAdvFieldsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ADV_FIELDS,
Seq: NextSeq(),
}
}
func NewBleAdvSetDataReq() *BleAdvSetDataReq {
return &BleAdvSetDataReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ADV_SET_DATA,
Seq: NextSeq(),
}
}
func NewBleAdvRspSetDataReq() *BleAdvRspSetDataReq {
return &BleAdvRspSetDataReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ADV_RSP_SET_DATA,
Seq: NextSeq(),
}
}
func NewBleAdvStartReq() *BleAdvStartReq {
return &BleAdvStartReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ADV_START,
Seq: NextSeq(),
}
}
func NewBleAdvStopReq() *BleAdvStopReq {
return &BleAdvStopReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ADV_STOP,
Seq: NextSeq(),
}
}
func NewBleClearSvcsReq() *BleClearSvcsReq {
return &BleClearSvcsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_CLEAR_SVCS,
Seq: NextSeq(),
}
}
func NewBleAddSvcsReq() *BleAddSvcsReq {
return &BleAddSvcsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ADD_SVCS,
Seq: NextSeq(),
}
}
func NewBleCommitSvcsReq() *BleCommitSvcsReq {
return &BleCommitSvcsReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_COMMIT_SVCS,
Seq: NextSeq(),
}
}
func NewAccessStatusReq() *BleAccessStatusReq {
return &BleAccessStatusReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_ACCESS_STATUS,
Seq: NextSeq(),
}
}
func NewNotifyReq() *BleNotifyReq {
return &BleNotifyReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_NOTIFY,
Seq: NextSeq(),
}
}
func NewFindChrReq() *BleFindChrReq {
return &BleFindChrReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_FIND_CHR,
Seq: NextSeq(),
}
}
func NewSyncReq() *BleSyncReq {
return &BleSyncReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SYNC,
Seq: NextSeq(),
}
}
func NewBleSmInjectIoReq() *BleSmInjectIoReq {
return &BleSmInjectIoReq{
Op: MSG_OP_REQ,
Type: MSG_TYPE_SM_INJECT_IO,
Seq: NextSeq(),
}
}
func ConnFindXact(x *BleXport, connHandle uint16) (BleConnDesc, error) {
r := NewBleConnFindReq()
r.ConnHandle = connHandle
key := SeqKey(r.Seq)
bl, err := x.AddListener(key)
if err != nil {
return BleConnDesc{}, err
}
defer x.RemoveListener(bl)
return connFind(x, bl, r)
}
func GenRandAddrXact(x *BleXport) (BleAddr, error) {
r := NewBleGenRandAddrReq()
key := SeqKey(r.Seq)
bl, err := x.AddListener(key)
if err != nil {
return BleAddr{}, err
}
defer x.RemoveListener(bl)
return genRandAddr(x, bl, r)
}
func SetRandAddrXact(x *BleXport, addr BleAddr) error {
r := NewBleSetRandAddrReq()
r.Addr = addr
key := SeqKey(r.Seq)
bl, err := x.AddListener(key)
if err != nil {
return err
}
defer x.RemoveListener(bl)
return setRandAddr(x, bl, r)
}
func SetPreferredMtuXact(x *BleXport, mtu uint16) error {
r := NewBleSetPreferredMtuReq()
r.Mtu = mtu
key := SeqKey(r.Seq)
bl, err := x.AddListener(key)
if err != nil {
return err
}
defer x.RemoveListener(bl)
return setPreferredMtu(x, bl, r)
}
func ResetXact(x *BleXport) error {
r := NewResetReq()
key := SeqKey(r.Seq)
bl, err := x.AddListener(key)
if err != nil {
return err
}
defer x.RemoveListener(bl)
return reset(x, bl, r)
}
func ClearSvcsXact(x *BleXport) error {
r := NewBleClearSvcsReq()
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer x.RemoveListener(bl)
return clearSvcs(x, bl, r)
}
func AddSvcsXact(x *BleXport, svcs []BleAddSvc) error {
r := NewBleAddSvcsReq()
r.Svcs = svcs
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer x.RemoveListener(bl)
return addSvcs(x, bl, r)
}
func CommitSvcsXact(x *BleXport) ([]BleRegSvc, error) {
r := NewBleCommitSvcsReq()
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return nil, err
}
defer x.RemoveListener(bl)
return commitSvcs(x, bl, r)
}
func AccessStatusXact(x *BleXport, attStatus uint8, data []byte) error {
r := NewAccessStatusReq()
r.AttStatus = attStatus
r.Data.Bytes = data
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer x.RemoveListener(bl)
return accessStatus(x, bl, r)
}
func NotifyXact(x *BleXport, connHandle uint16, attrHandle uint16,
data []byte) error {
r := NewNotifyReq()
r.ConnHandle = connHandle
r.AttrHandle = attrHandle
r.Data.Bytes = data
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer x.RemoveListener(bl)
return notify(x, bl, r)
}
func FindChrXact(x *BleXport, svcUuid BleUuid, chrUuid BleUuid) (
uint16, uint16, error) {
r := NewFindChrReq()
r.SvcUuid = svcUuid
r.ChrUuid = chrUuid
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return 0, 0, err
}
defer x.RemoveListener(bl)
return findChr(x, bl, r)
}
func SyncXact(x *BleXport) (bool, error) {
r := NewSyncReq()
bl, err := x.AddListener(SeqKey(r.Seq))
if err != nil {
return false, err
}
synced, err := checkSync(x, bl, r)
if err != nil {
return false, err
}
return synced, nil
}
func DiscoverDeviceWithName(
bx *BleXport,
ownAddrType BleAddrType,
timeout time.Duration,
name string) (*BleDev, error) {
advPred := func(r BleAdvReport) bool {
return r.Fields.Name != nil && *r.Fields.Name == name
}
return DiscoverDevice(bx, ownAddrType, timeout, advPred)
}
func BleAdvFieldsToReq(f BleAdvFields) *BleAdvFieldsReq {
r := NewBleAdvFieldsReq()
r.Flags = f.Flags
r.Uuids16 = f.Uuids16
r.Uuids16IsComplete = f.Uuids16IsComplete
r.Uuids32 = f.Uuids32
r.Uuids32IsComplete = f.Uuids32IsComplete
r.Uuids128 = f.Uuids128
r.Uuids128IsComplete = f.Uuids128IsComplete
r.Name = f.Name
r.NameIsComplete = f.NameIsComplete
r.TxPwrLvl = f.TxPwrLvl
r.SlaveItvlMin = f.SlaveItvlMin
r.SlaveItvlMax = f.SlaveItvlMax
r.SvcDataUuid16 = BleBytes{f.SvcDataUuid16}
r.PublicTgtAddrs = f.PublicTgtAddrs
r.Appearance = f.Appearance
r.AdvItvl = f.AdvItvl
r.SvcDataUuid32 = BleBytes{f.SvcDataUuid32}
r.SvcDataUuid128 = BleBytes{f.SvcDataUuid128}
r.Uri = f.Uri
r.MfgData = BleBytes{f.MfgData}
return r
}
func BleSvcToAddSvc(svc BleSvc) BleAddSvc {
as := BleAddSvc{
Uuid: svc.Uuid,
SvcType: svc.SvcType,
}
for _, chr := range svc.Chrs {
ac := BleAddChr{
Uuid: chr.Uuid,
Flags: chr.Flags,
MinKeySize: chr.MinKeySize,
}
for _, dsc := range chr.Dscs {
ad := BleAddDsc{
Uuid: dsc.Uuid,
AttFlags: dsc.AttFlags,
MinKeySize: dsc.MinKeySize,
}
ac.Dscs = append(ac.Dscs, ad)
}
as.Chrs = append(as.Chrs, ac)
}
return as
}
func GapService(devName string) BleSvc {
return BleSvc{
Uuid: BleUuid{0x1800, [16]byte{}},
SvcType: BLE_SVC_TYPE_PRIMARY,
Chrs: []BleChr{
// Device name.
BleChr{
Uuid: BleUuid{0x2a00, [16]byte{}},
Flags: BLE_GATT_F_READ,
MinKeySize: 0,
AccessCb: func(access BleGattAccess) (uint8, []byte) {
return 0, []byte(devName)
},
},
// Appearance.
BleChr{
Uuid: BleUuid{0x2a01, [16]byte{}},
Flags: BLE_GATT_F_READ,
MinKeySize: 0,
AccessCb: func(access BleGattAccess) (uint8, []byte) {
return 0, []byte{0, 0}
},
},
// Peripheral privacy flag.
BleChr{
Uuid: BleUuid{0x2a02, [16]byte{}},
Flags: BLE_GATT_F_READ,
MinKeySize: 0,
AccessCb: func(access BleGattAccess) (uint8, []byte) {
return 0, []byte{0}
},
},
// Reconnection address.
BleChr{
Uuid: BleUuid{0x2a03, [16]byte{}},
Flags: BLE_GATT_F_READ,
MinKeySize: 0,
AccessCb: func(access BleGattAccess) (uint8, []byte) {
return 0, []byte{0, 0, 0, 0, 0, 0}
},
},
// Peripheral preferred connection parameters.
BleChr{
Uuid: BleUuid{0x2a04, [16]byte{}},
Flags: BLE_GATT_F_READ,
MinKeySize: 0,
AccessCb: func(access BleGattAccess) (uint8, []byte) {
return 0, []byte{0, 0, 0, 0, 0, 0, 0, 0}
},
},
},
}
}
func GattService() BleSvc {
return BleSvc{
Uuid: BleUuid{0x1801, [16]byte{}},
SvcType: BLE_SVC_TYPE_PRIMARY,
Chrs: []BleChr{
// Device name.
BleChr{
Uuid: BleUuid{0x2a05, [16]byte{}},
Flags: BLE_GATT_F_INDICATE,
MinKeySize: 0,
AccessCb: func(access BleGattAccess) (uint8, []byte) {
return 0, []byte{0, 0, 0, 0}
},
},
},
}
}
func BuildMgmtChrs(mgmtProto sesn.MgmtProto) (BleMgmtChrs, error) {
mgmtChrs := BleMgmtChrs{}
nmpSvcUuid, _ := ParseUuid(NmpPlainSvcUuid)
nmpChrUuid, _ := ParseUuid(NmpPlainChrUuid)
ompSvcUuid, _ := ParseUuid(OmpUnsecSvcUuid)
ompReqChrUuid, _ := ParseUuid(OmpUnsecReqChrUuid)
ompRspChrUuid, _ := ParseUuid(OmpUnsecRspChrUuid)
resSvcUuid, _ := ParseUuid(IotivitySvcUuid)
resReqChrUuid, _ := ParseUuid(IotivityReqChrUuid)
resRspChrUuid, _ := ParseUuid(IotivityRspChrUuid)
switch mgmtProto {
case sesn.MGMT_PROTO_NMP:
mgmtChrs.NmpReqChr = &BleChrId{nmpSvcUuid, nmpChrUuid}
mgmtChrs.NmpRspChr = &BleChrId{nmpSvcUuid, nmpChrUuid}
case sesn.MGMT_PROTO_OMP:
mgmtChrs.NmpReqChr = &BleChrId{ompSvcUuid, ompReqChrUuid}
mgmtChrs.NmpRspChr = &BleChrId{ompSvcUuid, ompRspChrUuid}
default:
return mgmtChrs,
fmt.Errorf("invalid management protocol: %+v", mgmtProto)
}
mgmtChrs.ResReqChr = &BleChrId{resSvcUuid, resReqChrUuid}
mgmtChrs.ResRspChr = &BleChrId{resSvcUuid, resRspChrUuid}
return mgmtChrs, nil
}
func IsSecErr(err error) bool {
bhdErr := nmxutil.ToBleHost(err)
if bhdErr == nil {
return false
}
switch bhdErr.Status - ERR_CODE_ATT_BASE {
case ERR_CODE_ATT_INSUFFICIENT_AUTHEN,
ERR_CODE_ATT_INSUFFICIENT_AUTHOR,
ERR_CODE_ATT_INSUFFICIENT_KEY_SZ,
ERR_CODE_ATT_INSUFFICIENT_ENC:
return true
default:
return false
}
}
// Attempts to convert the given error to a BLE security error. The conversion
// succeeds if the error represents a pairing failure due to missing or
// mismatched key material.
func ToSecurityErr(err error) error {
bhe := nmxutil.ToBleHost(err)
if bhe == nil {
return nil
}
code := ErrCodeToSmUs(bhe.Status)
if code == -1 {
code = ErrCodeToSmPeer(bhe.Status)
}
switch code {
case ERR_CODE_SM_ERR_PASSKEY,
ERR_CODE_SM_ERR_OOB,
ERR_CODE_SM_ERR_CONFIRM_MISMATCH,
ERR_CODE_SM_ERR_UNSPECIFIED,
ERR_CODE_SM_ERR_DHKEY,
ERR_CODE_SM_ERR_NUMCMP:
return nmxutil.NewBleSecurityError(err.Error())
default:
return nil
}
}