1 /* Spa SCO Sink
2 *
3 * Copyright © 2019 Collabora Ltd.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <unistd.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <arpa/inet.h>
29 #include <sys/ioctl.h>
30
31 #include <spa/support/plugin.h>
32 #include <spa/support/loop.h>
33 #include <spa/support/log.h>
34 #include <spa/support/system.h>
35 #include <spa/utils/result.h>
36 #include <spa/utils/list.h>
37 #include <spa/utils/keys.h>
38 #include <spa/utils/names.h>
39 #include <spa/utils/string.h>
40 #include <spa/monitor/device.h>
41
42 #include <spa/node/node.h>
43 #include <spa/node/utils.h>
44 #include <spa/node/io.h>
45 #include <spa/node/keys.h>
46 #include <spa/param/param.h>
47 #include <spa/param/latency-utils.h>
48 #include <spa/param/audio/format.h>
49 #include <spa/param/audio/format-utils.h>
50 #include <spa/pod/filter.h>
51
52 #include <sbc/sbc.h>
53
54 #include "defs.h"
55
56 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.sco");
57 #undef SPA_LOG_TOPIC_DEFAULT
58 #define SPA_LOG_TOPIC_DEFAULT &log_topic
59
60 #define DEFAULT_CLOCK_NAME "clock.system.monotonic"
61
62 struct props {
63 uint32_t min_latency;
64 uint32_t max_latency;
65 char clock_name[64];
66 };
67
68 #define MAX_BUFFERS 32
69
70 struct buffer {
71 uint32_t id;
72 unsigned int outstanding:1;
73 struct spa_buffer *buf;
74 struct spa_meta_header *h;
75 struct spa_list link;
76 };
77
78 struct port {
79 struct spa_audio_info current_format;
80 int frame_size;
81 unsigned int have_format:1;
82
83 uint64_t info_all;
84 struct spa_port_info info;
85 struct spa_io_buffers *io;
86 struct spa_io_rate_match *rate_match;
87 struct spa_latency_info latency;
88 #define IDX_EnumFormat 0
89 #define IDX_Meta 1
90 #define IDX_IO 2
91 #define IDX_Format 3
92 #define IDX_Buffers 4
93 #define IDX_Latency 5
94 #define N_PORT_PARAMS 6
95 struct spa_param_info params[N_PORT_PARAMS];
96
97 struct buffer buffers[MAX_BUFFERS];
98 uint32_t n_buffers;
99
100 struct spa_list free;
101 struct spa_list ready;
102
103 struct buffer *current_buffer;
104 uint32_t ready_offset;
105 uint8_t write_buffer[4096];
106 uint32_t write_buffer_size;
107 };
108
109 struct impl {
110 struct spa_handle handle;
111 struct spa_node node;
112
113 /* Support */
114 struct spa_log *log;
115 struct spa_loop *data_loop;
116 struct spa_system *data_system;
117
118 /* Hooks and callbacks */
119 struct spa_hook_list hooks;
120 struct spa_callbacks callbacks;
121
122 /* Info */
123 uint64_t info_all;
124 struct spa_node_info info;
125 #define IDX_PropInfo 0
126 #define IDX_Props 1
127 #define N_NODE_PARAMS 2
128 struct spa_param_info params[N_NODE_PARAMS];
129 struct props props;
130
131 /* Transport */
132 struct spa_bt_transport *transport;
133 struct spa_hook transport_listener;
134
135 /* Port */
136 struct port port;
137
138 /* Flags */
139 unsigned int started:1;
140 unsigned int following:1;
141
142 /* Sources */
143 struct spa_source source;
144
145 /* Timer */
146 int timerfd;
147 struct timespec now;
148 struct spa_io_clock *clock;
149 struct spa_io_position *position;
150
151 /* mSBC */
152 sbc_t msbc;
153 uint8_t *buffer;
154 uint8_t *buffer_head;
155 uint8_t *buffer_next;
156 int buffer_size;
157
158 /* Times */
159 uint64_t start_time;
160 uint64_t total_samples;
161 };
162
163 #define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0)
164
165 static const uint32_t default_min_latency = MIN_LATENCY;
166 static const uint32_t default_max_latency = MAX_LATENCY;
167 static const char sntable[4] = { 0x08, 0x38, 0xC8, 0xF8 };
168
reset_props(struct props * props)169 static void reset_props(struct props *props)
170 {
171 props->min_latency = default_min_latency;
172 props->max_latency = default_max_latency;
173 strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name));
174 }
175
impl_node_enum_params(void * object,int seq,uint32_t id,uint32_t start,uint32_t num,const struct spa_pod * filter)176 static int impl_node_enum_params(void *object, int seq,
177 uint32_t id, uint32_t start, uint32_t num,
178 const struct spa_pod *filter)
179 {
180 struct impl *this = object;
181 struct spa_pod *param;
182 struct spa_pod_builder b = { 0 };
183 uint8_t buffer[1024];
184 struct spa_result_node_params result;
185 uint32_t count = 0;
186
187 spa_return_val_if_fail(this != NULL, -EINVAL);
188 spa_return_val_if_fail(num != 0, -EINVAL);
189
190 result.id = id;
191 result.next = start;
192 next:
193 result.index = result.next++;
194
195 spa_pod_builder_init(&b, buffer, sizeof(buffer));
196
197 switch (id) {
198 case SPA_PARAM_PropInfo:
199 {
200 struct props *p = &this->props;
201
202 switch (result.index) {
203 case 0:
204 param = spa_pod_builder_add_object(&b,
205 SPA_TYPE_OBJECT_PropInfo, id,
206 SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_minLatency),
207 SPA_PROP_INFO_name, SPA_POD_String("The minimum latency"),
208 SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->min_latency, 1, INT32_MAX));
209 break;
210 case 1:
211 param = spa_pod_builder_add_object(&b,
212 SPA_TYPE_OBJECT_PropInfo, id,
213 SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_maxLatency),
214 SPA_PROP_INFO_name, SPA_POD_String("The maximum latency"),
215 SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->max_latency, 1, INT32_MAX));
216 break;
217 default:
218 return 0;
219 }
220 break;
221 }
222 case SPA_PARAM_Props:
223 {
224 struct props *p = &this->props;
225
226 switch (result.index) {
227 case 0:
228 param = spa_pod_builder_add_object(&b,
229 SPA_TYPE_OBJECT_Props, id,
230 SPA_PROP_minLatency, SPA_POD_Int(p->min_latency),
231 SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency));
232 break;
233 default:
234 return 0;
235 }
236 break;
237 }
238 default:
239 return -ENOENT;
240 }
241
242 if (spa_pod_filter(&b, &result.param, param, filter) < 0)
243 goto next;
244
245 spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
246
247 if (++count != num)
248 goto next;
249
250 return 0;
251 }
252
set_timeout(struct impl * this,uint64_t time)253 static int set_timeout(struct impl *this, uint64_t time)
254 {
255 struct itimerspec ts;
256 ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC;
257 ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC;
258 ts.it_interval.tv_sec = 0;
259 ts.it_interval.tv_nsec = 0;
260 return spa_system_timerfd_settime(this->data_system,
261 this->timerfd, 0, &ts, NULL);
262 }
263
set_timers(struct impl * this)264 static int set_timers(struct impl *this)
265 {
266 return set_timeout(this, this->following ? 0 : 1);
267 }
268
get_next_timeout(struct impl * this,uint64_t now_time,uint64_t processed_samples)269 static uint64_t get_next_timeout(struct impl *this, uint64_t now_time, uint64_t processed_samples)
270 {
271 struct port *port = &this->port;
272 uint64_t playback_time = 0, elapsed_time = 0, next_time = 1;
273
274 this->total_samples += processed_samples;
275
276 playback_time = (this->total_samples * SPA_NSEC_PER_SEC) / port->current_format.info.raw.rate;
277 if (now_time > this->start_time)
278 elapsed_time = now_time - this->start_time;
279 if (elapsed_time < playback_time)
280 next_time = playback_time - elapsed_time;
281
282 return next_time;
283 }
284
do_reassign_follower(struct spa_loop * loop,bool async,uint32_t seq,const void * data,size_t size,void * user_data)285 static int do_reassign_follower(struct spa_loop *loop,
286 bool async,
287 uint32_t seq,
288 const void *data,
289 size_t size,
290 void *user_data)
291 {
292 struct impl *this = user_data;
293 set_timers(this);
294 return 0;
295 }
296
is_following(struct impl * this)297 static inline bool is_following(struct impl *this)
298 {
299 return this->position && this->clock && this->position->clock.id != this->clock->id;
300 }
301
impl_node_set_io(void * object,uint32_t id,void * data,size_t size)302 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
303 {
304 struct impl *this = object;
305 bool following;
306
307 spa_return_val_if_fail(this != NULL, -EINVAL);
308
309 switch (id) {
310 case SPA_IO_Clock:
311 this->clock = data;
312 if (this->clock != NULL) {
313 spa_scnprintf(this->clock->name,
314 sizeof(this->clock->name),
315 "%s", this->props.clock_name);
316 }
317 break;
318 case SPA_IO_Position:
319 this->position = data;
320 break;
321 default:
322 return -ENOENT;
323 }
324
325 following = is_following(this);
326 if (this->started && following != this->following) {
327 spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following);
328 this->following = following;
329 spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this);
330 }
331 return 0;
332 }
333
334 static void emit_node_info(struct impl *this, bool full);
335
apply_props(struct impl * this,const struct spa_pod * param)336 static int apply_props(struct impl *this, const struct spa_pod *param)
337 {
338 struct props new_props = this->props;
339 int changed = 0;
340
341 if (param == NULL) {
342 reset_props(&new_props);
343 } else {
344 spa_pod_parse_object(param,
345 SPA_TYPE_OBJECT_Props, NULL,
346 SPA_PROP_minLatency, SPA_POD_OPT_Int(&new_props.min_latency),
347 SPA_PROP_maxLatency, SPA_POD_OPT_Int(&new_props.max_latency));
348 }
349
350 changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0);
351 this->props = new_props;
352 return changed;
353 }
354
impl_node_set_param(void * object,uint32_t id,uint32_t flags,const struct spa_pod * param)355 static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
356 const struct spa_pod *param)
357 {
358 struct impl *this = object;
359
360 spa_return_val_if_fail(this != NULL, -EINVAL);
361
362 switch (id) {
363 case SPA_PARAM_Props:
364 {
365 if (apply_props(this, param) > 0) {
366 this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
367 this->params[IDX_Props].flags ^= SPA_PARAM_INFO_SERIAL;
368 emit_node_info(this, false);
369 }
370 break;
371 }
372 default:
373 return -ENOENT;
374 }
375
376 return 0;
377 }
378
flush_data(struct impl * this)379 static void flush_data(struct impl *this)
380 {
381 struct port *port = &this->port;
382 struct spa_data *datas;
383 uint64_t next_timeout = 1;
384 const uint32_t min_in_size =
385 (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
386 MSBC_DECODED_SIZE : this->transport->write_mtu;
387 uint8_t * const packet =
388 (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
389 this->buffer_head : port->write_buffer;
390
391 if (this->transport == NULL || this->transport->sco_io == NULL)
392 return;
393
394 if (!spa_list_is_empty(&port->ready)) {
395 /* get buffer */
396 if (!port->current_buffer) {
397 spa_return_if_fail(!spa_list_is_empty(&port->ready));
398 port->current_buffer = spa_list_first(&port->ready, struct buffer, link);
399 port->ready_offset = 0;
400 }
401 datas = port->current_buffer->buf->datas;
402
403 /* if buffer has data, copy it into the write buffer */
404 if (datas[0].chunk->size - port->ready_offset > 0) {
405 const uint32_t avail =
406 SPA_MIN(min_in_size, datas[0].chunk->size - port->ready_offset);
407 const uint32_t size =
408 (avail + port->write_buffer_size) > min_in_size ?
409 min_in_size - port->write_buffer_size : avail;
410 memcpy(port->write_buffer + port->write_buffer_size,
411 (uint8_t *)datas[0].data + port->ready_offset,
412 size);
413 port->write_buffer_size += size;
414 port->ready_offset += size;
415 } else {
416 struct buffer *b;
417
418 b = port->current_buffer;
419 port->current_buffer = NULL;
420
421 /* reuse buffer */
422 spa_list_remove(&b->link);
423 b->outstanding = true;
424 spa_log_trace(this->log, "sco-sink %p: reuse buffer %u", this, b->id);
425 port->io->buffer_id = b->id;
426 spa_node_call_reuse_buffer(&this->callbacks, 0, b->id);
427 }
428 }
429
430 /* send the data if the write buffer is full */
431 if (port->write_buffer_size >= min_in_size) {
432 uint64_t now_time;
433 static int sn = 0;
434 int processed = 0;
435 ssize_t out_encoded;
436 int written;
437
438 spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &this->now);
439 now_time = SPA_TIMESPEC_TO_NSEC(&this->now);
440 if (this->start_time == 0)
441 this->start_time = now_time;
442
443 if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
444 /* Encode */
445 if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) {
446 /* Buffer overrun; shouldn't usually happen. Drop data and reset. */
447 this->buffer_head = this->buffer_next = this->buffer;
448 spa_log_warn(this->log, "sco-sink: mSBC buffer overrun, dropping data");
449 }
450 this->buffer_next[0] = 0x01;
451 this->buffer_next[1] = sntable[sn];
452 this->buffer_next[59] = 0x00;
453 sn = (sn + 1) % 4;
454 processed = sbc_encode(&this->msbc, port->write_buffer, port->write_buffer_size,
455 this->buffer_next + 2, MSBC_ENCODED_SIZE - 3, &out_encoded);
456 if (processed < 0) {
457 spa_log_warn(this->log, "sbc_encode failed: %d", processed);
458 return;
459 }
460 this->buffer_next += out_encoded + 3;
461 port->write_buffer_size = 0;
462
463 /* Write */
464 written = spa_bt_sco_io_write(this->transport->sco_io, packet,
465 this->buffer_next - this->buffer_head);
466 if (written < 0) {
467 spa_log_warn(this->log, "failed to write data: %d (%s)",
468 written, spa_strerror(written));
469 goto stop;
470 }
471 spa_log_trace(this->log, "wrote socket data %d", written);
472
473 this->buffer_head += written;
474
475 if (this->buffer_head == this->buffer_next)
476 this->buffer_head = this->buffer_next = this->buffer;
477 else if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) {
478 /* Written bytes is not necessarily commensurate
479 * with MSBC_ENCODED_SIZE. If this occurs, copy data.
480 */
481 int size = this->buffer_next - this->buffer_head;
482 spa_memmove(this->buffer, this->buffer_head, size);
483 this->buffer_next = this->buffer + size;
484 this->buffer_head = this->buffer;
485 }
486 } else {
487 written = spa_bt_sco_io_write(this->transport->sco_io, packet,
488 port->write_buffer_size);
489 if (written < 0) {
490 spa_log_warn(this->log, "sco-sink: write failure: %d (%s)",
491 written, spa_strerror(written));
492 goto stop;
493 } else if (written == 0) {
494 /* EAGAIN or similar, just skip ahead */
495 written = SPA_MIN(port->write_buffer_size, (uint32_t)48);
496 }
497
498 processed = written;
499 port->write_buffer_size -= written;
500
501 if (port->write_buffer_size > 0 && written > 0) {
502 spa_memmove(port->write_buffer, port->write_buffer + written, port->write_buffer_size);
503 }
504 }
505
506 spa_log_trace(this->log, "write socket data %d", written);
507
508 next_timeout = get_next_timeout(this, now_time, processed / port->frame_size);
509
510 if (this->clock) {
511 this->clock->nsec = now_time;
512 this->clock->position = this->total_samples;
513 this->clock->delay = processed / port->frame_size;
514 this->clock->rate_diff = 1.0f;
515 this->clock->next_nsec = next_timeout;
516 }
517 }
518
519 /* schedule next timeout */
520 set_timeout(this, next_timeout);
521 return;
522
523 stop:
524 if (this->source.loop)
525 spa_loop_remove_source(this->data_loop, &this->source);
526 }
527
sco_on_timeout(struct spa_source * source)528 static void sco_on_timeout(struct spa_source *source)
529 {
530 struct impl *this = source->data;
531 struct port *port = &this->port;
532 uint64_t exp;
533
534 if (this->transport == NULL)
535 return;
536
537 /* Read the timerfd */
538 if (this->started && spa_system_timerfd_read(this->data_system, this->timerfd, &exp) < 0)
539 spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno));
540
541 /* delay if no buffers available */
542 if (!this->following && spa_list_is_empty(&port->ready)) {
543 set_timeout(this, this->transport->write_mtu / port->frame_size * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate);
544 port->io->status = SPA_STATUS_NEED_DATA;
545 spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA);
546 return;
547 }
548
549 /* Flush data */
550 flush_data(this);
551 }
552
553 /* greater common divider */
gcd(int a,int b)554 static int gcd(int a, int b) {
555 while(b) {
556 int c = b;
557 b = a % b;
558 a = c;
559 }
560 return a;
561 }
562 /* least common multiple */
lcm(int a,int b)563 static int lcm(int a, int b) {
564 return (a*b)/gcd(a,b);
565 }
566
do_start(struct impl * this)567 static int do_start(struct impl *this)
568 {
569 bool do_accept;
570 int res;
571
572 /* Don't do anything if the node has already started */
573 if (this->started)
574 return 0;
575
576 /* Make sure the transport is valid */
577 spa_return_val_if_fail(this->transport != NULL, -EIO);
578
579 this->following = is_following(this);
580
581 spa_log_debug(this->log, "%p: start following:%d", this, this->following);
582
583 /* Do accept if Gateway; otherwise do connect for Head Unit */
584 do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
585
586 /* acquire the socket fd (false -> connect | true -> accept) */
587 if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0)
588 return res;
589
590 /* Init mSBC if needed */
591 if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
592 sbc_init_msbc(&this->msbc, 0);
593 /* Libsbc expects audio samples by default in host endianness, mSBC requires little endian */
594 this->msbc.endian = SBC_LE;
595
596 /* write_mtu might not be correct at this point, so we'll throw
597 * in some common ones, at the cost of a potentially larger
598 * allocation (size <= 120 * write_mtu). If it still fails to be
599 * commensurate, we may end up doing memmoves, but nothing worse
600 * is going to happen.
601 */
602 this->buffer_size = lcm(24, lcm(60, lcm(this->transport->write_mtu, 2 * MSBC_ENCODED_SIZE)));
603 this->buffer = calloc(this->buffer_size, sizeof(uint8_t));
604 this->buffer_head = this->buffer_next = this->buffer;
605 }
606
607 spa_return_val_if_fail(this->transport->write_mtu <= sizeof(this->port.write_buffer), -EINVAL);
608
609 /* start socket i/o */
610 if ((res = spa_bt_transport_ensure_sco_io(this->transport, this->data_loop)) < 0)
611 goto fail;
612
613 /* Add the timeout callback */
614 this->source.data = this;
615 this->source.fd = this->timerfd;
616 this->source.func = sco_on_timeout;
617 this->source.mask = SPA_IO_IN;
618 this->source.rmask = 0;
619 spa_loop_add_source(this->data_loop, &this->source);
620
621 /* start processing */
622 set_timers(this);
623
624 /* Set the started flag */
625 this->started = true;
626
627 return 0;
628
629 fail:
630 free(this->buffer);
631 this->buffer = NULL;
632 spa_bt_transport_release(this->transport);
633 return res;
634 }
635
636 /* Drop any buffered data remaining in the port */
drop_port_output(struct impl * this)637 static void drop_port_output(struct impl *this)
638 {
639 struct port *port = &this->port;
640
641 port->write_buffer_size = 0;
642 port->current_buffer = NULL;
643 port->ready_offset = 0;
644
645 while (!spa_list_is_empty(&port->ready)) {
646 struct buffer *b;
647 b = spa_list_first(&port->ready, struct buffer, link);
648
649 spa_list_remove(&b->link);
650 b->outstanding = true;
651 port->io->buffer_id = b->id;
652 spa_node_call_reuse_buffer(&this->callbacks, 0, b->id);
653 }
654 }
655
do_remove_source(struct spa_loop * loop,bool async,uint32_t seq,const void * data,size_t size,void * user_data)656 static int do_remove_source(struct spa_loop *loop,
657 bool async,
658 uint32_t seq,
659 const void *data,
660 size_t size,
661 void *user_data)
662 {
663 struct impl *this = user_data;
664
665 this->start_time = 0;
666 this->total_samples = 0;
667 set_timeout(this, 0);
668 if (this->source.loop)
669 spa_loop_remove_source(this->data_loop, &this->source);
670
671 /* Drop buffered data in the ready queue. Ideally there shouldn't be any. */
672 drop_port_output(this);
673
674 return 0;
675 }
676
do_stop(struct impl * this)677 static int do_stop(struct impl *this)
678 {
679 int res = 0;
680
681 if (!this->started)
682 return 0;
683
684 spa_log_trace(this->log, "sco-sink %p: stop", this);
685
686 spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this);
687
688 this->started = false;
689
690 if (this->buffer) {
691 free(this->buffer);
692 this->buffer = NULL;
693 this->buffer_head = this->buffer_next = this->buffer;
694 }
695
696 if (this->transport) {
697 /* Release the transport; it is responsible for closing the fd */
698 res = spa_bt_transport_release(this->transport);
699 }
700
701 return res;
702 }
703
impl_node_send_command(void * object,const struct spa_command * command)704 static int impl_node_send_command(void *object, const struct spa_command *command)
705 {
706 struct impl *this = object;
707 struct port *port;
708 int res;
709
710 spa_return_val_if_fail(this != NULL, -EINVAL);
711 spa_return_val_if_fail(command != NULL, -EINVAL);
712
713 port = &this->port;
714
715 switch (SPA_NODE_COMMAND_ID(command)) {
716 case SPA_NODE_COMMAND_Start:
717 if (!port->have_format)
718 return -EIO;
719 if (port->n_buffers == 0)
720 return -EIO;
721 if ((res = do_start(this)) < 0)
722 return res;
723 break;
724 case SPA_NODE_COMMAND_Pause:
725 case SPA_NODE_COMMAND_Suspend:
726 if ((res = do_stop(this)) < 0)
727 return res;
728 break;
729 default:
730 return -ENOTSUP;
731 }
732 return 0;
733 }
734
emit_node_info(struct impl * this,bool full)735 static void emit_node_info(struct impl *this, bool full)
736 {
737 static const struct spa_dict_item hu_node_info_items[] = {
738 { SPA_KEY_DEVICE_API, "bluez5" },
739 { SPA_KEY_MEDIA_CLASS, "Audio/Sink" },
740 { SPA_KEY_NODE_DRIVER, "true" },
741 };
742
743 const struct spa_dict_item ag_node_info_items[] = {
744 { SPA_KEY_DEVICE_API, "bluez5" },
745 { SPA_KEY_MEDIA_CLASS, "Stream/Input/Audio" },
746 { "media.name", ((this->transport && this->transport->device->name) ?
747 this->transport->device->name : "HSP/HFP") },
748 };
749 bool is_ag = this->transport &&
750 (this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY);
751 uint64_t old = full ? this->info.change_mask : 0;
752
753 if (full)
754 this->info.change_mask = this->info_all;
755 if (this->info.change_mask) {
756 this->info.props = is_ag ?
757 &SPA_DICT_INIT_ARRAY(ag_node_info_items) :
758 &SPA_DICT_INIT_ARRAY(hu_node_info_items);
759 spa_node_emit_info(&this->hooks, &this->info);
760 this->info.change_mask = old;
761 }
762 }
763
emit_port_info(struct impl * this,struct port * port,bool full)764 static void emit_port_info(struct impl *this, struct port *port, bool full)
765 {
766 uint64_t old = full ? port->info.change_mask : 0;
767 if (full)
768 port->info.change_mask = port->info_all;
769 if (port->info.change_mask) {
770 spa_node_emit_port_info(&this->hooks,
771 SPA_DIRECTION_INPUT, 0, &port->info);
772 port->info.change_mask = old;
773 }
774 }
775
776 static int
impl_node_add_listener(void * object,struct spa_hook * listener,const struct spa_node_events * events,void * data)777 impl_node_add_listener(void *object,
778 struct spa_hook *listener,
779 const struct spa_node_events *events,
780 void *data)
781 {
782 struct impl *this = object;
783 struct spa_hook_list save;
784
785 spa_return_val_if_fail(this != NULL, -EINVAL);
786
787 spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
788
789 emit_node_info(this, true);
790 emit_port_info(this, &this->port, true);
791
792 spa_hook_list_join(&this->hooks, &save);
793
794 return 0;
795 }
796
797 static int
impl_node_set_callbacks(void * object,const struct spa_node_callbacks * callbacks,void * data)798 impl_node_set_callbacks(void *object,
799 const struct spa_node_callbacks *callbacks,
800 void *data)
801 {
802 struct impl *this = object;
803
804 spa_return_val_if_fail(this != NULL, -EINVAL);
805
806 this->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
807
808 return 0;
809 }
810
impl_node_sync(void * object,int seq)811 static int impl_node_sync(void *object, int seq)
812 {
813 struct impl *this = object;
814
815 spa_return_val_if_fail(this != NULL, -EINVAL);
816
817 spa_node_emit_result(&this->hooks, seq, 0, 0, NULL);
818
819 return 0;
820 }
821
impl_node_add_port(void * object,enum spa_direction direction,uint32_t port_id,const struct spa_dict * props)822 static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id,
823 const struct spa_dict *props)
824 {
825 return -ENOTSUP;
826 }
827
impl_node_remove_port(void * object,enum spa_direction direction,uint32_t port_id)828 static int impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id)
829 {
830 return -ENOTSUP;
831 }
832
833 static int
impl_node_port_enum_params(void * object,int seq,enum spa_direction direction,uint32_t port_id,uint32_t id,uint32_t start,uint32_t num,const struct spa_pod * filter)834 impl_node_port_enum_params(void *object, int seq,
835 enum spa_direction direction, uint32_t port_id,
836 uint32_t id, uint32_t start, uint32_t num,
837 const struct spa_pod *filter)
838 {
839
840 struct impl *this = object;
841 struct port *port;
842 struct spa_pod *param;
843 struct spa_pod_builder b = { 0 };
844 uint8_t buffer[1024];
845 struct spa_result_node_params result;
846 uint32_t count = 0;
847
848 spa_return_val_if_fail(this != NULL, -EINVAL);
849 spa_return_val_if_fail(num != 0, -EINVAL);
850
851 spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
852 port = &this->port;
853
854 result.id = id;
855 result.next = start;
856 next:
857 result.index = result.next++;
858
859 spa_pod_builder_init(&b, buffer, sizeof(buffer));
860
861 switch (id) {
862 case SPA_PARAM_EnumFormat:
863 if (result.index > 0)
864 return 0;
865 if (this->transport == NULL)
866 return -EIO;
867
868 /* set the info structure */
869 struct spa_audio_info_raw info = { 0, };
870 info.format = SPA_AUDIO_FORMAT_S16_LE;
871 info.channels = 1;
872 info.position[0] = SPA_AUDIO_CHANNEL_MONO;
873
874 /* CVSD format has a rate of 8kHz
875 * MSBC format has a rate of 16kHz */
876 if (this->transport->codec == HFP_AUDIO_CODEC_MSBC)
877 info.rate = 16000;
878 else
879 info.rate = 8000;
880
881 /* build the param */
882 param = spa_format_audio_raw_build(&b, id, &info);
883
884 break;
885
886 case SPA_PARAM_Format:
887 if (!port->have_format)
888 return -EIO;
889 if (result.index > 0)
890 return 0;
891
892 param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw);
893 break;
894
895 case SPA_PARAM_Buffers:
896 if (!port->have_format)
897 return -EIO;
898 if (result.index > 0)
899 return 0;
900
901 param = spa_pod_builder_add_object(&b,
902 SPA_TYPE_OBJECT_ParamBuffers, id,
903 SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
904 SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
905 SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
906 this->props.max_latency * port->frame_size,
907 this->props.min_latency * port->frame_size,
908 INT32_MAX),
909 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size));
910 break;
911
912 case SPA_PARAM_Meta:
913 switch (result.index) {
914 case 0:
915 param = spa_pod_builder_add_object(&b,
916 SPA_TYPE_OBJECT_ParamMeta, id,
917 SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
918 SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
919 break;
920 default:
921 return 0;
922 }
923 break;
924
925 case SPA_PARAM_IO:
926 switch (result.index) {
927 case 0:
928 param = spa_pod_builder_add_object(&b,
929 SPA_TYPE_OBJECT_ParamIO, id,
930 SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers),
931 SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
932 break;
933 case 1:
934 param = spa_pod_builder_add_object(&b,
935 SPA_TYPE_OBJECT_ParamIO, id,
936 SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_RateMatch),
937 SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_rate_match)));
938 break;
939 default:
940 return 0;
941 }
942 break;
943
944 case SPA_PARAM_Latency:
945 switch (result.index) {
946 case 0:
947 param = spa_latency_build(&b, id, &port->latency);
948 break;
949 default:
950 return 0;
951 }
952 break;
953
954 default:
955 return -ENOENT;
956 }
957
958 if (spa_pod_filter(&b, &result.param, param, filter) < 0)
959 goto next;
960
961 spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
962
963 if (++count != num)
964 goto next;
965
966 return 0;
967 }
968
clear_buffers(struct impl * this,struct port * port)969 static int clear_buffers(struct impl *this, struct port *port)
970 {
971 do_stop(this);
972 if (port->n_buffers > 0) {
973 spa_list_init(&port->ready);
974 port->n_buffers = 0;
975 }
976 return 0;
977 }
978
port_set_format(struct impl * this,struct port * port,uint32_t flags,const struct spa_pod * format)979 static int port_set_format(struct impl *this, struct port *port,
980 uint32_t flags,
981 const struct spa_pod *format)
982 {
983 int err;
984
985 if (format == NULL) {
986 spa_log_debug(this->log, "clear format");
987 clear_buffers(this, port);
988 port->have_format = false;
989 } else {
990 struct spa_audio_info info = { 0 };
991
992 if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
993 return err;
994
995 if (info.media_type != SPA_MEDIA_TYPE_audio ||
996 info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
997 return -EINVAL;
998
999 if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
1000 return -EINVAL;
1001
1002 port->frame_size = info.info.raw.channels * 2;
1003 port->current_format = info;
1004 port->have_format = true;
1005 }
1006
1007 port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
1008 if (port->have_format) {
1009 port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
1010 port->info.flags = SPA_PORT_FLAG_LIVE;
1011 port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
1012 port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate);
1013 port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
1014 port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
1015 port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
1016 } else {
1017 port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
1018 port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
1019 }
1020 emit_port_info(this, port, false);
1021
1022 return 0;
1023 }
1024
1025 static int
impl_node_port_set_param(void * object,enum spa_direction direction,uint32_t port_id,uint32_t id,uint32_t flags,const struct spa_pod * param)1026 impl_node_port_set_param(void *object,
1027 enum spa_direction direction, uint32_t port_id,
1028 uint32_t id, uint32_t flags,
1029 const struct spa_pod *param)
1030 {
1031 struct impl *this = object;
1032 struct port *port;
1033 int res;
1034
1035 spa_return_val_if_fail(this != NULL, -EINVAL);
1036 spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL);
1037 port = &this->port;
1038
1039 switch (id) {
1040 case SPA_PARAM_Format:
1041 res = port_set_format(this, port, flags, param);
1042 break;
1043 case SPA_PARAM_Latency:
1044 res = 0;
1045 break;
1046 default:
1047 res = -ENOENT;
1048 break;
1049 }
1050 return res;
1051 }
1052
1053 static int
impl_node_port_use_buffers(void * object,enum spa_direction direction,uint32_t port_id,uint32_t flags,struct spa_buffer ** buffers,uint32_t n_buffers)1054 impl_node_port_use_buffers(void *object,
1055 enum spa_direction direction, uint32_t port_id,
1056 uint32_t flags,
1057 struct spa_buffer **buffers, uint32_t n_buffers)
1058 {
1059 struct impl *this = object;
1060 struct port *port;
1061 uint32_t i;
1062
1063 spa_return_val_if_fail(this != NULL, -EINVAL);
1064 spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
1065 port = &this->port;
1066
1067 spa_log_debug(this->log, "use buffers %d", n_buffers);
1068
1069 if (!port->have_format)
1070 return -EIO;
1071
1072 clear_buffers(this, port);
1073
1074 for (i = 0; i < n_buffers; i++) {
1075 struct buffer *b = &port->buffers[i];
1076
1077 b->buf = buffers[i];
1078 b->id = i;
1079 b->outstanding = true;
1080
1081 b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
1082
1083 if (buffers[i]->datas[0].data == NULL) {
1084 spa_log_error(this->log, "%p: need mapped memory", this);
1085 return -EINVAL;
1086 }
1087 }
1088 port->n_buffers = n_buffers;
1089
1090 return 0;
1091 }
1092
1093 static int
impl_node_port_set_io(void * object,enum spa_direction direction,uint32_t port_id,uint32_t id,void * data,size_t size)1094 impl_node_port_set_io(void *object,
1095 enum spa_direction direction,
1096 uint32_t port_id,
1097 uint32_t id,
1098 void *data, size_t size)
1099 {
1100 struct impl *this = object;
1101 struct port *port;
1102
1103 spa_return_val_if_fail(this != NULL, -EINVAL);
1104
1105 spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
1106 port = &this->port;
1107
1108 switch (id) {
1109 case SPA_IO_Buffers:
1110 port->io = data;
1111 break;
1112 case SPA_IO_RateMatch:
1113 port->rate_match = data;
1114 break;
1115 default:
1116 return -ENOENT;
1117 }
1118 return 0;
1119 }
1120
impl_node_port_reuse_buffer(void * object,uint32_t port_id,uint32_t buffer_id)1121 static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
1122 {
1123 return -ENOTSUP;
1124 }
1125
impl_node_process(void * object)1126 static int impl_node_process(void *object)
1127 {
1128 struct impl *this = object;
1129 struct port *port;
1130 struct spa_io_buffers *io;
1131
1132 spa_return_val_if_fail(this != NULL, -EINVAL);
1133
1134 port = &this->port;
1135 io = port->io;
1136 spa_return_val_if_fail(io != NULL, -EIO);
1137
1138 if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) {
1139 struct buffer *b = &port->buffers[io->buffer_id];
1140
1141 if (!b->outstanding) {
1142 spa_log_warn(this->log, "%p: buffer %u in use", this, io->buffer_id);
1143 io->status = -EINVAL;
1144 return -EINVAL;
1145 }
1146
1147 spa_log_trace(this->log, "%p: queue buffer %u", this, io->buffer_id);
1148
1149 spa_list_append(&port->ready, &b->link);
1150 b->outstanding = false;
1151 io->buffer_id = SPA_ID_INVALID;
1152 io->status = SPA_STATUS_OK;
1153 }
1154
1155 if (!spa_list_is_empty(&port->ready))
1156 flush_data(this);
1157
1158 return SPA_STATUS_HAVE_DATA;
1159 }
1160
1161 static const struct spa_node_methods impl_node = {
1162 SPA_VERSION_NODE_METHODS,
1163 .add_listener = impl_node_add_listener,
1164 .set_callbacks = impl_node_set_callbacks,
1165 .sync = impl_node_sync,
1166 .enum_params = impl_node_enum_params,
1167 .set_param = impl_node_set_param,
1168 .set_io = impl_node_set_io,
1169 .send_command = impl_node_send_command,
1170 .add_port = impl_node_add_port,
1171 .remove_port = impl_node_remove_port,
1172 .port_enum_params = impl_node_port_enum_params,
1173 .port_set_param = impl_node_port_set_param,
1174 .port_use_buffers = impl_node_port_use_buffers,
1175 .port_set_io = impl_node_port_set_io,
1176 .port_reuse_buffer = impl_node_port_reuse_buffer,
1177 .process = impl_node_process,
1178 };
1179
do_transport_destroy(struct spa_loop * loop,bool async,uint32_t seq,const void * data,size_t size,void * user_data)1180 static int do_transport_destroy(struct spa_loop *loop,
1181 bool async,
1182 uint32_t seq,
1183 const void *data,
1184 size_t size,
1185 void *user_data)
1186 {
1187 struct impl *this = user_data;
1188 this->transport = NULL;
1189 return 0;
1190 }
1191
transport_destroy(void * data)1192 static void transport_destroy(void *data)
1193 {
1194 struct impl *this = data;
1195 spa_log_debug(this->log, "transport %p destroy", this->transport);
1196 spa_loop_invoke(this->data_loop, do_transport_destroy, 0, NULL, 0, true, this);
1197 }
1198
1199 static const struct spa_bt_transport_events transport_events = {
1200 SPA_VERSION_BT_TRANSPORT_EVENTS,
1201 .destroy = transport_destroy,
1202 };
1203
impl_get_interface(struct spa_handle * handle,const char * type,void ** interface)1204 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
1205 {
1206 struct impl *this;
1207
1208 spa_return_val_if_fail(handle != NULL, -EINVAL);
1209 spa_return_val_if_fail(interface != NULL, -EINVAL);
1210
1211 this = (struct impl *) handle;
1212
1213 if (spa_streq(type, SPA_TYPE_INTERFACE_Node))
1214 *interface = &this->node;
1215 else
1216 return -ENOENT;
1217
1218 return 0;
1219 }
1220
impl_clear(struct spa_handle * handle)1221 static int impl_clear(struct spa_handle *handle)
1222 {
1223 struct impl *this = (struct impl *) handle;
1224 if (this->transport)
1225 spa_hook_remove(&this->transport_listener);
1226 spa_system_close(this->data_system, this->timerfd);
1227 return 0;
1228 }
1229
1230 static size_t
impl_get_size(const struct spa_handle_factory * factory,const struct spa_dict * params)1231 impl_get_size(const struct spa_handle_factory *factory,
1232 const struct spa_dict *params)
1233 {
1234 return sizeof(struct impl);
1235 }
1236
1237 static int
impl_init(const struct spa_handle_factory * factory,struct spa_handle * handle,const struct spa_dict * info,const struct spa_support * support,uint32_t n_support)1238 impl_init(const struct spa_handle_factory *factory,
1239 struct spa_handle *handle,
1240 const struct spa_dict *info,
1241 const struct spa_support *support,
1242 uint32_t n_support)
1243 {
1244 struct impl *this;
1245 struct port *port;
1246 const char *str;
1247
1248 spa_return_val_if_fail(factory != NULL, -EINVAL);
1249 spa_return_val_if_fail(handle != NULL, -EINVAL);
1250
1251 handle->get_interface = impl_get_interface;
1252 handle->clear = impl_clear;
1253
1254 this = (struct impl *) handle;
1255
1256 this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
1257 this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
1258 this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem);
1259
1260 spa_log_topic_init(this->log, &log_topic);
1261
1262 if (this->data_loop == NULL) {
1263 spa_log_error(this->log, "a data loop is needed");
1264 return -EINVAL;
1265 }
1266 if (this->data_system == NULL) {
1267 spa_log_error(this->log, "a data system is needed");
1268 return -EINVAL;
1269 }
1270
1271 this->node.iface = SPA_INTERFACE_INIT(
1272 SPA_TYPE_INTERFACE_Node,
1273 SPA_VERSION_NODE,
1274 &impl_node, this);
1275 spa_hook_list_init(&this->hooks);
1276
1277 reset_props(&this->props);
1278
1279 this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
1280 SPA_NODE_CHANGE_MASK_PARAMS |
1281 SPA_NODE_CHANGE_MASK_PROPS;
1282 this->info = SPA_NODE_INFO_INIT();
1283 this->info.max_input_ports = 1;
1284 this->info.max_output_ports = 0;
1285 this->info.flags = SPA_NODE_FLAG_RT;
1286 this->params[IDX_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
1287 this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
1288 this->info.params = this->params;
1289 this->info.n_params = N_NODE_PARAMS;
1290
1291 port = &this->port;
1292 port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
1293 SPA_PORT_CHANGE_MASK_PARAMS;
1294 port->info = SPA_PORT_INFO_INIT();
1295 port->info.flags = 0;
1296 port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
1297 port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
1298 port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
1299 port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
1300 port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
1301 port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
1302 port->info.params = port->params;
1303 port->info.n_params = N_PORT_PARAMS;
1304
1305 port->latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
1306 port->latency.min_quantum = 1.0f;
1307 port->latency.max_quantum = 1.0f;
1308
1309 spa_list_init(&port->ready);
1310
1311 if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT)))
1312 sscanf(str, "pointer:%p", &this->transport);
1313
1314 if (this->transport == NULL) {
1315 spa_log_error(this->log, "a transport is needed");
1316 return -EINVAL;
1317 }
1318 spa_bt_transport_add_listener(this->transport,
1319 &this->transport_listener, &transport_events, this);
1320
1321 this->timerfd = spa_system_timerfd_create(this->data_system,
1322 CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
1323
1324 return 0;
1325 }
1326
1327 static const struct spa_interface_info impl_interfaces[] = {
1328 {SPA_TYPE_INTERFACE_Node,},
1329 };
1330
1331 static int
impl_enum_interface_info(const struct spa_handle_factory * factory,const struct spa_interface_info ** info,uint32_t * index)1332 impl_enum_interface_info(const struct spa_handle_factory *factory,
1333 const struct spa_interface_info **info, uint32_t *index)
1334 {
1335 spa_return_val_if_fail(factory != NULL, -EINVAL);
1336 spa_return_val_if_fail(info != NULL, -EINVAL);
1337 spa_return_val_if_fail(index != NULL, -EINVAL);
1338
1339 switch (*index) {
1340 case 0:
1341 *info = &impl_interfaces[*index];
1342 break;
1343 default:
1344 return 0;
1345 }
1346 (*index)++;
1347 return 1;
1348 }
1349
1350 static const struct spa_dict_item info_items[] = {
1351 { SPA_KEY_FACTORY_AUTHOR, "Collabora Ltd. <contact@collabora.com>" },
1352 { SPA_KEY_FACTORY_DESCRIPTION, "Play bluetooth audio with hsp/hfp" },
1353 { SPA_KEY_FACTORY_USAGE, SPA_KEY_API_BLUEZ5_TRANSPORT"=<transport>" },
1354 };
1355
1356 static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items);
1357
1358 const struct spa_handle_factory spa_sco_sink_factory = {
1359 SPA_VERSION_HANDLE_FACTORY,
1360 SPA_NAME_API_BLUEZ5_SCO_SINK,
1361 &info,
1362 impl_get_size,
1363 impl_init,
1364 impl_enum_interface_info,
1365 };
1366