#include #include #include #include #include #include #include #include "nfcommander.h" #include "reader.h" #include "config.h" #include "command.h" #define MAX_PLUGIN_PATH NAME_MAX #define DEFAULT_POLL_TIMEOUT 1000 /* * Pointers into the tags array are used as stable identifiers * across the different NFCommander modules, so this array should * never be reallocated dynamically. * * As most readers have a limited number of tags they can power in * the field, this should not pose a practical limitation. */ #define MAX_TAGS 5 #define FLAG_PRESENT 16 #define FLAG_ACTIVE 32 #define FLAG_WRITE_FULL 64 #define FLAG_WRITE_DYNAMIC 128 static void* reader_module = NULL; static reader_plugin_handle reader_backend_handle = NULL; static reader_plugin_scan reader_backend_scan = NULL; static int timer_fd = -1; static struct { uint8_t flags; nfc_tag_info_t info; } tags[MAX_TAGS] = { 0 }; static void tag_data_sanitize(nfc_tag_info_t* tag){ size_t n = 0; for(n = 0; n < tag->static_length; n++){ if(tag->static_data[n] && !isprint(tag->static_data[n])){ tag->static_data[n] = ' '; } } for(n = 0; n < tag->dynamic_length; n++){ if(tag->dynamic_data[n] && !isprint(tag->dynamic_data[n])){ tag->dynamic_data[n] = ' '; } } } static int reader_init_backend(){ char plugin[MAX_PLUGIN_PATH] = ""; char* reader = config_get("nfc", "reader"); if(!reader){ printf("No reader plugin configured\n"); return -1; } snprintf(plugin, sizeof(plugin), "./reader_%s.so", reader); reader_module = dlopen(plugin, RTLD_NOW); if(!reader_module){ printf("Failed to load reader plugin %s: %s\n", plugin, dlerror()); return -1; } reader_backend_handle = dlsym(reader_module, "handle"); reader_backend_scan = dlsym(reader_module, "scan"); //call initializer reader_plugin_init init = dlsym(reader_module, "init"); if(!reader_backend_scan || !init || init()){ printf("Failed to initialize the reader\n"); return 1; } return 0; } int reader_init(){ //initialize poll timer size_t poll_timeout = DEFAULT_POLL_TIMEOUT; struct itimerspec timer_cfg = { 0 }; if(config_get("nfc", "interval")){ poll_timeout = strtoul(config_get("nfc", "interval"), NULL, 10); } timer_cfg.it_value.tv_sec = timer_cfg.it_interval.tv_sec = poll_timeout / 1000; timer_cfg.it_value.tv_nsec = timer_cfg.it_interval.tv_nsec = (poll_timeout % 1000) * 1e6; //create timerfd timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if(timer_fd < 0){ printf("Failed to create timer fd\n"); return 1; } if(timerfd_settime(timer_fd, 0, &timer_cfg, NULL)){ printf("Failed to set timer\n"); return 1; } core_manage_fd(timer_fd, 1, system_reader); return reader_init_backend(); } static void reader_print_tag(nfc_tag_info_t tag){ size_t n = 0; char* type = "UNST"; switch(tag.type){ case tag_unset: type = "UNST"; break; case tag_unknown: type = "UNKN"; break; case tag_mifare1: type = "MIFR"; break; case tag_ntag: type = "NTAG"; break; case tag_desfire: type = "DESF"; break; } printf("[%s:%lu ", type, tag.bytes_available); if(tag.static_length){ printf("D:%lu:%lu ", tag.static_length, tag.dynamic_length); } for(n = 0; n < tag.uid_length; n++){ printf("%02X", tag.uid[n]); } printf("]"); } static void reader_expire(){ size_t n = 0; for(n = 0; n < MAX_TAGS; n++){ tags[n].flags &= ~(FLAG_PRESENT); } } static void tag_info_free(nfc_tag_info_t* tag){ free(tag->static_data); tag->static_data = NULL; tag->static_length = 0; free(tag->dynamic_data); tag->dynamic_data = NULL; tag->dynamic_length = 0; } static int reader_process(){ size_t n = 0; for(n = 0; n < MAX_TAGS; n++){ if(tags[n].flags & FLAG_PRESENT){ if(!(tags[n].flags & FLAG_ACTIVE)){ //new tag tags[n].flags |= FLAG_ACTIVE; if(tags[n].flags & FLAG_TAG_LOCKED){ printf("Slot %2lu: ", n); reader_print_tag(tags[n].info); printf(" locked, unusable\n"); return 0; } if(tags[n].flags & FLAG_TAG_UNPROGRAMMED){ printf("Slot %2lu: ", n); reader_print_tag(tags[n].info); printf(" unprogrammed\n"); //TODO return 0; } if(tags[n].flags & FLAG_TAG_DATA_VALID){ printf("Slot %2lu: ", n); reader_print_tag(tags[n].info); printf(" valid\n"); command_start(&(tags[n].info)); return 0; } printf("Tag detected (slot %lu), but no state flags set by reader\n", n); } } else{ if(tags[n].flags & FLAG_ACTIVE){ //tag was removed - free any allocated members command_stop(&(tags[n].info)); tag_info_free(&(tags[n].info)); printf("Tag in slot %lu removed\n", n); } //reset all flags tags[n].flags = 0; } } return 0; } int reader_tag_present(uint8_t flags, nfc_tag_info_t* tag){ size_t n = 0; //sanitize input flags flags &= (TAG_STATUS_FLAGS); //check if tag already known for(n = 0; n < MAX_TAGS; n++){ if((tags[n].flags & (FLAG_PRESENT | FLAG_ACTIVE)) && tags[n].info.uid_length == tag->uid_length && !memcmp(tags[n].info.uid, tag->uid, tag->uid_length)){ //mark still present tags[n].flags |= FLAG_PRESENT; //if full data submitted, copy in and set flags if(!(tags[n].flags & TAG_STATUS_FLAGS)){ if(flags & TAG_STATUS_FLAGS){ memcpy(&(tags[n].info), tag, sizeof(nfc_tag_info_t)); tags[n].flags |= flags; tag_data_sanitize(&(tags[n].info)); return 0; } //if data has not yet been fully read, request a full read return TAG_READ_REQUESTED; } if(tags[n].flags & (FLAG_WRITE_FULL | FLAG_WRITE_DYNAMIC)){ flags = tags[n].flags; printf("Requesting %s data write on tag %lu\n", (flags & FLAG_WRITE_FULL) ? "full" : "partial", n); tags[n].flags &= ~(FLAG_WRITE_FULL | FLAG_WRITE_DYNAMIC); tag_data_sanitize(&(tags[n].info)); //this is kinda dangerous as we hand the reader backend our pointers memcpy(tag, &(tags[n].info), sizeof(nfc_tag_info_t)); return (flags & FLAG_WRITE_FULL) ? TAG_WRITE_FULL : TAG_WRITE_DYNAMIC; } return 0; } } //if not, add it for(n = 0; n < MAX_TAGS; n++){ if(!tags[n].flags){ tags[n].flags |= FLAG_PRESENT | flags; memcpy(&(tags[n].info), tag, sizeof(nfc_tag_info_t)); return (tags[n].flags & TAG_STATUS_FLAGS) ? 0 : TAG_READ_REQUESTED; } } //realistically, this will never happen as most readers barely //provide enough energy to power two tags in the field printf("Maximum of active tags reached, ignored\n"); return 0; } int reader_write(nfc_tag_info_t* tag, uint8_t flags){ size_t n = 0; for(n = 0; n < MAX_TAGS; n++){ if(tag == &(tags[n].info)){ if(flags & TAG_WRITE_FULL){ tags[n].flags |= FLAG_WRITE_FULL; } if(flags & TAG_WRITE_DYNAMIC){ tags[n].flags |= FLAG_WRITE_DYNAMIC; } } } return 0; } int reader_handle(int fd){ uint8_t buffer[1024]; if(fd == timer_fd){ //acknowledge timer read(fd, buffer, sizeof(buffer)); //mark all tags expired reader_expire(); //scan all tags if(reader_backend_scan()){ return 1; } //handle changes in active set return reader_process(); } if(reader_backend_handle){ return reader_backend_handle(fd); } return 0; } void reader_free(){ size_t n = 0; if(reader_module){ dlclose(reader_module); reader_backend_handle = NULL; reader_backend_scan = NULL; reader_module = NULL; } for(n = 0; n < MAX_TAGS; n++){ tag_info_free(&(tags[n].info)); } core_manage_fd(timer_fd, 0, system_reader); close(timer_fd); timer_fd = -1; }