summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/headset.c459
-rw-r--r--audio/headset.h38
-rw-r--r--audio/main.c4
-rw-r--r--audio/manager.c323
-rw-r--r--audio/manager.h25
5 files changed, 444 insertions, 405 deletions
diff --git a/audio/headset.c b/audio/headset.c
index 44a6ce03..85c233d5 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -51,14 +51,11 @@
#include "dbus.h"
#include "logging.h"
-
-#define BUF_SIZE 1024
+#include "manager.h"
+#include "headset.h"
#define RING_INTERVAL 3000
-#define AUDIO_MANAGER_PATH "/org/bluez/audio"
-#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset"
-
typedef enum {
HEADSET_EVENT_KEYPRESS,
HEADSET_EVENT_GAIN,
@@ -77,7 +74,6 @@ typedef enum {
struct pending_connect {
int ch;
- DBusConnection *conn;
DBusMessage *msg;
GIOChannel *io;
};
@@ -102,31 +98,10 @@ struct headset {
headset_state_t state;
struct pending_connect *pending_connect;
-};
-struct manager {
- GIOChannel *server_sk;
- uint32_t record_id;
- int default_hs;
- GSList *headset_list;
+ DBusConnection *conn;
};
-static DBusConnection *connection = NULL;
-
-struct manager *audio_manager_new(DBusConnection *conn);
-void audio_manager_free(struct manager *manager);
-struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda);
-void audio_manager_add_headset(struct manager *manager, struct headset *hs);
-gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan);
-
-struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda);
-void audio_headset_unref(struct headset *hs);
-gboolean audio_headset_close_input(struct headset *hs);
-gboolean audio_headset_open_input(struct headset *hs, const char *audio_input);
-gboolean audio_headset_close_output(struct headset *hs);
-gboolean audio_headset_open_output(struct headset *hs, const char *audio_output);
-GIOError audio_headset_send_ring(struct headset *hs);
-
static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg);
static void pending_connect_free(struct pending_connect *c)
@@ -135,8 +110,6 @@ static void pending_connect_free(struct pending_connect *c)
g_io_channel_unref(c->io);
if (c->msg)
dbus_message_unref(c->msg);
- if (c->conn)
- dbus_connection_unref(c->conn);
g_free(c);
}
@@ -157,13 +130,6 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg,
return send_message_and_unref(conn, derr);
}
-static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg,
- const char *descr)
-{
- return error_reply(conn, msg, "org.bluez.Error.InvalidArguments",
- descr ? descr : "Invalid arguments in method call");
-}
-
static DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg)
{
return error_reply(conn, msg, "org.bluez.Error.AlreadyConnected",
@@ -226,7 +192,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf)
dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain,
DBUS_TYPE_INVALID);
- send_message_and_unref(connection, signal);
+ send_message_and_unref(hs->conn, signal);
}
static void hs_signal(struct headset *hs, const char *name)
@@ -239,7 +205,7 @@ static void hs_signal(struct headset *hs, const char *name)
return;
}
- send_message_and_unref(connection, signal);
+ send_message_and_unref(hs->conn, signal);
}
static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len)
@@ -398,7 +364,7 @@ static void send_cancel_auth(struct headset *hs)
dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address,
DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID);
- send_message_and_unref(connection, cancel);
+ send_message_and_unref(hs->conn, cancel);
}
static void auth_callback(DBusPendingCall *call, void *data)
@@ -435,7 +401,7 @@ static void auth_callback(DBusPendingCall *call, void *data)
dbus_message_unref(reply);
}
-static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond,
+gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond,
struct manager *manager)
{
int srv_sk, cli_sk;
@@ -468,16 +434,17 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond,
return TRUE;
}
- hs = audio_manager_find_headset_by_bda(manager, &addr.rc_bdaddr);
+ hs = manager_find_headset_by_bda(manager, &addr.rc_bdaddr);
if (!hs) {
- hs = audio_headset_new(connection, &addr.rc_bdaddr);
+ hs = audio_headset_new(manager_get_dbus_conn(manager),
+ &addr.rc_bdaddr);
if (!hs) {
error("Unable to create a new headset object");
close(cli_sk);
return TRUE;
}
- audio_manager_add_headset(manager, hs);
+ manager_add_headset(manager, hs);
}
if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) {
@@ -505,7 +472,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond,
dbus_message_append_args(auth, DBUS_TYPE_STRING, &address,
DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID);
- if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) {
+ if (dbus_connection_send_with_reply(hs->conn, auth, &pending, -1) == FALSE) {
error("Sending of authorization request failed");
goto failed;
}
@@ -655,7 +622,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
reply = dbus_message_new_method_return(hs->pending_connect->msg);
if (reply)
- send_message_and_unref(connection, reply);
+ send_message_and_unref(hs->conn, reply);
}
/* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */
@@ -672,7 +639,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
failed:
if (hs->pending_connect) {
- err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err);
+ err_connect_failed(hs->conn, hs->pending_connect->msg, err);
if (hs->pending_connect->io)
g_io_channel_close(hs->pending_connect->io);
pending_connect_free(hs->pending_connect);
@@ -731,7 +698,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he
reply = dbus_message_new_method_return(hs->pending_connect->msg);
if (reply)
- send_message_and_unref(hs->pending_connect->conn, reply);
+ send_message_and_unref(hs->conn, reply);
}
pending_connect_free(hs->pending_connect);
@@ -742,7 +709,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he
failed:
if (hs->pending_connect) {
- err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err);
+ err_connect_failed(hs->conn, hs->pending_connect->msg, err);
if (hs->pending_connect->io)
g_io_channel_close(hs->pending_connect->io);
pending_connect_free(hs->pending_connect);
@@ -828,47 +795,11 @@ failed:
return -1;
}
-static int server_socket(uint8_t *channel)
+gint headset_bda_cmp(gconstpointer headset, gconstpointer bda)
{
- int sock, lm;
- struct sockaddr_rc addr;
- socklen_t sa_len;
-
- sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (sock < 0) {
- error("server socket: %s (%d)", strerror(errno), errno);
- return -1;
- }
-
- lm = RFCOMM_LM_SECURE;
- if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
- error("server setsockopt: %s (%d)", strerror(errno), errno);
- close(sock);
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, BDADDR_ANY);
- addr.rc_channel = 0;
-
- if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- error("server bind: %s", strerror(errno), errno);
- close(sock);
- return -1;
- }
-
- if (listen(sock, 1) < 0) {
- error("server listen: %s", strerror(errno), errno);
- close(sock);
- return -1;
- }
-
- sa_len = sizeof(struct sockaddr_rc);
- getsockname(sock, (struct sockaddr *) &addr, &sa_len);
- *channel = addr.rc_channel;
+ const struct headset *hs = headset;
- return sock;
+ return bacmp(&hs->bda, bda);
}
static int create_ag_record(sdp_buf_t *buf, uint8_t ch)
@@ -932,7 +863,7 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch)
return ret;
}
-static uint32_t add_ag_record(uint8_t channel)
+uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel)
{
DBusMessage *msg, *reply;
DBusError derr;
@@ -956,7 +887,7 @@ static uint32_t add_ag_record(uint8_t channel)
&buf.data, buf.data_size, DBUS_TYPE_INVALID);
dbus_error_init(&derr);
- reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr);
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr);
free(buf.data);
dbus_message_unref(msg);
@@ -984,7 +915,7 @@ static uint32_t add_ag_record(uint8_t channel)
return rec_id;
}
-static int remove_ag_record(uint32_t rec_id)
+int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id)
{
DBusMessage *msg, *reply;
DBusError derr;
@@ -1000,7 +931,7 @@ static int remove_ag_record(uint32_t rec_id)
DBUS_TYPE_INVALID);
dbus_error_init(&derr);
- reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr);
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr);
dbus_message_unref(msg);
@@ -1035,7 +966,7 @@ static void get_record_reply(DBusPendingCall *call, void *data)
if (dbus_set_error_from_message(&derr, reply)) {
error("GetRemoteServiceRecord failed: %s", derr.message);
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
dbus_error_free(&derr);
goto failed;
}
@@ -1045,14 +976,14 @@ static void get_record_reply(DBusPendingCall *call, void *data)
DBUS_TYPE_INVALID)) {
error("Unable to get args from GetRecordReply");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
if (!array) {
error("Unable to get handle array from reply");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
@@ -1060,7 +991,7 @@ static void get_record_reply(DBusPendingCall *call, void *data)
if (!record) {
error("Unable to extract service record from reply");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
@@ -1078,14 +1009,14 @@ static void get_record_reply(DBusPendingCall *call, void *data)
if (c->ch == -1) {
error("Unable to extract RFCOMM channel from service record");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
if (rfcomm_connect(hs, &err) < 0) {
error("Unable to connect");
if (c->msg)
- err_connect_failed(c->conn, c->msg, err);
+ err_connect_failed(hs->conn, c->msg, err);
goto failed;
}
@@ -1109,7 +1040,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)
DBusMessage *reply = NULL;
if (!hs || !hs->sco)
- return err_not_connected(connection, msg);
+ return err_not_connected(hs->conn, msg);
if (msg) {
reply = dbus_message_new_method_return(msg);
@@ -1120,8 +1051,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)
if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) {
g_io_channel_close(hs->pending_connect->io);
if (hs->pending_connect->msg)
- err_connect_failed(hs->pending_connect->conn,
- hs->pending_connect->msg,
+ err_connect_failed(hs->conn, hs->pending_connect->msg,
EINTR);
pending_connect_free(hs->pending_connect);
hs->pending_connect = NULL;
@@ -1131,7 +1061,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)
close_sco(hs);
if (reply)
- send_message_and_unref(connection, reply);
+ send_message_and_unref(hs->conn, reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1162,8 +1092,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)
if (hs->pending_connect->io)
g_io_channel_close(hs->pending_connect->io);
if (hs->pending_connect->msg)
- err_connect_failed(hs->pending_connect->conn,
- hs->pending_connect->msg,
+ err_connect_failed(hs->conn, hs->pending_connect->msg,
EINTR);
pending_connect_free(hs->pending_connect);
hs->pending_connect = NULL;
@@ -1180,7 +1109,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)
hs->data_length = 0;
if (reply)
- send_message_and_unref(connection, reply);
+ send_message_and_unref(hs->conn, reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1204,7 +1133,7 @@ static DBusHandlerResult hs_is_connected(struct headset *hs, DBusMessage *msg)
dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
DBUS_TYPE_INVALID);
- send_message_and_unref(connection, reply);
+ send_message_and_unref(hs->conn, reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1231,9 +1160,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
error("GetRemoteServiceHandles failed: %s", derr.message);
if (c->msg) {
if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed"))
- err_connect_failed(c->conn, c->msg, EHOSTDOWN);
+ err_connect_failed(hs->conn, c->msg, EHOSTDOWN);
else
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
}
dbus_error_free(&derr);
goto failed;
@@ -1245,21 +1174,21 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
error("Unable to get args from reply");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
if (!array) {
error("Unable to get handle array from reply");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
if (array_len < 1) {
debug("No record handles found");
if (c->msg)
- err_not_supported(c->conn, c->msg);
+ err_not_supported(hs->conn, c->msg);
goto failed;
}
@@ -1272,7 +1201,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
if (!msg) {
error("Unable to allocate new method call");
if (c->msg)
- err_connect_failed(c->conn, c->msg, ENOMEM);
+ err_connect_failed(hs->conn, c->msg, ENOMEM);
goto failed;
}
@@ -1284,10 +1213,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
DBUS_TYPE_UINT32, &handle,
DBUS_TYPE_INVALID);
- if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) {
+ if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) {
error("Sending GetRemoteServiceRecord failed");
if (c->msg)
- err_connect_failed(c->conn, c->msg, EIO);
+ err_connect_failed(hs->conn, c->msg, EIO);
goto failed;
}
@@ -1327,7 +1256,6 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS;
- hs->pending_connect->conn = dbus_connection_ref(connection);
hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL;
msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0",
@@ -1347,13 +1275,13 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
DBUS_TYPE_STRING, &hs_svc,
DBUS_TYPE_INVALID);
- if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) {
+ if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) {
error("Sending GetRemoteServiceHandles failed");
pending_connect_free(hs->pending_connect);
hs->pending_connect = NULL;
hs->state = HEADSET_STATE_DISCONNECTED;
dbus_message_unref(msg);
- return err_connect_failed(connection, msg, EIO);
+ return err_connect_failed(hs->conn, msg, EIO);
}
dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL);
@@ -1363,7 +1291,7 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
return DBUS_HANDLER_RESULT_HANDLED;;
}
-GIOError audio_headset_send_ring(struct headset *hs)
+static GIOError audio_headset_send_ring(struct headset *hs)
{
const char *ring_str = "\r\nRING\r\n";
GIOError err;
@@ -1408,7 +1336,7 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)
assert(hs != NULL);
if (hs->state < HEADSET_STATE_CONNECTED)
- return err_not_connected(connection, msg);
+ return err_not_connected(hs->conn, msg);
if (msg) {
reply = dbus_message_new_method_return(msg);
@@ -1423,14 +1351,14 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)
if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) {
dbus_message_unref(reply);
- return err_failed(connection, msg);
+ return err_failed(hs->conn, msg);
}
hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs);
done:
if (reply)
- send_message_and_unref(connection, reply);
+ send_message_and_unref(hs->conn, reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1439,8 +1367,8 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)
{
DBusMessage *reply = NULL;
- if (!hs)
- return err_not_connected(connection, msg);
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return err_not_connected(hs->conn, msg);
if (msg) {
reply = dbus_message_new_method_return(msg);
@@ -1458,7 +1386,7 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)
done:
if (reply)
- send_message_and_unref(connection, reply);
+ send_message_and_unref(hs->conn, reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1469,17 +1397,14 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
struct pending_connect *c;
int sk, err;
- if (!hs)
- return err_not_connected(connection, msg);
-
if (hs->state < HEADSET_STATE_CONNECTED)
- return err_not_connected(connection, msg); /* FIXME: in progress error? */
+ return err_not_connected(hs->conn, msg); /* FIXME: in progress error? */
if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect)
- return err_already_connected(connection, msg);
+ return err_already_connected(hs->conn, msg);
if (hs->sco)
- return err_already_connected(connection, msg);
+ return err_already_connected(hs->conn, msg);
c = g_try_new0(struct pending_connect, 1);
if (!c)
@@ -1487,14 +1412,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
hs->state = HEADSET_STATE_PLAY_IN_PROGRESS;
- c->conn = dbus_connection_ref(connection);
c->msg = msg ? dbus_message_ref(msg) : NULL;
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
if (sk < 0) {
err = errno;
error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);
- err_connect_failed(connection, msg, err);
+ err_connect_failed(hs->conn, msg, err);
goto failed;
}
@@ -1512,13 +1436,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
err = errno;
error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);
- err_connect_failed(connection, msg, err);
+ err_connect_failed(hs->conn, msg, err);
goto failed;
}
if (set_nonblocking(sk) < 0) {
err = errno;
- err_connect_failed(connection, msg, err);
+ err_connect_failed(hs->conn, msg, err);
goto failed;
}
@@ -1626,6 +1550,8 @@ struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda)
bacpy(&hs->bda, bda);
+ hs->conn = dbus_connection_ref(conn);
+
return hs;
}
@@ -1633,6 +1559,8 @@ void audio_headset_unref(struct headset *hs)
{
assert(hs != NULL);
+ dbus_connection_unref(hs->conn);
+
g_free(hs);
}
@@ -1727,266 +1655,7 @@ gboolean audio_headset_open_input(struct headset *hs, const char *input)
return TRUE;
}
-gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan)
-{
- int srv_sk;
-
- assert(manager != NULL);
-
- if (manager->server_sk) {
- error("Server socket already created");
- return FALSE;
- }
-
- srv_sk = server_socket(&chan);
- if (srv_sk < 0) {
- error("Unable to create server socket");
- return FALSE;
- }
-
- if (!manager->record_id)
- manager->record_id = add_ag_record(chan);
-
- if (!manager->record_id) {
- error("Unable to register service record");
- close(srv_sk);
- return FALSE;
- }
-
- manager->server_sk = g_io_channel_unix_new(srv_sk);
- if (!manager->server_sk) {
- error("Unable to allocate new GIOChannel");
- remove_ag_record(manager->record_id);
- manager->record_id = 0;
- close(srv_sk);
- return FALSE;
- }
-
- g_io_add_watch(manager->server_sk,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- (GIOFunc) server_io_cb, manager);
-
- return TRUE;
-}
-
-static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda)
-{
- const struct headset *hs = headset;
-
- return bacmp(&hs->bda, bda);
-}
-
-struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda)
-{
- GSList *elem;
-
- assert(manager);
- elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp);
-
- return elem ? elem->data : NULL;
-}
-
-void audio_manager_add_headset(struct manager *manager, struct headset *hs)
-{
- assert(manager && hs);
-
- if (g_slist_find(manager->headset_list, hs))
- return;
-
- manager->headset_list = g_slist_append(manager->headset_list, hs);
-}
-
-static DBusHandlerResult am_create_headset(struct manager *manager,
- DBusMessage *msg)
+const char *audio_headset_get_path(struct headset *hs)
{
- const char *object_path;
- const char *address;
- struct headset *hs;
- bdaddr_t bda;
- DBusMessage *reply;
- DBusError derr;
-
- if (!manager)
- return err_not_connected(connection, msg);
-
- dbus_error_init(&derr);
- if (!dbus_message_get_args(msg, &derr,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID)) {
- err_invalid_args(connection, msg, derr.message);
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- if (dbus_error_is_set(&derr)) {
- err_invalid_args(connection, msg, derr.message);
- dbus_error_free(&derr);
- return DBUS_HANDLER_RESULT_HANDLED;
- }
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- str2ba(address, &bda);
- hs = audio_manager_find_headset_by_bda(manager, &bda);
- if (!hs) {
- hs = audio_headset_new(connection, &bda);
- if (!hs)
- return error_reply(connection, msg,
- "org.bluez.Error.Failed",
- "Unable to create new headset object");
- audio_manager_add_headset(manager, hs);
- }
-
- object_path = hs->object_path;
- dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path,
- DBUS_TYPE_INVALID);
-
- return send_message_and_unref(connection, reply);
-}
-
-static DBusHandlerResult am_remove_headset(struct manager *manager,
- DBusMessage *msg)
-{
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static DBusHandlerResult am_list_headsets(struct manager *manager,
- DBusMessage *msg)
-{
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static DBusHandlerResult am_get_default_headset(struct manager *manager,
- DBusMessage *msg)
-{
- DBusMessage *reply;
- char object_path[128];
- const char *opath = object_path;
-
- if (!manager)
- return err_not_connected(connection, msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d",
- manager->default_hs);
- dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath,
- DBUS_TYPE_INVALID);
-
- return send_message_and_unref(connection, reply);
-}
-
-static DBusHandlerResult am_change_default_headset(struct manager *manager,
- DBusMessage *msg)
-{
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static DBusHandlerResult am_message(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- const char *interface, *member;
- struct manager *manager = data;
-
- interface = dbus_message_get_interface(msg);
- member = dbus_message_get_member(msg);
-
- if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) &&
- !strcmp("Introspect", member))
- return simple_introspect(conn, msg, data);
-
- if (strcmp(interface, "org.bluez.audio.Manager") != 0)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- if (strcmp(member, "CreateHeadset") == 0)
- return am_create_headset(manager, msg);
-
- if (strcmp(member, "RemoveHeadset") == 0)
- return am_remove_headset(manager, msg);
-
- if (strcmp(member, "ListHeadsets") == 0)
- return am_list_headsets(manager, msg);
-
- if (strcmp(member, "DefaultHeadset") == 0)
- return am_get_default_headset(manager, msg);
-
- if (strcmp(member, "ChangeDefaultHeadset") == 0)
- return am_change_default_headset(manager, msg);
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static const DBusObjectPathVTable am_table = {
- .message_function = am_message,
-};
-
-struct manager *audio_manager_new(DBusConnection *conn)
-{
- struct manager *manager;
-
- manager = g_new0(struct manager, 1);
-
- if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH,
- &am_table, manager)) {
- error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH);
- g_free(manager);
- return NULL;
- }
-
- return manager;
-}
-
-void audio_manager_free(struct manager *manager)
-{
- assert(manager != NULL);
-
- if (manager->record_id) {
- remove_ag_record(manager->record_id);
- manager->record_id = 0;
- }
-
- if (manager->server_sk) {
- g_io_channel_unref(manager->server_sk);
- manager->server_sk = NULL;
- }
-
- if (manager->headset_list) {
- g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref,
- manager);
- g_slist_free(manager->headset_list);
- manager->headset_list = NULL;
- }
-
- g_free(manager);
-}
-
-static struct manager *manager = NULL;
-
-int headset_init(DBusConnection *conn)
-{
- connection = dbus_connection_ref(conn);
-
- manager = audio_manager_new(connection);
- if (!manager) {
- error("Failed to create an audio manager");
- dbus_connection_unref(connection);
- return -1;
- }
-
- audio_manager_create_headset_server(manager, 12);
-
- return 0;
-}
-
-void headset_exit(void)
-{
- audio_manager_free(manager);
-
- manager = NULL;
-
- dbus_connection_unref(connection);
-
- connection = NULL;
+ return hs->object_path;
}
diff --git a/audio/headset.h b/audio/headset.h
index a87be7e3..3027a9c1 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -20,6 +20,40 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+#ifndef __AUDIO_HEADSET_H
+#define __AUDIO_HEADSET_H
-int headset_init(DBusConnection *conn);
-void headset_exit(void);
+#include <bluetooth/bluetooth.h>
+
+#include <dbus/dbus.h>
+
+struct headset;
+
+#include "manager.h"
+
+#define BUF_SIZE 1024
+
+struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda);
+
+void audio_headset_unref(struct headset *hs);
+
+uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel);
+
+int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id);
+
+gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond,
+ struct manager *manager);
+
+gint headset_bda_cmp(gconstpointer headset, gconstpointer bda);
+
+const char *audio_headset_get_path(struct headset *hs);
+
+gboolean audio_headset_close_output(struct headset *hs);
+
+gboolean audio_headset_open_output(struct headset *hs, const char *output);
+
+gboolean audio_headset_close_input(struct headset *hs);
+
+gboolean audio_headset_open_input(struct headset *hs, const char *input);
+
+#endif /* __AUDIO_HEADSET_H_ */
diff --git a/audio/main.c b/audio/main.c
index bf0ddf4d..6ef3b1c6 100644
--- a/audio/main.c
+++ b/audio/main.c
@@ -75,15 +75,11 @@ int main(int argc, char *argv[])
audio_init(conn);
- headset_init(conn);
-
if (argc > 1 && !strcmp(argv[1], "-s"))
register_external_service(conn, "audio", "Audio service", "");
g_main_loop_run(main_loop);
- headset_exit();
-
audio_exit();
dbus_connection_unref(conn);
diff --git a/audio/manager.c b/audio/manager.c
index a7e3afd9..743e50d5 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -28,9 +28,14 @@
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <stdint.h>
+#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
#include <glib.h>
#include <dbus/dbus.h>
@@ -38,6 +43,7 @@
#include "dbus.h"
#include "logging.h"
+#include "headset.h"
#include "manager.h"
#ifndef UNIX_PATH_MAX
@@ -46,10 +52,43 @@
#define SOCKET_NAME "/org/bluez/audio"
-static DBusConnection *connection = NULL;
+struct manager {
+ DBusConnection *conn;
+ GIOChannel *hs_server;
+ uint32_t hs_record_id;
+ int default_hs;
+ GSList *headset_list;
+};
+
+static struct manager *manager = NULL;
static int unix_sock = -1;
+/* FIXME: Remove these once global error functions exist */
+static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg,
+ const char *name, const char *descr)
+{
+ DBusMessage *derr;
+
+ if (!conn || !msg)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ derr = dbus_message_new_error(msg, name, descr);
+ if (!derr) {
+ error("Unable to allocate new error return");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ return send_message_and_unref(conn, derr);
+}
+
+static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg,
+ const char *descr)
+{
+ return error_reply(conn, msg, "org.bluez.Error.InvalidArguments",
+ descr ? descr : "Invalid arguments in method call");
+}
+
static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)
{
struct sockaddr_un addr;
@@ -79,6 +118,276 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)
return TRUE;
}
+static GIOChannel *server_socket(uint8_t *channel)
+{
+ int sock, lm;
+ struct sockaddr_rc addr;
+ socklen_t sa_len;
+ GIOChannel *io;
+
+ sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sock < 0) {
+ error("server socket: %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+
+ lm = RFCOMM_LM_SECURE;
+ if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+ error("server setsockopt: %s (%d)", strerror(errno), errno);
+ close(sock);
+ return NULL;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, BDADDR_ANY);
+ addr.rc_channel = 0;
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ error("server bind: %s", strerror(errno), errno);
+ close(sock);
+ return NULL;
+ }
+
+ if (listen(sock, 1) < 0) {
+ error("server listen: %s", strerror(errno), errno);
+ close(sock);
+ return NULL;
+ }
+
+ sa_len = sizeof(struct sockaddr_rc);
+ getsockname(sock, (struct sockaddr *) &addr, &sa_len);
+ *channel = addr.rc_channel;
+
+ io = g_io_channel_unix_new(sock);
+ if (!io) {
+ error("Unable to allocate new io channel");
+ close(sock);
+ return NULL;
+ }
+
+ return io;
+}
+
+static gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan)
+{
+ assert(manager != NULL);
+
+ if (manager->hs_server) {
+ error("Server socket already created");
+ return FALSE;
+ }
+
+ manager->hs_server = server_socket(&chan);
+ if (!manager->hs_server)
+ return FALSE;
+
+ if (!manager->hs_record_id)
+ manager->hs_record_id = headset_add_ag_record(manager->conn, chan);
+
+ if (!manager->hs_record_id) {
+ error("Unable to register service record");
+ g_io_channel_unref(manager->hs_server);
+ manager->hs_server = NULL;
+ return FALSE;
+ }
+
+ g_io_add_watch(manager->hs_server,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ (GIOFunc) headset_server_io_cb, manager);
+
+ return TRUE;
+}
+
+struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *bda)
+{
+ GSList *elem;
+
+ assert(manager);
+ elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp);
+
+ return elem ? elem->data : NULL;
+}
+
+void manager_add_headset(struct manager *manager, struct headset *hs)
+{
+ assert(manager && hs);
+
+ if (g_slist_find(manager->headset_list, hs))
+ return;
+
+ manager->headset_list = g_slist_append(manager->headset_list, hs);
+}
+
+static DBusHandlerResult am_create_headset(struct manager *manager,
+ DBusMessage *msg)
+{
+ const char *object_path;
+ const char *address;
+ struct headset *hs;
+ bdaddr_t bda;
+ DBusMessage *reply;
+ DBusError derr;
+
+ dbus_error_init(&derr);
+ if (!dbus_message_get_args(msg, &derr,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID)) {
+ err_invalid_args(manager->conn, msg, derr.message);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_error_is_set(&derr)) {
+ err_invalid_args(manager->conn, msg, derr.message);
+ dbus_error_free(&derr);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ str2ba(address, &bda);
+ hs = manager_find_headset_by_bda(manager, &bda);
+ if (!hs) {
+ hs = audio_headset_new(manager->conn, &bda);
+ if (!hs)
+ return error_reply(manager->conn, msg,
+ "org.bluez.Error.Failed",
+ "Unable to create new headset object");
+ manager_add_headset(manager, hs);
+ }
+
+ object_path = audio_headset_get_path(hs);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(manager->conn, reply);
+}
+
+static DBusHandlerResult am_remove_headset(struct manager *manager,
+ DBusMessage *msg)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult am_list_headsets(struct manager *manager,
+ DBusMessage *msg)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult am_get_default_headset(struct manager *manager,
+ DBusMessage *msg)
+{
+ DBusMessage *reply;
+ char object_path[128];
+ const char *opath = object_path;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d",
+ manager->default_hs);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(manager->conn, reply);
+}
+
+static DBusHandlerResult am_change_default_headset(struct manager *manager,
+ DBusMessage *msg)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult am_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *interface, *member;
+ struct manager *manager = data;
+
+ interface = dbus_message_get_interface(msg);
+ member = dbus_message_get_member(msg);
+
+ if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) &&
+ !strcmp("Introspect", member))
+ return simple_introspect(conn, msg, data);
+
+ if (strcmp(interface, "org.bluez.audio.Manager") != 0)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (strcmp(member, "CreateHeadset") == 0)
+ return am_create_headset(manager, msg);
+
+ if (strcmp(member, "RemoveHeadset") == 0)
+ return am_remove_headset(manager, msg);
+
+ if (strcmp(member, "ListHeadsets") == 0)
+ return am_list_headsets(manager, msg);
+
+ if (strcmp(member, "DefaultHeadset") == 0)
+ return am_get_default_headset(manager, msg);
+
+ if (strcmp(member, "ChangeDefaultHeadset") == 0)
+ return am_change_default_headset(manager, msg);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable am_table = {
+ .message_function = am_message,
+};
+
+static struct manager *audio_manager_new(DBusConnection *conn)
+{
+ struct manager *manager;
+
+ manager = g_new0(struct manager, 1);
+
+ if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH,
+ &am_table, manager)) {
+ error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH);
+ g_free(manager);
+ return NULL;
+ }
+
+ manager->conn = dbus_connection_ref(conn);
+
+ return manager;
+}
+
+static void audio_manager_free(struct manager *manager)
+{
+ assert(manager != NULL);
+
+ if (manager->hs_record_id) {
+ headset_remove_ag_record(manager->conn, manager->hs_record_id);
+ manager->hs_record_id = 0;
+ }
+
+ if (manager->hs_server) {
+ g_io_channel_unref(manager->hs_server);
+ manager->hs_server = NULL;
+ }
+
+ if (manager->headset_list) {
+ g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref,
+ manager);
+ g_slist_free(manager->headset_list);
+ manager->headset_list = NULL;
+ }
+
+ dbus_connection_unref(manager->conn);
+
+ g_free(manager);
+}
+
+DBusConnection *manager_get_dbus_conn(struct manager *manager)
+{
+ return manager->conn;
+}
+
int audio_init(DBusConnection *conn)
{
GIOChannel *io;
@@ -112,7 +421,13 @@ int audio_init(DBusConnection *conn)
g_io_channel_unref(io);
- connection = dbus_connection_ref(conn);
+ manager = audio_manager_new(conn);
+ if (!manager) {
+ error("Failed to create an audio manager");
+ return -1;
+ }
+
+ audio_manager_create_headset_server(manager, 12);
return 0;
}
@@ -123,7 +438,7 @@ void audio_exit(void)
unix_sock = -1;
- dbus_connection_unref(connection);
+ audio_manager_free(manager);
- connection = NULL;
+ manager = NULL;
}
diff --git a/audio/manager.h b/audio/manager.h
index f97b563a..8335c79a 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -20,6 +20,31 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+#ifndef __AUDIO_MANAGER_H
+#define __AUDIO_MANAGER_H
+
+#include <bluetooth/bluetooth.h>
+
+#include <dbus/dbus.h>
+
+struct manager;
+
+#include "headset.h"
+
+#define AUDIO_MANAGER_PATH "/org/bluez/audio"
+
+#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset"
+
+void manager_add_headset(struct manager *manager, struct headset *hs);
+
+struct headset *manager_find_headset_by_bda(struct manager *manager,
+ bdaddr_t *bda);
+
+DBusConnection *manager_get_dbus_conn(struct manager *manager);
int audio_init(DBusConnection *conn);
+
void audio_exit(void);
+
+#endif /* __AUDIO_MANAGER_H_ */
+