diff options
author | Samuel Ortiz <sameo@linux.intel.com> | 2013-05-22 18:12:56 +0200 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-05-22 18:29:50 +0200 |
commit | 64dc6d595238c27f7e95c9a3722ac73577eb673f (patch) | |
tree | 5bc348ff1d375fc1b6d31de5a49716eb981d1e85 | |
parent | 2d0abdde3120a217eeb194244a3778ee256ee5e4 (diff) | |
download | neard-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.c | 85 |
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; } |