nfc_t2t_parser.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. /* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
  2. *
  3. * The information contained herein is property of Nordic Semiconductor ASA.
  4. * Terms and conditions of usage are described in detail in NORDIC
  5. * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
  6. *
  7. * Licensees are granted free, non-transferable use of the information. NO
  8. * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
  9. * the file.
  10. *
  11. */
  12. #include <string.h>
  13. #include <stdbool.h>
  14. #include "app_trace.h"
  15. #include "nrf_delay.h"
  16. #include "app_util.h"
  17. #include "nfc_t2t_parser.h"
  18. #ifdef T2T_PARSER_ENABLE
  19. #define T2T_PARSER_TRACE app_trace_log
  20. #else
  21. #define T2T_PARSER_TRACE(...)
  22. #endif
  23. /// Gets least significant nibble (a 4-bit value) from a byte.
  24. #define LSN_GET(val) (val & 0x0F)
  25. /// Gets most significant nibble (a 4-bit value) from a byte.
  26. #define MSN_GET(val) ((val >> 4) & 0x0F)
  27. /**
  28. * @brief Function for inserting the TLV block into a @ref type_2_tag_t structure.
  29. *
  30. * The content of a TLV block structure pointed by the p_tlv_block is copied into a TLV block
  31. * array within the structure pointed by the p_type_2_tag.
  32. *
  33. * @param[in,out] p_type_2_tag Pointer to the structure that contains the TLV blocks array.
  34. * @param[in] p_tlv_block Pointer to the TLV block to insert.
  35. *
  36. * @retval NRF_SUCCESS If the block was inserted successfully.
  37. * @retval NRF_ERROR_NO_MEM If there is already maximum number of blocks stored in the array.
  38. *
  39. */
  40. static ret_code_t type_2_tag_tlv_block_insert(type_2_tag_t * p_type_2_tag,
  41. tlv_block_t * p_tlv_block)
  42. {
  43. if (p_type_2_tag->tlv_count == p_type_2_tag->max_tlv_blocks)
  44. {
  45. return NRF_ERROR_NO_MEM;
  46. }
  47. // Copy contents of the source block.
  48. p_type_2_tag->p_tlv_block_array[p_type_2_tag->tlv_count] = *p_tlv_block;
  49. p_type_2_tag->tlv_count++;
  50. return NRF_SUCCESS;
  51. }
  52. /**
  53. * @brief Function for checking if the TLV block length is correct.
  54. *
  55. * Some TLV block has predefined length:
  56. * TLV_NULL and TLV_TERMINATOR always have a length of 1 byte.
  57. * TLV_LOCK_CONTROL and TLV_MEMORY_CONTROL always have a length of 3 bytes.
  58. *
  59. * @param[in] p_block_to_check Pointer to the structure that contains the TLV block length.
  60. *
  61. * @retval TRUE If the length is correct.
  62. * @retval FALSE Otherwise.
  63. *
  64. */
  65. static bool tlv_block_is_data_length_correct(tlv_block_t * p_block_to_check)
  66. {
  67. switch(p_block_to_check->tag)
  68. {
  69. case TLV_NULL:
  70. case TLV_TERMINATOR:
  71. if (p_block_to_check->length != TLV_NULL_TERMINATOR_LEN)
  72. {
  73. return false;
  74. }
  75. break;
  76. case TLV_LOCK_CONTROL:
  77. case TLV_MEMORY_CONTROL:
  78. if (p_block_to_check->length != TLV_LOCK_MEMORY_CTRL_LEN)
  79. {
  80. return false;
  81. }
  82. break;
  83. case TLV_NDEF_MESSAGE:
  84. case TLV_PROPRIETARY:
  85. default:
  86. // Any length will do.
  87. break;
  88. }
  89. return true;
  90. }
  91. /**
  92. * @brief Function for checking if the end of the tag data area was reached.
  93. *
  94. * @param[in] p_type_2_tag Pointer to the structure that contains the data area size.
  95. * @param[in] offset Current byte offset.
  96. *
  97. * @retval TRUE If the offset indicates the end of the data area.
  98. * @retval FALSE Otherwise.
  99. *
  100. */
  101. static bool type_2_tag_is_end_reached(type_2_tag_t * p_type_2_tag, uint16_t offset)
  102. {
  103. return offset == (p_type_2_tag->cc.data_area_size + T2T_FIRST_DATA_BLOCK_OFFSET);
  104. }
  105. /**
  106. * @brief Function for checking if version of Type 2 Tag specification read from a tag is supported.
  107. *
  108. * @param[in] p_type_2_tag Pointer to the structure that contains the tag version.
  109. *
  110. * @retval TRUE If the version is supported and tag data can be parsed.
  111. * @retval FALSE Otherwise.
  112. *
  113. */
  114. static bool type_2_tag_is_version_supported(type_2_tag_t * p_type_2_tag)
  115. {
  116. // Simple check atm, as only 1 major version has been issued so far, so no backward compatibility
  117. // is needed, tags with newer version implemented shall be rejected according to the doc.
  118. return p_type_2_tag->cc.major_version == T2T_SUPPORTED_MAJOR_VERSION;
  119. }
  120. /**
  121. * @brief Function for checking if the field fits into the data area specified in
  122. * the Capability Container.
  123. *
  124. * @param[in] p_type_2_tag Pointer to the structure that contains the data area size.
  125. * @param[in] offset As Offset of the field to check.
  126. * @param[in] field_length Length of the field to check.
  127. *
  128. * @retval TRUE If the field fits into the data area.
  129. * @retval FALSE If the field exceeds the data area.
  130. *
  131. */
  132. static bool type_2_tag_is_field_within_data_range(type_2_tag_t * p_type_2_tag,
  133. uint16_t offset,
  134. uint16_t field_length)
  135. {
  136. // Invalid argument, return false.
  137. if (field_length == 0)
  138. {
  139. return false;
  140. }
  141. return ( (offset + field_length - 1) <
  142. (p_type_2_tag->cc.data_area_size + T2T_FIRST_DATA_BLOCK_OFFSET) )
  143. && ( offset >= T2T_FIRST_DATA_BLOCK_OFFSET );
  144. }
  145. /**
  146. * @brief Function for reading the tag field of a TLV block from the p_raw_data buffer.
  147. *
  148. * This function reads the tag field containing a TLV block type and inserts its value into
  149. * a structure pointed by the p_tlv_buf pointer.
  150. *
  151. * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far.
  152. * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag.
  153. * @param[in,out] p_t_offset As input: offset of the tag field to read. As output: offset of
  154. * the first byte after the tag field.
  155. * @param[out] p_tlv_buf Pointer to a @ref tlv_block_t structure where the tag type will be
  156. * inserted.
  157. *
  158. * @retval NRF_SUCCESS If the tag field at specified offset is correct.
  159. * @retval NRF_ERROR_INVALID_DATA If the tag field at specified offset exceeds the data
  160. * area specified in the Capability Container.
  161. *
  162. */
  163. static ret_code_t type_2_tag_type_extract(type_2_tag_t * p_type_2_tag,
  164. uint8_t * p_raw_data,
  165. uint16_t * p_t_offset,
  166. tlv_block_t * p_tlv_buf)
  167. {
  168. if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_t_offset, TLV_T_LENGTH))
  169. {
  170. return NRF_ERROR_INVALID_DATA;
  171. }
  172. p_tlv_buf->tag = p_raw_data[*p_t_offset];
  173. *p_t_offset += TLV_T_LENGTH;
  174. return NRF_SUCCESS;
  175. }
  176. /**
  177. * @brief Function for reading the length field of a TLV block from the p_raw_data buffer.
  178. *
  179. * This function reads the length field of a TLV block and inserts its value into a structure
  180. * pointed by the p_tlv_buf pointer.
  181. *
  182. * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far.
  183. * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag.
  184. * @param[in,out] p_l_offset As input: offset of the length field to read. As output: offset of
  185. * the first byte after the length field.
  186. * @param[out] p_tlv_buf Pointer to a @ref tlv_block_t structure where the length will be
  187. * inserted.
  188. *
  189. * @retval NRF_SUCCESS If the length field at specified offset is correct.
  190. * @retval NRF_ERROR_INVALID_DATA If the length field at specified offset exceeds the data
  191. * area specified in the Capability Container or has
  192. * incorrect format.
  193. *
  194. */
  195. static ret_code_t type_2_tag_length_extract(type_2_tag_t * p_type_2_tag,
  196. uint8_t * p_raw_data,
  197. uint16_t * p_l_offset,
  198. tlv_block_t * p_tlv_buf)
  199. {
  200. uint16_t length;
  201. if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_l_offset, TLV_L_SHORT_LENGTH))
  202. {
  203. return NRF_ERROR_INVALID_DATA;
  204. }
  205. length = p_raw_data[*p_l_offset];
  206. if (length == TLV_L_FORMAT_FLAG)
  207. {
  208. // Check another two bytes.
  209. if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_l_offset, TLV_L_LONG_LENGTH))
  210. {
  211. return NRF_ERROR_INVALID_DATA;
  212. }
  213. length = uint16_big_decode(&p_raw_data[*p_l_offset + 1]);
  214. // Long length value cannot be lower than 0xFF.
  215. if (length < 0xFF)
  216. {
  217. return NRF_ERROR_INVALID_DATA;
  218. }
  219. p_tlv_buf->length = length;
  220. *p_l_offset += TLV_L_LONG_LENGTH;
  221. }
  222. else
  223. {
  224. p_tlv_buf->length = length;
  225. *p_l_offset += TLV_L_SHORT_LENGTH;
  226. }
  227. return NRF_SUCCESS;
  228. }
  229. /**
  230. * @brief Function for reading a pointer to the value field of a TLV block from the p_raw_data buffer.
  231. *
  232. * This function reads a pointer to the value field of a TLV block and inserts it into
  233. * a structure pointed by the p_tlv_buf pointer. If there is no value field present in the
  234. * TLV block, NULL is inserted.
  235. *
  236. * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far.
  237. * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag.
  238. * @param[in,out] p_v_offset As input: offset of the value field to read. As output: offset of
  239. * the first byte after the value field.
  240. * @param[in,out] p_tlv_buf Pointer to a @ref tlv_block_t structure where the value field
  241. * pointer will be inserted.
  242. *
  243. * @retval NRF_SUCCESS If the value field at specified offset is correct.
  244. * @retval NRF_ERROR_INVALID_DATA If the value field at specified offset exceeds the data
  245. * area specified in the Capability Container.
  246. *
  247. */
  248. static ret_code_t type_2_tag_value_ptr_extract(type_2_tag_t * p_type_2_tag,
  249. uint8_t * p_raw_data,
  250. uint16_t * p_v_offset,
  251. tlv_block_t * p_tlv_buf)
  252. {
  253. if (p_tlv_buf->length == 0)
  254. {
  255. // Clear the value pointer, don't touch the offset.
  256. p_tlv_buf->p_value = NULL;
  257. }
  258. else
  259. {
  260. if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_v_offset, p_tlv_buf->length))
  261. {
  262. return NRF_ERROR_INVALID_DATA;
  263. }
  264. p_tlv_buf->p_value = p_raw_data + *p_v_offset;
  265. *p_v_offset += p_tlv_buf->length;
  266. }
  267. return NRF_SUCCESS;
  268. }
  269. /**
  270. * @brief Function for reading a single TLV block from the p_raw_data buffer.
  271. *
  272. * This function reads a single TLV block from the p_raw_data buffer and stores its contents in a
  273. * structure pointed by the p_tlv_buf.
  274. *
  275. * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far.
  276. * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag.
  277. * @param[in,out] p_tlv_offset As input: offset of the TLV block to read. As output: offset of the
  278. * next TLV block, 0 if it was the last block.
  279. * @param[out] p_tlv_buf Pointer to a @ref tlv_block_t structure that will be filled with
  280. * the data read.
  281. *
  282. * @retval NRF_SUCCESS If the parsing operation of the block succeeded. Otherwise, an error
  283. * code is returned.
  284. *
  285. */
  286. static ret_code_t type_2_tag_tlv_block_extract(type_2_tag_t * p_type_2_tag,
  287. uint8_t * p_raw_data,
  288. uint16_t * p_offset,
  289. tlv_block_t * p_tlv_buf)
  290. {
  291. ret_code_t err_code;
  292. memset(p_tlv_buf, 0, sizeof(tlv_block_t));
  293. // TLV Tag field.
  294. err_code = type_2_tag_type_extract(p_type_2_tag, p_raw_data, p_offset, p_tlv_buf);
  295. if (err_code != NRF_SUCCESS)
  296. {
  297. return err_code;
  298. }
  299. // Further processing depends on tag field value.
  300. switch(p_tlv_buf->tag)
  301. {
  302. case TLV_NULL:
  303. // Simply ignore NULL blocks, leave the incremented offset.
  304. break;
  305. case TLV_TERMINATOR:
  306. // Write 0 to the offset variable, indicating that last TLV block was found.
  307. *p_offset = 0;
  308. break;
  309. case TLV_LOCK_CONTROL:
  310. case TLV_MEMORY_CONTROL:
  311. case TLV_NDEF_MESSAGE:
  312. case TLV_PROPRIETARY:
  313. default:
  314. // Unknown blocks should also be extracted.
  315. err_code = type_2_tag_length_extract(p_type_2_tag, p_raw_data, p_offset, p_tlv_buf);
  316. if (err_code != NRF_SUCCESS)
  317. {
  318. return err_code;
  319. }
  320. if (p_tlv_buf->length > 0)
  321. {
  322. err_code = type_2_tag_value_ptr_extract(p_type_2_tag, p_raw_data, p_offset, p_tlv_buf);
  323. if (err_code != NRF_SUCCESS)
  324. {
  325. return err_code;
  326. }
  327. }
  328. break;
  329. }
  330. return NRF_SUCCESS;
  331. }
  332. /**
  333. * @brief Function for checking the checksum bytes of the UID stored in internal area.
  334. *
  335. * This function calculates the block check character (BCC) bytes based on the parsed serial number
  336. * and compares them with bytes read from the Type 2 Tag.
  337. *
  338. * @param[in] p_sn Pointer to the @ref type_2_tag_serial_number_t structure to check.
  339. *
  340. * @retval TRUE If the calculated BCC matched the BCC from the tag.
  341. * @retval FALSE Otherwise.
  342. *
  343. */
  344. static bool type_2_tag_is_bcc_correct(type_2_tag_serial_number_t * p_sn)
  345. {
  346. uint8_t bcc1 = (uint8_t)T2T_UID_BCC_CASCADE_BYTE ^
  347. (uint8_t)p_sn->manufacturer_id ^
  348. (uint8_t)((p_sn->serial_number_part_1 >> 8) & 0xFF) ^
  349. (uint8_t)(p_sn->serial_number_part_1 & 0xFF);
  350. uint8_t bcc2 = (uint8_t)((p_sn->serial_number_part_2 >> 24) & 0xFF) ^
  351. (uint8_t)((p_sn->serial_number_part_2 >> 16) & 0xFF) ^
  352. (uint8_t)((p_sn->serial_number_part_2 >> 8) & 0xFF) ^
  353. (uint8_t)( p_sn->serial_number_part_2 & 0xFF);
  354. return (bcc1 == p_sn->check_byte_0) && (bcc2 == p_sn->check_byte_1);
  355. }
  356. /**
  357. * @brief Function for parsing an internal area of a Type 2 Tag.
  358. *
  359. * This function reads data from an internal area in the raw data buffer and fills the
  360. * @ref type_2_tag_serial_number_t structure within @ref type_2_tag_t.
  361. *
  362. * @param[in,out] p_type_2_tag Pointer to the structure that will be filled with parsed data.
  363. * @param[in] p_raw_data Pointer to the buffer with raw data from the tag.
  364. *
  365. * @retval NRF_SUCCESS If the parsing operation of the internal area succeeded.
  366. * Otherwise, an error code is returned.
  367. *
  368. */
  369. static ret_code_t type_2_tag_internal_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data)
  370. {
  371. p_type_2_tag->sn.manufacturer_id = p_raw_data[0];
  372. p_type_2_tag->sn.serial_number_part_1 = uint16_big_decode(&p_raw_data[1]);
  373. p_type_2_tag->sn.check_byte_0 = p_raw_data[3];
  374. p_type_2_tag->sn.serial_number_part_2 = uint32_big_decode(&p_raw_data[4]);
  375. p_type_2_tag->sn.check_byte_1 = p_raw_data[8];
  376. p_type_2_tag->sn.internal = p_raw_data[9];
  377. p_type_2_tag->lock_bytes = uint16_big_decode(&p_raw_data[10]);
  378. if (!type_2_tag_is_bcc_correct(&p_type_2_tag->sn))
  379. {
  380. T2T_PARSER_TRACE("Warning! BCC of the serial number is not correct!\r\n");
  381. }
  382. return NRF_SUCCESS;
  383. }
  384. /**
  385. * @brief Function for parsing a Capabiliy Container area of a Type 2 Tag.
  386. *
  387. * This function reads data from a Capability Container area in the raw data buffer and fills the
  388. * @ref type_2_tag_capability_container_t structure within @ref type_2_tag_t.
  389. *
  390. * @param[in,out] p_type_2_tag Pointer to the structure that will be filled with parsed data.
  391. * @param[in] p_raw_data Pointer to the buffer with raw data from the tag.
  392. *
  393. * @retval NRF_SUCCESS If the parsing operation of the Capability Container succeeded.
  394. * Otherwise, an error code is returned.
  395. *
  396. */
  397. static ret_code_t type_2_tag_cc_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data)
  398. {
  399. uint8_t * p_cc_block = p_raw_data + T2T_CC_BLOCK_OFFSET;
  400. if (p_cc_block[0] != T2T_NFC_FORUM_DEFINED_DATA)
  401. {
  402. return NRF_ERROR_INVALID_DATA;
  403. }
  404. p_type_2_tag->cc.major_version = MSN_GET(p_cc_block[1]);
  405. p_type_2_tag->cc.minor_version = LSN_GET(p_cc_block[1]);
  406. p_type_2_tag->cc.data_area_size = p_cc_block[2] * 8;
  407. p_type_2_tag->cc.read_access = MSN_GET(p_cc_block[3]);
  408. p_type_2_tag->cc.write_access = LSN_GET(p_cc_block[3]);
  409. return NRF_SUCCESS;
  410. }
  411. /**
  412. * @brief Function for parsing a single TLV block.
  413. *
  414. * This function reads a single TLV block from the raw data buffer, from the position indicated by
  415. * the p_tlv_offset, and adds it to the @ref type_2_tag_t structure.
  416. *
  417. * @param[in,out] p_type_2_tag Pointer to the structure that will be filled with parsed data.
  418. * @param[in] p_raw_data Pointer to the buffer with raw data from the tag.
  419. * @param[in,out] p_tlv_offset As input: offset of the TLV block to parse. As output: offset of the
  420. * next TLV block, 0 if it was the last block.
  421. *
  422. * @retval NRF_SUCCESS If the parsing operation of the block succeeded. Otherwise, an error
  423. * code is returned.
  424. *
  425. */
  426. static ret_code_t type_2_tag_tlv_parse(type_2_tag_t * p_type_2_tag,
  427. uint8_t * p_raw_data,
  428. uint16_t * p_tlv_offset)
  429. {
  430. ret_code_t err_code;
  431. tlv_block_t new_block;
  432. // Get tag field.
  433. err_code = type_2_tag_tlv_block_extract(p_type_2_tag, p_raw_data, p_tlv_offset, &new_block);
  434. if (err_code != NRF_SUCCESS)
  435. {
  436. return err_code;
  437. }
  438. if (!tlv_block_is_data_length_correct(&new_block))
  439. {
  440. return NRF_ERROR_INVALID_DATA;
  441. }
  442. // Further action depends on tag type.
  443. switch (new_block.tag)
  444. {
  445. case TLV_NULL:
  446. case TLV_TERMINATOR:
  447. // Ignore them.
  448. break;
  449. case TLV_LOCK_CONTROL:
  450. case TLV_MEMORY_CONTROL:
  451. case TLV_NDEF_MESSAGE:
  452. case TLV_PROPRIETARY:
  453. default:
  454. // Unknown tag types are also added.
  455. err_code = type_2_tag_tlv_block_insert(p_type_2_tag, &new_block);
  456. if (err_code != NRF_SUCCESS)
  457. {
  458. T2T_PARSER_TRACE("Warning! Not enough memory to insert all of the blocks!\r\n");
  459. return err_code;
  460. }
  461. break;
  462. }
  463. return NRF_SUCCESS;
  464. }
  465. void type_2_tag_clear(type_2_tag_t * p_type_2_tag)
  466. {
  467. p_type_2_tag->tlv_count = 0;
  468. memset(&p_type_2_tag->cc, 0, sizeof(p_type_2_tag->cc));
  469. memset(&p_type_2_tag->sn, 0, sizeof(p_type_2_tag->sn));
  470. }
  471. ret_code_t type_2_tag_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data)
  472. {
  473. ret_code_t err_code;
  474. type_2_tag_clear(p_type_2_tag);
  475. err_code = type_2_tag_internal_parse(p_type_2_tag, p_raw_data);
  476. if (err_code != NRF_SUCCESS)
  477. {
  478. return err_code;
  479. }
  480. err_code = type_2_tag_cc_parse(p_type_2_tag, p_raw_data);
  481. if (err_code != NRF_SUCCESS)
  482. {
  483. return err_code;
  484. }
  485. if (!type_2_tag_is_version_supported(p_type_2_tag))
  486. {
  487. return NRF_ERROR_NOT_SUPPORTED;
  488. }
  489. uint16_t offset = T2T_FIRST_DATA_BLOCK_OFFSET;
  490. while(offset > 0)
  491. {
  492. // Check if end of tag is reached (no terminator block was present).
  493. if (type_2_tag_is_end_reached(p_type_2_tag, offset))
  494. {
  495. T2T_PARSER_TRACE("Warning! No terminator block was found in the tag!\r\n");
  496. break;
  497. }
  498. err_code = type_2_tag_tlv_parse(p_type_2_tag, p_raw_data, &offset);
  499. if (err_code != NRF_SUCCESS)
  500. {
  501. return err_code;
  502. }
  503. }
  504. return NRF_SUCCESS;
  505. }
  506. void type_2_tag_printout(type_2_tag_t * p_type_2_tag)
  507. {
  508. uint32_t i, j;
  509. T2T_PARSER_TRACE("Type 2 Tag contents:\r\n");
  510. T2T_PARSER_TRACE("\r\n Number of TLV blocks: %d\r\n", p_type_2_tag->tlv_count);
  511. T2T_PARSER_TRACE("\r\n Internal data:\r\n");
  512. T2T_PARSER_TRACE(" Manufacturer ID: 0x%02x\r\n", p_type_2_tag->sn.manufacturer_id);
  513. T2T_PARSER_TRACE(" Serial number part 1: 0x%04x\r\n", p_type_2_tag->sn.serial_number_part_1);
  514. T2T_PARSER_TRACE(" Check byte 0: 0x%02x\r\n", p_type_2_tag->sn.check_byte_0);
  515. T2T_PARSER_TRACE(" Serial number part 2: 0x%08lx\r\n", p_type_2_tag->sn.serial_number_part_2);
  516. T2T_PARSER_TRACE(" Check byte 1: 0x%02x\r\n", p_type_2_tag->sn.check_byte_1);
  517. T2T_PARSER_TRACE(" Internal byte: 0x%02x\r\n", p_type_2_tag->sn.internal);
  518. T2T_PARSER_TRACE(" Lock bytes: 0x%04x\r\n", p_type_2_tag->lock_bytes);
  519. T2T_PARSER_TRACE("\r\n Capability Container data:\r\n");
  520. T2T_PARSER_TRACE(" Major version number: %d\r\n", p_type_2_tag->cc.major_version);
  521. T2T_PARSER_TRACE(" Minor version number: %d\r\n", p_type_2_tag->cc.minor_version);
  522. T2T_PARSER_TRACE(" Data area size: %d\r\n", p_type_2_tag->cc.data_area_size);
  523. T2T_PARSER_TRACE(" Read access: 0x%02X\r\n", p_type_2_tag->cc.read_access);
  524. T2T_PARSER_TRACE(" Write access: 0x%02X\r\n", p_type_2_tag->cc.write_access);
  525. nrf_delay_ms(100);
  526. for(i = 0; i < p_type_2_tag->tlv_count; i++)
  527. {
  528. T2T_PARSER_TRACE("\r\n TLV block 0x%02X: ", p_type_2_tag->p_tlv_block_array[i].tag);
  529. switch(p_type_2_tag->p_tlv_block_array[i].tag)
  530. {
  531. case TLV_LOCK_CONTROL:
  532. T2T_PARSER_TRACE("Lock Control\r\n");
  533. break;
  534. case TLV_MEMORY_CONTROL:
  535. T2T_PARSER_TRACE("Memory Control\r\n");
  536. break;
  537. case TLV_NDEF_MESSAGE:
  538. T2T_PARSER_TRACE("NDEF Message\r\n");
  539. break;
  540. case TLV_PROPRIETARY:
  541. T2T_PARSER_TRACE("Proprietary\r\n");
  542. break;
  543. case TLV_NULL:
  544. T2T_PARSER_TRACE("Null\r\n");
  545. break;
  546. case TLV_TERMINATOR:
  547. T2T_PARSER_TRACE("Terminator\r\n");
  548. break;
  549. default:
  550. T2T_PARSER_TRACE("Unknown\r\n");
  551. break;
  552. }
  553. T2T_PARSER_TRACE(" Length: %d\r\n", p_type_2_tag->p_tlv_block_array[i].length);
  554. if (p_type_2_tag->p_tlv_block_array[i].length > 0)
  555. {
  556. T2T_PARSER_TRACE(" Data: ");
  557. for (j = 0; j < p_type_2_tag->p_tlv_block_array[i].length; j++)
  558. {
  559. T2T_PARSER_TRACE("%02X ", p_type_2_tag->p_tlv_block_array[i].p_value[j]);
  560. if ( ((j + 1) % 16 == 0) && (j != p_type_2_tag->p_tlv_block_array[i].length - 1) )
  561. {
  562. T2T_PARSER_TRACE("\r\n ");
  563. nrf_delay_ms(10);
  564. }
  565. }
  566. T2T_PARSER_TRACE("\r\n");
  567. }
  568. }
  569. }