blob: a0d916ed742750878bb6d066b3fc605500c1e0fa [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package nmble
import (
"fmt"
log "github.com/Sirupsen/logrus"
. "mynewt.apache.org/newtmgr/nmxact/bledefs"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
"mynewt.apache.org/newtmgr/nmxact/sesn"
)
type AdvertiseCfg struct {
// Mandatory
OwnAddrType BleAddrType
ConnMode BleAdvConnMode
DiscMode BleAdvDiscMode
ItvlMin uint16
ItvlMax uint16
ChannelMap uint8
FilterPolicy BleAdvFilterPolicy
HighDutyCycle bool
AdvFields BleAdvFields
RspFields BleAdvFields
SesnCfg sesn.SesnCfg
// Only required for direct advertisements
PeerAddr *BleAddr
}
func NewAdvertiseCfg() AdvertiseCfg {
return AdvertiseCfg{
OwnAddrType: BLE_ADDR_TYPE_RANDOM,
ConnMode: BLE_ADV_CONN_MODE_UND,
DiscMode: BLE_ADV_DISC_MODE_GEN,
}
}
type Advertiser struct {
bx *BleXport
stopChan chan struct{}
stoppedChan chan struct{}
}
func NewAdvertiser(bx *BleXport) *Advertiser {
return &Advertiser{
bx: bx,
}
}
func (a *Advertiser) fields(f BleAdvFields) ([]byte, error) {
r := BleAdvFieldsToReq(f)
bl, err := a.bx.AddListener(SeqKey(r.Seq))
if err != nil {
return nil, err
}
defer a.bx.RemoveListener(bl)
return advFields(a.bx, bl, r)
}
func (a *Advertiser) setAdvData(data []byte) error {
r := NewBleAdvSetDataReq()
r.Data = BleBytes{data}
bl, err := a.bx.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer a.bx.RemoveListener(bl)
if err := advSetData(a.bx, bl, r); err != nil {
return err
}
return nil
}
func (a *Advertiser) setRspData(data []byte) error {
r := NewBleAdvRspSetDataReq()
r.Data = BleBytes{data}
bl, err := a.bx.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer a.bx.RemoveListener(bl)
if err := advRspSetData(a.bx, bl, r); err != nil {
return err
}
return nil
}
func (a *Advertiser) advertise(cfg AdvertiseCfg) (uint16, *Listener, error) {
r := NewBleAdvStartReq()
r.OwnAddrType = cfg.OwnAddrType
r.DurationMs = 0x7fffffff
r.ConnMode = cfg.ConnMode
r.DiscMode = cfg.DiscMode
r.ItvlMin = cfg.ItvlMin
r.ItvlMax = cfg.ItvlMax
r.ChannelMap = cfg.ChannelMap
r.FilterPolicy = cfg.FilterPolicy
r.HighDutyCycle = cfg.HighDutyCycle
r.PeerAddr = cfg.PeerAddr
bl, err := a.bx.AddListener(SeqKey(r.Seq))
if err != nil {
return 0, nil, err
}
connHandle, err := advStart(a.bx, bl, a.stopChan, r)
if err != nil {
a.bx.RemoveListener(bl)
if !nmxutil.IsXport(err) {
// The transport did not restart; always attempt to cancel the
// advertise operation. In some cases, the host has already stopped
// advertising and will respond with an "ealready" error that can be
// ignored.
if err := a.stopAdvertising(); err != nil {
log.Debugf("Failed to cancel advertise in progress: %s",
err.Error())
}
}
return 0, nil, err
}
return connHandle, bl, nil
}
func (a *Advertiser) stopAdvertising() error {
r := NewBleAdvStopReq()
bl, err := a.bx.AddListener(SeqKey(r.Seq))
if err != nil {
return err
}
defer a.bx.RemoveListener(bl)
return advStop(a.bx, bl, r)
}
func (a *Advertiser) buildSesn(cfg AdvertiseCfg, connHandle uint16,
bl *Listener) (sesn.Sesn, error) {
s, err := NewBleSesn(a.bx, cfg.SesnCfg)
if err != nil {
return nil, err
}
if err := s.OpenConnected(connHandle, bl); err != nil {
return nil, err
}
return s, nil
}
func (a *Advertiser) Start(cfg AdvertiseCfg) (sesn.Sesn, error) {
var advData []byte
var rspData []byte
var connHandle uint16
var bl *Listener
var err error
fns := []func() error{
// Convert advertising fields to data.
func() error {
advData, err = a.fields(cfg.AdvFields)
return err
},
// Set advertising data.
func() error {
return a.setAdvData(advData)
},
// Convert response fields to data.
func() error {
rspData, err = a.fields(cfg.RspFields)
return err
},
// Set response data.
func() error {
return a.setRspData(rspData)
},
// Advertise
func() error {
connHandle, bl, err = a.advertise(cfg)
return err
},
}
a.stopChan = make(chan struct{})
a.stoppedChan = make(chan struct{})
defer func() {
a.stopChan = nil
close(a.stoppedChan)
}()
if err := a.bx.AcquireSlave(a); err != nil {
return nil, err
}
defer a.bx.ReleaseSlave()
for _, fn := range fns {
// Check for abort before each step.
select {
case <-a.stopChan:
return nil, fmt.Errorf("advertise aborted")
default:
}
if err := fn(); err != nil {
return nil, err
}
}
return a.buildSesn(cfg, connHandle, bl)
}
func (a *Advertiser) Stop() error {
stopChan := a.stopChan
if stopChan == nil {
return fmt.Errorf("advertiser already stopped")
}
close(stopChan)
a.bx.StopWaitingForSlave(a, fmt.Errorf("advertise aborted"))
a.stopAdvertising()
// Block until abort is complete.
<-a.stoppedChan
return nil
}