nmxact/nmble/ble_advertiser.go (182 lines of code) (raw):

/** * 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 }