1/* Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6#include <errno.h> 7#include <stdint.h> 8#include <syslog.h> 9 10#include "a2dp-codecs.h" 11#include "cras_a2dp_endpoint.h" 12#include "cras_a2dp_iodev.h" 13#include "cras_iodev.h" 14#include "cras_bt_constants.h" 15#include "cras_bt_endpoint.h" 16#include "cras_system_state.h" 17#include "cras_util.h" 18 19#define A2DP_SOURCE_ENDPOINT_PATH "/org/chromium/Cras/Bluetooth/A2DPSource" 20#define A2DP_SINK_ENDPOINT_PATH "/org/chromium/Cras/Bluetooth/A2DPSink" 21 22 23/* Pointers for the only connected a2dp device. */ 24static struct a2dp { 25 struct cras_iodev *iodev; 26 struct cras_bt_device *device; 27} connected_a2dp; 28 29static int cras_a2dp_get_capabilities(struct cras_bt_endpoint *endpoint, 30 void *capabilities, int *len) 31{ 32 a2dp_sbc_t *sbc_caps = capabilities; 33 34 if (*len < sizeof(*sbc_caps)) 35 return -ENOSPC; 36 37 *len = sizeof(*sbc_caps); 38 39 /* Return all capabilities. */ 40 sbc_caps->channel_mode = SBC_CHANNEL_MODE_MONO | 41 SBC_CHANNEL_MODE_DUAL_CHANNEL | 42 SBC_CHANNEL_MODE_STEREO | 43 SBC_CHANNEL_MODE_JOINT_STEREO; 44 sbc_caps->frequency = SBC_SAMPLING_FREQ_16000 | 45 SBC_SAMPLING_FREQ_32000 | 46 SBC_SAMPLING_FREQ_44100 | 47 SBC_SAMPLING_FREQ_48000; 48 sbc_caps->allocation_method = SBC_ALLOCATION_SNR | 49 SBC_ALLOCATION_LOUDNESS; 50 sbc_caps->subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8; 51 sbc_caps->block_length = SBC_BLOCK_LENGTH_4 | 52 SBC_BLOCK_LENGTH_8 | 53 SBC_BLOCK_LENGTH_12 | 54 SBC_BLOCK_LENGTH_16; 55 sbc_caps->min_bitpool = MIN_BITPOOL; 56 sbc_caps->max_bitpool = MAX_BITPOOL; 57 58 return 0; 59} 60 61static int cras_a2dp_select_configuration(struct cras_bt_endpoint *endpoint, 62 void *capabilities, int len, 63 void *configuration) 64{ 65 a2dp_sbc_t *sbc_caps = capabilities; 66 a2dp_sbc_t *sbc_config = configuration; 67 68 /* Pick the highest configuration. */ 69 if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) { 70 sbc_config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; 71 } else if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_STEREO) { 72 sbc_config->channel_mode = SBC_CHANNEL_MODE_STEREO; 73 } else if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) { 74 sbc_config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; 75 } else if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_MONO) { 76 sbc_config->channel_mode = SBC_CHANNEL_MODE_MONO; 77 } else { 78 syslog(LOG_WARNING, "No supported channel modes."); 79 return -ENOSYS; 80 } 81 82 if (sbc_caps->frequency & SBC_SAMPLING_FREQ_48000) { 83 sbc_config->frequency = SBC_SAMPLING_FREQ_48000; 84 } else if (sbc_caps->frequency & SBC_SAMPLING_FREQ_44100) { 85 sbc_config->frequency = SBC_SAMPLING_FREQ_44100; 86 } else if (sbc_caps->frequency & SBC_SAMPLING_FREQ_32000) { 87 sbc_config->frequency = SBC_SAMPLING_FREQ_32000; 88 } else if (sbc_caps->frequency & SBC_SAMPLING_FREQ_16000) { 89 sbc_config->frequency = SBC_SAMPLING_FREQ_16000; 90 } else { 91 syslog(LOG_WARNING, "No supported sampling frequencies."); 92 return -ENOSYS; 93 } 94 95 if (sbc_caps->allocation_method & SBC_ALLOCATION_LOUDNESS) { 96 sbc_config->allocation_method = SBC_ALLOCATION_LOUDNESS; 97 } else if (sbc_caps->allocation_method & SBC_ALLOCATION_SNR) { 98 sbc_config->allocation_method = SBC_ALLOCATION_SNR; 99 } else { 100 syslog(LOG_WARNING, "No supported allocation method."); 101 return -ENOSYS; 102 } 103 104 if (sbc_caps->subbands & SBC_SUBBANDS_8) { 105 sbc_config->subbands = SBC_SUBBANDS_8; 106 } else if (sbc_caps->subbands & SBC_SUBBANDS_4) { 107 sbc_config->subbands = SBC_SUBBANDS_4; 108 } else { 109 syslog(LOG_WARNING, "No supported subbands."); 110 return -ENOSYS; 111 } 112 113 if (sbc_caps->block_length & SBC_BLOCK_LENGTH_16) { 114 sbc_config->block_length = SBC_BLOCK_LENGTH_16; 115 } else if (sbc_caps->block_length & SBC_BLOCK_LENGTH_12) { 116 sbc_config->block_length = SBC_BLOCK_LENGTH_12; 117 } else if (sbc_caps->block_length & SBC_BLOCK_LENGTH_8) { 118 sbc_config->block_length = SBC_BLOCK_LENGTH_8; 119 } else if (sbc_caps->block_length & SBC_BLOCK_LENGTH_4) { 120 sbc_config->block_length = SBC_BLOCK_LENGTH_4; 121 } else { 122 syslog(LOG_WARNING, "No supported block length."); 123 return -ENOSYS; 124 } 125 126 sbc_config->min_bitpool = (sbc_caps->min_bitpool > MIN_BITPOOL 127 ? sbc_caps->min_bitpool : MIN_BITPOOL); 128 sbc_config->max_bitpool = (sbc_caps->max_bitpool < MAX_BITPOOL 129 ? sbc_caps->max_bitpool : MAX_BITPOOL); 130 131 return 0; 132} 133 134static void cras_a2dp_set_configuration(struct cras_bt_endpoint *endpoint, 135 struct cras_bt_transport *transport) 136{ 137 struct cras_bt_device *device; 138 139 device = cras_bt_transport_device(transport); 140 cras_bt_device_a2dp_configured(device); 141} 142 143static void cras_a2dp_suspend(struct cras_bt_endpoint *endpoint, 144 struct cras_bt_transport *transport) 145{ 146 struct cras_bt_device *device = cras_bt_transport_device(transport); 147 cras_a2dp_suspend_connected_device(device); 148} 149 150static void a2dp_transport_state_changed(struct cras_bt_endpoint *endpoint, 151 struct cras_bt_transport *transport) 152{ 153 if (connected_a2dp.iodev && transport) { 154 /* When pending message is received in bluez, try to aquire 155 * the transport. */ 156 if (cras_bt_transport_fd(transport) != -1 && 157 cras_bt_transport_state(transport) == 158 CRAS_BT_TRANSPORT_STATE_PENDING) 159 cras_bt_transport_try_acquire(transport); 160 } 161} 162 163static struct cras_bt_endpoint cras_a2dp_endpoint = { 164 /* BlueZ connects the device A2DP Sink to our A2DP Source endpoint, 165 * and the device A2DP Source to our A2DP Sink. It's best if you don't 166 * think about it too hard. 167 */ 168 .object_path = A2DP_SOURCE_ENDPOINT_PATH, 169 .uuid = A2DP_SOURCE_UUID, 170 .codec = A2DP_CODEC_SBC, 171 172 .get_capabilities = cras_a2dp_get_capabilities, 173 .select_configuration = cras_a2dp_select_configuration, 174 .set_configuration = cras_a2dp_set_configuration, 175 .suspend = cras_a2dp_suspend, 176 .transport_state_changed = a2dp_transport_state_changed 177}; 178 179int cras_a2dp_endpoint_create(DBusConnection *conn) 180{ 181 return cras_bt_endpoint_add(conn, &cras_a2dp_endpoint); 182} 183 184void cras_a2dp_start(struct cras_bt_device *device) 185{ 186 struct cras_bt_transport *transport = cras_a2dp_endpoint.transport; 187 188 if (!transport || device != cras_bt_transport_device(transport)) { 189 syslog(LOG_ERR, "Device and active transport not match."); 190 return; 191 } 192 193 if (connected_a2dp.iodev) { 194 syslog(LOG_WARNING, 195 "Replacing existing endpoint configuration"); 196 a2dp_iodev_destroy(connected_a2dp.iodev); 197 } 198 199 connected_a2dp.iodev = a2dp_iodev_create(transport); 200 connected_a2dp.device = cras_bt_transport_device(transport); 201 202 if (!connected_a2dp.iodev) 203 syslog(LOG_WARNING, "Failed to create a2dp iodev"); 204} 205 206struct cras_bt_device *cras_a2dp_connected_device() 207{ 208 return connected_a2dp.device; 209} 210 211void cras_a2dp_suspend_connected_device(struct cras_bt_device *device) 212{ 213 if (connected_a2dp.device != device) 214 return; 215 216 if (connected_a2dp.iodev) { 217 syslog(LOG_INFO, "Destroying iodev for A2DP device"); 218 a2dp_iodev_destroy(connected_a2dp.iodev); 219 connected_a2dp.iodev = NULL; 220 connected_a2dp.device = NULL; 221 } 222} 223