summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/adapter.h37
-rw-r--r--src/adapter.c142
2 files changed, 174 insertions, 5 deletions
diff --git a/include/adapter.h b/include/adapter.h
new file mode 100644
index 0000000..d140a51
--- /dev/null
+++ b/include/adapter.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * neard - Near Field Communication manager
+ *
+ * Copyright (C) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __NEAR_ADAPTER_H
+#define __NEAR_ADAPTER_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef int (*near_recv)(uint8_t *resp, size_t length, void *data);
+
+struct near_target *near_adapter_last_target(uint32_t idx);
+int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol);
+int near_adapter_disconnect(uint32_t idx);
+int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length, near_recv rx_cb, void *data);
+int near_adapter_recv(uint32_t idx, uint8_t *buf, size_t length);
+
+#endif
diff --git a/src/adapter.c b/src/adapter.c
index d00d054..74e73cd 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -51,6 +51,16 @@ struct near_adapter {
GList *target_list;
struct near_target *active_target;
int active_sock;
+
+ GIOChannel *channel;
+ guint watch;
+ GList *ioreq_list;
+};
+
+struct near_adapter_ioreq {
+ uint32_t target_idx;
+ near_recv cb;
+ void *data;
};
/* HACK HACK */
@@ -354,6 +364,8 @@ int __near_adapter_add_target(uint32_t idx, struct near_target *target)
targets_changed(adapter);
+ __near_tag_read(target);
+
return 0;
}
@@ -417,11 +429,67 @@ struct near_target *near_adapter_last_target(uint32_t idx)
return list->data;
}
-int near_adapter_connect(uint32_t idx, uint32_t target_idx)
+static void adapter_flush_rx(struct near_adapter *adapter, int error)
+{
+ GList *list;
+
+ for (list = adapter->ioreq_list; list; list = list->next) {
+ struct near_adapter_ioreq *req = list->data;
+
+ if (req == NULL)
+ continue;
+
+ req->cb(NULL, error, req->data);
+ g_free(req);
+ }
+
+ g_list_free(adapter->ioreq_list);
+ adapter->ioreq_list = NULL;
+}
+
+static gboolean adapter_recv_event(GIOChannel *channel, GIOCondition condition,
+ gpointer user_data)
+{
+ struct near_adapter *adapter = user_data;
+ unsigned char buf[1024];
+ struct near_adapter_ioreq *req;
+ GList *first;
+ int sk;
+ size_t len;
+
+ DBG("condition 0x%x", condition);
+
+ if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ near_error("Error while reading NFC bytes");
+
+ adapter_flush_rx(adapter, -EIO);
+ adapter->watch = 0;
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(channel);
+ first = g_list_first(adapter->ioreq_list);
+ if (first == NULL)
+ return TRUE;
+
+ req = first->data;
+ if (req == NULL)
+ goto out;
+
+ len = recv(sk, buf, sizeof(buf), 0);
+ req->cb(buf, len, req->data);
+
+out:
+ adapter->ioreq_list = g_list_remove(adapter->ioreq_list, first);
+ g_free(req);
+
+ return TRUE;
+}
+
+int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol)
{
struct near_adapter *adapter;
struct near_target *target;
- uint32_t protocols;
struct sockaddr_nfc addr;
int err, sock;
@@ -438,15 +506,14 @@ int near_adapter_connect(uint32_t idx, uint32_t target_idx)
if (target == NULL)
return -ENOLINK;
- protocols = __near_target_get_protocols(target);
-
- sock = socket(AF_NFC, SOCK_SEQPACKET, protocols);
+ sock = socket(AF_NFC, SOCK_SEQPACKET, NFC_SOCKPROTO_RAW);
if (sock == -1)
return sock;
addr.sa_family = AF_NFC;
addr.dev_idx = idx;
addr.target_idx = target_idx;
+ addr.nfc_protocol = protocol;
err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err) {
@@ -457,6 +524,17 @@ int near_adapter_connect(uint32_t idx, uint32_t target_idx)
adapter->active_target = target;
adapter->active_sock = sock;
+ if (adapter->channel == NULL)
+ adapter->channel = g_io_channel_unix_new(adapter->active_sock);
+
+ g_io_channel_set_flags(adapter->channel, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_close_on_unref(adapter->channel, TRUE);
+
+ if (adapter->watch == 0)
+ adapter->watch = g_io_add_watch(adapter->channel,
+ G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+ adapter_recv_event, adapter);
+
return 0;
}
@@ -473,12 +551,66 @@ int near_adapter_disconnect(uint32_t idx)
if (adapter->active_sock == -1)
return -ENOLINK;
+ if (adapter->watch > 0) {
+ g_source_remove(adapter->watch);
+ adapter->watch = 0;
+ }
+
+ adapter->channel = NULL;
close(adapter->active_sock);
adapter->active_sock = -1;
+ adapter_flush_rx(adapter, -ENOLINK);
return 0;
}
+int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length,
+ near_recv cb, void *data)
+{
+ struct near_adapter *adapter;
+ struct near_adapter_ioreq *req = NULL;
+ int err;
+
+ DBG("idx %d", idx);
+
+ adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
+ if (adapter == NULL)
+ return -ENODEV;
+
+ if (adapter->active_sock == -1)
+ return -ENOLINK;
+
+ if (cb != NULL && adapter->watch != 0) {
+ req = g_try_malloc0(sizeof(*req));
+ if (req == NULL)
+ return -ENOMEM;
+
+ req->target_idx = __near_target_get_idx(adapter->active_target);
+ req->cb = cb;
+ req->data = data;
+
+ adapter->ioreq_list =
+ g_list_append(adapter->ioreq_list, req);
+ }
+
+ err = send(adapter->active_sock, buf, length, 0);
+ if (err < 0)
+ goto out_err;
+
+ return err;
+
+out_err:
+ if (req != NULL) {
+ GList *last = g_list_last(adapter->ioreq_list);
+
+ g_free(req);
+ adapter->ioreq_list =
+ g_list_delete_link(adapter->ioreq_list, last);
+ }
+
+ return err;
+}
+
int __near_adapter_init(void)
{
DBG("");