snep: Adapt to the latest p2p changes
authorSamuel Ortiz <sameo@linux.intel.com>
Thu, 15 Mar 2012 00:44:52 +0000 (01:44 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Thu, 15 Mar 2012 00:44:52 +0000 (01:44 +0100)
Besides the close routine, the snep code also now supports non blocking
reads and multiple clients connecting simultaneously.

plugins/snep.c

index 6bfdf9d..a31c127 100644 (file)
 /* Response codes */
 #define SNEP_RESP_CONTINUE  0x80
 #define SNEP_RESP_SUCCESS   0x81
-#define SNEP_RESP_NOT_FOUND 0x80
-#define SNEP_RESP_EXCESS    0x80
-#define SNEP_RESP_BAD_REQ   0x80
-#define SNEP_RESP_NOT_IMPL  0x80
-#define SNEP_RESP_VERSION   0x80
-#define SNEP_RESP_REJECT    0x80
+#define SNEP_RESP_NOT_FOUND 0xc0
+#define SNEP_RESP_EXCESS    0xc1
+#define SNEP_RESP_BAD_REQ   0xc2
+#define SNEP_RESP_NOT_IMPL  0xe0
+#define SNEP_RESP_VERSION   0xe1
+#define SNEP_RESP_REJECT    0xff
 
 struct p2p_snep_data {
        uint8_t *nfc_data;
        uint32_t nfc_data_length;
        uint32_t nfc_data_current_length;
        uint8_t *nfc_data_ptr;
+       uint32_t adapter_idx;
+       uint32_t target_idx;
+       near_tag_io_cb cb;
 };
 
 struct p2p_snep_req_frame {
@@ -79,7 +82,15 @@ struct p2p_snep_resp_frame {
        uint8_t info[];
 } __attribute__((packed));
 
-static struct p2p_snep_data snep_data;
+static GHashTable *snep_client_hash = NULL;
+
+static void free_snep_client(gpointer data)
+{
+       struct p2p_snep_data *snep_data = data;
+
+       g_free(snep_data->nfc_data);
+       g_free(snep_data);
+}
 
 static void snep_response_noinfo(int client_fd, uint8_t response)
 {
@@ -93,86 +104,125 @@ static void snep_response_noinfo(int client_fd, uint8_t response)
        send(client_fd, &resp, sizeof(resp), 0);
 }
 
+static void snep_close(int client_fd, int err)
+{
+       struct p2p_snep_data *snep_data;
+
+       DBG("");
+
+       snep_data = g_hash_table_lookup(snep_client_hash,
+                                       GINT_TO_POINTER(client_fd));
+       if (snep_data == NULL)
+               return;
+
+       snep_data->cb(snep_data->adapter_idx, snep_data->target_idx, err);
+
+       g_hash_table_remove(snep_client_hash, GINT_TO_POINTER(client_fd));
+}
+
 static near_bool_t snep_read_ndef(int client_fd,
-               uint32_t adapter_idx, uint32_t target_idx,
-               near_tag_io_cb cb, int ndef_length, near_bool_t allocate)
+                                       struct p2p_snep_data *snep_data)
 {
        int bytes_recv, remaining_bytes;
+       struct near_tag *tag;
 
        DBG("");
 
-       if (allocate == TRUE) {
-               g_free(snep_data.nfc_data);
+       remaining_bytes = snep_data->nfc_data_length -
+                               snep_data->nfc_data_current_length;
 
-               snep_data.nfc_data = g_try_malloc0(ndef_length + TLV_SIZE);
-               if (snep_data.nfc_data == NULL)
-                       return FALSE;
+       DBG("Remaining bytes %d", remaining_bytes);
 
-               snep_data.nfc_data[0] = TLV_NDEF;
-               snep_data.nfc_data[1] = ndef_length;
+       bytes_recv = recv(client_fd, snep_data->nfc_data_ptr, remaining_bytes,
+                                                               MSG_DONTWAIT);
+       if (bytes_recv < 0) {
+               near_error("%d %s", bytes_recv, strerror(errno));
 
-               snep_data.nfc_data_length = ndef_length + TLV_SIZE;
-               snep_data.nfc_data_current_length = TLV_SIZE;
-               snep_data.nfc_data_ptr = snep_data.nfc_data + TLV_SIZE;
-       }
+               /* Some more data should show up */
+               if (errno == EAGAIN)
+                       return TRUE;
 
-       remaining_bytes = snep_data.nfc_data_length - snep_data.nfc_data_current_length;
-       bytes_recv = recv(client_fd, snep_data.nfc_data_ptr, remaining_bytes, 0);
-       if (bytes_recv < 0) {
-               near_error("Could not read SNEP NDEF buffer %d", bytes_recv);
-               return FALSE;
+               goto out;
        }
 
        DBG("Received %d bytes", bytes_recv);
 
-       snep_data.nfc_data_current_length += bytes_recv;
-
-       if (snep_data.nfc_data_length == snep_data.nfc_data_current_length) {
-               struct near_tag *tag;
-
-               snep_data.nfc_data_current_length = 0;
-               snep_response_noinfo(client_fd, SNEP_RESP_SUCCESS);
-               tag = near_target_add_tag(adapter_idx, target_idx,
-                                       snep_data.nfc_data,
-                                       snep_data.nfc_data_length);
-               if (tag == NULL) {
-                       g_free(snep_data.nfc_data);
-                       return FALSE;
-               }
-
-               near_tlv_parse(tag, cb);
-               g_free(snep_data.nfc_data);
+       snep_data->nfc_data_current_length += bytes_recv;
+       snep_data->nfc_data_ptr += bytes_recv;
 
-               return FALSE;
-       } else {
+       if (snep_data->nfc_data_length != snep_data->nfc_data_current_length) {
                snep_response_noinfo(client_fd, SNEP_RESP_CONTINUE);
 
                return TRUE;
        }
+
+       snep_response_noinfo(client_fd, SNEP_RESP_SUCCESS);
+       tag = near_target_add_tag(snep_data->adapter_idx, snep_data->target_idx,
+                                       snep_data->nfc_data,
+                                       snep_data->nfc_data_length);
+       if (tag == NULL)
+               goto out;
+
+       near_tlv_parse(tag, snep_data->cb);
+
+out:
+       g_hash_table_remove(snep_client_hash, GINT_TO_POINTER(client_fd));
+
+       return FALSE;
 }
 
 static near_bool_t snep_read(int client_fd,
                                uint32_t adapter_idx, uint32_t target_idx,
                                near_tag_io_cb cb)
 {
+       struct p2p_snep_data *snep_data;
        struct p2p_snep_req_frame frame;
        int bytes_recv;
        uint32_t ndef_length;
 
        DBG("");
 
-       /* If current length is not 0, we're waiting for a fragment */
-       if (snep_data.nfc_data_current_length > 0) {
-               return snep_read_ndef(client_fd, adapter_idx, target_idx, cb,
-                                                               0, FALSE);
-       }
+       snep_data = g_hash_table_lookup(snep_client_hash,
+                                       GINT_TO_POINTER(client_fd));
 
+       /*
+        * We already got something from this client, we should try
+        * to continue reading.
+        */
+       if (snep_data != NULL)
+               return snep_read_ndef(client_fd, snep_data);
+
+       /* TODO Try with PEEK */
        bytes_recv = recv(client_fd, &frame, sizeof(frame), 0);
        if (bytes_recv < 0) {
                near_error("Could not read SNEP frame %d", bytes_recv);
                return bytes_recv;
        }
 
+       ndef_length = GINT_FROM_BE(frame.length);
+
+       DBG("Allocating SNEP data %d", ndef_length);
+
+       snep_data = g_try_malloc0(sizeof(struct p2p_snep_data));
+       if (snep_data == NULL)
+               return FALSE;
+
+       snep_data->nfc_data = g_try_malloc0(ndef_length + TLV_SIZE);
+       if (snep_data->nfc_data == NULL)
+               return FALSE;
+
+       snep_data->nfc_data[0] = TLV_NDEF;
+       snep_data->nfc_data[1] = ndef_length;
+       snep_data->nfc_data_length = ndef_length + TLV_SIZE;
+       snep_data->nfc_data_current_length = TLV_SIZE;
+       snep_data->nfc_data_ptr = snep_data->nfc_data + TLV_SIZE;
+       snep_data->adapter_idx = adapter_idx;
+       snep_data->target_idx = target_idx;
+       snep_data->cb = cb;
+
+       g_hash_table_insert(snep_client_hash,
+                                       GINT_TO_POINTER(client_fd), snep_data);
+
        DBG("Request 0x%x", frame.request);
 
        switch (frame.request) {
@@ -182,9 +232,7 @@ static near_bool_t snep_read(int client_fd,
                snep_response_noinfo(client_fd, SNEP_RESP_NOT_IMPL);
                return FALSE;
        case SNEP_REQ_PUT:
-               ndef_length = GINT_FROM_BE(frame.length);
-               return snep_read_ndef(client_fd, adapter_idx, target_idx, cb,
-                               ndef_length, TRUE);
+               return snep_read_ndef(client_fd, snep_data);
                break;
        }
 
@@ -195,14 +243,21 @@ struct near_p2p_driver snep_driver = {
        .name = "SNEP",
        .service_name = "urn:nfc:sn:snep",
        .read = snep_read,
+       .close = snep_close,
 };
 
 int snep_init(void)
 {
+       snep_client_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_snep_client);
+
        return near_p2p_register(&snep_driver);
 }
 
 void snep_exit(void)
 {
        near_p2p_unregister(&snep_driver);
+
+       g_hash_table_destroy(snep_client_hash);
+       snep_client_hash = NULL;
 }