summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2008-02-18 14:17:33 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2008-02-18 14:17:33 +0000
commitc5463ab43438de912b5fd642ec8e248f1e9683dd (patch)
tree40550133a06122472db2f621028ac146a30238ba
parentff51f4b0b2724754184baa08b09a47cad7b7b4d5 (diff)
Cleanup headset connection logic to accommodate necessary HFP fixes
-rw-r--r--audio/headset.c557
1 files changed, 296 insertions, 261 deletions
diff --git a/audio/headset.c b/audio/headset.c
index 84517f1d..bfad8455 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -87,15 +87,19 @@ static char *str_state[] = {
"HEADSET_STATE_PLAYING",
};
+struct connect_cb {
+ int id;
+ headset_stream_cb_t cb;
+ void *cb_data;
+};
+
struct pending_connect {
DBusMessage *msg;
DBusPendingCall *call;
GIOChannel *io;
- int sock;
int err;
- unsigned int id;
- headset_stream_cb_t cb;
- void *cb_data;
+ headset_state_t target_state;
+ GSList *callbacks;
};
struct headset {
@@ -121,7 +125,7 @@ struct headset {
int type;
headset_state_t state;
- GSList *pending;
+ struct pending_connect *pending;
int sp_gain;
int mic_gain;
@@ -135,8 +139,10 @@ struct event {
int (*callback) (struct device *device, const char *buf);
};
-static int rfcomm_connect(struct device *device, struct pending_connect *c);
-static int get_handles(struct device *device, struct pending_connect *c);
+static int rfcomm_connect(struct device *device, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id);
+static int get_handles(struct device *device, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id);
static int headset_send(struct headset *hs, char *format, ...)
{
@@ -152,7 +158,7 @@ static int headset_send(struct headset *hs, char *format, ...)
if (count < 0)
return -EINVAL;
- if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) {
+ if (!hs->rfcomm) {
error("headset_send: the headset is not connected");
return -EIO;
}
@@ -370,22 +376,6 @@ static int handle_event(struct device *device, const char *buf)
return -EINVAL;
}
-static void pending_connect_free(struct pending_connect *c)
-{
- if (c->io) {
- g_io_channel_close(c->io);
- g_io_channel_unref(c->io);
- }
- if (c->msg)
- dbus_message_unref(c->msg);
- if (c->call) {
- dbus_pending_call_cancel(c->call);
- dbus_pending_call_unref(c->call);
- }
-
- g_free(c);
-}
-
static void close_sco(struct device *device)
{
struct headset *hs = device->headset;
@@ -485,72 +475,104 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond,
return FALSE;
}
-static void pending_connect_ok(struct pending_connect *c, struct device *dev)
+static void pending_connect_complete(struct connect_cb *cb, struct device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->pending->err)
+ cb->cb(NULL, cb->cb_data);
+ else
+ cb->cb(dev, cb->cb_data);
+}
+
+static void pending_connect_finalize(struct device *dev)
{
struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
+
+ g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev);
- if (c->msg) {
- DBusMessage *reply = dbus_message_new_method_return(c->msg);
- if (reply)
- send_message_and_unref(dev->conn, reply);
+ g_slist_foreach(p->callbacks, (GFunc) g_free, NULL);
+ g_slist_free(p->callbacks);
+
+ if (p->io) {
+ g_io_channel_close(p->io);
+ g_io_channel_unref(p->io);
}
- if (c->cb) {
- if (hs->rfcomm && hs->sco)
- c->cb(dev, c->cb_data);
- else
- c->cb(NULL, c->cb_data);
+ if (p->msg)
+ dbus_message_unref(p->msg);
+
+ if (p->call) {
+ dbus_pending_call_cancel(p->call);
+ dbus_pending_call_unref(p->call);
}
- pending_connect_free(c);
+ g_free(p);
+
+ hs->pending = NULL;
}
-static gboolean finalize_stream_setup(struct device *dev)
+static void pending_connect_init(struct headset *hs, headset_state_t target_state)
{
- struct headset *hs = dev->headset;
-
- g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, dev);
- g_slist_free(hs->pending);
- hs->pending = NULL;
+ if (hs->pending) {
+ if (hs->pending->target_state < target_state)
+ hs->pending->target_state = target_state;
+ return;
+ }
- return FALSE;
+ hs->pending = g_new0(struct pending_connect, 1);
+ hs->pending->target_state = target_state;
}
-static void pending_connect_failed(struct pending_connect *c, struct device *dev)
+static unsigned int connect_cb_new(struct headset *hs,
+ headset_state_t target_state,
+ headset_stream_cb_t func,
+ void *user_data)
{
- if (c->msg)
- error_connection_attempt_failed(dev->conn, c->msg, c->err);
- if (c->cb)
- c->cb(NULL, c->cb_data);
- pending_connect_free(c);
+ struct connect_cb *cb;
+ unsigned int free_cb_id = 1;
+
+ pending_connect_init(hs, target_state);
+
+ cb = g_new(struct connect_cb, 1);
+
+ cb->cb = func;
+ cb->cb_data = user_data;
+ cb->id = free_cb_id++;
+
+ hs->pending->callbacks = g_slist_append(hs->pending->callbacks,
+ cb);
+
+ return cb->id;
}
static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
struct device *device)
{
struct headset *hs;
- struct pending_connect *c;
int ret, sk;
socklen_t len;
+ struct pending_connect *p;
if (cond & G_IO_NVAL)
return FALSE;
hs = device->headset;
- c = hs->pending->data;
+ p = hs->pending;
sk = g_io_channel_unix_get_fd(chan);
len = sizeof(ret);
if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
- c->err = errno;
- error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err),
- c->err);
+ p->err = errno;
+ error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err),
+ p->err);
goto failed;
}
if (ret != 0) {
- c->err = ret;
+ p->err = ret;
error("connect(): %s (%d)", strerror(ret), ret);
goto failed;
}
@@ -559,11 +581,10 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
info("SCO fd=%d", sk);
hs->sco = chan;
- c->io = NULL;
+ p->io = NULL;
+
+ pending_connect_finalize(device);
- g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device);
- g_slist_free(hs->pending);
- hs->pending = NULL;
fcntl(sk, F_SETFL, 0);
headset_set_state(device, HEADSET_STATE_PLAYING);
@@ -571,9 +592,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
return FALSE;
failed:
- g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device);
- g_slist_free(hs->pending);
- hs->pending = NULL;
+ pending_connect_finalize(device);
if (hs->rfcomm)
headset_set_state(device, HEADSET_STATE_CONNECTED);
else
@@ -582,18 +601,16 @@ failed:
return FALSE;
}
-static int sco_connect(struct device *device, struct pending_connect *c)
+static int sco_connect(struct device *dev, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id)
{
- struct headset *hs = device->headset;
+ struct headset *hs = dev->headset;
struct sockaddr_sco addr;
- gboolean do_callback = FALSE;
+ GIOChannel *io;
int sk, err;
- if (!g_slist_find(hs->pending, c))
- hs->pending = g_slist_append(hs->pending, c);
-
if (hs->state != HEADSET_STATE_CONNECTED)
- return 0;
+ return -EINVAL;
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
if (sk < 0) {
@@ -602,10 +619,10 @@ static int sco_connect(struct device *device, struct pending_connect *c)
return -err;
}
- c->io = g_io_channel_unix_new(sk);
- if (!c->io) {
+ io = g_io_channel_unix_new(sk);
+ if (!io) {
close(sk);
- return -EINVAL;
+ return -ENOMEM;
}
memset(&addr, 0, sizeof(addr));
@@ -615,46 +632,55 @@ static int sco_connect(struct device *device, struct pending_connect *c)
if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
err = errno;
error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);
- return -err;
+ goto failed;
}
if (set_nonblocking(sk) < 0) {
err = errno;
- return -err;
+ goto failed;
}
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
- bacpy(&addr.sco_bdaddr, &device->dst);
+ bacpy(&addr.sco_bdaddr, &dev->dst);
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- if (!(errno == EAGAIN || errno == EINPROGRESS)) {
- err = errno;
- error("connect: %s (%d)", strerror(errno), errno);
- return -err;
- }
+ err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
- g_io_add_watch(c->io,
- G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
- (GIOFunc) sco_connect_cb, device);
- } else
- do_callback = TRUE;
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
+ err = errno;
+ error("connect: %s (%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS);
+
+ pending_connect_init(hs, HEADSET_STATE_PLAYING);
+
+ if (cb) {
+ unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING,
+ cb, user_data);
+ if (cb_id)
+ *cb_id = id;
+ }
- headset_set_state(device, HEADSET_STATE_PLAY_IN_PROGRESS);
- if (!g_slist_find(hs->pending, c))
- hs->pending = g_slist_append(hs->pending, c);
+ g_io_add_watch(io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+ (GIOFunc) sco_connect_cb, dev);
- if (do_callback)
- sco_connect_cb(c->io, G_IO_OUT, device);
+ hs->pending->io = io;
return 0;
+
+failed:
+ g_io_channel_close(io);
+ g_io_channel_unref(io);
+ return -err;
}
static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
- struct device *device)
+ struct device *dev)
{
struct headset *hs;
- struct pending_connect *c;
+ struct pending_connect *p;
char hs_address[18];
int sk, ret;
socklen_t len;
@@ -662,62 +688,59 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
if (cond & G_IO_NVAL)
return FALSE;
- hs = device->headset;
- c = hs->pending->data;
+ hs = dev->headset;
+ p = hs->pending;
sk = g_io_channel_unix_get_fd(chan);
len = sizeof(ret);
if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
- c->err = errno;
- error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err), c->err);
+ p->err = errno;
+ error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), p->err);
goto failed;
}
if (ret != 0) {
- c->err = ret;
+ p->err = ret;
error("connect(): %s (%d)", strerror(ret), ret);
goto failed;
}
- ba2str(&device->dst, hs_address);
+ ba2str(&dev->dst, hs_address);
hs->rfcomm = chan;
- c->io = NULL;
+ p->io = NULL;
if (server_is_enabled(HANDSFREE_SVCLASS_ID) && hs->hfp_handle != 0)
hs->hfp_active = TRUE;
else
hs->hfp_active = FALSE;
- headset_set_state(device, HEADSET_STATE_CONNECTED);
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
- debug("%s: Connected to %s", device->path, hs_address);
+ debug("%s: Connected to %s", dev->path, hs_address);
g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
- (GIOFunc) rfcomm_io_cb, device);
+ (GIOFunc) rfcomm_io_cb, dev);
- if (c->cb) {
- if (sco_connect(device, c) < 0) {
- c->err = EIO;
+ if (p->target_state == HEADSET_STATE_PLAYING) {
+ p->err = sco_connect(dev, NULL, NULL, NULL);
+ if (p->err < 0)
goto failed;
- }
return FALSE;
}
- g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device);
- g_slist_free(hs->pending);
- hs->pending = NULL;
+ pending_connect_finalize(dev);
return FALSE;
failed:
- g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device);
- g_slist_free(hs->pending);
- hs->pending = NULL;
+ if (p->msg)
+ error_connection_attempt_failed(dev->conn, p->msg, p->err);
+ pending_connect_finalize(dev);
if (hs->rfcomm)
- headset_set_state(device, HEADSET_STATE_CONNECTED);
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
else
- headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
return FALSE;
}
@@ -731,11 +754,9 @@ static void get_record_reply(DBusPendingCall *call, void *data)
sdp_record_t *record = NULL;
sdp_list_t *protos, *classes = NULL;
uuid_t uuid;
- struct device *device = data;
- struct headset *hs = device->headset;
- struct pending_connect *c;
-
- c = hs->pending->data;
+ struct device *dev = data;
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
reply = dbus_pending_call_steal_reply(call);
@@ -812,10 +833,11 @@ static void get_record_reply(DBusPendingCall *call, void *data)
hs->rfcomm_ch = ch;
- err = rfcomm_connect(device, NULL);
+ err = rfcomm_connect(dev, NULL, NULL, NULL);
if (err < 0) {
error("Unable to connect: %s (%s)", strerror(-err), -err);
- c->err = -err;
+ p->err = -err;
+ error_connection_attempt_failed(dev->conn, p->msg, p->err);
goto failed;
}
@@ -823,15 +845,15 @@ static void get_record_reply(DBusPendingCall *call, void *data)
sdp_record_free(record);
dbus_message_unref(reply);
- device_finish_sdp_transaction(device);
+ device_finish_sdp_transaction(dev);
return;
failed_not_supported:
- if (c->msg) {
- error_not_supported(device->conn, c->msg);
- dbus_message_unref(c->msg);
- c->msg = NULL;
+ if (p->msg) {
+ error_not_supported(dev->conn, p->msg);
+ dbus_message_unref(p->msg);
+ p->msg = NULL;
}
failed:
if (classes)
@@ -840,11 +862,9 @@ failed:
sdp_record_free(record);
if (reply)
dbus_message_unref(reply);
- g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device);
- g_slist_free(hs->pending);
- hs->pending = NULL;
- headset_set_state(device, HEADSET_STATE_DISCONNECTED);
- device_finish_sdp_transaction(device);
+ pending_connect_finalize(dev);
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+ device_finish_sdp_transaction(dev);
}
static void get_handles_reply(DBusPendingCall *call, void *data)
@@ -852,28 +872,26 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
DBusMessage *msg = NULL, *reply;
DBusPendingCall *pending;
DBusError derr;
- struct device *device = data;
- struct headset *hs = device->headset;
- struct pending_connect *c;
+ struct device *dev = data;
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
char address[18], *addr_ptr = address;
dbus_uint32_t *array = NULL;
dbus_uint32_t handle;
int array_len;
- c = hs->pending->data;
-
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 (c->msg) {
+ if (p->msg) {
if (dbus_error_has_name(&derr,
"org.bluez.Error.ConnectionAttemptFailed"))
- error_connection_attempt_failed(device->conn, c->msg,
- EHOSTDOWN);
+ error_connection_attempt_failed(dev->conn, p->msg,
+ EHOSTDOWN);
else
- error_not_supported(device->conn, c->msg);
+ error_not_supported(dev->conn, p->msg);
}
dbus_error_free(&derr);
goto failed;
@@ -886,15 +904,15 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
DBUS_TYPE_INVALID)) {
error("Unable to get args from reply: %s", derr.message);
dbus_error_free(&derr);
- if (c->msg)
- error_not_supported(device->conn, c->msg);
+ if (p->msg)
+ error_not_supported(dev->conn, p->msg);
goto failed;
}
if (!array) {
error("get_handles_reply: Unable to get handle array from reply");
- if (c->msg)
- error_not_supported(device->conn, c->msg);
+ if (p->msg)
+ error_not_supported(dev->conn, p->msg);
goto failed;
}
@@ -902,32 +920,32 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
if (hs->search_hfp) {
debug("No record handles found for hfp");
hs->search_hfp = FALSE;
- get_handles(device, c);
+ get_handles(dev, NULL, NULL, NULL);
dbus_message_unref(reply);
return;
}
debug("No record handles found for hsp");
- if (c->msg)
- error_not_supported(device->conn, c->msg);
+ if (p->msg)
+ error_not_supported(dev->conn, p->msg);
goto failed;
}
if (array_len > 1)
debug("Multiple records found. Using the first one.");
- msg = dbus_message_new_method_call("org.bluez", device->adapter_path,
+ msg = dbus_message_new_method_call("org.bluez", dev->adapter_path,
"org.bluez.Adapter",
"GetRemoteServiceRecord");
if (!msg) {
error("Unable to allocate new method call");
- if (c->msg)
- error_out_of_memory(device->conn, c->msg);
+ if (p->msg)
+ error_out_of_memory(dev->conn, p->msg);
goto failed;
}
- ba2str(&device->dst, address);
+ ba2str(&dev->dst, address);
handle = array[0];
@@ -935,14 +953,14 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
DBUS_TYPE_UINT32, &handle,
DBUS_TYPE_INVALID);
- if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) {
+ if (!dbus_connection_send_with_reply(dev->conn, msg, &pending, -1)) {
error("Sending GetRemoteServiceRecord failed");
- if (c->msg)
- error_connection_attempt_failed(device->conn, c->msg, EIO);
+ if (p->msg)
+ error_connection_attempt_failed(dev->conn, p->msg, EIO);
goto failed;
}
- dbus_pending_call_set_notify(pending, get_record_reply, device, NULL);
+ dbus_pending_call_set_notify(pending, get_record_reply, dev, NULL);
dbus_pending_call_unref(pending);
dbus_message_unref(msg);
dbus_message_unref(reply);
@@ -952,19 +970,14 @@ static void get_handles_reply(DBusPendingCall *call, void *data)
failed:
if (msg)
dbus_message_unref(msg);
- /* The reply was already sent above */
- if (c->msg) {
- dbus_message_unref(c->msg);
- c->msg = NULL;
- }
dbus_message_unref(reply);
- g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device);
- g_slist_free(hs->pending);
- hs->pending = NULL;
- headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+ p->err = EIO;
+ pending_connect_finalize(dev);
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
}
-static int get_handles(struct device *device, struct pending_connect *c)
+static int get_handles(struct device *device, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id)
{
DBusPendingCall *pending;
struct headset *hs = device->headset;
@@ -978,7 +991,7 @@ static int get_handles(struct device *device, struct pending_connect *c)
"GetRemoteServiceHandles");
if (!msg) {
error("Could not create a new dbus message");
- return -EINVAL;
+ return -ENOMEM;
}
if (hs->search_hfp)
@@ -999,45 +1012,56 @@ static int get_handles(struct device *device, struct pending_connect *c)
return -EIO;
}
+ pending_connect_init(hs, HEADSET_STATE_CONNECTED);
+
+ if (cb) {
+ unsigned int id;
+ id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
+ cb, user_data);
+ if (cb_id)
+ *cb_id = id;
+ }
+
dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL);
- if (c)
- c->call = pending;
+
+ if (hs->pending)
+ hs->pending->call = pending;
else
dbus_pending_call_unref(pending);
+
dbus_message_unref(msg);
return 0;
}
-static int rfcomm_connect(struct device *device, struct pending_connect *c)
+static int rfcomm_connect(struct device *dev, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id)
{
- struct headset *hs = device->headset;
+ struct headset *hs = dev->headset;
struct sockaddr_rc addr;
+ GIOChannel *io;
char address[18];
int sk, err;
- if (c != NULL) {
- if (!g_slist_find(hs->pending, c))
- hs->pending = g_slist_append(hs->pending, c);
+ if (hs->rfcomm_ch < 0)
+ return get_handles(dev, cb, user_data, cb_id);
- if (hs->state == HEADSET_STATE_DISCONNECTED)
- return get_handles(device, c);
- else
- return 0;
- }
- else
- c = hs->pending->data;
-
- ba2str(&device->dst, address);
+ ba2str(&dev->dst, address);
- debug("%s: Connecting to %s channel %d", device->path, address,
+ debug("%s: Connecting to %s channel %d", dev->path, address,
hs->rfcomm_ch);
sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (sk < 0) {
err = errno;
- error("socket: %s (%d)", strerror(err), err);
- goto failed;
+ error("socket(BTPROTO_RFCOMM): %s (%d)", strerror(err), err);
+ return -err;
+ }
+
+ io = g_io_channel_unix_new(sk);
+ if (!io) {
+ close(sk);
+ return -ENOMEM;
}
memset(&addr, 0, sizeof(addr));
@@ -1058,34 +1082,38 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c)
memset(&addr, 0, sizeof(addr));
addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &device->dst);
+ bacpy(&addr.rc_bdaddr, &dev->dst);
addr.rc_channel = hs->rfcomm_ch;
- c->io = g_io_channel_unix_new(sk);
- if (!c->io) {
- err = ENOMEM;
- error("channel_unix_new failed in rfcomm connect");
+ err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
+ err = errno;
+ error("connect() failed: %s (%d)", strerror(err), err);
goto failed;
}
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- if (!(errno == EAGAIN || errno == EINPROGRESS)) {
- err = errno;
- error("connect() failed: %s (%d)", strerror(err), err);
- goto failed;
- }
+ headset_set_state(dev, HEADSET_STATE_CONNECT_IN_PROGRESS);
- g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL,
- (GIOFunc) rfcomm_connect_cb, device);
- } else
- rfcomm_connect_cb(c->io, G_IO_OUT, device);
+ pending_connect_init(hs, HEADSET_STATE_CONNECTED);
+
+ if (cb) {
+ unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
+ cb, user_data);
+ if (cb_id)
+ *cb_id = id;
+ }
+
+ g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) rfcomm_connect_cb, dev);
+
+ hs->pending->io = io;
return 0;
failed:
- if (!c->io && sk >= 0)
- close(sk);
-
+ g_io_channel_close(io);
+ g_io_channel_unref(io);
return -err;
}
@@ -1182,29 +1210,20 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
{
struct device *device = data;
struct headset *hs = device->headset;
- struct pending_connect *c;
int err;
- if (hs->state > HEADSET_STATE_DISCONNECTED)
+ if (hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS)
+ return error_in_progress(conn, msg, "Connect in progress");
+ else if (hs->state > HEADSET_STATE_CONNECT_IN_PROGRESS)
return error_already_connected(conn, msg);
- c = g_try_new0(struct pending_connect, 1);
- if (!c) {
- error("Out of memory when allocating struct pending_connect");
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
- }
-
- c->msg = dbus_message_ref(msg);
-
- err = rfcomm_connect(device, c);
+ err = rfcomm_connect(device, NULL, NULL, NULL);
if (err < 0)
- goto error;
+ return error_connection_attempt_failed(conn, msg, -err);
- return DBUS_HANDLER_RESULT_HANDLED;
+ hs->pending->msg = dbus_message_ref(msg);
-error:
- pending_connect_free(c);
- return error_connection_attempt_failed(conn, msg, -err);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
static gboolean ring_timer_cb(gpointer data)
@@ -1318,26 +1337,26 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg,
{
struct device *device = data;
struct headset *hs = device->headset;
- struct pending_connect *c;
int err;
- if (hs->state < HEADSET_STATE_CONNECTED)
+ switch (hs->state) {
+ case HEADSET_STATE_DISCONNECTED:
+ case HEADSET_STATE_CONNECT_IN_PROGRESS:
return error_not_connected(conn, msg);
-
- if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS)
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ return error_in_progress(conn, msg, "Play in progress");
+ case HEADSET_STATE_PLAYING:
return error_already_connected(conn, msg);
+ case HEADSET_STATE_CONNECTED:
+ default:
+ break;
+ }
- c = g_try_new0(struct pending_connect, 1);
- if (!c)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- c->msg = msg ? dbus_message_ref(msg) : NULL;
-
- err = sco_connect(device, c);
- if (err < 0) {
- pending_connect_free(c);
+ err = sco_connect(device, NULL, NULL, NULL);
+ if (err < 0)
return error_failed(conn, msg, strerror(-err));
- }
+
+ hs->pending->msg = dbus_message_ref(msg);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -1592,7 +1611,8 @@ static DBusSignalVTable headset_signals[] = {
{ NULL, NULL }
};
-static void headset_set_channel(struct headset *headset, sdp_record_t *record)
+static void headset_set_channel(struct headset *headset, sdp_record_t *record,
+ uint16_t svc)
{
int ch;
sdp_list_t *protos;
@@ -1609,7 +1629,9 @@ static void headset_set_channel(struct headset *headset, sdp_record_t *record)
if (ch > 0) {
headset->rfcomm_ch = ch;
- debug("Discovered Headset service on RFCOMM channel %d", ch);
+ debug("Discovered %s service on RFCOMM channel %d",
+ svc == HEADSET_SVCLASS_ID ? "Headset" : "Handsfree",
+ ch);
} else
error("Unable to get RFCOMM channel from Headset record");
}
@@ -1649,7 +1671,7 @@ void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc)
return;
}
- headset_set_channel(headset, record);
+ headset_set_channel(headset, record, svc);
}
struct headset *headset_init(struct device *dev, sdp_record_t *record,
@@ -1684,7 +1706,7 @@ struct headset *headset_init(struct device *dev, sdp_record_t *record,
return NULL;
}
- headset_set_channel(hs, record);
+ headset_set_channel(hs, record, svc);
register_iface:
if (!dbus_connection_register_interface(dev->conn, dev->path,
AUDIO_HEADSET_INTERFACE,
@@ -1828,61 +1850,74 @@ void headset_free(struct device *dev)
gboolean headset_cancel_stream(struct device *dev, unsigned int id)
{
struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
GSList *l;
- struct pending_connect *pending = NULL;
+ struct connect_cb *cb = NULL;
+
+ if (!p)
+ return FALSE;
- for (l = hs->pending; l != NULL; l = l->next) {
- struct pending_connect *tmp = l->data;
+ for (l = p->callbacks; l != NULL; l = l->next) {
+ struct connect_cb *tmp = l->data;
if (tmp->id == id) {
- pending = tmp;
+ cb = tmp;
break;
}
}
- if (!pending)
+ if (!cb)
return FALSE;
- hs->pending = g_slist_remove(hs->pending, pending);
- pending_connect_free(pending);
+ p->callbacks = g_slist_remove(p->callbacks, cb);
+ g_free(cb);
- if (!hs->pending)
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+ if (p->callbacks || p->msg)
+ return TRUE;
+
+ pending_connect_finalize(dev);
+
+ /* FIXME: disconnecting is not correct for all scenarios, e.g. if the
+ * stream request came when we were already connected then we should
+ * stay connected after canceling the request too */
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
return TRUE;
}
+static gboolean dummy_connect_complete(struct device *dev)
+{
+ pending_connect_finalize(dev);
+ return FALSE;
+}
+
unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb,
void *user_data)
{
struct headset *hs = dev->headset;
- struct pending_connect *c;
- static unsigned int cb_id = 0;
+ unsigned int id;
int err;
- c = g_new0(struct pending_connect, 1);
- c->cb = cb;
- c->cb_data = user_data;
- c->id = ++cb_id;
-
if (hs->rfcomm && hs->sco) {
- hs->pending = g_slist_append(hs->pending, c);
- g_idle_add((GSourceFunc) finalize_stream_setup, dev);
- return c->id;
+ id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
+ g_idle_add((GSourceFunc) dummy_connect_complete, dev);
+ return id;
}
if (hs->rfcomm == NULL)
- err = rfcomm_connect(dev, c);
+ err = rfcomm_connect(dev, cb, user_data, &id);
else if (hs->sco == NULL)
- err = sco_connect(dev, c);
+ err = sco_connect(dev, cb, user_data, &id);
if (err < 0)
goto error;
- return c->id;
+ hs->pending->target_state = HEADSET_STATE_PLAYING;
+
+ return id;
error:
- pending_connect_free(c);
+ pending_connect_finalize(dev);
return 0;
}
@@ -1936,7 +1971,7 @@ void headset_set_state(struct device *dev, headset_state_t state)
if (hs->state == state)
return;
- switch(state) {
+ switch (state) {
case HEADSET_STATE_DISCONNECTED:
close_sco(dev);
headset_close_rfcomm(dev);