/* * 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 3 #include <afb/afb-binding.h> #include "afm-nfc-common.h" #define WAIT_FOR_REMOVE(dev) { while (0 == nfc_initiator_target_is_present(dev, NULL)) {} } static afb_event_t 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 *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, nfc_binding_data *data) { 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)); if (data->jresp) { json_object_put(data->jresp); data->jresp = NULL; } json_object_get(jresp); data->jresp = jresp; afb_event_push(presence_event, jresp); } static void *nfc_loop_thread(void *ptr) { nfc_binding_data *data = ptr; while (1) { nfc_target nt; json_object *jresp; int res = nfc_initiator_poll_target(data->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, data); pthread_mutex_unlock(&mutex); WAIT_FOR_REMOVE(data->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)); if (data->jresp) { json_object_put(data->jresp); data->jresp = NULL; } afb_event_push(presence_event, jresp); pthread_mutex_unlock(&mutex); } nfc_close(data->dev); nfc_exit(data->ctx); free(data); return NULL; } static nfc_binding_data *get_libnfc_instance() { nfc_context *ctx = NULL; nfc_device *dev = NULL; nfc_binding_data *data; nfc_init(&ctx); dev = nfc_open(ctx, NULL); if (dev == NULL) { AFB_ERROR("Cannot get context for libnfc"); nfc_exit(ctx); return NULL; } if (nfc_initiator_init(dev) < 0) { AFB_ERROR("Cannot get initiator mode from libnfc"); nfc_close(dev); nfc_exit(ctx); return NULL; } data = malloc(sizeof(nfc_binding_data)); if (data) { data->ctx = ctx; data->dev = dev; } return data; } static int init(afb_api_t api) { pthread_t thread_id; nfc_binding_data *data = get_libnfc_instance(); presence_event = afb_daemon_make_event("presence"); if (data) { afb_api_set_userdata(api, data); return pthread_create(&thread_id, NULL, nfc_loop_thread, data); } return -ENODEV; } static void subscribe(afb_req_t request) { const char *value = afb_req_value(request, "value"); afb_api_t api = afb_req_get_api(request); nfc_binding_data *data = afb_api_get_userdata(api); 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); if (data && data->jresp) { json_object_get(data->jresp); afb_event_push(presence_event, data->jresp); } pthread_mutex_unlock(&mutex); return; } afb_req_fail(request, "failed", "Invalid event"); } static void unsubscribe(afb_req_t 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_v3 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_v3 afbBindingV3 = { .api = "nfc", .specification = "NFC service API", .verbs = binding_verbs, .init = init, };