summaryrefslogtreecommitdiffstats
path: root/audio/headset.c
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2007-01-23 20:34:45 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2007-01-23 20:34:45 +0000
commit7f496bf581e831e8b256ce9b0447a13a882fdad3 (patch)
treeb15b57c985aadd5f6acf3eb52daac40d94a6dd33 /audio/headset.c
parent49cc58fa6f9b70c5d2c0dbd0fb2d6efbae8c246d (diff)
rework of the headset daemon code
Diffstat (limited to 'audio/headset.c')
-rw-r--r--audio/headset.c1075
1 files changed, 662 insertions, 413 deletions
diff --git a/audio/headset.c b/audio/headset.c
index 728d38d2..78d13092 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -35,6 +35,7 @@
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
+#include <assert.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
@@ -55,59 +56,77 @@
#define RING_INTERVAL 3000
-#define HEADSET_PATH "/org/bluez/headset"
-static const char *hs_path = HEADSET_PATH;
+#define AUDIO_MANAGER_PATH "/org/bluez/audio"
+#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset"
struct pending_connect {
- bdaddr_t bda;
int ch;
DBusConnection *conn;
DBusMessage *msg;
GIOChannel *io;
};
+typedef enum {
+ HEADSET_STATE_DISCONNECTED,
+ HEADSET_STATE_CONNECT_IN_PROGRESS,
+ HEADSET_STATE_CONNECTED,
+ HEADSET_STATE_PLAY_IN_PROGRESS,
+ HEADSET_STATE_PLAYING,
+} headset_state_t;
+
struct headset {
- char address[18];
+ char object_path[128];
+ bdaddr_t bda;
GIOChannel *rfcomm;
GIOChannel *sco;
+ char *input;
GIOChannel *audio_input;
- int out;
+ char *output;
+ GIOChannel *audio_output;
guint ring_timer;
char buf[BUF_SIZE];
int data_start;
int data_length;
-};
-
-static struct pending_connect *connect_in_progress = NULL;
-static uint8_t config_channel = 12;
+ headset_state_t state;
+ struct pending_connect *connect_in_progress;
+ uint32_t record_id;
+ GIOChannel *server_sk;
+};
-static uint32_t record_id = 0;
+struct manager {
+ GSList *headset_list;
+};
-static char *on_init_bda = NULL;
static DBusConnection *connection = NULL;
-
static GMainLoop *main_loop = NULL;
-static struct headset *hs = NULL;
-
-static GIOChannel *server_sk = NULL;
-
-static char *audio_input = NULL;
-static char *audio_output = NULL;
-
-static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
- const char *address);
-static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg);
-static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg);
-static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg);
-static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg);
-static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg);
+struct manager* audio_manager_new(DBusConnection *conn);
+void audio_manager_add_headset(struct manager *amanager, struct headset *hs);
+static DBusHandlerResult am_default_headset(struct manager *amanager, DBusMessage *msg);
+static DBusHandlerResult am_connect(struct manager *amanager, DBusMessage *msg);
+
+struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel);
+int audio_headset_close_input(struct headset* hs);
+int audio_headset_open_input(struct headset* hs, const char *audio_input);
+int audio_headset_close_output(struct headset* hs);
+int audio_headset_open_output(struct headset* hs, const char *audio_output);
+void audio_headset_create_server_socket(struct headset *hs, uint8_t chan);
+int audio_headset_send_ring(struct headset *hs);
+
+static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg);
+static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg);
+static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg);
+static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg);
+static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg);
+static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg);
+static void hs_signal(struct headset *hs, const char *name);
+static void hs_signal_gain_setting(struct headset *hs, const char *buf);
static int set_nonblocking(int fd, int *err)
{
@@ -146,8 +165,6 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io)
if (c->conn)
dbus_connection_unref(c->conn);
free(c);
-
- connect_in_progress = NULL;
}
static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg,
@@ -203,7 +220,7 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg)
return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed");
}
-static void send_gain_setting(const char *buf)
+static void hs_signal_gain_setting(struct headset *hs, const char *buf)
{
const char *name;
DBusMessage *signal;
@@ -226,7 +243,7 @@ static void send_gain_setting(const char *buf)
return;
}
- signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name);
+ signal = dbus_message_new_signal(hs->object_path, "org.bluez.audio.Headset", name);
if (!signal) {
error("Unable to allocate new GainChanged signal");
return;
@@ -241,11 +258,11 @@ static void send_gain_setting(const char *buf)
dbus_message_unref(signal);
}
-static void send_simple_signal(const char *name)
+static void hs_signal(struct headset *hs, const char *name)
{
DBusMessage *signal;
- signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name);
+ signal = dbus_message_new_signal(hs->object_path, "org.bluez.audio.Headset", name);
if (!signal) {
error("Unable to allocate new AnswerRequested signal");
return;
@@ -255,28 +272,34 @@ static void send_simple_signal(const char *name)
dbus_message_unref(signal);
}
-static void parse_headset_event(const char *buf, char *rsp, int rsp_len)
+static int parse_headset_event(const char *buf, char *rsp, int rsp_len)
{
+ int rv = 0;
+
printf("Received: %s\n", buf);
/* Return an error if this is not a proper AT command */
if (strncmp(buf, "AT", 2)) {
snprintf(rsp, rsp_len, "\r\nERROR\r\n");
- return;
+ return rv;
}
buf += 2;
if (!strncmp(buf, "+CKPD", 5))
- send_simple_signal("AnswerRequested");
+ rv = 0;
else if (!strncmp(buf, "+VG", 3))
- send_gain_setting(buf);
+ rv = 1;
snprintf(rsp, rsp_len, "\r\nOK\r\n");
+
+ /* return 1 if gain event */
+ return rv;
}
-static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
+static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
+ struct headset *hs = (struct headset *)data;
int sk, ret, free_space;
unsigned char buf[BUF_SIZE];
char *cr;
@@ -322,8 +345,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_
memset(rsp, 0, sizeof(rsp));
- parse_headset_event(&hs->buf[hs->data_start],
- rsp, sizeof(rsp));
+ if (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp)) == 1)
+ hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2);
+ else
+ hs_signal(hs, "AnswerRequested");
len = strlen(rsp);
written = 0;
@@ -355,15 +380,19 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_
return TRUE;
failed:
- info("Disconnected from %s", hs->address);
- send_simple_signal("Disconnected");
- if (hs->sco)
+ info("Disconnected from %s", hs->object_path);
+ hs_signal(hs, "Disconnected");
+ if (hs->sco) {
g_io_channel_close(hs->sco);
- if (hs->out >= 0)
- close(hs->out);
+ hs->sco = NULL;
+ }
+ if (hs->audio_output) {
+ g_io_channel_close(hs->audio_output);
+ hs->audio_output = NULL;
+ }
g_io_channel_close(chan);
- free(hs);
- hs = NULL;
+ hs->rfcomm = NULL;
+
return FALSE;
}
@@ -372,6 +401,10 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
int srv_sk, cli_sk;
struct sockaddr_rc addr;
socklen_t size;
+ char hs_address[18];
+ struct headset *hs = (struct headset *)data;
+
+ assert(hs != NULL);
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
@@ -381,7 +414,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
if (cond & (G_IO_HUP | G_IO_ERR)) {
error("Hangup or error on rfcomm server socket");
g_io_channel_close(chan);
- server_sk = NULL;
+ hs->server_sk = NULL;
return TRUE;
}
@@ -394,125 +427,72 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
return TRUE;
}
- if (hs || connect_in_progress) {
+ if (hs->state > HEADSET_STATE_DISCONNECTED) {
debug("Refusing new connection since one already exists");
close(cli_sk);
return TRUE;
}
- hs = malloc(sizeof(struct headset));
- if (!hs) {
- error("Allocating new hs connection struct failed!");
- close(cli_sk);
- return TRUE;
- }
-
- memset(hs, 0, sizeof(struct headset));
-
- hs->out = -1;
-
hs->rfcomm = g_io_channel_unix_new(cli_sk);
if (!hs->rfcomm) {
error("Allocating new GIOChannel failed!");
close(cli_sk);
- free(hs);
- hs = NULL;
return TRUE;
}
- ba2str(&addr.rc_bdaddr, hs->address);
+ ba2str(&addr.rc_bdaddr, hs_address);
- debug("Accepted connection from %s", hs->address);
+ debug("Accepted connection from %s, %s", hs_address, hs->object_path);
- send_simple_signal("Connected");
+ hs_signal(hs, "Connected");
- g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb,
- hs);
+ g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs);
return TRUE;
}
-static gboolean audio_input_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
+static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
- int in, out, data_size, written;
+ struct headset *hs = (struct headset *)data;
char buf[1024];
+ gsize bytes_read;
+ gsize bytes_written, total_bytes_written;
+ GIOError err;
if (cond & G_IO_NVAL) {
- g_io_channel_unref(chan);
- hs->audio_input = NULL;
+ audio_headset_close_input(hs);
return FALSE;
}
if (cond & (G_IO_HUP | G_IO_ERR)) {
- g_io_channel_close(hs->audio_input);
- hs->audio_input = NULL;
- if (hs->out >= 0) {
- close(hs->out);
- hs->out = -1;
- }
+ audio_headset_close_input(hs);
return FALSE;
}
- in = g_io_channel_unix_get_fd(chan);
- out = g_io_channel_unix_get_fd(hs->sco);
-
- data_size = read(in, buf, sizeof(buf));
- if (data_size < 0) {
- error("read: %s (%d)", strerror(errno), errno);
- g_io_channel_close(chan);
- hs->audio_input = NULL;
- return TRUE;
- }
-
- /* EOF */
- if (data_size == 0) {
- debug("Reached end of file");
- g_io_channel_close(chan);
- hs->audio_input = NULL;
- return TRUE;
- }
-
- written = 0;
-
- while (written < data_size) {
- int ret;
-
- ret = write(out, &buf[written], data_size - written);
-
- if (ret < 0) {
- error("write(%d, %p, %d): %s (%d)", out, &buf[data_size],
- data_size - written, strerror(errno), errno);
- g_io_channel_close(chan);
- hs->audio_input = NULL;
- return TRUE;
- }
-
- debug("wrote %d bytes to %s", ret, audio_output);
-
- written += ret;
- }
+ err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read);
+ if (err != G_IO_ERROR_NONE)
+ return FALSE;
+
+ total_bytes_written = bytes_written = 0;
+ do {
+ /* FIXME: make it async */
+ err = g_io_channel_write(hs->sco, buf, bytes_read, &bytes_written);
+ total_bytes_written += bytes_written;
+ } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read);
return TRUE;
}
-static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
+static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
- int in, ret;
+ struct headset *hs = (struct headset *)data;
char buf[1024];
+ gsize bytes_read;
+ gsize bytes_written, total_bytes_written;
+ GIOError err;
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
- if (hs) {
- if (hs->audio_input) {
- g_io_channel_close(hs->audio_input);
- hs->audio_input = NULL;
- }
- if (hs->out >= 0) {
- close(hs->out);
- hs->out = -1;
- }
- }
-
return FALSE;
}
@@ -524,34 +504,36 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_dat
g_io_channel_close(hs->audio_input);
hs->audio_input = NULL;
}
- send_simple_signal("Stopped");
+ hs_signal(hs, "Stopped");
return FALSE;
}
- if (!audio_output) {
- debug("sco_io_cb: Unhandled IO condition");
- return TRUE;
- }
-
- in = g_io_channel_unix_get_fd(chan);
- if (hs->out < 0)
- hs->out = open(audio_output, O_WRONLY | O_SYNC | O_CREAT);
+ if (!hs->audio_output && hs->output)
+ audio_headset_open_output(hs, hs->output);
- if (hs->out < 0) {
- error("open(%s): %s (%d)", audio_output, strerror(errno), errno);
+ if (!hs->audio_output) {
+ error("no audio output");
g_io_channel_close(chan);
+ hs->sco = NULL;
return TRUE;
}
- ret = read(in, buf, sizeof(buf));
- if (ret > 0)
- ret = write(hs->out, buf, ret);
+ err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read);
+ if (err != G_IO_ERROR_NONE)
+ return FALSE;
+
+ total_bytes_written = bytes_written = 0;
+ do {
+ /* FIXME: make it async */
+ err = g_io_channel_write(hs->audio_output, buf, bytes_read, &bytes_written);
+ total_bytes_written += bytes_written;
+ } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read);
return TRUE;
}
static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
- struct pending_connect *c)
+ struct headset *hs)
{
int ret, sk, err, flags;
DBusMessage *reply;
@@ -579,52 +561,50 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
debug("SCO socket %d opened", sk);
- if (audio_output)
- flags = G_IO_IN;
- else
- flags = 0;
+ flags = hs->audio_output ? G_IO_IN : 0;
hs->sco = chan;
- g_io_add_watch(chan, flags, sco_io_cb, NULL);
+ g_io_add_watch(chan, flags, sco_input_to_audio_output_cb, NULL);
- reply = dbus_message_new_method_return(c->msg);
- if (reply) {
- dbus_connection_send(c->conn, reply, NULL);
- dbus_message_unref(reply);
- }
-
- if (audio_input) {
- int in;
-
- in = open(audio_input, O_RDONLY | O_NOCTTY);
-
- if (in < 0)
- error("open(%s): %s %d", audio_input, strerror(errno), errno);
- else {
- hs->audio_input = g_io_channel_unix_new(in);
- g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_cb, NULL);
+ if (hs->connect_in_progress->msg) {
+ reply = dbus_message_new_method_return(hs->connect_in_progress->msg);
+ if (reply) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
}
-
}
- pending_connect_free(c, FALSE);
+ if (hs->audio_input)
+ g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_to_sco_cb, hs);
- send_simple_signal("Playing");
+ pending_connect_free(hs->connect_in_progress, FALSE);
+ hs->connect_in_progress = NULL;
+
+ hs->state = HEADSET_STATE_PLAYING;
+ hs_signal(hs, "Playing");
return FALSE;
failed:
- err_connect_failed(c->conn, c->msg, err);
- pending_connect_free(c, TRUE);
+ if (hs->connect_in_progress) {
+ err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err);
+ pending_connect_free(hs->connect_in_progress, TRUE);
+ hs->connect_in_progress = NULL;
+ }
return FALSE;
}
-static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c)
+static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs)
{
+ char hs_address[18];
int sk, ret, err;
socklen_t len;
-
+
+ if (!hs->connect_in_progress) {
+ error("connect in progress is null in rfcomm_connect_cb!");
+ return FALSE;
+ }
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
return FALSE;
@@ -645,56 +625,52 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe
goto failed;
}
- hs = malloc(sizeof(struct headset));
- if (!hs) {
- err = ENOMEM;
- error("Allocating new hs connection struct failed!");
- goto failed;
- }
-
- memset(hs, 0, sizeof(struct headset));
-
- hs->out = -1;
-
- ba2str(&c->bda, hs->address);
+ ba2str(&hs->bda, hs_address);
hs->rfcomm = chan;
- send_simple_signal("Connected");
+ hs->state = HEADSET_STATE_CONNECTED;
+ hs_signal(hs, "Connected");
- debug("Connected to %s", hs->address);
+ debug("Connected to %s", hs_address);
g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs);
- if (c->msg) {
+ if (hs->connect_in_progress->msg) {
DBusMessage *reply;
- reply = dbus_message_new_method_return(c->msg);
+ reply = dbus_message_new_method_return(hs->connect_in_progress->msg);
if (reply) {
- dbus_connection_send(c->conn, reply, NULL);
+ dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
}
+ pending_connect_free(hs->connect_in_progress, FALSE);
+ hs->connect_in_progress = NULL;
}
- pending_connect_free(c, FALSE);
-
return FALSE;
failed:
- err_connect_failed(c->conn, c->msg, err);
- pending_connect_free(c, TRUE);
+ if (hs->connect_in_progress) {
+ err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err);
+ pending_connect_free(hs->connect_in_progress, TRUE);
+ hs->connect_in_progress = NULL;
+ }
+ hs->state = HEADSET_STATE_DISCONNECTED;
return FALSE;
}
-static int rfcomm_connect(struct pending_connect *c, int *err)
+static int rfcomm_connect(struct headset *hs, int *err)
{
struct sockaddr_rc addr;
char address[18];
int sk;
- ba2str(&c->bda, address);
+ assert(hs != NULL && hs->connect_in_progress != NULL);
+
+ ba2str(&hs->bda, address);
- debug("Connecting to %s channel %d", address, c->ch);
+ debug("Connecting to %s channel %d", address, hs->connect_in_progress->ch);
sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (sk < 0) {
@@ -721,11 +697,15 @@ static int rfcomm_connect(struct pending_connect *c, int *err)
memset(&addr, 0, sizeof(addr));
addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &c->bda);
- addr.rc_channel = c->ch;
+ bacpy(&addr.rc_bdaddr, &hs->bda);
+ addr.rc_channel = hs->connect_in_progress->ch;
- c->io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(c->io, TRUE);
+ hs->connect_in_progress->io = g_io_channel_unix_new(sk);
+ if (!hs->connect_in_progress->io) {
+ error("channel_unix_new failed in rfcomm connect");
+ goto failed;
+ }
+ g_io_channel_set_close_on_unref(hs->connect_in_progress->io, TRUE);
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
if (!(errno == EAGAIN || errno == EINPROGRESS)) {
@@ -737,25 +717,25 @@ static int rfcomm_connect(struct pending_connect *c, int *err)
debug("Connect in progress");
- g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, c);
+ g_io_add_watch(hs->connect_in_progress->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, hs);
} else {
debug("Connect succeeded with first try");
- rfcomm_connect_cb(c->io, G_IO_OUT, c);
+ rfcomm_connect_cb(hs->connect_in_progress->io, G_IO_OUT, hs);
}
return 0;
failed:
- if (sk >= 0)
- close(sk);
+ if (hs->connect_in_progress->io) {
+ g_io_channel_close(hs->connect_in_progress->io);
+ hs->connect_in_progress->io = NULL;
+ } else {
+ if (sk >= 0)
+ close(sk);
+ }
return -1;
}
-static void sig_term(int sig)
-{
- g_main_loop_quit(main_loop);
-}
-
static int server_socket(uint8_t *channel)
{
int sock;
@@ -936,98 +916,6 @@ static int remove_ag_record(uint32_t rec_id)
return 0;
}
-static void create_server_socket(void)
-{
- uint8_t chan = config_channel;
- int srv_sk;
-
- srv_sk = server_socket(&chan);
- if (srv_sk < 0) {
- error("Unable to create server socket");
- return;
- }
-
- if (!record_id)
- record_id = add_ag_record(chan);
-
- if (!record_id) {
- error("Unable to register service record");
- close(srv_sk);
- return;
- }
-
- server_sk = g_io_channel_unix_new(srv_sk);
- if (!server_sk) {
- error("Unable to allocate new GIOChannel");
- remove_ag_record(record_id);
- record_id = 0;
- return;
- }
-
- g_io_channel_set_close_on_unref(server_sk, TRUE);
-
- g_io_add_watch(server_sk, G_IO_IN, (GIOFunc) server_io_cb, NULL);
-
- g_io_channel_unref(server_sk);
-}
-
-static DBusHandlerResult hs_message(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- const char *interface, *member;
-
- 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.Headset") != 0)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- if (strcmp(member, "ConnectHeadset") == 0)
- return hs_connect(conn, msg, NULL);
-
- if (strcmp(member, "Disconnect") == 0)
- return hs_disconnect(conn, msg);
-
- if (strcmp(member, "IndicateCall") == 0)
- return hs_ring(conn, msg);
-
- if (strcmp(member, "CancelCall") == 0)
- return hs_cancel_ringing(conn, msg);
-
- if (strcmp(member, "Play") == 0)
- return hs_play(conn, msg);
-
- if (strcmp(member, "Stop") == 0)
- return hs_stop(conn, msg);
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static const DBusObjectPathVTable hs_table = {
- .message_function = hs_message,
-};
-
-int setup_dbus(DBusConnection *conn)
-{
- if (!dbus_connection_register_object_path(conn, hs_path,
- &hs_table, NULL)) {
- error("D-Bus failed to register %s path", hs_path);
- return -1;
- }
-
- if (config_channel)
- record_id = add_ag_record(config_channel);
-
- if (on_init_bda)
- hs_connect(NULL, NULL, on_init_bda);
-
- return 0;
-}
-
static void record_reply(DBusPendingCall *call, void *data)
{
DBusMessage *reply;
@@ -1036,14 +924,19 @@ static void record_reply(DBusPendingCall *call, void *data)
int array_len, record_len, err = EIO;
sdp_record_t *record = NULL;
sdp_list_t *protos;
- struct pending_connect *c = data;
+ struct headset *hs = data;
+ struct pending_connect *c;
+
+ assert (hs != NULL && hs->connect_in_progress);
+ c = hs->connect_in_progress;
reply = dbus_pending_call_steal_reply(call);
dbus_error_init(&derr);
if (dbus_set_error_from_message(&derr, reply)) {
error("GetRemoteServiceRecord failed: %s", derr.message);
- err_not_supported(c->conn, c->msg);
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
dbus_error_free(&derr);
goto failed;
}
@@ -1054,14 +947,16 @@ static void record_reply(DBusPendingCall *call, void *data)
if (!array) {
error("Unable to get handle array from reply");
- err_not_supported(c->conn, c->msg);
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
goto failed;
}
record = sdp_extract_pdu(array, &record_len);
if (!record) {
error("Unable to extract service record from reply");
- err_not_supported(c->conn, c->msg);
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
goto failed;
}
@@ -1077,13 +972,15 @@ static void record_reply(DBusPendingCall *call, void *data)
if (c->ch == -1) {
error("Unable to extract RFCOMM channel from service record");
- err_not_supported(c->conn, c->msg);
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
goto failed;
}
- if (rfcomm_connect(c, &err) < 0) {
+ if (rfcomm_connect(hs, &err) < 0) {
error("Unable to connect");
- err_connect_failed(c->conn, c->msg, err);
+ if (c->msg)
+ err_connect_failed(c->conn, c->msg, err);
goto failed;
}
@@ -1096,7 +993,8 @@ failed:
if (record)
sdp_record_free(record);
dbus_message_unref(reply);
- pending_connect_free(c, TRUE);
+ pending_connect_free(hs->connect_in_progress, TRUE);
+ hs->connect_in_progress = NULL;
}
static void handles_reply(DBusPendingCall *call, void *data)
@@ -1104,21 +1002,27 @@ static void handles_reply(DBusPendingCall *call, void *data)
DBusMessage *msg = NULL, *reply;
DBusPendingCall *pending;
DBusError derr;
- struct pending_connect *c = data;
+ struct headset *hs = data;
+ struct pending_connect *c;
char address[18], *addr_ptr = address;
dbus_uint32_t *array = NULL;
dbus_uint32_t handle;
int array_len;
+ assert (hs != NULL && hs->connect_in_progress);
+ c = hs->connect_in_progress;
+
reply = dbus_pending_call_steal_reply(call);
dbus_error_init(&derr);
if (dbus_set_error_from_message(&derr, reply)) {
error("GetRemoteServiceHandles failed: %s", derr.message);
- if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectFailed"))
- err_connect_failed(c->conn, c->msg, EHOSTDOWN);
- else
- err_not_supported(c->conn, c->msg);
+ if (c->msg) {
+ if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectFailed"))
+ err_connect_failed(c->conn, c->msg, EHOSTDOWN);
+ else
+ err_not_supported(c->conn, c->msg);
+ }
dbus_error_free(&derr);
goto failed;
}
@@ -1129,13 +1033,15 @@ static void handles_reply(DBusPendingCall *call, void *data)
if (!array) {
error("Unable to get handle array from reply");
- err_not_supported(c->conn, c->msg);
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
goto failed;
}
if (array_len < 1) {
debug("No record handles found");
- err_not_supported(c->conn, c->msg);
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
goto failed;
}
@@ -1147,11 +1053,12 @@ static void handles_reply(DBusPendingCall *call, void *data)
"GetRemoteServiceRecord");
if (!msg) {
error("Unable to allocate new method call");
- err_connect_failed(c->conn, c->msg, ENOMEM);
+ if (c->msg)
+ err_connect_failed(c->conn, c->msg, ENOMEM);
goto failed;
}
- ba2str(&c->bda, address);
+ ba2str(&hs->bda, address);
handle = array[0];
@@ -1161,7 +1068,8 @@ static void handles_reply(DBusPendingCall *call, void *data)
if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) {
error("Sending GetRemoteServiceRecord failed");
- err_connect_failed(c->conn, c->msg, EIO);
+ if (c->msg)
+ err_connect_failed(c->conn, c->msg, EIO);
goto failed;
}
@@ -1179,11 +1087,12 @@ failed:
pending_connect_free(c, TRUE);
}
-static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg)
+static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)
{
DBusError derr;
DBusMessage *reply;
const char *address;
+ char hs_address[18];
dbus_error_init(&derr);
@@ -1192,113 +1101,120 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg)
DBUS_TYPE_INVALID);
if (dbus_error_is_set(&derr)) {
- err_invalid_args(conn, msg, derr.message);
+ err_invalid_args(connection, msg, derr.message);
dbus_error_free(&derr);
return DBUS_HANDLER_RESULT_HANDLED;
}
- if (!hs || strcasecmp(address, hs->address) != 0)
- return err_not_connected(conn, msg);
+ if (!hs || strcasecmp(address, hs_address) != 0)
+ return err_not_connected(connection, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- if (hs->sco)
+ if (hs->sco) {
g_io_channel_close(hs->sco);
- if (hs->out >= 0) {
- close(hs->out);
- hs->out = -1;
+ hs->sco = NULL;
}
- if (hs->rfcomm)
- g_io_channel_close(hs->rfcomm);
- info("Disconnected from %s", hs->address);
+ audio_headset_close_output(hs);
+
+ if (hs->rfcomm) {
+ g_io_channel_close(hs->rfcomm);
+ hs->rfcomm = NULL;
+ }
- send_simple_signal("Disconnected");
+ info("Disconnected from %s", hs_address);
- free(hs);
- hs = NULL;
+ hs_signal(hs, "Disconnected");
- dbus_connection_send(conn, reply, NULL);
+ dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
- const char *address)
+static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
{
DBusPendingCall *pending;
- struct pending_connect *c;
const char *hs_svc = "hsp";
+ char hs_address[18];
- if (!address) {
- DBusError derr;
+ assert (hs != NULL);
- dbus_error_init(&derr);
+ /* this block should be move to the manager Connect */
+/* if (!address && msg && conn) { */
+/* DBusError derr; */
- dbus_message_get_args(msg, &derr,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID);
+/* dbus_error_init(&derr); */
- if (dbus_error_is_set(&derr)) {
- err_invalid_args(conn, msg, derr.message);
- dbus_error_free(&derr);
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- }
+/* dbus_message_get_args(msg, &derr, */
+/* DBUS_TYPE_STRING, &address, */
+/* DBUS_TYPE_INVALID); */
+
+/* if (dbus_error_is_set(&derr)) { */
+/* err_invalid_args(conn, msg, derr.message); */
+/* dbus_error_free(&derr); */
+/* return DBUS_HANDLER_RESULT_HANDLED; */
+/* } */
+/* } */
- if (hs)
- return err_already_connected(conn, msg);
+ if (hs->state > HEADSET_STATE_DISCONNECTED || hs->connect_in_progress) {
+ error("Already connected");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
- c = malloc(sizeof(struct pending_connect));
- if (!c) {
+ hs->connect_in_progress = malloc(sizeof(struct pending_connect));
+ if (!hs->connect_in_progress) {
error("Out of memory when allocating new struct pending_connect");
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
- connect_in_progress = c;
-
- memset(c, 0, sizeof(struct pending_connect));
-
- str2ba(address, &c->bda);
-
- c->conn = dbus_connection_ref(conn);
- c->msg = dbus_message_ref(msg);
+ memset(hs->connect_in_progress, 0, sizeof(struct pending_connect));
+ hs->connect_in_progress->conn = dbus_connection_ref(connection);
+ hs->connect_in_progress->msg = msg ? dbus_message_ref(msg) : NULL;
msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0",
"org.bluez.Adapter",
"GetRemoteServiceHandles");
if (!msg) {
- pending_connect_free(c, TRUE);
+ pending_connect_free(hs->connect_in_progress, TRUE);
+ hs->connect_in_progress = NULL;
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
- dbus_message_append_args(msg, DBUS_TYPE_STRING, &address,
+ ba2str(&hs->bda, hs_address);
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_address,
DBUS_TYPE_STRING, &hs_svc,
DBUS_TYPE_INVALID);
- if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) {
+ if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) {
error("Sending GetRemoteServiceHandles failed");
- pending_connect_free(c, TRUE);
+ pending_connect_free(hs->connect_in_progress, TRUE);
+ hs->connect_in_progress = NULL;
dbus_message_unref(msg);
return err_connect_failed(connection, msg, EIO);
}
- dbus_pending_call_set_notify(pending, handles_reply, c, NULL);
+ dbus_pending_call_set_notify(pending, handles_reply, hs->connect_in_progress, NULL);
dbus_message_unref(msg);
return DBUS_HANDLER_RESULT_HANDLED;;
}
-static int send_ring(GIOChannel *io)
+int audio_headset_send_ring(struct headset *hs)
{
const char *ring_str = "\r\nRING\r\n";
int sk, written, len;
+ assert (hs != NULL);
+ if (!hs->rfcomm) {
+ error("the headset %s is not connected", hs->object_path);
+ }
+
sk = g_io_channel_unix_get_fd(hs->rfcomm);
len = strlen(ring_str);
@@ -1318,20 +1234,26 @@ static int send_ring(GIOChannel *io)
return 0;
}
-static gboolean ring_timer(gpointer user_data)
+static gboolean ring_timer(gpointer data)
{
- if (send_ring(hs->rfcomm) < 0)
+ struct headset *hs = data;
+
+ assert(hs != NULL);
+
+ if (audio_headset_send_ring(hs) < 0)
error("Sending RING failed");
return TRUE;
}
-static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg)
+static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)
{
DBusMessage *reply;
- if (!hs)
- return err_not_connected(conn, msg);
+ assert(hs != NULL);
+
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return err_not_connected(connection, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -1342,26 +1264,26 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg)
goto done;
}
- if (send_ring(hs->rfcomm) < 0) {
+ if (audio_headset_send_ring(hs) < 0) {
dbus_message_unref(reply);
- return err_failed(conn, msg);
+ return err_failed(connection, msg);
}
- hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL);
+ hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, hs);
done:
- dbus_connection_send(conn, reply, NULL);
+ dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg)
+static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)
{
DBusMessage *reply;
if (!hs)
- return err_not_connected(conn, msg);
+ return err_not_connected(connection, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -1376,38 +1298,43 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *ms
hs->ring_timer = 0;
done:
- dbus_connection_send(conn, reply, NULL);
+ dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg)
+static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
{
struct sockaddr_sco addr;
struct pending_connect *c;
+ char hs_address[18];
int sk, err;
if (!hs)
- return err_not_connected(conn, msg);
+ return err_not_connected(connection, msg);
+
+ if (hs->state <= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress)
+ return err_already_connected(connection, msg); /* FIXME: in progress error? */
if (hs->sco)
- return err_already_connected(conn, msg);
+ return err_already_connected(connection, msg);
- c = malloc(sizeof(struct pending_connect));
- if (!c)
+ hs->connect_in_progress = malloc(sizeof(struct pending_connect));
+ if (!hs->connect_in_progress)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- memset(c, 0, sizeof(struct pending_connect));
+ memset(hs->connect_in_progress, 0, sizeof(struct pending_connect));
- c->conn = dbus_connection_ref(conn);
+ c = hs->connect_in_progress;
+ c->conn = dbus_connection_ref(connection);
c->msg = dbus_message_ref(msg);
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(conn, msg, err);
+ err_connect_failed(connection, msg, err);
goto failed;
}
@@ -1426,18 +1353,18 @@ static DBusHandlerResult hs_play(DBusConnection *conn, 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(conn, msg, err);
+ err_connect_failed(connection, msg, err);
goto failed;
}
if (set_nonblocking(sk, &err) < 0) {
- err_connect_failed(conn, msg, err);
+ err_connect_failed(connection, msg, err);
goto failed;
}
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
- str2ba(hs->address, &addr.sco_bdaddr);
+ str2ba(hs_address, &addr.sco_bdaddr);
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
if (!(errno == EAGAIN || errno == EINPROGRESS)) {
@@ -1451,7 +1378,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg)
g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c);
} else {
debug("Connect succeeded with first try");
- sco_connect_cb(c->io, G_IO_OUT, c);
+ sco_connect_cb(c->io, G_IO_OUT, hs);
}
return 0;
@@ -1463,12 +1390,12 @@ failed:
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg)
+static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)
{
DBusMessage *reply;
if (!hs || !hs->sco)
- return err_not_connected(conn, msg);
+ return err_not_connected(connection, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -1477,49 +1404,358 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg)
g_io_channel_close(hs->sco);
hs->sco = NULL;
- if (hs->out >= 0) {
- close(hs->out);
- hs->out = -1;
+ hs_signal(hs, "Stopped");
+ hs->state = HEADSET_STATE_CONNECTED;
+
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+void audio_headset_create_server_socket(struct headset *hs, uint8_t chan)
+{
+ int srv_sk;
+
+ assert (hs != NULL);
+
+ if (hs->server_sk) {
+ error("Server socket already created");
+ return;
+ }
+
+ srv_sk = server_socket(&chan);
+ if (srv_sk < 0) {
+ error("Unable to create server socket");
+ return;
+ }
+
+ if (!hs->record_id)
+ hs->record_id = add_ag_record(chan);
+
+ if (!hs->record_id) {
+ error("Unable to register service record");
+ close(srv_sk);
+ return;
+ }
+
+ hs->server_sk = g_io_channel_unix_new(srv_sk);
+ if (!hs->server_sk) {
+ error("Unable to allocate new GIOChannel");
+ remove_ag_record(hs->record_id);
+ hs->record_id = 0;
+ close(srv_sk);
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(hs->server_sk, TRUE);
+
+ g_io_add_watch(hs->server_sk, G_IO_IN, (GIOFunc) server_io_cb, hs);
+
+ g_io_channel_unref(hs->server_sk);
+}
+
+static DBusHandlerResult hs_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct headset *hs = data;
+ const char *interface, *member;
+
+ assert (hs != NULL);
+
+ 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.Headset") != 0)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (strcmp(member, "Connect") == 0)
+ return hs_connect(hs, msg);
+
+ if (strcmp(member, "Disconnect") == 0)
+ return hs_disconnect(hs, msg);
+
+ if (strcmp(member, "IndicateCall") == 0)
+ return hs_ring(hs, msg);
+
+ if (strcmp(member, "CancelCall") == 0)
+ return hs_cancel_ringing(hs, msg);
+
+ if (strcmp(member, "Play") == 0)
+ return hs_play(hs, msg);
+
+ if (strcmp(member, "Stop") == 0)
+ return hs_stop(hs, msg);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable hs_table = {
+ .message_function = hs_message,
+};
+
+struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel)
+{
+ static int headset_uid = 0;
+ struct headset *hs;
+
+ hs = malloc(sizeof(struct headset));
+ if (!hs) {
+ error("Allocating new hs connection struct failed!");
+ return NULL;
+ }
+
+ memset(hs, 0, sizeof(struct headset));
+
+ snprintf(hs->object_path, sizeof(hs->object_path), AUDIO_HEADSET_PATH_BASE "%d", headset_uid++);
+
+ if (!dbus_connection_register_object_path(conn, hs->object_path,
+ &hs_table, hs)) {
+ error("D-Bus failed to register %s path", hs->object_path);
+ return NULL;
+ }
+
+ if (channel)
+ hs->record_id = add_ag_record(channel);
+ if (bda) {
+ str2ba(bda, &hs->bda);
+ hs_connect(hs, NULL);
+ }
+
+ return hs;
+}
+
+int audio_headset_close_output(struct headset* hs)
+{
+ assert (hs != NULL);
+
+ if (hs->audio_output == NULL)
+ return FALSE;
+
+ g_io_channel_close(hs->audio_output);
+ hs->audio_output = NULL;
+ return FALSE;
+}
+
+int audio_headset_open_output(struct headset* hs, const char *output)
+{
+ int out;
+
+ assert (hs != NULL && output != NULL);
+
+ audio_headset_close_output(hs);
+ if (output && hs->output) {
+ free(hs->output);
+ hs->output = strdup(output);
}
- send_simple_signal("Stopped");
+ assert (hs->output);
- dbus_connection_send(conn, reply, NULL);
+ out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT);
+
+ if (out < 0) {
+ error("open(%s): %s %d", hs->output, strerror(errno), errno);
+ return TRUE;
+ }
+
+ hs->audio_output = g_io_channel_unix_new(out);
+ if (!hs->audio_output) {
+ error("Allocating new channel for audio output!");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int audio_headset_close_input(struct headset* hs)
+{
+ assert (hs != NULL);
+
+ if (hs->audio_input == NULL)
+ return FALSE;
+
+ g_io_channel_close(hs->audio_input);
+ hs->audio_input = NULL;
+
+ return FALSE;
+}
+
+int audio_headset_open_input(struct headset* hs, const char *input)
+{
+ int in;
+
+ assert (hs != NULL);
+
+ audio_headset_close_input(hs);
+
+ /* we keep the input name, and NULL can be use to reopen */
+ if (input && hs->input) {
+ free(hs->input);
+ hs->input = strdup(input);
+ }
+
+ assert (hs->input);
+
+ in = open(hs->input, O_RDONLY | O_NOCTTY);
+
+ if (in < 0) {
+ error("open(%s): %s %d", hs->input, strerror(errno), errno);
+ return TRUE;
+ }
+
+ hs->audio_input = g_io_channel_unix_new(in);
+ if (!hs->audio_input) {
+ error("Allocating new channel for audio input!");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void audio_manager_add_headset(struct manager *amanager, struct headset *hs)
+{
+ assert(amanager && hs);
+
+ if (g_slist_find(amanager->headset_list, hs))
+ return;
+
+ amanager->headset_list = g_slist_append(amanager->headset_list, hs);
+}
+
+static DBusHandlerResult am_connect(struct manager *amanager,
+ DBusMessage *msg)
+{
+ char object_path[128];
+ DBusMessage *reply;
+
+ if (!amanager)
+ 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", 0);
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &object_path,
+ DBUS_TYPE_INVALID);
+ dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
+static DBusHandlerResult am_default_headset(struct manager *amanager,
+ DBusMessage *msg)
+{
+ DBusMessage *reply;
+
+ if (!amanager)
+ return err_not_connected(connection, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult am_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *interface, *member;
+ struct manager *amanager = (struct 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.Headset") != 0)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (strcmp(member, "Connect") == 0)
+ return am_connect(amanager, msg);
+
+ if (strcmp(member, "DefaultHeadset") == 0)
+ return am_default_headset(amanager, 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* amanager;
+
+ amanager = malloc(sizeof(struct manager));
+ if (!amanager) {
+ error("Allocating new hs connection struct failed!");
+ return NULL;
+ }
+
+ memset(amanager, 0, sizeof(struct manager));
+
+ if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH,
+ &am_table, amanager)) {
+ error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH);
+ return NULL;
+ }
+
+ return amanager;
+}
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(main_loop);
+}
+
int main(int argc, char *argv[])
{
- DBusConnection *system_bus;
+ uint8_t opt_channel = 12;
+ char *opt_bda = NULL;
+ char *opt_input = NULL;
+ char *opt_output = NULL;
+ struct headset *hs;
+ struct manager *manager;
struct sigaction sa;
int opt;
while ((opt = getopt(argc, argv, "c:o:i:")) != EOF) {
switch (opt) {
case 'c':
- config_channel = strtol(optarg, NULL, 0);
+ opt_channel = strtol(optarg, NULL, 0);
break;
case 'i':
- audio_input = optarg;
+ opt_input = optarg;
break;
case 'o':
- audio_output = optarg;
+ opt_output = optarg;
break;
default:
- printf("Usage: %s -c local_channel [-n] [-o output] [-i input] [bdaddr]\n", argv[0]);
+ printf("Usage: %s -c local_channel [-o output] [-i input] [bdaddr]\n", argv[0]);
exit(1);
}
}
- if (argv[optind])
- on_init_bda = argv[optind];
+ if (optind < argc && argv[optind])
+ opt_bda = argv[optind];
- start_logging("headset", "Bluetooth Headset daemon");
+ start_logging("headset", "Bluetooth headset service daemon");
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
@@ -1535,27 +1771,40 @@ int main(int argc, char *argv[])
main_loop = g_main_loop_new(NULL, FALSE);
- system_bus = init_dbus(NULL, NULL, NULL);
- if (!system_bus) {
+ connection = init_dbus(NULL, NULL, NULL);
+ if (!connection) {
error("Connection to system bus failed");
g_main_loop_unref(main_loop);
exit(1);
}
- connection = system_bus;
-
- if (setup_dbus(system_bus) < 0) {
- error("Connection setup failed");
- dbus_connection_unref(system_bus);
+ manager = audio_manager_new(connection);
+ if (!manager) {
+ error("Failed to create an audio manager");
+ dbus_connection_unref(connection);
g_main_loop_unref(main_loop);
exit(1);
}
- create_server_socket();
+ if (opt_bda) {
+ hs = audio_headset_new(connection, opt_bda, opt_channel);
+ if (!hs) {
+ error("Connection setup failed");
+ dbus_connection_unref(connection);
+ g_main_loop_unref(main_loop);
+ exit(1);
+ }
+ if (opt_output)
+ audio_headset_open_output(hs, opt_output);
+ if (opt_input)
+ audio_headset_open_input(hs, opt_input);
+ audio_headset_create_server_socket(hs, opt_channel);
+ audio_manager_add_headset(manager, hs);
+ }
g_main_loop_run(main_loop);
- dbus_connection_unref(system_bus);
+ dbus_connection_unref(connection);
g_main_loop_unref(main_loop);