picopass_worker.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include "picopass_worker_i.h"
  2. #define TAG "PicopassWorker"
  3. const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
  4. const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
  5. static void picopass_worker_enable_field() {
  6. st25r3916TxRxOn();
  7. rfalLowPowerModeStop();
  8. rfalWorker();
  9. }
  10. static ReturnCode picopass_worker_disable_field(ReturnCode rc) {
  11. st25r3916TxRxOff();
  12. rfalLowPowerModeStart();
  13. return rc;
  14. }
  15. /***************************** Picopass Worker API *******************************/
  16. PicopassWorker* picopass_worker_alloc() {
  17. PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker));
  18. // Worker thread attributes
  19. picopass_worker->thread = furi_thread_alloc();
  20. furi_thread_set_name(picopass_worker->thread, "PicopassWorker");
  21. furi_thread_set_stack_size(picopass_worker->thread, 8192);
  22. furi_thread_set_callback(picopass_worker->thread, picopass_worker_task);
  23. furi_thread_set_context(picopass_worker->thread, picopass_worker);
  24. picopass_worker->callback = NULL;
  25. picopass_worker->context = NULL;
  26. picopass_worker->storage = furi_record_open(RECORD_STORAGE);
  27. picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
  28. return picopass_worker;
  29. }
  30. void picopass_worker_free(PicopassWorker* picopass_worker) {
  31. furi_assert(picopass_worker);
  32. furi_thread_free(picopass_worker->thread);
  33. furi_record_close(RECORD_STORAGE);
  34. free(picopass_worker);
  35. }
  36. PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) {
  37. return picopass_worker->state;
  38. }
  39. void picopass_worker_start(
  40. PicopassWorker* picopass_worker,
  41. PicopassWorkerState state,
  42. PicopassDeviceData* dev_data,
  43. PicopassWorkerCallback callback,
  44. void* context) {
  45. furi_assert(picopass_worker);
  46. furi_assert(dev_data);
  47. picopass_worker->callback = callback;
  48. picopass_worker->context = context;
  49. picopass_worker->dev_data = dev_data;
  50. picopass_worker_change_state(picopass_worker, state);
  51. furi_thread_start(picopass_worker->thread);
  52. }
  53. void picopass_worker_stop(PicopassWorker* picopass_worker) {
  54. furi_assert(picopass_worker);
  55. if(picopass_worker->state == PicopassWorkerStateBroken ||
  56. picopass_worker->state == PicopassWorkerStateReady) {
  57. return;
  58. }
  59. picopass_worker_disable_field(ERR_NONE);
  60. picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
  61. furi_thread_join(picopass_worker->thread);
  62. }
  63. void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
  64. picopass_worker->state = state;
  65. }
  66. /***************************** Picopass Worker Thread *******************************/
  67. ReturnCode picopass_detect_card(int timeout) {
  68. UNUSED(timeout);
  69. ReturnCode err;
  70. err = rfalPicoPassPollerInitialize();
  71. if(err != ERR_NONE) {
  72. FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err);
  73. return err;
  74. }
  75. err = rfalFieldOnAndStartGT();
  76. if(err != ERR_NONE) {
  77. FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err);
  78. return err;
  79. }
  80. err = rfalPicoPassPollerCheckPresence();
  81. if(err != ERR_RF_COLLISION) {
  82. FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err);
  83. return err;
  84. }
  85. return ERR_NONE;
  86. }
  87. ReturnCode picopass_read_card(PicopassBlock* AA1) {
  88. rfalPicoPassIdentifyRes idRes;
  89. rfalPicoPassSelectRes selRes;
  90. rfalPicoPassReadCheckRes rcRes;
  91. rfalPicoPassCheckRes chkRes;
  92. ReturnCode err;
  93. uint8_t div_key[8] = {0};
  94. uint8_t mac[4] = {0};
  95. uint8_t ccnr[12] = {0};
  96. err = rfalPicoPassPollerIdentify(&idRes);
  97. if(err != ERR_NONE) {
  98. FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
  99. return err;
  100. }
  101. err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
  102. if(err != ERR_NONE) {
  103. FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
  104. return err;
  105. }
  106. err = rfalPicoPassPollerReadCheck(&rcRes);
  107. if(err != ERR_NONE) {
  108. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  109. return err;
  110. }
  111. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  112. loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
  113. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  114. err = rfalPicoPassPollerCheck(mac, &chkRes);
  115. if(err != ERR_NONE) {
  116. FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
  117. return err;
  118. }
  119. rfalPicoPassReadBlockRes csn;
  120. err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn);
  121. memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data));
  122. rfalPicoPassReadBlockRes cfg;
  123. err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg);
  124. memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data));
  125. size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT;
  126. for(size_t i = 2; i < app_limit; i++) {
  127. FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i);
  128. rfalPicoPassReadBlockRes block;
  129. err = rfalPicoPassPollerReadBlock(i, &block);
  130. if(err != ERR_NONE) {
  131. FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err);
  132. return err;
  133. }
  134. FURI_LOG_D(
  135. TAG,
  136. "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x",
  137. i,
  138. block.data[0],
  139. block.data[1],
  140. block.data[2],
  141. block.data[3],
  142. block.data[4],
  143. block.data[5],
  144. block.data[6],
  145. block.data[7]);
  146. memcpy(AA1[i].data, block.data, sizeof(block.data));
  147. }
  148. return ERR_NONE;
  149. }
  150. int32_t picopass_worker_task(void* context) {
  151. PicopassWorker* picopass_worker = context;
  152. picopass_worker_enable_field();
  153. if(picopass_worker->state == PicopassWorkerStateDetect) {
  154. picopass_worker_detect(picopass_worker);
  155. }
  156. picopass_worker_disable_field(ERR_NONE);
  157. picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
  158. return 0;
  159. }
  160. void picopass_worker_detect(PicopassWorker* picopass_worker) {
  161. picopass_device_data_clear(picopass_worker->dev_data);
  162. PicopassDeviceData* dev_data = picopass_worker->dev_data;
  163. PicopassBlock* AA1 = dev_data->AA1;
  164. PicopassPacs* pacs = &dev_data->pacs;
  165. ReturnCode err;
  166. while(picopass_worker->state == PicopassWorkerStateDetect) {
  167. if(picopass_detect_card(1000) == ERR_NONE) {
  168. // Process first found device
  169. err = picopass_read_card(AA1);
  170. if(err != ERR_NONE) {
  171. FURI_LOG_E(TAG, "picopass_read_card error %d", err);
  172. }
  173. err = picopass_device_parse_credential(AA1, pacs);
  174. if(err != ERR_NONE) {
  175. FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
  176. }
  177. err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
  178. if(err != ERR_NONE) {
  179. FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
  180. }
  181. // Notify caller and exit
  182. if(picopass_worker->callback) {
  183. picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
  184. }
  185. break;
  186. }
  187. furi_delay_ms(100);
  188. }
  189. }