| /* |
| * |
| * neard - Near Field Communication manager |
| * |
| * Copyright (C) 2011 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #include <glib.h> |
| |
| #include <gdbus.h> |
| |
| #include "near.h" |
| |
| #define TYPE3_IDM_LEN 8 |
| #define TYPE3_ATTR_BLOCK_SIZE 16 |
| |
| struct near_tag { |
| char *path; |
| |
| uint32_t adapter_idx; |
| uint32_t target_idx; |
| |
| uint32_t protocol; |
| uint32_t type; |
| enum near_tag_sub_type sub_type; |
| enum near_tag_memory_layout layout; |
| bool readonly; |
| |
| uint8_t nfcid[NFC_MAX_NFCID1_LEN]; |
| uint8_t nfcid_len; |
| |
| uint8_t iso15693_dsfid; |
| uint8_t iso15693_uid_len; |
| uint8_t iso15693_uid[NFC_MAX_ISO15693_UID_LEN]; |
| |
| size_t data_length; |
| uint8_t *data; |
| |
| uint32_t next_record; |
| GList *records; |
| bool blank; |
| |
| /* Tag specific structures */ |
| struct { |
| uint8_t IDm[TYPE3_IDM_LEN]; |
| uint8_t attr[TYPE3_ATTR_BLOCK_SIZE]; |
| uint8_t ic_type; |
| } t3; |
| |
| struct { |
| uint16_t max_ndef_size; |
| uint16_t c_apdu_max_size; |
| uint16_t r_apdu_max_size; |
| uint16_t file_id; |
| } t4; |
| |
| struct { |
| uint8_t blk_size; |
| uint8_t num_blks; |
| } t5; |
| |
| DBusMessage *write_msg; /* Pending write message */ |
| struct near_ndef_message *write_ndef; |
| }; |
| |
| static DBusConnection *connection = NULL; |
| |
| static GHashTable *tag_hash; |
| |
| static GSList *driver_list = NULL; |
| |
| struct near_tag *near_tag_get_tag(uint32_t adapter_idx, uint32_t target_idx) |
| { |
| struct near_tag *tag; |
| char *path; |
| |
| path = g_strdup_printf("%s/nfc%u/tag%u", NFC_PATH, |
| adapter_idx, target_idx); |
| if (!path) |
| return NULL; |
| |
| tag = g_hash_table_lookup(tag_hash, path); |
| g_free(path); |
| |
| /* TODO refcount */ |
| return tag; |
| } |
| |
| static void append_records(DBusMessageIter *iter, void *user_data) |
| { |
| struct near_tag *tag = user_data; |
| GList *list; |
| |
| DBG(""); |
| |
| for (list = tag->records; list; list = list->next) { |
| struct near_ndef_record *record = list->data; |
| char *path; |
| |
| path = __near_ndef_record_get_path(record); |
| if (!path) |
| continue; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, |
| &path); |
| } |
| } |
| |
| static const char *type_string(struct near_tag *tag) |
| { |
| const char *type; |
| |
| DBG("type 0x%x", tag->type); |
| |
| switch (tag->type) { |
| case NFC_PROTO_JEWEL: |
| type = "Type 1"; |
| break; |
| |
| case NFC_PROTO_MIFARE: |
| type = "Type 2"; |
| break; |
| |
| case NFC_PROTO_FELICA: |
| type = "Type 3"; |
| break; |
| |
| case NFC_PROTO_ISO14443: |
| type = "Type 4A"; |
| break; |
| |
| case NFC_PROTO_ISO14443_B: |
| type = "Type 4B"; |
| break; |
| |
| case NFC_PROTO_ISO15693: |
| type = "Type 5"; |
| break; |
| |
| default: |
| type = NULL; |
| near_error("Unknown tag type 0x%x", tag->type); |
| break; |
| } |
| |
| return type; |
| } |
| |
| static uint8_t uid_array(struct near_tag *tag, uint8_t **uid) |
| { |
| if (tag->nfcid_len) { |
| DBG("NFCID: "); |
| for(int i = 0; i < tag->nfcid_len; i++) |
| DBG("%x", tag->nfcid[i]); |
| |
| *uid = tag->nfcid; |
| |
| return tag->nfcid_len; |
| } else if (tag->iso15693_uid_len) { |
| DBG("ISO-UID: "); |
| for(int i = 0; i < tag->iso15693_uid_len; i++) |
| DBG("%x", tag->iso15693_uid[i]); |
| |
| *uid = tag->iso15693_uid; |
| |
| return tag->iso15693_uid_len; |
| } |
| |
| return 0; |
| } |
| |
| static const char *protocol_string(struct near_tag *tag) |
| { |
| const char *protocol; |
| |
| DBG("protocol 0x%x", tag->protocol); |
| |
| switch (tag->protocol) { |
| case NFC_PROTO_FELICA_MASK: |
| protocol = "Felica"; |
| break; |
| |
| case NFC_PROTO_MIFARE_MASK: |
| protocol = "MIFARE"; |
| break; |
| |
| case NFC_PROTO_JEWEL_MASK: |
| protocol = "Jewel"; |
| break; |
| |
| case NFC_PROTO_ISO14443_MASK: |
| case NFC_PROTO_ISO14443_B_MASK: |
| protocol = "ISO-DEP"; |
| break; |
| |
| case NFC_PROTO_ISO15693_MASK: |
| protocol = "ISO-15693"; |
| break; |
| |
| default: |
| near_error("Unknown tag protocol 0x%x", tag->protocol); |
| protocol = NULL; |
| } |
| |
| return protocol; |
| } |
| |
| static gboolean property_get_type(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct near_tag *tag = user_data; |
| const char *type; |
| |
| type = type_string(tag); |
| if (!type) |
| return FALSE; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type); |
| |
| return TRUE; |
| } |
| |
| static gboolean property_get_uid(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct near_tag *tag = user_data; |
| DBusMessageIter entry; |
| uint8_t *uid; |
| uint8_t len; |
| |
| len = uid_array(tag, &uid); |
| if (!uid || !len) |
| return FALSE; |
| |
| dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, |
| "{y}", &entry); |
| |
| for (int i = 0; i < len; i++) |
| dbus_message_iter_append_basic(&entry, DBUS_TYPE_BYTE, |
| (void *)&uid[i]); |
| |
| dbus_message_iter_close_container(iter, &entry); |
| |
| return TRUE; |
| } |
| |
| static gboolean property_get_protocol(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct near_tag *tag = user_data; |
| const char *protocol; |
| |
| protocol = protocol_string(tag); |
| if (!protocol) |
| return FALSE; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &protocol); |
| |
| return TRUE; |
| } |
| |
| static gboolean property_get_readonly(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct near_tag *tag = user_data; |
| dbus_bool_t readonly; |
| |
| readonly = tag->readonly; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &readonly); |
| |
| return TRUE; |
| } |
| |
| static gboolean property_get_adapter(const GDBusPropertyTable *property, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct near_tag *tag = user_data; |
| struct near_adapter *adapter; |
| const char *path; |
| |
| adapter = __near_adapter_get(tag->adapter_idx); |
| if (!adapter) |
| return FALSE; |
| |
| path = __near_adapter_get_path(adapter); |
| if (!path) |
| return FALSE; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); |
| |
| return TRUE; |
| |
| } |
| |
| static void tag_read_cb(uint32_t adapter_idx, uint32_t target_idx, int status) |
| { |
| struct near_tag *tag; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| |
| if (!tag) |
| return; |
| |
| if (tag->write_msg) { |
| dbus_message_unref(tag->write_msg); |
| tag->write_msg = NULL; |
| } |
| |
| __near_adapter_start_check_presence(adapter_idx, target_idx); |
| } |
| |
| static void write_cb(uint32_t adapter_idx, uint32_t target_idx, int status) |
| { |
| struct near_tag *tag; |
| DBusConnection *conn; |
| DBusMessage *reply; |
| |
| DBG("Write status %d", status); |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| return; |
| |
| conn = near_dbus_get_connection(); |
| if (!conn) |
| goto out; |
| |
| if (status != 0) { |
| reply = __near_error_failed(tag->write_msg, EINVAL); |
| if (reply) |
| g_dbus_send_message(conn, reply); |
| } else { |
| g_dbus_send_reply(conn, tag->write_msg, DBUS_TYPE_INVALID); |
| } |
| |
| near_ndef_records_free(tag->records); |
| tag->records = NULL; |
| g_free(tag->data); |
| tag->data = NULL; |
| |
| if (status == 0) { |
| /* |
| * If writing succeeded, |
| * check presence will be restored after reading |
| */ |
| __near_tag_read(tag, tag_read_cb); |
| return; |
| } |
| |
| out: |
| dbus_message_unref(tag->write_msg); |
| tag->write_msg = NULL; |
| |
| __near_adapter_start_check_presence(tag->adapter_idx, tag->target_idx); |
| } |
| |
| static void format_cb(uint32_t adapter_idx, uint32_t target_idx, int status) |
| { |
| struct near_tag *tag; |
| int err; |
| |
| DBG("format status %d", status); |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| return; |
| |
| if (!tag->write_msg) |
| return; |
| |
| if (status == 0) { |
| err = __near_tag_write(tag, tag->write_ndef, |
| write_cb); |
| if (err < 0) |
| goto error; |
| } else { |
| err = status; |
| goto error; |
| } |
| |
| return; |
| |
| error: |
| write_cb(tag->adapter_idx, tag->target_idx, err); |
| } |
| |
| static DBusMessage *write_ndef(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct near_tag *tag = data; |
| struct near_ndef_message *ndef, *ndef_with_header = NULL; |
| int tlv_len_size, err; |
| |
| DBG("conn %p", conn); |
| |
| if (tag->readonly) { |
| DBG("Read only tag"); |
| return __near_error_permission_denied(msg); |
| } |
| |
| if (tag->write_msg) |
| return __near_error_in_progress(msg); |
| |
| ndef = __ndef_build_from_message(msg); |
| if (!ndef) |
| return __near_error_failed(msg, EINVAL); |
| |
| tag->write_msg = dbus_message_ref(msg); |
| |
| /* Add NDEF header information depends upon tag type */ |
| switch (tag->type) { |
| case NFC_PROTO_JEWEL: |
| case NFC_PROTO_MIFARE: |
| case NFC_PROTO_ISO15693: |
| if (ndef->length < 0xff) |
| tlv_len_size = 3; |
| else |
| tlv_len_size = 5; |
| |
| ndef_with_header = g_try_malloc0(sizeof( |
| struct near_ndef_message)); |
| if (!ndef_with_header) |
| goto fail; |
| |
| ndef_with_header->offset = 0; |
| ndef_with_header->length = ndef->length + tlv_len_size; |
| ndef_with_header->data = |
| g_try_malloc0(ndef->length + tlv_len_size); |
| if (!ndef_with_header->data) |
| goto fail; |
| |
| ndef_with_header->data[0] = TLV_NDEF; |
| |
| if (ndef->length < 0xff) { |
| ndef_with_header->data[1] = ndef->length; |
| } else { |
| ndef_with_header->data[1] = 0xff; |
| ndef_with_header->data[2] = |
| (uint8_t)(ndef->length >> 8); |
| ndef_with_header->data[3] = (uint8_t)(ndef->length); |
| } |
| |
| memcpy(ndef_with_header->data + tlv_len_size - 1, ndef->data, |
| ndef->length); |
| ndef_with_header->data[ndef->length + tlv_len_size - 1] = |
| TLV_END; |
| break; |
| |
| case NFC_PROTO_FELICA: |
| ndef_with_header = g_try_malloc0(sizeof( |
| struct near_ndef_message)); |
| if (!ndef_with_header) |
| goto fail; |
| |
| ndef_with_header->offset = 0; |
| ndef_with_header->length = ndef->length; |
| ndef_with_header->data = g_try_malloc0( |
| ndef_with_header->length); |
| if (!ndef_with_header->data) |
| goto fail; |
| |
| memcpy(ndef_with_header->data, ndef->data, ndef->length); |
| |
| break; |
| |
| case NFC_PROTO_ISO14443: |
| case NFC_PROTO_ISO14443_B: |
| ndef_with_header = g_try_malloc0(sizeof( |
| struct near_ndef_message)); |
| if (!ndef_with_header) |
| goto fail; |
| |
| ndef_with_header->offset = 0; |
| ndef_with_header->length = ndef->length + 2; |
| ndef_with_header->data = g_try_malloc0(ndef->length + 2); |
| if (!ndef_with_header->data) |
| goto fail; |
| |
| ndef_with_header->data[0] = (uint8_t)(ndef->length >> 8); |
| ndef_with_header->data[1] = (uint8_t)(ndef->length); |
| memcpy(ndef_with_header->data + 2, ndef->data, ndef->length); |
| |
| break; |
| |
| default: |
| near_ndef_msg_free(ndef); |
| |
| return __near_error_failed(msg, EOPNOTSUPP); |
| } |
| |
| near_ndef_msg_free(ndef); |
| |
| tag->write_ndef = ndef_with_header; |
| err = __near_tag_write(tag, ndef_with_header, write_cb); |
| if (err < 0) { |
| g_free(ndef_with_header->data); |
| g_free(ndef_with_header); |
| |
| return __near_error_failed(msg, -err); |
| } |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| |
| fail: |
| dbus_message_unref(tag->write_msg); |
| tag->write_msg = NULL; |
| |
| return __near_error_failed(msg, ENOMEM); |
| } |
| |
| static DBusMessage *deactivate_tag(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct near_tag *tag = data; |
| struct near_adapter *adapter; |
| int err; |
| |
| DBG("deactivating tag %p", conn); |
| |
| adapter = __near_adapter_get(tag->adapter_idx); |
| if (!adapter) |
| return __near_error_failed(msg, EINVAL); |
| |
| __near_adapter_stop_check_presence(tag->adapter_idx, tag->target_idx); |
| |
| err = __near_netlink_deactivate_target(tag->adapter_idx, |
| tag->target_idx); |
| if (err < 0) |
| return __near_error_failed(msg, -err); |
| |
| near_adapter_disconnect(tag->adapter_idx); |
| |
| if (__near_adapter_is_constant_poll(adapter)) |
| __near_adapter_start_poll(adapter); |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| } |
| |
| static const GDBusMethodTable tag_methods[] = { |
| { GDBUS_ASYNC_METHOD("Write", GDBUS_ARGS({"attributes", "a{sv}"}), |
| NULL, write_ndef) }, |
| { GDBUS_METHOD("Deactivate", NULL, NULL, deactivate_tag) }, |
| { }, |
| }; |
| |
| static const GDBusPropertyTable tag_properties[] = { |
| { "Type", "s", property_get_type }, |
| { "Protocol", "s", property_get_protocol }, |
| { "ReadOnly", "b", property_get_readonly }, |
| { "Adapter", "o", property_get_adapter }, |
| { "Uid", "ay", property_get_uid }, |
| |
| { } |
| }; |
| |
| void __near_tag_append_records(struct near_tag *tag, DBusMessageIter *iter) |
| { |
| GList *list; |
| |
| for (list = tag->records; list; list = list->next) { |
| struct near_ndef_record *record = list->data; |
| char *path; |
| |
| path = __near_ndef_record_get_path(record); |
| if (!path) |
| continue; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, |
| &path); |
| } |
| } |
| |
| #define NFC_TAG_A (NFC_PROTO_ISO14443_MASK | NFC_PROTO_NFC_DEP_MASK | \ |
| NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK) |
| #define NFC_TAG_A_TYPE2 0x00 |
| #define NFC_TAG_A_TYPE4 0x01 |
| #define NFC_TAG_A_NFC_DEP 0x02 |
| #define NFC_TAG_A_TYPE4_DEP 0x03 |
| |
| #define NFC_TAG_A_SENS_RES_SSD_JEWEL 0x00 |
| #define NFC_TAG_A_SENS_RES_PLATCONF_JEWEL 0x0c |
| |
| #define NFC_TAG_A_SEL_PROT(sel_res) (((sel_res) & 0x60) >> 5) |
| #define NFC_TAG_A_SEL_CASCADE(sel_res) (((sel_res) & 0x04) >> 2) |
| #define NFC_TAG_A_SENS_RES_SSD(sens_res) ((sens_res) & 0x001f) |
| #define NFC_TAG_A_SENS_RES_PLATCONF(sens_res) (((sens_res) & 0x0f00) >> 8) |
| |
| static enum near_tag_sub_type get_tag_type2_sub_type(uint8_t sel_res) |
| { |
| switch (sel_res) { |
| case 0x00: |
| return NEAR_TAG_NFC_T2_MIFARE_ULTRALIGHT; |
| case 0x08: |
| return NEAR_TAG_NFC_T2_MIFARE_CLASSIC_1K; |
| case 0x09: |
| return NEAR_TAG_NFC_T2_MIFARE_MINI; |
| case 0x18: |
| return NEAR_TAG_NFC_T2_MIFARE_CLASSIC_4K; |
| case 0x20: |
| return NEAR_TAG_NFC_T2_MIFARE_DESFIRE; |
| case 0x28: |
| return NEAR_TAG_NFC_T2_JCOP30; |
| case 0x38: |
| return NEAR_TAG_NFC_T2_MIFARE_4K_EMUL; |
| case 0x88: |
| return NEAR_TAG_NFC_T2_MIFARE_1K_INFINEON; |
| case 0x98: |
| return NEAR_TAG_NFC_T2_MPCOS; |
| } |
| |
| return NEAR_TAG_NFC_SUBTYPE_UNKNOWN; |
| } |
| |
| static void set_tag_type(struct near_tag *tag, |
| uint16_t sens_res, uint8_t sel_res) |
| { |
| uint8_t platconf, ssd, proto; |
| |
| DBG("protocol 0x%x sens_res 0x%x sel_res 0x%x", tag->protocol, |
| sens_res, sel_res); |
| |
| switch (tag->protocol) { |
| case NFC_PROTO_JEWEL_MASK: |
| platconf = NFC_TAG_A_SENS_RES_PLATCONF(sens_res); |
| ssd = NFC_TAG_A_SENS_RES_SSD(sens_res); |
| |
| DBG("Jewel"); |
| |
| if ((ssd == NFC_TAG_A_SENS_RES_SSD_JEWEL) && |
| (platconf == NFC_TAG_A_SENS_RES_PLATCONF_JEWEL)) |
| tag->type = NFC_PROTO_JEWEL; |
| break; |
| |
| case NFC_PROTO_MIFARE_MASK: |
| case NFC_PROTO_ISO14443_MASK: |
| proto = NFC_TAG_A_SEL_PROT(sel_res); |
| |
| DBG("proto 0x%x", proto); |
| |
| switch (proto) { |
| case NFC_TAG_A_TYPE2: |
| tag->type = NFC_PROTO_MIFARE; |
| tag->sub_type = get_tag_type2_sub_type(sel_res); |
| break; |
| case NFC_TAG_A_TYPE4: |
| tag->type = NFC_PROTO_ISO14443; |
| break; |
| case NFC_TAG_A_TYPE4_DEP: |
| tag->type = NFC_PROTO_NFC_DEP; |
| break; |
| } |
| break; |
| |
| case NFC_PROTO_FELICA_MASK: |
| tag->type = NFC_PROTO_FELICA; |
| break; |
| |
| case NFC_PROTO_ISO14443_B_MASK: |
| tag->type = NFC_PROTO_ISO14443_B; |
| break; |
| |
| case NFC_PROTO_ISO15693_MASK: |
| tag->type = NFC_PROTO_ISO15693; |
| break; |
| |
| default: |
| tag->type = NFC_PROTO_MAX; |
| break; |
| } |
| |
| DBG("tag type 0x%x", tag->type); |
| } |
| |
| static int tag_initialize(struct near_tag *tag, |
| uint32_t adapter_idx, uint32_t target_idx, |
| uint32_t protocols, |
| uint16_t sens_res, uint8_t sel_res, |
| uint8_t *nfcid, uint8_t nfcid_len, |
| uint8_t iso15693_dsfid, |
| uint8_t iso15693_uid_len, uint8_t *iso15693_uid) |
| { |
| DBG(""); |
| |
| tag->path = g_strdup_printf("%s/nfc%u/tag%u", NFC_PATH, |
| adapter_idx, target_idx); |
| if (!tag->path) |
| return -ENOMEM; |
| tag->adapter_idx = adapter_idx; |
| tag->target_idx = target_idx; |
| tag->protocol = protocols; |
| tag->next_record = 0; |
| tag->readonly = false; |
| |
| if (nfcid_len && nfcid_len <= NFC_MAX_NFCID1_LEN) { |
| tag->nfcid_len = nfcid_len; |
| memcpy(tag->nfcid, nfcid, nfcid_len); |
| } else if (iso15693_uid_len && |
| iso15693_uid_len <= NFC_MAX_ISO15693_UID_LEN) { |
| tag->iso15693_dsfid = iso15693_dsfid; |
| tag->iso15693_uid_len = iso15693_uid_len; |
| memcpy(tag->iso15693_uid, iso15693_uid, iso15693_uid_len); |
| } |
| |
| set_tag_type(tag, sens_res, sel_res); |
| |
| return 0; |
| } |
| |
| struct near_tag *__near_tag_add(uint32_t adapter_idx, uint32_t target_idx, |
| uint32_t protocols, |
| uint16_t sens_res, uint8_t sel_res, |
| uint8_t *nfcid, uint8_t nfcid_len, |
| uint8_t iso15693_dsfid, |
| uint8_t iso15693_uid_len, uint8_t *iso15693_uid) |
| { |
| struct near_tag *tag; |
| char *path; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (tag) |
| return NULL; |
| |
| tag = g_try_malloc0(sizeof(struct near_tag)); |
| if (!tag) |
| return NULL; |
| |
| if (tag_initialize(tag, adapter_idx, target_idx, |
| protocols, |
| sens_res, sel_res, |
| nfcid, nfcid_len, |
| iso15693_dsfid, |
| iso15693_uid_len, iso15693_uid) < 0) { |
| g_free(tag); |
| return NULL; |
| } |
| |
| path = g_strdup(tag->path); |
| if (!path) { |
| g_free(tag); |
| return NULL; |
| } |
| |
| g_hash_table_insert(tag_hash, path, tag); |
| |
| DBG("connection %p", connection); |
| |
| g_dbus_register_interface(connection, tag->path, |
| NFC_TAG_INTERFACE, |
| tag_methods, NULL, |
| tag_properties, tag, NULL); |
| |
| return tag; |
| } |
| |
| void __near_tag_remove(struct near_tag *tag) |
| { |
| char *path = tag->path; |
| |
| DBG("path %s", tag->path); |
| |
| g_hash_table_remove(tag_hash, path); |
| } |
| |
| const char *__near_tag_get_path(struct near_tag *tag) |
| { |
| return tag->path; |
| } |
| |
| uint32_t __near_tag_get_type(struct near_tag *tag) |
| { |
| return tag->type; |
| } |
| |
| enum near_tag_sub_type near_tag_get_subtype(uint32_t adapter_idx, |
| uint32_t target_idx) |
| |
| { |
| struct near_tag *tag; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| return NEAR_TAG_NFC_SUBTYPE_UNKNOWN; |
| |
| return tag->sub_type; |
| } |
| |
| uint8_t *near_tag_get_nfcid(uint32_t adapter_idx, uint32_t target_idx, |
| uint8_t *nfcid_len) |
| { |
| struct near_tag *tag; |
| uint8_t *nfcid; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| goto fail; |
| |
| nfcid = g_try_malloc0(tag->nfcid_len); |
| if (!nfcid) |
| goto fail; |
| |
| memcpy(nfcid, tag->nfcid, tag->nfcid_len); |
| *nfcid_len = tag->nfcid_len; |
| |
| return nfcid; |
| |
| fail: |
| *nfcid_len = 0; |
| return NULL; |
| } |
| |
| int near_tag_set_nfcid(uint32_t adapter_idx, uint32_t target_idx, |
| uint8_t *nfcid, size_t nfcid_len) |
| { |
| struct near_tag *tag; |
| |
| DBG("NFCID len %zu", nfcid_len); |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| return -ENODEV; |
| |
| if (tag->nfcid_len > 0) |
| return -EALREADY; |
| |
| if (nfcid_len > NFC_MAX_NFCID1_LEN) |
| return -EINVAL; |
| |
| memcpy(tag->nfcid, nfcid, nfcid_len); |
| tag->nfcid_len = nfcid_len; |
| |
| return 0; |
| } |
| |
| uint8_t *near_tag_get_iso15693_dsfid(uint32_t adapter_idx, uint32_t target_idx) |
| { |
| struct near_tag *tag; |
| uint8_t *iso15693_dsfid; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| goto fail; |
| |
| iso15693_dsfid = g_try_malloc0(NFC_MAX_ISO15693_DSFID_LEN); |
| if (!iso15693_dsfid) |
| goto fail; |
| |
| memcpy(iso15693_dsfid, &tag->iso15693_dsfid, |
| NFC_MAX_ISO15693_DSFID_LEN); |
| |
| return iso15693_dsfid; |
| |
| fail: |
| return NULL; |
| } |
| |
| uint8_t *near_tag_get_iso15693_uid(uint32_t adapter_idx, uint32_t target_idx) |
| { |
| struct near_tag *tag; |
| uint8_t *iso15693_uid; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| goto fail; |
| |
| iso15693_uid = g_try_malloc0(tag->iso15693_uid_len); |
| if (!iso15693_uid) |
| goto fail; |
| |
| memcpy(iso15693_uid, tag->iso15693_uid, tag->iso15693_uid_len); |
| |
| return iso15693_uid; |
| |
| fail: |
| return NULL; |
| } |
| |
| int near_tag_add_data(uint32_t adapter_idx, uint32_t target_idx, |
| uint8_t *data, size_t data_length) |
| { |
| struct near_tag *tag; |
| |
| tag = near_tag_get_tag(adapter_idx, target_idx); |
| if (!tag) |
| return -ENODEV; |
| |
| tag->data_length = data_length; |
| tag->data = g_try_malloc0(data_length); |
| if (!tag->data) |
| return -ENOMEM; |
| |
| if (data) |
| memcpy(tag->data, data, data_length); |
| |
| return 0; |
| } |
| |
| int near_tag_add_records(struct near_tag *tag, GList *records, |
| near_tag_io_cb cb, int status) |
| { |
| GList *list; |
| struct near_ndef_record *record; |
| char *path; |
| |
| DBG("records %p", records); |
| |
| for (list = records; list; list = list->next) { |
| record = list->data; |
| |
| path = g_strdup_printf("%s/nfc%u/tag%u/record%u", |
| NFC_PATH, tag->adapter_idx, |
| tag->target_idx, tag->next_record); |
| |
| if (!path) |
| continue; |
| |
| __near_ndef_record_register(record, path); |
| |
| tag->next_record++; |
| tag->records = g_list_append(tag->records, record); |
| } |
| |
| __near_agent_ndef_parse_records(tag->records); |
| |
| near_dbus_property_changed_array(tag->path, |
| NFC_TAG_INTERFACE, "Records", |
| DBUS_TYPE_OBJECT_PATH, append_records, |
| tag); |
| |
| if (cb) |
| cb(tag->adapter_idx, tag->target_idx, status); |
| |
| g_list_free(records); |
| |
| return 0; |
| } |
| |
| void near_tag_set_ro(struct near_tag *tag, bool readonly) |
| { |
| tag->readonly = readonly; |
| } |
| |
| void near_tag_set_blank(struct near_tag *tag, bool blank) |
| { |
| tag->blank = blank; |
| } |
| |
| bool near_tag_get_blank(struct near_tag *tag) |
| { |
| return tag->blank; |
| } |
| |
| uint8_t *near_tag_get_data(struct near_tag *tag, size_t *data_length) |
| { |
| if (!data_length) |
| return NULL; |
| |
| *data_length = tag->data_length; |
| |
| return tag->data; |
| } |
| |
| size_t near_tag_get_data_length(struct near_tag *tag) |
| { |
| return tag->data_length; |
| } |
| |
| uint32_t near_tag_get_adapter_idx(struct near_tag *tag) |
| { |
| return tag->adapter_idx; |
| } |
| |
| uint32_t near_tag_get_target_idx(struct near_tag *tag) |
| { |
| return tag->target_idx; |
| } |
| |
| enum near_tag_memory_layout near_tag_get_memory_layout(struct near_tag *tag) |
| { |
| if (!tag) |
| return NEAR_TAG_MEMORY_UNKNOWN; |
| |
| return tag->layout; |
| } |
| |
| void near_tag_set_memory_layout(struct near_tag *tag, |
| enum near_tag_memory_layout layout) |
| { |
| if (!tag) |
| return; |
| |
| tag->layout = layout; |
| } |
| |
| void near_tag_set_max_ndef_size(struct near_tag *tag, uint16_t size) |
| { |
| if (!tag) |
| return; |
| |
| tag->t4.max_ndef_size = size; |
| } |
| |
| uint16_t near_tag_get_max_ndef_size(struct near_tag *tag) |
| { |
| if (!tag) |
| return 0; |
| |
| return tag->t4.max_ndef_size; |
| } |
| |
| void near_tag_set_c_apdu_max_size(struct near_tag *tag, uint16_t size) |
| { |
| if (!tag) |
| return; |
| |
| tag->t4.c_apdu_max_size = size; |
| } |
| |
| uint16_t near_tag_get_c_apdu_max_size(struct near_tag *tag) |
| { |
| if (!tag) |
| return 0; |
| |
| return tag->t4.c_apdu_max_size; |
| } |
| |
| void near_tag_set_r_apdu_max_size(struct near_tag *tag, uint16_t size) |
| { |
| if (!tag) |
| return; |
| |
| tag->t4.r_apdu_max_size = size; |
| } |
| |
| uint16_t near_tag_get_r_apdu_max_size(struct near_tag *tag) |
| { |
| if (!tag) |
| return 0; |
| |
| return tag->t4.r_apdu_max_size; |
| } |
| |
| void near_tag_set_file_id(struct near_tag *tag, uint16_t file_id) |
| { |
| if (!tag) |
| return; |
| |
| tag->t4.file_id = file_id; |
| } |
| |
| uint16_t near_tag_get_file_id(struct near_tag *tag) |
| { |
| if (!tag) |
| return 0; |
| |
| return tag->t4.file_id; |
| } |
| |
| void near_tag_set_idm(struct near_tag *tag, uint8_t *idm, uint8_t len) |
| { |
| if (!tag || len > TYPE3_IDM_LEN) |
| return; |
| |
| memset(tag->t3.IDm, 0, TYPE3_IDM_LEN); |
| memcpy(tag->t3.IDm, idm, len); |
| } |
| |
| uint8_t *near_tag_get_idm(struct near_tag *tag, uint8_t *len) |
| { |
| if (!tag || !len) |
| return NULL; |
| |
| *len = TYPE3_IDM_LEN; |
| return tag->t3.IDm; |
| } |
| |
| void near_tag_set_attr_block(struct near_tag *tag, uint8_t *attr, uint8_t len) |
| { |
| if (!tag || len > TYPE3_ATTR_BLOCK_SIZE) |
| return; |
| |
| memset(tag->t3.attr, 0, TYPE3_ATTR_BLOCK_SIZE); |
| memcpy(tag->t3.attr, attr, len); |
| } |
| |
| uint8_t *near_tag_get_attr_block(struct near_tag *tag, uint8_t *len) |
| { |
| if (!tag || !len) |
| return NULL; |
| |
| *len = TYPE3_ATTR_BLOCK_SIZE; |
| return tag->t3.attr; |
| } |
| |
| void near_tag_set_ic_type(struct near_tag *tag, uint8_t ic_type) |
| { |
| if (!tag) |
| return; |
| |
| tag->t3.ic_type = ic_type; |
| } |
| |
| uint8_t near_tag_get_ic_type(struct near_tag *tag) |
| { |
| if (!tag) |
| return 0; |
| |
| return tag->t3.ic_type; |
| } |
| |
| uint8_t near_tag_get_blk_size(struct near_tag *tag) |
| { |
| return tag->t5.blk_size; |
| } |
| |
| void near_tag_set_blk_size(struct near_tag *tag, uint8_t blk_size) |
| { |
| tag->t5.blk_size = blk_size; |
| } |
| |
| uint8_t near_tag_get_num_blks(struct near_tag *tag) |
| { |
| return tag->t5.num_blks; |
| } |
| |
| void near_tag_set_num_blks(struct near_tag *tag, uint8_t num_blks) |
| { |
| tag->t5.num_blks = num_blks; |
| } |
| |
| static gint cmp_prio(gconstpointer a, gconstpointer b) |
| { |
| const struct near_tag_driver *driver1 = a; |
| const struct near_tag_driver *driver2 = b; |
| |
| return driver2->priority - driver1->priority; |
| } |
| |
| int near_tag_driver_register(struct near_tag_driver *driver) |
| { |
| DBG(""); |
| |
| if (!driver->read) |
| return -EINVAL; |
| |
| driver_list = g_slist_insert_sorted(driver_list, driver, cmp_prio); |
| |
| return 0; |
| } |
| |
| void near_tag_driver_unregister(struct near_tag_driver *driver) |
| { |
| DBG(""); |
| |
| driver_list = g_slist_remove(driver_list, driver); |
| } |
| |
| int __near_tag_read(struct near_tag *tag, near_tag_io_cb cb) |
| { |
| GSList *list; |
| |
| DBG("type 0x%x", tag->type); |
| |
| /* Stop check presence while reading */ |
| __near_adapter_stop_check_presence(tag->adapter_idx, tag->target_idx); |
| |
| for (list = driver_list; list; list = list->next) { |
| struct near_tag_driver *driver = list->data; |
| |
| DBG("driver type 0x%x", driver->type); |
| |
| if (driver->type == tag->type) |
| return driver->read(tag->adapter_idx, tag->target_idx, |
| cb); |
| } |
| |
| return 0; |
| } |
| |
| int __near_tag_write(struct near_tag *tag, |
| struct near_ndef_message *ndef, |
| near_tag_io_cb cb) |
| { |
| GSList *list; |
| int err; |
| |
| DBG("type 0x%x", tag->type); |
| |
| for (list = driver_list; list; list = list->next) { |
| struct near_tag_driver *driver = list->data; |
| |
| DBG("driver type 0x%x", driver->type); |
| |
| if (driver->type == tag->type) { |
| /* Stop check presence while writing */ |
| __near_adapter_stop_check_presence(tag->adapter_idx, |
| tag->target_idx); |
| |
| if (tag->blank && driver->format) { |
| DBG("Blank tag detected, formatting"); |
| err = driver->format(tag->adapter_idx, |
| tag->target_idx, format_cb); |
| } else { |
| err = driver->write(tag->adapter_idx, |
| tag->target_idx, ndef, |
| cb); |
| } |
| |
| break; |
| } |
| } |
| |
| if (!list) |
| err = -EOPNOTSUPP; |
| |
| if (err < 0) |
| __near_adapter_start_check_presence(tag->adapter_idx, |
| tag->target_idx); |
| |
| return err; |
| } |
| |
| int __near_tag_check_presence(struct near_tag *tag, near_tag_io_cb cb) |
| { |
| GSList *list; |
| |
| DBG("type 0x%x", tag->type); |
| |
| for (list = driver_list; list; list = list->next) { |
| struct near_tag_driver *driver = list->data; |
| |
| DBG("driver type 0x%x", driver->type); |
| |
| if (driver->type == tag->type) { |
| if (!driver->check_presence) |
| continue; |
| |
| return driver->check_presence(tag->adapter_idx, tag->target_idx, cb); |
| } |
| } |
| |
| return -EOPNOTSUPP; |
| } |
| |
| int near_tag_activate_target(uint32_t adapter_idx, uint32_t target_idx, |
| uint32_t protocol) |
| { |
| return __near_netlink_activate_target(adapter_idx, target_idx, |
| protocol); |
| } |
| |
| static void free_tag(gpointer data) |
| { |
| struct near_tag *tag = data; |
| |
| DBG("tag %p", tag); |
| |
| near_ndef_records_free(tag->records); |
| |
| g_dbus_unregister_interface(connection, tag->path, |
| NFC_TAG_INTERFACE); |
| |
| g_free(tag->path); |
| g_free(tag->data); |
| g_free(tag); |
| } |
| |
| int __near_tag_init(void) |
| { |
| DBG(""); |
| |
| connection = near_dbus_get_connection(); |
| |
| tag_hash = g_hash_table_new_full(g_str_hash, g_str_equal, |
| g_free, free_tag); |
| |
| return 0; |
| } |
| |
| void __near_tag_cleanup(void) |
| { |
| DBG(""); |
| |
| g_hash_table_destroy(tag_hash); |
| tag_hash = NULL; |
| } |