1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include "pw_bluetooth/low_energy/central2.h" 18 #include "pw_bluetooth_sapphire/internal/connection.h" 19 #include "pw_bluetooth_sapphire/internal/host/gap/adapter.h" 20 #include "pw_multibuf/allocator.h" 21 22 namespace pw::bluetooth_sapphire { 23 24 /// Must only be constructed and destroyed on the Bluetooth thread. 25 class Central final : public pw::bluetooth::low_energy::Central2 { 26 public: 27 // The maximum number of scan results to queue in each ScanHandle 28 static constexpr uint8_t kMaxScanResultsQueueSize = 10; 29 30 // TODO: https://pwbug.dev/377301546 - Don't expose Adapter in public API. 31 /// Must only be constructed on the Bluetooth thread. 32 /// @param allocator The allocator to use for advertising data buffers. 33 Central(bt::gap::Adapter::WeakPtr adapter, 34 pw::async::Dispatcher& dispatcher, 35 pw::multibuf::MultiBufAllocator& allocator); 36 ~Central() override; 37 38 async2::OnceReceiver<ConnectResult> Connect( 39 pw::bluetooth::PeerId peer_id, 40 bluetooth::low_energy::Connection2::ConnectionOptions options) override 41 PW_LOCKS_EXCLUDED(lock()); 42 43 async2::OnceReceiver<ScanStartResult> Scan( 44 const ScanOptions& options) override PW_LOCKS_EXCLUDED(lock()); 45 46 static pw::sync::Mutex& lock(); 47 48 private: 49 class ScanHandleImpl final : public ScanHandle { 50 public: ScanHandleImpl(uint16_t scan_id,Central * central)51 explicit ScanHandleImpl(uint16_t scan_id, Central* central) 52 : scan_id_(scan_id), central_(central) {} 53 54 // Synchronously clears ScanState's pointer to this object and 55 // asynchronously stops the scan procedure. 56 ~ScanHandleImpl() override; 57 58 void QueueScanResultLocked(ScanResult&& result) 59 PW_EXCLUSIVE_LOCKS_REQUIRED(lock()); 60 WakeLocked()61 void WakeLocked() PW_EXCLUSIVE_LOCKS_REQUIRED(lock()) { 62 std::move(waker_).Wake(); 63 } 64 OnScanErrorLocked()65 void OnScanErrorLocked() PW_EXCLUSIVE_LOCKS_REQUIRED(lock()) { 66 central_ = nullptr; 67 WakeLocked(); 68 } 69 70 private: 71 async2::Poll<pw::Result<ScanResult>> PendResult( 72 async2::Context& cx) override; 73 74 // std::unique_ptr custom Deleter Release()75 void Release() override { delete this; } 76 77 const uint16_t scan_id_; 78 79 // Set to null when Central is destroyed or scanning has stopped. 80 Central* central_ PW_GUARDED_BY(lock()); 81 82 // PendResult() waker. Set when Pending was returned. 83 async2::Waker waker_ PW_GUARDED_BY(lock()); 84 std::queue<ScanResult> results_ PW_GUARDED_BY(lock()); 85 }; 86 87 // Created and destroyed on the Bluetooth thread. 88 class ScanState { 89 public: 90 // Must be run on Bluetooth thread. Not thread safe. 91 explicit ScanState( 92 std::unique_ptr<bt::gap::LowEnergyDiscoverySession> session, 93 ScanHandleImpl* scan_handle, 94 uint16_t scan_id, 95 Central* central); 96 97 ~ScanState(); 98 OnScanHandleDestroyedLocked()99 void OnScanHandleDestroyedLocked() PW_EXCLUSIVE_LOCKS_REQUIRED(lock()) { 100 scan_handle_ = nullptr; 101 } 102 103 private: 104 // Must be run on Bluetooth thread. Not thread safe. 105 void OnScanResult(const bt::gap::Peer& peer) PW_LOCKS_EXCLUDED(lock()); 106 107 // Must be run on Bluetooth thread. Not thread safe. 108 void OnError() PW_LOCKS_EXCLUDED(lock()); 109 110 const uint16_t scan_id_; 111 112 // Set to null synchronously when ScanHandleImpl is destroyed. 113 ScanHandleImpl* scan_handle_ PW_GUARDED_BY(lock()); 114 115 // Members must only be accessed on Bluetooth thread. 116 Central* const central_; 117 std::unique_ptr<bt::gap::LowEnergyDiscoverySession> session_; 118 }; 119 120 // Asynchronously stops the scan corresponding to `scan_id` and synchronously 121 // clears `ScanState.scan_handle_`. 122 void StopScanLocked(uint16_t scan_id) PW_EXCLUSIVE_LOCKS_REQUIRED(lock()); 123 124 void OnConnectionResult(bt::PeerId peer_id, 125 bt::gap::Adapter::LowEnergy::ConnectionResult result, 126 async2::OnceSender<ConnectResult> result_sender) 127 PW_LOCKS_EXCLUDED(lock()); 128 129 std::unordered_map<uint16_t, ScanState> scans_ PW_GUARDED_BY(lock()); 130 131 // Must only be used on the Bluetooth thread. 132 bt::gap::Adapter::WeakPtr adapter_; 133 134 // Dispatcher for Bluetooth thread. Thread safe. 135 pw::async::Dispatcher& dispatcher_; 136 pw::async::HeapDispatcher heap_dispatcher_; 137 138 pw::multibuf::MultiBufAllocator& allocator_; 139 140 // Must only be used on the Bluetooth thread. 141 WeakSelf<Central> weak_factory_{this}; 142 143 // Thread safe to copy and destroy, but WeakPtr should only be used 144 // or dereferenced on the Bluetooth thread (WeakRef is not thread safe). 145 WeakSelf<Central>::WeakPtr self_{weak_factory_.GetWeakPtr()}; 146 }; 147 148 } // namespace pw::bluetooth_sapphire 149