diff options
| author | Lennart Poettering <lennart@poettering.net> | 2009-02-12 03:35:57 +0100 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2009-02-12 03:35:57 +0100 | 
| commit | f0cc23d6e5dd9ce718e66978d7de2dbc94ded537 (patch) | |
| tree | bff90bcabee7c4b047af8fc55c8da12b2f3aa932 /src/modules | |
| parent | 5d154255cbab0726ab30ec1f9e0b745face778fa (diff) | |
| parent | 86bec09fa843160dd671cf53a32c13fafa0cb932 (diff) | |
Merge commit 'elmarco/bt-wip'
Diffstat (limited to 'src/modules')
| -rw-r--r-- | src/modules/bluetooth/module-bluetooth-device.c | 297 | ||||
| -rw-r--r-- | src/modules/bluetooth/module-bluetooth-discover.c | 23 | ||||
| -rw-r--r-- | src/modules/module-suspend-on-idle.c | 3 | 
3 files changed, 254 insertions, 69 deletions
| diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index d909d70d..94195957 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -43,6 +43,7 @@  #include <pulsecore/rtpoll.h>  #include <pulsecore/time-smoother.h>  #include <pulsecore/rtclock.h> +#include <pulsecore/namereg.h>  #include <modules/dbus-util.h> @@ -71,7 +72,9 @@ PA_MODULE_USAGE(          "profile=<a2dp|hsp> "          "rate=<sample rate> "          "channels=<number of channels> " -        "path=<device object path>"); +        "path=<device object path> " +	"sco_sink=<SCO over PCM sink name> " +	"sco_source=<SCO over PCM source name>");  static const char* const valid_modargs[] = {      "name", @@ -83,6 +86,8 @@ static const char* const valid_modargs[] = {      "rate",      "channels",      "path", +    "sco_sink", +    "sco_source",      NULL  }; @@ -98,6 +103,14 @@ struct a2dp_info {      uint16_t seq_num;                    /* Cumulative packet sequence */  }; +struct hsp_info { +    pcm_capabilities_t pcm_capabilities; +    pa_sink *sco_sink; +    pa_source *sco_source; +    pa_hook_slot *sink_state_changed_slot; +    pa_hook_slot *source_state_changed_slot; +}; +  enum profile {      PROFILE_A2DP,      PROFILE_HSP, @@ -132,6 +145,7 @@ struct userdata {      size_t block_size;      struct a2dp_info a2dp; +    struct hsp_info hsp;      pa_dbus_connection *connection;      enum profile profile; @@ -143,6 +157,9 @@ struct userdata {      int write_type, read_type;  }; +static int init_bt(struct userdata *u); +static int init_profile(struct userdata *u); +  static int service_send(int fd, const bt_audio_msg_header_t *msg) {      size_t length;      ssize_t r; @@ -255,23 +272,33 @@ static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *          return -1;      } -    if (u->profile != PROFILE_A2DP) -        return 0; +    if (u->profile == PROFILE_HSP) { +        if (bytes_left <= 0 || codec->length != sizeof(u->hsp.pcm_capabilities)) +            return -1; -    while (bytes_left > 0) { -        if (codec->type == BT_A2DP_CODEC_SBC) -            break; +        pa_assert(codec->type == BT_HFP_CODEC_PCM); -        bytes_left -= codec->length; -        codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length); +        memcpy(&u->hsp.pcm_capabilities, codec, sizeof(u->hsp.pcm_capabilities));      } -    if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities)) -        return -1; +    if (u->profile == PROFILE_A2DP) { + +        while (bytes_left > 0) { +            if (codec->type == BT_A2DP_CODEC_SBC) +                break; + +            bytes_left -= codec->length; +            codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length); +        } + +        if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities)) +            return -1; -    pa_assert(codec->type == BT_A2DP_CODEC_SBC); +        pa_assert(codec->type == BT_A2DP_CODEC_SBC); + +        memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities)); +    } -    memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));      return 0;  } @@ -1218,66 +1245,157 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p      return pa_sprintf_malloc("bluez_%s.%s", type, n);  } +#define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source)) + +static void sco_over_pcm_state_update(struct userdata *u) { +    pa_assert(u); + +    if (PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) || +        PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) { + +        if (u->service_fd > 0) +            return; + +        pa_log_debug("Resuming SCO over PCM"); +        if ((init_bt(u) < 0) || (init_profile(u) < 0)) +            pa_log("Can't resume SCO over PCM"); + +    } else { + +        if (u->service_fd <= 0) +            return; + +        pa_log_debug("Closing SCO over PCM"); +        pa_close(u->service_fd); +        u->service_fd = 0; + +    } +} + +static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct userdata *u) { +    pa_assert(c); +    pa_sink_assert_ref(s); +    pa_assert(u); + +    if (s != u->hsp.sco_sink) +        return PA_HOOK_OK; + +    sco_over_pcm_state_update(u); + +    return PA_HOOK_OK; +} + +static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct userdata *u) { +    pa_assert(c); +    pa_source_assert_ref(s); +    pa_assert(u); + +    if (s != u->hsp.sco_source) +        return PA_HOOK_OK; + +    sco_over_pcm_state_update(u); + +    return PA_HOOK_OK; +} +  static int add_sink(struct userdata *u) { -    pa_sink_new_data data; -    pa_bool_t b; -    pa_sink_new_data_init(&data); -    data.driver = __FILE__; -    data.module = u->module; -    pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); -    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); -    data.card = u->card; -    data.name = get_name("sink", u->modargs, u->device->address, &b); -    data.namereg_fail = b; +    if (USE_SCO_OVER_PCM(u)) { +        pa_proplist *p; -    u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); -    pa_sink_new_data_done(&data); +        u->sink = u->hsp.sco_sink; +        u->sink->card = u->card; /* FIXME! */ +        p = pa_proplist_new(); +        pa_proplist_sets(p, "bluetooth.protocol", "sco"); +        pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p); +        pa_proplist_free(p); -    if (!u->sink) { -        pa_log_error("Failed to create sink"); -        return -1; +	if (!u->hsp.sink_state_changed_slot) +            u->hsp.sink_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u); + +    } else { +        pa_sink_new_data data; +        pa_bool_t b; + +        pa_sink_new_data_init(&data); +        data.driver = __FILE__; +        data.module = u->module; +        pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); +        pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); +        data.card = u->card; +        data.name = get_name("sink", u->modargs, u->device->address, &b); +        data.namereg_fail = b; + +        u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); +        pa_sink_new_data_done(&data); + +        if (!u->sink) { +            pa_log_error("Failed to create sink"); +            return -1; +        } + +        u->sink->userdata = u; +        u->sink->parent.process_msg = sink_process_msg; + +        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); +        pa_sink_set_rtpoll(u->sink, u->rtpoll);      } -    u->sink->userdata = u; -    u->sink->parent.process_msg = sink_process_msg;  /*     u->sink->get_volume = sink_get_volume_cb; */  /*     u->sink->set_volume = sink_set_volume_cb; */ -    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); -    pa_sink_set_rtpoll(u->sink, u->rtpoll); -      return 0;  }  static int add_source(struct userdata *u) { -    pa_source_new_data data; -    pa_bool_t b; +    pa_proplist *p; -    pa_source_new_data_init(&data); -    data.driver = __FILE__; -    data.module = u->module; -    pa_source_new_data_set_sample_spec(&data, &u->sample_spec); -    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); -    data.card = u->card; -    data.name = get_name("source", u->modargs, u->device->address, &b); -    data.namereg_fail = b; +    if (USE_SCO_OVER_PCM(u)) { +        u->source = u->hsp.sco_source; +        u->source->card = u->card; /* FIXME! */ +        p = pa_proplist_new(); +        pa_proplist_sets(p, "bluetooth.protocol", "sco"); +        pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p); +        pa_proplist_free(p); -    u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); -    pa_source_new_data_done(&data); +        if (!u->hsp.source_state_changed_slot) +            u->hsp.source_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u); -    if (!u->source) { -        pa_log_error("Failed to create source"); -        return -1; +    } else { +        pa_source_new_data data; +        pa_bool_t b; + +        pa_source_new_data_init(&data); +        data.driver = __FILE__; +        data.module = u->module; +        pa_source_new_data_set_sample_spec(&data, &u->sample_spec); +        pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); +        data.card = u->card; +        data.name = get_name("source", u->modargs, u->device->address, &b); +        data.namereg_fail = b; + +        u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); +        pa_source_new_data_done(&data); + +        if (!u->source) { +            pa_log_error("Failed to create source"); +            return -1; +        } + +        u->source->userdata = u; +        u->source->parent.process_msg = source_process_msg; + +        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); +        pa_source_set_rtpoll(u->source, u->rtpoll);      } -    u->source->userdata = u; -    u->source->parent.process_msg = source_process_msg;  /*     u->source->get_volume = source_get_volume_cb; */  /*     u->source->set_volume = source_set_volume_cb; */ -    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); -    pa_source_set_rtpoll(u->source, u->rtpoll); +    p = pa_proplist_new(); +    pa_proplist_sets(p, "bluetooth.nrec", pa_yes_no(u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC)); +    pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p); +    pa_proplist_free(p);      return 0;  } @@ -1285,6 +1403,19 @@ static int add_source(struct userdata *u) {  static int init_bt(struct userdata *u) {      pa_assert(u); +    /* shutdown bt */ +    if (u->stream_fd >= 0) { +        pa_close(u->stream_fd); +        u->stream_fd = -1; +    } + +    if (u->service_fd >= 0) { +        pa_close(u->service_fd); +        u->service_fd = -1; +    } + +    u->write_type = u->read_type = 0; +      /* connect to the bluez audio service */      if ((u->service_fd = bt_audio_service_open()) < 0) {          pa_log_error("Couldn't connect to bluetooth audio service"); @@ -1295,17 +1426,6 @@ static int init_bt(struct userdata *u) {      return 0;  } -static void shutdown_bt(struct userdata *u) { -    pa_assert(u); - -    if (u->stream_fd <= 0) { -        pa_close(u->stream_fd); -        u->stream_fd = -1; -    } - -    u->write_type = u->read_type = 0; -} -  static int setup_bt(struct userdata *u) {      pa_assert(u); @@ -1319,6 +1439,11 @@ static int setup_bt(struct userdata *u) {      pa_log_debug("Connection to the device configured"); +    if (USE_SCO_OVER_PCM(u)) { +        pa_log_debug("Configured to use SCO over PCM"); +        return 0; +    } +      if (setup_stream_fd(u) < 0)          return -1; @@ -1360,6 +1485,16 @@ static void stop_thread(struct userdata *u) {          u->rtpoll_item = NULL;      } +    if (u->hsp.sink_state_changed_slot) { +        pa_hook_slot_free(u->hsp.sink_state_changed_slot); +        u->hsp.sink_state_changed_slot = NULL; +    } + +    if (u->hsp.source_state_changed_slot) { +        pa_hook_slot_free(u->hsp.source_state_changed_slot); +        u->hsp.source_state_changed_slot = NULL; +    } +      if (u->sink) {          pa_sink_unref(u->sink);          u->sink = NULL; @@ -1378,6 +1513,12 @@ static int start_thread(struct userdata *u) {      pa_assert(!u->thread);      pa_assert(!u->rtpoll_item); +    if (USE_SCO_OVER_PCM(u)) { +        pa_sink_ref(u->sink); +        pa_source_ref(u->source); +        return 0; +    } +      u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);      pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);      pollfd->fd = u->stream_fd; @@ -1411,16 +1552,18 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {      if (u->sink) {          inputs = pa_sink_move_all_start(u->sink); -        pa_sink_unlink(u->sink); +	if (!USE_SCO_OVER_PCM(u)) +            pa_sink_unlink(u->sink);      }      if (u->source) {          outputs = pa_source_move_all_start(u->source); -        pa_source_unlink(u->source); +	if (!USE_SCO_OVER_PCM(u)) +            pa_source_unlink(u->source);      }      stop_thread(u); -    shutdown_bt(u); +    init_bt(u);      if (u->write_memchunk.memblock) {          pa_memblock_unref(u->write_memchunk.memblock); @@ -1428,6 +1571,14 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {      }      u->profile = *d; + +    /* Reinitialize the sample spec to default with module argument rate */ +    u->sample_spec = u->module->core->default_sample_spec; +    if (pa_modargs_get_value_u32(u->modargs, "rate", &u->sample_spec.rate) < 0 || +        u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) { +        u->sample_spec = u->module->core->default_sample_spec; +    } +      init_profile(u);      if (u->sink || u->source) @@ -1605,6 +1756,18 @@ int pa__init(pa_module* m) {      u->sample_spec = m->core->default_sample_spec;      u->modargs = ma; +    if (pa_modargs_get_value(ma, "sco_sink", NULL) && +        !(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) { +        pa_log("SCO sink not found"); +        goto fail; +    } + +    if (pa_modargs_get_value(ma, "sco_source", NULL) && +        !(u->hsp.sco_source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_source", NULL), PA_NAMEREG_SOURCE))) { +        pa_log("SCO source not found"); +        goto fail; +    } +      if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 ||          u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {          pa_log_error("Failed to get rate from module arguments"); @@ -1705,10 +1868,10 @@ void pa__done(pa_module *m) {      if (!(u = m->userdata))          return; -    if (u->sink) +    if (u->sink && !USE_SCO_OVER_PCM(u))          pa_sink_unlink(u->sink); -    if (u->source) +    if (u->source && !USE_SCO_OVER_PCM(u))          pa_source_unlink(u->source);      stop_thread(u); diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index f9540b32..3f8a7511 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -42,15 +42,20 @@  PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");  PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");  PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_USAGE("async=<Asynchronous initialization?>"); +PA_MODULE_USAGE("sco_sink=<name of sink> " +		"sco_source=<name of source>" +		"async=<Asynchronous initialization?>");  static const char* const valid_modargs[] = { +    "sco_sink", +    "sco_source",      "async",      NULL  };  struct userdata {      pa_module *module; +    pa_modargs *ma;      pa_core *core;      pa_dbus_connection *connection;      pa_bluetooth_discovery *discovery; @@ -71,6 +76,16 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p              /* Oh, awesome, a new device has shown up and been connected! */              args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path); + +            if (pa_modargs_get_value(u->ma, "sco_sink", NULL) && +                pa_modargs_get_value(u->ma, "sco_source", NULL)) { +                char *tmp; + +                tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args, pa_modargs_get_value(u->ma, "sco_sink", NULL), pa_modargs_get_value(u->ma, "sco_source", NULL)); +                pa_xfree(args); +                args = tmp; +            } +              pa_log_debug("Loading module-bluetooth-device %s", args);              m = pa_module_load(u->module->core, "module-bluetooth-device", args);              pa_xfree(args); @@ -123,13 +138,14 @@ int pa__init(pa_module* m) {      }      if (pa_modargs_get_value_boolean(ma, "async", &async) < 0) { -        pa_log("Failed to parse tsched argument."); +        pa_log("Failed to parse async argument.");          goto fail;      }      m->userdata = u = pa_xnew0(struct userdata, 1);      u->module = m;      u->core = m->core; +    u->ma = ma;      if (setup_dbus(u) < 0)          goto fail; @@ -162,5 +178,8 @@ void pa__done(pa_module* m) {      if (u->connection)          pa_dbus_connection_unref(u->connection); +    if (u->ma) +        pa_modargs_free(u->ma); +      pa_xfree(u);  } diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index 5e5e53e7..22d49f76 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -174,6 +174,9 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu      pa_source_output_assert_ref(s);      pa_assert(u); +    if (!s->source) +        return PA_HOOK_OK; +      if (pa_source_check_suspend(s->source) <= 0) {          struct device_info *d;          if ((d = pa_hashmap_get(u->device_infos, s->source))) | 
