• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 #include <poll.h>
18 #include <sys/socket.h>
19 #include <unistd.h>
20 
21 #include <vector>
22 
23 #include "btm_sco_hfp_hal.h"
24 #include "gd/common/init_flags.h"
25 #include "osi/include/log.h"
26 #include "stack/acl/acl.h"
27 #include "stack/include/acl_api.h"
28 
29 namespace hfp_hal_interface {
30 namespace {
31 bool offload_supported = false;
32 bool offload_enabled = false;
33 
34 struct mgmt_bt_codec {
35   uint8_t codec;
36   uint8_t packet_size;
37   uint8_t data_path;
38   uint32_t data_length;
39   uint8_t data[];
40 } __attribute__((packed));
41 
42 typedef struct cached_codec_info {
43   struct bt_codec inner;
44   uint8_t pkt_size;
45 } cached_codec_info;
46 
47 std::vector<cached_codec_info> cached_codecs;
48 
49 #define RETRY_ON_INTR(fn) \
50   do {                    \
51   } while ((fn) == -1 && errno == EINTR)
52 
53 #define MGMT_EV_SIZE_MAX 1024
54 #define MGMT_PKT_HDR_SIZE 6
55 struct mgmt_pkt {
56   uint16_t opcode;
57   uint16_t index;
58   uint16_t len;
59   uint8_t data[MGMT_EV_SIZE_MAX];
60 } __attribute__((packed));
61 
62 #define MGMT_EV_COMMAND_COMPLETE 0x1
63 
64 struct mgmt_ev_cmd_complete {
65   uint16_t opcode;
66   uint8_t status;
67   uint8_t data[];
68 } __attribute__((packed));
69 
70 #define MGMT_OP_GET_SCO_CODEC_CAPABILITIES 0x0100
71 #define MGMT_SCO_CODEC_CVSD 0x1
72 #define MGMT_SCO_CODEC_MSBC_TRANSPARENT 0x2
73 #define MGMT_SCO_CODEC_MSBC 0x3
74 
75 struct mgmt_cp_get_codec_capabilities {
76   uint16_t hci_dev;
77   uint32_t num_codecs;
78   uint8_t codecs[];
79 } __attribute__((packed));
80 
81 struct mgmt_rp_get_codec_capabilities {
82   uint16_t hci_dev;
83   uint8_t offload_capable;
84   uint32_t num_codecs;
85   struct mgmt_bt_codec codecs[];
86 } __attribute__((packed));
87 
88 #define MGMT_POLL_TIMEOUT_MS 2000
89 
cache_codec_capabilities(struct mgmt_rp_get_codec_capabilities * rp)90 void cache_codec_capabilities(struct mgmt_rp_get_codec_capabilities* rp) {
91   uint8_t* ptr = reinterpret_cast<uint8_t*>(rp->codecs);
92   // Copy into cached codec information
93   offload_supported = rp->offload_capable;
94   for (int i = 0; i < rp->num_codecs; i++) {
95     struct mgmt_bt_codec* mc = reinterpret_cast<struct mgmt_bt_codec*>(ptr);
96     cached_codec_info c = {
97         .inner =
98             {
99                 .codec = static_cast<codec>(1 << (mc->codec - 1)),
100                 .data_path = mc->data_path,
101                 .data = mc->data_length == 0
102                             ? std::vector<uint8_t>{}
103                             : std::vector<uint8_t>(mc->data,
104                                                    mc->data + mc->data_length),
105             },
106         .pkt_size = mc->packet_size,
107     };
108     ptr += sizeof(*mc);
109 
110     LOG_INFO("Caching HFP codec %u, data path %u, data len %d, pkt_size %u",
111              (uint64_t)c.inner.codec, c.inner.data_path, c.inner.data.size(),
112              c.pkt_size);
113 
114     cached_codecs.push_back(c);
115   }
116 }
117 
118 struct sockaddr_hci {
119   sa_family_t hci_family;
120   unsigned short hci_dev;
121   unsigned short hci_channel;
122 };
123 
124 constexpr uint8_t BTPROTO_HCI = 1;
125 constexpr uint16_t HCI_CHANNEL_CONTROL = 3;
126 constexpr uint16_t HCI_DEV_NONE = 0xffff;
127 
btsocket_open_mgmt(uint16_t hci)128 int btsocket_open_mgmt(uint16_t hci) {
129   int fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_NONBLOCK, BTPROTO_HCI);
130   if (fd < 0) {
131     LOG_DEBUG("Failed to open BT socket.");
132     return -errno;
133   }
134 
135   struct sockaddr_hci addr = {
136       .hci_family = AF_BLUETOOTH,
137       .hci_dev = HCI_DEV_NONE,
138       .hci_channel = HCI_CHANNEL_CONTROL,
139   };
140 
141   int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
142   if (ret < 0) {
143     LOG_DEBUG("Failed to bind BT socket.");
144     close(fd);
145     return -errno;
146   }
147 
148   return fd;
149 }
150 
mgmt_get_codec_capabilities(int fd,uint16_t hci)151 int mgmt_get_codec_capabilities(int fd, uint16_t hci) {
152   // Write read codec capabilities
153   struct mgmt_pkt ev;
154   ev.opcode = MGMT_OP_GET_SCO_CODEC_CAPABILITIES;
155   ev.index = HCI_DEV_NONE;
156   ev.len = sizeof(struct mgmt_cp_get_codec_capabilities) + 3;
157 
158   struct mgmt_cp_get_codec_capabilities* cp =
159       reinterpret_cast<struct mgmt_cp_get_codec_capabilities*>(ev.data);
160   cp->hci_dev = hci;
161   cp->num_codecs = 3;
162   cp->codecs[0] = MGMT_SCO_CODEC_CVSD;
163   cp->codecs[1] = MGMT_SCO_CODEC_MSBC_TRANSPARENT;
164   cp->codecs[2] = MGMT_SCO_CODEC_MSBC;
165 
166   int ret;
167 
168   struct pollfd writable[1];
169   writable[0].fd = fd;
170   writable[0].events = POLLOUT;
171 
172   do {
173     ret = poll(writable, 1, MGMT_POLL_TIMEOUT_MS);
174     if (ret > 0) {
175       RETRY_ON_INTR(ret = write(fd, &ev, MGMT_PKT_HDR_SIZE + ev.len));
176       if (ret < 0) {
177         LOG_DEBUG("Failed to call MGMT_OP_GET_SCO_CODEC_CAPABILITIES: %d",
178                   -errno);
179         return -errno;
180       };
181       break;
182     }
183   } while (ret > 0);
184 
185   if (ret <= 0) {
186     LOG_DEBUG("Failed waiting for mgmt socket to be writable.");
187     return -1;
188   }
189 
190   struct pollfd fds[1];
191 
192   fds[0].fd = fd;
193   fds[0].events = POLLIN;
194 
195   do {
196     ret = poll(fds, 1, MGMT_POLL_TIMEOUT_MS);
197     if (ret > 0) {
198       if (fds[0].revents & POLLIN) {
199         RETRY_ON_INTR(ret = read(fd, &ev, sizeof(ev)));
200         if (ret < 0) {
201           LOG_DEBUG("Failed to read mgmt socket: %d", -errno);
202           return -errno;
203         }
204 
205         if (ev.opcode == MGMT_EV_COMMAND_COMPLETE) {
206           struct mgmt_ev_cmd_complete* cc =
207               reinterpret_cast<struct mgmt_ev_cmd_complete*>(ev.data);
208           if (cc->opcode == MGMT_OP_GET_SCO_CODEC_CAPABILITIES &&
209               cc->status == 0) {
210             struct mgmt_rp_get_codec_capabilities* rp =
211                 reinterpret_cast<struct mgmt_rp_get_codec_capabilities*>(
212                     cc->data);
213             if (rp->hci_dev == hci) {
214               cache_codec_capabilities(rp);
215               return 0;
216             }
217           }
218         }
219       }
220     } else if (ret == 0) {
221       LOG_DEBUG("Timeout while waiting for codec capabilities response.");
222       ret = -1;
223     }
224   } while (ret > 0);
225 
226   return ret;
227 }
228 
229 #define MGMT_OP_NOTIFY_SCO_CONNECTION_CHANGE 0x0101
230 struct mgmt_cp_notify_sco_connection_change {
231   uint16_t hci_dev;
232   uint8_t addr[6];
233   uint8_t addr_type;
234   uint8_t connected;
235   uint8_t codec;
236 } __attribute__((packed));
237 
mgmt_notify_sco_connection_change(int fd,int hci,RawAddress device,bool is_connected,int codec)238 int mgmt_notify_sco_connection_change(int fd, int hci, RawAddress device,
239                                       bool is_connected, int codec) {
240   struct mgmt_pkt ev;
241   ev.opcode = MGMT_OP_NOTIFY_SCO_CONNECTION_CHANGE;
242   ev.index = HCI_DEV_NONE;
243   ev.len = sizeof(struct mgmt_cp_notify_sco_connection_change);
244 
245   struct mgmt_cp_notify_sco_connection_change* cp =
246       reinterpret_cast<struct mgmt_cp_notify_sco_connection_change*>(ev.data);
247 
248   cp->hci_dev = hci;
249   cp->connected = is_connected;
250   cp->codec = codec;
251   memcpy(cp->addr, device.address, sizeof(cp->addr));
252   cp->addr_type = 0;
253 
254   int ret;
255 
256   struct pollfd writable[1];
257   writable[0].fd = fd;
258   writable[0].events = POLLOUT;
259 
260   do {
261     ret = poll(writable, 1, MGMT_POLL_TIMEOUT_MS);
262     if (ret > 0) {
263       RETRY_ON_INTR(ret = write(fd, &ev, MGMT_PKT_HDR_SIZE + ev.len));
264       if (ret < 0) {
265         LOG_ERROR("Failed to call MGMT_OP_NOTIFY_SCO_CONNECTION_CHANGE: %d",
266                   -errno);
267         return -errno;
268       };
269       break;
270     }
271   } while (ret > 0);
272 
273   if (ret <= 0) {
274     LOG_DEBUG("Failed waiting for mgmt socket to be writable.");
275     return -1;
276   }
277 
278   return 0;
279 }
280 }  // namespace
281 
init()282 void init() {
283   int hci = bluetooth::common::InitFlags::GetAdapterIndex();
284   int fd = btsocket_open_mgmt(hci);
285   if (fd < 0) {
286     LOG_ERROR("Failed to open mgmt channel, error= %d.", fd);
287     return;
288   }
289 
290   int ret = mgmt_get_codec_capabilities(fd, hci);
291   if (ret) {
292     LOG_ERROR("Failed to get codec capabilities with error = %d.", ret);
293   } else {
294     LOG_INFO("Successfully queried SCO codec capabilities.");
295   }
296 
297   close(fd);
298 }
299 
300 // Check if wideband speech is supported on local device
get_wbs_supported()301 bool get_wbs_supported() {
302   for (cached_codec_info c : cached_codecs) {
303     if (c.inner.codec == MSBC || c.inner.codec == MSBC_TRANSPARENT) {
304       return true;
305     }
306   }
307   return false;
308 }
309 
310 // Here SWB default to be false at linux, change if needed
get_swb_supported()311 bool get_swb_supported() { return false; }
312 
313 // Checks the supported codecs
get_codec_capabilities(uint64_t codecs)314 bt_codecs get_codec_capabilities(uint64_t codecs) {
315   bt_codecs codec_list = {.offload_capable = offload_supported};
316 
317   for (auto c : cached_codecs) {
318     if (c.inner.codec & codecs) {
319       codec_list.codecs.push_back(c.inner);
320     }
321   }
322 
323   return codec_list;
324 }
325 
326 // Check if hardware offload is supported
get_offload_supported()327 bool get_offload_supported() { return offload_supported; }
328 
329 // Check if hardware offload is enabled
get_offload_enabled()330 bool get_offload_enabled() { return offload_supported && offload_enabled; }
331 
332 // Set offload enable/disable
enable_offload(bool enable)333 bool enable_offload(bool enable) {
334   if (!offload_supported && enable) {
335     LOG_ERROR("%s: Cannot enable SCO-offload since it is not supported.",
336               __func__);
337     return false;
338   }
339   offload_enabled = enable;
340   return true;
341 }
342 
get_single_codec(int codec,bt_codec ** out)343 static bool get_single_codec(int codec, bt_codec** out) {
344   for (cached_codec_info& c : cached_codecs) {
345     if (c.inner.codec == codec) {
346       *out = &c.inner;
347       return true;
348     }
349   }
350 
351   return false;
352 }
353 
354 constexpr uint8_t OFFLOAD_DATAPATH = 0x01;
355 
356 // Notify the codec datapath to lower layer for offload mode
set_codec_datapath(esco_coding_format_t coding_format)357 void set_codec_datapath(esco_coding_format_t coding_format) {
358   bool found;
359   bt_codec* codec;
360   uint8_t codec_id;
361 
362   switch (coding_format) {
363     case BTM_SCO_CODEC_CVSD:
364       codec_id = codec::CVSD;
365       break;
366     case BTM_SCO_CODEC_MSBC:
367       codec_id = get_offload_enabled() ? codec::MSBC : codec::MSBC_TRANSPARENT;
368       break;
369     default:
370       LOG_WARN("Unsupported format (%u). Won't set datapath.", coding_format);
371       return;
372   }
373 
374   found = get_single_codec(codec_id, &codec);
375   if (!found) {
376     LOG_ERROR(
377         "Failed to find codec config for format (%u). Won't set datapath.",
378         coding_format);
379     return;
380   }
381 
382   LOG_INFO("Configuring datapath for codec (%u)", codec->codec);
383   if (codec->codec == codec::MSBC && !get_offload_enabled()) {
384     LOG_ERROR(
385         "Tried to configure offload data path for format (%u) with offload "
386         "disabled. Won't set datapath.",
387         coding_format);
388     return;
389   }
390 
391   if (get_offload_enabled()) {
392     /* TODO(b/237373343): expect the data content to be represented differently
393      */
394     std::vector<uint8_t> data;
395     switch (coding_format) {
396       case BTM_SCO_CODEC_CVSD:
397         data = {0x00};
398         break;
399       case BTM_SCO_CODEC_MSBC:
400         data = {0x01};
401         break;
402       default:
403         break;
404     }
405 
406     btm_configure_data_path(btm_data_direction::CONTROLLER_TO_HOST,
407                             OFFLOAD_DATAPATH, data);
408     btm_configure_data_path(btm_data_direction::HOST_TO_CONTROLLER,
409                             OFFLOAD_DATAPATH, data);
410   }
411 }
412 
get_packet_size(int codec)413 int get_packet_size(int codec) {
414   for (const cached_codec_info& c : cached_codecs) {
415     if (c.inner.codec == codec) {
416       return c.pkt_size;
417     }
418   }
419 
420   return kDefaultPacketSize;
421 }
422 
notify_sco_connection_change(RawAddress device,bool is_connected,int codec)423 void notify_sco_connection_change(RawAddress device, bool is_connected,
424                                   int codec) {
425   int hci = bluetooth::common::InitFlags::GetAdapterIndex();
426   int fd = btsocket_open_mgmt(hci);
427   if (fd < 0) {
428     LOG_ERROR("Failed to open mgmt channel, error= %d.", fd);
429     return;
430   }
431 
432   // Default to cvsd and try to convert codec.
433   int converted_codec = MGMT_SCO_CODEC_CVSD;
434 
435   if (codec == codec::MSBC) {
436     converted_codec = MGMT_SCO_CODEC_MSBC;
437   } else if (codec == codec::MSBC_TRANSPARENT) {
438     converted_codec = MGMT_SCO_CODEC_MSBC_TRANSPARENT;
439   }
440 
441   int ret = mgmt_notify_sco_connection_change(fd, hci, device, is_connected,
442                                               converted_codec);
443   if (ret) {
444     LOG_ERROR(
445         "Failed to notify HAL of connection change: hci %d, device %s, "
446         "connected %d, codec %d",
447         hci, ADDRESS_TO_LOGGABLE_CSTR(device), is_connected, codec);
448   } else {
449     LOG_INFO(
450         "Notified HAL of connection change: hci %d, device %s, connected %d, "
451         "codec %d",
452         hci, ADDRESS_TO_LOGGABLE_CSTR(device), is_connected, codec);
453   }
454 
455   close(fd);
456 }
457 
update_esco_parameters(enh_esco_params_t * p_parms)458 void update_esco_parameters(enh_esco_params_t* p_parms) {
459   if (get_offload_enabled()) {
460     p_parms->input_transport_unit_size = 0x01;
461     p_parms->output_transport_unit_size = 0x01;
462   } else {
463     p_parms->input_transport_unit_size = 0x00;
464     p_parms->output_transport_unit_size = 0x00;
465   }
466 }
467 }  // namespace hfp_hal_interface
468