diff options
Diffstat (limited to 'binding/afm-nfc-binding.c')
-rw-r--r-- | binding/afm-nfc-binding.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/binding/afm-nfc-binding.c b/binding/afm-nfc-binding.c new file mode 100644 index 0000000..c92c36c --- /dev/null +++ b/binding/afm-nfc-binding.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2018 Konsulko Group + * Author: Matt Ranostay <matt.ranostay@konsulko.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * TODO: add support for NFC p2p transactions + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <pthread.h> +#include <glib.h> +#include <glib-object.h> +#include <json-c/json.h> +#include <nfc/nfc.h> +#include <nfc/nfc-types.h> + +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define WAIT_FOR_REMOVE(dev) { while (0 == nfc_initiator_target_is_present(dev, NULL)) {} } + +static struct afb_event presence_event; +static char *current_uid = NULL; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static const nfc_modulation modulations[] = { + { .nmt = NMT_ISO14443A, .nbr = NBR_106 }, +}; + +static char *to_hex_string(unsigned char *data, size_t size) +{ + char *buffer = malloc((2 * size) + 1); + char *tmp = buffer; + int i; + + if (buffer == NULL) + return buffer; + + for (i = 0; i < size; i++) { + tmp += sprintf(tmp, "%.2x", data[i]); + } + + return buffer; +} + +static char *get_tag_uid(nfc_target *nt) +{ + if (nt->nm.nmt == NMT_ISO14443A) + return to_hex_string((unsigned char *) &nt->nti.nai.abtUid, nt->nti.nai.szUidLen); + + return NULL; +} + +static void send_detect_event(char *current_id) +{ + json_object *jresp; + + if (current_id == NULL) + return; + + jresp = json_object_new_object(); + + json_object_object_add(jresp, "status", json_object_new_string("detected")); + json_object_object_add(jresp, "uid", json_object_new_string(current_uid)); + + afb_event_push(presence_event, jresp); +} + +static void *nfc_loop_thread(void *ptr) +{ + nfc_context *ctx = NULL; + nfc_device *dev = NULL; + + nfc_init(&ctx); + + dev = nfc_open(ctx, NULL); + + if (dev == NULL) { + AFB_ERROR("Cannot get context for libnfc"); + nfc_exit(ctx); + exit(EXIT_FAILURE); + } + + if (nfc_initiator_init(dev) < 0) { + AFB_ERROR("Cannot get initiator mode from libnfc"); + nfc_close(dev); + nfc_exit(ctx); + exit(EXIT_FAILURE); + } + + while (1) { + nfc_target nt; + json_object *jresp; + int res = nfc_initiator_poll_target(dev, modulations, ARRAY_SIZE(modulations), 0xff, 2, &nt); + + if (res < 0) + break; + + pthread_mutex_lock(&mutex); + + current_uid = get_tag_uid(&nt); + send_detect_event(current_uid); + + pthread_mutex_unlock(&mutex); + + WAIT_FOR_REMOVE(dev); + + pthread_mutex_lock(&mutex); + + jresp = json_object_new_object(); + json_object_object_add(jresp, "status", json_object_new_string("removed")); + json_object_object_add(jresp, "uid", json_object_new_string(current_uid)); + afb_event_push(presence_event, jresp); + + free(current_uid); + current_uid = NULL; + + pthread_mutex_unlock(&mutex); + } + + nfc_close(dev); + nfc_exit(ctx); + + return NULL; +} + +static int init() +{ + pthread_t thread_id; + + presence_event = afb_daemon_make_event("presence"); + + return pthread_create(&thread_id, NULL, nfc_loop_thread, NULL); +} + +static void subscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + + if (value && !strcasecmp(value, "presence")) { + afb_req_subscribe(request, presence_event); + afb_req_success(request, NULL, NULL); + + // send initial tag if exists + pthread_mutex_lock(&mutex); + send_detect_event(current_uid); + pthread_mutex_unlock(&mutex); + + return; + } + + afb_req_fail(request, "failed", "Invalid event"); +} + +static void unsubscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + + if (value && !strcasecmp(value, "presence")) { + afb_req_unsubscribe(request, presence_event); + afb_req_success(request, NULL, NULL); + return; + } + + afb_req_fail(request, "failed", "Invalid event"); +} + +static const struct afb_verb_v2 binding_verbs[] = { + { .verb = "subscribe", .callback = subscribe, .info = "Subscribe to NFC events" }, + { .verb = "unsubscribe", .callback = unsubscribe, .info = "Unsubscribe to NFC events" }, + { } +}; + +/* + * binder API description + */ +const struct afb_binding_v2 afbBindingV2 = { + .api = "nfc", + .specification = "NFC service API", + .verbs = binding_verbs, + .init = init, +}; |