ibutton-app.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. #include "ibutton-app.h"
  2. #include <stdarg.h>
  3. #include <callback-connector.h>
  4. #include <m-string.h>
  5. const char* iButtonApp::app_folder = "ibutton";
  6. const char* iButtonApp::app_extension = ".ibtn";
  7. void iButtonApp::run(void) {
  8. iButtonEvent event;
  9. bool consumed;
  10. bool exit = false;
  11. scenes[current_scene]->on_enter(this);
  12. while(!exit) {
  13. view.receive_event(&event);
  14. consumed = scenes[current_scene]->on_event(this, &event);
  15. if(!consumed) {
  16. if(event.type == iButtonEvent::Type::EventTypeBack) {
  17. exit = switch_to_previous_scene();
  18. }
  19. }
  20. };
  21. scenes[current_scene]->on_exit(this);
  22. }
  23. void iButtonApp::print_key_data(void) {
  24. uint8_t* key_data = key.get_data();
  25. switch(key.get_key_type()) {
  26. case iButtonKeyType::KeyDallas:
  27. printf(
  28. "Dallas %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
  29. key_data[0],
  30. key_data[1],
  31. key_data[2],
  32. key_data[3],
  33. key_data[4],
  34. key_data[5],
  35. key_data[6],
  36. key_data[7]);
  37. break;
  38. case iButtonKeyType::KeyCyfral:
  39. printf("Cyfral %02X %02X\r\n", key_data[0], key_data[1]);
  40. break;
  41. case iButtonKeyType::KeyMetakom:
  42. printf(
  43. "Metakom %02X %02X %02X %02X\r\n", key_data[0], key_data[1], key_data[2], key_data[3]);
  44. break;
  45. }
  46. }
  47. bool iButtonApp::read_hex_byte(string_t args, uint8_t* byte) {
  48. char* endptr;
  49. *byte = strtoul(string_get_cstr(args), &endptr, 16);
  50. if(*endptr == '\0') {
  51. return false;
  52. }
  53. size_t ws = string_search_char(args, ' ');
  54. if(ws != 2) {
  55. return false;
  56. }
  57. string_right(args, ws);
  58. string_strim(args);
  59. return true;
  60. }
  61. void iButtonApp::cli_cmd_callback(Cli* cli, string_t args, void* context) {
  62. iButtonApp::Scene scene;
  63. string_t cmd;
  64. string_init(cmd);
  65. if(!string_cmp_str(args, "read")) {
  66. scene = iButtonApp::Scene::SceneCliRead;
  67. printf("Reading key ...\r\n");
  68. } else {
  69. // Parse write / emulate commands
  70. size_t ws = string_search_char(args, ' ');
  71. if(ws == STRING_FAILURE) {
  72. printf("Incorrect input. Try tm <read | write | emulate> [key_type] [key_data]");
  73. string_clear(cmd);
  74. return;
  75. } else {
  76. string_set_n(cmd, args, 0, ws);
  77. string_right(args, ws);
  78. string_strim(args);
  79. }
  80. if(!string_cmp_str(cmd, "write")) {
  81. scene = iButtonApp::Scene::SceneCliWrite;
  82. } else if(!string_cmp_str(cmd, "emulate")) {
  83. scene = iButtonApp::Scene::SceneCliEmulate;
  84. } else {
  85. printf("Incorrect input. Try tm <write | emulate> <key_type> <key_data>");
  86. string_clear(cmd);
  87. return;
  88. }
  89. string_clear(cmd);
  90. // Parse key type
  91. string_t key_type;
  92. string_init(key_type);
  93. ws = string_search_char(args, ' ');
  94. string_set_n(key_type, args, 0, ws);
  95. uint8_t bytes_to_read = 0;
  96. if(!string_cmp_str(key_type, "0")) {
  97. key.set_type(iButtonKeyType::KeyDallas);
  98. bytes_to_read = 8;
  99. } else if(!string_cmp_str(key_type, "1")) {
  100. key.set_type(iButtonKeyType::KeyCyfral);
  101. bytes_to_read = 2;
  102. } else if(!string_cmp_str(key_type, "2")) {
  103. key.set_type(iButtonKeyType::KeyMetakom);
  104. bytes_to_read = 4;
  105. } else {
  106. printf("Incorrect key type. Try 0 - KeyDallas, 1 - KeyCyfral, 2 - KeyMetakom");
  107. string_clear(key_type);
  108. return;
  109. }
  110. string_clear(key_type);
  111. // Read key data
  112. string_right(args, 1);
  113. string_strim(args);
  114. uint8_t key_data[8] = {};
  115. uint8_t i = 0;
  116. bool ret = true;
  117. while((i < bytes_to_read) && ret) {
  118. ret = read_hex_byte(args, &key_data[i++]);
  119. }
  120. if(i != bytes_to_read) {
  121. printf("Incorrect key data. Type %d key data hex digits", bytes_to_read);
  122. return;
  123. }
  124. key.set_data(key_data, bytes_to_read);
  125. if(scene == iButtonApp::Scene::SceneCliWrite) {
  126. printf("Writing key ");
  127. } else {
  128. printf("Emulating key ");
  129. }
  130. print_key_data();
  131. }
  132. switch_to_next_scene(scene);
  133. // Wait return event
  134. iButtonApp::CliEvent result;
  135. if(osMessageQueueGet(cli_event_result, &result, NULL, osWaitForever) != osOK) {
  136. printf("Command execution error");
  137. return;
  138. }
  139. // Process return event
  140. switch(result) {
  141. case iButtonApp::CliEvent::CliReadSuccess:
  142. case iButtonApp::CliEvent::CliReadCRCError:
  143. print_key_data();
  144. if(result == iButtonApp::CliEvent::CliReadCRCError) {
  145. printf("Warning: invalid CRC");
  146. }
  147. break;
  148. case iButtonApp::CliEvent::CliReadNotKeyError:
  149. printf("Read error: not a key");
  150. break;
  151. case iButtonApp::CliEvent::CliTimeout:
  152. printf("Timeout error");
  153. break;
  154. case iButtonApp::CliEvent::CliInterrupt:
  155. printf("Command interrupted");
  156. break;
  157. case iButtonApp::CliEvent::CliWriteSuccess:
  158. printf("Write success");
  159. break;
  160. case iButtonApp::CliEvent::CliWriteFail:
  161. printf("Write fail");
  162. break;
  163. default:
  164. break;
  165. }
  166. return;
  167. }
  168. void iButtonApp::cli_send_event(iButtonApp::CliEvent scene) {
  169. osMessageQueuePut(cli_event_result, &scene, 0, osWaitForever);
  170. }
  171. iButtonApp::iButtonApp() {
  172. api_hal_power_insomnia_enter();
  173. cli_event_result = osMessageQueueNew(1, sizeof(iButtonApp::Scene), NULL);
  174. key_worker = new KeyWorker(&ibutton_gpio);
  175. sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex"));
  176. fs_api = static_cast<FS_Api*>(furi_record_open("sdcard"));
  177. cli = static_cast<Cli*>(furi_record_open("cli"));
  178. notification = static_cast<NotificationApp*>(furi_record_open("notification"));
  179. auto callback = cbc::obtain_connector(this, &iButtonApp::cli_cmd_callback);
  180. cli_add_command(cli, "tm", callback, cli);
  181. // we need random
  182. srand(DWT->CYCCNT);
  183. }
  184. iButtonApp::~iButtonApp() {
  185. cli_delete_command(cli, "tm");
  186. furi_record_close("sdcard-ex");
  187. furi_record_close("sdcard");
  188. furi_record_close("cli");
  189. furi_record_close("notification");
  190. osMessageQueueDelete(cli_event_result);
  191. for(std::map<Scene, iButtonScene*>::iterator it = scenes.begin(); it != scenes.end(); ++it) {
  192. delete it->second;
  193. scenes.erase(it);
  194. }
  195. api_hal_power_insomnia_exit();
  196. }
  197. iButtonAppViewManager* iButtonApp::get_view_manager() {
  198. return &view;
  199. }
  200. void iButtonApp::switch_to_next_scene(Scene next_scene) {
  201. previous_scenes_list.push_front(current_scene);
  202. if(next_scene != Scene::SceneExit) {
  203. scenes[current_scene]->on_exit(this);
  204. current_scene = next_scene;
  205. scenes[current_scene]->on_enter(this);
  206. }
  207. }
  208. void iButtonApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {
  209. Scene previous_scene = Scene::SceneStart;
  210. bool scene_found = false;
  211. while(!scene_found) {
  212. previous_scene = get_previous_scene();
  213. for(Scene element : scenes_list) {
  214. if(previous_scene == element || previous_scene == Scene::SceneStart) {
  215. scene_found = true;
  216. break;
  217. }
  218. }
  219. }
  220. scenes[current_scene]->on_exit(this);
  221. current_scene = previous_scene;
  222. scenes[current_scene]->on_enter(this);
  223. }
  224. bool iButtonApp::switch_to_previous_scene(uint8_t count) {
  225. Scene previous_scene = Scene::SceneStart;
  226. for(uint8_t i = 0; i < count; i++) {
  227. previous_scene = get_previous_scene();
  228. if(previous_scene == Scene::SceneExit) break;
  229. }
  230. if(previous_scene == Scene::SceneExit) {
  231. return true;
  232. } else {
  233. scenes[current_scene]->on_exit(this);
  234. current_scene = previous_scene;
  235. scenes[current_scene]->on_enter(this);
  236. return false;
  237. }
  238. }
  239. iButtonApp::Scene iButtonApp::get_previous_scene() {
  240. Scene scene = previous_scenes_list.front();
  241. previous_scenes_list.pop_front();
  242. return scene;
  243. }
  244. const GpioPin* iButtonApp::get_ibutton_pin() {
  245. // TODO open record
  246. return &ibutton_gpio;
  247. }
  248. KeyWorker* iButtonApp::get_key_worker() {
  249. return key_worker;
  250. }
  251. iButtonKey* iButtonApp::get_key() {
  252. return &key;
  253. }
  254. SdCard_Api* iButtonApp::get_sd_ex_api() {
  255. return sd_ex_api;
  256. }
  257. FS_Api* iButtonApp::get_fs_api() {
  258. return fs_api;
  259. }
  260. char* iButtonApp::get_file_name() {
  261. return file_name;
  262. }
  263. uint8_t iButtonApp::get_file_name_size() {
  264. return file_name_size;
  265. }
  266. void iButtonApp::notify_green_blink() {
  267. notification_message(notification, &sequence_blink_green_10);
  268. }
  269. void iButtonApp::notify_yellow_blink() {
  270. notification_message(notification, &sequence_blink_yellow_10);
  271. }
  272. void iButtonApp::notify_red_blink() {
  273. notification_message(notification, &sequence_blink_red_10);
  274. }
  275. void iButtonApp::notify_error() {
  276. notification_message(notification, &sequence_error);
  277. }
  278. void iButtonApp::notify_success() {
  279. notification_message(notification, &sequence_success);
  280. }
  281. void iButtonApp::notify_green_on() {
  282. notification_message_block(notification, &sequence_set_green_255);
  283. }
  284. void iButtonApp::notify_green_off() {
  285. notification_message(notification, &sequence_reset_green);
  286. }
  287. void iButtonApp::notify_red_on() {
  288. notification_message_block(notification, &sequence_set_red_255);
  289. }
  290. void iButtonApp::notify_red_off() {
  291. notification_message(notification, &sequence_reset_red);
  292. }
  293. void iButtonApp::set_text_store(const char* text...) {
  294. va_list args;
  295. va_start(args, text);
  296. vsnprintf(text_store, text_store_size, text, args);
  297. va_end(args);
  298. }
  299. char* iButtonApp::get_text_store() {
  300. return text_store;
  301. }
  302. uint8_t iButtonApp::get_text_store_size() {
  303. return text_store_size;
  304. }
  305. void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) {
  306. const uint8_t prefix_size = 9;
  307. const char* prefix[prefix_size] = {
  308. "ancient",
  309. "hollow",
  310. "strange",
  311. "disappeared",
  312. "unknown",
  313. "unthinkable",
  314. "unnamable",
  315. "nameless",
  316. "my",
  317. };
  318. const uint8_t suffix_size = 8;
  319. const char* suffix[suffix_size] = {
  320. "door",
  321. "entrance",
  322. "doorway",
  323. "entry",
  324. "portal",
  325. "entree",
  326. "opening",
  327. "crack",
  328. };
  329. sniprintf(
  330. name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]);
  331. // to upper
  332. name[0] = name[0] - 0x20;
  333. }
  334. // file managment
  335. void iButtonApp::show_file_error_message(const char* error_text) {
  336. set_text_store(error_text);
  337. get_sd_ex_api()->show_error(get_sd_ex_api()->context, get_text_store());
  338. }
  339. bool iButtonApp::save_key(const char* key_name) {
  340. File key_file;
  341. string_t key_file_name;
  342. bool result = false;
  343. FS_Error fs_result;
  344. uint16_t write_count;
  345. // Create ibutton directory if necessary
  346. fs_result = get_fs_api()->common.mkdir(app_folder);
  347. if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
  348. show_file_error_message("Cannot create\napplication folder");
  349. return false;
  350. };
  351. // First remove key if it was saved
  352. string_init_set_str(key_file_name, app_folder);
  353. string_cat_str(key_file_name, "/");
  354. string_cat_str(key_file_name, get_key()->get_name());
  355. string_cat_str(key_file_name, app_extension);
  356. fs_result = get_fs_api()->common.remove(string_get_cstr(key_file_name));
  357. if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) {
  358. string_clear(key_file_name);
  359. show_file_error_message("Cannot remove\nold key file");
  360. return false;
  361. };
  362. // Save the key
  363. get_key()->set_name(key_name);
  364. string_set_str(key_file_name, app_folder);
  365. string_cat_str(key_file_name, "/");
  366. string_cat_str(key_file_name, get_key()->get_name());
  367. string_cat_str(key_file_name, app_extension);
  368. bool res = get_fs_api()->file.open(
  369. &key_file, string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS);
  370. string_clear(key_file_name);
  371. if(res) {
  372. // type header
  373. const char* key_type = "E";
  374. switch(get_key()->get_key_type()) {
  375. case iButtonKeyType::KeyCyfral:
  376. key_type = "C";
  377. break;
  378. case iButtonKeyType::KeyDallas:
  379. key_type = "D";
  380. break;
  381. case iButtonKeyType::KeyMetakom:
  382. key_type = "M";
  383. break;
  384. }
  385. write_count = get_fs_api()->file.write(&key_file, key_type, 1);
  386. if(key_file.error_id != FSE_OK || write_count != 1) {
  387. show_file_error_message("Cannot write\nto key file");
  388. get_fs_api()->file.close(&key_file);
  389. return false;
  390. }
  391. const uint8_t byte_text_size = 4;
  392. char byte_text[byte_text_size];
  393. for(uint8_t i = 0; i < get_key()->get_type_data_size(); i++) {
  394. sniprintf(byte_text, byte_text_size, " %02X", get_key()->get_data()[i]);
  395. write_count = get_fs_api()->file.write(&key_file, byte_text, 3);
  396. if(key_file.error_id != FSE_OK || write_count != 3) {
  397. show_file_error_message("Cannot write\nto key file");
  398. get_fs_api()->file.close(&key_file);
  399. return false;
  400. }
  401. }
  402. result = true;
  403. } else {
  404. show_file_error_message("Cannot create\nnew key file");
  405. }
  406. get_fs_api()->file.close(&key_file);
  407. get_sd_ex_api()->check_error(get_sd_ex_api()->context);
  408. return result;
  409. }
  410. bool iButtonApp::load_key() {
  411. bool result = false;
  412. // Input events and views are managed by file_select
  413. bool res = get_sd_ex_api()->file_select(
  414. get_sd_ex_api()->context,
  415. app_folder,
  416. app_extension,
  417. get_file_name(),
  418. get_file_name_size(),
  419. get_key()->get_name());
  420. if(res) {
  421. string_t key_str;
  422. File key_file;
  423. uint16_t read_count;
  424. // Get key file path
  425. string_init_set_str(key_str, app_folder);
  426. string_cat_str(key_str, "/");
  427. string_cat_str(key_str, get_file_name());
  428. string_cat_str(key_str, app_extension);
  429. // Open key file
  430. get_fs_api()->file.open(
  431. &key_file, string_get_cstr(key_str), FSAM_READ, FSOM_OPEN_EXISTING);
  432. string_clear(key_str);
  433. if(key_file.error_id != FSE_OK) {
  434. show_file_error_message("Cannot open\nkey file");
  435. get_fs_api()->file.close(&key_file);
  436. return false;
  437. }
  438. const uint8_t byte_text_size = 4;
  439. char byte_text[byte_text_size] = {0, 0, 0, 0};
  440. // load type header
  441. read_count = get_fs_api()->file.read(&key_file, byte_text, 1);
  442. if(key_file.error_id != FSE_OK || read_count != 1) {
  443. show_file_error_message("Cannot read\nkey file");
  444. get_fs_api()->file.close(&key_file);
  445. return false;
  446. }
  447. iButtonKeyType key_type = iButtonKeyType::KeyCyfral;
  448. if(strcmp(byte_text, "C") == 0) {
  449. key_type = iButtonKeyType::KeyCyfral;
  450. } else if(strcmp(byte_text, "M") == 0) {
  451. key_type = iButtonKeyType::KeyMetakom;
  452. } else if(strcmp(byte_text, "D") == 0) {
  453. key_type = iButtonKeyType::KeyDallas;
  454. } else {
  455. show_file_error_message("Cannot parse\nkey file");
  456. get_fs_api()->file.close(&key_file);
  457. return false;
  458. }
  459. get_key()->set_type(key_type);
  460. // load data
  461. uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0};
  462. for(uint8_t i = 0; i < get_key()->get_type_data_size(); i++) {
  463. // space
  464. read_count = get_fs_api()->file.read(&key_file, byte_text, 1);
  465. if(key_file.error_id != FSE_OK || read_count != 1) {
  466. show_file_error_message("Cannot read\nkey file");
  467. get_fs_api()->file.close(&key_file);
  468. return false;
  469. }
  470. // value
  471. read_count = get_fs_api()->file.read(&key_file, byte_text, 2);
  472. if(key_file.error_id != FSE_OK || read_count != 2) {
  473. show_file_error_message("Cannot read\nkey file");
  474. get_fs_api()->file.close(&key_file);
  475. return false;
  476. }
  477. // convert hex value to byte
  478. key_data[i] = strtol(byte_text, NULL, 16);
  479. }
  480. get_fs_api()->file.close(&key_file);
  481. get_key()->set_name(get_file_name());
  482. get_key()->set_type(key_type);
  483. get_key()->set_data(key_data, IBUTTON_KEY_DATA_SIZE);
  484. result = true;
  485. }
  486. get_sd_ex_api()->check_error(get_sd_ex_api()->context);
  487. return result;
  488. }
  489. bool iButtonApp::delete_key() {
  490. iButtonKey* key = get_key();
  491. string_t key_file_name;
  492. bool result = false;
  493. string_init_set_str(key_file_name, app_folder);
  494. string_cat_str(key_file_name, "/");
  495. string_cat_str(key_file_name, key->get_name());
  496. string_cat_str(key_file_name, app_extension);
  497. result = (get_fs_api()->common.remove(string_get_cstr(key_file_name)) == FSE_OK);
  498. string_clear(key_file_name);
  499. return result;
  500. }