p2p: Initial NPP implementation
authorSamuel Ortiz <sameo@linux.intel.com>
Thu, 15 Dec 2011 17:53:02 +0000 (18:53 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Thu, 15 Dec 2011 18:16:47 +0000 (19:16 +0100)
NPP is Android's NDEF Push Protocol, running on top of LLCP.
This code allows us to fetch exported information from Adroid NFC enabled
devices.

plugins/p2p.c
src/adapter.c

index e60f183..64dbe71 100644 (file)
 #include <near/ndef.h>
 #include <near/tlv.h>
 
-static int p2p_read(uint32_t adapter_idx,
-               uint32_t target_idx, near_tag_read_cb cb)
+#define AF_NFC 39
+#define TLV_SIZE 2
+
+struct p2p_npp_channel {
+       near_tag_read_cb cb;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       int fd;
+       guint watch;
+       GIOChannel *channel;
+};
+
+struct p2p_npp_ndef_entry {
+       uint8_t action;
+       uint32_t ndef_length;
+       uint8_t ndef[];
+} __attribute__((packed));
+
+struct p2p_npp_frame {
+       uint8_t version;
+       uint32_t n_ndef;
+       struct p2p_npp_ndef_entry ndefs[];
+} __attribute__((packed));
+
+static struct p2p_npp_channel npp_server;
+
+static void npp_read_ndef(int client_fd)
+{
+       struct near_tag *tag;
+       struct p2p_npp_frame frame;
+       struct p2p_npp_ndef_entry entry;
+       int bytes_recv, n_ndef, i, ndef_length, total_ndef_length;
+       size_t tag_length;
+       uint8_t *ndefs, *nfc_data, *current_ndef;
+
+       ndefs = NULL;
+       total_ndef_length = 0;
+
+       bytes_recv = recv(client_fd, &frame, sizeof(frame), 0);
+       if (bytes_recv < 0) {
+               near_error("Could not read NPP frame %d", bytes_recv);
+               return;
+       }
+
+       n_ndef = GINT_FROM_BE(frame.n_ndef);
+
+       DBG("version %d %d NDEFs", frame.version, n_ndef);
+
+       for (i = 0; i < n_ndef; i++) {
+               bytes_recv = recv(client_fd, &entry, sizeof(entry), 0);
+               if (bytes_recv < 0) {
+                       near_error("Could not read NPP NDEF entry %d",
+                                                               bytes_recv);
+                       break;
+               }
+
+               ndef_length = GINT_FROM_BE(entry.ndef_length);
+               total_ndef_length += ndef_length + TLV_SIZE;
+               DBG("NDEF %d length %d", i, ndef_length);
+
+               ndefs = g_try_realloc(ndefs, total_ndef_length);
+               if (ndefs == NULL) {
+                       near_error("Could not allocate NDEF buffer %d",
+                                                               bytes_recv);
+                       break;
+               }
+
+               current_ndef = ndefs + total_ndef_length
+                                       - (ndef_length + TLV_SIZE);
+               current_ndef[0] = TLV_NDEF;
+               current_ndef[1] = ndef_length;
+
+               bytes_recv = recv(client_fd, current_ndef + TLV_SIZE,
+                                       ndef_length, 0);
+               if (bytes_recv < 0) {
+                       near_error("Could not read NDEF entry %d",
+                                                       bytes_recv);
+                       break;
+               }
+       }
+
+       if (total_ndef_length == 0)
+               return;
+
+       DBG("Total NDEF length %d", total_ndef_length);
+
+       tag = near_target_add_tag(npp_server.adapter_idx,
+                                       npp_server.target_idx,
+                                       total_ndef_length);
+       if (tag == NULL) {
+               g_free(ndefs);
+               return;
+       }
+
+       for (i = 0; i < total_ndef_length; i++)
+               DBG("NDEF[%d] 0x%x", i, ndefs[i]);
+
+       nfc_data = near_tag_get_data(tag, &tag_length);
+       memcpy(nfc_data, ndefs, total_ndef_length);
+
+       near_tlv_parse(tag, npp_server.cb, nfc_data, total_ndef_length);
+
+       g_free(ndefs);
+}
+
+static gboolean npp_listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       struct sockaddr_nfc_llcp client_addr;
+       int server_fd, client_fd;
+       socklen_t client_addr_len;
+
+       DBG("condition 0x%x", condition);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               DBG("ERROR");
+       }
+
+       if (condition & G_IO_IN) {
+               server_fd = g_io_channel_unix_get_fd(channel);
+
+               client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
+               if (client_fd < 0) {
+                       DBG("accept failed %d", client_fd);
+
+                       close(server_fd);
+                       return FALSE;
+               }
+
+               DBG("client dsap %d ssap %d",
+                       client_addr.dsap, client_addr.ssap);
+
+               npp_read_ndef(client_fd);
+
+               close(client_fd);
+
+               return FALSE;
+       }
+
+       return FALSE;
+}
+
+static int npp_bind(uint32_t adapter_idx, uint32_t target_idx,
+                                               near_tag_read_cb cb)
 {
        int err;
+       struct sockaddr_nfc_llcp addr;
 
-       DBG("");
+       npp_server.adapter_idx = adapter_idx;
+       npp_server.target_idx = target_idx;
+       npp_server.cb = cb;
+       npp_server.fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP);
+       if (npp_server.fd < 0)
+               return -errno;
+
+       memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp));
+       addr.sa_family = AF_NFC;
+       addr.dev_idx = adapter_idx;
+       addr.nfc_protocol = NFC_PROTO_NFC_DEP;
+       addr.service_name_len = strlen("com.android.npp");
+       strcpy(addr.service_name, "com.android.npp");
 
-       err = near_adapter_connect(adapter_idx, target_idx, NFC_PROTO_NFC_DEP);
+       err = bind(npp_server.fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nfc_llcp));
        if (err < 0) {
-               near_error("Could not connect %d", err);
+               DBG("bind failed %d", err);
 
+               close(npp_server.fd);
                return err;
        }
 
+       err = listen(npp_server.fd, 10);
+       if (err < 0) {
+               DBG("listen failed %d", err);
+
+               close(npp_server.fd);
+               return err;
+       }
+
+       npp_server.channel = g_io_channel_unix_new(npp_server.fd);
+       g_io_channel_set_close_on_unref(npp_server.channel, TRUE);
+
+       npp_server.watch = g_io_add_watch(npp_server.channel,
+                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                               npp_listener_event,
+                               (gpointer) &npp_server.channel);
+
        return 0;
 }
 
+static int p2p_read(uint32_t adapter_idx,
+               uint32_t target_idx, near_tag_read_cb cb)
+{
+       int err;
+
+       err = npp_bind(adapter_idx, target_idx, cb);
+
+       return err;
+}
+
 static struct near_tag_driver p2p_driver = {
                .type     = NEAR_TAG_NFC_DEP,
                .read_tag = p2p_read,
index 98d1f25..c056d50 100644 (file)
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
+#include <string.h>
 
 #include <glib.h>
 
@@ -373,6 +374,12 @@ void __near_adapter_remove(struct near_adapter *adapter)
        g_hash_table_remove(adapter_hash, GINT_TO_POINTER(adapter->idx));
 }
 
+static int dep_link_up(uint32_t idx, uint32_t target_idx)
+{
+       return __near_netlink_dep_link_up(idx, target_idx,
+                                       NFC_COMM_ACTIVE, NFC_RF_INITIATOR);
+}
+
 static void tag_read_cb(uint32_t adapter_idx, int status)
 {
        if (status < 0)
@@ -407,6 +414,9 @@ int __near_adapter_add_target(uint32_t idx, uint32_t target_idx,
 
        __near_tag_read(target, tag_read_cb);
 
+       if (protocols & NFC_PROTO_NFC_DEP_MASK)
+               dep_link_up(idx, target_idx);
+
        return 0;
 }