summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hcid/dbus-rfcomm.c160
1 files changed, 100 insertions, 60 deletions
diff --git a/hcid/dbus-rfcomm.c b/hcid/dbus-rfcomm.c
index 90027a81..8142b227 100644
--- a/hcid/dbus-rfcomm.c
+++ b/hcid/dbus-rfcomm.c
@@ -50,8 +50,8 @@
#include "dbus.h"
/* Waiting for udev to create the device node */
-#define MAX_OPEN_TRIES 6
-#define OPEN_WAIT (1000 * 300)
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300 /* ms */
static int rfcomm_ctl = -1;
@@ -73,6 +73,10 @@ struct pending_connect {
int canceled;
struct sockaddr_rc laddr;
struct sockaddr_rc raddr;
+
+ /* Used only when we wait for udev to create the device node */
+ struct rfcomm_node *node;
+ int ntries;
};
static struct slist *pending_connects = NULL;
@@ -199,15 +203,94 @@ static gboolean rfcomm_disconnect_cb(GIOChannel *io, GIOCondition cond,
return FALSE;
}
-static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
- struct pending_connect *c)
+static void rfcomm_connect_cb_devnode_opened(int fd,
+ struct pending_connect *c,
+ struct rfcomm_node *node)
{
- int sk, ret, err, fd = -1, i;
- socklen_t len;
+ DBusMessage *reply = NULL;
char *ptr;
- struct rfcomm_dev_req req;
+
+ reply = dbus_message_new_method_return(c->msg);
+ if (!reply) {
+ error_failed(c->conn, c->msg, ENOMEM);
+ goto failed;
+ }
+
+ ptr = node->name;
+ if (!dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &ptr,
+ DBUS_TYPE_INVALID)) {
+ error_failed(c->conn, c->msg, ENOMEM);
+ goto failed;
+ }
+
+ node->owner = strdup(dbus_message_get_sender(c->msg));
+ if (!node->owner) {
+ error_failed(c->conn, c->msg, ENOMEM);
+ goto failed;
+ }
+
+ node->io = g_io_channel_unix_new(fd);
+ g_io_channel_set_close_on_unref(node->io, TRUE);
+ node->io_id = g_io_add_watch(node->io, G_IO_ERR | G_IO_HUP,
+ (GIOFunc) rfcomm_disconnect_cb, node);
+
+ send_reply_and_unref(c->conn, reply);
+
+ connected_nodes = slist_append(connected_nodes, node);
+
+ goto done;
+
+failed:
+ close(fd);
+ rfcomm_release(node, NULL);
+ rfcomm_node_free(node);
+ if (reply)
+ dbus_message_unref(reply);
+done:
+ pending_connects = slist_remove(pending_connects, c);
+ pending_connect_free(c);
+}
+
+static gboolean rfcomm_connect_cb_continue(void *data)
+{
+ struct pending_connect *c = data;
+ struct rfcomm_node *node = c->node;
+ int fd;
+
+ fd = open(node->name, O_RDONLY | O_NOCTTY);
+ if (fd < 0) {
+ if (++c->ntries >= MAX_OPEN_TRIES) {
+ int err = errno;
+ error("Could not open %s: %s (%d)",
+ node->name, strerror(err), err);
+ error_connection_attempt_failed(c->conn, c->msg, err);
+ goto failed;
+ }
+ return TRUE;
+ }
+
+ rfcomm_connect_cb_devnode_opened(fd, c, node);
+
+ return FALSE;
+
+failed:
+ rfcomm_release(node, NULL);
+ rfcomm_node_free(node);
+
+ pending_connects = slist_remove(pending_connects, c);
+ pending_connect_free(c);
+
+ return FALSE;
+}
+
+static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
+ struct pending_connect *c)
+{
struct rfcomm_node *node = NULL;
- DBusMessage *reply = NULL;
+ struct rfcomm_dev_req req;
+ int sk, ret, err, fd = -1;
+ socklen_t len;
if (c->canceled) {
error_connect_canceled(c->conn, c->msg);
@@ -267,65 +350,22 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
rfcomm_node_name_from_id(node->id, node->name, sizeof(node->name));
- /* FIXME: instead of looping here we should create a timer and
- * return to the mainloop */
- for (i = 0; i < MAX_OPEN_TRIES; i++) {
- fd = open(node->name, O_RDONLY | O_NOCTTY);
- if (fd >= 0)
- break;
- usleep(OPEN_WAIT);
- }
-
+ fd = open(node->name, O_RDONLY | O_NOCTTY);
if (fd < 0) {
- err = errno;
- error("Could not open %s: %s (%d)", node->name,
- strerror(err), err);
- error_connection_attempt_failed(c->conn, c->msg, err);
- goto failed;
+ c->node = node;
+ c->ntries = 0;
+ g_timeout_add(OPEN_WAIT, rfcomm_connect_cb_continue, c);
+ return FALSE;
}
- reply = dbus_message_new_method_return(c->msg);
- if (!reply) {
- error_failed(c->conn, c->msg, ENOMEM);
- goto failed;
- }
+ rfcomm_connect_cb_devnode_opened(fd, c, node);
- ptr = node->name;
- if (!dbus_message_append_args(reply,
- DBUS_TYPE_STRING, &ptr,
- DBUS_TYPE_INVALID)) {
- error_failed(c->conn, c->msg, ENOMEM);
- goto failed;
- }
-
- node->owner = strdup(dbus_message_get_sender(c->msg));
- if (!node->owner) {
- error_failed(c->conn, c->msg, ENOMEM);
- goto failed;
- }
-
- node->io = g_io_channel_unix_new(fd);
- g_io_channel_set_close_on_unref(node->io, TRUE);
- node->io_id = g_io_add_watch(node->io, G_IO_ERR | G_IO_HUP,
- (GIOFunc) rfcomm_disconnect_cb, node);
-
- send_reply_and_unref(c->conn, reply);
-
- connected_nodes = slist_append(connected_nodes, node);
-
- goto done;
+ return FALSE;
failed:
- if (fd >= 0)
- close(fd);
- if (node) {
- if (node->id >= 0)
- rfcomm_release(node, NULL);
+ if (node)
rfcomm_node_free(node);
- }
- if (reply)
- dbus_message_unref(reply);
-done:
+
pending_connects = slist_remove(pending_connects, c);
pending_connect_free(c);