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:15 UTC
[mynewt-newtmgr] 05/06: nmxact - Restructure ble code
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 9141ee90c9f45c11182040218ce9e971173f8e1a
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Wed Aug 2 16:21:22 2017 -0700
nmxact - Restructure ble code
The BLE nmxact code had become basically unreadable due to changing
requirements and an attempt to provide unrealistic and unnecessary
thread-safety guarantees. This commit simplifies the BLE code
considerably.
---
newtmgr/bll/bll_oic_sesn.go | 135 ++++--
newtmgr/bll/bll_plain_sesn.go | 4 +-
newtmgr/cli/getres.go | 7 +-
nmxact/bledefs/bledefs.go | 29 +-
nmxact/example/ble_loop/ble_loop.go | 23 +-
nmxact/example/ble_scan/ble_scan.go | 2 +-
nmxact/mgmt/transceiver.go | 202 +++++++++
nmxact/nmble/ble_act.go | 95 ++++
nmxact/nmble/ble_advertiser.go | 9 +-
nmxact/nmble/ble_coap_sesn.go | 165 -------
nmxact/nmble/ble_fsm.go | 824 -----------------------------------
nmxact/nmble/ble_oic_sesn.go | 174 --------
nmxact/nmble/ble_plain_sesn.go | 175 --------
nmxact/nmble/ble_proto.go | 155 +++++--
nmxact/nmble/ble_scanner.go | 4 +-
nmxact/nmble/ble_sesn.go | 342 +++++++++++++++
nmxact/nmble/ble_util.go | 116 ++++-
nmxact/nmble/ble_xport.go | 13 +-
nmxact/nmble/conn.go | 713 ++++++++++++++++++++++++++++++
nmxact/nmble/dispatch.go | 10 +-
nmxact/nmble/profile.go | 82 ++++
nmxact/nmble/transceiver.go | 256 -----------
nmxact/nmserial/serial_oic_sesn.go | 4 +-
nmxact/nmserial/serial_plain_sesn.go | 4 +-
nmxact/nmxutil/nmxutil.go | 7 +
nmxact/oic/oic.go | 8 -
nmxact/oic/resmgr.go | 8 +-
nmxact/omp/frag.go | 51 ---
nmxact/sesn/sesn.go | 12 +-
nmxact/sesn/sesn_cfg.go | 29 ++
nmxact/sesn/sesn_util.go | 6 +-
nmxact/udp/udp_oic_sesn.go | 4 +-
nmxact/udp/udp_plain_sesn.go | 4 +-
nmxact/xact/getres.go | 2 +-
34 files changed, 1881 insertions(+), 1793 deletions(-)
diff --git a/newtmgr/bll/bll_oic_sesn.go b/newtmgr/bll/bll_oic_sesn.go
index 28c4c53..fc042f5 100644
--- a/newtmgr/bll/bll_oic_sesn.go
+++ b/newtmgr/bll/bll_oic_sesn.go
@@ -41,12 +41,19 @@ import (
type BllOicSesn struct {
cfg BllSesnCfg
- cln ble.Client
- nmpReqChr *ble.Characteristic
- nmpRspChr *ble.Characteristic
- d *omp.Dispatcher
- mtx sync.Mutex
- attMtu int
+ cln ble.Client
+ d *omp.Dispatcher
+ mtx sync.Mutex
+ attMtu int
+
+ nmpReqChr *ble.Characteristic
+ nmpRspChr *ble.Characteristic
+ publicReqChr *ble.Characteristic
+ publicRspChr *ble.Characteristic
+ gwReqChr *ble.Characteristic
+ gwRspChr *ble.Characteristic
+ privateReqChr *ble.Characteristic
+ privateRspChr *ble.Characteristic
}
func NewBllOicSesn(cfg BllSesnCfg) *BllOicSesn {
@@ -89,43 +96,65 @@ func (bls *BllOicSesn) connect() error {
return nil
}
-func (bls *BllOicSesn) discoverAll() error {
- log.Debugf("Discovering profile")
- p, err := bls.cln.DiscoverProfile(true)
- if err != nil {
- return err
- }
+func findChr(profile *ble.Profile, svcUuid bledefs.BleUuid,
+ chrUuid bledefs.BleUuid) (*ble.Characteristic, error) {
- ompUnsecSvcUuid, _ := ParseUuid(bledefs.OmpUnsecSvcUuid)
- reqChrUuid, _ := ParseUuid(bledefs.OmpUnsecReqChrUuid)
- rspChrUuid, _ := ParseUuid(bledefs.OmpUnsecRspChrUuid)
-
- for _, s := range p.Services {
+ for _, s := range profile.Services {
uuid, err := UuidFromBllUuid(s.UUID)
if err != nil {
- return err
+ return nil, err
}
- if bledefs.CompareUuids(uuid, ompUnsecSvcUuid) == 0 {
+ if bledefs.CompareUuids(uuid, svcUuid) == 0 {
for _, c := range s.Characteristics {
uuid, err := UuidFromBllUuid(c.UUID)
if err != nil {
- return err
+ return nil, err
}
- if bledefs.CompareUuids(uuid, reqChrUuid) == 0 {
- bls.nmpReqChr = c
- }
- if bledefs.CompareUuids(uuid, rspChrUuid) == 0 {
- bls.nmpRspChr = c
+ if bledefs.CompareUuids(uuid, chrUuid) == 0 {
+ return c, nil
}
}
}
}
- if bls.nmpReqChr == nil || bls.nmpRspChr == nil {
- return fmt.Errorf(
- "Peer doesn't support a suitable service / characteristic")
+ return nil, nil
+}
+
+func (bls *BllOicSesn) discoverAll() error {
+ log.Debugf("Discovering profile")
+ p, err := bls.cln.DiscoverProfile(true)
+ if err != nil {
+ return err
+ }
+
+ ompSvcUuid, _ := bledefs.ParseUuid(bledefs.OmpUnsecSvcUuid)
+ ompReqChrUuid, _ := bledefs.ParseUuid(bledefs.OmpUnsecReqChrUuid)
+ ompRspChrUuid, _ := bledefs.ParseUuid(bledefs.OmpUnsecRspChrUuid)
+
+ gwSvcUuid, _ := bledefs.ParseUuid(bledefs.GwSvcUuid)
+ gwReqChrUuid, _ := bledefs.ParseUuid(bledefs.GwReqChrUuid)
+ gwRspChrUuid, _ := bledefs.ParseUuid(bledefs.GwRspChrUuid)
+
+ bls.nmpReqChr, err = findChr(p, ompSvcUuid, ompReqChrUuid)
+ if err != nil {
+ return err
+ }
+
+ bls.nmpRspChr, err = findChr(p, ompSvcUuid, ompRspChrUuid)
+ if err != nil {
+ return err
+ }
+
+ bls.gwReqChr, err = findChr(p, gwSvcUuid, gwReqChrUuid)
+ if err != nil {
+ return err
+ }
+
+ bls.gwRspChr, err = findChr(p, gwSvcUuid, gwRspChrUuid)
+ if err != nil {
+ return err
}
return nil
@@ -138,8 +167,20 @@ func (bls *BllOicSesn) subscribe() error {
bls.d.Dispatch(data)
}
- if err := bls.cln.Subscribe(bls.nmpRspChr, false, onNotify); err != nil {
- return err
+ if bls.nmpRspChr != nil {
+ if err := bls.cln.Subscribe(bls.nmpRspChr, false,
+ onNotify); err != nil {
+
+ return err
+ }
+ }
+
+ if bls.gwRspChr != nil {
+ if err := bls.cln.Subscribe(bls.gwRspChr, false,
+ onNotify); err != nil {
+
+ return err
+ }
}
return nil
@@ -239,6 +280,11 @@ func (bls *BllOicSesn) TxNmpOnce(msg *nmp.NmpMsg, opt sesn.TxOptions) (
"Attempt to transmit over closed BLE session")
}
+ if bls.nmpReqChr == nil || bls.nmpRspChr == nil {
+ return nil, fmt.Errorf("Cannot send NMP request; peer doesn't " +
+ "support request or response characteristic")
+ }
+
b, err := bls.EncodeNmpMsg(msg)
if err != nil {
return nil, err
@@ -272,8 +318,31 @@ func (bls *BllOicSesn) TxNmpOnce(msg *nmp.NmpMsg, opt sesn.TxOptions) (
}
}
-func (bls *BllOicSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
+func (bls *BllOicSesn) resReqChr(resType sesn.ResourceType) (
+ *ble.Characteristic, error) {
+
+ m := map[sesn.ResourceType]*ble.Characteristic{
+ sesn.RES_TYPE_PUBLIC: bls.publicReqChr,
+ sesn.RES_TYPE_UNAUTH: bls.gwReqChr,
+ sesn.RES_TYPE_SECURE: bls.privateReqChr,
+ }
+
+ chr := m[resType]
+ if chr == nil {
+ return nil, fmt.Errorf("BLE session not configured with "+
+ "characteristic for %s resources", resType)
+ }
+
+ return chr, nil
+}
+
+func (bls *BllOicSesn) GetResourceOnce(resType sesn.ResourceType, uri string,
+ opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
+
+ chr, err := bls.resReqChr(resType)
+ if err != nil {
+ return 0, nil, err
+ }
token := nmxutil.NextToken()
@@ -289,7 +358,7 @@ func (bls *BllOicSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
}
// Send request.
- if err := bls.cln.WriteCharacteristic(bls.nmpReqChr, req, true); err != nil {
+ if err := bls.cln.WriteCharacteristic(chr, req, true); err != nil {
return 0, nil, err
}
diff --git a/newtmgr/bll/bll_plain_sesn.go b/newtmgr/bll/bll_plain_sesn.go
index 0808bb4..1f6dccc 100644
--- a/newtmgr/bll/bll_plain_sesn.go
+++ b/newtmgr/bll/bll_plain_sesn.go
@@ -251,8 +251,8 @@ func (bps *BllPlainSesn) TxNmpOnce(msg *nmp.NmpMsg, opt sesn.TxOptions) (
}
}
-func (bps *BllPlainSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
+func (bps *BllPlainSesn) GetResourceOnce(resType sesn.ResourceType, uri string,
+ opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
return 0, nil,
fmt.Errorf("Resource API not supported by plain (non-OIC) session")
diff --git a/newtmgr/cli/getres.go b/newtmgr/cli/getres.go
index a70b922..049fe94 100644
--- a/newtmgr/cli/getres.go
+++ b/newtmgr/cli/getres.go
@@ -26,6 +26,7 @@ import (
"mynewt.apache.org/newt/util"
"mynewt.apache.org/newtmgr/newtmgr/nmutil"
+ "mynewt.apache.org/newtmgr/nmxact/sesn"
"mynewt.apache.org/newtmgr/nmxact/xact"
)
@@ -34,7 +35,10 @@ func getResRunCmd(cmd *cobra.Command, args []string) {
nmUsage(cmd, nil)
}
- // XXX: Parse type.
+ rt, err := sesn.ParseResType(args[0])
+ if err != nil {
+ nmUsage(cmd, err)
+ }
uri := args[1]
@@ -46,6 +50,7 @@ func getResRunCmd(cmd *cobra.Command, args []string) {
c := xact.NewGetResCmd()
c.SetTxOptions(nmutil.TxOptions())
c.Uri = uri
+ c.Typ = rt
res, err := c.Run(s)
if err != nil {
diff --git a/nmxact/bledefs/bledefs.go b/nmxact/bledefs/bledefs.go
index b5c103a..4f0b8c9 100644
--- a/nmxact/bledefs/bledefs.go
+++ b/nmxact/bledefs/bledefs.go
@@ -31,17 +31,23 @@ const BLE_ATT_ATTR_MAX_LEN = 512
const BLE_ATT_MTU_DFLT = 23
-const NmpPlainSvcUuid = "8D53DC1D-1DB7-4CD3-868B-8A527460AA84"
-const NmpPlainChrUuid = "DA2E7828-FBCE-4E01-AE9E-261174997C48"
+const CccdUuid = 0x2902
-const OmpUnsecSvcUuid = "ADE3D529-C784-4F63-A987-EB69F70EE816"
-const OmpUnsecReqChrUuid = "AD7B334F-4637-4B86-90B6-9D787F03D218"
-const OmpUnsecRspChrUuid = "E9241982-4580-42C4-8831-95048216B256"
+const NmpPlainSvcUuid = "8d53dc1d-1db7-4cd3-868b-8a527460aa84"
+const NmpPlainChrUuid = "da2e7828-fbce-4e01-ae9e-261174997c48"
+
+const OmpUnsecSvcUuid = "ade3d529-c784-4f63-a987-eb69f70ee816"
+const OmpUnsecReqChrUuid = "ad7b334f-4637-4b86-90b6-9d787f03d218"
+const OmpUnsecRspChrUuid = "e9241982-4580-42c4-8831-95048216b256"
const OmpSecSvcUuid = 0xfe18
const OmpSecReqChrUuid = 0x1000
const OmpSecRspChrUuid = 0x1001
+const GwSvcUuid = "0c08c213-98ed-4e43-a499-7e1137c39567"
+const GwReqChrUuid = "69b8a928-2ab2-487b-923e-54ce53a18bc1"
+const GwRspChrUuid = "bca10aea-5df1-4248-b72b-f52955ad9c88"
+
type BleAddrType int
const (
@@ -804,9 +810,12 @@ type BleChrId struct {
}
type BleMgmtChrs struct {
- NmpReqChr *BleChrId
- NmpRspChr *BleChrId
- ResPublicChr *BleChrId
- ResGwChr *BleChrId
- ResPrivateChr *BleChrId
+ NmpReqChr *BleChrId
+ NmpRspChr *BleChrId
+ ResPublicReqChr *BleChrId
+ ResPublicRspChr *BleChrId
+ ResGwReqChr *BleChrId
+ ResGwRspChr *BleChrId
+ ResPrivateReqChr *BleChrId
+ ResPrivateRspChr *BleChrId
}
diff --git a/nmxact/example/ble_loop/ble_loop.go b/nmxact/example/ble_loop/ble_loop.go
index 0e5bd99..0844e2d 100644
--- a/nmxact/example/ble_loop/ble_loop.go
+++ b/nmxact/example/ble_loop/ble_loop.go
@@ -28,6 +28,7 @@ import (
log "github.com/Sirupsen/logrus"
+ "mynewt.apache.org/newt/util"
"mynewt.apache.org/newtmgr/nmxact/bledefs"
"mynewt.apache.org/newtmgr/nmxact/nmble"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
@@ -53,22 +54,27 @@ func configExitHandler(x xport.Xport, s sesn.Sesn) {
s := <-sigChan
switch s {
case os.Interrupt, syscall.SIGTERM:
- onExit()
- os.Exit(0)
+ go func() {
+ onExit()
+ os.Exit(0)
+ }()
+
+ case syscall.SIGQUIT:
+ util.PrintStacks()
}
}
}()
}
func main() {
- //nmxutil.SetLogLevel(log.DebugLevel)
- nmxutil.SetLogLevel(log.InfoLevel)
+ 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.usbmodem142111"
+ params.DevPath = "/dev/cu.usbmodem142141"
x, err := nmble.NewBleXport(params)
if err != nil {
@@ -103,7 +109,7 @@ func main() {
// * Plain NMP (not tunnelled over OIC).
// * We use a random address.
sc := sesn.NewSesnCfg()
- sc.MgmtProto = sesn.MGMT_PROTO_NMP
+ sc.MgmtProto = sesn.MGMT_PROTO_OMP
sc.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM
sc.PeerSpec.Ble = *dev
@@ -127,14 +133,11 @@ func main() {
if err := s.Open(); err != nil {
fmt.Fprintf(os.Stderr, "error starting BLE session: %s\n",
err.Error())
+ time.Sleep(time.Second)
continue
}
}
- 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/example/ble_scan/ble_scan.go b/nmxact/example/ble_scan/ble_scan.go
index 3a01146..0f08a03 100644
--- a/nmxact/example/ble_scan/ble_scan.go
+++ b/nmxact/example/ble_scan/ble_scan.go
@@ -71,7 +71,7 @@ func main() {
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 {
diff --git a/nmxact/mgmt/transceiver.go b/nmxact/mgmt/transceiver.go
new file mode 100644
index 0000000..d43f48e
--- /dev/null
+++ b/nmxact/mgmt/transceiver.go
@@ -0,0 +1,202 @@
+package mgmt
+
+import (
+ "encoding/hex"
+ "fmt"
+ "sync"
+ "time"
+
+ log "github.com/Sirupsen/logrus"
+ "github.com/runtimeco/go-coap"
+
+ "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 TxFn func(req []byte) error
+
+type Transceiver struct {
+ // Only for plain NMP; nil for OMP transceivers.
+ nd *nmp.Dispatcher
+
+ // Used for OMP and CoAP resource requests.
+ od *omp.Dispatcher
+
+ wg sync.WaitGroup
+}
+
+func NewTransceiver(mgmtProto sesn.MgmtProto, logDepth int) (
+ *Transceiver, error) {
+
+ t := &Transceiver{}
+
+ if mgmtProto == sesn.MGMT_PROTO_NMP {
+ t.nd = nmp.NewDispatcher(logDepth)
+ }
+
+ od, err := omp.NewDispatcher(true, logDepth)
+ if err != nil {
+ return nil, err
+ }
+ t.od = od
+
+ return t, nil
+}
+
+func (t *Transceiver) txPlain(txCb TxFn, req *nmp.NmpMsg,
+ timeout time.Duration) (nmp.NmpRsp, error) {
+
+ nl, err := t.nd.AddListener(req.Hdr.Seq)
+ if err != nil {
+ return nil, err
+ }
+ defer t.nd.RemoveListener(req.Hdr.Seq)
+
+ b, err := nmp.EncodeNmpPlain(req)
+ if err != nil {
+ return nil, err
+ }
+
+ log.Debugf("Tx NMP request: %s", hex.Dump(b))
+
+ if err := txCb(b); err != nil {
+ return nil, err
+ }
+
+ // Now wait for NMP response.
+ for {
+ select {
+ case err := <-nl.ErrChan:
+ return nil, err
+ case rsp := <-nl.RspChan:
+ return rsp, nil
+ case <-nl.AfterTimeout(timeout):
+ return nil, nmxutil.NewRspTimeoutError("NMP timeout")
+ }
+ }
+}
+
+func (t *Transceiver) txOmp(txCb TxFn, req *nmp.NmpMsg, timeout time.Duration) (
+ nmp.NmpRsp, error) {
+
+ nl, err := t.od.AddNmpListener(req.Hdr.Seq)
+ if err != nil {
+ return nil, err
+ }
+ defer t.od.RemoveNmpListener(req.Hdr.Seq)
+
+ b, err := omp.EncodeOmpTcp(req)
+ if err != nil {
+ return nil, err
+ }
+
+ log.Debugf("Tx OMP request: %s", hex.Dump(b))
+
+ if err := txCb(b); err != nil {
+ return nil, err
+ }
+
+ // Now wait for NMP response.
+ for {
+ select {
+ case err := <-nl.ErrChan:
+ return nil, err
+ case rsp := <-nl.RspChan:
+ return rsp, nil
+ case <-nl.AfterTimeout(timeout):
+ return nil, nmxutil.NewRspTimeoutError("NMP timeout")
+ }
+ }
+}
+
+func (t *Transceiver) TxNmp(txCb TxFn, req *nmp.NmpMsg, timeout time.Duration) (
+ nmp.NmpRsp, error) {
+
+ if t.nd != nil {
+ return t.txPlain(txCb, req, timeout)
+ } else {
+ return t.txOmp(txCb, req, timeout)
+ }
+}
+
+func (t *Transceiver) TxOic(txCb TxFn, req coap.Message,
+ timeout time.Duration) (coap.Message, error) {
+
+ b, err := oic.Encode(req)
+ if err != nil {
+ return nil, err
+ }
+
+ var rspExpected bool
+ switch req.Type() {
+ case coap.Confirmable:
+ rspExpected = true
+ case coap.NonConfirmable:
+ rspExpected = req.Code() == coap.GET
+ default:
+ rspExpected = false
+ }
+
+ var ol *oic.Listener
+ if rspExpected {
+ ol, err = t.od.AddOicListener(req.Token())
+ if err != nil {
+ return nil, err
+ }
+ defer t.od.RemoveOicListener(req.Token())
+ }
+
+ log.Debugf("Tx OIC request: %s", hex.Dump(b))
+ if err := txCb(b); err != nil {
+ return nil, err
+ }
+
+ if !rspExpected {
+ return nil, nil
+ }
+
+ for {
+ select {
+ case err := <-ol.ErrChan:
+ return nil, err
+ case rsp := <-ol.RspChan:
+ return rsp, nil
+ case <-ol.AfterTimeout(timeout):
+ return nil, nmxutil.NewRspTimeoutError("OIC timeout")
+ }
+ }
+}
+
+func (t *Transceiver) DispatchNmpRsp(data []byte) {
+ if t.nd != nil {
+ t.nd.Dispatch(data)
+ } else {
+ t.od.Dispatch(data)
+ }
+}
+
+func (t *Transceiver) DispatchCoap(data []byte) {
+ t.od.Dispatch(data)
+}
+
+func (t *Transceiver) ErrorOne(seq uint8, err error) {
+ if t.nd != nil {
+ t.nd.ErrorOne(seq, err)
+ } else {
+ t.od.ErrorOneNmp(seq, err)
+ }
+}
+
+func (t *Transceiver) ErrorAll(err error) {
+ if t.nd != nil {
+ t.nd.ErrorAll(err)
+ }
+ t.od.ErrorAll(err)
+}
+
+func (t *Transceiver) AbortRx(seq uint8) {
+ t.ErrorOne(seq, fmt.Errorf("rx aborted"))
+}
diff --git a/nmxact/nmble/ble_act.go b/nmxact/nmble/ble_act.go
index 0712b47..bd97133 100644
--- a/nmxact/nmble/ble_act.go
+++ b/nmxact/nmble/ble_act.go
@@ -311,6 +311,101 @@ func discAllChrs(x *BleXport, bl *Listener, r *BleDiscAllChrsReq) (
}
// Blocking.
+func discAllDscs(x *BleXport, bl *Listener, r *BleDiscAllDscsReq) (
+ []*BleDiscDsc, error) {
+
+ const rspType = MSG_TYPE_DISC_ALL_DSCS
+ const evtType = MSG_TYPE_DISC_DSC_EVT
+
+ j, err := json.Marshal(r)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := x.Tx(j); err != nil {
+ return nil, err
+ }
+
+ dscs := []*BleDiscDsc{}
+ for {
+ select {
+ case err := <-bl.ErrChan:
+ return nil, err
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleDiscAllDscsRsp:
+ bl.Acked = true
+ if msg.Status != 0 {
+ return nil, StatusError(MSG_OP_RSP, rspType, msg.Status)
+ }
+
+ case *BleDiscDscEvt:
+ switch msg.Status {
+ case 0:
+ dscs = append(dscs, &msg.Dsc)
+ case ERR_CODE_EDONE:
+ return dscs, nil
+ default:
+ return nil, StatusError(MSG_OP_EVT, evtType, msg.Status)
+ }
+
+ default:
+ }
+
+ case <-bl.AfterTimeout(x.RspTimeout()):
+ return nil, BhdTimeoutError(rspType, r.Seq)
+ }
+ }
+}
+
+// Blocking.
+func write(x *BleXport, bl *Listener, r *BleWriteReq) error {
+ const rspType = MSG_TYPE_WRITE_CMD
+ const evtType = MSG_TYPE_WRITE_ACK_EVT
+
+ j, err := json.Marshal(r)
+ if err != nil {
+ return err
+ }
+
+ if err := x.Tx(j); err != nil {
+ return err
+ }
+
+ for {
+ select {
+ case err := <-bl.ErrChan:
+ return err
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleWriteRsp:
+ bl.Acked = true
+ if msg.Status != 0 {
+ return StatusError(MSG_OP_RSP, rspType, msg.Status)
+ } else {
+ return nil
+ }
+
+ case *BleDiscChrEvt:
+ switch msg.Status {
+ case 0:
+ return nil
+ default:
+ return StatusError(MSG_OP_EVT, evtType, msg.Status)
+ }
+
+ default:
+ }
+
+ case <-bl.AfterTimeout(x.RspTimeout()):
+ return BhdTimeoutError(rspType, r.Seq)
+ }
+ }
+}
+
+// Blocking.
func writeCmd(x *BleXport, bl *Listener, r *BleWriteCmdReq) error {
const rspType = MSG_TYPE_WRITE_CMD
diff --git a/nmxact/nmble/ble_advertiser.go b/nmxact/nmble/ble_advertiser.go
index 6888009..c6b012d 100644
--- a/nmxact/nmble/ble_advertiser.go
+++ b/nmxact/nmble/ble_advertiser.go
@@ -122,12 +122,15 @@ func (a *Advertiser) stopAdvertising() error {
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 {
+ s, err := NewBleSesn(a.bx, cfg.Ble.SesnCfg)
+ if err != nil {
return nil, err
}
+ //if err := s.OpenConnected(connHandle, bl); err != nil {
+ //return nil, err
+ //}
+
return s, nil
}
diff --git a/nmxact/nmble/ble_coap_sesn.go b/nmxact/nmble/ble_coap_sesn.go
deleted file mode 100644
index 05c6016..0000000
--- a/nmxact/nmble/ble_coap_sesn.go
+++ /dev/null
@@ -1,165 +0,0 @@
-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) (
- coap.COAPCode, []byte, error) {
-
- token := nmxutil.NextToken()
- req, err := oic.CreateGet(true, uri, token)
- if err != nil {
- return 0, nil, err
- }
-
- rsp, err := bcs.bf.TxOic(req, oic.RES_TYPE_PUBLIC, opt.Timeout)
- if err != nil {
- return 0, nil, err
- }
-
- return rsp.Code(), rsp.Payload(), nil
-}
diff --git a/nmxact/nmble/ble_fsm.go b/nmxact/nmble/ble_fsm.go
deleted file mode 100644
index 8cd5c6f..0000000
--- a/nmxact/nmble/ble_fsm.go
+++ /dev/null
@@ -1,824 +0,0 @@
-/**
- * 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"
- "sync/atomic"
- "time"
-
- log "github.com/Sirupsen/logrus"
- "github.com/runtimeco/go-coap"
-
- . "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/sesn"
-)
-
-var nextId uint32
-
-func getNextId() uint32 {
- return atomic.AddUint32(&nextId, 1) - 1
-}
-
-const DFLT_ATT_MTU = 23
-
-type BleSesnState int32
-
-const (
- SESN_STATE_UNCONNECTED BleSesnState = 0
- SESN_STATE_CONNECTING = 1
- SESN_STATE_EXCHANGE_MTU = 2
- SESN_STATE_DISCOVER_SVC = 3
- SESN_STATE_DISCOVER_CHR = 4
- SESN_STATE_SECURITY = 5
- SESN_STATE_SUBSCRIBE = 6
- SESN_STATE_GET_INFO = 7
- SESN_STATE_DONE = 8
- SESN_STATE_TERMINATING = 9
- SESN_STATE_CONN_CANCELLING = 10
-)
-
-type BleFsmDisconnectType int
-
-const (
- FSM_DISCONNECT_TYPE_UNOPENED BleFsmDisconnectType = iota
- FSM_DISCONNECT_TYPE_IMMEDIATE_TIMEOUT
- FSM_DISCONNECT_TYPE_OPENED
- FSM_DISCONNECT_TYPE_REQUESTED
-)
-
-type BleDisconnectEntry struct {
- Dt BleFsmDisconnectType
- Peer BleDev
- Err error
-}
-
-type BleFsmParamsCentral struct {
- PeerDev BleDev
- ConnTries int
- ConnTimeout time.Duration
-}
-
-type BleFsmParams struct {
- Bx *BleXport
- OwnAddrType BleAddrType
- EncryptWhen BleEncryptWhen
- Central BleFsmParamsCentral
- MgmtChrs BleMgmtChrs
- MgmtProto sesn.MgmtProto
-}
-
-type FsmChr struct {
- Uuid BleUuid
- DefHandle uint16
- ValHandle uint16
- Properties uint8
-}
-
-type FsmSvc struct {
- Uuid BleUuid
- StartHandle uint16
- EndHandle uint16
- Chrs []FsmChr
-}
-
-type BleFsm struct {
- params BleFsmParams
-
- connHandle uint16
- connDesc BleConnDesc
- peerDev BleDev
- svcs []FsmSvc
- chrs map[BleChrId]FsmChr
- prevDisconnect BleDisconnectEntry
- attMtu int
- state BleSesnState
- txvr *Transceiver
- rxer *Receiver
- errFunnel nmxutil.ErrFunnel
- id uint32
- wg sync.WaitGroup
-
- encBcast nmxutil.Bcaster
- disconnectChan chan struct{}
- rxNmpChan chan []byte
-}
-
-func NewBleFsm(p BleFsmParams) *BleFsm {
- bf := &BleFsm{
- params: p,
-
- attMtu: DFLT_ATT_MTU,
- id: getNextId(),
- }
-
- bf.chrs = map[BleChrId]FsmChr{}
-
- bf.rxer = NewReceiver(bf.id, p.Bx, 1)
-
- bf.errFunnel.AccumDelay = 250 * time.Millisecond
- bf.errFunnel.LessCb = fsmErrorLess
-
- return bf
-}
-
-func (bf *BleFsm) disconnectError(reason int) error {
- str := fmt.Sprintf("BLE peer disconnected; "+
- "reason=\"%s\" (%d) peer=%s handle=%d",
- ErrCodeToString(reason), reason, bf.peerDev.String(),
- bf.connHandle)
- return nmxutil.NewBleSesnDisconnectError(reason, str)
-}
-
-func (bf *BleFsm) closedError(msg string) error {
- return nmxutil.NewSesnClosedError(
- fmt.Sprintf("%s; state=%d", msg, bf.state))
-}
-
-func (bf *BleFsm) connInfo() (BleConnDesc, error) {
- return ConnFindXact(bf.params.Bx, bf.connHandle)
-}
-
-func fsmErrorLess(a error, b error) bool {
- aIsXport := nmxutil.IsXport(a)
- bIsXport := nmxutil.IsXport(b)
- aIsTerm := nmxutil.IsBleSesnDisconnect(a)
- bIsTerm := nmxutil.IsBleSesnDisconnect(b)
-
- if aIsXport {
- return false
- }
-
- if aIsTerm {
- return !bIsXport && !bIsTerm
- }
-
- return false
-}
-
-func calcDisconnectType(state BleSesnState) BleFsmDisconnectType {
- switch state {
- case SESN_STATE_EXCHANGE_MTU:
- return FSM_DISCONNECT_TYPE_IMMEDIATE_TIMEOUT
-
- case SESN_STATE_DONE:
- return FSM_DISCONNECT_TYPE_OPENED
-
- case SESN_STATE_TERMINATING, SESN_STATE_CONN_CANCELLING:
- return FSM_DISCONNECT_TYPE_REQUESTED
-
- default:
- return FSM_DISCONNECT_TYPE_UNOPENED
- }
-}
-
-func (bf *BleFsm) chrHandle(chrId *BleChrId, name string) (uint16, error) {
- if chrId == nil {
- return 0, fmt.Errorf("BLE session not configured with "+
- "characteristic \"%s\"", name)
- }
-
- chr, ok := bf.chrs[*chrId]
- if !ok {
- return 0, fmt.Errorf("BLE peer does not support characteristic "+
- "\"%s\" (svc=%s chr=%s)",
- name, chrId.SvcUuid.String(), chrId.ChrUuid.String())
- }
-
- return chr.ValHandle, nil
-}
-
-func (bf *BleFsm) nmpReqHandle() (uint16, error) {
- return bf.chrHandle(bf.params.MgmtChrs.NmpReqChr, "NMP request")
-}
-
-func (bf *BleFsm) nmpRspHandle() (uint16, error) {
- return bf.chrHandle(bf.params.MgmtChrs.NmpRspChr, "NMP response")
-}
-
-func (bf *BleFsm) oicResHandle(resType oic.ResType) (uint16, error) {
- switch resType {
- case oic.RES_TYPE_PUBLIC:
- return bf.chrHandle(bf.params.MgmtChrs.ResPublicChr,
- "Public resource")
- case oic.RES_TYPE_GW:
- return bf.chrHandle(bf.params.MgmtChrs.ResGwChr,
- "Gateway resource")
- case oic.RES_TYPE_PRIVATE:
- return bf.chrHandle(bf.params.MgmtChrs.ResPrivateChr,
- "Private resource")
-
- default:
- return 0, fmt.Errorf("Invalid resource type: %#v", resType)
- }
-}
-
-func (bf *BleFsm) shutdown(err error) {
- bf.params.Bx.StopWaitingForMaster(bf, err)
-
- bf.rxer.RemoveAll("shutdown")
- if bf.txvr != nil {
- bf.txvr.Stop(err)
- }
-
- bf.state = SESN_STATE_UNCONNECTED
-
- // Wait for all listeners to get removed.
- bf.rxer.WaitUntilNoListeners()
- bf.wg.Wait()
-
- close(bf.rxNmpChan)
- close(bf.disconnectChan)
-}
-
-// Listens for an error in the state machine. On error, the session is
-// considered disconnected and the error is reported to the client.
-func (bf *BleFsm) listenForError() {
- bf.wg.Add(1)
-
- go func() {
- err := <-bf.errFunnel.Wait()
-
- dt := calcDisconnectType(bf.state)
- bf.prevDisconnect = BleDisconnectEntry{dt, bf.peerDev, err}
-
- bf.wg.Done()
- bf.shutdown(err)
- }()
-}
-
-// Listens for events in the background.
-func (bf *BleFsm) eventListen(bl *Listener) error {
- bf.wg.Add(1)
-
- go func() {
- defer bf.wg.Done()
- defer bf.rxer.RemoveListener("connect", bl)
-
- for {
- select {
- case err, ok := <-bl.ErrChan:
- if ok {
- bf.errFunnel.Insert(err)
- }
- return
-
- case bm := <-bl.MsgChan:
- switch msg := bm.(type) {
- case *BleMtuChangeEvt:
- if msg.Status != 0 {
- err := StatusError(MSG_OP_EVT,
- MSG_TYPE_MTU_CHANGE_EVT,
- msg.Status)
- log.Debugf(err.Error())
- } else {
- log.Debugf("BLE ATT MTU updated; from=%d to=%d",
- bf.attMtu, msg.Mtu)
- bf.attMtu = int(msg.Mtu)
- }
-
- case *BleEncChangeEvt:
- var err error
- if msg.Status != 0 {
- err = StatusError(MSG_OP_EVT,
- MSG_TYPE_ENC_CHANGE_EVT,
- msg.Status)
- log.Debugf(err.Error())
- } else {
- log.Debugf("Connection encrypted; conn_handle=%d",
- msg.ConnHandle)
- }
-
- // Notify any listeners of the encryption change event.
- bf.encBcast.SendAndClear(err)
-
- case *BleDisconnectEvt:
- err := bf.disconnectError(msg.Reason)
- bf.errFunnel.Insert(err)
- return
-
- default:
- }
- }
- }
- }()
- return nil
-}
-
-func (bf *BleFsm) connect() error {
- bf.peerDev = bf.params.Central.PeerDev
-
- r := NewBleConnectReq()
- r.OwnAddrType = bf.params.OwnAddrType
- r.PeerAddrType = bf.peerDev.AddrType
- r.PeerAddr = bf.peerDev.Addr
-
- // Initiating a connection requires dedicated master privileges.
- if err := bf.params.Bx.AcquireMaster(bf); err != nil {
- return err
- }
- defer bf.params.Bx.ReleaseMaster()
-
- bl, err := bf.rxer.AddListener("connect", SeqKey(r.Seq))
- if err != nil {
- return err
- }
-
- // Connection operation now in progress.
- bf.state = SESN_STATE_CONNECTING
-
- // Tell blehostd to initiate connection.
- if bf.connHandle, err = connect(bf.params.Bx, bl, r,
- bf.params.Central.ConnTimeout); err != nil {
-
- bhe := nmxutil.ToBleHost(err)
- if bhe != nil && bhe.Status == ERR_CODE_EDONE {
- // Already connected.
- bf.rxer.RemoveListener("connect", bl)
- return nil
- } else if !nmxutil.IsXport(err) {
- // The transport did not restart; always attempt to cancel the
- // connect operation. In most cases, the host has already stopped
- // connecting and will respond with an "ealready" error that can be
- // ignored.
- if err := bf.connCancel(); err != nil {
- log.Errorf("Failed to cancel connect in progress: %s",
- err.Error())
- }
- }
-
- bf.rxer.RemoveListener("connect", bl)
- return err
- }
-
- // Listen for events in the background.
- if err := bf.eventListen(bl); err != nil {
- return err
- }
-
- return nil
-}
-
-func (bf *BleFsm) terminateSetState() error {
- switch bf.state {
- case SESN_STATE_UNCONNECTED,
- SESN_STATE_CONNECTING,
- SESN_STATE_CONN_CANCELLING:
- return fmt.Errorf("BLE terminate failed; not connected")
- case SESN_STATE_TERMINATING:
- return fmt.Errorf(
- "BLE terminate failed; session already being closed")
- default:
- bf.state = SESN_STATE_TERMINATING
- }
-
- return nil
-}
-
-func (bf *BleFsm) terminate() error {
- if err := bf.terminateSetState(); err != nil {
- return err
- }
-
- r := NewBleTerminateReq()
- r.ConnHandle = bf.connHandle
- r.HciReason = ERR_CODE_HCI_REM_USER_CONN_TERM
-
- bl, err := bf.rxer.AddListener("terminate", SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer bf.rxer.RemoveListener("terminate", bl)
-
- if err := terminate(bf.params.Bx, bl, r); err != nil {
- return err
- }
-
- return nil
-}
-
-func (bf *BleFsm) connCancel() error {
- r := NewBleConnCancelReq()
-
- bl, err := bf.rxer.AddListener("conn-cancel", SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer bf.rxer.RemoveListener("conn-cancel", bl)
-
- if err := connCancel(bf.params.Bx, bl, r); err != nil {
- return err
- }
-
- return nil
-}
-
-func (bf *BleFsm) discAllSvcs() error {
- r := NewBleDiscAllSvcsReq()
- r.ConnHandle = bf.connHandle
-
- bl, err := bf.rxer.AddListener("disc-all-svcs", SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer bf.rxer.RemoveListener("disc-all-svcs", bl)
-
- svcs, err := discAllSvcs(bf.params.Bx, bl, r)
- if err != nil {
- return err
- }
-
- bf.svcs = nil
- for _, s := range svcs {
- bf.svcs = append(bf.svcs, FsmSvc{
- Uuid: s.Uuid,
- StartHandle: uint16(s.StartHandle),
- EndHandle: uint16(s.EndHandle),
- })
- }
-
- return nil
-}
-
-func (bf *BleFsm) encInitiate() error {
- r := NewBleSecurityInitiateReq()
- r.ConnHandle = bf.connHandle
-
- bl, err := bf.rxer.AddListener("enc-initiate", SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer bf.rxer.RemoveListener("enc-initiate", bl)
-
- // Initiate the encryption procedure.
- if err := encInitiate(bf.params.Bx, bl, r); err != nil {
- return err
- }
-
- // Block until the procedure completes.
- itf := <-bf.encBcast.Listen()
- return itf.(error)
-}
-
-func (bf *BleFsm) discAllChrsOnce(svc *FsmSvc) error {
- r := NewBleDiscAllChrsReq()
- r.ConnHandle = bf.connHandle
- r.StartHandle = int(svc.StartHandle)
- r.EndHandle = int(svc.EndHandle)
-
- bl, err := bf.rxer.AddListener("disc-all-chrs", SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer bf.rxer.RemoveListener("disc-all-chrs", bl)
-
- chrs, err := discAllChrs(bf.params.Bx, bl, r)
- if err != nil {
- return err
- }
-
- for _, c := range chrs {
- fc := FsmChr{
- Uuid: c.Uuid,
- DefHandle: uint16(c.DefHandle),
- ValHandle: uint16(c.ValHandle),
- Properties: uint8(c.Properties),
- }
- svc.Chrs = append(svc.Chrs, fc)
- bf.chrs[BleChrId{svc.Uuid, c.Uuid}] = fc
- }
-
- return nil
-}
-
-func (bf *BleFsm) discAllChrs() error {
- for _, s := range bf.svcs {
- if err := bf.discAllChrsOnce(&s); err != nil {
- return err
- }
- }
-
- // Listen for NMP responses in the background.
- attHandle, err := bf.nmpRspHandle()
- if err != nil {
- return err
- }
-
- txvr, err := NewTransceiver(bf.id, bf.connHandle, bf.params.MgmtProto,
- bf.params.Bx, bf.rxer, 1)
- if err != nil {
- return err
- }
- bf.txvr = txvr
-
- errChan, err := bf.txvr.NmpRspListen(attHandle)
- if err != nil {
- return err
- }
-
- bf.wg.Add(1)
- go func() {
- defer bf.wg.Done()
-
- if err := <-errChan; err != nil {
- bf.errFunnel.Insert(err)
- }
- }()
-
- return nil
-}
-
-func (bf *BleFsm) exchangeMtu() error {
- r := NewBleExchangeMtuReq()
- r.ConnHandle = bf.connHandle
-
- bl, err := bf.rxer.AddListener("exchange-mtu", SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer bf.rxer.RemoveListener("exchange-mtu", bl)
-
- 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 ||
- bhe.Status == ERR_CODE_ATT_BASE+ERR_CODE_ATT_REQ_NOT_SUPPORTED) {
-
- return nil
- }
- return err
- }
-
- bf.attMtu = mtu
- return nil
-}
-
-func (bf *BleFsm) subscribe() error {
- rspHandle, err := bf.nmpRspHandle()
- if err != nil {
- return err
- }
-
- return bf.txvr.WriteCmd(rspHandle+1, []byte{1, 0}, "subscribe")
-}
-
-func (bf *BleFsm) shouldEncrypt() bool {
- switch bf.params.EncryptWhen {
- case BLE_ENCRYPT_NEVER:
- return false
-
- case BLE_ENCRYPT_ALWAYS:
- return true
-
- case BLE_ENCRYPT_PRIV_ONLY:
- return bf.peerDev.AddrType == BLE_ADDR_TYPE_RPA_PUB ||
- bf.peerDev.AddrType == BLE_ADDR_TYPE_RPA_RND
-
- default:
- log.Errorf("Invalid BleEncryptWhen value: %d",
- bf.params.EncryptWhen)
- return false
- }
-}
-
-func (bf *BleFsm) executeState() (bool, error) {
- switch bf.state {
- case SESN_STATE_UNCONNECTED:
- if err := bf.connect(); err != nil {
- return false, err
- }
- bf.state = SESN_STATE_EXCHANGE_MTU
-
- case SESN_STATE_EXCHANGE_MTU:
- if err := bf.exchangeMtu(); err != nil {
- bhe := nmxutil.ToBleHost(err)
- retry := bhe != nil && bhe.Status == ERR_CODE_ENOTCONN
- return retry, err
- }
- bf.state = SESN_STATE_DISCOVER_SVC
-
- case SESN_STATE_DISCOVER_SVC:
- if err := bf.discAllSvcs(); 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
- }
- bf.state = SESN_STATE_SECURITY
-
- case SESN_STATE_SECURITY:
- 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 {
- 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:
- /* Open complete. */
- return false, fmt.Errorf("BleFsm already done being opened")
-
- default:
- return false, fmt.Errorf("BleFsm already being opened")
- }
-
- return false, nil
-}
-
-func (bf *BleFsm) DisconnectChan() <-chan struct{} {
- return bf.disconnectChan
-}
-
-func (bf *BleFsm) RxNmpChan() <-chan []byte {
- return bf.rxNmpChan
-}
-
-func (bf *BleFsm) PrevDisconnect() BleDisconnectEntry {
- return bf.prevDisconnect
-}
-
-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)
- bf.txvr = nil
-
- bf.listenForError()
-
- for {
- retry, err := bf.executeState()
- if err != nil {
- // If stop fails, assume the connection wasn't established and
- // force an error.
- if bf.Stop() != nil {
- bf.errFunnel.Insert(err)
- }
- <-bf.disconnectChan
- return retry, err
- } else if bf.state == SESN_STATE_DONE {
- // We are fully connected. Listen for errors in the background.
- return false, nil
- }
- }
-}
-
-// @return bool Whether another start attempt should be made;
-// error The error that caused the start attempt to
-// fail; nil on success.
-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()
- if !retry {
- break
- }
- }
-
- if err != nil {
- nmxutil.Assert(!bf.IsOpen())
- nmxutil.Assert(bf.IsClosed())
- return err
- }
-
- 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_GET_INFO
-
- 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 {
- state := bf.state
-
- switch state {
- case SESN_STATE_UNCONNECTED,
- SESN_STATE_TERMINATING,
- SESN_STATE_CONN_CANCELLING:
-
- return bf.closedError("Attempt to close an unopened BLE session")
-
- case SESN_STATE_CONNECTING:
- bf.connCancel()
- bf.errFunnel.Insert(fmt.Errorf("Connection attempt cancelled"))
- return nil
-
- default:
- if err := bf.terminate(); err != nil {
- return err
- }
- return nil
- }
-}
-
-func (bf *BleFsm) IsOpen() bool {
- return bf.state == SESN_STATE_DONE
-}
-
-func (bf *BleFsm) IsClosed() bool {
- return bf.state == SESN_STATE_UNCONNECTED
-}
-
-func (bf *BleFsm) TxNmp(req *nmp.NmpMsg, timeout time.Duration) (
- nmp.NmpRsp, error) {
-
- if !bf.IsOpen() {
- return nil, bf.closedError(
- "Attempt to transmit over closed BLE session")
- }
-
- attHandle, err := bf.nmpReqHandle()
- if err != nil {
- return nil, err
- }
-
- return bf.txvr.TxNmp(attHandle, req, timeout)
-}
-
-func (bf *BleFsm) TxOic(req coap.Message, resType oic.ResType,
- timeout time.Duration) (coap.Message, error) {
-
- if !bf.IsOpen() {
- return nil, bf.closedError(
- "Attempt to transmit over closed BLE session")
- }
-
- attHandle, err := bf.oicResHandle(resType)
- if err != nil {
- return nil, err
- }
-
- return bf.txvr.TxOic(attHandle, req, timeout)
-}
-
-func (bf *BleFsm) AbortNmpRx(seq uint8) error {
- return bf.txvr.AbortNmpRx(seq)
-}
diff --git a/nmxact/nmble/ble_oic_sesn.go b/nmxact/nmble/ble_oic_sesn.go
deleted file mode 100644
index dc46be4..0000000
--- a/nmxact/nmble/ble_oic_sesn.go
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * 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 (
- "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 BleOicSesn struct {
- bf *BleFsm
- closeTimeout time.Duration
- onCloseCb sesn.OnCloseFn
- wg sync.WaitGroup
- closeBlocker nmxutil.Blocker
-}
-
-func NewBleOicSesn(bx *BleXport, cfg sesn.SesnCfg) *BleOicSesn {
- bos := &BleOicSesn{
- closeTimeout: cfg.Ble.CloseTimeout,
- onCloseCb: cfg.OnCloseCb,
- }
-
- iotUuid, _ := ParseUuid(OmpUnsecSvcUuid)
- reqChrUuid, _ := ParseUuid(OmpUnsecReqChrUuid)
- rspChrUuid, _ := ParseUuid(OmpUnsecRspChrUuid)
-
- bos.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,
- },
- MgmtChrs: BleMgmtChrs{
- NmpReqChr: &BleChrId{iotUuid, reqChrUuid},
- NmpRspChr: &BleChrId{iotUuid, rspChrUuid},
- },
- MgmtProto: sesn.MGMT_PROTO_OMP,
- EncryptWhen: cfg.Ble.EncryptWhen,
- })
-
- return bos
-}
-
-func (bos *BleOicSesn) AbortRx(seq uint8) error {
- return bos.bf.AbortNmpRx(seq)
-}
-
-func (bos *BleOicSesn) Open() error {
- // Ensure subsequent calls to Close() block.
- bos.closeBlocker.Block()
-
- if err := bos.bf.Start(); err != nil {
- if !nmxutil.IsSesnAlreadyOpen(err) {
- bos.closeBlocker.Unblock()
- }
- return err
- }
-
- // Listen for disconnect in the background.
- bos.wg.Add(1)
- go func() {
- // If the session is being closed, unblock the close() call.
- defer bos.closeBlocker.Unblock()
-
- // Block until disconnect.
- <-bos.bf.DisconnectChan()
- nmxutil.Assert(!bos.IsOpen())
- pd := bos.bf.PrevDisconnect()
-
- // Signal error to all listeners.
- bos.wg.Done()
- bos.wg.Wait()
-
- // Only execute the client's disconnect callback if the disconnect was
- // unsolicited.
- if pd.Dt != FSM_DISCONNECT_TYPE_REQUESTED && bos.onCloseCb != nil {
- bos.onCloseCb(bos, pd.Err)
- }
- }()
-
- return nil
-}
-
-func (bos *BleOicSesn) Close() error {
- err := bos.bf.Stop()
- if err != nil {
- return err
- }
-
- // Block until close completes.
- bos.closeBlocker.Wait()
- return nil
-}
-
-func (bos *BleOicSesn) IsOpen() bool {
- return bos.bf.IsOpen()
-}
-
-func (bos *BleOicSesn) EncodeNmpMsg(m *nmp.NmpMsg) ([]byte, error) {
- return omp.EncodeOmpTcp(m)
-}
-
-// Blocking.
-func (bos *BleOicSesn) TxNmpOnce(req *nmp.NmpMsg, opt sesn.TxOptions) (
- nmp.NmpRsp, error) {
-
- return bos.bf.TxNmp(req, opt.Timeout)
-}
-
-func (bos *BleOicSesn) MtuIn() int {
- return bos.bf.attMtu -
- NOTIFY_CMD_BASE_SZ -
- omp.OMP_MSG_OVERHEAD -
- nmp.NMP_HDR_SIZE
-}
-
-func (bos *BleOicSesn) MtuOut() int {
- mtu := bos.bf.attMtu -
- WRITE_CMD_BASE_SZ -
- omp.OMP_MSG_OVERHEAD -
- nmp.NMP_HDR_SIZE
- return util.IntMin(mtu, BLE_ATT_ATTR_MAX_LEN)
-}
-
-func (bos *BleOicSesn) ConnInfo() (BleConnDesc, error) {
- return bos.bf.connInfo()
-}
-
-func (bos *BleOicSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
-
- token := nmxutil.NextToken()
- req, err := oic.CreateGet(true, uri, token)
- if err != nil {
- return 0, nil, err
- }
-
- rsp, err := bos.bf.TxOic(req, oic.RES_TYPE_PUBLIC, opt.Timeout)
- if err != nil {
- return 0, nil, err
- }
-
- return rsp.Code(), rsp.Payload(), nil
-}
diff --git a/nmxact/nmble/ble_plain_sesn.go b/nmxact/nmble/ble_plain_sesn.go
deleted file mode 100644
index 3802d7f..0000000
--- a/nmxact/nmble/ble_plain_sesn.go
+++ /dev/null
@@ -1,175 +0,0 @@
-/**
- * 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"
-
- "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/sesn"
-)
-
-type BlePlainSesn struct {
- bf *BleFsm
- d *nmp.Dispatcher
- closeTimeout time.Duration
- onCloseCb sesn.OnCloseFn
- wg sync.WaitGroup
- closeBlocker nmxutil.Blocker
-}
-
-func NewBlePlainSesn(bx *BleXport, cfg sesn.SesnCfg) *BlePlainSesn {
- bps := &BlePlainSesn{
- closeTimeout: cfg.Ble.CloseTimeout,
- onCloseCb: cfg.OnCloseCb,
- }
-
- svcUuid, _ := ParseUuid(NmpPlainSvcUuid)
- chrUuid, _ := ParseUuid(NmpPlainChrUuid)
-
- bps.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,
- },
- MgmtChrs: BleMgmtChrs{
- NmpReqChr: &BleChrId{svcUuid, chrUuid},
- NmpRspChr: &BleChrId{svcUuid, chrUuid},
- },
- EncryptWhen: cfg.Ble.EncryptWhen,
- })
-
- return bps
-}
-
-func (bps *BlePlainSesn) AbortRx(seq uint8) error {
- return bps.d.ErrorOne(seq, fmt.Errorf("Rx aborted"))
-}
-
-func (bps *BlePlainSesn) Open() error {
- // Ensure subsequent calls to Close() block.
- bps.closeBlocker.Block()
-
- if err := bps.bf.Start(); err != nil {
- if !nmxutil.IsSesnAlreadyOpen(err) {
- bps.closeBlocker.Unblock()
- }
- return err
- }
-
- bps.d = nmp.NewDispatcher(3)
-
- // Listen for disconnect in the background.
- bps.wg.Add(1)
- go func() {
- // If the session is being closed, unblock the close() call.
- defer bps.closeBlocker.Unblock()
-
- // Block until disconnect.
- <-bps.bf.DisconnectChan()
- nmxutil.Assert(!bps.IsOpen())
-
- pd := bps.bf.PrevDisconnect()
-
- // Signal error to all listeners.
- bps.d.ErrorAll(pd.Err)
- bps.wg.Done()
- bps.wg.Wait()
-
- // Only execute the client's disconnect callback if the disconnect was
- // unsolicited.
- if pd.Dt != FSM_DISCONNECT_TYPE_REQUESTED && bps.onCloseCb != nil {
- bps.onCloseCb(bps, pd.Err)
- }
- }()
-
- // Listen for NMP responses in the background.
- bps.wg.Add(1)
- go func() {
- defer bps.wg.Done()
-
- for {
- data, ok := <-bps.bf.RxNmpChan()
- if !ok {
- // Disconnected.
- return
- } else {
- bps.d.Dispatch(data)
- }
- }
- }()
-
- return nil
-}
-
-func (bps *BlePlainSesn) Close() error {
- err := bps.bf.Stop()
- if err != nil {
- return err
- }
-
- // Block until close completes.
- bps.closeBlocker.Wait()
- return nil
-}
-
-func (bps *BlePlainSesn) IsOpen() bool {
- return bps.bf.IsOpen()
-}
-
-func (bps *BlePlainSesn) EncodeNmpMsg(m *nmp.NmpMsg) ([]byte, error) {
- return nmp.EncodeNmpPlain(m)
-}
-
-// Blocking.
-func (bps *BlePlainSesn) TxNmpOnce(req *nmp.NmpMsg, opt sesn.TxOptions) (
- nmp.NmpRsp, error) {
-
- return bps.bf.TxNmp(req, opt.Timeout)
-}
-
-func (bps *BlePlainSesn) MtuIn() int {
- return bps.bf.attMtu - NOTIFY_CMD_BASE_SZ - nmp.NMP_HDR_SIZE
-}
-
-func (bps *BlePlainSesn) MtuOut() int {
- mtu := bps.bf.attMtu - WRITE_CMD_BASE_SZ - nmp.NMP_HDR_SIZE
- return util.IntMin(mtu, BLE_ATT_ATTR_MAX_LEN)
-}
-
-func (bps *BlePlainSesn) ConnInfo() (BleConnDesc, error) {
- return bps.bf.connInfo()
-}
-
-func (bps *BlePlainSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
-
- return 0, nil, fmt.Errorf("BlePlainSesn.GetResourceOnce() unsupported")
-}
diff --git a/nmxact/nmble/ble_proto.go b/nmxact/nmble/ble_proto.go
index 82b73f5..61c086a 100644
--- a/nmxact/nmble/ble_proto.go
+++ b/nmxact/nmble/ble_proto.go
@@ -38,6 +38,8 @@ type BleBytes struct {
Bytes []byte
}
+const BLE_CONN_HANDLE_NONE uint16 = 0xffff
+
const BLE_SEQ_MIN BleSeq = 0
const BLE_SEQ_EVT_MIN BleSeq = 0xffffff00
const BLE_SEQ_NONE BleSeq = 0xffffffff
@@ -285,43 +287,45 @@ const (
MSG_TYPE_DISC_SVC_UUID = 6
MSG_TYPE_DISC_ALL_CHRS = 7
MSG_TYPE_DISC_CHR_UUID = 8
- MSG_TYPE_WRITE = 9
- MSG_TYPE_WRITE_CMD = 10
- MSG_TYPE_EXCHANGE_MTU = 11
- MSG_TYPE_GEN_RAND_ADDR = 12
- MSG_TYPE_SET_RAND_ADDR = 13
- MSG_TYPE_CONN_CANCEL = 14
- MSG_TYPE_SCAN = 15
- MSG_TYPE_SCAN_CANCEL = 16
- MSG_TYPE_SET_PREFERRED_MTU = 17
- MSG_TYPE_SECURITY_INITIATE = 18
- MSG_TYPE_CONN_FIND = 19
- MSG_TYPE_RESET = 20
- MSG_TYPE_ADV_START = 21
- MSG_TYPE_ADV_STOP = 22
- 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_NOTIFY = 30
- MSG_TYPE_FIND_CHR = 31
+ MSG_TYPE_DISC_ALL_DSCS = 9
+ MSG_TYPE_WRITE = 10
+ MSG_TYPE_WRITE_CMD = 11
+ MSG_TYPE_EXCHANGE_MTU = 12
+ MSG_TYPE_GEN_RAND_ADDR = 13
+ MSG_TYPE_SET_RAND_ADDR = 14
+ MSG_TYPE_CONN_CANCEL = 15
+ MSG_TYPE_SCAN = 16
+ MSG_TYPE_SCAN_CANCEL = 17
+ MSG_TYPE_SET_PREFERRED_MTU = 18
+ MSG_TYPE_SECURITY_INITIATE = 19
+ MSG_TYPE_CONN_FIND = 20
+ MSG_TYPE_RESET = 21
+ MSG_TYPE_ADV_START = 22
+ MSG_TYPE_ADV_STOP = 23
+ MSG_TYPE_ADV_SET_DATA = 24
+ MSG_TYPE_ADV_RSP_SET_DATA = 25
+ MSG_TYPE_ADV_FIELDS = 26
+ MSG_TYPE_CLEAR_SVCS = 27
+ MSG_TYPE_ADD_SVCS = 28
+ MSG_TYPE_COMMIT_SVCS = 29
+ MSG_TYPE_ACCESS_STATUS = 30
+ MSG_TYPE_NOTIFY = 31
+ MSG_TYPE_FIND_CHR = 32
MSG_TYPE_SYNC_EVT = 2049
MSG_TYPE_CONNECT_EVT = 2050
MSG_TYPE_DISCONNECT_EVT = 2051
MSG_TYPE_DISC_SVC_EVT = 2052
MSG_TYPE_DISC_CHR_EVT = 2053
- MSG_TYPE_WRITE_ACK_EVT = 2054
- MSG_TYPE_NOTIFY_RX_EVT = 2055
- MSG_TYPE_MTU_CHANGE_EVT = 2056
- MSG_TYPE_SCAN_EVT = 2057
- MSG_TYPE_SCAN_TMO_EVT = 2058
- MSG_TYPE_ENC_CHANGE_EVT = 2059
- MSG_TYPE_RESET_EVT = 2060
- MSG_TYPE_ACCESS_EVT = 2061
+ MSG_TYPE_DISC_DSC_EVT = 2054
+ MSG_TYPE_WRITE_ACK_EVT = 2055
+ MSG_TYPE_NOTIFY_RX_EVT = 2056
+ MSG_TYPE_MTU_CHANGE_EVT = 2057
+ MSG_TYPE_SCAN_EVT = 2058
+ MSG_TYPE_SCAN_TMO_EVT = 2059
+ MSG_TYPE_ENC_CHANGE_EVT = 2060
+ MSG_TYPE_RESET_EVT = 2061
+ MSG_TYPE_ACCESS_EVT = 2062
)
var MsgOpStringMap = map[MsgOp]string{
@@ -337,8 +341,10 @@ var MsgTypeStringMap = map[MsgType]string{
MSG_TYPE_TERMINATE: "terminate",
MSG_TYPE_DISC_ALL_SVCS: "disc_all_svcs",
MSG_TYPE_DISC_SVC_UUID: "disc_svc_uuid",
- MSG_TYPE_DISC_CHR_UUID: "disc_chr_uuid",
MSG_TYPE_DISC_ALL_CHRS: "disc_all_chrs",
+ MSG_TYPE_DISC_CHR_UUID: "disc_chr_uuid",
+ MSG_TYPE_DISC_ALL_DSCS: "disc_all_dscs",
+ MSG_TYPE_WRITE: "write",
MSG_TYPE_WRITE_CMD: "write_cmd",
MSG_TYPE_EXCHANGE_MTU: "exchange_mtu",
MSG_TYPE_GEN_RAND_ADDR: "gen_rand_addr",
@@ -367,6 +373,8 @@ var MsgTypeStringMap = map[MsgType]string{
MSG_TYPE_DISCONNECT_EVT: "disconnect_evt",
MSG_TYPE_DISC_SVC_EVT: "disc_svc_evt",
MSG_TYPE_DISC_CHR_EVT: "disc_chr_evt",
+ MSG_TYPE_DISC_DSC_EVT: "disc_dsc_evt",
+ MSG_TYPE_WRITE_ACK_EVT: "write_ack_evt",
MSG_TYPE_NOTIFY_RX_EVT: "notify_rx_evt",
MSG_TYPE_MTU_CHANGE_EVT: "mtu_change_evt",
MSG_TYPE_SCAN_EVT: "scan_evt",
@@ -397,6 +405,11 @@ type BleDiscChr struct {
Uuid BleUuid `json:"uuid"`
}
+type BleDiscDsc struct {
+ Handle uint16 `json:"handle"`
+ Uuid BleUuid `json:"uuid"`
+}
+
type BleSyncReq struct {
// Header
Op MsgOp `json:"op"`
@@ -548,6 +561,28 @@ type BleDiscSvcEvt struct {
Svc BleDiscSvc `json:"service"`
}
+type BleDiscAllChrsReq struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ ConnHandle uint16 `json:"conn_handle"`
+ StartHandle int `json:"start_handle"`
+ EndHandle int `json:"end_handle"`
+}
+
+type BleDiscAllChrsRsp struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+}
+
type BleDiscChrUuidReq struct {
// Header
Op MsgOp `json:"op"`
@@ -561,7 +596,28 @@ type BleDiscChrUuidReq struct {
Uuid BleUuid `json:"chr_uuid"`
}
-type BleDiscAllChrsReq struct {
+type BleDiscChrUuidRsp struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+}
+
+type BleDiscChrEvt struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+ Chr BleDiscChr `json:"characteristic"`
+}
+
+type BleDiscAllDscsReq struct {
// Header
Op MsgOp `json:"op"`
Type MsgType `json:"type"`
@@ -573,7 +629,7 @@ type BleDiscAllChrsReq struct {
EndHandle int `json:"end_handle"`
}
-type BleDiscAllChrsRsp struct {
+type BleDiscAllDscsRsp struct {
// Header
Op MsgOp `json:"op"`
Type MsgType `json:"type"`
@@ -583,6 +639,18 @@ type BleDiscAllChrsRsp struct {
Status int `json:"status"`
}
+type BleDiscDscEvt struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ Status int `json:"status"`
+ ChrDefHandle uint16 `json:"chr_def_handle"`
+ Dsc BleDiscDsc `json:"descriptor"`
+}
+
type BleErrRsp struct {
// Header
Op MsgOp `json:"op"`
@@ -604,7 +672,19 @@ type BleSyncRsp struct {
Synced bool `json:"synced"`
}
-type BleDiscChrUuidRsp struct {
+type BleWriteReq struct {
+ // Header
+ Op MsgOp `json:"op"`
+ Type MsgType `json:"type"`
+ Seq BleSeq `json:"seq"`
+
+ // Mandatory
+ ConnHandle uint16 `json:"conn_handle"`
+ AttrHandle int `json:"attr_handle"`
+ Data BleBytes `json:"data"`
+}
+
+type BleWriteRsp struct {
// Header
Op MsgOp `json:"op"`
Type MsgType `json:"type"`
@@ -614,15 +694,14 @@ type BleDiscChrUuidRsp struct {
Status int `json:"status"`
}
-type BleDiscChrEvt struct {
+type BleWriteAckEvt struct {
// Header
Op MsgOp `json:"op"`
Type MsgType `json:"type"`
Seq BleSeq `json:"seq"`
// Mandatory
- Status int `json:"status"`
- Chr BleDiscChr `json:"characteristic"`
+ Status int `json:"status"`
}
type BleWriteCmdReq struct {
diff --git a/nmxact/nmble/ble_scanner.go b/nmxact/nmble/ble_scanner.go
index 5e44d39..b93a5e9 100644
--- a/nmxact/nmble/ble_scanner.go
+++ b/nmxact/nmble/ble_scanner.go
@@ -40,7 +40,7 @@ type BleScanner struct {
bx *BleXport
discoverer *Discoverer
reportedDevs map[BleDev]string
- bos *BleOicSesn
+ bos *BleSesn
enabled bool
// Protects accesses to the reported devices map.
@@ -96,7 +96,7 @@ func (s *BleScanner) connect(dev BleDev) error {
}
s.mtx.Lock()
- s.bos = session.(*BleOicSesn)
+ s.bos = session.(*BleSesn)
s.mtx.Unlock()
if err := s.bos.Open(); err != nil {
diff --git a/nmxact/nmble/ble_sesn.go b/nmxact/nmble/ble_sesn.go
new file mode 100644
index 0000000..0918e58
--- /dev/null
+++ b/nmxact/nmble/ble_sesn.go
@@ -0,0 +1,342 @@
+/**
+ * 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"
+
+ "github.com/runtimeco/go-coap"
+
+ "mynewt.apache.org/newt/util"
+ . "mynewt.apache.org/newtmgr/nmxact/bledefs"
+ "mynewt.apache.org/newtmgr/nmxact/mgmt"
+ "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"
+)
+
+var dummyNotifyListener = NewNotifyListener()
+
+type BleSesn struct {
+ cfg sesn.SesnCfg
+ bx *BleXport
+ conn *Conn
+ mgmtChrs BleMgmtChrs
+ txvr *mgmt.Transceiver
+
+ wg sync.WaitGroup
+ closeBlocker nmxutil.Blocker
+ stopChan chan struct{}
+}
+
+func (s *BleSesn) init() error {
+ s.conn = NewConn(s.bx)
+
+ s.stopChan = make(chan struct{})
+
+ txvr, err := mgmt.NewTransceiver(s.cfg.MgmtProto, 3)
+ if err != nil {
+ return err
+ }
+ s.txvr = txvr
+ return nil
+}
+
+func NewBleSesn(bx *BleXport, cfg sesn.SesnCfg) (*BleSesn, error) {
+ mgmtChrs, err := BuildMgmtChrs(cfg.MgmtProto)
+ if err != nil {
+ return nil, err
+ }
+
+ s := &BleSesn{
+ cfg: cfg,
+ bx: bx,
+ mgmtChrs: mgmtChrs,
+ }
+
+ if err := s.init(); err != nil {
+ return nil, err
+ }
+
+ return s, nil
+}
+
+func (s *BleSesn) AbortRx(seq uint8) error {
+ s.txvr.AbortRx(seq)
+ return nil
+}
+
+func (s *BleSesn) disconnectListen() {
+ // Listen for disconnect in the background.
+ s.wg.Add(1)
+ go func() {
+ // If the session is being closed, unblock the close() call.
+ defer s.closeBlocker.Unblock()
+
+ // Block until disconnect.
+ err := <-s.conn.DisconnectChan()
+ nmxutil.Assert(!s.IsOpen())
+
+ // Signal error to all listeners.
+ close(s.stopChan)
+ s.wg.Done()
+ s.wg.Wait()
+
+ if s.cfg.OnCloseCb != nil {
+ s.cfg.OnCloseCb(s, err)
+ }
+ }()
+}
+
+func (s *BleSesn) getChr(chrId *BleChrId) (*Characteristic, error) {
+ if chrId == nil {
+ return nil, fmt.Errorf("BLE session not configured with required "+
+ "characteristic: %s", *chrId)
+ }
+
+ chr := s.conn.Profile().FindChrByUuid(*chrId)
+ if chr == nil {
+ return nil, fmt.Errorf("BLE peer doesn't support required "+
+ "characteristic: %s", *chrId)
+ }
+
+ return chr, nil
+}
+
+func (s *BleSesn) createNotifyListener(chrId *BleChrId) *NotifyListener {
+ chr, _ := s.getChr(chrId)
+ if chr == nil {
+ return dummyNotifyListener
+ }
+
+ nl := s.conn.ListenForNotifications(chr)
+ if nl == nil {
+ return dummyNotifyListener
+ }
+
+ return nl
+}
+
+func (s *BleSesn) notifyListen() {
+ nmpRspNl := s.createNotifyListener(s.mgmtChrs.NmpRspChr)
+
+ s.wg.Add(1)
+ go func() {
+ defer s.wg.Done()
+
+ for {
+ select {
+ case err := <-nmpRspNl.ErrChan:
+ s.txvr.ErrorAll(err)
+ return
+
+ case n, ok := <-nmpRspNl.NotifyChan:
+ if ok {
+ s.txvr.DispatchNmpRsp(n.Data)
+ }
+
+ case <-s.stopChan:
+ return
+ }
+ }
+ }()
+}
+
+func (s *BleSesn) openOnce() (bool, error) {
+ if s.IsOpen() {
+ return false, nmxutil.NewSesnAlreadyOpenError(
+ "Attempt to open an already-open bll session")
+ }
+
+ if err := s.init(); err != nil {
+ return false, err
+ }
+
+ // Ensure subsequent calls to Close() block.
+ s.closeBlocker.Block()
+
+ // Listen for disconnect in the background.
+ s.disconnectListen()
+
+ if err := s.conn.Connect(
+ s.bx,
+ s.cfg.Ble.OwnAddrType,
+ s.cfg.PeerSpec.Ble,
+ s.cfg.Ble.Central.ConnTimeout); err != nil {
+
+ return false, err
+ }
+
+ if err := s.conn.ExchangeMtu(); err != nil {
+ bhdErr := nmxutil.ToBleHost(err)
+ retry := bhdErr != nil && bhdErr.Status == ERR_CODE_ENOTCONN
+ return retry, err
+ }
+
+ if err := s.conn.DiscoverSvcs(); err != nil {
+ return false, err
+ }
+
+ if chr, _ := s.getChr(s.mgmtChrs.NmpRspChr); chr != nil {
+ if chr.SubscribeType() != 0 {
+ if err := s.conn.Subscribe(chr); err != nil {
+ return false, err
+ }
+ }
+ }
+
+ // Listen for incoming notifications in the background.
+ s.notifyListen()
+
+ return false, nil
+}
+
+func (s *BleSesn) Open() error {
+ var err error
+ for i := 0; i < s.cfg.Ble.Central.ConnTries; i++ {
+ var retry bool
+
+ retry, err = s.openOnce()
+ if err != nil {
+ s.Close()
+ }
+
+ if !retry {
+ break
+ }
+ }
+
+ return err
+}
+
+func (s *BleSesn) OpenConnected(
+ connHandle uint16, eventListener *Listener) error {
+
+ if err := s.conn.Inherit(connHandle, eventListener); err != nil {
+ if !nmxutil.IsSesnAlreadyOpen(err) {
+ s.closeBlocker.Unblock()
+ }
+ return err
+ }
+
+ // Ensure subsequent calls to Close() block.
+ s.closeBlocker.Block()
+
+ // Listen for disconnect in the background.
+ s.disconnectListen()
+
+ // Listen for incoming notifications in the background.
+ s.notifyListen()
+
+ return nil
+}
+
+func (s *BleSesn) Close() error {
+ if err := s.conn.Stop(); err != nil {
+ return err
+ }
+
+ // Block until close completes.
+ s.closeBlocker.Wait()
+ return nil
+}
+
+func (s *BleSesn) IsOpen() bool {
+ return s.conn.IsConnected()
+}
+
+func (s *BleSesn) EncodeNmpMsg(m *nmp.NmpMsg) ([]byte, error) {
+ switch s.cfg.MgmtProto {
+ case sesn.MGMT_PROTO_NMP:
+ return nmp.EncodeNmpPlain(m)
+
+ case sesn.MGMT_PROTO_OMP:
+ return omp.EncodeOmpTcp(m)
+
+ default:
+ return nil,
+ fmt.Errorf("invalid management protocol: %+v", s.cfg.MgmtProto)
+ }
+}
+
+// Blocking.
+func (s *BleSesn) TxNmpOnce(req *nmp.NmpMsg, opt sesn.TxOptions) (
+ nmp.NmpRsp, error) {
+
+ chr, err := s.getChr(s.mgmtChrs.NmpReqChr)
+ if err != nil {
+ return nil, err
+ }
+
+ txRaw := func(b []byte) error {
+ return s.conn.WriteChrNoRsp(chr, b, "nmp")
+ }
+
+ return s.txvr.TxNmp(txRaw, req, opt.Timeout)
+}
+
+func (s *BleSesn) MtuIn() int {
+ return s.conn.AttMtu() -
+ NOTIFY_CMD_BASE_SZ -
+ omp.OMP_MSG_OVERHEAD -
+ nmp.NMP_HDR_SIZE
+}
+
+func (s *BleSesn) MtuOut() int {
+ mtu := s.conn.AttMtu() -
+ WRITE_CMD_BASE_SZ -
+ omp.OMP_MSG_OVERHEAD -
+ nmp.NMP_HDR_SIZE
+ return util.IntMin(mtu, BLE_ATT_ATTR_MAX_LEN)
+}
+
+func (s *BleSesn) ConnInfo() (BleConnDesc, error) {
+ return s.conn.ConnInfo(), nil
+}
+
+func (s *BleSesn) GetResourceOnce(resType sesn.ResourceType, uri string,
+ opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
+
+ token := nmxutil.NextToken()
+ req, err := oic.CreateGet(true, uri, token)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ chrId := ResChrIdLookup(s.mgmtChrs, resType)
+ chr, err := s.getChr(chrId)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ txRaw := func(b []byte) error {
+ return s.conn.WriteChrNoRsp(chr, b, "oic")
+ }
+
+ rsp, err := s.txvr.TxOic(txRaw, req, opt.Timeout)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ return rsp.Code(), rsp.Payload(), nil
+}
diff --git a/nmxact/nmble/ble_util.go b/nmxact/nmble/ble_util.go
index 6b6690d..2630536 100644
--- a/nmxact/nmble/ble_util.go
+++ b/nmxact/nmble/ble_util.go
@@ -26,9 +26,12 @@ import (
"time"
log "github.com/Sirupsen/logrus"
+ "github.com/runtimeco/go-coap"
. "mynewt.apache.org/newtmgr/nmxact/bledefs"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
+ "mynewt.apache.org/newtmgr/nmxact/oic"
+ "mynewt.apache.org/newtmgr/nmxact/sesn"
)
const WRITE_CMD_BASE_SZ = 3
@@ -194,6 +197,14 @@ func NewBleDiscAllChrsReq() *BleDiscAllChrsReq {
}
}
+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,
@@ -225,10 +236,14 @@ func NewBleWriteCmdReq() *BleWriteCmdReq {
Op: MSG_OP_REQ,
Type: MSG_TYPE_WRITE_CMD,
Seq: NextSeq(),
+ }
+}
- ConnHandle: 0,
- AttrHandle: 0,
- Data: BleBytes{},
+func NewBleWriteReq() *BleWriteReq {
+ return &BleWriteReq{
+ Op: MSG_OP_REQ,
+ Type: MSG_TYPE_WRITE,
+ Seq: NextSeq(),
}
}
@@ -664,3 +679,98 @@ func GattService() BleSvc {
},
}
}
+
+func GenCoapService(x *BleXport, svcUuid BleUuid, reqChrUuid BleUuid,
+ rspChrUuid BleUuid, resources []oic.Resource) (BleSvc, error) {
+
+ svr := NewBleOicSvr(x, svcUuid, rspChrUuid)
+ for _, r := range resources {
+ if err := svr.AddResource(r); err != nil {
+ return BleSvc{}, err
+ }
+ }
+
+ svc := BleSvc{
+ Uuid: svcUuid,
+ SvcType: BLE_SVC_TYPE_PRIMARY,
+ Chrs: []BleChr{
+ BleChr{
+ Uuid: reqChrUuid,
+ Flags: BLE_GATT_F_WRITE_NO_RSP, /* XXX: Security */
+ MinKeySize: 0,
+ AccessCb: func(access BleGattAccess) (uint8, []byte) {
+ return svr.Rx(access), nil
+ },
+ },
+ BleChr{
+ Uuid: rspChrUuid,
+ Flags: BLE_GATT_F_NOTIFY, /* XXX: Security */
+ MinKeySize: 0,
+ AccessCb: nil,
+ },
+ },
+ }
+
+ return svc, nil
+}
+
+func GwService(x *BleXport) (BleSvc, error) {
+ svcUuid, _ := ParseUuid(GwSvcUuid)
+ reqChrUuid, _ := ParseUuid(GwReqChrUuid)
+ rspChrUuid, _ := ParseUuid(GwRspChrUuid)
+
+ resources := []oic.Resource{
+ oic.Resource{
+ Name: "mynewt.yourmom",
+ ReadCb: func(uri string, data []byte) (coap.COAPCode, []byte) {
+ return coap.Content, []byte{1, 2, 3, 4}
+ },
+ },
+ }
+
+ return GenCoapService(x, svcUuid, reqChrUuid, rspChrUuid, resources)
+}
+
+func ResChrIdLookup(mgmtChrs BleMgmtChrs, resType sesn.ResourceType) *BleChrId {
+ m := map[sesn.ResourceType]*BleChrId{
+ sesn.RES_TYPE_PUBLIC: mgmtChrs.ResPublicReqChr,
+ sesn.RES_TYPE_UNAUTH: mgmtChrs.ResGwReqChr,
+ sesn.RES_TYPE_SECURE: mgmtChrs.ResPrivateReqChr,
+ }
+
+ return m[resType]
+}
+
+func BuildMgmtChrs(mgmtProto sesn.MgmtProto) (BleMgmtChrs, error) {
+ mgmtChrs := BleMgmtChrs{}
+
+ nmpSvcUuid, _ := ParseUuid(NmpPlainSvcUuid)
+ nmpChrUuid, _ := ParseUuid(NmpPlainChrUuid)
+
+ ompSvcUuid, _ := ParseUuid(OmpUnsecSvcUuid)
+ ompReqChrUuid, _ := ParseUuid(OmpUnsecReqChrUuid)
+ ompRspChrUuid, _ := ParseUuid(OmpUnsecRspChrUuid)
+
+ gwSvcUuid, _ := ParseUuid(GwSvcUuid)
+ gwReqChrUuid, _ := ParseUuid(GwReqChrUuid)
+ gwRspChrUuid, _ := ParseUuid(GwRspChrUuid)
+
+ 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.ResGwReqChr = &BleChrId{gwSvcUuid, gwReqChrUuid}
+ mgmtChrs.ResGwRspChr = &BleChrId{gwSvcUuid, gwRspChrUuid}
+
+ return mgmtChrs, nil
+}
diff --git a/nmxact/nmble/ble_xport.go b/nmxact/nmble/ble_xport.go
index f67354a..da7cf30 100644
--- a/nmxact/nmble/ble_xport.go
+++ b/nmxact/nmble/ble_xport.go
@@ -27,13 +27,11 @@ import (
"time"
log "github.com/Sirupsen/logrus"
- "github.com/runtimeco/go-coap"
"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/oic"
"mynewt.apache.org/newtmgr/nmxact/scan"
"mynewt.apache.org/newtmgr/nmxact/sesn"
)
@@ -184,16 +182,7 @@ func (bx *BleXport) BuildAdvertiser() (adv.Advertiser, error) {
}
func (bx *BleXport) BuildSesn(cfg sesn.SesnCfg) (sesn.Sesn, error) {
- switch cfg.MgmtProto {
- case sesn.MGMT_PROTO_NMP:
- return NewBlePlainSesn(bx, cfg), nil
- case sesn.MGMT_PROTO_OMP:
- return NewBleOicSesn(bx, cfg), nil
- default:
- return nil, fmt.Errorf(
- "Invalid management protocol: %d; expected NMP or OMP",
- cfg.MgmtProto)
- }
+ return NewBleSesn(bx, cfg)
}
func (bx *BleXport) addSyncListener() (*Listener, error) {
diff --git a/nmxact/nmble/conn.go b/nmxact/nmble/conn.go
new file mode 100644
index 0000000..bf8fd10
--- /dev/null
+++ b/nmxact/nmble/conn.go
@@ -0,0 +1,713 @@
+package nmble
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ log "github.com/Sirupsen/logrus"
+
+ . "mynewt.apache.org/newtmgr/nmxact/bledefs"
+ "mynewt.apache.org/newtmgr/nmxact/nmxutil"
+)
+
+type Notification struct {
+ Chr *Characteristic
+ Data []byte
+ Indication bool
+}
+
+type NotifyListener struct {
+ NotifyChan chan Notification
+ ErrChan chan error
+}
+
+func NewNotifyListener() *NotifyListener {
+ return &NotifyListener{
+ NotifyChan: make(chan Notification),
+ ErrChan: make(chan error),
+ }
+}
+
+type Conn struct {
+ bx *BleXport
+ rxvr *Receiver
+ attMtu int
+ profile Profile
+ desc BleConnDesc
+
+ connHandle uint16
+ connecting bool
+ disconnectChan chan error
+ wg sync.WaitGroup
+
+ // Terminates all go routines. Gets set to null after disconnect.
+ stopChan chan struct{}
+
+ notifyMap map[*Characteristic][](*NotifyListener)
+
+ // Protects:
+ // * connHandle
+ // * connecting
+ // * notifyMap
+ // * stopChan
+ mtx sync.Mutex
+}
+
+func NewConn(bx *BleXport) *Conn {
+ return &Conn{
+ bx: bx,
+ rxvr: NewReceiver(nmxutil.GetNextId(), bx, 1),
+ connHandle: BLE_CONN_HANDLE_NONE,
+ attMtu: BLE_ATT_MTU_DFLT,
+ disconnectChan: make(chan error, 1),
+ stopChan: make(chan struct{}),
+ notifyMap: map[*Characteristic][](*NotifyListener){},
+ }
+}
+
+func (c *Conn) DisconnectChan() <-chan error {
+ return c.disconnectChan
+}
+
+func (c *Conn) initiateShutdown() bool {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ if c.stopChan == nil {
+ return false
+ }
+
+ close(c.stopChan)
+ c.stopChan = nil
+
+ return true
+}
+
+func (c *Conn) abortNotifyListeners(err error) {
+ // No need to lock mutex; this should only be called after all go routines
+ // have terminated.
+ for _, nls := range c.notifyMap {
+ for _, nl := range nls {
+ nl.ErrChan <- err
+ close(nl.NotifyChan)
+ close(nl.ErrChan)
+ }
+ }
+}
+
+func (c *Conn) shutdown(err error) {
+ if !c.initiateShutdown() {
+ return
+ }
+
+ c.connecting = false
+ c.connHandle = BLE_CONN_HANDLE_NONE
+
+ c.bx.StopWaitingForMaster(c, err)
+
+ c.rxvr.RemoveAll("shutdown")
+ c.rxvr.WaitUntilNoListeners()
+
+ c.wg.Wait()
+
+ c.abortNotifyListeners(err)
+
+ c.disconnectChan <- err
+ close(c.disconnectChan)
+}
+
+func (c *Conn) newDisconnectError(reason int) error {
+ str := fmt.Sprintf("BLE peer disconnected; "+
+ "reason=\"%s\" (%d) connection=%s",
+ ErrCodeToString(reason), reason, c.desc.String())
+
+ return nmxutil.NewBleSesnDisconnectError(reason, str)
+}
+
+// Listens for events in the background.
+func (c *Conn) eventListen(bl *Listener) error {
+ c.wg.Add(1)
+
+ go func() {
+ defer c.wg.Done()
+ defer c.rxvr.RemoveListener("connect", bl)
+
+ for {
+ select {
+ case <-c.stopChan:
+ return
+
+ case err := <-bl.ErrChan:
+ go c.shutdown(err)
+ return
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleMtuChangeEvt:
+ if msg.Status != 0 {
+ err := StatusError(MSG_OP_EVT,
+ MSG_TYPE_MTU_CHANGE_EVT,
+ msg.Status)
+ log.Debugf(err.Error())
+ } else {
+ log.Debugf("BLE ATT MTU updated; from=%d to=%d",
+ c.attMtu, msg.Mtu)
+ c.attMtu = int(msg.Mtu)
+ }
+
+ case *BleEncChangeEvt:
+ var err error
+ if msg.Status != 0 {
+ err = StatusError(MSG_OP_EVT,
+ MSG_TYPE_ENC_CHANGE_EVT,
+ msg.Status)
+ log.Debugf(err.Error())
+ } else {
+ log.Debugf("Connection encrypted; conn_handle=%d",
+ msg.ConnHandle)
+ }
+
+ case *BleDisconnectEvt:
+ go c.shutdown(c.newDisconnectError(msg.Reason))
+ return
+
+ default:
+ }
+ }
+ }
+ }()
+ return nil
+}
+
+func (c *Conn) rxNotify(msg *BleNotifyRxEvt) {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ chr := c.profile.FindChrByHandle(uint16(msg.AttrHandle))
+ if chr == nil {
+ return
+ }
+
+ nls := c.notifyMap[chr]
+ if nls == nil {
+ return
+ }
+
+ n := Notification{
+ Chr: chr,
+ Data: msg.Data.Bytes,
+ Indication: msg.Indication,
+ }
+ for _, nl := range nls {
+ nl.NotifyChan <- n
+ }
+}
+
+// Listens for incoming notifications and indications.
+func (c *Conn) notifyListen() error {
+ key := TchKey(MSG_TYPE_NOTIFY_RX_EVT, int(c.connHandle))
+ bl, err := c.rxvr.AddListener("notifications", key)
+ if err != nil {
+ return err
+ }
+
+ c.wg.Add(1)
+ go func() {
+ defer c.wg.Done()
+ defer c.rxvr.RemoveListener("notifications", bl)
+
+ for {
+ select {
+ case <-c.stopChan:
+ return
+
+ case <-bl.ErrChan:
+ return
+
+ case bm := <-bl.MsgChan:
+ switch msg := bm.(type) {
+ case *BleNotifyRxEvt:
+ c.rxNotify(msg)
+ }
+ }
+ }
+ }()
+
+ return nil
+}
+
+func (c *Conn) startConnecting() error {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ if c.stopChan == nil {
+ return fmt.Errorf("Attempt to re-use conn object")
+ }
+
+ if c.connHandle != BLE_CONN_HANDLE_NONE {
+ return nmxutil.NewSesnAlreadyOpenError(
+ "BLE connection already established")
+ }
+ if c.connecting {
+ return nmxutil.NewSesnAlreadyOpenError(
+ "BLE connection already being established")
+ }
+
+ c.connecting = true
+ return nil
+}
+
+func (c *Conn) stopConnecting() {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ c.connecting = false
+}
+
+func (c *Conn) finalizeConnection(connHandle uint16,
+ eventListener *Listener) error {
+
+ c.mtx.Lock()
+ c.connecting = false
+ c.connHandle = connHandle
+ c.mtx.Unlock()
+
+ // Listen for events in the background.
+ if err := c.eventListen(eventListener); err != nil {
+ return err
+ }
+
+ // Listen for notifications in the background.
+ if err := c.notifyListen(); err != nil {
+ return err
+ }
+
+ d, err := ConnFindXact(c.bx, c.connHandle)
+ if err != nil {
+ return err
+ }
+ c.desc = d
+
+ return nil
+}
+
+func (c *Conn) IsConnected() bool {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ return c.connHandle != BLE_CONN_HANDLE_NONE
+}
+
+func (c *Conn) Connect(bx *BleXport, ownAddrType BleAddrType, peer BleDev,
+ timeout time.Duration) error {
+
+ if err := c.startConnecting(); err != nil {
+ return err
+ }
+ defer c.stopConnecting()
+
+ r := NewBleConnectReq()
+ r.OwnAddrType = ownAddrType
+ r.PeerAddrType = peer.AddrType
+ r.PeerAddr = peer.Addr
+
+ // Initiating a connection requires dedicated master privileges.
+ if err := c.bx.AcquireMaster(c); err != nil {
+ return err
+ }
+ defer c.bx.ReleaseMaster()
+
+ bl, err := c.rxvr.AddListener("connect", SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+
+ // Tell blehostd to initiate connection.
+ connHandle, err := connect(c.bx, bl, r, timeout)
+ if err != nil {
+ bhe := nmxutil.ToBleHost(err)
+ if bhe != nil && bhe.Status == ERR_CODE_EDONE {
+ // Already connected.
+ c.rxvr.RemoveListener("connect", bl)
+ return fmt.Errorf("Already connected to peer %s", peer)
+ } else if !nmxutil.IsXport(err) {
+ // The transport did not restart; always attempt to cancel the
+ // connect operation. In most cases, the host has already stopped
+ // connecting and will respond with an "ealready" error that can be
+ // ignored.
+ if err := c.connCancel(); err != nil {
+ log.Errorf("Failed to cancel connect in progress: %s",
+ err.Error())
+ }
+ }
+
+ c.rxvr.RemoveListener("connect", bl)
+ return err
+ }
+
+ if err := c.finalizeConnection(connHandle, bl); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *Conn) Inherit(connHandle uint16, bl *Listener) error {
+ if err := c.startConnecting(); err != nil {
+ return err
+ }
+
+ if err := c.finalizeConnection(connHandle, bl); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *Conn) ConnInfo() BleConnDesc {
+ return c.desc
+}
+
+func (c *Conn) AttMtu() int {
+ return c.attMtu
+}
+
+func (c *Conn) discAllDscsOnce(startHandle uint16, endHandle uint16) (
+ []*Descriptor, error) {
+
+ r := NewBleDiscAllDscsReq()
+ r.ConnHandle = c.connHandle
+ r.StartHandle = int(startHandle)
+ r.EndHandle = int(endHandle)
+
+ bl, err := c.rxvr.AddListener("disc-all-dscs", SeqKey(r.Seq))
+ if err != nil {
+ return nil, err
+ }
+ defer c.rxvr.RemoveListener("disc-all-dscs", bl)
+
+ rawDscs, err := discAllDscs(c.bx, bl, r)
+ if err != nil {
+ return nil, err
+ }
+
+ dscs := make([]*Descriptor, len(rawDscs))
+ for i, rd := range rawDscs {
+ dscs[i] = &Descriptor{
+ Uuid: rd.Uuid,
+ Handle: uint16(rd.Handle),
+ }
+ }
+
+ return dscs, nil
+}
+
+func (c *Conn) discAllDscs(chrs []*Characteristic, svcEndHandle uint16) error {
+ for i, _ := range chrs {
+ chr := chrs[i]
+
+ // Only discover descriptors if this characteristic has a CCCD.
+ if chr.SubscribeType() != 0 {
+ var endHandle uint16
+ if i < len(chrs)-1 {
+ endHandle = chrs[i+1].DefHandle - 1
+ } else {
+ endHandle = svcEndHandle
+ }
+
+ dscs, err := c.discAllDscsOnce(chrs[i].ValHandle, endHandle)
+ if err != nil {
+ return err
+ }
+
+ chr.Dscs = dscs
+ }
+ }
+
+ return nil
+}
+
+func (c *Conn) discAllChrsOnce(svc Service) ([]*Characteristic, error) {
+ r := NewBleDiscAllChrsReq()
+ r.ConnHandle = c.connHandle
+ r.StartHandle = int(svc.StartHandle)
+ r.EndHandle = int(svc.EndHandle)
+
+ bl, err := c.rxvr.AddListener("disc-all-chrs", SeqKey(r.Seq))
+ if err != nil {
+ return nil, err
+ }
+ defer c.rxvr.RemoveListener("disc-all-chrs", bl)
+
+ rawChrs, err := discAllChrs(c.bx, bl, r)
+ if err != nil {
+ return nil, err
+ }
+
+ chrs := make([]*Characteristic, len(rawChrs))
+ for i, rc := range rawChrs {
+ chrs[i] = &Characteristic{
+ Uuid: rc.Uuid,
+ DefHandle: uint16(rc.DefHandle),
+ ValHandle: uint16(rc.ValHandle),
+ Properties: BleChrFlags(rc.Properties),
+ }
+ }
+
+ if err := c.discAllDscs(chrs, svc.EndHandle); err != nil {
+ return nil, err
+ }
+
+ return chrs, nil
+}
+
+func (c *Conn) discAllChrs(svcs []Service) error {
+ for i, _ := range svcs {
+ chrs, err := c.discAllChrsOnce(svcs[i])
+ if err != nil {
+ return err
+ }
+
+ svcs[i].Chrs = chrs
+ }
+
+ return nil
+}
+
+func (c *Conn) discAllSvcs() ([]Service, error) {
+ r := NewBleDiscAllSvcsReq()
+ r.ConnHandle = c.connHandle
+
+ bl, err := c.rxvr.AddListener("disc-all-svcs", SeqKey(r.Seq))
+ if err != nil {
+ return nil, err
+ }
+ defer c.rxvr.RemoveListener("disc-all-svcs", bl)
+
+ rawSvcs, err := discAllSvcs(c.bx, bl, r)
+ if err != nil {
+ return nil, err
+ }
+
+ svcs := make([]Service, len(rawSvcs))
+ for i, rs := range rawSvcs {
+ svcs[i] = Service{
+ Uuid: rs.Uuid,
+ StartHandle: uint16(rs.StartHandle),
+ EndHandle: uint16(rs.EndHandle),
+ }
+ }
+
+ return svcs, nil
+}
+
+func (c *Conn) DiscoverSvcs() error {
+ svcs, err := c.discAllSvcs()
+ if err != nil {
+ return err
+ }
+
+ if err := c.discAllChrs(svcs); err != nil {
+ return err
+ }
+
+ c.profile.SetServices(svcs)
+
+ return nil
+}
+
+func (c *Conn) Profile() *Profile {
+ return &c.profile
+}
+
+func isExchangeMtuError(err error) bool {
+ if err == nil {
+ return false
+ }
+
+ // If the operation failed because the peer already initiated the
+ // exchange, just pretend it was successful.
+ bhe := nmxutil.ToBleHost(err)
+ if bhe == nil {
+ return true
+ }
+
+ switch bhe.Status {
+ case ERR_CODE_EALREADY:
+ return false
+ case ERR_CODE_ATT_BASE + ERR_CODE_ATT_REQ_NOT_SUPPORTED:
+ return false
+ default:
+ return true
+ }
+}
+
+func (c *Conn) ExchangeMtu() error {
+ r := NewBleExchangeMtuReq()
+ r.ConnHandle = c.connHandle
+
+ bl, err := c.rxvr.AddListener("exchange-mtu", SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer c.rxvr.RemoveListener("exchange-mtu", bl)
+
+ mtu, err := exchangeMtu(c.bx, bl, r)
+ if isExchangeMtuError(err) {
+ return err
+ }
+
+ c.attMtu = mtu
+ return nil
+}
+
+func (c *Conn) WriteHandle(handle uint16, payload []byte,
+ name string) error {
+
+ r := NewBleWriteReq()
+ r.ConnHandle = c.connHandle
+ r.AttrHandle = int(handle)
+ r.Data.Bytes = payload
+
+ bl, err := c.rxvr.AddListener(name, SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer c.rxvr.RemoveListener(name, bl)
+
+ if err := write(c.bx, bl, r); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *Conn) WriteHandleNoRsp(handle uint16, payload []byte,
+ name string) error {
+
+ r := NewBleWriteCmdReq()
+ r.ConnHandle = c.connHandle
+ r.AttrHandle = int(handle)
+ r.Data.Bytes = payload
+
+ bl, err := c.rxvr.AddListener(name, SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer c.rxvr.RemoveListener(name, bl)
+
+ if err := writeCmd(c.bx, bl, r); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *Conn) WriteChr(chr *Characteristic, payload []byte,
+ name string) error {
+
+ return c.WriteHandle(chr.ValHandle, payload, name)
+}
+
+func (c *Conn) WriteChrNoRsp(chr *Characteristic, payload []byte,
+ name string) error {
+
+ return c.WriteHandleNoRsp(chr.ValHandle, payload, name)
+}
+
+func (c *Conn) Subscribe(chr *Characteristic) error {
+ uuid := BleUuid{CccdUuid, [16]byte{}}
+ dsc := FindDscByUuid(chr, uuid)
+ if dsc == nil {
+ return fmt.Errorf("Cannot subscribe to characteristic %s; no CCCD",
+ chr.Uuid.String())
+ }
+
+ var payload []byte
+ switch chr.SubscribeType() {
+ case BLE_GATT_F_NOTIFY:
+ payload = []byte{1, 0}
+ case BLE_GATT_F_INDICATE:
+ payload = []byte{2, 0}
+ default:
+ return fmt.Errorf("Cannot subscribe to characteristic %s; "+
+ "properties indicate unsubscribable", chr.Uuid.String())
+ }
+
+ return c.WriteHandle(dsc.Handle, payload, "subscribe")
+}
+
+func (c *Conn) ListenForNotifications(chr *Characteristic) *NotifyListener {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ nl := NewNotifyListener()
+ slice := c.notifyMap[chr]
+
+ slice = append(slice, nl)
+ c.notifyMap[chr] = slice
+
+ return nl
+}
+
+func (c *Conn) terminate() error {
+ r := NewBleTerminateReq()
+ r.ConnHandle = c.connHandle
+ r.HciReason = ERR_CODE_HCI_REM_USER_CONN_TERM
+
+ bl, err := c.rxvr.AddListener("terminate", SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer c.rxvr.RemoveListener("terminate", bl)
+
+ if err := terminate(c.bx, bl, r); err != nil {
+ // Ignore ealready errors.
+ bhdErr := nmxutil.ToBleHost(err)
+ if bhdErr == nil || bhdErr.Status != ERR_CODE_EALREADY {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (c *Conn) connCancel() error {
+ r := NewBleConnCancelReq()
+
+ bl, err := c.rxvr.AddListener("conn-cancel", SeqKey(r.Seq))
+ if err != nil {
+ return err
+ }
+ defer c.rxvr.RemoveListener("conn-cancel", bl)
+
+ if err := connCancel(c.bx, bl, r); err != nil {
+ // Ignore ealready errors.
+ bhdErr := nmxutil.ToBleHost(err)
+ if bhdErr == nil || bhdErr.Status != ERR_CODE_EALREADY {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (c *Conn) Stop() error {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ var err error
+ if c.connHandle != BLE_CONN_HANDLE_NONE {
+ err = c.terminate()
+ } else if c.connecting {
+ err = c.connCancel()
+ }
+
+ if err != nil {
+ // Something went unexpectedly wrong. Just force a shutdown.
+ go c.shutdown(err)
+ }
+
+ return nil
+}
diff --git a/nmxact/nmble/dispatch.go b/nmxact/nmble/dispatch.go
index b097078..ffdb1d4 100644
--- a/nmxact/nmble/dispatch.go
+++ b/nmxact/nmble/dispatch.go
@@ -61,6 +61,8 @@ func discAllSvcsRspCtor() Msg { return &BleDiscAllSvcsRsp{} }
func discSvcUuidRspCtor() Msg { return &BleDiscSvcUuidRsp{} }
func discAllChrsRspCtor() Msg { return &BleDiscAllChrsRsp{} }
func discChrUuidRspCtor() Msg { return &BleDiscChrUuidRsp{} }
+func discAllDscsRspCtor() Msg { return &BleDiscAllDscsRsp{} }
+func writeRspCtor() Msg { return &BleWriteRsp{} }
func writeCmdRspCtor() Msg { return &BleWriteCmdRsp{} }
func exchangeMtuRspCtor() Msg { return &BleExchangeMtuRsp{} }
func genRandAddrRspCtor() Msg { return &BleGenRandAddrRsp{} }
@@ -89,6 +91,8 @@ func connectEvtCtor() Msg { return &BleConnectEvt{} }
func disconnectEvtCtor() Msg { return &BleDisconnectEvt{} }
func discSvcEvtCtor() Msg { return &BleDiscSvcEvt{} }
func discChrEvtCtor() Msg { return &BleDiscChrEvt{} }
+func discDscEvtCtor() Msg { return &BleDiscDscEvt{} }
+func writeAckEvtCtor() Msg { return &BleWriteAckEvt{} }
func notifyRxEvtCtor() Msg { return &BleNotifyRxEvt{} }
func mtuChangeEvtCtor() Msg { return &BleMtuChangeEvt{} }
func scanEvtCtor() Msg { return &BleScanEvt{} }
@@ -104,8 +108,10 @@ var msgCtorMap = map[OpTypePair]msgCtor{
{MSG_OP_RSP, MSG_TYPE_TERMINATE}: terminateRspCtor,
{MSG_OP_RSP, MSG_TYPE_DISC_ALL_SVCS}: discAllSvcsRspCtor,
{MSG_OP_RSP, MSG_TYPE_DISC_SVC_UUID}: discSvcUuidRspCtor,
- {MSG_OP_RSP, MSG_TYPE_DISC_CHR_UUID}: discChrUuidRspCtor,
{MSG_OP_RSP, MSG_TYPE_DISC_ALL_CHRS}: discAllChrsRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_DISC_CHR_UUID}: discChrUuidRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_DISC_ALL_DSCS}: discAllDscsRspCtor,
+ {MSG_OP_RSP, MSG_TYPE_WRITE}: writeRspCtor,
{MSG_OP_RSP, MSG_TYPE_WRITE_CMD}: writeCmdRspCtor,
{MSG_OP_RSP, MSG_TYPE_EXCHANGE_MTU}: exchangeMtuRspCtor,
{MSG_OP_RSP, MSG_TYPE_GEN_RAND_ADDR}: genRandAddrRspCtor,
@@ -134,6 +140,8 @@ var msgCtorMap = map[OpTypePair]msgCtor{
{MSG_OP_EVT, MSG_TYPE_DISCONNECT_EVT}: disconnectEvtCtor,
{MSG_OP_EVT, MSG_TYPE_DISC_SVC_EVT}: discSvcEvtCtor,
{MSG_OP_EVT, MSG_TYPE_DISC_CHR_EVT}: discChrEvtCtor,
+ {MSG_OP_EVT, MSG_TYPE_DISC_DSC_EVT}: discDscEvtCtor,
+ {MSG_OP_EVT, MSG_TYPE_WRITE_ACK_EVT}: writeAckEvtCtor,
{MSG_OP_EVT, MSG_TYPE_NOTIFY_RX_EVT}: notifyRxEvtCtor,
{MSG_OP_EVT, MSG_TYPE_MTU_CHANGE_EVT}: mtuChangeEvtCtor,
{MSG_OP_EVT, MSG_TYPE_SCAN_EVT}: scanEvtCtor,
diff --git a/nmxact/nmble/profile.go b/nmxact/nmble/profile.go
new file mode 100644
index 0000000..0e98740
--- /dev/null
+++ b/nmxact/nmble/profile.go
@@ -0,0 +1,82 @@
+package nmble
+
+import (
+ . "mynewt.apache.org/newtmgr/nmxact/bledefs"
+)
+
+type Descriptor struct {
+ Uuid BleUuid
+ Handle uint16
+ AttFlags BleAttFlags
+}
+
+type Characteristic struct {
+ Uuid BleUuid
+ DefHandle uint16
+ ValHandle uint16
+ Properties BleChrFlags
+ Dscs []*Descriptor
+}
+
+type Service struct {
+ Uuid BleUuid
+ StartHandle uint16
+ EndHandle uint16
+ Chrs []*Characteristic
+}
+
+type Profile struct {
+ svcs []Service
+ chrs map[BleChrId]*Characteristic
+ attrs map[uint16]*Characteristic
+}
+
+func (c *Characteristic) SubscribeType() BleChrFlags {
+ if c.Properties&BLE_GATT_F_NOTIFY != 0 {
+ return BLE_GATT_F_NOTIFY
+ } else {
+ return c.Properties & BLE_GATT_F_INDICATE
+ }
+}
+
+func NewProfile() Profile {
+ return Profile{
+ chrs: map[BleChrId]*Characteristic{},
+ attrs: map[uint16]*Characteristic{},
+ }
+}
+
+func (p *Profile) Services() []Service {
+ return p.svcs
+}
+
+func (p *Profile) SetServices(svcs []Service) {
+ p.svcs = svcs
+ p.chrs = map[BleChrId]*Characteristic{}
+ p.attrs = map[uint16]*Characteristic{}
+
+ for _, s := range svcs {
+ for _, c := range s.Chrs {
+ p.chrs[BleChrId{s.Uuid, c.Uuid}] = c
+ p.attrs[uint16(c.ValHandle)] = c
+ }
+ }
+}
+
+func (p *Profile) FindChrByUuid(id BleChrId) *Characteristic {
+ return p.chrs[id]
+}
+
+func (p *Profile) FindChrByHandle(handle uint16) *Characteristic {
+ return p.attrs[handle]
+}
+
+func FindDscByUuid(chr *Characteristic, uuid BleUuid) *Descriptor {
+ for _, d := range chr.Dscs {
+ if CompareUuids(uuid, d.Uuid) == 0 {
+ return d
+ }
+ }
+
+ return nil
+}
diff --git a/nmxact/nmble/transceiver.go b/nmxact/nmble/transceiver.go
deleted file mode 100644
index 34705b0..0000000
--- a/nmxact/nmble/transceiver.go
+++ /dev/null
@@ -1,256 +0,0 @@
-package nmble
-
-import (
- "encoding/hex"
- "fmt"
- "sync"
- "time"
-
- log "github.com/Sirupsen/logrus"
- "github.com/runtimeco/go-coap"
-
- "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 Transceiver struct {
- connHandle uint16
- bx *BleXport
- rxer *Receiver
- nd *nmp.Dispatcher
- od *omp.Dispatcher
-
- wg sync.WaitGroup
- nmpErrChan chan error
-}
-
-func NewTransceiver(id uint32, connHandle uint16, mgmtProto sesn.MgmtProto,
- bx *BleXport, rxer *Receiver, logDepth int) (*Transceiver, error) {
-
- t := &Transceiver{
- connHandle: connHandle,
- bx: bx,
- rxer: rxer,
- }
-
- switch mgmtProto {
- case sesn.MGMT_PROTO_NMP:
- t.nd = nmp.NewDispatcher(logDepth)
-
- case sesn.MGMT_PROTO_OMP:
- od, err := omp.NewDispatcher(true, logDepth)
- if err != nil {
- return nil, err
- }
- t.od = od
-
- default:
- return nil,
- fmt.Errorf("Invalid session management protocol: %#v", mgmtProto)
- }
-
- return t, nil
-}
-
-func (t *Transceiver) WriteCmd(attHandle uint16, payload []byte,
- name string) error {
-
- r := NewBleWriteCmdReq()
- r.ConnHandle = t.connHandle
- r.AttrHandle = int(attHandle)
- r.Data.Bytes = payload
-
- bl, err := t.rxer.AddListener(name, SeqKey(r.Seq))
- if err != nil {
- return err
- }
- defer t.rxer.RemoveListener(name, bl)
-
- if err := writeCmd(t.bx, bl, r); err != nil {
- return err
- }
-
- return nil
-}
-
-func (t *Transceiver) txPlain(attHandle uint16, req *nmp.NmpMsg,
- timeout time.Duration) (nmp.NmpRsp, error) {
-
- nl, err := t.nd.AddListener(req.Hdr.Seq)
- if err != nil {
- return nil, err
- }
- defer t.nd.RemoveListener(req.Hdr.Seq)
-
- b, err := nmp.EncodeNmpPlain(req)
- if err != nil {
- return nil, err
- }
-
- log.Debugf("Tx NMP request: %s", hex.Dump(b))
-
- if err := t.WriteCmd(attHandle, b, "nmp-rsp"); err != nil {
- return nil, err
- }
-
- // Now wait for NMP response.
- for {
- select {
- case err := <-nl.ErrChan:
- return nil, err
- case rsp := <-nl.RspChan:
- return rsp, nil
- case <-nl.AfterTimeout(timeout):
- return nil, nmxutil.NewRspTimeoutError("NMP timeout")
- }
- }
-}
-
-func (t *Transceiver) txOmp(attHandle uint16, req *nmp.NmpMsg,
- timeout time.Duration) (nmp.NmpRsp, error) {
-
- nl, err := t.od.AddNmpListener(req.Hdr.Seq)
- if err != nil {
- return nil, err
- }
- defer t.od.RemoveNmpListener(req.Hdr.Seq)
-
- b, err := omp.EncodeOmpTcp(req)
- if err != nil {
- return nil, err
- }
-
- log.Debugf("Tx OMP request: %s", hex.Dump(b))
-
- if err := t.WriteCmd(attHandle, b, "omp-rsp"); err != nil {
- return nil, err
- }
-
- // Now wait for NMP response.
- for {
- select {
- case err := <-nl.ErrChan:
- return nil, err
- case rsp := <-nl.RspChan:
- return rsp, nil
- case <-nl.AfterTimeout(timeout):
- return nil, nmxutil.NewRspTimeoutError("NMP timeout")
- }
- }
-}
-
-func (t *Transceiver) TxNmp(attHandle uint16, req *nmp.NmpMsg,
- timeout time.Duration) (nmp.NmpRsp, error) {
-
- if t.nd != nil {
- return t.txPlain(attHandle, req, timeout)
- } else {
- return t.txOmp(attHandle, req, timeout)
- }
-}
-
-func (t *Transceiver) TxOic(attHandle uint16, req coap.Message,
- timeout time.Duration) (coap.Message, error) {
-
- b, err := oic.Encode(req)
- if err != nil {
- return nil, err
- }
-
- // XXX: Only listen if message expects an ack.
-
- ol, err := t.od.AddOicListener(req.Token())
- if err != nil {
- return nil, err
- }
- defer t.od.RemoveOicListener(req.Token())
-
- log.Debugf("Tx OIC request: %s", hex.Dump(b))
- if err := t.WriteCmd(attHandle, b, "oic-rsp"); err != nil {
- return nil, err
- }
-
- for {
- select {
- case err := <-ol.ErrChan:
- return nil, err
- case rsp := <-ol.RspChan:
- return rsp, nil
- case <-ol.AfterTimeout(timeout):
- return nil, nmxutil.NewRspTimeoutError("OIC timeout")
- }
- }
-}
-
-func (t *Transceiver) NmpRspListen(attHandle uint16) (<-chan error, error) {
- if t.nmpErrChan != nil {
- return t.nmpErrChan, nil
- }
-
- key := TchKey(MSG_TYPE_NOTIFY_RX_EVT, int(t.connHandle))
- bl, err := t.rxer.AddListener("nmp-rsp", key)
- if err != nil {
- return nil, err
- }
-
- t.wg.Add(1)
-
- t.nmpErrChan = make(chan error)
-
- go func() {
- defer t.wg.Done()
- defer t.rxer.RemoveListener("nmp-rsp", bl)
-
- for {
- select {
- case err, ok := <-bl.ErrChan:
- if !ok {
- return
- }
- t.nmpErrChan <- err
-
- case bm := <-bl.MsgChan:
- switch msg := bm.(type) {
- case *BleNotifyRxEvt:
- if msg.AttrHandle == int(attHandle) {
- if t.nd != nil {
- t.nd.Dispatch(msg.Data.Bytes)
- } else {
- t.od.Dispatch(msg.Data.Bytes)
- }
- } else {
- // XXX: Forward non-management notifications elsewhere.
- }
- }
- }
- }
- }()
- return t.nmpErrChan, nil
-}
-
-func (t *Transceiver) Stop(err error) {
- if t.nd != nil {
- t.nd.ErrorAll(err)
- } else {
- t.od.ErrorAll(err)
- t.od.Stop()
- }
-
- t.wg.Wait()
-
- ch := t.nmpErrChan
- if ch != nil {
- close(ch)
- }
-}
-
-func (t *Transceiver) AbortNmpRx(seq uint8) error {
- return t.nd.ErrorOne(seq, fmt.Errorf("Rx aborted"))
-}
-
-func (t *Transceiver) AbortOmpRx(seq uint8) error {
- return t.od.ErrorOneNmp(seq, fmt.Errorf("Rx aborted"))
-}
diff --git a/nmxact/nmserial/serial_oic_sesn.go b/nmxact/nmserial/serial_oic_sesn.go
index fd38477..8279065 100644
--- a/nmxact/nmserial/serial_oic_sesn.go
+++ b/nmxact/nmserial/serial_oic_sesn.go
@@ -152,8 +152,8 @@ func (sos *SerialOicSesn) TxNmpOnce(m *nmp.NmpMsg, opt sesn.TxOptions) (
}
}
-func (sos *SerialOicSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
+func (sos *SerialOicSesn) GetResourceOnce(resType sesn.ResourceType,
+ uri string, opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
token := nmxutil.NextToken()
diff --git a/nmxact/nmserial/serial_plain_sesn.go b/nmxact/nmserial/serial_plain_sesn.go
index 6348410..5f219da 100644
--- a/nmxact/nmserial/serial_plain_sesn.go
+++ b/nmxact/nmserial/serial_plain_sesn.go
@@ -157,8 +157,8 @@ func (sps *SerialPlainSesn) TxNmpOnce(m *nmp.NmpMsg, opt sesn.TxOptions) (
}
}
-func (sps *SerialPlainSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
+func (sps *SerialPlainSesn) GetResourceOnce(resType sesn.ResourceType,
+ uri string, opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
return 0, nil, fmt.Errorf("SerialPlainSesn.GetResourceOnce() unsupported")
}
diff --git a/nmxact/nmxutil/nmxutil.go b/nmxact/nmxutil/nmxutil.go
index 72bd00e..d95c368 100644
--- a/nmxact/nmxutil/nmxutil.go
+++ b/nmxact/nmxutil/nmxutil.go
@@ -27,6 +27,7 @@ import (
"path"
"runtime"
"sync"
+ "sync/atomic"
"time"
log "github.com/Sirupsen/logrus"
@@ -115,6 +116,12 @@ func DecodeCborMap(cbor []byte) (map[string]interface{}, error) {
return m, nil
}
+var nextId uint32
+
+func GetNextId() uint32 {
+ return atomic.AddUint32(&nextId, 1) - 1
+}
+
func LogListener(parentLevel int, title string, extra string) {
_, file, line, _ := runtime.Caller(parentLevel)
file = path.Base(file)
diff --git a/nmxact/oic/oic.go b/nmxact/oic/oic.go
index 808eb49..f5052ed 100644
--- a/nmxact/oic/oic.go
+++ b/nmxact/oic/oic.go
@@ -26,14 +26,6 @@ import (
"github.com/runtimeco/go-coap"
)
-type ResType int
-
-const (
- RES_TYPE_PUBLIC ResType = iota
- RES_TYPE_GW
- RES_TYPE_PRIVATE
-)
-
var messageIdMtx sync.Mutex
var nextMessageId uint16
diff --git a/nmxact/oic/resmgr.go b/nmxact/oic/resmgr.go
index 9a1a651..467204d 100644
--- a/nmxact/oic/resmgr.go
+++ b/nmxact/oic/resmgr.go
@@ -7,8 +7,8 @@ import (
"github.com/runtimeco/go-coap"
)
-type ResWriteFn func(data []byte) coap.COAPCode
-type ResReadFn func(data []byte) (coap.COAPCode, []byte)
+type ResWriteFn func(uri string, data []byte) coap.COAPCode
+type ResReadFn func(uri string, data []byte) (coap.COAPCode, []byte)
type Resource struct {
Name string
@@ -55,14 +55,14 @@ func (rm *ResMgr) Access(m coap.Message) (coap.COAPCode, []byte) {
if r.ReadCb == nil {
return coap.MethodNotAllowed, nil
} else {
- return r.ReadCb(m.Payload())
+ return r.ReadCb(path, m.Payload())
}
case coap.PUT:
if r.WriteCb == nil {
return coap.MethodNotAllowed, nil
} else {
- return r.WriteCb(m.Payload()), nil
+ return r.WriteCb(path, m.Payload()), nil
}
default:
diff --git a/nmxact/omp/frag.go b/nmxact/omp/frag.go
deleted file mode 100644
index 0ad8e91..0000000
--- a/nmxact/omp/frag.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * 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 omp
-
-import (
- log "github.com/Sirupsen/logrus"
- "github.com/runtimeco/go-coap"
-)
-
-type Reassembler struct {
- cur []byte
-}
-
-func NewReassembler() *Reassembler {
- return &Reassembler{}
-}
-
-func (r *Reassembler) RxFrag(frag []byte) *coap.TcpMessage {
- r.cur = append(r.cur, frag...)
-
- var tm *coap.TcpMessage
- var err error
- tm, r.cur, err = coap.PullTcp(r.cur)
- if err != nil {
- log.Debugf("received invalid CoAP-TCP packet: %s", err.Error())
- return nil
- }
-
- if tm != nil {
- r.cur = nil
- }
-
- return tm
-}
diff --git a/nmxact/sesn/sesn.go b/nmxact/sesn/sesn.go
index 3b3a059..8db9438 100644
--- a/nmxact/sesn/sesn.go
+++ b/nmxact/sesn/sesn.go
@@ -27,14 +27,6 @@ import (
"mynewt.apache.org/newtmgr/nmxact/nmp"
)
-type ResourceType int
-
-const (
- RES_TYPE_PUBLIC ResourceType = iota
- RES_TYPE_GATEWAY
- RES_TYPE_PRIVATE
-)
-
type TxOptions struct {
Timeout time.Duration
Tries int
@@ -102,6 +94,8 @@ type Sesn interface {
// * other error
TxNmpOnce(m *nmp.NmpMsg, opt TxOptions) (nmp.NmpRsp, error)
- GetResourceOnce(uri string, opt TxOptions) (coap.COAPCode, []byte, error)
+ GetResourceOnce(resType ResourceType, uri string, opt TxOptions) (
+ coap.COAPCode, []byte, error)
+
//SetResource(uri string, value []byte, opt TxOptions) error
}
diff --git a/nmxact/sesn/sesn_cfg.go b/nmxact/sesn/sesn_cfg.go
index 7c11d33..c3e84f4 100644
--- a/nmxact/sesn/sesn_cfg.go
+++ b/nmxact/sesn/sesn_cfg.go
@@ -20,6 +20,7 @@
package sesn
import (
+ "fmt"
"time"
"mynewt.apache.org/newtmgr/nmxact/bledefs"
@@ -32,6 +33,34 @@ const (
MGMT_PROTO_OMP
)
+type ResourceType int
+
+const (
+ RES_TYPE_PUBLIC ResourceType = iota
+ RES_TYPE_UNAUTH
+ RES_TYPE_SECURE
+)
+
+var resTypeMap = map[ResourceType]string{
+ RES_TYPE_PUBLIC: "public",
+ RES_TYPE_UNAUTH: "unauth",
+ RES_TYPE_SECURE: "secure",
+}
+
+func (r ResourceType) String() string {
+ return resTypeMap[r]
+}
+
+func ParseResType(s string) (ResourceType, error) {
+ for r, n := range resTypeMap {
+ if s == n {
+ return r, nil
+ }
+ }
+
+ return ResourceType(0), fmt.Errorf("Unknown resource type: %s", s)
+}
+
type OnCloseFn func(s Sesn, err error)
type PeerSpec struct {
diff --git a/nmxact/sesn/sesn_util.go b/nmxact/sesn/sesn_util.go
index b964639..6399ee9 100644
--- a/nmxact/sesn/sesn_util.go
+++ b/nmxact/sesn/sesn_util.go
@@ -40,10 +40,12 @@ func TxNmp(s Sesn, m *nmp.NmpMsg, o TxOptions) (nmp.NmpRsp, error) {
}
}
-func GetResource(s Sesn, uri string, o TxOptions) (coap.COAPCode, []byte, error) {
+func GetResource(s Sesn, resType ResourceType, uri string, o TxOptions) (
+ coap.COAPCode, []byte, error) {
+
retries := o.Tries - 1
for i := 0; ; i++ {
- code, r, err := s.GetResourceOnce(uri, o)
+ code, r, err := s.GetResourceOnce(resType, uri, o)
if err == nil {
return code, r, nil
}
diff --git a/nmxact/udp/udp_oic_sesn.go b/nmxact/udp/udp_oic_sesn.go
index da1c68c..9690221 100644
--- a/nmxact/udp/udp_oic_sesn.go
+++ b/nmxact/udp/udp_oic_sesn.go
@@ -145,8 +145,8 @@ func (uos *UdpOicSesn) AbortRx(seq uint8) error {
return nil
}
-func (uos *UdpOicSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
+func (uos *UdpOicSesn) GetResourceOnce(resType sesn.ResourceType, uri string,
+ opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
token := nmxutil.NextToken()
diff --git a/nmxact/udp/udp_plain_sesn.go b/nmxact/udp/udp_plain_sesn.go
index 9e0ce2d..4f79310 100644
--- a/nmxact/udp/udp_plain_sesn.go
+++ b/nmxact/udp/udp_plain_sesn.go
@@ -134,8 +134,8 @@ func (ups *UdpPlainSesn) AbortRx(seq uint8) error {
return ups.d.ErrorOne(seq, fmt.Errorf("Rx aborted"))
}
-func (ups *UdpPlainSesn) GetResourceOnce(uri string, opt sesn.TxOptions) (
- coap.COAPCode, []byte, error) {
+func (ups *UdpPlainSesn) GetResourceOnce(resType sesn.ResourceType,
+ uri string, opt sesn.TxOptions) (coap.COAPCode, []byte, error) {
return 0, nil, fmt.Errorf("UdpPlainSesn.GetResourceOnce() unsupported")
}
diff --git a/nmxact/xact/getres.go b/nmxact/xact/getres.go
index 1bf85bc..e56067d 100644
--- a/nmxact/xact/getres.go
+++ b/nmxact/xact/getres.go
@@ -55,7 +55,7 @@ func (r *GetResResult) Status() int {
}
func (c *GetResCmd) Run(s sesn.Sesn) (Result, error) {
- status, val, err := sesn.GetResource(s, c.Uri, c.TxOptions())
+ status, val, err := sesn.GetResource(s, c.Typ, c.Uri, c.TxOptions())
if err != nil {
return nil, err
}
--
To stop receiving notification emails like this one, please contact
"commits@mynewt.apache.org" <co...@mynewt.apache.org>.