/* * Copyright (C) 2018 Konsulko Group * Author: Matt Ranostay * * 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 #include #include #include #include #include #include #include #include #include #include #define AFB_BINDING_VERSION 3 #include #include "afm-nfc-common.h" #define STR(X) STR1(X) #define STR1(X) #X static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static afb_event_t presence_event; static void __attribute__((unused)) dbg_dump_adapter_info(neardal_adapter *adp) { #define DBG_ADP(__x) if (adp->__x) \ AFB_API_DEBUG(afbBindingV3root, "adapter %s=%s\n", STR(__x), (adp->__x)) DBG_ADP(name); DBG_ADP(mode); } static void __attribute__((unused)) dbg_dump_tag_info(neardal_tag *tag) { #define DBG_TAG(__x) if (tag->__x) \ AFB_API_DEBUG(afbBindingV3root, "tag %s=%s\n", STR(__x), (tag->__x)) DBG_TAG(name); DBG_TAG(type); } static void __attribute__((unused)) dbg_dump_record_content(neardal_record *rcd) { #define DBG_RECORD(__x) if (rcd->__x) \ AFB_API_DEBUG(afbBindingV3root, "record %s=%s", STR(__x), (rcd->__x)) DBG_RECORD(authentication); DBG_RECORD(representation); DBG_RECORD(passphrase); DBG_RECORD(encryption); DBG_RECORD(encoding); DBG_RECORD(language); DBG_RECORD(action); DBG_RECORD(mime); DBG_RECORD(type); DBG_RECORD(ssid); DBG_RECORD(uri); } static void adapter_added(const char *aname, void *ptr) { nfc_binding_data *data = ptr; neardal_adapter*adapter; int ret; if (!aname || !ptr) return; ret = neardal_get_adapter_properties(aname, &adapter); if (ret != NEARDAL_SUCCESS) { AFB_API_ERROR(afbBindingV3root, "'%s', cant get properties!\n", aname); return; } dbg_dump_adapter_info(adapter); if (data->adapter) { AFB_API_DEBUG(afbBindingV3root, "ignoring new if: '%s', using '%s'\n", aname, data->adapter); return; } data->adapter = g_strdup(aname); } static void adapter_removed(const char *aname, void *ptr) { nfc_binding_data *data = ptr; if (!data || !data->adapter) return; if (!strcmp(data->adapter, aname)) { g_free(data->adapter); data->adapter = NULL; } AFB_API_DEBUG(afbBindingV3root, "'%s removed'\n", aname); g_main_loop_quit(data->loop); } static void adapter_prop_changed(char *aname, char *prop, void *value, void *cookie) { nfc_binding_data *data; unsigned int val; int ret = NEARDAL_SUCCESS; if (!value || !prop || !aname || !cookie) return; data = cookie; val = GPOINTER_TO_UINT(value); AFB_API_DEBUG(afbBindingV3root, "'%s' -> %s=0x%X\n", aname, prop, val); if (!strcmp(prop, "Polling") && !val) ret = neardal_start_poll(data->adapter); if ((ret != NEARDAL_SUCCESS) && (ret != NEARDAL_ERROR_POLLING_ALREADY_ACTIVE)) { AFB_API_ERROR(afbBindingV3root, "failed to start polling %s : ret=0x%x (%s)\n", data->adapter, ret, neardal_error_get_text(ret)); goto out; } if (!strcmp(prop, "Powered") && !val) ret = neardal_set_adapter_property(data->adapter, NEARD_ADP_PROP_POWERED, GINT_TO_POINTER(1)); if (ret != NEARDAL_SUCCESS) { AFB_API_ERROR(afbBindingV3root, "failed to power %s : ret=0x%x (%s)\n", data->adapter, ret, neardal_error_get_text(ret)); goto out; } if (!strcmp(prop, "Mode")) AFB_API_DEBUG(afbBindingV3root, "%s, %s, %s \n", aname, prop, (char*)value); out: return; } static void record_found(const char *tag_name, void *ptr) { json_object *jresp, *jdict, *jtemp; nfc_binding_data *data = ptr; neardal_record *record; GVariant *value, *v = NULL; GVariantIter iter; char *s = NULL; int ret; if (!tag_name || !ptr) return; ret = neardal_get_record_properties(tag_name, &record); if (ret != NEARDAL_SUCCESS) { AFB_API_ERROR(afbBindingV3root, "read record properties for %s tag, err:0x%x (%s)", tag_name, ret, neardal_error_get_text(ret)); return; } value = neardal_record_to_g_variant(record); jresp = json_object_new_object(); jdict = json_object_new_object(); g_variant_iter_init(&iter, value); json_object_object_add(jresp, "status", json_object_new_string("detected")); while (g_variant_iter_loop(&iter, "{sv}", &s, &v)) { gchar *str = g_variant_print(v, 0); str[strlen(str) - 1] = '\0'; AFB_API_DEBUG(afbBindingV3root, "%s tag, record %s= %s", tag_name, s, str); json_object_object_add(jdict, s, json_object_new_string(str + 1)); g_free(str); } dbg_dump_record_content(record); neardal_free_record(record); /* * get record dictionary and look for 'Representation' field which should * contain the uid value the identity agent needs. * */ if (json_object_object_get_ex(jdict, "Representation", &jtemp)) { const char *uid = json_object_get_string(jtemp); pthread_mutex_lock(&mutex); data->jresp = jresp; json_object_object_add(jresp, "uid", json_object_new_string(uid)); pthread_mutex_unlock(&mutex); afb_event_push(presence_event, jresp); AFB_API_DEBUG(afbBindingV3root, "sent presence event, record content %s, tag %s", uid, tag_name); } return; } static void tag_found(const char *tag_name, void *ptr) { neardal_tag *tag; int ret; if (!tag_name) return; ret = neardal_get_tag_properties(tag_name, &tag); if (ret != NEARDAL_SUCCESS) { AFB_API_ERROR(afbBindingV3root, "read tag %s properties, err:0x%x (%s)", tag_name, ret, neardal_error_get_text(ret)); return; } dbg_dump_tag_info(tag); neardal_free_tag(tag); return; } static void tag_lost(const char *tag_name, void *ptr) { nfc_binding_data *data = ptr; json_object *jresp; if (!tag_name || !ptr) return; pthread_mutex_lock(&mutex); if (data->jresp) { json_object_put(data->jresp); data->jresp = NULL; } pthread_mutex_unlock(&mutex); jresp = json_object_new_object(); json_object_object_add(jresp, "status", json_object_new_string("removed")); afb_event_push(presence_event, jresp); AFB_API_DEBUG(afbBindingV3root, "%s tag removed, quit loop", tag_name); g_main_loop_quit(data->loop); } static void install_adapter_callbacks(void *usrptr) { neardal_set_cb_adapter_added(adapter_added, usrptr); neardal_set_cb_adapter_removed(adapter_removed, usrptr); neardal_set_cb_adapter_property_changed(adapter_prop_changed, usrptr); AFB_API_DEBUG(afbBindingV3root, "NFC adapter callbacks registered\n"); } static void install_tag_callbacks(void *usrptr) { neardal_set_cb_tag_found(tag_found, usrptr); neardal_set_cb_tag_lost(tag_lost, usrptr); neardal_set_cb_record_found(record_found, usrptr); AFB_API_DEBUG(afbBindingV3root, "NFC tag callbacks registered\n"); } static void __attribute__((unused)) remove_adapter_callbacks(void *usrptr) { neardal_set_cb_adapter_added(NULL, usrptr); neardal_set_cb_adapter_removed(NULL, usrptr); neardal_set_cb_adapter_property_changed(NULL, usrptr); AFB_API_DEBUG(afbBindingV3root, "NFC adapter callbacks removed\n"); } static void remove_tag_callbacks(void *usrptr) { neardal_set_cb_tag_found(NULL, usrptr); neardal_set_cb_tag_lost(NULL, usrptr); neardal_set_cb_record_found(NULL, usrptr); AFB_API_DEBUG(afbBindingV3root, "NFC tag callbacks removed\n"); } static void *poll_adapter(void *ptr) { neardal_adapter* nfcdev = NULL; nfc_binding_data *data = ptr; int ret; if (!data) return NULL; install_adapter_callbacks(data); ret = neardal_get_adapter_properties(data->adapter, &nfcdev); if (ret != NEARDAL_SUCCESS) { AFB_API_ERROR(afbBindingV3root, "configuring '%s', cant get properties!\n", data->adapter); goto out; } if (nfcdev->powered) goto ploop; ret = neardal_set_adapter_property(data->adapter, NEARD_ADP_PROP_POWERED, GINT_TO_POINTER(1)); if (ret != NEARDAL_SUCCESS) AFB_API_ERROR(afbBindingV3root, "failed to power up %s adapter: ret=0x%x (%s)\n", data->adapter, ret, neardal_error_get_text(ret)); ploop: neardal_free_adapter(nfcdev); data->loop = g_main_loop_new(NULL, FALSE); for (;;) { install_tag_callbacks(data); ret = neardal_start_poll(data->adapter); if ((ret == NEARDAL_SUCCESS) || (ret == NEARDAL_ERROR_POLLING_ALREADY_ACTIVE)) g_main_loop_run(data->loop); remove_tag_callbacks(data); if (data->adapter == NULL) break; } AFB_API_DEBUG(afbBindingV3root, "exiting polling loop"); out: g_free(data->adapter); g_free(data); return NULL; } static int get_adapter(nfc_binding_data *const data, unsigned int id) { char **adapters = NULL; int num_adapters, ret; if (!data) return -EINVAL; ret = neardal_get_adapters(&adapters, &num_adapters); if (ret != NEARDAL_SUCCESS) { AFB_API_DEBUG(afbBindingV3root, "failed to find adapters ret=0x%x (%s)", ret, neardal_error_get_text(ret)); return -EIO; } if (id > num_adapters - 1) { AFB_API_DEBUG(afbBindingV3root, "adapter out of range (%d - %d)", id, num_adapters); ret = -EINVAL; goto out; } data->adapter = g_strdup(adapters[id]); out: neardal_free_array(&adapters); return ret; } static int init(afb_api_t api) { nfc_binding_data *data; pthread_t thread_id; int ret; data = g_malloc0(sizeof(nfc_binding_data)); presence_event = afb_api_make_event(api, "presence"); if (!afb_event_is_valid(presence_event)) { AFB_API_ERROR(api, "Failed to create event"); g_free(data); return -EINVAL; } /* get the first adapter */ ret = get_adapter(data, 0); if (ret != NEARDAL_SUCCESS) { AFB_API_ERROR(api, "Failed to get adapter. Please start neard.\n"); g_free(data); return 0; /* ignore error if no adapter */ } AFB_API_DEBUG(api, " %s adapter found", data->adapter); afb_api_set_userdata(api, data); ret = pthread_create(&thread_id, NULL, poll_adapter, data); if (ret) { AFB_API_ERROR(api, "polling pthread creation failed"); g_free(data->adapter); g_free(data); } return 0; } static void subscribe(afb_req_t request) { const char *value = afb_req_value(request, "value"); const char *ename = afb_event_name(presence_event); if (!value || !ename) return afb_req_reply(request, NULL, "invalid", NULL); if (strcasecmp(value, ename)) return afb_req_reply(request, NULL, "invalid", NULL); if (afb_req_subscribe(request, presence_event) < 0) { AFB_REQ_ERROR(request, "subscribe to presence_event failed"); afb_req_reply(request, NULL, "failed", "Invalid event"); return; } afb_req_reply(request, NULL, NULL, NULL); } static void unsubscribe(afb_req_t request) { const char *value = afb_req_value(request, "value"); const char *ename = afb_event_name(presence_event); if (!value || !ename) return afb_req_reply(request, NULL, "invalid", NULL); if (strcasecmp(value, ename)) return afb_req_reply(request, NULL, "invalid", NULL); if (afb_req_unsubscribe(request, presence_event) < 0) { AFB_REQ_ERROR(request, "unsubscribe to presence_event failed"); afb_req_reply(request, NULL, "failed", "Invalid event"); return; } afb_req_reply(request, NULL, NULL, NULL); } static const afb_verb_t binding_verbs[] = { { .verb = "subscribe", .callback = subscribe, .info = "Subscribe to NFC events" }, { .verb = "unsubscribe", .callback = unsubscribe, .info = "Unsubscribe to NFC events" }, { } }; /* * binder API description */ const afb_binding_t afbBindingExport = { .api = "nfc", .specification = "NFC service API", .info = "AGL nfc service", .verbs = binding_verbs, .init = init, };