infrared_signal.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /*
  2. This file was taken from the project:
  3. https://github.com/DarkFlippers/unleashed-firmware
  4. The original project is licensed under the GNU GPLv3
  5. No modifications were made to this file.
  6. */
  7. #include "infrared_signal.h"
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <core/check.h>
  11. #include <infrared_worker.h>
  12. #include <infrared_transmit.h>
  13. #define TAG "InfraredSignal"
  14. struct InfraredSignal {
  15. bool is_raw;
  16. union {
  17. InfraredMessage message;
  18. InfraredRawSignal raw;
  19. } payload;
  20. };
  21. static void infrared_signal_clear_timings(InfraredSignal* signal) {
  22. if(signal->is_raw) {
  23. free(signal->payload.raw.timings);
  24. signal->payload.raw.timings_size = 0;
  25. signal->payload.raw.timings = NULL;
  26. }
  27. }
  28. static bool infrared_signal_is_message_valid(InfraredMessage* message) {
  29. if(!infrared_is_protocol_valid(message->protocol)) {
  30. FURI_LOG_E(TAG, "Unknown protocol");
  31. return false;
  32. }
  33. uint32_t address_length = infrared_get_protocol_address_length(message->protocol);
  34. uint32_t address_mask = (1UL << address_length) - 1;
  35. if(message->address != (message->address & address_mask)) {
  36. FURI_LOG_E(
  37. TAG,
  38. "Address is out of range (mask 0x%08lX): 0x%lX\r\n",
  39. address_mask,
  40. message->address);
  41. return false;
  42. }
  43. uint32_t command_length = infrared_get_protocol_command_length(message->protocol);
  44. uint32_t command_mask = (1UL << command_length) - 1;
  45. if(message->command != (message->command & command_mask)) {
  46. FURI_LOG_E(
  47. TAG,
  48. "Command is out of range (mask 0x%08lX): 0x%lX\r\n",
  49. command_mask,
  50. message->command);
  51. return false;
  52. }
  53. return true;
  54. }
  55. static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
  56. if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
  57. FURI_LOG_E(
  58. TAG,
  59. "Frequency is out of range (%X - %X): %lX",
  60. INFRARED_MIN_FREQUENCY,
  61. INFRARED_MAX_FREQUENCY,
  62. raw->frequency);
  63. return false;
  64. } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) {
  65. FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle);
  66. return false;
  67. } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) {
  68. FURI_LOG_E(
  69. TAG,
  70. "Timings amount is out of range (0 - %X): %zX",
  71. MAX_TIMINGS_AMOUNT,
  72. raw->timings_size);
  73. return false;
  74. }
  75. return true;
  76. }
  77. static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
  78. const char* protocol_name = infrared_get_protocol_name(message->protocol);
  79. return flipper_format_write_string_cstr(ff, "type", "parsed") &&
  80. flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
  81. flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) &&
  82. flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
  83. }
  84. static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
  85. furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
  86. return flipper_format_write_string_cstr(ff, "type", "raw") &&
  87. flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
  88. flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) &&
  89. flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size);
  90. }
  91. static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) {
  92. FuriString* buf;
  93. buf = furi_string_alloc();
  94. bool success = false;
  95. do {
  96. if(!flipper_format_read_string(ff, "protocol", buf)) break;
  97. InfraredMessage message;
  98. message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));
  99. success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) &&
  100. flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) &&
  101. infrared_signal_is_message_valid(&message);
  102. if(!success) break;
  103. infrared_signal_set_message(signal, &message);
  104. } while(0);
  105. furi_string_free(buf);
  106. return success;
  107. }
  108. static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) {
  109. uint32_t timings_size, frequency;
  110. float duty_cycle;
  111. bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) &&
  112. flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) &&
  113. flipper_format_get_value_count(ff, "data", &timings_size);
  114. if(!success || timings_size > MAX_TIMINGS_AMOUNT) {
  115. return false;
  116. }
  117. uint32_t* timings = malloc(sizeof(uint32_t) * timings_size);
  118. success = flipper_format_read_uint32(ff, "data", timings, timings_size);
  119. if(success) {
  120. infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
  121. }
  122. free(timings);
  123. return success;
  124. }
  125. static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) {
  126. FuriString* tmp = furi_string_alloc();
  127. bool success = false;
  128. do {
  129. if(!flipper_format_read_string(ff, "type", tmp)) break;
  130. if(furi_string_equal(tmp, "raw")) {
  131. success = infrared_signal_read_raw(signal, ff);
  132. } else if(furi_string_equal(tmp, "parsed")) {
  133. success = infrared_signal_read_message(signal, ff);
  134. } else {
  135. FURI_LOG_E(TAG, "Unknown signal type");
  136. }
  137. } while(false);
  138. furi_string_free(tmp);
  139. return success;
  140. }
  141. InfraredSignal* infrared_signal_alloc() {
  142. InfraredSignal* signal = malloc(sizeof(InfraredSignal));
  143. signal->is_raw = false;
  144. signal->payload.message.protocol = InfraredProtocolUnknown;
  145. return signal;
  146. }
  147. void infrared_signal_free(InfraredSignal* signal) {
  148. infrared_signal_clear_timings(signal);
  149. free(signal);
  150. }
  151. bool infrared_signal_is_raw(InfraredSignal* signal) {
  152. return signal->is_raw;
  153. }
  154. bool infrared_signal_is_valid(InfraredSignal* signal) {
  155. return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
  156. infrared_signal_is_message_valid(&signal->payload.message);
  157. }
  158. void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {
  159. if(other->is_raw) {
  160. const InfraredRawSignal* raw = &other->payload.raw;
  161. infrared_signal_set_raw_signal(
  162. signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
  163. } else {
  164. const InfraredMessage* message = &other->payload.message;
  165. infrared_signal_set_message(signal, message);
  166. }
  167. }
  168. void infrared_signal_set_raw_signal(
  169. InfraredSignal* signal,
  170. const uint32_t* timings,
  171. size_t timings_size,
  172. uint32_t frequency,
  173. float duty_cycle) {
  174. infrared_signal_clear_timings(signal);
  175. // If the frequency is out of bounds, set it to the closest bound same for duty cycle
  176. // TODO: Should we return error instead? Also infrared_signal_is_valid is used only in CLI for some reason?!
  177. if(frequency > INFRARED_MAX_FREQUENCY) {
  178. frequency = INFRARED_MAX_FREQUENCY;
  179. } else if(frequency < INFRARED_MIN_FREQUENCY) {
  180. frequency = INFRARED_MIN_FREQUENCY;
  181. }
  182. if((duty_cycle <= (float)0) || (duty_cycle > (float)1)) {
  183. duty_cycle = (float)0.33;
  184. }
  185. // In case of timings out of bounds we just call return
  186. if((timings_size <= 0) || (timings_size > MAX_TIMINGS_AMOUNT)) {
  187. return;
  188. }
  189. signal->is_raw = true;
  190. signal->payload.raw.timings_size = timings_size;
  191. signal->payload.raw.frequency = frequency;
  192. signal->payload.raw.duty_cycle = duty_cycle;
  193. signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));
  194. memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
  195. }
  196. InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
  197. furi_assert(signal->is_raw);
  198. return &signal->payload.raw;
  199. }
  200. void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {
  201. infrared_signal_clear_timings(signal);
  202. signal->is_raw = false;
  203. signal->payload.message = *message;
  204. }
  205. InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
  206. furi_assert(!signal->is_raw);
  207. return &signal->payload.message;
  208. }
  209. bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
  210. if(!flipper_format_write_comment_cstr(ff, "") ||
  211. !flipper_format_write_string_cstr(ff, "name", name)) {
  212. return false;
  213. } else if(signal->is_raw) {
  214. return infrared_signal_save_raw(&signal->payload.raw, ff);
  215. } else {
  216. return infrared_signal_save_message(&signal->payload.message, ff);
  217. }
  218. }
  219. bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {
  220. FuriString* tmp = furi_string_alloc();
  221. bool success = false;
  222. do {
  223. if(!flipper_format_read_string(ff, "name", tmp)) break;
  224. furi_string_set(name, tmp);
  225. if(!infrared_signal_read_body(signal, ff)) break;
  226. success = true;
  227. } while(0);
  228. furi_string_free(tmp);
  229. return success;
  230. }
  231. bool infrared_signal_search_and_read(
  232. InfraredSignal* signal,
  233. FlipperFormat* ff,
  234. const FuriString* name) {
  235. bool success = false;
  236. FuriString* tmp = furi_string_alloc();
  237. do {
  238. bool is_name_found = false;
  239. while(flipper_format_read_string(ff, "name", tmp)) {
  240. is_name_found = furi_string_equal(name, tmp);
  241. if(is_name_found) break;
  242. }
  243. if(!is_name_found) break; //-V547
  244. if(!infrared_signal_read_body(signal, ff)) break; //-V779
  245. success = true;
  246. } while(false);
  247. furi_string_free(tmp);
  248. return success;
  249. }
  250. void infrared_signal_transmit(InfraredSignal* signal) {
  251. if(signal->is_raw) {
  252. InfraredRawSignal* raw_signal = &signal->payload.raw;
  253. infrared_send_raw_ext(
  254. raw_signal->timings,
  255. raw_signal->timings_size,
  256. true,
  257. raw_signal->frequency,
  258. raw_signal->duty_cycle);
  259. } else {
  260. InfraredMessage* message = &signal->payload.message;
  261. infrared_send(message, 1);
  262. }
  263. }