You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2017/08/04 19:28:12 UTC
[mynewt-newtmgr] 02/06: nmxact - GATT server
This is an automated email from the ASF dual-hosted git repository.
ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newtmgr.git
commit e119e6be463ee104d25e4f09f1cdb755f1ee73bf
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Aug 4 12:06:31 2017 -0700
nmxact - GATT server
---
newtmgr/bll/bll_xport.go | 5 +
.../newtmgr/nmxact/bledefs/bledefs.go | 7 +-
nmxact/adv/adv.go | 43 ++++
nmxact/bledefs/bledefs.go | 168 ++++++++++++++-
nmxact/example/ble_adv/ble_adv | Bin 0 -> 14025652 bytes
.../{ble_loop/ble_loop.go => ble_adv/ble_adv.go} | 85 +++-----
nmxact/example/ble_adv/bleholog | 0
nmxact/example/ble_loop/ble_loop.go | 6 +-
nmxact/nmble/ble_act.go | 165 +++++++++++++--
nmxact/nmble/ble_advertiser.go | 214 +++++++++++++++++++
nmxact/nmble/ble_coap_sesn.go | 176 ++++++++++++++++
nmxact/nmble/ble_fsm.go | 105 ++++++----
nmxact/nmble/ble_proto.go | 226 ++++++++++++++++++---
nmxact/nmble/ble_util.go | 138 +++++++++++++
nmxact/nmble/ble_xport.go | 50 ++++-
nmxact/nmble/chrmgr.go | 122 +++++++++++
nmxact/nmble/dispatch.go | 20 ++
nmxact/nmserial/serial_xport.go | 5 +
nmxact/nmxutil/nmxutil.go | 6 +-
nmxact/scan/scan.go | 1 -
nmxact/sesn/sesn.go | 2 +
nmxact/sesn/sesn_cfg.go | 19 --
nmxact/udp/udp_xport.go | 5 +
nmxact/xport/xport.go | 2 +
24 files changed, 1403 insertions(+), 167 deletions(-)
diff --git a/newtmgr/bll/bll_xport.go b/newtmgr/bll/bll_xport.go
index aa3519f..bb17e17 100644
--- a/newtmgr/bll/bll_xport.go
+++ b/newtmgr/bll/bll_xport.go
@@ -25,6 +25,7 @@ import (
"github.com/currantlabs/ble"
"github.com/currantlabs/ble/examples/lib/dev"
+ "mynewt.apache.org/newtmgr/nmxact/adv"
"mynewt.apache.org/newtmgr/nmxact/scan"
"mynewt.apache.org/newtmgr/nmxact/sesn"
)
@@ -93,3 +94,7 @@ func (bx *BllXport) BuildScanner() (scan.Scanner, error) {
func (bx *BllXport) Tx(data []byte) error {
return fmt.Errorf("BllXport.Tx() not supported")
}
+
+func (bx *BllXport) BuildAdvertiser() (adv.Advertiser, error) {
+ return nil, fmt.Errorf("BllXport#BuildAdvertiser unsupported")
+}
diff --git a/newtmgr/vendor/mynewt.apache.org/newtmgr/nmxact/bledefs/bledefs.go b/newtmgr/vendor/mynewt.apache.org/newtmgr/nmxact/bledefs/bledefs.go
index eeb310f..3541ac2 100644
--- a/newtmgr/vendor/mynewt.apache.org/newtmgr/nmxact/bledefs/bledefs.go
+++ b/newtmgr/vendor/mynewt.apache.org/newtmgr/nmxact/bledefs/bledefs.go
@@ -526,9 +526,10 @@ const (
)
var BleAdvFilterPolicyStringMap = map[BleAdvFilterPolicy]string{
- BLE_ADV_FILTER_POLICY_NON: "non",
- BLE_ADV_FILTER_POLICY_LTD: "ltd",
- BLE_ADV_FILTER_POLICY_GEN: "gen",
+ BLE_ADV_FILTER_POLICY_NONE: "none",
+ BLE_ADV_FILTER_POLICY_SCAN: "scan",
+ BLE_ADV_FILTER_POLICY_CONN: "conn",
+ BLE_ADV_FILTER_POLICY_BOTH: "both",
}
func BleAdvFilterPolicyToString(discMode BleAdvFilterPolicy) string {
diff --git a/nmxact/adv/adv.go b/nmxact/adv/adv.go
new file mode 100644
index 0000000..859d6ea
--- /dev/null
+++ b/nmxact/adv/adv.go
@@ -0,0 +1,43 @@
+package adv
+
+import (
+ "mynewt.apache.org/newtmgr/nmxact/bledefs"
+ "mynewt.apache.org/newtmgr/nmxact/sesn"
+)
+
+type CfgBle struct {
+ // Mandatory
+ OwnAddrType bledefs.BleAddrType
+ ConnMode bledefs.BleAdvConnMode
+ DiscMode bledefs.BleAdvDiscMode
+ ItvlMin uint16
+ ItvlMax uint16
+ ChannelMap uint8
+ FilterPolicy bledefs.BleAdvFilterPolicy
+ HighDutyCycle bool
+ AdvFields bledefs.BleAdvFields
+ RspFields bledefs.BleAdvFields
+ SesnCfg sesn.SesnCfg
+
+ // Only required for direct advertisements
+ PeerAddr *bledefs.BleAddr
+}
+
+type Cfg struct {
+ Ble CfgBle
+}
+
+type Advertiser interface {
+ Start(cfg Cfg) (sesn.Sesn, error)
+ Stop() error
+}
+
+func NewCfg() Cfg {
+ return Cfg{
+ Ble: CfgBle{
+ OwnAddrType: bledefs.BLE_ADDR_TYPE_RANDOM,
+ ConnMode: bledefs.BLE_ADV_CONN_MODE_UND,
+ DiscMode: bledefs.BLE_ADV_DISC_MODE_GEN,
+ },
+ }
+}
diff --git a/nmxact/bledefs/bledefs.go b/nmxact/bledefs/bledefs.go
index eeb310f..0a0ece3 100644
--- a/nmxact/bledefs/bledefs.go
+++ b/nmxact/bledefs/bledefs.go
@@ -520,15 +520,17 @@ func (a *BleAdvDiscMode) UnmarshalJSON(data []byte) error {
type BleAdvFilterPolicy int
const (
- BLE_ADV_FILTER_POLICY_NON BleAdvFilterPolicy = iota
- BLE_ADV_FILTER_POLICY_LTD
- BLE_ADV_FILTER_POLICY_GEN
+ BLE_ADV_FILTER_POLICY_NONE BleAdvFilterPolicy = iota
+ BLE_ADV_FILTER_POLICY_SCAN
+ BLE_ADV_FILTER_POLICY_CONN
+ BLE_ADV_FILTER_POLICY_BOTH
)
var BleAdvFilterPolicyStringMap = map[BleAdvFilterPolicy]string{
- BLE_ADV_FILTER_POLICY_NON: "non",
- BLE_ADV_FILTER_POLICY_LTD: "ltd",
- BLE_ADV_FILTER_POLICY_GEN: "gen",
+ BLE_ADV_FILTER_POLICY_NONE: "none",
+ BLE_ADV_FILTER_POLICY_SCAN: "scan",
+ BLE_ADV_FILTER_POLICY_CONN: "conn",
+ BLE_ADV_FILTER_POLICY_BOTH: "both",
}
func BleAdvFilterPolicyToString(discMode BleAdvFilterPolicy) string {
@@ -606,6 +608,13 @@ type BleAdvReport struct {
type BleAdvRptFn func(r BleAdvReport)
type BleAdvPredicate func(adv BleAdvReport) bool
+type BleRole int
+
+const (
+ BLE_ROLE_MASTER BleRole = iota
+ BLE_ROLE_SLAVE
+)
+
type BleConnDesc struct {
ConnHandle uint16
OwnIdAddrType BleAddrType
@@ -616,6 +625,7 @@ type BleConnDesc struct {
PeerIdAddr BleAddr
PeerOtaAddrType BleAddrType
PeerOtaAddr BleAddr
+ Role BleRole
}
func (d *BleConnDesc) String() string {
@@ -640,3 +650,149 @@ const (
BLE_ENCRYPT_PRIV_ONLY
BLE_ENCRYPT_ALWAYS
)
+
+type BleGattOp int
+
+const (
+ BLE_GATT_ACCESS_OP_READ_CHR BleGattOp = 0
+ BLE_GATT_ACCESS_OP_WRITE_CHR = 1
+ BLE_GATT_ACCESS_OP_READ_DSC = 2
+ BLE_GATT_ACCESS_OP_WRITE_DSC = 3
+)
+
+var BleGattOpStringMap = map[BleGattOp]string{
+ BLE_GATT_ACCESS_OP_READ_CHR: "read_chr",
+ BLE_GATT_ACCESS_OP_WRITE_CHR: "write_chr",
+ BLE_GATT_ACCESS_OP_READ_DSC: "read_dsc",
+ BLE_GATT_ACCESS_OP_WRITE_DSC: "write_dsc",
+}
+
+func BleGattOpToString(op BleGattOp) string {
+ s := BleGattOpStringMap[op]
+ if s == "" {
+ return "???"
+ }
+
+ return s
+}
+
+func BleGattOpFromString(s string) (BleGattOp, error) {
+ for op, name := range BleGattOpStringMap {
+ if s == name {
+ return op, nil
+ }
+ }
+
+ return BleGattOp(0),
+ fmt.Errorf("Invalid BleGattOp string: %s", s)
+}
+
+type BleSvcType int
+
+const (
+ BLE_SVC_TYPE_PRIMARY BleSvcType = iota
+ BLE_SVC_TYPE_SECONDARY
+)
+
+var BleSvcTypeStringMap = map[BleSvcType]string{
+ BLE_SVC_TYPE_PRIMARY: "primary",
+ BLE_SVC_TYPE_SECONDARY: "secondary",
+}
+
+func BleSvcTypeToString(svcType BleSvcType) string {
+ s := BleSvcTypeStringMap[svcType]
+ if s == "" {
+ return "???"
+ }
+
+ return s
+}
+
+func BleSvcTypeFromString(s string) (BleSvcType, error) {
+ for svcType, name := range BleSvcTypeStringMap {
+ if s == name {
+ return svcType, nil
+ }
+ }
+
+ return BleSvcType(0),
+ fmt.Errorf("Invalid BleSvcType string: %s", s)
+}
+
+func (a BleSvcType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(BleSvcTypeToString(a))
+}
+
+func (a *BleSvcType) UnmarshalJSON(data []byte) error {
+ var err error
+
+ var s string
+ if err := json.Unmarshal(data, &s); err != nil {
+ return err
+ }
+
+ *a, err = BleSvcTypeFromString(s)
+ return err
+}
+
+type BleChrFlags int
+
+const (
+ BLE_GATT_F_BROADCAST BleChrFlags = 0x0001
+ BLE_GATT_F_READ = 0x0002
+ BLE_GATT_F_WRITE_NO_RSP = 0x0004
+ BLE_GATT_F_WRITE = 0x0008
+ BLE_GATT_F_NOTIFY = 0x0010
+ BLE_GATT_F_INDICATE = 0x0020
+ BLE_GATT_F_AUTH_SIGN_WRITE = 0x0040
+ BLE_GATT_F_RELIABLE_WRITE = 0x0080
+ BLE_GATT_F_AUX_WRITE = 0x0100
+ BLE_GATT_F_READ_ENC = 0x0200
+ BLE_GATT_F_READ_AUTHEN = 0x0400
+ BLE_GATT_F_READ_AUTHOR = 0x0800
+ BLE_GATT_F_WRITE_ENC = 0x1000
+ BLE_GATT_F_WRITE_AUTHEN = 0x2000
+ BLE_GATT_F_WRITE_AUTHOR = 0x4000
+)
+
+type BleAttFlags int
+
+const (
+ BLE_ATT_F_READ BleAttFlags = 0x01
+ BLE_ATT_F_WRITE = 0x02
+ BLE_ATT_F_READ_ENC = 0x04
+ BLE_ATT_F_READ_AUTHEN = 0x08
+ BLE_ATT_F_READ_AUTHOR = 0x10
+ BLE_ATT_F_WRITE_ENC = 0x20
+ BLE_ATT_F_WRITE_AUTHEN = 0x40
+ BLE_ATT_F_WRITE_AUTHOR = 0x80
+)
+
+type BleGattAccess struct {
+ Op BleGattOp
+ SvcUuid BleUuid
+ ChrUuid BleUuid
+ Data []byte
+}
+
+type BleGattAccessFn func(access BleGattAccess) uint8
+
+type BleDsc struct {
+ Uuid BleUuid
+ AttFlags BleAttFlags
+ MinKeySize int
+}
+
+type BleChr struct {
+ Uuid BleUuid
+ Flags BleChrFlags
+ MinKeySize int
+ AccessCb BleGattAccessFn
+ Dscs []BleDsc
+}
+
+type BleSvc struct {
+ Uuid BleUuid
+ SvcType BleSvcType
+ Chrs []BleChr
+}
diff --git a/nmxact/example/ble_adv/ble_adv b/nmxact/example/ble_adv/ble_adv
new file mode 100755
index 0000000..a3f23f3
Binary files /dev/null and b/nmxact/example/ble_adv/ble_adv differ
diff --git a/nmxact/example/ble_loop/ble_loop.go b/nmxact/example/ble_adv/ble_adv.go
similarity index 51%
copy from nmxact/example/ble_loop/ble_loop.go
copy to nmxact/example/ble_adv/ble_adv.go
index 9e79d29..a82a771 100644
--- a/nmxact/example/ble_loop/ble_loop.go
+++ b/nmxact/example/ble_adv/ble_adv.go
@@ -24,21 +24,20 @@ import (
"os"
"os/signal"
"syscall"
- "time"
log "github.com/Sirupsen/logrus"
- "mynewt.apache.org/newtmgr/nmxact/bledefs"
+ "mynewt.apache.org/newt/util"
+ "mynewt.apache.org/newtmgr/nmxact/adv"
"mynewt.apache.org/newtmgr/nmxact/nmble"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/sesn"
- "mynewt.apache.org/newtmgr/nmxact/xact"
"mynewt.apache.org/newtmgr/nmxact/xport"
)
func configExitHandler(x xport.Xport, s sesn.Sesn) {
onExit := func() {
- if s.IsOpen() {
+ if s != nil && s.IsOpen() {
s.Close()
}
@@ -55,20 +54,24 @@ func configExitHandler(x xport.Xport, s sesn.Sesn) {
case os.Interrupt, syscall.SIGTERM:
onExit()
os.Exit(0)
+
+ case syscall.SIGQUIT:
+ util.PrintStacks()
}
}
}()
}
func main() {
- //nmxutil.SetLogLevel(log.DebugLevel)
- nmxutil.SetLogLevel(log.InfoLevel)
+ nmxutil.Debug = true
+ nmxutil.SetLogLevel(log.DebugLevel)
+ //nmxutil.SetLogLevel(log.InfoLevel)
// Initialize the BLE transport.
params := nmble.NewXportCfg()
params.SockPath = "/tmp/blehostd-uds"
params.BlehostdPath = "blehostd"
- params.DevPath = "/dev/cu.usbmodem142121"
+ params.DevPath = "/dev/cu.usbmodem142141"
x, err := nmble.NewBleXport(params)
if err != nil {
@@ -85,69 +88,29 @@ func main() {
}
defer x.Stop()
- // Find a device to connect to:
- // * Peer has name "nimble-bleprph"
- // * We use a random address.
- dev, err := nmble.DiscoverDeviceWithName(
- x, bledefs.BLE_ADDR_TYPE_RANDOM, 10*time.Second, "c4")
- if err != nil {
- fmt.Fprintf(os.Stderr, "error discovering device: %s\n", err.Error())
- os.Exit(1)
- }
- if dev == nil {
- fmt.Fprintf(os.Stderr, "couldn't find device")
- os.Exit(1)
- }
-
- // Prepare a BLE session:
- // * Plain NMP (not tunnelled over OIC).
- // * We use a random address.
- sc := sesn.NewSesnCfg()
- sc.MgmtProto = sesn.MGMT_PROTO_NMP
- sc.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM
- sc.PeerSpec.Ble = *dev
+ configExitHandler(x, nil)
- s, err := x.BuildSesn(sc)
+ advertiser, err := x.BuildAdvertiser()
if err != nil {
- fmt.Fprintf(os.Stderr, "error creating BLE session: %s\n", err.Error())
+ fmt.Fprintf(os.Stderr, "error building BLE advertiser: %s\n",
+ err.Error())
os.Exit(1)
}
- configExitHandler(x, s)
-
- // Repeatedly:
- // * Connect to peer if unconnected.
- // * Send an echo command to peer.
- //
- // If blehostd crashes or the controller is unplugged, nmxact should
- // recover on the next connect attempt.
for {
- if !s.IsOpen() {
- // Connect to the peer (open the session).
- if err := s.Open(); err != nil {
- fmt.Fprintf(os.Stderr, "error starting BLE session: %s\n",
- err.Error())
- continue
- }
- }
-
- // Send an echo command to the peer.
- c := xact.NewEchoCmd()
- c.Payload = "hello"
-
- res, err := c.Run(s)
+ ac := adv.NewCfg()
+ ac.Ble.AdvFields.Flags = new(uint8)
+ *ac.Ble.AdvFields.Flags = 6
+ ac.Ble.AdvFields.Name = new(string)
+ *ac.Ble.AdvFields.Name = "gwadv"
+ _, err := advertiser.Start(ac)
if err != nil {
- fmt.Fprintf(os.Stderr, "error executing echo command: %s\n",
+ fmt.Fprintf(os.Stderr, "error starting advertise: %s\n",
err.Error())
- continue
- }
-
- if res.Status() != 0 {
- fmt.Printf("Peer responded negatively to echo command; status=%d\n",
- res.Status())
+ os.Exit(1)
}
- eres := res.(*xact.EchoResult)
- fmt.Printf("Peer echoed back: %s\n", eres.Rsp.Payload)
+ //s.Close()
+ select {}
}
}
diff --git a/nmxact/example/ble_adv/bleholog b/nmxact/example/ble_adv/bleholog
new file mode 100644
index 0000000..e69de29
diff --git a/nmxact/example/ble_loop/ble_loop.go b/nmxact/example/ble_loop/ble_loop.go
index 9e79d29..0e5bd99 100644
--- a/nmxact/example/ble_loop/ble_loop.go
+++ b/nmxact/example/ble_loop/ble_loop.go
@@ -68,7 +68,7 @@ func main() {
params := nmble.NewXportCfg()
params.SockPath = "/tmp/blehostd-uds"
params.BlehostdPath = "blehostd"
- params.DevPath = "/dev/cu.usbmodem142121"
+ params.DevPath = "/dev/cu.usbmodem142111"
x, err := nmble.NewBleXport(params)
if err != nil {
@@ -131,6 +131,10 @@ func main() {
}
}
+ if err := s.Open(); err != nil {
+ s.Close()
+ }
+
// Send an echo command to the peer.
c := xact.NewEchoCmd()
c.Payload = "hello"
diff --git a/nmxact/nmble/ble_act.go b/nmxact/nmble/ble_act.go
index 62c30ac..cfa4c14 100644
--- a/nmxact/nmble/ble_act.go
+++ b/nmxact/nmble/ble_act.go
@@ -159,7 +159,7 @@ func connCancel(x *BleXport, bl *Listener, r *BleConnCancelReq) error {
// Blocking.
func discSvcUuid(x *BleXport, bl *Listener, r *BleDiscSvcUuidReq) (
- *BleSvc, error) {
+ *BleDiscSvc, error) {
const rspType = MSG_TYPE_DISC_SVC_UUID
const evtType = MSG_TYPE_DISC_SVC_EVT
@@ -173,7 +173,7 @@ func discSvcUuid(x *BleXport, bl *Listener, r *BleDiscSvcUuidReq) (
return nil, err
}
- var svc *BleSvc
+ var svc *BleDiscSvc
for {
select {
case err := <-bl.ErrChan:
@@ -214,7 +214,7 @@ func discSvcUuid(x *BleXport, bl *Listener, r *BleDiscSvcUuidReq) (
// Blocking.
func discAllChrs(x *BleXport, bl *Listener, r *BleDiscAllChrsReq) (
- []*BleChr, error) {
+ []*BleDiscChr, error) {
const rspType = MSG_TYPE_DISC_ALL_CHRS
const evtType = MSG_TYPE_DISC_CHR_EVT
@@ -228,7 +228,7 @@ func discAllChrs(x *BleXport, bl *Listener, r *BleDiscAllChrsReq) (
return nil, err
}
- chrs := []*BleChr{}
+ chrs := []*BleDiscChr{}
for {
select {
case err := <-bl.ErrChan:
@@ -533,37 +533,47 @@ func encInitiate(x *BleXport, bl *Listener,
}
// Blocking
-func advStart(x *BleXport, bl *Listener, r *BleAdvStartReq) error {
+func advStart(x *BleXport, bl *Listener, r *BleAdvStartReq) (uint16, error) {
const rspType = MSG_TYPE_ADV_START
j, err := json.Marshal(r)
if err != nil {
- return err
+ return 0, err
}
if err := x.Tx(j); err != nil {
- return err
+ return 0, err
}
for {
select {
case err := <-bl.ErrChan:
- return err
+ return 0, err
case bm := <-bl.MsgChan:
switch msg := bm.(type) {
case *BleAdvStartRsp:
bl.Acked = true
if msg.Status != 0 {
- return StatusError(MSG_OP_RSP, rspType, msg.Status)
+ return 0, StatusError(MSG_OP_RSP, rspType, msg.Status)
+ }
+
+ case *BleConnectEvt:
+ if msg.Status == 0 {
+ return msg.ConnHandle, nil
+ } else {
+ str := fmt.Sprintf("BLE peer failed to connect to us; "+
+ "status=%s (%d)",
+ ErrCodeToString(msg.Status), msg.Status)
+ log.Debugf(str)
+ return 0, nmxutil.NewBleHostError(msg.Status, str)
}
- return nil
default:
}
case <-bl.AfterTimeout(x.RspTimeout()):
- return BhdTimeoutError(rspType, r.Seq)
+ return 0, BhdTimeoutError(rspType, r.Seq)
}
}
}
@@ -704,7 +714,7 @@ func advFields(x *BleXport, bl *Listener, r *BleAdvFieldsReq) (
return nil, StatusError(MSG_OP_RSP, rspType, msg.Status)
}
- return msg.Data, nil
+ return msg.Data.Bytes, nil
default:
}
@@ -715,6 +725,136 @@ func advFields(x *BleXport, bl *Listener, r *BleAdvFieldsReq) (
}
}
+func clearSvcs(x *BleXport, bl *Listener, r *BleClearSvcsReq) error {
+ const rspType = MSG_TYPE_CLEAR_SVCS
+
+ j, err := json.Marshal(r)
+ if err != nil {
+ return err
+ }
+
+ x.txNoSync(j)
+ for {
+ select {
+ case err := <-bl.ErrChan:
+ return err
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleClearSvcsRsp:
+ bl.Acked = true
+ if msg.Status != 0 {
+ return StatusError(MSG_OP_RSP, rspType, msg.Status)
+ }
+ return nil
+
+ default:
+ }
+
+ case <-bl.AfterTimeout(x.RspTimeout()):
+ return BhdTimeoutError(rspType, r.Seq)
+ }
+ }
+}
+
+func addSvcs(x *BleXport, bl *Listener, r *BleAddSvcsReq) error {
+ const rspType = MSG_TYPE_ADD_SVCS
+
+ j, err := json.Marshal(r)
+ if err != nil {
+ return err
+ }
+
+ x.txNoSync(j)
+ for {
+ select {
+ case err := <-bl.ErrChan:
+ return err
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleAddSvcsRsp:
+ bl.Acked = true
+ if msg.Status != 0 {
+ return StatusError(MSG_OP_RSP, rspType, msg.Status)
+ }
+ return nil
+
+ default:
+ }
+
+ case <-bl.AfterTimeout(x.RspTimeout()):
+ return BhdTimeoutError(rspType, r.Seq)
+ }
+ }
+}
+
+func commitSvcs(x *BleXport, bl *Listener, r *BleCommitSvcsReq) (
+ []BleRegSvc, error) {
+
+ const rspType = MSG_TYPE_COMMIT_SVCS
+
+ j, err := json.Marshal(r)
+ if err != nil {
+ return nil, err
+ }
+
+ x.txNoSync(j)
+ for {
+ select {
+ case err := <-bl.ErrChan:
+ return nil, err
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleCommitSvcsRsp:
+ bl.Acked = true
+ if msg.Status != 0 {
+ return nil, StatusError(MSG_OP_RSP, rspType, msg.Status)
+ }
+ return msg.Svcs, nil
+
+ default:
+ }
+
+ case <-bl.AfterTimeout(x.RspTimeout()):
+ return nil, BhdTimeoutError(rspType, r.Seq)
+ }
+ }
+}
+
+func accessStatus(x *BleXport, bl *Listener, r *BleAccessStatusReq) error {
+ const rspType = MSG_TYPE_ACCESS_STATUS
+
+ j, err := json.Marshal(r)
+ if err != nil {
+ return err
+ }
+
+ x.Tx(j)
+ for {
+ select {
+ case err := <-bl.ErrChan:
+ return err
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleAccessStatusRsp:
+ bl.Acked = true
+ if msg.Status != 0 {
+ return StatusError(MSG_OP_RSP, rspType, msg.Status)
+ }
+ return nil
+
+ default:
+ }
+
+ case <-bl.AfterTimeout(x.RspTimeout()):
+ return BhdTimeoutError(rspType, r.Seq)
+ }
+ }
+}
+
// Asks the controller to generate a random address. This is done when the
// transport is starting up, and therefore does not require the transport to be
// synced. Only the transport should call this function.
@@ -821,6 +961,7 @@ func setPreferredMtu(x *BleXport, bl *Listener,
case <-bl.AfterTimeout(x.RspTimeout()):
return BhdTimeoutError(rspType, r.Seq)
+
}
}
}
diff --git a/nmxact/nmble/ble_advertiser.go b/nmxact/nmble/ble_advertiser.go
new file mode 100644
index 0000000..6888009
--- /dev/null
+++ b/nmxact/nmble/ble_advertiser.go
@@ -0,0 +1,214 @@
+package nmble
+
+import (
+ "fmt"
+
+ log "github.com/Sirupsen/logrus"
+
+ "mynewt.apache.org/newtmgr/nmxact/adv"
+ . "mynewt.apache.org/newtmgr/nmxact/bledefs"
+ "mynewt.apache.org/newtmgr/nmxact/nmxutil"
+ "mynewt.apache.org/newtmgr/nmxact/sesn"
+)
+
+type Advertiser struct {
+ bx *BleXport
+ stopChan chan struct{}
+ stoppedChan chan struct{}
+}
+
+func NewAdvertiser(bx *BleXport) *Advertiser {
+ return &Advertiser{
+ bx: bx,
+ }
+}
+
+func (a *Advertiser) fields(f BleAdvFields) ([]byte, error) {
+ r := BleAdvFieldsToReq(f)
+
+ bl, err := a.bx.AddListener(SeqKey(r.Seq))
+ if err != nil {
+ return nil, err
+ }
+ defer a.bx.RemoveListener(bl)
+
+ return advFields(a.bx, bl, r)
+}
+
+func (a *Advertiser) setAdvData(data []byte) error {
+ r := NewBleAdvSetDataReq()
+ r.Data = BleBytes{data}
+
+ bl, err := a.bx.AddListener(SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer a.bx.RemoveListener(bl)
+
+ if err := advSetData(a.bx, bl, r); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (a *Advertiser) setRspData(data []byte) error {
+ r := NewBleAdvRspSetDataReq()
+ r.Data = BleBytes{data}
+
+ bl, err := a.bx.AddListener(SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer a.bx.RemoveListener(bl)
+
+ if err := advRspSetData(a.bx, bl, r); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (a *Advertiser) advertise(cfg adv.Cfg) (uint16, *Listener, error) {
+ r := NewBleAdvStartReq()
+
+ r.OwnAddrType = cfg.Ble.OwnAddrType
+ r.DurationMs = 0x7fffffff
+ r.ConnMode = cfg.Ble.ConnMode
+ r.DiscMode = cfg.Ble.DiscMode
+ r.ItvlMin = cfg.Ble.ItvlMin
+ r.ItvlMax = cfg.Ble.ItvlMax
+ r.ChannelMap = cfg.Ble.ChannelMap
+ r.FilterPolicy = cfg.Ble.FilterPolicy
+ r.HighDutyCycle = cfg.Ble.HighDutyCycle
+ r.PeerAddr = cfg.Ble.PeerAddr
+
+ bl, err := a.bx.AddListener(SeqKey(r.Seq))
+ if err != nil {
+ return 0, nil, err
+ }
+
+ connHandle, err := advStart(a.bx, bl, r)
+ if err != nil {
+ a.bx.RemoveListener(bl)
+ if !nmxutil.IsXport(err) {
+ // The transport did not restart; always attempt to cancel the
+ // advertise operation. In some cases, the host has already stopped
+ // advertising and will respond with an "ealready" error that can be
+ // ignored.
+ if err := a.stopAdvertising(); err != nil {
+ log.Errorf("Failed to cancel advertise in progress: %s",
+ err.Error())
+ }
+ }
+ return 0, nil, err
+ }
+
+ return connHandle, bl, nil
+}
+
+func (a *Advertiser) stopAdvertising() error {
+ r := NewBleAdvStopReq()
+
+ bl, err := a.bx.AddListener(SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer a.bx.RemoveListener(bl)
+
+ return advStop(a.bx, bl, r)
+}
+
+func (a *Advertiser) buildSesn(cfg adv.Cfg, connHandle uint16, bl *Listener) (
+ sesn.Sesn, error) {
+
+ // XXX: Build different kinds of sessions.
+ s := NewBleCoapSesn(a.bx, cfg.Ble.SesnCfg)
+ if err := s.OpenConnected(connHandle, bl); err != nil {
+ return nil, err
+ }
+
+ return s, nil
+}
+
+func (a *Advertiser) Start(cfg adv.Cfg) (sesn.Sesn, error) {
+ var advData []byte
+ var rspData []byte
+ var connHandle uint16
+ var bl *Listener
+ var err error
+
+ fns := []func() error{
+ // Convert advertising fields to data.
+ func() error {
+ advData, err = a.fields(cfg.Ble.AdvFields)
+ return err
+ },
+
+ // Set advertising data.
+ func() error {
+ return a.setAdvData(advData)
+ },
+
+ // Convert response fields to data.
+ func() error {
+ rspData, err = a.fields(cfg.Ble.RspFields)
+ return err
+ },
+
+ // Set response data.
+ func() error {
+ return a.setRspData(rspData)
+ },
+
+ // Advertise
+ func() error {
+ connHandle, bl, err = a.advertise(cfg)
+ return err
+ },
+ }
+
+ a.stopChan = make(chan struct{})
+ a.stoppedChan = make(chan struct{})
+
+ defer func() {
+ a.stopChan = nil
+ close(a.stoppedChan)
+ }()
+
+ if err := a.bx.AcquireSlave(a); err != nil {
+ return nil, err
+ }
+ defer a.bx.ReleaseSlave()
+
+ for _, fn := range fns {
+ // Check for abort before each step.
+ select {
+ case <-a.stopChan:
+ return nil, fmt.Errorf("advertise aborted")
+ default:
+ }
+
+ if err := fn(); err != nil {
+ return nil, err
+ }
+ }
+
+ return a.buildSesn(cfg, connHandle, bl)
+}
+
+func (a *Advertiser) Stop() error {
+ stopChan := a.stopChan
+ if stopChan == nil {
+ return fmt.Errorf("advertiser already stopped")
+ }
+ close(stopChan)
+
+ a.bx.StopWaitingForSlave(a, fmt.Errorf("advertise aborted"))
+ a.stopAdvertising()
+
+ // Block until abort is complete.
+ <-a.stoppedChan
+
+ return nil
+}
diff --git a/nmxact/nmble/ble_coap_sesn.go b/nmxact/nmble/ble_coap_sesn.go
new file mode 100644
index 0000000..a96127e
--- /dev/null
+++ b/nmxact/nmble/ble_coap_sesn.go
@@ -0,0 +1,176 @@
+package nmble
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/runtimeco/go-coap"
+
+ "mynewt.apache.org/newt/util"
+ . "mynewt.apache.org/newtmgr/nmxact/bledefs"
+ "mynewt.apache.org/newtmgr/nmxact/nmp"
+ "mynewt.apache.org/newtmgr/nmxact/nmxutil"
+ "mynewt.apache.org/newtmgr/nmxact/oic"
+ "mynewt.apache.org/newtmgr/nmxact/omp"
+ "mynewt.apache.org/newtmgr/nmxact/sesn"
+)
+
+type BleCoapSesn struct {
+ bf *BleFsm
+ d *oic.Dispatcher
+ closeTimeout time.Duration
+ onCloseCb sesn.OnCloseFn
+ wg sync.WaitGroup
+
+ closeChan chan struct{}
+}
+
+func NewBleCoapSesn(bx *BleXport, cfg sesn.SesnCfg) *BleCoapSesn {
+ bcs := &BleCoapSesn{
+ closeTimeout: cfg.Ble.CloseTimeout,
+ onCloseCb: cfg.OnCloseCb,
+ }
+
+ bcs.bf = NewBleFsm(BleFsmParams{
+ Bx: bx,
+ OwnAddrType: cfg.Ble.OwnAddrType,
+ Central: BleFsmParamsCentral{
+ PeerDev: cfg.PeerSpec.Ble,
+ ConnTries: cfg.Ble.Central.ConnTries,
+ ConnTimeout: cfg.Ble.Central.ConnTimeout,
+ },
+
+ EncryptWhen: cfg.Ble.EncryptWhen,
+ })
+
+ return bcs
+}
+
+func (bcs *BleCoapSesn) AbortRx(seq uint8) error {
+ return fmt.Errorf("BleCoapSesn.AbortRx unimplemented")
+}
+
+func (bcs *BleCoapSesn) openInit() {
+ // This channel gets closed when the session closes.
+ bcs.closeChan = make(chan struct{})
+ bcs.d = oic.NewDispatcher(true, 3)
+
+ // Listen for disconnect in the background.
+ bcs.wg.Add(1)
+ go func() {
+ // If the session is being closed, unblock the close() call.
+ defer close(bcs.closeChan)
+
+ // Block until disconnect.
+ <-bcs.bf.DisconnectChan()
+ nmxutil.Assert(!bcs.IsOpen())
+ pd := bcs.bf.PrevDisconnect()
+
+ // Signal error to all listeners.
+ bcs.d.ErrorAll(pd.Err)
+ bcs.wg.Done()
+ bcs.wg.Wait()
+
+ // Only execute the client's disconnect callback if the disconnect was
+ // unsolicited.
+ if pd.Dt != FSM_DISCONNECT_TYPE_REQUESTED && bcs.onCloseCb != nil {
+ bcs.onCloseCb(bcs, pd.Err)
+ }
+ }()
+}
+
+func (bcs *BleCoapSesn) OpenConnected(
+ connHandle uint16, eventListener *Listener) error {
+
+ bcs.openInit()
+
+ if err := bcs.bf.StartConnected(connHandle, eventListener); err != nil {
+ close(bcs.closeChan)
+ return err
+ }
+ return nil
+}
+
+func (bcs *BleCoapSesn) Open() error {
+ bcs.openInit()
+
+ if err := bcs.bf.Start(); err != nil {
+ close(bcs.closeChan)
+ return err
+ }
+ return nil
+}
+
+func (bcs *BleCoapSesn) Close() error {
+ err := bcs.bf.Stop()
+ if err != nil {
+ return err
+ }
+
+ // Block until close completes.
+ <-bcs.closeChan
+ return nil
+}
+
+func (bcs *BleCoapSesn) IsOpen() bool {
+ return bcs.bf.IsOpen()
+}
+
+func (bcs *BleCoapSesn) EncodeNmpMsg(m *nmp.NmpMsg) ([]byte, error) {
+ return nil, fmt.Errorf("BleCoapSesn.EncodeNmpMsg unimplemented")
+}
+
+// Blocking.
+func (bcs *BleCoapSesn) TxNmpOnce(m *nmp.NmpMsg, opt sesn.TxOptions) (
+ nmp.NmpRsp, error) {
+
+ return nil, fmt.Errorf("BleCoapSesn.TxNmpOnce unimplemented")
+}
+
+func (bcs *BleCoapSesn) MtuIn() int {
+ return bcs.bf.attMtu -
+ NOTIFY_CMD_BASE_SZ -
+ omp.OMP_MSG_OVERHEAD -
+ nmp.NMP_HDR_SIZE
+}
+
+func (bcs *BleCoapSesn) MtuOut() int {
+ mtu := bcs.bf.attMtu -
+ WRITE_CMD_BASE_SZ -
+ omp.OMP_MSG_OVERHEAD -
+ nmp.NMP_HDR_SIZE
+ return util.IntMin(mtu, BLE_ATT_ATTR_MAX_LEN)
+}
+
+func (bcs *BleCoapSesn) ConnInfo() (BleConnDesc, error) {
+ return bcs.bf.connInfo()
+}
+
+func (bcs *BleCoapSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
+ []byte, error) {
+
+ token := nmxutil.NextToken()
+
+ ol, err := bcs.d.AddListener(token)
+ if err != nil {
+ return nil, err
+ }
+ defer bcs.d.RemoveListener(token)
+
+ req, err := oic.EncodeGet(uri, token)
+ if err != nil {
+ return nil, err
+ }
+
+ rsp, err := bcs.bf.TxOic(req, ol, opt.Timeout)
+ if err != nil {
+ return nil, err
+ }
+
+ if rsp.Code != coap.Content {
+ return nil, fmt.Errorf("UNEXPECTED OIC ACK: %#v", rsp)
+ }
+
+ return rsp.Payload, nil
+}
diff --git a/nmxact/nmble/ble_fsm.go b/nmxact/nmble/ble_fsm.go
index 8d2fd1c..f319db5 100644
--- a/nmxact/nmble/ble_fsm.go
+++ b/nmxact/nmble/ble_fsm.go
@@ -54,9 +54,10 @@ const (
SESN_STATE_DISCOVER_CHR = 4
SESN_STATE_SECURITY = 5
SESN_STATE_SUBSCRIBE = 6
- SESN_STATE_DONE = 7
- SESN_STATE_TERMINATING = 8
- SESN_STATE_CONN_CANCELLING = 9
+ SESN_STATE_GET_INFO = 7
+ SESN_STATE_DONE = 8
+ SESN_STATE_TERMINATING = 9
+ SESN_STATE_CONN_CANCELLING = 10
)
type BleFsmDisconnectType int
@@ -94,10 +95,11 @@ type BleFsm struct {
params BleFsmParams
connHandle uint16
+ connDesc BleConnDesc
peerDev BleDev
- nmpSvc *BleSvc
- nmpReqChr *BleChr
- nmpRspChr *BleChr
+ nmpSvc *BleDiscSvc
+ nmpReqChr *BleDiscChr
+ nmpRspChr *BleDiscChr
prevDisconnect BleDisconnectEntry
attMtu int
state BleSesnState
@@ -144,15 +146,6 @@ func (bf *BleFsm) connInfo() (BleConnDesc, error) {
return ConnFindXact(bf.params.Bx, bf.connHandle)
}
-func (bf *BleFsm) logConnection() {
- desc, err := bf.connInfo()
- if err != nil {
- return
- }
-
- log.Debugf("BLE connection attempt succeeded; %s", desc.String())
-}
-
func fsmErrorLess(a error, b error) bool {
aIsXport := nmxutil.IsXport(a)
bIsXport := nmxutil.IsXport(b)
@@ -217,7 +210,7 @@ func (bf *BleFsm) listenForError() {
}
// Listens for events in the background.
-func (bf *BleFsm) eventListen(bl *Listener, seq BleSeq) error {
+func (bf *BleFsm) eventListen(bl *Listener) error {
bf.wg.Add(1)
go func() {
@@ -354,7 +347,7 @@ func (bf *BleFsm) connect() error {
}
// Listen for events in the background.
- if err := bf.eventListen(bl, r.Seq); err != nil {
+ if err := bf.eventListen(bl); err != nil {
return err
}
@@ -420,7 +413,7 @@ func (bf *BleFsm) connCancel() error {
return nil
}
-func (bf *BleFsm) discSvcUuidOnce(uuid BleUuid) (*BleSvc, error) {
+func (bf *BleFsm) discSvcUuidOnce(uuid BleUuid) (*BleDiscSvc, error) {
r := NewBleDiscSvcUuidReq()
r.ConnHandle = bf.connHandle
r.Uuid = uuid
@@ -539,6 +532,12 @@ func (bf *BleFsm) exchangeMtu() error {
mtu, err := exchangeMtu(bf.params.Bx, bl, r)
if err != nil {
+ // If the operation failed because the peer already initiated the
+ // exchange, just pretend it was successful.
+ bhe := nmxutil.ToBleHost(err)
+ if bhe != nil && bhe.Status == ERR_CODE_EALREADY {
+ return nil
+ }
return err
}
@@ -620,31 +619,43 @@ func (bf *BleFsm) executeState() (bool, error) {
bf.state = SESN_STATE_DISCOVER_SVC
case SESN_STATE_DISCOVER_SVC:
- if err := bf.discSvcUuid(); err != nil {
- return false, err
+ if len(bf.params.SvcUuids) > 0 {
+ if err := bf.discSvcUuid(); err != nil {
+ return false, err
+ }
}
bf.state = SESN_STATE_DISCOVER_CHR
case SESN_STATE_DISCOVER_CHR:
- if err := bf.discAllChrs(); err != nil {
- return false, err
- }
- if bf.shouldEncrypt() {
- bf.state = SESN_STATE_SECURITY
- } else {
- bf.state = SESN_STATE_SUBSCRIBE
+ if bf.nmpSvc != nil {
+ if err := bf.discAllChrs(); err != nil {
+ return false, err
+ }
}
+ bf.state = SESN_STATE_SECURITY
case SESN_STATE_SECURITY:
- if err := bf.encInitiate(); err != nil {
- return false, err
+ if bf.shouldEncrypt() {
+ if err := bf.encInitiate(); err != nil {
+ return false, err
+ }
}
bf.state = SESN_STATE_SUBSCRIBE
case SESN_STATE_SUBSCRIBE:
- if err := bf.subscribe(); err != nil {
+ if bf.nmpRspChr != nil {
+ if err := bf.subscribe(); err != nil {
+ return false, err
+ }
+ }
+ bf.state = SESN_STATE_GET_INFO
+
+ case SESN_STATE_GET_INFO:
+ desc, err := bf.connInfo()
+ if err != nil {
return false, err
}
+ bf.connDesc = desc
bf.state = SESN_STATE_DONE
case SESN_STATE_DONE:
@@ -670,13 +681,11 @@ func (bf *BleFsm) PrevDisconnect() BleDisconnectEntry {
return bf.prevDisconnect
}
-func (bf *BleFsm) startOnce() (bool, error) {
- if !bf.IsClosed() {
- return false, nmxutil.NewSesnAlreadyOpenError(fmt.Sprintf(
- "Attempt to open an already-open BLE session (state=%d)",
- bf.state))
- }
+func (bf *BleFsm) IsCentral() bool {
+ return bf.connDesc.Role == BLE_ROLE_MASTER
+}
+func (bf *BleFsm) startOnce() (bool, error) {
bf.disconnectChan = make(chan struct{})
bf.rxNmpChan = make(chan []byte)
@@ -705,6 +714,12 @@ func (bf *BleFsm) startOnce() (bool, error) {
func (bf *BleFsm) Start() error {
var err error
+ if !bf.IsClosed() {
+ return nmxutil.NewSesnAlreadyOpenError(fmt.Sprintf(
+ "Attempt to open an already-open BLE session (state=%d)",
+ bf.state))
+ }
+
for i := 0; i < bf.params.Central.ConnTries; i++ {
var retry bool
retry, err = bf.startOnce()
@@ -722,6 +737,24 @@ func (bf *BleFsm) Start() error {
return nil
}
+func (bf *BleFsm) StartConnected(
+ connHandle uint16, eventListener *Listener) error {
+
+ bf.connHandle = connHandle
+ if err := bf.eventListen(eventListener); err != nil {
+ return err
+ }
+
+ bf.state = SESN_STATE_EXCHANGE_MTU
+ if _, err := bf.startOnce(); err != nil {
+ nmxutil.Assert(!bf.IsOpen())
+ nmxutil.Assert(bf.IsClosed())
+ return err
+ }
+
+ return nil
+}
+
// @return bool true if stop complete;
// false if disconnect is now pending.
func (bf *BleFsm) Stop() error {
diff --git a/nmxact/nmble/ble_proto.go b/nmxact/nmble/ble_proto.go
index aa8dc9c..0716be2 100644
--- a/nmxact/nmble/ble_proto.go
+++ b/nmxact/nmble/ble_proto.go
@@ -226,6 +226,46 @@ var HciErrCodeStringMap = map[int]string{
ERR_CODE_HCI_COARSE_CLK_ADJ: "coarse clk adj",
}
+const (
+ ERR_CODE_ATT_INVALID_HANDLE int = 0x01
+ ERR_CODE_ATT_READ_NOT_PERMITTED = 0x02
+ ERR_CODE_ATT_WRITE_NOT_PERMITTED = 0x03
+ ERR_CODE_ATT_INVALID_PDU = 0x04
+ ERR_CODE_ATT_INSUFFICIENT_AUTHEN = 0x05
+ ERR_CODE_ATT_REQ_NOT_SUPPORTED = 0x06
+ ERR_CODE_ATT_INVALID_OFFSET = 0x07
+ ERR_CODE_ATT_INSUFFICIENT_AUTHOR = 0x08
+ ERR_CODE_ATT_PREPARE_QUEUE_FULL = 0x09
+ ERR_CODE_ATT_ATTR_NOT_FOUND = 0x0a
+ ERR_CODE_ATT_ATTR_NOT_LONG = 0x0b
+ ERR_CODE_ATT_INSUFFICIENT_KEY_SZ = 0x0c
+ ERR_CODE_ATT_INVALID_ATTR_VALUE_LEN = 0x0d
+ ERR_CODE_ATT_UNLIKELY = 0x0e
+ ERR_CODE_ATT_INSUFFICIENT_ENC = 0x0f
+ ERR_CODE_ATT_UNSUPPORTED_GROUP = 0x10
+ ERR_CODE_ATT_INSUFFICIENT_RES = 0x11
+)
+
+var AttErrCodeStringMap = map[int]string{
+ ERR_CODE_ATT_INVALID_HANDLE: "invalid handle",
+ ERR_CODE_ATT_READ_NOT_PERMITTED: "read not permitted",
+ ERR_CODE_ATT_WRITE_NOT_PERMITTED: "write not permitted",
+ ERR_CODE_ATT_INVALID_PDU: "invalid pdu",
+ ERR_CODE_ATT_INSUFFICIENT_AUTHEN: "insufficient authentication",
+ ERR_CODE_ATT_REQ_NOT_SUPPORTED: "request not supported",
+ ERR_CODE_ATT_INVALID_OFFSET: "invalid offset",
+ ERR_CODE_ATT_INSUFFICIENT_AUTHOR: "insufficient authorization",
+ ERR_CODE_ATT_PREPARE_QUEUE_FULL: "prepare queue full",
+ ERR_CODE_ATT_ATTR_NOT_FOUND: "attribute not found",
+ ERR_CODE_ATT_ATTR_NOT_LONG: "attribute not long",
+ ERR_CODE_ATT_INSUFFICIENT_KEY_SZ: "insufficient key size",
+ ERR_CODE_ATT_INVALID_ATTR_VALUE_LEN: "invalid attribute value length",
+ ERR_CODE_ATT_UNLIKELY: "unlikely error",
+ ERR_CODE_ATT_INSUFFICIENT_ENC: "insufficient encryption",
+ ERR_CODE_ATT_UNSUPPORTED_GROUP: "unsupported group",
+ ERR_CODE_ATT_INSUFFICIENT_RES: "insufficient resources",
+}
+
// These values never get transmitted or received, so their precise values
// don't matter. We specify them explicitly here to match the blehostd source.
const (
@@ -262,6 +302,10 @@ const (
MSG_TYPE_ADV_SET_DATA = 23
MSG_TYPE_ADV_RSP_SET_DATA = 24
MSG_TYPE_ADV_FIELDS = 25
+ MSG_TYPE_CLEAR_SVCS = 26
+ MSG_TYPE_ADD_SVCS = 27
+ MSG_TYPE_COMMIT_SVCS = 28
+ MSG_TYPE_ACCESS_STATUS = 29
MSG_TYPE_SYNC_EVT = 2049
MSG_TYPE_CONNECT_EVT = 2050
@@ -275,6 +319,7 @@ const (
MSG_TYPE_SCAN_TMO_EVT = 2058
MSG_TYPE_ENC_CHANGE_EVT = 2059
MSG_TYPE_RESET_EVT = 2060
+ MSG_TYPE_ACCESS_EVT = 2061
)
var MsgOpStringMap = map[MsgOp]string{
@@ -307,6 +352,10 @@ var MsgTypeStringMap = map[MsgType]string{
MSG_TYPE_ADV_SET_DATA: "adv_set_data",
MSG_TYPE_ADV_RSP_SET_DATA: "adv_rsp_set_data",
MSG_TYPE_ADV_FIELDS: "adv_fields",
+ MSG_TYPE_CLEAR_SVCS: "clear_svcs",
+ MSG_TYPE_ADD_SVCS: "add_svcs",
+ MSG_TYPE_COMMIT_SVCS: "commit_svcs",
+ MSG_TYPE_ACCESS_STATUS: "access_status",
MSG_TYPE_SYNC_EVT: "sync_evt",
MSG_TYPE_CONNECT_EVT: "connect_evt",
@@ -319,6 +368,7 @@ var MsgTypeStringMap = map[MsgType]string{
MSG_TYPE_SCAN_TMO_EVT: "scan_tmo_evt",
MSG_TYPE_ENC_CHANGE_EVT: "enc_change_evt",
MSG_TYPE_RESET_EVT: "reset_evt",
+ MSG_TYPE_ACCESS_EVT: "access_evt",
}
type BleHdr struct {
@@ -329,13 +379,13 @@ type BleHdr struct {
type Msg interface{}
-type BleSvc struct {
+type BleDiscSvc struct {
StartHandle int `json:"start_handle"`
EndHandle int `json:"end_handle"`
Uuid BleUuid `json:"uuid"`
}
-type BleChr struct {
+type BleDiscChr struct {
DefHandle int `json:"def_handle"`
ValHandle int `json:"val_handle"`
Properties int `json:"properties"`
@@ -469,8 +519,8 @@ type BleDiscSvcEvt struct {
Seq BleSeq `json:"seq"`
// Mandatory
- Status int `json:"status"`
- Svc BleSvc `json:"service"`
+ Status int `json:"status"`
+ Svc BleDiscSvc `json:"service"`
}
type BleDiscChrUuidReq struct {
@@ -546,8 +596,8 @@ type BleDiscChrEvt struct {
Seq BleSeq `json:"seq"`
// Mandatory
- Status int `json:"status"`
- Chr BleChr `json:"characteristic"`
+ Status int `json:"status"`
+ Chr BleDiscChr `json:"characteristic"`
}
type BleWriteCmdReq struct {
@@ -870,7 +920,7 @@ type BleAdvStartReq struct {
HighDutyCycle bool `json:"high_duty_cycle"`
// Only required for direct advertisements
- PeerAddr *BleAddr `json:"peer_addr"`
+ PeerAddr *BleAddr `json:"peer_addr,omitempty"`
}
type BleAdvStartRsp struct {
@@ -907,7 +957,7 @@ type BleAdvSetDataReq struct {
Seq BleSeq `json:"seq"`
// Mandatory
- Data []byte `json:"data"`
+ Data BleBytes `json:"data"`
}
type BleAdvSetDataRsp struct {
@@ -927,7 +977,7 @@ type BleAdvRspSetDataReq struct {
Seq BleSeq `json:"seq"`
// Mandatory
- Data []byte `json:"data"`
+ Data BleBytes `json:"data"`
}
type BleAdvRspSetDataRsp struct {
@@ -950,51 +1000,51 @@ type BleAdvFieldsReq struct {
Flags *uint8 `json:"flags,omitempty"`
/*** 0x02,0x03 - 16-bit service class UUIDs. */
- Uuids16 []BleUuid16 `json:"uuids16"`
+ Uuids16 []BleUuid16 `json:"uuids16,omitempty"`
Uuids16IsComplete bool `json:"uuids16_is_complete"`
/*** 0x04,0x05 - 32-bit service class UUIDs. */
- Uuids32 []uint32 `json:"uuids32"`
+ Uuids32 []uint32 `json:"uuids32,omitempty"`
Uuids32IsComplete bool `json:"uuids32_is_complete"`
/*** 0x06,0x07 - 128-bit service class UUIDs. */
- Uuids128 []BleUuid128 `json:"uuids128"`
+ Uuids128 []BleUuid128 `json:"uuids128,omitempty"`
Uuids128IsComplete bool `json:"uuids128_is_complete"`
/*** 0x08,0x09 - Local name. */
- Name *string `json:"name,omitempty"`
+ Name *string `json:"name,omitempty,omitempty"`
NameIsComplete bool `json:"name_is_complete"`
/*** 0x0a - Tx power level. */
- TxPwrLvl *int8 `json:"tx_pwr_lvl"`
+ TxPwrLvl *int8 `json:"tx_pwr_lvl,omitempty"`
/*** 0x0d - Slave connection interval range. */
- SlaveItvlMin *uint16 `json:"slave_itvl_min"`
- SlaveItvlMax *uint16 `json:"slave_itvl_max"`
+ SlaveItvlMin *uint16 `json:"slave_itvl_min,omitempty"`
+ SlaveItvlMax *uint16 `json:"slave_itvl_max,omitempty"`
/*** 0x16 - Service data - 16-bit UUID. */
- SvcDataUuid16 []byte `json:"svc_data_uuid16"`
+ SvcDataUuid16 BleBytes `json:"svc_data_uuid16,omitempty"`
/*** 0x17 - Public target address. */
- PublicTgtAddrs []BleAddr `json:"public_tgt_addrs"`
+ PublicTgtAddrs []BleAddr `json:"public_tgt_addrs,omitempty"`
/*** 0x19 - Appearance. */
- Appearance *uint16 `json:"appearance"`
+ Appearance *uint16 `json:"appearance,omitempty"`
/*** 0x1a - Advertising interval. */
- AdvItvl *uint16 `json:"adv_itvl"`
+ AdvItvl *uint16 `json:"adv_itvl,omitempty"`
/*** 0x20 - Service data - 32-bit UUID. */
- SvcDataUuid32 []byte `json:"svc_data_uuid32"`
+ SvcDataUuid32 BleBytes `json:"svc_data_uuid32,omitempty"`
/*** 0x21 - Service data - 128-bit UUID. */
- SvcDataUuid128 []byte `json:"svc_data_uuid128"`
+ SvcDataUuid128 BleBytes `json:"svc_data_uuid128,omitempty"`
/*** 0x24 - URI. */
Uri *string `json:"uri,omitempty"`
/*** 0xff - Manufacturer specific data. */
- MfgData []byte `json:"mfg_data"`
+ MfgData BleBytes `json:"mfg_data,omitempty"`
}
type BleAdvFieldsRsp struct {
@@ -1004,8 +1054,8 @@ type BleAdvFieldsRsp struct {
Seq BleSeq `json:"seq"`
// Mandatory
- Status int `json:"status"`
- Data []byte `json:"data"`
+ Status int `json:"status"`
+ Data BleBytes `json:"data"`
}
type BleResetEvt struct {
@@ -1018,6 +1068,132 @@ type BleResetEvt struct {
Reason int `json:"reason"`
}
+type BleClearSvcsReq struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+}
+
+type BleClearSvcsRsp struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+}
+
+type BleAddDsc struct {
+ Uuid BleUuid `json:"uuid"`
+ AttFlags BleAttFlags `json:"att_flags"`
+ MinKeySize int `json:"min_key_size"`
+}
+
+type BleAddChr struct {
+ Uuid BleUuid `json:"uuid"`
+ Flags BleChrFlags `json:"flags"`
+ MinKeySize int `json:"min_key_size"`
+ Dscs []BleAddDsc `json:"descriptors,omitempty"`
+}
+
+type BleAddSvc struct {
+ Uuid BleUuid `json:"uuid"`
+ SvcType BleSvcType `json:"type"`
+ Chrs []BleAddChr `json:"characteristics,omitempty"`
+}
+
+type BleAddSvcsReq struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ Svcs []BleAddSvc `json:"services"`
+}
+
+type BleAddSvcsRsp struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+}
+
+type BleRegDsc struct {
+ Uuid BleUuid `json:"uuid"`
+ Handle uint16 `json:"handle"`
+}
+
+type BleRegChr struct {
+ Uuid BleUuid `json:"uuid"`
+ DefHandle uint16 `json:"def_handle"`
+ ValHandle uint16 `json:"val_handle"`
+ Dscs []BleRegDsc `json:"descriptors"`
+}
+
+type BleRegSvc struct {
+ Uuid BleUuid `json:"uuid"`
+ Handle uint16 `json:"handle"`
+ Chrs []BleRegChr `json:"characteristics"`
+}
+
+type BleCommitSvcsReq struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+}
+
+type BleCommitSvcsRsp struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+
+ // Optional
+ Svcs []BleRegSvc `json:"services"`
+}
+
+type BleAccessEvt struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ GattOp BleGattOp `json:"gatt_op"`
+ ConnHandle uint16 `json:"conn_handle"`
+ AttHandle uint16 `json:"att_handle"`
+ Data BleBytes `json:"data"`
+}
+
+type BleAccessStatusReq struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ AttStatus uint8 `json:"att_status"`
+}
+
+type BleAccessStatusRsp struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+}
+
func ErrCodeToString(e int) string {
var s string
diff --git a/nmxact/nmble/ble_util.go b/nmxact/nmble/ble_util.go
index a469a55..aecf4dd 100644
--- a/nmxact/nmble/ble_util.go
+++ b/nmxact/nmble/ble_util.go
@@ -312,6 +312,38 @@ func NewBleAdvStopReq() *BleAdvStopReq {
}
}
+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 ConnFindXact(x *BleXport, connHandle uint16) (BleConnDesc, error) {
r := NewBleConnFindReq()
r.ConnHandle = connHandle
@@ -380,6 +412,56 @@ func ResetXact(x *BleXport) error {
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) error {
+ r := NewAccessStatusReq()
+ r.AttStatus = attStatus
+
+ bl, err := x.AddListener(SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer x.RemoveListener(bl)
+
+ return accessStatus(x, bl, r)
+}
+
func DiscoverDeviceWithName(
bx *BleXport,
ownAddrType BleAddrType,
@@ -392,3 +474,59 @@ func DiscoverDeviceWithName(
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
+}
diff --git a/nmxact/nmble/ble_xport.go b/nmxact/nmble/ble_xport.go
index 0e622da..071f3e0 100644
--- a/nmxact/nmble/ble_xport.go
+++ b/nmxact/nmble/ble_xport.go
@@ -29,6 +29,7 @@ import (
log "github.com/Sirupsen/logrus"
"mynewt.apache.org/newt/util/unixchild"
+ "mynewt.apache.org/newtmgr/nmxact/adv"
. "mynewt.apache.org/newtmgr/nmxact/bledefs"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/scan"
@@ -115,6 +116,8 @@ type BleXport struct {
randAddr *BleAddr
stateMtx sync.Mutex
scanner *BleScanner
+ advertiser *Advertiser
+ cm ChrMgr
}
func NewBleXport(cfg XportCfg) (*BleXport, error) {
@@ -168,6 +171,16 @@ func (bx *BleXport) BuildScanner() (scan.Scanner, error) {
return bx.scanner, nil
}
+func (bx *BleXport) BuildAdvertiser() (adv.Advertiser, error) {
+ // The transport only allows a single advertiser. This is because the
+ // slave privileges need to managed among all the advertise operations.
+ if bx.advertiser == nil {
+ bx.advertiser = NewAdvertiser(bx)
+ }
+
+ return bx.advertiser, nil
+}
+
func (bx *BleXport) BuildSesn(cfg sesn.SesnCfg) (sesn.Sesn, error) {
switch cfg.MgmtProto {
case sesn.MGMT_PROTO_NMP:
@@ -193,6 +206,12 @@ func (bx *BleXport) addResetListener() (*Listener, error) {
return bx.AddListener(key)
}
+func (bx *BleXport) addAccessListener() (*Listener, error) {
+ key := TchKey(MSG_TYPE_ACCESS_EVT, -1)
+ nmxutil.LogAddListener(3, key, 0, "access")
+ return bx.AddListener(key)
+}
+
func (bx *BleXport) querySyncStatus() (bool, error) {
req := &BleSyncReq{
Op: MSG_OP_REQ,
@@ -432,12 +451,16 @@ func (bx *BleXport) startOnce() error {
"Timeout waiting for host <-> controller sync")
bx.shutdown(true, err)
return err
+ case <-bx.stopChan:
+ return nmxutil.NewXportError("Transport startup aborted")
}
}
}
- // Host and controller are synced. Listen for sync loss and stack reset in
- // the background.
+ // Host and controller are synced. Listen for events in the background:
+ // * sync loss
+ // * stack reset
+ // * GATT access
go func() {
resetl, err := bx.addResetListener()
if err != nil {
@@ -446,6 +469,13 @@ func (bx *BleXport) startOnce() error {
}
defer bx.RemoveListener(resetl)
+ accessl, err := bx.addAccessListener()
+ if err != nil {
+ bx.shutdown(true, err)
+ return
+ }
+ defer bx.RemoveListener(accessl)
+
for {
select {
case err := <-syncl.ErrChan:
@@ -481,6 +511,18 @@ func (bx *BleXport) startOnce() error {
}
}
+ case err := <-accessl.ErrChan:
+ bx.shutdown(true, err)
+ return
+ case bm := <-accessl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleAccessEvt:
+ if err := bx.cm.Access(bx, msg); err != nil {
+ log.Debugf("Error sending access status: %s",
+ err.Error())
+ }
+ }
+
case <-bx.stopChan:
return
}
@@ -583,6 +625,10 @@ func (bx *BleXport) Tx(data []byte) error {
return bx.txNoSync(data)
}
+func (bx *BleXport) SetServices(svcs []BleSvc) error {
+ return bx.cm.SetServices(bx, svcs)
+}
+
func (bx *BleXport) AddListener(key ListenerKey) (*Listener, error) {
listener := NewListener()
if err := bx.d.AddListener(key, listener); err != nil {
diff --git a/nmxact/nmble/chrmgr.go b/nmxact/nmble/chrmgr.go
new file mode 100644
index 0000000..24eacf1
--- /dev/null
+++ b/nmxact/nmble/chrmgr.go
@@ -0,0 +1,122 @@
+package nmble
+
+import (
+ "fmt"
+
+ . "mynewt.apache.org/newtmgr/nmxact/bledefs"
+)
+
+type chrMgrElem struct {
+ AttHandle uint16
+ SvcUuid BleUuid
+ ChrUuid BleUuid
+ Cb BleGattAccessFn
+}
+
+type ChrMgr struct {
+ chrs map[uint16]chrMgrElem
+}
+
+func (cm *ChrMgr) Clear() {
+ cm.chrs = map[uint16]chrMgrElem{}
+}
+
+func (cm *ChrMgr) add(chr chrMgrElem) error {
+ if _, ok := cm.chrs[chr.AttHandle]; ok {
+ return fmt.Errorf("Characteristic with duplicate ATT handle: %d",
+ chr.AttHandle)
+ }
+
+ cm.chrs[chr.AttHandle] = chr
+ return nil
+}
+
+func (cm *ChrMgr) SetServices(x *BleXport, svcs []BleSvc) error {
+ if err := ClearSvcsXact(x); err != nil {
+ return err
+ }
+ cm.Clear()
+
+ addSvcs := make([]BleAddSvc, len(svcs))
+ for i, svc := range svcs {
+ addSvcs[i] = BleSvcToAddSvc(svc)
+ }
+
+ if err := AddSvcsXact(x, addSvcs); err != nil {
+ return err
+ }
+
+ regSvcs, err := CommitSvcsXact(x)
+ if err != nil {
+ return err
+ }
+
+ // [uuid => svc]
+ // / \
+ // [uuid => chr] [uuid => chr]
+ svcMap := map[BleUuid]map[BleUuid]BleChr{}
+
+ for _, svc := range svcs {
+ m := map[BleUuid]BleChr{}
+ svcMap[svc.Uuid] = m
+
+ for _, chr := range svc.Chrs {
+ m[chr.Uuid] = chr
+ }
+ }
+
+ for _, rs := range regSvcs {
+ srcSvc, ok := svcMap[rs.Uuid]
+ if !ok {
+ // XXX: Log
+ continue
+ }
+
+ for _, rc := range rs.Chrs {
+ srcChr, ok := srcSvc[rc.Uuid]
+ if !ok {
+ // XXX: Log
+ continue
+ }
+
+ cm.add(chrMgrElem{
+ AttHandle: rc.ValHandle,
+ SvcUuid: rs.Uuid,
+ ChrUuid: rc.Uuid,
+ Cb: srcChr.AccessCb,
+ })
+ }
+ }
+
+ return nil
+}
+
+func (cm *ChrMgr) findByAttHandle(handle uint16) *chrMgrElem {
+ chr, ok := cm.chrs[handle]
+ if !ok {
+ return nil
+ } else {
+ return &chr
+ }
+}
+
+func (cm *ChrMgr) Access(x *BleXport, evt *BleAccessEvt) error {
+ chr := cm.findByAttHandle(evt.AttHandle)
+ if chr == nil {
+ return AccessStatusXact(x, uint8(ERR_CODE_ATT_INVALID_HANDLE))
+ }
+
+ if chr.Cb == nil {
+ return AccessStatusXact(x, 0)
+ }
+
+ access := BleGattAccess{
+ Op: evt.GattOp,
+ SvcUuid: chr.SvcUuid,
+ ChrUuid: chr.ChrUuid,
+ Data: evt.Data.Bytes,
+ }
+
+ status := chr.Cb(access)
+ return AccessStatusXact(x, status)
+}
diff --git a/nmxact/nmble/dispatch.go b/nmxact/nmble/dispatch.go
index 10e6fee..1e8f767 100644
--- a/nmxact/nmble/dispatch.go
+++ b/nmxact/nmble/dispatch.go
@@ -71,6 +71,15 @@ func setPreferredMtuRspCtor() Msg { return &BleSetPreferredMtuRsp{} }
func securityInitiateRspCtor() Msg { return &BleSecurityInitiateRsp{} }
func connFindRspCtor() Msg { return &BleConnFindRsp{} }
func resetRspCtor() Msg { return &BleResetRsp{} }
+func advStartRspCtor() Msg { return &BleAdvStartRsp{} }
+func advStopRspCtor() Msg { return &BleAdvStopRsp{} }
+func advSetDataRspCtor() Msg { return &BleAdvSetDataRsp{} }
+func advRspSetDataRspCtor() Msg { return &BleAdvRspSetDataRsp{} }
+func advFieldsRspCtor() Msg { return &BleAdvFieldsRsp{} }
+func clearSvcsRspCtor() Msg { return &BleClearSvcsRsp{} }
+func addSvcsRspCtor() Msg { return &BleAddSvcsRsp{} }
+func commitSvcsRspCtor() Msg { return &BleCommitSvcsRsp{} }
+func accessStatusRspCtor() Msg { return &BleAccessStatusRsp{} }
func syncEvtCtor() Msg { return &BleSyncEvt{} }
func connectEvtCtor() Msg { return &BleConnectEvt{} }
@@ -83,6 +92,7 @@ func scanEvtCtor() Msg { return &BleScanEvt{} }
func scanTmoEvtCtor() Msg { return &BleScanTmoEvt{} }
func encChangeEvtCtor() Msg { return &BleEncChangeEvt{} }
func resetEvtCtor() Msg { return &BleResetEvt{} }
+func accessEvtCtor() Msg { return &BleAccessEvt{} }
var msgCtorMap = map[OpTypePair]msgCtor{
{MSG_OP_RSP, MSG_TYPE_ERR}: errRspCtor,
@@ -103,6 +113,15 @@ var msgCtorMap = map[OpTypePair]msgCtor{
{MSG_OP_RSP, MSG_TYPE_SECURITY_INITIATE}: securityInitiateRspCtor,
{MSG_OP_RSP, MSG_TYPE_CONN_FIND}: connFindRspCtor,
{MSG_OP_RSP, MSG_TYPE_RESET}: resetRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ADV_START}: advStartRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ADV_STOP}: advStopRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ADV_SET_DATA}: advSetDataRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ADV_RSP_SET_DATA}: advRspSetDataRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ADV_FIELDS}: advFieldsRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_CLEAR_SVCS}: clearSvcsRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ADD_SVCS}: addSvcsRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_COMMIT_SVCS}: commitSvcsRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_ACCESS_STATUS}: accessStatusRspCtor,
{MSG_OP_EVT, MSG_TYPE_SYNC_EVT}: syncEvtCtor,
{MSG_OP_EVT, MSG_TYPE_CONNECT_EVT}: connectEvtCtor,
@@ -115,6 +134,7 @@ var msgCtorMap = map[OpTypePair]msgCtor{
{MSG_OP_EVT, MSG_TYPE_SCAN_TMO_EVT}: scanTmoEvtCtor,
{MSG_OP_EVT, MSG_TYPE_ENC_CHANGE_EVT}: encChangeEvtCtor,
{MSG_OP_EVT, MSG_TYPE_RESET_EVT}: resetEvtCtor,
+ {MSG_OP_EVT, MSG_TYPE_ACCESS_EVT}: accessEvtCtor,
}
func NewDispatcher() *Dispatcher {
diff --git a/nmxact/nmserial/serial_xport.go b/nmxact/nmserial/serial_xport.go
index 1c8cb98..9d6dca9 100644
--- a/nmxact/nmserial/serial_xport.go
+++ b/nmxact/nmserial/serial_xport.go
@@ -32,6 +32,7 @@ import (
"github.com/tarm/serial"
"mynewt.apache.org/newt/util"
+ "mynewt.apache.org/newtmgr/nmxact/adv"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/scan"
"mynewt.apache.org/newtmgr/nmxact/sesn"
@@ -239,3 +240,7 @@ func (sx *SerialXport) Rx() ([]byte, error) {
}
return nil, err
}
+
+func (sx *SerialXport) BuildAdvertiser() (adv.Advertiser, error) {
+ return nil, fmt.Errorf("SerialXport#BuildAdvertiser unsupported")
+}
diff --git a/nmxact/nmxutil/nmxutil.go b/nmxact/nmxutil/nmxutil.go
index dc6b462..72bd00e 100644
--- a/nmxact/nmxutil/nmxutil.go
+++ b/nmxact/nmxutil/nmxutil.go
@@ -84,6 +84,10 @@ func NextNmpSeq() uint8 {
return val
}
+func SeqToToken(seq uint8) []byte {
+ return []byte{seq}
+}
+
func NextToken() []byte {
seqMutex.Lock()
defer seqMutex.Unlock()
@@ -93,7 +97,7 @@ func NextToken() []byte {
oicSeqBeenRead = true
}
- token := []byte{nextOicSeq}
+ token := SeqToToken(nextOicSeq)
nextOicSeq++
return token
diff --git a/nmxact/scan/scan.go b/nmxact/scan/scan.go
index aa56930..db1ade2 100644
--- a/nmxact/scan/scan.go
+++ b/nmxact/scan/scan.go
@@ -61,7 +61,6 @@ type Scanner interface {
func BleOmpScanCfg(ScanCb ScanFn) Cfg {
sc := sesn.NewSesnCfg()
sc.MgmtProto = sesn.MGMT_PROTO_OMP
- sc.Ble.IsCentral = true
sc.Ble.EncryptWhen = bledefs.BLE_ENCRYPT_PRIV_ONLY
sc.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM
diff --git a/nmxact/sesn/sesn.go b/nmxact/sesn/sesn.go
index 4716413..b13fa66 100644
--- a/nmxact/sesn/sesn.go
+++ b/nmxact/sesn/sesn.go
@@ -87,6 +87,8 @@ type Sesn interface {
// separate thread, as sesn receive operations are blocking.
AbortRx(nmpSeq uint8) error
+ // XXX AbortResource(seq uint8) error
+
////// Internal to nmxact:
EncodeNmpMsg(msg *nmp.NmpMsg) ([]byte, error)
diff --git a/nmxact/sesn/sesn_cfg.go b/nmxact/sesn/sesn_cfg.go
index da84b06..7c11d33 100644
--- a/nmxact/sesn/sesn_cfg.go
+++ b/nmxact/sesn/sesn_cfg.go
@@ -45,31 +45,14 @@ type SesnCfgBleCentral struct {
// XXX: Missing fields.
}
-type SesnCfgBlePeriph struct {
- Duration time.Duration
- ConnMode bledefs.BleAdvConnMode
- DiscMode bledefs.BleAdvDiscMode
- ItvlMin uint16
- ItvlMax uint16
- ChannelMap uint8
- FilterPolicy bledefs.BleAdvFilterPolicy
- HighDutyCycle bool
- AdvFields bledefs.BleAdvFields
- RspFields bledefs.BleAdvFields
-}
-
type SesnCfgBle struct {
// General configuration.
- IsCentral bool
OwnAddrType bledefs.BleAddrType
EncryptWhen bledefs.BleEncryptWhen
CloseTimeout time.Duration
// Central configuration.
Central SesnCfgBleCentral
-
- // Peripheral configuration.
- Periph SesnCfgBlePeriph
}
type SesnCfg struct {
@@ -88,7 +71,6 @@ func NewSesnCfg() SesnCfg {
// future, there will need to be some global default, or something that
// gets read from blehostd.
Ble: SesnCfgBle{
- IsCentral: true,
OwnAddrType: bledefs.BLE_ADDR_TYPE_RANDOM,
CloseTimeout: 30 * time.Second,
@@ -96,7 +78,6 @@ func NewSesnCfg() SesnCfg {
ConnTries: 3,
ConnTimeout: 10 * time.Second,
},
- Periph: SesnCfgBlePeriph{},
},
}
}
diff --git a/nmxact/udp/udp_xport.go b/nmxact/udp/udp_xport.go
index 2147dcc..954ab9f 100644
--- a/nmxact/udp/udp_xport.go
+++ b/nmxact/udp/udp_xport.go
@@ -22,6 +22,7 @@ package udp
import (
"fmt"
+ "mynewt.apache.org/newtmgr/nmxact/adv"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/scan"
"mynewt.apache.org/newtmgr/nmxact/sesn"
@@ -71,3 +72,7 @@ func (ux *UdpXport) Stop() error {
func (ux *UdpXport) Tx(bytes []byte) error {
return fmt.Errorf("unsupported")
}
+
+func (ux *UdpXport) BuildAdvertiser() (adv.Advertiser, error) {
+ return nil, fmt.Errorf("UdpXport#BuildAdvertiser unsupported")
+}
diff --git a/nmxact/xport/xport.go b/nmxact/xport/xport.go
index 865bfb2..0cca2a4 100644
--- a/nmxact/xport/xport.go
+++ b/nmxact/xport/xport.go
@@ -20,6 +20,7 @@
package xport
import (
+ "mynewt.apache.org/newtmgr/nmxact/adv"
"mynewt.apache.org/newtmgr/nmxact/scan"
"mynewt.apache.org/newtmgr/nmxact/sesn"
)
@@ -32,6 +33,7 @@ type Xport interface {
BuildSesn(cfg sesn.SesnCfg) (sesn.Sesn, error)
BuildScanner() (scan.Scanner, error)
+ BuildAdvertiser() (adv.Advertiser, error)
Tx(data []byte) error
}
--
To stop receiving notification emails like this one, please contact
"commits@mynewt.apache.org" <co...@mynewt.apache.org>.