| /****************************************************************************** |
| * |
| * Copyright (C) 2010-2014 Broadcom Corporation |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| * Handle ndef messages |
| * |
| ******************************************************************************/ |
| #include <string.h> |
| #include "ndef_utils.h" |
| #include "nfa_api.h" |
| #include "nfa_dm_int.h" |
| #include "nfa_sys.h" |
| #include "nfa_sys_int.h" |
| #include "nfc_api.h" |
| |
| /******************************************************************************* |
| * URI Well-known-type prefixes |
| *******************************************************************************/ |
| const uint8_t* nfa_dm_ndef_wkt_uri_str_tbl[] = { |
| NULL, /* 0x00 */ |
| (const uint8_t*)"http://www.", /* 0x01 */ |
| (const uint8_t*)"https://www.", /* 0x02 */ |
| (const uint8_t*)"http://", /* 0x03 */ |
| (const uint8_t*)"https://", /* 0x04 */ |
| (const uint8_t*)"tel:", /* 0x05 */ |
| (const uint8_t*)"mailto:", /* 0x06 */ |
| (const uint8_t*)"ftp://anonymous:anonymous@", /* 0x07 */ |
| (const uint8_t*)"ftp://ftp.", /* 0x08 */ |
| (const uint8_t*)"ftps://", /* 0x09 */ |
| (const uint8_t*)"sftp://", /* 0x0A */ |
| (const uint8_t*)"smb://", /* 0x0B */ |
| (const uint8_t*)"nfs://", /* 0x0C */ |
| (const uint8_t*)"ftp://", /* 0x0D */ |
| (const uint8_t*)"dav://", /* 0x0E */ |
| (const uint8_t*)"news:", /* 0x0F */ |
| (const uint8_t*)"telnet://", /* 0x10 */ |
| (const uint8_t*)"imap:", /* 0x11 */ |
| (const uint8_t*)"rtsp://", /* 0x12 */ |
| (const uint8_t*)"urn:", /* 0x13 */ |
| (const uint8_t*)"pop:", /* 0x14 */ |
| (const uint8_t*)"sip:", /* 0x15 */ |
| (const uint8_t*)"sips:", /* 0x16 */ |
| (const uint8_t*)"tftp:", /* 0x17 */ |
| (const uint8_t*)"btspp://", /* 0x18 */ |
| (const uint8_t*)"btl2cap://", /* 0x19 */ |
| (const uint8_t*)"btgoep://", /* 0x1A */ |
| (const uint8_t*)"tcpobex://", /* 0x1B */ |
| (const uint8_t*)"irdaobex://", /* 0x1C */ |
| (const uint8_t*)"file://", /* 0x1D */ |
| (const uint8_t*)"urn:epc:id:", /* 0x1E */ |
| (const uint8_t*)"urn:epc:tag:", /* 0x1F */ |
| (const uint8_t*)"urn:epc:pat:", /* 0x20 */ |
| (const uint8_t*)"urn:epc:raw:", /* 0x21 */ |
| (const uint8_t*)"urn:epc:", /* 0x22 */ |
| (const uint8_t*)"urn:nfc:" /* 0x23 */ |
| }; |
| #define NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE \ |
| (sizeof(nfa_dm_ndef_wkt_uri_str_tbl) / sizeof(uint8_t*)) |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_dereg_hdlr_by_handle |
| ** |
| ** Description Deregister NDEF record type handler |
| ** |
| ** Returns TRUE (message buffer to be freed by caller) |
| ** |
| *******************************************************************************/ |
| void nfa_dm_ndef_dereg_hdlr_by_handle(tNFA_HANDLE ndef_type_handle) { |
| tNFA_DM_CB* p_cb = &nfa_dm_cb; |
| uint16_t hdlr_idx; |
| hdlr_idx = (uint16_t)(ndef_type_handle & NFA_HANDLE_MASK); |
| |
| if (p_cb->p_ndef_handler[hdlr_idx]) { |
| GKI_freebuf(p_cb->p_ndef_handler[hdlr_idx]); |
| p_cb->p_ndef_handler[hdlr_idx] = NULL; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_dereg_all |
| ** |
| ** Description Deregister all NDEF record type handlers (called during |
| ** shutdown(. |
| ** |
| ** Returns Nothing |
| ** |
| *******************************************************************************/ |
| void nfa_dm_ndef_dereg_all(void) { |
| tNFA_DM_CB* p_cb = &nfa_dm_cb; |
| uint32_t i; |
| |
| for (i = 0; i < NFA_NDEF_MAX_HANDLERS; i++) { |
| /* If this is a free slot, then remember it */ |
| if (p_cb->p_ndef_handler[i] != NULL) { |
| GKI_freebuf(p_cb->p_ndef_handler[i]); |
| p_cb->p_ndef_handler[i] = NULL; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_reg_hdlr |
| ** |
| ** Description Register NDEF record type handler |
| ** |
| ** Returns TRUE if message buffer is to be freed by caller |
| ** |
| *******************************************************************************/ |
| bool nfa_dm_ndef_reg_hdlr(tNFA_DM_MSG* p_data) { |
| tNFA_DM_CB* p_cb = &nfa_dm_cb; |
| uint32_t hdlr_idx, i; |
| tNFA_DM_API_REG_NDEF_HDLR* p_reg_info = (tNFA_DM_API_REG_NDEF_HDLR*)p_data; |
| tNFA_NDEF_REGISTER ndef_register; |
| |
| /* If registering default handler, check to see if one is already registered |
| */ |
| if (p_reg_info->tnf == NFA_TNF_DEFAULT) { |
| /* check if default handler is already registered */ |
| if (p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX]) { |
| NFA_TRACE_WARNING0("Default NDEF handler being changed."); |
| |
| /* Free old registration info */ |
| nfa_dm_ndef_dereg_hdlr_by_handle( |
| (tNFA_HANDLE)NFA_NDEF_DEFAULT_HANDLER_IDX); |
| } |
| NFA_TRACE_DEBUG0("Default NDEF handler successfully registered."); |
| hdlr_idx = NFA_NDEF_DEFAULT_HANDLER_IDX; |
| } |
| /* Get available entry in ndef_handler table, and check if requested type is |
| already registered */ |
| else { |
| hdlr_idx = NFA_HANDLE_INVALID; |
| |
| /* Check if this type is already registered */ |
| for (i = (NFA_NDEF_DEFAULT_HANDLER_IDX + 1); i < NFA_NDEF_MAX_HANDLERS; |
| i++) { |
| /* If this is a free slot, then remember it */ |
| if (p_cb->p_ndef_handler[i] == NULL) { |
| hdlr_idx = i; |
| break; |
| } |
| } |
| } |
| |
| if (hdlr_idx != NFA_HANDLE_INVALID) { |
| /* Update the table */ |
| p_cb->p_ndef_handler[hdlr_idx] = p_reg_info; |
| |
| p_reg_info->ndef_type_handle = |
| (tNFA_HANDLE)(NFA_HANDLE_GROUP_NDEF_HANDLER | hdlr_idx); |
| |
| ndef_register.ndef_type_handle = p_reg_info->ndef_type_handle; |
| ndef_register.status = NFA_STATUS_OK; |
| |
| NFA_TRACE_DEBUG1("NDEF handler successfully registered. Handle=0x%08x", |
| p_reg_info->ndef_type_handle); |
| tNFA_NDEF_EVT_DATA nfa_ndef_evt_data; |
| nfa_ndef_evt_data.ndef_reg = ndef_register; |
| (*(p_reg_info->p_ndef_cback))(NFA_NDEF_REGISTER_EVT, &nfa_ndef_evt_data); |
| |
| /* indicate that we will free message buffer when type_handler is |
| * deregistered */ |
| return false; |
| } else { |
| /* Error */ |
| NFA_TRACE_ERROR0("NDEF handler failed to register."); |
| ndef_register.ndef_type_handle = NFA_HANDLE_INVALID; |
| ndef_register.status = NFA_STATUS_FAILED; |
| tNFA_NDEF_EVT_DATA nfa_ndef_evt_data; |
| nfa_ndef_evt_data.ndef_reg = ndef_register; |
| (*(p_reg_info->p_ndef_cback))(NFA_NDEF_REGISTER_EVT, &nfa_ndef_evt_data); |
| |
| return true; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_dereg_hdlr |
| ** |
| ** Description Deregister NDEF record type handler |
| ** |
| ** Returns TRUE (message buffer to be freed by caller) |
| ** |
| *******************************************************************************/ |
| bool nfa_dm_ndef_dereg_hdlr(tNFA_DM_MSG* p_data) { |
| tNFA_DM_API_DEREG_NDEF_HDLR* p_dereginfo = |
| (tNFA_DM_API_DEREG_NDEF_HDLR*)p_data; |
| |
| /* Make sure this is a NDEF_HDLR handle */ |
| if (((p_dereginfo->ndef_type_handle & NFA_HANDLE_GROUP_MASK) != |
| NFA_HANDLE_GROUP_NDEF_HANDLER) || |
| ((p_dereginfo->ndef_type_handle & NFA_HANDLE_MASK) >= |
| NFA_NDEF_MAX_HANDLERS)) { |
| NFA_TRACE_ERROR1("Invalid handle for NDEF type handler: 0x%08x", |
| p_dereginfo->ndef_type_handle); |
| } else { |
| nfa_dm_ndef_dereg_hdlr_by_handle(p_dereginfo->ndef_type_handle); |
| } |
| |
| return true; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_find_next_handler |
| ** |
| ** Description Find next ndef handler for a given record type |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| tNFA_DM_API_REG_NDEF_HDLR* nfa_dm_ndef_find_next_handler( |
| tNFA_DM_API_REG_NDEF_HDLR* p_init_handler, uint8_t tnf, |
| uint8_t* p_type_name, uint8_t type_name_len, uint8_t* p_payload, |
| uint32_t payload_len) { |
| tNFA_DM_CB* p_cb = &nfa_dm_cb; |
| uint8_t i; |
| |
| /* if init_handler is NULL, then start with the first non-default handler */ |
| if (!p_init_handler) |
| i = NFA_NDEF_DEFAULT_HANDLER_IDX + 1; |
| else { |
| /* Point to handler index after p_init_handler */ |
| i = (p_init_handler->ndef_type_handle & NFA_HANDLE_MASK) + 1; |
| } |
| |
| /* Look for next handler */ |
| for (; i < NFA_NDEF_MAX_HANDLERS; i++) { |
| /* Check if TNF matches */ |
| if ((p_cb->p_ndef_handler[i]) && (p_cb->p_ndef_handler[i]->tnf == tnf)) { |
| /* TNF matches. */ |
| /* If handler is for a specific URI type, check if type is WKT URI, */ |
| /* and that the URI prefix abrieviation for this handler matches */ |
| if (p_cb->p_ndef_handler[i]->flags & NFA_NDEF_FLAGS_WKT_URI) { |
| /* This is a handler for a specific URI type */ |
| /* Check if this recurd is WKT URI */ |
| if ((p_payload) && (type_name_len == 1) && (*p_type_name == 'U')) { |
| /* Check if URI prefix abrieviation matches */ |
| if ((payload_len > 1) && |
| (p_payload[0] == p_cb->p_ndef_handler[i]->uri_id)) { |
| /* URI prefix abrieviation matches */ |
| /* If handler does not specify an absolute URI, then match found. */ |
| /* If absolute URI, then compare URI for match (skip over uri_id in |
| * ndef payload) */ |
| if ((p_cb->p_ndef_handler[i]->uri_id != NFA_NDEF_URI_ID_ABSOLUTE) || |
| (memcmp(&p_payload[1], p_cb->p_ndef_handler[i]->name, |
| p_cb->p_ndef_handler[i]->name_len) == 0)) { |
| /* Handler found. */ |
| break; |
| } |
| } |
| /* Check if handler is absolute URI but NDEF is using prefix |
| abrieviation */ |
| else if ((p_cb->p_ndef_handler[i]->uri_id == |
| NFA_NDEF_URI_ID_ABSOLUTE) && |
| (p_payload[0] != NFA_NDEF_URI_ID_ABSOLUTE)) { |
| /* Handler is absolute URI but NDEF is using prefix abrieviation. |
| * Compare URI prefix */ |
| if ((p_payload[0] < NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE) && |
| (memcmp(p_cb->p_ndef_handler[i]->name, |
| (char*)nfa_dm_ndef_wkt_uri_str_tbl[p_payload[0]], |
| p_cb->p_ndef_handler[i]->name_len) == 0)) { |
| /* Handler found. */ |
| break; |
| } |
| } |
| /* Check if handler is using prefix abrieviation, but NDEF is using |
| absolute URI */ |
| else if ((p_cb->p_ndef_handler[i]->uri_id != |
| NFA_NDEF_URI_ID_ABSOLUTE) && |
| (p_payload[0] == NFA_NDEF_URI_ID_ABSOLUTE)) { |
| /* Handler is using prefix abrieviation, but NDEF is using absolute |
| * URI. Compare URI prefix */ |
| if ((p_cb->p_ndef_handler[i]->uri_id < |
| NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE) && |
| (memcmp(&p_payload[1], |
| nfa_dm_ndef_wkt_uri_str_tbl[p_cb->p_ndef_handler[i] |
| ->uri_id], |
| strlen((const char*)nfa_dm_ndef_wkt_uri_str_tbl |
| [p_cb->p_ndef_handler[i]->uri_id])) == 0)) { |
| /* Handler found. */ |
| break; |
| } |
| } |
| } |
| } |
| /* Not looking for specific URI. Check if type_name for this handler |
| matches the NDEF record's type_name */ |
| else if (p_cb->p_ndef_handler[i]->name_len == type_name_len) { |
| if ((type_name_len == 0) || (memcmp(p_cb->p_ndef_handler[i]->name, |
| p_type_name, type_name_len) == 0)) { |
| /* Handler found */ |
| break; |
| } |
| } |
| } |
| } |
| |
| if (i < NFA_NDEF_MAX_HANDLERS) |
| return (p_cb->p_ndef_handler[i]); |
| else |
| return (NULL); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_clear_notified_flag |
| ** |
| ** Description Clear 'whole_message_notified' flag for all the handlers |
| ** (flag used to indicate that this handler has already |
| ** handled the entire incoming NDEF message) |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void nfa_dm_ndef_clear_notified_flag(void) { |
| tNFA_DM_CB* p_cb = &nfa_dm_cb; |
| uint8_t i; |
| |
| for (i = 0; i < NFA_NDEF_MAX_HANDLERS; i++) { |
| if (p_cb->p_ndef_handler[i]) { |
| p_cb->p_ndef_handler[i]->flags &= ~NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function nfa_dm_ndef_handle_message |
| ** |
| ** Description Handle incoming ndef message |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void nfa_dm_ndef_handle_message(tNFA_STATUS status, uint8_t* p_msg_buf, |
| uint32_t len) { |
| tNFA_DM_CB* p_cb = &nfa_dm_cb; |
| tNDEF_STATUS ndef_status; |
| uint8_t *p_rec, *p_ndef_start, *p_type, *p_payload, *p_rec_end; |
| uint32_t payload_len; |
| uint8_t tnf, type_len, rec_hdr_flags, id_len; |
| tNFA_DM_API_REG_NDEF_HDLR* p_handler; |
| tNFA_NDEF_DATA ndef_data; |
| uint8_t rec_count = 0; |
| bool record_handled, entire_message_handled; |
| |
| NFA_TRACE_DEBUG3("nfa_dm_ndef_handle_message status=%i, msgbuf=%08x, len=%i", |
| status, p_msg_buf, len); |
| |
| if (status != NFA_STATUS_OK) { |
| /* If problem reading NDEF message, then exit (no action required) */ |
| return; |
| } |
| |
| /* If in exclusive RF mode is activer, then route NDEF message callback |
| * registered with NFA_StartExclusiveRfControl */ |
| if ((p_cb->flags & NFA_DM_FLAGS_EXCL_RF_ACTIVE) && |
| (p_cb->p_excl_ndef_cback)) { |
| /* No ndef-handler handle, since this callback is not from |
| * RegisterNDefHandler */ |
| ndef_data.ndef_type_handle = 0; |
| ndef_data.p_data = p_msg_buf; |
| ndef_data.len = len; |
| tNFA_NDEF_EVT_DATA nfa_ndef_evt_data; |
| nfa_ndef_evt_data.ndef_data = ndef_data; |
| (*p_cb->p_excl_ndef_cback)(NFA_NDEF_DATA_EVT, &nfa_ndef_evt_data); |
| return; |
| } |
| |
| /* Handle zero length - notify default handler */ |
| if (len == 0) { |
| p_handler = p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX]; |
| if (p_handler != NULL) { |
| NFA_TRACE_DEBUG0( |
| "Notifying default handler of zero-length NDEF message..."); |
| ndef_data.ndef_type_handle = p_handler->ndef_type_handle; |
| ndef_data.p_data = NULL; /* Start of record */ |
| ndef_data.len = 0; |
| tNFA_NDEF_EVT_DATA nfa_ndef_evt_data; |
| nfa_ndef_evt_data.ndef_data = ndef_data; |
| (*p_handler->p_ndef_cback)(NFA_NDEF_DATA_EVT, &nfa_ndef_evt_data); |
| } |
| return; |
| } |
| |
| /* Validate the NDEF message */ |
| ndef_status = NDEF_MsgValidate(p_msg_buf, len, true); |
| if (ndef_status != NDEF_OK) { |
| NFA_TRACE_ERROR1("Received invalid NDEF message. NDEF status=0x%x", |
| ndef_status); |
| return; |
| } |
| |
| /* NDEF message received from backgound polling. Pass the NDEF message to the |
| * NDEF handlers */ |
| |
| /* New NDEF message. Clear 'notified' flag for all the handlers */ |
| nfa_dm_ndef_clear_notified_flag(); |
| |
| /* Indicate that no handler has handled this entire NDEF message (e.g. |
| * connection-handover handler *) */ |
| entire_message_handled = false; |
| |
| /* Get first record in message */ |
| p_rec = p_ndef_start = p_msg_buf; |
| |
| /* Check each record in the NDEF message */ |
| while (p_rec != NULL) { |
| /* Get record type */ |
| p_type = NDEF_RecGetType(p_rec, &tnf, &type_len); |
| |
| /* Indicate record not handled yet */ |
| record_handled = false; |
| |
| /* Get pointer to record payload */ |
| p_payload = NDEF_RecGetPayload(p_rec, &payload_len); |
| |
| /* Find first handler for this type */ |
| p_handler = nfa_dm_ndef_find_next_handler(NULL, tnf, p_type, type_len, |
| p_payload, payload_len); |
| if (p_handler == NULL) { |
| /* Not a registered NDEF type. Use default handler */ |
| p_handler = p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX]; |
| if (p_handler != NULL) { |
| NFA_TRACE_DEBUG0("No handler found. Using default handler..."); |
| } |
| } |
| |
| while (p_handler) { |
| /* If handler is for whole NDEF message, and it has already been notified, |
| * then skip notification */ |
| if (p_handler->flags & NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED) { |
| /* Look for next handler */ |
| p_handler = nfa_dm_ndef_find_next_handler( |
| p_handler, tnf, p_type, type_len, p_payload, payload_len); |
| continue; |
| } |
| |
| /* Get pointer to record payload */ |
| NFA_TRACE_DEBUG1("Calling ndef type handler (%x)", |
| p_handler->ndef_type_handle); |
| |
| ndef_data.ndef_type_handle = p_handler->ndef_type_handle; |
| ndef_data.p_data = p_rec; /* Start of record */ |
| |
| /* Calculate length of NDEF record */ |
| if (p_payload != NULL) |
| ndef_data.len = payload_len + (uint32_t)(p_payload - p_rec); |
| else { |
| /* If no payload, calculate length of ndef record header */ |
| p_rec_end = p_rec; |
| |
| /* First byte is the header flags */ |
| rec_hdr_flags = *p_rec_end++; |
| |
| /* Next byte is the type field length */ |
| type_len = *p_rec_end++; |
| |
| /* Next is the payload length (1 or 4 bytes) */ |
| if (rec_hdr_flags & NDEF_SR_MASK) { |
| p_rec_end++; |
| } else { |
| p_rec_end += 4; |
| } |
| |
| /* ID field Length */ |
| if (rec_hdr_flags & NDEF_IL_MASK) |
| id_len = *p_rec_end++; |
| else |
| id_len = 0; |
| p_rec_end += id_len; |
| |
| ndef_data.len = (uint32_t)(p_rec_end - p_rec); |
| } |
| |
| /* If handler wants entire ndef message, then pass pointer to start of |
| * message and */ |
| /* set 'notified' flag so handler won't get notified on subsequent records |
| * for this */ |
| /* NDEF message. */ |
| if (p_handler->flags & NFA_NDEF_FLAGS_HANDLE_WHOLE_MESSAGE) { |
| ndef_data.p_data = p_ndef_start; /* Start of NDEF message */ |
| ndef_data.len = len; |
| p_handler->flags |= NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED; |
| |
| /* Indicate that at least one handler has received entire NDEF message |
| */ |
| entire_message_handled = true; |
| } |
| |
| /* Notify NDEF type handler */ |
| tNFA_NDEF_EVT_DATA nfa_ndef_evt_data; |
| nfa_ndef_evt_data.ndef_data = ndef_data; |
| (*p_handler->p_ndef_cback)(NFA_NDEF_DATA_EVT, &nfa_ndef_evt_data); |
| |
| /* Indicate that at lease one handler has received this record */ |
| record_handled = true; |
| |
| /* Look for next handler */ |
| p_handler = nfa_dm_ndef_find_next_handler( |
| p_handler, tnf, p_type, type_len, p_payload, payload_len); |
| } |
| |
| /* Check if at least one handler was notified of this record (only happens |
| * if no default handler was register) */ |
| if ((!record_handled) && (!entire_message_handled)) { |
| /* Unregistered NDEF record type; no default handler */ |
| NFA_TRACE_WARNING1("Unhandled NDEF record (#%i)", rec_count); |
| } |
| |
| rec_count++; |
| p_rec = NDEF_MsgGetNextRec(p_rec); |
| } |
| } |