1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <cstdint>
20 #include <functional>
21 #include <optional>
22 
23 #include "hci/address.h"
24 
25 namespace rootcanal {
26 
27 using ::bluetooth::hci::Address;
28 using TaskId = uint32_t;
29 
30 /*
31  * Notes about SCO / eSCO connection establishment:
32  *
33  * - Connections will always be established if possible as eSCO connections.
34  * The LMP parameter negotiation is skipped, instead the required parameters
35  * are directly sent to the peer.
36  *
37  * - If an synchronous connection setup fails with eSCO parameter negotiation,
38  * it is _not_ retried with SCO parameter negotiation.
39  *
40  * - If the parameters are compatible with the values returned from
41  * HCI Accept Synchronous Connection Request on the peer,
42  * the peer selects a valid link configuration which it returns
43  * in response.
44  */
45 
46 struct ScoLinkParameters {
47   uint8_t transmission_interval;
48   uint8_t retransmission_window;
49   uint16_t rx_packet_length;
50   uint16_t tx_packet_length;
51   uint8_t air_mode;
52   bool extended;
53 };
54 
55 struct ScoConnectionParameters {
56   uint32_t transmit_bandwidth;
57   uint32_t receive_bandwidth;
58   uint16_t max_latency;  // 0-3 reserved, 0xFFFF = don't care
59   uint16_t voice_setting;
60   uint8_t retransmission_effort;
61   uint16_t packet_type;
62 
63   // Return true if packet_type enables extended SCO packets.
64   bool IsExtended() const;
65 
66   // Return the link parameters for these connection parameters, if the
67   // parameters are coherent, none otherwise.
68   std::optional<ScoLinkParameters> GetLinkParameters() const;
69 };
70 
71 enum ScoState {
72   SCO_STATE_CLOSED = 0,
73   SCO_STATE_PENDING,
74   SCO_STATE_SENT_ESCO_CONNECTION_REQUEST,
75   SCO_STATE_SENT_SCO_CONNECTION_REQUEST,
76   SCO_STATE_OPENED,
77 };
78 
79 enum ScoDatapath {
80   NORMAL = 0,   // data is provided by the host over HCI
81   SPOOFED = 1,  // rootcanal generates data itself
82 };
83 
84 class ScoConnection {
85  public:
ScoConnection(Address address,ScoConnectionParameters const & parameters,ScoState state,ScoDatapath datapath,bool legacy)86   ScoConnection(Address address, ScoConnectionParameters const& parameters,
87                 ScoState state, ScoDatapath datapath, bool legacy)
88       : address_(address),
89         parameters_(parameters),
90         link_parameters_(),
91         state_(state),
92         datapath_(datapath),
93         legacy_(legacy) {}
94 
95   ~ScoConnection();
96 
IsLegacy()97   bool IsLegacy() const { return legacy_; }
GetAddress()98   Address GetAddress() const { return address_; }
GetState()99   ScoState GetState() const { return state_; }
SetState(ScoState state)100   void SetState(ScoState state) { state_ = state; }
101 
102   void StartStream(std::function<TaskId()> startStream);
103   void StopStream(std::function<void(TaskId)> stopStream);
104 
GetConnectionParameters()105   ScoConnectionParameters GetConnectionParameters() const {
106     return parameters_;
107   }
GetLinkParameters()108   ScoLinkParameters GetLinkParameters() const { return link_parameters_; }
SetLinkParameters(ScoLinkParameters const & parameters)109   void SetLinkParameters(ScoLinkParameters const& parameters) {
110     link_parameters_ = parameters;
111   }
112 
113   // Negotiate the connection parameters.
114   // Update the local connection parameters with negotiated values.
115   // Return true if the negotiation was successful, false otherwise.
116   bool NegotiateLinkParameters(ScoConnectionParameters const& peer);
117 
GetDatapath()118   ScoDatapath GetDatapath() const { return datapath_; }
119 
120  private:
121   Address address_;
122   ScoConnectionParameters parameters_;
123   ScoLinkParameters link_parameters_;
124   ScoState state_;
125 
126   // whether we use HCI, spoof the data, or potential future datapaths
127   ScoDatapath datapath_;
128 
129   // The handle of the async task managing the SCO stream, used to simulate
130   // offloaded input. None if HCI is used for input packets.
131   std::optional<TaskId> stream_handle_{};
132 
133   // Mark connections opened with the HCI command Add SCO Connection.
134   // The connection status is reported with HCI Connection Complete event
135   // rather than HCI Synchronous Connection Complete event.
136   bool legacy_;
137 };
138 
139 }  // namespace rootcanal
140