summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2013-05-22 18:12:56 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2013-05-22 18:29:50 +0200
commit64dc6d595238c27f7e95c9a3722ac73577eb673f (patch)
tree5bc348ff1d375fc1b6d31de5a49716eb981d1e85
parent2d0abdde3120a217eeb194244a3778ee256ee5e4 (diff)
downloadneard-64dc6d595238c27f7e95c9a3722ac73577eb673f.tar.gz
neard-64dc6d595238c27f7e95c9a3722ac73577eb673f.tar.bz2
neard-64dc6d595238c27f7e95c9a3722ac73577eb673f.zip
p2p: Fall back to blocking sockets if the async connection fails
Due to a kernel bug, trying to connect through a non blocking socket may fail and return POLLHUP. In that case the connection procedure falls back to using blocking sockets and this needs to be delayed until the kernel sends a DM to the peer. This is slightly hackish but this code path should get obsolete once neard runs on top of 3.10 or later kernels.
-rw-r--r--plugins/p2p.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/plugins/p2p.c b/plugins/p2p.c
index b05395f..26f1e62 100644
--- a/plugins/p2p.c
+++ b/plugins/p2p.c
@@ -283,6 +283,80 @@ static gboolean p2p_listener_event(GIOChannel *channel, GIOCondition condition,
return !driver->single_connection;
}
+static int p2p_connect_blocking(uint32_t adapter_idx, uint32_t target_idx,
+ struct near_ndef_message *ndef,
+ near_device_io_cb cb,
+ struct near_p2p_driver *driver)
+{
+ int fd, err = 0;
+ struct timeval timeout;
+ struct sockaddr_nfc_llcp addr;
+
+ DBG("");
+
+ fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP);
+ if (fd < 0)
+ return -errno;
+
+ memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp));
+ addr.sa_family = AF_NFC;
+ addr.dev_idx = adapter_idx;
+ addr.target_idx = target_idx;
+ addr.nfc_protocol = NFC_PROTO_NFC_DEP;
+ addr.service_name_len = strlen(driver->service_name);
+ strcpy(addr.service_name, driver->service_name);
+
+ timeout.tv_sec = 8;
+ timeout.tv_usec = 0;
+
+ if (setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
+ sizeof(timeout)) < 0)
+ near_error("Could not set the receive timeout\n");
+
+ if (setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
+ sizeof(timeout)) < 0)
+ near_error("Could not set the send timeout\n");
+
+ err = connect(fd, (struct sockaddr *) &addr,
+ sizeof(struct sockaddr_nfc_llcp));
+ if (err < 0) {
+ near_error("Connect failed %d", err);
+ close(fd);
+
+ return err;
+ }
+
+ return fd;
+}
+
+static gboolean p2p_push_blocking(gpointer user_data)
+{
+ struct p2p_connect *conn = user_data;
+ int fd, err;
+
+ DBG("");
+
+ fd = p2p_connect_blocking(conn->adapter_idx, conn->target_idx,
+ conn->ndef, conn->cb, conn->driver);
+ if (fd < 0) {
+ err = fd;
+ goto out;
+ }
+
+ err = conn->driver->push(fd, conn->adapter_idx, conn->target_idx,
+ conn->ndef, conn->cb);
+
+out:
+ if (err < 0)
+ conn->cb(conn->adapter_idx, conn->target_idx, err);
+
+ g_free(conn->ndef->data);
+ g_free(conn->ndef);
+ g_free(conn);
+
+ return FALSE;
+}
+
static gboolean check_nval(GIOChannel *io)
{
struct pollfd fds;
@@ -316,6 +390,17 @@ static gboolean p2p_connect_event(GIOChannel *channel, GIOCondition condition,
if ((condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) ||
check_nval(channel)) {
near_error("%s connect error", conn->driver->name);
+
+ if (condition & G_IO_HUP) {
+ DBG("Trying a blocking connect");
+
+ close(fd);
+
+ g_timeout_add(300, p2p_push_blocking, conn);
+
+ return FALSE;
+ }
+
err = -EIO;
goto out;
}