/src/adhd/cras/src/server/cras_sco.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright 2013 The ChromiumOS Authors |
2 | | * Use of this source code is governed by a BSD-style license that can be |
3 | | * found in the LICENSE file. |
4 | | */ |
5 | | |
6 | | #include "cras/src/server/cras_sco.h" |
7 | | |
8 | | #include <stdbool.h> |
9 | | #include <stdint.h> |
10 | | #include <stdlib.h> |
11 | | #include <string.h> |
12 | | #include <sys/param.h> |
13 | | #include <sys/poll.h> |
14 | | #include <sys/socket.h> |
15 | | #include <syslog.h> |
16 | | #include <unistd.h> |
17 | | |
18 | | #include "cras/src/common/bluetooth.h" |
19 | | #include "cras/src/common/byte_buffer.h" |
20 | | #include "cras/src/common/cras_audio_codec.h" |
21 | | #include "cras/src/common/cras_sbc_codec.h" |
22 | | #include "cras/src/common/cras_string.h" |
23 | | #include "cras/src/plc/cras_plc.h" |
24 | | #include "cras/src/server/audio_thread.h" |
25 | | #include "cras/src/server/cras_bt_device.h" |
26 | | #include "cras/src/server/cras_hfp_slc.h" |
27 | | #include "cras/src/server/cras_iodev_list.h" |
28 | | #include "cras/src/server/cras_server_metrics.h" |
29 | | #include "cras/src/server/cras_sr.h" |
30 | | #include "cras/src/server/cras_sr_bt_util.h" |
31 | | #include "cras_audio_format.h" |
32 | | #include "cras_types.h" |
33 | | #include "packet_status_logger.h" |
34 | | |
35 | | /* The max buffer size. Note that the actual used size must set to multiple |
36 | | * of SCO packet size, and the packet size does not necessarily be equal to |
37 | | * MTU. We should keep this as common multiple of possible packet sizes, for |
38 | | * example: 48, 60, 64, 128. |
39 | | */ |
40 | 0 | #define MAX_HFP_BUF_SIZE_BYTES 28800 |
41 | | |
42 | | // rate(8kHz) * sample_size(2 bytes) * channels(1) |
43 | | #define HFP_BYTE_RATE 16000 |
44 | | |
45 | | // Per Bluetooth Core v5.0 and HFP 1.7 specification. |
46 | 0 | #define MSBC_H2_HEADER_LEN 2 |
47 | 0 | #define MSBC_FRAME_LEN 57 |
48 | 0 | #define MSBC_FRAME_SIZE 59 |
49 | 0 | #define MSBC_CODE_SIZE 240 |
50 | 0 | #define MSBC_SYNC_WORD 0xAD |
51 | | |
52 | | /* For one mSBC 1 compressed wideband audio channel the HCI packets will |
53 | | * be 3 octets of HCI header + 60 octets of data. */ |
54 | 0 | #define MSBC_PKT_SIZE 60 |
55 | | |
56 | 0 | #define H2_HEADER_0 0x01 |
57 | | |
58 | | /* Supported HCI SCO packet sizes. The wideband speech mSBC frame parsing |
59 | | * code ties to limited packet size values. Specifically list them out |
60 | | * to check against when setting packet size. The first entry is the default |
61 | | * value as a fallback. |
62 | | * |
63 | | * Temp buffer size should be set to least common multiple of HCI SCO packet |
64 | | * size and MSBC_PKT_SIZE for optimizing buffer copy. |
65 | | * To add a new supported packet size value, add corresponding entry to the |
66 | | * lists, test the read/write msbc code, and fix the code if needed. |
67 | | */ |
68 | | static const size_t wbs_supported_packet_size[] = {60, 24, 48, 72, 0}; |
69 | | static const size_t wbs_hci_sco_buffer_size[] = {60, 120, 240, 360, 0}; |
70 | | |
71 | | /* Second octet of H2 header is composed by 4 bits fixed 0x8 and 4 bits |
72 | | * sequence number 0000, 0011, 1100, 1111. */ |
73 | | static const uint8_t h2_header_frames_count[] = {0x08, 0x38, 0xc8, 0xf8}; |
74 | | |
75 | | /* Structure to hold variables for a HFP connection. Since HFP supports |
76 | | * bi-direction audio, two iodevs should share one cras_sco if they |
77 | | * represent two directions of the same HFP headset |
78 | | */ |
79 | | struct cras_sco { |
80 | | // The file descriptor for SCO socket. |
81 | | int fd; |
82 | | // If the cras_sco has started to read/write SCO data. This is |
83 | | // only meaningful for non-offload case. |
84 | | int started; |
85 | | // The max transmit unit reported from BT adapter. |
86 | | unsigned int mtu; |
87 | | // The size of SCO packet to read/write preferred by |
88 | | // adapter, could be different than mtu. |
89 | | unsigned int packet_size; |
90 | | // The buffer to hold samples read from SCO socket. |
91 | | struct byte_buffer* capture_buf; |
92 | | // The buffer to hold samples about to write to SCO socket. |
93 | | struct byte_buffer* playback_buf; |
94 | | // mSBC codec to decode input audio in wideband speech mode. |
95 | | struct cras_audio_codec* msbc_read; |
96 | | // mSBC codec to encode output audio in wideband speech mode. |
97 | | struct cras_audio_codec* msbc_write; |
98 | | // PLC component to handle the packet loss of input audio in |
99 | | // wideband speech mode. |
100 | | struct cras_msbc_plc* msbc_plc; |
101 | | // Number of total written mSBC frames. |
102 | | unsigned int msbc_num_out_frames; |
103 | | // Number of total read mSBC frames. |
104 | | unsigned int msbc_num_in_frames; |
105 | | // Number of total lost mSBC frames. |
106 | | unsigned int msbc_num_lost_frames; |
107 | | // Callback to call when SCO socket can read. It returns the |
108 | | // number of PCM bytes read. |
109 | | int (*read_cb)(struct cras_sco* sco); |
110 | | // Callback to call when SCO socket can write. |
111 | | int (*write_cb)(struct cras_sco* sco); |
112 | | // Temp buffer for writing HCI SCO packet in wideband. |
113 | | uint8_t* write_buf; |
114 | | // Temp buffer for reading HCI SCO packet in wideband. |
115 | | uint8_t* read_buf; |
116 | | // The audio format bytes for input device. 0 means |
117 | | // there is no input device for the cras_sco. |
118 | | size_t input_format_bytes; |
119 | | // The audio format bytes for output device. 0 means |
120 | | // there is no output device for the cras_sco. |
121 | | size_t output_format_bytes; |
122 | | // Write pointer of write_buf. |
123 | | size_t write_wp; |
124 | | // Read pointer of write_buf. |
125 | | size_t write_rp; |
126 | | // Write pointer of read_buf. |
127 | | size_t read_wp; |
128 | | // Read pointer of read_buf. |
129 | | size_t read_rp; |
130 | | // Callback used to align mSBC frame reading with read buf. |
131 | | int (*read_align_cb)(uint8_t* buf); |
132 | | // Flag to mark if the current mSBC frame |
133 | | // read is corrupted. |
134 | | bool msbc_read_current_corrupted; |
135 | | // The logger for packet status in WBS. |
136 | | struct packet_status_logger* wbs_logger; |
137 | | // The buffer for saving the input to the sr. |
138 | | struct byte_buffer* sr_buf; |
139 | | // The sr instance. |
140 | | struct cras_sr* sr; |
141 | | // Indicates whether cras_sr is enabled. |
142 | | bool is_cras_sr_bt_enabled; |
143 | | // The associated bt device. |
144 | | struct cras_bt_device* device; |
145 | | }; |
146 | | |
147 | | static size_t wbs_get_supported_packet_size(size_t packet_size, |
148 | 0 | size_t* buffer_size) { |
149 | 0 | int i; |
150 | |
|
151 | 0 | for (i = 0; wbs_supported_packet_size[i] != 0; i++) { |
152 | 0 | if (packet_size == wbs_supported_packet_size[i]) { |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | } |
156 | | /* In case of unsupported value, error log and fallback to |
157 | | * MSBC_PKT_SIZE(60). */ |
158 | 0 | if (wbs_supported_packet_size[i] == 0) { |
159 | 0 | syslog(LOG_WARNING, "Unsupported packet size %zu", packet_size); |
160 | 0 | i = 0; |
161 | 0 | } |
162 | |
|
163 | 0 | if (buffer_size) { |
164 | 0 | *buffer_size = wbs_hci_sco_buffer_size[i]; |
165 | 0 | } |
166 | 0 | return wbs_supported_packet_size[i]; |
167 | 0 | } |
168 | | |
169 | | int cras_sco_add_iodev(struct cras_sco* sco, |
170 | | enum CRAS_STREAM_DIRECTION direction, |
171 | 0 | struct cras_audio_format* format) { |
172 | 0 | if (direction == CRAS_STREAM_OUTPUT) { |
173 | 0 | if (sco->output_format_bytes) { |
174 | 0 | goto invalid; |
175 | 0 | } |
176 | 0 | sco->output_format_bytes = cras_get_format_bytes(format); |
177 | |
|
178 | 0 | buf_reset(sco->playback_buf); |
179 | 0 | } else if (direction == CRAS_STREAM_INPUT) { |
180 | 0 | if (sco->input_format_bytes) { |
181 | 0 | goto invalid; |
182 | 0 | } |
183 | 0 | sco->input_format_bytes = cras_get_format_bytes(format); |
184 | |
|
185 | 0 | buf_reset(sco->capture_buf); |
186 | 0 | } |
187 | | |
188 | 0 | return 0; |
189 | | |
190 | 0 | invalid: |
191 | 0 | return -EINVAL; |
192 | 0 | } |
193 | | |
194 | | int cras_sco_rm_iodev(struct cras_sco* sco, |
195 | 0 | enum CRAS_STREAM_DIRECTION direction) { |
196 | 0 | if (direction == CRAS_STREAM_OUTPUT && sco->output_format_bytes) { |
197 | 0 | memset(sco->playback_buf->bytes, 0, sco->playback_buf->used_size); |
198 | 0 | sco->output_format_bytes = 0; |
199 | 0 | } else if (direction == CRAS_STREAM_INPUT && sco->input_format_bytes) { |
200 | 0 | sco->input_format_bytes = 0; |
201 | 0 | } else { |
202 | 0 | return -EINVAL; |
203 | 0 | } |
204 | | |
205 | 0 | return 0; |
206 | 0 | } |
207 | | |
208 | 0 | int cras_sco_has_iodev(struct cras_sco* sco) { |
209 | 0 | return sco->output_format_bytes || sco->input_format_bytes; |
210 | 0 | } |
211 | | |
212 | | void cras_sco_buf_acquire(struct cras_sco* sco, |
213 | | enum CRAS_STREAM_DIRECTION direction, |
214 | | uint8_t** buf, |
215 | 0 | unsigned* count) { |
216 | 0 | size_t format_bytes; |
217 | 0 | unsigned int buf_avail; |
218 | |
|
219 | 0 | if (direction == CRAS_STREAM_OUTPUT && sco->output_format_bytes) { |
220 | 0 | *buf = buf_write_pointer_size(sco->playback_buf, &buf_avail); |
221 | 0 | format_bytes = sco->output_format_bytes; |
222 | 0 | } else if (direction == CRAS_STREAM_INPUT && sco->input_format_bytes) { |
223 | 0 | *buf = buf_read_pointer_size(sco->capture_buf, &buf_avail); |
224 | 0 | format_bytes = sco->input_format_bytes; |
225 | 0 | } else { |
226 | 0 | *count = 0; |
227 | 0 | return; |
228 | 0 | } |
229 | | |
230 | 0 | if (*count * format_bytes > buf_avail) { |
231 | 0 | *count = buf_avail / format_bytes; |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | int cras_sco_buf_size(struct cras_sco* sco, |
236 | 0 | enum CRAS_STREAM_DIRECTION direction) { |
237 | 0 | if (direction == CRAS_STREAM_OUTPUT && sco->output_format_bytes) { |
238 | 0 | return sco->playback_buf->used_size / sco->output_format_bytes; |
239 | 0 | } else if (direction == CRAS_STREAM_INPUT && sco->input_format_bytes) { |
240 | 0 | return sco->capture_buf->used_size / sco->input_format_bytes; |
241 | 0 | } |
242 | 0 | return 0; |
243 | 0 | } |
244 | | |
245 | | void cras_sco_buf_release(struct cras_sco* sco, |
246 | | enum CRAS_STREAM_DIRECTION direction, |
247 | 0 | unsigned written_frames) { |
248 | 0 | if (direction == CRAS_STREAM_OUTPUT && sco->output_format_bytes) { |
249 | 0 | buf_increment_write(sco->playback_buf, |
250 | 0 | written_frames * sco->output_format_bytes); |
251 | 0 | } else if (direction == CRAS_STREAM_INPUT && sco->input_format_bytes) { |
252 | 0 | buf_increment_read(sco->capture_buf, |
253 | 0 | written_frames * sco->input_format_bytes); |
254 | 0 | } else { |
255 | 0 | written_frames = 0; |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | int cras_sco_buf_queued(struct cras_sco* sco, |
260 | 0 | enum CRAS_STREAM_DIRECTION direction) { |
261 | 0 | if (direction == CRAS_STREAM_OUTPUT && sco->output_format_bytes) { |
262 | 0 | return buf_queued(sco->playback_buf) / sco->output_format_bytes; |
263 | 0 | } else if (direction == CRAS_STREAM_INPUT && sco->input_format_bytes) { |
264 | 0 | return buf_queued(sco->capture_buf) / sco->input_format_bytes; |
265 | 0 | } else { |
266 | 0 | return 0; |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | int cras_sco_fill_output_with_zeros(struct cras_sco* sco, |
271 | 0 | unsigned int nframes) { |
272 | 0 | unsigned int buf_avail; |
273 | 0 | unsigned int nbytes; |
274 | 0 | uint8_t* buf; |
275 | 0 | int i; |
276 | 0 | int ret = 0; |
277 | |
|
278 | 0 | if (sco->output_format_bytes) { |
279 | 0 | nbytes = nframes * sco->output_format_bytes; |
280 | | // Loop twice to make sure ring buffer is filled. |
281 | 0 | for (i = 0; i < 2; i++) { |
282 | 0 | buf = buf_write_pointer_size(sco->playback_buf, &buf_avail); |
283 | 0 | if (buf_avail == 0) { |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | buf_avail = MIN(nbytes, buf_avail); |
287 | 0 | memset(buf, 0, buf_avail); |
288 | 0 | buf_increment_write(sco->playback_buf, buf_avail); |
289 | 0 | nbytes -= buf_avail; |
290 | 0 | ret += buf_avail / sco->output_format_bytes; |
291 | 0 | } |
292 | 0 | } |
293 | 0 | return ret; |
294 | 0 | } |
295 | | |
296 | 0 | void cras_sco_force_output_level(struct cras_sco* sco, unsigned int level) { |
297 | 0 | if (sco->output_format_bytes) { |
298 | 0 | level *= sco->output_format_bytes; |
299 | 0 | level = MIN(level, MAX_HFP_BUF_SIZE_BYTES); |
300 | 0 | buf_adjust_readable(sco->playback_buf, level); |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | 0 | int sco_write_msbc(struct cras_sco* sco) { |
305 | 0 | size_t encoded; |
306 | 0 | int err; |
307 | 0 | int pcm_encoded; |
308 | 0 | unsigned int pcm_avail, to_write; |
309 | 0 | uint8_t* samples; |
310 | 0 | uint8_t* wp; |
311 | |
|
312 | 0 | if (sco->write_rp + sco->packet_size <= sco->write_wp) { |
313 | 0 | goto msbc_send_again; |
314 | 0 | } |
315 | | |
316 | | // Make sure there are MSBC_CODE_SIZE bytes to encode. |
317 | 0 | samples = buf_read_pointer_size(sco->playback_buf, &pcm_avail); |
318 | 0 | if (pcm_avail < MSBC_CODE_SIZE) { |
319 | 0 | to_write = MSBC_CODE_SIZE - pcm_avail; |
320 | | /* |
321 | | * Size of playback_buf is multiple of MSBC_CODE_SIZE so we |
322 | | * are safe to prepare the buffer by appending some zero bytes. |
323 | | */ |
324 | 0 | wp = buf_write_pointer_size(sco->playback_buf, &pcm_avail); |
325 | 0 | memset(wp, 0, to_write); |
326 | 0 | buf_increment_write(sco->playback_buf, to_write); |
327 | |
|
328 | 0 | samples = buf_read_pointer_size(sco->playback_buf, &pcm_avail); |
329 | 0 | if (pcm_avail < MSBC_CODE_SIZE) { |
330 | 0 | return -EINVAL; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | // Encode the next MSBC_CODE_SIZE of bytes. |
335 | 0 | wp = sco->write_buf + sco->write_wp; |
336 | 0 | wp[0] = H2_HEADER_0; |
337 | 0 | wp[1] = h2_header_frames_count[sco->msbc_num_out_frames % 4]; |
338 | 0 | pcm_encoded = sco->msbc_write->encode( |
339 | 0 | sco->msbc_write, samples, pcm_avail, wp + MSBC_H2_HEADER_LEN, |
340 | 0 | MSBC_PKT_SIZE - MSBC_H2_HEADER_LEN, &encoded); |
341 | 0 | if (pcm_encoded < 0) { |
342 | 0 | syslog(LOG_WARNING, "msbc encoding err: %s", cras_strerror(pcm_encoded)); |
343 | 0 | return pcm_encoded; |
344 | 0 | } |
345 | | // The HFP spec specifies a zero padding byte in the end. |
346 | 0 | wp[MSBC_FRAME_SIZE] = 0; |
347 | 0 | buf_increment_read(sco->playback_buf, pcm_encoded); |
348 | 0 | pcm_avail -= pcm_encoded; |
349 | 0 | sco->write_wp += MSBC_PKT_SIZE; |
350 | 0 | sco->msbc_num_out_frames++; |
351 | |
|
352 | 0 | if (sco->write_rp + sco->packet_size > sco->write_wp) { |
353 | 0 | return 0; |
354 | 0 | } |
355 | | |
356 | 0 | msbc_send_again: |
357 | 0 | err = send(sco->fd, sco->write_buf + sco->write_rp, sco->packet_size, 0); |
358 | 0 | if (err < 0) { |
359 | 0 | if (errno == EINTR) { |
360 | 0 | goto msbc_send_again; |
361 | 0 | } |
362 | 0 | return err; |
363 | 0 | } |
364 | 0 | if (err != (int)sco->packet_size) { |
365 | 0 | syslog(LOG_WARNING, "Partially write %d bytes for mSBC", err); |
366 | 0 | return -EIO; |
367 | 0 | } |
368 | 0 | sco->write_rp += sco->packet_size; |
369 | 0 | if (sco->write_rp == sco->write_wp) { |
370 | 0 | sco->write_rp = 0; |
371 | 0 | sco->write_wp = 0; |
372 | 0 | } |
373 | |
|
374 | 0 | return err; |
375 | 0 | } |
376 | | |
377 | 0 | int sco_write(struct cras_sco* sco) { |
378 | 0 | int err = 0; |
379 | 0 | unsigned to_send; |
380 | 0 | uint8_t* samples; |
381 | | |
382 | | // Write something |
383 | 0 | samples = buf_read_pointer_size(sco->playback_buf, &to_send); |
384 | 0 | if (to_send < sco->packet_size) { |
385 | 0 | return 0; |
386 | 0 | } |
387 | 0 | to_send = sco->packet_size; |
388 | |
|
389 | 0 | send_sample: |
390 | 0 | err = send(sco->fd, samples, to_send, 0); |
391 | 0 | if (err < 0) { |
392 | 0 | if (errno == EINTR) { |
393 | 0 | goto send_sample; |
394 | 0 | } |
395 | | |
396 | 0 | return err; |
397 | 0 | } |
398 | | |
399 | 0 | if (err != (int)sco->packet_size) { |
400 | 0 | syslog(LOG_WARNING, "Partially write %d bytes for SCO packet size %u", err, |
401 | 0 | sco->packet_size); |
402 | 0 | return -EIO; |
403 | 0 | } |
404 | | |
405 | 0 | buf_increment_read(sco->playback_buf, to_send); |
406 | |
|
407 | 0 | return err; |
408 | 0 | } |
409 | | |
410 | 0 | static int h2_header_get_seq(const uint8_t* p) { |
411 | 0 | int i; |
412 | 0 | for (i = 0; i < 4; i++) { |
413 | 0 | if (*p == h2_header_frames_count[i]) { |
414 | 0 | return i; |
415 | 0 | } |
416 | 0 | } |
417 | 0 | return -ENOENT; |
418 | 0 | } |
419 | | |
420 | | /* |
421 | | * Extract mSBC frame from SCO socket input bytes, given that the mSBC frame |
422 | | * could be lost or corrupted. |
423 | | * Args: |
424 | | * input - Pointer to input bytes read from SCO socket. |
425 | | * len - Length of input bytes. |
426 | | * seq_out - To be filled by the sequence number of mSBC packet. |
427 | | * Returns: |
428 | | * The starting position of mSBC frame if found. |
429 | | */ |
430 | | static const uint8_t* extract_msbc_frame(const uint8_t* input, |
431 | | int len, |
432 | 0 | unsigned int* seq_out) { |
433 | 0 | int rp = 0; |
434 | 0 | int seq = -1; |
435 | 0 | while (len - rp >= MSBC_FRAME_SIZE) { |
436 | 0 | if ((input[rp] != H2_HEADER_0) || (input[rp + 2] != MSBC_SYNC_WORD)) { |
437 | 0 | rp++; |
438 | 0 | continue; |
439 | 0 | } |
440 | 0 | seq = h2_header_get_seq(input + rp + 1); |
441 | 0 | if (seq < 0) { |
442 | 0 | rp++; |
443 | 0 | continue; |
444 | 0 | } |
445 | | // `seq` is guaranteed to be positive now. |
446 | 0 | *seq_out = (unsigned int)seq; |
447 | 0 | return input + rp; |
448 | 0 | } |
449 | 0 | return NULL; |
450 | 0 | } |
451 | | |
452 | | // Log value 0 when packet is received. |
453 | 0 | static void log_wbs_packet_received(struct cras_sco* sco) { |
454 | 0 | if (sco->wbs_logger) { |
455 | 0 | packet_status_logger_update(sco->wbs_logger, 0); |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | | // Log value 1 when packet is lost. |
460 | 0 | static void log_wbs_packet_lost(struct cras_sco* sco) { |
461 | 0 | if (sco->wbs_logger) { |
462 | 0 | packet_status_logger_update(sco->wbs_logger, 1); |
463 | 0 | } |
464 | 0 | } |
465 | | |
466 | | /* |
467 | | * Handle the case when mSBC frame is considered lost. |
468 | | * Args: |
469 | | * sco - The cras_sco instance holding mSBC codec and PLC objects. |
470 | | */ |
471 | 0 | static int handle_packet_loss(struct cras_sco* sco) { |
472 | 0 | int decoded; |
473 | 0 | unsigned int pcm_avail; |
474 | 0 | uint8_t* in_bytes; |
475 | | |
476 | | /* It's possible client doesn't consume data causing overrun. In that |
477 | | * case we treat it as one mSBC frame read but dropped. */ |
478 | 0 | sco->msbc_num_in_frames++; |
479 | 0 | sco->msbc_num_lost_frames++; |
480 | |
|
481 | 0 | log_wbs_packet_lost(sco); |
482 | |
|
483 | 0 | in_bytes = buf_write_pointer_size(sco->capture_buf, &pcm_avail); |
484 | 0 | if (pcm_avail < MSBC_CODE_SIZE) { |
485 | 0 | return 0; |
486 | 0 | } |
487 | | |
488 | 0 | decoded = |
489 | 0 | cras_msbc_plc_handle_bad_frames(sco->msbc_plc, sco->msbc_read, in_bytes); |
490 | 0 | if (decoded < 0) { |
491 | 0 | return decoded; |
492 | 0 | } |
493 | | |
494 | 0 | buf_increment_write(sco->capture_buf, decoded); |
495 | |
|
496 | 0 | return decoded; |
497 | 0 | } |
498 | | |
499 | | // Checks if mSBC frame header aligns with the beginning of buffer. |
500 | 0 | static int msbc_frame_align(uint8_t* buf) { |
501 | 0 | if ((buf[0] != H2_HEADER_0) || (buf[2] != MSBC_SYNC_WORD)) { |
502 | 0 | syslog(LOG_DEBUG, "Waiting for valid mSBC frame head"); |
503 | 0 | return 0; |
504 | 0 | } |
505 | 0 | return 1; |
506 | 0 | } |
507 | | |
508 | 0 | int sco_read_msbc(struct cras_sco* sco) { |
509 | 0 | int err = 0; |
510 | 0 | unsigned int pcm_avail = 0; |
511 | 0 | int decoded; |
512 | 0 | size_t pcm_decoded = 0; |
513 | 0 | size_t pcm_read = 0; |
514 | 0 | uint8_t* capture_buf; |
515 | 0 | const uint8_t* frame_head = NULL; |
516 | 0 | unsigned int seq; |
517 | |
|
518 | 0 | struct msghdr msg = {0}; |
519 | 0 | struct iovec iov; |
520 | 0 | struct cmsghdr* cmsg; |
521 | 0 | const unsigned int control_size = CMSG_SPACE(sizeof(int)); |
522 | 0 | char control[control_size]; |
523 | 0 | uint8_t pkt_status; |
524 | |
|
525 | 0 | if (sco->read_rp + MSBC_PKT_SIZE <= sco->read_wp) { |
526 | 0 | goto extract; |
527 | 0 | } |
528 | | |
529 | 0 | memset(control, 0, sizeof(control)); |
530 | 0 | recv_msbc_bytes: |
531 | 0 | msg.msg_iov = &iov; |
532 | 0 | msg.msg_iovlen = 1; |
533 | 0 | iov.iov_base = sco->read_buf + sco->read_wp; |
534 | 0 | iov.iov_len = sco->packet_size; |
535 | 0 | msg.msg_control = control; |
536 | 0 | msg.msg_controllen = control_size; |
537 | |
|
538 | 0 | err = recvmsg(sco->fd, &msg, 0); |
539 | 0 | if (err < 0) { |
540 | 0 | syslog(LOG_WARNING, "HCI SCO packet read err %s", cras_strerror(errno)); |
541 | 0 | if (errno == EINTR) { |
542 | 0 | goto recv_msbc_bytes; |
543 | 0 | } |
544 | 0 | return err; |
545 | 0 | } |
546 | | /* |
547 | | * Treat return code 0 (socket shutdown) as error here. BT stack |
548 | | * shall send signal to main thread for device disconnection. |
549 | | */ |
550 | 0 | if (err != (int)sco->packet_size) { |
551 | | /* Allow the SCO packet size be modified from the default MTU |
552 | | * value to the size of SCO data we first read. This is for |
553 | | * some adapters who prefers a different value than MTU for |
554 | | * transmitting SCO packet. |
555 | | * Accept only supported packed sizes or fail. |
556 | | */ |
557 | 0 | if (err && (sco->packet_size == sco->mtu) && |
558 | 0 | err == wbs_get_supported_packet_size(err, NULL)) { |
559 | 0 | syslog(LOG_NOTICE, "Adjusting mSBC packet size, %d from %d bytes", err, |
560 | 0 | sco->packet_size); |
561 | 0 | sco->packet_size = err; |
562 | 0 | } else { |
563 | 0 | syslog(LOG_WARNING, "Partially read %d bytes for mSBC packet", err); |
564 | 0 | return -EIO; |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | | /* Offset in input data breaks mSBC frame parsing. Discard this packet |
569 | | * until read alignment succeed. */ |
570 | 0 | if (sco->read_align_cb) { |
571 | 0 | if (!sco->read_align_cb(sco->read_buf)) { |
572 | 0 | return 0; |
573 | 0 | } else { |
574 | 0 | sco->read_align_cb = NULL; |
575 | 0 | } |
576 | 0 | } |
577 | 0 | sco->read_wp += err; |
578 | |
|
579 | 0 | pkt_status = 0; |
580 | 0 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; |
581 | 0 | cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
582 | 0 | if (cmsg->cmsg_level == SOL_BLUETOOTH && |
583 | 0 | cmsg->cmsg_type == BT_SCM_PKT_STATUS) { |
584 | 0 | size_t len = cmsg->cmsg_len - sizeof(*cmsg); |
585 | 0 | memcpy(&pkt_status, CMSG_DATA(cmsg), len); |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | /* |
590 | | * HCI SCO packet status flag: |
591 | | * 0x00 - correctly received data. |
592 | | * 0x01 - possibly invalid data. |
593 | | * 0x10 - No data received. |
594 | | * 0x11 - Data partially lost. |
595 | | * |
596 | | * If the latest SCO packet read doesn't cross the boundary of a mSBC |
597 | | * frame, the packet status flag can be used to derive if the current |
598 | | * mSBC frame is corrupted. |
599 | | */ |
600 | 0 | if (sco->read_rp + MSBC_PKT_SIZE >= sco->read_wp) { |
601 | 0 | sco->msbc_read_current_corrupted |= (pkt_status > 0); |
602 | 0 | } |
603 | | |
604 | | // Read buffer not enough to parse another mSBC frame. |
605 | 0 | if (sco->read_rp + MSBC_PKT_SIZE > sco->read_wp) { |
606 | 0 | return 0; |
607 | 0 | } |
608 | | |
609 | 0 | extract: |
610 | 0 | if (sco->msbc_read_current_corrupted) { |
611 | 0 | syslog(LOG_DEBUG, "mSBC frame corrputed from packet status"); |
612 | 0 | sco->msbc_read_current_corrupted = 0; |
613 | 0 | frame_head = NULL; |
614 | 0 | } else { |
615 | 0 | frame_head = extract_msbc_frame(sco->read_buf + sco->read_rp, |
616 | 0 | sco->read_wp - sco->read_rp, &seq); |
617 | 0 | if (!frame_head) { |
618 | 0 | syslog(LOG_DEBUG, "Failed to extract msbc frame"); |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | | /* |
623 | | * Done with parsing the raw bytes just read. If mSBC frame head not |
624 | | * found, we shall handle it as packet loss. |
625 | | */ |
626 | 0 | sco->read_rp += MSBC_PKT_SIZE; |
627 | 0 | if (sco->read_rp == sco->read_wp) { |
628 | 0 | sco->read_rp = 0; |
629 | 0 | sco->read_wp = 0; |
630 | 0 | } |
631 | 0 | if (!frame_head) { |
632 | 0 | return handle_packet_loss(sco); |
633 | 0 | } |
634 | | |
635 | | /* |
636 | | * Consider packet loss when found discontinuity in sequence number. |
637 | | */ |
638 | 0 | while (seq != (sco->msbc_num_in_frames % 4)) { |
639 | 0 | syslog(LOG_DEBUG, "SCO packet seq unmatch"); |
640 | 0 | err = handle_packet_loss(sco); |
641 | 0 | if (err < 0) { |
642 | 0 | return err; |
643 | 0 | } |
644 | 0 | pcm_read += err; |
645 | 0 | } |
646 | | |
647 | | // Check if there's room for more PCM. |
648 | 0 | capture_buf = buf_write_pointer_size(sco->capture_buf, &pcm_avail); |
649 | 0 | if (pcm_avail < MSBC_CODE_SIZE) { |
650 | 0 | return pcm_read; |
651 | 0 | } |
652 | | |
653 | 0 | decoded = sco->msbc_read->decode( |
654 | 0 | sco->msbc_read, frame_head + MSBC_H2_HEADER_LEN, MSBC_FRAME_LEN, |
655 | 0 | capture_buf, pcm_avail, &pcm_decoded); |
656 | 0 | if (decoded < 0) { |
657 | | /* |
658 | | * If mSBC frame cannot be decoded, consider this packet is |
659 | | * corrupted and lost. |
660 | | */ |
661 | 0 | syslog(LOG_WARNING, "mSBC decode failed"); |
662 | 0 | err = handle_packet_loss(sco); |
663 | 0 | if (err < 0) { |
664 | 0 | return err; |
665 | 0 | } |
666 | 0 | pcm_read += err; |
667 | 0 | } else { |
668 | | // Good mSBC frame decoded. |
669 | 0 | log_wbs_packet_received(sco); |
670 | 0 | buf_increment_write(sco->capture_buf, pcm_decoded); |
671 | 0 | sco->msbc_num_in_frames++; |
672 | 0 | cras_msbc_plc_handle_good_frames(sco->msbc_plc, capture_buf, capture_buf); |
673 | 0 | pcm_read += pcm_decoded; |
674 | 0 | } |
675 | 0 | return pcm_read; |
676 | 0 | } |
677 | | |
678 | 0 | int sco_read(struct cras_sco* sco) { |
679 | 0 | int err = 0; |
680 | 0 | unsigned to_read; |
681 | 0 | uint8_t* capture_buf; |
682 | |
|
683 | 0 | capture_buf = buf_write_pointer_size(sco->capture_buf, &to_read); |
684 | |
|
685 | 0 | if (to_read < sco->packet_size) { |
686 | 0 | return 0; |
687 | 0 | } |
688 | 0 | to_read = sco->packet_size; |
689 | |
|
690 | 0 | recv_sample: |
691 | 0 | err = recv(sco->fd, capture_buf, to_read, 0); |
692 | 0 | if (err < 0) { |
693 | 0 | syslog(LOG_WARNING, "Read error %s", cras_strerror(errno)); |
694 | 0 | if (errno == EINTR) { |
695 | 0 | goto recv_sample; |
696 | 0 | } |
697 | | |
698 | 0 | return err; |
699 | 0 | } |
700 | | |
701 | 0 | if (err != (int)sco->packet_size) { |
702 | | /* Allow the SCO packet size be modified from the default MTU |
703 | | * value to the size of SCO data we first read. This is for |
704 | | * some adapters who prefers a different value than MTU for |
705 | | * transmitting SCO packet. |
706 | | */ |
707 | 0 | if (err && (sco->packet_size == sco->mtu)) { |
708 | 0 | syslog(LOG_NOTICE, "Adjusting SCO packet size, %d from %d bytes", err, |
709 | 0 | sco->packet_size); |
710 | 0 | sco->packet_size = err; |
711 | 0 | } else { |
712 | 0 | syslog(LOG_WARNING, "Partially read %d bytes for %u size SCO packet", err, |
713 | 0 | sco->packet_size); |
714 | 0 | return -EIO; |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | 0 | buf_increment_write(sco->capture_buf, err); |
719 | |
|
720 | 0 | return err; |
721 | 0 | } |
722 | | |
723 | 0 | static void swap_capture_buf_and_sr_buf(struct cras_sco* sco) { |
724 | 0 | struct byte_buffer* tmp = sco->sr_buf; |
725 | 0 | sco->sr_buf = sco->capture_buf; |
726 | 0 | sco->capture_buf = tmp; |
727 | 0 | } |
728 | | |
729 | | /* Callback function to handle sample read and write. |
730 | | * Note that we poll the SCO socket for read sample, since it reflects |
731 | | * there is actual some sample to read while the socket always reports |
732 | | * writable even when device buffer is full. |
733 | | * The strategy is to synchronize read & write operations: |
734 | | * 1. Read one chunk of MTU bytes of data. |
735 | | * 2. When input device not attached, ignore the data just read. |
736 | | * 3. When output device attached, write one chunk of MTU bytes of data. |
737 | | */ |
738 | 0 | static int cras_sco_callback(void* arg, int revents) { |
739 | 0 | struct cras_sco* sco = (struct cras_sco*)arg; |
740 | 0 | int err = 0; |
741 | |
|
742 | 0 | if (!sco->started) { |
743 | 0 | return 0; |
744 | 0 | } |
745 | | |
746 | | // Allow last read before handling error or hang-up events. |
747 | 0 | if (revents & POLLIN) { |
748 | 0 | if (sco->is_cras_sr_bt_enabled) { |
749 | 0 | swap_capture_buf_and_sr_buf(sco); |
750 | 0 | err = sco->read_cb(sco); |
751 | 0 | swap_capture_buf_and_sr_buf(sco); |
752 | 0 | } else { |
753 | 0 | err = sco->read_cb(sco); |
754 | 0 | } |
755 | 0 | if (err < 0) { |
756 | 0 | syslog(LOG_WARNING, "Read error"); |
757 | 0 | goto read_write_error; |
758 | 0 | } |
759 | 0 | if (sco->is_cras_sr_bt_enabled) { |
760 | 0 | int num_consumed = |
761 | 0 | cras_sr_process(sco->sr, sco->sr_buf, sco->capture_buf); |
762 | 0 | if (num_consumed < err) { |
763 | 0 | syslog(LOG_DEBUG, |
764 | 0 | "Number of consumed samples is less than provided. (%d < %d).", |
765 | 0 | num_consumed, err); |
766 | 0 | } |
767 | 0 | } |
768 | 0 | } |
769 | | // Ignore the bytes just read if input dev not in present |
770 | 0 | if (!sco->input_format_bytes) { |
771 | 0 | buf_increment_read(sco->capture_buf, err); |
772 | 0 | } |
773 | |
|
774 | 0 | if (revents & (POLLERR | POLLHUP)) { |
775 | 0 | syslog(LOG_WARNING, "Error polling SCO socket, revent %d", revents); |
776 | 0 | if (revents & POLLHUP) { |
777 | 0 | syslog(LOG_DEBUG, "Received POLLHUP, reconnecting HFP."); |
778 | 0 | audio_thread_rm_callback(sco->fd); |
779 | 0 | cras_bt_device_hfp_reconnect(sco->device); |
780 | 0 | return 0; |
781 | 0 | } |
782 | 0 | goto read_write_error; |
783 | 0 | } |
784 | | |
785 | | /* Without output stream's presence, we shall still send zero packets |
786 | | * to HF. This is required for some HF devices to start sending non-zero |
787 | | * data to AG. |
788 | | */ |
789 | 0 | if (!sco->output_format_bytes) { |
790 | 0 | buf_increment_write(sco->playback_buf, |
791 | 0 | sco->msbc_write ? err : sco->packet_size); |
792 | 0 | } |
793 | |
|
794 | 0 | err = sco->write_cb(sco); |
795 | 0 | if (err < 0) { |
796 | 0 | syslog(LOG_WARNING, "Write error"); |
797 | 0 | goto read_write_error; |
798 | 0 | } |
799 | | |
800 | 0 | return 0; |
801 | | |
802 | 0 | read_write_error: |
803 | | /* |
804 | | * This callback is executing in audio thread, so it's safe to |
805 | | * unregister itself by audio_thread_rm_callback(). |
806 | | */ |
807 | 0 | audio_thread_rm_callback(sco->fd); |
808 | 0 | close(sco->fd); |
809 | 0 | sco->fd = -1; |
810 | 0 | return 0; |
811 | 0 | } |
812 | | |
813 | 0 | struct cras_sco* cras_sco_create(struct cras_bt_device* device) { |
814 | 0 | struct cras_sco* sco; |
815 | 0 | sco = (struct cras_sco*)calloc(1, sizeof(*sco)); |
816 | 0 | if (!sco) { |
817 | 0 | goto error; |
818 | 0 | } |
819 | | |
820 | 0 | sco->capture_buf = byte_buffer_create(MAX_HFP_BUF_SIZE_BYTES); |
821 | 0 | if (!sco->capture_buf) { |
822 | 0 | goto error; |
823 | 0 | } |
824 | | |
825 | 0 | sco->playback_buf = byte_buffer_create(MAX_HFP_BUF_SIZE_BYTES); |
826 | 0 | if (!sco->playback_buf) { |
827 | 0 | goto error; |
828 | 0 | } |
829 | 0 | sco->fd = -1; |
830 | 0 | sco->device = device; |
831 | |
|
832 | 0 | return sco; |
833 | | |
834 | 0 | error: |
835 | 0 | if (sco) { |
836 | 0 | if (sco->capture_buf) { |
837 | 0 | byte_buffer_destroy(&sco->capture_buf); |
838 | 0 | } |
839 | 0 | if (sco->playback_buf) { |
840 | 0 | byte_buffer_destroy(&sco->playback_buf); |
841 | 0 | } |
842 | 0 | free(sco); |
843 | 0 | } |
844 | 0 | return NULL; |
845 | 0 | } |
846 | | |
847 | | int cras_sco_enable_cras_sr_bt(struct cras_sco* sco, |
848 | 0 | enum cras_sr_bt_model model) { |
849 | 0 | int rc = 0; |
850 | |
|
851 | 0 | sco->sr_buf = byte_buffer_create(MAX_HFP_BUF_SIZE_BYTES); |
852 | 0 | if (!sco->sr_buf) { |
853 | 0 | syslog(LOG_ERR, "byte_buffer_create failed."); |
854 | 0 | rc = -ENOMEM; |
855 | 0 | goto cras_sco_enable_cras_sr_bt_failed; |
856 | 0 | } |
857 | | |
858 | 0 | sco->sr = cras_sr_create(cras_sr_bt_get_model_spec(model), |
859 | 0 | buf_available(sco->sr_buf)); |
860 | 0 | if (!sco->sr) { |
861 | 0 | syslog(LOG_WARNING, "cras_sr_create failed."); |
862 | 0 | rc = -ENOENT; |
863 | 0 | goto cras_sco_enable_cras_sr_bt_failed; |
864 | 0 | } |
865 | | |
866 | 0 | sco->is_cras_sr_bt_enabled = true; |
867 | |
|
868 | 0 | return 0; |
869 | | |
870 | 0 | cras_sco_enable_cras_sr_bt_failed: |
871 | 0 | cras_sco_disable_cras_sr_bt(sco); |
872 | 0 | return rc; |
873 | 0 | } |
874 | | |
875 | 0 | void cras_sco_disable_cras_sr_bt(struct cras_sco* sco) { |
876 | 0 | byte_buffer_destroy(&sco->sr_buf); |
877 | 0 | cras_sr_destroy(sco->sr); |
878 | 0 | sco->sr = NULL; |
879 | 0 | sco->is_cras_sr_bt_enabled = false; |
880 | 0 | } |
881 | | |
882 | | void cras_sco_set_wbs_logger(struct cras_sco* sco, |
883 | 0 | struct packet_status_logger* wbs_logger) { |
884 | 0 | sco->wbs_logger = wbs_logger; |
885 | 0 | } |
886 | | |
887 | 0 | int cras_sco_set_fd(struct cras_sco* sco, int fd) { |
888 | | /* Valid only when existing fd isn't set and the new fd is |
889 | | * non-negative to prevent leak. */ |
890 | 0 | if (sco->fd >= 0 || fd < 0) { |
891 | 0 | return -EINVAL; |
892 | 0 | } |
893 | 0 | sco->fd = fd; |
894 | 0 | return 0; |
895 | 0 | } |
896 | | |
897 | 0 | int cras_sco_get_fd(struct cras_sco* sco) { |
898 | 0 | return sco->fd; |
899 | 0 | } |
900 | | |
901 | 0 | int cras_sco_close_fd(struct cras_sco* sco) { |
902 | 0 | int ret; |
903 | 0 | if (sco->fd < 0) { |
904 | 0 | return -EINVAL; |
905 | 0 | } |
906 | 0 | ret = close(sco->fd); |
907 | 0 | sco->fd = -1; |
908 | 0 | return ret < 0 ? -errno : 0; |
909 | 0 | } |
910 | | |
911 | 0 | int cras_sco_running(struct cras_sco* sco) { |
912 | 0 | return sco->started; |
913 | 0 | } |
914 | | |
915 | 0 | int cras_sco_start(unsigned int mtu, int codec, struct cras_sco* sco) { |
916 | 0 | int ret; |
917 | |
|
918 | 0 | if (sco->fd < 0) { |
919 | 0 | syslog(LOG_WARNING, "Start SCO without valid fd(%d) set", sco->fd); |
920 | 0 | return -EINVAL; |
921 | 0 | } |
922 | | |
923 | 0 | sco->mtu = mtu; |
924 | | |
925 | | // Initialize to MTU, it may change when actually read the socket. |
926 | 0 | sco->packet_size = mtu; |
927 | 0 | buf_reset(sco->playback_buf); |
928 | 0 | buf_reset(sco->capture_buf); |
929 | 0 | if (sco->sr_buf) { |
930 | 0 | buf_reset(sco->sr_buf); |
931 | 0 | } |
932 | |
|
933 | 0 | if (codec == HFP_CODEC_ID_MSBC) { |
934 | 0 | size_t packet_size; |
935 | 0 | size_t buffer_size; |
936 | |
|
937 | 0 | packet_size = wbs_get_supported_packet_size(sco->packet_size, &buffer_size); |
938 | 0 | sco->packet_size = packet_size; |
939 | 0 | sco->write_buf = (uint8_t*)calloc(buffer_size, sizeof(*sco->write_buf)); |
940 | 0 | sco->read_buf = (uint8_t*)calloc(buffer_size, sizeof(*sco->read_buf)); |
941 | 0 | if (!sco->write_buf || !sco->read_buf) { |
942 | 0 | ret = -ENOMEM; |
943 | 0 | goto mem_err; |
944 | 0 | } |
945 | | |
946 | 0 | sco->write_cb = sco_write_msbc; |
947 | 0 | sco->read_cb = sco_read_msbc; |
948 | 0 | sco->msbc_read = cras_msbc_codec_create(); |
949 | 0 | sco->msbc_write = cras_msbc_codec_create(); |
950 | 0 | sco->msbc_plc = cras_msbc_plc_create(); |
951 | |
|
952 | 0 | packet_status_logger_init(sco->wbs_logger); |
953 | 0 | } else { |
954 | 0 | sco->write_cb = sco_write; |
955 | 0 | sco->read_cb = sco_read; |
956 | 0 | } |
957 | | |
958 | 0 | audio_thread_add_events_callback(sco->fd, cras_sco_callback, sco, |
959 | 0 | POLLIN | POLLERR | POLLHUP); |
960 | |
|
961 | 0 | sco->started = 1; |
962 | 0 | sco->msbc_num_out_frames = 0; |
963 | 0 | sco->msbc_num_in_frames = 0; |
964 | 0 | sco->msbc_num_lost_frames = 0; |
965 | 0 | sco->write_rp = 0; |
966 | 0 | sco->write_wp = 0; |
967 | 0 | sco->read_rp = 0; |
968 | 0 | sco->read_wp = 0; |
969 | | |
970 | | // Mark as aligned if packet size equals to MSBC_PKT_SIZE. |
971 | 0 | sco->read_align_cb = |
972 | 0 | (sco->packet_size == MSBC_PKT_SIZE) ? NULL : msbc_frame_align; |
973 | 0 | sco->msbc_read_current_corrupted = 0; |
974 | 0 | return 0; |
975 | 0 | mem_err: |
976 | 0 | free(sco->write_buf); |
977 | 0 | free(sco->read_buf); |
978 | 0 | return ret; |
979 | 0 | } |
980 | | |
981 | 0 | int cras_sco_stop(struct cras_sco* sco) { |
982 | 0 | int ret; |
983 | |
|
984 | 0 | if (!sco->started) { |
985 | 0 | syslog(LOG_WARNING, "stop sco that hasn't been started"); |
986 | 0 | return 0; |
987 | 0 | } |
988 | | |
989 | 0 | ret = audio_thread_rm_callback_sync(cras_iodev_list_get_audio_thread(), |
990 | 0 | sco->fd); |
991 | 0 | if (ret < 0) { |
992 | 0 | syslog(LOG_WARNING, "rm callback sync error %d", ret); |
993 | 0 | } |
994 | 0 | sco->started = 0; |
995 | 0 | ret = cras_sco_close_fd(sco); |
996 | 0 | if (ret < 0) { |
997 | 0 | syslog(LOG_WARNING, "failed to close sco fd: %d", ret); |
998 | 0 | } |
999 | | |
1000 | | // Unset the write/read callbacks. |
1001 | 0 | sco->write_cb = NULL; |
1002 | 0 | sco->read_cb = NULL; |
1003 | |
|
1004 | 0 | if (sco->write_buf) { |
1005 | 0 | free(sco->write_buf); |
1006 | 0 | sco->write_buf = NULL; |
1007 | 0 | } |
1008 | 0 | if (sco->read_buf) { |
1009 | 0 | free(sco->read_buf); |
1010 | 0 | sco->read_buf = NULL; |
1011 | 0 | } |
1012 | |
|
1013 | 0 | if (sco->msbc_read) { |
1014 | 0 | cras_sbc_codec_destroy(sco->msbc_read); |
1015 | 0 | sco->msbc_read = NULL; |
1016 | 0 | } |
1017 | 0 | if (sco->msbc_write) { |
1018 | 0 | cras_sbc_codec_destroy(sco->msbc_write); |
1019 | 0 | sco->msbc_write = NULL; |
1020 | 0 | } |
1021 | 0 | if (sco->msbc_plc) { |
1022 | 0 | cras_msbc_plc_destroy(sco->msbc_plc); |
1023 | 0 | sco->msbc_plc = NULL; |
1024 | 0 | } |
1025 | |
|
1026 | 0 | if (sco->msbc_num_in_frames) { |
1027 | 0 | cras_server_metrics_hfp_packet_loss((float)sco->msbc_num_lost_frames / |
1028 | 0 | sco->msbc_num_in_frames); |
1029 | 0 | } |
1030 | |
|
1031 | 0 | cras_sco_disable_cras_sr_bt(sco); |
1032 | |
|
1033 | 0 | return 0; |
1034 | 0 | } |
1035 | | |
1036 | 0 | void cras_sco_destroy(struct cras_sco* sco) { |
1037 | 0 | if (sco->capture_buf) { |
1038 | 0 | byte_buffer_destroy(&sco->capture_buf); |
1039 | 0 | } |
1040 | |
|
1041 | 0 | if (sco->playback_buf) { |
1042 | 0 | byte_buffer_destroy(&sco->playback_buf); |
1043 | 0 | } |
1044 | |
|
1045 | 0 | if (sco->sr_buf) { |
1046 | 0 | byte_buffer_destroy(&sco->sr_buf); |
1047 | 0 | } |
1048 | |
|
1049 | 0 | free(sco); |
1050 | 0 | } |