[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.12-67-g4ae124b
Lennart Poettering
gitmailer-noreply at 0pointer.de
Wed Sep 10 15:13:10 PDT 2008
This is an automated email from the git hooks/post-receive script. It was
generated because of a push to the "PulseAudio Sound Server" repository.
The master branch has been updated
from 2ab4bb76b217e481cf28cff0138726d9a7672895 (commit)
- Log -----------------------------------------------------------------
4ae124b... Move bluetooth proximity module to src/modules/bluetooth/
8b02c2f... Change all int vars that doesn't allow negative values to uint
78a3c72... Move bluetooth discover and device modules to src/modules/bluetooth
76bae38... Cleanup some code
6093e32... Remove some warnings
02a9273... Free mempool
447e027... Fix "file not found" error on load of module-bt-device for Ubuntu Intrepid Ibex
8769bf4... Merge A2DP and SCO thread functions
199bdf2... Add some more device properties to the sink properties list
e2f3a86... Remove check for SIOCOUTQ and add proper includes
dc4f796... Use union instead of different pointer types to the same memory area to make the code C99 compliant
d1cc632... Move render and write to the fd to a separate function
88a21e9... Change MIN/MAX to PA_MIN/PA_MAX
27bc1ea... Remove unnecessary initialization of getcaps_req->flags
027940b... Remove u->channels and u->rates, since it's redundant info
0e81757... Fix some memory leaking
e752cac... Change sbc_initialized to pa_bool_t
708905c... pa__done for module-bt-device
61013fb... Fix some debug messages and other cosmetic changes
e570767... Refactor a2dp thread execution flow and improve time estimation
2f455bf... A2DP poorly working
c89301d... Fix sample size
e545479... Fix block_size calculation
b5c4d2e... Configure bt connection for a2dp
85a931f... Get rid of hw_constraint function. It's code now lives inside bt_setconf().
77138dd... Change default sink name to bluetooth_sink
6c10b10... Try to improve time estimation
123ba4f... Fix handling of PA_SINK_MESSAGE_GET_LATENCY
0d37b91... Remove PA_SINK_NETWORK flag and move the passage of streamfd to the rt thread just before the thread creation
435eb07... Change pa_sink_render to pa_sink_render_into_full and remove some unnecessary checks on the rt thread
2e51b93... Make stream socket non-blocking
71f1d68... Fix block size for SCO
fcd7dc1... Add include for core-util.h
eb1e308... Initialize rtpoll_item
0519e5d... Add include for sample.h
d48961f... Change close() to pa_close()
b4ded21... Change strerror() to pa_cstrerror()
16d5aab... Get rid of SINK_MESSAGE_PASS_SOCKET, since we don't really need it
aa310a4... Changes for pa_modargs_get_value_u32 for integer arguments and correct some error messages
0396a60... Copy arguments values instead of just getting a pointer for them
f992296... Hand the stream socket to the rt thread
255f9b0... Initial code for rt thread
b8b761a... Fix PA_USEC_PER_SEC missing
a3f0756... BlueZ connection configuration done
e7b0839... Adds SBC Codec to pa tree
c62c2ff... Add module-bt-device and dependencies to automake
ee68292... Initial file for module-bt-device
d8a0ec5... Add code from bluez/audio/ipc.[ch]
ffe76a2... Add sender=org.bluez to dbus add match
fe8bd53... Remove modargs, since module-bt-discover doesn't have any argument
1e03c32... Refactor all linked lists to use pulsecore/llist.h
d893a1f... Remove block delimiters from single line if blocks
cadc666... Remove some unused vars and labels
a69c020... Change booleans to pa_bool_t
d90bb18... We don't need call_dbus_method anymore
9907b46... Don't need to explicity check if hcid is running anymore
2b68562... Improve dbus communication
e5d25e0... Changing all private functions to static
3909d9b... Remove VERBOSE definition
c9f5659... Adding dynamic bluetooth audio devices detection
314dade... Fix the symdef include
6fccd58... Fix comparison of strings of different case
9d18b90... Adding module-bt-discover to Makefile.am
8b511f5... Adding module-bt-discover
-----------------------------------------------------------------------
Summary of changes:
src/Makefile.am | 56 +-
src/modules/bluetooth/ipc.c | 119 ++
src/modules/bluetooth/ipc.h | 308 +++++
src/modules/bluetooth/module-bluetooth-device.c | 950 +++++++++++++
src/modules/bluetooth/module-bluetooth-discover.c | 532 ++++++++
.../module-bluetooth-proximity.c} | 2 +-
.../proximity-helper.c} | 0
src/modules/bluetooth/rtp.h | 76 ++
src/modules/bluetooth/sbc.c | 1411 ++++++++++++++++++++
src/modules/bluetooth/sbc.h | 97 ++
src/modules/bluetooth/sbc_math.h | 72 +
src/modules/bluetooth/sbc_tables.h | 167 +++
12 files changed, 3775 insertions(+), 15 deletions(-)
create mode 100644 src/modules/bluetooth/ipc.c
create mode 100644 src/modules/bluetooth/ipc.h
create mode 100644 src/modules/bluetooth/module-bluetooth-device.c
create mode 100644 src/modules/bluetooth/module-bluetooth-discover.c
rename src/modules/{module-bt-proximity.c => bluetooth/module-bluetooth-proximity.c} (99%)
rename src/modules/{bt-proximity-helper.c => bluetooth/proximity-helper.c} (100%)
create mode 100644 src/modules/bluetooth/rtp.h
create mode 100644 src/modules/bluetooth/sbc.c
create mode 100644 src/modules/bluetooth/sbc.h
create mode 100644 src/modules/bluetooth/sbc_math.h
create mode 100644 src/modules/bluetooth/sbc_tables.h
-----------------------------------------------------------------------
commit 8b511f55985c4c881f34a2bd625450dee3dc7c4a
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 17 16:29:49 2008 -0300
Adding module-bt-discover
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
new file mode 100644
index 0000000..f855ad1
--- /dev/null
+++ b/src/modules/module-bt-discover.c
@@ -0,0 +1,461 @@
+/* TODO LIST
+ * listen to signals AdapterAdded, AdapterRemoved, DeviceCreated and DeviceRemoved
+ * listen to org.freedesktop.DBus.NameOwnerChanged, to properly handle hcid activity
+ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+#include "dbus-util.h"
+/* TODO: Create symdef file for static linking
+ * #include "module-bt-proximity-symdef.h" */
+
+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("");
+
+#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device"
+#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB"
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
+#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB"
+#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB"
+#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB"
+#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB"
+#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
+#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
+#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB"
+#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB"
+
+#define VERBOSE 1
+
+typedef struct adapter adapter_t;
+typedef struct device device_t;
+typedef struct uuid uuid_t;
+
+struct uuid {
+ char *uuid;
+ uuid_t *next;
+};
+
+struct device {
+ char *name;
+ char *object_path;
+ int paired;
+ adapter_t *adapter;
+ char *alias;
+ int connected;
+ uuid_t *uuid_list;
+ char *address;
+ int class;
+ int trusted;
+ device_t *next;
+};
+
+struct adapter {
+ char *object_path;
+ char *mode;
+ char *address;
+ device_t *device_list;
+ adapter_t *next;
+};
+
+struct userdata {
+ pa_module *module;
+ pa_dbus_connection *conn;
+ adapter_t *adapter_list;
+};
+
+uuid_t *uuid_new(const char *uuid) {
+ uuid_t *node = pa_xnew0(uuid_t, 1);
+ node->uuid = pa_xstrdup(uuid);
+ node->next = NULL;
+ return node;
+}
+
+void uuid_list_append(uuid_t *node, const char *uuid) {
+ while (node->next != NULL) node = node->next;
+ node->next = uuid_new(uuid);
+}
+
+device_t *device_new(const char *device, adapter_t *adapter) {
+ device_t *node = pa_xnew0(device_t, 1);
+ node->name = NULL;
+ node->object_path = pa_xstrdup(device);
+ node->paired = -1;
+ node->adapter = adapter;
+ node->alias = NULL;
+ node->connected = -1;
+ node->uuid_list = uuid_new("UUID_HEAD");
+ node->address = NULL;
+ node->class = -1;
+ node->trusted = -1;
+ node->next = NULL;
+ return node;
+}
+
+void device_list_append(device_t *node, const char *device, adapter_t *adapter) {
+ while (node->next != NULL) node = node->next;
+ node->next = device_new(device, adapter);
+}
+
+adapter_t *adapter_new(const char *adapter) {
+ adapter_t *node = pa_xnew0(adapter_t, 1);
+ node->object_path = pa_xstrdup(adapter);
+ node->mode = NULL;
+ node->address = NULL;
+ node->device_list = device_new("/DEVICE_HEAD", NULL);
+ node->next = NULL;
+ return node;
+}
+
+void adapter_list_append(adapter_t *node, const char *adapter) {
+ while (node->next != NULL) node = node->next;
+ node->next = adapter_new(adapter);
+}
+
+void print_devices(device_t *device_list) {
+ device_t *device_list_i = device_list;
+ while (device_list_i != NULL) {
+ uuid_t *uuid_list_i = device_list_i->uuid_list;
+ if (strcmp(device_list_i->object_path, "/DEVICE_HEAD") != 0) {
+ pa_log(" [ %s ]", device_list_i->object_path);
+ pa_log(" Name = %s", device_list_i->name);
+ pa_log(" Paired = %d", device_list_i->paired);
+ pa_log(" Adapter = %s", device_list_i->adapter->object_path);
+ pa_log(" Alias = %s", device_list_i->alias);
+ pa_log(" Connected = %d", device_list_i->connected);
+ pa_log(" UUIDs = ");
+ while (uuid_list_i != NULL) {
+ if (strcmp(uuid_list_i->uuid, "UUID_HEAD") != 0) {
+ pa_log(" %s", uuid_list_i->uuid);
+ }
+ uuid_list_i = uuid_list_i->next;
+ }
+ pa_log(" Address = %s", device_list_i->address);
+ pa_log(" Class = 0x%x", device_list_i->class);
+ pa_log(" Trusted = %d", device_list_i->trusted);
+ }
+ device_list_i = device_list_i->next;
+ }
+}
+
+void print_adapters(adapter_t *adapter_list) {
+ adapter_t *adapter_list_i = adapter_list;
+ while (adapter_list_i != NULL) {
+ if (strcmp(adapter_list_i->object_path, "/ADAPTER_HEAD") != 0) {
+ pa_log("[ %s ]", adapter_list_i->object_path);
+ pa_log(" Mode = %s", adapter_list_i->mode);
+ pa_log(" Address = %s", adapter_list_i->address);
+ print_devices(adapter_list_i->device_list);
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+}
+
+DBusMessageIter call_dbus_method(pa_dbus_connection *conn, const char *destination, const char *path, const char *interface,
+ const char *method) {
+ DBusMessage *msg;
+ DBusPendingCall *pending;
+ DBusMessageIter args;
+
+ /* construct the DBusMessage */
+ msg = dbus_message_new_method_call(destination, path, interface, method);
+
+ /* send the message and get a handle for a reply */
+ if (!dbus_connection_send_with_reply(pa_dbus_connection_get(conn), msg, &pending, -1)) {
+ pa_log("Out Of Memory!");
+ }
+ if (pending == NULL) {
+ pa_log("Pending Call Null");
+ }
+ dbus_connection_flush(pa_dbus_connection_get(conn));
+
+ /* free msg */
+ dbus_message_unref(msg);
+
+ /* wait for reply */
+ dbus_pending_call_block(pending);
+
+ /* get the reply */
+ msg = dbus_pending_call_steal_reply(pending);
+ if (msg == NULL) {
+ pa_log("Reply Null");
+ }
+
+ /* free pending */
+ dbus_pending_call_unref(pending);
+
+ /* read the reply */
+ if (!dbus_message_iter_init(msg, &args))
+ pa_log("Reply has no arguments");
+
+ dbus_message_unref(msg);
+
+ return args;
+
+}
+
+void detect_adapters(adapter_t *adapter_list, pa_dbus_connection *conn) {
+ DBusMessageIter arg_i, element_i, dict_i, variant_i;
+ adapter_t *adapter_list_i;
+ const char *key, *value;
+
+ /* get adapters */
+ arg_i = call_dbus_method(conn, "org.bluez", "/", "org.bluez.Manager", "ListAdapters");
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
+ dbus_message_iter_get_basic(&element_i, &value);
+ adapter_list_append(adapter_list, value);
+ }
+ dbus_message_iter_next(&element_i);
+ }
+
+ /* get adapter properties */
+ adapter_list_i = adapter_list->next;
+ while (adapter_list_i != NULL) {
+ arg_i = call_dbus_method(conn, "org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "GetProperties");
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(&element_i, &dict_i);
+ dbus_message_iter_get_basic(&dict_i, &key);
+ dbus_message_iter_next(&dict_i);
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+ dbus_message_iter_get_basic(&variant_i, &value);
+ if (strcmp(key, "Mode") == 0) {
+ adapter_list_i->mode = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Address") == 0) {
+ adapter_list_i->address = pa_xstrdup(value);
+ }
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+}
+
+void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
+ DBusMessageIter arg_i, element_i, dict_i, variant_i;
+ adapter_t *adapter_list_i;
+ device_t *device_list_i, *device_list_prev_i;
+ const char *key, *value;
+ unsigned int uvalue;
+
+ /* get devices of each adapter */
+ adapter_list_i = adapter_list->next;
+ while (adapter_list_i != NULL) {
+ arg_i = call_dbus_method(conn, "org.bluez", adapter_list_i->object_path , "org.bluez.Adapter", "ListDevices");
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
+ dbus_message_iter_get_basic(&element_i, &value);
+ device_list_append(adapter_list_i->device_list, value, adapter_list_i);
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+
+ /* get device properties */
+ adapter_list_i = adapter_list->next;
+ while (adapter_list_i != NULL) {
+ device_list_prev_i = adapter_list_i->device_list;
+ device_list_i = adapter_list_i->device_list->next;
+ while (device_list_i != NULL) {
+ arg_i = call_dbus_method(conn, "org.bluez", device_list_i->object_path, "org.bluez.Device", "GetProperties");
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(&element_i, &dict_i);
+ dbus_message_iter_get_basic(&dict_i, &key);
+ dbus_message_iter_next(&dict_i);
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+ if (strcmp(key, "Name") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &value);
+ device_list_i->name = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Paired") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->paired = uvalue;
+ }
+ else if (strcmp(key, "Alias") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &value);
+ device_list_i->alias = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Connected") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->connected = uvalue;
+ }
+ else if (strcmp(key, "UUIDs") == 0) {
+ DBusMessageIter uuid_i;
+ int is_audio_device = 0;
+ dbus_message_iter_recurse(&variant_i, &uuid_i);
+ while (dbus_message_iter_get_arg_type(&uuid_i) != DBUS_TYPE_INVALID) {
+ dbus_message_iter_get_basic(&uuid_i, &value);
+ if ( (strcmp(value, HSP_HS_UUID) == 0) || (strcmp(value, HFP_HS_UUID) == 0) ||
+ (strcmp(value, A2DP_SOURCE_UUID) == 0) || (strcmp(value, A2DP_SINK_UUID) == 0) )
+ is_audio_device = 1;
+ uuid_list_append(device_list_i->uuid_list, value);
+ dbus_message_iter_next(&uuid_i);
+ }
+ if (!is_audio_device) {
+ /* remove current device */
+ device_list_prev_i->next = device_list_i->next;
+ pa_xfree(device_list_i);
+ break;
+ }
+ }
+ else if (strcmp(key, "Address") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &value);
+ device_list_i->address = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Class") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->class = uvalue;
+ }
+ else if (strcmp(key, "Trusted") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->trusted = uvalue;
+ }
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ device_list_prev_i = device_list_prev_i->next;
+ if (device_list_prev_i == NULL)
+ device_list_i = NULL;
+ else
+ device_list_i = device_list_prev_i->next;
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+}
+
+void pa__done(pa_module* m) {
+ struct userdata *u;
+ adapter_t *adapter_list_i, *adapter_list_next_i;
+ device_t *device_list_i, *device_list_next_i;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if ((adapter_list_i = u->adapter_list) != NULL) {
+ while ((adapter_list_next_i = adapter_list_i->next) != NULL) {
+ if ((device_list_i = adapter_list_i->device_list) != NULL) {
+ while ((device_list_next_i = device_list_i->next) != NULL) {
+ pa_xfree(device_list_i);
+ device_list_i = device_list_next_i;
+ }
+ pa_xfree(device_list_i);
+ }
+ pa_xfree(adapter_list_i);
+ adapter_list_i = adapter_list_next_i;
+ }
+ pa_xfree(adapter_list_i);
+ }
+
+ pa_dbus_connection_unref(u->conn);
+ pa_xfree(u);
+ pa_log("Unloading module-bt-discover");
+ return;
+}
+
+int pa__init(pa_module* m) {
+ pa_modargs *ma = NULL;
+ DBusError err;
+ DBusMessageIter arg_i, element_i;
+ adapter_t *adapter_list_i;
+ device_t *device_list_i;
+ const char *value;
+ unsigned int hcid_running = 0;
+ struct userdata *u;
+
+ pa_assert(m);
+ dbus_error_init(&err);
+ pa_log("Loading module-bt-discover");
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+
+ /* connect to the bus */
+ u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
+ if ( dbus_error_is_set(&err) || (u->conn == NULL) ) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ goto fail;
+ }
+
+ /* check if hcid is running */
+ arg_i = call_dbus_method(u->conn, "org.freedesktop.DBus", "/org/freedesktop/DBus" , "org.freedesktop.DBus", "ListNames");
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&element_i, &value);
+ if (strcmp(value, "org.bluez") == 0)
+ hcid_running = 1;
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ if (!hcid_running) {
+ pa_log("hcid not running");
+ goto fail;
+ }
+
+ /* static detection of bluetooth devices */
+ u->adapter_list = adapter_new("/ADAPTER_HEAD");
+ detect_adapters(u->adapter_list, u->conn);
+ detect_devices(u->adapter_list, u->conn);
+
+ if (VERBOSE) print_adapters(u->adapter_list);
+
+ /* load device modules */
+ adapter_list_i = u->adapter_list->next;
+ while (adapter_list_i != NULL) {
+ device_list_i = adapter_list_i->device_list->next;
+ while (device_list_i != NULL) {
+ pa_log("Loading module-bt-device for %s", device_list_i->name); /* CHECK: Should it be name or alias? */
+ /* call module */
+ device_list_i = device_list_i->next;
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+ dbus_error_free(&err);
+ pa__done(m);
+ return -1;
+}
commit 9d18b90d05712d751f82b03ade51a2fca4ea10b2
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 17 16:30:53 2008 -0300
Adding module-bt-discover to Makefile.am
diff --git a/src/Makefile.am b/src/Makefile.am
index 1663d66..ae0fca0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1160,7 +1160,8 @@ endif
if HAVE_BLUEZ
modlibexec_LTLIBRARIES += \
- module-bt-proximity.la
+ module-bt-proximity.la \
+ module-bt-discover.la
pulselibexec_PROGRAMS += \
bt-proximity-helper
@@ -1220,6 +1221,7 @@ SYMDEF_FILES = \
modules/module-suspend-on-idle-symdef.h \
modules/module-hal-detect-symdef.h \
modules/module-bt-proximity-symdef.h \
+ modules/module-bt-discover-symdef.h \
modules/gconf/module-gconf-symdef.h \
modules/module-position-event-sounds-symdef.h \
modules/module-console-kit-symdef.h
@@ -1560,6 +1562,12 @@ bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+# Bluetooth discover
+module_bt_discover_la_SOURCES = modules/module-bt-discover.c
+module_bt_discover_la_LDFLAGS = -module -avoid-version
+module_bt_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bt_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
###################################
# Some minor stuff #
###################################
commit 6fccd5828ac7971216bdf43ded4f76960bf98516
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 17 16:56:03 2008 -0300
Fix comparison of strings of different case
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index f855ad1..5c3cc1b 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -1,8 +1,3 @@
-/* TODO LIST
- * listen to signals AdapterAdded, AdapterRemoved, DeviceCreated and DeviceRemoved
- * listen to org.freedesktop.DBus.NameOwnerChanged, to properly handle hcid activity
- */
-
/***
This file is part of PulseAudio.
@@ -35,25 +30,17 @@
#include <pulsecore/modargs.h>
#include "dbus-util.h"
-/* TODO: Create symdef file for static linking
- * #include "module-bt-proximity-symdef.h" */
+#include "module-bt-proximity-symdef.h"
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("");
-#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device"
-#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB"
#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
-#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB"
#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB"
-#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB"
-#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB"
#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
-#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB"
-#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB"
#define VERBOSE 1
@@ -323,8 +310,8 @@ void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
dbus_message_iter_recurse(&variant_i, &uuid_i);
while (dbus_message_iter_get_arg_type(&uuid_i) != DBUS_TYPE_INVALID) {
dbus_message_iter_get_basic(&uuid_i, &value);
- if ( (strcmp(value, HSP_HS_UUID) == 0) || (strcmp(value, HFP_HS_UUID) == 0) ||
- (strcmp(value, A2DP_SOURCE_UUID) == 0) || (strcmp(value, A2DP_SINK_UUID) == 0) )
+ if ( (strcasecmp(value, HSP_HS_UUID) == 0) || (strcasecmp(value, HFP_HS_UUID) == 0) ||
+ (strcasecmp(value, A2DP_SOURCE_UUID) == 0) || (strcasecmp(value, A2DP_SINK_UUID) == 0) )
is_audio_device = 1;
uuid_list_append(device_list_i->uuid_list, value);
dbus_message_iter_next(&uuid_i);
commit 314dadeade9046167f6c47cde2719b805de523c0
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Jul 18 11:02:59 2008 -0300
Fix the symdef include
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 5c3cc1b..4f862f9 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -30,7 +30,7 @@
#include <pulsecore/modargs.h>
#include "dbus-util.h"
-#include "module-bt-proximity-symdef.h"
+#include "module-bt-discover-symdef.h"
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
commit c9f56593e37a27e68d2fdfb26b3a48043dd072cb
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Jul 21 09:42:29 2008 -0300
Adding dynamic bluetooth audio devices detection
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 4f862f9..f6a9736 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -348,6 +348,69 @@ void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
}
}
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
+ DBusMessageIter arg_i;
+ DBusError err;
+ const char *value;
+ struct userdata *u;
+
+ pa_assert(bus);
+ pa_assert(msg);
+ pa_assert(userdata);
+ u = userdata;
+ dbus_error_init(&err);
+
+ pa_log("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(msg),
+ dbus_message_get_path(msg),
+ dbus_message_get_member(msg));
+
+ if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterAdded")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: adapter %s added", value);
+ }
+ }
+ else if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterRemoved")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: adapter %s removed", value);
+ }
+ }
+ else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceCreated")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: device %s created", value);
+ }
+ }
+ else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: device %s removed", value);
+ }
+ }
+
+finish:
+ dbus_error_free(&err);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
void pa__done(pa_module* m) {
struct userdata *u;
adapter_t *adapter_list_i, *adapter_list_next_i;
@@ -418,7 +481,7 @@ int pa__init(pa_module* m) {
goto fail;
}
- /* static detection of bluetooth devices */
+ /* static detection of bluetooth audio devices */
u->adapter_list = adapter_new("/ADAPTER_HEAD");
detect_adapters(u->adapter_list, u->conn);
detect_devices(u->adapter_list, u->conn);
@@ -437,6 +500,25 @@ int pa__init(pa_module* m) {
adapter_list_i = adapter_list_i->next;
}
+ /* dynamic detection of bluetooth audio devices */
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+ dbus_connection_flush(pa_dbus_connection_get(u->conn));
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',interface='org.bluez.Manager'", &err);
+ dbus_connection_flush(pa_dbus_connection_get(u->conn));
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.Manager signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',interface='org.bluez.Adapter'", &err);
+ dbus_connection_flush(pa_dbus_connection_get(u->conn));
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+
return 0;
fail:
commit 3909d9bf90529204c96a9ba70f17582038df6e93
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Jul 21 10:53:20 2008 -0300
Remove VERBOSE definition
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index f6a9736..f8ec2e9 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -42,8 +42,6 @@ PA_MODULE_USAGE("");
#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
-#define VERBOSE 1
-
typedef struct adapter adapter_t;
typedef struct device device_t;
typedef struct uuid uuid_t;
@@ -486,7 +484,7 @@ int pa__init(pa_module* m) {
detect_adapters(u->adapter_list, u->conn);
detect_devices(u->adapter_list, u->conn);
- if (VERBOSE) print_adapters(u->adapter_list);
+ print_adapters(u->adapter_list);
/* load device modules */
adapter_list_i = u->adapter_list->next;
commit e5d25e0542819d14f0ae58c3ac2d481243c0eb32
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Jul 21 11:23:25 2008 -0300
Changing all private functions to static
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index f8ec2e9..f1dcd2e 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -79,19 +79,19 @@ struct userdata {
adapter_t *adapter_list;
};
-uuid_t *uuid_new(const char *uuid) {
+static uuid_t *uuid_new(const char *uuid) {
uuid_t *node = pa_xnew0(uuid_t, 1);
node->uuid = pa_xstrdup(uuid);
node->next = NULL;
return node;
}
-void uuid_list_append(uuid_t *node, const char *uuid) {
+static void uuid_list_append(uuid_t *node, const char *uuid) {
while (node->next != NULL) node = node->next;
node->next = uuid_new(uuid);
}
-device_t *device_new(const char *device, adapter_t *adapter) {
+static device_t *device_new(const char *device, adapter_t *adapter) {
device_t *node = pa_xnew0(device_t, 1);
node->name = NULL;
node->object_path = pa_xstrdup(device);
@@ -107,12 +107,12 @@ device_t *device_new(const char *device, adapter_t *adapter) {
return node;
}
-void device_list_append(device_t *node, const char *device, adapter_t *adapter) {
+static void device_list_append(device_t *node, const char *device, adapter_t *adapter) {
while (node->next != NULL) node = node->next;
node->next = device_new(device, adapter);
}
-adapter_t *adapter_new(const char *adapter) {
+static adapter_t *adapter_new(const char *adapter) {
adapter_t *node = pa_xnew0(adapter_t, 1);
node->object_path = pa_xstrdup(adapter);
node->mode = NULL;
@@ -122,12 +122,12 @@ adapter_t *adapter_new(const char *adapter) {
return node;
}
-void adapter_list_append(adapter_t *node, const char *adapter) {
+static void adapter_list_append(adapter_t *node, const char *adapter) {
while (node->next != NULL) node = node->next;
node->next = adapter_new(adapter);
}
-void print_devices(device_t *device_list) {
+static void print_devices(device_t *device_list) {
device_t *device_list_i = device_list;
while (device_list_i != NULL) {
uuid_t *uuid_list_i = device_list_i->uuid_list;
@@ -153,7 +153,7 @@ void print_devices(device_t *device_list) {
}
}
-void print_adapters(adapter_t *adapter_list) {
+static void print_adapters(adapter_t *adapter_list) {
adapter_t *adapter_list_i = adapter_list;
while (adapter_list_i != NULL) {
if (strcmp(adapter_list_i->object_path, "/ADAPTER_HEAD") != 0) {
@@ -166,7 +166,7 @@ void print_adapters(adapter_t *adapter_list) {
}
}
-DBusMessageIter call_dbus_method(pa_dbus_connection *conn, const char *destination, const char *path, const char *interface,
+static DBusMessageIter call_dbus_method(pa_dbus_connection *conn, const char *destination, const char *path, const char *interface,
const char *method) {
DBusMessage *msg;
DBusPendingCall *pending;
@@ -209,7 +209,7 @@ DBusMessageIter call_dbus_method(pa_dbus_connection *conn, const char *destinati
}
-void detect_adapters(adapter_t *adapter_list, pa_dbus_connection *conn) {
+static void detect_adapters(adapter_t *adapter_list, pa_dbus_connection *conn) {
DBusMessageIter arg_i, element_i, dict_i, variant_i;
adapter_t *adapter_list_i;
const char *key, *value;
@@ -250,7 +250,7 @@ void detect_adapters(adapter_t *adapter_list, pa_dbus_connection *conn) {
}
}
-void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
+static void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
DBusMessageIter arg_i, element_i, dict_i, variant_i;
adapter_t *adapter_list_i;
device_t *device_list_i, *device_list_prev_i;
commit 2b68562323acea24530d938e0a78984c8a73e5ae
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Jul 22 10:07:30 2008 -0300
Improve dbus communication
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index f1dcd2e..6c388a3 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -209,26 +209,57 @@ static DBusMessageIter call_dbus_method(pa_dbus_connection *conn, const char *de
}
-static void detect_adapters(adapter_t *adapter_list, pa_dbus_connection *conn) {
+static void detect_adapters(struct userdata *u) {
+ DBusError e;
+ DBusMessage *m = NULL, *r = NULL;
DBusMessageIter arg_i, element_i, dict_i, variant_i;
adapter_t *adapter_list_i;
const char *key, *value;
+ pa_assert(u);
+ dbus_error_init(&e);
+
/* get adapters */
- arg_i = call_dbus_method(conn, "org.bluez", "/", "org.bluez.Manager", "ListAdapters");
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Manager.ListAdapters failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Manager.ListAdapters reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Manager.ListAdapters argument is not an array");
+ goto fail;
+ }
dbus_message_iter_recurse(&arg_i, &element_i);
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&element_i, &value);
- adapter_list_append(adapter_list, value);
+ adapter_list_append(u->adapter_list, value);
}
dbus_message_iter_next(&element_i);
}
/* get adapter properties */
- adapter_list_i = adapter_list->next;
+ adapter_list_i = u->adapter_list->next;
while (adapter_list_i != NULL) {
- arg_i = call_dbus_method(conn, "org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "GetProperties");
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "GetProperties"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Adapter.GetProperties failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Adapter.GetProperties reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Adapter.GetProperties argument is not an array");
+ goto fail;
+ }
dbus_message_iter_recurse(&arg_i, &element_i);
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
@@ -248,19 +279,44 @@ static void detect_adapters(adapter_t *adapter_list, pa_dbus_connection *conn) {
}
adapter_list_i = adapter_list_i->next;
}
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+ dbus_error_free(&e);
}
-static void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
+static void detect_devices(struct userdata *u) {
+ DBusError e;
+ DBusMessage *m = NULL, *r = NULL;
DBusMessageIter arg_i, element_i, dict_i, variant_i;
adapter_t *adapter_list_i;
device_t *device_list_i, *device_list_prev_i;
const char *key, *value;
unsigned int uvalue;
+ pa_assert(u);
+ dbus_error_init(&e);
+
/* get devices of each adapter */
- adapter_list_i = adapter_list->next;
+ adapter_list_i = u->adapter_list->next;
while (adapter_list_i != NULL) {
- arg_i = call_dbus_method(conn, "org.bluez", adapter_list_i->object_path , "org.bluez.Adapter", "ListDevices");
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "ListDevices"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Adapter.ListDevices failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Adapter.ListDevices reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Adapter.ListDevices argument is not an array");
+ goto fail;
+ }
dbus_message_iter_recurse(&arg_i, &element_i);
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
@@ -273,12 +329,25 @@ static void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
}
/* get device properties */
- adapter_list_i = adapter_list->next;
+ adapter_list_i = u->adapter_list->next;
while (adapter_list_i != NULL) {
device_list_prev_i = adapter_list_i->device_list;
device_list_i = adapter_list_i->device_list->next;
while (device_list_i != NULL) {
- arg_i = call_dbus_method(conn, "org.bluez", device_list_i->object_path, "org.bluez.Device", "GetProperties");
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", device_list_i->object_path, "org.bluez.Device", "GetProperties"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Device.GetProperties reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Device.GetProperties argument is not an array");
+ goto fail;
+ }
dbus_message_iter_recurse(&arg_i, &element_i);
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
@@ -344,6 +413,13 @@ static void detect_devices(adapter_t *adapter_list, pa_dbus_connection *conn) {
}
adapter_list_i = adapter_list_i->next;
}
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+ dbus_error_free(&e);
}
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
@@ -481,8 +557,8 @@ int pa__init(pa_module* m) {
/* static detection of bluetooth audio devices */
u->adapter_list = adapter_new("/ADAPTER_HEAD");
- detect_adapters(u->adapter_list, u->conn);
- detect_devices(u->adapter_list, u->conn);
+ detect_adapters(u);
+ detect_devices(u);
print_adapters(u->adapter_list);
commit 9907b461d486d50b5ae5ace2cc133f482789af5f
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Jul 22 10:08:37 2008 -0300
Don't need to explicity check if hcid is running anymore
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 6c388a3..19c8282 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -235,6 +235,8 @@ static void detect_adapters(struct userdata *u) {
goto fail;
}
dbus_message_iter_recurse(&arg_i, &element_i);
+ // TODO: Review error checking
+ // should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ?
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&element_i, &value);
@@ -318,6 +320,8 @@ static void detect_devices(struct userdata *u) {
goto fail;
}
dbus_message_iter_recurse(&arg_i, &element_i);
+ // TODO: Review error checking
+ // should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ?
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&element_i, &value);
@@ -523,7 +527,6 @@ int pa__init(pa_module* m) {
adapter_t *adapter_list_i;
device_t *device_list_i;
const char *value;
- unsigned int hcid_running = 0;
struct userdata *u;
pa_assert(m);
@@ -539,22 +542,6 @@ int pa__init(pa_module* m) {
goto fail;
}
- /* check if hcid is running */
- arg_i = call_dbus_method(u->conn, "org.freedesktop.DBus", "/org/freedesktop/DBus" , "org.freedesktop.DBus", "ListNames");
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
- dbus_message_iter_get_basic(&element_i, &value);
- if (strcmp(value, "org.bluez") == 0)
- hcid_running = 1;
- }
- dbus_message_iter_next(&element_i);
- }
- if (!hcid_running) {
- pa_log("hcid not running");
- goto fail;
- }
-
/* static detection of bluetooth audio devices */
u->adapter_list = adapter_new("/ADAPTER_HEAD");
detect_adapters(u);
commit d90bb1859a543e3783e1293ada37d5c0545b420b
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Jul 22 10:11:26 2008 -0300
We don't need call_dbus_method anymore
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 19c8282..35d6365 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -166,49 +166,6 @@ static void print_adapters(adapter_t *adapter_list) {
}
}
-static DBusMessageIter call_dbus_method(pa_dbus_connection *conn, const char *destination, const char *path, const char *interface,
- const char *method) {
- DBusMessage *msg;
- DBusPendingCall *pending;
- DBusMessageIter args;
-
- /* construct the DBusMessage */
- msg = dbus_message_new_method_call(destination, path, interface, method);
-
- /* send the message and get a handle for a reply */
- if (!dbus_connection_send_with_reply(pa_dbus_connection_get(conn), msg, &pending, -1)) {
- pa_log("Out Of Memory!");
- }
- if (pending == NULL) {
- pa_log("Pending Call Null");
- }
- dbus_connection_flush(pa_dbus_connection_get(conn));
-
- /* free msg */
- dbus_message_unref(msg);
-
- /* wait for reply */
- dbus_pending_call_block(pending);
-
- /* get the reply */
- msg = dbus_pending_call_steal_reply(pending);
- if (msg == NULL) {
- pa_log("Reply Null");
- }
-
- /* free pending */
- dbus_pending_call_unref(pending);
-
- /* read the reply */
- if (!dbus_message_iter_init(msg, &args))
- pa_log("Reply has no arguments");
-
- dbus_message_unref(msg);
-
- return args;
-
-}
-
static void detect_adapters(struct userdata *u) {
DBusError e;
DBusMessage *m = NULL, *r = NULL;
@@ -235,8 +192,8 @@ static void detect_adapters(struct userdata *u) {
goto fail;
}
dbus_message_iter_recurse(&arg_i, &element_i);
- // TODO: Review error checking
- // should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ?
+ /* TODO: Review error checking
+ * should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ? */
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&element_i, &value);
@@ -320,8 +277,8 @@ static void detect_devices(struct userdata *u) {
goto fail;
}
dbus_message_iter_recurse(&arg_i, &element_i);
- // TODO: Review error checking
- // should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ?
+ /* TODO: Review error checking
+ * should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ? */
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&element_i, &value);
commit a69c020c552e1152d3a321020ea41a3ac3c76e12
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Jul 22 10:37:34 2008 -0300
Change booleans to pa_bool_t
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 35d6365..6d93974 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -28,6 +28,7 @@
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
#include "dbus-util.h"
#include "module-bt-discover-symdef.h"
@@ -334,13 +335,13 @@ static void detect_devices(struct userdata *u) {
}
else if (strcmp(key, "UUIDs") == 0) {
DBusMessageIter uuid_i;
- int is_audio_device = 0;
+ pa_bool_t is_audio_device = FALSE;
dbus_message_iter_recurse(&variant_i, &uuid_i);
while (dbus_message_iter_get_arg_type(&uuid_i) != DBUS_TYPE_INVALID) {
dbus_message_iter_get_basic(&uuid_i, &value);
if ( (strcasecmp(value, HSP_HS_UUID) == 0) || (strcasecmp(value, HFP_HS_UUID) == 0) ||
(strcasecmp(value, A2DP_SOURCE_UUID) == 0) || (strcasecmp(value, A2DP_SINK_UUID) == 0) )
- is_audio_device = 1;
+ is_audio_device = TRUE;
uuid_list_append(device_list_i->uuid_list, value);
dbus_message_iter_next(&uuid_i);
}
commit cadc6660dd6344f142c57fce306bde551f7a8685
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Jul 22 10:48:50 2008 -0300
Remove some unused vars and labels
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 6d93974..9c18a86 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -442,7 +442,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *
}
}
-finish:
dbus_error_free(&err);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -481,10 +480,8 @@ void pa__done(pa_module* m) {
int pa__init(pa_module* m) {
pa_modargs *ma = NULL;
DBusError err;
- DBusMessageIter arg_i, element_i;
adapter_t *adapter_list_i;
device_t *device_list_i;
- const char *value;
struct userdata *u;
pa_assert(m);
commit d893a1ff530cc4fe5c3ad8623ba2eb9f89770914
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Jul 22 10:55:04 2008 -0300
Remove block delimiters from single line if blocks
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 9c18a86..8b662a1 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -141,9 +141,8 @@ static void print_devices(device_t *device_list) {
pa_log(" Connected = %d", device_list_i->connected);
pa_log(" UUIDs = ");
while (uuid_list_i != NULL) {
- if (strcmp(uuid_list_i->uuid, "UUID_HEAD") != 0) {
+ if (strcmp(uuid_list_i->uuid, "UUID_HEAD") != 0)
pa_log(" %s", uuid_list_i->uuid);
- }
uuid_list_i = uuid_list_i->next;
}
pa_log(" Address = %s", device_list_i->address);
@@ -228,12 +227,10 @@ static void detect_adapters(struct userdata *u) {
dbus_message_iter_next(&dict_i);
dbus_message_iter_recurse(&dict_i, &variant_i);
dbus_message_iter_get_basic(&variant_i, &value);
- if (strcmp(key, "Mode") == 0) {
+ if (strcmp(key, "Mode") == 0)
adapter_list_i->mode = pa_xstrdup(value);
- }
- else if (strcmp(key, "Address") == 0) {
+ else if (strcmp(key, "Address") == 0)
adapter_list_i->address = pa_xstrdup(value);
- }
}
dbus_message_iter_next(&element_i);
}
commit 1e03c323cac98528a514d7c605a82b8b7a521a29
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Jul 23 11:12:57 2008 -0300
Refactor all linked lists to use pulsecore/llist.h
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index 8b662a1..b00a9c1 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -29,6 +29,7 @@
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
#include "dbus-util.h"
#include "module-bt-discover-symdef.h"
@@ -43,95 +44,100 @@ PA_MODULE_USAGE("");
#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
-typedef struct adapter adapter_t;
-typedef struct device device_t;
-typedef struct uuid uuid_t;
-
struct uuid {
char *uuid;
- uuid_t *next;
+ PA_LLIST_FIELDS(struct uuid);
};
struct device {
char *name;
char *object_path;
int paired;
- adapter_t *adapter;
+ struct adapter *adapter;
char *alias;
int connected;
- uuid_t *uuid_list;
+ PA_LLIST_HEAD(struct uuid, uuid_list);
char *address;
int class;
int trusted;
- device_t *next;
+ PA_LLIST_FIELDS(struct device);
};
struct adapter {
char *object_path;
char *mode;
char *address;
- device_t *device_list;
- adapter_t *next;
+ PA_LLIST_HEAD(struct device, device_list);
+ PA_LLIST_FIELDS(struct adapter);
};
struct userdata {
pa_module *module;
pa_dbus_connection *conn;
- adapter_t *adapter_list;
+ PA_LLIST_HEAD(struct adapter, adapter_list);
};
-static uuid_t *uuid_new(const char *uuid) {
- uuid_t *node = pa_xnew0(uuid_t, 1);
+static struct uuid *uuid_new(const char *uuid) {
+ struct uuid *node = pa_xnew(struct uuid, 1);
node->uuid = pa_xstrdup(uuid);
- node->next = NULL;
+ PA_LLIST_INIT(struct uuid, node);
return node;
}
-static void uuid_list_append(uuid_t *node, const char *uuid) {
- while (node->next != NULL) node = node->next;
- node->next = uuid_new(uuid);
+static void uuid_free(struct uuid *uuid) {
+ pa_xfree(uuid);
}
-static device_t *device_new(const char *device, adapter_t *adapter) {
- device_t *node = pa_xnew0(device_t, 1);
+static struct device *device_new(const char *device, struct adapter *adapter) {
+ struct device *node = pa_xnew(struct device, 1);
node->name = NULL;
node->object_path = pa_xstrdup(device);
node->paired = -1;
node->adapter = adapter;
node->alias = NULL;
node->connected = -1;
- node->uuid_list = uuid_new("UUID_HEAD");
+ PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
node->address = NULL;
node->class = -1;
node->trusted = -1;
- node->next = NULL;
+ PA_LLIST_INIT(struct device, node);
return node;
}
-static void device_list_append(device_t *node, const char *device, adapter_t *adapter) {
- while (node->next != NULL) node = node->next;
- node->next = device_new(device, adapter);
+static void device_free(struct device *device) {
+ struct uuid *uuid_list_i;
+ while (device->uuid_list) {
+ uuid_list_i = device->uuid_list;
+ PA_LLIST_REMOVE(struct uuid, device->uuid_list, uuid_list_i);
+ uuid_free(uuid_list_i);
+ }
+ pa_xfree(device);
}
-static adapter_t *adapter_new(const char *adapter) {
- adapter_t *node = pa_xnew0(adapter_t, 1);
+static struct adapter *adapter_new(const char *adapter) {
+ struct adapter *node = pa_xnew(struct adapter, 1);
node->object_path = pa_xstrdup(adapter);
node->mode = NULL;
node->address = NULL;
- node->device_list = device_new("/DEVICE_HEAD", NULL);
- node->next = NULL;
+ PA_LLIST_HEAD_INIT(struct device, node->device_list);
+ PA_LLIST_INIT(struct adapter, node);
return node;
}
-static void adapter_list_append(adapter_t *node, const char *adapter) {
- while (node->next != NULL) node = node->next;
- node->next = adapter_new(adapter);
+static void adapter_free(struct adapter *adapter) {
+ struct device *device_list_i;
+ while (adapter->device_list) {
+ device_list_i = adapter->device_list;
+ PA_LLIST_REMOVE(struct device, adapter->device_list, device_list_i);
+ device_free(device_list_i);
+ }
+ pa_xfree(adapter);
}
-static void print_devices(device_t *device_list) {
- device_t *device_list_i = device_list;
+static void print_devices(struct device *device_list) {
+ struct device *device_list_i = device_list;
while (device_list_i != NULL) {
- uuid_t *uuid_list_i = device_list_i->uuid_list;
+ struct uuid *uuid_list_i = device_list_i->uuid_list;
if (strcmp(device_list_i->object_path, "/DEVICE_HEAD") != 0) {
pa_log(" [ %s ]", device_list_i->object_path);
pa_log(" Name = %s", device_list_i->name);
@@ -153,8 +159,8 @@ static void print_devices(device_t *device_list) {
}
}
-static void print_adapters(adapter_t *adapter_list) {
- adapter_t *adapter_list_i = adapter_list;
+static void print_adapters(struct adapter *adapter_list) {
+ struct adapter *adapter_list_i = adapter_list;
while (adapter_list_i != NULL) {
if (strcmp(adapter_list_i->object_path, "/ADAPTER_HEAD") != 0) {
pa_log("[ %s ]", adapter_list_i->object_path);
@@ -170,7 +176,7 @@ static void detect_adapters(struct userdata *u) {
DBusError e;
DBusMessage *m = NULL, *r = NULL;
DBusMessageIter arg_i, element_i, dict_i, variant_i;
- adapter_t *adapter_list_i;
+ struct adapter *adapter_list_i;
const char *key, *value;
pa_assert(u);
@@ -192,18 +198,18 @@ static void detect_adapters(struct userdata *u) {
goto fail;
}
dbus_message_iter_recurse(&arg_i, &element_i);
- /* TODO: Review error checking
- * should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ? */
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
+ struct adapter *node;
dbus_message_iter_get_basic(&element_i, &value);
- adapter_list_append(u->adapter_list, value);
+ node = adapter_new(value);
+ PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
}
dbus_message_iter_next(&element_i);
}
/* get adapter properties */
- adapter_list_i = u->adapter_list->next;
+ adapter_list_i = u->adapter_list;
while (adapter_list_i != NULL) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "GetProperties"));
r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
@@ -249,8 +255,8 @@ static void detect_devices(struct userdata *u) {
DBusError e;
DBusMessage *m = NULL, *r = NULL;
DBusMessageIter arg_i, element_i, dict_i, variant_i;
- adapter_t *adapter_list_i;
- device_t *device_list_i, *device_list_prev_i;
+ struct adapter *adapter_list_i;
+ struct device *device_list_i;
const char *key, *value;
unsigned int uvalue;
@@ -258,7 +264,7 @@ static void detect_devices(struct userdata *u) {
dbus_error_init(&e);
/* get devices of each adapter */
- adapter_list_i = u->adapter_list->next;
+ adapter_list_i = u->adapter_list;
while (adapter_list_i != NULL) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "ListDevices"));
r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
@@ -275,12 +281,12 @@ static void detect_devices(struct userdata *u) {
goto fail;
}
dbus_message_iter_recurse(&arg_i, &element_i);
- /* TODO: Review error checking
- * should this be changed to while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) ? */
while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
+ struct device *node;
dbus_message_iter_get_basic(&element_i, &value);
- device_list_append(adapter_list_i->device_list, value, adapter_list_i);
+ node = device_new(value, adapter_list_i);
+ PA_LLIST_PREPEND(struct device, adapter_list_i->device_list, node);
}
dbus_message_iter_next(&element_i);
}
@@ -288,10 +294,9 @@ static void detect_devices(struct userdata *u) {
}
/* get device properties */
- adapter_list_i = u->adapter_list->next;
+ adapter_list_i = u->adapter_list;
while (adapter_list_i != NULL) {
- device_list_prev_i = adapter_list_i->device_list;
- device_list_i = adapter_list_i->device_list->next;
+ device_list_i = adapter_list_i->device_list;
while (device_list_i != NULL) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", device_list_i->object_path, "org.bluez.Device", "GetProperties"));
r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
@@ -335,16 +340,18 @@ static void detect_devices(struct userdata *u) {
pa_bool_t is_audio_device = FALSE;
dbus_message_iter_recurse(&variant_i, &uuid_i);
while (dbus_message_iter_get_arg_type(&uuid_i) != DBUS_TYPE_INVALID) {
+ struct uuid *node;
dbus_message_iter_get_basic(&uuid_i, &value);
+ node = uuid_new(value);
+ PA_LLIST_PREPEND(struct uuid, device_list_i->uuid_list, node);
if ( (strcasecmp(value, HSP_HS_UUID) == 0) || (strcasecmp(value, HFP_HS_UUID) == 0) ||
(strcasecmp(value, A2DP_SOURCE_UUID) == 0) || (strcasecmp(value, A2DP_SINK_UUID) == 0) )
is_audio_device = TRUE;
- uuid_list_append(device_list_i->uuid_list, value);
dbus_message_iter_next(&uuid_i);
}
if (!is_audio_device) {
- /* remove current device */
- device_list_prev_i->next = device_list_i->next;
+ /* remove the current device from the list */
+ PA_LLIST_REMOVE(struct device, adapter_list_i->device_list, device_list_i);
pa_xfree(device_list_i);
break;
}
@@ -364,11 +371,7 @@ static void detect_devices(struct userdata *u) {
}
dbus_message_iter_next(&element_i);
}
- device_list_prev_i = device_list_prev_i->next;
- if (device_list_prev_i == NULL)
- device_list_i = NULL;
- else
- device_list_i = device_list_prev_i->next;
+ device_list_i = device_list_i->next;
}
adapter_list_i = adapter_list_i->next;
}
@@ -445,27 +448,17 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *
void pa__done(pa_module* m) {
struct userdata *u;
- adapter_t *adapter_list_i, *adapter_list_next_i;
- device_t *device_list_i, *device_list_next_i;
+ struct adapter *adapter_list_i;
pa_assert(m);
if (!(u = m->userdata))
return;
- if ((adapter_list_i = u->adapter_list) != NULL) {
- while ((adapter_list_next_i = adapter_list_i->next) != NULL) {
- if ((device_list_i = adapter_list_i->device_list) != NULL) {
- while ((device_list_next_i = device_list_i->next) != NULL) {
- pa_xfree(device_list_i);
- device_list_i = device_list_next_i;
- }
- pa_xfree(device_list_i);
- }
- pa_xfree(adapter_list_i);
- adapter_list_i = adapter_list_next_i;
- }
- pa_xfree(adapter_list_i);
+ while (u->adapter_list) {
+ adapter_list_i = u->adapter_list;
+ PA_LLIST_REMOVE(struct adapter, u->adapter_list, adapter_list_i);
+ adapter_free(adapter_list_i);
}
pa_dbus_connection_unref(u->conn);
@@ -477,15 +470,16 @@ void pa__done(pa_module* m) {
int pa__init(pa_module* m) {
pa_modargs *ma = NULL;
DBusError err;
- adapter_t *adapter_list_i;
- device_t *device_list_i;
+ struct adapter *adapter_list_i;
+ struct device *device_list_i;
struct userdata *u;
pa_assert(m);
- dbus_error_init(&err);
pa_log("Loading module-bt-discover");
+ dbus_error_init(&err);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
+ PA_LLIST_HEAD_INIT(struct adapter, u->adapter_list);
/* connect to the bus */
u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
@@ -495,19 +489,18 @@ int pa__init(pa_module* m) {
}
/* static detection of bluetooth audio devices */
- u->adapter_list = adapter_new("/ADAPTER_HEAD");
detect_adapters(u);
detect_devices(u);
print_adapters(u->adapter_list);
/* load device modules */
- adapter_list_i = u->adapter_list->next;
+ adapter_list_i = u->adapter_list;
while (adapter_list_i != NULL) {
- device_list_i = adapter_list_i->device_list->next;
+ device_list_i = adapter_list_i->device_list;
while (device_list_i != NULL) {
- pa_log("Loading module-bt-device for %s", device_list_i->name); /* CHECK: Should it be name or alias? */
- /* call module */
+ pa_log("Loading module-bt-device for %s", device_list_i->name);
+ /* TODO: call module */
device_list_i = device_list_i->next;
}
adapter_list_i = adapter_list_i->next;
commit fe8bd53e22e5f8cffe44c2d773e1f0f51360fa28
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 24 14:36:01 2008 -0300
Remove modargs, since module-bt-discover doesn't have any argument
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index b00a9c1..af3448b 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -468,7 +468,6 @@ void pa__done(pa_module* m) {
}
int pa__init(pa_module* m) {
- pa_modargs *ma = NULL;
DBusError err;
struct adapter *adapter_list_i;
struct device *device_list_i;
@@ -511,15 +510,12 @@ int pa__init(pa_module* m) {
pa_log_error("Failed to add filter function");
goto fail;
}
- dbus_connection_flush(pa_dbus_connection_get(u->conn));
dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',interface='org.bluez.Manager'", &err);
- dbus_connection_flush(pa_dbus_connection_get(u->conn));
if (dbus_error_is_set(&err)) {
pa_log_error("Unable to subscribe to org.bluez.Manager signals: %s: %s", err.name, err.message);
goto fail;
}
dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',interface='org.bluez.Adapter'", &err);
- dbus_connection_flush(pa_dbus_connection_get(u->conn));
if (dbus_error_is_set(&err)) {
pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
goto fail;
@@ -528,8 +524,6 @@ int pa__init(pa_module* m) {
return 0;
fail:
- if (ma)
- pa_modargs_free(ma);
dbus_error_free(&err);
pa__done(m);
return -1;
commit ffe76a2daf873e04e39e6a7019f39bbd731b7df2
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 31 16:17:03 2008 -0300
Add sender=org.bluez to dbus add match
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
index af3448b..2b0d6c5 100644
--- a/src/modules/module-bt-discover.c
+++ b/src/modules/module-bt-discover.c
@@ -19,7 +19,9 @@
USA.
***/
+#ifdef HAVE_CONFIG_H
#include <config.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
@@ -510,12 +512,12 @@ int pa__init(pa_module* m) {
pa_log_error("Failed to add filter function");
goto fail;
}
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',interface='org.bluez.Manager'", &err);
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Manager'", &err);
if (dbus_error_is_set(&err)) {
pa_log_error("Unable to subscribe to org.bluez.Manager signals: %s: %s", err.name, err.message);
goto fail;
}
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',interface='org.bluez.Adapter'", &err);
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter'", &err);
if (dbus_error_is_set(&err)) {
pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
goto fail;
commit d8a0ec53da1afa182ab246f8798160ba1047b749
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 31 20:15:32 2008 -0300
Add code from bluez/audio/ipc.[ch]
diff --git a/src/modules/bt-ipc.c b/src/modules/bt-ipc.c
new file mode 100644
index 0000000..e585328
--- /dev/null
+++ b/src/modules/bt-ipc.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "bt-ipc.h"
+
+/* This table contains the string representation for messages */
+static const char *strmsg[] = {
+ "BT_GETCAPABILITIES_REQ",
+ "BT_GETCAPABILITIES_RSP",
+ "BT_SETCONFIGURATION_REQ",
+ "BT_SETCONFIGURATION_RSP",
+ "BT_STREAMSTART_REQ",
+ "BT_STREAMSTART_RSP",
+ "BT_STREAMSTOP_REQ",
+ "BT_STREAMSTOP_RSP",
+ "BT_STREAMSUSPEND_IND",
+ "BT_STREAMRESUME_IND",
+ "BT_CONTROL_REQ",
+ "BT_CONTROL_RSP",
+ "BT_CONTROL_IND",
+ "BT_STREAMFD_IND",
+};
+
+int bt_audio_service_open()
+{
+ int sk;
+ int err;
+ struct sockaddr_un addr = {
+ AF_UNIX, BT_IPC_SOCKET_NAME
+ };
+
+ sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ fprintf(stderr, "%s: connect() failed: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ close(sk);
+ errno = err;
+ return -1;
+ }
+
+ return sk;
+}
+
+int bt_audio_service_close(int sk)
+{
+ return close(sk);
+}
+
+int bt_audio_service_get_data_fd(int sk)
+{
+ char cmsg_b[CMSG_SPACE(sizeof(int))], m;
+ int err, ret;
+ struct iovec iov = { &m, sizeof(m) };
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+
+ memset(&msgh, 0, sizeof(msgh));
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_control = &cmsg_b;
+ msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+ ret = recvmsg(sk, &msgh, 0);
+ if (ret < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ /* Receive auxiliary data in msgh */
+ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS)
+ return (*(int *) CMSG_DATA(cmsg));
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+const char *bt_audio_strmsg(int type)
+{
+ if (type < 0 || type > (sizeof(strmsg) / sizeof(strmsg[0])))
+ return NULL;
+
+ return strmsg[type];
+}
+
diff --git a/src/modules/bt-ipc.h b/src/modules/bt-ipc.h
new file mode 100644
index 0000000..c900fcd
--- /dev/null
+++ b/src/modules/bt-ipc.h
@@ -0,0 +1,308 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/*
+ Message sequence chart of streaming sequence for A2DP transport
+
+ Audio daemon User
+ on snd_pcm_open
+ <--BT_GETCAPABILITIES_REQ
+
+ BT_GETCAPABILITIES_RSP-->
+
+ on snd_pcm_hw_params
+ <--BT_SETCONFIGURATION_REQ
+
+ BT_SETCONFIGURATION_RSP-->
+
+ on snd_pcm_prepare
+ <--BT_STREAMSTART_REQ
+
+ <Moves to streaming state>
+ BT_STREAMSTART_RSP-->
+
+ BT_STREAMFD_IND -->
+
+ < streams data >
+ ..........
+
+ on snd_pcm_drop/snd_pcm_drain
+
+ <--BT_STREAMSTOP_REQ
+
+ <Moves to open state>
+ BT_STREAMSTOP_RSP-->
+
+ on IPC close or appl crash
+ <Moves to idle>
+
+ */
+
+#ifndef BT_AUDIOCLIENT_H
+#define BT_AUDIOCLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#define BT_AUDIO_IPC_PACKET_SIZE 128
+#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
+
+/* Generic message header definition, except for RSP messages */
+typedef struct {
+ uint8_t msg_type;
+} __attribute__ ((packed)) bt_audio_msg_header_t;
+
+/* Generic message header definition, for all RSP messages */
+typedef struct {
+ bt_audio_msg_header_t msg_h;
+ uint8_t posix_errno;
+} __attribute__ ((packed)) bt_audio_rsp_msg_header_t;
+
+/* Messages list */
+#define BT_GETCAPABILITIES_REQ 0
+#define BT_GETCAPABILITIES_RSP 1
+
+#define BT_SETCONFIGURATION_REQ 2
+#define BT_SETCONFIGURATION_RSP 3
+
+#define BT_STREAMSTART_REQ 4
+#define BT_STREAMSTART_RSP 5
+
+#define BT_STREAMSTOP_REQ 6
+#define BT_STREAMSTOP_RSP 7
+
+#define BT_STREAMSUSPEND_IND 8
+#define BT_STREAMRESUME_IND 9
+
+#define BT_CONTROL_REQ 10
+#define BT_CONTROL_RSP 11
+#define BT_CONTROL_IND 12
+
+#define BT_STREAMFD_IND 13
+
+/* BT_GETCAPABILITIES_REQ */
+
+#define BT_CAPABILITIES_TRANSPORT_A2DP 0
+#define BT_CAPABILITIES_TRANSPORT_SCO 1
+#define BT_CAPABILITIES_TRANSPORT_ANY 2
+
+#define BT_CAPABILITIES_ACCESS_MODE_READ 1
+#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2
+#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3
+
+#define BT_FLAG_AUTOCONNECT 1
+
+struct bt_getcapabilities_req {
+ bt_audio_msg_header_t h;
+ char device[18]; /* Address of the remote Device */
+ uint8_t transport; /* Requested transport */
+ uint8_t flags; /* Requested flags */
+} __attribute__ ((packed));
+
+/* BT_GETCAPABILITIES_RSP */
+
+/**
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ */
+
+#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_SBC_SAMPLING_FREQ_48000 1
+
+#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3)
+#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1)
+#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1
+
+#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3)
+#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2)
+#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1)
+#define BT_A2DP_BLOCK_LENGTH_16 1
+
+#define BT_A2DP_SUBBANDS_4 (1 << 1)
+#define BT_A2DP_SUBBANDS_8 1
+
+#define BT_A2DP_ALLOCATION_SNR (1 << 1)
+#define BT_A2DP_ALLOCATION_LOUDNESS 1
+
+#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_MPEG_SAMPLING_FREQ_48000 1
+
+#define BT_MPEG_LAYER_1 (1 << 2)
+#define BT_MPEG_LAYER_2 (1 << 1)
+#define BT_MPEG_LAYER_3 1
+
+typedef struct {
+ uint8_t channel_mode;
+ uint8_t frequency;
+ uint8_t allocation_method;
+ uint8_t subbands;
+ uint8_t block_length;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) sbc_capabilities_t;
+
+typedef struct {
+ uint8_t channel_mode;
+ uint8_t crc;
+ uint8_t layer;
+ uint8_t frequency;
+ uint8_t mpf;
+ uint16_t bitrate;
+} __attribute__ ((packed)) mpeg_capabilities_t;
+
+struct bt_getcapabilities_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+ uint8_t transport; /* Granted transport */
+ sbc_capabilities_t sbc_capabilities; /* A2DP only */
+ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+ uint16_t sampling_rate; /* SCO only */
+} __attribute__ ((packed));
+
+/* BT_SETCONFIGURATION_REQ */
+struct bt_setconfiguration_req {
+ bt_audio_msg_header_t h;
+ char device[18]; /* Address of the remote Device */
+ uint8_t transport; /* Requested transport */
+ uint8_t access_mode; /* Requested access mode */
+ sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field
+ and next one must be filled */
+ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+} __attribute__ ((packed));
+
+/* BT_SETCONFIGURATION_RSP */
+struct bt_setconfiguration_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+ uint8_t transport; /* Granted transport */
+ uint8_t access_mode; /* Granted access mode */
+ uint16_t link_mtu; /* Max length that transport supports */
+} __attribute__ ((packed));
+
+/* BT_STREAMSTART_REQ */
+#define BT_STREAM_ACCESS_READ 0
+#define BT_STREAM_ACCESS_WRITE 1
+#define BT_STREAM_ACCESS_READWRITE 2
+struct bt_streamstart_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTART_RSP */
+struct bt_streamstart_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+} __attribute__ ((packed));
+
+/* BT_STREAMFD_IND */
+/* This message is followed by one byte of data containing the stream data fd
+ as ancilliary data */
+struct bt_streamfd_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTOP_REQ */
+struct bt_streamstop_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTOP_RSP */
+struct bt_streamstop_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSUSPEND_IND */
+struct bt_streamsuspend_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMRESUME_IND */
+struct bt_streamresume_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_CONTROL_REQ */
+
+#define BT_CONTROL_KEY_POWER 0x40
+#define BT_CONTROL_KEY_VOL_UP 0x41
+#define BT_CONTROL_KEY_VOL_DOWN 0x42
+#define BT_CONTROL_KEY_MUTE 0x43
+#define BT_CONTROL_KEY_PLAY 0x44
+#define BT_CONTROL_KEY_STOP 0x45
+#define BT_CONTROL_KEY_PAUSE 0x46
+#define BT_CONTROL_KEY_RECORD 0x47
+#define BT_CONTROL_KEY_REWIND 0x48
+#define BT_CONTROL_KEY_FAST_FORWARD 0x49
+#define BT_CONTROL_KEY_EJECT 0x4A
+#define BT_CONTROL_KEY_FORWARD 0x4B
+#define BT_CONTROL_KEY_BACKWARD 0x4C
+
+struct bt_control_req {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* BT_CONTROL_RSP */
+struct bt_control_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* BT_CONTROL_IND */
+struct bt_control_ind {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* Function declaration */
+
+/* Opens a connection to the audio service: return a socket descriptor */
+int bt_audio_service_open();
+
+/* Closes a connection to the audio service */
+int bt_audio_service_close(int sk);
+
+/* Receives stream data file descriptor : must be called after a
+BT_STREAMFD_IND message is returned */
+int bt_audio_service_get_data_fd(int sk);
+
+/* Human readable message type string */
+const char *bt_audio_strmsg(int type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BT_AUDIOCLIENT_H */
commit ee68292c6165218bf8c488edb38036dae780766a
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 31 20:16:43 2008 -0300
Initial file for module-bt-device
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
new file mode 100644
index 0000000..8690e31
--- /dev/null
+++ b/src/modules/module-bt-device.c
@@ -0,0 +1,154 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+
+ 1. Connects to the BlueZ audio service via one BlueZ specific well known unix socket
+ bluez/audio/ipc: bt_audio_service_open()
+ 2. Configures a connection to the BT device
+ 3. Gets a BT socket fd passed in via the unix socket
+ bluez/audio/ipc: bt_audio_service_get_data_fd()
+ 4. Hands this over to its RT thread.
+ pa_thread_mq
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+#include "dbus-util.h"
+#include "module-bt-device-symdef.h"
+#include "bt-ipc.h"
+
+PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "addr=<address of the device> "
+ "uuid=<the profile that this device will work on>");
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
+
+ const char *addr;
+ const char *uuid;
+};
+
+static const char* const valid_modargs[] = {
+ "addr",
+ "uuid",
+ NULL
+};
+
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
+ int e;
+ pa_log("sending %s", bt_audio_strmsg(msg->msg_type)); /*debug*/
+ if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
+ e = 0;
+ else {
+ e = -errno;
+ pa_log_error("Error sending data to audio service: %s(%d)", strerror(errno), errno);
+ }
+ return e;
+}
+
+static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
+ int err;
+ const char *type;
+
+ pa_log/*_debug*/("trying to receive msg from audio service...");
+ if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
+ type = bt_audio_strmsg(inmsg->msg_type);
+ if (type) {
+ pa_log/*_debug*/("Received %s", type);
+ err = 0;
+ }
+ else {
+ err = -EINVAL;
+ pa_log_error("Bogus message type %d received from audio service", inmsg->msg_type);
+ }
+ }
+ else {
+ err = -errno;
+ pa_log_error("Error receiving data from audio service: %s(%d)", strerror(errno), errno);
+ }
+
+ return err;
+}
+
+static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) {
+ int err = audioservice_recv(sk, rsp_hdr);
+ if (err == 0) {
+ if (rsp_hdr->msg_type != expected_type) {
+ err = -EINVAL;
+ pa_log_error("Bogus message %s received while %s was expected", bt_audio_strmsg(rsp_hdr->msg_type),
+ bt_audio_strmsg(expected_type));
+ }
+ }
+ return err;
+}
+
+int pa__init(pa_module* m) {
+ pa_modargs *ma;
+ struct userdata *u;
+ int sk = -1;
+
+ pa_assert(m);
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log_error("failed to parse module arguments");
+ goto fail;
+ }
+ if (!(u->addr = pa_modargs_get_value(ma, "addr", NULL))) {
+ pa_log_error("failed to parse device address from module arguments");
+ goto fail;
+ }
+ if (!(u->uuid = pa_modargs_get_value(ma, "uuid", NULL))) {
+ pa_log_error("failed to parse device uuid from module arguments");
+ goto fail;
+ }
+ pa_log("Loading module-bt-device for %s, UUID=%s", u->addr, u->uuid);
+
+ /* Connects to the BlueZ audio service via one BlueZ specific well known unix socket */
+ sk = bt_audio_service_open();
+ if (sk <= 0) {
+ pa_log_error("couldn't connect to bluetooth audio service");
+ goto fail;
+ }
+ pa_log("socket to audio service: %d", sk); /*debug*/
+
+ return 0;
+
+fail:
+ pa__done(m);
+ return -1;
+}
+
+void pa__done(pa_module *m) {
+ pa_log("Unloading module-bt-device");
+}
+
commit c62c2ff4ad334baf6fb3a74af2e24f1a48421fbe
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Jul 31 20:17:17 2008 -0300
Add module-bt-device and dependencies to automake
diff --git a/src/Makefile.am b/src/Makefile.am
index ae0fca0..71bddd5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1161,7 +1161,9 @@ endif
if HAVE_BLUEZ
modlibexec_LTLIBRARIES += \
module-bt-proximity.la \
- module-bt-discover.la
+ module-bt-discover.la \
+ libbt-ipc.la \
+ module-bt-device.la
pulselibexec_PROGRAMS += \
bt-proximity-helper
@@ -1222,6 +1224,7 @@ SYMDEF_FILES = \
modules/module-hal-detect-symdef.h \
modules/module-bt-proximity-symdef.h \
modules/module-bt-discover-symdef.h \
+ modules/module-bt-device-symdef.h \
modules/gconf/module-gconf-symdef.h \
modules/module-position-event-sounds-symdef.h \
modules/module-console-kit-symdef.h
@@ -1568,6 +1571,17 @@ module_bt_discover_la_LDFLAGS = -module -avoid-version
module_bt_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
module_bt_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+# Bluetooth device
+libbt_ipc_la_SOURCES = modules/bt-ipc.c modules/bt-ipc.h
+libbt_ipc_la_LDFLAGS = -avoid-version
+libbt_ipc_la_LIBADD = $(AM_LIBADD)
+libbt_ipc_la_CFLAGS = $(AM_CFLAGS)
+
+module_bt_device_la_SOURCES = modules/module-bt-device.c
+module_bt_device_la_LDFLAGS = -module -avoid-version
+module_bt_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbt-ipc.la
+module_bt_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
###################################
# Some minor stuff #
###################################
commit e7b0839d17a0d9c084ee7301e9db651ddd3b5e30
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 11 13:26:10 2008 -0300
Adds SBC Codec to pa tree
diff --git a/src/Makefile.am b/src/Makefile.am
index 71bddd5..055ece5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1163,6 +1163,7 @@ modlibexec_LTLIBRARIES += \
module-bt-proximity.la \
module-bt-discover.la \
libbt-ipc.la \
+ libbt-sbc.la \
module-bt-device.la
pulselibexec_PROGRAMS += \
@@ -1572,6 +1573,11 @@ module_bt_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus
module_bt_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
# Bluetooth device
+libbt_sbc_la_SOURCES = modules/bt-sbc.c modules/bt-sbc.h modules/bt-sbc_tables.h modules/bt-sbc_math.h
+libbt_sbc_la_LDFLAGS = -avoid-version
+libbt_sbc_la_LIBADD = $(AM_LIBADD)
+libbt_sbc_la_CFLAGS = $(AM_CFLAGS)
+
libbt_ipc_la_SOURCES = modules/bt-ipc.c modules/bt-ipc.h
libbt_ipc_la_LDFLAGS = -avoid-version
libbt_ipc_la_LIBADD = $(AM_LIBADD)
@@ -1579,7 +1585,7 @@ libbt_ipc_la_CFLAGS = $(AM_CFLAGS)
module_bt_device_la_SOURCES = modules/module-bt-device.c
module_bt_device_la_LDFLAGS = -module -avoid-version
-module_bt_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbt-ipc.la
+module_bt_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbt-ipc.la libbt-sbc.la
module_bt_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
###################################
diff --git a/src/modules/bt-sbc.c b/src/modules/bt-sbc.c
new file mode 100644
index 0000000..8e7b060
--- /dev/null
+++ b/src/modules/bt-sbc.c
@@ -0,0 +1,1411 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2008 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* todo items:
+
+ use a log2 table for byte integer scale factors calculation (sum log2 results
+ for high and low bytes) fill bitpool by 16 bits instead of one at a time in
+ bits allocation/bitpool generation port to the dsp
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "bt-sbc_math.h"
+#include "bt-sbc_tables.h"
+
+#include "bt-sbc.h"
+
+#define SBC_SYNCWORD 0x9C
+
+/* This structure contains an unpacked SBC frame.
+ Yes, there is probably quite some unused space herein */
+struct sbc_frame {
+ uint8_t frequency;
+ uint8_t block_mode;
+ uint8_t blocks;
+ enum {
+ MONO = SBC_MODE_MONO,
+ DUAL_CHANNEL = SBC_MODE_DUAL_CHANNEL,
+ STEREO = SBC_MODE_STEREO,
+ JOINT_STEREO = SBC_MODE_JOINT_STEREO
+ } mode;
+ uint8_t channels;
+ enum {
+ LOUDNESS = SBC_AM_LOUDNESS,
+ SNR = SBC_AM_SNR
+ } allocation;
+ uint8_t subband_mode;
+ uint8_t subbands;
+ uint8_t bitpool;
+ uint8_t codesize;
+ uint8_t length;
+
+ /* bit number x set means joint stereo has been used in subband x */
+ uint8_t joint;
+
+ /* only the lower 4 bits of every element are to be used */
+ uint8_t scale_factor[2][8];
+
+ /* raw integer subband samples in the frame */
+
+ int32_t sb_sample_f[16][2][8];
+ int32_t sb_sample[16][2][8]; /* modified subband samples */
+ int16_t pcm_sample[2][16*8]; /* original pcm audio samples */
+};
+
+struct sbc_decoder_state {
+ int subbands;
+ int32_t V[2][170];
+ int offset[2][16];
+};
+
+struct sbc_encoder_state {
+ int subbands;
+ int position[2];
+ int32_t X[2][160];
+};
+
+/*
+ * Calculates the CRC-8 of the first len bits in data
+ */
+static const uint8_t crc_table[256] = {
+ 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
+ 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
+ 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
+ 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
+ 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
+ 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
+ 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
+ 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
+ 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
+ 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
+ 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
+ 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
+ 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
+ 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
+ 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
+ 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
+ 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
+ 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
+ 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
+ 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
+ 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
+ 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
+ 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
+ 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
+ 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
+ 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
+ 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
+ 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
+ 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
+ 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
+ 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
+ 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
+};
+
+static uint8_t sbc_crc8(const uint8_t *data, size_t len)
+{
+ uint8_t crc = 0x0f;
+ size_t i;
+ uint8_t octet;
+
+ for (i = 0; i < len / 8; i++)
+ crc = crc_table[crc ^ data[i]];
+
+ octet = data[i];
+ for (i = 0; i < len % 8; i++) {
+ char bit = ((octet ^ crc) & 0x80) >> 7;
+
+ crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
+
+ octet = octet << 1;
+ }
+
+ return crc;
+}
+
+/*
+ * Code straight from the spec to calculate the bits array
+ * Takes a pointer to the frame in question, a pointer to the bits array and
+ * the sampling frequency (as 2 bit integer)
+ */
+static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
+{
+ uint8_t sf = frame->frequency;
+
+ if (frame->mode == MONO || frame->mode == DUAL_CHANNEL) {
+ int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+ int ch, sb;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ max_bitneed = 0;
+ if (frame->allocation == SNR) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ bitneed[ch][sb] = frame->scale_factor[ch][sb];
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ } else {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->scale_factor[ch][sb] == 0)
+ bitneed[ch][sb] = -5;
+ else {
+ if (frame->subbands == 4)
+ loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+ else
+ loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+ if (loudness > 0)
+ bitneed[ch][sb] = loudness / 2;
+ else
+ bitneed[ch][sb] = loudness;
+ }
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+
+ bitcount = 0;
+ slicecount = 0;
+ bitslice = max_bitneed + 1;
+ do {
+ bitslice--;
+ bitcount += slicecount;
+ slicecount = 0;
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+ slicecount++;
+ else if (bitneed[ch][sb] == bitslice + 1)
+ slicecount += 2;
+ }
+ } while (bitcount + slicecount < frame->bitpool);
+
+ if (bitcount + slicecount == frame->bitpool) {
+ bitcount += slicecount;
+ bitslice--;
+ }
+
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (bitneed[ch][sb] < bitslice + 2)
+ bits[ch][sb] = 0;
+ else {
+ bits[ch][sb] = bitneed[ch][sb] - bitslice;
+ if (bits[ch][sb] > 16)
+ bits[ch][sb] = 16;
+ }
+ }
+
+ for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) {
+ if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+ bits[ch][sb]++;
+ bitcount++;
+ } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+ bits[ch][sb] = 2;
+ bitcount += 2;
+ }
+ }
+
+ for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) {
+ if (bits[ch][sb] < 16) {
+ bits[ch][sb]++;
+ bitcount++;
+ }
+ }
+
+ }
+
+ } else if (frame->mode == STEREO || frame->mode == JOINT_STEREO) {
+ int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+ int ch, sb;
+
+ max_bitneed = 0;
+ if (frame->allocation == SNR) {
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ bitneed[ch][sb] = frame->scale_factor[ch][sb];
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+ } else {
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->scale_factor[ch][sb] == 0)
+ bitneed[ch][sb] = -5;
+ else {
+ if (frame->subbands == 4)
+ loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+ else
+ loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+ if (loudness > 0)
+ bitneed[ch][sb] = loudness / 2;
+ else
+ bitneed[ch][sb] = loudness;
+ }
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+ }
+
+ bitcount = 0;
+ slicecount = 0;
+ bitslice = max_bitneed + 1;
+ do {
+ bitslice--;
+ bitcount += slicecount;
+ slicecount = 0;
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+ slicecount++;
+ else if (bitneed[ch][sb] == bitslice + 1)
+ slicecount += 2;
+ }
+ }
+ } while (bitcount + slicecount < frame->bitpool);
+
+ if (bitcount + slicecount == frame->bitpool) {
+ bitcount += slicecount;
+ bitslice--;
+ }
+
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (bitneed[ch][sb] < bitslice + 2) {
+ bits[ch][sb] = 0;
+ } else {
+ bits[ch][sb] = bitneed[ch][sb] - bitslice;
+ if (bits[ch][sb] > 16)
+ bits[ch][sb] = 16;
+ }
+ }
+ }
+
+ ch = 0;
+ sb = 0;
+ while (bitcount < frame->bitpool) {
+ if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+ bits[ch][sb]++;
+ bitcount++;
+ } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+ bits[ch][sb] = 2;
+ bitcount += 2;
+ }
+ if (ch == 1) {
+ ch = 0;
+ sb++;
+ if (sb >= frame->subbands) break;
+ } else
+ ch = 1;
+ }
+
+ ch = 0;
+ sb = 0;
+ while (bitcount < frame->bitpool) {
+ if (bits[ch][sb] < 16) {
+ bits[ch][sb]++;
+ bitcount++;
+ }
+ if (ch == 1) {
+ ch = 0;
+ sb++;
+ if (sb >= frame->subbands) break;
+ } else
+ ch = 1;
+ }
+
+ }
+
+}
+
+/*
+ * Unpacks a SBC frame at the beginning of the stream in data,
+ * which has at most len bytes into frame.
+ * Returns the length in bytes of the packed frame, or a negative
+ * value on error. The error codes are:
+ *
+ * -1 Data stream too short
+ * -2 Sync byte incorrect
+ * -3 CRC8 incorrect
+ * -4 Bitpool value out of bounds
+ */
+static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
+ size_t len)
+{
+ int consumed;
+ /* Will copy the parts of the header that are relevant to crc
+ * calculation here */
+ uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int crc_pos = 0;
+ int32_t temp;
+
+ int audio_sample;
+ int ch, sb, blk, bit; /* channel, subband, block and bit standard
+ counters */
+ int bits[2][8]; /* bits distribution */
+ uint32_t levels[2][8]; /* levels derived from that */
+
+ if (len < 4)
+ return -1;
+
+ if (data[0] != SBC_SYNCWORD)
+ return -2;
+
+ frame->frequency = (data[1] >> 6) & 0x03;
+
+ frame->block_mode = (data[1] >> 4) & 0x03;
+ switch (frame->block_mode) {
+ case SBC_BLK_4:
+ frame->blocks = 4;
+ break;
+ case SBC_BLK_8:
+ frame->blocks = 8;
+ break;
+ case SBC_BLK_12:
+ frame->blocks = 12;
+ break;
+ case SBC_BLK_16:
+ frame->blocks = 16;
+ break;
+ }
+
+ frame->mode = (data[1] >> 2) & 0x03;
+ switch (frame->mode) {
+ case MONO:
+ frame->channels = 1;
+ break;
+ case DUAL_CHANNEL: /* fall-through */
+ case STEREO:
+ case JOINT_STEREO:
+ frame->channels = 2;
+ break;
+ }
+
+ frame->allocation = (data[1] >> 1) & 0x01;
+
+ frame->subband_mode = (data[1] & 0x01);
+ frame->subbands = frame->subband_mode ? 8 : 4;
+
+ frame->bitpool = data[2];
+
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > 16 * frame->subbands)
+ return -4;
+
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > 32 * frame->subbands)
+ return -4;
+
+ /* data[3] is crc, we're checking it later */
+
+ consumed = 32;
+
+ crc_header[0] = data[1];
+ crc_header[1] = data[2];
+ crc_pos = 16;
+
+ if (frame->mode == JOINT_STEREO) {
+ if (len * 8 < consumed + frame->subbands)
+ return -1;
+
+ frame->joint = 0x00;
+ for (sb = 0; sb < frame->subbands - 1; sb++)
+ frame->joint |= ((data[4] >> (7 - sb)) & 0x01) << sb;
+ if (frame->subbands == 4)
+ crc_header[crc_pos / 8] = data[4] & 0xf0;
+ else
+ crc_header[crc_pos / 8] = data[4];
+
+ consumed += frame->subbands;
+ crc_pos += frame->subbands;
+ }
+
+ if (len * 8 < consumed + (4 * frame->subbands * frame->channels))
+ return -1;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ /* FIXME assert(consumed % 4 == 0); */
+ frame->scale_factor[ch][sb] =
+ (data[consumed >> 3] >> (4 - (consumed & 0x7))) & 0x0F;
+ crc_header[crc_pos >> 3] |=
+ frame->scale_factor[ch][sb] << (4 - (crc_pos & 0x7));
+
+ consumed += 4;
+ crc_pos += 4;
+ }
+ }
+
+ if (data[3] != sbc_crc8(crc_header, crc_pos))
+ return -3;
+
+ sbc_calculate_bits(frame, bits);
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++)
+ levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+ }
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (levels[ch][sb] > 0) {
+ audio_sample = 0;
+ for (bit = 0; bit < bits[ch][sb]; bit++) {
+ if (consumed > len * 8)
+ return -1;
+
+ if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
+ audio_sample |= 1 << (bits[ch][sb] - bit - 1);
+
+ consumed++;
+ }
+
+ frame->sb_sample[blk][ch][sb] =
+ (((audio_sample << 1) | 1) << frame->scale_factor[ch][sb]) /
+ levels[ch][sb] - (1 << frame->scale_factor[ch][sb]);
+ } else
+ frame->sb_sample[blk][ch][sb] = 0;
+ }
+ }
+ }
+
+ if (frame->mode == JOINT_STEREO) {
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->joint & (0x01 << sb)) {
+ temp = frame->sb_sample[blk][0][sb] +
+ frame->sb_sample[blk][1][sb];
+ frame->sb_sample[blk][1][sb] =
+ frame->sb_sample[blk][0][sb] -
+ frame->sb_sample[blk][1][sb];
+ frame->sb_sample[blk][0][sb] = temp;
+ }
+ }
+ }
+ }
+
+ if ((consumed & 0x7) != 0)
+ consumed += 8 - (consumed & 0x7);
+
+ return consumed >> 3;
+}
+
+static void sbc_decoder_init(struct sbc_decoder_state *state,
+ const struct sbc_frame *frame)
+{
+ int i, ch;
+
+ memset(state->V, 0, sizeof(state->V));
+ state->subbands = frame->subbands;
+
+ for (ch = 0; ch < 2; ch++)
+ for (i = 0; i < frame->subbands * 2; i++)
+ state->offset[ch][i] = (10 * i + 10);
+}
+
+static inline void sbc_synthesize_four(struct sbc_decoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int i, k, idx;
+ int32_t *v = state->V[ch];
+ int *offset = state->offset[ch];
+
+ for (i = 0; i < 8; i++) {
+ /* Shifting */
+ offset[i]--;
+ if (offset[i] < 0) {
+ offset[i] = 79;
+ memcpy(v + 80, v, 9 * sizeof(*v));
+ }
+
+ /* Distribute the new matrix value to the shifted position */
+ v[offset[i]] = SCALE4_STAGED1(
+ MULA(synmatrix4[i][0], frame->sb_sample[blk][ch][0],
+ MULA(synmatrix4[i][1], frame->sb_sample[blk][ch][1],
+ MULA(synmatrix4[i][2], frame->sb_sample[blk][ch][2],
+ MUL (synmatrix4[i][3], frame->sb_sample[blk][ch][3])))));
+ }
+
+ /* Compute the samples */
+ for (idx = 0, i = 0; i < 4; i++, idx += 5) {
+ k = (i + 4) & 0xf;
+
+ /* Store in output, Q0 */
+ frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED2(
+ MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0],
+ MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0],
+ MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1],
+ MULA(v[offset[k] + 3], sbc_proto_4_40m1[idx + 1],
+ MULA(v[offset[i] + 4], sbc_proto_4_40m0[idx + 2],
+ MULA(v[offset[k] + 5], sbc_proto_4_40m1[idx + 2],
+ MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3],
+ MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3],
+ MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4],
+ MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4])))))))))));
+ }
+}
+
+static inline void sbc_synthesize_eight(struct sbc_decoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int i, j, k, idx;
+ int *offset = state->offset[ch];
+
+ for (i = 0; i < 16; i++) {
+ /* Shifting */
+ offset[i]--;
+ if (offset[i] < 0) {
+ offset[i] = 159;
+ for (j = 0; j < 9; j++)
+ state->V[ch][j + 160] = state->V[ch][j];
+ }
+
+ /* Distribute the new matrix value to the shifted position */
+ state->V[ch][offset[i]] = SCALE8_STAGED1(
+ MULA(synmatrix8[i][0], frame->sb_sample[blk][ch][0],
+ MULA(synmatrix8[i][1], frame->sb_sample[blk][ch][1],
+ MULA(synmatrix8[i][2], frame->sb_sample[blk][ch][2],
+ MULA(synmatrix8[i][3], frame->sb_sample[blk][ch][3],
+ MULA(synmatrix8[i][4], frame->sb_sample[blk][ch][4],
+ MULA(synmatrix8[i][5], frame->sb_sample[blk][ch][5],
+ MULA(synmatrix8[i][6], frame->sb_sample[blk][ch][6],
+ MUL( synmatrix8[i][7], frame->sb_sample[blk][ch][7])))))))));
+ }
+
+ /* Compute the samples */
+ for (idx = 0, i = 0; i < 8; i++, idx += 5) {
+ k = (i + 8) & 0xf;
+
+ /* Store in output */
+ frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED2( // Q0
+ MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0],
+ MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0],
+ MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1],
+ MULA(state->V[ch][offset[k] + 3], sbc_proto_8_80m1[idx + 1],
+ MULA(state->V[ch][offset[i] + 4], sbc_proto_8_80m0[idx + 2],
+ MULA(state->V[ch][offset[k] + 5], sbc_proto_8_80m1[idx + 2],
+ MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3],
+ MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3],
+ MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4],
+ MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4])))))))))));
+ }
+}
+
+static int sbc_synthesize_audio(struct sbc_decoder_state *state,
+ struct sbc_frame *frame)
+{
+ int ch, blk;
+
+ switch (frame->subbands) {
+ case 4:
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_synthesize_four(state, frame, ch, blk);
+ }
+ return frame->blocks * 4;
+
+ case 8:
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_synthesize_eight(state, frame, ch, blk);
+ }
+ return frame->blocks * 8;
+
+ default:
+ return -EIO;
+ }
+}
+
+static void sbc_encoder_init(struct sbc_encoder_state *state,
+ const struct sbc_frame *frame)
+{
+ memset(&state->X, 0, sizeof(state->X));
+ state->subbands = frame->subbands;
+ state->position[0] = state->position[1] = 9 * frame->subbands;
+}
+
+static inline void _sbc_analyze_four(const int32_t *in, int32_t *out)
+{
+ sbc_fixed_t t[8], s[5];
+
+ t[0] = SCALE4_STAGE1( /* Q8 */
+ MULA(_sbc_proto_4[0], in[8] - in[32], /* Q18 */
+ MUL( _sbc_proto_4[1], in[16] - in[24])));
+
+ t[1] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[2], in[1],
+ MULA(_sbc_proto_4[3], in[9],
+ MULA(_sbc_proto_4[4], in[17],
+ MULA(_sbc_proto_4[5], in[25],
+ MUL( _sbc_proto_4[6], in[33]))))));
+
+ t[2] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[7], in[2],
+ MULA(_sbc_proto_4[8], in[10],
+ MULA(_sbc_proto_4[9], in[18],
+ MULA(_sbc_proto_4[10], in[26],
+ MUL( _sbc_proto_4[11], in[34]))))));
+
+ t[3] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[12], in[3],
+ MULA(_sbc_proto_4[13], in[11],
+ MULA(_sbc_proto_4[14], in[19],
+ MULA(_sbc_proto_4[15], in[27],
+ MUL( _sbc_proto_4[16], in[35]))))));
+
+ t[4] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[17], in[4] + in[36],
+ MULA(_sbc_proto_4[18], in[12] + in[28],
+ MUL( _sbc_proto_4[19], in[20]))));
+
+ t[5] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[16], in[5],
+ MULA(_sbc_proto_4[15], in[13],
+ MULA(_sbc_proto_4[14], in[21],
+ MULA(_sbc_proto_4[13], in[29],
+ MUL( _sbc_proto_4[12], in[37]))))));
+
+ /* don't compute t[6]... this term always multiplies
+ * with cos(pi/2) = 0 */
+
+ t[7] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[6], in[7],
+ MULA(_sbc_proto_4[5], in[15],
+ MULA(_sbc_proto_4[4], in[23],
+ MULA(_sbc_proto_4[3], in[31],
+ MUL( _sbc_proto_4[2], in[39]))))));
+
+ s[0] = MUL( _anamatrix4[0], t[0] + t[4]);
+ s[1] = MUL( _anamatrix4[2], t[2]);
+ s[2] = MULA(_anamatrix4[1], t[1] + t[3],
+ MUL(_anamatrix4[3], t[5]));
+ s[3] = MULA(_anamatrix4[3], t[1] + t[3],
+ MUL(_anamatrix4[1], -t[5] + t[7]));
+ s[4] = MUL( _anamatrix4[3], t[7]);
+
+ out[0] = SCALE4_STAGE2( s[0] + s[1] + s[2] + s[4]); /* Q0 */
+ out[1] = SCALE4_STAGE2(-s[0] + s[1] + s[3]);
+ out[2] = SCALE4_STAGE2(-s[0] + s[1] - s[3]);
+ out[3] = SCALE4_STAGE2( s[0] + s[1] - s[2] - s[4]);
+}
+
+static inline void sbc_analyze_four(struct sbc_encoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int32_t *x = &state->X[ch][state->position[ch]];
+ int16_t *pcm = &frame->pcm_sample[ch][blk * 4];
+
+ /* Input 4 Audio Samples */
+ x[40] = x[0] = pcm[3];
+ x[41] = x[1] = pcm[2];
+ x[42] = x[2] = pcm[1];
+ x[43] = x[3] = pcm[0];
+
+ _sbc_analyze_four(x, frame->sb_sample_f[blk][ch]);
+
+ state->position[ch] -= 4;
+ if (state->position[ch] < 0)
+ state->position[ch] = 36;
+}
+
+static inline void _sbc_analyze_eight(const int32_t *in, int32_t *out)
+{
+ sbc_fixed_t t[8], s[8];
+
+ t[0] = SCALE8_STAGE1( /* Q10 */
+ MULA(_sbc_proto_8[0], (in[16] - in[64]), /* Q18 = Q18 * Q0 */
+ MULA(_sbc_proto_8[1], (in[32] - in[48]),
+ MULA(_sbc_proto_8[2], in[4],
+ MULA(_sbc_proto_8[3], in[20],
+ MULA(_sbc_proto_8[4], in[36],
+ MUL( _sbc_proto_8[5], in[52])))))));
+
+ t[1] = SCALE8_STAGE1(
+ MULA(_sbc_proto_8[6], in[2],
+ MULA(_sbc_proto_8[7], in[18],
+ MULA(_sbc_proto_8[8], in[34],
+ MULA(_sbc_proto_8[9], in[50],
+ MUL(_sbc_proto_8[10], in[66]))))));
+
+ t[2] = SCALE8_STAGE1(
+ MULA(_sbc_proto_8[11], in[1],
+ MULA(_sbc_proto_8[12], in[17],
+ MULA(_sbc_proto_8[13], in[33],
+ MULA(_sbc_proto_8[14], in[49],
+ MULA(_sbc_proto_8[15], in[65],
+ MULA(_sbc_proto_8[16], in[3],
+ MULA(_sbc_proto_8[17], in[19],
+ MULA(_sbc_proto_8[18], in[35],
+ MULA(_sbc_proto_8[19], in[51],
+ MUL( _sbc_proto_8[20], in[67])))))))))));
+
+ t[3] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[21], in[5],
+ MULA( _sbc_proto_8[22], in[21],
+ MULA( _sbc_proto_8[23], in[37],
+ MULA( _sbc_proto_8[24], in[53],
+ MULA( _sbc_proto_8[25], in[69],
+ MULA(-_sbc_proto_8[15], in[15],
+ MULA(-_sbc_proto_8[14], in[31],
+ MULA(-_sbc_proto_8[13], in[47],
+ MULA(-_sbc_proto_8[12], in[63],
+ MUL( -_sbc_proto_8[11], in[79])))))))))));
+
+ t[4] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[26], in[6],
+ MULA( _sbc_proto_8[27], in[22],
+ MULA( _sbc_proto_8[28], in[38],
+ MULA( _sbc_proto_8[29], in[54],
+ MULA( _sbc_proto_8[30], in[70],
+ MULA(-_sbc_proto_8[10], in[14],
+ MULA(-_sbc_proto_8[9], in[30],
+ MULA(-_sbc_proto_8[8], in[46],
+ MULA(-_sbc_proto_8[7], in[62],
+ MUL( -_sbc_proto_8[6], in[78])))))))))));
+
+ t[5] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[31], in[7],
+ MULA( _sbc_proto_8[32], in[23],
+ MULA( _sbc_proto_8[33], in[39],
+ MULA( _sbc_proto_8[34], in[55],
+ MULA( _sbc_proto_8[35], in[71],
+ MULA(-_sbc_proto_8[20], in[13],
+ MULA(-_sbc_proto_8[19], in[29],
+ MULA(-_sbc_proto_8[18], in[45],
+ MULA(-_sbc_proto_8[17], in[61],
+ MUL( -_sbc_proto_8[16], in[77])))))))))));
+
+ t[6] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[36], (in[8] + in[72]),
+ MULA( _sbc_proto_8[37], (in[24] + in[56]),
+ MULA( _sbc_proto_8[38], in[40],
+ MULA(-_sbc_proto_8[39], in[12],
+ MULA(-_sbc_proto_8[5], in[28],
+ MULA(-_sbc_proto_8[4], in[44],
+ MULA(-_sbc_proto_8[3], in[60],
+ MUL( -_sbc_proto_8[2], in[76])))))))));
+
+ t[7] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[35], in[9],
+ MULA( _sbc_proto_8[34], in[25],
+ MULA( _sbc_proto_8[33], in[41],
+ MULA( _sbc_proto_8[32], in[57],
+ MULA( _sbc_proto_8[31], in[73],
+ MULA(-_sbc_proto_8[25], in[11],
+ MULA(-_sbc_proto_8[24], in[27],
+ MULA(-_sbc_proto_8[23], in[43],
+ MULA(-_sbc_proto_8[22], in[59],
+ MUL( -_sbc_proto_8[21], in[75])))))))))));
+
+ s[0] = MULA( _anamatrix8[0], t[0],
+ MUL( _anamatrix8[1], t[6]));
+ s[1] = MUL( _anamatrix8[7], t[1]);
+ s[2] = MULA( _anamatrix8[2], t[2],
+ MULA( _anamatrix8[3], t[3],
+ MULA( _anamatrix8[4], t[5],
+ MUL( _anamatrix8[5], t[7]))));
+ s[3] = MUL( _anamatrix8[6], t[4]);
+ s[4] = MULA( _anamatrix8[3], t[2],
+ MULA(-_anamatrix8[5], t[3],
+ MULA(-_anamatrix8[2], t[5],
+ MUL( -_anamatrix8[4], t[7]))));
+ s[5] = MULA( _anamatrix8[4], t[2],
+ MULA(-_anamatrix8[2], t[3],
+ MULA( _anamatrix8[5], t[5],
+ MUL( _anamatrix8[3], t[7]))));
+ s[6] = MULA( _anamatrix8[1], t[0],
+ MUL( -_anamatrix8[0], t[6]));
+ s[7] = MULA( _anamatrix8[5], t[2],
+ MULA(-_anamatrix8[4], t[3],
+ MULA( _anamatrix8[3], t[5],
+ MUL( -_anamatrix8[2], t[7]))));
+
+ out[0] = SCALE8_STAGE2( s[0] + s[1] + s[2] + s[3]);
+ out[1] = SCALE8_STAGE2( s[1] - s[3] + s[4] + s[6]);
+ out[2] = SCALE8_STAGE2( s[1] - s[3] + s[5] - s[6]);
+ out[3] = SCALE8_STAGE2(-s[0] + s[1] + s[3] + s[7]);
+ out[4] = SCALE8_STAGE2(-s[0] + s[1] + s[3] - s[7]);
+ out[5] = SCALE8_STAGE2( s[1] - s[3] - s[5] - s[6]);
+ out[6] = SCALE8_STAGE2( s[1] - s[3] - s[4] + s[6]);
+ out[7] = SCALE8_STAGE2( s[0] + s[1] - s[2] + s[3]);
+}
+
+static inline void sbc_analyze_eight(struct sbc_encoder_state *state,
+ struct sbc_frame *frame, int ch,
+ int blk)
+{
+ int32_t *x = &state->X[ch][state->position[ch]];
+ int16_t *pcm = &frame->pcm_sample[ch][blk * 8];
+
+ /* Input 8 Audio Samples */
+ x[80] = x[0] = pcm[7];
+ x[81] = x[1] = pcm[6];
+ x[82] = x[2] = pcm[5];
+ x[83] = x[3] = pcm[4];
+ x[84] = x[4] = pcm[3];
+ x[85] = x[5] = pcm[2];
+ x[86] = x[6] = pcm[1];
+ x[87] = x[7] = pcm[0];
+
+ _sbc_analyze_eight(x, frame->sb_sample_f[blk][ch]);
+
+ state->position[ch] -= 8;
+ if (state->position[ch] < 0)
+ state->position[ch] = 72;
+}
+
+static int sbc_analyze_audio(struct sbc_encoder_state *state,
+ struct sbc_frame *frame)
+{
+ int ch, blk;
+
+ switch (frame->subbands) {
+ case 4:
+ for (ch = 0; ch < frame->channels; ch++)
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_analyze_four(state, frame, ch, blk);
+ return frame->blocks * 4;
+
+ case 8:
+ for (ch = 0; ch < frame->channels; ch++)
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_analyze_eight(state, frame, ch, blk);
+ return frame->blocks * 8;
+
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * Packs the SBC frame from frame into the memory at data. At most len
+ * bytes will be used, should more memory be needed an appropriate
+ * error code will be returned. Returns the length of the packed frame
+ * on success or a negative value on error.
+ *
+ * The error codes are:
+ * -1 Not enough memory reserved
+ * -2 Unsupported sampling rate
+ * -3 Unsupported number of blocks
+ * -4 Unsupported number of subbands
+ * -5 Bitpool value out of bounds
+ * -99 not implemented
+ */
+
+static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
+{
+ int produced;
+ /* Will copy the header parts for CRC-8 calculation here */
+ uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int crc_pos = 0;
+
+ uint16_t audio_sample;
+
+ int ch, sb, blk, bit; /* channel, subband, block and bit counters */
+ int bits[2][8]; /* bits distribution */
+ int levels[2][8]; /* levels are derived from that */
+
+ u_int32_t scalefactor[2][8]; /* derived from frame->scale_factor */
+
+ data[0] = SBC_SYNCWORD;
+
+ data[1] = (frame->frequency & 0x03) << 6;
+
+ data[1] |= (frame->block_mode & 0x03) << 4;
+
+ data[1] |= (frame->mode & 0x03) << 2;
+
+ data[1] |= (frame->allocation & 0x01) << 1;
+
+ switch (frame->subbands) {
+ case 4:
+ /* Nothing to do */
+ break;
+ case 8:
+ data[1] |= 0x01;
+ break;
+ default:
+ return -4;
+ break;
+ }
+
+ data[2] = frame->bitpool;
+
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > frame->subbands << 4)
+ return -5;
+
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > frame->subbands << 5)
+ return -5;
+
+ /* Can't fill in crc yet */
+
+ produced = 32;
+
+ crc_header[0] = data[1];
+ crc_header[1] = data[2];
+ crc_pos = 16;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ frame->scale_factor[ch][sb] = 0;
+ scalefactor[ch][sb] = 2;
+ for (blk = 0; blk < frame->blocks; blk++) {
+ while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) {
+ frame->scale_factor[ch][sb]++;
+ scalefactor[ch][sb] *= 2;
+ }
+ }
+ }
+ }
+
+ if (frame->mode == JOINT_STEREO) {
+ /* like frame->sb_sample but joint stereo */
+ int32_t sb_sample_j[16][2];
+ /* scalefactor and scale_factor in joint case */
+ u_int32_t scalefactor_j[2];
+ uint8_t scale_factor_j[2];
+
+ frame->joint = 0;
+
+ for (sb = 0; sb < frame->subbands - 1; sb++) {
+ scale_factor_j[0] = 0;
+ scalefactor_j[0] = 2;
+ scale_factor_j[1] = 0;
+ scalefactor_j[1] = 2;
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ /* Calculate joint stereo signal */
+ sb_sample_j[blk][0] =
+ (frame->sb_sample_f[blk][0][sb] +
+ frame->sb_sample_f[blk][1][sb]) >> 1;
+ sb_sample_j[blk][1] =
+ (frame->sb_sample_f[blk][0][sb] -
+ frame->sb_sample_f[blk][1][sb]) >> 1;
+
+ /* calculate scale_factor_j and scalefactor_j for joint case */
+ while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) {
+ scale_factor_j[0]++;
+ scalefactor_j[0] *= 2;
+ }
+ while (scalefactor_j[1] < fabs(sb_sample_j[blk][1])) {
+ scale_factor_j[1]++;
+ scalefactor_j[1] *= 2;
+ }
+ }
+
+ /* decide whether to join this subband */
+ if ((scalefactor[0][sb] + scalefactor[1][sb]) >
+ (scalefactor_j[0] + scalefactor_j[1]) ) {
+ /* use joint stereo for this subband */
+ frame->joint |= 1 << sb;
+ frame->scale_factor[0][sb] = scale_factor_j[0];
+ frame->scale_factor[1][sb] = scale_factor_j[1];
+ scalefactor[0][sb] = scalefactor_j[0];
+ scalefactor[1][sb] = scalefactor_j[1];
+ for (blk = 0; blk < frame->blocks; blk++) {
+ frame->sb_sample_f[blk][0][sb] =
+ sb_sample_j[blk][0];
+ frame->sb_sample_f[blk][1][sb] =
+ sb_sample_j[blk][1];
+ }
+ }
+ }
+
+ data[4] = 0;
+ for (sb = 0; sb < frame->subbands - 1; sb++)
+ data[4] |= ((frame->joint >> sb) & 0x01) << (frame->subbands - 1 - sb);
+
+ crc_header[crc_pos >> 3] = data[4];
+
+ produced += frame->subbands;
+ crc_pos += frame->subbands;
+ }
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ data[produced >> 3] <<= 4;
+ crc_header[crc_pos >> 3] <<= 4;
+ data[produced >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+ crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+
+ produced += 4;
+ crc_pos += 4;
+ }
+ }
+
+ /* align the last crc byte */
+ if (crc_pos % 8)
+ crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8);
+
+ data[3] = sbc_crc8(crc_header, crc_pos);
+
+ sbc_calculate_bits(frame, bits);
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++)
+ levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+ }
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (levels[ch][sb] > 0) {
+ audio_sample =
+ (uint16_t) ((((frame->sb_sample_f[blk][ch][sb]*levels[ch][sb]) >>
+ (frame->scale_factor[ch][sb] + 1)) +
+ levels[ch][sb]) >> 1);
+ audio_sample <<= 16 - bits[ch][sb];
+ for (bit = 0; bit < bits[ch][sb]; bit++) {
+ data[produced >> 3] <<= 1;
+ if (audio_sample & 0x8000)
+ data[produced >> 3] |= 0x1;
+ audio_sample <<= 1;
+ produced++;
+ }
+ }
+ }
+ }
+ }
+
+ /* align the last byte */
+ if (produced % 8) {
+ data[produced >> 3] <<= 8 - (produced % 8);
+ }
+
+ return (produced + 7) >> 3;
+}
+
+struct sbc_priv {
+ int init;
+ struct sbc_frame frame;
+ struct sbc_decoder_state dec_state;
+ struct sbc_encoder_state enc_state;
+};
+
+static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
+{
+ sbc->frequency = SBC_FREQ_44100;
+ sbc->mode = SBC_MODE_STEREO;
+ sbc->subbands = SBC_SB_8;
+ sbc->blocks = SBC_BLK_16;
+ sbc->bitpool = 32;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ sbc->endian = SBC_LE;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ sbc->endian = SBC_BE;
+#else
+#error "Unknown byte order"
+#endif
+}
+
+int sbc_init(sbc_t *sbc, unsigned long flags)
+{
+ if (!sbc)
+ return -EIO;
+
+ memset(sbc, 0, sizeof(sbc_t));
+
+ sbc->priv = malloc(sizeof(struct sbc_priv));
+ if (!sbc->priv)
+ return -ENOMEM;
+
+ memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+ sbc_set_defaults(sbc, flags);
+
+ return 0;
+}
+
+int sbc_parse(sbc_t *sbc, void *input, int input_len)
+{
+ return sbc_decode(sbc, input, input_len, NULL, 0, NULL);
+}
+
+int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *written)
+{
+ struct sbc_priv *priv;
+ char *ptr;
+ int i, ch, framelen, samples;
+
+ if (!sbc && !input)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ framelen = sbc_unpack_frame(input, &priv->frame, input_len);
+
+ if (!priv->init) {
+ sbc_decoder_init(&priv->dec_state, &priv->frame);
+ priv->init = 1;
+
+ sbc->frequency = priv->frame.frequency;
+ sbc->mode = priv->frame.mode;
+ sbc->subbands = priv->frame.subband_mode;
+ sbc->blocks = priv->frame.block_mode;
+ sbc->allocation = priv->frame.allocation;
+ sbc->bitpool = priv->frame.bitpool;
+
+ priv->frame.codesize = sbc_get_codesize(sbc);
+ priv->frame.length = sbc_get_frame_length(sbc);
+ }
+
+ if (!output)
+ return framelen;
+
+ if (written)
+ *written = 0;
+
+ samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame);
+
+ ptr = output;
+
+ if (output_len < samples * priv->frame.channels * 2)
+ samples = output_len / (priv->frame.channels * 2);
+
+ for (i = 0; i < samples; i++) {
+ for (ch = 0; ch < priv->frame.channels; ch++) {
+ int16_t s;
+ s = priv->frame.pcm_sample[ch][i];
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (sbc->endian == SBC_BE) {
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (sbc->endian == SBC_LE) {
+#else
+#error "Unknown byte order"
+#endif
+ *ptr++ = (s & 0xff00) >> 8;
+ *ptr++ = (s & 0x00ff);
+ } else {
+ *ptr++ = (s & 0x00ff);
+ *ptr++ = (s & 0xff00) >> 8;
+ }
+ }
+ }
+
+ if (written)
+ *written = samples * priv->frame.channels * 2;
+
+ return framelen;
+}
+
+int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *written)
+{
+ struct sbc_priv *priv;
+ char *ptr;
+ int i, ch, framelen, samples;
+
+ if (!sbc && !input)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ if (written)
+ *written = 0;
+
+ if (!priv->init) {
+ priv->frame.frequency = sbc->frequency;
+ priv->frame.mode = sbc->mode;
+ priv->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ priv->frame.allocation = sbc->allocation;
+ priv->frame.subband_mode = sbc->subbands;
+ priv->frame.subbands = sbc->subbands ? 8 : 4;
+ priv->frame.block_mode = sbc->blocks;
+ priv->frame.blocks = 4 + (sbc->blocks * 4);
+ priv->frame.bitpool = sbc->bitpool;
+ priv->frame.codesize = sbc_get_codesize(sbc);
+ priv->frame.length = sbc_get_frame_length(sbc);
+
+ sbc_encoder_init(&priv->enc_state, &priv->frame);
+ priv->init = 1;
+ }
+
+ /* input must be large enough to encode a complete frame */
+ if (input_len < priv->frame.codesize)
+ return 0;
+
+ /* output must be large enough to receive the encoded frame */
+ if (!output || output_len < priv->frame.length)
+ return -ENOSPC;
+
+ ptr = input;
+
+ for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) {
+ for (ch = 0; ch < priv->frame.channels; ch++) {
+ int16_t s;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (sbc->endian == SBC_BE)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (sbc->endian == SBC_LE)
+#else
+#error "Unknown byte order"
+#endif
+ s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff);
+ else
+ s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8;
+ ptr += 2;
+ priv->frame.pcm_sample[ch][i] = s;
+ }
+ }
+
+ samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
+
+ framelen = sbc_pack_frame(output, &priv->frame, output_len);
+
+ if (written)
+ *written = framelen;
+
+ return samples * priv->frame.channels * 2;
+}
+
+void sbc_finish(sbc_t *sbc)
+{
+ if (!sbc)
+ return;
+
+ if (sbc->priv)
+ free(sbc->priv);
+
+ memset(sbc, 0, sizeof(sbc_t));
+}
+
+int sbc_get_frame_length(sbc_t *sbc)
+{
+ int ret;
+ uint8_t subbands, channels, blocks, joint;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ channels = priv->frame.channels;
+ joint = priv->frame.joint;
+ }
+
+ ret = 4 + (4 * subbands * channels) / 8;
+
+ /* This term is not always evenly divide so we round it up */
+ if (channels == 1)
+ ret += ((blocks * channels * sbc->bitpool) + 7) / 8;
+ else
+ ret += (((joint ? subbands : 0) + blocks * sbc->bitpool) + 7)
+ / 8;
+
+ return ret;
+}
+
+int sbc_get_frame_duration(sbc_t *sbc)
+{
+ uint8_t subbands, blocks;
+ uint16_t frequency;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ }
+
+ switch (sbc->frequency) {
+ case SBC_FREQ_16000:
+ frequency = 16000;
+ break;
+
+ case SBC_FREQ_32000:
+ frequency = 32000;
+ break;
+
+ case SBC_FREQ_44100:
+ frequency = 44100;
+ break;
+
+ case SBC_FREQ_48000:
+ frequency = 48000;
+ break;
+ default:
+ return 0;
+ }
+
+ return (1000000 * blocks * subbands) / frequency;
+}
+
+int sbc_get_codesize(sbc_t *sbc)
+{
+ uint8_t subbands, channels, blocks;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ channels = priv->frame.channels;
+ }
+
+ return subbands * blocks * channels * 2;
+}
+
+int sbc_reinit(sbc_t *sbc, unsigned long flags)
+{
+ struct sbc_priv *priv;
+
+ if (!sbc || !sbc->priv)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ if (priv->init == 1)
+ memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+ sbc_set_defaults(sbc, flags);
+
+ return 0;
+}
diff --git a/src/modules/bt-sbc.h b/src/modules/bt-sbc.h
new file mode 100644
index 0000000..ab47e32
--- /dev/null
+++ b/src/modules/bt-sbc.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_H
+#define __SBC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/* sampling frequency */
+#define SBC_FREQ_16000 0x00
+#define SBC_FREQ_32000 0x01
+#define SBC_FREQ_44100 0x02
+#define SBC_FREQ_48000 0x03
+
+/* blocks */
+#define SBC_BLK_4 0x00
+#define SBC_BLK_8 0x01
+#define SBC_BLK_12 0x02
+#define SBC_BLK_16 0x03
+
+/* channel mode */
+#define SBC_MODE_MONO 0x00
+#define SBC_MODE_DUAL_CHANNEL 0x01
+#define SBC_MODE_STEREO 0x02
+#define SBC_MODE_JOINT_STEREO 0x03
+
+/* allocation method */
+#define SBC_AM_LOUDNESS 0x00
+#define SBC_AM_SNR 0x01
+
+/* subbands */
+#define SBC_SB_4 0x00
+#define SBC_SB_8 0x01
+
+/* Data endianess */
+#define SBC_LE 0x00
+#define SBC_BE 0x01
+
+struct sbc_struct {
+ unsigned long flags;
+
+ uint8_t frequency;
+ uint8_t blocks;
+ uint8_t subbands;
+ uint8_t mode;
+ uint8_t allocation;
+ uint8_t bitpool;
+ uint8_t endian;
+
+ void *priv;
+};
+
+typedef struct sbc_struct sbc_t;
+
+int sbc_init(sbc_t *sbc, unsigned long flags);
+int sbc_reinit(sbc_t *sbc, unsigned long flags);
+int sbc_parse(sbc_t *sbc, void *input, int input_len);
+int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *len);
+int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *written);
+int sbc_get_frame_length(sbc_t *sbc);
+int sbc_get_frame_duration(sbc_t *sbc);
+int sbc_get_codesize(sbc_t *sbc);
+void sbc_finish(sbc_t *sbc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SBC_H */
diff --git a/src/modules/bt-sbc_math.h b/src/modules/bt-sbc_math.h
new file mode 100644
index 0000000..b3d87a6
--- /dev/null
+++ b/src/modules/bt-sbc_math.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2008 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define fabs(x) ((x) < 0 ? -(x) : (x))
+/* C does not provide an explicit arithmetic shift right but this will
+ always be correct and every compiler *should* generate optimal code */
+#define ASR(val, bits) ((-2 >> 1 == -1) ? \
+ ((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
+
+#define SCALE_PROTO4_TBL 15
+#define SCALE_ANA4_TBL 17
+#define SCALE_PROTO8_TBL 16
+#define SCALE_ANA8_TBL 17
+#define SCALE_SPROTO4_TBL 12
+#define SCALE_SPROTO8_TBL 14
+#define SCALE_NPROTO4_TBL 11
+#define SCALE_NPROTO8_TBL 11
+#define SCALE4_STAGE1_BITS 15
+#define SCALE4_STAGE2_BITS 16
+#define SCALE4_STAGED1_BITS 15
+#define SCALE4_STAGED2_BITS 16
+#define SCALE8_STAGE1_BITS 15
+#define SCALE8_STAGE2_BITS 15
+#define SCALE8_STAGED1_BITS 15
+#define SCALE8_STAGED2_BITS 16
+
+typedef int32_t sbc_fixed_t;
+
+#define SCALE4_STAGE1(src) ASR(src, SCALE4_STAGE1_BITS)
+#define SCALE4_STAGE2(src) ASR(src, SCALE4_STAGE2_BITS)
+#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS)
+#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS)
+#define SCALE8_STAGE1(src) ASR(src, SCALE8_STAGE1_BITS)
+#define SCALE8_STAGE2(src) ASR(src, SCALE8_STAGE2_BITS)
+#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS)
+#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS)
+
+#define SBC_FIXED_0(val) { val = 0; }
+#define MUL(a, b) ((a) * (b))
+#ifdef __arm__
+#define MULA(a, b, res) ({ \
+ int tmp = res; \
+ __asm__( \
+ "mla %0, %2, %3, %0" \
+ : "=&r" (tmp) \
+ : "0" (tmp), "r" (a), "r" (b)); \
+ tmp; })
+#else
+#define MULA(a, b, res) ((a) * (b) + (res))
+#endif
diff --git a/src/modules/bt-sbc_tables.h b/src/modules/bt-sbc_tables.h
new file mode 100644
index 0000000..7ac4e68
--- /dev/null
+++ b/src/modules/bt-sbc_tables.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset4[4][4] = {
+ { -1, 0, 0, 0 },
+ { -2, 0, 0, 1 },
+ { -2, 0, 0, 1 },
+ { -2, 0, 0, 1 }
+};
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset8[4][8] = {
+ { -2, 0, 0, 0, 0, 0, 0, 1 },
+ { -3, 0, 0, 0, 0, 0, 1, 2 },
+ { -4, 0, 0, 0, 0, 0, 1, 2 },
+ { -4, 0, 0, 0, 0, 0, 1, 2 }
+};
+
+#define SP4(val) ASR(val, SCALE_PROTO4_TBL)
+#define SA4(val) ASR(val, SCALE_ANA4_TBL)
+#define SP8(val) ASR(val, SCALE_PROTO8_TBL)
+#define SA8(val) ASR(val, SCALE_ANA8_TBL)
+#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
+#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
+#define SN4(val) ASR(val, SCALE_NPROTO4_TBL)
+#define SN8(val) ASR(val, SCALE_NPROTO8_TBL)
+
+static const int32_t _sbc_proto_4[20] = {
+ SP4(0x02cb3e8c), SP4(0x22b63dc0), SP4(0x002329cc), SP4(0x053b7548),
+ SP4(0x31eab940), SP4(0xec1f5e60), SP4(0xff3773a8), SP4(0x0061c5a7),
+ SP4(0x07646680), SP4(0x3f239480), SP4(0xf89f23a8), SP4(0x007a4737),
+ SP4(0x00b32807), SP4(0x083ddc80), SP4(0x4825e480), SP4(0x0191e578),
+ SP4(0x00ff11ca), SP4(0x00fb7991), SP4(0x069fdc58), SP4(0x4b584000)
+};
+
+static const int32_t _anamatrix4[4] = {
+ SA4(0x2d413cc0), SA4(0x3b20d780), SA4(0x40000000), SA4(0x187de2a0)
+};
+
+static const int32_t _sbc_proto_8[40] = {
+ SP8(0x02e5cd20), SP8(0x22d0c200), SP8(0x006bfe27), SP8(0x07808930),
+ SP8(0x3f1c8800), SP8(0xf8810d70), SP8(0x002cfdc6), SP8(0x055acf28),
+ SP8(0x31f566c0), SP8(0xebfe57e0), SP8(0xff27c437), SP8(0x001485cc),
+ SP8(0x041c6e58), SP8(0x2a7cfa80), SP8(0xe4c4a240), SP8(0xfe359e4c),
+ SP8(0x0048b1f8), SP8(0x0686ce30), SP8(0x38eec5c0), SP8(0xf2a1b9f0),
+ SP8(0xffe8904a), SP8(0x0095698a), SP8(0x0824a480), SP8(0x443b3c00),
+ SP8(0xfd7badc8), SP8(0x00d3e2d9), SP8(0x00c183d2), SP8(0x084e1950),
+ SP8(0x4810d800), SP8(0x017f43fe), SP8(0x01056dd8), SP8(0x00e9cb9f),
+ SP8(0x07d7d090), SP8(0x4a708980), SP8(0x0488fae8), SP8(0x0113bd20),
+ SP8(0x0107b1a8), SP8(0x069fb3c0), SP8(0x4b3db200), SP8(0x00763f48)
+};
+
+static const int32_t sbc_proto_4_40m0[] = {
+ SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
+ SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8),
+ SS4(0x027c1434), SS4(0x0019118b), SS4(0xfff3c74c), SS4(0xff137330),
+ SS4(0xf81b8d70), SS4(0x00ec1b8b), SS4(0xfff0b71a), SS4(0xffe99b00),
+ SS4(0xfef84470), SS4(0xf6fb4370), SS4(0xffcdc351), SS4(0xffe01dc7)
+};
+
+static const int32_t sbc_proto_4_40m1[] = {
+ SS4(0xffe090ce), SS4(0xff2c0475), SS4(0xf694f800), SS4(0xff2c0475),
+ SS4(0xffe090ce), SS4(0xffe01dc7), SS4(0xffcdc351), SS4(0xf6fb4370),
+ SS4(0xfef84470), SS4(0xffe99b00), SS4(0xfff0b71a), SS4(0x00ec1b8b),
+ SS4(0xf81b8d70), SS4(0xff137330), SS4(0xfff3c74c), SS4(0x0019118b),
+ SS4(0x027c1434), SS4(0xf9c2a8d8), SS4(0xff589157), SS4(0xfffb9ac7)
+};
+
+static const int32_t sbc_proto_8_80m0[] = {
+ SS8(0x00000000), SS8(0xfe8d1970), SS8(0xee979f00), SS8(0x11686100),
+ SS8(0x0172e690), SS8(0xfff5bd1a), SS8(0xfdf1c8d4), SS8(0xeac182c0),
+ SS8(0x0d9daee0), SS8(0x00e530da), SS8(0xffe9811d), SS8(0xfd52986c),
+ SS8(0xe7054ca0), SS8(0x0a00d410), SS8(0x006c1de4), SS8(0xffdba705),
+ SS8(0xfcbc98e8), SS8(0xe3889d20), SS8(0x06af2308), SS8(0x000bb7db),
+ SS8(0xffca00ed), SS8(0xfc3fbb68), SS8(0xe071bc00), SS8(0x03bf7948),
+ SS8(0xffc4e05c), SS8(0xffb54b3b), SS8(0xfbedadc0), SS8(0xdde26200),
+ SS8(0x0142291c), SS8(0xff960e94), SS8(0xff9f3e17), SS8(0xfbd8f358),
+ SS8(0xdbf79400), SS8(0xff405e01), SS8(0xff7d4914), SS8(0xff8b1a31),
+ SS8(0xfc1417b8), SS8(0xdac7bb40), SS8(0xfdbb828c), SS8(0xff762170)
+};
+
+static const int32_t sbc_proto_8_80m1[] = {
+ SS8(0xff7c272c), SS8(0xfcb02620), SS8(0xda612700), SS8(0xfcb02620),
+ SS8(0xff7c272c), SS8(0xff762170), SS8(0xfdbb828c), SS8(0xdac7bb40),
+ SS8(0xfc1417b8), SS8(0xff8b1a31), SS8(0xff7d4914), SS8(0xff405e01),
+ SS8(0xdbf79400), SS8(0xfbd8f358), SS8(0xff9f3e17), SS8(0xff960e94),
+ SS8(0x0142291c), SS8(0xdde26200), SS8(0xfbedadc0), SS8(0xffb54b3b),
+ SS8(0xffc4e05c), SS8(0x03bf7948), SS8(0xe071bc00), SS8(0xfc3fbb68),
+ SS8(0xffca00ed), SS8(0x000bb7db), SS8(0x06af2308), SS8(0xe3889d20),
+ SS8(0xfcbc98e8), SS8(0xffdba705), SS8(0x006c1de4), SS8(0x0a00d410),
+ SS8(0xe7054ca0), SS8(0xfd52986c), SS8(0xffe9811d), SS8(0x00e530da),
+ SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a)
+};
+
+static const int32_t _anamatrix8[8] = {
+ SA8(0x3b20d780), SA8(0x187de2a0), SA8(0x3ec52f80), SA8(0x3536cc40),
+ SA8(0x238e7680), SA8(0x0c7c5c20), SA8(0x2d413cc0), SA8(0x40000000)
+};
+
+static const int32_t synmatrix4[8][4] = {
+ { SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) },
+ { SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) },
+ { SN4(0x00000000), SN4(0x00000000), SN4(0x00000000), SN4(0x00000000) },
+ { SN4(0xfcf043ac), SN4(0x07641af0), SN4(0xf89be510), SN4(0x030fbc54) },
+ { SN4(0xfa57d868), SN4(0x05a82798), SN4(0x05a82798), SN4(0xfa57d868) },
+ { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) },
+ { SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000) },
+ { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) }
+};
+
+static const int32_t synmatrix8[16][8] = {
+ { SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798),
+ SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798) },
+ { SN8(0x0471ced0), SN8(0xf8275a10), SN8(0x018f8b84), SN8(0x06a6d988),
+ SN8(0xf9592678), SN8(0xfe70747c), SN8(0x07d8a5f0), SN8(0xfb8e3130) },
+ { SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac),
+ SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54) },
+ { SN8(0x018f8b84), SN8(0xfb8e3130), SN8(0x06a6d988), SN8(0xf8275a10),
+ SN8(0x07d8a5f0), SN8(0xf9592678), SN8(0x0471ced0), SN8(0xfe70747c) },
+ { SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000),
+ SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000) },
+ { SN8(0xfe70747c), SN8(0x0471ced0), SN8(0xf9592678), SN8(0x07d8a5f0),
+ SN8(0xf8275a10), SN8(0x06a6d988), SN8(0xfb8e3130), SN8(0x018f8b84) },
+ { SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54),
+ SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac) },
+ { SN8(0xfb8e3130), SN8(0x07d8a5f0), SN8(0xfe70747c), SN8(0xf9592678),
+ SN8(0x06a6d988), SN8(0x018f8b84), SN8(0xf8275a10), SN8(0x0471ced0) },
+ { SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868),
+ SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868) },
+ { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+ SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) },
+ { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+ SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+ { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+ SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+ { SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000),
+ SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000) },
+ { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+ SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+ { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+ SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+ { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+ SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }
+};
commit a3f075670af9ad50b10543572c7bf70ac5476fae
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 11 13:27:13 2008 -0300
BlueZ connection configuration done
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 8690e31..21dfa4d 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -17,54 +17,97 @@
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
-
- 1. Connects to the BlueZ audio service via one BlueZ specific well known unix socket
- bluez/audio/ipc: bt_audio_service_open()
- 2. Configures a connection to the BT device
- 3. Gets a BT socket fd passed in via the unix socket
- bluez/audio/ipc: bt_audio_service_get_data_fd()
- 4. Hands this over to its RT thread.
- pa_thread_mq
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#include <string.h>
+
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
#include "dbus-util.h"
#include "module-bt-device-symdef.h"
#include "bt-ipc.h"
+#include "bt-sbc.h"
+
+#define DEFAULT_SINK_NAME "bt_sink"
+#define BUFFER_SIZE 2048
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
+ "name=<name of the device> "
"addr=<address of the device> "
- "uuid=<the profile that this device will work on>");
+ "profile=<a2dp|hsp>");
+
+struct bt_a2dp {
+ sbc_capabilities_t sbc_capabilities;
+ sbc_t sbc; /* Codec data */
+ int sbc_initialized; /* Keep track if the encoder is initialized */
+ int codesize; /* SBC codesize */
+ int samples; /* Number of encoded samples */
+ uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
+ int count; /* Codec transfer buffer counter */
+
+ int nsamples; /* Cumulative number of codec samples */
+ uint16_t seq_num; /* Cumulative packet sequence */
+ int frame_count; /* Current frames in buffer*/
+};
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
+ pa_thread *thread;
+
+ pa_sample_spec ss;
+ pa_smoother *smoother;
+
+ pa_memchunk memchunk;
+
+ const char *name;
const char *addr;
- const char *uuid;
+ const char *profile;
+ int rate;
+ int channels;
+
+ int audioservice_fd;
+ int stream_fd;
+ int transport;
+ int link_mtu;
+
+ struct bt_a2dp a2dp;
};
static const char* const valid_modargs[] = {
+ "name",
"addr",
- "uuid",
+ "profile",
+ "rate",
+ "channels",
NULL
};
-static int audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
+static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
int e;
- pa_log("sending %s", bt_audio_strmsg(msg->msg_type)); /*debug*/
+ pa_log/*_debug*/("sending %s", bt_audio_strmsg(msg->msg_type));
if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
e = 0;
else {
@@ -74,8 +117,8 @@ static int audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
return e;
}
-static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
- int err;
+static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
+ int e;
const char *type;
pa_log/*_debug*/("trying to receive msg from audio service...");
@@ -83,67 +126,686 @@ static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
type = bt_audio_strmsg(inmsg->msg_type);
if (type) {
pa_log/*_debug*/("Received %s", type);
- err = 0;
+ e = 0;
}
else {
- err = -EINVAL;
+ e = -EINVAL;
pa_log_error("Bogus message type %d received from audio service", inmsg->msg_type);
}
}
else {
- err = -errno;
+ e = -errno;
pa_log_error("Error receiving data from audio service: %s(%d)", strerror(errno), errno);
}
- return err;
+ return e;
}
-static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) {
- int err = audioservice_recv(sk, rsp_hdr);
- if (err == 0) {
+static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) {
+ int e = bt_audioservice_recv(sk, rsp_hdr);
+ if (e == 0) {
if (rsp_hdr->msg_type != expected_type) {
- err = -EINVAL;
+ e = -EINVAL;
pa_log_error("Bogus message %s received while %s was expected", bt_audio_strmsg(rsp_hdr->msg_type),
bt_audio_strmsg(expected_type));
}
}
- return err;
+ return e;
+}
+
+static int bt_getcaps(struct userdata *u) {
+ int e;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
+ struct bt_getcapabilities_req *getcaps_req = (void*) buf;
+ struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf;
+
+ memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ;
+ strncpy(getcaps_req->device, u->addr, 18);
+ if (strcasecmp(u->profile, "a2dp") == 0)
+ getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ else if (strcasecmp(u->profile, "hsp") == 0)
+ getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ else {
+ pa_log_error("invalid profile argument: %s", u->profile);
+ return -1;
+ }
+ getcaps_req->flags = 0;
+ getcaps_req->flags |= BT_FLAG_AUTOCONNECT;
+
+ e = bt_audioservice_send(u->audioservice_fd, &getcaps_req->h);
+ if (e < 0) {
+ pa_log_error("failed to send GETCAPABILITIES_REQ");
+ return e;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &rsp_hdr->msg_h, BT_GETCAPABILITIES_RSP);
+ if (e < 0) {
+ pa_log_error("failed to expect for GETCAPABILITIES_RSP");
+ return e;
+ }
+ if (rsp_hdr->posix_errno != 0) {
+ pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+ return -rsp_hdr->posix_errno;
+ }
+
+ if ((u->transport = getcaps_rsp->transport) == BT_CAPABILITIES_TRANSPORT_A2DP)
+ u->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities;
+
+ return 0;
+}
+
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
+ switch (freq) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ case BT_SBC_SAMPLING_FREQ_32000:
+ return 53;
+ case BT_SBC_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ pa_log_warn("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case BT_SBC_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ pa_log_warn("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ pa_log_warn("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static int bt_a2dp_init(struct userdata *u) {
+ sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
+ unsigned int max_bitpool, min_bitpool, rate, channels;
+ int dir;
+
+ switch (u->rate) {
+ case 48000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
+ break;
+ case 44100:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_44100;
+ break;
+ case 32000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_32000;
+ break;
+ case 16000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
+ break;
+ default:
+ pa_log_error("Rate %d not supported", rate);
+ return -1;
+ }
+
+// if (cfg->has_channel_mode)
+// cap->channel_mode = cfg->channel_mode;
+// else
+ if (channels == 2) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ } else {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ }
+
+ if (!cap->channel_mode) {
+ pa_log_error("No supported channel modes");
+ return -1;
+ }
+
+// if (cfg->has_block_length)
+// cap->block_length = cfg->block_length;
+// else
+ if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ pa_log_error("No supported block lengths");
+ return -1;
+ }
+
+// if (cfg->has_subbands)
+// cap->subbands = cfg->subbands;
+ if (cap->subbands & BT_A2DP_SUBBANDS_8)
+ cap->subbands = BT_A2DP_SUBBANDS_8;
+ else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+ cap->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ pa_log_error("No supported subbands");
+ return -1;
+ }
+
+// if (cfg->has_allocation_method)
+// cap->allocation_method = cfg->allocation_method;
+ if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+ cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+ cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+// if (cfg->has_bitpool)
+// min_bitpool = max_bitpool = cfg->bitpool;
+// else {
+ min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool);
+ max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
+// }
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return 0;
+}
+
+static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
+ sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
+
+ if (a2dp->sbc_initialized)
+ sbc_reinit(&a2dp->sbc, 0);
+ else
+ sbc_init(&a2dp->sbc, 0);
+ a2dp->sbc_initialized = 1;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
+ a2dp->sbc.frequency = SBC_FREQ_16000;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
+ a2dp->sbc.frequency = SBC_FREQ_32000;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
+ a2dp->sbc.frequency = SBC_FREQ_44100;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
+ a2dp->sbc.frequency = SBC_FREQ_48000;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ a2dp->sbc.mode = SBC_MODE_MONO;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ a2dp->sbc.mode = SBC_MODE_STEREO;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+
+ a2dp->sbc.allocation = active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS;
+
+ switch (active_capabilities.subbands) {
+ case BT_A2DP_SUBBANDS_4:
+ a2dp->sbc.subbands = SBC_SB_4;
+ break;
+ case BT_A2DP_SUBBANDS_8:
+ a2dp->sbc.subbands = SBC_SB_8;
+ break;
+ }
+
+ switch (active_capabilities.block_length) {
+ case BT_A2DP_BLOCK_LENGTH_4:
+ a2dp->sbc.blocks = SBC_BLK_4;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_8:
+ a2dp->sbc.blocks = SBC_BLK_8;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_12:
+ a2dp->sbc.blocks = SBC_BLK_12;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_16:
+ a2dp->sbc.blocks = SBC_BLK_16;
+ break;
+ }
+
+ a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+ a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
+// a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+}
+
+static int bt_setconf(struct userdata *u) {
+ int e;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
+ struct bt_setconfiguration_req *setconf_req = (void*) buf;
+ struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf;
+
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ e = bt_a2dp_init(u);
+ if (e < 0) {
+ pa_log_error("a2dp_init error");
+ return e;
+ }
+ }
+
+ memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;
+ strncpy(setconf_req->device, u->addr, 18);
+ setconf_req->transport = u->transport;
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ setconf_req->sbc_capabilities = u->a2dp.sbc_capabilities;
+ setconf_req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
+
+ e = bt_audioservice_send(u->audioservice_fd, &setconf_req->h);
+ if (e < 0) {
+ pa_log_error("failed to send BT_SETCONFIGURATION_REQ");
+ return e;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &rsp_hdr->msg_h, BT_SETCONFIGURATION_RSP);
+ if (e < 0) {
+ pa_log_error("failed to expect BT_SETCONFIGURATION_RSP");
+ return e;
+ }
+
+ if (rsp_hdr->posix_errno != 0) {
+ pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+ return -rsp_hdr->posix_errno;
+ }
+
+ u->transport = setconf_rsp->transport;
+ u->link_mtu = setconf_rsp->link_mtu;
+
+ /* setup SBC encoder now we agree on parameters */
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ bt_a2dp_setup(&u->a2dp);
+ pa_log/*debug*/("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
+ }
+
+ return 0;
+}
+
+static int bt_getstreamfd(struct userdata *u) {
+ int e, opt_name;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstart_req *start_req = (void*) buf;
+ bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
+ struct bt_streamfd_ind *streamfd_ind = (void*) buf;
+
+ memset(start_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ start_req->h.msg_type = BT_STREAMSTART_REQ;
+
+ e = bt_audioservice_send(u->audioservice_fd, &start_req->h);
+ if (e < 0) {
+ pa_log_error("failed to send BT_STREAMSTART_REQ");
+ return e;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &rsp_hdr->msg_h, BT_STREAMSTART_RSP);
+ if (e < 0) {
+ pa_log_error("failed to expect BT_STREAMSTART_RSP");
+ return e;
+ }
+
+ if (rsp_hdr->posix_errno != 0) {
+ pa_log_error("BT_START failed : %s(%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+ return -rsp_hdr->posix_errno;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &streamfd_ind->h, BT_STREAMFD_IND);
+ if (e < 0) {
+ pa_log_error("failed to expect BT_STREAMFD_IND");
+ return e;
+ }
+
+ if (u->stream_fd >= 0)
+ close(u->stream_fd);
+
+ u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
+ if (u->stream_fd < 0) {
+ pa_log_error("failed to get data fd");
+ return -errno;
+ }
+
+// if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+// opt_name = SO_SNDTIMEO;
+// if (setsockopt(u->stream_fd, SOL_SOCKET, opt_name, &t, sizeof(t)) < 0) {
+// pa_log_error("failed to set socket options for A2DP");
+// return -errno;
+// }
+// }
+// else {
+// opt_name = SCO_TXBUFS;
+// if (setsockopt(u->stream_fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0)
+// return 0;
+// opt_name = SO_SNDBUF;
+// if (setsockopt(u->stream_fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0)
+// return 0;
+// /* FIXME : handle error codes */
+// }
+
+ return 0;
+}
+
+static int bt_hw_constraint(struct userdata *u) {
+ /*TODO: A2DP */
+ u->ss.format = PA_SAMPLE_S16LE;
+ u->ss.rate = 8000;
+ u->ss.channels = 1;
+ return 0;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_SET_STATE:
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+ break;
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+ break;
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+// pa_usec_t w, r;
+// r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+// w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
+// *((pa_usec_t*) data) = w > r ? w - r : 0;
+ break;
+ }
+
+// case SINK_MESSAGE_PASS_SOCKET: {
+// struct pollfd *pollfd;
+// pa_assert(!u->rtpoll_item);
+// 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->fd;
+// pollfd->events = pollfd->revents = 0;
+// return 0;
+// }
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void thread_func(void *userdata) {
+// struct userdata *u = userdata;
+// int write_type = 0;
+//
+// pa_assert(u);
+//
+// pa_log_debug("Thread starting up");
+//
+// pa_thread_mq_install(&u->thread_mq);
+// pa_rtpoll_install(u->rtpoll);
+//
+// pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+//
+// for (;;) {
+// int ret;
+//
+// if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+// if (u->sink->thread_info.rewind_requested)
+// pa_sink_process_rewind(u->sink, 0);
+//
+// if (u->rtpoll_item) {
+// struct pollfd *pollfd;
+// pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+//
+// /* Render some data and write it to the fifo */
+// if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+// pa_usec_t usec;
+// int64_t n;
+//
+// for (;;) {
+// ssize_t l;
+// void *p;
+//
+// if (u->memchunk.length <= 0)
+// pa_sink_render(u->sink, u->block_size, &u->memchunk);
+//
+// pa_assert(u->memchunk.length > 0);
+//
+// p = pa_memblock_acquire(u->memchunk.memblock);
+// l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+// pa_memblock_release(u->memchunk.memblock);
+//
+// pa_assert(l != 0);
+//
+// if (l < 0) {
+//
+// if (errno == EINTR)
+// continue;
+// else if (errno == EAGAIN) {
+//
+// /* OK, we filled all socket buffers up
+// * now. */
+// goto filled_up;
+//
+// } else {
+// pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+// goto fail;
+// }
+//
+// } else {
+// u->offset += l;
+//
+// u->memchunk.index += l;
+// u->memchunk.length -= l;
+//
+// if (u->memchunk.length <= 0) {
+// pa_memblock_unref(u->memchunk.memblock);
+// pa_memchunk_reset(&u->memchunk);
+// }
+//
+// pollfd->revents = 0;
+//
+// if (u->memchunk.length > 0)
+//
+// /* OK, we wrote less that we asked for,
+// * hence we can assume that the socket
+// * buffers are full now */
+// goto filled_up;
+// }
+// }
+//
+// filled_up:
+//
+// /* At this spot we know that the socket buffers are
+// * fully filled up. This is the best time to estimate
+// * the playback position of the server */
+//
+// n = u->offset;
+//
+//#ifdef SIOCOUTQ
+// {
+// int l;
+// if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+// n -= l;
+// }
+//#endif
+//
+// usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+//
+// if (usec > u->latency)
+// usec -= u->latency;
+// else
+// usec = 0;
+//
+// pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+// }
+//
+// /* Hmm, nothing to do. Let's sleep */
+// pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+// }
+//
+// if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+// goto fail;
+//
+// if (ret == 0)
+// goto finish;
+//
+// if (u->rtpoll_item) {
+// struct pollfd* pollfd;
+//
+// pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+//
+// if (pollfd->revents & ~POLLOUT) {
+// pa_log("FIFO shutdown.");
+// goto fail;
+// }
+// }
+// }
+//
+//fail:
+// /* If this was no regular exit from the loop we have to continue
+// * processing messages until we received PA_MESSAGE_SHUTDOWN */
+// pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+// pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+//
+//finish:
+// pa_log_debug("Thread shutting down");
}
int pa__init(pa_module* m) {
+ int e;
+ char *rate, *channels;
pa_modargs *ma;
+ pa_sink_new_data data;
struct userdata *u;
- int sk = -1;
pa_assert(m);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
+ u->core = m->core;
+ u->audioservice_fd = -1;
+ u->stream_fd = -1;
+ u->transport = -1;
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+ pa_memchunk_reset(&u->memchunk);
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
+ u->rtpoll_item = NULL;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log_error("failed to parse module arguments");
goto fail;
}
+ if (!(u->name = pa_modargs_get_value(ma, "name", DEFAULT_SINK_NAME))) {
+ pa_log_error("failed to get device address from module arguments");
+ goto fail;
+ }
if (!(u->addr = pa_modargs_get_value(ma, "addr", NULL))) {
- pa_log_error("failed to parse device address from module arguments");
+ pa_log_error("failed to get device address from module arguments");
goto fail;
}
- if (!(u->uuid = pa_modargs_get_value(ma, "uuid", NULL))) {
- pa_log_error("failed to parse device uuid from module arguments");
+ if (!(u->profile = pa_modargs_get_value(ma, "profile", NULL))) {
+ pa_log_error("failed to get profile from module arguments");
goto fail;
}
- pa_log("Loading module-bt-device for %s, UUID=%s", u->addr, u->uuid);
+ if (!(rate = pa_modargs_get_value(ma, "rate", NULL))) {
+ pa_log_error("failed to get rate from module arguments");
+ goto fail;
+ }
+ u->rate = atoi(rate);
+ if (!(channels = pa_modargs_get_value(ma, "channels", NULL))) {
+ pa_log_error("failed to get profile from module arguments");
+ goto fail;
+ }
+ u->channels = atoi(channels);
+ pa_log("Loading module-bt-device for %s (%s), profile %s", u->name, u->addr, u->profile);
- /* Connects to the BlueZ audio service via one BlueZ specific well known unix socket */
- sk = bt_audio_service_open();
- if (sk <= 0) {
+ /* connect to the bluez audio service */
+ u->audioservice_fd = bt_audio_service_open();
+ if (u->audioservice_fd <= 0) {
pa_log_error("couldn't connect to bluetooth audio service");
goto fail;
}
- pa_log("socket to audio service: %d", sk); /*debug*/
+ /* queries device capabilities */
+ e = bt_getcaps(u);
+ if (e < 0) {
+ pa_log_error("failed to get device capabilities");
+ goto fail;
+ }
+
+ /* configures the connection */
+ e = bt_setconf(u);
+ if (e < 0) {
+ pa_log_error("failed to set config");
+ goto fail;
+ }
+
+ /* gets the device socket */
+ e = bt_getstreamfd(u);
+ if (e < 0) {
+ pa_log_error("failed to get stream fd (%d)", e);
+ goto fail;
+ }
+
+ /* configure hw supported sample specs */
+ e = bt_hw_constraint(u);
+ if (e < 0) {
+ pa_log_error("failed to configure sample spec");
+ goto fail;
+ }
+
+ /* create sink */
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, u->name);
+ pa_sink_new_data_set_sample_spec(&data, &u->ss);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth sink '%s' (%s)", u->name, u->addr);
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_NETWORK);
+ pa_sink_new_data_done(&data);
+ if (!u->sink) {
+ pa_log_error("failed to create sink");
+ goto fail;
+ }
+ 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);
+
+ /* start rt thread */
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log_error("failed to create thread");
+ goto fail;
+ }
+ pa_sink_put(u->sink);
+
+ pa_modargs_free(ma);
return 0;
fail:
+ if (ma)
+ pa_modargs_free(ma);
pa__done(m);
return -1;
}
commit b8b761a81f613269db3822491aa2724f8172f92d
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 11 13:52:59 2008 -0300
Fix PA_USEC_PER_SEC missing
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 21dfa4d..9a40c55 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -26,6 +26,7 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/thread.h>
commit 255f9b0fe65631815895c7a2c0c4dd6752ec23f3
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 11 18:10:14 2008 -0300
Initial code for rt thread
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 9a40c55..c0ca637 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -24,9 +24,12 @@
#endif
#include <string.h>
+#include <errno.h>
+#include <poll.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/thread.h>
@@ -78,7 +81,7 @@ struct userdata {
pa_rtpoll_item *rtpoll_item;
pa_thread *thread;
- pa_sample_spec ss;
+ int64_t offset;
pa_smoother *smoother;
pa_memchunk memchunk;
@@ -88,11 +91,15 @@ struct userdata {
const char *profile;
int rate;
int channels;
+ pa_sample_spec ss;
int audioservice_fd;
int stream_fd;
+
int transport;
int link_mtu;
+ size_t block_size;
+ pa_usec_t latency;
struct bt_a2dp a2dp;
};
@@ -235,7 +242,6 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
static int bt_a2dp_init(struct userdata *u) {
sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
unsigned int max_bitpool, min_bitpool, rate, channels;
- int dir;
switch (u->rate) {
case 48000:
@@ -427,7 +433,7 @@ static int bt_setconf(struct userdata *u) {
}
u->transport = setconf_rsp->transport;
- u->link_mtu = setconf_rsp->link_mtu;
+ u->block_size = u->link_mtu = setconf_rsp->link_mtu;
/* setup SBC encoder now we agree on parameters */
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
@@ -440,7 +446,7 @@ static int bt_setconf(struct userdata *u) {
}
static int bt_getstreamfd(struct userdata *u) {
- int e, opt_name;
+ int e/*, opt_name*/;
char buf[BT_AUDIO_IPC_PACKET_SIZE];
struct bt_streamstart_req *start_req = (void*) buf;
bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
@@ -532,10 +538,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
-// pa_usec_t w, r;
-// r = pa_smoother_get(u->smoother, pa_rtclock_usec());
-// w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
-// *((pa_usec_t*) data) = w > r ? w - r : 0;
+ pa_usec_t w, r;
+ r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+ w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
+ *((pa_usec_t*) data) = w > r ? w - r : 0;
break;
}
@@ -554,94 +560,94 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
}
static void thread_func(void *userdata) {
-// struct userdata *u = userdata;
-// int write_type = 0;
-//
-// pa_assert(u);
-//
-// pa_log_debug("Thread starting up");
-//
-// pa_thread_mq_install(&u->thread_mq);
-// pa_rtpoll_install(u->rtpoll);
-//
-// pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
-//
-// for (;;) {
-// int ret;
-//
-// if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
-// if (u->sink->thread_info.rewind_requested)
-// pa_sink_process_rewind(u->sink, 0);
-//
-// if (u->rtpoll_item) {
-// struct pollfd *pollfd;
-// pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-//
-// /* Render some data and write it to the fifo */
-// if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
-// pa_usec_t usec;
-// int64_t n;
-//
-// for (;;) {
-// ssize_t l;
-// void *p;
-//
-// if (u->memchunk.length <= 0)
-// pa_sink_render(u->sink, u->block_size, &u->memchunk);
-//
-// pa_assert(u->memchunk.length > 0);
-//
-// p = pa_memblock_acquire(u->memchunk.memblock);
-// l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
-// pa_memblock_release(u->memchunk.memblock);
-//
-// pa_assert(l != 0);
-//
-// if (l < 0) {
-//
-// if (errno == EINTR)
-// continue;
-// else if (errno == EAGAIN) {
-//
-// /* OK, we filled all socket buffers up
-// * now. */
-// goto filled_up;
-//
-// } else {
-// pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
-// goto fail;
-// }
-//
-// } else {
-// u->offset += l;
-//
-// u->memchunk.index += l;
-// u->memchunk.length -= l;
-//
-// if (u->memchunk.length <= 0) {
-// pa_memblock_unref(u->memchunk.memblock);
-// pa_memchunk_reset(&u->memchunk);
-// }
-//
-// pollfd->revents = 0;
-//
-// if (u->memchunk.length > 0)
-//
-// /* OK, we wrote less that we asked for,
-// * hence we can assume that the socket
-// * buffers are full now */
-// goto filled_up;
-// }
-// }
-//
-// filled_up:
-//
-// /* At this spot we know that the socket buffers are
-// * fully filled up. This is the best time to estimate
-// * the playback position of the server */
-//
-// n = u->offset;
-//
+ struct userdata *u = userdata;
+ int write_type = 0;
+
+ pa_assert(u);
+
+ pa_log/*_debug*/("Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ for (;;) {
+ int ret;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ if (u->sink->thread_info.rewind_requested)
+ pa_sink_process_rewind(u->sink, 0);
+
+ if (u->rtpoll_item) {
+ struct pollfd *pollfd;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Render some data and write it to the fifo */
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+ pa_usec_t usec;
+ int64_t n;
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, u->block_size, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ l = pa_write(u->stream_fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+ pa_memblock_release(u->memchunk.memblock);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+ else if (errno == EAGAIN) {
+
+ /* OK, we filled all socket buffers up
+ * now. */
+ goto filled_up;
+
+ } else {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+ u->offset += l;
+
+ u->memchunk.index += l;
+ u->memchunk.length -= l;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
+
+ pollfd->revents = 0;
+
+ if (u->memchunk.length > 0)
+
+ /* OK, we wrote less that we asked for,
+ * hence we can assume that the socket
+ * buffers are full now */
+ goto filled_up;
+ }
+ }
+
+ filled_up:
+
+ /* At this spot we know that the socket buffers are
+ * fully filled up. This is the best time to estimate
+ * the playback position of the server */
+
+ n = u->offset;
+
//#ifdef SIOCOUTQ
// {
// int l;
@@ -649,52 +655,52 @@ static void thread_func(void *userdata) {
// n -= l;
// }
//#endif
-//
-// usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
-//
-// if (usec > u->latency)
-// usec -= u->latency;
-// else
-// usec = 0;
-//
-// pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
-// }
-//
-// /* Hmm, nothing to do. Let's sleep */
-// pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
-// }
-//
-// if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
-// goto fail;
-//
-// if (ret == 0)
-// goto finish;
-//
-// if (u->rtpoll_item) {
-// struct pollfd* pollfd;
-//
-// pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-//
-// if (pollfd->revents & ~POLLOUT) {
-// pa_log("FIFO shutdown.");
-// goto fail;
-// }
-// }
-// }
-//
-//fail:
-// /* If this was no regular exit from the loop we have to continue
-// * processing messages until we received PA_MESSAGE_SHUTDOWN */
-// pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
-// pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-//
-//finish:
-// pa_log_debug("Thread shutting down");
+
+ usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+
+ if (usec > u->latency)
+ usec -= u->latency;
+ else
+ usec = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+ }
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ if (u->rtpoll_item) {
+ struct pollfd* pollfd;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ if (pollfd->revents & ~POLLOUT) {
+ pa_log("FIFO shutdown.");
+ goto fail;
+ }
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("Thread shutting down");
}
int pa__init(pa_module* m) {
int e;
- char *rate, *channels;
+ const char *rate, *channels;
pa_modargs *ma;
pa_sink_new_data data;
struct userdata *u;
@@ -706,6 +712,8 @@ int pa__init(pa_module* m) {
u->audioservice_fd = -1;
u->stream_fd = -1;
u->transport = -1;
+ u->offset = 0;
+ u->latency = 0;
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
pa_memchunk_reset(&u->memchunk);
u->rtpoll = pa_rtpoll_new();
@@ -783,7 +791,7 @@ int pa__init(pa_module* m) {
pa_sink_new_data_set_sample_spec(&data, &u->ss);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth sink '%s' (%s)", u->name, u->addr);
- u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_NETWORK);
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_NETWORK);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log_error("failed to create sink");
commit f992296b9763ceef1f2f013a89b98fe3e68366d5
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 11 22:54:09 2008 -0300
Hand the stream socket to the rt thread
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index c0ca637..da2fa33 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -113,6 +113,10 @@ static const char* const valid_modargs[] = {
NULL
};
+enum {
+ SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
+};
+
static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
int e;
pa_log/*_debug*/("sending %s", bt_audio_strmsg(msg->msg_type));
@@ -545,15 +549,15 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
}
-// case SINK_MESSAGE_PASS_SOCKET: {
-// struct pollfd *pollfd;
-// pa_assert(!u->rtpoll_item);
-// 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->fd;
-// pollfd->events = pollfd->revents = 0;
-// return 0;
-// }
+ case SINK_MESSAGE_PASS_SOCKET: {
+ struct pollfd *pollfd;
+ pa_assert(!u->rtpoll_item);
+ 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;
+ pollfd->events = pollfd->revents = 0;
+ return 0;
+ }
}
return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -809,6 +813,9 @@ int pa__init(pa_module* m) {
}
pa_sink_put(u->sink);
+ /* hand the socket to the rt thread */
+ pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
+
pa_modargs_free(ma);
return 0;
commit 0396a6052903b8ceec71c3159fa1a84eb6d3aa96
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 13 14:52:26 2008 -0300
Copy arguments values instead of just getting a pointer for them
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index da2fa33..ab50f24 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -728,15 +728,15 @@ int pa__init(pa_module* m) {
pa_log_error("failed to parse module arguments");
goto fail;
}
- if (!(u->name = pa_modargs_get_value(ma, "name", DEFAULT_SINK_NAME))) {
+ if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "name", DEFAULT_SINK_NAME)))) {
pa_log_error("failed to get device address from module arguments");
goto fail;
}
- if (!(u->addr = pa_modargs_get_value(ma, "addr", NULL))) {
+ if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "addr", NULL)))) {
pa_log_error("failed to get device address from module arguments");
goto fail;
}
- if (!(u->profile = pa_modargs_get_value(ma, "profile", NULL))) {
+ if (!(u->profile = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) {
pa_log_error("failed to get profile from module arguments");
goto fail;
}
commit aa310a40f88379db3ee976721ac666b89f09aabe
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 13 15:56:05 2008 -0300
Changes for pa_modargs_get_value_u32 for integer arguments and correct some error messages
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index ab50f24..be6795e 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -729,7 +729,7 @@ int pa__init(pa_module* m) {
goto fail;
}
if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "name", DEFAULT_SINK_NAME)))) {
- pa_log_error("failed to get device address from module arguments");
+ pa_log_error("failed to get device name from module arguments");
goto fail;
}
if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "addr", NULL)))) {
@@ -740,16 +740,14 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get profile from module arguments");
goto fail;
}
- if (!(rate = pa_modargs_get_value(ma, "rate", NULL))) {
+ if (pa_modargs_get_value_u32(ma, "rate", &u->rate) < 0) {
pa_log_error("failed to get rate from module arguments");
goto fail;
}
- u->rate = atoi(rate);
- if (!(channels = pa_modargs_get_value(ma, "channels", NULL))) {
- pa_log_error("failed to get profile from module arguments");
+ if (pa_modargs_get_value_u32(ma, "channels", &u->channels) < 0) {
+ pa_log_error("failed to get channels from module arguments");
goto fail;
}
- u->channels = atoi(channels);
pa_log("Loading module-bt-device for %s (%s), profile %s", u->name, u->addr, u->profile);
/* connect to the bluez audio service */
commit 16d5aab0202fac8a57f2a486d141b9d789eb39ad
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 13 16:06:36 2008 -0300
Get rid of SINK_MESSAGE_PASS_SOCKET, since we don't really need it
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index be6795e..a92cc55 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -113,10 +113,6 @@ static const char* const valid_modargs[] = {
NULL
};
-enum {
- SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
-};
-
static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
int e;
pa_log/*_debug*/("sending %s", bt_audio_strmsg(msg->msg_type));
@@ -549,15 +545,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
}
- case SINK_MESSAGE_PASS_SOCKET: {
- struct pollfd *pollfd;
- pa_assert(!u->rtpoll_item);
- 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;
- pollfd->events = pollfd->revents = 0;
- return 0;
- }
}
return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -811,9 +798,6 @@ int pa__init(pa_module* m) {
}
pa_sink_put(u->sink);
- /* hand the socket to the rt thread */
- pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
-
pa_modargs_free(ma);
return 0;
commit b4ded21e08f9c397cd13b1e6701544940598d702
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 13 16:07:19 2008 -0300
Change strerror() to pa_cstrerror()
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index a92cc55..9b91a7c 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -120,7 +120,7 @@ static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
e = 0;
else {
e = -errno;
- pa_log_error("Error sending data to audio service: %s(%d)", strerror(errno), errno);
+ pa_log_error("Error sending data to audio service: %s(%d)", pa_cstrerror(errno), errno);
}
return e;
}
@@ -143,7 +143,7 @@ static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
}
else {
e = -errno;
- pa_log_error("Error receiving data from audio service: %s(%d)", strerror(errno), errno);
+ pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno);
}
return e;
@@ -194,7 +194,7 @@ static int bt_getcaps(struct userdata *u) {
return e;
}
if (rsp_hdr->posix_errno != 0) {
- pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+ pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
return -rsp_hdr->posix_errno;
}
@@ -428,7 +428,7 @@ static int bt_setconf(struct userdata *u) {
}
if (rsp_hdr->posix_errno != 0) {
- pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+ pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
return -rsp_hdr->posix_errno;
}
@@ -468,7 +468,7 @@ static int bt_getstreamfd(struct userdata *u) {
}
if (rsp_hdr->posix_errno != 0) {
- pa_log_error("BT_START failed : %s(%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
+ pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
return -rsp_hdr->posix_errno;
}
commit d48961f2588f5058b7abffb0a006efe1518e2afb
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 13 16:08:20 2008 -0300
Change close() to pa_close()
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 9b91a7c..474e590 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -479,7 +479,7 @@ static int bt_getstreamfd(struct userdata *u) {
}
if (u->stream_fd >= 0)
- close(u->stream_fd);
+ pa_close(u->stream_fd);
u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
if (u->stream_fd < 0) {
commit 0519e5d446123649c1f0ae6bc3f88bb6a1e63c68
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 13 16:52:28 2008 -0300
Add include for sample.h
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 474e590..383ff4e 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -29,6 +29,7 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulse/sample.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
commit eb1e308710a27e0bba13a1b948225762c71946cf
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 14 18:05:01 2008 -0300
Initialize rtpoll_item
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 383ff4e..3a75684 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -695,6 +695,7 @@ int pa__init(pa_module* m) {
const char *rate, *channels;
pa_modargs *ma;
pa_sink_new_data data;
+ struct pollfd *pollfd;
struct userdata *u;
pa_assert(m);
@@ -765,6 +766,10 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get stream fd (%d)", e);
goto fail;
}
+ 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;
+ pollfd->events = pollfd->revents = 0;
/* configure hw supported sample specs */
e = bt_hw_constraint(u);
commit fcd7dc1fd715b13f7fe29f8041a4e00a3666af2e
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 14 18:12:26 2008 -0300
Add include for core-util.h
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 3a75684..8769d18 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -30,6 +30,7 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
commit 71f1d68dac9ec3b8f94b0e3bbfdddc28b4abaf7d
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 14 20:56:58 2008 -0300
Fix block size for SCO
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 8769d18..619a4d4 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -435,7 +435,11 @@ static int bt_setconf(struct userdata *u) {
}
u->transport = setconf_rsp->transport;
- u->block_size = u->link_mtu = setconf_rsp->link_mtu;
+ u->link_mtu = setconf_rsp->link_mtu;
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ /* TODO: Check the max possible size */ u->block_size = u->link_mtu / 2;
+ else
+ u->block_size = u->link_mtu;
/* setup SBC encoder now we agree on parameters */
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
commit 2e51b9302631ad1105381ae97b20f5adbee47cff
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 14 20:57:32 2008 -0300
Make stream socket non-blocking
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 619a4d4..79ed101 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -509,6 +509,7 @@ static int bt_getstreamfd(struct userdata *u) {
// return 0;
// /* FIXME : handle error codes */
// }
+ pa_make_fd_nonblock(u->stream_fd);
return 0;
}
commit 435eb0711b10ac0f33051a31bd74ed1f41277aa6
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 14 20:59:28 2008 -0300
Change pa_sink_render to pa_sink_render_into_full and remove some unnecessary checks on the rt thread
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 79ed101..cb16601 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -87,6 +87,7 @@ struct userdata {
pa_smoother *smoother;
pa_memchunk memchunk;
+ pa_mempool *mempool;
const char *name;
const char *addr;
@@ -572,123 +573,115 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
+ struct pollfd *pollfd;
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
- if (u->sink->thread_info.rewind_requested)
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ if (u->sink->thread_info.rewind_requested) {
+ pa_log("rewind_requested");
pa_sink_process_rewind(u->sink, 0);
+ pa_log("rewind_finished");
+ }
+ }
- if (u->rtpoll_item) {
- struct pollfd *pollfd;
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
- /* Render some data and write it to the fifo */
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- pa_usec_t usec;
- int64_t n;
-
- for (;;) {
- ssize_t l;
- void *p;
-
- if (u->memchunk.length <= 0)
- pa_sink_render(u->sink, u->block_size, &u->memchunk);
-
- pa_assert(u->memchunk.length > 0);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- l = pa_write(u->stream_fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
- pa_memblock_release(u->memchunk.memblock);
-
- pa_assert(l != 0);
-
- if (l < 0) {
-
- if (errno == EINTR)
- continue;
- else if (errno == EAGAIN) {
-
- /* OK, we filled all socket buffers up
- * now. */
- goto filled_up;
-
- } else {
- pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- goto fail;
- }
-
- } else {
- u->offset += l;
-
- u->memchunk.index += l;
- u->memchunk.length -= l;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
-
- pollfd->revents = 0;
-
- if (u->memchunk.length > 0)
-
- /* OK, we wrote less that we asked for,
- * hence we can assume that the socket
- * buffers are full now */
- goto filled_up;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Render some data and write it to the fifo */
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+ pa_usec_t usec;
+ int64_t n;
+ pa_log("Render some data and write it to the fifo");
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
+ pa_log("memblock allocated");
+ u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
+ pa_log("memchunk length");
+ pa_sink_render_into_full(u->sink, &u->memchunk);
+ pa_log("rendered");
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ pa_log("memblock acquired");
+ l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
+ pa_log("memblock written");
+ pa_memblock_release(u->memchunk.memblock);
+ pa_log("memblock released");
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+ pa_log("l = %d < 0", l);
+ if (errno == EINTR)
+ continue;
+ else if (errno == EAGAIN)
+ goto filled_up;
+ else {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
}
+ } else {
+ pa_log("l = %d >= 0", l);
+ u->offset += l;
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_log("memblock unrefered");
+ pa_memchunk_reset(&u->memchunk);
+ pa_log("memchunk reseted");
+ pollfd->revents = 0;
}
+ }
- filled_up:
-
- /* At this spot we know that the socket buffers are
- * fully filled up. This is the best time to estimate
- * the playback position of the server */
+filled_up:
- n = u->offset;
+ pa_log("filled_up");
+ /* At this spot we know that the socket buffers are fully filled up.
+ * This is the best time to estimate the playback position of the server */
+ n = u->offset;
//#ifdef SIOCOUTQ
-// {
-// int l;
-// if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
-// n -= l;
-// }
+// {
+// int l;
+// if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+// n -= l;
+// }
//#endif
- usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
-
- if (usec > u->latency)
- usec -= u->latency;
- else
- usec = 0;
-
- pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
- }
-
- /* Hmm, nothing to do. Let's sleep */
- pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+ usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+ if (usec > u->latency)
+ usec -= u->latency;
+ else
+ usec = 0;
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
}
- if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ /* Hmm, nothing to do. Let's sleep */
+ pa_log("let's sleep");
+ pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+ pa_log("ret < 0");
goto fail;
+ }
+ pa_log("waking up");
- if (ret == 0)
+ if (ret == 0) {
+ pa_log("ret == 0");
goto finish;
+ }
- if (u->rtpoll_item) {
- struct pollfd* pollfd;
-
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
- if (pollfd->revents & ~POLLOUT) {
- pa_log("FIFO shutdown.");
- goto fail;
- }
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ if (pollfd->revents & ~POLLOUT) {
+ pa_log("FIFO shutdown.");
+ goto fail;
}
}
fail:
- /* If this was no regular exit from the loop we have to continue
- * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
+ pa_log("fail");
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
@@ -714,6 +707,7 @@ int pa__init(pa_module* m) {
u->offset = 0;
u->latency = 0;
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+ u->mempool = pa_mempool_new(FALSE);
pa_memchunk_reset(&u->memchunk);
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
commit 0d37b912ee342a8b50a25fb29fdfdcf1ddac742c
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 15 16:23:54 2008 -0300
Remove PA_SINK_NETWORK flag and move the passage of streamfd to the rt thread just before the thread creation
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index cb16601..3881dd8 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -766,10 +766,6 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get stream fd (%d)", e);
goto fail;
}
- 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;
- pollfd->events = pollfd->revents = 0;
/* configure hw supported sample specs */
e = bt_hw_constraint(u);
@@ -786,7 +782,7 @@ int pa__init(pa_module* m) {
pa_sink_new_data_set_sample_spec(&data, &u->ss);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth sink '%s' (%s)", u->name, u->addr);
- u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_NETWORK);
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log_error("failed to create sink");
@@ -797,6 +793,11 @@ int pa__init(pa_module* m) {
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
+ 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;
+ pollfd->events = pollfd->revents = 0;
+
/* start rt thread */
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log_error("failed to create thread");
commit 123ba4f5f17dbc82b2e82be3db1bf11a12e7c84a
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 15 16:56:26 2008 -0300
Fix handling of PA_SINK_MESSAGE_GET_LATENCY
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 3881dd8..4c2a92f 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -526,6 +526,7 @@ static int bt_hw_constraint(struct userdata *u) {
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
+ pa_log/*_debug*/("got message: %d", code);
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
@@ -550,7 +551,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
r = pa_smoother_get(u->smoother, pa_rtclock_usec());
w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
- break;
+ return 0;
}
}
commit 6c10b106d8836a18de3a4d7986dafdb426557669
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 15 18:13:25 2008 -0300
Try to improve time estimation
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 4c2a92f..1a231df 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -642,13 +642,13 @@ filled_up:
* This is the best time to estimate the playback position of the server */
n = u->offset;
-//#ifdef SIOCOUTQ
-// {
-// int l;
-// if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
-// n -= l;
-// }
-//#endif
+#ifdef SIOCOUTQ
+ {
+ int l;
+ if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= l;
+ }
+#endif
usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
if (usec > u->latency)
commit 77138dd8f702cf8dd15892ea6199b604493d97a9
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sat Aug 16 13:54:28 2008 -0300
Change default sink name to bluetooth_sink
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 1a231df..95257da 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -45,7 +45,7 @@
#include "bt-ipc.h"
#include "bt-sbc.h"
-#define DEFAULT_SINK_NAME "bt_sink"
+#define DEFAULT_SINK_NAME "bluetooth_sink"
#define BUFFER_SIZE 2048
#define MAX_BITPOOL 64
#define MIN_BITPOOL 2
commit 85a931f3334c7e743a3f7afdca7e23d5989ba672
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sat Aug 16 16:00:32 2008 -0300
Get rid of hw_constraint function. It's code now lives inside bt_setconf().
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 95257da..e808cfb 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -410,6 +410,10 @@ static int bt_setconf(struct userdata *u) {
}
}
+ u->ss.format = PA_SAMPLE_S16LE;
+ u->ss.rate = u->rate;
+ u->ss.channels = u->channels;
+
memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;
strncpy(setconf_req->device, u->addr, 18);
@@ -515,14 +519,6 @@ static int bt_getstreamfd(struct userdata *u) {
return 0;
}
-static int bt_hw_constraint(struct userdata *u) {
- /*TODO: A2DP */
- u->ss.format = PA_SAMPLE_S16LE;
- u->ss.rate = 8000;
- u->ss.channels = 1;
- return 0;
-}
-
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
@@ -768,13 +764,6 @@ int pa__init(pa_module* m) {
goto fail;
}
- /* configure hw supported sample specs */
- e = bt_hw_constraint(u);
- if (e < 0) {
- pa_log_error("failed to configure sample spec");
- goto fail;
- }
-
/* create sink */
pa_sink_new_data_init(&data);
data.driver = __FILE__;
commit b5c4d2e5f1a9a8ac013e95163d5513736b2534ca
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sat Aug 16 16:03:20 2008 -0300
Configure bt connection for a2dp
diff --git a/src/modules/bt-rtp.h b/src/modules/bt-rtp.h
new file mode 100644
index 0000000..690bd43
--- /dev/null
+++ b/src/modules/bt-rtp.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ uint8_t cc:4;
+ uint8_t x:1;
+ uint8_t p:1;
+ uint8_t v:2;
+
+ uint8_t pt:7;
+ uint8_t m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ uint8_t frame_count:4;
+ uint8_t rfa0:1;
+ uint8_t is_last_fragment:1;
+ uint8_t is_first_fragment:1;
+ uint8_t is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ uint8_t v:2;
+ uint8_t p:1;
+ uint8_t x:1;
+ uint8_t cc:4;
+
+ uint8_t m:1;
+ uint8_t pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ uint8_t is_fragmented:1;
+ uint8_t is_first_fragment:1;
+ uint8_t is_last_fragment:1;
+ uint8_t rfa0:1;
+ uint8_t frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index e808cfb..87d6ea6 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -44,11 +44,15 @@
#include "module-bt-device-symdef.h"
#include "bt-ipc.h"
#include "bt-sbc.h"
+#include "bt-rtp.h"
#define DEFAULT_SINK_NAME "bluetooth_sink"
#define BUFFER_SIZE 2048
#define MAX_BITPOOL 64
#define MIN_BITPOOL 2
+#define SOL_SCO 17
+#define SCO_TXBUFS 0x03
+#define SCO_RXBUFS 0x04
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
@@ -244,7 +248,7 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
static int bt_a2dp_init(struct userdata *u) {
sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
- unsigned int max_bitpool, min_bitpool, rate, channels;
+ unsigned int max_bitpool, min_bitpool;
switch (u->rate) {
case 48000:
@@ -260,14 +264,14 @@ static int bt_a2dp_init(struct userdata *u) {
cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
break;
default:
- pa_log_error("Rate %d not supported", rate);
+ pa_log_error("Rate %d not supported", u->rate);
return -1;
}
// if (cfg->has_channel_mode)
// cap->channel_mode = cfg->channel_mode;
// else
- if (channels == 2) {
+ if (u->channels == 2) {
if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
@@ -364,7 +368,7 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
- a2dp->sbc.allocation = active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS;
+ a2dp->sbc.allocation = (active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS);
switch (active_capabilities.subbands) {
case BT_A2DP_SUBBANDS_4:
@@ -392,7 +396,7 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
a2dp->sbc.bitpool = active_capabilities.max_bitpool;
a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
-// a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
}
static int bt_setconf(struct userdata *u) {
@@ -457,7 +461,9 @@ static int bt_setconf(struct userdata *u) {
}
static int bt_getstreamfd(struct userdata *u) {
- int e/*, opt_name*/;
+ int e;
+// uint32_t period_count = io->buffer_size / io->period_size;
+// struct timeval t = { 0, period_count };
char buf[BT_AUDIO_IPC_PACKET_SIZE];
struct bt_streamstart_req *start_req = (void*) buf;
bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
@@ -494,23 +500,20 @@ static int bt_getstreamfd(struct userdata *u) {
u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
if (u->stream_fd < 0) {
- pa_log_error("failed to get data fd");
+ pa_log_error("failed to get data fd: %s (%d)",pa_cstrerror(errno), errno);
return -errno;
}
// if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
-// opt_name = SO_SNDTIMEO;
-// if (setsockopt(u->stream_fd, SOL_SOCKET, opt_name, &t, sizeof(t)) < 0) {
-// pa_log_error("failed to set socket options for A2DP");
+// if (setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)) < 0) {
+// pa_log_error("failed to set socket options for A2DP: %s (%d)",pa_cstrerror(errno), errno);
// return -errno;
// }
// }
// else {
-// opt_name = SCO_TXBUFS;
-// if (setsockopt(u->stream_fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0)
+// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
// return 0;
-// opt_name = SO_SNDBUF;
-// if (setsockopt(u->stream_fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0)
+// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
// return 0;
// /* FIXME : handle error codes */
// }
@@ -703,6 +706,7 @@ int pa__init(pa_module* m) {
u->transport = -1;
u->offset = 0;
u->latency = 0;
+ u->a2dp.sbc_initialized = 0;
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->mempool = pa_mempool_new(FALSE);
pa_memchunk_reset(&u->memchunk);
commit e54547956306485e4c0c961c9499ee8f19628bca
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sun Aug 17 01:54:12 2008 -0300
Fix block_size calculation
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 87d6ea6..4fb4fd7 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -445,17 +445,16 @@ static int bt_setconf(struct userdata *u) {
u->transport = setconf_rsp->transport;
u->link_mtu = setconf_rsp->link_mtu;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
- /* TODO: Check the max possible size */ u->block_size = u->link_mtu / 2;
- else
- u->block_size = u->link_mtu;
/* setup SBC encoder now we agree on parameters */
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
bt_a2dp_setup(&u->a2dp);
+ u->block_size = u->a2dp.codesize;
pa_log/*debug*/("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
}
+ else
+ u->block_size = u->link_mtu;
return 0;
}
commit c89301da262cdb835bb852f0b3d43aaf12a520f7
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sun Aug 17 20:35:56 2008 -0300
Fix sample size
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 4fb4fd7..5c69e90 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -412,9 +412,11 @@ static int bt_setconf(struct userdata *u) {
pa_log_error("a2dp_init error");
return e;
}
+ u->ss.format = PA_SAMPLE_S16LE;
}
+ else
+ u->ss.format = PA_SAMPLE_U8;
- u->ss.format = PA_SAMPLE_S16LE;
u->ss.rate = u->rate;
u->ss.channels = u->channels;
commit 2f455bfa5a8cfde0df9df1510e1bce560be12fe9
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sun Aug 17 20:36:33 2008 -0300
A2DP poorly working
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 5c69e90..4e1b051 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -597,9 +597,9 @@ static void thread_func(void *userdata) {
void *p;
u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log("memblock allocated");
+ pa_log("memblock allocated %d", u->block_size);
u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log("memchunk length");
+ pa_log("memchunk length %d", u->memchunk.length);
pa_sink_render_into_full(u->sink, &u->memchunk);
pa_log("rendered");
@@ -608,7 +608,7 @@ static void thread_func(void *userdata) {
p = pa_memblock_acquire(u->memchunk.memblock);
pa_log("memblock acquired");
l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
- pa_log("memblock written");
+ pa_log("memblock written %d bytes", l);
pa_memblock_release(u->memchunk.memblock);
pa_log("memblock released");
@@ -690,6 +690,186 @@ finish:
pa_log_debug("Thread shutting down");
}
+static void a2dp_thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ int write_type = 0;
+
+ pa_assert(u);
+
+ pa_log/*_debug*/("A2DP Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ for (;;) {
+ int ret;
+ struct pollfd *pollfd;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ if (u->sink->thread_info.rewind_requested) {
+ pa_log("rewind_requested");
+ pa_sink_process_rewind(u->sink, 0);
+ pa_log("rewind_finished");
+ }
+ }
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Render some data and write it to the fifo */
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+ pa_usec_t usec;
+ int64_t n;
+ pa_log("Render some data and write it to the fifo");
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+ struct bt_a2dp *a2dp = &u->a2dp;
+ int frame_size, encoded, written;
+
+ u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
+ pa_log("memblock allocated %d", u->block_size);
+ u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
+ pa_log("memchunk length %d", u->memchunk.length);
+ pa_sink_render_into_full(u->sink, &u->memchunk);
+ pa_log("rendered");
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ pa_log("memblock acquired");
+
+ frame_size = sbc_get_frame_length(&a2dp->sbc);
+ pa_log("frame_size: %d", frame_size);
+
+ encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
+ sizeof(a2dp->buffer) - a2dp->count, &written);
+ pa_log("encoded: %d", encoded);
+ pa_log("written: %d", encoded);
+ if (encoded <= 0) {
+ pa_log_error("SBC encoding error (%d)", encoded);
+ goto fail;
+ }
+ pa_memblock_release(u->memchunk.memblock);
+ pa_log("memblock released");
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_log("memblock unrefered");
+ pa_memchunk_reset(&u->memchunk);
+ pa_log("memchunk reseted");
+
+ a2dp->count += written;
+ a2dp->frame_count++;
+ a2dp->samples += encoded / frame_size;
+ a2dp->nsamples += encoded / frame_size;
+
+ if (a2dp->count + written >= u->link_mtu) {
+ struct rtp_header *header = (void *) a2dp->buffer;
+ struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
+
+ memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+
+ payload->frame_count = a2dp->frame_count;
+ header->v = 2;
+ header->pt = 1;
+ header->sequence_number = htons(a2dp->seq_num);
+ header->timestamp = htonl(a2dp->nsamples);
+ header->ssrc = htonl(1);
+
+ l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, 0);
+ pa_log("avdtp_write %d", a2dp->count);
+
+ if (l > 0) {
+ /* Reset buffer of data to send */
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ a2dp->frame_count = 0;
+ a2dp->samples = 0;
+ a2dp->seq_num++;
+ }
+
+ pa_log("avdtp written %d bytes", l);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+ pa_log("l = %d < 0", l);
+ if (errno == EINTR) {
+ pa_log("EINTR");
+ continue;
+ }
+ else if (errno == EAGAIN) {
+ pa_log("EAGAIN");
+ goto filled_up;
+ }
+ else {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ } else {
+ pa_log("l = %d >= 0", l);
+ u->offset += l;
+ pollfd->revents = 0;
+ goto filled_up;
+ }
+ }
+ }
+
+filled_up:
+
+ pa_log("filled_up");
+ /* At this spot we know that the socket buffers are fully filled up.
+ * This is the best time to estimate the playback position of the server */
+ n = u->offset;
+
+#ifdef SIOCOUTQ
+ {
+ int l;
+ if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= l;
+ }
+#endif
+
+ usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+ if (usec > u->latency)
+ usec -= u->latency;
+ else
+ usec = 0;
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pa_log("let's sleep");
+ pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+ pa_log("ret < 0");
+ goto fail;
+ }
+ pa_log("waking up");
+
+ if (ret == 0) {
+ pa_log("ret == 0");
+ goto finish;
+ }
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ if (pollfd->revents & ~POLLOUT) {
+ pa_log("FIFO shutdown.");
+ goto fail;
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
+ pa_log("fail");
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("A2DP Thread shutting down");
+}
+
int pa__init(pa_module* m) {
int e;
const char *rate, *channels;
@@ -794,9 +974,17 @@ int pa__init(pa_module* m) {
pollfd->events = pollfd->revents = 0;
/* start rt thread */
- if (!(u->thread = pa_thread_new(thread_func, u))) {
- pa_log_error("failed to create thread");
- goto fail;
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ if (!(u->thread = pa_thread_new(a2dp_thread_func, u))) {
+ pa_log_error("failed to create thread");
+ goto fail;
+ }
+ }
+ else {
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log_error("failed to create thread");
+ goto fail;
+ }
}
pa_sink_put(u->sink);
commit e570767896371336e76813231f7dc939a08db157
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Aug 19 16:06:21 2008 -0300
Refactor a2dp thread execution flow and improve time estimation
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 4e1b051..b5eccb3 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -30,10 +30,11 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/socket-util.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
@@ -505,20 +506,20 @@ static int bt_getstreamfd(struct userdata *u) {
return -errno;
}
-// if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
-// if (setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)) < 0) {
-// pa_log_error("failed to set socket options for A2DP: %s (%d)",pa_cstrerror(errno), errno);
-// return -errno;
-// }
-// }
-// else {
-// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// /* FIXME : handle error codes */
-// }
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ if (pa_socket_set_sndbuf(u->stream_fd, 10*u->link_mtu) < 0) {
+ pa_log_error("failed to set socket options for A2DP: %s (%d)",pa_cstrerror(errno), errno);
+ return -errno;
+ }
+ }
+
+// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
+// return 0;
+// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
+// return 0;
+// /* FIXME : handle error codes */
pa_make_fd_nonblock(u->stream_fd);
+// pa_make_socket_low_delay(u->stream_fd);
return 0;
}
@@ -692,11 +693,10 @@ finish:
static void a2dp_thread_func(void *userdata) {
struct userdata *u = userdata;
- int write_type = 0;
pa_assert(u);
- pa_log/*_debug*/("A2DP Thread starting up");
+ pa_log_debug("A2DP Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -709,127 +709,107 @@ static void a2dp_thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
if (u->sink->thread_info.rewind_requested) {
- pa_log("rewind_requested");
pa_sink_process_rewind(u->sink, 0);
- pa_log("rewind_finished");
}
}
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- /* Render some data and write it to the fifo */
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
- pa_log("Render some data and write it to the fifo");
-
- for (;;) {
- ssize_t l;
+ ssize_t l;
+ int write_type = 0, written;
+ struct bt_a2dp *a2dp = &u->a2dp;
+ struct rtp_header *header = (void *) a2dp->buffer;
+ struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
+
+ do {
+ /* Render some data */
void *p;
- struct bt_a2dp *a2dp = &u->a2dp;
- int frame_size, encoded, written;
+ int frame_size, encoded;
u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log("memblock allocated %d", u->block_size);
+ pa_log_debug("memblock asked size%d", u->block_size);
u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log("memchunk length %d", u->memchunk.length);
+ pa_log_debug("memchunk length %d", u->memchunk.length);
pa_sink_render_into_full(u->sink, &u->memchunk);
- pa_log("rendered");
+ pa_log_debug("rendered");
pa_assert(u->memchunk.length > 0);
p = pa_memblock_acquire(u->memchunk.memblock);
- pa_log("memblock acquired");
-
frame_size = sbc_get_frame_length(&a2dp->sbc);
- pa_log("frame_size: %d", frame_size);
+ pa_log_debug("frame_size: %d", frame_size);
encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
sizeof(a2dp->buffer) - a2dp->count, &written);
- pa_log("encoded: %d", encoded);
- pa_log("written: %d", encoded);
+ pa_log_debug("encoded: %d", encoded);
+ pa_log_debug("written: %d", written);
if (encoded <= 0) {
pa_log_error("SBC encoding error (%d)", encoded);
goto fail;
}
pa_memblock_release(u->memchunk.memblock);
- pa_log("memblock released");
pa_memblock_unref(u->memchunk.memblock);
- pa_log("memblock unrefered");
pa_memchunk_reset(&u->memchunk);
- pa_log("memchunk reseted");
+ pa_log_debug("memchunk reseted");
a2dp->count += written;
a2dp->frame_count++;
a2dp->samples += encoded / frame_size;
a2dp->nsamples += encoded / frame_size;
- if (a2dp->count + written >= u->link_mtu) {
- struct rtp_header *header = (void *) a2dp->buffer;
- struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
-
- memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+ } while (a2dp->count + written <= u->link_mtu);
- payload->frame_count = a2dp->frame_count;
- header->v = 2;
- header->pt = 1;
- header->sequence_number = htons(a2dp->seq_num);
- header->timestamp = htonl(a2dp->nsamples);
- header->ssrc = htonl(1);
+ /* write it to the fifo */
+ memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+ payload->frame_count = a2dp->frame_count;
+ header->v = 2;
+ header->pt = 1;
+ header->sequence_number = htons(a2dp->seq_num);
+ header->timestamp = htonl(a2dp->nsamples);
+ header->ssrc = htonl(1);
- l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, 0);
- pa_log("avdtp_write %d", a2dp->count);
+avdtp_write:
+ l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, write_type);
+ pa_log_debug("avdtp_write: requested %d bytes; written %d bytes", a2dp->count, l);
- if (l > 0) {
- /* Reset buffer of data to send */
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
- a2dp->frame_count = 0;
- a2dp->samples = 0;
- a2dp->seq_num++;
- }
+ pa_assert(l != 0);
- pa_log("avdtp written %d bytes", l);
-
- pa_assert(l != 0);
-
- if (l < 0) {
- pa_log("l = %d < 0", l);
- if (errno == EINTR) {
- pa_log("EINTR");
- continue;
- }
- else if (errno == EAGAIN) {
- pa_log("EAGAIN");
- goto filled_up;
- }
- else {
- pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- goto fail;
- }
- } else {
- pa_log("l = %d >= 0", l);
- u->offset += l;
- pollfd->revents = 0;
- goto filled_up;
- }
+ if (l < 0) {
+ if (errno == EINTR) {
+ pa_log_debug("EINTR");
+ continue;
+ }
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+ goto avdtp_write;
+ }
+ else {
+ pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
}
}
-filled_up:
+ u->offset += a2dp->codesize*a2dp->frame_count;
+ pollfd->revents = 0;
- pa_log("filled_up");
- /* At this spot we know that the socket buffers are fully filled up.
- * This is the best time to estimate the playback position of the server */
- n = u->offset;
+ /* Reset buffer of data to send */
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ a2dp->frame_count = 0;
+ a2dp->samples = 0;
+ a2dp->seq_num++;
+ /* feed the time smoother */
+ n = u->offset;
#ifdef SIOCOUTQ
{
- int l;
- if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
- n -= l;
+ int ll;
+ if (ioctl(u->fd, SIOCOUTQ, &ll) >= 0 && ll > 0)
+ n -= ll;
}
#endif
-
usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
if (usec > u->latency)
usec -= u->latency;
@@ -839,35 +819,34 @@ filled_up:
}
/* Hmm, nothing to do. Let's sleep */
- pa_log("let's sleep");
+ pa_log_debug("A2DP thread going to sleep");
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
-
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
pa_log("ret < 0");
goto fail;
}
- pa_log("waking up");
+ pa_log_debug("A2DP thread waking up");
if (ret == 0) {
- pa_log("ret == 0");
+ pa_log_warn("ret == 0");
goto finish;
}
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~POLLOUT) {
- pa_log("FIFO shutdown.");
+ pa_log_error("FIFO shutdown.");
goto fail;
}
}
fail:
/* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
- pa_log("fail");
+ pa_log_debug("A2DP thread failed");
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
- pa_log_debug("A2DP Thread shutting down");
+ pa_log_debug("A2DP thread shutting down");
}
int pa__init(pa_module* m) {
commit 61013fbb3d4c41eb22576da18c7d9ed00c77bf47
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Aug 19 17:35:43 2008 -0300
Fix some debug messages and other cosmetic changes
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index b5eccb3..374171e 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -123,7 +123,7 @@ static const char* const valid_modargs[] = {
static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
int e;
- pa_log/*_debug*/("sending %s", bt_audio_strmsg(msg->msg_type));
+ pa_log_debug("sending %s", bt_audio_strmsg(msg->msg_type));
if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
e = 0;
else {
@@ -137,11 +137,11 @@ static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
int e;
const char *type;
- pa_log/*_debug*/("trying to receive msg from audio service...");
+ pa_log_debug("trying to receive msg from audio service...");
if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
type = bt_audio_strmsg(inmsg->msg_type);
if (type) {
- pa_log/*_debug*/("Received %s", type);
+ pa_log_debug("Received %s", type);
e = 0;
}
else {
@@ -453,7 +453,7 @@ static int bt_setconf(struct userdata *u) {
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
bt_a2dp_setup(&u->a2dp);
u->block_size = u->a2dp.codesize;
- pa_log/*debug*/("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ pa_log_info("sbc parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
}
else
@@ -465,7 +465,6 @@ static int bt_setconf(struct userdata *u) {
static int bt_getstreamfd(struct userdata *u) {
int e;
// uint32_t period_count = io->buffer_size / io->period_size;
-// struct timeval t = { 0, period_count };
char buf[BT_AUDIO_IPC_PACKET_SIZE];
struct bt_streamstart_req *start_req = (void*) buf;
bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
@@ -527,7 +526,7 @@ static int bt_getstreamfd(struct userdata *u) {
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
- pa_log/*_debug*/("got message: %d", code);
+ pa_log_debug("got message: %d", code);
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
@@ -560,13 +559,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static void thread_func(void *userdata) {
+static void sco_thread_func(void *userdata) {
struct userdata *u = userdata;
int write_type = 0;
pa_assert(u);
- pa_log/*_debug*/("Thread starting up");
+ pa_log_debug("SCO thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -579,70 +578,61 @@ static void thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
if (u->sink->thread_info.rewind_requested) {
- pa_log("rewind_requested");
pa_sink_process_rewind(u->sink, 0);
- pa_log("rewind_finished");
}
}
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- /* Render some data and write it to the fifo */
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
- pa_log("Render some data and write it to the fifo");
for (;;) {
+ /* Render some data and write it to the fifo */
ssize_t l;
void *p;
u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log("memblock allocated %d", u->block_size);
+ pa_log_debug("memblock asked size %d", u->block_size);
u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log("memchunk length %d", u->memchunk.length);
+ pa_log_debug("memchunk length %d", u->memchunk.length);
pa_sink_render_into_full(u->sink, &u->memchunk);
- pa_log("rendered");
pa_assert(u->memchunk.length > 0);
p = pa_memblock_acquire(u->memchunk.memblock);
- pa_log("memblock acquired");
+
l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
- pa_log("memblock written %d bytes", l);
+ pa_log_debug("memblock written to socket: %d bytes", l);
pa_memblock_release(u->memchunk.memblock);
- pa_log("memblock released");
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log("memchunk reseted");
pa_assert(l != 0);
if (l < 0) {
- pa_log("l = %d < 0", l);
- if (errno == EINTR)
+ if (errno == EINTR) {
+ pa_log_debug("EINTR");
continue;
- else if (errno == EAGAIN)
+ }
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
goto filled_up;
+ }
else {
pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
goto fail;
}
} else {
- pa_log("l = %d >= 0", l);
u->offset += l;
- pa_memblock_unref(u->memchunk.memblock);
- pa_log("memblock unrefered");
- pa_memchunk_reset(&u->memchunk);
- pa_log("memchunk reseted");
pollfd->revents = 0;
}
}
filled_up:
-
- pa_log("filled_up");
- /* At this spot we know that the socket buffers are fully filled up.
- * This is the best time to estimate the playback position of the server */
n = u->offset;
-
#ifdef SIOCOUTQ
{
int l;
@@ -650,7 +640,6 @@ filled_up:
n -= l;
}
#endif
-
usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
if (usec > u->latency)
usec -= u->latency;
@@ -660,35 +649,34 @@ filled_up:
}
/* Hmm, nothing to do. Let's sleep */
- pa_log("let's sleep");
+ pa_log_debug("SCO thread going to sleep");
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
-
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
pa_log("ret < 0");
goto fail;
}
- pa_log("waking up");
+ pa_log_debug("SCO thread waking up");
if (ret == 0) {
- pa_log("ret == 0");
+ pa_log_warn("ret == 0");
goto finish;
}
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~POLLOUT) {
- pa_log("FIFO shutdown.");
+ pa_log_error("FIFO shutdown.");
goto fail;
}
}
fail:
/* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
- pa_log("fail");
+ pa_log_debug("SCO thread failed");
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
- pa_log_debug("Thread shutting down");
+ pa_log_debug("SCO thread shutting down");
}
static void a2dp_thread_func(void *userdata) {
@@ -726,26 +714,24 @@ static void a2dp_thread_func(void *userdata) {
do {
/* Render some data */
- void *p;
int frame_size, encoded;
+ void *p;
u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log_debug("memblock asked size%d", u->block_size);
+ pa_log_debug("memblock asked size %d", u->block_size);
u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
pa_log_debug("memchunk length %d", u->memchunk.length);
pa_sink_render_into_full(u->sink, &u->memchunk);
- pa_log_debug("rendered");
pa_assert(u->memchunk.length > 0);
p = pa_memblock_acquire(u->memchunk.memblock);
frame_size = sbc_get_frame_length(&a2dp->sbc);
- pa_log_debug("frame_size: %d", frame_size);
+ pa_log_debug("SBC frame_size: %d", frame_size);
encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
sizeof(a2dp->buffer) - a2dp->count, &written);
- pa_log_debug("encoded: %d", encoded);
- pa_log_debug("written: %d", written);
+ pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
if (encoded <= 0) {
pa_log_error("SBC encoding error (%d)", encoded);
goto fail;
@@ -822,7 +808,7 @@ avdtp_write:
pa_log_debug("A2DP thread going to sleep");
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
- pa_log("ret < 0");
+ pa_log_error("rtpoll_run < 0");
goto fail;
}
pa_log_debug("A2DP thread waking up");
@@ -898,7 +884,6 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get channels from module arguments");
goto fail;
}
- pa_log("Loading module-bt-device for %s (%s), profile %s", u->name, u->addr, u->profile);
/* connect to the bluez audio service */
u->audioservice_fd = bt_audio_service_open();
@@ -906,6 +891,7 @@ int pa__init(pa_module* m) {
pa_log_error("couldn't connect to bluetooth audio service");
goto fail;
}
+ pa_log_debug("connected to the bluetooth audio service");
/* queries device capabilities */
e = bt_getcaps(u);
@@ -913,6 +899,7 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get device capabilities");
goto fail;
}
+ pa_log_debug("got device capabilities");
/* configures the connection */
e = bt_setconf(u);
@@ -920,6 +907,7 @@ int pa__init(pa_module* m) {
pa_log_error("failed to set config");
goto fail;
}
+ pa_log_debug("connection to the device configured");
/* gets the device socket */
e = bt_getstreamfd(u);
@@ -927,6 +915,7 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get stream fd (%d)", e);
goto fail;
}
+ pa_log_debug("got the device socket");
/* create sink */
pa_sink_new_data_init(&data);
@@ -955,13 +944,13 @@ int pa__init(pa_module* m) {
/* start rt thread */
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
if (!(u->thread = pa_thread_new(a2dp_thread_func, u))) {
- pa_log_error("failed to create thread");
+ pa_log_error("failed to create A2DP thread");
goto fail;
}
}
else {
- if (!(u->thread = pa_thread_new(thread_func, u))) {
- pa_log_error("failed to create thread");
+ if (!(u->thread = pa_thread_new(sco_thread_func, u))) {
+ pa_log_error("failed to create SCO thread");
goto fail;
}
}
@@ -978,6 +967,5 @@ fail:
}
void pa__done(pa_module *m) {
- pa_log("Unloading module-bt-device");
}
commit 708905c1dc46e301984000febfd5a0675f1e8940
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Wed Aug 20 10:52:25 2008 -0300
pa__done for module-bt-device
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 374171e..790cf0a 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -652,13 +652,13 @@ filled_up:
pa_log_debug("SCO thread going to sleep");
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
- pa_log("ret < 0");
+ pa_log("rtpoll_run < 0");
goto fail;
}
pa_log_debug("SCO thread waking up");
if (ret == 0) {
- pa_log_warn("ret == 0");
+ pa_log_debug("rtpoll_run == 0");
goto finish;
}
@@ -814,7 +814,7 @@ avdtp_write:
pa_log_debug("A2DP thread waking up");
if (ret == 0) {
- pa_log_warn("ret == 0");
+ pa_log_debug("rtpoll_run == 0");
goto finish;
}
@@ -967,5 +967,42 @@ fail:
}
void pa__done(pa_module *m) {
-}
+ struct userdata *u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ if (u->stream_fd >= 0)
+ pa_close(u->stream_fd);
+
+ if (u->audioservice_fd >= 0)
+ pa_close(u->audioservice_fd);
+
+ pa_xfree(u);
+}
commit e752caca80df70070c34554db81e2089db8d1961
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 21 15:12:03 2008 -0300
Change sbc_initialized to pa_bool_t
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 790cf0a..053ef08 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -67,7 +67,7 @@ PA_MODULE_USAGE(
struct bt_a2dp {
sbc_capabilities_t sbc_capabilities;
sbc_t sbc; /* Codec data */
- int sbc_initialized; /* Keep track if the encoder is initialized */
+ pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */
int codesize; /* SBC codesize */
int samples; /* Number of encoded samples */
uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
@@ -343,7 +343,7 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
sbc_reinit(&a2dp->sbc, 0);
else
sbc_init(&a2dp->sbc, 0);
- a2dp->sbc_initialized = 1;
+ a2dp->sbc_initialized = TRUE;
if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
a2dp->sbc.frequency = SBC_FREQ_16000;
@@ -852,7 +852,7 @@ int pa__init(pa_module* m) {
u->transport = -1;
u->offset = 0;
u->latency = 0;
- u->a2dp.sbc_initialized = 0;
+ u->a2dp.sbc_initialized = FALSE;
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->mempool = pa_mempool_new(FALSE);
pa_memchunk_reset(&u->memchunk);
commit 0e817579ee13904e7751860a1c91291143d6e53c
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 21 17:06:41 2008 -0300
Fix some memory leaking
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 053ef08..5bc0f28 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -94,9 +94,9 @@ struct userdata {
pa_memchunk memchunk;
pa_mempool *mempool;
- const char *name;
- const char *addr;
- const char *profile;
+ char *name;
+ char *addr;
+ char *profile;
int rate;
int channels;
pa_sample_spec ss;
@@ -994,10 +994,20 @@ void pa__done(pa_module *m) {
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
+ /* TODO: free mempool */
if (u->smoother)
pa_smoother_free(u->smoother);
+ if (u->name)
+ pa_xfree(u->name);
+
+ if (u->addr)
+ pa_xfree(u->addr);
+
+ if (u->profile)
+ pa_xfree(u->profile);
+
if (u->stream_fd >= 0)
pa_close(u->stream_fd);
commit 027940b316e51bda5fe7a8b8d9d4bde925ffb04d
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 21 17:23:16 2008 -0300
Remove u->channels and u->rates, since it's redundant info
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 5bc0f28..30f8c70 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -97,8 +97,6 @@ struct userdata {
char *name;
char *addr;
char *profile;
- int rate;
- int channels;
pa_sample_spec ss;
int audioservice_fd;
@@ -251,7 +249,7 @@ static int bt_a2dp_init(struct userdata *u) {
sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
unsigned int max_bitpool, min_bitpool;
- switch (u->rate) {
+ switch (u->ss.rate) {
case 48000:
cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
break;
@@ -265,14 +263,14 @@ static int bt_a2dp_init(struct userdata *u) {
cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
break;
default:
- pa_log_error("Rate %d not supported", u->rate);
+ pa_log_error("Rate %d not supported", u->ss.rate);
return -1;
}
// if (cfg->has_channel_mode)
// cap->channel_mode = cfg->channel_mode;
// else
- if (u->channels == 2) {
+ if (u->ss.channels == 2) {
if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
@@ -418,9 +416,6 @@ static int bt_setconf(struct userdata *u) {
else
u->ss.format = PA_SAMPLE_U8;
- u->ss.rate = u->rate;
- u->ss.channels = u->channels;
-
memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;
strncpy(setconf_req->device, u->addr, 18);
@@ -837,7 +832,6 @@ finish:
int pa__init(pa_module* m) {
int e;
- const char *rate, *channels;
pa_modargs *ma;
pa_sink_new_data data;
struct pollfd *pollfd;
@@ -876,11 +870,11 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get profile from module arguments");
goto fail;
}
- if (pa_modargs_get_value_u32(ma, "rate", &u->rate) < 0) {
+ if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) {
pa_log_error("failed to get rate from module arguments");
goto fail;
}
- if (pa_modargs_get_value_u32(ma, "channels", &u->channels) < 0) {
+ if (pa_modargs_get_value_u32(ma, "channels", &u->ss.channels) < 0) {
pa_log_error("failed to get channels from module arguments");
goto fail;
}
commit 27bc1ea423c30744b5e1688a190ecacb312d19aa
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 21 17:34:27 2008 -0300
Remove unnecessary initialization of getcaps_req->flags
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 30f8c70..ee78350 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -185,8 +185,7 @@ static int bt_getcaps(struct userdata *u) {
pa_log_error("invalid profile argument: %s", u->profile);
return -1;
}
- getcaps_req->flags = 0;
- getcaps_req->flags |= BT_FLAG_AUTOCONNECT;
+ getcaps_req->flags = BT_FLAG_AUTOCONNECT;
e = bt_audioservice_send(u->audioservice_fd, &getcaps_req->h);
if (e < 0) {
commit 88a21e967760a7cc71b8b41f8d5f45bb3be21d58
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 21 17:36:17 2008 -0300
Change MIN/MAX to PA_MIN/PA_MAX
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index ee78350..ee094e7 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -323,8 +323,8 @@ static int bt_a2dp_init(struct userdata *u) {
// if (cfg->has_bitpool)
// min_bitpool = max_bitpool = cfg->bitpool;
// else {
- min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool);
- max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
+ min_bitpool = PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+ max_bitpool = PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
// }
cap->min_bitpool = min_bitpool;
commit d1cc63227853238ec9a9b90ddaee05e147303def
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Thu Aug 21 21:31:43 2008 -0300
Move render and write to the fd to a separate function
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index ee094e7..8bac98b 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -673,6 +673,92 @@ finish:
pa_log_debug("SCO thread shutting down");
}
+static int a2dp_process_render(struct userdata *u) {
+ ssize_t l;
+ int write_type = 0, written;
+ struct bt_a2dp *a2dp = &u->a2dp;
+ struct rtp_header *header = (void *) a2dp->buffer;
+ struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
+
+ pa_assert(u);
+
+ do {
+ /* Render some data */
+ int frame_size, encoded;
+ void *p;
+
+ u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
+ pa_log_debug("memblock asked size %d", u->block_size);
+ u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
+ pa_log_debug("memchunk length %d", u->memchunk.length);
+ pa_sink_render_into_full(u->sink, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ frame_size = sbc_get_frame_length(&a2dp->sbc);
+ pa_log_debug("SBC frame_size: %d", frame_size);
+
+ encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
+ sizeof(a2dp->buffer) - a2dp->count, &written);
+ pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
+ if (encoded <= 0) {
+ pa_log_error("SBC encoding error (%d)", encoded);
+ return -1;
+ }
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("memchunk reseted");
+
+ a2dp->count += written;
+ a2dp->frame_count++;
+ a2dp->samples += encoded / frame_size;
+ a2dp->nsamples += encoded / frame_size;
+
+ } while (a2dp->count + written <= u->link_mtu);
+
+ /* write it to the fifo */
+ memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+ payload->frame_count = a2dp->frame_count;
+ header->v = 2;
+ header->pt = 1;
+ header->sequence_number = htons(a2dp->seq_num);
+ header->timestamp = htonl(a2dp->nsamples);
+ header->ssrc = htonl(1);
+
+avdtp_write:
+ l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, write_type);
+ pa_log_debug("avdtp_write: requested %d bytes; written %d bytes", a2dp->count, l);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+ if (errno == EINTR) {
+ pa_log_debug("EINTR");
+ goto avdtp_write;
+ }
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+ goto avdtp_write;
+ }
+ else {
+ pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ return -1;
+ }
+ }
+
+ u->offset += a2dp->codesize*a2dp->frame_count;
+
+ /* Reset buffer of data to send */
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ a2dp->frame_count = 0;
+ a2dp->samples = 0;
+ a2dp->seq_num++;
+
+ return 0;
+}
+
static void a2dp_thread_func(void *userdata) {
struct userdata *u = userdata;
@@ -688,6 +774,8 @@ static void a2dp_thread_func(void *userdata) {
for (;;) {
int ret;
struct pollfd *pollfd;
+ int64_t n;
+ pa_usec_t usec;
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
if (u->sink->thread_info.rewind_requested) {
@@ -698,89 +786,10 @@ static void a2dp_thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- pa_usec_t usec;
- int64_t n;
- ssize_t l;
- int write_type = 0, written;
- struct bt_a2dp *a2dp = &u->a2dp;
- struct rtp_header *header = (void *) a2dp->buffer;
- struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
-
- do {
- /* Render some data */
- int frame_size, encoded;
- void *p;
-
- u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log_debug("memblock asked size %d", u->block_size);
- u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log_debug("memchunk length %d", u->memchunk.length);
- pa_sink_render_into_full(u->sink, &u->memchunk);
-
- pa_assert(u->memchunk.length > 0);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- frame_size = sbc_get_frame_length(&a2dp->sbc);
- pa_log_debug("SBC frame_size: %d", frame_size);
-
- encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
- sizeof(a2dp->buffer) - a2dp->count, &written);
- pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
- if (encoded <= 0) {
- pa_log_error("SBC encoding error (%d)", encoded);
- goto fail;
- }
- pa_memblock_release(u->memchunk.memblock);
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- pa_log_debug("memchunk reseted");
-
- a2dp->count += written;
- a2dp->frame_count++;
- a2dp->samples += encoded / frame_size;
- a2dp->nsamples += encoded / frame_size;
-
- } while (a2dp->count + written <= u->link_mtu);
-
- /* write it to the fifo */
- memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
- payload->frame_count = a2dp->frame_count;
- header->v = 2;
- header->pt = 1;
- header->sequence_number = htons(a2dp->seq_num);
- header->timestamp = htonl(a2dp->nsamples);
- header->ssrc = htonl(1);
-
-avdtp_write:
- l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, write_type);
- pa_log_debug("avdtp_write: requested %d bytes; written %d bytes", a2dp->count, l);
-
- pa_assert(l != 0);
-
- if (l < 0) {
- if (errno == EINTR) {
- pa_log_debug("EINTR");
- continue;
- }
- else if (errno == EAGAIN) {
- pa_log_debug("EAGAIN");
- goto avdtp_write;
- }
- else {
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- goto fail;
- }
- }
-
- u->offset += a2dp->codesize*a2dp->frame_count;
+ if (a2dp_process_render(u) < 0)
+ goto fail;
pollfd->revents = 0;
- /* Reset buffer of data to send */
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
- a2dp->frame_count = 0;
- a2dp->samples = 0;
- a2dp->seq_num++;
-
/* feed the time smoother */
n = u->offset;
#ifdef SIOCOUTQ
commit dc4f79657f52b6b7baf22488246e8537add20834
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 25 21:56:31 2008 -0300
Use union instead of different pointer types to the same memory area to make the code C99 compliant
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 8bac98b..304feb8 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -169,42 +169,44 @@ static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int ex
static int bt_getcaps(struct userdata *u) {
int e;
- char buf[BT_AUDIO_IPC_PACKET_SIZE];
- bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
- struct bt_getcapabilities_req *getcaps_req = (void*) buf;
- struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf;
-
- memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
- getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ;
- strncpy(getcaps_req->device, u->addr, 18);
+ union {
+ bt_audio_rsp_msg_header_t rsp_hdr;
+ struct bt_getcapabilities_req getcaps_req;
+ struct bt_getcapabilities_rsp getcaps_rsp;
+ uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ } msg;
+
+ memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ msg.getcaps_req.h.msg_type = BT_GETCAPABILITIES_REQ;
+ strncpy(msg.getcaps_req.device, u->addr, 18);
if (strcasecmp(u->profile, "a2dp") == 0)
- getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
else if (strcasecmp(u->profile, "hsp") == 0)
- getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
else {
pa_log_error("invalid profile argument: %s", u->profile);
return -1;
}
- getcaps_req->flags = BT_FLAG_AUTOCONNECT;
+ msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
- e = bt_audioservice_send(u->audioservice_fd, &getcaps_req->h);
+ e = bt_audioservice_send(u->audioservice_fd, &msg.getcaps_req.h);
if (e < 0) {
pa_log_error("failed to send GETCAPABILITIES_REQ");
return e;
}
- e = bt_audioservice_expect(u->audioservice_fd, &rsp_hdr->msg_h, BT_GETCAPABILITIES_RSP);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_GETCAPABILITIES_RSP);
if (e < 0) {
pa_log_error("failed to expect for GETCAPABILITIES_RSP");
return e;
}
- if (rsp_hdr->posix_errno != 0) {
- pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
- return -rsp_hdr->posix_errno;
+ if (msg.rsp_hdr.posix_errno != 0) {
+ pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
+ return -msg.rsp_hdr.posix_errno;
}
- if ((u->transport = getcaps_rsp->transport) == BT_CAPABILITIES_TRANSPORT_A2DP)
- u->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities;
+ if ((u->transport = msg.getcaps_rsp.transport) == BT_CAPABILITIES_TRANSPORT_A2DP)
+ u->a2dp.sbc_capabilities = msg.getcaps_rsp.sbc_capabilities;
return 0;
}
@@ -399,10 +401,12 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
static int bt_setconf(struct userdata *u) {
int e;
- char buf[BT_AUDIO_IPC_PACKET_SIZE];
- bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
- struct bt_setconfiguration_req *setconf_req = (void*) buf;
- struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf;
+ union {
+ bt_audio_rsp_msg_header_t rsp_hdr;
+ struct bt_setconfiguration_req setconf_req;
+ struct bt_setconfiguration_rsp setconf_rsp;
+ uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ } msg;
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
e = bt_a2dp_init(u);
@@ -415,33 +419,33 @@ static int bt_setconf(struct userdata *u) {
else
u->ss.format = PA_SAMPLE_U8;
- memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
- setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;
- strncpy(setconf_req->device, u->addr, 18);
- setconf_req->transport = u->transport;
+ memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ msg.setconf_req.h.msg_type = BT_SETCONFIGURATION_REQ;
+ strncpy(msg.setconf_req.device, u->addr, 18);
+ msg.setconf_req.transport = u->transport;
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
- setconf_req->sbc_capabilities = u->a2dp.sbc_capabilities;
- setconf_req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
+ msg.setconf_req.sbc_capabilities = u->a2dp.sbc_capabilities;
+ msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
- e = bt_audioservice_send(u->audioservice_fd, &setconf_req->h);
+ e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h);
if (e < 0) {
pa_log_error("failed to send BT_SETCONFIGURATION_REQ");
return e;
}
- e = bt_audioservice_expect(u->audioservice_fd, &rsp_hdr->msg_h, BT_SETCONFIGURATION_RSP);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_SETCONFIGURATION_RSP);
if (e < 0) {
pa_log_error("failed to expect BT_SETCONFIGURATION_RSP");
return e;
}
- if (rsp_hdr->posix_errno != 0) {
- pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
- return -rsp_hdr->posix_errno;
+ if (msg.rsp_hdr.posix_errno != 0) {
+ pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
+ return -msg.rsp_hdr.posix_errno;
}
- u->transport = setconf_rsp->transport;
- u->link_mtu = setconf_rsp->link_mtu;
+ u->transport = msg.setconf_rsp.transport;
+ u->link_mtu = msg.setconf_rsp.link_mtu;
/* setup SBC encoder now we agree on parameters */
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
@@ -459,32 +463,34 @@ static int bt_setconf(struct userdata *u) {
static int bt_getstreamfd(struct userdata *u) {
int e;
// uint32_t period_count = io->buffer_size / io->period_size;
- char buf[BT_AUDIO_IPC_PACKET_SIZE];
- struct bt_streamstart_req *start_req = (void*) buf;
- bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;
- struct bt_streamfd_ind *streamfd_ind = (void*) buf;
+ union {
+ bt_audio_rsp_msg_header_t rsp_hdr;
+ struct bt_streamstart_req start_req;
+ struct bt_streamfd_ind streamfd_ind;
+ uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ } msg;
- memset(start_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
- start_req->h.msg_type = BT_STREAMSTART_REQ;
+ memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ msg.start_req.h.msg_type = BT_STREAMSTART_REQ;
- e = bt_audioservice_send(u->audioservice_fd, &start_req->h);
+ e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h);
if (e < 0) {
pa_log_error("failed to send BT_STREAMSTART_REQ");
return e;
}
- e = bt_audioservice_expect(u->audioservice_fd, &rsp_hdr->msg_h, BT_STREAMSTART_RSP);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_STREAMSTART_RSP);
if (e < 0) {
pa_log_error("failed to expect BT_STREAMSTART_RSP");
return e;
}
- if (rsp_hdr->posix_errno != 0) {
- pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno);
- return -rsp_hdr->posix_errno;
+ if (msg.rsp_hdr.posix_errno != 0) {
+ pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
+ return -msg.rsp_hdr.posix_errno;
}
- e = bt_audioservice_expect(u->audioservice_fd, &streamfd_ind->h, BT_STREAMFD_IND);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.streamfd_ind.h, BT_STREAMFD_IND);
if (e < 0) {
pa_log_error("failed to expect BT_STREAMFD_IND");
return e;
commit e2f3a86ac28d8c9b8621ce6f83a35508f852148c
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 25 22:15:25 2008 -0300
Remove check for SIOCOUTQ and add proper includes
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 304feb8..985ad7e 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -26,6 +26,8 @@
#include <string.h>
#include <errno.h>
#include <poll.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
@@ -587,10 +589,10 @@ static void sco_thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
+ ssize_t l;
for (;;) {
/* Render some data and write it to the fifo */
- ssize_t l;
void *p;
u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
@@ -633,13 +635,8 @@ static void sco_thread_func(void *userdata) {
filled_up:
n = u->offset;
-#ifdef SIOCOUTQ
- {
- int l;
- if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
- n -= l;
- }
-#endif
+ if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= l;
usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
if (usec > u->latency)
usec -= u->latency;
@@ -778,7 +775,7 @@ static void a2dp_thread_func(void *userdata) {
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
for (;;) {
- int ret;
+ int ret, l;
struct pollfd *pollfd;
int64_t n;
pa_usec_t usec;
@@ -792,19 +789,14 @@ static void a2dp_thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- if (a2dp_process_render(u) < 0)
+ if (l = a2dp_process_render(u) < 0)
goto fail;
pollfd->revents = 0;
/* feed the time smoother */
n = u->offset;
-#ifdef SIOCOUTQ
- {
- int ll;
- if (ioctl(u->fd, SIOCOUTQ, &ll) >= 0 && ll > 0)
- n -= ll;
- }
-#endif
+ if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= l;
usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
if (usec > u->latency)
usec -= u->latency;
commit 199bdf2f45088b59fa897ff6098da40577f3022e
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Mon Aug 25 22:48:27 2008 -0300
Add some more device properties to the sink properties list
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 985ad7e..e6763f9 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -105,6 +105,7 @@ struct userdata {
int stream_fd;
int transport;
+ char *strtransport;
int link_mtu;
size_t block_size;
pa_usec_t latency;
@@ -447,6 +448,7 @@ static int bt_setconf(struct userdata *u) {
}
u->transport = msg.setconf_rsp.transport;
+ u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO"));
u->link_mtu = msg.setconf_rsp.link_mtu;
/* setup SBC encoder now we agree on parameters */
@@ -924,7 +926,14 @@ int pa__init(pa_module* m) {
pa_sink_new_data_set_name(&data, u->name);
pa_sink_new_data_set_sample_spec(&data, &u->ss);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth sink '%s' (%s)", u->name, u->addr);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s '%s' (%s)", u->strtransport, u->name, u->addr);
+ pa_proplist_setf(data.proplist, "bluetooth.protocol", u->profile);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_API, "bluez");
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "headset"); /*FIXME*/
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_VENDOR_PRODUCT_ID, "product_id"); /*FIXME*/
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_SERIAL, "serial"); /*FIXME*/
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
commit 8769bf455f5df4b8338e36186d078aeb13a1c59d
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Tue Aug 26 09:43:48 2008 -0300
Merge A2DP and SCO thread functions
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index e6763f9..aa67d61 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -563,119 +563,52 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static void sco_thread_func(void *userdata) {
- struct userdata *u = userdata;
+static int sco_process_render(struct userdata *u) {
+ void *p;
+ ssize_t l;
int write_type = 0;
- pa_assert(u);
+ u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
+ pa_log_debug("memblock asked size %d", u->block_size);
+ u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
+ pa_log_debug("memchunk length %d", u->memchunk.length);
+ pa_sink_render_into_full(u->sink, &u->memchunk);
- pa_log_debug("SCO thread starting up");
-
- pa_thread_mq_install(&u->thread_mq);
- pa_rtpoll_install(u->rtpoll);
+ pa_assert(u->memchunk.length > 0);
- pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+ p = pa_memblock_acquire(u->memchunk.memblock);
- for (;;) {
- int ret;
- struct pollfd *pollfd;
+sco_write:
+ l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
+ pa_log_debug("memblock written to socket: %d bytes", l);
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
- if (u->sink->thread_info.rewind_requested) {
- pa_sink_process_rewind(u->sink, 0);
- }
- }
-
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- pa_usec_t usec;
- int64_t n;
- ssize_t l;
-
- for (;;) {
- /* Render some data and write it to the fifo */
- void *p;
-
- u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log_debug("memblock asked size %d", u->block_size);
- u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log_debug("memchunk length %d", u->memchunk.length);
- pa_sink_render_into_full(u->sink, &u->memchunk);
-
- pa_assert(u->memchunk.length > 0);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
-
- l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
- pa_log_debug("memblock written to socket: %d bytes", l);
- pa_memblock_release(u->memchunk.memblock);
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- pa_log("memchunk reseted");
-
- pa_assert(l != 0);
-
- if (l < 0) {
- if (errno == EINTR) {
- pa_log_debug("EINTR");
- continue;
- }
- else if (errno == EAGAIN) {
- pa_log_debug("EAGAIN");
- goto filled_up;
- }
- else {
- pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- goto fail;
- }
- } else {
- u->offset += l;
- pollfd->revents = 0;
- }
- }
-
-filled_up:
- n = u->offset;
- if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
- n -= l;
- usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
- if (usec > u->latency)
- usec -= u->latency;
- else
- usec = 0;
- pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
- }
+ pa_assert(l != 0);
- /* Hmm, nothing to do. Let's sleep */
- pa_log_debug("SCO thread going to sleep");
- pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
- if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
- pa_log("rtpoll_run < 0");
- goto fail;
+ if (l < 0) {
+ if (errno == EINTR) {
+ pa_log_debug("EINTR");
+ goto sco_write;
}
- pa_log_debug("SCO thread waking up");
-
- if (ret == 0) {
- pa_log_debug("rtpoll_run == 0");
- goto finish;
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+ goto sco_write;
}
-
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- if (pollfd->revents & ~POLLOUT) {
- pa_log_error("FIFO shutdown.");
- goto fail;
+ else {
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("memchunk reseted");
+ pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ return -1;
}
+ } else {
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("memchunk reseted");
+ u->offset += l;
+ return 0;
}
-
-fail:
- /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
- pa_log_debug("SCO thread failed");
- pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
- pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-finish:
- pa_log_debug("SCO thread shutting down");
}
static int a2dp_process_render(struct userdata *u) {
@@ -764,12 +697,12 @@ avdtp_write:
return 0;
}
-static void a2dp_thread_func(void *userdata) {
+static void thread_func(void *userdata) {
struct userdata *u = userdata;
pa_assert(u);
- pa_log_debug("A2DP Thread starting up");
+ pa_log_debug("IO Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
@@ -791,8 +724,14 @@ static void a2dp_thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- if (l = a2dp_process_render(u) < 0)
- goto fail;
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ if (l = a2dp_process_render(u) < 0)
+ goto fail;
+ }
+ else {
+ if ((l = sco_process_render(u)) < 0)
+ goto fail;
+ }
pollfd->revents = 0;
/* feed the time smoother */
@@ -808,13 +747,13 @@ static void a2dp_thread_func(void *userdata) {
}
/* Hmm, nothing to do. Let's sleep */
- pa_log_debug("A2DP thread going to sleep");
+ pa_log_debug("IO thread going to sleep");
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
pa_log_error("rtpoll_run < 0");
goto fail;
}
- pa_log_debug("A2DP thread waking up");
+ pa_log_debug("IO thread waking up");
if (ret == 0) {
pa_log_debug("rtpoll_run == 0");
@@ -830,12 +769,12 @@ static void a2dp_thread_func(void *userdata) {
fail:
/* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
- pa_log_debug("A2DP thread failed");
+ pa_log_debug("IO thread failed");
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
- pa_log_debug("A2DP thread shutting down");
+ pa_log_debug("IO thread shutting down");
}
int pa__init(pa_module* m) {
@@ -951,17 +890,9 @@ int pa__init(pa_module* m) {
pollfd->events = pollfd->revents = 0;
/* start rt thread */
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- if (!(u->thread = pa_thread_new(a2dp_thread_func, u))) {
- pa_log_error("failed to create A2DP thread");
- goto fail;
- }
- }
- else {
- if (!(u->thread = pa_thread_new(sco_thread_func, u))) {
- pa_log_error("failed to create SCO thread");
- goto fail;
- }
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log_error("failed to create IO thread");
+ goto fail;
}
pa_sink_put(u->sink);
commit 447e02721725a543d8181904889ed540a95f0425
Author: Russ Dill <russ.dill at gmail.com>
Date: Thu Aug 28 10:38:47 2008 -0300
Fix "file not found" error on load of module-bt-device for Ubuntu Intrepid Ibex
diff --git a/src/Makefile.am b/src/Makefile.am
index 055ece5..6e39681 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1585,7 +1585,7 @@ libbt_ipc_la_CFLAGS = $(AM_CFLAGS)
module_bt_device_la_SOURCES = modules/module-bt-device.c
module_bt_device_la_LDFLAGS = -module -avoid-version
-module_bt_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbt-ipc.la libbt-sbc.la
+module_bt_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbt-ipc.la libbt-sbc.la libsocket-util.la
module_bt_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
###################################
commit 02a9273a2ec8405a6a72e462cb6204e10ebf8c1e
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 29 11:05:00 2008 -0300
Free mempool
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index aa67d61..0f137b7 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -934,7 +934,9 @@ void pa__done(pa_module *m) {
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
- /* TODO: free mempool */
+
+ if (u->mempool)
+ pa_mempool_free(u->mempool);
if (u->smoother)
pa_smoother_free(u->smoother);
commit 6093e325ccd620c16cb6f96ea19ab271cb94e2ad
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 29 11:46:02 2008 -0300
Remove some warnings
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index 0f137b7..c738a99 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -28,6 +28,7 @@
#include <poll.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
+#include <arpa/inet.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
@@ -75,7 +76,7 @@ struct bt_a2dp {
uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
int count; /* Codec transfer buffer counter */
- int nsamples; /* Cumulative number of codec samples */
+ uint32_t nsamples; /* Cumulative number of codec samples */
uint16_t seq_num; /* Cumulative packet sequence */
int frame_count; /* Current frames in buffer*/
};
@@ -666,7 +667,7 @@ static int a2dp_process_render(struct userdata *u) {
header->ssrc = htonl(1);
avdtp_write:
- l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, write_type);
+ l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, &write_type);
pa_log_debug("avdtp_write: requested %d bytes; written %d bytes", a2dp->count, l);
pa_assert(l != 0);
@@ -712,7 +713,7 @@ static void thread_func(void *userdata) {
for (;;) {
int ret, l;
struct pollfd *pollfd;
- int64_t n;
+ uint64_t n;
pa_usec_t usec;
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
@@ -725,7 +726,7 @@ static void thread_func(void *userdata) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- if (l = a2dp_process_render(u) < 0)
+ if ((l = a2dp_process_render(u)) < 0)
goto fail;
}
else {
@@ -780,6 +781,7 @@ finish:
int pa__init(pa_module* m) {
int e;
pa_modargs *ma;
+ uint32_t channels;
pa_sink_new_data data;
struct pollfd *pollfd;
struct userdata *u;
@@ -821,10 +823,11 @@ int pa__init(pa_module* m) {
pa_log_error("failed to get rate from module arguments");
goto fail;
}
- if (pa_modargs_get_value_u32(ma, "channels", &u->ss.channels) < 0) {
+ if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
pa_log_error("failed to get channels from module arguments");
goto fail;
}
+ u->ss.channels = (uint8_t) channels;
/* connect to the bluez audio service */
u->audioservice_fd = bt_audio_service_open();
commit 76bae38460807d2c9f877f546f2b8be2ef728378
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 29 11:56:12 2008 -0300
Cleanup some code
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
index c738a99..654130b 100644
--- a/src/modules/module-bt-device.c
+++ b/src/modules/module-bt-device.c
@@ -272,9 +272,6 @@ static int bt_a2dp_init(struct userdata *u) {
return -1;
}
-// if (cfg->has_channel_mode)
-// cap->channel_mode = cfg->channel_mode;
-// else
if (u->ss.channels == 2) {
if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
@@ -292,9 +289,6 @@ static int bt_a2dp_init(struct userdata *u) {
return -1;
}
-// if (cfg->has_block_length)
-// cap->block_length = cfg->block_length;
-// else
if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
@@ -308,8 +302,6 @@ static int bt_a2dp_init(struct userdata *u) {
return -1;
}
-// if (cfg->has_subbands)
-// cap->subbands = cfg->subbands;
if (cap->subbands & BT_A2DP_SUBBANDS_8)
cap->subbands = BT_A2DP_SUBBANDS_8;
else if (cap->subbands & BT_A2DP_SUBBANDS_4)
@@ -319,19 +311,13 @@ static int bt_a2dp_init(struct userdata *u) {
return -1;
}
-// if (cfg->has_allocation_method)
-// cap->allocation_method = cfg->allocation_method;
if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
-// if (cfg->has_bitpool)
-// min_bitpool = max_bitpool = cfg->bitpool;
-// else {
min_bitpool = PA_MAX(MIN_BITPOOL, cap->min_bitpool);
max_bitpool = PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
-// }
cap->min_bitpool = min_bitpool;
cap->max_bitpool = max_bitpool;
commit 78a3c72f60646ab9a44eab94b3afa4652ddeb8db
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Fri Aug 29 20:22:14 2008 -0300
Move bluetooth discover and device modules to src/modules/bluetooth
diff --git a/src/Makefile.am b/src/Makefile.am
index 6e39681..c5af829 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,7 +45,7 @@ endif
# Compiler/linker flags #
###################################
-AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/modules -I$(top_builddir)/src/modules/rtp -I$(top_builddir)/src/modules/gconf
+AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/modules -I$(top_builddir)/src/modules/rtp -I$(top_builddir)/src/modules/gconf -I$(top_builddir)/src/modules/bluetooth
AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS
AM_CFLAGS += $(LTDLINCL)
AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(LIBSPEEX_CFLAGS)
@@ -1161,10 +1161,10 @@ endif
if HAVE_BLUEZ
modlibexec_LTLIBRARIES += \
module-bt-proximity.la \
- module-bt-discover.la \
- libbt-ipc.la \
- libbt-sbc.la \
- module-bt-device.la
+ module-bluetooth-discover.la \
+ libbluetooth-ipc.la \
+ libbluetooth-sbc.la \
+ module-bluetooth-device.la
pulselibexec_PROGRAMS += \
bt-proximity-helper
@@ -1224,8 +1224,8 @@ SYMDEF_FILES = \
modules/module-suspend-on-idle-symdef.h \
modules/module-hal-detect-symdef.h \
modules/module-bt-proximity-symdef.h \
- modules/module-bt-discover-symdef.h \
- modules/module-bt-device-symdef.h \
+ modules/bluetooth/module-bluetooth-discover-symdef.h \
+ modules/bluetooth/module-bluetooth-device-symdef.h \
modules/gconf/module-gconf-symdef.h \
modules/module-position-event-sounds-symdef.h \
modules/module-console-kit-symdef.h
@@ -1237,6 +1237,7 @@ $(SYMDEF_FILES): modules/module-defs.h.m4
$(MKDIR_P) modules
$(MKDIR_P) modules/gconf
$(MKDIR_P) modules/rtp
+ $(MKDIR_P) modules/bluetooth
$(M4) -Dfname="$@" $< > $@
# Simple protocol
@@ -1566,27 +1567,26 @@ bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-# Bluetooth discover
-module_bt_discover_la_SOURCES = modules/module-bt-discover.c
-module_bt_discover_la_LDFLAGS = -module -avoid-version
-module_bt_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
-module_bt_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-
-# Bluetooth device
-libbt_sbc_la_SOURCES = modules/bt-sbc.c modules/bt-sbc.h modules/bt-sbc_tables.h modules/bt-sbc_math.h
-libbt_sbc_la_LDFLAGS = -avoid-version
-libbt_sbc_la_LIBADD = $(AM_LIBADD)
-libbt_sbc_la_CFLAGS = $(AM_CFLAGS)
-
-libbt_ipc_la_SOURCES = modules/bt-ipc.c modules/bt-ipc.h
-libbt_ipc_la_LDFLAGS = -avoid-version
-libbt_ipc_la_LIBADD = $(AM_LIBADD)
-libbt_ipc_la_CFLAGS = $(AM_CFLAGS)
-
-module_bt_device_la_SOURCES = modules/module-bt-device.c
-module_bt_device_la_LDFLAGS = -module -avoid-version
-module_bt_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbt-ipc.la libbt-sbc.la libsocket-util.la
-module_bt_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+# Bluetooth sink / source
+module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
+module_bluetooth_discover_la_LDFLAGS = -module -avoid-version
+module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h
+libbluetooth_sbc_la_LDFLAGS = -avoid-version
+libbluetooth_sbc_la_LIBADD = $(AM_LIBADD)
+libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS)
+
+libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h
+libbluetooth_ipc_la_LDFLAGS = -avoid-version
+libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)
+libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS)
+
+module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c
+module_bluetooth_device_la_LDFLAGS = -module -avoid-version
+module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libsocket-util.la
+module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
###################################
# Some minor stuff #
diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c
new file mode 100644
index 0000000..e7b712d
--- /dev/null
+++ b/src/modules/bluetooth/ipc.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "ipc.h"
+
+/* This table contains the string representation for messages */
+static const char *strmsg[] = {
+ "BT_GETCAPABILITIES_REQ",
+ "BT_GETCAPABILITIES_RSP",
+ "BT_SETCONFIGURATION_REQ",
+ "BT_SETCONFIGURATION_RSP",
+ "BT_STREAMSTART_REQ",
+ "BT_STREAMSTART_RSP",
+ "BT_STREAMSTOP_REQ",
+ "BT_STREAMSTOP_RSP",
+ "BT_STREAMSUSPEND_IND",
+ "BT_STREAMRESUME_IND",
+ "BT_CONTROL_REQ",
+ "BT_CONTROL_RSP",
+ "BT_CONTROL_IND",
+ "BT_STREAMFD_IND",
+};
+
+int bt_audio_service_open()
+{
+ int sk;
+ int err;
+ struct sockaddr_un addr = {
+ AF_UNIX, BT_IPC_SOCKET_NAME
+ };
+
+ sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ fprintf(stderr, "%s: connect() failed: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ close(sk);
+ errno = err;
+ return -1;
+ }
+
+ return sk;
+}
+
+int bt_audio_service_close(int sk)
+{
+ return close(sk);
+}
+
+int bt_audio_service_get_data_fd(int sk)
+{
+ char cmsg_b[CMSG_SPACE(sizeof(int))], m;
+ int err, ret;
+ struct iovec iov = { &m, sizeof(m) };
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+
+ memset(&msgh, 0, sizeof(msgh));
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_control = &cmsg_b;
+ msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+ ret = recvmsg(sk, &msgh, 0);
+ if (ret < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ /* Receive auxiliary data in msgh */
+ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS)
+ return (*(int *) CMSG_DATA(cmsg));
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+const char *bt_audio_strmsg(int type)
+{
+ if (type < 0 || type > (sizeof(strmsg) / sizeof(strmsg[0])))
+ return NULL;
+
+ return strmsg[type];
+}
+
diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h
new file mode 100644
index 0000000..c900fcd
--- /dev/null
+++ b/src/modules/bluetooth/ipc.h
@@ -0,0 +1,308 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/*
+ Message sequence chart of streaming sequence for A2DP transport
+
+ Audio daemon User
+ on snd_pcm_open
+ <--BT_GETCAPABILITIES_REQ
+
+ BT_GETCAPABILITIES_RSP-->
+
+ on snd_pcm_hw_params
+ <--BT_SETCONFIGURATION_REQ
+
+ BT_SETCONFIGURATION_RSP-->
+
+ on snd_pcm_prepare
+ <--BT_STREAMSTART_REQ
+
+ <Moves to streaming state>
+ BT_STREAMSTART_RSP-->
+
+ BT_STREAMFD_IND -->
+
+ < streams data >
+ ..........
+
+ on snd_pcm_drop/snd_pcm_drain
+
+ <--BT_STREAMSTOP_REQ
+
+ <Moves to open state>
+ BT_STREAMSTOP_RSP-->
+
+ on IPC close or appl crash
+ <Moves to idle>
+
+ */
+
+#ifndef BT_AUDIOCLIENT_H
+#define BT_AUDIOCLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#define BT_AUDIO_IPC_PACKET_SIZE 128
+#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
+
+/* Generic message header definition, except for RSP messages */
+typedef struct {
+ uint8_t msg_type;
+} __attribute__ ((packed)) bt_audio_msg_header_t;
+
+/* Generic message header definition, for all RSP messages */
+typedef struct {
+ bt_audio_msg_header_t msg_h;
+ uint8_t posix_errno;
+} __attribute__ ((packed)) bt_audio_rsp_msg_header_t;
+
+/* Messages list */
+#define BT_GETCAPABILITIES_REQ 0
+#define BT_GETCAPABILITIES_RSP 1
+
+#define BT_SETCONFIGURATION_REQ 2
+#define BT_SETCONFIGURATION_RSP 3
+
+#define BT_STREAMSTART_REQ 4
+#define BT_STREAMSTART_RSP 5
+
+#define BT_STREAMSTOP_REQ 6
+#define BT_STREAMSTOP_RSP 7
+
+#define BT_STREAMSUSPEND_IND 8
+#define BT_STREAMRESUME_IND 9
+
+#define BT_CONTROL_REQ 10
+#define BT_CONTROL_RSP 11
+#define BT_CONTROL_IND 12
+
+#define BT_STREAMFD_IND 13
+
+/* BT_GETCAPABILITIES_REQ */
+
+#define BT_CAPABILITIES_TRANSPORT_A2DP 0
+#define BT_CAPABILITIES_TRANSPORT_SCO 1
+#define BT_CAPABILITIES_TRANSPORT_ANY 2
+
+#define BT_CAPABILITIES_ACCESS_MODE_READ 1
+#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2
+#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3
+
+#define BT_FLAG_AUTOCONNECT 1
+
+struct bt_getcapabilities_req {
+ bt_audio_msg_header_t h;
+ char device[18]; /* Address of the remote Device */
+ uint8_t transport; /* Requested transport */
+ uint8_t flags; /* Requested flags */
+} __attribute__ ((packed));
+
+/* BT_GETCAPABILITIES_RSP */
+
+/**
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ */
+
+#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_SBC_SAMPLING_FREQ_48000 1
+
+#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3)
+#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1)
+#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1
+
+#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3)
+#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2)
+#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1)
+#define BT_A2DP_BLOCK_LENGTH_16 1
+
+#define BT_A2DP_SUBBANDS_4 (1 << 1)
+#define BT_A2DP_SUBBANDS_8 1
+
+#define BT_A2DP_ALLOCATION_SNR (1 << 1)
+#define BT_A2DP_ALLOCATION_LOUDNESS 1
+
+#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_MPEG_SAMPLING_FREQ_48000 1
+
+#define BT_MPEG_LAYER_1 (1 << 2)
+#define BT_MPEG_LAYER_2 (1 << 1)
+#define BT_MPEG_LAYER_3 1
+
+typedef struct {
+ uint8_t channel_mode;
+ uint8_t frequency;
+ uint8_t allocation_method;
+ uint8_t subbands;
+ uint8_t block_length;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) sbc_capabilities_t;
+
+typedef struct {
+ uint8_t channel_mode;
+ uint8_t crc;
+ uint8_t layer;
+ uint8_t frequency;
+ uint8_t mpf;
+ uint16_t bitrate;
+} __attribute__ ((packed)) mpeg_capabilities_t;
+
+struct bt_getcapabilities_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+ uint8_t transport; /* Granted transport */
+ sbc_capabilities_t sbc_capabilities; /* A2DP only */
+ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+ uint16_t sampling_rate; /* SCO only */
+} __attribute__ ((packed));
+
+/* BT_SETCONFIGURATION_REQ */
+struct bt_setconfiguration_req {
+ bt_audio_msg_header_t h;
+ char device[18]; /* Address of the remote Device */
+ uint8_t transport; /* Requested transport */
+ uint8_t access_mode; /* Requested access mode */
+ sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field
+ and next one must be filled */
+ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+} __attribute__ ((packed));
+
+/* BT_SETCONFIGURATION_RSP */
+struct bt_setconfiguration_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+ uint8_t transport; /* Granted transport */
+ uint8_t access_mode; /* Granted access mode */
+ uint16_t link_mtu; /* Max length that transport supports */
+} __attribute__ ((packed));
+
+/* BT_STREAMSTART_REQ */
+#define BT_STREAM_ACCESS_READ 0
+#define BT_STREAM_ACCESS_WRITE 1
+#define BT_STREAM_ACCESS_READWRITE 2
+struct bt_streamstart_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTART_RSP */
+struct bt_streamstart_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+} __attribute__ ((packed));
+
+/* BT_STREAMFD_IND */
+/* This message is followed by one byte of data containing the stream data fd
+ as ancilliary data */
+struct bt_streamfd_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTOP_REQ */
+struct bt_streamstop_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTOP_RSP */
+struct bt_streamstop_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSUSPEND_IND */
+struct bt_streamsuspend_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMRESUME_IND */
+struct bt_streamresume_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_CONTROL_REQ */
+
+#define BT_CONTROL_KEY_POWER 0x40
+#define BT_CONTROL_KEY_VOL_UP 0x41
+#define BT_CONTROL_KEY_VOL_DOWN 0x42
+#define BT_CONTROL_KEY_MUTE 0x43
+#define BT_CONTROL_KEY_PLAY 0x44
+#define BT_CONTROL_KEY_STOP 0x45
+#define BT_CONTROL_KEY_PAUSE 0x46
+#define BT_CONTROL_KEY_RECORD 0x47
+#define BT_CONTROL_KEY_REWIND 0x48
+#define BT_CONTROL_KEY_FAST_FORWARD 0x49
+#define BT_CONTROL_KEY_EJECT 0x4A
+#define BT_CONTROL_KEY_FORWARD 0x4B
+#define BT_CONTROL_KEY_BACKWARD 0x4C
+
+struct bt_control_req {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* BT_CONTROL_RSP */
+struct bt_control_rsp {
+ bt_audio_rsp_msg_header_t rsp_h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* BT_CONTROL_IND */
+struct bt_control_ind {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* Function declaration */
+
+/* Opens a connection to the audio service: return a socket descriptor */
+int bt_audio_service_open();
+
+/* Closes a connection to the audio service */
+int bt_audio_service_close(int sk);
+
+/* Receives stream data file descriptor : must be called after a
+BT_STREAMFD_IND message is returned */
+int bt_audio_service_get_data_fd(int sk);
+
+/* Human readable message type string */
+const char *bt_audio_strmsg(int type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BT_AUDIOCLIENT_H */
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
new file mode 100644
index 0000000..335a8ed
--- /dev/null
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -0,0 +1,949 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/sample.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+
+#include "dbus-util.h"
+#include "module-bluetooth-device-symdef.h"
+#include "ipc.h"
+#include "sbc.h"
+#include "rtp.h"
+
+#define DEFAULT_SINK_NAME "bluetooth_sink"
+#define BUFFER_SIZE 2048
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+#define SOL_SCO 17
+#define SCO_TXBUFS 0x03
+#define SCO_RXBUFS 0x04
+
+PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
+PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "name=<name of the device> "
+ "addr=<address of the device> "
+ "profile=<a2dp|hsp>");
+
+struct bt_a2dp {
+ sbc_capabilities_t sbc_capabilities;
+ sbc_t sbc; /* Codec data */
+ pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */
+ int codesize; /* SBC codesize */
+ int samples; /* Number of encoded samples */
+ uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
+ int count; /* Codec transfer buffer counter */
+
+ uint32_t nsamples; /* Cumulative number of codec samples */
+ uint16_t seq_num; /* Cumulative packet sequence */
+ int frame_count; /* Current frames in buffer*/
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
+
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
+ pa_thread *thread;
+
+ int64_t offset;
+ pa_smoother *smoother;
+
+ pa_memchunk memchunk;
+ pa_mempool *mempool;
+
+ char *name;
+ char *addr;
+ char *profile;
+ pa_sample_spec ss;
+
+ int audioservice_fd;
+ int stream_fd;
+
+ int transport;
+ char *strtransport;
+ int link_mtu;
+ size_t block_size;
+ pa_usec_t latency;
+
+ struct bt_a2dp a2dp;
+};
+
+static const char* const valid_modargs[] = {
+ "name",
+ "addr",
+ "profile",
+ "rate",
+ "channels",
+ NULL
+};
+
+static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
+ int e;
+ pa_log_debug("sending %s", bt_audio_strmsg(msg->msg_type));
+ if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
+ e = 0;
+ else {
+ e = -errno;
+ pa_log_error("Error sending data to audio service: %s(%d)", pa_cstrerror(errno), errno);
+ }
+ return e;
+}
+
+static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
+ int e;
+ const char *type;
+
+ pa_log_debug("trying to receive msg from audio service...");
+ if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
+ type = bt_audio_strmsg(inmsg->msg_type);
+ if (type) {
+ pa_log_debug("Received %s", type);
+ e = 0;
+ }
+ else {
+ e = -EINVAL;
+ pa_log_error("Bogus message type %d received from audio service", inmsg->msg_type);
+ }
+ }
+ else {
+ e = -errno;
+ pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno);
+ }
+
+ return e;
+}
+
+static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) {
+ int e = bt_audioservice_recv(sk, rsp_hdr);
+ if (e == 0) {
+ if (rsp_hdr->msg_type != expected_type) {
+ e = -EINVAL;
+ pa_log_error("Bogus message %s received while %s was expected", bt_audio_strmsg(rsp_hdr->msg_type),
+ bt_audio_strmsg(expected_type));
+ }
+ }
+ return e;
+}
+
+static int bt_getcaps(struct userdata *u) {
+ int e;
+ union {
+ bt_audio_rsp_msg_header_t rsp_hdr;
+ struct bt_getcapabilities_req getcaps_req;
+ struct bt_getcapabilities_rsp getcaps_rsp;
+ uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ } msg;
+
+ memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ msg.getcaps_req.h.msg_type = BT_GETCAPABILITIES_REQ;
+ strncpy(msg.getcaps_req.device, u->addr, 18);
+ if (strcasecmp(u->profile, "a2dp") == 0)
+ msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ else if (strcasecmp(u->profile, "hsp") == 0)
+ msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ else {
+ pa_log_error("invalid profile argument: %s", u->profile);
+ return -1;
+ }
+ msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
+
+ e = bt_audioservice_send(u->audioservice_fd, &msg.getcaps_req.h);
+ if (e < 0) {
+ pa_log_error("failed to send GETCAPABILITIES_REQ");
+ return e;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_GETCAPABILITIES_RSP);
+ if (e < 0) {
+ pa_log_error("failed to expect for GETCAPABILITIES_RSP");
+ return e;
+ }
+ if (msg.rsp_hdr.posix_errno != 0) {
+ pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
+ return -msg.rsp_hdr.posix_errno;
+ }
+
+ if ((u->transport = msg.getcaps_rsp.transport) == BT_CAPABILITIES_TRANSPORT_A2DP)
+ u->a2dp.sbc_capabilities = msg.getcaps_rsp.sbc_capabilities;
+
+ return 0;
+}
+
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
+ switch (freq) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ case BT_SBC_SAMPLING_FREQ_32000:
+ return 53;
+ case BT_SBC_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ pa_log_warn("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case BT_SBC_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ pa_log_warn("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ pa_log_warn("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static int bt_a2dp_init(struct userdata *u) {
+ sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
+ unsigned int max_bitpool, min_bitpool;
+
+ switch (u->ss.rate) {
+ case 48000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
+ break;
+ case 44100:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_44100;
+ break;
+ case 32000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_32000;
+ break;
+ case 16000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
+ break;
+ default:
+ pa_log_error("Rate %d not supported", u->ss.rate);
+ return -1;
+ }
+
+ if (u->ss.channels == 2) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ } else {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ }
+
+ if (!cap->channel_mode) {
+ pa_log_error("No supported channel modes");
+ return -1;
+ }
+
+ if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ pa_log_error("No supported block lengths");
+ return -1;
+ }
+
+ if (cap->subbands & BT_A2DP_SUBBANDS_8)
+ cap->subbands = BT_A2DP_SUBBANDS_8;
+ else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+ cap->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ pa_log_error("No supported subbands");
+ return -1;
+ }
+
+ if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+ cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+ cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+ min_bitpool = PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+ max_bitpool = PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return 0;
+}
+
+static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
+ sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
+
+ if (a2dp->sbc_initialized)
+ sbc_reinit(&a2dp->sbc, 0);
+ else
+ sbc_init(&a2dp->sbc, 0);
+ a2dp->sbc_initialized = TRUE;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
+ a2dp->sbc.frequency = SBC_FREQ_16000;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
+ a2dp->sbc.frequency = SBC_FREQ_32000;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
+ a2dp->sbc.frequency = SBC_FREQ_44100;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
+ a2dp->sbc.frequency = SBC_FREQ_48000;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ a2dp->sbc.mode = SBC_MODE_MONO;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ a2dp->sbc.mode = SBC_MODE_STEREO;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+
+ a2dp->sbc.allocation = (active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS);
+
+ switch (active_capabilities.subbands) {
+ case BT_A2DP_SUBBANDS_4:
+ a2dp->sbc.subbands = SBC_SB_4;
+ break;
+ case BT_A2DP_SUBBANDS_8:
+ a2dp->sbc.subbands = SBC_SB_8;
+ break;
+ }
+
+ switch (active_capabilities.block_length) {
+ case BT_A2DP_BLOCK_LENGTH_4:
+ a2dp->sbc.blocks = SBC_BLK_4;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_8:
+ a2dp->sbc.blocks = SBC_BLK_8;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_12:
+ a2dp->sbc.blocks = SBC_BLK_12;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_16:
+ a2dp->sbc.blocks = SBC_BLK_16;
+ break;
+ }
+
+ a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+ a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+}
+
+static int bt_setconf(struct userdata *u) {
+ int e;
+ union {
+ bt_audio_rsp_msg_header_t rsp_hdr;
+ struct bt_setconfiguration_req setconf_req;
+ struct bt_setconfiguration_rsp setconf_rsp;
+ uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ } msg;
+
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ e = bt_a2dp_init(u);
+ if (e < 0) {
+ pa_log_error("a2dp_init error");
+ return e;
+ }
+ u->ss.format = PA_SAMPLE_S16LE;
+ }
+ else
+ u->ss.format = PA_SAMPLE_U8;
+
+ memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ msg.setconf_req.h.msg_type = BT_SETCONFIGURATION_REQ;
+ strncpy(msg.setconf_req.device, u->addr, 18);
+ msg.setconf_req.transport = u->transport;
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ msg.setconf_req.sbc_capabilities = u->a2dp.sbc_capabilities;
+ msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
+
+ e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h);
+ if (e < 0) {
+ pa_log_error("failed to send BT_SETCONFIGURATION_REQ");
+ return e;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_SETCONFIGURATION_RSP);
+ if (e < 0) {
+ pa_log_error("failed to expect BT_SETCONFIGURATION_RSP");
+ return e;
+ }
+
+ if (msg.rsp_hdr.posix_errno != 0) {
+ pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
+ return -msg.rsp_hdr.posix_errno;
+ }
+
+ u->transport = msg.setconf_rsp.transport;
+ u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO"));
+ u->link_mtu = msg.setconf_rsp.link_mtu;
+
+ /* setup SBC encoder now we agree on parameters */
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ bt_a2dp_setup(&u->a2dp);
+ u->block_size = u->a2dp.codesize;
+ pa_log_info("sbc parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
+ }
+ else
+ u->block_size = u->link_mtu;
+
+ return 0;
+}
+
+static int bt_getstreamfd(struct userdata *u) {
+ int e;
+// uint32_t period_count = io->buffer_size / io->period_size;
+ union {
+ bt_audio_rsp_msg_header_t rsp_hdr;
+ struct bt_streamstart_req start_req;
+ struct bt_streamfd_ind streamfd_ind;
+ uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ } msg;
+
+ memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ msg.start_req.h.msg_type = BT_STREAMSTART_REQ;
+
+ e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h);
+ if (e < 0) {
+ pa_log_error("failed to send BT_STREAMSTART_REQ");
+ return e;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_STREAMSTART_RSP);
+ if (e < 0) {
+ pa_log_error("failed to expect BT_STREAMSTART_RSP");
+ return e;
+ }
+
+ if (msg.rsp_hdr.posix_errno != 0) {
+ pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
+ return -msg.rsp_hdr.posix_errno;
+ }
+
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.streamfd_ind.h, BT_STREAMFD_IND);
+ if (e < 0) {
+ pa_log_error("failed to expect BT_STREAMFD_IND");
+ return e;
+ }
+
+ if (u->stream_fd >= 0)
+ pa_close(u->stream_fd);
+
+ u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
+ if (u->stream_fd < 0) {
+ pa_log_error("failed to get data fd: %s (%d)",pa_cstrerror(errno), errno);
+ return -errno;
+ }
+
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ if (pa_socket_set_sndbuf(u->stream_fd, 10*u->link_mtu) < 0) {
+ pa_log_error("failed to set socket options for A2DP: %s (%d)",pa_cstrerror(errno), errno);
+ return -errno;
+ }
+ }
+
+// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
+// return 0;
+// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
+// return 0;
+// /* FIXME : handle error codes */
+ pa_make_fd_nonblock(u->stream_fd);
+// pa_make_socket_low_delay(u->stream_fd);
+
+ return 0;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ pa_log_debug("got message: %d", code);
+ switch (code) {
+
+ case PA_SINK_MESSAGE_SET_STATE:
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+ break;
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+ break;
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t w, r;
+ r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+ w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
+ *((pa_usec_t*) data) = w > r ? w - r : 0;
+ return 0;
+ }
+
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int sco_process_render(struct userdata *u) {
+ void *p;
+ ssize_t l;
+ int write_type = 0;
+
+ u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
+ pa_log_debug("memblock asked size %d", u->block_size);
+ u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
+ pa_log_debug("memchunk length %d", u->memchunk.length);
+ pa_sink_render_into_full(u->sink, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+
+sco_write:
+ l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
+ pa_log_debug("memblock written to socket: %d bytes", l);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+ if (errno == EINTR) {
+ pa_log_debug("EINTR");
+ goto sco_write;
+ }
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+ goto sco_write;
+ }
+ else {
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("memchunk reseted");
+ pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ return -1;
+ }
+ } else {
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("memchunk reseted");
+ u->offset += l;
+ return 0;
+ }
+}
+
+static int a2dp_process_render(struct userdata *u) {
+ ssize_t l;
+ int write_type = 0, written;
+ struct bt_a2dp *a2dp = &u->a2dp;
+ struct rtp_header *header = (void *) a2dp->buffer;
+ struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
+
+ pa_assert(u);
+
+ do {
+ /* Render some data */
+ int frame_size, encoded;
+ void *p;
+
+ u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
+ pa_log_debug("memblock asked size %d", u->block_size);
+ u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
+ pa_log_debug("memchunk length %d", u->memchunk.length);
+ pa_sink_render_into_full(u->sink, &u->memchunk);
+
+ pa_assert(u->memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ frame_size = sbc_get_frame_length(&a2dp->sbc);
+ pa_log_debug("SBC frame_size: %d", frame_size);
+
+ encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
+ sizeof(a2dp->buffer) - a2dp->count, &written);
+ pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
+ if (encoded <= 0) {
+ pa_log_error("SBC encoding error (%d)", encoded);
+ return -1;
+ }
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ pa_log_debug("memchunk reseted");
+
+ a2dp->count += written;
+ a2dp->frame_count++;
+ a2dp->samples += encoded / frame_size;
+ a2dp->nsamples += encoded / frame_size;
+
+ } while (a2dp->count + written <= u->link_mtu);
+
+ /* write it to the fifo */
+ memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+ payload->frame_count = a2dp->frame_count;
+ header->v = 2;
+ header->pt = 1;
+ header->sequence_number = htons(a2dp->seq_num);
+ header->timestamp = htonl(a2dp->nsamples);
+ header->ssrc = htonl(1);
+
+avdtp_write:
+ l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, &write_type);
+ pa_log_debug("avdtp_write: requested %d bytes; written %d bytes", a2dp->count, l);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+ if (errno == EINTR) {
+ pa_log_debug("EINTR");
+ goto avdtp_write;
+ }
+ else if (errno == EAGAIN) {
+ pa_log_debug("EAGAIN");
+ goto avdtp_write;
+ }
+ else {
+ pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ return -1;
+ }
+ }
+
+ u->offset += a2dp->codesize*a2dp->frame_count;
+
+ /* Reset buffer of data to send */
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ a2dp->frame_count = 0;
+ a2dp->samples = 0;
+ a2dp->seq_num++;
+
+ return 0;
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ pa_log_debug("IO Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ for (;;) {
+ int ret, l;
+ struct pollfd *pollfd;
+ uint64_t n;
+ pa_usec_t usec;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ if (u->sink->thread_info.rewind_requested) {
+ pa_sink_process_rewind(u->sink, 0);
+ }
+ }
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ if ((l = a2dp_process_render(u)) < 0)
+ goto fail;
+ }
+ else {
+ if ((l = sco_process_render(u)) < 0)
+ goto fail;
+ }
+ pollfd->revents = 0;
+
+ /* feed the time smoother */
+ n = u->offset;
+ if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= l;
+ usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+ if (usec > u->latency)
+ usec -= u->latency;
+ else
+ usec = 0;
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pa_log_debug("IO thread going to sleep");
+ pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+ pa_log_error("rtpoll_run < 0");
+ goto fail;
+ }
+ pa_log_debug("IO thread waking up");
+
+ if (ret == 0) {
+ pa_log_debug("rtpoll_run == 0");
+ goto finish;
+ }
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ if (pollfd->revents & ~POLLOUT) {
+ pa_log_error("FIFO shutdown.");
+ goto fail;
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
+ pa_log_debug("IO thread failed");
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("IO thread shutting down");
+}
+
+int pa__init(pa_module* m) {
+ int e;
+ pa_modargs *ma;
+ uint32_t channels;
+ pa_sink_new_data data;
+ struct pollfd *pollfd;
+ struct userdata *u;
+
+ pa_assert(m);
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->core = m->core;
+ u->audioservice_fd = -1;
+ u->stream_fd = -1;
+ u->transport = -1;
+ u->offset = 0;
+ u->latency = 0;
+ u->a2dp.sbc_initialized = FALSE;
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+ u->mempool = pa_mempool_new(FALSE);
+ pa_memchunk_reset(&u->memchunk);
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
+ u->rtpoll_item = NULL;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log_error("failed to parse module arguments");
+ goto fail;
+ }
+ if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "name", DEFAULT_SINK_NAME)))) {
+ pa_log_error("failed to get device name from module arguments");
+ goto fail;
+ }
+ if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "addr", NULL)))) {
+ pa_log_error("failed to get device address from module arguments");
+ goto fail;
+ }
+ if (!(u->profile = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) {
+ pa_log_error("failed to get profile from module arguments");
+ goto fail;
+ }
+ if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) {
+ pa_log_error("failed to get rate from module arguments");
+ goto fail;
+ }
+ if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
+ pa_log_error("failed to get channels from module arguments");
+ goto fail;
+ }
+ u->ss.channels = (uint8_t) channels;
+
+ /* connect to the bluez audio service */
+ u->audioservice_fd = bt_audio_service_open();
+ if (u->audioservice_fd <= 0) {
+ pa_log_error("couldn't connect to bluetooth audio service");
+ goto fail;
+ }
+ pa_log_debug("connected to the bluetooth audio service");
+
+ /* queries device capabilities */
+ e = bt_getcaps(u);
+ if (e < 0) {
+ pa_log_error("failed to get device capabilities");
+ goto fail;
+ }
+ pa_log_debug("got device capabilities");
+
+ /* configures the connection */
+ e = bt_setconf(u);
+ if (e < 0) {
+ pa_log_error("failed to set config");
+ goto fail;
+ }
+ pa_log_debug("connection to the device configured");
+
+ /* gets the device socket */
+ e = bt_getstreamfd(u);
+ if (e < 0) {
+ pa_log_error("failed to get stream fd (%d)", e);
+ goto fail;
+ }
+ pa_log_debug("got the device socket");
+
+ /* create sink */
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, u->name);
+ pa_sink_new_data_set_sample_spec(&data, &u->ss);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s '%s' (%s)", u->strtransport, u->name, u->addr);
+ pa_proplist_setf(data.proplist, "bluetooth.protocol", u->profile);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_API, "bluez");
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "headset"); /*FIXME*/
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_VENDOR_PRODUCT_ID, "product_id"); /*FIXME*/
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_SERIAL, "serial"); /*FIXME*/
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+ if (!u->sink) {
+ pa_log_error("failed to create sink");
+ goto fail;
+ }
+ 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->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;
+ pollfd->events = pollfd->revents = 0;
+
+ /* start rt thread */
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log_error("failed to create IO thread");
+ goto fail;
+ }
+ pa_sink_put(u->sink);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+ pa__done(m);
+ return -1;
+}
+
+void pa__done(pa_module *m) {
+ struct userdata *u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ if (u->mempool)
+ pa_mempool_free(u->mempool);
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ if (u->name)
+ pa_xfree(u->name);
+
+ if (u->addr)
+ pa_xfree(u->addr);
+
+ if (u->profile)
+ pa_xfree(u->profile);
+
+ if (u->stream_fd >= 0)
+ pa_close(u->stream_fd);
+
+ if (u->audioservice_fd >= 0)
+ pa_close(u->audioservice_fd);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
new file mode 100644
index 0000000..12473df
--- /dev/null
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -0,0 +1,532 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+
+#include "dbus-util.h"
+#include "module-bluetooth-discover-symdef.h"
+
+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("");
+
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
+#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB"
+#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
+#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
+
+struct uuid {
+ char *uuid;
+ PA_LLIST_FIELDS(struct uuid);
+};
+
+struct device {
+ char *name;
+ char *object_path;
+ int paired;
+ struct adapter *adapter;
+ char *alias;
+ int connected;
+ PA_LLIST_HEAD(struct uuid, uuid_list);
+ char *address;
+ int class;
+ int trusted;
+ PA_LLIST_FIELDS(struct device);
+};
+
+struct adapter {
+ char *object_path;
+ char *mode;
+ char *address;
+ PA_LLIST_HEAD(struct device, device_list);
+ PA_LLIST_FIELDS(struct adapter);
+};
+
+struct userdata {
+ pa_module *module;
+ pa_dbus_connection *conn;
+ PA_LLIST_HEAD(struct adapter, adapter_list);
+};
+
+static struct uuid *uuid_new(const char *uuid) {
+ struct uuid *node = pa_xnew(struct uuid, 1);
+ node->uuid = pa_xstrdup(uuid);
+ PA_LLIST_INIT(struct uuid, node);
+ return node;
+}
+
+static void uuid_free(struct uuid *uuid) {
+ pa_xfree(uuid);
+}
+
+static struct device *device_new(const char *device, struct adapter *adapter) {
+ struct device *node = pa_xnew(struct device, 1);
+ node->name = NULL;
+ node->object_path = pa_xstrdup(device);
+ node->paired = -1;
+ node->adapter = adapter;
+ node->alias = NULL;
+ node->connected = -1;
+ PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
+ node->address = NULL;
+ node->class = -1;
+ node->trusted = -1;
+ PA_LLIST_INIT(struct device, node);
+ return node;
+}
+
+static void device_free(struct device *device) {
+ struct uuid *uuid_list_i;
+ while (device->uuid_list) {
+ uuid_list_i = device->uuid_list;
+ PA_LLIST_REMOVE(struct uuid, device->uuid_list, uuid_list_i);
+ uuid_free(uuid_list_i);
+ }
+ pa_xfree(device);
+}
+
+static struct adapter *adapter_new(const char *adapter) {
+ struct adapter *node = pa_xnew(struct adapter, 1);
+ node->object_path = pa_xstrdup(adapter);
+ node->mode = NULL;
+ node->address = NULL;
+ PA_LLIST_HEAD_INIT(struct device, node->device_list);
+ PA_LLIST_INIT(struct adapter, node);
+ return node;
+}
+
+static void adapter_free(struct adapter *adapter) {
+ struct device *device_list_i;
+ while (adapter->device_list) {
+ device_list_i = adapter->device_list;
+ PA_LLIST_REMOVE(struct device, adapter->device_list, device_list_i);
+ device_free(device_list_i);
+ }
+ pa_xfree(adapter);
+}
+
+static void print_devices(struct device *device_list) {
+ struct device *device_list_i = device_list;
+ while (device_list_i != NULL) {
+ struct uuid *uuid_list_i = device_list_i->uuid_list;
+ if (strcmp(device_list_i->object_path, "/DEVICE_HEAD") != 0) {
+ pa_log(" [ %s ]", device_list_i->object_path);
+ pa_log(" Name = %s", device_list_i->name);
+ pa_log(" Paired = %d", device_list_i->paired);
+ pa_log(" Adapter = %s", device_list_i->adapter->object_path);
+ pa_log(" Alias = %s", device_list_i->alias);
+ pa_log(" Connected = %d", device_list_i->connected);
+ pa_log(" UUIDs = ");
+ while (uuid_list_i != NULL) {
+ if (strcmp(uuid_list_i->uuid, "UUID_HEAD") != 0)
+ pa_log(" %s", uuid_list_i->uuid);
+ uuid_list_i = uuid_list_i->next;
+ }
+ pa_log(" Address = %s", device_list_i->address);
+ pa_log(" Class = 0x%x", device_list_i->class);
+ pa_log(" Trusted = %d", device_list_i->trusted);
+ }
+ device_list_i = device_list_i->next;
+ }
+}
+
+static void print_adapters(struct adapter *adapter_list) {
+ struct adapter *adapter_list_i = adapter_list;
+ while (adapter_list_i != NULL) {
+ if (strcmp(adapter_list_i->object_path, "/ADAPTER_HEAD") != 0) {
+ pa_log("[ %s ]", adapter_list_i->object_path);
+ pa_log(" Mode = %s", adapter_list_i->mode);
+ pa_log(" Address = %s", adapter_list_i->address);
+ print_devices(adapter_list_i->device_list);
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+}
+
+static void detect_adapters(struct userdata *u) {
+ DBusError e;
+ DBusMessage *m = NULL, *r = NULL;
+ DBusMessageIter arg_i, element_i, dict_i, variant_i;
+ struct adapter *adapter_list_i;
+ const char *key, *value;
+
+ pa_assert(u);
+ dbus_error_init(&e);
+
+ /* get adapters */
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Manager.ListAdapters failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Manager.ListAdapters reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Manager.ListAdapters argument is not an array");
+ goto fail;
+ }
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
+ struct adapter *node;
+ dbus_message_iter_get_basic(&element_i, &value);
+ node = adapter_new(value);
+ PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
+ }
+ dbus_message_iter_next(&element_i);
+ }
+
+ /* get adapter properties */
+ adapter_list_i = u->adapter_list;
+ while (adapter_list_i != NULL) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "GetProperties"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Adapter.GetProperties failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Adapter.GetProperties reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Adapter.GetProperties argument is not an array");
+ goto fail;
+ }
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(&element_i, &dict_i);
+ dbus_message_iter_get_basic(&dict_i, &key);
+ dbus_message_iter_next(&dict_i);
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+ dbus_message_iter_get_basic(&variant_i, &value);
+ if (strcmp(key, "Mode") == 0)
+ adapter_list_i->mode = pa_xstrdup(value);
+ else if (strcmp(key, "Address") == 0)
+ adapter_list_i->address = pa_xstrdup(value);
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+ dbus_error_free(&e);
+}
+
+static void detect_devices(struct userdata *u) {
+ DBusError e;
+ DBusMessage *m = NULL, *r = NULL;
+ DBusMessageIter arg_i, element_i, dict_i, variant_i;
+ struct adapter *adapter_list_i;
+ struct device *device_list_i;
+ const char *key, *value;
+ unsigned int uvalue;
+
+ pa_assert(u);
+ dbus_error_init(&e);
+
+ /* get devices of each adapter */
+ adapter_list_i = u->adapter_list;
+ while (adapter_list_i != NULL) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "ListDevices"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Adapter.ListDevices failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Adapter.ListDevices reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Adapter.ListDevices argument is not an array");
+ goto fail;
+ }
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
+ struct device *node;
+ dbus_message_iter_get_basic(&element_i, &value);
+ node = device_new(value, adapter_list_i);
+ PA_LLIST_PREPEND(struct device, adapter_list_i->device_list, node);
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+
+ /* get device properties */
+ adapter_list_i = u->adapter_list;
+ while (adapter_list_i != NULL) {
+ device_list_i = adapter_list_i->device_list;
+ while (device_list_i != NULL) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", device_list_i->object_path, "org.bluez.Device", "GetProperties"));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ if (!r) {
+ pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
+ goto fail;
+ }
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("org.bluez.Device.GetProperties reply has no arguments");
+ goto fail;
+ }
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("org.bluez.Device.GetProperties argument is not an array");
+ goto fail;
+ }
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(&element_i, &dict_i);
+ dbus_message_iter_get_basic(&dict_i, &key);
+ dbus_message_iter_next(&dict_i);
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+ if (strcmp(key, "Name") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &value);
+ device_list_i->name = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Paired") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->paired = uvalue;
+ }
+ else if (strcmp(key, "Alias") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &value);
+ device_list_i->alias = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Connected") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->connected = uvalue;
+ }
+ else if (strcmp(key, "UUIDs") == 0) {
+ DBusMessageIter uuid_i;
+ pa_bool_t is_audio_device = FALSE;
+ dbus_message_iter_recurse(&variant_i, &uuid_i);
+ while (dbus_message_iter_get_arg_type(&uuid_i) != DBUS_TYPE_INVALID) {
+ struct uuid *node;
+ dbus_message_iter_get_basic(&uuid_i, &value);
+ node = uuid_new(value);
+ PA_LLIST_PREPEND(struct uuid, device_list_i->uuid_list, node);
+ if ( (strcasecmp(value, HSP_HS_UUID) == 0) || (strcasecmp(value, HFP_HS_UUID) == 0) ||
+ (strcasecmp(value, A2DP_SOURCE_UUID) == 0) || (strcasecmp(value, A2DP_SINK_UUID) == 0) )
+ is_audio_device = TRUE;
+ dbus_message_iter_next(&uuid_i);
+ }
+ if (!is_audio_device) {
+ /* remove the current device from the list */
+ PA_LLIST_REMOVE(struct device, adapter_list_i->device_list, device_list_i);
+ pa_xfree(device_list_i);
+ break;
+ }
+ }
+ else if (strcmp(key, "Address") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &value);
+ device_list_i->address = pa_xstrdup(value);
+ }
+ else if (strcmp(key, "Class") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->class = uvalue;
+ }
+ else if (strcmp(key, "Trusted") == 0) {
+ dbus_message_iter_get_basic(&variant_i, &uvalue);
+ device_list_i->trusted = uvalue;
+ }
+ }
+ dbus_message_iter_next(&element_i);
+ }
+ device_list_i = device_list_i->next;
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+ dbus_error_free(&e);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
+ DBusMessageIter arg_i;
+ DBusError err;
+ const char *value;
+ struct userdata *u;
+
+ pa_assert(bus);
+ pa_assert(msg);
+ pa_assert(userdata);
+ u = userdata;
+ dbus_error_init(&err);
+
+ pa_log("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(msg),
+ dbus_message_get_path(msg),
+ dbus_message_get_member(msg));
+
+ if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterAdded")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: adapter %s added", value);
+ }
+ }
+ else if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterRemoved")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: adapter %s removed", value);
+ }
+ }
+ else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceCreated")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: device %s created", value);
+ }
+ }
+ else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
+ if (!dbus_message_iter_init(msg, &arg_i))
+ pa_log("dbus: message has no parameters");
+ else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
+ pa_log("dbus: argument is not object path");
+ else {
+ dbus_message_iter_get_basic(&arg_i, &value);
+ pa_log("hcid: device %s removed", value);
+ }
+ }
+
+ dbus_error_free(&err);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+void pa__done(pa_module* m) {
+ struct userdata *u;
+ struct adapter *adapter_list_i;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ while (u->adapter_list) {
+ adapter_list_i = u->adapter_list;
+ PA_LLIST_REMOVE(struct adapter, u->adapter_list, adapter_list_i);
+ adapter_free(adapter_list_i);
+ }
+
+ pa_dbus_connection_unref(u->conn);
+ pa_xfree(u);
+ pa_log("Unloading module-bt-discover");
+ return;
+}
+
+int pa__init(pa_module* m) {
+ DBusError err;
+ struct adapter *adapter_list_i;
+ struct device *device_list_i;
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_log("Loading module-bt-discover");
+ dbus_error_init(&err);
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ PA_LLIST_HEAD_INIT(struct adapter, u->adapter_list);
+
+ /* connect to the bus */
+ u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
+ if ( dbus_error_is_set(&err) || (u->conn == NULL) ) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ goto fail;
+ }
+
+ /* static detection of bluetooth audio devices */
+ detect_adapters(u);
+ detect_devices(u);
+
+ print_adapters(u->adapter_list);
+
+ /* load device modules */
+ adapter_list_i = u->adapter_list;
+ while (adapter_list_i != NULL) {
+ device_list_i = adapter_list_i->device_list;
+ while (device_list_i != NULL) {
+ pa_log("Loading module-bt-device for %s", device_list_i->name);
+ /* TODO: call module */
+ device_list_i = device_list_i->next;
+ }
+ adapter_list_i = adapter_list_i->next;
+ }
+
+ /* dynamic detection of bluetooth audio devices */
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Manager'", &err);
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.Manager signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter'", &err);
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ dbus_error_free(&err);
+ pa__done(m);
+ return -1;
+}
diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h
new file mode 100644
index 0000000..690bd43
--- /dev/null
+++ b/src/modules/bluetooth/rtp.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ uint8_t cc:4;
+ uint8_t x:1;
+ uint8_t p:1;
+ uint8_t v:2;
+
+ uint8_t pt:7;
+ uint8_t m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ uint8_t frame_count:4;
+ uint8_t rfa0:1;
+ uint8_t is_last_fragment:1;
+ uint8_t is_first_fragment:1;
+ uint8_t is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ uint8_t v:2;
+ uint8_t p:1;
+ uint8_t x:1;
+ uint8_t cc:4;
+
+ uint8_t m:1;
+ uint8_t pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ uint8_t is_fragmented:1;
+ uint8_t is_first_fragment:1;
+ uint8_t is_last_fragment:1;
+ uint8_t rfa0:1;
+ uint8_t frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
new file mode 100644
index 0000000..6303421
--- /dev/null
+++ b/src/modules/bluetooth/sbc.c
@@ -0,0 +1,1411 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2008 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* todo items:
+
+ use a log2 table for byte integer scale factors calculation (sum log2 results
+ for high and low bytes) fill bitpool by 16 bits instead of one at a time in
+ bits allocation/bitpool generation port to the dsp
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc.h"
+
+#define SBC_SYNCWORD 0x9C
+
+/* This structure contains an unpacked SBC frame.
+ Yes, there is probably quite some unused space herein */
+struct sbc_frame {
+ uint8_t frequency;
+ uint8_t block_mode;
+ uint8_t blocks;
+ enum {
+ MONO = SBC_MODE_MONO,
+ DUAL_CHANNEL = SBC_MODE_DUAL_CHANNEL,
+ STEREO = SBC_MODE_STEREO,
+ JOINT_STEREO = SBC_MODE_JOINT_STEREO
+ } mode;
+ uint8_t channels;
+ enum {
+ LOUDNESS = SBC_AM_LOUDNESS,
+ SNR = SBC_AM_SNR
+ } allocation;
+ uint8_t subband_mode;
+ uint8_t subbands;
+ uint8_t bitpool;
+ uint8_t codesize;
+ uint8_t length;
+
+ /* bit number x set means joint stereo has been used in subband x */
+ uint8_t joint;
+
+ /* only the lower 4 bits of every element are to be used */
+ uint8_t scale_factor[2][8];
+
+ /* raw integer subband samples in the frame */
+
+ int32_t sb_sample_f[16][2][8];
+ int32_t sb_sample[16][2][8]; /* modified subband samples */
+ int16_t pcm_sample[2][16*8]; /* original pcm audio samples */
+};
+
+struct sbc_decoder_state {
+ int subbands;
+ int32_t V[2][170];
+ int offset[2][16];
+};
+
+struct sbc_encoder_state {
+ int subbands;
+ int position[2];
+ int32_t X[2][160];
+};
+
+/*
+ * Calculates the CRC-8 of the first len bits in data
+ */
+static const uint8_t crc_table[256] = {
+ 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
+ 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
+ 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
+ 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
+ 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
+ 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
+ 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
+ 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
+ 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
+ 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
+ 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
+ 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
+ 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
+ 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
+ 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
+ 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
+ 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
+ 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
+ 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
+ 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
+ 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
+ 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
+ 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
+ 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
+ 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
+ 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
+ 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
+ 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
+ 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
+ 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
+ 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
+ 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
+};
+
+static uint8_t sbc_crc8(const uint8_t *data, size_t len)
+{
+ uint8_t crc = 0x0f;
+ size_t i;
+ uint8_t octet;
+
+ for (i = 0; i < len / 8; i++)
+ crc = crc_table[crc ^ data[i]];
+
+ octet = data[i];
+ for (i = 0; i < len % 8; i++) {
+ char bit = ((octet ^ crc) & 0x80) >> 7;
+
+ crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
+
+ octet = octet << 1;
+ }
+
+ return crc;
+}
+
+/*
+ * Code straight from the spec to calculate the bits array
+ * Takes a pointer to the frame in question, a pointer to the bits array and
+ * the sampling frequency (as 2 bit integer)
+ */
+static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
+{
+ uint8_t sf = frame->frequency;
+
+ if (frame->mode == MONO || frame->mode == DUAL_CHANNEL) {
+ int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+ int ch, sb;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ max_bitneed = 0;
+ if (frame->allocation == SNR) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ bitneed[ch][sb] = frame->scale_factor[ch][sb];
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ } else {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->scale_factor[ch][sb] == 0)
+ bitneed[ch][sb] = -5;
+ else {
+ if (frame->subbands == 4)
+ loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+ else
+ loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+ if (loudness > 0)
+ bitneed[ch][sb] = loudness / 2;
+ else
+ bitneed[ch][sb] = loudness;
+ }
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+
+ bitcount = 0;
+ slicecount = 0;
+ bitslice = max_bitneed + 1;
+ do {
+ bitslice--;
+ bitcount += slicecount;
+ slicecount = 0;
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+ slicecount++;
+ else if (bitneed[ch][sb] == bitslice + 1)
+ slicecount += 2;
+ }
+ } while (bitcount + slicecount < frame->bitpool);
+
+ if (bitcount + slicecount == frame->bitpool) {
+ bitcount += slicecount;
+ bitslice--;
+ }
+
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (bitneed[ch][sb] < bitslice + 2)
+ bits[ch][sb] = 0;
+ else {
+ bits[ch][sb] = bitneed[ch][sb] - bitslice;
+ if (bits[ch][sb] > 16)
+ bits[ch][sb] = 16;
+ }
+ }
+
+ for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) {
+ if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+ bits[ch][sb]++;
+ bitcount++;
+ } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+ bits[ch][sb] = 2;
+ bitcount += 2;
+ }
+ }
+
+ for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) {
+ if (bits[ch][sb] < 16) {
+ bits[ch][sb]++;
+ bitcount++;
+ }
+ }
+
+ }
+
+ } else if (frame->mode == STEREO || frame->mode == JOINT_STEREO) {
+ int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+ int ch, sb;
+
+ max_bitneed = 0;
+ if (frame->allocation == SNR) {
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ bitneed[ch][sb] = frame->scale_factor[ch][sb];
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+ } else {
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->scale_factor[ch][sb] == 0)
+ bitneed[ch][sb] = -5;
+ else {
+ if (frame->subbands == 4)
+ loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+ else
+ loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+ if (loudness > 0)
+ bitneed[ch][sb] = loudness / 2;
+ else
+ bitneed[ch][sb] = loudness;
+ }
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+ }
+
+ bitcount = 0;
+ slicecount = 0;
+ bitslice = max_bitneed + 1;
+ do {
+ bitslice--;
+ bitcount += slicecount;
+ slicecount = 0;
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+ slicecount++;
+ else if (bitneed[ch][sb] == bitslice + 1)
+ slicecount += 2;
+ }
+ }
+ } while (bitcount + slicecount < frame->bitpool);
+
+ if (bitcount + slicecount == frame->bitpool) {
+ bitcount += slicecount;
+ bitslice--;
+ }
+
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (bitneed[ch][sb] < bitslice + 2) {
+ bits[ch][sb] = 0;
+ } else {
+ bits[ch][sb] = bitneed[ch][sb] - bitslice;
+ if (bits[ch][sb] > 16)
+ bits[ch][sb] = 16;
+ }
+ }
+ }
+
+ ch = 0;
+ sb = 0;
+ while (bitcount < frame->bitpool) {
+ if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+ bits[ch][sb]++;
+ bitcount++;
+ } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+ bits[ch][sb] = 2;
+ bitcount += 2;
+ }
+ if (ch == 1) {
+ ch = 0;
+ sb++;
+ if (sb >= frame->subbands) break;
+ } else
+ ch = 1;
+ }
+
+ ch = 0;
+ sb = 0;
+ while (bitcount < frame->bitpool) {
+ if (bits[ch][sb] < 16) {
+ bits[ch][sb]++;
+ bitcount++;
+ }
+ if (ch == 1) {
+ ch = 0;
+ sb++;
+ if (sb >= frame->subbands) break;
+ } else
+ ch = 1;
+ }
+
+ }
+
+}
+
+/*
+ * Unpacks a SBC frame at the beginning of the stream in data,
+ * which has at most len bytes into frame.
+ * Returns the length in bytes of the packed frame, or a negative
+ * value on error. The error codes are:
+ *
+ * -1 Data stream too short
+ * -2 Sync byte incorrect
+ * -3 CRC8 incorrect
+ * -4 Bitpool value out of bounds
+ */
+static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
+ size_t len)
+{
+ int consumed;
+ /* Will copy the parts of the header that are relevant to crc
+ * calculation here */
+ uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int crc_pos = 0;
+ int32_t temp;
+
+ int audio_sample;
+ int ch, sb, blk, bit; /* channel, subband, block and bit standard
+ counters */
+ int bits[2][8]; /* bits distribution */
+ uint32_t levels[2][8]; /* levels derived from that */
+
+ if (len < 4)
+ return -1;
+
+ if (data[0] != SBC_SYNCWORD)
+ return -2;
+
+ frame->frequency = (data[1] >> 6) & 0x03;
+
+ frame->block_mode = (data[1] >> 4) & 0x03;
+ switch (frame->block_mode) {
+ case SBC_BLK_4:
+ frame->blocks = 4;
+ break;
+ case SBC_BLK_8:
+ frame->blocks = 8;
+ break;
+ case SBC_BLK_12:
+ frame->blocks = 12;
+ break;
+ case SBC_BLK_16:
+ frame->blocks = 16;
+ break;
+ }
+
+ frame->mode = (data[1] >> 2) & 0x03;
+ switch (frame->mode) {
+ case MONO:
+ frame->channels = 1;
+ break;
+ case DUAL_CHANNEL: /* fall-through */
+ case STEREO:
+ case JOINT_STEREO:
+ frame->channels = 2;
+ break;
+ }
+
+ frame->allocation = (data[1] >> 1) & 0x01;
+
+ frame->subband_mode = (data[1] & 0x01);
+ frame->subbands = frame->subband_mode ? 8 : 4;
+
+ frame->bitpool = data[2];
+
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > 16 * frame->subbands)
+ return -4;
+
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > 32 * frame->subbands)
+ return -4;
+
+ /* data[3] is crc, we're checking it later */
+
+ consumed = 32;
+
+ crc_header[0] = data[1];
+ crc_header[1] = data[2];
+ crc_pos = 16;
+
+ if (frame->mode == JOINT_STEREO) {
+ if (len * 8 < consumed + frame->subbands)
+ return -1;
+
+ frame->joint = 0x00;
+ for (sb = 0; sb < frame->subbands - 1; sb++)
+ frame->joint |= ((data[4] >> (7 - sb)) & 0x01) << sb;
+ if (frame->subbands == 4)
+ crc_header[crc_pos / 8] = data[4] & 0xf0;
+ else
+ crc_header[crc_pos / 8] = data[4];
+
+ consumed += frame->subbands;
+ crc_pos += frame->subbands;
+ }
+
+ if (len * 8 < consumed + (4 * frame->subbands * frame->channels))
+ return -1;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ /* FIXME assert(consumed % 4 == 0); */
+ frame->scale_factor[ch][sb] =
+ (data[consumed >> 3] >> (4 - (consumed & 0x7))) & 0x0F;
+ crc_header[crc_pos >> 3] |=
+ frame->scale_factor[ch][sb] << (4 - (crc_pos & 0x7));
+
+ consumed += 4;
+ crc_pos += 4;
+ }
+ }
+
+ if (data[3] != sbc_crc8(crc_header, crc_pos))
+ return -3;
+
+ sbc_calculate_bits(frame, bits);
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++)
+ levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+ }
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (levels[ch][sb] > 0) {
+ audio_sample = 0;
+ for (bit = 0; bit < bits[ch][sb]; bit++) {
+ if (consumed > len * 8)
+ return -1;
+
+ if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
+ audio_sample |= 1 << (bits[ch][sb] - bit - 1);
+
+ consumed++;
+ }
+
+ frame->sb_sample[blk][ch][sb] =
+ (((audio_sample << 1) | 1) << frame->scale_factor[ch][sb]) /
+ levels[ch][sb] - (1 << frame->scale_factor[ch][sb]);
+ } else
+ frame->sb_sample[blk][ch][sb] = 0;
+ }
+ }
+ }
+
+ if (frame->mode == JOINT_STEREO) {
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->joint & (0x01 << sb)) {
+ temp = frame->sb_sample[blk][0][sb] +
+ frame->sb_sample[blk][1][sb];
+ frame->sb_sample[blk][1][sb] =
+ frame->sb_sample[blk][0][sb] -
+ frame->sb_sample[blk][1][sb];
+ frame->sb_sample[blk][0][sb] = temp;
+ }
+ }
+ }
+ }
+
+ if ((consumed & 0x7) != 0)
+ consumed += 8 - (consumed & 0x7);
+
+ return consumed >> 3;
+}
+
+static void sbc_decoder_init(struct sbc_decoder_state *state,
+ const struct sbc_frame *frame)
+{
+ int i, ch;
+
+ memset(state->V, 0, sizeof(state->V));
+ state->subbands = frame->subbands;
+
+ for (ch = 0; ch < 2; ch++)
+ for (i = 0; i < frame->subbands * 2; i++)
+ state->offset[ch][i] = (10 * i + 10);
+}
+
+static inline void sbc_synthesize_four(struct sbc_decoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int i, k, idx;
+ int32_t *v = state->V[ch];
+ int *offset = state->offset[ch];
+
+ for (i = 0; i < 8; i++) {
+ /* Shifting */
+ offset[i]--;
+ if (offset[i] < 0) {
+ offset[i] = 79;
+ memcpy(v + 80, v, 9 * sizeof(*v));
+ }
+
+ /* Distribute the new matrix value to the shifted position */
+ v[offset[i]] = SCALE4_STAGED1(
+ MULA(synmatrix4[i][0], frame->sb_sample[blk][ch][0],
+ MULA(synmatrix4[i][1], frame->sb_sample[blk][ch][1],
+ MULA(synmatrix4[i][2], frame->sb_sample[blk][ch][2],
+ MUL (synmatrix4[i][3], frame->sb_sample[blk][ch][3])))));
+ }
+
+ /* Compute the samples */
+ for (idx = 0, i = 0; i < 4; i++, idx += 5) {
+ k = (i + 4) & 0xf;
+
+ /* Store in output, Q0 */
+ frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED2(
+ MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0],
+ MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0],
+ MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1],
+ MULA(v[offset[k] + 3], sbc_proto_4_40m1[idx + 1],
+ MULA(v[offset[i] + 4], sbc_proto_4_40m0[idx + 2],
+ MULA(v[offset[k] + 5], sbc_proto_4_40m1[idx + 2],
+ MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3],
+ MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3],
+ MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4],
+ MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4])))))))))));
+ }
+}
+
+static inline void sbc_synthesize_eight(struct sbc_decoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int i, j, k, idx;
+ int *offset = state->offset[ch];
+
+ for (i = 0; i < 16; i++) {
+ /* Shifting */
+ offset[i]--;
+ if (offset[i] < 0) {
+ offset[i] = 159;
+ for (j = 0; j < 9; j++)
+ state->V[ch][j + 160] = state->V[ch][j];
+ }
+
+ /* Distribute the new matrix value to the shifted position */
+ state->V[ch][offset[i]] = SCALE8_STAGED1(
+ MULA(synmatrix8[i][0], frame->sb_sample[blk][ch][0],
+ MULA(synmatrix8[i][1], frame->sb_sample[blk][ch][1],
+ MULA(synmatrix8[i][2], frame->sb_sample[blk][ch][2],
+ MULA(synmatrix8[i][3], frame->sb_sample[blk][ch][3],
+ MULA(synmatrix8[i][4], frame->sb_sample[blk][ch][4],
+ MULA(synmatrix8[i][5], frame->sb_sample[blk][ch][5],
+ MULA(synmatrix8[i][6], frame->sb_sample[blk][ch][6],
+ MUL( synmatrix8[i][7], frame->sb_sample[blk][ch][7])))))))));
+ }
+
+ /* Compute the samples */
+ for (idx = 0, i = 0; i < 8; i++, idx += 5) {
+ k = (i + 8) & 0xf;
+
+ /* Store in output */
+ frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED2( // Q0
+ MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0],
+ MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0],
+ MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1],
+ MULA(state->V[ch][offset[k] + 3], sbc_proto_8_80m1[idx + 1],
+ MULA(state->V[ch][offset[i] + 4], sbc_proto_8_80m0[idx + 2],
+ MULA(state->V[ch][offset[k] + 5], sbc_proto_8_80m1[idx + 2],
+ MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3],
+ MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3],
+ MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4],
+ MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4])))))))))));
+ }
+}
+
+static int sbc_synthesize_audio(struct sbc_decoder_state *state,
+ struct sbc_frame *frame)
+{
+ int ch, blk;
+
+ switch (frame->subbands) {
+ case 4:
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_synthesize_four(state, frame, ch, blk);
+ }
+ return frame->blocks * 4;
+
+ case 8:
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_synthesize_eight(state, frame, ch, blk);
+ }
+ return frame->blocks * 8;
+
+ default:
+ return -EIO;
+ }
+}
+
+static void sbc_encoder_init(struct sbc_encoder_state *state,
+ const struct sbc_frame *frame)
+{
+ memset(&state->X, 0, sizeof(state->X));
+ state->subbands = frame->subbands;
+ state->position[0] = state->position[1] = 9 * frame->subbands;
+}
+
+static inline void _sbc_analyze_four(const int32_t *in, int32_t *out)
+{
+ sbc_fixed_t t[8], s[5];
+
+ t[0] = SCALE4_STAGE1( /* Q8 */
+ MULA(_sbc_proto_4[0], in[8] - in[32], /* Q18 */
+ MUL( _sbc_proto_4[1], in[16] - in[24])));
+
+ t[1] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[2], in[1],
+ MULA(_sbc_proto_4[3], in[9],
+ MULA(_sbc_proto_4[4], in[17],
+ MULA(_sbc_proto_4[5], in[25],
+ MUL( _sbc_proto_4[6], in[33]))))));
+
+ t[2] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[7], in[2],
+ MULA(_sbc_proto_4[8], in[10],
+ MULA(_sbc_proto_4[9], in[18],
+ MULA(_sbc_proto_4[10], in[26],
+ MUL( _sbc_proto_4[11], in[34]))))));
+
+ t[3] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[12], in[3],
+ MULA(_sbc_proto_4[13], in[11],
+ MULA(_sbc_proto_4[14], in[19],
+ MULA(_sbc_proto_4[15], in[27],
+ MUL( _sbc_proto_4[16], in[35]))))));
+
+ t[4] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[17], in[4] + in[36],
+ MULA(_sbc_proto_4[18], in[12] + in[28],
+ MUL( _sbc_proto_4[19], in[20]))));
+
+ t[5] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[16], in[5],
+ MULA(_sbc_proto_4[15], in[13],
+ MULA(_sbc_proto_4[14], in[21],
+ MULA(_sbc_proto_4[13], in[29],
+ MUL( _sbc_proto_4[12], in[37]))))));
+
+ /* don't compute t[6]... this term always multiplies
+ * with cos(pi/2) = 0 */
+
+ t[7] = SCALE4_STAGE1(
+ MULA(_sbc_proto_4[6], in[7],
+ MULA(_sbc_proto_4[5], in[15],
+ MULA(_sbc_proto_4[4], in[23],
+ MULA(_sbc_proto_4[3], in[31],
+ MUL( _sbc_proto_4[2], in[39]))))));
+
+ s[0] = MUL( _anamatrix4[0], t[0] + t[4]);
+ s[1] = MUL( _anamatrix4[2], t[2]);
+ s[2] = MULA(_anamatrix4[1], t[1] + t[3],
+ MUL(_anamatrix4[3], t[5]));
+ s[3] = MULA(_anamatrix4[3], t[1] + t[3],
+ MUL(_anamatrix4[1], -t[5] + t[7]));
+ s[4] = MUL( _anamatrix4[3], t[7]);
+
+ out[0] = SCALE4_STAGE2( s[0] + s[1] + s[2] + s[4]); /* Q0 */
+ out[1] = SCALE4_STAGE2(-s[0] + s[1] + s[3]);
+ out[2] = SCALE4_STAGE2(-s[0] + s[1] - s[3]);
+ out[3] = SCALE4_STAGE2( s[0] + s[1] - s[2] - s[4]);
+}
+
+static inline void sbc_analyze_four(struct sbc_encoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int32_t *x = &state->X[ch][state->position[ch]];
+ int16_t *pcm = &frame->pcm_sample[ch][blk * 4];
+
+ /* Input 4 Audio Samples */
+ x[40] = x[0] = pcm[3];
+ x[41] = x[1] = pcm[2];
+ x[42] = x[2] = pcm[1];
+ x[43] = x[3] = pcm[0];
+
+ _sbc_analyze_four(x, frame->sb_sample_f[blk][ch]);
+
+ state->position[ch] -= 4;
+ if (state->position[ch] < 0)
+ state->position[ch] = 36;
+}
+
+static inline void _sbc_analyze_eight(const int32_t *in, int32_t *out)
+{
+ sbc_fixed_t t[8], s[8];
+
+ t[0] = SCALE8_STAGE1( /* Q10 */
+ MULA(_sbc_proto_8[0], (in[16] - in[64]), /* Q18 = Q18 * Q0 */
+ MULA(_sbc_proto_8[1], (in[32] - in[48]),
+ MULA(_sbc_proto_8[2], in[4],
+ MULA(_sbc_proto_8[3], in[20],
+ MULA(_sbc_proto_8[4], in[36],
+ MUL( _sbc_proto_8[5], in[52])))))));
+
+ t[1] = SCALE8_STAGE1(
+ MULA(_sbc_proto_8[6], in[2],
+ MULA(_sbc_proto_8[7], in[18],
+ MULA(_sbc_proto_8[8], in[34],
+ MULA(_sbc_proto_8[9], in[50],
+ MUL(_sbc_proto_8[10], in[66]))))));
+
+ t[2] = SCALE8_STAGE1(
+ MULA(_sbc_proto_8[11], in[1],
+ MULA(_sbc_proto_8[12], in[17],
+ MULA(_sbc_proto_8[13], in[33],
+ MULA(_sbc_proto_8[14], in[49],
+ MULA(_sbc_proto_8[15], in[65],
+ MULA(_sbc_proto_8[16], in[3],
+ MULA(_sbc_proto_8[17], in[19],
+ MULA(_sbc_proto_8[18], in[35],
+ MULA(_sbc_proto_8[19], in[51],
+ MUL( _sbc_proto_8[20], in[67])))))))))));
+
+ t[3] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[21], in[5],
+ MULA( _sbc_proto_8[22], in[21],
+ MULA( _sbc_proto_8[23], in[37],
+ MULA( _sbc_proto_8[24], in[53],
+ MULA( _sbc_proto_8[25], in[69],
+ MULA(-_sbc_proto_8[15], in[15],
+ MULA(-_sbc_proto_8[14], in[31],
+ MULA(-_sbc_proto_8[13], in[47],
+ MULA(-_sbc_proto_8[12], in[63],
+ MUL( -_sbc_proto_8[11], in[79])))))))))));
+
+ t[4] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[26], in[6],
+ MULA( _sbc_proto_8[27], in[22],
+ MULA( _sbc_proto_8[28], in[38],
+ MULA( _sbc_proto_8[29], in[54],
+ MULA( _sbc_proto_8[30], in[70],
+ MULA(-_sbc_proto_8[10], in[14],
+ MULA(-_sbc_proto_8[9], in[30],
+ MULA(-_sbc_proto_8[8], in[46],
+ MULA(-_sbc_proto_8[7], in[62],
+ MUL( -_sbc_proto_8[6], in[78])))))))))));
+
+ t[5] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[31], in[7],
+ MULA( _sbc_proto_8[32], in[23],
+ MULA( _sbc_proto_8[33], in[39],
+ MULA( _sbc_proto_8[34], in[55],
+ MULA( _sbc_proto_8[35], in[71],
+ MULA(-_sbc_proto_8[20], in[13],
+ MULA(-_sbc_proto_8[19], in[29],
+ MULA(-_sbc_proto_8[18], in[45],
+ MULA(-_sbc_proto_8[17], in[61],
+ MUL( -_sbc_proto_8[16], in[77])))))))))));
+
+ t[6] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[36], (in[8] + in[72]),
+ MULA( _sbc_proto_8[37], (in[24] + in[56]),
+ MULA( _sbc_proto_8[38], in[40],
+ MULA(-_sbc_proto_8[39], in[12],
+ MULA(-_sbc_proto_8[5], in[28],
+ MULA(-_sbc_proto_8[4], in[44],
+ MULA(-_sbc_proto_8[3], in[60],
+ MUL( -_sbc_proto_8[2], in[76])))))))));
+
+ t[7] = SCALE8_STAGE1(
+ MULA( _sbc_proto_8[35], in[9],
+ MULA( _sbc_proto_8[34], in[25],
+ MULA( _sbc_proto_8[33], in[41],
+ MULA( _sbc_proto_8[32], in[57],
+ MULA( _sbc_proto_8[31], in[73],
+ MULA(-_sbc_proto_8[25], in[11],
+ MULA(-_sbc_proto_8[24], in[27],
+ MULA(-_sbc_proto_8[23], in[43],
+ MULA(-_sbc_proto_8[22], in[59],
+ MUL( -_sbc_proto_8[21], in[75])))))))))));
+
+ s[0] = MULA( _anamatrix8[0], t[0],
+ MUL( _anamatrix8[1], t[6]));
+ s[1] = MUL( _anamatrix8[7], t[1]);
+ s[2] = MULA( _anamatrix8[2], t[2],
+ MULA( _anamatrix8[3], t[3],
+ MULA( _anamatrix8[4], t[5],
+ MUL( _anamatrix8[5], t[7]))));
+ s[3] = MUL( _anamatrix8[6], t[4]);
+ s[4] = MULA( _anamatrix8[3], t[2],
+ MULA(-_anamatrix8[5], t[3],
+ MULA(-_anamatrix8[2], t[5],
+ MUL( -_anamatrix8[4], t[7]))));
+ s[5] = MULA( _anamatrix8[4], t[2],
+ MULA(-_anamatrix8[2], t[3],
+ MULA( _anamatrix8[5], t[5],
+ MUL( _anamatrix8[3], t[7]))));
+ s[6] = MULA( _anamatrix8[1], t[0],
+ MUL( -_anamatrix8[0], t[6]));
+ s[7] = MULA( _anamatrix8[5], t[2],
+ MULA(-_anamatrix8[4], t[3],
+ MULA( _anamatrix8[3], t[5],
+ MUL( -_anamatrix8[2], t[7]))));
+
+ out[0] = SCALE8_STAGE2( s[0] + s[1] + s[2] + s[3]);
+ out[1] = SCALE8_STAGE2( s[1] - s[3] + s[4] + s[6]);
+ out[2] = SCALE8_STAGE2( s[1] - s[3] + s[5] - s[6]);
+ out[3] = SCALE8_STAGE2(-s[0] + s[1] + s[3] + s[7]);
+ out[4] = SCALE8_STAGE2(-s[0] + s[1] + s[3] - s[7]);
+ out[5] = SCALE8_STAGE2( s[1] - s[3] - s[5] - s[6]);
+ out[6] = SCALE8_STAGE2( s[1] - s[3] - s[4] + s[6]);
+ out[7] = SCALE8_STAGE2( s[0] + s[1] - s[2] + s[3]);
+}
+
+static inline void sbc_analyze_eight(struct sbc_encoder_state *state,
+ struct sbc_frame *frame, int ch,
+ int blk)
+{
+ int32_t *x = &state->X[ch][state->position[ch]];
+ int16_t *pcm = &frame->pcm_sample[ch][blk * 8];
+
+ /* Input 8 Audio Samples */
+ x[80] = x[0] = pcm[7];
+ x[81] = x[1] = pcm[6];
+ x[82] = x[2] = pcm[5];
+ x[83] = x[3] = pcm[4];
+ x[84] = x[4] = pcm[3];
+ x[85] = x[5] = pcm[2];
+ x[86] = x[6] = pcm[1];
+ x[87] = x[7] = pcm[0];
+
+ _sbc_analyze_eight(x, frame->sb_sample_f[blk][ch]);
+
+ state->position[ch] -= 8;
+ if (state->position[ch] < 0)
+ state->position[ch] = 72;
+}
+
+static int sbc_analyze_audio(struct sbc_encoder_state *state,
+ struct sbc_frame *frame)
+{
+ int ch, blk;
+
+ switch (frame->subbands) {
+ case 4:
+ for (ch = 0; ch < frame->channels; ch++)
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_analyze_four(state, frame, ch, blk);
+ return frame->blocks * 4;
+
+ case 8:
+ for (ch = 0; ch < frame->channels; ch++)
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_analyze_eight(state, frame, ch, blk);
+ return frame->blocks * 8;
+
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * Packs the SBC frame from frame into the memory at data. At most len
+ * bytes will be used, should more memory be needed an appropriate
+ * error code will be returned. Returns the length of the packed frame
+ * on success or a negative value on error.
+ *
+ * The error codes are:
+ * -1 Not enough memory reserved
+ * -2 Unsupported sampling rate
+ * -3 Unsupported number of blocks
+ * -4 Unsupported number of subbands
+ * -5 Bitpool value out of bounds
+ * -99 not implemented
+ */
+
+static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
+{
+ int produced;
+ /* Will copy the header parts for CRC-8 calculation here */
+ uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int crc_pos = 0;
+
+ uint16_t audio_sample;
+
+ int ch, sb, blk, bit; /* channel, subband, block and bit counters */
+ int bits[2][8]; /* bits distribution */
+ int levels[2][8]; /* levels are derived from that */
+
+ u_int32_t scalefactor[2][8]; /* derived from frame->scale_factor */
+
+ data[0] = SBC_SYNCWORD;
+
+ data[1] = (frame->frequency & 0x03) << 6;
+
+ data[1] |= (frame->block_mode & 0x03) << 4;
+
+ data[1] |= (frame->mode & 0x03) << 2;
+
+ data[1] |= (frame->allocation & 0x01) << 1;
+
+ switch (frame->subbands) {
+ case 4:
+ /* Nothing to do */
+ break;
+ case 8:
+ data[1] |= 0x01;
+ break;
+ default:
+ return -4;
+ break;
+ }
+
+ data[2] = frame->bitpool;
+
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > frame->subbands << 4)
+ return -5;
+
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > frame->subbands << 5)
+ return -5;
+
+ /* Can't fill in crc yet */
+
+ produced = 32;
+
+ crc_header[0] = data[1];
+ crc_header[1] = data[2];
+ crc_pos = 16;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ frame->scale_factor[ch][sb] = 0;
+ scalefactor[ch][sb] = 2;
+ for (blk = 0; blk < frame->blocks; blk++) {
+ while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) {
+ frame->scale_factor[ch][sb]++;
+ scalefactor[ch][sb] *= 2;
+ }
+ }
+ }
+ }
+
+ if (frame->mode == JOINT_STEREO) {
+ /* like frame->sb_sample but joint stereo */
+ int32_t sb_sample_j[16][2];
+ /* scalefactor and scale_factor in joint case */
+ u_int32_t scalefactor_j[2];
+ uint8_t scale_factor_j[2];
+
+ frame->joint = 0;
+
+ for (sb = 0; sb < frame->subbands - 1; sb++) {
+ scale_factor_j[0] = 0;
+ scalefactor_j[0] = 2;
+ scale_factor_j[1] = 0;
+ scalefactor_j[1] = 2;
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ /* Calculate joint stereo signal */
+ sb_sample_j[blk][0] =
+ (frame->sb_sample_f[blk][0][sb] +
+ frame->sb_sample_f[blk][1][sb]) >> 1;
+ sb_sample_j[blk][1] =
+ (frame->sb_sample_f[blk][0][sb] -
+ frame->sb_sample_f[blk][1][sb]) >> 1;
+
+ /* calculate scale_factor_j and scalefactor_j for joint case */
+ while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) {
+ scale_factor_j[0]++;
+ scalefactor_j[0] *= 2;
+ }
+ while (scalefactor_j[1] < fabs(sb_sample_j[blk][1])) {
+ scale_factor_j[1]++;
+ scalefactor_j[1] *= 2;
+ }
+ }
+
+ /* decide whether to join this subband */
+ if ((scalefactor[0][sb] + scalefactor[1][sb]) >
+ (scalefactor_j[0] + scalefactor_j[1]) ) {
+ /* use joint stereo for this subband */
+ frame->joint |= 1 << sb;
+ frame->scale_factor[0][sb] = scale_factor_j[0];
+ frame->scale_factor[1][sb] = scale_factor_j[1];
+ scalefactor[0][sb] = scalefactor_j[0];
+ scalefactor[1][sb] = scalefactor_j[1];
+ for (blk = 0; blk < frame->blocks; blk++) {
+ frame->sb_sample_f[blk][0][sb] =
+ sb_sample_j[blk][0];
+ frame->sb_sample_f[blk][1][sb] =
+ sb_sample_j[blk][1];
+ }
+ }
+ }
+
+ data[4] = 0;
+ for (sb = 0; sb < frame->subbands - 1; sb++)
+ data[4] |= ((frame->joint >> sb) & 0x01) << (frame->subbands - 1 - sb);
+
+ crc_header[crc_pos >> 3] = data[4];
+
+ produced += frame->subbands;
+ crc_pos += frame->subbands;
+ }
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ data[produced >> 3] <<= 4;
+ crc_header[crc_pos >> 3] <<= 4;
+ data[produced >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+ crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+
+ produced += 4;
+ crc_pos += 4;
+ }
+ }
+
+ /* align the last crc byte */
+ if (crc_pos % 8)
+ crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8);
+
+ data[3] = sbc_crc8(crc_header, crc_pos);
+
+ sbc_calculate_bits(frame, bits);
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++)
+ levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+ }
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (levels[ch][sb] > 0) {
+ audio_sample =
+ (uint16_t) ((((frame->sb_sample_f[blk][ch][sb]*levels[ch][sb]) >>
+ (frame->scale_factor[ch][sb] + 1)) +
+ levels[ch][sb]) >> 1);
+ audio_sample <<= 16 - bits[ch][sb];
+ for (bit = 0; bit < bits[ch][sb]; bit++) {
+ data[produced >> 3] <<= 1;
+ if (audio_sample & 0x8000)
+ data[produced >> 3] |= 0x1;
+ audio_sample <<= 1;
+ produced++;
+ }
+ }
+ }
+ }
+ }
+
+ /* align the last byte */
+ if (produced % 8) {
+ data[produced >> 3] <<= 8 - (produced % 8);
+ }
+
+ return (produced + 7) >> 3;
+}
+
+struct sbc_priv {
+ int init;
+ struct sbc_frame frame;
+ struct sbc_decoder_state dec_state;
+ struct sbc_encoder_state enc_state;
+};
+
+static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
+{
+ sbc->frequency = SBC_FREQ_44100;
+ sbc->mode = SBC_MODE_STEREO;
+ sbc->subbands = SBC_SB_8;
+ sbc->blocks = SBC_BLK_16;
+ sbc->bitpool = 32;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ sbc->endian = SBC_LE;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ sbc->endian = SBC_BE;
+#else
+#error "Unknown byte order"
+#endif
+}
+
+int sbc_init(sbc_t *sbc, unsigned long flags)
+{
+ if (!sbc)
+ return -EIO;
+
+ memset(sbc, 0, sizeof(sbc_t));
+
+ sbc->priv = malloc(sizeof(struct sbc_priv));
+ if (!sbc->priv)
+ return -ENOMEM;
+
+ memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+ sbc_set_defaults(sbc, flags);
+
+ return 0;
+}
+
+int sbc_parse(sbc_t *sbc, void *input, int input_len)
+{
+ return sbc_decode(sbc, input, input_len, NULL, 0, NULL);
+}
+
+int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *written)
+{
+ struct sbc_priv *priv;
+ char *ptr;
+ int i, ch, framelen, samples;
+
+ if (!sbc && !input)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ framelen = sbc_unpack_frame(input, &priv->frame, input_len);
+
+ if (!priv->init) {
+ sbc_decoder_init(&priv->dec_state, &priv->frame);
+ priv->init = 1;
+
+ sbc->frequency = priv->frame.frequency;
+ sbc->mode = priv->frame.mode;
+ sbc->subbands = priv->frame.subband_mode;
+ sbc->blocks = priv->frame.block_mode;
+ sbc->allocation = priv->frame.allocation;
+ sbc->bitpool = priv->frame.bitpool;
+
+ priv->frame.codesize = sbc_get_codesize(sbc);
+ priv->frame.length = sbc_get_frame_length(sbc);
+ }
+
+ if (!output)
+ return framelen;
+
+ if (written)
+ *written = 0;
+
+ samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame);
+
+ ptr = output;
+
+ if (output_len < samples * priv->frame.channels * 2)
+ samples = output_len / (priv->frame.channels * 2);
+
+ for (i = 0; i < samples; i++) {
+ for (ch = 0; ch < priv->frame.channels; ch++) {
+ int16_t s;
+ s = priv->frame.pcm_sample[ch][i];
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (sbc->endian == SBC_BE) {
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (sbc->endian == SBC_LE) {
+#else
+#error "Unknown byte order"
+#endif
+ *ptr++ = (s & 0xff00) >> 8;
+ *ptr++ = (s & 0x00ff);
+ } else {
+ *ptr++ = (s & 0x00ff);
+ *ptr++ = (s & 0xff00) >> 8;
+ }
+ }
+ }
+
+ if (written)
+ *written = samples * priv->frame.channels * 2;
+
+ return framelen;
+}
+
+int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *written)
+{
+ struct sbc_priv *priv;
+ char *ptr;
+ int i, ch, framelen, samples;
+
+ if (!sbc && !input)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ if (written)
+ *written = 0;
+
+ if (!priv->init) {
+ priv->frame.frequency = sbc->frequency;
+ priv->frame.mode = sbc->mode;
+ priv->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ priv->frame.allocation = sbc->allocation;
+ priv->frame.subband_mode = sbc->subbands;
+ priv->frame.subbands = sbc->subbands ? 8 : 4;
+ priv->frame.block_mode = sbc->blocks;
+ priv->frame.blocks = 4 + (sbc->blocks * 4);
+ priv->frame.bitpool = sbc->bitpool;
+ priv->frame.codesize = sbc_get_codesize(sbc);
+ priv->frame.length = sbc_get_frame_length(sbc);
+
+ sbc_encoder_init(&priv->enc_state, &priv->frame);
+ priv->init = 1;
+ }
+
+ /* input must be large enough to encode a complete frame */
+ if (input_len < priv->frame.codesize)
+ return 0;
+
+ /* output must be large enough to receive the encoded frame */
+ if (!output || output_len < priv->frame.length)
+ return -ENOSPC;
+
+ ptr = input;
+
+ for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) {
+ for (ch = 0; ch < priv->frame.channels; ch++) {
+ int16_t s;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (sbc->endian == SBC_BE)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (sbc->endian == SBC_LE)
+#else
+#error "Unknown byte order"
+#endif
+ s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff);
+ else
+ s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8;
+ ptr += 2;
+ priv->frame.pcm_sample[ch][i] = s;
+ }
+ }
+
+ samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
+
+ framelen = sbc_pack_frame(output, &priv->frame, output_len);
+
+ if (written)
+ *written = framelen;
+
+ return samples * priv->frame.channels * 2;
+}
+
+void sbc_finish(sbc_t *sbc)
+{
+ if (!sbc)
+ return;
+
+ if (sbc->priv)
+ free(sbc->priv);
+
+ memset(sbc, 0, sizeof(sbc_t));
+}
+
+int sbc_get_frame_length(sbc_t *sbc)
+{
+ int ret;
+ uint8_t subbands, channels, blocks, joint;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ channels = priv->frame.channels;
+ joint = priv->frame.joint;
+ }
+
+ ret = 4 + (4 * subbands * channels) / 8;
+
+ /* This term is not always evenly divide so we round it up */
+ if (channels == 1)
+ ret += ((blocks * channels * sbc->bitpool) + 7) / 8;
+ else
+ ret += (((joint ? subbands : 0) + blocks * sbc->bitpool) + 7)
+ / 8;
+
+ return ret;
+}
+
+int sbc_get_frame_duration(sbc_t *sbc)
+{
+ uint8_t subbands, blocks;
+ uint16_t frequency;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ }
+
+ switch (sbc->frequency) {
+ case SBC_FREQ_16000:
+ frequency = 16000;
+ break;
+
+ case SBC_FREQ_32000:
+ frequency = 32000;
+ break;
+
+ case SBC_FREQ_44100:
+ frequency = 44100;
+ break;
+
+ case SBC_FREQ_48000:
+ frequency = 48000;
+ break;
+ default:
+ return 0;
+ }
+
+ return (1000000 * blocks * subbands) / frequency;
+}
+
+int sbc_get_codesize(sbc_t *sbc)
+{
+ uint8_t subbands, channels, blocks;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ channels = priv->frame.channels;
+ }
+
+ return subbands * blocks * channels * 2;
+}
+
+int sbc_reinit(sbc_t *sbc, unsigned long flags)
+{
+ struct sbc_priv *priv;
+
+ if (!sbc || !sbc->priv)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ if (priv->init == 1)
+ memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+ sbc_set_defaults(sbc, flags);
+
+ return 0;
+}
diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h
new file mode 100644
index 0000000..ab47e32
--- /dev/null
+++ b/src/modules/bluetooth/sbc.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_H
+#define __SBC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/* sampling frequency */
+#define SBC_FREQ_16000 0x00
+#define SBC_FREQ_32000 0x01
+#define SBC_FREQ_44100 0x02
+#define SBC_FREQ_48000 0x03
+
+/* blocks */
+#define SBC_BLK_4 0x00
+#define SBC_BLK_8 0x01
+#define SBC_BLK_12 0x02
+#define SBC_BLK_16 0x03
+
+/* channel mode */
+#define SBC_MODE_MONO 0x00
+#define SBC_MODE_DUAL_CHANNEL 0x01
+#define SBC_MODE_STEREO 0x02
+#define SBC_MODE_JOINT_STEREO 0x03
+
+/* allocation method */
+#define SBC_AM_LOUDNESS 0x00
+#define SBC_AM_SNR 0x01
+
+/* subbands */
+#define SBC_SB_4 0x00
+#define SBC_SB_8 0x01
+
+/* Data endianess */
+#define SBC_LE 0x00
+#define SBC_BE 0x01
+
+struct sbc_struct {
+ unsigned long flags;
+
+ uint8_t frequency;
+ uint8_t blocks;
+ uint8_t subbands;
+ uint8_t mode;
+ uint8_t allocation;
+ uint8_t bitpool;
+ uint8_t endian;
+
+ void *priv;
+};
+
+typedef struct sbc_struct sbc_t;
+
+int sbc_init(sbc_t *sbc, unsigned long flags);
+int sbc_reinit(sbc_t *sbc, unsigned long flags);
+int sbc_parse(sbc_t *sbc, void *input, int input_len);
+int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *len);
+int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
+ int output_len, int *written);
+int sbc_get_frame_length(sbc_t *sbc);
+int sbc_get_frame_duration(sbc_t *sbc);
+int sbc_get_codesize(sbc_t *sbc);
+void sbc_finish(sbc_t *sbc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SBC_H */
diff --git a/src/modules/bluetooth/sbc_math.h b/src/modules/bluetooth/sbc_math.h
new file mode 100644
index 0000000..b3d87a6
--- /dev/null
+++ b/src/modules/bluetooth/sbc_math.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2008 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define fabs(x) ((x) < 0 ? -(x) : (x))
+/* C does not provide an explicit arithmetic shift right but this will
+ always be correct and every compiler *should* generate optimal code */
+#define ASR(val, bits) ((-2 >> 1 == -1) ? \
+ ((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
+
+#define SCALE_PROTO4_TBL 15
+#define SCALE_ANA4_TBL 17
+#define SCALE_PROTO8_TBL 16
+#define SCALE_ANA8_TBL 17
+#define SCALE_SPROTO4_TBL 12
+#define SCALE_SPROTO8_TBL 14
+#define SCALE_NPROTO4_TBL 11
+#define SCALE_NPROTO8_TBL 11
+#define SCALE4_STAGE1_BITS 15
+#define SCALE4_STAGE2_BITS 16
+#define SCALE4_STAGED1_BITS 15
+#define SCALE4_STAGED2_BITS 16
+#define SCALE8_STAGE1_BITS 15
+#define SCALE8_STAGE2_BITS 15
+#define SCALE8_STAGED1_BITS 15
+#define SCALE8_STAGED2_BITS 16
+
+typedef int32_t sbc_fixed_t;
+
+#define SCALE4_STAGE1(src) ASR(src, SCALE4_STAGE1_BITS)
+#define SCALE4_STAGE2(src) ASR(src, SCALE4_STAGE2_BITS)
+#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS)
+#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS)
+#define SCALE8_STAGE1(src) ASR(src, SCALE8_STAGE1_BITS)
+#define SCALE8_STAGE2(src) ASR(src, SCALE8_STAGE2_BITS)
+#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS)
+#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS)
+
+#define SBC_FIXED_0(val) { val = 0; }
+#define MUL(a, b) ((a) * (b))
+#ifdef __arm__
+#define MULA(a, b, res) ({ \
+ int tmp = res; \
+ __asm__( \
+ "mla %0, %2, %3, %0" \
+ : "=&r" (tmp) \
+ : "0" (tmp), "r" (a), "r" (b)); \
+ tmp; })
+#else
+#define MULA(a, b, res) ((a) * (b) + (res))
+#endif
diff --git a/src/modules/bluetooth/sbc_tables.h b/src/modules/bluetooth/sbc_tables.h
new file mode 100644
index 0000000..7ac4e68
--- /dev/null
+++ b/src/modules/bluetooth/sbc_tables.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley at xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset4[4][4] = {
+ { -1, 0, 0, 0 },
+ { -2, 0, 0, 1 },
+ { -2, 0, 0, 1 },
+ { -2, 0, 0, 1 }
+};
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset8[4][8] = {
+ { -2, 0, 0, 0, 0, 0, 0, 1 },
+ { -3, 0, 0, 0, 0, 0, 1, 2 },
+ { -4, 0, 0, 0, 0, 0, 1, 2 },
+ { -4, 0, 0, 0, 0, 0, 1, 2 }
+};
+
+#define SP4(val) ASR(val, SCALE_PROTO4_TBL)
+#define SA4(val) ASR(val, SCALE_ANA4_TBL)
+#define SP8(val) ASR(val, SCALE_PROTO8_TBL)
+#define SA8(val) ASR(val, SCALE_ANA8_TBL)
+#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
+#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
+#define SN4(val) ASR(val, SCALE_NPROTO4_TBL)
+#define SN8(val) ASR(val, SCALE_NPROTO8_TBL)
+
+static const int32_t _sbc_proto_4[20] = {
+ SP4(0x02cb3e8c), SP4(0x22b63dc0), SP4(0x002329cc), SP4(0x053b7548),
+ SP4(0x31eab940), SP4(0xec1f5e60), SP4(0xff3773a8), SP4(0x0061c5a7),
+ SP4(0x07646680), SP4(0x3f239480), SP4(0xf89f23a8), SP4(0x007a4737),
+ SP4(0x00b32807), SP4(0x083ddc80), SP4(0x4825e480), SP4(0x0191e578),
+ SP4(0x00ff11ca), SP4(0x00fb7991), SP4(0x069fdc58), SP4(0x4b584000)
+};
+
+static const int32_t _anamatrix4[4] = {
+ SA4(0x2d413cc0), SA4(0x3b20d780), SA4(0x40000000), SA4(0x187de2a0)
+};
+
+static const int32_t _sbc_proto_8[40] = {
+ SP8(0x02e5cd20), SP8(0x22d0c200), SP8(0x006bfe27), SP8(0x07808930),
+ SP8(0x3f1c8800), SP8(0xf8810d70), SP8(0x002cfdc6), SP8(0x055acf28),
+ SP8(0x31f566c0), SP8(0xebfe57e0), SP8(0xff27c437), SP8(0x001485cc),
+ SP8(0x041c6e58), SP8(0x2a7cfa80), SP8(0xe4c4a240), SP8(0xfe359e4c),
+ SP8(0x0048b1f8), SP8(0x0686ce30), SP8(0x38eec5c0), SP8(0xf2a1b9f0),
+ SP8(0xffe8904a), SP8(0x0095698a), SP8(0x0824a480), SP8(0x443b3c00),
+ SP8(0xfd7badc8), SP8(0x00d3e2d9), SP8(0x00c183d2), SP8(0x084e1950),
+ SP8(0x4810d800), SP8(0x017f43fe), SP8(0x01056dd8), SP8(0x00e9cb9f),
+ SP8(0x07d7d090), SP8(0x4a708980), SP8(0x0488fae8), SP8(0x0113bd20),
+ SP8(0x0107b1a8), SP8(0x069fb3c0), SP8(0x4b3db200), SP8(0x00763f48)
+};
+
+static const int32_t sbc_proto_4_40m0[] = {
+ SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
+ SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8),
+ SS4(0x027c1434), SS4(0x0019118b), SS4(0xfff3c74c), SS4(0xff137330),
+ SS4(0xf81b8d70), SS4(0x00ec1b8b), SS4(0xfff0b71a), SS4(0xffe99b00),
+ SS4(0xfef84470), SS4(0xf6fb4370), SS4(0xffcdc351), SS4(0xffe01dc7)
+};
+
+static const int32_t sbc_proto_4_40m1[] = {
+ SS4(0xffe090ce), SS4(0xff2c0475), SS4(0xf694f800), SS4(0xff2c0475),
+ SS4(0xffe090ce), SS4(0xffe01dc7), SS4(0xffcdc351), SS4(0xf6fb4370),
+ SS4(0xfef84470), SS4(0xffe99b00), SS4(0xfff0b71a), SS4(0x00ec1b8b),
+ SS4(0xf81b8d70), SS4(0xff137330), SS4(0xfff3c74c), SS4(0x0019118b),
+ SS4(0x027c1434), SS4(0xf9c2a8d8), SS4(0xff589157), SS4(0xfffb9ac7)
+};
+
+static const int32_t sbc_proto_8_80m0[] = {
+ SS8(0x00000000), SS8(0xfe8d1970), SS8(0xee979f00), SS8(0x11686100),
+ SS8(0x0172e690), SS8(0xfff5bd1a), SS8(0xfdf1c8d4), SS8(0xeac182c0),
+ SS8(0x0d9daee0), SS8(0x00e530da), SS8(0xffe9811d), SS8(0xfd52986c),
+ SS8(0xe7054ca0), SS8(0x0a00d410), SS8(0x006c1de4), SS8(0xffdba705),
+ SS8(0xfcbc98e8), SS8(0xe3889d20), SS8(0x06af2308), SS8(0x000bb7db),
+ SS8(0xffca00ed), SS8(0xfc3fbb68), SS8(0xe071bc00), SS8(0x03bf7948),
+ SS8(0xffc4e05c), SS8(0xffb54b3b), SS8(0xfbedadc0), SS8(0xdde26200),
+ SS8(0x0142291c), SS8(0xff960e94), SS8(0xff9f3e17), SS8(0xfbd8f358),
+ SS8(0xdbf79400), SS8(0xff405e01), SS8(0xff7d4914), SS8(0xff8b1a31),
+ SS8(0xfc1417b8), SS8(0xdac7bb40), SS8(0xfdbb828c), SS8(0xff762170)
+};
+
+static const int32_t sbc_proto_8_80m1[] = {
+ SS8(0xff7c272c), SS8(0xfcb02620), SS8(0xda612700), SS8(0xfcb02620),
+ SS8(0xff7c272c), SS8(0xff762170), SS8(0xfdbb828c), SS8(0xdac7bb40),
+ SS8(0xfc1417b8), SS8(0xff8b1a31), SS8(0xff7d4914), SS8(0xff405e01),
+ SS8(0xdbf79400), SS8(0xfbd8f358), SS8(0xff9f3e17), SS8(0xff960e94),
+ SS8(0x0142291c), SS8(0xdde26200), SS8(0xfbedadc0), SS8(0xffb54b3b),
+ SS8(0xffc4e05c), SS8(0x03bf7948), SS8(0xe071bc00), SS8(0xfc3fbb68),
+ SS8(0xffca00ed), SS8(0x000bb7db), SS8(0x06af2308), SS8(0xe3889d20),
+ SS8(0xfcbc98e8), SS8(0xffdba705), SS8(0x006c1de4), SS8(0x0a00d410),
+ SS8(0xe7054ca0), SS8(0xfd52986c), SS8(0xffe9811d), SS8(0x00e530da),
+ SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a)
+};
+
+static const int32_t _anamatrix8[8] = {
+ SA8(0x3b20d780), SA8(0x187de2a0), SA8(0x3ec52f80), SA8(0x3536cc40),
+ SA8(0x238e7680), SA8(0x0c7c5c20), SA8(0x2d413cc0), SA8(0x40000000)
+};
+
+static const int32_t synmatrix4[8][4] = {
+ { SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) },
+ { SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) },
+ { SN4(0x00000000), SN4(0x00000000), SN4(0x00000000), SN4(0x00000000) },
+ { SN4(0xfcf043ac), SN4(0x07641af0), SN4(0xf89be510), SN4(0x030fbc54) },
+ { SN4(0xfa57d868), SN4(0x05a82798), SN4(0x05a82798), SN4(0xfa57d868) },
+ { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) },
+ { SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000) },
+ { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) }
+};
+
+static const int32_t synmatrix8[16][8] = {
+ { SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798),
+ SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798) },
+ { SN8(0x0471ced0), SN8(0xf8275a10), SN8(0x018f8b84), SN8(0x06a6d988),
+ SN8(0xf9592678), SN8(0xfe70747c), SN8(0x07d8a5f0), SN8(0xfb8e3130) },
+ { SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac),
+ SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54) },
+ { SN8(0x018f8b84), SN8(0xfb8e3130), SN8(0x06a6d988), SN8(0xf8275a10),
+ SN8(0x07d8a5f0), SN8(0xf9592678), SN8(0x0471ced0), SN8(0xfe70747c) },
+ { SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000),
+ SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000) },
+ { SN8(0xfe70747c), SN8(0x0471ced0), SN8(0xf9592678), SN8(0x07d8a5f0),
+ SN8(0xf8275a10), SN8(0x06a6d988), SN8(0xfb8e3130), SN8(0x018f8b84) },
+ { SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54),
+ SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac) },
+ { SN8(0xfb8e3130), SN8(0x07d8a5f0), SN8(0xfe70747c), SN8(0xf9592678),
+ SN8(0x06a6d988), SN8(0x018f8b84), SN8(0xf8275a10), SN8(0x0471ced0) },
+ { SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868),
+ SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868) },
+ { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+ SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) },
+ { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+ SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+ { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+ SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+ { SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000),
+ SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000) },
+ { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+ SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+ { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+ SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+ { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+ SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }
+};
diff --git a/src/modules/bt-ipc.c b/src/modules/bt-ipc.c
deleted file mode 100644
index e585328..0000000
--- a/src/modules/bt-ipc.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include "bt-ipc.h"
-
-/* This table contains the string representation for messages */
-static const char *strmsg[] = {
- "BT_GETCAPABILITIES_REQ",
- "BT_GETCAPABILITIES_RSP",
- "BT_SETCONFIGURATION_REQ",
- "BT_SETCONFIGURATION_RSP",
- "BT_STREAMSTART_REQ",
- "BT_STREAMSTART_RSP",
- "BT_STREAMSTOP_REQ",
- "BT_STREAMSTOP_RSP",
- "BT_STREAMSUSPEND_IND",
- "BT_STREAMRESUME_IND",
- "BT_CONTROL_REQ",
- "BT_CONTROL_RSP",
- "BT_CONTROL_IND",
- "BT_STREAMFD_IND",
-};
-
-int bt_audio_service_open()
-{
- int sk;
- int err;
- struct sockaddr_un addr = {
- AF_UNIX, BT_IPC_SOCKET_NAME
- };
-
- sk = socket(PF_LOCAL, SOCK_STREAM, 0);
- if (sk < 0) {
- err = errno;
- fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
- __FUNCTION__, strerror(err), err);
- errno = err;
- return -1;
- }
-
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = errno;
- fprintf(stderr, "%s: connect() failed: %s (%d)\n",
- __FUNCTION__, strerror(err), err);
- close(sk);
- errno = err;
- return -1;
- }
-
- return sk;
-}
-
-int bt_audio_service_close(int sk)
-{
- return close(sk);
-}
-
-int bt_audio_service_get_data_fd(int sk)
-{
- char cmsg_b[CMSG_SPACE(sizeof(int))], m;
- int err, ret;
- struct iovec iov = { &m, sizeof(m) };
- struct msghdr msgh;
- struct cmsghdr *cmsg;
-
- memset(&msgh, 0, sizeof(msgh));
- msgh.msg_iov = &iov;
- msgh.msg_iovlen = 1;
- msgh.msg_control = &cmsg_b;
- msgh.msg_controllen = CMSG_LEN(sizeof(int));
-
- ret = recvmsg(sk, &msgh, 0);
- if (ret < 0) {
- err = errno;
- fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
- __FUNCTION__, strerror(err), err);
- errno = err;
- return -1;
- }
-
- /* Receive auxiliary data in msgh */
- for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET
- && cmsg->cmsg_type == SCM_RIGHTS)
- return (*(int *) CMSG_DATA(cmsg));
- }
-
- errno = EINVAL;
- return -1;
-}
-
-const char *bt_audio_strmsg(int type)
-{
- if (type < 0 || type > (sizeof(strmsg) / sizeof(strmsg[0])))
- return NULL;
-
- return strmsg[type];
-}
-
diff --git a/src/modules/bt-ipc.h b/src/modules/bt-ipc.h
deleted file mode 100644
index c900fcd..0000000
--- a/src/modules/bt-ipc.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-/*
- Message sequence chart of streaming sequence for A2DP transport
-
- Audio daemon User
- on snd_pcm_open
- <--BT_GETCAPABILITIES_REQ
-
- BT_GETCAPABILITIES_RSP-->
-
- on snd_pcm_hw_params
- <--BT_SETCONFIGURATION_REQ
-
- BT_SETCONFIGURATION_RSP-->
-
- on snd_pcm_prepare
- <--BT_STREAMSTART_REQ
-
- <Moves to streaming state>
- BT_STREAMSTART_RSP-->
-
- BT_STREAMFD_IND -->
-
- < streams data >
- ..........
-
- on snd_pcm_drop/snd_pcm_drain
-
- <--BT_STREAMSTOP_REQ
-
- <Moves to open state>
- BT_STREAMSTOP_RSP-->
-
- on IPC close or appl crash
- <Moves to idle>
-
- */
-
-#ifndef BT_AUDIOCLIENT_H
-#define BT_AUDIOCLIENT_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <errno.h>
-
-#define BT_AUDIO_IPC_PACKET_SIZE 128
-#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
-
-/* Generic message header definition, except for RSP messages */
-typedef struct {
- uint8_t msg_type;
-} __attribute__ ((packed)) bt_audio_msg_header_t;
-
-/* Generic message header definition, for all RSP messages */
-typedef struct {
- bt_audio_msg_header_t msg_h;
- uint8_t posix_errno;
-} __attribute__ ((packed)) bt_audio_rsp_msg_header_t;
-
-/* Messages list */
-#define BT_GETCAPABILITIES_REQ 0
-#define BT_GETCAPABILITIES_RSP 1
-
-#define BT_SETCONFIGURATION_REQ 2
-#define BT_SETCONFIGURATION_RSP 3
-
-#define BT_STREAMSTART_REQ 4
-#define BT_STREAMSTART_RSP 5
-
-#define BT_STREAMSTOP_REQ 6
-#define BT_STREAMSTOP_RSP 7
-
-#define BT_STREAMSUSPEND_IND 8
-#define BT_STREAMRESUME_IND 9
-
-#define BT_CONTROL_REQ 10
-#define BT_CONTROL_RSP 11
-#define BT_CONTROL_IND 12
-
-#define BT_STREAMFD_IND 13
-
-/* BT_GETCAPABILITIES_REQ */
-
-#define BT_CAPABILITIES_TRANSPORT_A2DP 0
-#define BT_CAPABILITIES_TRANSPORT_SCO 1
-#define BT_CAPABILITIES_TRANSPORT_ANY 2
-
-#define BT_CAPABILITIES_ACCESS_MODE_READ 1
-#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2
-#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3
-
-#define BT_FLAG_AUTOCONNECT 1
-
-struct bt_getcapabilities_req {
- bt_audio_msg_header_t h;
- char device[18]; /* Address of the remote Device */
- uint8_t transport; /* Requested transport */
- uint8_t flags; /* Requested flags */
-} __attribute__ ((packed));
-
-/* BT_GETCAPABILITIES_RSP */
-
-/**
- * SBC Codec parameters as per A2DP profile 1.0 § 4.3
- */
-
-#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3)
-#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2)
-#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1)
-#define BT_SBC_SAMPLING_FREQ_48000 1
-
-#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3)
-#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
-#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1)
-#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1
-
-#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3)
-#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2)
-#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1)
-#define BT_A2DP_BLOCK_LENGTH_16 1
-
-#define BT_A2DP_SUBBANDS_4 (1 << 1)
-#define BT_A2DP_SUBBANDS_8 1
-
-#define BT_A2DP_ALLOCATION_SNR (1 << 1)
-#define BT_A2DP_ALLOCATION_LOUDNESS 1
-
-#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5)
-#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4)
-#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3)
-#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2)
-#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1)
-#define BT_MPEG_SAMPLING_FREQ_48000 1
-
-#define BT_MPEG_LAYER_1 (1 << 2)
-#define BT_MPEG_LAYER_2 (1 << 1)
-#define BT_MPEG_LAYER_3 1
-
-typedef struct {
- uint8_t channel_mode;
- uint8_t frequency;
- uint8_t allocation_method;
- uint8_t subbands;
- uint8_t block_length;
- uint8_t min_bitpool;
- uint8_t max_bitpool;
-} __attribute__ ((packed)) sbc_capabilities_t;
-
-typedef struct {
- uint8_t channel_mode;
- uint8_t crc;
- uint8_t layer;
- uint8_t frequency;
- uint8_t mpf;
- uint16_t bitrate;
-} __attribute__ ((packed)) mpeg_capabilities_t;
-
-struct bt_getcapabilities_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
- uint8_t transport; /* Granted transport */
- sbc_capabilities_t sbc_capabilities; /* A2DP only */
- mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
- uint16_t sampling_rate; /* SCO only */
-} __attribute__ ((packed));
-
-/* BT_SETCONFIGURATION_REQ */
-struct bt_setconfiguration_req {
- bt_audio_msg_header_t h;
- char device[18]; /* Address of the remote Device */
- uint8_t transport; /* Requested transport */
- uint8_t access_mode; /* Requested access mode */
- sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field
- and next one must be filled */
- mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
-} __attribute__ ((packed));
-
-/* BT_SETCONFIGURATION_RSP */
-struct bt_setconfiguration_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
- uint8_t transport; /* Granted transport */
- uint8_t access_mode; /* Granted access mode */
- uint16_t link_mtu; /* Max length that transport supports */
-} __attribute__ ((packed));
-
-/* BT_STREAMSTART_REQ */
-#define BT_STREAM_ACCESS_READ 0
-#define BT_STREAM_ACCESS_WRITE 1
-#define BT_STREAM_ACCESS_READWRITE 2
-struct bt_streamstart_req {
- bt_audio_msg_header_t h;
-} __attribute__ ((packed));
-
-/* BT_STREAMSTART_RSP */
-struct bt_streamstart_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
-} __attribute__ ((packed));
-
-/* BT_STREAMFD_IND */
-/* This message is followed by one byte of data containing the stream data fd
- as ancilliary data */
-struct bt_streamfd_ind {
- bt_audio_msg_header_t h;
-} __attribute__ ((packed));
-
-/* BT_STREAMSTOP_REQ */
-struct bt_streamstop_req {
- bt_audio_msg_header_t h;
-} __attribute__ ((packed));
-
-/* BT_STREAMSTOP_RSP */
-struct bt_streamstop_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
-} __attribute__ ((packed));
-
-/* BT_STREAMSUSPEND_IND */
-struct bt_streamsuspend_ind {
- bt_audio_msg_header_t h;
-} __attribute__ ((packed));
-
-/* BT_STREAMRESUME_IND */
-struct bt_streamresume_ind {
- bt_audio_msg_header_t h;
-} __attribute__ ((packed));
-
-/* BT_CONTROL_REQ */
-
-#define BT_CONTROL_KEY_POWER 0x40
-#define BT_CONTROL_KEY_VOL_UP 0x41
-#define BT_CONTROL_KEY_VOL_DOWN 0x42
-#define BT_CONTROL_KEY_MUTE 0x43
-#define BT_CONTROL_KEY_PLAY 0x44
-#define BT_CONTROL_KEY_STOP 0x45
-#define BT_CONTROL_KEY_PAUSE 0x46
-#define BT_CONTROL_KEY_RECORD 0x47
-#define BT_CONTROL_KEY_REWIND 0x48
-#define BT_CONTROL_KEY_FAST_FORWARD 0x49
-#define BT_CONTROL_KEY_EJECT 0x4A
-#define BT_CONTROL_KEY_FORWARD 0x4B
-#define BT_CONTROL_KEY_BACKWARD 0x4C
-
-struct bt_control_req {
- bt_audio_msg_header_t h;
- uint8_t mode; /* Control Mode */
- uint8_t key; /* Control Key */
-} __attribute__ ((packed));
-
-/* BT_CONTROL_RSP */
-struct bt_control_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
- uint8_t mode; /* Control Mode */
- uint8_t key; /* Control Key */
-} __attribute__ ((packed));
-
-/* BT_CONTROL_IND */
-struct bt_control_ind {
- bt_audio_msg_header_t h;
- uint8_t mode; /* Control Mode */
- uint8_t key; /* Control Key */
-} __attribute__ ((packed));
-
-/* Function declaration */
-
-/* Opens a connection to the audio service: return a socket descriptor */
-int bt_audio_service_open();
-
-/* Closes a connection to the audio service */
-int bt_audio_service_close(int sk);
-
-/* Receives stream data file descriptor : must be called after a
-BT_STREAMFD_IND message is returned */
-int bt_audio_service_get_data_fd(int sk);
-
-/* Human readable message type string */
-const char *bt_audio_strmsg(int type);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* BT_AUDIOCLIENT_H */
diff --git a/src/modules/bt-rtp.h b/src/modules/bt-rtp.h
deleted file mode 100644
index 690bd43..0000000
--- a/src/modules/bt-rtp.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-
-struct rtp_header {
- uint8_t cc:4;
- uint8_t x:1;
- uint8_t p:1;
- uint8_t v:2;
-
- uint8_t pt:7;
- uint8_t m:1;
-
- uint16_t sequence_number;
- uint32_t timestamp;
- uint32_t ssrc;
- uint32_t csrc[0];
-} __attribute__ ((packed));
-
-struct rtp_payload {
- uint8_t frame_count:4;
- uint8_t rfa0:1;
- uint8_t is_last_fragment:1;
- uint8_t is_first_fragment:1;
- uint8_t is_fragmented:1;
-} __attribute__ ((packed));
-
-#elif __BYTE_ORDER == __BIG_ENDIAN
-
-struct rtp_header {
- uint8_t v:2;
- uint8_t p:1;
- uint8_t x:1;
- uint8_t cc:4;
-
- uint8_t m:1;
- uint8_t pt:7;
-
- uint16_t sequence_number;
- uint32_t timestamp;
- uint32_t ssrc;
- uint32_t csrc[0];
-} __attribute__ ((packed));
-
-struct rtp_payload {
- uint8_t is_fragmented:1;
- uint8_t is_first_fragment:1;
- uint8_t is_last_fragment:1;
- uint8_t rfa0:1;
- uint8_t frame_count:4;
-} __attribute__ ((packed));
-
-#else
-#error "Unknown byte order"
-#endif
diff --git a/src/modules/bt-sbc.c b/src/modules/bt-sbc.c
deleted file mode 100644
index 8e7b060..0000000
--- a/src/modules/bt-sbc.c
+++ /dev/null
@@ -1,1411 +0,0 @@
-/*
- *
- * Bluetooth low-complexity, subband codec (SBC) library
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
- * Copyright (C) 2005-2008 Brad Midgley <bmidgley at xmission.com>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-/* todo items:
-
- use a log2 table for byte integer scale factors calculation (sum log2 results
- for high and low bytes) fill bitpool by 16 bits instead of one at a time in
- bits allocation/bitpool generation port to the dsp
-
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include "bt-sbc_math.h"
-#include "bt-sbc_tables.h"
-
-#include "bt-sbc.h"
-
-#define SBC_SYNCWORD 0x9C
-
-/* This structure contains an unpacked SBC frame.
- Yes, there is probably quite some unused space herein */
-struct sbc_frame {
- uint8_t frequency;
- uint8_t block_mode;
- uint8_t blocks;
- enum {
- MONO = SBC_MODE_MONO,
- DUAL_CHANNEL = SBC_MODE_DUAL_CHANNEL,
- STEREO = SBC_MODE_STEREO,
- JOINT_STEREO = SBC_MODE_JOINT_STEREO
- } mode;
- uint8_t channels;
- enum {
- LOUDNESS = SBC_AM_LOUDNESS,
- SNR = SBC_AM_SNR
- } allocation;
- uint8_t subband_mode;
- uint8_t subbands;
- uint8_t bitpool;
- uint8_t codesize;
- uint8_t length;
-
- /* bit number x set means joint stereo has been used in subband x */
- uint8_t joint;
-
- /* only the lower 4 bits of every element are to be used */
- uint8_t scale_factor[2][8];
-
- /* raw integer subband samples in the frame */
-
- int32_t sb_sample_f[16][2][8];
- int32_t sb_sample[16][2][8]; /* modified subband samples */
- int16_t pcm_sample[2][16*8]; /* original pcm audio samples */
-};
-
-struct sbc_decoder_state {
- int subbands;
- int32_t V[2][170];
- int offset[2][16];
-};
-
-struct sbc_encoder_state {
- int subbands;
- int position[2];
- int32_t X[2][160];
-};
-
-/*
- * Calculates the CRC-8 of the first len bits in data
- */
-static const uint8_t crc_table[256] = {
- 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
- 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
- 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
- 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
- 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
- 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
- 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
- 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
- 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
- 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
- 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
- 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
- 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
- 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
- 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
- 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
- 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
- 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
- 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
- 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
- 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
- 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
- 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
- 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
- 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
- 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
- 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
- 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
- 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
- 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
- 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
- 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
-};
-
-static uint8_t sbc_crc8(const uint8_t *data, size_t len)
-{
- uint8_t crc = 0x0f;
- size_t i;
- uint8_t octet;
-
- for (i = 0; i < len / 8; i++)
- crc = crc_table[crc ^ data[i]];
-
- octet = data[i];
- for (i = 0; i < len % 8; i++) {
- char bit = ((octet ^ crc) & 0x80) >> 7;
-
- crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
-
- octet = octet << 1;
- }
-
- return crc;
-}
-
-/*
- * Code straight from the spec to calculate the bits array
- * Takes a pointer to the frame in question, a pointer to the bits array and
- * the sampling frequency (as 2 bit integer)
- */
-static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
-{
- uint8_t sf = frame->frequency;
-
- if (frame->mode == MONO || frame->mode == DUAL_CHANNEL) {
- int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
- int ch, sb;
-
- for (ch = 0; ch < frame->channels; ch++) {
- max_bitneed = 0;
- if (frame->allocation == SNR) {
- for (sb = 0; sb < frame->subbands; sb++) {
- bitneed[ch][sb] = frame->scale_factor[ch][sb];
- if (bitneed[ch][sb] > max_bitneed)
- max_bitneed = bitneed[ch][sb];
- }
- } else {
- for (sb = 0; sb < frame->subbands; sb++) {
- if (frame->scale_factor[ch][sb] == 0)
- bitneed[ch][sb] = -5;
- else {
- if (frame->subbands == 4)
- loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
- else
- loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
- if (loudness > 0)
- bitneed[ch][sb] = loudness / 2;
- else
- bitneed[ch][sb] = loudness;
- }
- if (bitneed[ch][sb] > max_bitneed)
- max_bitneed = bitneed[ch][sb];
- }
- }
-
- bitcount = 0;
- slicecount = 0;
- bitslice = max_bitneed + 1;
- do {
- bitslice--;
- bitcount += slicecount;
- slicecount = 0;
- for (sb = 0; sb < frame->subbands; sb++) {
- if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
- slicecount++;
- else if (bitneed[ch][sb] == bitslice + 1)
- slicecount += 2;
- }
- } while (bitcount + slicecount < frame->bitpool);
-
- if (bitcount + slicecount == frame->bitpool) {
- bitcount += slicecount;
- bitslice--;
- }
-
- for (sb = 0; sb < frame->subbands; sb++) {
- if (bitneed[ch][sb] < bitslice + 2)
- bits[ch][sb] = 0;
- else {
- bits[ch][sb] = bitneed[ch][sb] - bitslice;
- if (bits[ch][sb] > 16)
- bits[ch][sb] = 16;
- }
- }
-
- for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) {
- if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
- bits[ch][sb]++;
- bitcount++;
- } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
- bits[ch][sb] = 2;
- bitcount += 2;
- }
- }
-
- for (sb = 0; bitcount < frame->bitpool && sb < frame->subbands; sb++) {
- if (bits[ch][sb] < 16) {
- bits[ch][sb]++;
- bitcount++;
- }
- }
-
- }
-
- } else if (frame->mode == STEREO || frame->mode == JOINT_STEREO) {
- int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
- int ch, sb;
-
- max_bitneed = 0;
- if (frame->allocation == SNR) {
- for (ch = 0; ch < 2; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- bitneed[ch][sb] = frame->scale_factor[ch][sb];
- if (bitneed[ch][sb] > max_bitneed)
- max_bitneed = bitneed[ch][sb];
- }
- }
- } else {
- for (ch = 0; ch < 2; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- if (frame->scale_factor[ch][sb] == 0)
- bitneed[ch][sb] = -5;
- else {
- if (frame->subbands == 4)
- loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
- else
- loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
- if (loudness > 0)
- bitneed[ch][sb] = loudness / 2;
- else
- bitneed[ch][sb] = loudness;
- }
- if (bitneed[ch][sb] > max_bitneed)
- max_bitneed = bitneed[ch][sb];
- }
- }
- }
-
- bitcount = 0;
- slicecount = 0;
- bitslice = max_bitneed + 1;
- do {
- bitslice--;
- bitcount += slicecount;
- slicecount = 0;
- for (ch = 0; ch < 2; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
- slicecount++;
- else if (bitneed[ch][sb] == bitslice + 1)
- slicecount += 2;
- }
- }
- } while (bitcount + slicecount < frame->bitpool);
-
- if (bitcount + slicecount == frame->bitpool) {
- bitcount += slicecount;
- bitslice--;
- }
-
- for (ch = 0; ch < 2; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- if (bitneed[ch][sb] < bitslice + 2) {
- bits[ch][sb] = 0;
- } else {
- bits[ch][sb] = bitneed[ch][sb] - bitslice;
- if (bits[ch][sb] > 16)
- bits[ch][sb] = 16;
- }
- }
- }
-
- ch = 0;
- sb = 0;
- while (bitcount < frame->bitpool) {
- if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
- bits[ch][sb]++;
- bitcount++;
- } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
- bits[ch][sb] = 2;
- bitcount += 2;
- }
- if (ch == 1) {
- ch = 0;
- sb++;
- if (sb >= frame->subbands) break;
- } else
- ch = 1;
- }
-
- ch = 0;
- sb = 0;
- while (bitcount < frame->bitpool) {
- if (bits[ch][sb] < 16) {
- bits[ch][sb]++;
- bitcount++;
- }
- if (ch == 1) {
- ch = 0;
- sb++;
- if (sb >= frame->subbands) break;
- } else
- ch = 1;
- }
-
- }
-
-}
-
-/*
- * Unpacks a SBC frame at the beginning of the stream in data,
- * which has at most len bytes into frame.
- * Returns the length in bytes of the packed frame, or a negative
- * value on error. The error codes are:
- *
- * -1 Data stream too short
- * -2 Sync byte incorrect
- * -3 CRC8 incorrect
- * -4 Bitpool value out of bounds
- */
-static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
- size_t len)
-{
- int consumed;
- /* Will copy the parts of the header that are relevant to crc
- * calculation here */
- uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int crc_pos = 0;
- int32_t temp;
-
- int audio_sample;
- int ch, sb, blk, bit; /* channel, subband, block and bit standard
- counters */
- int bits[2][8]; /* bits distribution */
- uint32_t levels[2][8]; /* levels derived from that */
-
- if (len < 4)
- return -1;
-
- if (data[0] != SBC_SYNCWORD)
- return -2;
-
- frame->frequency = (data[1] >> 6) & 0x03;
-
- frame->block_mode = (data[1] >> 4) & 0x03;
- switch (frame->block_mode) {
- case SBC_BLK_4:
- frame->blocks = 4;
- break;
- case SBC_BLK_8:
- frame->blocks = 8;
- break;
- case SBC_BLK_12:
- frame->blocks = 12;
- break;
- case SBC_BLK_16:
- frame->blocks = 16;
- break;
- }
-
- frame->mode = (data[1] >> 2) & 0x03;
- switch (frame->mode) {
- case MONO:
- frame->channels = 1;
- break;
- case DUAL_CHANNEL: /* fall-through */
- case STEREO:
- case JOINT_STEREO:
- frame->channels = 2;
- break;
- }
-
- frame->allocation = (data[1] >> 1) & 0x01;
-
- frame->subband_mode = (data[1] & 0x01);
- frame->subbands = frame->subband_mode ? 8 : 4;
-
- frame->bitpool = data[2];
-
- if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
- frame->bitpool > 16 * frame->subbands)
- return -4;
-
- if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
- frame->bitpool > 32 * frame->subbands)
- return -4;
-
- /* data[3] is crc, we're checking it later */
-
- consumed = 32;
-
- crc_header[0] = data[1];
- crc_header[1] = data[2];
- crc_pos = 16;
-
- if (frame->mode == JOINT_STEREO) {
- if (len * 8 < consumed + frame->subbands)
- return -1;
-
- frame->joint = 0x00;
- for (sb = 0; sb < frame->subbands - 1; sb++)
- frame->joint |= ((data[4] >> (7 - sb)) & 0x01) << sb;
- if (frame->subbands == 4)
- crc_header[crc_pos / 8] = data[4] & 0xf0;
- else
- crc_header[crc_pos / 8] = data[4];
-
- consumed += frame->subbands;
- crc_pos += frame->subbands;
- }
-
- if (len * 8 < consumed + (4 * frame->subbands * frame->channels))
- return -1;
-
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- /* FIXME assert(consumed % 4 == 0); */
- frame->scale_factor[ch][sb] =
- (data[consumed >> 3] >> (4 - (consumed & 0x7))) & 0x0F;
- crc_header[crc_pos >> 3] |=
- frame->scale_factor[ch][sb] << (4 - (crc_pos & 0x7));
-
- consumed += 4;
- crc_pos += 4;
- }
- }
-
- if (data[3] != sbc_crc8(crc_header, crc_pos))
- return -3;
-
- sbc_calculate_bits(frame, bits);
-
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++)
- levels[ch][sb] = (1 << bits[ch][sb]) - 1;
- }
-
- for (blk = 0; blk < frame->blocks; blk++) {
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- if (levels[ch][sb] > 0) {
- audio_sample = 0;
- for (bit = 0; bit < bits[ch][sb]; bit++) {
- if (consumed > len * 8)
- return -1;
-
- if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
- audio_sample |= 1 << (bits[ch][sb] - bit - 1);
-
- consumed++;
- }
-
- frame->sb_sample[blk][ch][sb] =
- (((audio_sample << 1) | 1) << frame->scale_factor[ch][sb]) /
- levels[ch][sb] - (1 << frame->scale_factor[ch][sb]);
- } else
- frame->sb_sample[blk][ch][sb] = 0;
- }
- }
- }
-
- if (frame->mode == JOINT_STEREO) {
- for (blk = 0; blk < frame->blocks; blk++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- if (frame->joint & (0x01 << sb)) {
- temp = frame->sb_sample[blk][0][sb] +
- frame->sb_sample[blk][1][sb];
- frame->sb_sample[blk][1][sb] =
- frame->sb_sample[blk][0][sb] -
- frame->sb_sample[blk][1][sb];
- frame->sb_sample[blk][0][sb] = temp;
- }
- }
- }
- }
-
- if ((consumed & 0x7) != 0)
- consumed += 8 - (consumed & 0x7);
-
- return consumed >> 3;
-}
-
-static void sbc_decoder_init(struct sbc_decoder_state *state,
- const struct sbc_frame *frame)
-{
- int i, ch;
-
- memset(state->V, 0, sizeof(state->V));
- state->subbands = frame->subbands;
-
- for (ch = 0; ch < 2; ch++)
- for (i = 0; i < frame->subbands * 2; i++)
- state->offset[ch][i] = (10 * i + 10);
-}
-
-static inline void sbc_synthesize_four(struct sbc_decoder_state *state,
- struct sbc_frame *frame, int ch, int blk)
-{
- int i, k, idx;
- int32_t *v = state->V[ch];
- int *offset = state->offset[ch];
-
- for (i = 0; i < 8; i++) {
- /* Shifting */
- offset[i]--;
- if (offset[i] < 0) {
- offset[i] = 79;
- memcpy(v + 80, v, 9 * sizeof(*v));
- }
-
- /* Distribute the new matrix value to the shifted position */
- v[offset[i]] = SCALE4_STAGED1(
- MULA(synmatrix4[i][0], frame->sb_sample[blk][ch][0],
- MULA(synmatrix4[i][1], frame->sb_sample[blk][ch][1],
- MULA(synmatrix4[i][2], frame->sb_sample[blk][ch][2],
- MUL (synmatrix4[i][3], frame->sb_sample[blk][ch][3])))));
- }
-
- /* Compute the samples */
- for (idx = 0, i = 0; i < 4; i++, idx += 5) {
- k = (i + 4) & 0xf;
-
- /* Store in output, Q0 */
- frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED2(
- MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0],
- MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0],
- MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1],
- MULA(v[offset[k] + 3], sbc_proto_4_40m1[idx + 1],
- MULA(v[offset[i] + 4], sbc_proto_4_40m0[idx + 2],
- MULA(v[offset[k] + 5], sbc_proto_4_40m1[idx + 2],
- MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3],
- MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3],
- MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4],
- MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4])))))))))));
- }
-}
-
-static inline void sbc_synthesize_eight(struct sbc_decoder_state *state,
- struct sbc_frame *frame, int ch, int blk)
-{
- int i, j, k, idx;
- int *offset = state->offset[ch];
-
- for (i = 0; i < 16; i++) {
- /* Shifting */
- offset[i]--;
- if (offset[i] < 0) {
- offset[i] = 159;
- for (j = 0; j < 9; j++)
- state->V[ch][j + 160] = state->V[ch][j];
- }
-
- /* Distribute the new matrix value to the shifted position */
- state->V[ch][offset[i]] = SCALE8_STAGED1(
- MULA(synmatrix8[i][0], frame->sb_sample[blk][ch][0],
- MULA(synmatrix8[i][1], frame->sb_sample[blk][ch][1],
- MULA(synmatrix8[i][2], frame->sb_sample[blk][ch][2],
- MULA(synmatrix8[i][3], frame->sb_sample[blk][ch][3],
- MULA(synmatrix8[i][4], frame->sb_sample[blk][ch][4],
- MULA(synmatrix8[i][5], frame->sb_sample[blk][ch][5],
- MULA(synmatrix8[i][6], frame->sb_sample[blk][ch][6],
- MUL( synmatrix8[i][7], frame->sb_sample[blk][ch][7])))))))));
- }
-
- /* Compute the samples */
- for (idx = 0, i = 0; i < 8; i++, idx += 5) {
- k = (i + 8) & 0xf;
-
- /* Store in output */
- frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED2( // Q0
- MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0],
- MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0],
- MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1],
- MULA(state->V[ch][offset[k] + 3], sbc_proto_8_80m1[idx + 1],
- MULA(state->V[ch][offset[i] + 4], sbc_proto_8_80m0[idx + 2],
- MULA(state->V[ch][offset[k] + 5], sbc_proto_8_80m1[idx + 2],
- MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3],
- MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3],
- MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4],
- MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4])))))))))));
- }
-}
-
-static int sbc_synthesize_audio(struct sbc_decoder_state *state,
- struct sbc_frame *frame)
-{
- int ch, blk;
-
- switch (frame->subbands) {
- case 4:
- for (ch = 0; ch < frame->channels; ch++) {
- for (blk = 0; blk < frame->blocks; blk++)
- sbc_synthesize_four(state, frame, ch, blk);
- }
- return frame->blocks * 4;
-
- case 8:
- for (ch = 0; ch < frame->channels; ch++) {
- for (blk = 0; blk < frame->blocks; blk++)
- sbc_synthesize_eight(state, frame, ch, blk);
- }
- return frame->blocks * 8;
-
- default:
- return -EIO;
- }
-}
-
-static void sbc_encoder_init(struct sbc_encoder_state *state,
- const struct sbc_frame *frame)
-{
- memset(&state->X, 0, sizeof(state->X));
- state->subbands = frame->subbands;
- state->position[0] = state->position[1] = 9 * frame->subbands;
-}
-
-static inline void _sbc_analyze_four(const int32_t *in, int32_t *out)
-{
- sbc_fixed_t t[8], s[5];
-
- t[0] = SCALE4_STAGE1( /* Q8 */
- MULA(_sbc_proto_4[0], in[8] - in[32], /* Q18 */
- MUL( _sbc_proto_4[1], in[16] - in[24])));
-
- t[1] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[2], in[1],
- MULA(_sbc_proto_4[3], in[9],
- MULA(_sbc_proto_4[4], in[17],
- MULA(_sbc_proto_4[5], in[25],
- MUL( _sbc_proto_4[6], in[33]))))));
-
- t[2] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[7], in[2],
- MULA(_sbc_proto_4[8], in[10],
- MULA(_sbc_proto_4[9], in[18],
- MULA(_sbc_proto_4[10], in[26],
- MUL( _sbc_proto_4[11], in[34]))))));
-
- t[3] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[12], in[3],
- MULA(_sbc_proto_4[13], in[11],
- MULA(_sbc_proto_4[14], in[19],
- MULA(_sbc_proto_4[15], in[27],
- MUL( _sbc_proto_4[16], in[35]))))));
-
- t[4] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[17], in[4] + in[36],
- MULA(_sbc_proto_4[18], in[12] + in[28],
- MUL( _sbc_proto_4[19], in[20]))));
-
- t[5] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[16], in[5],
- MULA(_sbc_proto_4[15], in[13],
- MULA(_sbc_proto_4[14], in[21],
- MULA(_sbc_proto_4[13], in[29],
- MUL( _sbc_proto_4[12], in[37]))))));
-
- /* don't compute t[6]... this term always multiplies
- * with cos(pi/2) = 0 */
-
- t[7] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[6], in[7],
- MULA(_sbc_proto_4[5], in[15],
- MULA(_sbc_proto_4[4], in[23],
- MULA(_sbc_proto_4[3], in[31],
- MUL( _sbc_proto_4[2], in[39]))))));
-
- s[0] = MUL( _anamatrix4[0], t[0] + t[4]);
- s[1] = MUL( _anamatrix4[2], t[2]);
- s[2] = MULA(_anamatrix4[1], t[1] + t[3],
- MUL(_anamatrix4[3], t[5]));
- s[3] = MULA(_anamatrix4[3], t[1] + t[3],
- MUL(_anamatrix4[1], -t[5] + t[7]));
- s[4] = MUL( _anamatrix4[3], t[7]);
-
- out[0] = SCALE4_STAGE2( s[0] + s[1] + s[2] + s[4]); /* Q0 */
- out[1] = SCALE4_STAGE2(-s[0] + s[1] + s[3]);
- out[2] = SCALE4_STAGE2(-s[0] + s[1] - s[3]);
- out[3] = SCALE4_STAGE2( s[0] + s[1] - s[2] - s[4]);
-}
-
-static inline void sbc_analyze_four(struct sbc_encoder_state *state,
- struct sbc_frame *frame, int ch, int blk)
-{
- int32_t *x = &state->X[ch][state->position[ch]];
- int16_t *pcm = &frame->pcm_sample[ch][blk * 4];
-
- /* Input 4 Audio Samples */
- x[40] = x[0] = pcm[3];
- x[41] = x[1] = pcm[2];
- x[42] = x[2] = pcm[1];
- x[43] = x[3] = pcm[0];
-
- _sbc_analyze_four(x, frame->sb_sample_f[blk][ch]);
-
- state->position[ch] -= 4;
- if (state->position[ch] < 0)
- state->position[ch] = 36;
-}
-
-static inline void _sbc_analyze_eight(const int32_t *in, int32_t *out)
-{
- sbc_fixed_t t[8], s[8];
-
- t[0] = SCALE8_STAGE1( /* Q10 */
- MULA(_sbc_proto_8[0], (in[16] - in[64]), /* Q18 = Q18 * Q0 */
- MULA(_sbc_proto_8[1], (in[32] - in[48]),
- MULA(_sbc_proto_8[2], in[4],
- MULA(_sbc_proto_8[3], in[20],
- MULA(_sbc_proto_8[4], in[36],
- MUL( _sbc_proto_8[5], in[52])))))));
-
- t[1] = SCALE8_STAGE1(
- MULA(_sbc_proto_8[6], in[2],
- MULA(_sbc_proto_8[7], in[18],
- MULA(_sbc_proto_8[8], in[34],
- MULA(_sbc_proto_8[9], in[50],
- MUL(_sbc_proto_8[10], in[66]))))));
-
- t[2] = SCALE8_STAGE1(
- MULA(_sbc_proto_8[11], in[1],
- MULA(_sbc_proto_8[12], in[17],
- MULA(_sbc_proto_8[13], in[33],
- MULA(_sbc_proto_8[14], in[49],
- MULA(_sbc_proto_8[15], in[65],
- MULA(_sbc_proto_8[16], in[3],
- MULA(_sbc_proto_8[17], in[19],
- MULA(_sbc_proto_8[18], in[35],
- MULA(_sbc_proto_8[19], in[51],
- MUL( _sbc_proto_8[20], in[67])))))))))));
-
- t[3] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[21], in[5],
- MULA( _sbc_proto_8[22], in[21],
- MULA( _sbc_proto_8[23], in[37],
- MULA( _sbc_proto_8[24], in[53],
- MULA( _sbc_proto_8[25], in[69],
- MULA(-_sbc_proto_8[15], in[15],
- MULA(-_sbc_proto_8[14], in[31],
- MULA(-_sbc_proto_8[13], in[47],
- MULA(-_sbc_proto_8[12], in[63],
- MUL( -_sbc_proto_8[11], in[79])))))))))));
-
- t[4] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[26], in[6],
- MULA( _sbc_proto_8[27], in[22],
- MULA( _sbc_proto_8[28], in[38],
- MULA( _sbc_proto_8[29], in[54],
- MULA( _sbc_proto_8[30], in[70],
- MULA(-_sbc_proto_8[10], in[14],
- MULA(-_sbc_proto_8[9], in[30],
- MULA(-_sbc_proto_8[8], in[46],
- MULA(-_sbc_proto_8[7], in[62],
- MUL( -_sbc_proto_8[6], in[78])))))))))));
-
- t[5] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[31], in[7],
- MULA( _sbc_proto_8[32], in[23],
- MULA( _sbc_proto_8[33], in[39],
- MULA( _sbc_proto_8[34], in[55],
- MULA( _sbc_proto_8[35], in[71],
- MULA(-_sbc_proto_8[20], in[13],
- MULA(-_sbc_proto_8[19], in[29],
- MULA(-_sbc_proto_8[18], in[45],
- MULA(-_sbc_proto_8[17], in[61],
- MUL( -_sbc_proto_8[16], in[77])))))))))));
-
- t[6] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[36], (in[8] + in[72]),
- MULA( _sbc_proto_8[37], (in[24] + in[56]),
- MULA( _sbc_proto_8[38], in[40],
- MULA(-_sbc_proto_8[39], in[12],
- MULA(-_sbc_proto_8[5], in[28],
- MULA(-_sbc_proto_8[4], in[44],
- MULA(-_sbc_proto_8[3], in[60],
- MUL( -_sbc_proto_8[2], in[76])))))))));
-
- t[7] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[35], in[9],
- MULA( _sbc_proto_8[34], in[25],
- MULA( _sbc_proto_8[33], in[41],
- MULA( _sbc_proto_8[32], in[57],
- MULA( _sbc_proto_8[31], in[73],
- MULA(-_sbc_proto_8[25], in[11],
- MULA(-_sbc_proto_8[24], in[27],
- MULA(-_sbc_proto_8[23], in[43],
- MULA(-_sbc_proto_8[22], in[59],
- MUL( -_sbc_proto_8[21], in[75])))))))))));
-
- s[0] = MULA( _anamatrix8[0], t[0],
- MUL( _anamatrix8[1], t[6]));
- s[1] = MUL( _anamatrix8[7], t[1]);
- s[2] = MULA( _anamatrix8[2], t[2],
- MULA( _anamatrix8[3], t[3],
- MULA( _anamatrix8[4], t[5],
- MUL( _anamatrix8[5], t[7]))));
- s[3] = MUL( _anamatrix8[6], t[4]);
- s[4] = MULA( _anamatrix8[3], t[2],
- MULA(-_anamatrix8[5], t[3],
- MULA(-_anamatrix8[2], t[5],
- MUL( -_anamatrix8[4], t[7]))));
- s[5] = MULA( _anamatrix8[4], t[2],
- MULA(-_anamatrix8[2], t[3],
- MULA( _anamatrix8[5], t[5],
- MUL( _anamatrix8[3], t[7]))));
- s[6] = MULA( _anamatrix8[1], t[0],
- MUL( -_anamatrix8[0], t[6]));
- s[7] = MULA( _anamatrix8[5], t[2],
- MULA(-_anamatrix8[4], t[3],
- MULA( _anamatrix8[3], t[5],
- MUL( -_anamatrix8[2], t[7]))));
-
- out[0] = SCALE8_STAGE2( s[0] + s[1] + s[2] + s[3]);
- out[1] = SCALE8_STAGE2( s[1] - s[3] + s[4] + s[6]);
- out[2] = SCALE8_STAGE2( s[1] - s[3] + s[5] - s[6]);
- out[3] = SCALE8_STAGE2(-s[0] + s[1] + s[3] + s[7]);
- out[4] = SCALE8_STAGE2(-s[0] + s[1] + s[3] - s[7]);
- out[5] = SCALE8_STAGE2( s[1] - s[3] - s[5] - s[6]);
- out[6] = SCALE8_STAGE2( s[1] - s[3] - s[4] + s[6]);
- out[7] = SCALE8_STAGE2( s[0] + s[1] - s[2] + s[3]);
-}
-
-static inline void sbc_analyze_eight(struct sbc_encoder_state *state,
- struct sbc_frame *frame, int ch,
- int blk)
-{
- int32_t *x = &state->X[ch][state->position[ch]];
- int16_t *pcm = &frame->pcm_sample[ch][blk * 8];
-
- /* Input 8 Audio Samples */
- x[80] = x[0] = pcm[7];
- x[81] = x[1] = pcm[6];
- x[82] = x[2] = pcm[5];
- x[83] = x[3] = pcm[4];
- x[84] = x[4] = pcm[3];
- x[85] = x[5] = pcm[2];
- x[86] = x[6] = pcm[1];
- x[87] = x[7] = pcm[0];
-
- _sbc_analyze_eight(x, frame->sb_sample_f[blk][ch]);
-
- state->position[ch] -= 8;
- if (state->position[ch] < 0)
- state->position[ch] = 72;
-}
-
-static int sbc_analyze_audio(struct sbc_encoder_state *state,
- struct sbc_frame *frame)
-{
- int ch, blk;
-
- switch (frame->subbands) {
- case 4:
- for (ch = 0; ch < frame->channels; ch++)
- for (blk = 0; blk < frame->blocks; blk++)
- sbc_analyze_four(state, frame, ch, blk);
- return frame->blocks * 4;
-
- case 8:
- for (ch = 0; ch < frame->channels; ch++)
- for (blk = 0; blk < frame->blocks; blk++)
- sbc_analyze_eight(state, frame, ch, blk);
- return frame->blocks * 8;
-
- default:
- return -EIO;
- }
-}
-
-/*
- * Packs the SBC frame from frame into the memory at data. At most len
- * bytes will be used, should more memory be needed an appropriate
- * error code will be returned. Returns the length of the packed frame
- * on success or a negative value on error.
- *
- * The error codes are:
- * -1 Not enough memory reserved
- * -2 Unsupported sampling rate
- * -3 Unsupported number of blocks
- * -4 Unsupported number of subbands
- * -5 Bitpool value out of bounds
- * -99 not implemented
- */
-
-static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
-{
- int produced;
- /* Will copy the header parts for CRC-8 calculation here */
- uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int crc_pos = 0;
-
- uint16_t audio_sample;
-
- int ch, sb, blk, bit; /* channel, subband, block and bit counters */
- int bits[2][8]; /* bits distribution */
- int levels[2][8]; /* levels are derived from that */
-
- u_int32_t scalefactor[2][8]; /* derived from frame->scale_factor */
-
- data[0] = SBC_SYNCWORD;
-
- data[1] = (frame->frequency & 0x03) << 6;
-
- data[1] |= (frame->block_mode & 0x03) << 4;
-
- data[1] |= (frame->mode & 0x03) << 2;
-
- data[1] |= (frame->allocation & 0x01) << 1;
-
- switch (frame->subbands) {
- case 4:
- /* Nothing to do */
- break;
- case 8:
- data[1] |= 0x01;
- break;
- default:
- return -4;
- break;
- }
-
- data[2] = frame->bitpool;
-
- if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
- frame->bitpool > frame->subbands << 4)
- return -5;
-
- if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
- frame->bitpool > frame->subbands << 5)
- return -5;
-
- /* Can't fill in crc yet */
-
- produced = 32;
-
- crc_header[0] = data[1];
- crc_header[1] = data[2];
- crc_pos = 16;
-
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- frame->scale_factor[ch][sb] = 0;
- scalefactor[ch][sb] = 2;
- for (blk = 0; blk < frame->blocks; blk++) {
- while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) {
- frame->scale_factor[ch][sb]++;
- scalefactor[ch][sb] *= 2;
- }
- }
- }
- }
-
- if (frame->mode == JOINT_STEREO) {
- /* like frame->sb_sample but joint stereo */
- int32_t sb_sample_j[16][2];
- /* scalefactor and scale_factor in joint case */
- u_int32_t scalefactor_j[2];
- uint8_t scale_factor_j[2];
-
- frame->joint = 0;
-
- for (sb = 0; sb < frame->subbands - 1; sb++) {
- scale_factor_j[0] = 0;
- scalefactor_j[0] = 2;
- scale_factor_j[1] = 0;
- scalefactor_j[1] = 2;
-
- for (blk = 0; blk < frame->blocks; blk++) {
- /* Calculate joint stereo signal */
- sb_sample_j[blk][0] =
- (frame->sb_sample_f[blk][0][sb] +
- frame->sb_sample_f[blk][1][sb]) >> 1;
- sb_sample_j[blk][1] =
- (frame->sb_sample_f[blk][0][sb] -
- frame->sb_sample_f[blk][1][sb]) >> 1;
-
- /* calculate scale_factor_j and scalefactor_j for joint case */
- while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) {
- scale_factor_j[0]++;
- scalefactor_j[0] *= 2;
- }
- while (scalefactor_j[1] < fabs(sb_sample_j[blk][1])) {
- scale_factor_j[1]++;
- scalefactor_j[1] *= 2;
- }
- }
-
- /* decide whether to join this subband */
- if ((scalefactor[0][sb] + scalefactor[1][sb]) >
- (scalefactor_j[0] + scalefactor_j[1]) ) {
- /* use joint stereo for this subband */
- frame->joint |= 1 << sb;
- frame->scale_factor[0][sb] = scale_factor_j[0];
- frame->scale_factor[1][sb] = scale_factor_j[1];
- scalefactor[0][sb] = scalefactor_j[0];
- scalefactor[1][sb] = scalefactor_j[1];
- for (blk = 0; blk < frame->blocks; blk++) {
- frame->sb_sample_f[blk][0][sb] =
- sb_sample_j[blk][0];
- frame->sb_sample_f[blk][1][sb] =
- sb_sample_j[blk][1];
- }
- }
- }
-
- data[4] = 0;
- for (sb = 0; sb < frame->subbands - 1; sb++)
- data[4] |= ((frame->joint >> sb) & 0x01) << (frame->subbands - 1 - sb);
-
- crc_header[crc_pos >> 3] = data[4];
-
- produced += frame->subbands;
- crc_pos += frame->subbands;
- }
-
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- data[produced >> 3] <<= 4;
- crc_header[crc_pos >> 3] <<= 4;
- data[produced >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
- crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
-
- produced += 4;
- crc_pos += 4;
- }
- }
-
- /* align the last crc byte */
- if (crc_pos % 8)
- crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8);
-
- data[3] = sbc_crc8(crc_header, crc_pos);
-
- sbc_calculate_bits(frame, bits);
-
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++)
- levels[ch][sb] = (1 << bits[ch][sb]) - 1;
- }
-
- for (blk = 0; blk < frame->blocks; blk++) {
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- if (levels[ch][sb] > 0) {
- audio_sample =
- (uint16_t) ((((frame->sb_sample_f[blk][ch][sb]*levels[ch][sb]) >>
- (frame->scale_factor[ch][sb] + 1)) +
- levels[ch][sb]) >> 1);
- audio_sample <<= 16 - bits[ch][sb];
- for (bit = 0; bit < bits[ch][sb]; bit++) {
- data[produced >> 3] <<= 1;
- if (audio_sample & 0x8000)
- data[produced >> 3] |= 0x1;
- audio_sample <<= 1;
- produced++;
- }
- }
- }
- }
- }
-
- /* align the last byte */
- if (produced % 8) {
- data[produced >> 3] <<= 8 - (produced % 8);
- }
-
- return (produced + 7) >> 3;
-}
-
-struct sbc_priv {
- int init;
- struct sbc_frame frame;
- struct sbc_decoder_state dec_state;
- struct sbc_encoder_state enc_state;
-};
-
-static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
-{
- sbc->frequency = SBC_FREQ_44100;
- sbc->mode = SBC_MODE_STEREO;
- sbc->subbands = SBC_SB_8;
- sbc->blocks = SBC_BLK_16;
- sbc->bitpool = 32;
-#if __BYTE_ORDER == __LITTLE_ENDIAN
- sbc->endian = SBC_LE;
-#elif __BYTE_ORDER == __BIG_ENDIAN
- sbc->endian = SBC_BE;
-#else
-#error "Unknown byte order"
-#endif
-}
-
-int sbc_init(sbc_t *sbc, unsigned long flags)
-{
- if (!sbc)
- return -EIO;
-
- memset(sbc, 0, sizeof(sbc_t));
-
- sbc->priv = malloc(sizeof(struct sbc_priv));
- if (!sbc->priv)
- return -ENOMEM;
-
- memset(sbc->priv, 0, sizeof(struct sbc_priv));
-
- sbc_set_defaults(sbc, flags);
-
- return 0;
-}
-
-int sbc_parse(sbc_t *sbc, void *input, int input_len)
-{
- return sbc_decode(sbc, input, input_len, NULL, 0, NULL);
-}
-
-int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
- int output_len, int *written)
-{
- struct sbc_priv *priv;
- char *ptr;
- int i, ch, framelen, samples;
-
- if (!sbc && !input)
- return -EIO;
-
- priv = sbc->priv;
-
- framelen = sbc_unpack_frame(input, &priv->frame, input_len);
-
- if (!priv->init) {
- sbc_decoder_init(&priv->dec_state, &priv->frame);
- priv->init = 1;
-
- sbc->frequency = priv->frame.frequency;
- sbc->mode = priv->frame.mode;
- sbc->subbands = priv->frame.subband_mode;
- sbc->blocks = priv->frame.block_mode;
- sbc->allocation = priv->frame.allocation;
- sbc->bitpool = priv->frame.bitpool;
-
- priv->frame.codesize = sbc_get_codesize(sbc);
- priv->frame.length = sbc_get_frame_length(sbc);
- }
-
- if (!output)
- return framelen;
-
- if (written)
- *written = 0;
-
- samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame);
-
- ptr = output;
-
- if (output_len < samples * priv->frame.channels * 2)
- samples = output_len / (priv->frame.channels * 2);
-
- for (i = 0; i < samples; i++) {
- for (ch = 0; ch < priv->frame.channels; ch++) {
- int16_t s;
- s = priv->frame.pcm_sample[ch][i];
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
- if (sbc->endian == SBC_BE) {
-#elif __BYTE_ORDER == __BIG_ENDIAN
- if (sbc->endian == SBC_LE) {
-#else
-#error "Unknown byte order"
-#endif
- *ptr++ = (s & 0xff00) >> 8;
- *ptr++ = (s & 0x00ff);
- } else {
- *ptr++ = (s & 0x00ff);
- *ptr++ = (s & 0xff00) >> 8;
- }
- }
- }
-
- if (written)
- *written = samples * priv->frame.channels * 2;
-
- return framelen;
-}
-
-int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
- int output_len, int *written)
-{
- struct sbc_priv *priv;
- char *ptr;
- int i, ch, framelen, samples;
-
- if (!sbc && !input)
- return -EIO;
-
- priv = sbc->priv;
-
- if (written)
- *written = 0;
-
- if (!priv->init) {
- priv->frame.frequency = sbc->frequency;
- priv->frame.mode = sbc->mode;
- priv->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
- priv->frame.allocation = sbc->allocation;
- priv->frame.subband_mode = sbc->subbands;
- priv->frame.subbands = sbc->subbands ? 8 : 4;
- priv->frame.block_mode = sbc->blocks;
- priv->frame.blocks = 4 + (sbc->blocks * 4);
- priv->frame.bitpool = sbc->bitpool;
- priv->frame.codesize = sbc_get_codesize(sbc);
- priv->frame.length = sbc_get_frame_length(sbc);
-
- sbc_encoder_init(&priv->enc_state, &priv->frame);
- priv->init = 1;
- }
-
- /* input must be large enough to encode a complete frame */
- if (input_len < priv->frame.codesize)
- return 0;
-
- /* output must be large enough to receive the encoded frame */
- if (!output || output_len < priv->frame.length)
- return -ENOSPC;
-
- ptr = input;
-
- for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) {
- for (ch = 0; ch < priv->frame.channels; ch++) {
- int16_t s;
-#if __BYTE_ORDER == __LITTLE_ENDIAN
- if (sbc->endian == SBC_BE)
-#elif __BYTE_ORDER == __BIG_ENDIAN
- if (sbc->endian == SBC_LE)
-#else
-#error "Unknown byte order"
-#endif
- s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff);
- else
- s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8;
- ptr += 2;
- priv->frame.pcm_sample[ch][i] = s;
- }
- }
-
- samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
-
- framelen = sbc_pack_frame(output, &priv->frame, output_len);
-
- if (written)
- *written = framelen;
-
- return samples * priv->frame.channels * 2;
-}
-
-void sbc_finish(sbc_t *sbc)
-{
- if (!sbc)
- return;
-
- if (sbc->priv)
- free(sbc->priv);
-
- memset(sbc, 0, sizeof(sbc_t));
-}
-
-int sbc_get_frame_length(sbc_t *sbc)
-{
- int ret;
- uint8_t subbands, channels, blocks, joint;
- struct sbc_priv *priv;
-
- priv = sbc->priv;
- if (!priv->init) {
- subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
- channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
- joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
- } else {
- subbands = priv->frame.subbands;
- blocks = priv->frame.blocks;
- channels = priv->frame.channels;
- joint = priv->frame.joint;
- }
-
- ret = 4 + (4 * subbands * channels) / 8;
-
- /* This term is not always evenly divide so we round it up */
- if (channels == 1)
- ret += ((blocks * channels * sbc->bitpool) + 7) / 8;
- else
- ret += (((joint ? subbands : 0) + blocks * sbc->bitpool) + 7)
- / 8;
-
- return ret;
-}
-
-int sbc_get_frame_duration(sbc_t *sbc)
-{
- uint8_t subbands, blocks;
- uint16_t frequency;
- struct sbc_priv *priv;
-
- priv = sbc->priv;
- if (!priv->init) {
- subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
- } else {
- subbands = priv->frame.subbands;
- blocks = priv->frame.blocks;
- }
-
- switch (sbc->frequency) {
- case SBC_FREQ_16000:
- frequency = 16000;
- break;
-
- case SBC_FREQ_32000:
- frequency = 32000;
- break;
-
- case SBC_FREQ_44100:
- frequency = 44100;
- break;
-
- case SBC_FREQ_48000:
- frequency = 48000;
- break;
- default:
- return 0;
- }
-
- return (1000000 * blocks * subbands) / frequency;
-}
-
-int sbc_get_codesize(sbc_t *sbc)
-{
- uint8_t subbands, channels, blocks;
- struct sbc_priv *priv;
-
- priv = sbc->priv;
- if (!priv->init) {
- subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
- channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
- } else {
- subbands = priv->frame.subbands;
- blocks = priv->frame.blocks;
- channels = priv->frame.channels;
- }
-
- return subbands * blocks * channels * 2;
-}
-
-int sbc_reinit(sbc_t *sbc, unsigned long flags)
-{
- struct sbc_priv *priv;
-
- if (!sbc || !sbc->priv)
- return -EIO;
-
- priv = sbc->priv;
-
- if (priv->init == 1)
- memset(sbc->priv, 0, sizeof(struct sbc_priv));
-
- sbc_set_defaults(sbc, flags);
-
- return 0;
-}
diff --git a/src/modules/bt-sbc.h b/src/modules/bt-sbc.h
deleted file mode 100644
index ab47e32..0000000
--- a/src/modules/bt-sbc.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *
- * Bluetooth low-complexity, subband codec (SBC) library
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
- * Copyright (C) 2005-2006 Brad Midgley <bmidgley at xmission.com>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifndef __SBC_H
-#define __SBC_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-/* sampling frequency */
-#define SBC_FREQ_16000 0x00
-#define SBC_FREQ_32000 0x01
-#define SBC_FREQ_44100 0x02
-#define SBC_FREQ_48000 0x03
-
-/* blocks */
-#define SBC_BLK_4 0x00
-#define SBC_BLK_8 0x01
-#define SBC_BLK_12 0x02
-#define SBC_BLK_16 0x03
-
-/* channel mode */
-#define SBC_MODE_MONO 0x00
-#define SBC_MODE_DUAL_CHANNEL 0x01
-#define SBC_MODE_STEREO 0x02
-#define SBC_MODE_JOINT_STEREO 0x03
-
-/* allocation method */
-#define SBC_AM_LOUDNESS 0x00
-#define SBC_AM_SNR 0x01
-
-/* subbands */
-#define SBC_SB_4 0x00
-#define SBC_SB_8 0x01
-
-/* Data endianess */
-#define SBC_LE 0x00
-#define SBC_BE 0x01
-
-struct sbc_struct {
- unsigned long flags;
-
- uint8_t frequency;
- uint8_t blocks;
- uint8_t subbands;
- uint8_t mode;
- uint8_t allocation;
- uint8_t bitpool;
- uint8_t endian;
-
- void *priv;
-};
-
-typedef struct sbc_struct sbc_t;
-
-int sbc_init(sbc_t *sbc, unsigned long flags);
-int sbc_reinit(sbc_t *sbc, unsigned long flags);
-int sbc_parse(sbc_t *sbc, void *input, int input_len);
-int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
- int output_len, int *len);
-int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
- int output_len, int *written);
-int sbc_get_frame_length(sbc_t *sbc);
-int sbc_get_frame_duration(sbc_t *sbc);
-int sbc_get_codesize(sbc_t *sbc);
-void sbc_finish(sbc_t *sbc);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __SBC_H */
diff --git a/src/modules/bt-sbc_math.h b/src/modules/bt-sbc_math.h
deleted file mode 100644
index b3d87a6..0000000
--- a/src/modules/bt-sbc_math.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *
- * Bluetooth low-complexity, subband codec (SBC) library
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
- * Copyright (C) 2005-2008 Brad Midgley <bmidgley at xmission.com>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#define fabs(x) ((x) < 0 ? -(x) : (x))
-/* C does not provide an explicit arithmetic shift right but this will
- always be correct and every compiler *should* generate optimal code */
-#define ASR(val, bits) ((-2 >> 1 == -1) ? \
- ((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
-
-#define SCALE_PROTO4_TBL 15
-#define SCALE_ANA4_TBL 17
-#define SCALE_PROTO8_TBL 16
-#define SCALE_ANA8_TBL 17
-#define SCALE_SPROTO4_TBL 12
-#define SCALE_SPROTO8_TBL 14
-#define SCALE_NPROTO4_TBL 11
-#define SCALE_NPROTO8_TBL 11
-#define SCALE4_STAGE1_BITS 15
-#define SCALE4_STAGE2_BITS 16
-#define SCALE4_STAGED1_BITS 15
-#define SCALE4_STAGED2_BITS 16
-#define SCALE8_STAGE1_BITS 15
-#define SCALE8_STAGE2_BITS 15
-#define SCALE8_STAGED1_BITS 15
-#define SCALE8_STAGED2_BITS 16
-
-typedef int32_t sbc_fixed_t;
-
-#define SCALE4_STAGE1(src) ASR(src, SCALE4_STAGE1_BITS)
-#define SCALE4_STAGE2(src) ASR(src, SCALE4_STAGE2_BITS)
-#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS)
-#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS)
-#define SCALE8_STAGE1(src) ASR(src, SCALE8_STAGE1_BITS)
-#define SCALE8_STAGE2(src) ASR(src, SCALE8_STAGE2_BITS)
-#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS)
-#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS)
-
-#define SBC_FIXED_0(val) { val = 0; }
-#define MUL(a, b) ((a) * (b))
-#ifdef __arm__
-#define MULA(a, b, res) ({ \
- int tmp = res; \
- __asm__( \
- "mla %0, %2, %3, %0" \
- : "=&r" (tmp) \
- : "0" (tmp), "r" (a), "r" (b)); \
- tmp; })
-#else
-#define MULA(a, b, res) ((a) * (b) + (res))
-#endif
diff --git a/src/modules/bt-sbc_tables.h b/src/modules/bt-sbc_tables.h
deleted file mode 100644
index 7ac4e68..0000000
--- a/src/modules/bt-sbc_tables.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- *
- * Bluetooth low-complexity, subband codec (SBC) library
- *
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel at holtmann.org>
- * Copyright (C) 2004-2005 Henryk Ploetz <henryk at ploetzli.ch>
- * Copyright (C) 2005-2006 Brad Midgley <bmidgley at xmission.com>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-/* A2DP specification: Appendix B, page 69 */
-static const int sbc_offset4[4][4] = {
- { -1, 0, 0, 0 },
- { -2, 0, 0, 1 },
- { -2, 0, 0, 1 },
- { -2, 0, 0, 1 }
-};
-
-/* A2DP specification: Appendix B, page 69 */
-static const int sbc_offset8[4][8] = {
- { -2, 0, 0, 0, 0, 0, 0, 1 },
- { -3, 0, 0, 0, 0, 0, 1, 2 },
- { -4, 0, 0, 0, 0, 0, 1, 2 },
- { -4, 0, 0, 0, 0, 0, 1, 2 }
-};
-
-#define SP4(val) ASR(val, SCALE_PROTO4_TBL)
-#define SA4(val) ASR(val, SCALE_ANA4_TBL)
-#define SP8(val) ASR(val, SCALE_PROTO8_TBL)
-#define SA8(val) ASR(val, SCALE_ANA8_TBL)
-#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
-#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
-#define SN4(val) ASR(val, SCALE_NPROTO4_TBL)
-#define SN8(val) ASR(val, SCALE_NPROTO8_TBL)
-
-static const int32_t _sbc_proto_4[20] = {
- SP4(0x02cb3e8c), SP4(0x22b63dc0), SP4(0x002329cc), SP4(0x053b7548),
- SP4(0x31eab940), SP4(0xec1f5e60), SP4(0xff3773a8), SP4(0x0061c5a7),
- SP4(0x07646680), SP4(0x3f239480), SP4(0xf89f23a8), SP4(0x007a4737),
- SP4(0x00b32807), SP4(0x083ddc80), SP4(0x4825e480), SP4(0x0191e578),
- SP4(0x00ff11ca), SP4(0x00fb7991), SP4(0x069fdc58), SP4(0x4b584000)
-};
-
-static const int32_t _anamatrix4[4] = {
- SA4(0x2d413cc0), SA4(0x3b20d780), SA4(0x40000000), SA4(0x187de2a0)
-};
-
-static const int32_t _sbc_proto_8[40] = {
- SP8(0x02e5cd20), SP8(0x22d0c200), SP8(0x006bfe27), SP8(0x07808930),
- SP8(0x3f1c8800), SP8(0xf8810d70), SP8(0x002cfdc6), SP8(0x055acf28),
- SP8(0x31f566c0), SP8(0xebfe57e0), SP8(0xff27c437), SP8(0x001485cc),
- SP8(0x041c6e58), SP8(0x2a7cfa80), SP8(0xe4c4a240), SP8(0xfe359e4c),
- SP8(0x0048b1f8), SP8(0x0686ce30), SP8(0x38eec5c0), SP8(0xf2a1b9f0),
- SP8(0xffe8904a), SP8(0x0095698a), SP8(0x0824a480), SP8(0x443b3c00),
- SP8(0xfd7badc8), SP8(0x00d3e2d9), SP8(0x00c183d2), SP8(0x084e1950),
- SP8(0x4810d800), SP8(0x017f43fe), SP8(0x01056dd8), SP8(0x00e9cb9f),
- SP8(0x07d7d090), SP8(0x4a708980), SP8(0x0488fae8), SP8(0x0113bd20),
- SP8(0x0107b1a8), SP8(0x069fb3c0), SP8(0x4b3db200), SP8(0x00763f48)
-};
-
-static const int32_t sbc_proto_4_40m0[] = {
- SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
- SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8),
- SS4(0x027c1434), SS4(0x0019118b), SS4(0xfff3c74c), SS4(0xff137330),
- SS4(0xf81b8d70), SS4(0x00ec1b8b), SS4(0xfff0b71a), SS4(0xffe99b00),
- SS4(0xfef84470), SS4(0xf6fb4370), SS4(0xffcdc351), SS4(0xffe01dc7)
-};
-
-static const int32_t sbc_proto_4_40m1[] = {
- SS4(0xffe090ce), SS4(0xff2c0475), SS4(0xf694f800), SS4(0xff2c0475),
- SS4(0xffe090ce), SS4(0xffe01dc7), SS4(0xffcdc351), SS4(0xf6fb4370),
- SS4(0xfef84470), SS4(0xffe99b00), SS4(0xfff0b71a), SS4(0x00ec1b8b),
- SS4(0xf81b8d70), SS4(0xff137330), SS4(0xfff3c74c), SS4(0x0019118b),
- SS4(0x027c1434), SS4(0xf9c2a8d8), SS4(0xff589157), SS4(0xfffb9ac7)
-};
-
-static const int32_t sbc_proto_8_80m0[] = {
- SS8(0x00000000), SS8(0xfe8d1970), SS8(0xee979f00), SS8(0x11686100),
- SS8(0x0172e690), SS8(0xfff5bd1a), SS8(0xfdf1c8d4), SS8(0xeac182c0),
- SS8(0x0d9daee0), SS8(0x00e530da), SS8(0xffe9811d), SS8(0xfd52986c),
- SS8(0xe7054ca0), SS8(0x0a00d410), SS8(0x006c1de4), SS8(0xffdba705),
- SS8(0xfcbc98e8), SS8(0xe3889d20), SS8(0x06af2308), SS8(0x000bb7db),
- SS8(0xffca00ed), SS8(0xfc3fbb68), SS8(0xe071bc00), SS8(0x03bf7948),
- SS8(0xffc4e05c), SS8(0xffb54b3b), SS8(0xfbedadc0), SS8(0xdde26200),
- SS8(0x0142291c), SS8(0xff960e94), SS8(0xff9f3e17), SS8(0xfbd8f358),
- SS8(0xdbf79400), SS8(0xff405e01), SS8(0xff7d4914), SS8(0xff8b1a31),
- SS8(0xfc1417b8), SS8(0xdac7bb40), SS8(0xfdbb828c), SS8(0xff762170)
-};
-
-static const int32_t sbc_proto_8_80m1[] = {
- SS8(0xff7c272c), SS8(0xfcb02620), SS8(0xda612700), SS8(0xfcb02620),
- SS8(0xff7c272c), SS8(0xff762170), SS8(0xfdbb828c), SS8(0xdac7bb40),
- SS8(0xfc1417b8), SS8(0xff8b1a31), SS8(0xff7d4914), SS8(0xff405e01),
- SS8(0xdbf79400), SS8(0xfbd8f358), SS8(0xff9f3e17), SS8(0xff960e94),
- SS8(0x0142291c), SS8(0xdde26200), SS8(0xfbedadc0), SS8(0xffb54b3b),
- SS8(0xffc4e05c), SS8(0x03bf7948), SS8(0xe071bc00), SS8(0xfc3fbb68),
- SS8(0xffca00ed), SS8(0x000bb7db), SS8(0x06af2308), SS8(0xe3889d20),
- SS8(0xfcbc98e8), SS8(0xffdba705), SS8(0x006c1de4), SS8(0x0a00d410),
- SS8(0xe7054ca0), SS8(0xfd52986c), SS8(0xffe9811d), SS8(0x00e530da),
- SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a)
-};
-
-static const int32_t _anamatrix8[8] = {
- SA8(0x3b20d780), SA8(0x187de2a0), SA8(0x3ec52f80), SA8(0x3536cc40),
- SA8(0x238e7680), SA8(0x0c7c5c20), SA8(0x2d413cc0), SA8(0x40000000)
-};
-
-static const int32_t synmatrix4[8][4] = {
- { SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) },
- { SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) },
- { SN4(0x00000000), SN4(0x00000000), SN4(0x00000000), SN4(0x00000000) },
- { SN4(0xfcf043ac), SN4(0x07641af0), SN4(0xf89be510), SN4(0x030fbc54) },
- { SN4(0xfa57d868), SN4(0x05a82798), SN4(0x05a82798), SN4(0xfa57d868) },
- { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) },
- { SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000) },
- { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) }
-};
-
-static const int32_t synmatrix8[16][8] = {
- { SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798),
- SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798) },
- { SN8(0x0471ced0), SN8(0xf8275a10), SN8(0x018f8b84), SN8(0x06a6d988),
- SN8(0xf9592678), SN8(0xfe70747c), SN8(0x07d8a5f0), SN8(0xfb8e3130) },
- { SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac),
- SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54) },
- { SN8(0x018f8b84), SN8(0xfb8e3130), SN8(0x06a6d988), SN8(0xf8275a10),
- SN8(0x07d8a5f0), SN8(0xf9592678), SN8(0x0471ced0), SN8(0xfe70747c) },
- { SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000),
- SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000) },
- { SN8(0xfe70747c), SN8(0x0471ced0), SN8(0xf9592678), SN8(0x07d8a5f0),
- SN8(0xf8275a10), SN8(0x06a6d988), SN8(0xfb8e3130), SN8(0x018f8b84) },
- { SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54),
- SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac) },
- { SN8(0xfb8e3130), SN8(0x07d8a5f0), SN8(0xfe70747c), SN8(0xf9592678),
- SN8(0x06a6d988), SN8(0x018f8b84), SN8(0xf8275a10), SN8(0x0471ced0) },
- { SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868),
- SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868) },
- { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
- SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) },
- { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
- SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
- { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
- SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
- { SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000),
- SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000) },
- { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
- SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
- { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
- SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
- { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
- SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }
-};
diff --git a/src/modules/module-bt-device.c b/src/modules/module-bt-device.c
deleted file mode 100644
index 654130b..0000000
--- a/src/modules/module-bt-device.c
+++ /dev/null
@@ -1,949 +0,0 @@
-/***
- This file is part of PulseAudio.
-
- Copyright 2008 Joao Paulo Rechi Vita
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <linux/sockios.h>
-#include <arpa/inet.h>
-
-#include <pulse/xmalloc.h>
-#include <pulse/timeval.h>
-#include <pulse/sample.h>
-#include <pulsecore/module.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/socket-util.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
-#include <pulsecore/rtpoll.h>
-#include <pulsecore/time-smoother.h>
-#include <pulsecore/rtclock.h>
-
-#include "dbus-util.h"
-#include "module-bt-device-symdef.h"
-#include "bt-ipc.h"
-#include "bt-sbc.h"
-#include "bt-rtp.h"
-
-#define DEFAULT_SINK_NAME "bluetooth_sink"
-#define BUFFER_SIZE 2048
-#define MAX_BITPOOL 64
-#define MIN_BITPOOL 2
-#define SOL_SCO 17
-#define SCO_TXBUFS 0x03
-#define SCO_RXBUFS 0x04
-
-PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
-PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
- "name=<name of the device> "
- "addr=<address of the device> "
- "profile=<a2dp|hsp>");
-
-struct bt_a2dp {
- sbc_capabilities_t sbc_capabilities;
- sbc_t sbc; /* Codec data */
- pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */
- int codesize; /* SBC codesize */
- int samples; /* Number of encoded samples */
- uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
- int count; /* Codec transfer buffer counter */
-
- uint32_t nsamples; /* Cumulative number of codec samples */
- uint16_t seq_num; /* Cumulative packet sequence */
- int frame_count; /* Current frames in buffer*/
-};
-
-struct userdata {
- pa_core *core;
- pa_module *module;
- pa_sink *sink;
-
- pa_thread_mq thread_mq;
- pa_rtpoll *rtpoll;
- pa_rtpoll_item *rtpoll_item;
- pa_thread *thread;
-
- int64_t offset;
- pa_smoother *smoother;
-
- pa_memchunk memchunk;
- pa_mempool *mempool;
-
- char *name;
- char *addr;
- char *profile;
- pa_sample_spec ss;
-
- int audioservice_fd;
- int stream_fd;
-
- int transport;
- char *strtransport;
- int link_mtu;
- size_t block_size;
- pa_usec_t latency;
-
- struct bt_a2dp a2dp;
-};
-
-static const char* const valid_modargs[] = {
- "name",
- "addr",
- "profile",
- "rate",
- "channels",
- NULL
-};
-
-static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
- int e;
- pa_log_debug("sending %s", bt_audio_strmsg(msg->msg_type));
- if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
- e = 0;
- else {
- e = -errno;
- pa_log_error("Error sending data to audio service: %s(%d)", pa_cstrerror(errno), errno);
- }
- return e;
-}
-
-static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
- int e;
- const char *type;
-
- pa_log_debug("trying to receive msg from audio service...");
- if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
- type = bt_audio_strmsg(inmsg->msg_type);
- if (type) {
- pa_log_debug("Received %s", type);
- e = 0;
- }
- else {
- e = -EINVAL;
- pa_log_error("Bogus message type %d received from audio service", inmsg->msg_type);
- }
- }
- else {
- e = -errno;
- pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno);
- }
-
- return e;
-}
-
-static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) {
- int e = bt_audioservice_recv(sk, rsp_hdr);
- if (e == 0) {
- if (rsp_hdr->msg_type != expected_type) {
- e = -EINVAL;
- pa_log_error("Bogus message %s received while %s was expected", bt_audio_strmsg(rsp_hdr->msg_type),
- bt_audio_strmsg(expected_type));
- }
- }
- return e;
-}
-
-static int bt_getcaps(struct userdata *u) {
- int e;
- union {
- bt_audio_rsp_msg_header_t rsp_hdr;
- struct bt_getcapabilities_req getcaps_req;
- struct bt_getcapabilities_rsp getcaps_rsp;
- uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
- } msg;
-
- memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
- msg.getcaps_req.h.msg_type = BT_GETCAPABILITIES_REQ;
- strncpy(msg.getcaps_req.device, u->addr, 18);
- if (strcasecmp(u->profile, "a2dp") == 0)
- msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
- else if (strcasecmp(u->profile, "hsp") == 0)
- msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
- else {
- pa_log_error("invalid profile argument: %s", u->profile);
- return -1;
- }
- msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
-
- e = bt_audioservice_send(u->audioservice_fd, &msg.getcaps_req.h);
- if (e < 0) {
- pa_log_error("failed to send GETCAPABILITIES_REQ");
- return e;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_GETCAPABILITIES_RSP);
- if (e < 0) {
- pa_log_error("failed to expect for GETCAPABILITIES_RSP");
- return e;
- }
- if (msg.rsp_hdr.posix_errno != 0) {
- pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
- return -msg.rsp_hdr.posix_errno;
- }
-
- if ((u->transport = msg.getcaps_rsp.transport) == BT_CAPABILITIES_TRANSPORT_A2DP)
- u->a2dp.sbc_capabilities = msg.getcaps_rsp.sbc_capabilities;
-
- return 0;
-}
-
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
- switch (freq) {
- case BT_SBC_SAMPLING_FREQ_16000:
- case BT_SBC_SAMPLING_FREQ_32000:
- return 53;
- case BT_SBC_SAMPLING_FREQ_44100:
- switch (mode) {
- case BT_A2DP_CHANNEL_MODE_MONO:
- case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
- return 31;
- case BT_A2DP_CHANNEL_MODE_STEREO:
- case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
- return 53;
- default:
- pa_log_warn("Invalid channel mode %u", mode);
- return 53;
- }
- case BT_SBC_SAMPLING_FREQ_48000:
- switch (mode) {
- case BT_A2DP_CHANNEL_MODE_MONO:
- case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
- return 29;
- case BT_A2DP_CHANNEL_MODE_STEREO:
- case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
- return 51;
- default:
- pa_log_warn("Invalid channel mode %u", mode);
- return 51;
- }
- default:
- pa_log_warn("Invalid sampling freq %u", freq);
- return 53;
- }
-}
-
-static int bt_a2dp_init(struct userdata *u) {
- sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
- unsigned int max_bitpool, min_bitpool;
-
- switch (u->ss.rate) {
- case 48000:
- cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
- break;
- case 44100:
- cap->frequency = BT_SBC_SAMPLING_FREQ_44100;
- break;
- case 32000:
- cap->frequency = BT_SBC_SAMPLING_FREQ_32000;
- break;
- case 16000:
- cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
- break;
- default:
- pa_log_error("Rate %d not supported", u->ss.rate);
- return -1;
- }
-
- if (u->ss.channels == 2) {
- if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
- cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
- else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
- cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
- else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
- cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
- } else {
- if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
- cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
- }
-
- if (!cap->channel_mode) {
- pa_log_error("No supported channel modes");
- return -1;
- }
-
- if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
- cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
- else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
- cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
- else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
- cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
- else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
- cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
- else {
- pa_log_error("No supported block lengths");
- return -1;
- }
-
- if (cap->subbands & BT_A2DP_SUBBANDS_8)
- cap->subbands = BT_A2DP_SUBBANDS_8;
- else if (cap->subbands & BT_A2DP_SUBBANDS_4)
- cap->subbands = BT_A2DP_SUBBANDS_4;
- else {
- pa_log_error("No supported subbands");
- return -1;
- }
-
- if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
- cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
- else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
- cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
-
- min_bitpool = PA_MAX(MIN_BITPOOL, cap->min_bitpool);
- max_bitpool = PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
-
- cap->min_bitpool = min_bitpool;
- cap->max_bitpool = max_bitpool;
-
- return 0;
-}
-
-static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
- sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
-
- if (a2dp->sbc_initialized)
- sbc_reinit(&a2dp->sbc, 0);
- else
- sbc_init(&a2dp->sbc, 0);
- a2dp->sbc_initialized = TRUE;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
- a2dp->sbc.frequency = SBC_FREQ_16000;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
- a2dp->sbc.frequency = SBC_FREQ_32000;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
- a2dp->sbc.frequency = SBC_FREQ_44100;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
- a2dp->sbc.frequency = SBC_FREQ_48000;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
- a2dp->sbc.mode = SBC_MODE_MONO;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
- a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
- a2dp->sbc.mode = SBC_MODE_STEREO;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
- a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
-
- a2dp->sbc.allocation = (active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS);
-
- switch (active_capabilities.subbands) {
- case BT_A2DP_SUBBANDS_4:
- a2dp->sbc.subbands = SBC_SB_4;
- break;
- case BT_A2DP_SUBBANDS_8:
- a2dp->sbc.subbands = SBC_SB_8;
- break;
- }
-
- switch (active_capabilities.block_length) {
- case BT_A2DP_BLOCK_LENGTH_4:
- a2dp->sbc.blocks = SBC_BLK_4;
- break;
- case BT_A2DP_BLOCK_LENGTH_8:
- a2dp->sbc.blocks = SBC_BLK_8;
- break;
- case BT_A2DP_BLOCK_LENGTH_12:
- a2dp->sbc.blocks = SBC_BLK_12;
- break;
- case BT_A2DP_BLOCK_LENGTH_16:
- a2dp->sbc.blocks = SBC_BLK_16;
- break;
- }
-
- a2dp->sbc.bitpool = active_capabilities.max_bitpool;
- a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
-}
-
-static int bt_setconf(struct userdata *u) {
- int e;
- union {
- bt_audio_rsp_msg_header_t rsp_hdr;
- struct bt_setconfiguration_req setconf_req;
- struct bt_setconfiguration_rsp setconf_rsp;
- uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
- } msg;
-
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- e = bt_a2dp_init(u);
- if (e < 0) {
- pa_log_error("a2dp_init error");
- return e;
- }
- u->ss.format = PA_SAMPLE_S16LE;
- }
- else
- u->ss.format = PA_SAMPLE_U8;
-
- memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
- msg.setconf_req.h.msg_type = BT_SETCONFIGURATION_REQ;
- strncpy(msg.setconf_req.device, u->addr, 18);
- msg.setconf_req.transport = u->transport;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
- msg.setconf_req.sbc_capabilities = u->a2dp.sbc_capabilities;
- msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
-
- e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h);
- if (e < 0) {
- pa_log_error("failed to send BT_SETCONFIGURATION_REQ");
- return e;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_SETCONFIGURATION_RSP);
- if (e < 0) {
- pa_log_error("failed to expect BT_SETCONFIGURATION_RSP");
- return e;
- }
-
- if (msg.rsp_hdr.posix_errno != 0) {
- pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
- return -msg.rsp_hdr.posix_errno;
- }
-
- u->transport = msg.setconf_rsp.transport;
- u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO"));
- u->link_mtu = msg.setconf_rsp.link_mtu;
-
- /* setup SBC encoder now we agree on parameters */
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- bt_a2dp_setup(&u->a2dp);
- u->block_size = u->a2dp.codesize;
- pa_log_info("sbc parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
- u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
- }
- else
- u->block_size = u->link_mtu;
-
- return 0;
-}
-
-static int bt_getstreamfd(struct userdata *u) {
- int e;
-// uint32_t period_count = io->buffer_size / io->period_size;
- union {
- bt_audio_rsp_msg_header_t rsp_hdr;
- struct bt_streamstart_req start_req;
- struct bt_streamfd_ind streamfd_ind;
- uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
- } msg;
-
- memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
- msg.start_req.h.msg_type = BT_STREAMSTART_REQ;
-
- e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h);
- if (e < 0) {
- pa_log_error("failed to send BT_STREAMSTART_REQ");
- return e;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_STREAMSTART_RSP);
- if (e < 0) {
- pa_log_error("failed to expect BT_STREAMSTART_RSP");
- return e;
- }
-
- if (msg.rsp_hdr.posix_errno != 0) {
- pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
- return -msg.rsp_hdr.posix_errno;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.streamfd_ind.h, BT_STREAMFD_IND);
- if (e < 0) {
- pa_log_error("failed to expect BT_STREAMFD_IND");
- return e;
- }
-
- if (u->stream_fd >= 0)
- pa_close(u->stream_fd);
-
- u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
- if (u->stream_fd < 0) {
- pa_log_error("failed to get data fd: %s (%d)",pa_cstrerror(errno), errno);
- return -errno;
- }
-
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- if (pa_socket_set_sndbuf(u->stream_fd, 10*u->link_mtu) < 0) {
- pa_log_error("failed to set socket options for A2DP: %s (%d)",pa_cstrerror(errno), errno);
- return -errno;
- }
- }
-
-// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// /* FIXME : handle error codes */
- pa_make_fd_nonblock(u->stream_fd);
-// pa_make_socket_low_delay(u->stream_fd);
-
- return 0;
-}
-
-static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
- struct userdata *u = PA_SINK(o)->userdata;
-
- pa_log_debug("got message: %d", code);
- switch (code) {
-
- case PA_SINK_MESSAGE_SET_STATE:
- switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
- case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
- pa_smoother_pause(u->smoother, pa_rtclock_usec());
- break;
- case PA_SINK_IDLE:
- case PA_SINK_RUNNING:
- if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
- pa_smoother_resume(u->smoother, pa_rtclock_usec());
- break;
- case PA_SINK_UNLINKED:
- case PA_SINK_INIT:
- ;
- }
- break;
-
- case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t w, r;
- r = pa_smoother_get(u->smoother, pa_rtclock_usec());
- w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
- *((pa_usec_t*) data) = w > r ? w - r : 0;
- return 0;
- }
-
- }
-
- return pa_sink_process_msg(o, code, data, offset, chunk);
-}
-
-static int sco_process_render(struct userdata *u) {
- void *p;
- ssize_t l;
- int write_type = 0;
-
- u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log_debug("memblock asked size %d", u->block_size);
- u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log_debug("memchunk length %d", u->memchunk.length);
- pa_sink_render_into_full(u->sink, &u->memchunk);
-
- pa_assert(u->memchunk.length > 0);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
-
-sco_write:
- l = pa_write(u->stream_fd, (uint8_t*) p, u->memchunk.length, &write_type);
- pa_log_debug("memblock written to socket: %d bytes", l);
-
- pa_assert(l != 0);
-
- if (l < 0) {
- if (errno == EINTR) {
- pa_log_debug("EINTR");
- goto sco_write;
- }
- else if (errno == EAGAIN) {
- pa_log_debug("EAGAIN");
- goto sco_write;
- }
- else {
- pa_memblock_release(u->memchunk.memblock);
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- pa_log_debug("memchunk reseted");
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- return -1;
- }
- } else {
- pa_memblock_release(u->memchunk.memblock);
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- pa_log_debug("memchunk reseted");
- u->offset += l;
- return 0;
- }
-}
-
-static int a2dp_process_render(struct userdata *u) {
- ssize_t l;
- int write_type = 0, written;
- struct bt_a2dp *a2dp = &u->a2dp;
- struct rtp_header *header = (void *) a2dp->buffer;
- struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
-
- pa_assert(u);
-
- do {
- /* Render some data */
- int frame_size, encoded;
- void *p;
-
- u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
- pa_log_debug("memblock asked size %d", u->block_size);
- u->memchunk.length = pa_memblock_get_length(u->memchunk.memblock);
- pa_log_debug("memchunk length %d", u->memchunk.length);
- pa_sink_render_into_full(u->sink, &u->memchunk);
-
- pa_assert(u->memchunk.length > 0);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- frame_size = sbc_get_frame_length(&a2dp->sbc);
- pa_log_debug("SBC frame_size: %d", frame_size);
-
- encoded = sbc_encode(&a2dp->sbc, (uint8_t*) p, a2dp->codesize, a2dp->buffer + a2dp->count,
- sizeof(a2dp->buffer) - a2dp->count, &written);
- pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
- if (encoded <= 0) {
- pa_log_error("SBC encoding error (%d)", encoded);
- return -1;
- }
- pa_memblock_release(u->memchunk.memblock);
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- pa_log_debug("memchunk reseted");
-
- a2dp->count += written;
- a2dp->frame_count++;
- a2dp->samples += encoded / frame_size;
- a2dp->nsamples += encoded / frame_size;
-
- } while (a2dp->count + written <= u->link_mtu);
-
- /* write it to the fifo */
- memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
- payload->frame_count = a2dp->frame_count;
- header->v = 2;
- header->pt = 1;
- header->sequence_number = htons(a2dp->seq_num);
- header->timestamp = htonl(a2dp->nsamples);
- header->ssrc = htonl(1);
-
-avdtp_write:
- l = pa_write(u->stream_fd, a2dp->buffer, a2dp->count, &write_type);
- pa_log_debug("avdtp_write: requested %d bytes; written %d bytes", a2dp->count, l);
-
- pa_assert(l != 0);
-
- if (l < 0) {
- if (errno == EINTR) {
- pa_log_debug("EINTR");
- goto avdtp_write;
- }
- else if (errno == EAGAIN) {
- pa_log_debug("EAGAIN");
- goto avdtp_write;
- }
- else {
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- return -1;
- }
- }
-
- u->offset += a2dp->codesize*a2dp->frame_count;
-
- /* Reset buffer of data to send */
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
- a2dp->frame_count = 0;
- a2dp->samples = 0;
- a2dp->seq_num++;
-
- return 0;
-}
-
-static void thread_func(void *userdata) {
- struct userdata *u = userdata;
-
- pa_assert(u);
-
- pa_log_debug("IO Thread starting up");
-
- pa_thread_mq_install(&u->thread_mq);
- pa_rtpoll_install(u->rtpoll);
-
- pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
-
- for (;;) {
- int ret, l;
- struct pollfd *pollfd;
- uint64_t n;
- pa_usec_t usec;
-
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
- if (u->sink->thread_info.rewind_requested) {
- pa_sink_process_rewind(u->sink, 0);
- }
- }
-
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
-
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- if ((l = a2dp_process_render(u)) < 0)
- goto fail;
- }
- else {
- if ((l = sco_process_render(u)) < 0)
- goto fail;
- }
- pollfd->revents = 0;
-
- /* feed the time smoother */
- n = u->offset;
- if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
- n -= l;
- usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
- if (usec > u->latency)
- usec -= u->latency;
- else
- usec = 0;
- pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
- }
-
- /* Hmm, nothing to do. Let's sleep */
- pa_log_debug("IO thread going to sleep");
- pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
- if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
- pa_log_error("rtpoll_run < 0");
- goto fail;
- }
- pa_log_debug("IO thread waking up");
-
- if (ret == 0) {
- pa_log_debug("rtpoll_run == 0");
- goto finish;
- }
-
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- if (pollfd->revents & ~POLLOUT) {
- pa_log_error("FIFO shutdown.");
- goto fail;
- }
- }
-
-fail:
- /* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
- pa_log_debug("IO thread failed");
- pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
- pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-finish:
- pa_log_debug("IO thread shutting down");
-}
-
-int pa__init(pa_module* m) {
- int e;
- pa_modargs *ma;
- uint32_t channels;
- pa_sink_new_data data;
- struct pollfd *pollfd;
- struct userdata *u;
-
- pa_assert(m);
- m->userdata = u = pa_xnew0(struct userdata, 1);
- u->module = m;
- u->core = m->core;
- u->audioservice_fd = -1;
- u->stream_fd = -1;
- u->transport = -1;
- u->offset = 0;
- u->latency = 0;
- u->a2dp.sbc_initialized = FALSE;
- u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
- u->mempool = pa_mempool_new(FALSE);
- pa_memchunk_reset(&u->memchunk);
- u->rtpoll = pa_rtpoll_new();
- pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
- u->rtpoll_item = NULL;
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log_error("failed to parse module arguments");
- goto fail;
- }
- if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "name", DEFAULT_SINK_NAME)))) {
- pa_log_error("failed to get device name from module arguments");
- goto fail;
- }
- if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "addr", NULL)))) {
- pa_log_error("failed to get device address from module arguments");
- goto fail;
- }
- if (!(u->profile = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) {
- pa_log_error("failed to get profile from module arguments");
- goto fail;
- }
- if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) {
- pa_log_error("failed to get rate from module arguments");
- goto fail;
- }
- if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
- pa_log_error("failed to get channels from module arguments");
- goto fail;
- }
- u->ss.channels = (uint8_t) channels;
-
- /* connect to the bluez audio service */
- u->audioservice_fd = bt_audio_service_open();
- if (u->audioservice_fd <= 0) {
- pa_log_error("couldn't connect to bluetooth audio service");
- goto fail;
- }
- pa_log_debug("connected to the bluetooth audio service");
-
- /* queries device capabilities */
- e = bt_getcaps(u);
- if (e < 0) {
- pa_log_error("failed to get device capabilities");
- goto fail;
- }
- pa_log_debug("got device capabilities");
-
- /* configures the connection */
- e = bt_setconf(u);
- if (e < 0) {
- pa_log_error("failed to set config");
- goto fail;
- }
- pa_log_debug("connection to the device configured");
-
- /* gets the device socket */
- e = bt_getstreamfd(u);
- if (e < 0) {
- pa_log_error("failed to get stream fd (%d)", e);
- goto fail;
- }
- pa_log_debug("got the device socket");
-
- /* create sink */
- pa_sink_new_data_init(&data);
- data.driver = __FILE__;
- data.module = m;
- pa_sink_new_data_set_name(&data, u->name);
- pa_sink_new_data_set_sample_spec(&data, &u->ss);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s '%s' (%s)", u->strtransport, u->name, u->addr);
- pa_proplist_setf(data.proplist, "bluetooth.protocol", u->profile);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_API, "bluez");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "headset"); /*FIXME*/
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_VENDOR_PRODUCT_ID, "product_id"); /*FIXME*/
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_SERIAL, "serial"); /*FIXME*/
- u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
- pa_sink_new_data_done(&data);
- if (!u->sink) {
- pa_log_error("failed to create sink");
- goto fail;
- }
- 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->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;
- pollfd->events = pollfd->revents = 0;
-
- /* start rt thread */
- if (!(u->thread = pa_thread_new(thread_func, u))) {
- pa_log_error("failed to create IO thread");
- goto fail;
- }
- pa_sink_put(u->sink);
-
- pa_modargs_free(ma);
- return 0;
-
-fail:
- if (ma)
- pa_modargs_free(ma);
- pa__done(m);
- return -1;
-}
-
-void pa__done(pa_module *m) {
- struct userdata *u;
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- if (u->sink)
- pa_sink_unlink(u->sink);
-
- if (u->thread) {
- pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
- pa_thread_free(u->thread);
- }
-
- pa_thread_mq_done(&u->thread_mq);
-
- if (u->sink)
- pa_sink_unref(u->sink);
-
- if (u->rtpoll_item)
- pa_rtpoll_item_free(u->rtpoll_item);
-
- if (u->rtpoll)
- pa_rtpoll_free(u->rtpoll);
-
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
-
- if (u->mempool)
- pa_mempool_free(u->mempool);
-
- if (u->smoother)
- pa_smoother_free(u->smoother);
-
- if (u->name)
- pa_xfree(u->name);
-
- if (u->addr)
- pa_xfree(u->addr);
-
- if (u->profile)
- pa_xfree(u->profile);
-
- if (u->stream_fd >= 0)
- pa_close(u->stream_fd);
-
- if (u->audioservice_fd >= 0)
- pa_close(u->audioservice_fd);
-
- pa_xfree(u);
-}
diff --git a/src/modules/module-bt-discover.c b/src/modules/module-bt-discover.c
deleted file mode 100644
index 2b0d6c5..0000000
--- a/src/modules/module-bt-discover.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/***
- This file is part of PulseAudio.
-
- Copyright 2008 Joao Paulo Rechi Vita
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <pulse/xmalloc.h>
-#include <pulsecore/module.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/llist.h>
-
-#include "dbus-util.h"
-#include "module-bt-discover-symdef.h"
-
-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("");
-
-#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
-#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB"
-#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
-#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
-
-struct uuid {
- char *uuid;
- PA_LLIST_FIELDS(struct uuid);
-};
-
-struct device {
- char *name;
- char *object_path;
- int paired;
- struct adapter *adapter;
- char *alias;
- int connected;
- PA_LLIST_HEAD(struct uuid, uuid_list);
- char *address;
- int class;
- int trusted;
- PA_LLIST_FIELDS(struct device);
-};
-
-struct adapter {
- char *object_path;
- char *mode;
- char *address;
- PA_LLIST_HEAD(struct device, device_list);
- PA_LLIST_FIELDS(struct adapter);
-};
-
-struct userdata {
- pa_module *module;
- pa_dbus_connection *conn;
- PA_LLIST_HEAD(struct adapter, adapter_list);
-};
-
-static struct uuid *uuid_new(const char *uuid) {
- struct uuid *node = pa_xnew(struct uuid, 1);
- node->uuid = pa_xstrdup(uuid);
- PA_LLIST_INIT(struct uuid, node);
- return node;
-}
-
-static void uuid_free(struct uuid *uuid) {
- pa_xfree(uuid);
-}
-
-static struct device *device_new(const char *device, struct adapter *adapter) {
- struct device *node = pa_xnew(struct device, 1);
- node->name = NULL;
- node->object_path = pa_xstrdup(device);
- node->paired = -1;
- node->adapter = adapter;
- node->alias = NULL;
- node->connected = -1;
- PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
- node->address = NULL;
- node->class = -1;
- node->trusted = -1;
- PA_LLIST_INIT(struct device, node);
- return node;
-}
-
-static void device_free(struct device *device) {
- struct uuid *uuid_list_i;
- while (device->uuid_list) {
- uuid_list_i = device->uuid_list;
- PA_LLIST_REMOVE(struct uuid, device->uuid_list, uuid_list_i);
- uuid_free(uuid_list_i);
- }
- pa_xfree(device);
-}
-
-static struct adapter *adapter_new(const char *adapter) {
- struct adapter *node = pa_xnew(struct adapter, 1);
- node->object_path = pa_xstrdup(adapter);
- node->mode = NULL;
- node->address = NULL;
- PA_LLIST_HEAD_INIT(struct device, node->device_list);
- PA_LLIST_INIT(struct adapter, node);
- return node;
-}
-
-static void adapter_free(struct adapter *adapter) {
- struct device *device_list_i;
- while (adapter->device_list) {
- device_list_i = adapter->device_list;
- PA_LLIST_REMOVE(struct device, adapter->device_list, device_list_i);
- device_free(device_list_i);
- }
- pa_xfree(adapter);
-}
-
-static void print_devices(struct device *device_list) {
- struct device *device_list_i = device_list;
- while (device_list_i != NULL) {
- struct uuid *uuid_list_i = device_list_i->uuid_list;
- if (strcmp(device_list_i->object_path, "/DEVICE_HEAD") != 0) {
- pa_log(" [ %s ]", device_list_i->object_path);
- pa_log(" Name = %s", device_list_i->name);
- pa_log(" Paired = %d", device_list_i->paired);
- pa_log(" Adapter = %s", device_list_i->adapter->object_path);
- pa_log(" Alias = %s", device_list_i->alias);
- pa_log(" Connected = %d", device_list_i->connected);
- pa_log(" UUIDs = ");
- while (uuid_list_i != NULL) {
- if (strcmp(uuid_list_i->uuid, "UUID_HEAD") != 0)
- pa_log(" %s", uuid_list_i->uuid);
- uuid_list_i = uuid_list_i->next;
- }
- pa_log(" Address = %s", device_list_i->address);
- pa_log(" Class = 0x%x", device_list_i->class);
- pa_log(" Trusted = %d", device_list_i->trusted);
- }
- device_list_i = device_list_i->next;
- }
-}
-
-static void print_adapters(struct adapter *adapter_list) {
- struct adapter *adapter_list_i = adapter_list;
- while (adapter_list_i != NULL) {
- if (strcmp(adapter_list_i->object_path, "/ADAPTER_HEAD") != 0) {
- pa_log("[ %s ]", adapter_list_i->object_path);
- pa_log(" Mode = %s", adapter_list_i->mode);
- pa_log(" Address = %s", adapter_list_i->address);
- print_devices(adapter_list_i->device_list);
- }
- adapter_list_i = adapter_list_i->next;
- }
-}
-
-static void detect_adapters(struct userdata *u) {
- DBusError e;
- DBusMessage *m = NULL, *r = NULL;
- DBusMessageIter arg_i, element_i, dict_i, variant_i;
- struct adapter *adapter_list_i;
- const char *key, *value;
-
- pa_assert(u);
- dbus_error_init(&e);
-
- /* get adapters */
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
- if (!r) {
- pa_log("org.bluez.Manager.ListAdapters failed: %s", e.message);
- goto fail;
- }
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("org.bluez.Manager.ListAdapters reply has no arguments");
- goto fail;
- }
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("org.bluez.Manager.ListAdapters argument is not an array");
- goto fail;
- }
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
- struct adapter *node;
- dbus_message_iter_get_basic(&element_i, &value);
- node = adapter_new(value);
- PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
- }
- dbus_message_iter_next(&element_i);
- }
-
- /* get adapter properties */
- adapter_list_i = u->adapter_list;
- while (adapter_list_i != NULL) {
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "GetProperties"));
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
- if (!r) {
- pa_log("org.bluez.Adapter.GetProperties failed: %s", e.message);
- goto fail;
- }
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("org.bluez.Adapter.GetProperties reply has no arguments");
- goto fail;
- }
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("org.bluez.Adapter.GetProperties argument is not an array");
- goto fail;
- }
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
- dbus_message_iter_recurse(&element_i, &dict_i);
- dbus_message_iter_get_basic(&dict_i, &key);
- dbus_message_iter_next(&dict_i);
- dbus_message_iter_recurse(&dict_i, &variant_i);
- dbus_message_iter_get_basic(&variant_i, &value);
- if (strcmp(key, "Mode") == 0)
- adapter_list_i->mode = pa_xstrdup(value);
- else if (strcmp(key, "Address") == 0)
- adapter_list_i->address = pa_xstrdup(value);
- }
- dbus_message_iter_next(&element_i);
- }
- adapter_list_i = adapter_list_i->next;
- }
-
-fail:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
- dbus_error_free(&e);
-}
-
-static void detect_devices(struct userdata *u) {
- DBusError e;
- DBusMessage *m = NULL, *r = NULL;
- DBusMessageIter arg_i, element_i, dict_i, variant_i;
- struct adapter *adapter_list_i;
- struct device *device_list_i;
- const char *key, *value;
- unsigned int uvalue;
-
- pa_assert(u);
- dbus_error_init(&e);
-
- /* get devices of each adapter */
- adapter_list_i = u->adapter_list;
- while (adapter_list_i != NULL) {
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "ListDevices"));
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
- if (!r) {
- pa_log("org.bluez.Adapter.ListDevices failed: %s", e.message);
- goto fail;
- }
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("org.bluez.Adapter.ListDevices reply has no arguments");
- goto fail;
- }
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("org.bluez.Adapter.ListDevices argument is not an array");
- goto fail;
- }
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
- struct device *node;
- dbus_message_iter_get_basic(&element_i, &value);
- node = device_new(value, adapter_list_i);
- PA_LLIST_PREPEND(struct device, adapter_list_i->device_list, node);
- }
- dbus_message_iter_next(&element_i);
- }
- adapter_list_i = adapter_list_i->next;
- }
-
- /* get device properties */
- adapter_list_i = u->adapter_list;
- while (adapter_list_i != NULL) {
- device_list_i = adapter_list_i->device_list;
- while (device_list_i != NULL) {
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", device_list_i->object_path, "org.bluez.Device", "GetProperties"));
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
- if (!r) {
- pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
- goto fail;
- }
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("org.bluez.Device.GetProperties reply has no arguments");
- goto fail;
- }
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("org.bluez.Device.GetProperties argument is not an array");
- goto fail;
- }
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
- dbus_message_iter_recurse(&element_i, &dict_i);
- dbus_message_iter_get_basic(&dict_i, &key);
- dbus_message_iter_next(&dict_i);
- dbus_message_iter_recurse(&dict_i, &variant_i);
- if (strcmp(key, "Name") == 0) {
- dbus_message_iter_get_basic(&variant_i, &value);
- device_list_i->name = pa_xstrdup(value);
- }
- else if (strcmp(key, "Paired") == 0) {
- dbus_message_iter_get_basic(&variant_i, &uvalue);
- device_list_i->paired = uvalue;
- }
- else if (strcmp(key, "Alias") == 0) {
- dbus_message_iter_get_basic(&variant_i, &value);
- device_list_i->alias = pa_xstrdup(value);
- }
- else if (strcmp(key, "Connected") == 0) {
- dbus_message_iter_get_basic(&variant_i, &uvalue);
- device_list_i->connected = uvalue;
- }
- else if (strcmp(key, "UUIDs") == 0) {
- DBusMessageIter uuid_i;
- pa_bool_t is_audio_device = FALSE;
- dbus_message_iter_recurse(&variant_i, &uuid_i);
- while (dbus_message_iter_get_arg_type(&uuid_i) != DBUS_TYPE_INVALID) {
- struct uuid *node;
- dbus_message_iter_get_basic(&uuid_i, &value);
- node = uuid_new(value);
- PA_LLIST_PREPEND(struct uuid, device_list_i->uuid_list, node);
- if ( (strcasecmp(value, HSP_HS_UUID) == 0) || (strcasecmp(value, HFP_HS_UUID) == 0) ||
- (strcasecmp(value, A2DP_SOURCE_UUID) == 0) || (strcasecmp(value, A2DP_SINK_UUID) == 0) )
- is_audio_device = TRUE;
- dbus_message_iter_next(&uuid_i);
- }
- if (!is_audio_device) {
- /* remove the current device from the list */
- PA_LLIST_REMOVE(struct device, adapter_list_i->device_list, device_list_i);
- pa_xfree(device_list_i);
- break;
- }
- }
- else if (strcmp(key, "Address") == 0) {
- dbus_message_iter_get_basic(&variant_i, &value);
- device_list_i->address = pa_xstrdup(value);
- }
- else if (strcmp(key, "Class") == 0) {
- dbus_message_iter_get_basic(&variant_i, &uvalue);
- device_list_i->class = uvalue;
- }
- else if (strcmp(key, "Trusted") == 0) {
- dbus_message_iter_get_basic(&variant_i, &uvalue);
- device_list_i->trusted = uvalue;
- }
- }
- dbus_message_iter_next(&element_i);
- }
- device_list_i = device_list_i->next;
- }
- adapter_list_i = adapter_list_i->next;
- }
-
-fail:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
- dbus_error_free(&e);
-}
-
-static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
- DBusMessageIter arg_i;
- DBusError err;
- const char *value;
- struct userdata *u;
-
- pa_assert(bus);
- pa_assert(msg);
- pa_assert(userdata);
- u = userdata;
- dbus_error_init(&err);
-
- pa_log("dbus: interface=%s, path=%s, member=%s\n",
- dbus_message_get_interface(msg),
- dbus_message_get_path(msg),
- dbus_message_get_member(msg));
-
- if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterAdded")) {
- if (!dbus_message_iter_init(msg, &arg_i))
- pa_log("dbus: message has no parameters");
- else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
- pa_log("dbus: argument is not object path");
- else {
- dbus_message_iter_get_basic(&arg_i, &value);
- pa_log("hcid: adapter %s added", value);
- }
- }
- else if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterRemoved")) {
- if (!dbus_message_iter_init(msg, &arg_i))
- pa_log("dbus: message has no parameters");
- else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
- pa_log("dbus: argument is not object path");
- else {
- dbus_message_iter_get_basic(&arg_i, &value);
- pa_log("hcid: adapter %s removed", value);
- }
- }
- else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceCreated")) {
- if (!dbus_message_iter_init(msg, &arg_i))
- pa_log("dbus: message has no parameters");
- else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
- pa_log("dbus: argument is not object path");
- else {
- dbus_message_iter_get_basic(&arg_i, &value);
- pa_log("hcid: device %s created", value);
- }
- }
- else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
- if (!dbus_message_iter_init(msg, &arg_i))
- pa_log("dbus: message has no parameters");
- else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
- pa_log("dbus: argument is not object path");
- else {
- dbus_message_iter_get_basic(&arg_i, &value);
- pa_log("hcid: device %s removed", value);
- }
- }
-
- dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_HANDLED;
-}
-
-void pa__done(pa_module* m) {
- struct userdata *u;
- struct adapter *adapter_list_i;
-
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- while (u->adapter_list) {
- adapter_list_i = u->adapter_list;
- PA_LLIST_REMOVE(struct adapter, u->adapter_list, adapter_list_i);
- adapter_free(adapter_list_i);
- }
-
- pa_dbus_connection_unref(u->conn);
- pa_xfree(u);
- pa_log("Unloading module-bt-discover");
- return;
-}
-
-int pa__init(pa_module* m) {
- DBusError err;
- struct adapter *adapter_list_i;
- struct device *device_list_i;
- struct userdata *u;
-
- pa_assert(m);
- pa_log("Loading module-bt-discover");
- dbus_error_init(&err);
- m->userdata = u = pa_xnew0(struct userdata, 1);
- u->module = m;
- PA_LLIST_HEAD_INIT(struct adapter, u->adapter_list);
-
- /* connect to the bus */
- u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
- if ( dbus_error_is_set(&err) || (u->conn == NULL) ) {
- pa_log("Failed to get D-Bus connection: %s", err.message);
- goto fail;
- }
-
- /* static detection of bluetooth audio devices */
- detect_adapters(u);
- detect_devices(u);
-
- print_adapters(u->adapter_list);
-
- /* load device modules */
- adapter_list_i = u->adapter_list;
- while (adapter_list_i != NULL) {
- device_list_i = adapter_list_i->device_list;
- while (device_list_i != NULL) {
- pa_log("Loading module-bt-device for %s", device_list_i->name);
- /* TODO: call module */
- device_list_i = device_list_i->next;
- }
- adapter_list_i = adapter_list_i->next;
- }
-
- /* dynamic detection of bluetooth audio devices */
- if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
- pa_log_error("Failed to add filter function");
- goto fail;
- }
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Manager'", &err);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.Manager signals: %s: %s", err.name, err.message);
- goto fail;
- }
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter'", &err);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
- goto fail;
- }
-
- return 0;
-
-fail:
- dbus_error_free(&err);
- pa__done(m);
- return -1;
-}
commit 8b02c2fbc16db2a1d7dc34835bd939f984816f7e
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sat Aug 30 17:41:00 2008 -0300
Change all int vars that doesn't allow negative values to uint
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 335a8ed..e76264a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -71,14 +71,14 @@ struct bt_a2dp {
sbc_capabilities_t sbc_capabilities;
sbc_t sbc; /* Codec data */
pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */
- int codesize; /* SBC codesize */
- int samples; /* Number of encoded samples */
+ uint16_t codesize; /* SBC codesize */
+ uint8_t samples; /* Number of encoded samples */
uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
- int count; /* Codec transfer buffer counter */
+ uint16_t count; /* Codec transfer buffer counter */
uint32_t nsamples; /* Cumulative number of codec samples */
uint16_t seq_num; /* Cumulative packet sequence */
- int frame_count; /* Current frames in buffer*/
+ uint8_t frame_count; /* Current frames in buffer*/
};
struct userdata {
@@ -102,12 +102,12 @@ struct userdata {
char *profile;
pa_sample_spec ss;
- int audioservice_fd;
- int stream_fd;
+ uint8_t audioservice_fd;
+ uint8_t stream_fd;
- int transport;
+ uint8_t transport;
char *strtransport;
- int link_mtu;
+ uint16_t link_mtu;
size_t block_size;
pa_usec_t latency;
@@ -600,7 +600,8 @@ sco_write:
static int a2dp_process_render(struct userdata *u) {
ssize_t l;
- int write_type = 0, written;
+ uint8_t write_type = 0;
+ uint16_t written;
struct bt_a2dp *a2dp = &u->a2dp;
struct rtp_header *header = (void *) a2dp->buffer;
struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
@@ -609,7 +610,8 @@ static int a2dp_process_render(struct userdata *u) {
do {
/* Render some data */
- int frame_size, encoded;
+ uint16_t frame_size;
+ uint16_t encoded;
void *p;
u->memchunk.memblock = pa_memblock_new(u->mempool, u->block_size);
@@ -640,7 +642,6 @@ static int a2dp_process_render(struct userdata *u) {
a2dp->frame_count++;
a2dp->samples += encoded / frame_size;
a2dp->nsamples += encoded / frame_size;
-
} while (a2dp->count + written <= u->link_mtu);
/* write it to the fifo */
commit 4ae124b1c92f21bc6b089863bf9c97145f44eb99
Author: João Paulo Rechi Vita <joao.vita at gmail.com>
Date: Sun Aug 31 09:22:36 2008 -0300
Move bluetooth proximity module to src/modules/bluetooth/
diff --git a/src/Makefile.am b/src/Makefile.am
index c5af829..4c92f91 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1160,14 +1160,14 @@ endif
if HAVE_BLUEZ
modlibexec_LTLIBRARIES += \
- module-bt-proximity.la \
+ module-bluetooth-proximity.la \
module-bluetooth-discover.la \
libbluetooth-ipc.la \
libbluetooth-sbc.la \
module-bluetooth-device.la
pulselibexec_PROGRAMS += \
- bt-proximity-helper
+ proximity-helper
endif
# These are generated by a M4 script
@@ -1223,7 +1223,7 @@ SYMDEF_FILES = \
modules/module-rescue-streams-symdef.h \
modules/module-suspend-on-idle-symdef.h \
modules/module-hal-detect-symdef.h \
- modules/module-bt-proximity-symdef.h \
+ modules/bluetooth/module-bluetooth-proximity-symdef.h \
modules/bluetooth/module-bluetooth-discover-symdef.h \
modules/bluetooth/module-bluetooth-device-symdef.h \
modules/gconf/module-gconf-symdef.h \
@@ -1557,15 +1557,15 @@ gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Bluetooth proximity
-module_bt_proximity_la_SOURCES = modules/module-bt-proximity.c
-module_bt_proximity_la_LDFLAGS = -module -avoid-version
-module_bt_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
-module_bt_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/bt-proximity-helper\"
+module_bluetooth_proximity_la_SOURCES = modules/bluetooth/module-bluetooth-proximity.c
+module_bluetooth_proximity_la_LDFLAGS = -module -avoid-version
+module_bluetooth_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bluetooth_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/proximity-helper\"
-bt_proximity_helper_SOURCES = modules/bt-proximity-helper.c
-bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
-bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
-bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+proximity_helper_SOURCES = modules/bluetooth/proximity-helper.c
+proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
+proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
+proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Bluetooth sink / source
module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
@@ -1635,7 +1635,7 @@ daemon.conf: daemon/daemon.conf.in Makefile
install-exec-hook:
chown root $(DESTDIR)$(bindir)/pulseaudio ; true
chmod u+s $(DESTDIR)$(bindir)/pulseaudio
- -chmod u+s $(DESTDIR)$(pulselibexecdir)/bt-proximity-helper
+ -chmod u+s $(DESTDIR)$(pulselibexecdir)/proximity-helper
ln -sf pacat $(DESTDIR)$(bindir)/parec
rm -f $(DESTDIR)$(modlibexecdir)/*.a
rm -f $(DESTDIR)$(libdir)/libpulsedsp.a
diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c
new file mode 100644
index 0000000..5cca36e
--- /dev/null
+++ b/src/modules/bluetooth/module-bluetooth-proximity.c
@@ -0,0 +1,489 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2005-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
+
+#include "dbus-util.h"
+#include "module-bluetooth-proximity-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "sink=<sink name> "
+ "hci=<hci device> "
+);
+
+#define DEFAULT_HCI "hci0"
+
+static const char* const valid_modargs[] = {
+ "sink",
+ "rssi",
+ "hci",
+ NULL,
+};
+
+struct bonding {
+ struct userdata *userdata;
+ char address[18];
+
+ pid_t pid;
+ int fd;
+
+ pa_io_event *io_event;
+
+ enum {
+ UNKNOWN,
+ FOUND,
+ NOT_FOUND
+ } state;
+};
+
+struct userdata {
+ pa_module *module;
+ pa_dbus_connection *dbus_connection;
+
+ char *sink_name;
+ char *hci, *hci_path;
+
+ pa_hashmap *bondings;
+
+ unsigned n_found;
+ unsigned n_unknown;
+
+ pa_bool_t muted;
+};
+
+static void update_volume(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->muted && u->n_found > 0) {
+ pa_sink *s;
+
+ u->muted = FALSE;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
+ return;
+ }
+
+ pa_log_info("Found %u BT devices, unmuting.", u->n_found);
+ pa_sink_set_mute(s, FALSE);
+
+ } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
+ pa_sink *s;
+
+ u->muted = TRUE;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
+ return;
+ }
+
+ pa_log_info("No BT devices found, muting.");
+ pa_sink_set_mute(s, TRUE);
+
+ } else
+ pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
+}
+
+static void bonding_free(struct bonding *b) {
+ pa_assert(b);
+
+ if (b->state == FOUND)
+ pa_assert_se(b->userdata->n_found-- >= 1);
+
+ if (b->state == UNKNOWN)
+ pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+ if (b->pid != (pid_t) -1) {
+ kill(b->pid, SIGTERM);
+ waitpid(b->pid, NULL, 0);
+ }
+
+ if (b->fd >= 0)
+ pa_close(b->fd);
+
+ if (b->io_event)
+ b->userdata->module->core->mainloop->io_free(b->io_event);
+
+ pa_xfree(b);
+}
+
+static void io_event_cb(
+ pa_mainloop_api*a,
+ pa_io_event* e,
+ int fd,
+ pa_io_event_flags_t events,
+ void *userdata) {
+
+ struct bonding *b = userdata;
+ char x;
+ ssize_t r;
+
+ pa_assert(b);
+
+ if ((r = read(fd, &x, 1)) <= 0) {
+ pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
+
+ pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
+ bonding_free(b);
+ return;
+ }
+
+ pa_assert_se(r == 1);
+
+ if (b->state == UNKNOWN)
+ pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+ if (x == '+') {
+ pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
+
+ b->state = FOUND;
+ b->userdata->n_found++;
+
+ pa_log_info("Device '%s' is alive.", b->address);
+
+ } else {
+ pa_assert(x == '-');
+ pa_assert(b->state == UNKNOWN || b->state == FOUND);
+
+ if (b->state == FOUND)
+ b->userdata->n_found--;
+
+ b->state = NOT_FOUND;
+
+ pa_log_info("Device '%s' is dead.", b->address);
+ }
+
+ update_volume(b->userdata);
+}
+
+static struct bonding* bonding_new(struct userdata *u, const char *a) {
+ struct bonding *b = NULL;
+ DBusMessage *m = NULL, *r = NULL;
+ DBusError e;
+ const char *class;
+
+ pa_assert(u);
+ pa_assert(a);
+
+ pa_return_val_if_fail(strlen(a) == 17, NULL);
+ pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
+
+ if (!r) {
+ pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
+ goto fail;
+ }
+
+ if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
+ goto fail;
+ }
+
+ if (strcmp(class, "phone")) {
+ pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
+ goto fail;
+ }
+
+ b = pa_xnew(struct bonding, 1);
+ b->userdata = u;
+ pa_strlcpy(b->address, a, sizeof(b->address));
+ b->pid = (pid_t) -1;
+ b->fd = -1;
+ b->io_event = NULL;
+ b->state = UNKNOWN;
+ u->n_unknown ++;
+
+ pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
+
+ if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
+ pa_log("Failed to start helper tool.");
+ goto fail;
+ }
+
+ b->io_event = u->module->core->mainloop->io_new(
+ u->module->core->mainloop,
+ b->fd,
+ PA_IO_EVENT_INPUT,
+ io_event_cb,
+ b);
+
+ dbus_message_unref(m);
+ dbus_message_unref(r);
+
+ pa_hashmap_put(u->bondings, b->address, b);
+
+ return b;
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+
+ if (b)
+ bonding_free(b);
+
+ dbus_error_free(&e);
+ return NULL;
+}
+
+static void bonding_remove(struct userdata *u, const char *a) {
+ struct bonding *b;
+ pa_assert(u);
+
+ pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
+
+ pa_log_info("No longer watching device '%s'", b->address);
+ bonding_free(b);
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
+ struct userdata *u = userdata;
+ DBusError e;
+
+ dbus_error_init(&e);
+
+ if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
+ const char *a;
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
+ goto finish;
+ }
+
+ bonding_new(u, a);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
+
+ const char *a;
+
+ if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+ pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
+ goto finish;
+ }
+
+ bonding_remove(u, a);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+finish:
+
+ dbus_error_free(&e);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int add_matches(struct userdata *u, pa_bool_t add) {
+ char *filter1, *filter2;
+ DBusError e;
+ int r = -1;
+
+ pa_assert(u);
+ dbus_error_init(&e);
+
+ filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
+ filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
+
+ if (add) {
+ dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+ if (dbus_error_is_set(&e)) {
+ pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
+ goto finish;
+ }
+ } else
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+
+ if (add) {
+ dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+ if (dbus_error_is_set(&e)) {
+ pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+ goto finish;
+ }
+ } else
+ dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+ if (add)
+ pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
+ else
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
+
+ r = 0;
+
+finish:
+ pa_xfree(filter1);
+ pa_xfree(filter2);
+ dbus_error_free(&e);
+
+ return r;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ DBusError e;
+ DBusMessage *msg = NULL, *r = NULL;
+ DBusMessageIter iter, sub;
+
+ pa_assert(m);
+ dbus_error_init(&e);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
+ u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
+ u->n_found = u->n_unknown = 0;
+ u->muted = FALSE;
+
+ u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
+ pa_log("Failed to get D-Bus connection: %s", e.message);
+ goto fail;
+ }
+
+ if (add_matches(u, TRUE) < 0)
+ goto fail;
+
+ pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
+
+ if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
+ pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
+ goto fail;
+ }
+
+ dbus_message_iter_init(r, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+ const char *a = NULL;
+
+ dbus_message_iter_get_basic(&sub, &a);
+ bonding_new(u, a);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ dbus_message_unref(r);
+ dbus_message_unref(msg);
+
+ pa_modargs_free(ma);
+
+ if (pa_hashmap_size(u->bondings) == 0)
+ pa_log_warn("Warning: no phone device bonded.");
+
+ update_volume(u);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ dbus_error_free(&e);
+
+ if (msg)
+ dbus_message_unref(msg);
+
+ if (r)
+ dbus_message_unref(r);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->bondings) {
+ struct bonding *b;
+
+ while ((b = pa_hashmap_steal_first(u->bondings)))
+ bonding_free(b);
+
+ pa_hashmap_free(u->bondings, NULL, NULL);
+ }
+
+ if (u->dbus_connection) {
+ add_matches(u, FALSE);
+ pa_dbus_connection_unref(u->dbus_connection);
+ }
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u->hci_path);
+ pa_xfree(u->hci);
+ pa_xfree(u);
+}
diff --git a/src/modules/bluetooth/proximity-helper.c b/src/modules/bluetooth/proximity-helper.c
new file mode 100644
index 0000000..3767f01
--- /dev/null
+++ b/src/modules/bluetooth/proximity-helper.c
@@ -0,0 +1,202 @@
+/*
+ * Small SUID helper that allows us to ping a BT device. Borrows
+ * heavily from bluez-utils' l2ping, which is licensed as GPL2+
+ * and comes with a copyright like this:
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk at qualcomm.com>
+ * Copyright (C) 2002-2007 Marcel Holtmann <marcel at holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define PING_STRING "PulseAudio"
+#define IDENT 200
+#define TIMEOUT 4
+#define INTERVAL 2
+
+static void update_status(int found) {
+ static int status = -1;
+
+ if (!found && status != 0)
+ printf("-");
+ if (found && status <= 0)
+ printf("+");
+
+ fflush(stdout);
+ status = !!found;
+}
+
+int main(int argc, char *argv[]) {
+ struct sockaddr_l2 addr;
+ union {
+ l2cap_cmd_hdr hdr;
+ uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
+ } packet;
+ int fd = -1;
+ uint8_t id = IDENT;
+ int connected = 0;
+
+ assert(argc == 2);
+
+ for (;;) {
+ fd_set fds;
+ struct timeval end;
+ ssize_t r;
+
+ if (!connected) {
+
+ if (fd >= 0)
+ close(fd);
+
+ if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
+ fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, BDADDR_ANY);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "bind() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(argv[1], &addr.l2_bdaddr);
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ sleep(INTERVAL);
+ continue;
+ }
+
+ fprintf(stderr, "connect() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ connected = 1;
+ }
+
+ assert(connected);
+
+ memset(&packet, 0, sizeof(packet));
+ strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
+ packet.hdr.ident = id;
+ packet.hdr.len = htobs(sizeof(PING_STRING));
+ packet.hdr.code = L2CAP_ECHO_REQ;
+
+ if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ continue;
+ }
+
+ fprintf(stderr, "send() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ assert(r == sizeof(packet));
+
+ gettimeofday(&end, NULL);
+ end.tv_sec += TIMEOUT;
+
+ for (;;) {
+ struct timeval now, delta;
+
+ gettimeofday(&now, NULL);
+
+ if (timercmp(&end, &now, <=)) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ break;
+ }
+
+ timersub(&end, &now, &delta);
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
+ fprintf(stderr, "select() failed: %s", strerror(errno));
+ goto finish;
+ }
+
+ if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
+
+ if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+ update_status(0);
+ connected = 0;
+ sleep(INTERVAL);
+ break;
+ }
+
+ fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
+ goto finish;
+ }
+
+ assert(r >= L2CAP_CMD_HDR_SIZE);
+
+ if (packet.hdr.ident != id)
+ continue;
+
+ if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
+
+ if (++id >= 0xFF)
+ id = IDENT;
+
+ update_status(1);
+ sleep(INTERVAL);
+ break;
+ }
+ }
+ }
+
+finish:
+
+ if (fd >= 0)
+ close(fd);
+
+ return 1;
+}
diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bt-proximity-helper.c
deleted file mode 100644
index 3767f01..0000000
--- a/src/modules/bt-proximity-helper.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Small SUID helper that allows us to ping a BT device. Borrows
- * heavily from bluez-utils' l2ping, which is licensed as GPL2+
- * and comes with a copyright like this:
- *
- * Copyright (C) 2000-2001 Qualcomm Incorporated
- * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk at qualcomm.com>
- * Copyright (C) 2002-2007 Marcel Holtmann <marcel at holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#undef NDEBUG
-
-#include <assert.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/select.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
-
-#define PING_STRING "PulseAudio"
-#define IDENT 200
-#define TIMEOUT 4
-#define INTERVAL 2
-
-static void update_status(int found) {
- static int status = -1;
-
- if (!found && status != 0)
- printf("-");
- if (found && status <= 0)
- printf("+");
-
- fflush(stdout);
- status = !!found;
-}
-
-int main(int argc, char *argv[]) {
- struct sockaddr_l2 addr;
- union {
- l2cap_cmd_hdr hdr;
- uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
- } packet;
- int fd = -1;
- uint8_t id = IDENT;
- int connected = 0;
-
- assert(argc == 2);
-
- for (;;) {
- fd_set fds;
- struct timeval end;
- ssize_t r;
-
- if (!connected) {
-
- if (fd >= 0)
- close(fd);
-
- if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
- fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
- goto finish;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, BDADDR_ANY);
-
- if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- fprintf(stderr, "bind() failed: %s", strerror(errno));
- goto finish;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- str2ba(argv[1], &addr.l2_bdaddr);
-
- if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-
- if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
- update_status(0);
- sleep(INTERVAL);
- continue;
- }
-
- fprintf(stderr, "connect() failed: %s", strerror(errno));
- goto finish;
- }
-
- connected = 1;
- }
-
- assert(connected);
-
- memset(&packet, 0, sizeof(packet));
- strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
- packet.hdr.ident = id;
- packet.hdr.len = htobs(sizeof(PING_STRING));
- packet.hdr.code = L2CAP_ECHO_REQ;
-
- if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
-
- if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
- update_status(0);
- connected = 0;
- sleep(INTERVAL);
- continue;
- }
-
- fprintf(stderr, "send() failed: %s", strerror(errno));
- goto finish;
- }
-
- assert(r == sizeof(packet));
-
- gettimeofday(&end, NULL);
- end.tv_sec += TIMEOUT;
-
- for (;;) {
- struct timeval now, delta;
-
- gettimeofday(&now, NULL);
-
- if (timercmp(&end, &now, <=)) {
- update_status(0);
- connected = 0;
- sleep(INTERVAL);
- break;
- }
-
- timersub(&end, &now, &delta);
-
- FD_ZERO(&fds);
- FD_SET(fd, &fds);
-
- if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
- fprintf(stderr, "select() failed: %s", strerror(errno));
- goto finish;
- }
-
- if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
-
- if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
- update_status(0);
- connected = 0;
- sleep(INTERVAL);
- break;
- }
-
- fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
- goto finish;
- }
-
- assert(r >= L2CAP_CMD_HDR_SIZE);
-
- if (packet.hdr.ident != id)
- continue;
-
- if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
-
- if (++id >= 0xFF)
- id = IDENT;
-
- update_status(1);
- sleep(INTERVAL);
- break;
- }
- }
- }
-
-finish:
-
- if (fd >= 0)
- close(fd);
-
- return 1;
-}
diff --git a/src/modules/module-bt-proximity.c b/src/modules/module-bt-proximity.c
deleted file mode 100644
index f924c3c..0000000
--- a/src/modules/module-bt-proximity.c
+++ /dev/null
@@ -1,489 +0,0 @@
-/***
- This file is part of PulseAudio.
-
- Copyright 2005-2006 Lennart Poettering
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <pulse/xmalloc.h>
-#include <pulsecore/module.h>
-#include <pulsecore/log.h>
-#include <pulsecore/namereg.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/start-child.h>
-
-#include "dbus-util.h"
-#include "module-bt-proximity-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE(
- "sink=<sink name> "
- "hci=<hci device> "
-);
-
-#define DEFAULT_HCI "hci0"
-
-static const char* const valid_modargs[] = {
- "sink",
- "rssi",
- "hci",
- NULL,
-};
-
-struct bonding {
- struct userdata *userdata;
- char address[18];
-
- pid_t pid;
- int fd;
-
- pa_io_event *io_event;
-
- enum {
- UNKNOWN,
- FOUND,
- NOT_FOUND
- } state;
-};
-
-struct userdata {
- pa_module *module;
- pa_dbus_connection *dbus_connection;
-
- char *sink_name;
- char *hci, *hci_path;
-
- pa_hashmap *bondings;
-
- unsigned n_found;
- unsigned n_unknown;
-
- pa_bool_t muted;
-};
-
-static void update_volume(struct userdata *u) {
- pa_assert(u);
-
- if (u->muted && u->n_found > 0) {
- pa_sink *s;
-
- u->muted = FALSE;
-
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
- pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
- return;
- }
-
- pa_log_info("Found %u BT devices, unmuting.", u->n_found);
- pa_sink_set_mute(s, FALSE);
-
- } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
- pa_sink *s;
-
- u->muted = TRUE;
-
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
- pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
- return;
- }
-
- pa_log_info("No BT devices found, muting.");
- pa_sink_set_mute(s, TRUE);
-
- } else
- pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
-}
-
-static void bonding_free(struct bonding *b) {
- pa_assert(b);
-
- if (b->state == FOUND)
- pa_assert_se(b->userdata->n_found-- >= 1);
-
- if (b->state == UNKNOWN)
- pa_assert_se(b->userdata->n_unknown-- >= 1);
-
- if (b->pid != (pid_t) -1) {
- kill(b->pid, SIGTERM);
- waitpid(b->pid, NULL, 0);
- }
-
- if (b->fd >= 0)
- pa_close(b->fd);
-
- if (b->io_event)
- b->userdata->module->core->mainloop->io_free(b->io_event);
-
- pa_xfree(b);
-}
-
-static void io_event_cb(
- pa_mainloop_api*a,
- pa_io_event* e,
- int fd,
- pa_io_event_flags_t events,
- void *userdata) {
-
- struct bonding *b = userdata;
- char x;
- ssize_t r;
-
- pa_assert(b);
-
- if ((r = read(fd, &x, 1)) <= 0) {
- pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
-
- pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
- bonding_free(b);
- return;
- }
-
- pa_assert_se(r == 1);
-
- if (b->state == UNKNOWN)
- pa_assert_se(b->userdata->n_unknown-- >= 1);
-
- if (x == '+') {
- pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
-
- b->state = FOUND;
- b->userdata->n_found++;
-
- pa_log_info("Device '%s' is alive.", b->address);
-
- } else {
- pa_assert(x == '-');
- pa_assert(b->state == UNKNOWN || b->state == FOUND);
-
- if (b->state == FOUND)
- b->userdata->n_found--;
-
- b->state = NOT_FOUND;
-
- pa_log_info("Device '%s' is dead.", b->address);
- }
-
- update_volume(b->userdata);
-}
-
-static struct bonding* bonding_new(struct userdata *u, const char *a) {
- struct bonding *b = NULL;
- DBusMessage *m = NULL, *r = NULL;
- DBusError e;
- const char *class;
-
- pa_assert(u);
- pa_assert(a);
-
- pa_return_val_if_fail(strlen(a) == 17, NULL);
- pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
-
- dbus_error_init(&e);
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
- pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
-
- if (!r) {
- pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
- goto fail;
- }
-
- if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
- pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
- goto fail;
- }
-
- if (strcmp(class, "phone")) {
- pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
- goto fail;
- }
-
- b = pa_xnew(struct bonding, 1);
- b->userdata = u;
- pa_strlcpy(b->address, a, sizeof(b->address));
- b->pid = (pid_t) -1;
- b->fd = -1;
- b->io_event = NULL;
- b->state = UNKNOWN;
- u->n_unknown ++;
-
- pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
-
- if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
- pa_log("Failed to start helper tool.");
- goto fail;
- }
-
- b->io_event = u->module->core->mainloop->io_new(
- u->module->core->mainloop,
- b->fd,
- PA_IO_EVENT_INPUT,
- io_event_cb,
- b);
-
- dbus_message_unref(m);
- dbus_message_unref(r);
-
- pa_hashmap_put(u->bondings, b->address, b);
-
- return b;
-
-fail:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
-
- if (b)
- bonding_free(b);
-
- dbus_error_free(&e);
- return NULL;
-}
-
-static void bonding_remove(struct userdata *u, const char *a) {
- struct bonding *b;
- pa_assert(u);
-
- pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
-
- pa_log_info("No longer watching device '%s'", b->address);
- bonding_free(b);
-}
-
-static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
- struct userdata *u = userdata;
- DBusError e;
-
- dbus_error_init(&e);
-
- if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
- const char *a;
-
- if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
- pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
- goto finish;
- }
-
- bonding_new(u, a);
-
- return DBUS_HANDLER_RESULT_HANDLED;
-
- } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
-
- const char *a;
-
- if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
- pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
- goto finish;
- }
-
- bonding_remove(u, a);
-
- return DBUS_HANDLER_RESULT_HANDLED;
- }
-
-finish:
-
- dbus_error_free(&e);
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static int add_matches(struct userdata *u, pa_bool_t add) {
- char *filter1, *filter2;
- DBusError e;
- int r = -1;
-
- pa_assert(u);
- dbus_error_init(&e);
-
- filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
- filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
-
- if (add) {
- dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
-
- if (dbus_error_is_set(&e)) {
- pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
- goto finish;
- }
- } else
- dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
-
-
- if (add) {
- dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
-
- if (dbus_error_is_set(&e)) {
- pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
- dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
- goto finish;
- }
- } else
- dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
-
- if (add)
- pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
- else
- dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
-
- r = 0;
-
-finish:
- pa_xfree(filter1);
- pa_xfree(filter2);
- dbus_error_free(&e);
-
- return r;
-}
-
-int pa__init(pa_module*m) {
- pa_modargs *ma = NULL;
- struct userdata *u;
- DBusError e;
- DBusMessage *msg = NULL, *r = NULL;
- DBusMessageIter iter, sub;
-
- pa_assert(m);
- dbus_error_init(&e);
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
-
- m->userdata = u = pa_xnew0(struct userdata, 1);
- u->module = m;
- u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
- u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
- u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
- u->n_found = u->n_unknown = 0;
- u->muted = FALSE;
-
- u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-
- if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
- pa_log("Failed to get D-Bus connection: %s", e.message);
- goto fail;
- }
-
- if (add_matches(u, TRUE) < 0)
- goto fail;
-
- pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
-
- if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
- pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
- goto fail;
- }
-
- dbus_message_iter_init(r, &iter);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
- pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
- goto fail;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
- const char *a = NULL;
-
- dbus_message_iter_get_basic(&sub, &a);
- bonding_new(u, a);
-
- dbus_message_iter_next(&sub);
- }
-
- dbus_message_unref(r);
- dbus_message_unref(msg);
-
- pa_modargs_free(ma);
-
- if (pa_hashmap_size(u->bondings) == 0)
- pa_log_warn("Warning: no phone device bonded.");
-
- update_volume(u);
-
- return 0;
-
-fail:
-
- if (ma)
- pa_modargs_free(ma);
-
- pa__done(m);
-
- dbus_error_free(&e);
-
- if (msg)
- dbus_message_unref(msg);
-
- if (r)
- dbus_message_unref(r);
-
- return -1;
-}
-
-void pa__done(pa_module*m) {
- struct userdata *u;
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- if (u->bondings) {
- struct bonding *b;
-
- while ((b = pa_hashmap_steal_first(u->bondings)))
- bonding_free(b);
-
- pa_hashmap_free(u->bondings, NULL, NULL);
- }
-
- if (u->dbus_connection) {
- add_matches(u, FALSE);
- pa_dbus_connection_unref(u->dbus_connection);
- }
-
- pa_xfree(u->sink_name);
- pa_xfree(u->hci_path);
- pa_xfree(u->hci);
- pa_xfree(u);
-}
--
hooks/post-receive
PulseAudio Sound Server
More information about the pulseaudio-commits
mailing list