summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2007-01-24 16:28:22 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2007-01-24 16:28:22 +0000
commit76b5515202080d8a862e450092c70aaf299dbab2 (patch)
tree6cc8a08be6f0d7fee4df42d3e622e98b4da5512d
parentb992c01c50b195751dbe288061d24b4126550a1c (diff)
More headset.c rework
-rw-r--r--audio/headset.c721
1 files changed, 432 insertions, 289 deletions
diff --git a/audio/headset.c b/audio/headset.c
index 78d13092..078f9332 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -67,11 +67,12 @@ struct pending_connect {
};
typedef enum {
- HEADSET_STATE_DISCONNECTED,
- HEADSET_STATE_CONNECT_IN_PROGRESS,
- HEADSET_STATE_CONNECTED,
- HEADSET_STATE_PLAY_IN_PROGRESS,
- HEADSET_STATE_PLAYING,
+ HEADSET_STATE_UNAUTHORIZED,
+ HEADSET_STATE_DISCONNECTED,
+ HEADSET_STATE_CONNECT_IN_PROGRESS,
+ HEADSET_STATE_CONNECTED,
+ HEADSET_STATE_PLAY_IN_PROGRESS,
+ HEADSET_STATE_PLAYING,
} headset_state_t;
struct headset {
@@ -94,11 +95,11 @@ struct headset {
headset_state_t state;
struct pending_connect *connect_in_progress;
- uint32_t record_id;
- GIOChannel *server_sk;
};
struct manager {
+ GIOChannel *server_sk;
+ uint32_t record_id;
GSList *headset_list;
};
@@ -107,17 +108,20 @@ static DBusConnection *connection = NULL;
static GMainLoop *main_loop = NULL;
struct manager* audio_manager_new(DBusConnection *conn);
+void audio_manager_free(struct manager* amanager);
+struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda);
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);
+void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan);
+static DBusHandlerResult am_get_default_headset(struct manager *amanager, DBusMessage *msg);
+static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *msg);
-struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel);
+struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda);
+void audio_headset_unref(struct headset* hs);
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);
+GIOError 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);
@@ -156,9 +160,9 @@ static int set_nonblocking(int fd, int *err)
return 0;
}
-static void pending_connect_free(struct pending_connect *c, gboolean unref_io)
+static void pending_connect_free(struct pending_connect *c)
{
- if (unref_io && c->io)
+ if (c->io)
g_io_channel_unref(c->io);
if (c->msg)
dbus_message_unref(c->msg);
@@ -172,7 +176,7 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg,
{
DBusMessage *derr;
- if (!conn)
+ if (!conn || !msg)
return DBUS_HANDLER_RESULT_HANDLED;
derr = dbus_message_new_error(msg, name, descr);
@@ -299,10 +303,12 @@ static int parse_headset_event(const char *buf, char *rsp, int rsp_len)
static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
- struct headset *hs = (struct headset *)data;
- int sk, ret, free_space;
+ struct headset *hs = data;
unsigned char buf[BUF_SIZE];
char *cr;
+ gsize bytes_read = 0;
+ gsize free_space;
+ GIOError err;
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
@@ -312,23 +318,21 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
if (cond & (G_IO_ERR | G_IO_HUP))
goto failed;
- sk = g_io_channel_unix_get_fd(chan);
-
- ret = read(sk, buf, sizeof(buf) - 1);
- if (ret <= 0)
+ err = g_io_channel_read(chan, (gchar *)buf, sizeof(buf) - 1, &bytes_read);
+ if (err != G_IO_ERROR_NONE)
goto failed;
free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1;
- if (free_space < ret) {
+ if (free_space < bytes_read) {
/* Very likely that the HS is sending us garbage so
* just ignore the data and disconnect */
error("Too much data to fit incomming buffer");
goto failed;
}
- memcpy(&hs->buf[hs->data_start], buf, ret);
- hs->data_length += ret;
+ memcpy(&hs->buf[hs->data_start], buf, bytes_read);
+ hs->data_length += bytes_read;
/* Make sure the data is null terminated so we can use string
* functions */
@@ -337,7 +341,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
cr = strchr(&hs->buf[hs->data_start], '\r');
if (cr) {
char rsp[BUF_SIZE];
- int len, written;
+ gsize count, bytes_written, total_bytes_written;
off_t cmd_len;
cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start];
@@ -345,25 +349,24 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
memset(rsp, 0, sizeof(rsp));
+ /* FIXME: make a better parse function */
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;
+ count = strlen(rsp);
+ total_bytes_written = bytes_written = 0;
+ err = G_IO_ERROR_NONE;
- while (written < len) {
- int ret;
-
- ret = write(sk, &rsp[written], len - written);
- if (ret < 0) {
- error("write: %s (%d)", strerror(errno), errno);
- break;
- }
-
- written += ret;
- }
+ while (err == G_IO_ERROR_NONE && total_bytes_written < count) {
+ /* FIXME: make it async */
+ err = g_io_channel_write(hs->rfcomm, rsp + total_bytes_written,
+ count - total_bytes_written, &bytes_written);
+ if (err != G_IO_ERROR_NONE)
+ error("Error while writting to the audio output channel");
+ total_bytes_written += bytes_written;
+ };
hs->data_start += cmd_len;
hs->data_length -= cmd_len;
@@ -380,18 +383,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
return TRUE;
failed:
- info("Disconnected from %s", hs->object_path);
- hs_signal(hs, "Disconnected");
- if (hs->sco) {
- g_io_channel_close(hs->sco);
- hs->sco = NULL;
- }
- if (hs->audio_output) {
- g_io_channel_close(hs->audio_output);
- hs->audio_output = NULL;
- }
- g_io_channel_close(chan);
- hs->rfcomm = NULL;
+ hs_disconnect(hs, NULL);
return FALSE;
}
@@ -402,9 +394,10 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
struct sockaddr_rc addr;
socklen_t size;
char hs_address[18];
- struct headset *hs = (struct headset *)data;
+ struct headset *hs = NULL;
+ struct manager *amanager = (struct manager *) data;
- assert(hs != NULL);
+ assert(amanager != NULL);
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
@@ -414,7 +407,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);
- hs->server_sk = NULL;
+ amanager->server_sk = NULL;
return TRUE;
}
@@ -427,7 +420,20 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
return TRUE;
}
- if (hs->state > HEADSET_STATE_DISCONNECTED) {
+ if ((hs = audio_manager_find_headset_by_bda(amanager, &addr.rc_bdaddr)) != NULL) {
+ debug("Audio manager server accept() find a matching headset in its list");
+ if (!hs->object_path) {
+ debug("But something is wrong with the headset object path");
+ return TRUE;
+ }
+ debug("Incoming connection on the server_sk for object %s", hs->object_path);
+ } else {
+ /* FIXME: make authorization dbus calls *here or in headset code? */
+ hs = audio_headset_new(connection, &addr.rc_bdaddr);
+ /* audio_headset_authorize(hs); */
+ }
+
+ if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) {
debug("Refusing new connection since one already exists");
close(cli_sk);
return TRUE;
@@ -440,30 +446,36 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
return TRUE;
}
+ g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs);
+
ba2str(&addr.rc_bdaddr, hs_address);
debug("Accepted connection from %s, %s", hs_address, hs->object_path);
hs_signal(hs, "Connected");
- g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs);
-
return TRUE;
}
static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
- struct headset *hs = (struct headset *)data;
+ struct headset *hs = data;
char buf[1024];
gsize bytes_read;
gsize bytes_written, total_bytes_written;
GIOError err;
- if (cond & G_IO_NVAL) {
+ if (!hs || !hs->sco) {
+ error("The headset is invalid or does not have a SCO connection up");
audio_headset_close_input(hs);
return FALSE;
}
+ if (cond & G_IO_NVAL) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
if (cond & (G_IO_HUP | G_IO_ERR)) {
audio_headset_close_input(hs);
return FALSE;
@@ -474,18 +486,23 @@ static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpoin
return FALSE;
total_bytes_written = bytes_written = 0;
- do {
+ err = G_IO_ERROR_NONE;
+
+ while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read) {
/* FIXME: make it async */
- err = g_io_channel_write(hs->sco, buf, bytes_read, &bytes_written);
+ err = g_io_channel_write(hs->sco, buf + total_bytes_written,
+ bytes_read - total_bytes_written, &bytes_written);
+ if (err != G_IO_ERROR_NONE)
+ error("Error while writting to the audio output channel");
total_bytes_written += bytes_written;
- } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read);
+ };
return TRUE;
}
static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
- struct headset *hs = (struct headset *)data;
+ struct headset *hs = data;
char buf[1024];
gsize bytes_read;
gsize bytes_written, total_bytes_written;
@@ -500,10 +517,12 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond
error("Audio connection got disconnected");
g_io_channel_close(chan);
hs->sco = NULL;
- if (hs->audio_input) {
- g_io_channel_close(hs->audio_input);
- hs->audio_input = NULL;
+ if (hs->audio_output) {
+ g_io_channel_close(hs->audio_output);
+ hs->audio_output = NULL;
}
+ assert(hs->rfcomm);
+ hs->state = HEADSET_STATE_CONNECTED;
hs_signal(hs, "Stopped");
return FALSE;
}
@@ -511,23 +530,28 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond
if (!hs->audio_output && hs->output)
audio_headset_open_output(hs, hs->output);
+ err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read);
+
+ if (err != G_IO_ERROR_NONE)
+ return FALSE;
+
if (!hs->audio_output) {
error("no audio output");
- g_io_channel_close(chan);
- hs->sco = NULL;
return TRUE;
}
- 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 {
+ err = G_IO_ERROR_NONE;
+
+ while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read) {
/* FIXME: make it async */
- err = g_io_channel_write(hs->audio_output, buf, bytes_read, &bytes_written);
+ err = g_io_channel_write(hs->audio_output, buf + total_bytes_written,
+ bytes_read - total_bytes_written, &bytes_written);
+ if (err != G_IO_ERROR_NONE) {
+ error("Error while writting to the audio output channel");
+ }
total_bytes_written += bytes_written;
- } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read);
+ };
return TRUE;
}
@@ -539,6 +563,9 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
DBusMessage *reply;
socklen_t len;
+ assert(hs != NULL && hs->connect_in_progress != NULL &&
+ hs->sco == NULL && hs->state == HEADSET_STATE_PLAY_IN_PROGRESS);
+
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
return FALSE;
@@ -559,12 +586,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
goto failed;
}
- debug("SCO socket %d opened", sk);
-
- flags = hs->audio_output ? G_IO_IN : 0;
+ debug("SCO socket opened for headset %s", hs->object_path);
hs->sco = chan;
- g_io_add_watch(chan, flags, sco_input_to_audio_output_cb, NULL);
+ hs->connect_in_progress->io = NULL;
+
+ flags = hs->audio_output ? G_IO_IN : 0;
+ g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs);
if (hs->connect_in_progress->msg) {
reply = dbus_message_new_method_return(hs->connect_in_progress->msg);
@@ -574,10 +602,11 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
}
}
+ /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */
if (hs->audio_input)
g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_to_sco_cb, hs);
- pending_connect_free(hs->connect_in_progress, FALSE);
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
hs->state = HEADSET_STATE_PLAYING;
@@ -588,10 +617,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
failed:
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);
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
}
+ assert(hs->rfcomm);
+ hs->state = HEADSET_STATE_CONNECTED;
+
return FALSE;
}
@@ -601,10 +633,10 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he
int sk, ret, err;
socklen_t len;
- if (!hs->connect_in_progress) {
- error("connect in progress is null in rfcomm_connect_cb!");
- return FALSE;
- }
+ assert(hs != NULL && hs->connect_in_progress != NULL &&
+ hs->rfcomm == NULL &&
+ hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS);
+
if (cond & G_IO_NVAL) {
g_io_channel_unref(chan);
return FALSE;
@@ -643,7 +675,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
}
- pending_connect_free(hs->connect_in_progress, FALSE);
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
}
@@ -652,7 +684,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he
failed:
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);
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
}
hs->state = HEADSET_STATE_DISCONNECTED;
@@ -666,7 +698,8 @@ static int rfcomm_connect(struct headset *hs, int *err)
char address[18];
int sk;
- assert(hs != NULL && hs->connect_in_progress != NULL);
+ assert(hs != NULL && hs->connect_in_progress != NULL &&
+ hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS);
ba2str(&hs->bda, address);
@@ -916,7 +949,7 @@ static int remove_ag_record(uint32_t rec_id)
return 0;
}
-static void record_reply(DBusPendingCall *call, void *data)
+static void get_record_reply(DBusPendingCall *call, void *data)
{
DBusMessage *reply;
DBusError derr;
@@ -927,7 +960,7 @@ static void record_reply(DBusPendingCall *call, void *data)
struct headset *hs = data;
struct pending_connect *c;
- assert (hs != NULL && hs->connect_in_progress);
+ assert(hs != NULL && hs->connect_in_progress && !hs->rfcomm);
c = hs->connect_in_progress;
reply = dbus_pending_call_steal_reply(call);
@@ -941,9 +974,14 @@ static void record_reply(DBusPendingCall *call, void *data)
goto failed;
}
- dbus_message_get_args(reply, NULL,
+ if (!dbus_message_get_args(reply, NULL,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len,
- DBUS_TYPE_INVALID);
+ DBUS_TYPE_INVALID)) {
+ error("Unable to get args from GetRecordReply");
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
+ goto failed;
+ }
if (!array) {
error("Unable to get handle array from reply");
@@ -968,6 +1006,7 @@ static void record_reply(DBusPendingCall *call, void *data)
c->ch = sdp_get_proto_port(protos, RFCOMM_UUID);
sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, NULL);
sdp_list_free(protos, NULL);
+ protos = NULL;
}
if (c->ch == -1) {
@@ -992,12 +1031,52 @@ static void record_reply(DBusPendingCall *call, void *data)
failed:
if (record)
sdp_record_free(record);
- dbus_message_unref(reply);
- pending_connect_free(hs->connect_in_progress, TRUE);
+ if (reply)
+ dbus_message_unref(reply);
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
+ hs->state = HEADSET_STATE_DISCONNECTED;
}
-static void handles_reply(DBusPendingCall *call, void *data)
+static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)
+{
+ DBusMessage *reply = NULL;
+ char hs_address[18];
+
+ if (msg) {
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ if (hs->state > HEADSET_STATE_CONNECTED)
+ hs_stop(hs, NULL);
+
+ if (hs->rfcomm) {
+ g_io_channel_close(hs->rfcomm);
+ hs->rfcomm = NULL;
+ hs->state = HEADSET_STATE_DISCONNECTED;
+ }
+
+ if (hs->connect_in_progress) {
+ pending_connect_free(hs->connect_in_progress);
+ hs->connect_in_progress = NULL;
+ hs->state = HEADSET_STATE_DISCONNECTED;
+ }
+
+ info("Disconnected from %s, %s", hs_address, hs->object_path);
+
+ hs_signal(hs, "Disconnected");
+
+ if (reply) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void get_handles_reply(DBusPendingCall *call, void *data)
{
DBusMessage *msg = NULL, *reply;
DBusPendingCall *pending;
@@ -1009,7 +1088,7 @@ static void handles_reply(DBusPendingCall *call, void *data)
dbus_uint32_t handle;
int array_len;
- assert (hs != NULL && hs->connect_in_progress);
+ assert(hs != NULL && hs->connect_in_progress);
c = hs->connect_in_progress;
reply = dbus_pending_call_steal_reply(call);
@@ -1027,9 +1106,15 @@ static void handles_reply(DBusPendingCall *call, void *data)
goto failed;
}
- dbus_message_get_args(reply, NULL,
+ if (!dbus_message_get_args(reply, NULL,
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len,
- DBUS_TYPE_INVALID);
+ DBUS_TYPE_INVALID)) {
+
+ error("Unable to get args from reply");
+ if (c->msg)
+ err_not_supported(c->conn, c->msg);
+ goto failed;
+ }
if (!array) {
error("Unable to get handle array from reply");
@@ -1073,7 +1158,7 @@ static void handles_reply(DBusPendingCall *call, void *data)
goto failed;
}
- dbus_pending_call_set_notify(pending, record_reply, c, NULL);
+ dbus_pending_call_set_notify(pending, get_record_reply, hs, NULL);
dbus_message_unref(msg);
dbus_message_unref(reply);
@@ -1084,56 +1169,7 @@ failed:
if (msg)
dbus_message_unref(msg);
dbus_message_unref(reply);
- pending_connect_free(c, TRUE);
-}
-
-static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)
-{
- DBusError derr;
- DBusMessage *reply;
- const char *address;
- char hs_address[18];
-
- dbus_error_init(&derr);
-
- dbus_message_get_args(msg, &derr,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID);
-
- if (dbus_error_is_set(&derr)) {
- 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(connection, msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- if (hs->sco) {
- g_io_channel_close(hs->sco);
- hs->sco = NULL;
- }
-
- audio_headset_close_output(hs);
-
- if (hs->rfcomm) {
- g_io_channel_close(hs->rfcomm);
- hs->rfcomm = NULL;
- }
-
- info("Disconnected from %s", hs_address);
-
- hs_signal(hs, "Disconnected");
-
- dbus_connection_send(connection, reply, NULL);
-
- dbus_message_unref(reply);
-
- return DBUS_HANDLER_RESULT_HANDLED;
+ hs_disconnect(hs, NULL);
}
static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
@@ -1142,24 +1178,11 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
const char *hs_svc = "hsp";
char hs_address[18];
- assert (hs != NULL);
-
- /* this block should be move to the manager Connect */
-/* if (!address && msg && conn) { */
-/* DBusError derr; */
-
-/* dbus_error_init(&derr); */
-
-/* dbus_message_get_args(msg, &derr, */
-/* DBUS_TYPE_STRING, &address, */
-/* DBUS_TYPE_INVALID); */
+ assert(hs != NULL);
-/* if (dbus_error_is_set(&derr)) { */
-/* err_invalid_args(conn, msg, derr.message); */
-/* dbus_error_free(&derr); */
-/* return DBUS_HANDLER_RESULT_HANDLED; */
-/* } */
-/* } */
+ if (hs->state == HEADSET_STATE_UNAUTHORIZED) {
+ error("This headset has not been audiothorized");
+ }
if (hs->state > HEADSET_STATE_DISCONNECTED || hs->connect_in_progress) {
error("Already connected");
@@ -1170,7 +1193,8 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
if (!hs->connect_in_progress) {
error("Out of memory when allocating new struct pending_connect");
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- }
+ }
+ hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS;
memset(hs->connect_in_progress, 0, sizeof(struct pending_connect));
hs->connect_in_progress->conn = dbus_connection_ref(connection);
@@ -1180,8 +1204,10 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)
"org.bluez.Adapter",
"GetRemoteServiceHandles");
if (!msg) {
- pending_connect_free(hs->connect_in_progress, TRUE);
+ error("Could not create a new dbus message");
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
+ hs->state = HEADSET_STATE_DISCONNECTED;
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
@@ -1190,51 +1216,48 @@ 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)) {
error("Sending GetRemoteServiceHandles failed");
- pending_connect_free(hs->connect_in_progress, TRUE);
+ pending_connect_free(hs->connect_in_progress);
hs->connect_in_progress = NULL;
+ hs->state = HEADSET_STATE_DISCONNECTED;
dbus_message_unref(msg);
return err_connect_failed(connection, msg, EIO);
}
- dbus_pending_call_set_notify(pending, handles_reply, hs->connect_in_progress, NULL);
+ dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL);
dbus_message_unref(msg);
return DBUS_HANDLER_RESULT_HANDLED;;
}
-int audio_headset_send_ring(struct headset *hs)
+GIOError audio_headset_send_ring(struct headset *hs)
{
const char *ring_str = "\r\nRING\r\n";
- int sk, written, len;
+ GIOError err;
+ gsize total_written, written, count;
- assert (hs != NULL);
- if (!hs->rfcomm) {
+ assert(hs != NULL);
+ if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) {
error("the headset %s is not connected", hs->object_path);
+ return G_IO_ERROR_UNKNOWN;
}
- sk = g_io_channel_unix_get_fd(hs->rfcomm);
-
- len = strlen(ring_str);
- written = 0;
-
- while (written < len) {
- int ret;
-
- ret = write(sk, ring_str + written, len - written);
-
- if (ret < 0)
- return ret;
+ count = strlen(ring_str);
+ written = total_written = 0;
- written += ret;
+ while (total_written < count) {
+ err = g_io_channel_write(hs->rfcomm, ring_str + total_written,
+ count - total_written, &written);
+ if (err != G_IO_ERROR_NONE)
+ return err;
+ total_written += written;
}
- return 0;
+ return G_IO_ERROR_NONE;
}
-static gboolean ring_timer(gpointer data)
+static gboolean ring_timer_cb(gpointer data)
{
struct headset *hs = data;
@@ -1248,16 +1271,18 @@ static gboolean ring_timer(gpointer data)
static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)
{
- DBusMessage *reply;
+ DBusMessage *reply = NULL;
assert(hs != NULL);
if (hs->state < HEADSET_STATE_CONNECTED)
return err_not_connected(connection, msg);
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ if (msg) {
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
if (hs->ring_timer) {
debug("Got Ring method call while ringing already in progress");
@@ -1269,25 +1294,29 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)
return err_failed(connection, msg);
}
- hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, hs);
+ hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs);
done:
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
+ if (reply) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+ }
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)
{
- DBusMessage *reply;
+ DBusMessage *reply = NULL;
if (!hs)
return err_not_connected(connection, msg);
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ if (msg) {
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
if (!hs->ring_timer) {
debug("Got CancelRinging method call but ringing is not in progress");
@@ -1298,8 +1327,10 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)
hs->ring_timer = 0;
done:
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
+ if (reply) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+ }
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1314,7 +1345,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
if (!hs)
return err_not_connected(connection, msg);
- if (hs->state <= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress)
+ 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)
@@ -1324,11 +1355,12 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
if (!hs->connect_in_progress)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ hs->state = HEADSET_STATE_PLAY_IN_PROGRESS;
memset(hs->connect_in_progress, 0, sizeof(struct pending_connect));
c = hs->connect_in_progress;
c->conn = dbus_connection_ref(connection);
- c->msg = dbus_message_ref(msg);
+ c->msg = msg ? dbus_message_ref(msg) : NULL;
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
if (sk < 0) {
@@ -1341,7 +1373,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
c->io = g_io_channel_unix_new(sk);
if (!c->io) {
close(sk);
- pending_connect_free(c, TRUE);
+ pending_connect_free(c);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
@@ -1350,6 +1382,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, BDADDR_ANY);
+
if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
err = errno;
error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);
@@ -1375,7 +1408,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
debug("Connect in progress");
- g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c);
+ g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, hs);
} else {
debug("Connect succeeded with first try");
sco_connect_cb(c->io, G_IO_OUT, hs);
@@ -1384,75 +1417,47 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)
return 0;
failed:
- if (c)
- pending_connect_free(c, TRUE);
- close(sk);
+ if (hs->connect_in_progress) {
+ pending_connect_free(hs->connect_in_progress);
+ hs->connect_in_progress = NULL;
+ }
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)
{
- DBusMessage *reply;
+ DBusMessage *reply = NULL;
if (!hs || !hs->sco)
return err_not_connected(connection, msg);
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- g_io_channel_close(hs->sco);
- hs->sco = NULL;
-
- 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 (msg) {
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
- 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;
+ if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->connect_in_progress) {
+ pending_connect_free(hs->connect_in_progress);
+ hs->connect_in_progress = NULL;
+ hs->state = HEADSET_STATE_CONNECTED;
}
- 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;
+ if (hs->sco) {
+ g_io_channel_close(hs->sco);
+ hs->sco = NULL;
+ hs->state = HEADSET_STATE_CONNECTED;
}
- g_io_channel_set_close_on_unref(hs->server_sk, TRUE);
+ hs_signal(hs, "Stopped");
+ hs->state = HEADSET_STATE_CONNECTED;
- g_io_add_watch(hs->server_sk, G_IO_IN, (GIOFunc) server_io_cb, hs);
+ if (reply) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+ }
- g_io_channel_unref(hs->server_sk);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult hs_message(DBusConnection *conn,
@@ -1461,7 +1466,7 @@ static DBusHandlerResult hs_message(DBusConnection *conn,
struct headset *hs = data;
const char *interface, *member;
- assert (hs != NULL);
+ assert(hs != NULL);
interface = dbus_message_get_interface(msg);
member = dbus_message_get_member(msg);
@@ -1498,7 +1503,11 @@ static const DBusObjectPathVTable hs_table = {
.message_function = hs_message,
};
-struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel)
+/*
+** audio_headset_new:
+** Create a unique dbus object path for the headset and allocates a new headset or return NULL if fail
+*/
+struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda)
{
static int headset_uid = 0;
struct headset *hs;
@@ -1516,44 +1525,48 @@ struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int cha
if (!dbus_connection_register_object_path(conn, hs->object_path,
&hs_table, hs)) {
error("D-Bus failed to register %s path", hs->object_path);
+ free (hs);
return NULL;
}
- if (channel)
- hs->record_id = add_ag_record(channel);
- if (bda) {
- str2ba(bda, &hs->bda);
- hs_connect(hs, NULL);
- }
+ bacpy(&hs->bda, bda);
return hs;
}
+void audio_headset_unref(struct headset* hs)
+{
+ assert(hs != NULL);
+
+ free(hs);
+}
+
int audio_headset_close_output(struct headset* hs)
{
- assert (hs != NULL);
+ assert(hs != NULL);
if (hs->audio_output == NULL)
return FALSE;
- g_io_channel_close(hs->audio_output);
+ g_io_channel_unref(hs->audio_output);
hs->audio_output = NULL;
return FALSE;
}
+/* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */
int audio_headset_open_output(struct headset* hs, const char *output)
{
int out;
- assert (hs != NULL && output != NULL);
-
+ assert(hs != NULL && output != NULL);
+
audio_headset_close_output(hs);
if (output && hs->output) {
free(hs->output);
hs->output = strdup(output);
}
- assert (hs->output);
+ assert(hs->output);
out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT);
@@ -1568,17 +1581,19 @@ int audio_headset_open_output(struct headset* hs, const char *output)
return TRUE;
}
+ g_io_channel_set_close_on_unref(hs->audio_output, TRUE);
+
return FALSE;
}
int audio_headset_close_input(struct headset* hs)
{
- assert (hs != NULL);
+ assert(hs != NULL);
if (hs->audio_input == NULL)
return FALSE;
- g_io_channel_close(hs->audio_input);
+ g_io_channel_unref(hs->audio_input);
hs->audio_input = NULL;
return FALSE;
@@ -1588,8 +1603,8 @@ int audio_headset_open_input(struct headset* hs, const char *input)
{
int in;
- assert (hs != NULL);
-
+ assert(hs != NULL);
+
audio_headset_close_input(hs);
/* we keep the input name, and NULL can be use to reopen */
@@ -1598,7 +1613,7 @@ int audio_headset_open_input(struct headset* hs, const char *input)
hs->input = strdup(input);
}
- assert (hs->input);
+ assert(hs->input);
in = open(hs->input, O_RDONLY | O_NOCTTY);
@@ -1612,9 +1627,70 @@ int audio_headset_open_input(struct headset* hs, const char *input)
error("Allocating new channel for audio input!");
return TRUE;
}
+ g_io_channel_set_close_on_unref(hs->audio_input, TRUE);
+
return FALSE;
}
+void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan)
+{
+ int srv_sk;
+
+ assert(amanager != NULL);
+
+ if (amanager->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 (!amanager->record_id)
+ amanager->record_id = add_ag_record(chan);
+
+ if (!amanager->record_id) {
+ error("Unable to register service record");
+ close(srv_sk);
+ return;
+ }
+
+ amanager->server_sk = g_io_channel_unix_new(srv_sk);
+ if (!amanager->server_sk) {
+ error("Unable to allocate new GIOChannel");
+ remove_ag_record(amanager->record_id);
+ amanager->record_id = 0;
+ close(srv_sk);
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(amanager->server_sk, TRUE);
+
+ g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager);
+
+ g_io_channel_unref(amanager->server_sk);
+}
+
+static gint headset_bda_cmp(gconstpointer aheadset, gconstpointer bda)
+{
+ const struct headset *hs = aheadset;
+
+ return bacmp(&hs->bda, bda);
+}
+
+struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda)
+{
+ GSList *elem;
+
+ assert(amanager);
+ elem = g_slist_find_custom(amanager->headset_list, bda, headset_bda_cmp);
+
+ return elem ? elem->data : NULL;
+}
+
void audio_manager_add_headset(struct manager *amanager, struct headset *hs)
{
assert(amanager && hs);
@@ -1625,22 +1701,42 @@ void audio_manager_add_headset(struct manager *amanager, struct headset *hs)
amanager->headset_list = g_slist_append(amanager->headset_list, hs);
}
-static DBusHandlerResult am_connect(struct manager *amanager,
- DBusMessage *msg)
+static DBusHandlerResult am_create_headset(struct manager *amanager,
+ DBusMessage *msg)
{
- char object_path[128];
+ const char *address;
+ struct headset *hs;
+ bdaddr_t bda;
DBusMessage *reply;
+ DBusError derr;
if (!amanager)
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;
-
- snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0);
- dbus_message_append_args(msg, DBUS_TYPE_STRING, &object_path,
+ str2ba(address, &bda);
+ if (!(hs = audio_manager_find_headset_by_bda(amanager, &bda))) {
+ hs = audio_headset_new(connection, &bda);
+ }
+ /* FIXME: we could send an error if the headset was already created or silently fail */
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs->object_path,
DBUS_TYPE_INVALID);
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
@@ -1648,10 +1744,11 @@ static DBusHandlerResult am_connect(struct manager *amanager,
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult am_default_headset(struct manager *amanager,
+static DBusHandlerResult am_get_default_headset(struct manager *amanager,
DBusMessage *msg)
{
DBusMessage *reply;
+ char object_path[128];
if (!amanager)
return err_not_connected(connection, msg);
@@ -1660,6 +1757,10 @@ static DBusHandlerResult am_default_headset(struct manager *amanager,
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path,
+ DBUS_TYPE_INVALID);
+
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
@@ -1682,11 +1783,11 @@ static DBusHandlerResult am_message(DBusConnection *conn,
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, "CreateHeadset") == 0)
+ return am_create_headset(amanager, msg);
if (strcmp(member, "DefaultHeadset") == 0)
- return am_default_headset(amanager, msg);
+ return am_get_default_headset(amanager, msg);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -1697,9 +1798,10 @@ static const DBusObjectPathVTable am_table = {
struct manager* audio_manager_new(DBusConnection *conn)
{
- struct manager* amanager;
+ struct manager *amanager;
amanager = malloc(sizeof(struct manager));
+
if (!amanager) {
error("Allocating new hs connection struct failed!");
return NULL;
@@ -1710,12 +1812,43 @@ struct manager* audio_manager_new(DBusConnection *conn)
if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH,
&am_table, amanager)) {
error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH);
+ free(amanager);
return NULL;
}
return amanager;
}
+static void headset_list_unref_each(gpointer aheadset, gpointer am)
+{
+ struct headset *hs = aheadset;
+
+ audio_headset_unref(hs);
+}
+
+void audio_manager_free(struct manager* amanager)
+{
+ assert(amanager != NULL);
+
+ if (amanager->record_id) {
+ remove_ag_record(amanager->record_id);
+ amanager->record_id = 0;
+ }
+
+ if (amanager->server_sk) {
+ g_io_channel_unref(amanager->server_sk);
+ amanager->server_sk = NULL;
+ }
+
+ if (amanager->headset_list) {
+ g_slist_foreach(amanager->headset_list, headset_list_unref_each, amanager);
+ g_slist_free(amanager->headset_list);
+ amanager->headset_list = NULL;
+ }
+
+ free(amanager);
+}
+
static void sig_term(int sig)
{
g_main_loop_quit(main_loop);
@@ -1727,6 +1860,7 @@ int main(int argc, char *argv[])
char *opt_bda = NULL;
char *opt_input = NULL;
char *opt_output = NULL;
+ bdaddr_t bda;
struct headset *hs;
struct manager *manager;
struct sigaction sa;
@@ -1786,24 +1920,33 @@ int main(int argc, char *argv[])
exit(1);
}
+ audio_manager_create_headset_server(manager, opt_channel);
+
if (opt_bda) {
- hs = audio_headset_new(connection, opt_bda, opt_channel);
+ str2ba(opt_bda, &bda);
+ hs = audio_headset_new(connection, &bda);
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);
+ /* connect */
+ hs_connect(hs, NULL);
}
g_main_loop_run(main_loop);
+ audio_manager_free(manager);
+ manager = NULL;
+
dbus_connection_unref(connection);
g_main_loop_unref(main_loop);