153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2008-2013 João Paulo Rechi Vita 553a5a1b3Sopenharmony_ci Copyright 2011-2013 BMW Car IT GmbH. 653a5a1b3Sopenharmony_ci Copyright 2018-2019 Pali Rohár <pali.rohar@gmail.com> 753a5a1b3Sopenharmony_ci 853a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 953a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 1053a5a1b3Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of the 1153a5a1b3Sopenharmony_ci License, or (at your option) any later version. 1253a5a1b3Sopenharmony_ci 1353a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1453a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1553a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1653a5a1b3Sopenharmony_ci General Public License for more details. 1753a5a1b3Sopenharmony_ci 1853a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public 1953a5a1b3Sopenharmony_ci License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 2053a5a1b3Sopenharmony_ci***/ 2153a5a1b3Sopenharmony_ci 2253a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2353a5a1b3Sopenharmony_ci#include <config.h> 2453a5a1b3Sopenharmony_ci#endif 2553a5a1b3Sopenharmony_ci 2653a5a1b3Sopenharmony_ci#include <errno.h> 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <arpa/inet.h> 2953a5a1b3Sopenharmony_ci 3053a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 3153a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 3253a5a1b3Sopenharmony_ci#include <pulse/utf8.h> 3353a5a1b3Sopenharmony_ci#include <pulse/util.h> 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/core-rtclock.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/json.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/message-handler.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/poll.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/shared.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/socket-util.h> 4753a5a1b3Sopenharmony_ci#include <pulsecore/thread.h> 4853a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h> 4953a5a1b3Sopenharmony_ci 5053a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 5153a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother_2.h> 5253a5a1b3Sopenharmony_ci#else 5353a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother.h> 5453a5a1b3Sopenharmony_ci#endif 5553a5a1b3Sopenharmony_ci 5653a5a1b3Sopenharmony_ci#include "a2dp-codecs.h" 5753a5a1b3Sopenharmony_ci#include "a2dp-codec-util.h" 5853a5a1b3Sopenharmony_ci#include "bluez5-util.h" 5953a5a1b3Sopenharmony_ci 6053a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("João Paulo Rechi Vita"); 6153a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source"); 6253a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 6353a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 6453a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 6553a5a1b3Sopenharmony_ci "path=<device object path>" 6653a5a1b3Sopenharmony_ci "autodetect_mtu=<boolean>" 6753a5a1b3Sopenharmony_ci "output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>" 6853a5a1b3Sopenharmony_ci "avrcp_absolute_volume=<synchronize volume with peer, true by default>" 6953a5a1b3Sopenharmony_ci); 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_ci#define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC) 7253a5a1b3Sopenharmony_ci#define FIXED_LATENCY_PLAYBACK_SCO (25 * PA_USEC_PER_MSEC) 7353a5a1b3Sopenharmony_ci#define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC) 7453a5a1b3Sopenharmony_ci#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC) 7553a5a1b3Sopenharmony_ci 7653a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 7753a5a1b3Sopenharmony_ci "path", 7853a5a1b3Sopenharmony_ci "autodetect_mtu", 7953a5a1b3Sopenharmony_ci "output_rate_refresh_interval_ms", 8053a5a1b3Sopenharmony_ci "avrcp_absolute_volume", 8153a5a1b3Sopenharmony_ci NULL 8253a5a1b3Sopenharmony_ci}; 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_cienum { 8553a5a1b3Sopenharmony_ci BLUETOOTH_MESSAGE_IO_THREAD_FAILED, 8653a5a1b3Sopenharmony_ci BLUETOOTH_MESSAGE_STREAM_FD_HUP, 8753a5a1b3Sopenharmony_ci BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING, 8853a5a1b3Sopenharmony_ci BLUETOOTH_MESSAGE_MAX 8953a5a1b3Sopenharmony_ci}; 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_cienum { 9253a5a1b3Sopenharmony_ci PA_SOURCE_MESSAGE_SETUP_STREAM = PA_SOURCE_MESSAGE_MAX, 9353a5a1b3Sopenharmony_ci}; 9453a5a1b3Sopenharmony_ci 9553a5a1b3Sopenharmony_cienum { 9653a5a1b3Sopenharmony_ci PA_SINK_MESSAGE_SETUP_STREAM = PA_SINK_MESSAGE_MAX, 9753a5a1b3Sopenharmony_ci}; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_citypedef struct bluetooth_msg { 10053a5a1b3Sopenharmony_ci pa_msgobject parent; 10153a5a1b3Sopenharmony_ci pa_card *card; 10253a5a1b3Sopenharmony_ci} bluetooth_msg; 10353a5a1b3Sopenharmony_ciPA_DEFINE_PRIVATE_CLASS(bluetooth_msg, pa_msgobject); 10453a5a1b3Sopenharmony_ci#define BLUETOOTH_MSG(o) (bluetooth_msg_cast(o)) 10553a5a1b3Sopenharmony_ci 10653a5a1b3Sopenharmony_cistruct userdata { 10753a5a1b3Sopenharmony_ci pa_module *module; 10853a5a1b3Sopenharmony_ci pa_core *core; 10953a5a1b3Sopenharmony_ci 11053a5a1b3Sopenharmony_ci pa_hook_slot *device_connection_changed_slot; 11153a5a1b3Sopenharmony_ci pa_hook_slot *device_battery_level_changed_slot; 11253a5a1b3Sopenharmony_ci pa_hook_slot *transport_state_changed_slot; 11353a5a1b3Sopenharmony_ci pa_hook_slot *transport_sink_volume_changed_slot; 11453a5a1b3Sopenharmony_ci pa_hook_slot *transport_source_volume_changed_slot; 11553a5a1b3Sopenharmony_ci 11653a5a1b3Sopenharmony_ci pa_hook_slot *sink_volume_changed_slot; 11753a5a1b3Sopenharmony_ci pa_hook_slot *source_volume_changed_slot; 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci pa_bluetooth_discovery *discovery; 12053a5a1b3Sopenharmony_ci pa_bluetooth_device *device; 12153a5a1b3Sopenharmony_ci pa_bluetooth_transport *transport; 12253a5a1b3Sopenharmony_ci bool transport_acquired; 12353a5a1b3Sopenharmony_ci bool stream_setup_done; 12453a5a1b3Sopenharmony_ci 12553a5a1b3Sopenharmony_ci pa_card *card; 12653a5a1b3Sopenharmony_ci pa_sink *sink; 12753a5a1b3Sopenharmony_ci pa_source *source; 12853a5a1b3Sopenharmony_ci pa_bluetooth_profile_t profile; 12953a5a1b3Sopenharmony_ci char *output_port_name; 13053a5a1b3Sopenharmony_ci char *input_port_name; 13153a5a1b3Sopenharmony_ci 13253a5a1b3Sopenharmony_ci pa_thread *thread; 13353a5a1b3Sopenharmony_ci pa_thread_mq thread_mq; 13453a5a1b3Sopenharmony_ci pa_rtpoll *rtpoll; 13553a5a1b3Sopenharmony_ci pa_rtpoll_item *rtpoll_item; 13653a5a1b3Sopenharmony_ci bluetooth_msg *msg; 13753a5a1b3Sopenharmony_ci 13853a5a1b3Sopenharmony_ci int stream_fd; 13953a5a1b3Sopenharmony_ci size_t read_link_mtu; 14053a5a1b3Sopenharmony_ci size_t write_link_mtu; 14153a5a1b3Sopenharmony_ci size_t read_block_size; 14253a5a1b3Sopenharmony_ci size_t write_block_size; 14353a5a1b3Sopenharmony_ci uint64_t read_index; 14453a5a1b3Sopenharmony_ci uint64_t write_index; 14553a5a1b3Sopenharmony_ci pa_usec_t started_at; 14653a5a1b3Sopenharmony_ci 14753a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 14853a5a1b3Sopenharmony_ci pa_smoother_2 *read_smoother; 14953a5a1b3Sopenharmony_ci#else 15053a5a1b3Sopenharmony_ci pa_smoother *read_smoother; 15153a5a1b3Sopenharmony_ci#endif 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci pa_memchunk write_memchunk; 15453a5a1b3Sopenharmony_ci 15553a5a1b3Sopenharmony_ci const pa_bt_codec *bt_codec; 15653a5a1b3Sopenharmony_ci 15753a5a1b3Sopenharmony_ci void *encoder_info; 15853a5a1b3Sopenharmony_ci pa_sample_spec encoder_sample_spec; 15953a5a1b3Sopenharmony_ci void *encoder_buffer; /* Codec transfer buffer */ 16053a5a1b3Sopenharmony_ci size_t encoder_buffer_size; /* Size of the buffer */ 16153a5a1b3Sopenharmony_ci size_t encoder_buffer_used; /* Used space in the buffer */ 16253a5a1b3Sopenharmony_ci 16353a5a1b3Sopenharmony_ci void *decoder_info; 16453a5a1b3Sopenharmony_ci pa_sample_spec decoder_sample_spec; 16553a5a1b3Sopenharmony_ci void *decoder_buffer; /* Codec transfer buffer */ 16653a5a1b3Sopenharmony_ci size_t decoder_buffer_size; /* Size of the buffer */ 16753a5a1b3Sopenharmony_ci 16853a5a1b3Sopenharmony_ci bool message_handler_registered; 16953a5a1b3Sopenharmony_ci}; 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_citypedef enum pa_bluetooth_form_factor { 17253a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_UNKNOWN, 17353a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_HEADSET, 17453a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_HANDSFREE, 17553a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_MICROPHONE, 17653a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_SPEAKER, 17753a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_HEADPHONE, 17853a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_PORTABLE, 17953a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_CAR, 18053a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_HIFI, 18153a5a1b3Sopenharmony_ci PA_BLUETOOTH_FORM_FACTOR_PHONE, 18253a5a1b3Sopenharmony_ci} pa_bluetooth_form_factor_t; 18353a5a1b3Sopenharmony_ci 18453a5a1b3Sopenharmony_ci/* Run from main thread */ 18553a5a1b3Sopenharmony_cistatic pa_bluetooth_form_factor_t form_factor_from_class(uint32_t class_of_device) { 18653a5a1b3Sopenharmony_ci unsigned major, minor; 18753a5a1b3Sopenharmony_ci pa_bluetooth_form_factor_t r; 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci static const pa_bluetooth_form_factor_t table[] = { 19053a5a1b3Sopenharmony_ci [1] = PA_BLUETOOTH_FORM_FACTOR_HEADSET, 19153a5a1b3Sopenharmony_ci [2] = PA_BLUETOOTH_FORM_FACTOR_HANDSFREE, 19253a5a1b3Sopenharmony_ci [4] = PA_BLUETOOTH_FORM_FACTOR_MICROPHONE, 19353a5a1b3Sopenharmony_ci [5] = PA_BLUETOOTH_FORM_FACTOR_SPEAKER, 19453a5a1b3Sopenharmony_ci [6] = PA_BLUETOOTH_FORM_FACTOR_HEADPHONE, 19553a5a1b3Sopenharmony_ci [7] = PA_BLUETOOTH_FORM_FACTOR_PORTABLE, 19653a5a1b3Sopenharmony_ci [8] = PA_BLUETOOTH_FORM_FACTOR_CAR, 19753a5a1b3Sopenharmony_ci [10] = PA_BLUETOOTH_FORM_FACTOR_HIFI 19853a5a1b3Sopenharmony_ci }; 19953a5a1b3Sopenharmony_ci 20053a5a1b3Sopenharmony_ci /* 20153a5a1b3Sopenharmony_ci * See Bluetooth Assigned Numbers for Baseband 20253a5a1b3Sopenharmony_ci * https://www.bluetooth.com/specifications/assigned-numbers/baseband/ 20353a5a1b3Sopenharmony_ci */ 20453a5a1b3Sopenharmony_ci major = (class_of_device >> 8) & 0x1F; 20553a5a1b3Sopenharmony_ci minor = (class_of_device >> 2) & 0x3F; 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_ci switch (major) { 20853a5a1b3Sopenharmony_ci case 2: 20953a5a1b3Sopenharmony_ci return PA_BLUETOOTH_FORM_FACTOR_PHONE; 21053a5a1b3Sopenharmony_ci case 4: 21153a5a1b3Sopenharmony_ci break; 21253a5a1b3Sopenharmony_ci default: 21353a5a1b3Sopenharmony_ci pa_log_debug("Unknown Bluetooth major device class %u", major); 21453a5a1b3Sopenharmony_ci return PA_BLUETOOTH_FORM_FACTOR_UNKNOWN; 21553a5a1b3Sopenharmony_ci } 21653a5a1b3Sopenharmony_ci 21753a5a1b3Sopenharmony_ci r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BLUETOOTH_FORM_FACTOR_UNKNOWN; 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci if (!r) 22053a5a1b3Sopenharmony_ci pa_log_debug("Unknown Bluetooth minor device class %u", minor); 22153a5a1b3Sopenharmony_ci 22253a5a1b3Sopenharmony_ci return r; 22353a5a1b3Sopenharmony_ci} 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci/* Run from main thread */ 22653a5a1b3Sopenharmony_cistatic const char *form_factor_to_string(pa_bluetooth_form_factor_t ff) { 22753a5a1b3Sopenharmony_ci switch (ff) { 22853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN: 22953a5a1b3Sopenharmony_ci return "unknown"; 23053a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HEADSET: 23153a5a1b3Sopenharmony_ci return "headset"; 23253a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE: 23353a5a1b3Sopenharmony_ci return "hands-free"; 23453a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE: 23553a5a1b3Sopenharmony_ci return "microphone"; 23653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_SPEAKER: 23753a5a1b3Sopenharmony_ci return "speaker"; 23853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE: 23953a5a1b3Sopenharmony_ci return "headphone"; 24053a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_PORTABLE: 24153a5a1b3Sopenharmony_ci return "portable"; 24253a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_CAR: 24353a5a1b3Sopenharmony_ci return "car"; 24453a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HIFI: 24553a5a1b3Sopenharmony_ci return "hifi"; 24653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_PHONE: 24753a5a1b3Sopenharmony_ci return "phone"; 24853a5a1b3Sopenharmony_ci } 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci pa_assert_not_reached(); 25153a5a1b3Sopenharmony_ci} 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci/* Run from main thread */ 25453a5a1b3Sopenharmony_cistatic void connect_ports(struct userdata *u, void *new_data, pa_direction_t direction) { 25553a5a1b3Sopenharmony_ci pa_device_port *port; 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_ci if (direction == PA_DIRECTION_OUTPUT) { 25853a5a1b3Sopenharmony_ci pa_sink_new_data *sink_new_data = new_data; 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name)); 26153a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_put(sink_new_data->ports, port->name, port) >= 0); 26253a5a1b3Sopenharmony_ci pa_device_port_ref(port); 26353a5a1b3Sopenharmony_ci } else { 26453a5a1b3Sopenharmony_ci pa_source_new_data *source_new_data = new_data; 26553a5a1b3Sopenharmony_ci 26653a5a1b3Sopenharmony_ci pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name)); 26753a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_put(source_new_data->ports, port->name, port) >= 0); 26853a5a1b3Sopenharmony_ci pa_device_port_ref(port); 26953a5a1b3Sopenharmony_ci } 27053a5a1b3Sopenharmony_ci} 27153a5a1b3Sopenharmony_ci 27253a5a1b3Sopenharmony_cistatic bool bt_prepare_encoder_buffer(struct userdata *u) 27353a5a1b3Sopenharmony_ci{ 27453a5a1b3Sopenharmony_ci size_t encoded_size, reserved_size, encoded_frames; 27553a5a1b3Sopenharmony_ci pa_assert(u); 27653a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 27753a5a1b3Sopenharmony_ci 27853a5a1b3Sopenharmony_ci /* If socket write MTU is less than encoded frame size, there could be 27953a5a1b3Sopenharmony_ci * up to one write MTU of data left in encoder buffer from previous round. 28053a5a1b3Sopenharmony_ci * 28153a5a1b3Sopenharmony_ci * Reserve space for at least 2 encoded frames to cover that. 28253a5a1b3Sopenharmony_ci * 28353a5a1b3Sopenharmony_ci * Note for A2DP codecs it is expected that size of encoded frame is less 28453a5a1b3Sopenharmony_ci * than write link MTU. Therefore each encoded frame is sent out completely 28553a5a1b3Sopenharmony_ci * and there is no used space in encoder buffer before next encoder call. 28653a5a1b3Sopenharmony_ci * 28753a5a1b3Sopenharmony_ci * For SCO socket all writes will be of MTU size to match payload length 28853a5a1b3Sopenharmony_ci * of HCI packet. Depending on selected USB Alternate Setting the payload 28953a5a1b3Sopenharmony_ci * length of HCI packet may exceed encoded frame size. For mSBC frame size 29053a5a1b3Sopenharmony_ci * is 60 bytes, payload length of HCI packet in USB Alts 3 is 72 byte, 29153a5a1b3Sopenharmony_ci * in USB Alts 5 it is 144 bytes. 29253a5a1b3Sopenharmony_ci * 29353a5a1b3Sopenharmony_ci * Reserve space for up to 1 + MTU / (encoded frame size) encoded frames 29453a5a1b3Sopenharmony_ci * to cover that. 29553a5a1b3Sopenharmony_ci * 29653a5a1b3Sopenharmony_ci * Note for current linux kernel (up to 5.13.x at least) there is no way to 29753a5a1b3Sopenharmony_ci * reliably detect socket MTU size. For now we just set SCO socket MTU to be 29853a5a1b3Sopenharmony_ci * large enough to cover all known sizes (largest is USB ALts 5 with 144 bytes) 29953a5a1b3Sopenharmony_ci * and adjust SCO write size to be equal to last SCO read size. This makes 30053a5a1b3Sopenharmony_ci * write size less or equal to MTU size. Reserving the same number of encoded 30153a5a1b3Sopenharmony_ci * frames to cover full MTU is still enough. 30253a5a1b3Sopenharmony_ci * See also https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/254#note_779802 30353a5a1b3Sopenharmony_ci */ 30453a5a1b3Sopenharmony_ci 30553a5a1b3Sopenharmony_ci if (u->bt_codec->get_encoded_block_size) 30653a5a1b3Sopenharmony_ci encoded_size = u->bt_codec->get_encoded_block_size(u->encoder_info, u->write_block_size); 30753a5a1b3Sopenharmony_ci else 30853a5a1b3Sopenharmony_ci encoded_size = u->write_block_size; 30953a5a1b3Sopenharmony_ci 31053a5a1b3Sopenharmony_ci encoded_frames = u->write_link_mtu / u->write_block_size + 1; 31153a5a1b3Sopenharmony_ci 31253a5a1b3Sopenharmony_ci if (encoded_frames < 2) 31353a5a1b3Sopenharmony_ci encoded_frames = 2; 31453a5a1b3Sopenharmony_ci 31553a5a1b3Sopenharmony_ci reserved_size = encoded_frames * encoded_size; 31653a5a1b3Sopenharmony_ci 31753a5a1b3Sopenharmony_ci if (u->encoder_buffer_size < reserved_size) { 31853a5a1b3Sopenharmony_ci u->encoder_buffer = pa_xrealloc(u->encoder_buffer, reserved_size); 31953a5a1b3Sopenharmony_ci u->encoder_buffer_size = reserved_size; 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci if (u->encoder_buffer_used > reserved_size) { 32253a5a1b3Sopenharmony_ci u->encoder_buffer_used = 0; 32353a5a1b3Sopenharmony_ci } 32453a5a1b3Sopenharmony_ci } 32553a5a1b3Sopenharmony_ci 32653a5a1b3Sopenharmony_ci /* Report if there is still not enough space for new block */ 32753a5a1b3Sopenharmony_ci if (u->encoder_buffer_size < u->encoder_buffer_used + encoded_size) 32853a5a1b3Sopenharmony_ci return false; 32953a5a1b3Sopenharmony_ci 33053a5a1b3Sopenharmony_ci return true; 33153a5a1b3Sopenharmony_ci} 33253a5a1b3Sopenharmony_ci 33353a5a1b3Sopenharmony_ci/* Run from IO thread */ 33453a5a1b3Sopenharmony_cistatic int bt_write_buffer(struct userdata *u) { 33553a5a1b3Sopenharmony_ci ssize_t written = 0; 33653a5a1b3Sopenharmony_ci 33753a5a1b3Sopenharmony_ci pa_assert(u); 33853a5a1b3Sopenharmony_ci pa_assert(u->transport); 33953a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci written = u->transport->write(u->transport, u->stream_fd, u->encoder_buffer, u->encoder_buffer_used, u->write_link_mtu); 34253a5a1b3Sopenharmony_ci 34353a5a1b3Sopenharmony_ci if (written > 0) { 34453a5a1b3Sopenharmony_ci /* calculate remainder */ 34553a5a1b3Sopenharmony_ci u->encoder_buffer_used -= written; 34653a5a1b3Sopenharmony_ci 34753a5a1b3Sopenharmony_ci /* move any remainder back to start of u->encoder_buffer */ 34853a5a1b3Sopenharmony_ci if (u->encoder_buffer_used) 34953a5a1b3Sopenharmony_ci memmove(u->encoder_buffer, u->encoder_buffer + written, u->encoder_buffer_used); 35053a5a1b3Sopenharmony_ci 35153a5a1b3Sopenharmony_ci return 1; 35253a5a1b3Sopenharmony_ci } else if (written == 0) { 35353a5a1b3Sopenharmony_ci /* Not enough data in encoder buffer */ 35453a5a1b3Sopenharmony_ci return 0; 35553a5a1b3Sopenharmony_ci } else { 35653a5a1b3Sopenharmony_ci /* Reset encoder sequence number and buffer positions */ 35753a5a1b3Sopenharmony_ci u->bt_codec->reset(u->encoder_info); 35853a5a1b3Sopenharmony_ci u->encoder_buffer_used = 0; 35953a5a1b3Sopenharmony_ci return -1; 36053a5a1b3Sopenharmony_ci } 36153a5a1b3Sopenharmony_ci} 36253a5a1b3Sopenharmony_ci 36353a5a1b3Sopenharmony_ci/* Run from IO thread */ 36453a5a1b3Sopenharmony_cistatic int bt_process_render(struct userdata *u) { 36553a5a1b3Sopenharmony_ci int ret; 36653a5a1b3Sopenharmony_ci 36753a5a1b3Sopenharmony_ci const uint8_t *ptr; 36853a5a1b3Sopenharmony_ci size_t processed; 36953a5a1b3Sopenharmony_ci size_t length; 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_ci pa_assert(u); 37253a5a1b3Sopenharmony_ci pa_assert(u->sink); 37353a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 37453a5a1b3Sopenharmony_ci 37553a5a1b3Sopenharmony_ci if (!bt_prepare_encoder_buffer(u)) 37653a5a1b3Sopenharmony_ci return false; 37753a5a1b3Sopenharmony_ci 37853a5a1b3Sopenharmony_ci /* First, render some data */ 37953a5a1b3Sopenharmony_ci if (!u->write_memchunk.memblock) 38053a5a1b3Sopenharmony_ci pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); 38153a5a1b3Sopenharmony_ci 38253a5a1b3Sopenharmony_ci pa_assert(u->write_memchunk.length == u->write_block_size); 38353a5a1b3Sopenharmony_ci 38453a5a1b3Sopenharmony_ci ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); 38553a5a1b3Sopenharmony_ci 38653a5a1b3Sopenharmony_ci length = u->bt_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), 38753a5a1b3Sopenharmony_ci ptr, u->write_memchunk.length, 38853a5a1b3Sopenharmony_ci u->encoder_buffer + u->encoder_buffer_used, u->encoder_buffer_size - u->encoder_buffer_used, 38953a5a1b3Sopenharmony_ci &processed); 39053a5a1b3Sopenharmony_ci 39153a5a1b3Sopenharmony_ci pa_memblock_release(u->write_memchunk.memblock); 39253a5a1b3Sopenharmony_ci 39353a5a1b3Sopenharmony_ci if (processed != u->write_memchunk.length) { 39453a5a1b3Sopenharmony_ci pa_log_error("Encoding error"); 39553a5a1b3Sopenharmony_ci return -1; 39653a5a1b3Sopenharmony_ci } 39753a5a1b3Sopenharmony_ci 39853a5a1b3Sopenharmony_ci /* Encoder function of BT codec may provide empty buffer, in this case do 39953a5a1b3Sopenharmony_ci * not post any empty buffer via BT socket. It may be because of codec 40053a5a1b3Sopenharmony_ci * internal state, e.g. encoder is waiting for more samples so it can 40153a5a1b3Sopenharmony_ci * provide encoded data. */ 40253a5a1b3Sopenharmony_ci 40353a5a1b3Sopenharmony_ci if (PA_LIKELY(length)) { 40453a5a1b3Sopenharmony_ci u->encoder_buffer_used += length; 40553a5a1b3Sopenharmony_ci ret = 1; 40653a5a1b3Sopenharmony_ci } else 40753a5a1b3Sopenharmony_ci ret = 0; 40853a5a1b3Sopenharmony_ci 40953a5a1b3Sopenharmony_ci u->write_index += (uint64_t) u->write_memchunk.length; 41053a5a1b3Sopenharmony_ci pa_memblock_unref(u->write_memchunk.memblock); 41153a5a1b3Sopenharmony_ci pa_memchunk_reset(&u->write_memchunk); 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci return ret; 41453a5a1b3Sopenharmony_ci} 41553a5a1b3Sopenharmony_ci 41653a5a1b3Sopenharmony_cistatic void bt_prepare_decoder_buffer(struct userdata *u) { 41753a5a1b3Sopenharmony_ci pa_assert(u); 41853a5a1b3Sopenharmony_ci 41953a5a1b3Sopenharmony_ci if (u->decoder_buffer_size < u->read_link_mtu) { 42053a5a1b3Sopenharmony_ci pa_xfree(u->decoder_buffer); 42153a5a1b3Sopenharmony_ci u->decoder_buffer = pa_xmalloc(u->read_link_mtu); 42253a5a1b3Sopenharmony_ci } 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci /* Decoder buffer cannot be larger then link MTU, otherwise 42553a5a1b3Sopenharmony_ci * decode method would produce larger output then read_block_size */ 42653a5a1b3Sopenharmony_ci u->decoder_buffer_size = u->read_link_mtu; 42753a5a1b3Sopenharmony_ci} 42853a5a1b3Sopenharmony_ci 42953a5a1b3Sopenharmony_ci/* Run from IO thread */ 43053a5a1b3Sopenharmony_cistatic ssize_t bt_transport_read(pa_bluetooth_transport *t, int fd, void *buffer, size_t size, pa_usec_t *p_timestamp) { 43153a5a1b3Sopenharmony_ci ssize_t received = 0; 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci pa_assert(t); 43453a5a1b3Sopenharmony_ci for (;;) { 43553a5a1b3Sopenharmony_ci uint8_t aux[1024]; 43653a5a1b3Sopenharmony_ci struct iovec iov; 43753a5a1b3Sopenharmony_ci struct cmsghdr *cm; 43853a5a1b3Sopenharmony_ci struct msghdr m; 43953a5a1b3Sopenharmony_ci bool found_tstamp = false; 44053a5a1b3Sopenharmony_ci 44153a5a1b3Sopenharmony_ci pa_zero(m); 44253a5a1b3Sopenharmony_ci pa_zero(aux); 44353a5a1b3Sopenharmony_ci pa_zero(iov); 44453a5a1b3Sopenharmony_ci 44553a5a1b3Sopenharmony_ci m.msg_iov = &iov; 44653a5a1b3Sopenharmony_ci m.msg_iovlen = 1; 44753a5a1b3Sopenharmony_ci m.msg_control = aux; 44853a5a1b3Sopenharmony_ci m.msg_controllen = sizeof(aux); 44953a5a1b3Sopenharmony_ci 45053a5a1b3Sopenharmony_ci iov.iov_base = buffer; 45153a5a1b3Sopenharmony_ci iov.iov_len = size; 45253a5a1b3Sopenharmony_ci 45353a5a1b3Sopenharmony_ci received = recvmsg(fd, &m, 0); 45453a5a1b3Sopenharmony_ci 45553a5a1b3Sopenharmony_ci if (received <= 0) { 45653a5a1b3Sopenharmony_ci 45753a5a1b3Sopenharmony_ci if (received < 0 && errno == EINTR) 45853a5a1b3Sopenharmony_ci /* Retry right away if we got interrupted */ 45953a5a1b3Sopenharmony_ci continue; 46053a5a1b3Sopenharmony_ci 46153a5a1b3Sopenharmony_ci else if (received < 0 && errno == EAGAIN) 46253a5a1b3Sopenharmony_ci /* Hmm, apparently the socket was not readable, give up for now. */ 46353a5a1b3Sopenharmony_ci return 0; 46453a5a1b3Sopenharmony_ci 46553a5a1b3Sopenharmony_ci pa_log_error("Failed to read data from socket: %s", received < 0 ? pa_cstrerror(errno) : "EOF"); 46653a5a1b3Sopenharmony_ci return -1; 46753a5a1b3Sopenharmony_ci } 46853a5a1b3Sopenharmony_ci 46953a5a1b3Sopenharmony_ci pa_assert((size_t) received <= size); 47053a5a1b3Sopenharmony_ci 47153a5a1b3Sopenharmony_ci /* allow write side to find out size of last read packet */ 47253a5a1b3Sopenharmony_ci t->last_read_size = received; 47353a5a1b3Sopenharmony_ci 47453a5a1b3Sopenharmony_ci if (p_timestamp) { 47553a5a1b3Sopenharmony_ci /* TODO: get timestamp from rtp */ 47653a5a1b3Sopenharmony_ci 47753a5a1b3Sopenharmony_ci for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) { 47853a5a1b3Sopenharmony_ci if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { 47953a5a1b3Sopenharmony_ci struct timeval *tv = (struct timeval*) CMSG_DATA(cm); 48053a5a1b3Sopenharmony_ci pa_rtclock_from_wallclock(tv); 48153a5a1b3Sopenharmony_ci *p_timestamp = pa_timeval_load(tv); 48253a5a1b3Sopenharmony_ci found_tstamp = true; 48353a5a1b3Sopenharmony_ci break; 48453a5a1b3Sopenharmony_ci } 48553a5a1b3Sopenharmony_ci } 48653a5a1b3Sopenharmony_ci 48753a5a1b3Sopenharmony_ci if (!found_tstamp) { 48853a5a1b3Sopenharmony_ci PA_ONCE_BEGIN { 48953a5a1b3Sopenharmony_ci pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); 49053a5a1b3Sopenharmony_ci } PA_ONCE_END; 49153a5a1b3Sopenharmony_ci *p_timestamp = pa_rtclock_now(); 49253a5a1b3Sopenharmony_ci } 49353a5a1b3Sopenharmony_ci } 49453a5a1b3Sopenharmony_ci 49553a5a1b3Sopenharmony_ci break; 49653a5a1b3Sopenharmony_ci } 49753a5a1b3Sopenharmony_ci 49853a5a1b3Sopenharmony_ci return received; 49953a5a1b3Sopenharmony_ci} 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci/* Run from IO thread */ 50253a5a1b3Sopenharmony_ci/* Read incoming data, decode it and post result (if any) to source output. 50353a5a1b3Sopenharmony_ci * Returns number of bytes posted to source output. */ 50453a5a1b3Sopenharmony_cistatic int bt_process_push(struct userdata *u) { 50553a5a1b3Sopenharmony_ci pa_usec_t tstamp; 50653a5a1b3Sopenharmony_ci uint8_t *ptr; 50753a5a1b3Sopenharmony_ci ssize_t received; 50853a5a1b3Sopenharmony_ci size_t processed = 0; 50953a5a1b3Sopenharmony_ci 51053a5a1b3Sopenharmony_ci pa_assert(u); 51153a5a1b3Sopenharmony_ci pa_assert(u->source); 51253a5a1b3Sopenharmony_ci pa_assert(u->read_smoother); 51353a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 51453a5a1b3Sopenharmony_ci pa_assert(u->transport); 51553a5a1b3Sopenharmony_ci 51653a5a1b3Sopenharmony_ci bt_prepare_decoder_buffer(u); 51753a5a1b3Sopenharmony_ci 51853a5a1b3Sopenharmony_ci received = bt_transport_read(u->transport, u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &tstamp); 51953a5a1b3Sopenharmony_ci 52053a5a1b3Sopenharmony_ci if (received <= 0) { 52153a5a1b3Sopenharmony_ci return received; 52253a5a1b3Sopenharmony_ci } 52353a5a1b3Sopenharmony_ci 52453a5a1b3Sopenharmony_ci pa_memchunk memchunk; 52553a5a1b3Sopenharmony_ci 52653a5a1b3Sopenharmony_ci memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); 52753a5a1b3Sopenharmony_ci memchunk.index = memchunk.length = 0; 52853a5a1b3Sopenharmony_ci 52953a5a1b3Sopenharmony_ci ptr = pa_memblock_acquire(memchunk.memblock); 53053a5a1b3Sopenharmony_ci memchunk.length = pa_memblock_get_length(memchunk.memblock); 53153a5a1b3Sopenharmony_ci 53253a5a1b3Sopenharmony_ci memchunk.length = u->bt_codec->decode_buffer(u->decoder_info, u->decoder_buffer, received, ptr, memchunk.length, &processed); 53353a5a1b3Sopenharmony_ci 53453a5a1b3Sopenharmony_ci pa_memblock_release(memchunk.memblock); 53553a5a1b3Sopenharmony_ci 53653a5a1b3Sopenharmony_ci if (processed != (size_t) received) { 53753a5a1b3Sopenharmony_ci pa_log_error("Decoding error"); 53853a5a1b3Sopenharmony_ci return -1; 53953a5a1b3Sopenharmony_ci } 54053a5a1b3Sopenharmony_ci 54153a5a1b3Sopenharmony_ci u->read_index += (uint64_t) memchunk.length; 54253a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 54353a5a1b3Sopenharmony_ci pa_smoother_2_resume(u->read_smoother, tstamp); 54453a5a1b3Sopenharmony_ci pa_smoother_2_put(u->read_smoother, tstamp, u->read_index); 54553a5a1b3Sopenharmony_ci#else 54653a5a1b3Sopenharmony_ci pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec)); 54753a5a1b3Sopenharmony_ci pa_smoother_resume(u->read_smoother, tstamp, true); 54853a5a1b3Sopenharmony_ci#endif 54953a5a1b3Sopenharmony_ci 55053a5a1b3Sopenharmony_ci /* Decoding of data may result in empty buffer, in this case 55153a5a1b3Sopenharmony_ci * do not post empty audio samples. It may happen due to algorithmic 55253a5a1b3Sopenharmony_ci * delay of audio codec. */ 55353a5a1b3Sopenharmony_ci if (PA_LIKELY(memchunk.length)) 55453a5a1b3Sopenharmony_ci pa_source_post(u->source, &memchunk); 55553a5a1b3Sopenharmony_ci 55653a5a1b3Sopenharmony_ci /* report decoded size */ 55753a5a1b3Sopenharmony_ci received = memchunk.length; 55853a5a1b3Sopenharmony_ci 55953a5a1b3Sopenharmony_ci pa_memblock_unref(memchunk.memblock); 56053a5a1b3Sopenharmony_ci 56153a5a1b3Sopenharmony_ci return received; 56253a5a1b3Sopenharmony_ci} 56353a5a1b3Sopenharmony_ci 56453a5a1b3Sopenharmony_cistatic void update_sink_buffer_size(struct userdata *u) { 56553a5a1b3Sopenharmony_ci int old_bufsize; 56653a5a1b3Sopenharmony_ci socklen_t len = sizeof(int); 56753a5a1b3Sopenharmony_ci int ret; 56853a5a1b3Sopenharmony_ci 56953a5a1b3Sopenharmony_ci ret = getsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &old_bufsize, &len); 57053a5a1b3Sopenharmony_ci if (ret == -1) { 57153a5a1b3Sopenharmony_ci pa_log_warn("Changing bluetooth buffer size: Failed to getsockopt(SO_SNDBUF): %s", pa_cstrerror(errno)); 57253a5a1b3Sopenharmony_ci } else { 57353a5a1b3Sopenharmony_ci int new_bufsize; 57453a5a1b3Sopenharmony_ci 57553a5a1b3Sopenharmony_ci /* Set send buffer size as small as possible. The minimum value is 1024 according to the 57653a5a1b3Sopenharmony_ci * socket man page. The data is written to the socket in chunks of write_block_size, so 57753a5a1b3Sopenharmony_ci * there should at least be room for two chunks in the buffer. Generally, write_block_size 57853a5a1b3Sopenharmony_ci * is larger than 512. If not, use the next multiple of write_block_size which is larger 57953a5a1b3Sopenharmony_ci * than 1024. */ 58053a5a1b3Sopenharmony_ci new_bufsize = 2 * u->write_block_size; 58153a5a1b3Sopenharmony_ci if (new_bufsize < 1024) 58253a5a1b3Sopenharmony_ci new_bufsize = (1024 / u->write_block_size + 1) * u->write_block_size; 58353a5a1b3Sopenharmony_ci 58453a5a1b3Sopenharmony_ci /* The kernel internally doubles the buffer size that was set by setsockopt and getsockopt 58553a5a1b3Sopenharmony_ci * returns the doubled value. */ 58653a5a1b3Sopenharmony_ci if (new_bufsize != old_bufsize / 2) { 58753a5a1b3Sopenharmony_ci ret = setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &new_bufsize, len); 58853a5a1b3Sopenharmony_ci if (ret == -1) 58953a5a1b3Sopenharmony_ci pa_log_warn("Changing bluetooth buffer size: Failed to change from %d to %d: %s", old_bufsize / 2, new_bufsize, pa_cstrerror(errno)); 59053a5a1b3Sopenharmony_ci else 59153a5a1b3Sopenharmony_ci pa_log_info("Changing bluetooth buffer size: Changed from %d to %d", old_bufsize / 2, new_bufsize); 59253a5a1b3Sopenharmony_ci } 59353a5a1b3Sopenharmony_ci } 59453a5a1b3Sopenharmony_ci} 59553a5a1b3Sopenharmony_ci 59653a5a1b3Sopenharmony_cistatic void teardown_stream(struct userdata *u) { 59753a5a1b3Sopenharmony_ci if (u->rtpoll_item) { 59853a5a1b3Sopenharmony_ci pa_rtpoll_item_free(u->rtpoll_item); 59953a5a1b3Sopenharmony_ci u->rtpoll_item = NULL; 60053a5a1b3Sopenharmony_ci } 60153a5a1b3Sopenharmony_ci 60253a5a1b3Sopenharmony_ci if (u->stream_fd >= 0) { 60353a5a1b3Sopenharmony_ci pa_close(u->stream_fd); 60453a5a1b3Sopenharmony_ci u->stream_fd = -1; 60553a5a1b3Sopenharmony_ci } 60653a5a1b3Sopenharmony_ci 60753a5a1b3Sopenharmony_ci if (u->read_smoother) { 60853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 60953a5a1b3Sopenharmony_ci pa_smoother_2_free(u->read_smoother); 61053a5a1b3Sopenharmony_ci#else 61153a5a1b3Sopenharmony_ci pa_smoother_free(u->read_smoother); 61253a5a1b3Sopenharmony_ci#endif 61353a5a1b3Sopenharmony_ci u->read_smoother = NULL; 61453a5a1b3Sopenharmony_ci } 61553a5a1b3Sopenharmony_ci 61653a5a1b3Sopenharmony_ci if (u->write_memchunk.memblock) { 61753a5a1b3Sopenharmony_ci pa_memblock_unref(u->write_memchunk.memblock); 61853a5a1b3Sopenharmony_ci pa_memchunk_reset(&u->write_memchunk); 61953a5a1b3Sopenharmony_ci } 62053a5a1b3Sopenharmony_ci 62153a5a1b3Sopenharmony_ci pa_log_debug("Audio stream torn down"); 62253a5a1b3Sopenharmony_ci u->stream_setup_done = false; 62353a5a1b3Sopenharmony_ci} 62453a5a1b3Sopenharmony_ci 62553a5a1b3Sopenharmony_cistatic int transport_acquire(struct userdata *u, bool optional) { 62653a5a1b3Sopenharmony_ci pa_assert(u->transport); 62753a5a1b3Sopenharmony_ci 62853a5a1b3Sopenharmony_ci if (u->transport_acquired) 62953a5a1b3Sopenharmony_ci return 0; 63053a5a1b3Sopenharmony_ci 63153a5a1b3Sopenharmony_ci pa_log_debug("Acquiring transport %s", u->transport->path); 63253a5a1b3Sopenharmony_ci 63353a5a1b3Sopenharmony_ci u->stream_fd = u->transport->acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu); 63453a5a1b3Sopenharmony_ci if (u->stream_fd < 0) 63553a5a1b3Sopenharmony_ci return u->stream_fd; 63653a5a1b3Sopenharmony_ci 63753a5a1b3Sopenharmony_ci /* transport_acquired must be set before calling 63853a5a1b3Sopenharmony_ci * pa_bluetooth_transport_set_state() */ 63953a5a1b3Sopenharmony_ci u->transport_acquired = true; 64053a5a1b3Sopenharmony_ci pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd); 64153a5a1b3Sopenharmony_ci 64253a5a1b3Sopenharmony_ci if (u->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_IDLE) { 64353a5a1b3Sopenharmony_ci if (pa_thread_mq_get() != NULL) 64453a5a1b3Sopenharmony_ci pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING, NULL, 0, NULL, NULL); 64553a5a1b3Sopenharmony_ci else 64653a5a1b3Sopenharmony_ci pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); 64753a5a1b3Sopenharmony_ci } 64853a5a1b3Sopenharmony_ci 64953a5a1b3Sopenharmony_ci return 0; 65053a5a1b3Sopenharmony_ci} 65153a5a1b3Sopenharmony_ci 65253a5a1b3Sopenharmony_cistatic void transport_release(struct userdata *u) { 65353a5a1b3Sopenharmony_ci pa_assert(u->transport); 65453a5a1b3Sopenharmony_ci 65553a5a1b3Sopenharmony_ci /* Ignore if already released */ 65653a5a1b3Sopenharmony_ci if (!u->transport_acquired) 65753a5a1b3Sopenharmony_ci return; 65853a5a1b3Sopenharmony_ci 65953a5a1b3Sopenharmony_ci pa_log_debug("Releasing transport %s", u->transport->path); 66053a5a1b3Sopenharmony_ci 66153a5a1b3Sopenharmony_ci u->transport->release(u->transport); 66253a5a1b3Sopenharmony_ci 66353a5a1b3Sopenharmony_ci u->transport_acquired = false; 66453a5a1b3Sopenharmony_ci 66553a5a1b3Sopenharmony_ci teardown_stream(u); 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci /* Set transport state to idle if this was not already done by the remote end closing 66853a5a1b3Sopenharmony_ci * the file descriptor. Only do this when called from the I/O thread */ 66953a5a1b3Sopenharmony_ci if (pa_thread_mq_get() != NULL && u->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) 67053a5a1b3Sopenharmony_ci pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL); 67153a5a1b3Sopenharmony_ci} 67253a5a1b3Sopenharmony_ci 67353a5a1b3Sopenharmony_ci/* Run from I/O thread */ 67453a5a1b3Sopenharmony_cistatic void handle_sink_block_size_change(struct userdata *u) { 67553a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, u->write_block_size); 67653a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, 67753a5a1b3Sopenharmony_ci (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? 67853a5a1b3Sopenharmony_ci FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) + 67953a5a1b3Sopenharmony_ci pa_bytes_to_usec(u->write_block_size, &u->encoder_sample_spec)); 68053a5a1b3Sopenharmony_ci 68153a5a1b3Sopenharmony_ci /* If there is still data in the memchunk, we have to discard it 68253a5a1b3Sopenharmony_ci * because the write_block_size may have changed. */ 68353a5a1b3Sopenharmony_ci if (u->write_memchunk.memblock) { 68453a5a1b3Sopenharmony_ci pa_memblock_unref(u->write_memchunk.memblock); 68553a5a1b3Sopenharmony_ci pa_memchunk_reset(&u->write_memchunk); 68653a5a1b3Sopenharmony_ci } 68753a5a1b3Sopenharmony_ci 68853a5a1b3Sopenharmony_ci update_sink_buffer_size(u); 68953a5a1b3Sopenharmony_ci} 69053a5a1b3Sopenharmony_ci 69153a5a1b3Sopenharmony_ci/* Run from I/O thread */ 69253a5a1b3Sopenharmony_cistatic void transport_config_mtu(struct userdata *u) { 69353a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 69453a5a1b3Sopenharmony_ci 69553a5a1b3Sopenharmony_ci if (u->encoder_info) { 69653a5a1b3Sopenharmony_ci u->write_block_size = u->bt_codec->get_write_block_size(u->encoder_info, u->write_link_mtu); 69753a5a1b3Sopenharmony_ci 69853a5a1b3Sopenharmony_ci if (!pa_frame_aligned(u->write_block_size, &u->sink->sample_spec)) { 69953a5a1b3Sopenharmony_ci pa_log_debug("Got invalid write MTU: %lu, rounding down", u->write_block_size); 70053a5a1b3Sopenharmony_ci u->write_block_size = pa_frame_align(u->write_block_size, &u->sink->sample_spec); 70153a5a1b3Sopenharmony_ci } 70253a5a1b3Sopenharmony_ci } 70353a5a1b3Sopenharmony_ci 70453a5a1b3Sopenharmony_ci if (u->decoder_info) { 70553a5a1b3Sopenharmony_ci u->read_block_size = u->bt_codec->get_read_block_size(u->decoder_info, u->read_link_mtu); 70653a5a1b3Sopenharmony_ci 70753a5a1b3Sopenharmony_ci if (!pa_frame_aligned(u->read_block_size, &u->source->sample_spec)) { 70853a5a1b3Sopenharmony_ci pa_log_debug("Got invalid read MTU: %lu, rounding down", u->read_block_size); 70953a5a1b3Sopenharmony_ci u->read_block_size = pa_frame_align(u->read_block_size, &u->source->sample_spec); 71053a5a1b3Sopenharmony_ci } 71153a5a1b3Sopenharmony_ci } 71253a5a1b3Sopenharmony_ci 71353a5a1b3Sopenharmony_ci if (u->sink) 71453a5a1b3Sopenharmony_ci handle_sink_block_size_change(u); 71553a5a1b3Sopenharmony_ci 71653a5a1b3Sopenharmony_ci if (u->source) 71753a5a1b3Sopenharmony_ci pa_source_set_fixed_latency_within_thread(u->source, 71853a5a1b3Sopenharmony_ci (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ? 71953a5a1b3Sopenharmony_ci FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) + 72053a5a1b3Sopenharmony_ci pa_bytes_to_usec(u->read_block_size, &u->decoder_sample_spec)); 72153a5a1b3Sopenharmony_ci} 72253a5a1b3Sopenharmony_ci 72353a5a1b3Sopenharmony_ci/* Run from I/O thread */ 72453a5a1b3Sopenharmony_cistatic int setup_stream(struct userdata *u) { 72553a5a1b3Sopenharmony_ci struct pollfd *pollfd; 72653a5a1b3Sopenharmony_ci int one; 72753a5a1b3Sopenharmony_ci 72853a5a1b3Sopenharmony_ci pa_assert(u->stream_fd >= 0); 72953a5a1b3Sopenharmony_ci 73053a5a1b3Sopenharmony_ci /* return if stream is already set up */ 73153a5a1b3Sopenharmony_ci if (u->stream_setup_done) 73253a5a1b3Sopenharmony_ci return 0; 73353a5a1b3Sopenharmony_ci 73453a5a1b3Sopenharmony_ci pa_log_info("Transport %s resuming", u->transport->path); 73553a5a1b3Sopenharmony_ci 73653a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 73753a5a1b3Sopenharmony_ci 73853a5a1b3Sopenharmony_ci if (u->encoder_info) { 73953a5a1b3Sopenharmony_ci if (u->bt_codec->reset(u->encoder_info) < 0) 74053a5a1b3Sopenharmony_ci return -1; 74153a5a1b3Sopenharmony_ci } 74253a5a1b3Sopenharmony_ci 74353a5a1b3Sopenharmony_ci if (u->decoder_info) { 74453a5a1b3Sopenharmony_ci if (u->bt_codec->reset(u->decoder_info) < 0) 74553a5a1b3Sopenharmony_ci return -1; 74653a5a1b3Sopenharmony_ci } 74753a5a1b3Sopenharmony_ci 74853a5a1b3Sopenharmony_ci transport_config_mtu(u); 74953a5a1b3Sopenharmony_ci 75053a5a1b3Sopenharmony_ci pa_make_fd_nonblock(u->stream_fd); 75153a5a1b3Sopenharmony_ci pa_make_socket_low_delay(u->stream_fd); 75253a5a1b3Sopenharmony_ci 75353a5a1b3Sopenharmony_ci one = 1; 75453a5a1b3Sopenharmony_ci if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) 75553a5a1b3Sopenharmony_ci pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno)); 75653a5a1b3Sopenharmony_ci 75753a5a1b3Sopenharmony_ci pa_log_debug("Stream properly set up, we're ready to roll!"); 75853a5a1b3Sopenharmony_ci 75953a5a1b3Sopenharmony_ci u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); 76053a5a1b3Sopenharmony_ci pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); 76153a5a1b3Sopenharmony_ci pollfd->fd = u->stream_fd; 76253a5a1b3Sopenharmony_ci pollfd->events = pollfd->revents = 0; 76353a5a1b3Sopenharmony_ci 76453a5a1b3Sopenharmony_ci u->read_index = u->write_index = 0; 76553a5a1b3Sopenharmony_ci u->started_at = 0; 76653a5a1b3Sopenharmony_ci u->stream_setup_done = true; 76753a5a1b3Sopenharmony_ci 76853a5a1b3Sopenharmony_ci if (u->source) 76953a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 77053a5a1b3Sopenharmony_ci u->read_smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->decoder_sample_spec), u->decoder_sample_spec.rate); 77153a5a1b3Sopenharmony_ci#else 77253a5a1b3Sopenharmony_ci u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, 2*PA_USEC_PER_SEC, true, true, 10, pa_rtclock_now(), true); 77353a5a1b3Sopenharmony_ci#endif 77453a5a1b3Sopenharmony_ci 77553a5a1b3Sopenharmony_ci return 0; 77653a5a1b3Sopenharmony_ci} 77753a5a1b3Sopenharmony_ci 77853a5a1b3Sopenharmony_ci/* Called from I/O thread, returns true if the transport was acquired or 77953a5a1b3Sopenharmony_ci * a connection was requested successfully. */ 78053a5a1b3Sopenharmony_cistatic bool setup_transport_and_stream(struct userdata *u) { 78153a5a1b3Sopenharmony_ci int transport_error; 78253a5a1b3Sopenharmony_ci 78353a5a1b3Sopenharmony_ci transport_error = transport_acquire(u, false); 78453a5a1b3Sopenharmony_ci if (transport_error < 0) { 78553a5a1b3Sopenharmony_ci if (transport_error != -EAGAIN) 78653a5a1b3Sopenharmony_ci return false; 78753a5a1b3Sopenharmony_ci } else { 78853a5a1b3Sopenharmony_ci if (setup_stream(u) < 0) 78953a5a1b3Sopenharmony_ci return false; 79053a5a1b3Sopenharmony_ci } 79153a5a1b3Sopenharmony_ci return true; 79253a5a1b3Sopenharmony_ci} 79353a5a1b3Sopenharmony_ci 79453a5a1b3Sopenharmony_ci/* Run from main thread */ 79553a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_source_volume_changed_cb(void *hook_data, void *call_data, void *slot_data) { 79653a5a1b3Sopenharmony_ci struct userdata *u = slot_data; 79753a5a1b3Sopenharmony_ci const pa_cvolume *new_volume = NULL; 79853a5a1b3Sopenharmony_ci pa_volume_t volume; 79953a5a1b3Sopenharmony_ci pa_bluetooth_transport_set_volume_cb notify_volume_change; 80053a5a1b3Sopenharmony_ci 80153a5a1b3Sopenharmony_ci /* In the HS/HF role, notify the AG of a change in speaker/microphone gain. 80253a5a1b3Sopenharmony_ci * In the AG role the command to change HW volume on the remote is already 80353a5a1b3Sopenharmony_ci * sent by the hardware callback (if the peer supports it and the sink 80453a5a1b3Sopenharmony_ci * or source set_volume callback is attached. Otherwise nothing is sent). 80553a5a1b3Sopenharmony_ci */ 80653a5a1b3Sopenharmony_ci pa_assert(pa_bluetooth_profile_should_attenuate_volume(u->profile)); 80753a5a1b3Sopenharmony_ci 80853a5a1b3Sopenharmony_ci if (u->sink == call_data) { 80953a5a1b3Sopenharmony_ci new_volume = pa_sink_get_volume(u->sink, false); 81053a5a1b3Sopenharmony_ci notify_volume_change = u->transport->set_sink_volume; 81153a5a1b3Sopenharmony_ci } else if (u->source == call_data) { 81253a5a1b3Sopenharmony_ci new_volume = pa_source_get_volume(u->source, false); 81353a5a1b3Sopenharmony_ci notify_volume_change = u->transport->set_source_volume; 81453a5a1b3Sopenharmony_ci } else { 81553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 81653a5a1b3Sopenharmony_ci } 81753a5a1b3Sopenharmony_ci 81853a5a1b3Sopenharmony_ci /* Volume control/notifications are optional */ 81953a5a1b3Sopenharmony_ci if (!notify_volume_change) 82053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 82153a5a1b3Sopenharmony_ci 82253a5a1b3Sopenharmony_ci volume = pa_cvolume_max(new_volume); 82353a5a1b3Sopenharmony_ci 82453a5a1b3Sopenharmony_ci notify_volume_change(u->transport, volume); 82553a5a1b3Sopenharmony_ci 82653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 82753a5a1b3Sopenharmony_ci} 82853a5a1b3Sopenharmony_ci 82953a5a1b3Sopenharmony_ci/* Run from IO thread */ 83053a5a1b3Sopenharmony_cistatic int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 83153a5a1b3Sopenharmony_ci struct userdata *u = PA_SOURCE(o)->userdata; 83253a5a1b3Sopenharmony_ci 83353a5a1b3Sopenharmony_ci pa_assert(u->source == PA_SOURCE(o)); 83453a5a1b3Sopenharmony_ci pa_assert(u->transport); 83553a5a1b3Sopenharmony_ci 83653a5a1b3Sopenharmony_ci switch (code) { 83753a5a1b3Sopenharmony_ci 83853a5a1b3Sopenharmony_ci case PA_SOURCE_MESSAGE_GET_LATENCY: { 83953a5a1b3Sopenharmony_ci#ifndef USE_SMOOTHER_2 84053a5a1b3Sopenharmony_ci int64_t wi, ri; 84153a5a1b3Sopenharmony_ci#endif 84253a5a1b3Sopenharmony_ci 84353a5a1b3Sopenharmony_ci if (u->read_smoother) { 84453a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 84553a5a1b3Sopenharmony_ci *((int64_t*) data) = u->source->thread_info.fixed_latency - pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->read_index); 84653a5a1b3Sopenharmony_ci#else 84753a5a1b3Sopenharmony_ci wi = pa_smoother_get(u->read_smoother, pa_rtclock_now()); 84853a5a1b3Sopenharmony_ci ri = pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec); 84953a5a1b3Sopenharmony_ci 85053a5a1b3Sopenharmony_ci *((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri; 85153a5a1b3Sopenharmony_ci#endif 85253a5a1b3Sopenharmony_ci } else 85353a5a1b3Sopenharmony_ci *((int64_t*) data) = 0; 85453a5a1b3Sopenharmony_ci 85553a5a1b3Sopenharmony_ci return 0; 85653a5a1b3Sopenharmony_ci } 85753a5a1b3Sopenharmony_ci 85853a5a1b3Sopenharmony_ci case PA_SOURCE_MESSAGE_SETUP_STREAM: 85953a5a1b3Sopenharmony_ci /* Skip stream setup if stream_fd has been invalidated. 86053a5a1b3Sopenharmony_ci This can occur if the stream has already been set up and 86153a5a1b3Sopenharmony_ci then immediately received POLLHUP. If the stream has 86253a5a1b3Sopenharmony_ci already been set up earlier, then this setup_stream() 86353a5a1b3Sopenharmony_ci call is redundant anyway, but currently the code 86453a5a1b3Sopenharmony_ci is such that this kind of unnecessary setup_stream() 86553a5a1b3Sopenharmony_ci calls can happen. */ 86653a5a1b3Sopenharmony_ci if (u->stream_fd < 0) 86753a5a1b3Sopenharmony_ci pa_log_debug("Skip source stream setup while closing"); 86853a5a1b3Sopenharmony_ci else 86953a5a1b3Sopenharmony_ci setup_stream(u); 87053a5a1b3Sopenharmony_ci return 0; 87153a5a1b3Sopenharmony_ci 87253a5a1b3Sopenharmony_ci } 87353a5a1b3Sopenharmony_ci 87453a5a1b3Sopenharmony_ci return pa_source_process_msg(o, code, data, offset, chunk); 87553a5a1b3Sopenharmony_ci} 87653a5a1b3Sopenharmony_ci 87753a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 87853a5a1b3Sopenharmony_cistatic int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 87953a5a1b3Sopenharmony_ci struct userdata *u; 88053a5a1b3Sopenharmony_ci 88153a5a1b3Sopenharmony_ci pa_assert(s); 88253a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 88353a5a1b3Sopenharmony_ci 88453a5a1b3Sopenharmony_ci switch (new_state) { 88553a5a1b3Sopenharmony_ci 88653a5a1b3Sopenharmony_ci case PA_SOURCE_SUSPENDED: 88753a5a1b3Sopenharmony_ci /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */ 88853a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_OPENED(s->thread_info.state)) 88953a5a1b3Sopenharmony_ci break; 89053a5a1b3Sopenharmony_ci 89153a5a1b3Sopenharmony_ci /* Stop the device if the sink is suspended as well */ 89253a5a1b3Sopenharmony_ci if (!u->sink || u->sink->state == PA_SINK_SUSPENDED) 89353a5a1b3Sopenharmony_ci transport_release(u); 89453a5a1b3Sopenharmony_ci 89553a5a1b3Sopenharmony_ci if (u->read_smoother) 89653a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 89753a5a1b3Sopenharmony_ci pa_smoother_2_pause(u->read_smoother, pa_rtclock_now()); 89853a5a1b3Sopenharmony_ci#else 89953a5a1b3Sopenharmony_ci pa_smoother_pause(u->read_smoother, pa_rtclock_now()); 90053a5a1b3Sopenharmony_ci#endif 90153a5a1b3Sopenharmony_ci break; 90253a5a1b3Sopenharmony_ci 90353a5a1b3Sopenharmony_ci case PA_SOURCE_IDLE: 90453a5a1b3Sopenharmony_ci case PA_SOURCE_RUNNING: 90553a5a1b3Sopenharmony_ci if (s->thread_info.state != PA_SOURCE_SUSPENDED) 90653a5a1b3Sopenharmony_ci break; 90753a5a1b3Sopenharmony_ci 90853a5a1b3Sopenharmony_ci /* Resume the device if the sink was suspended as well */ 90953a5a1b3Sopenharmony_ci if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) 91053a5a1b3Sopenharmony_ci if (!setup_transport_and_stream(u)) 91153a5a1b3Sopenharmony_ci return -1; 91253a5a1b3Sopenharmony_ci 91353a5a1b3Sopenharmony_ci /* We don't resume the smoother here. Instead we 91453a5a1b3Sopenharmony_ci * wait until the first packet arrives */ 91553a5a1b3Sopenharmony_ci 91653a5a1b3Sopenharmony_ci break; 91753a5a1b3Sopenharmony_ci 91853a5a1b3Sopenharmony_ci case PA_SOURCE_UNLINKED: 91953a5a1b3Sopenharmony_ci case PA_SOURCE_INIT: 92053a5a1b3Sopenharmony_ci case PA_SOURCE_INVALID_STATE: 92153a5a1b3Sopenharmony_ci break; 92253a5a1b3Sopenharmony_ci } 92353a5a1b3Sopenharmony_ci 92453a5a1b3Sopenharmony_ci return 0; 92553a5a1b3Sopenharmony_ci} 92653a5a1b3Sopenharmony_ci 92753a5a1b3Sopenharmony_ci/* Run from main thread */ 92853a5a1b3Sopenharmony_cistatic void source_set_volume_cb(pa_source *s) { 92953a5a1b3Sopenharmony_ci pa_volume_t volume; 93053a5a1b3Sopenharmony_ci struct userdata *u; 93153a5a1b3Sopenharmony_ci 93253a5a1b3Sopenharmony_ci pa_assert(s); 93353a5a1b3Sopenharmony_ci pa_assert(s->core); 93453a5a1b3Sopenharmony_ci 93553a5a1b3Sopenharmony_ci u = s->userdata; 93653a5a1b3Sopenharmony_ci 93753a5a1b3Sopenharmony_ci pa_assert(u); 93853a5a1b3Sopenharmony_ci pa_assert(u->source == s); 93953a5a1b3Sopenharmony_ci pa_assert(!pa_bluetooth_profile_should_attenuate_volume(u->profile)); 94053a5a1b3Sopenharmony_ci pa_assert(u->transport); 94153a5a1b3Sopenharmony_ci pa_assert(u->transport->set_source_volume); 94253a5a1b3Sopenharmony_ci 94353a5a1b3Sopenharmony_ci /* In the AG role, send a command to change microphone gain on the HS/HF */ 94453a5a1b3Sopenharmony_ci volume = u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume)); 94553a5a1b3Sopenharmony_ci 94653a5a1b3Sopenharmony_ci pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume); 94753a5a1b3Sopenharmony_ci} 94853a5a1b3Sopenharmony_ci 94953a5a1b3Sopenharmony_ci/* Run from main thread */ 95053a5a1b3Sopenharmony_cistatic void source_setup_volume_callback(pa_source *s) { 95153a5a1b3Sopenharmony_ci struct userdata *u; 95253a5a1b3Sopenharmony_ci 95353a5a1b3Sopenharmony_ci pa_assert(s); 95453a5a1b3Sopenharmony_ci pa_assert(s->core); 95553a5a1b3Sopenharmony_ci 95653a5a1b3Sopenharmony_ci u = s->userdata; 95753a5a1b3Sopenharmony_ci pa_assert(u); 95853a5a1b3Sopenharmony_ci pa_assert(u->source == s); 95953a5a1b3Sopenharmony_ci pa_assert(u->transport); 96053a5a1b3Sopenharmony_ci 96153a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume) 96253a5a1b3Sopenharmony_ci return; 96353a5a1b3Sopenharmony_ci 96453a5a1b3Sopenharmony_ci /* Remote volume control has to be supported for the callback to make sense, 96553a5a1b3Sopenharmony_ci * otherwise this source should continue performing attenuation in software 96653a5a1b3Sopenharmony_ci * without HW_VOLUME_CTL. 96753a5a1b3Sopenharmony_ci * If the peer is an AG however backend-native unconditionally provides this 96853a5a1b3Sopenharmony_ci * function, PA in the role of HS/HF is responsible for signalling support 96953a5a1b3Sopenharmony_ci * by emitting an initial volume command. 97053a5a1b3Sopenharmony_ci * For A2DP bluez-util also unconditionally provides this function to keep 97153a5a1b3Sopenharmony_ci * the peer informed about volume changes. 97253a5a1b3Sopenharmony_ci */ 97353a5a1b3Sopenharmony_ci if (!u->transport->set_source_volume) 97453a5a1b3Sopenharmony_ci return; 97553a5a1b3Sopenharmony_ci 97653a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) { 97753a5a1b3Sopenharmony_ci if (u->source_volume_changed_slot) 97853a5a1b3Sopenharmony_ci return; 97953a5a1b3Sopenharmony_ci 98053a5a1b3Sopenharmony_ci pa_log_debug("%s: Attaching volume hook to notify peer of changes", s->name); 98153a5a1b3Sopenharmony_ci 98253a5a1b3Sopenharmony_ci u->source_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED], 98353a5a1b3Sopenharmony_ci PA_HOOK_NORMAL, sink_source_volume_changed_cb, u); 98453a5a1b3Sopenharmony_ci 98553a5a1b3Sopenharmony_ci /* Send initial volume to peer, signalling support for volume control */ 98653a5a1b3Sopenharmony_ci u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume)); 98753a5a1b3Sopenharmony_ci } else { 98853a5a1b3Sopenharmony_ci /* It is yet unknown how (if at all) volume is synchronized for bidirectional 98953a5a1b3Sopenharmony_ci * A2DP codecs. Disallow attaching callbacks (and using HFP n_volume_steps) 99053a5a1b3Sopenharmony_ci * below to a pa_source if the peer is in A2DP_SINK role. This assert should 99153a5a1b3Sopenharmony_ci * be replaced with the proper logic when bidirectional codecs are implemented. 99253a5a1b3Sopenharmony_ci */ 99353a5a1b3Sopenharmony_ci pa_assert(u->profile != PA_BLUETOOTH_PROFILE_A2DP_SINK); 99453a5a1b3Sopenharmony_ci 99553a5a1b3Sopenharmony_ci if (s->set_volume == source_set_volume_cb) 99653a5a1b3Sopenharmony_ci return; 99753a5a1b3Sopenharmony_ci 99853a5a1b3Sopenharmony_ci pa_log_debug("%s: Resetting software volume for hardware attenuation by peer", s->name); 99953a5a1b3Sopenharmony_ci 100053a5a1b3Sopenharmony_ci /* Reset local attenuation */ 100153a5a1b3Sopenharmony_ci pa_source_set_soft_volume(s, NULL); 100253a5a1b3Sopenharmony_ci 100353a5a1b3Sopenharmony_ci pa_source_set_set_volume_callback(s, source_set_volume_cb); 100453a5a1b3Sopenharmony_ci s->n_volume_steps = HSP_MAX_GAIN + 1; 100553a5a1b3Sopenharmony_ci } 100653a5a1b3Sopenharmony_ci} 100753a5a1b3Sopenharmony_ci 100853a5a1b3Sopenharmony_ci/* Run from main thread */ 100953a5a1b3Sopenharmony_cistatic int add_source(struct userdata *u) { 101053a5a1b3Sopenharmony_ci pa_source_new_data data; 101153a5a1b3Sopenharmony_ci 101253a5a1b3Sopenharmony_ci pa_assert(u->transport); 101353a5a1b3Sopenharmony_ci 101453a5a1b3Sopenharmony_ci pa_source_new_data_init(&data); 101553a5a1b3Sopenharmony_ci data.module = u->module; 101653a5a1b3Sopenharmony_ci data.card = u->card; 101753a5a1b3Sopenharmony_ci data.driver = __FILE__; 101853a5a1b3Sopenharmony_ci data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); 101953a5a1b3Sopenharmony_ci data.namereg_fail = false; 102053a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); 102153a5a1b3Sopenharmony_ci if (u->bt_codec) 102253a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name); 102353a5a1b3Sopenharmony_ci pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec); 102453a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS 102553a5a1b3Sopenharmony_ci || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) 102653a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); 102753a5a1b3Sopenharmony_ci 102853a5a1b3Sopenharmony_ci connect_ports(u, &data, PA_DIRECTION_INPUT); 102953a5a1b3Sopenharmony_ci 103053a5a1b3Sopenharmony_ci if (!u->transport_acquired) 103153a5a1b3Sopenharmony_ci switch (u->profile) { 103253a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: 103353a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HFP_AG: 103453a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HSP_AG: 103553a5a1b3Sopenharmony_ci data.suspend_cause = PA_SUSPEND_USER; 103653a5a1b3Sopenharmony_ci break; 103753a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HSP_HS: 103853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HFP_HF: 103953a5a1b3Sopenharmony_ci /* u->stream_fd contains the error returned by the last transport_acquire() 104053a5a1b3Sopenharmony_ci * EAGAIN means we are waiting for a NewConnection signal */ 104153a5a1b3Sopenharmony_ci if (u->stream_fd == -EAGAIN) 104253a5a1b3Sopenharmony_ci data.suspend_cause = PA_SUSPEND_USER; 104353a5a1b3Sopenharmony_ci else 104453a5a1b3Sopenharmony_ci pa_assert_not_reached(); 104553a5a1b3Sopenharmony_ci break; 104653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_A2DP_SINK: 104753a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_OFF: 104853a5a1b3Sopenharmony_ci pa_assert_not_reached(); 104953a5a1b3Sopenharmony_ci break; 105053a5a1b3Sopenharmony_ci } 105153a5a1b3Sopenharmony_ci 105253a5a1b3Sopenharmony_ci u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); 105353a5a1b3Sopenharmony_ci pa_source_new_data_done(&data); 105453a5a1b3Sopenharmony_ci if (!u->source) { 105553a5a1b3Sopenharmony_ci pa_log_error("Failed to create source"); 105653a5a1b3Sopenharmony_ci return -1; 105753a5a1b3Sopenharmony_ci } 105853a5a1b3Sopenharmony_ci 105953a5a1b3Sopenharmony_ci u->source->userdata = u; 106053a5a1b3Sopenharmony_ci u->source->parent.process_msg = source_process_msg; 106153a5a1b3Sopenharmony_ci u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; 106253a5a1b3Sopenharmony_ci 106353a5a1b3Sopenharmony_ci source_setup_volume_callback(u->source); 106453a5a1b3Sopenharmony_ci 106553a5a1b3Sopenharmony_ci return 0; 106653a5a1b3Sopenharmony_ci} 106753a5a1b3Sopenharmony_ci 106853a5a1b3Sopenharmony_ci/* Run from IO thread */ 106953a5a1b3Sopenharmony_cistatic int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 107053a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK(o)->userdata; 107153a5a1b3Sopenharmony_ci 107253a5a1b3Sopenharmony_ci pa_assert(u->sink == PA_SINK(o)); 107353a5a1b3Sopenharmony_ci pa_assert(u->transport); 107453a5a1b3Sopenharmony_ci 107553a5a1b3Sopenharmony_ci switch (code) { 107653a5a1b3Sopenharmony_ci 107753a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: { 107853a5a1b3Sopenharmony_ci int64_t wi, ri, delay = 0; 107953a5a1b3Sopenharmony_ci 108053a5a1b3Sopenharmony_ci if (u->read_smoother) { 108153a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 108253a5a1b3Sopenharmony_ci /* This is only used for SCO where encoder and decoder sample specs are 108353a5a1b3Sopenharmony_ci * equal and output timing is based on the source. Therefore we can pass 108453a5a1b3Sopenharmony_ci * the write index without conversion. */ 108553a5a1b3Sopenharmony_ci delay = pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->write_index + u->write_block_size); 108653a5a1b3Sopenharmony_ci#else 108753a5a1b3Sopenharmony_ci ri = pa_smoother_get(u->read_smoother, pa_rtclock_now()); 108853a5a1b3Sopenharmony_ci wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->encoder_sample_spec); 108953a5a1b3Sopenharmony_ci delay = wi - ri; 109053a5a1b3Sopenharmony_ci#endif 109153a5a1b3Sopenharmony_ci } else if (u->started_at) { 109253a5a1b3Sopenharmony_ci ri = pa_rtclock_now() - u->started_at; 109353a5a1b3Sopenharmony_ci wi = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec); 109453a5a1b3Sopenharmony_ci delay = wi - ri; 109553a5a1b3Sopenharmony_ci } 109653a5a1b3Sopenharmony_ci 109753a5a1b3Sopenharmony_ci *((int64_t*) data) = u->sink->thread_info.fixed_latency + delay; 109853a5a1b3Sopenharmony_ci 109953a5a1b3Sopenharmony_ci return 0; 110053a5a1b3Sopenharmony_ci } 110153a5a1b3Sopenharmony_ci 110253a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_SETUP_STREAM: 110353a5a1b3Sopenharmony_ci /* Skip stream setup if stream_fd has been invalidated. 110453a5a1b3Sopenharmony_ci This can occur if the stream has already been set up and 110553a5a1b3Sopenharmony_ci then immediately received POLLHUP. If the stream has 110653a5a1b3Sopenharmony_ci already been set up earlier, then this setup_stream() 110753a5a1b3Sopenharmony_ci call is redundant anyway, but currently the code 110853a5a1b3Sopenharmony_ci is such that this kind of unnecessary setup_stream() 110953a5a1b3Sopenharmony_ci calls can happen. */ 111053a5a1b3Sopenharmony_ci if (u->stream_fd < 0) 111153a5a1b3Sopenharmony_ci pa_log_debug("Skip sink stream setup while closing"); 111253a5a1b3Sopenharmony_ci else 111353a5a1b3Sopenharmony_ci setup_stream(u); 111453a5a1b3Sopenharmony_ci return 0; 111553a5a1b3Sopenharmony_ci } 111653a5a1b3Sopenharmony_ci 111753a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 111853a5a1b3Sopenharmony_ci} 111953a5a1b3Sopenharmony_ci 112053a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 112153a5a1b3Sopenharmony_cistatic int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 112253a5a1b3Sopenharmony_ci struct userdata *u; 112353a5a1b3Sopenharmony_ci 112453a5a1b3Sopenharmony_ci pa_assert(s); 112553a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 112653a5a1b3Sopenharmony_ci 112753a5a1b3Sopenharmony_ci switch (new_state) { 112853a5a1b3Sopenharmony_ci 112953a5a1b3Sopenharmony_ci case PA_SINK_SUSPENDED: 113053a5a1b3Sopenharmony_ci /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */ 113153a5a1b3Sopenharmony_ci if (!PA_SINK_IS_OPENED(s->thread_info.state)) 113253a5a1b3Sopenharmony_ci break; 113353a5a1b3Sopenharmony_ci 113453a5a1b3Sopenharmony_ci /* Stop the device if the source is suspended as well */ 113553a5a1b3Sopenharmony_ci if (!u->source || u->source->state == PA_SOURCE_SUSPENDED) 113653a5a1b3Sopenharmony_ci /* We deliberately ignore whether stopping 113753a5a1b3Sopenharmony_ci * actually worked. Since the stream_fd is 113853a5a1b3Sopenharmony_ci * closed it doesn't really matter */ 113953a5a1b3Sopenharmony_ci transport_release(u); 114053a5a1b3Sopenharmony_ci 114153a5a1b3Sopenharmony_ci break; 114253a5a1b3Sopenharmony_ci 114353a5a1b3Sopenharmony_ci case PA_SINK_IDLE: 114453a5a1b3Sopenharmony_ci case PA_SINK_RUNNING: 114553a5a1b3Sopenharmony_ci if (s->thread_info.state != PA_SINK_SUSPENDED) 114653a5a1b3Sopenharmony_ci break; 114753a5a1b3Sopenharmony_ci 114853a5a1b3Sopenharmony_ci /* Resume the device if the source was suspended as well */ 114953a5a1b3Sopenharmony_ci if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) 115053a5a1b3Sopenharmony_ci if (!setup_transport_and_stream(u)) 115153a5a1b3Sopenharmony_ci return -1; 115253a5a1b3Sopenharmony_ci 115353a5a1b3Sopenharmony_ci break; 115453a5a1b3Sopenharmony_ci 115553a5a1b3Sopenharmony_ci case PA_SINK_UNLINKED: 115653a5a1b3Sopenharmony_ci case PA_SINK_INIT: 115753a5a1b3Sopenharmony_ci case PA_SINK_INVALID_STATE: 115853a5a1b3Sopenharmony_ci break; 115953a5a1b3Sopenharmony_ci } 116053a5a1b3Sopenharmony_ci 116153a5a1b3Sopenharmony_ci return 0; 116253a5a1b3Sopenharmony_ci} 116353a5a1b3Sopenharmony_ci 116453a5a1b3Sopenharmony_ci/* Run from main thread */ 116553a5a1b3Sopenharmony_cistatic void sink_set_volume_cb(pa_sink *s) { 116653a5a1b3Sopenharmony_ci pa_volume_t volume; 116753a5a1b3Sopenharmony_ci struct userdata *u; 116853a5a1b3Sopenharmony_ci 116953a5a1b3Sopenharmony_ci pa_assert(s); 117053a5a1b3Sopenharmony_ci pa_assert(s->core); 117153a5a1b3Sopenharmony_ci 117253a5a1b3Sopenharmony_ci u = s->userdata; 117353a5a1b3Sopenharmony_ci 117453a5a1b3Sopenharmony_ci pa_assert(u); 117553a5a1b3Sopenharmony_ci pa_assert(u->sink == s); 117653a5a1b3Sopenharmony_ci pa_assert(!pa_bluetooth_profile_should_attenuate_volume(u->profile)); 117753a5a1b3Sopenharmony_ci pa_assert(u->transport); 117853a5a1b3Sopenharmony_ci pa_assert(u->transport->set_sink_volume); 117953a5a1b3Sopenharmony_ci 118053a5a1b3Sopenharmony_ci /* In the AG role, send a command to change speaker gain on the HS/HF */ 118153a5a1b3Sopenharmony_ci volume = u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume)); 118253a5a1b3Sopenharmony_ci 118353a5a1b3Sopenharmony_ci pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume); 118453a5a1b3Sopenharmony_ci} 118553a5a1b3Sopenharmony_ci 118653a5a1b3Sopenharmony_ci/* Run from main thread */ 118753a5a1b3Sopenharmony_cistatic void sink_setup_volume_callback(pa_sink *s) { 118853a5a1b3Sopenharmony_ci struct userdata *u; 118953a5a1b3Sopenharmony_ci 119053a5a1b3Sopenharmony_ci pa_assert(s); 119153a5a1b3Sopenharmony_ci pa_assert(s->core); 119253a5a1b3Sopenharmony_ci 119353a5a1b3Sopenharmony_ci u = s->userdata; 119453a5a1b3Sopenharmony_ci pa_assert(u); 119553a5a1b3Sopenharmony_ci pa_assert(u->sink == s); 119653a5a1b3Sopenharmony_ci pa_assert(u->transport); 119753a5a1b3Sopenharmony_ci 119853a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume) 119953a5a1b3Sopenharmony_ci return; 120053a5a1b3Sopenharmony_ci 120153a5a1b3Sopenharmony_ci /* Remote volume control has to be supported for the callback to make sense, 120253a5a1b3Sopenharmony_ci * otherwise this sink should continue performing attenuation in software 120353a5a1b3Sopenharmony_ci * without HW_VOLUME_CTL. 120453a5a1b3Sopenharmony_ci * If the peer is an AG however backend-native unconditionally provides this 120553a5a1b3Sopenharmony_ci * function, PA in the role of HS/HF is responsible for signalling support 120653a5a1b3Sopenharmony_ci * by emitting an initial volume command. 120753a5a1b3Sopenharmony_ci */ 120853a5a1b3Sopenharmony_ci if (!u->transport->set_sink_volume) 120953a5a1b3Sopenharmony_ci return; 121053a5a1b3Sopenharmony_ci 121153a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) { 121253a5a1b3Sopenharmony_ci /* It is yet unknown how (if at all) volume is synchronized for bidirectional 121353a5a1b3Sopenharmony_ci * A2DP codecs. Disallow attaching hooks to a pa_sink if the peer is in 121453a5a1b3Sopenharmony_ci * A2DP_SOURCE role. This assert should be replaced with the proper logic 121553a5a1b3Sopenharmony_ci * when bidirectional codecs are implemented. 121653a5a1b3Sopenharmony_ci */ 121753a5a1b3Sopenharmony_ci pa_assert(u->profile != PA_BLUETOOTH_PROFILE_A2DP_SOURCE); 121853a5a1b3Sopenharmony_ci 121953a5a1b3Sopenharmony_ci if (u->sink_volume_changed_slot) 122053a5a1b3Sopenharmony_ci return; 122153a5a1b3Sopenharmony_ci 122253a5a1b3Sopenharmony_ci pa_log_debug("%s: Attaching volume hook to notify peer of changes", s->name); 122353a5a1b3Sopenharmony_ci 122453a5a1b3Sopenharmony_ci u->sink_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], 122553a5a1b3Sopenharmony_ci PA_HOOK_NORMAL, sink_source_volume_changed_cb, u); 122653a5a1b3Sopenharmony_ci 122753a5a1b3Sopenharmony_ci /* Send initial volume to peer, signalling support for volume control */ 122853a5a1b3Sopenharmony_ci u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume)); 122953a5a1b3Sopenharmony_ci } else { 123053a5a1b3Sopenharmony_ci if (s->set_volume == sink_set_volume_cb) 123153a5a1b3Sopenharmony_ci return; 123253a5a1b3Sopenharmony_ci 123353a5a1b3Sopenharmony_ci pa_log_debug("%s: Resetting software volume for hardware attenuation by peer", s->name); 123453a5a1b3Sopenharmony_ci 123553a5a1b3Sopenharmony_ci /* Reset local attenuation */ 123653a5a1b3Sopenharmony_ci pa_sink_set_soft_volume(s, NULL); 123753a5a1b3Sopenharmony_ci 123853a5a1b3Sopenharmony_ci pa_sink_set_set_volume_callback(s, sink_set_volume_cb); 123953a5a1b3Sopenharmony_ci 124053a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) 124153a5a1b3Sopenharmony_ci s->n_volume_steps = A2DP_MAX_GAIN + 1; 124253a5a1b3Sopenharmony_ci else 124353a5a1b3Sopenharmony_ci s->n_volume_steps = HSP_MAX_GAIN + 1; 124453a5a1b3Sopenharmony_ci } 124553a5a1b3Sopenharmony_ci} 124653a5a1b3Sopenharmony_ci 124753a5a1b3Sopenharmony_ci/* Run from main thread */ 124853a5a1b3Sopenharmony_cistatic int add_sink(struct userdata *u) { 124953a5a1b3Sopenharmony_ci pa_sink_new_data data; 125053a5a1b3Sopenharmony_ci 125153a5a1b3Sopenharmony_ci pa_assert(u->transport); 125253a5a1b3Sopenharmony_ci 125353a5a1b3Sopenharmony_ci pa_sink_new_data_init(&data); 125453a5a1b3Sopenharmony_ci data.module = u->module; 125553a5a1b3Sopenharmony_ci data.card = u->card; 125653a5a1b3Sopenharmony_ci data.driver = __FILE__; 125753a5a1b3Sopenharmony_ci data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); 125853a5a1b3Sopenharmony_ci data.namereg_fail = false; 125953a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); 126053a5a1b3Sopenharmony_ci if (u->bt_codec) 126153a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name); 126253a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec); 126353a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS 126453a5a1b3Sopenharmony_ci || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) 126553a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); 126653a5a1b3Sopenharmony_ci 126753a5a1b3Sopenharmony_ci connect_ports(u, &data, PA_DIRECTION_OUTPUT); 126853a5a1b3Sopenharmony_ci 126953a5a1b3Sopenharmony_ci if (!u->transport_acquired) 127053a5a1b3Sopenharmony_ci switch (u->profile) { 127153a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HFP_AG: 127253a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HSP_AG: 127353a5a1b3Sopenharmony_ci data.suspend_cause = PA_SUSPEND_USER; 127453a5a1b3Sopenharmony_ci break; 127553a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HSP_HS: 127653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HFP_HF: 127753a5a1b3Sopenharmony_ci /* u->stream_fd contains the error returned by the last transport_acquire() 127853a5a1b3Sopenharmony_ci * EAGAIN means we are waiting for a NewConnection signal */ 127953a5a1b3Sopenharmony_ci if (u->stream_fd == -EAGAIN) 128053a5a1b3Sopenharmony_ci data.suspend_cause = PA_SUSPEND_USER; 128153a5a1b3Sopenharmony_ci else 128253a5a1b3Sopenharmony_ci pa_assert_not_reached(); 128353a5a1b3Sopenharmony_ci break; 128453a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_A2DP_SINK: 128553a5a1b3Sopenharmony_ci /* Profile switch should have failed */ 128653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: 128753a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_OFF: 128853a5a1b3Sopenharmony_ci pa_assert_not_reached(); 128953a5a1b3Sopenharmony_ci break; 129053a5a1b3Sopenharmony_ci } 129153a5a1b3Sopenharmony_ci 129253a5a1b3Sopenharmony_ci u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); 129353a5a1b3Sopenharmony_ci pa_sink_new_data_done(&data); 129453a5a1b3Sopenharmony_ci if (!u->sink) { 129553a5a1b3Sopenharmony_ci pa_log_error("Failed to create sink"); 129653a5a1b3Sopenharmony_ci return -1; 129753a5a1b3Sopenharmony_ci } 129853a5a1b3Sopenharmony_ci 129953a5a1b3Sopenharmony_ci u->sink->userdata = u; 130053a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg; 130153a5a1b3Sopenharmony_ci u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; 130253a5a1b3Sopenharmony_ci 130353a5a1b3Sopenharmony_ci sink_setup_volume_callback(u->sink); 130453a5a1b3Sopenharmony_ci 130553a5a1b3Sopenharmony_ci return 0; 130653a5a1b3Sopenharmony_ci} 130753a5a1b3Sopenharmony_ci 130853a5a1b3Sopenharmony_ci/* Run from main thread */ 130953a5a1b3Sopenharmony_cistatic pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) { 131053a5a1b3Sopenharmony_ci static const pa_direction_t profile_direction[] = { 131153a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT, 131253a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT, 131353a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_HSP_HS] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, 131453a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_HSP_AG] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, 131553a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_HFP_HF] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, 131653a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_HFP_AG] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, 131753a5a1b3Sopenharmony_ci [PA_BLUETOOTH_PROFILE_OFF] = 0 131853a5a1b3Sopenharmony_ci }; 131953a5a1b3Sopenharmony_ci 132053a5a1b3Sopenharmony_ci return profile_direction[p]; 132153a5a1b3Sopenharmony_ci} 132253a5a1b3Sopenharmony_ci 132353a5a1b3Sopenharmony_ci/* Run from main thread */ 132453a5a1b3Sopenharmony_cistatic int transport_config(struct userdata *u) { 132553a5a1b3Sopenharmony_ci pa_assert(u); 132653a5a1b3Sopenharmony_ci pa_assert(u->transport); 132753a5a1b3Sopenharmony_ci pa_assert(!u->bt_codec); 132853a5a1b3Sopenharmony_ci pa_assert(!u->encoder_info); 132953a5a1b3Sopenharmony_ci pa_assert(!u->decoder_info); 133053a5a1b3Sopenharmony_ci 133153a5a1b3Sopenharmony_ci u->bt_codec = u->transport->bt_codec; 133253a5a1b3Sopenharmony_ci pa_assert(u->bt_codec); 133353a5a1b3Sopenharmony_ci 133453a5a1b3Sopenharmony_ci /* reset encoder buffer contents */ 133553a5a1b3Sopenharmony_ci u->encoder_buffer_used = 0; 133653a5a1b3Sopenharmony_ci 133753a5a1b3Sopenharmony_ci if (get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT) { 133853a5a1b3Sopenharmony_ci u->encoder_info = u->bt_codec->init(true, false, u->transport->config, u->transport->config_size, &u->encoder_sample_spec, u->core); 133953a5a1b3Sopenharmony_ci 134053a5a1b3Sopenharmony_ci if (!u->encoder_info) 134153a5a1b3Sopenharmony_ci return -1; 134253a5a1b3Sopenharmony_ci } 134353a5a1b3Sopenharmony_ci 134453a5a1b3Sopenharmony_ci if (get_profile_direction(u->profile) & PA_DIRECTION_INPUT) { 134553a5a1b3Sopenharmony_ci u->decoder_info = u->bt_codec->init(false, false, u->transport->config, u->transport->config_size, &u->decoder_sample_spec, u->core); 134653a5a1b3Sopenharmony_ci 134753a5a1b3Sopenharmony_ci if (!u->decoder_info) { 134853a5a1b3Sopenharmony_ci if (u->encoder_info) { 134953a5a1b3Sopenharmony_ci u->bt_codec->deinit(u->encoder_info); 135053a5a1b3Sopenharmony_ci u->encoder_info = NULL; 135153a5a1b3Sopenharmony_ci } 135253a5a1b3Sopenharmony_ci return -1; 135353a5a1b3Sopenharmony_ci } 135453a5a1b3Sopenharmony_ci } 135553a5a1b3Sopenharmony_ci 135653a5a1b3Sopenharmony_ci return 0; 135753a5a1b3Sopenharmony_ci} 135853a5a1b3Sopenharmony_ci 135953a5a1b3Sopenharmony_ci/* Run from main thread */ 136053a5a1b3Sopenharmony_cistatic int setup_transport(struct userdata *u) { 136153a5a1b3Sopenharmony_ci pa_bluetooth_transport *t; 136253a5a1b3Sopenharmony_ci 136353a5a1b3Sopenharmony_ci pa_assert(u); 136453a5a1b3Sopenharmony_ci pa_assert(!u->transport); 136553a5a1b3Sopenharmony_ci pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF); 136653a5a1b3Sopenharmony_ci 136753a5a1b3Sopenharmony_ci /* check if profile has a transport */ 136853a5a1b3Sopenharmony_ci t = u->device->transports[u->profile]; 136953a5a1b3Sopenharmony_ci if (!t || t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) { 137053a5a1b3Sopenharmony_ci pa_log_warn("Profile %s has no transport", pa_bluetooth_profile_to_string(u->profile)); 137153a5a1b3Sopenharmony_ci return -1; 137253a5a1b3Sopenharmony_ci } 137353a5a1b3Sopenharmony_ci 137453a5a1b3Sopenharmony_ci u->transport = t; 137553a5a1b3Sopenharmony_ci 137653a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) 137753a5a1b3Sopenharmony_ci transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */ 137853a5a1b3Sopenharmony_ci else { 137953a5a1b3Sopenharmony_ci int transport_error; 138053a5a1b3Sopenharmony_ci 138153a5a1b3Sopenharmony_ci transport_error = transport_acquire(u, false); 138253a5a1b3Sopenharmony_ci if (transport_error < 0 && transport_error != -EAGAIN) 138353a5a1b3Sopenharmony_ci return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */ 138453a5a1b3Sopenharmony_ci } 138553a5a1b3Sopenharmony_ci 138653a5a1b3Sopenharmony_ci return transport_config(u); 138753a5a1b3Sopenharmony_ci} 138853a5a1b3Sopenharmony_ci 138953a5a1b3Sopenharmony_ci/* Run from main thread */ 139053a5a1b3Sopenharmony_cistatic int init_profile(struct userdata *u) { 139153a5a1b3Sopenharmony_ci int r = 0; 139253a5a1b3Sopenharmony_ci pa_assert(u); 139353a5a1b3Sopenharmony_ci pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF); 139453a5a1b3Sopenharmony_ci 139553a5a1b3Sopenharmony_ci r = setup_transport(u); 139653a5a1b3Sopenharmony_ci if (r == -EINPROGRESS) 139753a5a1b3Sopenharmony_ci return 0; 139853a5a1b3Sopenharmony_ci else if (r < 0) 139953a5a1b3Sopenharmony_ci return -1; 140053a5a1b3Sopenharmony_ci 140153a5a1b3Sopenharmony_ci pa_assert(u->transport); 140253a5a1b3Sopenharmony_ci 140353a5a1b3Sopenharmony_ci if (get_profile_direction (u->profile) & PA_DIRECTION_OUTPUT) 140453a5a1b3Sopenharmony_ci if (add_sink(u) < 0) 140553a5a1b3Sopenharmony_ci r = -1; 140653a5a1b3Sopenharmony_ci 140753a5a1b3Sopenharmony_ci if (get_profile_direction (u->profile) & PA_DIRECTION_INPUT) 140853a5a1b3Sopenharmony_ci if (add_source(u) < 0) 140953a5a1b3Sopenharmony_ci r = -1; 141053a5a1b3Sopenharmony_ci 141153a5a1b3Sopenharmony_ci return r; 141253a5a1b3Sopenharmony_ci} 141353a5a1b3Sopenharmony_ci 141453a5a1b3Sopenharmony_cistatic int bt_render_block(struct userdata *u) { 141553a5a1b3Sopenharmony_ci int n_rendered; 141653a5a1b3Sopenharmony_ci 141753a5a1b3Sopenharmony_ci if (u->write_index <= 0) 141853a5a1b3Sopenharmony_ci u->started_at = pa_rtclock_now(); 141953a5a1b3Sopenharmony_ci 142053a5a1b3Sopenharmony_ci n_rendered = bt_process_render(u); 142153a5a1b3Sopenharmony_ci 142253a5a1b3Sopenharmony_ci if (n_rendered < 0) 142353a5a1b3Sopenharmony_ci n_rendered = -1; 142453a5a1b3Sopenharmony_ci 142553a5a1b3Sopenharmony_ci return n_rendered; 142653a5a1b3Sopenharmony_ci} 142753a5a1b3Sopenharmony_ci 142853a5a1b3Sopenharmony_ci/* I/O thread function */ 142953a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) { 143053a5a1b3Sopenharmony_ci struct userdata *u = userdata; 143153a5a1b3Sopenharmony_ci unsigned blocks_to_write = 0; 143253a5a1b3Sopenharmony_ci unsigned bytes_to_write = 0; 143353a5a1b3Sopenharmony_ci struct timeval tv_last_output_rate_change; 143453a5a1b3Sopenharmony_ci 143553a5a1b3Sopenharmony_ci pa_assert(u); 143653a5a1b3Sopenharmony_ci pa_assert(u->transport); 143753a5a1b3Sopenharmony_ci 143853a5a1b3Sopenharmony_ci pa_log_debug("IO Thread starting up"); 143953a5a1b3Sopenharmony_ci 144053a5a1b3Sopenharmony_ci if (u->core->realtime_scheduling) 144153a5a1b3Sopenharmony_ci pa_thread_make_realtime(u->core->realtime_priority); 144253a5a1b3Sopenharmony_ci 144353a5a1b3Sopenharmony_ci pa_thread_mq_install(&u->thread_mq); 144453a5a1b3Sopenharmony_ci 144553a5a1b3Sopenharmony_ci /* Setup the stream only if the transport was already acquired */ 144653a5a1b3Sopenharmony_ci if (u->transport_acquired) 144753a5a1b3Sopenharmony_ci setup_stream(u); 144853a5a1b3Sopenharmony_ci 144953a5a1b3Sopenharmony_ci pa_gettimeofday(&tv_last_output_rate_change); 145053a5a1b3Sopenharmony_ci 145153a5a1b3Sopenharmony_ci for (;;) { 145253a5a1b3Sopenharmony_ci struct pollfd *pollfd; 145353a5a1b3Sopenharmony_ci int ret; 145453a5a1b3Sopenharmony_ci bool disable_timer = true; 145553a5a1b3Sopenharmony_ci bool writable = false; 145653a5a1b3Sopenharmony_ci bool have_source = u->source ? PA_SOURCE_IS_LINKED(u->source->thread_info.state) : false; 145753a5a1b3Sopenharmony_ci bool have_sink = u->sink ? PA_SINK_IS_LINKED(u->sink->thread_info.state) : false; 145853a5a1b3Sopenharmony_ci 145953a5a1b3Sopenharmony_ci pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL; 146053a5a1b3Sopenharmony_ci 146153a5a1b3Sopenharmony_ci /* Check for stream error or close */ 146253a5a1b3Sopenharmony_ci if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) { 146353a5a1b3Sopenharmony_ci pa_log_info("FD error: %s%s%s%s", 146453a5a1b3Sopenharmony_ci pollfd->revents & POLLERR ? "POLLERR " :"", 146553a5a1b3Sopenharmony_ci pollfd->revents & POLLHUP ? "POLLHUP " :"", 146653a5a1b3Sopenharmony_ci pollfd->revents & POLLPRI ? "POLLPRI " :"", 146753a5a1b3Sopenharmony_ci pollfd->revents & POLLNVAL ? "POLLNVAL " :""); 146853a5a1b3Sopenharmony_ci 146953a5a1b3Sopenharmony_ci if (pollfd->revents & POLLHUP) { 147053a5a1b3Sopenharmony_ci pollfd = NULL; 147153a5a1b3Sopenharmony_ci teardown_stream(u); 147253a5a1b3Sopenharmony_ci blocks_to_write = 0; 147353a5a1b3Sopenharmony_ci bytes_to_write = 0; 147453a5a1b3Sopenharmony_ci pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL); 147553a5a1b3Sopenharmony_ci } else 147653a5a1b3Sopenharmony_ci goto fail; 147753a5a1b3Sopenharmony_ci } 147853a5a1b3Sopenharmony_ci 147953a5a1b3Sopenharmony_ci /* If there is a pollfd, the stream is set up and we need to do something */ 148053a5a1b3Sopenharmony_ci if (pollfd) { 148153a5a1b3Sopenharmony_ci 148253a5a1b3Sopenharmony_ci /* Handle source if present */ 148353a5a1b3Sopenharmony_ci if (have_source) { 148453a5a1b3Sopenharmony_ci 148553a5a1b3Sopenharmony_ci /* We should send two blocks to the device before we expect a response. */ 148653a5a1b3Sopenharmony_ci if (have_sink && u->write_index == 0 && u->read_index <= 0) 148753a5a1b3Sopenharmony_ci blocks_to_write = 2; 148853a5a1b3Sopenharmony_ci 148953a5a1b3Sopenharmony_ci /* If we got woken up by POLLIN let's do some reading */ 149053a5a1b3Sopenharmony_ci if (pollfd->revents & POLLIN) { 149153a5a1b3Sopenharmony_ci int n_read; 149253a5a1b3Sopenharmony_ci 149353a5a1b3Sopenharmony_ci n_read = bt_process_push(u); 149453a5a1b3Sopenharmony_ci 149553a5a1b3Sopenharmony_ci if (n_read < 0) 149653a5a1b3Sopenharmony_ci goto fail; 149753a5a1b3Sopenharmony_ci 149853a5a1b3Sopenharmony_ci if (have_sink && n_read > 0) { 149953a5a1b3Sopenharmony_ci /* We just read something, so we are supposed to write something, too 150053a5a1b3Sopenharmony_ci * 150153a5a1b3Sopenharmony_ci * If source and sink sample specifications are not equal, 150253a5a1b3Sopenharmony_ci * expected write size needs to be adjusted accordingly. 150353a5a1b3Sopenharmony_ci */ 150453a5a1b3Sopenharmony_ci if (pa_sample_spec_equal(&u->encoder_sample_spec, &u->decoder_sample_spec)) 150553a5a1b3Sopenharmony_ci bytes_to_write += n_read; 150653a5a1b3Sopenharmony_ci else 150753a5a1b3Sopenharmony_ci bytes_to_write += pa_usec_to_bytes(pa_bytes_to_usec(n_read, &u->decoder_sample_spec), &u->encoder_sample_spec); 150853a5a1b3Sopenharmony_ci blocks_to_write += bytes_to_write / u->write_block_size; 150953a5a1b3Sopenharmony_ci bytes_to_write = bytes_to_write % u->write_block_size; 151053a5a1b3Sopenharmony_ci } 151153a5a1b3Sopenharmony_ci } 151253a5a1b3Sopenharmony_ci } 151353a5a1b3Sopenharmony_ci 151453a5a1b3Sopenharmony_ci /* Handle sink if present */ 151553a5a1b3Sopenharmony_ci if (have_sink) { 151653a5a1b3Sopenharmony_ci 151753a5a1b3Sopenharmony_ci /* Process rewinds */ 151853a5a1b3Sopenharmony_ci if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) 151953a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 152053a5a1b3Sopenharmony_ci 152153a5a1b3Sopenharmony_ci /* Test if the stream is writable */ 152253a5a1b3Sopenharmony_ci if (pollfd->revents & POLLOUT) 152353a5a1b3Sopenharmony_ci writable = true; 152453a5a1b3Sopenharmony_ci 152553a5a1b3Sopenharmony_ci /* If we have a source, we let the source determine the timing 152653a5a1b3Sopenharmony_ci * for the sink */ 152753a5a1b3Sopenharmony_ci if (have_source) { 152853a5a1b3Sopenharmony_ci 152953a5a1b3Sopenharmony_ci /* If the stream is writable, send some data if necessary */ 153053a5a1b3Sopenharmony_ci if (writable) { 153153a5a1b3Sopenharmony_ci int result; 153253a5a1b3Sopenharmony_ci 153353a5a1b3Sopenharmony_ci if (blocks_to_write > 0) { 153453a5a1b3Sopenharmony_ci result = bt_render_block(u); 153553a5a1b3Sopenharmony_ci if (result < 0) 153653a5a1b3Sopenharmony_ci goto fail; 153753a5a1b3Sopenharmony_ci blocks_to_write -= result; 153853a5a1b3Sopenharmony_ci } 153953a5a1b3Sopenharmony_ci 154053a5a1b3Sopenharmony_ci result = bt_write_buffer(u); 154153a5a1b3Sopenharmony_ci 154253a5a1b3Sopenharmony_ci if (result < 0) 154353a5a1b3Sopenharmony_ci goto fail; 154453a5a1b3Sopenharmony_ci 154553a5a1b3Sopenharmony_ci if (result) 154653a5a1b3Sopenharmony_ci writable = false; 154753a5a1b3Sopenharmony_ci } 154853a5a1b3Sopenharmony_ci 154953a5a1b3Sopenharmony_ci /* writable controls whether we set POLLOUT when polling - we set it to 155053a5a1b3Sopenharmony_ci * false to enable POLLOUT. If there are more blocks to write, we want to 155153a5a1b3Sopenharmony_ci * be woken up immediately when the socket becomes writable. If there 155253a5a1b3Sopenharmony_ci * aren't currently any more blocks to write, then we'll have to wait 155353a5a1b3Sopenharmony_ci * until we've received more data, so in that case we only want to set 155453a5a1b3Sopenharmony_ci * POLLIN. Note that when we are woken up the next time, POLLOUT won't be 155553a5a1b3Sopenharmony_ci * set in revents even if the socket has meanwhile become writable, which 155653a5a1b3Sopenharmony_ci * may seem bad, but in that case we'll set POLLOUT in the subsequent 155753a5a1b3Sopenharmony_ci * poll, and the poll will return immediately, so our writes won't be 155853a5a1b3Sopenharmony_ci * delayed. */ 155953a5a1b3Sopenharmony_ci if (blocks_to_write > 0) 156053a5a1b3Sopenharmony_ci writable = false; 156153a5a1b3Sopenharmony_ci 156253a5a1b3Sopenharmony_ci /* There is no source, we have to use the system clock for timing */ 156353a5a1b3Sopenharmony_ci } else { 156453a5a1b3Sopenharmony_ci bool have_written = false; 156553a5a1b3Sopenharmony_ci pa_usec_t time_passed = 0; 156653a5a1b3Sopenharmony_ci pa_usec_t audio_sent = 0; 156753a5a1b3Sopenharmony_ci 156853a5a1b3Sopenharmony_ci if (u->started_at) { 156953a5a1b3Sopenharmony_ci time_passed = pa_rtclock_now() - u->started_at; 157053a5a1b3Sopenharmony_ci audio_sent = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec); 157153a5a1b3Sopenharmony_ci } 157253a5a1b3Sopenharmony_ci 157353a5a1b3Sopenharmony_ci /* A new block needs to be sent. */ 157453a5a1b3Sopenharmony_ci if (audio_sent <= time_passed) { 157553a5a1b3Sopenharmony_ci size_t bytes_to_send = pa_usec_to_bytes(time_passed - audio_sent, &u->encoder_sample_spec); 157653a5a1b3Sopenharmony_ci 157753a5a1b3Sopenharmony_ci /* There are more than two blocks that need to be written. It seems that 157853a5a1b3Sopenharmony_ci * the socket has not been accepting data fast enough (could be due to 157953a5a1b3Sopenharmony_ci * hiccups in the wireless transmission). We need to discard everything 158053a5a1b3Sopenharmony_ci * older than two block sizes to keep the latency from growing. */ 158153a5a1b3Sopenharmony_ci if (bytes_to_send > 2 * u->write_block_size) { 158253a5a1b3Sopenharmony_ci uint64_t skip_bytes; 158353a5a1b3Sopenharmony_ci pa_memchunk tmp; 158453a5a1b3Sopenharmony_ci size_t max_render_size = pa_frame_align(pa_mempool_block_size_max(u->core->mempool), &u->encoder_sample_spec); 158553a5a1b3Sopenharmony_ci pa_usec_t skip_usec; 158653a5a1b3Sopenharmony_ci 158753a5a1b3Sopenharmony_ci skip_bytes = bytes_to_send - 2 * u->write_block_size; 158853a5a1b3Sopenharmony_ci skip_usec = pa_bytes_to_usec(skip_bytes, &u->encoder_sample_spec); 158953a5a1b3Sopenharmony_ci 159053a5a1b3Sopenharmony_ci pa_log_debug("Skipping %llu us (= %llu bytes) in audio stream", 159153a5a1b3Sopenharmony_ci (unsigned long long) skip_usec, 159253a5a1b3Sopenharmony_ci (unsigned long long) skip_bytes); 159353a5a1b3Sopenharmony_ci 159453a5a1b3Sopenharmony_ci while (skip_bytes > 0) { 159553a5a1b3Sopenharmony_ci size_t bytes_to_render; 159653a5a1b3Sopenharmony_ci 159753a5a1b3Sopenharmony_ci if (skip_bytes > max_render_size) 159853a5a1b3Sopenharmony_ci bytes_to_render = max_render_size; 159953a5a1b3Sopenharmony_ci else 160053a5a1b3Sopenharmony_ci bytes_to_render = skip_bytes; 160153a5a1b3Sopenharmony_ci 160253a5a1b3Sopenharmony_ci pa_sink_render_full(u->sink, bytes_to_render, &tmp); 160353a5a1b3Sopenharmony_ci pa_memblock_unref(tmp.memblock); 160453a5a1b3Sopenharmony_ci u->write_index += bytes_to_render; 160553a5a1b3Sopenharmony_ci skip_bytes -= bytes_to_render; 160653a5a1b3Sopenharmony_ci } 160753a5a1b3Sopenharmony_ci 160853a5a1b3Sopenharmony_ci if (u->write_index > 0 && (get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT)) { 160953a5a1b3Sopenharmony_ci size_t new_write_block_size = u->bt_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu); 161053a5a1b3Sopenharmony_ci if (new_write_block_size) { 161153a5a1b3Sopenharmony_ci u->write_block_size = new_write_block_size; 161253a5a1b3Sopenharmony_ci handle_sink_block_size_change(u); 161353a5a1b3Sopenharmony_ci } 161453a5a1b3Sopenharmony_ci pa_gettimeofday(&tv_last_output_rate_change); 161553a5a1b3Sopenharmony_ci } 161653a5a1b3Sopenharmony_ci } 161753a5a1b3Sopenharmony_ci 161853a5a1b3Sopenharmony_ci blocks_to_write = 1; 161953a5a1b3Sopenharmony_ci } 162053a5a1b3Sopenharmony_ci 162153a5a1b3Sopenharmony_ci /* If the stream is writable, send some data if necessary */ 162253a5a1b3Sopenharmony_ci if (writable) { 162353a5a1b3Sopenharmony_ci int result; 162453a5a1b3Sopenharmony_ci 162553a5a1b3Sopenharmony_ci if (blocks_to_write > 0) { 162653a5a1b3Sopenharmony_ci int result = bt_render_block(u); 162753a5a1b3Sopenharmony_ci if (result < 0) 162853a5a1b3Sopenharmony_ci goto fail; 162953a5a1b3Sopenharmony_ci blocks_to_write -= result; 163053a5a1b3Sopenharmony_ci } 163153a5a1b3Sopenharmony_ci 163253a5a1b3Sopenharmony_ci result = bt_write_buffer(u); 163353a5a1b3Sopenharmony_ci 163453a5a1b3Sopenharmony_ci if (result < 0) 163553a5a1b3Sopenharmony_ci goto fail; 163653a5a1b3Sopenharmony_ci 163753a5a1b3Sopenharmony_ci if (result) { 163853a5a1b3Sopenharmony_ci writable = false; 163953a5a1b3Sopenharmony_ci have_written = true; 164053a5a1b3Sopenharmony_ci } 164153a5a1b3Sopenharmony_ci } 164253a5a1b3Sopenharmony_ci 164353a5a1b3Sopenharmony_ci /* If nothing was written during this iteration, either the stream 164453a5a1b3Sopenharmony_ci * is not writable or there was no write pending. Set up a timer that 164553a5a1b3Sopenharmony_ci * will wake up the thread when the next data needs to be written. */ 164653a5a1b3Sopenharmony_ci if (!have_written) { 164753a5a1b3Sopenharmony_ci pa_usec_t sleep_for; 164853a5a1b3Sopenharmony_ci pa_usec_t next_write_at; 164953a5a1b3Sopenharmony_ci 165053a5a1b3Sopenharmony_ci if (writable) { 165153a5a1b3Sopenharmony_ci /* There was no write pending on this iteration of the loop. 165253a5a1b3Sopenharmony_ci * Let's estimate when we need to wake up next */ 165353a5a1b3Sopenharmony_ci next_write_at = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec); 165453a5a1b3Sopenharmony_ci sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0; 165553a5a1b3Sopenharmony_ci /* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */ 165653a5a1b3Sopenharmony_ci 165753a5a1b3Sopenharmony_ci if ((get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT) && u->write_memchunk.memblock == NULL) { 165853a5a1b3Sopenharmony_ci /* bt_write_buffer() is keeping up with input, try increasing bitrate */ 165953a5a1b3Sopenharmony_ci if (u->bt_codec->increase_encoder_bitrate 166053a5a1b3Sopenharmony_ci && pa_timeval_age(&tv_last_output_rate_change) >= u->device->output_rate_refresh_interval_ms * PA_USEC_PER_MSEC) { 166153a5a1b3Sopenharmony_ci size_t new_write_block_size = u->bt_codec->increase_encoder_bitrate(u->encoder_info, u->write_link_mtu); 166253a5a1b3Sopenharmony_ci if (new_write_block_size) { 166353a5a1b3Sopenharmony_ci u->write_block_size = new_write_block_size; 166453a5a1b3Sopenharmony_ci handle_sink_block_size_change(u); 166553a5a1b3Sopenharmony_ci } 166653a5a1b3Sopenharmony_ci pa_gettimeofday(&tv_last_output_rate_change); 166753a5a1b3Sopenharmony_ci } 166853a5a1b3Sopenharmony_ci } 166953a5a1b3Sopenharmony_ci } else 167053a5a1b3Sopenharmony_ci /* We could not write because the stream was not ready. Let's try 167153a5a1b3Sopenharmony_ci * again in 500 ms and drop audio if we still can't write. The 167253a5a1b3Sopenharmony_ci * thread will also be woken up when we can write again. */ 167353a5a1b3Sopenharmony_ci sleep_for = PA_USEC_PER_MSEC * 500; 167453a5a1b3Sopenharmony_ci 167553a5a1b3Sopenharmony_ci pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for); 167653a5a1b3Sopenharmony_ci disable_timer = false; 167753a5a1b3Sopenharmony_ci } 167853a5a1b3Sopenharmony_ci } 167953a5a1b3Sopenharmony_ci } 168053a5a1b3Sopenharmony_ci 168153a5a1b3Sopenharmony_ci /* Set events to wake up the thread */ 168253a5a1b3Sopenharmony_ci pollfd->events = (short) (((have_sink && !writable) ? POLLOUT : 0) | (have_source ? POLLIN : 0)); 168353a5a1b3Sopenharmony_ci 168453a5a1b3Sopenharmony_ci } 168553a5a1b3Sopenharmony_ci 168653a5a1b3Sopenharmony_ci if (disable_timer) 168753a5a1b3Sopenharmony_ci pa_rtpoll_set_timer_disabled(u->rtpoll); 168853a5a1b3Sopenharmony_ci 168953a5a1b3Sopenharmony_ci if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) { 169053a5a1b3Sopenharmony_ci pa_log_debug("pa_rtpoll_run failed with: %d", ret); 169153a5a1b3Sopenharmony_ci goto fail; 169253a5a1b3Sopenharmony_ci } 169353a5a1b3Sopenharmony_ci 169453a5a1b3Sopenharmony_ci if (ret == 0) { 169553a5a1b3Sopenharmony_ci pa_log_debug("IO thread shutdown requested, stopping cleanly"); 169653a5a1b3Sopenharmony_ci transport_release(u); 169753a5a1b3Sopenharmony_ci goto finish; 169853a5a1b3Sopenharmony_ci } 169953a5a1b3Sopenharmony_ci } 170053a5a1b3Sopenharmony_ci 170153a5a1b3Sopenharmony_cifail: 170253a5a1b3Sopenharmony_ci /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */ 170353a5a1b3Sopenharmony_ci pa_log_debug("IO thread failed"); 170453a5a1b3Sopenharmony_ci pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_IO_THREAD_FAILED, NULL, 0, NULL, NULL); 170553a5a1b3Sopenharmony_ci pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); 170653a5a1b3Sopenharmony_ci 170753a5a1b3Sopenharmony_cifinish: 170853a5a1b3Sopenharmony_ci pa_log_debug("IO thread shutting down"); 170953a5a1b3Sopenharmony_ci} 171053a5a1b3Sopenharmony_ci 171153a5a1b3Sopenharmony_ci/* Run from main thread */ 171253a5a1b3Sopenharmony_cistatic int start_thread(struct userdata *u) { 171353a5a1b3Sopenharmony_ci pa_assert(u); 171453a5a1b3Sopenharmony_ci pa_assert(!u->thread); 171553a5a1b3Sopenharmony_ci pa_assert(!u->rtpoll); 171653a5a1b3Sopenharmony_ci pa_assert(!u->rtpoll_item); 171753a5a1b3Sopenharmony_ci 171853a5a1b3Sopenharmony_ci u->rtpoll = pa_rtpoll_new(); 171953a5a1b3Sopenharmony_ci 172053a5a1b3Sopenharmony_ci if (pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll) < 0) { 172153a5a1b3Sopenharmony_ci pa_log("pa_thread_mq_init() failed."); 172253a5a1b3Sopenharmony_ci return -1; 172353a5a1b3Sopenharmony_ci } 172453a5a1b3Sopenharmony_ci 172553a5a1b3Sopenharmony_ci if (!(u->thread = pa_thread_new("bluetooth", thread_func, u))) { 172653a5a1b3Sopenharmony_ci pa_log_error("Failed to create IO thread"); 172753a5a1b3Sopenharmony_ci return -1; 172853a5a1b3Sopenharmony_ci } 172953a5a1b3Sopenharmony_ci 173053a5a1b3Sopenharmony_ci if (u->sink) { 173153a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); 173253a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, u->rtpoll); 173353a5a1b3Sopenharmony_ci 173453a5a1b3Sopenharmony_ci /* If we are in the headset role, the sink should not become default 173553a5a1b3Sopenharmony_ci * unless there is no other sound device available. */ 173653a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) 173753a5a1b3Sopenharmony_ci u->sink->priority = 1500; 173853a5a1b3Sopenharmony_ci 173953a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 174053a5a1b3Sopenharmony_ci 174153a5a1b3Sopenharmony_ci if (u->sink->set_volume) 174253a5a1b3Sopenharmony_ci u->sink->set_volume(u->sink); 174353a5a1b3Sopenharmony_ci } 174453a5a1b3Sopenharmony_ci 174553a5a1b3Sopenharmony_ci if (u->source) { 174653a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); 174753a5a1b3Sopenharmony_ci pa_source_set_rtpoll(u->source, u->rtpoll); 174853a5a1b3Sopenharmony_ci 174953a5a1b3Sopenharmony_ci /* If we are in the headset role or the device is an a2dp source, 175053a5a1b3Sopenharmony_ci * the source should not become default unless there is no other 175153a5a1b3Sopenharmony_ci * sound device available. */ 175253a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) 175353a5a1b3Sopenharmony_ci u->source->priority = 1500; 175453a5a1b3Sopenharmony_ci 175553a5a1b3Sopenharmony_ci pa_source_put(u->source); 175653a5a1b3Sopenharmony_ci 175753a5a1b3Sopenharmony_ci if (u->source->set_volume) 175853a5a1b3Sopenharmony_ci u->source->set_volume(u->source); 175953a5a1b3Sopenharmony_ci } 176053a5a1b3Sopenharmony_ci 176153a5a1b3Sopenharmony_ci if (u->sink || u->source) 176253a5a1b3Sopenharmony_ci if (u->bt_codec) 176353a5a1b3Sopenharmony_ci pa_proplist_sets(u->card->proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name); 176453a5a1b3Sopenharmony_ci 176553a5a1b3Sopenharmony_ci /* Now that everything is set up we are ready to check for the Volume property. 176653a5a1b3Sopenharmony_ci * Sometimes its initial "change" notification arrives too early when the sink 176753a5a1b3Sopenharmony_ci * is not available or still in UNLINKED state; check it again here to know if 176853a5a1b3Sopenharmony_ci * our sink peer supports Absolute Volume; in that case we should not perform 176953a5a1b3Sopenharmony_ci * any attenuation but delegate all set_volume calls to the peer through this 177053a5a1b3Sopenharmony_ci * Volume property. 177153a5a1b3Sopenharmony_ci * 177253a5a1b3Sopenharmony_ci * Note that this works the other way around if the peer is in source profile: 177353a5a1b3Sopenharmony_ci * we are rendering audio and hence responsible for applying attenuation. The 177453a5a1b3Sopenharmony_ci * set_volume callback is always registered, and Volume is always passed to 177553a5a1b3Sopenharmony_ci * BlueZ unconditionally. BlueZ only sends a notification to the peer if it 177653a5a1b3Sopenharmony_ci * registered a notification request for absolute volume previously. 177753a5a1b3Sopenharmony_ci */ 177853a5a1b3Sopenharmony_ci if (u->transport && u->sink) 177953a5a1b3Sopenharmony_ci pa_bluetooth_transport_load_a2dp_sink_volume(u->transport); 178053a5a1b3Sopenharmony_ci 178153a5a1b3Sopenharmony_ci return 0; 178253a5a1b3Sopenharmony_ci} 178353a5a1b3Sopenharmony_ci 178453a5a1b3Sopenharmony_ci/* Run from main thread */ 178553a5a1b3Sopenharmony_cistatic void stop_thread(struct userdata *u) { 178653a5a1b3Sopenharmony_ci pa_assert(u); 178753a5a1b3Sopenharmony_ci 178853a5a1b3Sopenharmony_ci if (u->sink || u->source) 178953a5a1b3Sopenharmony_ci pa_proplist_unset(u->card->proplist, PA_PROP_BLUETOOTH_CODEC); 179053a5a1b3Sopenharmony_ci 179153a5a1b3Sopenharmony_ci if (u->sink) 179253a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 179353a5a1b3Sopenharmony_ci 179453a5a1b3Sopenharmony_ci if (u->source) 179553a5a1b3Sopenharmony_ci pa_source_unlink(u->source); 179653a5a1b3Sopenharmony_ci 179753a5a1b3Sopenharmony_ci if (u->thread) { 179853a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); 179953a5a1b3Sopenharmony_ci pa_thread_free(u->thread); 180053a5a1b3Sopenharmony_ci u->thread = NULL; 180153a5a1b3Sopenharmony_ci } 180253a5a1b3Sopenharmony_ci 180353a5a1b3Sopenharmony_ci if (u->rtpoll_item) { 180453a5a1b3Sopenharmony_ci pa_rtpoll_item_free(u->rtpoll_item); 180553a5a1b3Sopenharmony_ci u->rtpoll_item = NULL; 180653a5a1b3Sopenharmony_ci } 180753a5a1b3Sopenharmony_ci 180853a5a1b3Sopenharmony_ci if (u->rtpoll) { 180953a5a1b3Sopenharmony_ci pa_rtpoll_free(u->rtpoll); 181053a5a1b3Sopenharmony_ci u->rtpoll = NULL; 181153a5a1b3Sopenharmony_ci pa_thread_mq_done(&u->thread_mq); 181253a5a1b3Sopenharmony_ci } 181353a5a1b3Sopenharmony_ci 181453a5a1b3Sopenharmony_ci if (u->transport) { 181553a5a1b3Sopenharmony_ci transport_release(u); 181653a5a1b3Sopenharmony_ci u->transport = NULL; 181753a5a1b3Sopenharmony_ci } 181853a5a1b3Sopenharmony_ci 181953a5a1b3Sopenharmony_ci if (u->sink_volume_changed_slot) { 182053a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_volume_changed_slot); 182153a5a1b3Sopenharmony_ci u->sink_volume_changed_slot = NULL; 182253a5a1b3Sopenharmony_ci } 182353a5a1b3Sopenharmony_ci 182453a5a1b3Sopenharmony_ci if (u->source_volume_changed_slot) { 182553a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_volume_changed_slot); 182653a5a1b3Sopenharmony_ci u->source_volume_changed_slot = NULL; 182753a5a1b3Sopenharmony_ci } 182853a5a1b3Sopenharmony_ci 182953a5a1b3Sopenharmony_ci if (u->sink) { 183053a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 183153a5a1b3Sopenharmony_ci u->sink = NULL; 183253a5a1b3Sopenharmony_ci } 183353a5a1b3Sopenharmony_ci 183453a5a1b3Sopenharmony_ci if (u->source) { 183553a5a1b3Sopenharmony_ci pa_source_unref(u->source); 183653a5a1b3Sopenharmony_ci u->source = NULL; 183753a5a1b3Sopenharmony_ci } 183853a5a1b3Sopenharmony_ci 183953a5a1b3Sopenharmony_ci if (u->read_smoother) { 184053a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 184153a5a1b3Sopenharmony_ci pa_smoother_2_free(u->read_smoother); 184253a5a1b3Sopenharmony_ci#else 184353a5a1b3Sopenharmony_ci pa_smoother_free(u->read_smoother); 184453a5a1b3Sopenharmony_ci#endif 184553a5a1b3Sopenharmony_ci u->read_smoother = NULL; 184653a5a1b3Sopenharmony_ci } 184753a5a1b3Sopenharmony_ci 184853a5a1b3Sopenharmony_ci if (u->bt_codec) { 184953a5a1b3Sopenharmony_ci if (u->encoder_info) { 185053a5a1b3Sopenharmony_ci u->bt_codec->deinit(u->encoder_info); 185153a5a1b3Sopenharmony_ci u->encoder_info = NULL; 185253a5a1b3Sopenharmony_ci } 185353a5a1b3Sopenharmony_ci 185453a5a1b3Sopenharmony_ci if (u->decoder_info) { 185553a5a1b3Sopenharmony_ci u->bt_codec->deinit(u->decoder_info); 185653a5a1b3Sopenharmony_ci u->decoder_info = NULL; 185753a5a1b3Sopenharmony_ci } 185853a5a1b3Sopenharmony_ci 185953a5a1b3Sopenharmony_ci u->bt_codec = NULL; 186053a5a1b3Sopenharmony_ci } 186153a5a1b3Sopenharmony_ci 186253a5a1b3Sopenharmony_ci if (u->encoder_buffer) { 186353a5a1b3Sopenharmony_ci pa_xfree(u->encoder_buffer); 186453a5a1b3Sopenharmony_ci u->encoder_buffer = NULL; 186553a5a1b3Sopenharmony_ci } 186653a5a1b3Sopenharmony_ci 186753a5a1b3Sopenharmony_ci u->encoder_buffer_size = 0; 186853a5a1b3Sopenharmony_ci u->encoder_buffer_used = 0; 186953a5a1b3Sopenharmony_ci 187053a5a1b3Sopenharmony_ci if (u->decoder_buffer) { 187153a5a1b3Sopenharmony_ci pa_xfree(u->decoder_buffer); 187253a5a1b3Sopenharmony_ci u->decoder_buffer = NULL; 187353a5a1b3Sopenharmony_ci } 187453a5a1b3Sopenharmony_ci 187553a5a1b3Sopenharmony_ci u->decoder_buffer_size = 0; 187653a5a1b3Sopenharmony_ci} 187753a5a1b3Sopenharmony_ci 187853a5a1b3Sopenharmony_ci/* Run from main thread */ 187953a5a1b3Sopenharmony_cistatic pa_available_t get_port_availability(struct userdata *u, pa_direction_t direction) { 188053a5a1b3Sopenharmony_ci pa_available_t result = PA_AVAILABLE_NO; 188153a5a1b3Sopenharmony_ci unsigned i; 188253a5a1b3Sopenharmony_ci 188353a5a1b3Sopenharmony_ci pa_assert(u); 188453a5a1b3Sopenharmony_ci pa_assert(u->device); 188553a5a1b3Sopenharmony_ci 188653a5a1b3Sopenharmony_ci for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) { 188753a5a1b3Sopenharmony_ci pa_bluetooth_transport *transport; 188853a5a1b3Sopenharmony_ci 188953a5a1b3Sopenharmony_ci if (!(get_profile_direction(i) & direction)) 189053a5a1b3Sopenharmony_ci continue; 189153a5a1b3Sopenharmony_ci 189253a5a1b3Sopenharmony_ci if (!(transport = u->device->transports[i])) 189353a5a1b3Sopenharmony_ci continue; 189453a5a1b3Sopenharmony_ci 189553a5a1b3Sopenharmony_ci switch(transport->state) { 189653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED: 189753a5a1b3Sopenharmony_ci continue; 189853a5a1b3Sopenharmony_ci 189953a5a1b3Sopenharmony_ci case PA_BLUETOOTH_TRANSPORT_STATE_IDLE: 190053a5a1b3Sopenharmony_ci if (result == PA_AVAILABLE_NO) 190153a5a1b3Sopenharmony_ci result = PA_AVAILABLE_UNKNOWN; 190253a5a1b3Sopenharmony_ci 190353a5a1b3Sopenharmony_ci break; 190453a5a1b3Sopenharmony_ci 190553a5a1b3Sopenharmony_ci case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING: 190653a5a1b3Sopenharmony_ci return PA_AVAILABLE_YES; 190753a5a1b3Sopenharmony_ci } 190853a5a1b3Sopenharmony_ci } 190953a5a1b3Sopenharmony_ci 191053a5a1b3Sopenharmony_ci return result; 191153a5a1b3Sopenharmony_ci} 191253a5a1b3Sopenharmony_ci 191353a5a1b3Sopenharmony_ci/* Run from main thread */ 191453a5a1b3Sopenharmony_cistatic pa_available_t transport_state_to_availability(pa_bluetooth_transport_state_t state) { 191553a5a1b3Sopenharmony_ci switch (state) { 191653a5a1b3Sopenharmony_ci case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED: 191753a5a1b3Sopenharmony_ci return PA_AVAILABLE_NO; 191853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING: 191953a5a1b3Sopenharmony_ci return PA_AVAILABLE_YES; 192053a5a1b3Sopenharmony_ci default: 192153a5a1b3Sopenharmony_ci return PA_AVAILABLE_UNKNOWN; 192253a5a1b3Sopenharmony_ci } 192353a5a1b3Sopenharmony_ci} 192453a5a1b3Sopenharmony_ci 192553a5a1b3Sopenharmony_ci/* Run from main thread */ 192653a5a1b3Sopenharmony_cistatic void create_card_ports(struct userdata *u, pa_hashmap *ports) { 192753a5a1b3Sopenharmony_ci pa_device_port *port; 192853a5a1b3Sopenharmony_ci pa_device_port_new_data port_data; 192953a5a1b3Sopenharmony_ci pa_device_port_type_t input_type, output_type; 193053a5a1b3Sopenharmony_ci const char *name_prefix, *input_description, *output_description; 193153a5a1b3Sopenharmony_ci 193253a5a1b3Sopenharmony_ci pa_assert(u); 193353a5a1b3Sopenharmony_ci pa_assert(ports); 193453a5a1b3Sopenharmony_ci pa_assert(u->device); 193553a5a1b3Sopenharmony_ci 193653a5a1b3Sopenharmony_ci name_prefix = "unknown"; 193753a5a1b3Sopenharmony_ci input_description = _("Bluetooth Input"); 193853a5a1b3Sopenharmony_ci output_description = _("Bluetooth Output"); 193953a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_BLUETOOTH; 194053a5a1b3Sopenharmony_ci 194153a5a1b3Sopenharmony_ci switch (form_factor_from_class(u->device->class_of_device)) { 194253a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HEADSET: 194353a5a1b3Sopenharmony_ci name_prefix = "headset"; 194453a5a1b3Sopenharmony_ci input_description = output_description = _("Headset"); 194553a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_HEADSET; 194653a5a1b3Sopenharmony_ci break; 194753a5a1b3Sopenharmony_ci 194853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE: 194953a5a1b3Sopenharmony_ci name_prefix = "handsfree"; 195053a5a1b3Sopenharmony_ci input_description = output_description = _("Handsfree"); 195153a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_HANDSFREE; 195253a5a1b3Sopenharmony_ci break; 195353a5a1b3Sopenharmony_ci 195453a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE: 195553a5a1b3Sopenharmony_ci name_prefix = "microphone"; 195653a5a1b3Sopenharmony_ci input_description = _("Microphone"); 195753a5a1b3Sopenharmony_ci output_description = _("Bluetooth Output"); 195853a5a1b3Sopenharmony_ci input_type = PA_DEVICE_PORT_TYPE_MIC; 195953a5a1b3Sopenharmony_ci break; 196053a5a1b3Sopenharmony_ci 196153a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_SPEAKER: 196253a5a1b3Sopenharmony_ci name_prefix = "speaker"; 196353a5a1b3Sopenharmony_ci input_description = _("Bluetooth Input"); 196453a5a1b3Sopenharmony_ci output_description = _("Speaker"); 196553a5a1b3Sopenharmony_ci output_type = PA_DEVICE_PORT_TYPE_SPEAKER; 196653a5a1b3Sopenharmony_ci break; 196753a5a1b3Sopenharmony_ci 196853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE: 196953a5a1b3Sopenharmony_ci name_prefix = "headphone"; 197053a5a1b3Sopenharmony_ci input_description = _("Bluetooth Input"); 197153a5a1b3Sopenharmony_ci output_description = _("Headphone"); 197253a5a1b3Sopenharmony_ci output_type = PA_DEVICE_PORT_TYPE_HEADPHONES; 197353a5a1b3Sopenharmony_ci break; 197453a5a1b3Sopenharmony_ci 197553a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_PORTABLE: 197653a5a1b3Sopenharmony_ci name_prefix = "portable"; 197753a5a1b3Sopenharmony_ci input_description = output_description = _("Portable"); 197853a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_PORTABLE; 197953a5a1b3Sopenharmony_ci break; 198053a5a1b3Sopenharmony_ci 198153a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_CAR: 198253a5a1b3Sopenharmony_ci name_prefix = "car"; 198353a5a1b3Sopenharmony_ci input_description = output_description = _("Car"); 198453a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_CAR; 198553a5a1b3Sopenharmony_ci break; 198653a5a1b3Sopenharmony_ci 198753a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_HIFI: 198853a5a1b3Sopenharmony_ci name_prefix = "hifi"; 198953a5a1b3Sopenharmony_ci input_description = output_description = _("HiFi"); 199053a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_HIFI; 199153a5a1b3Sopenharmony_ci break; 199253a5a1b3Sopenharmony_ci 199353a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_PHONE: 199453a5a1b3Sopenharmony_ci name_prefix = "phone"; 199553a5a1b3Sopenharmony_ci input_description = output_description = _("Phone"); 199653a5a1b3Sopenharmony_ci input_type = output_type = PA_DEVICE_PORT_TYPE_PHONE; 199753a5a1b3Sopenharmony_ci break; 199853a5a1b3Sopenharmony_ci 199953a5a1b3Sopenharmony_ci case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN: 200053a5a1b3Sopenharmony_ci break; 200153a5a1b3Sopenharmony_ci } 200253a5a1b3Sopenharmony_ci 200353a5a1b3Sopenharmony_ci u->output_port_name = pa_sprintf_malloc("%s-output", name_prefix); 200453a5a1b3Sopenharmony_ci pa_device_port_new_data_init(&port_data); 200553a5a1b3Sopenharmony_ci pa_device_port_new_data_set_name(&port_data, u->output_port_name); 200653a5a1b3Sopenharmony_ci pa_device_port_new_data_set_description(&port_data, output_description); 200753a5a1b3Sopenharmony_ci pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_OUTPUT); 200853a5a1b3Sopenharmony_ci pa_device_port_new_data_set_type(&port_data, output_type); 200953a5a1b3Sopenharmony_ci pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_OUTPUT)); 201053a5a1b3Sopenharmony_ci pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0)); 201153a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); 201253a5a1b3Sopenharmony_ci pa_device_port_new_data_done(&port_data); 201353a5a1b3Sopenharmony_ci 201453a5a1b3Sopenharmony_ci u->input_port_name = pa_sprintf_malloc("%s-input", name_prefix); 201553a5a1b3Sopenharmony_ci pa_device_port_new_data_init(&port_data); 201653a5a1b3Sopenharmony_ci pa_device_port_new_data_set_name(&port_data, u->input_port_name); 201753a5a1b3Sopenharmony_ci pa_device_port_new_data_set_description(&port_data, input_description); 201853a5a1b3Sopenharmony_ci pa_device_port_new_data_set_direction(&port_data, PA_DIRECTION_INPUT); 201953a5a1b3Sopenharmony_ci pa_device_port_new_data_set_type(&port_data, input_type); 202053a5a1b3Sopenharmony_ci pa_device_port_new_data_set_available(&port_data, get_port_availability(u, PA_DIRECTION_INPUT)); 202153a5a1b3Sopenharmony_ci pa_assert_se(port = pa_device_port_new(u->core, &port_data, 0)); 202253a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_put(ports, port->name, port) >= 0); 202353a5a1b3Sopenharmony_ci pa_device_port_new_data_done(&port_data); 202453a5a1b3Sopenharmony_ci} 202553a5a1b3Sopenharmony_ci 202653a5a1b3Sopenharmony_ci/* Run from main thread */ 202753a5a1b3Sopenharmony_cistatic pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_profile_t profile, pa_hashmap *ports) { 202853a5a1b3Sopenharmony_ci pa_device_port *input_port, *output_port; 202953a5a1b3Sopenharmony_ci const char *name; 203053a5a1b3Sopenharmony_ci pa_card_profile *cp = NULL; 203153a5a1b3Sopenharmony_ci pa_bluetooth_profile_t *p; 203253a5a1b3Sopenharmony_ci 203353a5a1b3Sopenharmony_ci pa_assert(u->input_port_name); 203453a5a1b3Sopenharmony_ci pa_assert(u->output_port_name); 203553a5a1b3Sopenharmony_ci pa_assert_se(input_port = pa_hashmap_get(ports, u->input_port_name)); 203653a5a1b3Sopenharmony_ci pa_assert_se(output_port = pa_hashmap_get(ports, u->output_port_name)); 203753a5a1b3Sopenharmony_ci 203853a5a1b3Sopenharmony_ci name = pa_bluetooth_profile_to_string(profile); 203953a5a1b3Sopenharmony_ci 204053a5a1b3Sopenharmony_ci switch (profile) { 204153a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_A2DP_SINK: 204253a5a1b3Sopenharmony_ci cp = pa_card_profile_new(name, _("High Fidelity Playback (A2DP Sink)"), sizeof(pa_bluetooth_profile_t)); 204353a5a1b3Sopenharmony_ci cp->priority = 40; 204453a5a1b3Sopenharmony_ci cp->n_sinks = 1; 204553a5a1b3Sopenharmony_ci cp->n_sources = 0; 204653a5a1b3Sopenharmony_ci cp->max_sink_channels = 2; 204753a5a1b3Sopenharmony_ci cp->max_source_channels = 0; 204853a5a1b3Sopenharmony_ci pa_hashmap_put(output_port->profiles, cp->name, cp); 204953a5a1b3Sopenharmony_ci 205053a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 205153a5a1b3Sopenharmony_ci break; 205253a5a1b3Sopenharmony_ci 205353a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: 205453a5a1b3Sopenharmony_ci cp = pa_card_profile_new(name, _("High Fidelity Capture (A2DP Source)"), sizeof(pa_bluetooth_profile_t)); 205553a5a1b3Sopenharmony_ci cp->priority = 20; 205653a5a1b3Sopenharmony_ci cp->n_sinks = 0; 205753a5a1b3Sopenharmony_ci cp->n_sources = 1; 205853a5a1b3Sopenharmony_ci cp->max_sink_channels = 0; 205953a5a1b3Sopenharmony_ci cp->max_source_channels = 2; 206053a5a1b3Sopenharmony_ci pa_hashmap_put(input_port->profiles, cp->name, cp); 206153a5a1b3Sopenharmony_ci 206253a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 206353a5a1b3Sopenharmony_ci break; 206453a5a1b3Sopenharmony_ci 206553a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HSP_HS: 206653a5a1b3Sopenharmony_ci cp = pa_card_profile_new(name, _("Headset Head Unit (HSP)"), sizeof(pa_bluetooth_profile_t)); 206753a5a1b3Sopenharmony_ci cp->priority = 30; 206853a5a1b3Sopenharmony_ci cp->n_sinks = 1; 206953a5a1b3Sopenharmony_ci cp->n_sources = 1; 207053a5a1b3Sopenharmony_ci cp->max_sink_channels = 1; 207153a5a1b3Sopenharmony_ci cp->max_source_channels = 1; 207253a5a1b3Sopenharmony_ci pa_hashmap_put(input_port->profiles, cp->name, cp); 207353a5a1b3Sopenharmony_ci pa_hashmap_put(output_port->profiles, cp->name, cp); 207453a5a1b3Sopenharmony_ci 207553a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 207653a5a1b3Sopenharmony_ci break; 207753a5a1b3Sopenharmony_ci 207853a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HSP_AG: 207953a5a1b3Sopenharmony_ci cp = pa_card_profile_new(name, _("Headset Audio Gateway (HSP)"), sizeof(pa_bluetooth_profile_t)); 208053a5a1b3Sopenharmony_ci cp->priority = 10; 208153a5a1b3Sopenharmony_ci cp->n_sinks = 1; 208253a5a1b3Sopenharmony_ci cp->n_sources = 1; 208353a5a1b3Sopenharmony_ci cp->max_sink_channels = 1; 208453a5a1b3Sopenharmony_ci cp->max_source_channels = 1; 208553a5a1b3Sopenharmony_ci pa_hashmap_put(input_port->profiles, cp->name, cp); 208653a5a1b3Sopenharmony_ci pa_hashmap_put(output_port->profiles, cp->name, cp); 208753a5a1b3Sopenharmony_ci 208853a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 208953a5a1b3Sopenharmony_ci break; 209053a5a1b3Sopenharmony_ci 209153a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HFP_HF: 209253a5a1b3Sopenharmony_ci cp = pa_card_profile_new(name, _("Handsfree Head Unit (HFP)"), sizeof(pa_bluetooth_profile_t)); 209353a5a1b3Sopenharmony_ci cp->priority = 30; 209453a5a1b3Sopenharmony_ci cp->n_sinks = 1; 209553a5a1b3Sopenharmony_ci cp->n_sources = 1; 209653a5a1b3Sopenharmony_ci cp->max_sink_channels = 1; 209753a5a1b3Sopenharmony_ci cp->max_source_channels = 1; 209853a5a1b3Sopenharmony_ci pa_hashmap_put(input_port->profiles, cp->name, cp); 209953a5a1b3Sopenharmony_ci pa_hashmap_put(output_port->profiles, cp->name, cp); 210053a5a1b3Sopenharmony_ci 210153a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 210253a5a1b3Sopenharmony_ci break; 210353a5a1b3Sopenharmony_ci 210453a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_HFP_AG: 210553a5a1b3Sopenharmony_ci cp = pa_card_profile_new(name, _("Handsfree Audio Gateway (HFP)"), sizeof(pa_bluetooth_profile_t)); 210653a5a1b3Sopenharmony_ci cp->priority = 10; 210753a5a1b3Sopenharmony_ci cp->n_sinks = 1; 210853a5a1b3Sopenharmony_ci cp->n_sources = 1; 210953a5a1b3Sopenharmony_ci cp->max_sink_channels = 1; 211053a5a1b3Sopenharmony_ci cp->max_source_channels = 1; 211153a5a1b3Sopenharmony_ci pa_hashmap_put(input_port->profiles, cp->name, cp); 211253a5a1b3Sopenharmony_ci pa_hashmap_put(output_port->profiles, cp->name, cp); 211353a5a1b3Sopenharmony_ci 211453a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 211553a5a1b3Sopenharmony_ci break; 211653a5a1b3Sopenharmony_ci 211753a5a1b3Sopenharmony_ci case PA_BLUETOOTH_PROFILE_OFF: 211853a5a1b3Sopenharmony_ci pa_assert_not_reached(); 211953a5a1b3Sopenharmony_ci } 212053a5a1b3Sopenharmony_ci 212153a5a1b3Sopenharmony_ci *p = profile; 212253a5a1b3Sopenharmony_ci 212353a5a1b3Sopenharmony_ci if (u->device->transports[*p]) 212453a5a1b3Sopenharmony_ci cp->available = transport_state_to_availability(u->device->transports[*p]->state); 212553a5a1b3Sopenharmony_ci else 212653a5a1b3Sopenharmony_ci cp->available = PA_AVAILABLE_NO; 212753a5a1b3Sopenharmony_ci 212853a5a1b3Sopenharmony_ci return cp; 212953a5a1b3Sopenharmony_ci} 213053a5a1b3Sopenharmony_ci 213153a5a1b3Sopenharmony_ci/* Run from main thread */ 213253a5a1b3Sopenharmony_cistatic int set_profile_cb(pa_card *c, pa_card_profile *new_profile) { 213353a5a1b3Sopenharmony_ci struct userdata *u; 213453a5a1b3Sopenharmony_ci pa_bluetooth_profile_t *p; 213553a5a1b3Sopenharmony_ci 213653a5a1b3Sopenharmony_ci pa_assert(c); 213753a5a1b3Sopenharmony_ci pa_assert(new_profile); 213853a5a1b3Sopenharmony_ci pa_assert_se(u = c->userdata); 213953a5a1b3Sopenharmony_ci 214053a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(new_profile); 214153a5a1b3Sopenharmony_ci 214253a5a1b3Sopenharmony_ci if (*p != PA_BLUETOOTH_PROFILE_OFF) { 214353a5a1b3Sopenharmony_ci const pa_bluetooth_device *d = u->device; 214453a5a1b3Sopenharmony_ci 214553a5a1b3Sopenharmony_ci if (!d->transports[*p] || d->transports[*p]->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) { 214653a5a1b3Sopenharmony_ci pa_log_warn("Refused to switch profile to %s: Not connected", new_profile->name); 214753a5a1b3Sopenharmony_ci return -PA_ERR_IO; 214853a5a1b3Sopenharmony_ci } 214953a5a1b3Sopenharmony_ci } 215053a5a1b3Sopenharmony_ci 215153a5a1b3Sopenharmony_ci stop_thread(u); 215253a5a1b3Sopenharmony_ci 215353a5a1b3Sopenharmony_ci u->profile = *p; 215453a5a1b3Sopenharmony_ci 215553a5a1b3Sopenharmony_ci if (u->profile != PA_BLUETOOTH_PROFILE_OFF) 215653a5a1b3Sopenharmony_ci if (init_profile(u) < 0) 215753a5a1b3Sopenharmony_ci goto off; 215853a5a1b3Sopenharmony_ci 215953a5a1b3Sopenharmony_ci if (u->sink || u->source) 216053a5a1b3Sopenharmony_ci if (start_thread(u) < 0) 216153a5a1b3Sopenharmony_ci goto off; 216253a5a1b3Sopenharmony_ci 216353a5a1b3Sopenharmony_ci return 0; 216453a5a1b3Sopenharmony_ci 216553a5a1b3Sopenharmony_cioff: 216653a5a1b3Sopenharmony_ci stop_thread(u); 216753a5a1b3Sopenharmony_ci 216853a5a1b3Sopenharmony_ci pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); 216953a5a1b3Sopenharmony_ci 217053a5a1b3Sopenharmony_ci return -PA_ERR_IO; 217153a5a1b3Sopenharmony_ci} 217253a5a1b3Sopenharmony_ci 217353a5a1b3Sopenharmony_cistatic int uuid_to_profile(const char *uuid, pa_bluetooth_profile_t *_r) { 217453a5a1b3Sopenharmony_ci if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) 217553a5a1b3Sopenharmony_ci *_r = PA_BLUETOOTH_PROFILE_A2DP_SINK; 217653a5a1b3Sopenharmony_ci else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) 217753a5a1b3Sopenharmony_ci *_r = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; 217853a5a1b3Sopenharmony_ci else if (pa_bluetooth_uuid_is_hsp_hs(uuid)) 217953a5a1b3Sopenharmony_ci *_r = PA_BLUETOOTH_PROFILE_HSP_HS; 218053a5a1b3Sopenharmony_ci else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) 218153a5a1b3Sopenharmony_ci *_r = PA_BLUETOOTH_PROFILE_HFP_HF; 218253a5a1b3Sopenharmony_ci else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG)) 218353a5a1b3Sopenharmony_ci *_r = PA_BLUETOOTH_PROFILE_HSP_AG; 218453a5a1b3Sopenharmony_ci else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG)) 218553a5a1b3Sopenharmony_ci *_r = PA_BLUETOOTH_PROFILE_HFP_AG; 218653a5a1b3Sopenharmony_ci else 218753a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 218853a5a1b3Sopenharmony_ci 218953a5a1b3Sopenharmony_ci return 0; 219053a5a1b3Sopenharmony_ci} 219153a5a1b3Sopenharmony_ci 219253a5a1b3Sopenharmony_ci/* Run from main thread */ 219353a5a1b3Sopenharmony_cistatic int add_card(struct userdata *u) { 219453a5a1b3Sopenharmony_ci const pa_bluetooth_device *d; 219553a5a1b3Sopenharmony_ci pa_card_new_data data; 219653a5a1b3Sopenharmony_ci char *alias; 219753a5a1b3Sopenharmony_ci pa_bluetooth_form_factor_t ff; 219853a5a1b3Sopenharmony_ci pa_card_profile *cp; 219953a5a1b3Sopenharmony_ci pa_bluetooth_profile_t *p; 220053a5a1b3Sopenharmony_ci const char *uuid; 220153a5a1b3Sopenharmony_ci void *state; 220253a5a1b3Sopenharmony_ci 220353a5a1b3Sopenharmony_ci pa_assert(u); 220453a5a1b3Sopenharmony_ci pa_assert(u->device); 220553a5a1b3Sopenharmony_ci 220653a5a1b3Sopenharmony_ci d = u->device; 220753a5a1b3Sopenharmony_ci 220853a5a1b3Sopenharmony_ci pa_card_new_data_init(&data); 220953a5a1b3Sopenharmony_ci data.driver = __FILE__; 221053a5a1b3Sopenharmony_ci data.module = u->module; 221153a5a1b3Sopenharmony_ci 221253a5a1b3Sopenharmony_ci alias = pa_utf8_filter(d->alias); 221353a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, alias); 221453a5a1b3Sopenharmony_ci pa_xfree(alias); 221553a5a1b3Sopenharmony_ci 221653a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, d->address); 221753a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez"); 221853a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); 221953a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "bluetooth"); 222053a5a1b3Sopenharmony_ci 222153a5a1b3Sopenharmony_ci if ((ff = form_factor_from_class(d->class_of_device)) != PA_BLUETOOTH_FORM_FACTOR_UNKNOWN) 222253a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, form_factor_to_string(ff)); 222353a5a1b3Sopenharmony_ci 222453a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, "bluez.path", d->path); 222553a5a1b3Sopenharmony_ci pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", d->class_of_device); 222653a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, "bluez.alias", d->alias); 222753a5a1b3Sopenharmony_ci data.name = pa_sprintf_malloc("bluez_card.%s", d->address); 222853a5a1b3Sopenharmony_ci data.namereg_fail = false; 222953a5a1b3Sopenharmony_ci 223053a5a1b3Sopenharmony_ci if (d->has_battery_level) { 223153a5a1b3Sopenharmony_ci // See device_battery_level_changed_cb 223253a5a1b3Sopenharmony_ci uint8_t level = d->battery_level; 223353a5a1b3Sopenharmony_ci pa_proplist_setf(data.proplist, "bluetooth.battery", "%d%%", level); 223453a5a1b3Sopenharmony_ci } 223553a5a1b3Sopenharmony_ci 223653a5a1b3Sopenharmony_ci create_card_ports(u, data.ports); 223753a5a1b3Sopenharmony_ci 223853a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(uuid, d->uuids, state) { 223953a5a1b3Sopenharmony_ci pa_bluetooth_profile_t profile; 224053a5a1b3Sopenharmony_ci 224153a5a1b3Sopenharmony_ci if (uuid_to_profile(uuid, &profile) < 0) 224253a5a1b3Sopenharmony_ci continue; 224353a5a1b3Sopenharmony_ci 224453a5a1b3Sopenharmony_ci pa_log_debug("Trying to create profile %s (%s) for device %s (%s)", 224553a5a1b3Sopenharmony_ci pa_bluetooth_profile_to_string(profile), uuid, d->alias, d->address); 224653a5a1b3Sopenharmony_ci 224753a5a1b3Sopenharmony_ci if (pa_hashmap_get(data.profiles, pa_bluetooth_profile_to_string(profile))) { 224853a5a1b3Sopenharmony_ci pa_log_debug("%s already exists", pa_bluetooth_profile_to_string(profile)); 224953a5a1b3Sopenharmony_ci continue; 225053a5a1b3Sopenharmony_ci } 225153a5a1b3Sopenharmony_ci 225253a5a1b3Sopenharmony_ci if (!pa_bluetooth_device_supports_profile(d, profile)) { 225353a5a1b3Sopenharmony_ci pa_log_debug("%s is not supported by the device or adapter", pa_bluetooth_profile_to_string(profile)); 225453a5a1b3Sopenharmony_ci continue; 225553a5a1b3Sopenharmony_ci } 225653a5a1b3Sopenharmony_ci 225753a5a1b3Sopenharmony_ci cp = create_card_profile(u, profile, data.ports); 225853a5a1b3Sopenharmony_ci pa_hashmap_put(data.profiles, cp->name, cp); 225953a5a1b3Sopenharmony_ci } 226053a5a1b3Sopenharmony_ci 226153a5a1b3Sopenharmony_ci pa_assert(!pa_hashmap_isempty(data.profiles)); 226253a5a1b3Sopenharmony_ci 226353a5a1b3Sopenharmony_ci cp = pa_card_profile_new("off", _("Off"), sizeof(pa_bluetooth_profile_t)); 226453a5a1b3Sopenharmony_ci cp->available = PA_AVAILABLE_YES; 226553a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(cp); 226653a5a1b3Sopenharmony_ci *p = PA_BLUETOOTH_PROFILE_OFF; 226753a5a1b3Sopenharmony_ci pa_hashmap_put(data.profiles, cp->name, cp); 226853a5a1b3Sopenharmony_ci 226953a5a1b3Sopenharmony_ci u->card = pa_card_new(u->core, &data); 227053a5a1b3Sopenharmony_ci pa_card_new_data_done(&data); 227153a5a1b3Sopenharmony_ci if (!u->card) { 227253a5a1b3Sopenharmony_ci pa_log("Failed to allocate card."); 227353a5a1b3Sopenharmony_ci return -1; 227453a5a1b3Sopenharmony_ci } 227553a5a1b3Sopenharmony_ci 227653a5a1b3Sopenharmony_ci u->card->userdata = u; 227753a5a1b3Sopenharmony_ci u->card->set_profile = set_profile_cb; 227853a5a1b3Sopenharmony_ci pa_card_choose_initial_profile(u->card); 227953a5a1b3Sopenharmony_ci pa_card_put(u->card); 228053a5a1b3Sopenharmony_ci 228153a5a1b3Sopenharmony_ci p = PA_CARD_PROFILE_DATA(u->card->active_profile); 228253a5a1b3Sopenharmony_ci u->profile = *p; 228353a5a1b3Sopenharmony_ci 228453a5a1b3Sopenharmony_ci return 0; 228553a5a1b3Sopenharmony_ci} 228653a5a1b3Sopenharmony_ci 228753a5a1b3Sopenharmony_ci/* Run from main thread */ 228853a5a1b3Sopenharmony_cistatic void handle_transport_state_change(struct userdata *u, struct pa_bluetooth_transport *t) { 228953a5a1b3Sopenharmony_ci bool acquire = false; 229053a5a1b3Sopenharmony_ci bool release = false; 229153a5a1b3Sopenharmony_ci pa_card_profile *cp; 229253a5a1b3Sopenharmony_ci pa_device_port *port; 229353a5a1b3Sopenharmony_ci pa_available_t oldavail; 229453a5a1b3Sopenharmony_ci 229553a5a1b3Sopenharmony_ci pa_assert(u); 229653a5a1b3Sopenharmony_ci pa_assert(t); 229753a5a1b3Sopenharmony_ci pa_assert_se(cp = pa_hashmap_get(u->card->profiles, pa_bluetooth_profile_to_string(t->profile))); 229853a5a1b3Sopenharmony_ci 229953a5a1b3Sopenharmony_ci oldavail = cp->available; 230053a5a1b3Sopenharmony_ci /* 230153a5a1b3Sopenharmony_ci * If codec switching is in progress, transport state change should not 230253a5a1b3Sopenharmony_ci * make profile unavailable. 230353a5a1b3Sopenharmony_ci */ 230453a5a1b3Sopenharmony_ci if (!t->device->codec_switching_in_progress) 230553a5a1b3Sopenharmony_ci pa_card_profile_set_available(cp, transport_state_to_availability(t->state)); 230653a5a1b3Sopenharmony_ci 230753a5a1b3Sopenharmony_ci /* Update port availability */ 230853a5a1b3Sopenharmony_ci pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name)); 230953a5a1b3Sopenharmony_ci pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_OUTPUT)); 231053a5a1b3Sopenharmony_ci pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name)); 231153a5a1b3Sopenharmony_ci pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_INPUT)); 231253a5a1b3Sopenharmony_ci 231353a5a1b3Sopenharmony_ci /* Acquire or release transport as needed */ 231453a5a1b3Sopenharmony_ci acquire = (t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile); 231553a5a1b3Sopenharmony_ci release = (oldavail != PA_AVAILABLE_NO && t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile); 231653a5a1b3Sopenharmony_ci 231753a5a1b3Sopenharmony_ci if (acquire && transport_acquire(u, true) >= 0) { 231853a5a1b3Sopenharmony_ci if (u->source) { 231953a5a1b3Sopenharmony_ci pa_log_debug("Resuming source %s because its transport state changed to playing", u->source->name); 232053a5a1b3Sopenharmony_ci 232153a5a1b3Sopenharmony_ci /* When the ofono backend resumes source or sink when in the audio gateway role, the 232253a5a1b3Sopenharmony_ci * state of source or sink may already be RUNNING before the transport is acquired via 232353a5a1b3Sopenharmony_ci * hf_audio_agent_new_connection(), so the pa_source_suspend() call will not lead to a 232453a5a1b3Sopenharmony_ci * state change message. In this case we explicitly need to signal the I/O thread to 232553a5a1b3Sopenharmony_ci * set up the stream. */ 232653a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_OPENED(u->source->state)) 232753a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_SETUP_STREAM, NULL, 0, NULL); 232853a5a1b3Sopenharmony_ci 232953a5a1b3Sopenharmony_ci /* We remove the IDLE suspend cause, because otherwise 233053a5a1b3Sopenharmony_ci * module-loopback doesn't uncork its streams. FIXME: Messing with 233153a5a1b3Sopenharmony_ci * the IDLE suspend cause here is wrong, the correct way to handle 233253a5a1b3Sopenharmony_ci * this would probably be to uncork the loopback streams not only 233353a5a1b3Sopenharmony_ci * when the other end is unsuspended, but also when the other end's 233453a5a1b3Sopenharmony_ci * suspend cause changes to IDLE only (currently there's no 233553a5a1b3Sopenharmony_ci * notification mechanism for suspend cause changes, though). */ 233653a5a1b3Sopenharmony_ci pa_source_suspend(u->source, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER); 233753a5a1b3Sopenharmony_ci } 233853a5a1b3Sopenharmony_ci 233953a5a1b3Sopenharmony_ci if (u->sink) { 234053a5a1b3Sopenharmony_ci pa_log_debug("Resuming sink %s because its transport state changed to playing", u->sink->name); 234153a5a1b3Sopenharmony_ci 234253a5a1b3Sopenharmony_ci /* Same comment as above */ 234353a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(u->sink->state)) 234453a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SETUP_STREAM, NULL, 0, NULL); 234553a5a1b3Sopenharmony_ci 234653a5a1b3Sopenharmony_ci /* FIXME: See the previous comment. */ 234753a5a1b3Sopenharmony_ci pa_sink_suspend(u->sink, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER); 234853a5a1b3Sopenharmony_ci } 234953a5a1b3Sopenharmony_ci } 235053a5a1b3Sopenharmony_ci 235153a5a1b3Sopenharmony_ci if (release && u->transport_acquired) { 235253a5a1b3Sopenharmony_ci /* FIXME: this release is racy, since the audio stream might have 235353a5a1b3Sopenharmony_ci * been set up again in the meantime (but not processed yet by PA). 235453a5a1b3Sopenharmony_ci * BlueZ should probably release the transport automatically, and in 235553a5a1b3Sopenharmony_ci * that case we would just mark the transport as released */ 235653a5a1b3Sopenharmony_ci 235753a5a1b3Sopenharmony_ci /* Remote side closed the stream so we consider it PA_SUSPEND_USER */ 235853a5a1b3Sopenharmony_ci if (u->source) { 235953a5a1b3Sopenharmony_ci pa_log_debug("Suspending source %s because the remote end closed the stream", u->source->name); 236053a5a1b3Sopenharmony_ci pa_source_suspend(u->source, true, PA_SUSPEND_USER); 236153a5a1b3Sopenharmony_ci } 236253a5a1b3Sopenharmony_ci 236353a5a1b3Sopenharmony_ci if (u->sink) { 236453a5a1b3Sopenharmony_ci pa_log_debug("Suspending sink %s because the remote end closed the stream", u->sink->name); 236553a5a1b3Sopenharmony_ci pa_sink_suspend(u->sink, true, PA_SUSPEND_USER); 236653a5a1b3Sopenharmony_ci } 236753a5a1b3Sopenharmony_ci } 236853a5a1b3Sopenharmony_ci} 236953a5a1b3Sopenharmony_ci 237053a5a1b3Sopenharmony_ci/* Run from main thread */ 237153a5a1b3Sopenharmony_cistatic pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) { 237253a5a1b3Sopenharmony_ci pa_assert(d); 237353a5a1b3Sopenharmony_ci pa_assert(u); 237453a5a1b3Sopenharmony_ci 237553a5a1b3Sopenharmony_ci if (d != u->device || pa_bluetooth_device_any_transport_connected(d) || d->codec_switching_in_progress) 237653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 237753a5a1b3Sopenharmony_ci 237853a5a1b3Sopenharmony_ci pa_log_debug("Unloading module for device %s", d->path); 237953a5a1b3Sopenharmony_ci pa_module_unload(u->module, true); 238053a5a1b3Sopenharmony_ci 238153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 238253a5a1b3Sopenharmony_ci} 238353a5a1b3Sopenharmony_ci 238453a5a1b3Sopenharmony_cistatic pa_hook_result_t device_battery_level_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) { 238553a5a1b3Sopenharmony_ci uint8_t level; 238653a5a1b3Sopenharmony_ci 238753a5a1b3Sopenharmony_ci pa_assert(d); 238853a5a1b3Sopenharmony_ci pa_assert(u); 238953a5a1b3Sopenharmony_ci 239053a5a1b3Sopenharmony_ci if (d != u->device) 239153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 239253a5a1b3Sopenharmony_ci 239353a5a1b3Sopenharmony_ci if (d->has_battery_level) { 239453a5a1b3Sopenharmony_ci level = d->battery_level; 239553a5a1b3Sopenharmony_ci pa_proplist_setf(u->card->proplist, "bluetooth.battery", "%d%%", level); 239653a5a1b3Sopenharmony_ci } else { 239753a5a1b3Sopenharmony_ci pa_proplist_unset(u->card->proplist, "bluetooth.battery"); 239853a5a1b3Sopenharmony_ci } 239953a5a1b3Sopenharmony_ci 240053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 240153a5a1b3Sopenharmony_ci} 240253a5a1b3Sopenharmony_ci 240353a5a1b3Sopenharmony_ci/* Run from main thread */ 240453a5a1b3Sopenharmony_cistatic pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { 240553a5a1b3Sopenharmony_ci pa_assert(t); 240653a5a1b3Sopenharmony_ci pa_assert(u); 240753a5a1b3Sopenharmony_ci 240853a5a1b3Sopenharmony_ci if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) 240953a5a1b3Sopenharmony_ci pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); 241053a5a1b3Sopenharmony_ci 241153a5a1b3Sopenharmony_ci if (t->device == u->device) 241253a5a1b3Sopenharmony_ci handle_transport_state_change(u, t); 241353a5a1b3Sopenharmony_ci 241453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 241553a5a1b3Sopenharmony_ci} 241653a5a1b3Sopenharmony_ci 241753a5a1b3Sopenharmony_cistatic pa_hook_result_t transport_sink_volume_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { 241853a5a1b3Sopenharmony_ci pa_volume_t volume; 241953a5a1b3Sopenharmony_ci pa_cvolume v; 242053a5a1b3Sopenharmony_ci 242153a5a1b3Sopenharmony_ci pa_assert(t); 242253a5a1b3Sopenharmony_ci pa_assert(u); 242353a5a1b3Sopenharmony_ci 242453a5a1b3Sopenharmony_ci if (t != u->transport) 242553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 242653a5a1b3Sopenharmony_ci 242753a5a1b3Sopenharmony_ci volume = t->sink_volume; 242853a5a1b3Sopenharmony_ci 242953a5a1b3Sopenharmony_ci if (!u->sink) { 243053a5a1b3Sopenharmony_ci pa_log_warn("Received peer transport volume change without connected sink"); 243153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 243253a5a1b3Sopenharmony_ci } 243353a5a1b3Sopenharmony_ci 243453a5a1b3Sopenharmony_ci sink_setup_volume_callback(u->sink); 243553a5a1b3Sopenharmony_ci 243653a5a1b3Sopenharmony_ci pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume); 243753a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_should_attenuate_volume(t->profile)) 243853a5a1b3Sopenharmony_ci pa_sink_set_volume(u->sink, &v, true, true); 243953a5a1b3Sopenharmony_ci else 244053a5a1b3Sopenharmony_ci pa_sink_volume_changed(u->sink, &v); 244153a5a1b3Sopenharmony_ci 244253a5a1b3Sopenharmony_ci return PA_HOOK_OK; 244353a5a1b3Sopenharmony_ci} 244453a5a1b3Sopenharmony_ci 244553a5a1b3Sopenharmony_cistatic pa_hook_result_t transport_source_volume_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { 244653a5a1b3Sopenharmony_ci pa_volume_t volume; 244753a5a1b3Sopenharmony_ci pa_cvolume v; 244853a5a1b3Sopenharmony_ci 244953a5a1b3Sopenharmony_ci pa_assert(t); 245053a5a1b3Sopenharmony_ci pa_assert(u); 245153a5a1b3Sopenharmony_ci 245253a5a1b3Sopenharmony_ci if (t != u->transport) 245353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 245453a5a1b3Sopenharmony_ci 245553a5a1b3Sopenharmony_ci volume = t->source_volume; 245653a5a1b3Sopenharmony_ci 245753a5a1b3Sopenharmony_ci if (!u->source) { 245853a5a1b3Sopenharmony_ci pa_log_warn("Received peer transport volume change without connected source"); 245953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 246053a5a1b3Sopenharmony_ci } 246153a5a1b3Sopenharmony_ci 246253a5a1b3Sopenharmony_ci source_setup_volume_callback(u->source); 246353a5a1b3Sopenharmony_ci 246453a5a1b3Sopenharmony_ci pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume); 246553a5a1b3Sopenharmony_ci 246653a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_should_attenuate_volume(t->profile)) 246753a5a1b3Sopenharmony_ci pa_source_set_volume(u->source, &v, true, true); 246853a5a1b3Sopenharmony_ci else 246953a5a1b3Sopenharmony_ci pa_source_volume_changed(u->source, &v); 247053a5a1b3Sopenharmony_ci 247153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 247253a5a1b3Sopenharmony_ci} 247353a5a1b3Sopenharmony_ci 247453a5a1b3Sopenharmony_cistatic char* make_message_handler_path(const char *name) { 247553a5a1b3Sopenharmony_ci return pa_sprintf_malloc("/card/%s/bluez", name); 247653a5a1b3Sopenharmony_ci} 247753a5a1b3Sopenharmony_ci 247853a5a1b3Sopenharmony_cistatic void switch_codec_cb_handler(bool success, pa_bluetooth_profile_t profile, void *userdata) 247953a5a1b3Sopenharmony_ci{ 248053a5a1b3Sopenharmony_ci struct userdata *u = (struct userdata *) userdata; 248153a5a1b3Sopenharmony_ci 248253a5a1b3Sopenharmony_ci if (!success) 248353a5a1b3Sopenharmony_ci goto off; 248453a5a1b3Sopenharmony_ci 248553a5a1b3Sopenharmony_ci u->profile = profile; 248653a5a1b3Sopenharmony_ci 248753a5a1b3Sopenharmony_ci if (init_profile(u) < 0) { 248853a5a1b3Sopenharmony_ci pa_log_info("Failed to initialise profile after codec switching"); 248953a5a1b3Sopenharmony_ci goto off; 249053a5a1b3Sopenharmony_ci } 249153a5a1b3Sopenharmony_ci 249253a5a1b3Sopenharmony_ci if (u->sink || u->source) 249353a5a1b3Sopenharmony_ci if (start_thread(u) < 0) { 249453a5a1b3Sopenharmony_ci pa_log_info("Failed to start thread after codec switching"); 249553a5a1b3Sopenharmony_ci goto off; 249653a5a1b3Sopenharmony_ci } 249753a5a1b3Sopenharmony_ci 249853a5a1b3Sopenharmony_ci pa_log_info("Codec successfully switched to %s with profile: %s", 249953a5a1b3Sopenharmony_ci u->bt_codec->name, pa_bluetooth_profile_to_string(u->profile)); 250053a5a1b3Sopenharmony_ci 250153a5a1b3Sopenharmony_ci return; 250253a5a1b3Sopenharmony_ci 250353a5a1b3Sopenharmony_cioff: 250453a5a1b3Sopenharmony_ci pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); 250553a5a1b3Sopenharmony_ci} 250653a5a1b3Sopenharmony_ci 250753a5a1b3Sopenharmony_cistatic char *list_codecs(struct userdata *u) { 250853a5a1b3Sopenharmony_ci const pa_a2dp_codec_capabilities *a2dp_capabilities; 250953a5a1b3Sopenharmony_ci const pa_a2dp_codec_id *key; 251053a5a1b3Sopenharmony_ci pa_hashmap *a2dp_endpoints; 251153a5a1b3Sopenharmony_ci pa_json_encoder *encoder; 251253a5a1b3Sopenharmony_ci unsigned int i; 251353a5a1b3Sopenharmony_ci bool is_a2dp_sink; 251453a5a1b3Sopenharmony_ci void *state; 251553a5a1b3Sopenharmony_ci 251653a5a1b3Sopenharmony_ci encoder = pa_json_encoder_new(); 251753a5a1b3Sopenharmony_ci 251853a5a1b3Sopenharmony_ci pa_json_encoder_begin_element_array(encoder); 251953a5a1b3Sopenharmony_ci 252053a5a1b3Sopenharmony_ci if (pa_bluetooth_profile_is_a2dp(u->profile)) { 252153a5a1b3Sopenharmony_ci is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK; 252253a5a1b3Sopenharmony_ci 252353a5a1b3Sopenharmony_ci a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints; 252453a5a1b3Sopenharmony_ci 252553a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, a2dp_endpoints, state) { 252653a5a1b3Sopenharmony_ci for (i = 0; i < pa_bluetooth_a2dp_endpoint_conf_count(); i++) { 252753a5a1b3Sopenharmony_ci const pa_a2dp_endpoint_conf *endpoint_conf; 252853a5a1b3Sopenharmony_ci 252953a5a1b3Sopenharmony_ci endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i); 253053a5a1b3Sopenharmony_ci 253153a5a1b3Sopenharmony_ci if (memcmp(key, &endpoint_conf->id, sizeof(pa_a2dp_codec_id)) == 0) { 253253a5a1b3Sopenharmony_ci if (endpoint_conf->can_be_supported(is_a2dp_sink)) { 253353a5a1b3Sopenharmony_ci pa_json_encoder_begin_element_object(encoder); 253453a5a1b3Sopenharmony_ci 253553a5a1b3Sopenharmony_ci pa_json_encoder_add_member_string(encoder, "name", endpoint_conf->bt_codec.name); 253653a5a1b3Sopenharmony_ci pa_json_encoder_add_member_string(encoder, "description", endpoint_conf->bt_codec.description); 253753a5a1b3Sopenharmony_ci 253853a5a1b3Sopenharmony_ci pa_json_encoder_end_object(encoder); 253953a5a1b3Sopenharmony_ci } 254053a5a1b3Sopenharmony_ci } 254153a5a1b3Sopenharmony_ci } 254253a5a1b3Sopenharmony_ci } 254353a5a1b3Sopenharmony_ci } else { 254453a5a1b3Sopenharmony_ci /* find out active codec selection from device profile */ 254553a5a1b3Sopenharmony_ci for (i = 0; i < pa_bluetooth_hf_codec_count(); i++) { 254653a5a1b3Sopenharmony_ci const pa_bt_codec *hf_codec; 254753a5a1b3Sopenharmony_ci 254853a5a1b3Sopenharmony_ci hf_codec = pa_bluetooth_hf_codec_iter(i); 254953a5a1b3Sopenharmony_ci 255053a5a1b3Sopenharmony_ci if (true) { 255153a5a1b3Sopenharmony_ci pa_json_encoder_begin_element_object(encoder); 255253a5a1b3Sopenharmony_ci 255353a5a1b3Sopenharmony_ci pa_json_encoder_add_member_string(encoder, "name", hf_codec->name); 255453a5a1b3Sopenharmony_ci pa_json_encoder_add_member_string(encoder, "description", hf_codec->description); 255553a5a1b3Sopenharmony_ci 255653a5a1b3Sopenharmony_ci pa_json_encoder_end_object(encoder); 255753a5a1b3Sopenharmony_ci } 255853a5a1b3Sopenharmony_ci } 255953a5a1b3Sopenharmony_ci } 256053a5a1b3Sopenharmony_ci 256153a5a1b3Sopenharmony_ci pa_json_encoder_end_array(encoder); 256253a5a1b3Sopenharmony_ci 256353a5a1b3Sopenharmony_ci return pa_json_encoder_to_string_free(encoder); 256453a5a1b3Sopenharmony_ci} 256553a5a1b3Sopenharmony_ci 256653a5a1b3Sopenharmony_cistatic int bluez5_device_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) { 256753a5a1b3Sopenharmony_ci char *message_handler_path; 256853a5a1b3Sopenharmony_ci pa_hashmap *capabilities_hashmap; 256953a5a1b3Sopenharmony_ci pa_bluetooth_profile_t profile; 257053a5a1b3Sopenharmony_ci const pa_a2dp_endpoint_conf *endpoint_conf; 257153a5a1b3Sopenharmony_ci const char *codec_name; 257253a5a1b3Sopenharmony_ci struct userdata *u = userdata; 257353a5a1b3Sopenharmony_ci bool is_a2dp_sink; 257453a5a1b3Sopenharmony_ci 257553a5a1b3Sopenharmony_ci pa_assert(u); 257653a5a1b3Sopenharmony_ci pa_assert(message); 257753a5a1b3Sopenharmony_ci pa_assert(response); 257853a5a1b3Sopenharmony_ci 257953a5a1b3Sopenharmony_ci message_handler_path = make_message_handler_path(u->card->name); 258053a5a1b3Sopenharmony_ci 258153a5a1b3Sopenharmony_ci if (!object_path || !pa_streq(object_path, message_handler_path)) { 258253a5a1b3Sopenharmony_ci pa_xfree(message_handler_path); 258353a5a1b3Sopenharmony_ci return -PA_ERR_NOENTITY; 258453a5a1b3Sopenharmony_ci } 258553a5a1b3Sopenharmony_ci 258653a5a1b3Sopenharmony_ci pa_xfree(message_handler_path); 258753a5a1b3Sopenharmony_ci 258853a5a1b3Sopenharmony_ci if (u->device->codec_switching_in_progress) { 258953a5a1b3Sopenharmony_ci pa_log_info("Codec switching operation already in progress"); 259053a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 259153a5a1b3Sopenharmony_ci } 259253a5a1b3Sopenharmony_ci 259353a5a1b3Sopenharmony_ci if (!u->device->adapter->application_registered) { 259453a5a1b3Sopenharmony_ci pa_log_info("Old BlueZ version was detected, only SBC codec supported."); 259553a5a1b3Sopenharmony_ci return -PA_ERR_NOTIMPLEMENTED; 259653a5a1b3Sopenharmony_ci } 259753a5a1b3Sopenharmony_ci 259853a5a1b3Sopenharmony_ci if (u->profile == PA_BLUETOOTH_PROFILE_OFF) { 259953a5a1b3Sopenharmony_ci pa_log_info("Bluetooth profile is off. Message cannot be handled."); 260053a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 260153a5a1b3Sopenharmony_ci } 260253a5a1b3Sopenharmony_ci 260353a5a1b3Sopenharmony_ci if (pa_streq(message, "switch-codec")) { 260453a5a1b3Sopenharmony_ci if (u->profile != PA_BLUETOOTH_PROFILE_A2DP_SINK && 260553a5a1b3Sopenharmony_ci u->profile != PA_BLUETOOTH_PROFILE_A2DP_SOURCE) { 260653a5a1b3Sopenharmony_ci pa_log_info("Switching codecs only allowed for A2DP sink or source"); 260753a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 260853a5a1b3Sopenharmony_ci } 260953a5a1b3Sopenharmony_ci 261053a5a1b3Sopenharmony_ci if (!parameters) { 261153a5a1b3Sopenharmony_ci pa_log_info("Codec switching operation requires codec name string parameter"); 261253a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 261353a5a1b3Sopenharmony_ci } 261453a5a1b3Sopenharmony_ci 261553a5a1b3Sopenharmony_ci if (pa_json_object_get_type(parameters) != PA_JSON_TYPE_STRING) { 261653a5a1b3Sopenharmony_ci pa_log_info("Codec name object parameter must be a string"); 261753a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 261853a5a1b3Sopenharmony_ci } 261953a5a1b3Sopenharmony_ci 262053a5a1b3Sopenharmony_ci codec_name = pa_json_object_get_string(parameters); 262153a5a1b3Sopenharmony_ci 262253a5a1b3Sopenharmony_ci if (u->bt_codec && pa_streq(codec_name, u->bt_codec->name)) { 262353a5a1b3Sopenharmony_ci pa_log_info("Requested codec is currently selected codec"); 262453a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 262553a5a1b3Sopenharmony_ci } 262653a5a1b3Sopenharmony_ci 262753a5a1b3Sopenharmony_ci endpoint_conf = pa_bluetooth_get_a2dp_endpoint_conf(codec_name); 262853a5a1b3Sopenharmony_ci if (endpoint_conf == NULL) { 262953a5a1b3Sopenharmony_ci pa_log_info("Invalid codec %s specified for switching", codec_name); 263053a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 263153a5a1b3Sopenharmony_ci } 263253a5a1b3Sopenharmony_ci 263353a5a1b3Sopenharmony_ci is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK; 263453a5a1b3Sopenharmony_ci 263553a5a1b3Sopenharmony_ci if (!endpoint_conf->can_be_supported(is_a2dp_sink)) { 263653a5a1b3Sopenharmony_ci pa_log_info("Codec not found on system"); 263753a5a1b3Sopenharmony_ci return -PA_ERR_NOTSUPPORTED; 263853a5a1b3Sopenharmony_ci } 263953a5a1b3Sopenharmony_ci 264053a5a1b3Sopenharmony_ci /* 264153a5a1b3Sopenharmony_ci * We need to check if we have valid sink or source endpoints which 264253a5a1b3Sopenharmony_ci * were registered during the negotiation process. If we do, then we 264353a5a1b3Sopenharmony_ci * check if the specified codec is present among the codecs supported 264453a5a1b3Sopenharmony_ci * by the remote endpoint. 264553a5a1b3Sopenharmony_ci */ 264653a5a1b3Sopenharmony_ci if (pa_hashmap_isempty(is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints)) { 264753a5a1b3Sopenharmony_ci pa_log_info("No device endpoints found. Codec switching not allowed."); 264853a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 264953a5a1b3Sopenharmony_ci } 265053a5a1b3Sopenharmony_ci 265153a5a1b3Sopenharmony_ci capabilities_hashmap = pa_hashmap_get(is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints, &endpoint_conf->id); 265253a5a1b3Sopenharmony_ci if (!capabilities_hashmap) { 265353a5a1b3Sopenharmony_ci pa_log_info("No remote endpoint found for %s codec. Codec not supported by remote endpoint.", 265453a5a1b3Sopenharmony_ci endpoint_conf->bt_codec.name); 265553a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 265653a5a1b3Sopenharmony_ci } 265753a5a1b3Sopenharmony_ci 265853a5a1b3Sopenharmony_ci pa_log_info("Initiating codec switching process to %s", endpoint_conf->bt_codec.name); 265953a5a1b3Sopenharmony_ci 266053a5a1b3Sopenharmony_ci /* 266153a5a1b3Sopenharmony_ci * The current profile needs to be saved before we stop the thread and 266253a5a1b3Sopenharmony_ci * initiate the switch. u->profile will be changed in other places 266353a5a1b3Sopenharmony_ci * depending on the state of transport and port availability. 266453a5a1b3Sopenharmony_ci */ 266553a5a1b3Sopenharmony_ci profile = u->profile; 266653a5a1b3Sopenharmony_ci 266753a5a1b3Sopenharmony_ci stop_thread(u); 266853a5a1b3Sopenharmony_ci 266953a5a1b3Sopenharmony_ci if (!pa_bluetooth_device_switch_codec(u->device, profile, capabilities_hashmap, endpoint_conf, switch_codec_cb_handler, userdata) 267053a5a1b3Sopenharmony_ci && !u->device->codec_switching_in_progress) 267153a5a1b3Sopenharmony_ci goto profile_off; 267253a5a1b3Sopenharmony_ci 267353a5a1b3Sopenharmony_ci return PA_OK; 267453a5a1b3Sopenharmony_ci } else if (pa_streq(message, "list-codecs")) { 267553a5a1b3Sopenharmony_ci *response = list_codecs(u); 267653a5a1b3Sopenharmony_ci return PA_OK; 267753a5a1b3Sopenharmony_ci } else if (pa_streq(message, "get-codec")) { 267853a5a1b3Sopenharmony_ci pa_json_encoder *encoder; 267953a5a1b3Sopenharmony_ci encoder = pa_json_encoder_new(); 268053a5a1b3Sopenharmony_ci 268153a5a1b3Sopenharmony_ci if (u->bt_codec) 268253a5a1b3Sopenharmony_ci pa_json_encoder_add_element_string(encoder, u->bt_codec->name); 268353a5a1b3Sopenharmony_ci else 268453a5a1b3Sopenharmony_ci pa_json_encoder_add_element_null(encoder); 268553a5a1b3Sopenharmony_ci 268653a5a1b3Sopenharmony_ci *response = pa_json_encoder_to_string_free(encoder); 268753a5a1b3Sopenharmony_ci 268853a5a1b3Sopenharmony_ci return PA_OK; 268953a5a1b3Sopenharmony_ci } 269053a5a1b3Sopenharmony_ci 269153a5a1b3Sopenharmony_ci 269253a5a1b3Sopenharmony_ci return -PA_ERR_NOTIMPLEMENTED; 269353a5a1b3Sopenharmony_ci 269453a5a1b3Sopenharmony_ciprofile_off: 269553a5a1b3Sopenharmony_ci pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); 269653a5a1b3Sopenharmony_ci 269753a5a1b3Sopenharmony_ci return -PA_ERR_IO; 269853a5a1b3Sopenharmony_ci} 269953a5a1b3Sopenharmony_ci 270053a5a1b3Sopenharmony_ci/* Run from main thread context */ 270153a5a1b3Sopenharmony_cistatic int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { 270253a5a1b3Sopenharmony_ci struct bluetooth_msg *m = BLUETOOTH_MSG(obj); 270353a5a1b3Sopenharmony_ci struct userdata *u = m->card->userdata; 270453a5a1b3Sopenharmony_ci 270553a5a1b3Sopenharmony_ci switch (code) { 270653a5a1b3Sopenharmony_ci case BLUETOOTH_MESSAGE_IO_THREAD_FAILED: 270753a5a1b3Sopenharmony_ci if (m->card->module->unload_requested) 270853a5a1b3Sopenharmony_ci break; 270953a5a1b3Sopenharmony_ci 271053a5a1b3Sopenharmony_ci pa_log_debug("Switching the profile to off due to IO thread failure."); 271153a5a1b3Sopenharmony_ci pa_assert_se(pa_card_set_profile(m->card, pa_hashmap_get(m->card->profiles, "off"), false) >= 0); 271253a5a1b3Sopenharmony_ci break; 271353a5a1b3Sopenharmony_ci case BLUETOOTH_MESSAGE_STREAM_FD_HUP: 271453a5a1b3Sopenharmony_ci if (u->transport->state > PA_BLUETOOTH_TRANSPORT_STATE_IDLE) 271553a5a1b3Sopenharmony_ci pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); 271653a5a1b3Sopenharmony_ci break; 271753a5a1b3Sopenharmony_ci case BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING: 271853a5a1b3Sopenharmony_ci /* transport_acquired needs to be checked here, because a message could have been 271953a5a1b3Sopenharmony_ci * pending when the profile was switched. If the new transport has been acquired 272053a5a1b3Sopenharmony_ci * correctly, the call below will have no effect because the transport state is 272153a5a1b3Sopenharmony_ci * already PLAYING. If transport_acquire() failed for the new profile, the transport 272253a5a1b3Sopenharmony_ci * state should not be changed. If the transport has been released for other reasons 272353a5a1b3Sopenharmony_ci * (I/O thread shutdown), transport_acquired will also be false. */ 272453a5a1b3Sopenharmony_ci if (u->transport_acquired) 272553a5a1b3Sopenharmony_ci pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); 272653a5a1b3Sopenharmony_ci break; 272753a5a1b3Sopenharmony_ci } 272853a5a1b3Sopenharmony_ci 272953a5a1b3Sopenharmony_ci return 0; 273053a5a1b3Sopenharmony_ci} 273153a5a1b3Sopenharmony_ci 273253a5a1b3Sopenharmony_ciint pa__init(pa_module* m) { 273353a5a1b3Sopenharmony_ci struct userdata *u; 273453a5a1b3Sopenharmony_ci const char *path; 273553a5a1b3Sopenharmony_ci pa_modargs *ma; 273653a5a1b3Sopenharmony_ci bool autodetect_mtu, avrcp_absolute_volume; 273753a5a1b3Sopenharmony_ci char *message_handler_path; 273853a5a1b3Sopenharmony_ci uint32_t output_rate_refresh_interval_ms; 273953a5a1b3Sopenharmony_ci 274053a5a1b3Sopenharmony_ci pa_assert(m); 274153a5a1b3Sopenharmony_ci 274253a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 274353a5a1b3Sopenharmony_ci u->module = m; 274453a5a1b3Sopenharmony_ci u->core = m->core; 274553a5a1b3Sopenharmony_ci u->message_handler_registered = false; 274653a5a1b3Sopenharmony_ci 274753a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 274853a5a1b3Sopenharmony_ci pa_log_error("Failed to parse module arguments"); 274953a5a1b3Sopenharmony_ci goto fail_free_modargs; 275053a5a1b3Sopenharmony_ci } 275153a5a1b3Sopenharmony_ci 275253a5a1b3Sopenharmony_ci if (!(path = pa_modargs_get_value(ma, "path", NULL))) { 275353a5a1b3Sopenharmony_ci pa_log_error("Failed to get device path from module arguments"); 275453a5a1b3Sopenharmony_ci goto fail_free_modargs; 275553a5a1b3Sopenharmony_ci } 275653a5a1b3Sopenharmony_ci 275753a5a1b3Sopenharmony_ci if ((u->discovery = pa_shared_get(u->core, "bluetooth-discovery"))) 275853a5a1b3Sopenharmony_ci pa_bluetooth_discovery_ref(u->discovery); 275953a5a1b3Sopenharmony_ci else { 276053a5a1b3Sopenharmony_ci pa_log_error("module-bluez5-discover doesn't seem to be loaded, refusing to load module-bluez5-device"); 276153a5a1b3Sopenharmony_ci goto fail_free_modargs; 276253a5a1b3Sopenharmony_ci } 276353a5a1b3Sopenharmony_ci 276453a5a1b3Sopenharmony_ci if (!(u->device = pa_bluetooth_discovery_get_device_by_path(u->discovery, path))) { 276553a5a1b3Sopenharmony_ci pa_log_error("%s is unknown", path); 276653a5a1b3Sopenharmony_ci goto fail_free_modargs; 276753a5a1b3Sopenharmony_ci } 276853a5a1b3Sopenharmony_ci 276953a5a1b3Sopenharmony_ci autodetect_mtu = false; 277053a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) { 277153a5a1b3Sopenharmony_ci pa_log("Invalid boolean value for autodetect_mtu parameter"); 277253a5a1b3Sopenharmony_ci goto fail_free_modargs; 277353a5a1b3Sopenharmony_ci } 277453a5a1b3Sopenharmony_ci 277553a5a1b3Sopenharmony_ci u->device->autodetect_mtu = autodetect_mtu; 277653a5a1b3Sopenharmony_ci 277753a5a1b3Sopenharmony_ci output_rate_refresh_interval_ms = DEFAULT_OUTPUT_RATE_REFRESH_INTERVAL_MS; 277853a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, "output_rate_refresh_interval_ms", &output_rate_refresh_interval_ms) < 0) { 277953a5a1b3Sopenharmony_ci pa_log("Invalid value for output_rate_refresh_interval parameter."); 278053a5a1b3Sopenharmony_ci goto fail_free_modargs; 278153a5a1b3Sopenharmony_ci } 278253a5a1b3Sopenharmony_ci 278353a5a1b3Sopenharmony_ci u->device->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms; 278453a5a1b3Sopenharmony_ci 278553a5a1b3Sopenharmony_ci avrcp_absolute_volume = true; 278653a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "avrcp_absolute_volume", &avrcp_absolute_volume) < 0) { 278753a5a1b3Sopenharmony_ci pa_log("Invalid boolean value for avrcp_absolute_volume parameter"); 278853a5a1b3Sopenharmony_ci goto fail_free_modargs; 278953a5a1b3Sopenharmony_ci } 279053a5a1b3Sopenharmony_ci 279153a5a1b3Sopenharmony_ci u->device->avrcp_absolute_volume = avrcp_absolute_volume; 279253a5a1b3Sopenharmony_ci 279353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 279453a5a1b3Sopenharmony_ci 279553a5a1b3Sopenharmony_ci u->device_connection_changed_slot = 279653a5a1b3Sopenharmony_ci pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), 279753a5a1b3Sopenharmony_ci PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u); 279853a5a1b3Sopenharmony_ci 279953a5a1b3Sopenharmony_ci u->device_battery_level_changed_slot = 280053a5a1b3Sopenharmony_ci pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED), 280153a5a1b3Sopenharmony_ci PA_HOOK_NORMAL, (pa_hook_cb_t) device_battery_level_changed_cb, u); 280253a5a1b3Sopenharmony_ci 280353a5a1b3Sopenharmony_ci u->transport_state_changed_slot = 280453a5a1b3Sopenharmony_ci pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED), 280553a5a1b3Sopenharmony_ci PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u); 280653a5a1b3Sopenharmony_ci 280753a5a1b3Sopenharmony_ci u->transport_sink_volume_changed_slot = 280853a5a1b3Sopenharmony_ci pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_sink_volume_changed_cb, u); 280953a5a1b3Sopenharmony_ci 281053a5a1b3Sopenharmony_ci u->transport_source_volume_changed_slot = 281153a5a1b3Sopenharmony_ci pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_source_volume_changed_cb, u); 281253a5a1b3Sopenharmony_ci 281353a5a1b3Sopenharmony_ci if (add_card(u) < 0) 281453a5a1b3Sopenharmony_ci goto fail; 281553a5a1b3Sopenharmony_ci 281653a5a1b3Sopenharmony_ci if (!(u->msg = pa_msgobject_new(bluetooth_msg))) 281753a5a1b3Sopenharmony_ci goto fail; 281853a5a1b3Sopenharmony_ci 281953a5a1b3Sopenharmony_ci u->msg->parent.process_msg = device_process_msg; 282053a5a1b3Sopenharmony_ci u->msg->card = u->card; 282153a5a1b3Sopenharmony_ci u->stream_setup_done = false; 282253a5a1b3Sopenharmony_ci 282353a5a1b3Sopenharmony_ci if (u->profile != PA_BLUETOOTH_PROFILE_OFF) 282453a5a1b3Sopenharmony_ci if (init_profile(u) < 0) 282553a5a1b3Sopenharmony_ci goto off; 282653a5a1b3Sopenharmony_ci 282753a5a1b3Sopenharmony_ci if (u->sink || u->source) 282853a5a1b3Sopenharmony_ci if (start_thread(u) < 0) 282953a5a1b3Sopenharmony_ci goto off; 283053a5a1b3Sopenharmony_ci 283153a5a1b3Sopenharmony_ci message_handler_path = make_message_handler_path(u->card->name); 283253a5a1b3Sopenharmony_ci pa_message_handler_register(m->core, message_handler_path, "Bluez5 device message handler", 283353a5a1b3Sopenharmony_ci bluez5_device_message_handler, (void *) u); 283453a5a1b3Sopenharmony_ci pa_log_info("Bluez5 device message handler registered at path: %s", message_handler_path); 283553a5a1b3Sopenharmony_ci pa_xfree(message_handler_path); 283653a5a1b3Sopenharmony_ci u->message_handler_registered = true; 283753a5a1b3Sopenharmony_ci 283853a5a1b3Sopenharmony_ci return 0; 283953a5a1b3Sopenharmony_ci 284053a5a1b3Sopenharmony_cioff: 284153a5a1b3Sopenharmony_ci stop_thread(u); 284253a5a1b3Sopenharmony_ci 284353a5a1b3Sopenharmony_ci pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); 284453a5a1b3Sopenharmony_ci 284553a5a1b3Sopenharmony_ci return 0; 284653a5a1b3Sopenharmony_ci 284753a5a1b3Sopenharmony_cifail_free_modargs: 284853a5a1b3Sopenharmony_ci 284953a5a1b3Sopenharmony_ci if (ma) 285053a5a1b3Sopenharmony_ci pa_modargs_free(ma); 285153a5a1b3Sopenharmony_ci 285253a5a1b3Sopenharmony_cifail: 285353a5a1b3Sopenharmony_ci 285453a5a1b3Sopenharmony_ci pa__done(m); 285553a5a1b3Sopenharmony_ci 285653a5a1b3Sopenharmony_ci return -1; 285753a5a1b3Sopenharmony_ci} 285853a5a1b3Sopenharmony_ci 285953a5a1b3Sopenharmony_civoid pa__done(pa_module *m) { 286053a5a1b3Sopenharmony_ci char *message_handler_path; 286153a5a1b3Sopenharmony_ci struct userdata *u; 286253a5a1b3Sopenharmony_ci 286353a5a1b3Sopenharmony_ci pa_assert(m); 286453a5a1b3Sopenharmony_ci 286553a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 286653a5a1b3Sopenharmony_ci return; 286753a5a1b3Sopenharmony_ci 286853a5a1b3Sopenharmony_ci if (u->message_handler_registered) { 286953a5a1b3Sopenharmony_ci message_handler_path = make_message_handler_path(u->card->name); 287053a5a1b3Sopenharmony_ci pa_message_handler_unregister(m->core, message_handler_path); 287153a5a1b3Sopenharmony_ci pa_xfree(message_handler_path); 287253a5a1b3Sopenharmony_ci } 287353a5a1b3Sopenharmony_ci 287453a5a1b3Sopenharmony_ci stop_thread(u); 287553a5a1b3Sopenharmony_ci 287653a5a1b3Sopenharmony_ci if (u->device_connection_changed_slot) 287753a5a1b3Sopenharmony_ci pa_hook_slot_free(u->device_connection_changed_slot); 287853a5a1b3Sopenharmony_ci 287953a5a1b3Sopenharmony_ci if (u->device_battery_level_changed_slot) 288053a5a1b3Sopenharmony_ci pa_hook_slot_free(u->device_battery_level_changed_slot); 288153a5a1b3Sopenharmony_ci 288253a5a1b3Sopenharmony_ci if (u->transport_state_changed_slot) 288353a5a1b3Sopenharmony_ci pa_hook_slot_free(u->transport_state_changed_slot); 288453a5a1b3Sopenharmony_ci 288553a5a1b3Sopenharmony_ci if (u->transport_sink_volume_changed_slot) 288653a5a1b3Sopenharmony_ci pa_hook_slot_free(u->transport_sink_volume_changed_slot); 288753a5a1b3Sopenharmony_ci 288853a5a1b3Sopenharmony_ci if (u->transport_source_volume_changed_slot) 288953a5a1b3Sopenharmony_ci pa_hook_slot_free(u->transport_source_volume_changed_slot); 289053a5a1b3Sopenharmony_ci 289153a5a1b3Sopenharmony_ci if (u->encoder_buffer) 289253a5a1b3Sopenharmony_ci pa_xfree(u->encoder_buffer); 289353a5a1b3Sopenharmony_ci 289453a5a1b3Sopenharmony_ci if (u->decoder_buffer) 289553a5a1b3Sopenharmony_ci pa_xfree(u->decoder_buffer); 289653a5a1b3Sopenharmony_ci 289753a5a1b3Sopenharmony_ci if (u->msg) 289853a5a1b3Sopenharmony_ci pa_xfree(u->msg); 289953a5a1b3Sopenharmony_ci 290053a5a1b3Sopenharmony_ci if (u->card) 290153a5a1b3Sopenharmony_ci pa_card_free(u->card); 290253a5a1b3Sopenharmony_ci 290353a5a1b3Sopenharmony_ci if (u->discovery) 290453a5a1b3Sopenharmony_ci pa_bluetooth_discovery_unref(u->discovery); 290553a5a1b3Sopenharmony_ci 290653a5a1b3Sopenharmony_ci pa_xfree(u->output_port_name); 290753a5a1b3Sopenharmony_ci pa_xfree(u->input_port_name); 290853a5a1b3Sopenharmony_ci 290953a5a1b3Sopenharmony_ci pa_xfree(u); 291053a5a1b3Sopenharmony_ci} 291153a5a1b3Sopenharmony_ci 291253a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) { 291353a5a1b3Sopenharmony_ci struct userdata *u; 291453a5a1b3Sopenharmony_ci 291553a5a1b3Sopenharmony_ci pa_assert(m); 291653a5a1b3Sopenharmony_ci pa_assert_se(u = m->userdata); 291753a5a1b3Sopenharmony_ci 291853a5a1b3Sopenharmony_ci return (u->sink ? pa_sink_linked_by(u->sink) : 0) + (u->source ? pa_source_linked_by(u->source) : 0); 291953a5a1b3Sopenharmony_ci} 2920