bluetooth: SCO over PCM
authorMarc-André Lureau <marcandre.lureau@gmail.com>
Tue, 3 Feb 2009 15:15:41 +0000 (17:15 +0200)
committerMarc-André Lureau <marcandre.lureau@gmail.com>
Mon, 9 Feb 2009 20:14:45 +0000 (22:14 +0200)
src/modules/bluetooth/module-bluetooth-device.c
src/modules/bluetooth/module-bluetooth-discover.c

index 0067d91..2302b1d 100644 (file)
@@ -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,11 @@ struct a2dp_info {
     uint16_t seq_num;                    /* Cumulative packet sequence */
 };
 
+struct hsp_info {
+    pa_sink *sco_sink;
+    pa_source *sco_source;
+};
+
 enum profile {
     PROFILE_A2DP,
     PROFILE_HSP,
@@ -132,6 +142,7 @@ struct userdata {
     size_t block_size;
 
     struct a2dp_info a2dp;
+    struct hsp_info hsp;
     pa_dbus_connection *connection;
 
     enum profile profile;
@@ -1215,67 +1226,97 @@ 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 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;
+    } 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_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)) {
+        pa_proplist *p;
 
-    u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
-    pa_source_new_data_done(&data);
+        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);
 
-    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);
-
     return 0;
 }
 
@@ -1318,6 +1359,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;
 
@@ -1377,6 +1423,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;
@@ -1410,12 +1462,14 @@ 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);
@@ -1604,6 +1658,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");
@@ -1704,10 +1770,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);
index 0523648..3f8a751 100644 (file)
 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);
@@ -130,6 +145,7 @@ int pa__init(pa_module* m) {
     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);
 }