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