nmxact/nmble/ble_act.go (1,145 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 ( "encoding/json" "fmt" "time" log "github.com/sirupsen/logrus" . "mynewt.apache.org/newtmgr/nmxact/bledefs" "mynewt.apache.org/newtmgr/nmxact/nmxutil" ) // Blocking func connect(x *BleXport, bl *Listener, r *BleConnectReq) (uint16, error) { const rspType = MSG_TYPE_CONNECT j, err := json.Marshal(r) if err != nil { return 0, err } if err := x.Tx(j); err != nil { return 0, err } // Give blehostd three seconds of leeway to tell us the connection attempt // timed out. rspTimeout := time.Duration(r.DurationMs+3000) * time.Millisecond rspTmoChan := time.After(rspTimeout) bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return 0, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleConnectRsp: bl.Acked = true if msg.Status != 0 { str := fmt.Sprintf("BLE connection attempt failed; "+ "status=%s (%d)", ErrCodeToString(msg.Status), msg.Status) log.Debugf(str) return 0, nmxutil.NewBleHostError(msg.Status, str) } case *BleConnectEvt: if msg.Status == 0 { return msg.ConnHandle, nil } else { str := fmt.Sprintf("BLE connection attempt failed; "+ "status=%s (%d)", ErrCodeToString(msg.Status), msg.Status) log.Debugf(str) return 0, nmxutil.NewBleHostError(msg.Status, str) } case *BleConnCancelEvt: str := "BLE connection attempt cancelled" log.Debugf(str) return 0, fmt.Errorf("%s", str) default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil case _, ok := <-rspTmoChan: if ok { return 0, fmt.Errorf("Failed to connect to peer after %s", rspTimeout.String()) } rspTmoChan = nil } } } // Blocking func terminate(x *BleXport, bl *Listener, r *BleTerminateReq) error { const rspType = MSG_TYPE_TERMINATE j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleTerminateRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } else { return nil } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func connCancel(x *BleXport, bl *Listener, r *BleConnCancelReq) error { const rspType = MSG_TYPE_CONN_CANCEL j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } // Allow 10 seconds for the controller to cancel the connection. rspTimeout := 10 * time.Second rspTmoChan := time.After(rspTimeout) bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleConnCancelRsp: bl.Acked = true switch msg.Status { case 0: // Cancel initiated. Await connect failure. return nil case ERR_CODE_EALREADY: // No connect in progress. Pretend success. return nil default: return StatusError(MSG_OP_RSP, rspType, msg.Status) } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil case _, ok := <-rspTmoChan: if ok { x.Restart("Failed to cancel connect after " + rspTimeout.String()) } rspTmoChan = nil } } } // Blocking. func discAllSvcs(x *BleXport, bl *Listener, r *BleDiscAllSvcsReq) ( []*BleDiscSvc, error) { const rspType = MSG_TYPE_DISC_ALL_SVCS const evtType = MSG_TYPE_DISC_SVC_EVT j, err := json.Marshal(r) if err != nil { return nil, err } if err := x.Tx(j); err != nil { return nil, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) var svcs []*BleDiscSvc for { select { case err := <-bl.ErrChan: return nil, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleDiscAllSvcsRsp: bl.Acked = true if msg.Status != 0 { return nil, StatusError(MSG_OP_RSP, rspType, msg.Status) } case *BleDiscSvcEvt: switch msg.Status { case 0: svcs = append(svcs, &msg.Svc) case ERR_CODE_EDONE: return svcs, nil default: return nil, StatusError(MSG_OP_EVT, evtType, msg.Status) } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking. func discSvcUuid(x *BleXport, bl *Listener, r *BleDiscSvcUuidReq) ( *BleDiscSvc, error) { const rspType = MSG_TYPE_DISC_SVC_UUID const evtType = MSG_TYPE_DISC_SVC_EVT j, err := json.Marshal(r) if err != nil { return nil, err } if err := x.Tx(j); err != nil { return nil, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) var svc *BleDiscSvc for { select { case err := <-bl.ErrChan: return nil, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleDiscSvcUuidRsp: bl.Acked = true if msg.Status != 0 { return nil, StatusError(MSG_OP_RSP, rspType, msg.Status) } case *BleDiscSvcEvt: switch msg.Status { case 0: svc = &msg.Svc case ERR_CODE_EDONE: if svc == nil { return nil, nmxutil.FmtBleHostError( msg.Status, "Peer doesn't support required service: %s", r.Uuid.String()) } return svc, nil default: return nil, StatusError(MSG_OP_EVT, evtType, msg.Status) } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking. func discAllChrs(x *BleXport, bl *Listener, r *BleDiscAllChrsReq) ( []*BleDiscChr, error) { const rspType = MSG_TYPE_DISC_ALL_CHRS const evtType = MSG_TYPE_DISC_CHR_EVT j, err := json.Marshal(r) if err != nil { return nil, err } if err := x.Tx(j); err != nil { return nil, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) chrs := []*BleDiscChr{} for { select { case err := <-bl.ErrChan: return nil, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleDiscAllChrsRsp: bl.Acked = true if msg.Status != 0 { return nil, StatusError(MSG_OP_RSP, rspType, msg.Status) } case *BleDiscChrEvt: switch msg.Status { case 0: chrs = append(chrs, &msg.Chr) case ERR_CODE_EDONE: return chrs, nil default: return nil, StatusError(MSG_OP_EVT, evtType, msg.Status) } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // 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 } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) 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 _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // 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 } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) 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) } case *BleWriteAckEvt: switch msg.Status { case 0: return nil default: return StatusError(MSG_OP_EVT, evtType, msg.Status) } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking. func writeCmd(x *BleXport, bl *Listener, r *BleWriteCmdReq) error { const rspType = MSG_TYPE_WRITE_CMD j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleWriteCmdRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } else { return nil } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking. func notify(x *BleXport, bl *Listener, r *BleNotifyReq) error { const rspType = MSG_TYPE_NOTIFY j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleNotifyRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } else { return nil } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking. func findChr(x *BleXport, bl *Listener, r *BleFindChrReq) ( uint16, uint16, error) { const rspType = MSG_TYPE_NOTIFY j, err := json.Marshal(r) if err != nil { return 0, 0, err } if err := x.Tx(j); err != nil { return 0, 0, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return 0, 0, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleFindChrRsp: bl.Acked = true if msg.Status != 0 { return 0, 0, StatusError(MSG_OP_RSP, rspType, msg.Status) } else { return msg.DefHandle, msg.ValHandle, nil } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking. func exchangeMtu(x *BleXport, bl *Listener, r *BleExchangeMtuReq) ( int, error) { const rspType = MSG_TYPE_EXCHANGE_MTU const evtType = MSG_TYPE_MTU_CHANGE_EVT j, err := json.Marshal(r) if err != nil { return 0, err } if err := x.Tx(j); err != nil { return 0, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return 0, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleExchangeMtuRsp: bl.Acked = true if msg.Status != 0 { return 0, StatusError(MSG_OP_RSP, rspType, msg.Status) } case *BleMtuChangeEvt: if msg.Status != 0 { return 0, StatusError(MSG_OP_EVT, evtType, msg.Status) } else { return int(msg.Mtu), nil } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func actScan(x *BleXport, bl *Listener, r *BleScanReq) ( <-chan BleAdvReport, <-chan error, error) { const rspType = MSG_TYPE_SCAN j, err := json.Marshal(r) if err != nil { return nil, nil, err } if err := x.Tx(j); err != nil { return nil, nil, err } ach := make(chan BleAdvReport) ech := make(chan error) nmxutil.Assert(bl != nil) bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) go func() { defer close(ach) defer close(ech) for { select { case err := <-bl.ErrChan: ech <- err return case bm, ok := <-bl.MsgChan: if ok { switch msg := bm.(type) { case *BleScanRsp: bl.Acked = true if msg.Status != 0 { ech <- StatusError(MSG_OP_RSP, rspType, msg.Status) return } case *BleScanEvt: r := BleAdvReportFromScanEvt(msg) ach <- r case *BleScanCompleteEvt: if msg.Reason == 0 { // On successful completion, just return and allow // the ech channel to close. return } else { ech <- StatusError(MSG_OP_RSP, rspType, msg.Reason) return } default: } } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } }() return ach, ech, nil } func scanCancel(x *BleXport, bl *Listener, r *BleScanCancelReq) error { const rspType = MSG_TYPE_SCAN_CANCEL j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleScanCancelRsp: bl.Acked = true if msg.Status != 0 && msg.Status != ERR_CODE_EALREADY { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func connFind(x *BleXport, bl *Listener, r *BleConnFindReq) ( BleConnDesc, error) { const rspType = MSG_TYPE_CONN_FIND j, err := json.Marshal(r) if err != nil { return BleConnDesc{}, err } if err := x.Tx(j); err != nil { return BleConnDesc{}, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return BleConnDesc{}, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleConnFindRsp: bl.Acked = true if msg.Status != 0 { return BleConnDesc{}, StatusError(MSG_OP_RSP, rspType, msg.Status) } return BleDescFromConnFindRsp(msg), nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Tells the host to reset the controller. func reset(x *BleXport, bl *Listener, r *BleResetReq) error { const rspType = MSG_TYPE_RESET j, err := json.Marshal(r) if err != nil { return err } if err := x.txNoSync(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch bm.(type) { case *BleResetRsp: bl.Acked = true return nil default: } case _, ok := <-bhdTmoChan: if ok { return fmt.Errorf("Blehostd timeout: %s", MsgTypeToString(rspType)) } } } } // Blocking func securityInitiate(x *BleXport, bl *Listener, r *BleSecurityInitiateReq) error { const rspType = MSG_TYPE_SECURITY_INITIATE j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleSecurityInitiateRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } else { return nil } default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func smInjectIo(x *BleXport, bl *Listener, r *BleSmInjectIoReq) error { const rspType = MSG_TYPE_SM_INJECT_IO j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleSmInjectIoRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking func advStart(x *BleXport, bl *Listener, stopChan chan struct{}, r *BleAdvStartReq) (uint16, error) { const rspType = MSG_TYPE_ADV_START j, err := json.Marshal(r) if err != nil { return 0, err } if err := x.Tx(j); err != nil { return 0, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return 0, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAdvStartRsp: bl.Acked = true if msg.Status != 0 { return 0, StatusError(MSG_OP_RSP, rspType, msg.Status) } case *BleConnectEvt: if msg.Status == 0 { return msg.ConnHandle, nil } else { str := fmt.Sprintf("BLE peer failed to connect to us; "+ "status=%s (%d)", ErrCodeToString(msg.Status), msg.Status) log.Debugf(str) return 0, nmxutil.NewBleHostError(msg.Status, str) } case *BleAdvCompleteEvt: str := fmt.Sprintf("Advertising stopped; reason=%s (%d)", ErrCodeToString(msg.Reason), msg.Reason) log.Debugf(str) return 0, nmxutil.NewBleHostError(msg.Reason, str) default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil case <-stopChan: return 0, fmt.Errorf("advertise aborted") } } } // Blocking func advStop(x *BleXport, bl *Listener, r *BleAdvStopReq) error { const rspType = MSG_TYPE_ADV_STOP j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAdvStopRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking func advSetData(x *BleXport, bl *Listener, r *BleAdvSetDataReq) error { const rspType = MSG_TYPE_ADV_SET_DATA j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAdvSetDataRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking func advRspSetData(x *BleXport, bl *Listener, r *BleAdvRspSetDataReq) error { const rspType = MSG_TYPE_ADV_RSP_SET_DATA j, err := json.Marshal(r) if err != nil { return err } if err := x.Tx(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAdvRspSetDataRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Blocking func advFields(x *BleXport, bl *Listener, r *BleAdvFieldsReq) ( []byte, error) { const rspType = MSG_TYPE_ADV_FIELDS j, err := json.Marshal(r) if err != nil { return nil, err } if err := x.Tx(j); err != nil { return nil, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return nil, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAdvFieldsRsp: bl.Acked = true if msg.Status != 0 { return nil, StatusError(MSG_OP_RSP, rspType, msg.Status) } return msg.Data.Bytes, nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func clearSvcs(x *BleXport, bl *Listener, r *BleClearSvcsReq) error { const rspType = MSG_TYPE_CLEAR_SVCS j, err := json.Marshal(r) if err != nil { return err } if err := x.txNoSync(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleClearSvcsRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func addSvcs(x *BleXport, bl *Listener, r *BleAddSvcsReq) error { const rspType = MSG_TYPE_ADD_SVCS j, err := json.Marshal(r) if err != nil { return err } if err := x.txNoSync(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAddSvcsRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func commitSvcs(x *BleXport, bl *Listener, r *BleCommitSvcsReq) ( []BleRegSvc, error) { const rspType = MSG_TYPE_COMMIT_SVCS j, err := json.Marshal(r) if err != nil { return nil, err } if err := x.txNoSync(j); err != nil { return nil, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return nil, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleCommitSvcsRsp: bl.Acked = true if msg.Status != 0 { return nil, StatusError(MSG_OP_RSP, rspType, msg.Status) } return msg.Svcs, nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func accessStatus(x *BleXport, bl *Listener, r *BleAccessStatusReq) error { const rspType = MSG_TYPE_ACCESS_STATUS j, err := json.Marshal(r) if err != nil { return err } x.Tx(j) bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleAccessStatusRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Asks the controller to generate a random address. This is done when the // transport is starting up, and therefore does not require the transport to be // synced. Only the transport should call this function. func genRandAddr(x *BleXport, bl *Listener, r *BleGenRandAddrReq) ( BleAddr, error) { const rspType = MSG_TYPE_GEN_RAND_ADDR j, err := json.Marshal(r) if err != nil { return BleAddr{}, err } if err := x.txNoSync(j); err != nil { return BleAddr{}, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return BleAddr{}, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleGenRandAddrRsp: bl.Acked = true if msg.Status != 0 { return BleAddr{}, StatusError(MSG_OP_RSP, rspType, msg.Status) } return msg.Addr, nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Configures the controller with the specified random address. This is done // when the transport is starting up, and therefore does not require the // transport to be synced. Only the transport should call this function. func setRandAddr(x *BleXport, bl *Listener, r *BleSetRandAddrReq) error { const rspType = MSG_TYPE_SET_RAND_ADDR j, err := json.Marshal(r) if err != nil { return err } if err := x.txNoSync(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleSetRandAddrRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } // Configures the host with the specified preferred ATT MTU. This is done // when the transport is starting up, and therefore does not require the // transport to be synced. Only the transport should call this function. func setPreferredMtu(x *BleXport, bl *Listener, r *BleSetPreferredMtuReq) error { const rspType = MSG_TYPE_SET_PREFERRED_MTU j, err := json.Marshal(r) if err != nil { return err } if err := x.txNoSync(j); err != nil { return err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleSetPreferredMtuRsp: bl.Acked = true if msg.Status != 0 { return StatusError(MSG_OP_RSP, rspType, msg.Status) } return nil default: } case _, ok := <-bhdTmoChan: if ok { x.Restart("Blehostd timeout: " + MsgTypeToString(rspType)) } bhdTmoChan = nil } } } func checkSync(x *BleXport, bl *Listener, r *BleSyncReq) (bool, error) { const rspType = MSG_TYPE_SYNC j, err := json.Marshal(r) if err != nil { return false, err } if err := x.txNoSync(j); err != nil { return false, err } bhdTmoChan := bl.AfterTimeout(x.RspTimeout()) for { select { case err := <-bl.ErrChan: return false, err case bm := <-bl.MsgChan: switch msg := bm.(type) { case *BleSyncRsp: bl.Acked = true return msg.Synced, nil } case _, ok := <-bhdTmoChan: if ok { return false, fmt.Errorf("Blehostd timeout: %s", MsgTypeToString(rspType)) } } } }