subghz_cli.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. #include "subghz_cli.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <lib/toolbox/args.h>
  5. #include <lib/subghz/subghz_keystore.h>
  6. #include <lib/subghz/receiver.h>
  7. #include <lib/subghz/transmitter.h>
  8. #include <lib/subghz/subghz_file_encoder_worker.h>
  9. #include <lib/subghz/protocols/protocol_items.h>
  10. #include "helpers/subghz_chat.h"
  11. #include <notification/notification_messages.h>
  12. #include <flipper_format/flipper_format_i.h>
  13. #include <flipper.pb.h>
  14. #include <pb_decode.h>
  15. #define SUBGHZ_FREQUENCY_RANGE_STR \
  16. "299999755...348000000 or 386999938...464000000 or 778999847...928000000"
  17. #define SUBGHZ_REGION_FILENAME "/int/.region_data"
  18. void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
  19. UNUSED(context);
  20. uint32_t frequency = 433920000;
  21. if(furi_string_size(args)) {
  22. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  23. if(ret != 1) {
  24. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  25. cli_print_usage("subghz tx_carrier", "<Frequency: in Hz>", furi_string_get_cstr(args));
  26. return;
  27. }
  28. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  29. printf(
  30. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  31. frequency);
  32. return;
  33. }
  34. }
  35. furi_hal_subghz_reset();
  36. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  37. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  38. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
  39. furi_hal_gpio_write(&gpio_cc1101_g0, true);
  40. furi_hal_power_suppress_charge_enter();
  41. if(furi_hal_subghz_tx()) {
  42. printf("Transmitting at frequency %lu Hz\r\n", frequency);
  43. printf("Press CTRL+C to stop\r\n");
  44. while(!cli_cmd_interrupt_received(cli)) {
  45. furi_delay_ms(250);
  46. }
  47. } else {
  48. printf("This frequency can only be used for RX in your region\r\n");
  49. }
  50. furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
  51. furi_hal_subghz_sleep();
  52. furi_hal_power_suppress_charge_exit();
  53. }
  54. void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) {
  55. UNUSED(context);
  56. uint32_t frequency = 433920000;
  57. if(furi_string_size(args)) {
  58. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  59. if(ret != 1) {
  60. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  61. cli_print_usage("subghz rx_carrier", "<Frequency: in Hz>", furi_string_get_cstr(args));
  62. return;
  63. }
  64. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  65. printf(
  66. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  67. frequency);
  68. return;
  69. }
  70. }
  71. furi_hal_subghz_reset();
  72. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  73. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  74. printf("Receiving at frequency %lu Hz\r\n", frequency);
  75. printf("Press CTRL+C to stop\r\n");
  76. furi_hal_power_suppress_charge_enter();
  77. furi_hal_subghz_rx();
  78. while(!cli_cmd_interrupt_received(cli)) {
  79. furi_delay_ms(250);
  80. printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi());
  81. fflush(stdout);
  82. }
  83. furi_hal_power_suppress_charge_exit();
  84. furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
  85. furi_hal_subghz_sleep();
  86. }
  87. void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
  88. UNUSED(context);
  89. uint32_t frequency = 433920000;
  90. uint32_t key = 0x0074BADE;
  91. uint32_t repeat = 10;
  92. uint32_t te = 403;
  93. if(furi_string_size(args)) {
  94. int ret =
  95. sscanf(furi_string_get_cstr(args), "%lx %lu %lu %lu", &key, &frequency, &te, &repeat);
  96. if(ret != 4) {
  97. printf(
  98. "sscanf returned %d, key: %lx, frequency: %lu, te:%lu, repeat: %lu\r\n",
  99. ret,
  100. key,
  101. frequency,
  102. te,
  103. repeat);
  104. cli_print_usage(
  105. "subghz tx",
  106. "<3 Byte Key: in hex> <Frequency: in Hz> <Te us> <Repeat count>",
  107. furi_string_get_cstr(args));
  108. return;
  109. }
  110. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  111. printf(
  112. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  113. frequency);
  114. return;
  115. }
  116. }
  117. printf(
  118. "Transmitting at %lu, key %lx, te %lu, repeat %lu. Press CTRL+C to stop\r\n",
  119. frequency,
  120. key,
  121. te,
  122. repeat);
  123. FuriString* flipper_format_string = furi_string_alloc_printf(
  124. "Protocol: Princeton\n"
  125. "Bit: 24\n"
  126. "Key: 00 00 00 00 00 %02X %02X %02X\n"
  127. "TE: %lu\n"
  128. "Repeat: %lu\n",
  129. (uint8_t)((key >> 16) & 0xFFU),
  130. (uint8_t)((key >> 8) & 0xFFU),
  131. (uint8_t)(key & 0xFFU),
  132. te,
  133. repeat);
  134. FlipperFormat* flipper_format = flipper_format_string_alloc();
  135. Stream* stream = flipper_format_get_raw_stream(flipper_format);
  136. stream_clean(stream);
  137. stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
  138. SubGhzEnvironment* environment = subghz_environment_alloc();
  139. subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
  140. SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
  141. subghz_transmitter_deserialize(transmitter, flipper_format);
  142. furi_hal_subghz_reset();
  143. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  144. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  145. furi_hal_power_suppress_charge_enter();
  146. furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter);
  147. while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) {
  148. printf(".");
  149. fflush(stdout);
  150. furi_delay_ms(333);
  151. }
  152. furi_hal_subghz_stop_async_tx();
  153. furi_hal_subghz_sleep();
  154. furi_hal_power_suppress_charge_exit();
  155. flipper_format_free(flipper_format);
  156. subghz_transmitter_free(transmitter);
  157. subghz_environment_free(environment);
  158. }
  159. typedef struct {
  160. volatile bool overrun;
  161. FuriStreamBuffer* stream;
  162. size_t packet_count;
  163. } SubGhzCliCommandRx;
  164. static void subghz_cli_command_rx_capture_callback(bool level, uint32_t duration, void* context) {
  165. SubGhzCliCommandRx* instance = context;
  166. LevelDuration level_duration = level_duration_make(level, duration);
  167. if(instance->overrun) {
  168. instance->overrun = false;
  169. level_duration = level_duration_reset();
  170. }
  171. size_t ret =
  172. furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);
  173. if(sizeof(LevelDuration) != ret) instance->overrun = true;
  174. }
  175. static void subghz_cli_command_rx_callback(
  176. SubGhzReceiver* receiver,
  177. SubGhzProtocolDecoderBase* decoder_base,
  178. void* context) {
  179. SubGhzCliCommandRx* instance = context;
  180. instance->packet_count++;
  181. FuriString* text;
  182. text = furi_string_alloc();
  183. subghz_protocol_decoder_base_get_string(decoder_base, text);
  184. subghz_receiver_reset(receiver);
  185. printf("%s", furi_string_get_cstr(text));
  186. furi_string_free(text);
  187. }
  188. void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
  189. UNUSED(context);
  190. uint32_t frequency = 433920000;
  191. if(furi_string_size(args)) {
  192. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  193. if(ret != 1) {
  194. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  195. cli_print_usage("subghz rx", "<Frequency: in Hz>", furi_string_get_cstr(args));
  196. return;
  197. }
  198. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  199. printf(
  200. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  201. frequency);
  202. return;
  203. }
  204. }
  205. // Allocate context and buffers
  206. SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
  207. instance->stream =
  208. furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
  209. furi_check(instance->stream);
  210. SubGhzEnvironment* environment = subghz_environment_alloc();
  211. subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
  212. subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
  213. subghz_environment_set_came_atomo_rainbow_table_file_name(
  214. environment, EXT_PATH("subghz/assets/came_atomo"));
  215. subghz_environment_set_nice_flor_s_rainbow_table_file_name(
  216. environment, EXT_PATH("subghz/assets/nice_flor_s"));
  217. subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
  218. SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
  219. subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
  220. subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);
  221. // Configure radio
  222. furi_hal_subghz_reset();
  223. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  224. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  225. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
  226. furi_hal_power_suppress_charge_enter();
  227. // Prepare and start RX
  228. furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance);
  229. // Wait for packets to arrive
  230. printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
  231. LevelDuration level_duration;
  232. while(!cli_cmd_interrupt_received(cli)) {
  233. int ret = furi_stream_buffer_receive(
  234. instance->stream, &level_duration, sizeof(LevelDuration), 10);
  235. if(ret == sizeof(LevelDuration)) {
  236. if(level_duration_is_reset(level_duration)) {
  237. printf(".");
  238. subghz_receiver_reset(receiver);
  239. } else {
  240. bool level = level_duration_get_level(level_duration);
  241. uint32_t duration = level_duration_get_duration(level_duration);
  242. subghz_receiver_decode(receiver, level, duration);
  243. }
  244. }
  245. }
  246. // Shutdown radio
  247. furi_hal_subghz_stop_async_rx();
  248. furi_hal_subghz_sleep();
  249. furi_hal_power_suppress_charge_exit();
  250. printf("\r\nPackets received %zu\r\n", instance->packet_count);
  251. // Cleanup
  252. subghz_receiver_free(receiver);
  253. subghz_environment_free(environment);
  254. furi_stream_buffer_free(instance->stream);
  255. free(instance);
  256. }
  257. void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
  258. UNUSED(context);
  259. uint32_t frequency = 433920000;
  260. if(furi_string_size(args)) {
  261. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  262. if(ret != 1) {
  263. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  264. cli_print_usage("subghz rx", "<Frequency: in Hz>", furi_string_get_cstr(args));
  265. return;
  266. }
  267. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  268. printf(
  269. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  270. frequency);
  271. return;
  272. }
  273. }
  274. // Allocate context and buffers
  275. SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
  276. instance->stream =
  277. furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
  278. furi_check(instance->stream);
  279. // Configure radio
  280. furi_hal_subghz_reset();
  281. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async);
  282. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  283. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
  284. furi_hal_power_suppress_charge_enter();
  285. // Prepare and start RX
  286. furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance);
  287. // Wait for packets to arrive
  288. printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
  289. LevelDuration level_duration;
  290. size_t counter = 0;
  291. while(!cli_cmd_interrupt_received(cli)) {
  292. int ret = furi_stream_buffer_receive(
  293. instance->stream, &level_duration, sizeof(LevelDuration), 10);
  294. if(ret == 0) {
  295. continue;
  296. }
  297. if(ret != sizeof(LevelDuration)) {
  298. puts("stream corrupt");
  299. break;
  300. }
  301. if(level_duration_is_reset(level_duration)) {
  302. puts(". ");
  303. } else {
  304. bool level = level_duration_get_level(level_duration);
  305. uint32_t duration = level_duration_get_duration(level_duration);
  306. printf("%c%lu ", level ? '+' : '-', duration);
  307. }
  308. furi_thread_stdout_flush();
  309. counter++;
  310. if(counter > 255) {
  311. puts("\r\n");
  312. counter = 0;
  313. }
  314. }
  315. // Shutdown radio
  316. furi_hal_subghz_stop_async_rx();
  317. furi_hal_subghz_sleep();
  318. furi_hal_power_suppress_charge_exit();
  319. // Cleanup
  320. furi_stream_buffer_free(instance->stream);
  321. free(instance);
  322. }
  323. void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
  324. UNUSED(context);
  325. FuriString* file_name;
  326. file_name = furi_string_alloc();
  327. furi_string_set(file_name, ANY_PATH("subghz/test.sub"));
  328. Storage* storage = furi_record_open(RECORD_STORAGE);
  329. FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
  330. FuriString* temp_str;
  331. temp_str = furi_string_alloc();
  332. uint32_t temp_data32;
  333. bool check_file = false;
  334. do {
  335. if(furi_string_size(args)) {
  336. if(!args_read_string_and_trim(args, file_name)) {
  337. cli_print_usage(
  338. "subghz decode_raw", "<file_name: path_RAW_file>", furi_string_get_cstr(args));
  339. break;
  340. }
  341. }
  342. if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) {
  343. printf(
  344. "subghz decode_raw \033[0;31mError open file\033[0m %s\r\n",
  345. furi_string_get_cstr(file_name));
  346. break;
  347. }
  348. if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
  349. printf("subghz decode_raw \033[0;31mMissing or incorrect header\033[0m\r\n");
  350. break;
  351. }
  352. if(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE) &&
  353. temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
  354. } else {
  355. printf("subghz decode_raw \033[0;31mType or version mismatch\033[0m\r\n");
  356. break;
  357. }
  358. check_file = true;
  359. } while(false);
  360. furi_string_free(temp_str);
  361. flipper_format_free(fff_data_file);
  362. furi_record_close(RECORD_STORAGE);
  363. if(check_file) {
  364. // Allocate context
  365. SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
  366. SubGhzEnvironment* environment = subghz_environment_alloc();
  367. if(subghz_environment_load_keystore(
  368. environment, EXT_PATH("subghz/assets/keeloq_mfcodes"))) {
  369. printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n");
  370. } else {
  371. printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n");
  372. }
  373. if(subghz_environment_load_keystore(
  374. environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"))) {
  375. printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n");
  376. } else {
  377. printf(
  378. "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n");
  379. }
  380. subghz_environment_set_came_atomo_rainbow_table_file_name(
  381. environment, EXT_PATH("subghz/assets/came_atomo"));
  382. subghz_environment_set_nice_flor_s_rainbow_table_file_name(
  383. environment, EXT_PATH("subghz/assets/nice_flor_s"));
  384. subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
  385. SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
  386. subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
  387. subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);
  388. SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc();
  389. if(subghz_file_encoder_worker_start(file_worker_encoder, furi_string_get_cstr(file_name))) {
  390. //the worker needs a file in order to open and read part of the file
  391. furi_delay_ms(100);
  392. }
  393. printf(
  394. "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n",
  395. furi_string_get_cstr(file_name));
  396. LevelDuration level_duration;
  397. while(!cli_cmd_interrupt_received(cli)) {
  398. furi_delay_us(500); //you need to have time to read from the file from the SD card
  399. level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder);
  400. if(!level_duration_is_reset(level_duration)) {
  401. bool level = level_duration_get_level(level_duration);
  402. uint32_t duration = level_duration_get_duration(level_duration);
  403. subghz_receiver_decode(receiver, level, duration);
  404. } else {
  405. break;
  406. }
  407. }
  408. printf("\r\nPackets received \033[0;32m%u\033[0m\r\n", instance->packet_count);
  409. // Cleanup
  410. subghz_receiver_free(receiver);
  411. subghz_environment_free(environment);
  412. if(subghz_file_encoder_worker_is_running(file_worker_encoder)) {
  413. subghz_file_encoder_worker_stop(file_worker_encoder);
  414. }
  415. subghz_file_encoder_worker_free(file_worker_encoder);
  416. free(instance);
  417. }
  418. furi_string_free(file_name);
  419. }
  420. static void subghz_cli_command_print_usage() {
  421. printf("Usage:\r\n");
  422. printf("subghz <cmd> <args>\r\n");
  423. printf("Cmd list:\r\n");
  424. printf("\tchat <frequency:in Hz>\t - Chat with other Flippers\r\n");
  425. printf(
  426. "\ttx <3 byte Key: in hex> <frequency: in Hz> <te: us> <repeat: count>\t - Transmitting key\r\n");
  427. printf("\trx <frequency:in Hz>\t - Receive\r\n");
  428. printf("\trx_raw <frequency:in Hz>\t - Receive RAW\r\n");
  429. printf("\tdecode_raw <file_name: path_RAW_file>\t - Testing\r\n");
  430. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  431. printf("\r\n");
  432. printf(" debug cmd:\r\n");
  433. printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
  434. printf("\trx_carrier <frequency:in Hz>\t - Receive carrier\r\n");
  435. printf(
  436. "\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
  437. printf(
  438. "\tencrypt_raw <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt RAW data\r\n");
  439. }
  440. }
  441. static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) {
  442. UNUSED(cli);
  443. uint8_t iv[16];
  444. FuriString* source;
  445. FuriString* destination;
  446. source = furi_string_alloc();
  447. destination = furi_string_alloc();
  448. SubGhzKeystore* keystore = subghz_keystore_alloc();
  449. do {
  450. if(!args_read_string_and_trim(args, source)) {
  451. subghz_cli_command_print_usage();
  452. break;
  453. }
  454. if(!args_read_string_and_trim(args, destination)) {
  455. subghz_cli_command_print_usage();
  456. break;
  457. }
  458. if(!args_read_hex_bytes(args, iv, 16)) {
  459. subghz_cli_command_print_usage();
  460. break;
  461. }
  462. if(!subghz_keystore_load(keystore, furi_string_get_cstr(source))) {
  463. printf("Failed to load Keystore");
  464. break;
  465. }
  466. if(!subghz_keystore_save(keystore, furi_string_get_cstr(destination), iv)) {
  467. printf("Failed to save Keystore");
  468. break;
  469. }
  470. } while(false);
  471. subghz_keystore_free(keystore);
  472. furi_string_free(destination);
  473. furi_string_free(source);
  474. }
  475. static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) {
  476. UNUSED(cli);
  477. uint8_t iv[16];
  478. FuriString* source;
  479. FuriString* destination;
  480. source = furi_string_alloc();
  481. destination = furi_string_alloc();
  482. do {
  483. if(!args_read_string_and_trim(args, source)) {
  484. subghz_cli_command_print_usage();
  485. break;
  486. }
  487. if(!args_read_string_and_trim(args, destination)) {
  488. subghz_cli_command_print_usage();
  489. break;
  490. }
  491. if(!args_read_hex_bytes(args, iv, 16)) {
  492. subghz_cli_command_print_usage();
  493. break;
  494. }
  495. if(!subghz_keystore_raw_encrypted_save(
  496. furi_string_get_cstr(source), furi_string_get_cstr(destination), iv)) {
  497. printf("Failed to save Keystore");
  498. break;
  499. }
  500. } while(false);
  501. furi_string_free(destination);
  502. furi_string_free(source);
  503. }
  504. static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
  505. uint32_t frequency = 433920000;
  506. if(furi_string_size(args)) {
  507. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  508. if(ret != 1) {
  509. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  510. cli_print_usage("subghz chat", "<Frequency: in Hz>", furi_string_get_cstr(args));
  511. return;
  512. }
  513. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  514. printf(
  515. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  516. frequency);
  517. return;
  518. }
  519. }
  520. if(!furi_hal_region_is_frequency_allowed(frequency)) {
  521. printf(
  522. "In your region, only reception on this frequency (%lu) is allowed,\r\n"
  523. "the actual operation of the application is not possible\r\n ",
  524. frequency);
  525. return;
  526. }
  527. SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli);
  528. if(!subghz_chat_worker_start(subghz_chat, frequency)) {
  529. printf("Startup error SubGhzChatWorker\r\n");
  530. if(subghz_chat_worker_is_running(subghz_chat)) {
  531. subghz_chat_worker_stop(subghz_chat);
  532. subghz_chat_worker_free(subghz_chat);
  533. }
  534. return;
  535. }
  536. printf("Receiving at frequency %lu Hz\r\n", frequency);
  537. printf("Press CTRL+C to stop\r\n");
  538. furi_hal_power_suppress_charge_enter();
  539. size_t message_max_len = 64;
  540. uint8_t message[64] = {0};
  541. FuriString* input;
  542. input = furi_string_alloc();
  543. FuriString* name;
  544. name = furi_string_alloc();
  545. FuriString* output;
  546. output = furi_string_alloc();
  547. FuriString* sysmsg;
  548. sysmsg = furi_string_alloc();
  549. bool exit = false;
  550. SubGhzChatEvent chat_event;
  551. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  552. furi_string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr());
  553. furi_string_set(input, name);
  554. printf("%s", furi_string_get_cstr(input));
  555. fflush(stdout);
  556. while(!exit) {
  557. chat_event = subghz_chat_worker_get_event_chat(subghz_chat);
  558. switch(chat_event.event) {
  559. case SubGhzChatEventInputData:
  560. if(chat_event.c == CliSymbolAsciiETX) {
  561. printf("\r\n");
  562. chat_event.event = SubGhzChatEventUserExit;
  563. subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
  564. break;
  565. } else if(
  566. (chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) {
  567. size_t len = furi_string_utf8_length(input);
  568. if(len > furi_string_utf8_length(name)) {
  569. printf("%s", "\e[D\e[1P");
  570. fflush(stdout);
  571. //delete 1 char UTF
  572. const char* str = furi_string_get_cstr(input);
  573. size_t size = 0;
  574. FuriStringUTF8State s = FuriStringUTF8StateStarting;
  575. FuriStringUnicodeValue u = 0;
  576. furi_string_reset(sysmsg);
  577. while(*str) {
  578. furi_string_utf8_decode(*str, &s, &u);
  579. if((s == FuriStringUTF8StateError) || s == FuriStringUTF8StateStarting) {
  580. furi_string_utf8_push(sysmsg, u);
  581. if(++size >= len - 1) break;
  582. s = FuriStringUTF8StateStarting;
  583. }
  584. str++;
  585. }
  586. furi_string_set(input, sysmsg);
  587. }
  588. } else if(chat_event.c == CliSymbolAsciiCR) {
  589. printf("\r\n");
  590. furi_string_push_back(input, '\r');
  591. furi_string_push_back(input, '\n');
  592. while(!subghz_chat_worker_write(
  593. subghz_chat,
  594. (uint8_t*)furi_string_get_cstr(input),
  595. strlen(furi_string_get_cstr(input)))) {
  596. furi_delay_ms(10);
  597. }
  598. furi_string_printf(input, "%s", furi_string_get_cstr(name));
  599. printf("%s", furi_string_get_cstr(input));
  600. fflush(stdout);
  601. } else if(chat_event.c == CliSymbolAsciiLF) {
  602. //cut out the symbol \n
  603. } else {
  604. putc(chat_event.c, stdout);
  605. fflush(stdout);
  606. furi_string_push_back(input, chat_event.c);
  607. break;
  608. case SubGhzChatEventRXData:
  609. do {
  610. memset(message, 0x00, message_max_len);
  611. size_t len = subghz_chat_worker_read(subghz_chat, message, message_max_len);
  612. for(size_t i = 0; i < len; i++) {
  613. furi_string_push_back(output, message[i]);
  614. if(message[i] == '\n') {
  615. printf("\r");
  616. for(uint8_t i = 0; i < 80; i++) {
  617. printf(" ");
  618. }
  619. printf("\r %s", furi_string_get_cstr(output));
  620. printf("%s", furi_string_get_cstr(input));
  621. fflush(stdout);
  622. furi_string_reset(output);
  623. }
  624. }
  625. } while(subghz_chat_worker_available(subghz_chat));
  626. break;
  627. case SubGhzChatEventNewMessage:
  628. notification_message(notification, &sequence_single_vibro);
  629. break;
  630. case SubGhzChatEventUserEntrance:
  631. furi_string_printf(
  632. sysmsg,
  633. "\033[0;34m%s joined chat.\033[0m\r\n",
  634. furi_hal_version_get_name_ptr());
  635. subghz_chat_worker_write(
  636. subghz_chat,
  637. (uint8_t*)furi_string_get_cstr(sysmsg),
  638. strlen(furi_string_get_cstr(sysmsg)));
  639. break;
  640. case SubGhzChatEventUserExit:
  641. furi_string_printf(
  642. sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr());
  643. subghz_chat_worker_write(
  644. subghz_chat,
  645. (uint8_t*)furi_string_get_cstr(sysmsg),
  646. strlen(furi_string_get_cstr(sysmsg)));
  647. furi_delay_ms(10);
  648. exit = true;
  649. break;
  650. default:
  651. FURI_LOG_W("SubGhzChat", "Error event");
  652. break;
  653. }
  654. }
  655. if(!cli_is_connected(cli)) {
  656. printf("\r\n");
  657. chat_event.event = SubGhzChatEventUserExit;
  658. subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
  659. }
  660. }
  661. furi_string_free(input);
  662. furi_string_free(name);
  663. furi_string_free(output);
  664. furi_string_free(sysmsg);
  665. furi_hal_power_suppress_charge_exit();
  666. furi_record_close(RECORD_NOTIFICATION);
  667. if(subghz_chat_worker_is_running(subghz_chat)) {
  668. subghz_chat_worker_stop(subghz_chat);
  669. subghz_chat_worker_free(subghz_chat);
  670. }
  671. printf("\r\nExit chat\r\n");
  672. }
  673. static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
  674. FuriString* cmd;
  675. cmd = furi_string_alloc();
  676. do {
  677. if(!args_read_string_and_trim(args, cmd)) {
  678. subghz_cli_command_print_usage();
  679. break;
  680. }
  681. if(furi_string_cmp_str(cmd, "chat") == 0) {
  682. subghz_cli_command_chat(cli, args);
  683. break;
  684. }
  685. if(furi_string_cmp_str(cmd, "tx") == 0) {
  686. subghz_cli_command_tx(cli, args, context);
  687. break;
  688. }
  689. if(furi_string_cmp_str(cmd, "rx") == 0) {
  690. subghz_cli_command_rx(cli, args, context);
  691. break;
  692. }
  693. if(furi_string_cmp_str(cmd, "rx_raw") == 0) {
  694. subghz_cli_command_rx_raw(cli, args, context);
  695. break;
  696. }
  697. if(furi_string_cmp_str(cmd, "decode_raw") == 0) {
  698. subghz_cli_command_decode_raw(cli, args, context);
  699. break;
  700. }
  701. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  702. if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) {
  703. subghz_cli_command_encrypt_keeloq(cli, args);
  704. break;
  705. }
  706. if(furi_string_cmp_str(cmd, "encrypt_raw") == 0) {
  707. subghz_cli_command_encrypt_raw(cli, args);
  708. break;
  709. }
  710. if(furi_string_cmp_str(cmd, "tx_carrier") == 0) {
  711. subghz_cli_command_tx_carrier(cli, args, context);
  712. break;
  713. }
  714. if(furi_string_cmp_str(cmd, "rx_carrier") == 0) {
  715. subghz_cli_command_rx_carrier(cli, args, context);
  716. break;
  717. }
  718. }
  719. subghz_cli_command_print_usage();
  720. } while(false);
  721. furi_string_free(cmd);
  722. }
  723. static bool
  724. subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
  725. File* file = istream->state;
  726. uint16_t ret = storage_file_read(file, buf, count);
  727. return (count == ret);
  728. }
  729. static bool subghz_on_system_start_istream_decode_band(
  730. pb_istream_t* stream,
  731. const pb_field_t* field,
  732. void** arg) {
  733. (void)field;
  734. FuriHalRegion* region = *arg;
  735. PB_Region_Band band = {0};
  736. if(!pb_decode(stream, PB_Region_Band_fields, &band)) {
  737. FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream));
  738. return false;
  739. }
  740. region->bands_count += 1;
  741. region = realloc( //-V701
  742. region,
  743. sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count);
  744. size_t pos = region->bands_count - 1;
  745. region->bands[pos].start = band.start;
  746. region->bands[pos].end = band.end;
  747. region->bands[pos].power_limit = band.power_limit;
  748. region->bands[pos].duty_cycle = band.duty_cycle;
  749. *arg = region;
  750. FURI_LOG_I(
  751. "SubGhzOnStart",
  752. "Add allowed band: start %luHz, stop %luHz, power_limit %ddBm, duty_cycle %u%%",
  753. band.start,
  754. band.end,
  755. band.power_limit,
  756. band.duty_cycle);
  757. return true;
  758. }
  759. void subghz_on_system_start() {
  760. #ifdef SRV_CLI
  761. Cli* cli = furi_record_open(RECORD_CLI);
  762. cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
  763. furi_record_close(RECORD_CLI);
  764. #else
  765. UNUSED(subghz_cli_command);
  766. #endif
  767. #ifdef SRV_STORAGE
  768. Storage* storage = furi_record_open(RECORD_STORAGE);
  769. File* file = storage_file_alloc(storage);
  770. FileInfo fileinfo = {0};
  771. PB_Region pb_region = {0};
  772. pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band;
  773. do {
  774. if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK ||
  775. fileinfo.size == 0) {
  776. FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty");
  777. break;
  778. }
  779. if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
  780. FURI_LOG_E("SubGhzOnStart", "Unable to open region data");
  781. break;
  782. }
  783. pb_istream_t istream = {
  784. .callback = subghz_on_system_start_istream_read,
  785. .state = file,
  786. .errmsg = NULL,
  787. .bytes_left = fileinfo.size,
  788. };
  789. pb_region.bands.arg = malloc(sizeof(FuriHalRegion));
  790. if(!pb_decode(&istream, PB_Region_fields, &pb_region)) {
  791. FURI_LOG_E("SubGhzOnStart", "Invalid region data");
  792. free(pb_region.bands.arg);
  793. break;
  794. }
  795. FuriHalRegion* region = pb_region.bands.arg;
  796. memcpy(
  797. region->country_code,
  798. pb_region.country_code->bytes,
  799. pb_region.country_code->size < 4 ? pb_region.country_code->size : 3);
  800. furi_hal_region_set(region);
  801. } while(0);
  802. pb_release(PB_Region_fields, &pb_region);
  803. storage_file_free(file);
  804. furi_record_close(RECORD_STORAGE);
  805. #else
  806. UNUSED(subghz_cli_command);
  807. UNUSED(subghz_on_system_start_istream_decode_band);
  808. UNUSED(subghz_on_system_start_istream_read);
  809. #endif
  810. }