| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761 |
- // SPDX-License-Identifier: LGPL-2.1-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <glib.h>
- #include "btio/btio.h"
- #include "lib/bluetooth.h"
- #include "lib/sdp.h"
- #include "lib/sdp_lib.h"
- #include "profiles/audio/a2dp-codecs.h"
- #include "src/shared/queue.h"
- #include "src/log.h"
- #include "hal-msg.h"
- #include "ipc-common.h"
- #include "ipc.h"
- #include "a2dp.h"
- #include "utils.h"
- #include "bluetooth.h"
- #include "avdtp.h"
- #include "avrcp.h"
- #include "audio-msg.h"
- #define SVC_HINT_CAPTURING 0x08
- #define IDLE_TIMEOUT 1
- #define AUDIO_RETRY_TIMEOUT 2
- static GIOChannel *server = NULL;
- static GSList *devices = NULL;
- static GSList *endpoints = NULL;
- static GSList *setups = NULL;
- static bdaddr_t adapter_addr;
- static uint32_t record_id = 0;
- static guint audio_retry_id = 0;
- static bool audio_retrying = false;
- static struct ipc *hal_ipc = NULL;
- static struct ipc *audio_ipc = NULL;
- static struct queue *lseps = NULL;
- struct a2dp_preset {
- void *data;
- int8_t len;
- };
- struct a2dp_endpoint {
- uint8_t id;
- uint8_t codec;
- struct avdtp_local_sep *sep;
- struct a2dp_preset *caps;
- GSList *presets;
- };
- struct a2dp_device {
- bdaddr_t dst;
- uint8_t state;
- GIOChannel *io;
- struct avdtp *session;
- guint idle_id;
- };
- struct a2dp_setup {
- struct a2dp_device *dev;
- struct a2dp_endpoint *endpoint;
- struct a2dp_preset *preset;
- struct avdtp_stream *stream;
- uint8_t state;
- };
- static int device_cmp(gconstpointer s, gconstpointer user_data)
- {
- const struct a2dp_device *dev = s;
- const bdaddr_t *dst = user_data;
- return bacmp(&dev->dst, dst);
- }
- static void preset_free(void *data)
- {
- struct a2dp_preset *preset = data;
- g_free(preset->data);
- g_free(preset);
- }
- static void unregister_endpoint(void *data)
- {
- struct a2dp_endpoint *endpoint = data;
- if (endpoint->sep)
- avdtp_unregister_sep(lseps, endpoint->sep);
- if (endpoint->caps)
- preset_free(endpoint->caps);
- g_slist_free_full(endpoint->presets, preset_free);
- g_free(endpoint);
- }
- static void setup_free(void *data)
- {
- struct a2dp_setup *setup = data;
- if (!g_slist_find(setup->endpoint->presets, setup->preset))
- preset_free(setup->preset);
- g_free(setup);
- }
- static void setup_remove(struct a2dp_setup *setup)
- {
- setups = g_slist_remove(setups, setup);
- setup_free(setup);
- }
- static void setup_remove_all_by_dev(struct a2dp_device *dev)
- {
- GSList *l = setups;
- while (l) {
- struct a2dp_setup *setup = l->data;
- GSList *next = g_slist_next(l);
- if (setup->dev == dev)
- setup_remove(setup);
- l = next;
- }
- }
- static void a2dp_device_free(void *data)
- {
- struct a2dp_device *dev = data;
- if (dev->idle_id > 0)
- g_source_remove(dev->idle_id);
- if (dev->session)
- avdtp_unref(dev->session);
- if (dev->io) {
- g_io_channel_shutdown(dev->io, FALSE, NULL);
- g_io_channel_unref(dev->io);
- }
- setup_remove_all_by_dev(dev);
- g_free(dev);
- }
- static void a2dp_device_remove(struct a2dp_device *dev)
- {
- devices = g_slist_remove(devices, dev);
- a2dp_device_free(dev);
- }
- static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst)
- {
- struct a2dp_device *dev;
- dev = g_new0(struct a2dp_device, 1);
- bacpy(&dev->dst, dst);
- devices = g_slist_prepend(devices, dev);
- return dev;
- }
- static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb)
- {
- GError *err = NULL;
- dev->io = bt_io_connect(cb, dev, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
- BT_IO_OPT_DEST_BDADDR, &dev->dst,
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_INVALID);
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- return false;
- }
- return true;
- }
- static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
- {
- struct hal_ev_a2dp_conn_state ev;
- char address[18];
- if (dev->state == state)
- return;
- dev->state = state;
- ba2str(&dev->dst, address);
- DBG("device %s state %u", address, state);
- bdaddr2android(&dev->dst, ev.bdaddr);
- ev.state = state;
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE,
- sizeof(ev), &ev);
- if (state != HAL_A2DP_STATE_DISCONNECTED)
- return;
- bt_avrcp_disconnect(&dev->dst);
- a2dp_device_remove(dev);
- }
- static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
- {
- struct hal_ev_a2dp_audio_state ev;
- char address[18];
- if (setup->state == state)
- return;
- setup->state = state;
- ba2str(&setup->dev->dst, address);
- DBG("device %s state %u", address, state);
- bdaddr2android(&setup->dev->dst, ev.bdaddr);
- ev.state = state;
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE,
- sizeof(ev), &ev);
- }
- static void disconnect_cb(void *user_data)
- {
- struct a2dp_device *dev = user_data;
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
- }
- static int sbc_check_config(void *caps, uint8_t caps_len, void *conf,
- uint8_t conf_len)
- {
- a2dp_sbc_t *cap, *config;
- if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) {
- error("SBC: Invalid configuration size (%u)", conf_len);
- return -EINVAL;
- }
- cap = caps;
- config = conf;
- if (!(cap->frequency & config->frequency)) {
- error("SBC: Unsupported frequency (%u) by endpoint",
- config->frequency);
- return -EINVAL;
- }
- if (!(cap->channel_mode & config->channel_mode)) {
- error("SBC: Unsupported channel mode (%u) by endpoint",
- config->channel_mode);
- return -EINVAL;
- }
- if (!(cap->block_length & config->block_length)) {
- error("SBC: Unsupported block length (%u) by endpoint",
- config->block_length);
- return -EINVAL;
- }
- if (!(cap->allocation_method & config->allocation_method)) {
- error("SBC: Unsupported allocation method (%u) by endpoint",
- config->block_length);
- return -EINVAL;
- }
- if (config->max_bitpool < cap->min_bitpool) {
- error("SBC: Invalid maximun bitpool (%u < %u)",
- config->max_bitpool, cap->min_bitpool);
- return -EINVAL;
- }
- if (config->min_bitpool > cap->max_bitpool) {
- error("SBC: Invalid minimun bitpool (%u > %u)",
- config->min_bitpool, cap->min_bitpool);
- return -EINVAL;
- }
- if (config->max_bitpool > cap->max_bitpool)
- return -ERANGE;
- if (config->min_bitpool < cap->min_bitpool)
- return -ERANGE;
- return 0;
- }
- static int aac_check_config(void *caps, uint8_t caps_len, void *conf,
- uint8_t conf_len)
- {
- a2dp_aac_t *cap, *config;
- if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) {
- error("AAC: Invalid configuration size (%u)", conf_len);
- return -EINVAL;
- }
- cap = caps;
- config = conf;
- if (!(cap->object_type & config->object_type)) {
- error("AAC: Unsupported object type (%u) by endpoint",
- config->object_type);
- return -EINVAL;
- }
- if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) {
- error("AAC: Unsupported frequency (%u) by endpoint",
- AAC_GET_FREQUENCY(*config));
- return -EINVAL;
- }
- if (!(cap->channels & config->channels)) {
- error("AAC: Unsupported channels (%u) by endpoint",
- config->channels);
- return -EINVAL;
- }
- /* VBR support in SNK is mandatory but let's make sure we don't try to
- * have VBR on remote which for some reason does not support it
- */
- if (!cap->vbr && config->vbr) {
- error("AAC: Unsupported VBR (%u) by endpoint",
- config->vbr);
- return -EINVAL;
- }
- if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config))
- return -ERANGE;
- return 0;
- }
- static int aptx_check_config(void *caps, uint8_t caps_len, void *conf,
- uint8_t conf_len)
- {
- a2dp_aptx_t *cap, *config;
- if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) {
- error("APTX: Invalid configuration size (%u)", conf_len);
- return -EINVAL;
- }
- cap = caps;
- config = conf;
- if (!(cap->frequency & config->frequency)) {
- error("APTX: Unsupported frequenct (%u) by endpoint",
- config->frequency);
- return -EINVAL;
- }
- if (!(cap->channel_mode & config->channel_mode)) {
- error("APTX: Unsupported channel mode (%u) by endpoint",
- config->channel_mode);
- return -EINVAL;
- }
- return 0;
- }
- static int check_capabilities(struct a2dp_preset *preset,
- struct avdtp_media_codec_capability *codec,
- uint8_t codec_len)
- {
- a2dp_vendor_codec_t *vndcodec;
- /* Codec specific */
- switch (codec->media_codec_type) {
- case A2DP_CODEC_SBC:
- return sbc_check_config(codec->data, codec_len, preset->data,
- preset->len);
- case A2DP_CODEC_MPEG24:
- return aac_check_config(codec->data, codec_len, preset->data,
- preset->len);
- case A2DP_CODEC_VENDOR:
- vndcodec = (void *) codec->data;
- if (A2DP_GET_VENDOR_ID(*vndcodec) == APTX_VENDOR_ID &&
- A2DP_GET_CODEC_ID(*vndcodec) == APTX_CODEC_ID)
- return aptx_check_config(codec->data, codec_len,
- preset->data, preset->len);
- return -EINVAL;
- default:
- return -EINVAL;
- }
- }
- static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len,
- void *conf, uint8_t conf_len)
- {
- struct a2dp_preset *p;
- a2dp_sbc_t *cap, *config;
- cap = caps;
- config = conf;
- config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool);
- config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool);
- p = g_new0(struct a2dp_preset, 1);
- p->len = conf_len;
- p->data = g_memdup(conf, p->len);
- return p;
- }
- static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len,
- void *conf, uint8_t conf_len)
- {
- struct a2dp_preset *p;
- a2dp_aac_t *cap, *config;
- uint32_t bitrate;
- cap = caps;
- config = conf;
- bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config));
- AAC_SET_BITRATE(*config, bitrate);
- p = g_new0(struct a2dp_preset, 1);
- p->len = conf_len;
- p->data = g_memdup(conf, p->len);
- return p;
- }
- static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
- struct avdtp_media_codec_capability *codec,
- uint8_t codec_len)
- {
- /* Codec specific */
- switch (codec->media_codec_type) {
- case A2DP_CODEC_SBC:
- return sbc_select_range(codec->data, codec_len, preset->data,
- preset->len);
- case A2DP_CODEC_MPEG24:
- return aac_select_range(codec->data, codec_len, preset->data,
- preset->len);
- default:
- return NULL;
- }
- }
- static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint,
- struct avdtp_remote_sep *rsep)
- {
- struct avdtp_service_capability *service;
- struct avdtp_media_codec_capability *codec;
- GSList *l;
- uint8_t codec_len;
- service = avdtp_get_codec(rsep);
- codec = (struct avdtp_media_codec_capability *) service->data;
- codec_len = service->length - sizeof(*codec);
- for (l = endpoint->presets; l; l = g_slist_next(l)) {
- struct a2dp_preset *preset = l->data;
- int err;
- err = check_capabilities(preset, codec, codec_len);
- if (err == 0)
- return preset;
- if (err == -ERANGE)
- return select_preset_range(preset, codec, codec_len);
- }
- return NULL;
- }
- static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
- struct a2dp_preset *preset, struct avdtp_stream *stream)
- {
- struct a2dp_setup *setup;
- setup = g_new0(struct a2dp_setup, 1);
- setup->dev = dev;
- setup->endpoint = endpoint;
- setup->preset = preset;
- setup->stream = stream;
- setups = g_slist_append(setups, setup);
- if (dev->idle_id > 0) {
- g_source_remove(dev->idle_id);
- dev->idle_id = 0;
- }
- }
- static int select_configuration(struct a2dp_device *dev,
- struct a2dp_endpoint *endpoint,
- struct avdtp_remote_sep *rsep)
- {
- struct a2dp_preset *preset;
- struct avdtp_stream *stream;
- struct avdtp_service_capability *service;
- struct avdtp_media_codec_capability *codec;
- GSList *caps;
- int err;
- preset = select_preset(endpoint, rsep);
- if (!preset) {
- error("Unable to select codec preset");
- return -EINVAL;
- }
- service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
- caps = g_slist_append(NULL, service);
- codec = g_malloc0(sizeof(*codec) + preset->len);
- codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
- codec->media_codec_type = endpoint->codec;
- memcpy(codec->data, preset->data, preset->len);
- service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
- sizeof(*codec) + preset->len);
- caps = g_slist_append(caps, service);
- g_free(codec);
- err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps,
- &stream);
- g_slist_free_full(caps, g_free);
- if (err < 0) {
- error("avdtp_set_configuration: %s", strerror(-err));
- return err;
- }
- setup_add(dev, endpoint, preset, stream);
- return 0;
- }
- static void discover_cb(struct avdtp *session, GSList *seps,
- struct avdtp_error *err, void *user_data)
- {
- struct a2dp_device *dev = user_data;
- struct a2dp_endpoint *endpoint = NULL;
- struct avdtp_remote_sep *rsep = NULL;
- GSList *l;
- for (l = endpoints; l; l = g_slist_next(l)) {
- endpoint = l->data;
- rsep = avdtp_find_remote_sep(session, endpoint->sep);
- if (rsep)
- break;
- }
- if (!rsep) {
- error("Unable to find matching endpoint");
- goto failed;
- }
- if (select_configuration(dev, endpoint, rsep) < 0)
- goto failed;
- return;
- failed:
- avdtp_shutdown(session);
- }
- static gboolean idle_timeout(gpointer user_data)
- {
- struct a2dp_device *dev = user_data;
- int err;
- dev->idle_id = 0;
- err = avdtp_discover(dev->session, discover_cb, dev);
- if (err == 0)
- return FALSE;
- error("avdtp_discover: %s", strerror(-err));
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
- return FALSE;
- }
- static void signaling_connect_cb(GIOChannel *chan, GError *err,
- gpointer user_data)
- {
- struct a2dp_device *dev = user_data;
- struct avdtp *session;
- uint16_t imtu, omtu;
- GError *gerr = NULL;
- int fd;
- if (err) {
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
- error("%s", err->message);
- return;
- }
- bt_io_get(chan, &gerr,
- BT_IO_OPT_IMTU, &imtu,
- BT_IO_OPT_OMTU, &omtu,
- BT_IO_OPT_INVALID);
- if (gerr) {
- error("%s", gerr->message);
- g_error_free(gerr);
- goto failed;
- }
- fd = g_io_channel_unix_get_fd(chan);
- /* FIXME: Add proper version */
- session = avdtp_new(fd, imtu, omtu, 0x0100, lseps);
- if (!session)
- goto failed;
- dev->session = session;
- avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
- /* Proceed to stream setup if initiator */
- if (dev->io) {
- int perr;
- g_io_channel_unref(dev->io);
- dev->io = NULL;
- perr = avdtp_discover(dev->session, discover_cb, dev);
- if (perr < 0) {
- error("avdtp_discover: %s", strerror(-perr));
- goto failed;
- }
- bt_avrcp_connect(&dev->dst);
- } else /* Init idle timeout to discover */
- dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout,
- dev);
- return;
- failed:
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
- }
- static void bt_a2dp_connect(const void *buf, uint16_t len)
- {
- const struct hal_cmd_a2dp_connect *cmd = buf;
- struct a2dp_device *dev;
- uint8_t status;
- char addr[18];
- bdaddr_t dst;
- GSList *l;
- DBG("");
- android2bdaddr(&cmd->bdaddr, &dst);
- l = g_slist_find_custom(devices, &dst, device_cmp);
- if (l) {
- status = HAL_STATUS_FAILED;
- goto failed;
- }
- dev = a2dp_device_new(&dst);
- if (!a2dp_device_connect(dev, signaling_connect_cb)) {
- a2dp_device_remove(dev);
- status = HAL_STATUS_FAILED;
- goto failed;
- }
- ba2str(&dev->dst, addr);
- DBG("connecting to %s", addr);
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
- status = HAL_STATUS_SUCCESS;
- failed:
- ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
- }
- static void bt_a2dp_disconnect(const void *buf, uint16_t len)
- {
- const struct hal_cmd_a2dp_connect *cmd = buf;
- uint8_t status;
- struct a2dp_device *dev;
- GSList *l;
- bdaddr_t dst;
- DBG("");
- android2bdaddr(&cmd->bdaddr, &dst);
- l = g_slist_find_custom(devices, &dst, device_cmp);
- if (!l) {
- status = HAL_STATUS_FAILED;
- goto failed;
- }
- dev = l->data;
- status = HAL_STATUS_SUCCESS;
- if (dev->io) {
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
- goto failed;
- }
- /* Wait AVDTP session to shutdown */
- avdtp_shutdown(dev->session);
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING);
- failed:
- ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
- status);
- }
- static const struct ipc_handler cmd_handlers[] = {
- /* HAL_OP_A2DP_CONNECT */
- { bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
- /* HAL_OP_A2DP_DISCONNECT */
- { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) },
- };
- static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev)
- {
- GSList *l;
- for (l = setups; l; l = g_slist_next(l)) {
- struct a2dp_setup *setup = l->data;
- if (setup->dev == dev)
- return setup;
- }
- return NULL;
- }
- static void transport_connect_cb(GIOChannel *chan, GError *err,
- gpointer user_data)
- {
- struct a2dp_device *dev = user_data;
- struct a2dp_setup *setup;
- uint16_t imtu, omtu;
- GError *gerr = NULL;
- int fd;
- if (err) {
- error("%s", err->message);
- return;
- }
- setup = find_setup_by_device(dev);
- if (!setup) {
- error("Unable to find stream setup");
- return;
- }
- bt_io_get(chan, &gerr,
- BT_IO_OPT_IMTU, &imtu,
- BT_IO_OPT_OMTU, &omtu,
- BT_IO_OPT_INVALID);
- if (gerr) {
- error("%s", gerr->message);
- g_error_free(gerr);
- return;
- }
- fd = g_io_channel_unix_get_fd(chan);
- if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) {
- error("avdtp_stream_set_transport: failed");
- return;
- }
- g_io_channel_set_close_on_unref(chan, FALSE);
- if (dev->io) {
- g_io_channel_unref(dev->io);
- dev->io = NULL;
- }
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
- }
- static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
- {
- struct a2dp_device *dev;
- bdaddr_t dst;
- char address[18];
- GError *gerr = NULL;
- GSList *l;
- if (err) {
- error("%s", err->message);
- return;
- }
- bt_io_get(chan, &gerr,
- BT_IO_OPT_DEST_BDADDR, &dst,
- BT_IO_OPT_INVALID);
- if (gerr) {
- error("%s", gerr->message);
- g_error_free(gerr);
- g_io_channel_shutdown(chan, TRUE, NULL);
- return;
- }
- ba2str(&dst, address);
- DBG("Incoming connection from %s", address);
- l = g_slist_find_custom(devices, &dst, device_cmp);
- if (l) {
- transport_connect_cb(chan, err, l->data);
- return;
- }
- dev = a2dp_device_new(&dst);
- bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
- signaling_connect_cb(chan, err, dev);
- }
- static sdp_record_t *a2dp_record(void)
- {
- sdp_list_t *svclass_id, *pfseq, *apseq, *root;
- uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
- sdp_profile_desc_t profile[1];
- sdp_list_t *aproto, *proto[2];
- sdp_record_t *record;
- sdp_data_t *psm, *version, *features;
- uint16_t lp = AVDTP_UUID;
- uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
- record = sdp_record_alloc();
- if (!record)
- return NULL;
- sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
- root = sdp_list_append(NULL, &root_uuid);
- sdp_set_browse_groups(record, root);
- sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
- svclass_id = sdp_list_append(NULL, &a2dp_uuid);
- sdp_set_service_classes(record, svclass_id);
- sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
- profile[0].version = a2dp_ver;
- pfseq = sdp_list_append(NULL, &profile[0]);
- sdp_set_profile_descs(record, pfseq);
- sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
- proto[0] = sdp_list_append(NULL, &l2cap_uuid);
- psm = sdp_data_alloc(SDP_UINT16, &lp);
- proto[0] = sdp_list_append(proto[0], psm);
- apseq = sdp_list_append(NULL, proto[0]);
- sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
- proto[1] = sdp_list_append(NULL, &avdtp_uuid);
- version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
- proto[1] = sdp_list_append(proto[1], version);
- apseq = sdp_list_append(apseq, proto[1]);
- aproto = sdp_list_append(NULL, apseq);
- sdp_set_access_protos(record, aproto);
- features = sdp_data_alloc(SDP_UINT16, &feat);
- sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
- sdp_set_info_attr(record, "Audio Source", NULL, NULL);
- sdp_data_free(psm);
- sdp_data_free(version);
- sdp_list_free(proto[0], NULL);
- sdp_list_free(proto[1], NULL);
- sdp_list_free(apseq, NULL);
- sdp_list_free(pfseq, NULL);
- sdp_list_free(aproto, NULL);
- sdp_list_free(root, NULL);
- sdp_list_free(svclass_id, NULL);
- return record;
- }
- static gboolean sep_getcap_ind(struct avdtp *session,
- struct avdtp_local_sep *sep,
- GSList **caps, uint8_t *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_preset *cap = endpoint->caps;
- struct avdtp_service_capability *service;
- struct avdtp_media_codec_capability *codec;
- *caps = NULL;
- service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
- *caps = g_slist_append(*caps, service);
- codec = g_malloc0(sizeof(*codec) + cap->len);
- codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
- codec->media_codec_type = endpoint->codec;
- memcpy(codec->data, cap->data, cap->len);
- service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
- sizeof(*codec) + cap->len);
- *caps = g_slist_append(*caps, service);
- g_free(codec);
- return TRUE;
- }
- static int check_config(struct a2dp_endpoint *endpoint,
- struct a2dp_preset *config)
- {
- GSList *l;
- struct a2dp_preset *caps;
- for (l = endpoint->presets; l; l = g_slist_next(l)) {
- struct a2dp_preset *preset = l->data;
- if (preset->len != config->len)
- continue;
- if (memcmp(preset->data, config->data, preset->len) == 0)
- return 0;
- }
- caps = endpoint->caps;
- /* Codec specific */
- switch (endpoint->codec) {
- case A2DP_CODEC_SBC:
- return sbc_check_config(caps->data, caps->len, config->data,
- config->len);
- default:
- return -EINVAL;
- }
- }
- static struct a2dp_device *find_device_by_session(struct avdtp *session)
- {
- GSList *l;
- for (l = devices; l; l = g_slist_next(l)) {
- struct a2dp_device *dev = l->data;
- if (dev->session == session)
- return dev;
- }
- return NULL;
- }
- static struct a2dp_setup *find_setup(uint8_t id)
- {
- GSList *l;
- for (l = setups; l; l = g_slist_next(l)) {
- struct a2dp_setup *setup = l->data;
- if (setup->endpoint->id == id)
- return setup;
- }
- return NULL;
- }
- static void setup_remove_by_id(uint8_t id)
- {
- struct a2dp_setup *setup;
- setup = find_setup(id);
- if (!setup) {
- error("Unable to find stream setup for endpoint %u", id);
- return;
- }
- setup_remove(setup);
- }
- static gboolean sep_setconf_ind(struct avdtp *session,
- struct avdtp_local_sep *sep,
- struct avdtp_stream *stream,
- GSList *caps,
- avdtp_set_configuration_cb cb,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_device *dev;
- struct a2dp_preset *preset = NULL;
- DBG("");
- dev = find_device_by_session(session);
- if (!dev) {
- error("Unable to find device for session %p", session);
- return FALSE;
- }
- for (; caps != NULL; caps = g_slist_next(caps)) {
- struct avdtp_service_capability *cap = caps->data;
- struct avdtp_media_codec_capability *codec;
- if (cap->category == AVDTP_DELAY_REPORTING)
- return FALSE;
- if (cap->category != AVDTP_MEDIA_CODEC)
- continue;
- codec = (struct avdtp_media_codec_capability *) cap->data;
- if (codec->media_codec_type != endpoint->codec)
- return FALSE;
- preset = g_new0(struct a2dp_preset, 1);
- preset->len = cap->length - sizeof(*codec);
- preset->data = g_memdup(codec->data, preset->len);
- if (check_config(endpoint, preset) < 0) {
- preset_free(preset);
- return FALSE;
- }
- }
- if (!preset)
- return FALSE;
- setup_add(dev, endpoint, preset, stream);
- cb(session, stream, NULL);
- return TRUE;
- }
- static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, uint8_t *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for endpoint %u",
- endpoint->id);
- *err = AVDTP_SEP_NOT_IN_USE;
- return FALSE;
- }
- return TRUE;
- }
- static gboolean sep_close_ind(struct avdtp *session,
- struct avdtp_local_sep *sep,
- struct avdtp_stream *stream,
- uint8_t *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for endpoint %u",
- endpoint->id);
- *err = AVDTP_SEP_NOT_IN_USE;
- return FALSE;
- }
- bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
- setup_remove(setup);
- return TRUE;
- }
- static gboolean sep_start_ind(struct avdtp *session,
- struct avdtp_local_sep *sep,
- struct avdtp_stream *stream,
- uint8_t *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for endpoint %u",
- endpoint->id);
- *err = AVDTP_SEP_NOT_IN_USE;
- return FALSE;
- }
- bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
- return TRUE;
- }
- static gboolean sep_suspend_ind(struct avdtp *session,
- struct avdtp_local_sep *sep,
- struct avdtp_stream *stream,
- uint8_t *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for endpoint %u",
- endpoint->id);
- *err = AVDTP_SEP_NOT_IN_USE;
- return FALSE;
- }
- bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
- return TRUE;
- }
- static struct avdtp_sep_ind sep_ind = {
- .get_capability = sep_getcap_ind,
- .set_configuration = sep_setconf_ind,
- .open = sep_open_ind,
- .close = sep_close_ind,
- .start = sep_start_ind,
- .suspend = sep_suspend_ind,
- };
- static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream,
- struct avdtp_error *err, void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- int ret;
- DBG("");
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for endpoint %u",
- endpoint->id);
- return;
- }
- if (err)
- goto failed;
- ret = avdtp_open(session, stream);
- if (ret < 0) {
- error("avdtp_open: %s", strerror(-ret));
- goto failed;
- }
- return;
- failed:
- setup_remove(setup);
- }
- static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, struct avdtp_error *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_device *dev;
- DBG("");
- if (err)
- goto failed;
- dev = find_device_by_session(session);
- if (!dev) {
- error("Unable to find device for session");
- goto failed;
- }
- a2dp_device_connect(dev, transport_connect_cb);
- return;
- failed:
- setup_remove_by_id(endpoint->id);
- }
- static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, struct avdtp_error *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- if (err) {
- setup_remove_by_id(endpoint->id);
- return;
- }
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for %u endpoint",
- endpoint->id);
- return;
- }
- bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
- }
- static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, struct avdtp_error *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- if (err) {
- setup_remove_by_id(endpoint->id);
- return;
- }
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for %u endpoint",
- endpoint->id);
- return;
- }
- bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
- }
- static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, struct avdtp_error *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- struct a2dp_setup *setup;
- DBG("");
- if (err)
- return;
- setup = find_setup(endpoint->id);
- if (!setup) {
- error("Unable to find stream setup for %u endpoint",
- endpoint->id);
- return;
- }
- bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
- setup_remove(setup);
- }
- static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, struct avdtp_error *err,
- void *user_data)
- {
- struct a2dp_endpoint *endpoint = user_data;
- DBG("");
- if (err)
- return;
- setup_remove_by_id(endpoint->id);
- }
- static struct avdtp_sep_cfm sep_cfm = {
- .set_configuration = sep_setconf_cfm,
- .open = sep_open_cfm,
- .start = sep_start_cfm,
- .suspend = sep_suspend_cfm,
- .close = sep_close_cfm,
- .abort = sep_abort_cfm,
- };
- static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
- GSList *presets)
- {
- struct a2dp_endpoint *endpoint;
- /* FIXME: Add proper check for uuid */
- endpoint = g_new0(struct a2dp_endpoint, 1);
- endpoint->id = g_slist_length(endpoints) + 1;
- endpoint->codec = codec;
- endpoint->sep = avdtp_register_sep(lseps, AVDTP_SEP_TYPE_SOURCE,
- AVDTP_MEDIA_TYPE_AUDIO,
- codec, FALSE, &sep_ind,
- &sep_cfm, endpoint);
- endpoint->caps = presets->data;
- endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
- if (endpoint->codec == A2DP_CODEC_VENDOR) {
- a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
- avdtp_sep_set_vendor_codec(endpoint->sep,
- A2DP_GET_VENDOR_ID(*vndcodec),
- A2DP_GET_CODEC_ID(*vndcodec));
- }
- endpoints = g_slist_append(endpoints, endpoint);
- return endpoint->id;
- }
- static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
- uint16_t len)
- {
- GSList *l = NULL;
- uint8_t i;
- for (i = 0; count > i; i++) {
- const uint8_t *ptr = (const uint8_t *) p;
- struct a2dp_preset *preset;
- if (len < sizeof(struct audio_preset)) {
- DBG("Invalid preset index %u", i);
- g_slist_free_full(l, preset_free);
- return NULL;
- }
- len -= sizeof(struct audio_preset);
- if (len == 0 || len < p->len) {
- DBG("Invalid preset size of %u for index %u", len, i);
- g_slist_free_full(l, preset_free);
- return NULL;
- }
- preset = g_new0(struct a2dp_preset, 1);
- preset->len = p->len;
- preset->data = g_memdup(p->data, preset->len);
- l = g_slist_append(l, preset);
- len -= preset->len;
- ptr += sizeof(*p) + preset->len;
- p = (const struct audio_preset *) ptr;
- }
- return l;
- }
- static void bt_audio_open(const void *buf, uint16_t len)
- {
- const struct audio_cmd_open *cmd = buf;
- struct audio_rsp_open rsp;
- GSList *presets;
- DBG("");
- audio_retrying = false;
- if (cmd->presets == 0) {
- error("No audio presets found");
- goto failed;
- }
- presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
- if (!presets) {
- error("No audio presets found");
- goto failed;
- }
- rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
- if (rsp.id == 0) {
- g_slist_free_full(presets, preset_free);
- error("Unable to register endpoint");
- goto failed;
- }
- g_slist_free(presets);
- ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
- sizeof(rsp), &rsp, -1);
- return;
- failed:
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
- AUDIO_STATUS_FAILED);
- }
- static struct a2dp_endpoint *find_endpoint(uint8_t id)
- {
- GSList *l;
- for (l = endpoints; l; l = g_slist_next(l)) {
- struct a2dp_endpoint *endpoint = l->data;
- if (endpoint->id == id)
- return endpoint;
- }
- return NULL;
- }
- static void bt_audio_close(const void *buf, uint16_t len)
- {
- const struct audio_cmd_close *cmd = buf;
- struct a2dp_endpoint *endpoint;
- DBG("");
- endpoint = find_endpoint(cmd->id);
- if (!endpoint) {
- error("Unable to find endpoint %u", cmd->id);
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
- AUDIO_STATUS_FAILED);
- return;
- }
- endpoints = g_slist_remove(endpoints, endpoint);
- unregister_endpoint(endpoint);
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
- AUDIO_STATUS_SUCCESS);
- }
- static void bt_stream_open(const void *buf, uint16_t len)
- {
- const struct audio_cmd_open_stream *cmd = buf;
- struct audio_rsp_open_stream *rsp;
- struct a2dp_setup *setup;
- int fd;
- uint16_t omtu;
- DBG("");
- if (cmd->id)
- setup = find_setup(cmd->id);
- else
- setup = setups ? setups->data : NULL;
- if (!setup) {
- error("Unable to find stream for endpoint %u", cmd->id);
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
- AUDIO_STATUS_FAILED);
- return;
- }
- if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu,
- NULL)) {
- error("avdtp_stream_get_transport: failed");
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
- AUDIO_STATUS_FAILED);
- return;
- }
- len = sizeof(struct audio_rsp_open_stream) +
- sizeof(struct audio_preset) + setup->preset->len;
- rsp = g_malloc0(len);
- rsp->id = setup->endpoint->id;
- rsp->mtu = omtu;
- rsp->preset->len = setup->preset->len;
- memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
- ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
- len, rsp, fd);
- g_free(rsp);
- }
- static void bt_stream_close(const void *buf, uint16_t len)
- {
- const struct audio_cmd_close_stream *cmd = buf;
- struct a2dp_setup *setup;
- int err;
- DBG("");
- setup = find_setup(cmd->id);
- if (!setup) {
- error("Unable to find stream for endpoint %u", cmd->id);
- goto failed;
- }
- err = avdtp_close(setup->dev->session, setup->stream, FALSE);
- if (err < 0) {
- error("avdtp_close: %s", strerror(-err));
- goto failed;
- }
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
- AUDIO_STATUS_SUCCESS);
- return;
- failed:
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
- AUDIO_STATUS_FAILED);
- }
- static void bt_stream_resume(const void *buf, uint16_t len)
- {
- const struct audio_cmd_resume_stream *cmd = buf;
- struct a2dp_setup *setup;
- int err;
- DBG("");
- setup = find_setup(cmd->id);
- if (!setup) {
- error("Unable to find stream for endpoint %u", cmd->id);
- goto failed;
- }
- if (setup->state != HAL_AUDIO_STARTED) {
- err = avdtp_start(setup->dev->session, setup->stream);
- if (err < 0) {
- error("avdtp_start: %s", strerror(-err));
- goto failed;
- }
- }
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
- AUDIO_STATUS_SUCCESS);
- return;
- failed:
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
- AUDIO_STATUS_FAILED);
- }
- static void bt_stream_suspend(const void *buf, uint16_t len)
- {
- const struct audio_cmd_suspend_stream *cmd = buf;
- struct a2dp_setup *setup;
- int err;
- DBG("");
- setup = find_setup(cmd->id);
- if (!setup) {
- error("Unable to find stream for endpoint %u", cmd->id);
- goto failed;
- }
- err = avdtp_suspend(setup->dev->session, setup->stream);
- if (err < 0) {
- error("avdtp_suspend: %s", strerror(-err));
- goto failed;
- }
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
- AUDIO_STATUS_SUCCESS);
- return;
- failed:
- ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
- AUDIO_STATUS_FAILED);
- }
- static const struct ipc_handler audio_handlers[] = {
- /* AUDIO_OP_OPEN */
- { bt_audio_open, true, sizeof(struct audio_cmd_open) },
- /* AUDIO_OP_CLOSE */
- { bt_audio_close, false, sizeof(struct audio_cmd_close) },
- /* AUDIO_OP_OPEN_STREAM */
- { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
- /* AUDIO_OP_CLOSE_STREAM */
- { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
- /* AUDIO_OP_RESUME_STREAM */
- { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
- /* AUDIO_OP_SUSPEND_STREAM */
- { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
- };
- static void bt_audio_unregister(void)
- {
- DBG("");
- if (audio_retry_id > 0)
- g_source_remove(audio_retry_id);
- g_slist_free_full(endpoints, unregister_endpoint);
- endpoints = NULL;
- g_slist_free_full(setups, setup_free);
- setups = NULL;
- ipc_cleanup(audio_ipc);
- audio_ipc = NULL;
- queue_destroy(lseps, NULL);
- }
- static bool bt_audio_register(ipc_disconnect_cb disconnect)
- {
- DBG("");
- audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
- AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
- if (!audio_ipc)
- return false;
- ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers,
- G_N_ELEMENTS(audio_handlers));
- return true;
- }
- static gboolean audio_retry_register(void *data)
- {
- ipc_disconnect_cb cb = data;
- audio_retry_id = 0;
- audio_retrying = true;
- bt_audio_register(cb);
- return FALSE;
- }
- static void audio_disconnected(void *data)
- {
- GSList *l;
- bool restart;
- DBG("");
- if (audio_retrying)
- goto retry;
- restart = endpoints != NULL ? true : false;
- bt_audio_unregister();
- for (l = devices; l; l = g_slist_next(l)) {
- struct a2dp_device *dev = l->data;
- avdtp_shutdown(dev->session);
- }
- if (!restart)
- return;
- retry:
- audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT,
- audio_retry_register,
- audio_disconnected);
- }
- bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
- {
- GError *err = NULL;
- sdp_record_t *rec;
- DBG("");
- bacpy(&adapter_addr, addr);
- lseps = queue_new();
- server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_CENTRAL, true,
- BT_IO_OPT_INVALID);
- if (!server) {
- error("Failed to listen on AVDTP channel: %s", err->message);
- g_error_free(err);
- return false;
- }
- rec = a2dp_record();
- if (!rec) {
- error("Failed to allocate A2DP record");
- goto fail;
- }
- if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) {
- error("Failed to register A2DP record");
- sdp_record_free(rec);
- goto fail;
- }
- record_id = rec->handle;
- hal_ipc = ipc;
- ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers,
- G_N_ELEMENTS(cmd_handlers));
- if (bt_audio_register(audio_disconnected))
- return true;
- fail:
- g_io_channel_shutdown(server, TRUE, NULL);
- g_io_channel_unref(server);
- server = NULL;
- return false;
- }
- void bt_a2dp_unregister(void)
- {
- DBG("");
- g_slist_free_full(setups, setup_free);
- setups = NULL;
- g_slist_free_full(endpoints, unregister_endpoint);
- endpoints = NULL;
- g_slist_free_full(devices, a2dp_device_free);
- devices = NULL;
- ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP);
- hal_ipc = NULL;
- bt_adapter_remove_record(record_id);
- record_id = 0;
- if (server) {
- g_io_channel_shutdown(server, TRUE, NULL);
- g_io_channel_unref(server);
- server = NULL;
- }
- if (audio_ipc) {
- ipc_unregister(audio_ipc, AUDIO_SERVICE_ID);
- ipc_cleanup(audio_ipc);
- audio_ipc = NULL;
- }
- }
|