Repository: incubator-mynewt-newtmgr Updated Branches: refs/heads/master 5af109bf9 -> 99af734d9
nmxact - separate connect and discover procedures. Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/commit/99af734d Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/tree/99af734d Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/diff/99af734d Branch: refs/heads/master Commit: 99af734d98bba36b204dd893a529257afc4cdef6 Parents: 5af109b Author: Christopher Collins <ccoll...@apache.org> Authored: Fri May 19 18:03:00 2017 -0700 Committer: Christopher Collins <ccoll...@apache.org> Committed: Fri May 19 18:04:35 2017 -0700 ---------------------------------------------------------------------- newtmgr/cli/common.go | 25 ++++- newtmgr/config/ble_config.go | 24 ++++- nmxact/bledefs/bledefs.go | 1 + nmxact/example/ble_loop/ble_loop.go | 18 +++- nmxact/example/ble_plain/ble_plain.go | 18 +++- nmxact/example/ble_scan/ble_scan.go | 5 +- nmxact/nmble/ble_act.go | 5 +- nmxact/nmble/ble_fsm.go | 160 +++++------------------------ nmxact/nmble/ble_oic_sesn.go | 10 +- nmxact/nmble/ble_plain_sesn.go | 9 +- nmxact/nmble/ble_scanner.go | 130 +++++++++++++++-------- nmxact/nmble/ble_util.go | 14 +++ nmxact/nmble/discover.go | 142 +++++++++++++++++++++++++ nmxact/scan/scan.go | 37 ++++--- nmxact/sesn/sesn_cfg.go | 42 +++----- 15 files changed, 394 insertions(+), 246 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/newtmgr/cli/common.go ---------------------------------------------------------------------- diff --git a/newtmgr/cli/common.go b/newtmgr/cli/common.go index da3c348..0d11478 100644 --- a/newtmgr/cli/common.go +++ b/newtmgr/cli/common.go @@ -22,12 +22,13 @@ package cli import ( "fmt" + "mynewt.apache.org/newt/util" "mynewt.apache.org/newtmgr/newtmgr/config" "mynewt.apache.org/newtmgr/newtmgr/nmutil" + "mynewt.apache.org/newtmgr/nmxact/nmble" "mynewt.apache.org/newtmgr/nmxact/nmserial" "mynewt.apache.org/newtmgr/nmxact/sesn" "mynewt.apache.org/newtmgr/nmxact/xport" - "mynewt.apache.org/newt/util" ) var globalSesn sesn.Sesn @@ -109,8 +110,17 @@ func buildSesnCfg() (sesn.SesnCfg, error) { return sc, err } + x, err := GetXport() + if err != nil { + return sc, err + } + bx := x.(*nmble.BleXport) + sc.MgmtProto = sesn.MGMT_PROTO_NMP - config.FillSesnCfg(bc, &sc) + if err := config.FillSesnCfg(bx, bc, &sc); err != nil { + return sc, err + } + return sc, nil case config.CONN_TYPE_BLE_OIC: @@ -119,8 +129,17 @@ func buildSesnCfg() (sesn.SesnCfg, error) { return sc, err } + x, err := GetXport() + if err != nil { + return sc, err + } + bx := x.(*nmble.BleXport) + sc.MgmtProto = sesn.MGMT_PROTO_OMP - config.FillSesnCfg(bc, &sc) + if err := config.FillSesnCfg(bx, bc, &sc); err != nil { + return sc, err + } + return sc, nil default: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/newtmgr/config/ble_config.go ---------------------------------------------------------------------- diff --git a/newtmgr/config/ble_config.go b/newtmgr/config/ble_config.go index d501a71..52eb4f2 100644 --- a/newtmgr/config/ble_config.go +++ b/newtmgr/config/ble_config.go @@ -104,7 +104,7 @@ func ParseBleConnString(cs string) (*BleConfig, error) { return bc, nil } -func FillSesnCfg(bc *BleConfig, sc *sesn.SesnCfg) { +func FillSesnCfg(bx *nmble.BleXport, bc *BleConfig, sc *sesn.SesnCfg) error { sc.Ble.OwnAddrType = bc.OwnAddrType if nmutil.DeviceName != "" { @@ -112,16 +112,32 @@ func FillSesnCfg(bc *BleConfig, sc *sesn.SesnCfg) { } if bc.PeerName != "" { - sc.Ble.PeerSpec = sesn.BlePeerSpecName(bc.PeerName) + scanPred := func(r bledefs.BleAdvReport) bool { + return r.Name == bc.PeerName + } + dev, err := nmble.DiscoverDevice( + bx, bc.OwnAddrType, 15*time.Second, scanPred) + + if err != nil { + return err + } + if dev == nil { + return util.FmtNewtError( + "Unable to discover device with name \"%s\"", bc.PeerName) + } + + sc.PeerSpec.Ble = *dev } else { - sc.Ble.PeerSpec = sesn.BlePeerSpecDev(bledefs.BleDev{ + sc.PeerSpec.Ble = bledefs.BleDev{ AddrType: bc.PeerAddrType, Addr: bc.PeerAddr, - }) + } } // We don't need to stick around until a connection closes. sc.Ble.CloseTimeout = 10000 * time.Millisecond + + return nil } func BuildBleXport(bc *BleConfig) (*nmble.BleXport, error) { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/bledefs/bledefs.go ---------------------------------------------------------------------- diff --git a/nmxact/bledefs/bledefs.go b/nmxact/bledefs/bledefs.go index e6f620e..4e850a5 100644 --- a/nmxact/bledefs/bledefs.go +++ b/nmxact/bledefs/bledefs.go @@ -398,6 +398,7 @@ type BleAdvReport struct { MfgData []byte // false if not present } +type BleAdvRptFn func(r BleAdvReport) type BleAdvPredicate func(adv BleAdvReport) bool type BleConnDesc struct { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/example/ble_loop/ble_loop.go ---------------------------------------------------------------------- diff --git a/nmxact/example/ble_loop/ble_loop.go b/nmxact/example/ble_loop/ble_loop.go index 7c6140a..7a930a9 100644 --- a/nmxact/example/ble_loop/ble_loop.go +++ b/nmxact/example/ble_loop/ble_loop.go @@ -24,6 +24,7 @@ import ( "os" "os/signal" "syscall" + "time" "mynewt.apache.org/newtmgr/nmxact/bledefs" "mynewt.apache.org/newtmgr/nmxact/nmble" @@ -78,14 +79,27 @@ func main() { } defer x.Stop() + // Find a device to connect to: + // * Peer has name "nimble-bleprph" + // * We use a random address. + dev, err := nmble.DiscoverDeviceWithName( + x, bledefs.BLE_ADDR_TYPE_RANDOM, 10*time.Second, "nimble-bleprph") + if err != nil { + fmt.Fprintf(os.Stderr, "error discovering device: %s\n", err.Error()) + os.Exit(1) + } + if dev == nil { + fmt.Fprintf(os.Stderr, "couldn't find device") + os.Exit(1) + } + // Prepare a BLE session: // * Plain NMP (not tunnelled over OIC). // * We use a random address. - // * Peer has name "nimble-bleprph". sc := sesn.NewSesnCfg() sc.MgmtProto = sesn.MGMT_PROTO_NMP sc.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM - sc.Ble.PeerSpec = sesn.BlePeerSpecName("nimble-bleprph") + sc.PeerSpec.Ble = *dev s, err := x.BuildSesn(sc) if err != nil { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/example/ble_plain/ble_plain.go ---------------------------------------------------------------------- diff --git a/nmxact/example/ble_plain/ble_plain.go b/nmxact/example/ble_plain/ble_plain.go index a91285c..3872dec 100644 --- a/nmxact/example/ble_plain/ble_plain.go +++ b/nmxact/example/ble_plain/ble_plain.go @@ -22,6 +22,7 @@ package main import ( "fmt" "os" + "time" "mynewt.apache.org/newtmgr/nmxact/bledefs" "mynewt.apache.org/newtmgr/nmxact/nmble" @@ -51,14 +52,27 @@ func main() { } defer x.Stop() + // Find a device to connect to: + // * Peer has name "nimble-bleprph" + // * We use a random address. + dev, err := nmble.DiscoverDeviceWithName( + x, bledefs.BLE_ADDR_TYPE_RANDOM, 10*time.Second, "nimble-bleprph") + if err != nil { + fmt.Fprintf(os.Stderr, "error discovering device: %s\n", err.Error()) + os.Exit(1) + } + if dev == nil { + fmt.Fprintf(os.Stderr, "couldn't find device") + os.Exit(1) + } + // Prepare a BLE session: // * Plain NMP (not tunnelled over OIC). // * We use a random address. - // * Peer has name "nimble-bleprph". sc := sesn.NewSesnCfg() sc.MgmtProto = sesn.MGMT_PROTO_NMP sc.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM - sc.Ble.PeerSpec = sesn.BlePeerSpecName("nimble-bleprph") + sc.PeerSpec.Ble = *dev s, err := x.BuildSesn(sc) if err != nil { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/example/ble_scan/ble_scan.go ---------------------------------------------------------------------- diff --git a/nmxact/example/ble_scan/ble_scan.go b/nmxact/example/ble_scan/ble_scan.go index d9f794c..4ac80e1 100644 --- a/nmxact/example/ble_scan/ble_scan.go +++ b/nmxact/example/ble_scan/ble_scan.go @@ -105,6 +105,9 @@ func main() { for { sc := scan.BleOmpScanCfg(scanCb) + sc.Ble.ScanPred = func(adv bledefs.BleAdvReport) bool { + return adv.Name == "ccollins" + } if err := scanner.Start(sc); err != nil { fmt.Fprintf(os.Stderr, "error starting scan: %s\n", err.Error()) os.Exit(1) @@ -122,7 +125,7 @@ func main() { c := sesn.NewSesnCfg() c.MgmtProto = sesn.MGMT_PROTO_OMP c.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM - c.Ble.PeerSpec = sesn.BlePeerSpecDev(p.Opaque.(bledefs.BleDev)) + c.PeerSpec = p.PeerSpec s, err := x.BuildSesn(c) if err != nil { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/ble_act.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/ble_act.go b/nmxact/nmble/ble_act.go index ed129cd..95a6436 100644 --- a/nmxact/nmble/ble_act.go +++ b/nmxact/nmble/ble_act.go @@ -285,11 +285,8 @@ func exchangeMtu(x *BleXport, bl *BleListener, r *BleExchangeMtuReq) ( } } -type scanSuccessFn func() -type advRptFn func(r BleAdvReport) - func actScan(x *BleXport, bl *BleListener, r *BleScanReq, - abortChan chan struct{}, advRptCb advRptFn) error { + abortChan chan struct{}, advRptCb BleAdvRptFn) error { const rspType = MSG_TYPE_SCAN http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/ble_fsm.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/ble_fsm.go b/nmxact/nmble/ble_fsm.go index ed7b90b..f8a893a 100644 --- a/nmxact/nmble/ble_fsm.go +++ b/nmxact/nmble/ble_fsm.go @@ -14,7 +14,6 @@ import ( . "mynewt.apache.org/newtmgr/nmxact/bledefs" "mynewt.apache.org/newtmgr/nmxact/nmp" "mynewt.apache.org/newtmgr/nmxact/nmxutil" - "mynewt.apache.org/newtmgr/nmxact/sesn" ) var nextId uint32 @@ -29,16 +28,15 @@ type BleSesnState int32 const ( SESN_STATE_UNCONNECTED BleSesnState = 0 - SESN_STATE_SCANNING = 1 - SESN_STATE_CONNECTING = 2 - SESN_STATE_EXCHANGE_MTU = 3 - SESN_STATE_DISCOVER_SVC = 4 - SESN_STATE_DISCOVER_CHR = 5 - SESN_STATE_SECURITY = 6 - SESN_STATE_SUBSCRIBE = 7 - SESN_STATE_DONE = 8 - SESN_STATE_TERMINATING = 9 - SESN_STATE_CONN_CANCELLING = 10 + 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_DONE = 7 + SESN_STATE_TERMINATING = 8 + SESN_STATE_CONN_CANCELLING = 9 ) type BleFsmDisconnectType int @@ -61,20 +59,19 @@ type BleDisconnectFn func(dt BleFsmDisconnectType, peer BleDev, err error) type BleFsmParams struct { Bx *BleXport OwnAddrType BleAddrType - PeerSpec sesn.BlePeerSpec + PeerDev BleDev ConnTries int SvcUuid BleUuid ReqChrUuid BleUuid RspChrUuid BleUuid RxNmpCb BleRxNmpFn DisconnectCb BleDisconnectFn - Encrypt BleEncryptWhen + EncryptWhen BleEncryptWhen } type BleFsm struct { params BleFsmParams - peerDev *BleDev connHandle uint16 nmpSvc *BleSvc nmpReqChr *BleChr @@ -114,7 +111,8 @@ func NewBleFsm(p BleFsmParams) *BleFsm { 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) + ErrCodeToString(reason), reason, bf.params.PeerDev.String(), + bf.connHandle) return nmxutil.NewBleSesnDisconnectError(reason, str) } @@ -269,22 +267,16 @@ func (bf *BleFsm) processErr(err error) { // Remember some fields before we clear them. dt := calcDisconnectType(bf.getState()) - var peer BleDev - if bf.peerDev != nil { - peer = *bf.peerDev - } - bf.params.Bx.StopWaitingForMaster(bf, err) bf.errorAll(err) bf.setState(SESN_STATE_UNCONNECTED) - bf.peerDev = nil // Wait for all listeners to get removed. bf.wg.Wait() bf.errFunnel.Reset() - bf.params.DisconnectCb(dt, peer, err) + bf.params.DisconnectCb(dt, bf.params.PeerDev, err) } func (bf *BleFsm) connectListen(seq BleSeq) error { @@ -312,7 +304,7 @@ func (bf *BleFsm) connectListen(seq BleSeq) error { str := fmt.Sprintf("BLE connection attempt failed; "+ "status=%s (%d) peer=%s", ErrCodeToString(msg.Status), msg.Status, - bf.peerDev.String()) + bf.params.PeerDev.String()) log.Debugf(str) err := nmxutil.NewBleHostError(msg.Status, str) bf.connChan <- err @@ -337,7 +329,7 @@ func (bf *BleFsm) connectListen(seq BleSeq) error { str := fmt.Sprintf("BLE connection attempt failed; "+ "status=%s (%d) peer=%s", ErrCodeToString(msg.Status), msg.Status, - bf.peerDev.String()) + bf.params.PeerDev.String()) log.Debugf(str) err := nmxutil.NewBleHostError(msg.Status, str) bf.connChan <- err @@ -430,8 +422,8 @@ func (bf *BleFsm) nmpRspListen() error { func (bf *BleFsm) connect() error { r := NewBleConnectReq() r.OwnAddrType = bf.params.OwnAddrType - r.PeerAddrType = bf.peerDev.AddrType - r.PeerAddr = bf.peerDev.Addr + r.PeerAddrType = bf.params.PeerDev.AddrType + r.PeerAddr = bf.params.PeerDev.Addr // Initiating a connection requires dedicated master privileges. if err := bf.params.Bx.AcquireMaster(bf); err != nil { @@ -470,68 +462,6 @@ func (bf *BleFsm) connect() error { return err } -func (bf *BleFsm) scan() error { - r := NewBleScanReq() - r.OwnAddrType = bf.params.OwnAddrType - r.DurationMs = 15000 - r.FilterPolicy = BLE_SCAN_FILT_NO_WL - r.Limited = false - r.Passive = false - r.FilterDuplicates = true - - // Scanning requires dedicated master privileges. - if err := bf.params.Bx.AcquireMaster(bf); err != nil { - return err - } - defer bf.params.Bx.ReleaseMaster() - - bl, err := bf.addBleSeqListener("scan", r.Seq) - if err != nil { - return err - } - defer bf.removeBleSeqListener("scan", r.Seq) - - abortChan := make(chan struct{}, 1) - - // This function gets called for each incoming advertisement. - advRptCb := func(r BleAdvReport) { - // Ask client if we should connect to this advertiser. - if bf.params.PeerSpec.ScanPred(r) { - bf.peerDev = &r.Sender - abortChan <- struct{}{} - } - } - - err = actScan(bf.params.Bx, bl, r, abortChan, advRptCb) - if !nmxutil.IsXport(err) { - // The transport did not restart; always attempt to cancel the scan - // operation. In most cases, the host has already stopped scanning - // and will respond with an "ealready" error that can be ignored. - if err := bf.scanCancel(); err != nil { - log.Errorf("Failed to cancel scan in progress: %s", - err.Error()) - } - } - - return err -} - -func (bf *BleFsm) scanCancel() error { - r := NewBleScanCancelReq() - - bl, err := bf.addBleSeqListener("scan-cancel", r.Seq) - if err != nil { - return err - } - defer bf.removeBleSeqListener("scan-cancel", r.Seq) - - if err := scanCancel(bf.params.Bx, bl, r); err != nil { - return err - } - - return nil -} - func (bf *BleFsm) terminateSetState() error { switch bf.getState() { case SESN_STATE_UNCONNECTED, @@ -724,7 +654,7 @@ func (bf *BleFsm) subscribe() error { } func (bf *BleFsm) shouldEncrypt() bool { - switch bf.params.Encrypt { + switch bf.params.EncryptWhen { case BLE_ENCRYPT_NEVER: return false @@ -732,56 +662,22 @@ func (bf *BleFsm) shouldEncrypt() bool { return true case BLE_ENCRYPT_PRIV_ONLY: - return bf.peerDev.AddrType == BLE_ADDR_TYPE_RPA_PUB || - bf.peerDev.AddrType == BLE_ADDR_TYPE_RPA_RND + return bf.params.PeerDev.AddrType == BLE_ADDR_TYPE_RPA_PUB || + bf.params.PeerDev.AddrType == BLE_ADDR_TYPE_RPA_RND default: panic(fmt.Sprintf("Invalid BleEncryptWhen value: %d", - bf.params.Encrypt)) + bf.params.EncryptWhen)) } } -// Tries to populate the FSM's peerDev field. This function succeeds if the -// client specified the address of the peer to connect to. -func (bf *BleFsm) tryFillPeerDev() bool { - // The peer spec contains one of: - // * Peer address; - // * Predicate function to call during scanning. - // If a peer address is specified, fill in the peer field now so the - // scanning step can be skipped. Otherwise, the peer field gets populated - // during scanning. - if bf.params.PeerSpec.ScanPred == nil { - bf.peerDev = &bf.params.PeerSpec.Dev - return true - } - - return false -} - func (bf *BleFsm) executeState() (bool, error) { switch bf.getState() { case SESN_STATE_UNCONNECTED: - // Determine if we can immediately initiate a connection, or if we - // need to scan for a peer first. If the client specified a peer - // address, or if we have already successfully scanned, we initiate - // a connection now. Otherwise, we need to scan to determine which - // peer meets the specified scan criteria. - bf.tryFillPeerDev() - if bf.peerDev == nil { - // Peer not inferred yet. Initiate scan. - bf.setState(SESN_STATE_SCANNING) - if err := bf.scan(); err != nil { - return false, err - } - bf.setState(SESN_STATE_UNCONNECTED) - } else { - // We already know the address we want to connect to. Initiate - // a connection. - if err := bf.connect(); err != nil { - return false, err - } - bf.setState(SESN_STATE_EXCHANGE_MTU) + if err := bf.connect(); err != nil { + return false, err } + bf.setState(SESN_STATE_EXCHANGE_MTU) case SESN_STATE_EXCHANGE_MTU: if err := bf.exchangeMtu(); err != nil { @@ -885,10 +781,6 @@ func (bf *BleFsm) Stop() (bool, error) { bf.errFunnel.Insert(fmt.Errorf("Connection attempt cancelled")) return false, nil - case SESN_STATE_SCANNING: - bf.errFunnel.Insert(fmt.Errorf("Scan cancelled")) - return false, nil - default: if err := bf.terminate(); err != nil { return false, err @@ -928,7 +820,7 @@ func (bf *BleFsm) TxNmp(payload []byte, nl *nmp.NmpListener, msg := fmt.Sprintf( "NMP timeout; op=%d group=%d id=%d seq=%d peer=%#v", payload[0], payload[4]+payload[5]<<8, - payload[7], payload[6], bf.peerDev) + payload[7], payload[6], bf.params.PeerDev) return nil, nmxutil.NewNmpTimeoutError(msg) } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/ble_oic_sesn.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/ble_oic_sesn.go b/nmxact/nmble/ble_oic_sesn.go index 6cac7cf..c50f19d 100644 --- a/nmxact/nmble/ble_oic_sesn.go +++ b/nmxact/nmble/ble_oic_sesn.go @@ -20,7 +20,7 @@ type BleOicSesn struct { nls map[*nmp.NmpListener]struct{} od *omp.OmpDispatcher closeTimeout time.Duration - onCloseCb sesn.BleOnCloseFn + onCloseCb sesn.OnCloseFn closeChan chan error mtx sync.Mutex @@ -31,7 +31,7 @@ func NewBleOicSesn(bx *BleXport, cfg sesn.SesnCfg) *BleOicSesn { nls: map[*nmp.NmpListener]struct{}{}, od: omp.NewOmpDispatcher(), closeTimeout: cfg.Ble.CloseTimeout, - onCloseCb: cfg.Ble.OnCloseCb, + onCloseCb: cfg.OnCloseCb, } svcUuid := BleUuid{Uuid16: OmpSvcUuid} @@ -49,12 +49,12 @@ func NewBleOicSesn(bx *BleXport, cfg sesn.SesnCfg) *BleOicSesn { bos.bf = NewBleFsm(BleFsmParams{ Bx: bx, OwnAddrType: cfg.Ble.OwnAddrType, - PeerSpec: cfg.Ble.PeerSpec, + PeerDev: cfg.PeerSpec.Ble, ConnTries: cfg.Ble.ConnTries, SvcUuid: svcUuid, ReqChrUuid: reqChrUuid, RspChrUuid: rspChrUuid, - Encrypt: cfg.Ble.Encrypt, + EncryptWhen: cfg.Ble.EncryptWhen, RxNmpCb: func(d []byte) { bos.onRxNmp(d) }, DisconnectCb: func(dt BleFsmDisconnectType, p BleDev, e error) { bos.onDisconnect(dt, p, e) @@ -198,7 +198,7 @@ func (bos *BleOicSesn) onDisconnect(dt BleFsmDisconnectType, peer BleDev, // Only execute client's disconnect callback if the disconnect was // unsolicited and the session was fully open. if dt == FSM_DISCONNECT_TYPE_OPENED && bos.onCloseCb != nil { - bos.onCloseCb(bos, peer, err) + bos.onCloseCb(bos, err) } } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/ble_plain_sesn.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/ble_plain_sesn.go b/nmxact/nmble/ble_plain_sesn.go index f2048c3..ac56dee 100644 --- a/nmxact/nmble/ble_plain_sesn.go +++ b/nmxact/nmble/ble_plain_sesn.go @@ -19,7 +19,7 @@ type BlePlainSesn struct { nls map[*nmp.NmpListener]struct{} nd *nmp.NmpDispatcher closeTimeout time.Duration - onCloseCb sesn.BleOnCloseFn + onCloseCb sesn.OnCloseFn closeChan chan error mtx sync.Mutex @@ -30,7 +30,7 @@ func NewBlePlainSesn(bx *BleXport, cfg sesn.SesnCfg) *BlePlainSesn { nls: map[*nmp.NmpListener]struct{}{}, nd: nmp.NewNmpDispatcher(), closeTimeout: cfg.Ble.CloseTimeout, - onCloseCb: cfg.Ble.OnCloseCb, + onCloseCb: cfg.OnCloseCb, } svcUuid, err := ParseUuid(NmpPlainSvcUuid) @@ -46,11 +46,12 @@ func NewBlePlainSesn(bx *BleXport, cfg sesn.SesnCfg) *BlePlainSesn { bps.bf = NewBleFsm(BleFsmParams{ Bx: bx, OwnAddrType: cfg.Ble.OwnAddrType, - PeerSpec: cfg.Ble.PeerSpec, + PeerDev: cfg.PeerSpec.Ble, ConnTries: cfg.Ble.ConnTries, SvcUuid: svcUuid, ReqChrUuid: chrUuid, RspChrUuid: chrUuid, + EncryptWhen: cfg.Ble.EncryptWhen, RxNmpCb: func(d []byte) { bps.onRxNmp(d) }, DisconnectCb: func(dt BleFsmDisconnectType, p BleDev, e error) { bps.onDisconnect(dt, p, e) @@ -178,7 +179,7 @@ func (bps *BlePlainSesn) onDisconnect(dt BleFsmDisconnectType, peer BleDev, // Only execute client's disconnect callback if the disconnect was // unsolicited and the session was fully open. if dt == FSM_DISCONNECT_TYPE_OPENED && bps.onCloseCb != nil { - bps.onCloseCb(bps, peer, err) + bps.onCloseCb(bps, err) } } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/ble_scanner.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/ble_scanner.go b/nmxact/nmble/ble_scanner.go index 28605df..338b7cd 100644 --- a/nmxact/nmble/ble_scanner.go +++ b/nmxact/nmble/ble_scanner.go @@ -1,16 +1,16 @@ package nmble import ( - "bytes" - "encoding/base64" "fmt" "sync" + "time" log "github.com/Sirupsen/logrus" . "mynewt.apache.org/newtmgr/nmxact/bledefs" "mynewt.apache.org/newtmgr/nmxact/omp" "mynewt.apache.org/newtmgr/nmxact/scan" + "mynewt.apache.org/newtmgr/nmxact/sesn" "mynewt.apache.org/newtmgr/nmxact/xact" ) @@ -19,7 +19,8 @@ type BleScanner struct { cfg scan.Cfg bx *BleXport - reportedDevs map[BleDev][]byte + discoverer *Discoverer + reportedDevs map[BleDev]string bos *BleOicSesn od *omp.OmpDispatcher enabled bool @@ -31,54 +32,101 @@ type BleScanner struct { func NewBleScanner(bx *BleXport) *BleScanner { return &BleScanner{ bx: bx, - reportedDevs: map[BleDev][]byte{}, + reportedDevs: map[BleDev]string{}, } } -func (s *BleScanner) scan() (scan.ScanPeer, error) { - if err := s.bos.Open(); err != nil { - return scan.ScanPeer{}, err +func (s *BleScanner) discover() (*BleDev, error) { + s.discoverer = NewDiscoverer(DiscovererParams{ + Bx: s.bx, + OwnAddrType: s.cfg.SesnCfg.Ble.OwnAddrType, + Passive: false, + Duration: 15 * time.Second, + }) + defer func() { s.discoverer = nil }() + + var dev *BleDev + advRptCb := func(r BleAdvReport) { + if s.cfg.Ble.ScanPred(r) { + dev = &r.Sender + s.discoverer.Stop() + } + } + if err := s.discoverer.Start(advRptCb); err != nil { + return nil, err } - defer s.bos.Close() - // Now we are connected (and paired if required). Read the peer's hardware - // ID and report it upstream. + return dev, nil +} - desc, err := s.bos.ConnInfo() +func (s *BleScanner) connect(dev BleDev) error { + s.cfg.SesnCfg.PeerSpec.Ble = dev + session, err := s.bx.BuildSesn(s.cfg.SesnCfg) if err != nil { - return scan.ScanPeer{}, err + return err + } + s.bos = session.(*BleOicSesn) + + if err := s.bos.Open(); err != nil { + return err } + return nil +} + +func (s *BleScanner) readHwId() (string, error) { c := xact.NewConfigReadCmd() c.Name = "id/hwid" res, err := c.Run(s.bos) if err != nil { - return scan.ScanPeer{}, err + return "", err } if res.Status() != 0 { - return scan.ScanPeer{}, - fmt.Errorf("failed to read hardware ID; NMP status=%d", + return "", + fmt.Errorf("failed to read hardware ID; NMP status=%discoverer", res.Status()) } cres := res.(*xact.ConfigReadResult) + return cres.Rsp.Val, nil +} - rawId, err := base64.StdEncoding.DecodeString(cres.Rsp.Val) +func (s *BleScanner) scan() (*scan.ScanPeer, error) { + // Discover the first device which matches the specified predicate. + dev, err := s.discover() if err != nil { - return scan.ScanPeer{}, - fmt.Errorf("failed to decode hardware ID; undecoded=%s", - cres.Rsp.Val) + return nil, err + } + if dev == nil { + return nil, nil + } + + s.connect(*dev) + defer s.bos.Close() + + // Now we are connected (and paired if required). Read the peer's hardware + // ID and report it upstream. + hwId, err := s.readHwId() + if err != nil { + return nil, err + } + + desc, err := s.bos.ConnInfo() + if err != nil { + return nil, err } peer := scan.ScanPeer{ - HwId: rawId, - Opaque: BleDev{ - AddrType: desc.PeerIdAddrType, - Addr: desc.PeerIdAddr, + HwId: hwId, + PeerSpec: sesn.PeerSpec{ + Ble: BleDev{ + AddrType: desc.PeerIdAddrType, + Addr: desc.PeerIdAddr, + }, }, } - return peer, nil + return &peer, nil } func (s *BleScanner) Start(cfg scan.Cfg) error { @@ -87,11 +135,11 @@ func (s *BleScanner) Start(cfg scan.Cfg) error { } // Wrap predicate with logic that discards duplicates. - innerPred := cfg.SesnCfg.Ble.PeerSpec.ScanPred - cfg.SesnCfg.Ble.PeerSpec.ScanPred = func(adv BleAdvReport) bool { + innerPred := cfg.Ble.ScanPred + cfg.Ble.ScanPred = func(adv BleAdvReport) bool { // Filter devices that have already been reported. s.devMapMtx.Lock() - seen := s.reportedDevs[adv.Sender] != nil + seen := s.reportedDevs[adv.Sender] != "" s.devMapMtx.Unlock() if seen { @@ -101,14 +149,8 @@ func (s *BleScanner) Start(cfg scan.Cfg) error { } } - session, err := s.bx.BuildSesn(cfg.SesnCfg) - if err != nil { - return err - } - s.enabled = true s.cfg = cfg - s.bos = session.(*BleOicSesn) // Start background scanning. go func() { @@ -116,12 +158,12 @@ func (s *BleScanner) Start(cfg scan.Cfg) error { p, err := s.scan() if err != nil { log.Debugf("Scan error: %s", err.Error()) - } else { + } else if p != nil { s.devMapMtx.Lock() - s.reportedDevs[p.Opaque.(BleDev)] = p.HwId + s.reportedDevs[p.PeerSpec.Ble] = p.HwId s.devMapMtx.Unlock() - s.cfg.ScanCb(p) + s.cfg.ScanCb(*p) } } }() @@ -135,6 +177,10 @@ func (s *BleScanner) Stop() error { } s.enabled = false + discoverer := s.discoverer + if discoverer != nil { + discoverer.Stop() + } s.bos.Close() return nil } @@ -142,13 +188,13 @@ func (s *BleScanner) Stop() error { // @return true if the specified device was found and // forgetten; // false if the specified device is unknown. -func (s *BleScanner) ForgetDevice(hwid []byte) bool { +func (s *BleScanner) ForgetDevice(hwId string) bool { s.devMapMtx.Lock() defer s.devMapMtx.Unlock() - for d, h := range s.reportedDevs { - if bytes.Compare(h, hwid) == 0 { - delete(s.reportedDevs, d) + for discoverer, h := range s.reportedDevs { + if h == hwId { + delete(s.reportedDevs, discoverer) return true } } @@ -160,7 +206,7 @@ func (s *BleScanner) ForgetAllDevices() { s.devMapMtx.Lock() defer s.devMapMtx.Unlock() - for d, _ := range s.reportedDevs { - delete(s.reportedDevs, d) + for discoverer, _ := range s.reportedDevs { + delete(s.reportedDevs, discoverer) } } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/ble_util.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/ble_util.go b/nmxact/nmble/ble_util.go index fc5fe27..8dccb39 100644 --- a/nmxact/nmble/ble_util.go +++ b/nmxact/nmble/ble_util.go @@ -4,6 +4,7 @@ import ( "fmt" "runtime" "sync" + "time" log "github.com/Sirupsen/logrus" @@ -349,3 +350,16 @@ func ResetXact(x *BleXport) error { return reset(x, bl, r) } + +func DiscoverDeviceWithName( + bx *BleXport, + ownAddrType BleAddrType, + timeout time.Duration, + name string) (*BleDev, error) { + + advPred := func(r BleAdvReport) bool { + return r.Name == name + } + + return DiscoverDevice(bx, ownAddrType, timeout, advPred) +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/nmble/discover.go ---------------------------------------------------------------------- diff --git a/nmxact/nmble/discover.go b/nmxact/nmble/discover.go new file mode 100644 index 0000000..7650044 --- /dev/null +++ b/nmxact/nmble/discover.go @@ -0,0 +1,142 @@ +package nmble + +import ( + "fmt" + "time" + + log "github.com/Sirupsen/logrus" + + . "mynewt.apache.org/newtmgr/nmxact/bledefs" + "mynewt.apache.org/newtmgr/nmxact/nmxutil" +) + +type DiscovererParams struct { + Bx *BleXport + OwnAddrType BleAddrType + Passive bool + Duration time.Duration +} + +// Listens for advertisements; reports the ones that match the specified +// predicate. +type Discoverer struct { + params DiscovererParams + abortChan chan struct{} +} + +func NewDiscoverer(params DiscovererParams) *Discoverer { + return &Discoverer{ + params: params, + } +} + +func (d *Discoverer) scanCancel() error { + r := NewBleScanCancelReq() + + base := BleMsgBase{ + Op: -1, + Type: -1, + Seq: r.Seq, + ConnHandle: -1, + } + + bl := NewBleListener() + if err := d.params.Bx.Bd.AddListener(base, bl); err != nil { + return err + } + defer d.params.Bx.Bd.RemoveListener(base) + + if err := scanCancel(d.params.Bx, bl, r); err != nil { + return err + } + + return nil +} + +func (d *Discoverer) Start(advRptCb BleAdvRptFn) error { + // Scanning requires dedicated master privileges. + if err := d.params.Bx.AcquireMaster(d); err != nil { + return err + } + defer d.params.Bx.ReleaseMaster() + + r := NewBleScanReq() + r.OwnAddrType = d.params.OwnAddrType + r.DurationMs = int(d.params.Duration / time.Millisecond) + r.FilterPolicy = BLE_SCAN_FILT_NO_WL + r.Limited = false + r.Passive = d.params.Passive + r.FilterDuplicates = true + + base := BleMsgBase{ + Op: -1, + Type: -1, + Seq: r.Seq, + ConnHandle: -1, + } + + bl := NewBleListener() + if err := d.params.Bx.Bd.AddListener(base, bl); err != nil { + return err + } + defer d.params.Bx.Bd.RemoveListener(base) + + d.abortChan = make(chan struct{}, 1) + defer func() { d.abortChan = nil }() + + err := actScan(d.params.Bx, bl, r, d.abortChan, advRptCb) + if !nmxutil.IsXport(err) { + // The transport did not restart; always attempt to cancel the scan + // operation. In some cases, the host has already stopped scanning + // and will respond with an "ealready" error that can be ignored. + if err := d.scanCancel(); err != nil { + log.Errorf("Failed to cancel scan in progress: %s", + err.Error()) + } + } + + return err +} + +func (d *Discoverer) Stop() error { + ch := d.abortChan + + if ch == nil { + return fmt.Errorf("Attempt to stop inactive discoverer") + } + + ch <- struct{}{} + return nil +} + +// Discovers a single device. After a device is successfully discovered, +// discovery is stopped. +func DiscoverDevice( + bx *BleXport, + ownAddrType BleAddrType, + duration time.Duration, + advPred BleAdvPredicate) (*BleDev, error) { + + d := NewDiscoverer(DiscovererParams{ + Bx: bx, + OwnAddrType: ownAddrType, + Passive: false, + Duration: duration, + }) + + var dev *BleDev + advRptCb := func(adv BleAdvReport) { + if advPred(adv) { + dev = &adv.Sender + d.Stop() + } + } + + if err := d.Start(advRptCb); err != nil { + if !nmxutil.IsScanTmo(err) { + return nil, err + } + } + + return dev, nil +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/scan/scan.go ---------------------------------------------------------------------- diff --git a/nmxact/scan/scan.go b/nmxact/scan/scan.go index cadf5f9..a7f67ec 100644 --- a/nmxact/scan/scan.go +++ b/nmxact/scan/scan.go @@ -6,15 +6,23 @@ import ( ) type ScanPeer struct { - HwId []byte - Opaque interface{} + HwId string + PeerSpec sesn.PeerSpec } type ScanFn func(peer ScanPeer) +type CfgBle struct { + ScanPred bledefs.BleAdvPredicate +} + type Cfg struct { + // General configuration. ScanCb ScanFn SesnCfg sesn.SesnCfg + + // Transport-specific configuration. + Ble CfgBle } type Scanner interface { @@ -28,23 +36,22 @@ func BleOmpScanCfg(ScanCb ScanFn) Cfg { sc := sesn.NewSesnCfg() sc.MgmtProto = sesn.MGMT_PROTO_OMP sc.Ble.OwnAddrType = bledefs.BLE_ADDR_TYPE_RANDOM - sc.Ble.Encrypt = bledefs.BLE_ENCRYPT_PRIV_ONLY - sc.Ble.PeerSpec = sesn.BlePeerSpec{ - ScanPred: func(adv bledefs.BleAdvReport) bool { - for _, u := range adv.Uuids16 { - if u == bledefs.OmpSvcUuid { - return true - } - } - - return false - }, - } + sc.Ble.EncryptWhen = bledefs.BLE_ENCRYPT_PRIV_ONLY cfg := Cfg{ ScanCb: ScanCb, SesnCfg: sc, - } + Ble: CfgBle{ + ScanPred: func(adv bledefs.BleAdvReport) bool { + for _, u := range adv.Uuids16 { + if u == bledefs.OmpSvcUuid { + return true + } + } + return false + }, + }, + } return cfg } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newtmgr/blob/99af734d/nmxact/sesn/sesn_cfg.go ---------------------------------------------------------------------- diff --git a/nmxact/sesn/sesn_cfg.go b/nmxact/sesn/sesn_cfg.go index 0022640..042d8fd 100644 --- a/nmxact/sesn/sesn_cfg.go +++ b/nmxact/sesn/sesn_cfg.go @@ -13,55 +13,37 @@ const ( MGMT_PROTO_OMP ) -type BleOnCloseFn func(s Sesn, peer bledefs.BleDev, err error) +type OnCloseFn func(s Sesn, err error) -// Specifies the BLE peer to connect to. -type BlePeerSpec struct { - // This is filled in if you know the address of the peer to connect to. - Dev bledefs.BleDev - - // Otherwise, we must scan for a peer to connect to. This points to a - // function that indicates whether we should connect to the sender of the - // specified advertisement. This function gets called each time an - // incoming advertisement is received. If it returns true, the session - // will connect to the sender of the corresponding advertisement. Set this - // to nil if you populate the Dev field. - ScanPred bledefs.BleAdvPredicate -} - -func BlePeerSpecDev(dev bledefs.BleDev) BlePeerSpec { - return BlePeerSpec{Dev: dev} -} - -func BlePeerSpecName(name string) BlePeerSpec { - return BlePeerSpec{ - ScanPred: func(r bledefs.BleAdvReport) bool { - return r.Name == name - }, - } +type PeerSpec struct { + Ble bledefs.BleDev } type SesnCfgBle struct { OwnAddrType bledefs.BleAddrType - PeerSpec BlePeerSpec ConnTries int CloseTimeout time.Duration - OnCloseCb BleOnCloseFn - Encrypt bledefs.BleEncryptWhen + EncryptWhen bledefs.BleEncryptWhen } type SesnCfg struct { - // Used with all transport types. + // General configuration. MgmtProto MgmtProto + PeerSpec PeerSpec + OnCloseCb OnCloseFn - // Only used with BLE transports. + // Transport-specific configuration. Ble SesnCfgBle } func NewSesnCfg() SesnCfg { return SesnCfg{ + // XXX: For now, assume an own address type of random static. In the + // future, there will need to be some global default, or something that + // gets read from blehostd. Ble: SesnCfgBle{ + OwnAddrType: bledefs.BLE_ADDR_TYPE_RANDOM, ConnTries: 3, CloseTimeout: 30 * time.Second, },