Merge branch 'dev-upstream' into shutdown_idle

This commit is contained in:
SHxKenzuto
2022-12-09 17:18:28 +01:00
415 changed files with 7882 additions and 2423 deletions
+13 -13
View File
@@ -2,19 +2,19 @@ Import("env")
env.Append(
LINT_SOURCES=[
"lib/app-scened-template",
"lib/digital_signal",
"lib/drivers",
"lib/flipper_format",
"lib/infrared",
"lib/nfc",
"lib/one_wire",
"lib/ST25RFAL002",
"lib/subghz",
"lib/toolbox",
"lib/u8g2",
"lib/update_util",
"lib/print",
Dir("app-scened-template"),
Dir("digital_signal"),
Dir("drivers"),
Dir("flipper_format"),
Dir("infrared"),
Dir("nfc"),
Dir("one_wire"),
Dir("ST25RFAL002"),
Dir("subghz"),
Dir("toolbox"),
Dir("u8g2"),
Dir("update_util"),
Dir("print"),
],
SDK_HEADERS=[
File("one_wire/one_wire_host_timing.h"),
+3 -5
View File
@@ -45,11 +45,9 @@ void platformDisableIrqCallback() {
void platformSetIrqCallback(PlatformIrqCallback callback) {
rfal_platform.callback = callback;
rfal_platform.thread = furi_thread_alloc();
furi_thread_set_name(rfal_platform.thread, "RfalIrqDriver");
furi_thread_set_callback(rfal_platform.thread, rfal_platform_irq_thread);
furi_thread_set_stack_size(rfal_platform.thread, 1024);
rfal_platform.thread =
furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL);
furi_thread_mark_as_service(rfal_platform.thread);
furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr);
furi_thread_start(rfal_platform.thread);
+27
View File
@@ -132,6 +132,33 @@ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) {
return bq25896_regs.r03.OTG_CONFIG;
}
uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) {
furi_hal_i2c_read_reg_8(
handle, BQ25896_ADDRESS, 0x06, (uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);
return (uint16_t)bq25896_regs.r06.VREG * 16 + 3840;
}
void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) {
if(vreg_voltage < 3840) {
// Minimum value is 3840 mV
bq25896_regs.r06.VREG = 0;
} else {
// Find the nearest voltage value (subtract offset, divide into sections)
// Values are truncated downward as needed (e.g. 4200mV -> 4192 mV)
bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16);
}
// Do not allow values above 23 (0x17, 4208mV)
// Exceeding 4.2v will overcharge the battery!
if(bq25896_regs.r06.VREG > 23) {
bq25896_regs.r06.VREG = 23;
}
// Apply changes
furi_hal_i2c_write_reg_8(
handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);
}
bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle) {
furi_hal_i2c_read_reg_8(
handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT);
+9
View File
@@ -36,6 +36,15 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle);
/** Is otg enabled */
bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle);
/** Get VREG (charging) voltage in mV */
uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle);
/** Set VREG (charging) voltage in mV
*
* Valid range: 3840mV - 4208mV, in steps of 16mV
*/
void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage);
/** Check OTG BOOST Fault status */
bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle);
@@ -104,11 +104,8 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
furi_check(manifest->stack_size > 0);
app->thread = furi_thread_alloc();
furi_thread_set_stack_size(app->thread, manifest->stack_size);
furi_thread_set_name(app->thread, manifest->name);
furi_thread_set_callback(app->thread, flipper_application_thread);
furi_thread_set_context(app->thread, args);
app->thread = furi_thread_alloc_ex(
manifest->name, manifest->stack_size, flipper_application_thread, args);
return app->thread;
}
+1 -1
View File
@@ -313,7 +313,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa
furi_crash("Unknown FF type");
}
if((size_t)(i + 1) < write_data->data_size) {
if(((size_t)i + 1) < write_data->data_size) {
furi_string_cat(value, " ");
}
@@ -85,8 +85,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder
if(timings->min_split_time && !level) {
if(timing > timings->min_split_time) {
/* long low timing - check if we're ready for any of protocol modification */
for(size_t i = 0; decoder->protocol->databit_len[i] &&
(i < COUNT_OF(decoder->protocol->databit_len));
for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) &&
decoder->protocol->databit_len[i];
++i) {
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
return InfraredStatusReady;
@@ -199,7 +199,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco
bool found_length = false;
for(size_t i = 0;
decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len));
i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i];
++i) {
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
found_length = true;
@@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = {
.decode_repeat = NULL,
.encode_repeat = infrared_encoder_sirc_encode_repeat,
};
const InfraredCommonProtocolSpec protocol_kaseikyo = {
.timings =
{
.preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK,
.preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE,
.bit1_mark = INFRARED_KASEIKYO_BIT1_MARK,
.bit1_space = INFRARED_KASEIKYO_BIT1_SPACE,
.bit0_mark = INFRARED_KASEIKYO_BIT0_MARK,
.bit0_space = INFRARED_KASEIKYO_BIT0_SPACE,
.preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE,
.bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE,
.silence_time = INFRARED_KASEIKYO_SILENCE,
.min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME,
},
.databit_len[0] = 48,
.no_stop_bit = false,
.decode = infrared_common_decode_pdwm,
.encode = infrared_common_encode_pdwm,
.interpret = infrared_decoder_kaseikyo_interpret,
.decode_repeat = NULL,
.encode_repeat = NULL,
};
+14
View File
@@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = {
.free = infrared_encoder_sirc_free},
.get_protocol_spec = infrared_sirc_get_spec,
},
{
.decoder =
{.alloc = infrared_decoder_kaseikyo_alloc,
.decode = infrared_decoder_kaseikyo_decode,
.reset = infrared_decoder_kaseikyo_reset,
.check_ready = infrared_decoder_kaseikyo_check_ready,
.free = infrared_decoder_kaseikyo_free},
.encoder =
{.alloc = infrared_encoder_kaseikyo_alloc,
.encode = infrared_encoder_kaseikyo_encode,
.reset = infrared_encoder_kaseikyo_reset,
.free = infrared_encoder_kaseikyo_free},
.get_protocol_spec = infrared_kaseikyo_get_spec,
},
};
static int infrared_find_index_by_protocol(InfraredProtocol protocol);
+1
View File
@@ -31,6 +31,7 @@ typedef enum {
InfraredProtocolSIRC,
InfraredProtocolSIRC15,
InfraredProtocolSIRC20,
InfraredProtocolKaseikyo,
InfraredProtocolMAX,
} InfraredProtocol;
@@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat(
bool* level);
extern const InfraredCommonProtocolSpec protocol_sirc;
/***************************************************************************************************
* Kaseikyo protocol description
* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
****************************************************************************************************
* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble
* mark space Modulation up to period repeat repeat
* mark space
*
* 3360 1665 48 bit ...130000 3456 1728
* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________
* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________
*
***************************************************************************************************/
#define INFRARED_KASEIKYO_UNIT 432
#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT)
#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT)
#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT
#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT)
#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT
#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT
#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000
#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD
#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000
#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK
#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000)
#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us
#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us
void* infrared_decoder_kaseikyo_alloc(void);
void infrared_decoder_kaseikyo_reset(void* decoder);
void infrared_decoder_kaseikyo_free(void* decoder);
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder);
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration);
void* infrared_encoder_kaseikyo_alloc(void);
InfraredStatus
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level);
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message);
void infrared_encoder_kaseikyo_free(void* encoder_ptr);
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder);
InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder);
InfraredStatus infrared_encoder_kaseikyo_encode_repeat(
InfraredCommonEncoder* encoder,
uint32_t* duration,
bool* level);
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol);
extern const InfraredCommonProtocolSpec protocol_kaseikyo;
@@ -0,0 +1,54 @@
#include "infrared.h"
#include "infrared_protocol_defs_i.h"
#include <stdbool.h>
#include <stdint.h>
#include <furi.h>
#include "../infrared_i.h"
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) {
return infrared_common_decoder_check_ready(ctx);
}
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) {
furi_assert(decoder);
bool result = false;
uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0];
uint8_t vendor_parity = decoder->data[2] & 0x0f;
uint8_t genre1 = decoder->data[2] >> 4;
uint8_t genre2 = decoder->data[3] & 0x0f;
uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4);
uint8_t id = decoder->data[4] >> 6;
uint8_t parity = decoder->data[5];
uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1];
vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4);
uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4];
if(vendor_parity == vendor_parity_check && parity == parity_check) {
decoder->message.command = (uint32_t)data;
decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) |
((uint32_t)genre1 << 4) | (uint32_t)genre2;
decoder->message.protocol = InfraredProtocolKaseikyo;
decoder->message.repeat = false;
result = true;
}
return result;
}
void* infrared_decoder_kaseikyo_alloc(void) {
return infrared_common_decoder_alloc(&protocol_kaseikyo);
}
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) {
return infrared_common_decode(decoder, level, duration);
}
void infrared_decoder_kaseikyo_free(void* decoder) {
infrared_common_decoder_free(decoder);
}
void infrared_decoder_kaseikyo_reset(void* decoder) {
infrared_common_decoder_reset(decoder);
}
@@ -0,0 +1,45 @@
#include <core/check.h>
#include "common/infrared_common_i.h"
#include <stdint.h>
#include "../infrared_i.h"
#include "infrared_protocol_defs_i.h"
#include <furi.h>
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) {
furi_assert(encoder_ptr);
InfraredCommonEncoder* encoder = encoder_ptr;
infrared_common_encoder_reset(encoder);
uint32_t address = message->address;
uint16_t command = message->command;
uint8_t id = (address >> 24) & 3;
uint16_t vendor_id = (address >> 8) & 0xffff;
uint8_t genre1 = (address >> 4) & 0xf;
uint8_t genre2 = address & 0xf;
encoder->data[0] = (uint8_t)(vendor_id & 0xff);
encoder->data[1] = (uint8_t)(vendor_id >> 8);
uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1];
vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4);
encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4);
encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4);
encoder->data[4] = (id << 6) | (uint8_t)(command >> 4);
encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4];
encoder->bits_to_encode = encoder->protocol->databit_len[0];
}
void* infrared_encoder_kaseikyo_alloc(void) {
return infrared_common_encoder_alloc(&protocol_kaseikyo);
}
void infrared_encoder_kaseikyo_free(void* encoder_ptr) {
infrared_common_encoder_free(encoder_ptr);
}
InfraredStatus
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) {
return infrared_common_encode(encoder_ptr, duration, level);
}
@@ -0,0 +1,17 @@
#include "../infrared_i.h"
#include "infrared_protocol_defs_i.h"
static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = {
.name = "Kaseikyo",
.address_length = 26,
.command_length = 10,
.frequency = INFRARED_COMMON_CARRIER_FREQUENCY,
.duty_cycle = INFRARED_COMMON_DUTY_CYCLE,
};
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) {
if(protocol == InfraredProtocolKaseikyo)
return &infrared_kaseikyo_protocol_specification;
else
return NULL;
}
+1 -4
View File
@@ -223,10 +223,7 @@ void infrared_worker_rx_set_received_signal_callback(
InfraredWorker* infrared_worker_alloc() {
InfraredWorker* instance = malloc(sizeof(InfraredWorker));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "InfraredWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
instance->thread = furi_thread_alloc_ex("InfraredWorker", 2048, NULL, instance);
size_t buffer_size =
MAX(sizeof(InfraredWorkerTiming) * (MAX_TIMINGS_AMOUNT + 1),
+1 -1
View File
@@ -2,7 +2,7 @@ Import("env")
env.Append(
LINT_SOURCES=[
"lib/lfrfid",
Dir("."),
],
CPPPATH=[
"#/lib/lfrfid",
+1 -4
View File
@@ -61,10 +61,7 @@ static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context);
LFRFIDRawWorker* lfrfid_raw_worker_alloc() {
LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker));
worker->thread = furi_thread_alloc();
furi_thread_set_name(worker->thread, "lfrfid_raw_worker");
furi_thread_set_context(worker->thread, worker);
furi_thread_set_stack_size(worker->thread, 2048);
worker->thread = furi_thread_alloc_ex("LfrfidRawWorker", 2048, NULL, worker);
worker->events = furi_event_flag_alloc(NULL);
+2 -7
View File
@@ -29,11 +29,7 @@ LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) {
worker->raw_filename = NULL;
worker->mode_storage = NULL;
worker->thread = furi_thread_alloc();
furi_thread_set_name(worker->thread, "lfrfid_worker");
furi_thread_set_callback(worker->thread, lfrfid_worker_thread);
furi_thread_set_context(worker->thread, worker);
furi_thread_set_stack_size(worker->thread, 2048);
worker->thread = furi_thread_alloc_ex("LfrfidWorker", 2048, lfrfid_worker_thread, worker);
worker->protocols = dict;
@@ -140,9 +136,8 @@ size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol pro
static int32_t lfrfid_worker_thread(void* thread_context) {
LFRFIDWorker* worker = thread_context;
bool running = true;
while(running) {
while(true) {
uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever);
if(flags != FuriFlagErrorTimeout) {
// stop thread
+1 -1
View File
@@ -5,7 +5,7 @@ env.Append(
"#/lib/nfc",
],
SDK_HEADERS=[
File("#/lib/nfc/nfc_device.h"),
File("nfc_device.h"),
],
)
+2 -5
View File
@@ -104,11 +104,8 @@ ReaderAnalyzer* reader_analyzer_alloc() {
instance->stream =
furi_stream_buffer_alloc(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_callback(instance->thread, reader_analyzer_thread);
furi_thread_set_context(instance->thread, instance);
instance->thread =
furi_thread_alloc_ex("ReaderAnalyzerWorker", 2048, reader_analyzer_thread, instance);
furi_thread_set_priority(instance->thread, FuriThreadPriorityLow);
return instance;
+55 -34
View File
@@ -7,11 +7,12 @@
#include <lib/nfc/protocols/nfc_util.h>
#include <flipper_format/flipper_format.h>
#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache")
#define TAG "NfcDevice"
#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/.cache")
#define NFC_DEVICE_KEYS_EXTENSION ".keys"
static const char* nfc_file_header = "Flipper NFC device";
static const uint32_t nfc_file_version = 2;
static const uint32_t nfc_file_version = 3;
static const char* nfc_keys_file_header = "Flipper NFC keys";
static const uint32_t nfc_keys_file_version = 1;
@@ -26,6 +27,11 @@ NfcDevice* nfc_device_alloc() {
nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS);
nfc_dev->load_path = furi_string_alloc();
nfc_dev->dev_data.parsed_data = furi_string_alloc();
// Rename cache folder name for backward compatibility
if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) {
storage_common_rename(nfc_dev->storage, "/ext/nfc/cache", NFC_DEVICE_KEYS_FOLDER);
}
return nfc_dev;
}
@@ -213,6 +219,9 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) {
uint32_t auth_counter;
if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1))
auth_counter = 0;
data->curr_authlim = auth_counter;
data->auth_success = mf_ul_is_full_capture(data);
parsed = true;
} while(false);
@@ -627,7 +636,10 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
*app_head = app;
app_head = &app->next;
}
if(!parsed_apps) break;
if(!parsed_apps) {
// accept non-parsed apps, just log a warning:
FURI_LOG_W(TAG, "Non-parsed apps found!");
}
}
parsed = true;
} while(false);
@@ -1006,12 +1018,7 @@ static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow
furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION);
}
static bool nfc_device_save_file(
NfcDevice* dev,
const char* dev_name,
const char* folder,
const char* extension,
bool use_load_path) {
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
furi_assert(dev);
bool saved = false;
@@ -1021,19 +1028,10 @@ static bool nfc_device_save_file(
temp_str = furi_string_alloc();
do {
if(use_load_path && !furi_string_empty(dev->load_path)) {
// Get directory name
path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str);
// Create nfc directory if necessary
if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(temp_str))) break;
// Make path to file to save
furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension);
} else {
// Create nfc directory if necessary
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
// First remove nfc device file if it was saved
furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
}
// Create nfc directory if necessary
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
// First remove nfc device file if it was saved
furi_string_printf(temp_str, "%s", dev_name);
// Open file
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
// Write header
@@ -1048,7 +1046,9 @@ static bool nfc_device_save_file(
if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
break;
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break;
// Save ATQA in MSB order for correct companion apps display
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
// Save more data if necessary
if(dev->format == NfcDeviceSaveFormatMifareUl) {
@@ -1072,13 +1072,19 @@ static bool nfc_device_save_file(
return saved;
}
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true);
}
bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
bool nfc_device_save_shadow(NfcDevice* dev, const char* path) {
dev->shadow_file_exist = true;
return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true);
// Replace extension from .nfc to .shd if necessary
FuriString* orig_path = furi_string_alloc();
furi_string_set_str(orig_path, path);
FuriString* shadow_path = furi_string_alloc();
nfc_device_get_shadow_path(orig_path, shadow_path);
bool file_saved = nfc_device_save(dev, furi_string_get_cstr(shadow_path));
furi_string_free(orig_path);
furi_string_free(shadow_path);
return file_saved;
}
static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dialog) {
@@ -1090,6 +1096,9 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
temp_str = furi_string_alloc();
bool deprecated_version = false;
// Version 2 of file format had ATQA bytes swapped
uint32_t version_with_lsb_atqa = 2;
if(dev->loading_cb) {
dev->loading_cb(dev->loading_cb_ctx, true);
}
@@ -1108,9 +1117,12 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
// Read and verify file header
uint32_t version = 0;
if(!flipper_format_read_header(file, temp_str, &version)) break;
if(furi_string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) {
deprecated_version = true;
break;
if(furi_string_cmp_str(temp_str, nfc_file_header)) break;
if(version != nfc_file_version) {
if(version < version_with_lsb_atqa) {
deprecated_version = true;
break;
}
}
// Read Nfc device type
if(!flipper_format_read_string(file, "Device type", temp_str)) break;
@@ -1120,7 +1132,14 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
if(!(data_cnt == 4 || data_cnt == 7)) break;
data->uid_len = data_cnt;
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
if(version == version_with_lsb_atqa) {
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
} else {
uint8_t atqa[2] = {};
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
data->atqa[0] = atqa[1];
data->atqa[1] = atqa[0];
}
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
// Load CUID
uint8_t* cuid_start = data->uid;
@@ -1188,14 +1207,16 @@ bool nfc_file_select(NfcDevice* dev) {
const DialogsFileBrowserOptions browser_options = {
.extension = NFC_APP_EXTENSION,
.skip_assets = true,
.hide_dot_files = true,
.icon = &I_Nfc_10px,
.hide_ext = true,
.item_loader_callback = NULL,
.item_loader_context = NULL,
.base_path = NFC_APP_FOLDER,
};
bool res =
dialog_file_browser_show(dev->dialogs, dev->load_path, nfc_app_folder, &browser_options);
dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options);
furi_string_free(nfc_app_folder);
if(res) {
+11
View File
@@ -51,12 +51,23 @@ typedef struct {
MfClassicDict* dict;
} NfcMfClassicDictAttackData;
typedef enum {
NfcReadModeAuto,
NfcReadModeMfClassic,
NfcReadModeMfUltralight,
NfcReadModeMfDesfire,
NfcReadModeEMV,
NfcReadModeNFCA,
} NfcReadMode;
typedef struct {
FuriHalNfcDevData nfc_data;
NfcProtocol protocol;
NfcReadMode read_mode;
union {
NfcReaderRequestData reader_data;
NfcMfClassicDictAttackData mf_classic_dict_attack_data;
MfUltralightAuth mf_ul_auth;
};
union {
EmvData emv_data;
+103 -12
View File
@@ -12,11 +12,7 @@ NfcWorker* nfc_worker_alloc() {
NfcWorker* nfc_worker = malloc(sizeof(NfcWorker));
// Worker thread attributes
nfc_worker->thread = furi_thread_alloc();
furi_thread_set_name(nfc_worker->thread, "NfcWorker");
furi_thread_set_stack_size(nfc_worker->thread, 8192);
furi_thread_set_callback(nfc_worker->thread, nfc_worker_task);
furi_thread_set_context(nfc_worker->thread, nfc_worker);
nfc_worker->thread = furi_thread_alloc_ex("NfcWorker", 8192, nfc_worker_task, nfc_worker);
nfc_worker->callback = NULL;
nfc_worker->context = NULL;
@@ -70,12 +66,12 @@ void nfc_worker_start(
void nfc_worker_stop(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) {
return;
furi_assert(nfc_worker->thread);
if(furi_thread_get_state(nfc_worker->thread) != FuriThreadStateStopped) {
furi_hal_nfc_stop();
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
furi_thread_join(nfc_worker->thread);
}
furi_hal_nfc_stop();
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
furi_thread_join(nfc_worker->thread);
}
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
@@ -90,7 +86,11 @@ int32_t nfc_worker_task(void* context) {
furi_hal_nfc_exit_sleep();
if(nfc_worker->state == NfcWorkerStateRead) {
nfc_worker_read(nfc_worker);
if(nfc_worker->dev_data->read_mode == NfcReadModeAuto) {
nfc_worker_read(nfc_worker);
} else {
nfc_worker_read_type(nfc_worker);
}
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
nfc_worker_emulate_uid(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
@@ -394,6 +394,81 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
}
}
void nfc_worker_read_type(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
furi_assert(nfc_worker->callback);
NfcReadMode read_mode = nfc_worker->dev_data->read_mode;
nfc_device_data_clear(nfc_worker->dev_data);
NfcDeviceData* dev_data = nfc_worker->dev_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
FuriHalNfcTxRxContext tx_rx = {};
NfcWorkerEvent event = 0;
bool card_not_detected_notified = false;
while(nfc_worker->state == NfcWorkerStateRead) {
if(furi_hal_nfc_detect(nfc_data, 300)) {
FURI_LOG_D(TAG, "Card detected");
furi_hal_nfc_sleep();
// Process first found device
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
card_not_detected_notified = false;
if(nfc_data->type == FuriHalNfcTypeA) {
if(read_mode == NfcReadModeMfClassic) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic;
nfc_worker->dev_data->mf_classic_data.type = mf_classic_get_classic_type(
nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
if(nfc_worker_read_mf_classic(nfc_worker, &tx_rx)) {
FURI_LOG_D(TAG, "Card read");
dev_data->protocol = NfcDeviceProtocolMifareClassic;
event = NfcWorkerEventReadMfClassicDone;
break;
} else {
FURI_LOG_D(TAG, "Card read failed");
dev_data->protocol = NfcDeviceProtocolMifareClassic;
event = NfcWorkerEventReadMfClassicDictAttackRequired;
break;
}
} else if(read_mode == NfcReadModeMfUltralight) {
FURI_LOG_I(TAG, "Mifare Ultralight / NTAG");
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl;
if(nfc_worker_read_mf_ultralight(nfc_worker, &tx_rx)) {
event = NfcWorkerEventReadMfUltralight;
break;
}
} else if(read_mode == NfcReadModeMfDesfire) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire;
if(nfc_worker_read_mf_desfire(nfc_worker, &tx_rx)) {
event = NfcWorkerEventReadMfDesfire;
break;
}
} else if(read_mode == NfcReadModeEMV) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV;
if(nfc_worker_read_bank_card(nfc_worker, &tx_rx)) {
event = NfcWorkerEventReadBankCard;
break;
}
} else if(read_mode == NfcReadModeNFCA) {
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
event = NfcWorkerEventReadUidNfcA;
break;
}
} else {
if(!card_not_detected_notified) {
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
card_not_detected_notified = true;
}
}
}
furi_hal_nfc_sleep();
furi_delay_ms(100);
}
// Notify caller and exit
if(event > NfcWorkerEventReserved) {
nfc_worker->callback(event, nfc_worker->context);
}
}
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
@@ -452,10 +527,25 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
}
}
void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) {
furi_assert(context);
NfcWorker* nfc_worker = context;
nfc_worker->dev_data->mf_ul_auth = auth;
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context);
}
}
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfUltralightEmulator emulator = {};
mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data);
// TODO rework with reader analyzer
emulator.auth_received_callback = nfc_worker_mf_ultralight_auth_received_callback;
emulator.context = nfc_worker;
while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) {
mf_ul_reset_emulation(&emulator, true);
furi_hal_nfc_emulate_nfca(
@@ -830,7 +920,8 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) {
if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
if(data->auth_method == MfUltralightAuthMethodManual) {
if(data->auth_method == MfUltralightAuthMethodManual ||
data->auth_method == MfUltralightAuthMethodAuto) {
nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context);
key = nfc_util_bytes2num(data->auth_key, 4);
} else if(data->auth_method == MfUltralightAuthMethodAmeebo) {
+2 -3
View File
@@ -7,7 +7,6 @@ typedef struct NfcWorker NfcWorker;
typedef enum {
// Init states
NfcWorkerStateNone,
NfcWorkerStateBroken,
NfcWorkerStateReady,
// Main worker states
NfcWorkerStateRead,
@@ -66,8 +65,8 @@ typedef enum {
NfcWorkerEventDetectReaderMfkeyCollected,
// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey,
NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
+2
View File
@@ -35,6 +35,8 @@ int32_t nfc_worker_task(void* context);
void nfc_worker_read(NfcWorker* nfc_worker);
void nfc_worker_read_type(NfcWorker* nfc_worker);
void nfc_worker_emulate_uid(NfcWorker* nfc_worker);
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker);
+7 -37
View File
@@ -82,7 +82,7 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
}
}
static uint16_t mf_classic_get_total_block_num(MfClassicType type) {
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
if(type == MfClassicType1k) {
return 64;
} else if(type == MfClassicType4k) {
@@ -712,46 +712,16 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
furi_assert(tx_rx);
furi_assert(data);
uint8_t sectors_read = 0;
Crypto1 crypto = {};
uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type);
uint64_t key_a = 0;
uint64_t key_b = 0;
MfClassicSectorReader sec_reader = {};
MfClassicSector temp_sector = {};
for(size_t i = 0; i < total_sectors; i++) {
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);
// Load key A
if(mf_classic_is_key_found(data, i, MfClassicKeyA)) {
sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6);
} else {
sec_reader.key_a = MF_CLASSIC_NO_KEY;
}
// Load key B
if(mf_classic_is_key_found(data, i, MfClassicKeyB)) {
sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6);
} else {
sec_reader.key_b = MF_CLASSIC_NO_KEY;
}
if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) {
sec_reader.sector_num = i;
if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) {
uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
for(uint8_t j = 0; j < temp_sector.total_blocks; j++) {
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
}
sectors_read++;
} else {
// Invalid key, set it to not found
if(key_a != MF_CLASSIC_NO_KEY) {
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
} else {
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
}
}
}
mf_classic_read_sector(tx_rx, data, i);
}
uint8_t sectors_read = 0;
uint8_t keys_found = 0;
mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);
FURI_LOG_D(TAG, "Read %d sectors and %d keys", sectors_read, keys_found);
return sectors_read;
}
+2
View File
@@ -98,6 +98,8 @@ MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t S
uint8_t mf_classic_get_total_sectors_num(MfClassicType type);
uint16_t mf_classic_get_total_block_num(MfClassicType type);
uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector);
bool mf_classic_is_sector_trailer(uint8_t block);
+54 -3
View File
@@ -51,7 +51,7 @@ void mf_ul_reset(MfUltralightData* data) {
data->data_size = 0;
data->data_read = 0;
data->curr_authlim = 0;
data->has_auth = false;
data->auth_success = false;
}
static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) {
@@ -756,6 +756,34 @@ bool mf_ul_read_card(
mf_ultralight_read_tearing_flags(tx_rx, data);
}
data->curr_authlim = 0;
if(reader->pages_read == reader->pages_to_read &&
reader->supported_features & MfUltralightSupportAuth && !data->auth_success) {
MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data);
if(config->access.authlim == 0) {
// Attempt to auth with default PWD
uint16_t pack;
data->auth_success = mf_ultralight_authenticate(tx_rx, MF_UL_DEFAULT_PWD, &pack);
if(data->auth_success) {
config->auth_data.pwd.value = MF_UL_DEFAULT_PWD;
config->auth_data.pack.value = pack;
} else {
furi_hal_nfc_sleep();
furi_hal_nfc_activate_nfca(300, NULL);
}
}
}
}
if(reader->pages_read != reader->pages_to_read) {
if(reader->supported_features & MfUltralightSupportAuth) {
// Probably password protected, fix AUTH0 and PROT so before AUTH0
// can be written and since AUTH0 won't be readable, like on the
// original card
MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data);
config->auth0 = reader->pages_read;
config->access.prot = true;
}
}
return card_read;
@@ -1201,6 +1229,8 @@ static void mf_ul_emulate_write(
}
void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) {
emulator->comp_write_cmd_started = false;
emulator->sector_select_cmd_started = false;
emulator->curr_sector = 0;
emulator->ntag_i2c_plus_sector3_lockout = false;
emulator->auth_success = false;
@@ -1244,8 +1274,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d
emulator->config = mf_ultralight_get_config_pages(&emulator->data);
emulator->page_num = emulator->data.data_size / 4;
emulator->data_changed = false;
emulator->comp_write_cmd_started = false;
emulator->sector_select_cmd_started = false;
memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth));
mf_ul_reset_emulation(emulator, true);
}
@@ -1706,6 +1735,17 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_AUTH) {
if(emulator->supported_features & MfUltralightSupportAuth) {
if(buff_rx_len == (1 + 4) * 8) {
// Record password sent by PCD
memcpy(
emulator->auth_attempt.pwd.raw,
&buff_rx[1],
sizeof(emulator->auth_attempt.pwd.raw));
emulator->auth_attempted = true;
if(emulator->auth_received_callback) {
emulator->auth_received_callback(
emulator->auth_attempt, emulator->context);
}
uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data);
if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) {
if(emulator->data.curr_authlim != UINT16_MAX) {
@@ -1863,3 +1903,14 @@ bool mf_ul_prepare_emulation_response(
return tx_bits > 0;
}
bool mf_ul_is_full_capture(MfUltralightData* data) {
if(data->data_read != data->data_size) return false;
// Having read all the pages doesn't mean that we've got everything.
// By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000,
// so a default read on an auth-supported NTAG is never complete.
if(!(mf_ul_get_features(data->type) & MfUltralightSupportAuth)) return true;
MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data);
return config->auth_data.pwd.value != 0 || config->auth_data.pack.value != 0;
}
+14 -1
View File
@@ -28,10 +28,13 @@
#define MF_UL_NTAG203_COUNTER_PAGE (41)
#define MF_UL_DEFAULT_PWD (0xFFFFFFFF)
typedef enum {
MfUltralightAuthMethodManual,
MfUltralightAuthMethodAmeebo,
MfUltralightAuthMethodXiaomi,
MfUltralightAuthMethodAuto,
} MfUltralightAuthMethod;
// Important: order matters; some features are based on positioning in this enum
@@ -110,7 +113,6 @@ typedef struct {
uint8_t signature[32];
uint32_t counter[3];
uint8_t tearing[3];
bool has_auth;
MfUltralightAuthMethod auth_method;
uint8_t auth_key[4];
bool auth_success;
@@ -169,6 +171,9 @@ typedef struct {
MfUltralightFeatures supported_features;
} MfUltralightReader;
// TODO rework with reader analyzer
typedef void (*MfUltralightAuthReceivedCallback)(MfUltralightAuth auth, void* context);
typedef struct {
MfUltralightData data;
MfUltralightConfigPages* config;
@@ -185,6 +190,12 @@ typedef struct {
bool sector_select_cmd_started;
bool ntag_i2c_plus_sector3_lockout;
bool read_counter_incremented;
bool auth_attempted;
MfUltralightAuth auth_attempt;
// TODO rework with reader analyzer
MfUltralightAuthReceivedCallback auth_received_callback;
void* context;
} MfUltralightEmulator;
void mf_ul_reset(MfUltralightData* data);
@@ -241,3 +252,5 @@ bool mf_ul_prepare_emulation_response(
uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data);
uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data);
bool mf_ul_is_full_capture(MfUltralightData* data);
+4 -6
View File
@@ -37,11 +37,7 @@ iButtonWorker* ibutton_worker_alloc() {
worker->emulate_cb = NULL;
worker->cb_ctx = NULL;
worker->thread = furi_thread_alloc();
furi_thread_set_name(worker->thread, "ibutton_worker");
furi_thread_set_callback(worker->thread, ibutton_worker_thread);
furi_thread_set_context(worker->thread, worker);
furi_thread_set_stack_size(worker->thread, 2048);
worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker);
worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax);
@@ -135,7 +131,9 @@ void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) {
void ibutton_worker_notify_emulate(iButtonWorker* worker) {
iButtonMessage message = {.type = iButtonMessageNotifyEmulate};
furi_check(furi_message_queue_put(worker->messages, &message, 0) == FuriStatusOk);
// we're running in an interrupt context, so we can't wait
// and we can drop message if queue is full, that's ok for that message
furi_message_queue_put(worker->messages, &message, 0);
}
void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) {
+1 -1
View File
@@ -222,8 +222,8 @@ void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) {
memcpy(device_id, key_id, key_size);
onewire_slave_attach(worker->slave, worker->device);
onewire_slave_start(worker->slave);
onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker);
onewire_slave_start(worker->slave);
}
void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) {
+1 -1
View File
@@ -541,7 +541,7 @@ static size_t _etoa(
exp2 = (int)(expval * 3.321928094887362 + 0.5);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = (uint64_t)(exp2 + 1023) << 52U;
conv.U = ((uint64_t)exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
+346
View File
@@ -0,0 +1,346 @@
#include "ansonic.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#define TAG "SubGhzProtocolAnsonic"
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
#define CNT_TO_DIP(dip) \
(dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \
(dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \
(dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \
(dip & 0x0008 ? '1' : '0')
static const SubGhzBlockConst subghz_protocol_ansonic_const = {
.te_short = 555,
.te_long = 1111,
.te_delta = 120,
.min_count_bit_for_found = 12,
};
struct SubGhzProtocolDecoderAnsonic {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
};
struct SubGhzProtocolEncoderAnsonic {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
AnsonicDecoderStepReset = 0,
AnsonicDecoderStepFoundStartBit,
AnsonicDecoderStepSaveDuration,
AnsonicDecoderStepCheckDuration,
} AnsonicDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = {
.alloc = subghz_protocol_decoder_ansonic_alloc,
.free = subghz_protocol_decoder_ansonic_free,
.feed = subghz_protocol_decoder_ansonic_feed,
.reset = subghz_protocol_decoder_ansonic_reset,
.get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data,
.serialize = subghz_protocol_decoder_ansonic_serialize,
.deserialize = subghz_protocol_decoder_ansonic_deserialize,
.get_string = subghz_protocol_decoder_ansonic_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = {
.alloc = subghz_protocol_encoder_ansonic_alloc,
.free = subghz_protocol_encoder_ansonic_free,
.deserialize = subghz_protocol_encoder_ansonic_deserialize,
.stop = subghz_protocol_encoder_ansonic_stop,
.yield = subghz_protocol_encoder_ansonic_yield,
};
const SubGhzProtocol subghz_protocol_ansonic = {
.name = SUBGHZ_PROTOCOL_ANSONIC_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_ansonic_decoder,
.encoder = &subghz_protocol_ansonic_encoder,
};
void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic));
instance->base.protocol = &subghz_protocol_ansonic;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 52;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_ansonic_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderAnsonic* instance = context;
free(instance->encoder.upload);
free(instance);
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance
* @return true On success
*/
static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) {
furi_assert(instance);
size_t index = 0;
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
if(size_upload > instance->encoder.size_upload) {
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
return false;
} else {
instance->encoder.size_upload = size_upload;
}
//Send header
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35);
//Send start bit
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short);
//Send key data
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long);
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short);
}
}
return true;
}
bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderAnsonic* instance = context;
bool res = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
FURI_LOG_E(TAG, "Deserialize error");
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_ansonic_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
} while(false);
return res;
}
void subghz_protocol_encoder_ansonic_stop(void* context) {
SubGhzProtocolEncoderAnsonic* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) {
SubGhzProtocolEncoderAnsonic* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic));
instance->base.protocol = &subghz_protocol_ansonic;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_ansonic_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
free(instance);
}
void subghz_protocol_decoder_ansonic_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
instance->decoder.parser_step = AnsonicDecoderStepReset;
}
void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
switch(instance->decoder.parser_step) {
case AnsonicDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) <
subghz_protocol_ansonic_const.te_delta * 35)) {
//Found header Ansonic
instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit;
}
break;
case AnsonicDecoderStepFoundStartBit:
if(!level) {
break;
} else if(
DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) <
subghz_protocol_ansonic_const.te_delta) {
//Found start bit Ansonic
instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
} else {
instance->decoder.parser_step = AnsonicDecoderStepReset;
}
break;
case AnsonicDecoderStepSaveDuration:
if(!level) { //save interval
if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) {
instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit;
if(instance->decoder.decode_count_bit >=
subghz_protocol_ansonic_const.min_count_bit_for_found) {
instance->generic.serial = 0x0;
instance->generic.btn = 0x0;
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
break;
}
instance->decoder.te_last = duration;
instance->decoder.parser_step = AnsonicDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = AnsonicDecoderStepReset;
}
break;
case AnsonicDecoderStepCheckDuration:
if(level) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) <
subghz_protocol_ansonic_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) <
subghz_protocol_ansonic_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) <
subghz_protocol_ansonic_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) <
subghz_protocol_ansonic_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;
} else
instance->decoder.parser_step = AnsonicDecoderStepReset;
} else {
instance->decoder.parser_step = AnsonicDecoderStepReset;
}
break;
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) {
/*
* 12345678(10) k 9
* AAA => 10101010 1 01 0
*
* 1...10 - DIP
* k- KEY
*/
instance->cnt = instance->data & 0xFFF;
instance->btn = ((instance->data >> 1) & 0x3);
}
uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool subghz_protocol_decoder_ansonic_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
bool ret = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_ansonic_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
ret = true;
} while(false);
return ret;
}
void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderAnsonic* instance = context;
subghz_protocol_ansonic_check_remote_controller(&instance->generic);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%03lX\r\n"
"Btn:%X\r\n"
"DIP:" DIP_PATTERN "\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
instance->generic.btn,
CNT_TO_DIP(instance->generic.cnt));
}
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic"
typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic;
typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic;
extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder;
extern const SubGhzProtocol subghz_protocol_ansonic;
/**
* Allocate SubGhzProtocolEncoderAnsonic.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance
*/
void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderAnsonic.
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
*/
void subghz_protocol_encoder_ansonic_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
*/
void subghz_protocol_encoder_ansonic_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_ansonic_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderAnsonic.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance
*/
void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderAnsonic.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
*/
void subghz_protocol_decoder_ansonic_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderAnsonic.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
*/
void subghz_protocol_decoder_ansonic_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderAnsonic.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return true On success
*/
bool subghz_protocol_decoder_ansonic_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data SubGhzProtocolDecoderAnsonic.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
* @param output Resulting text
*/
void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output);
+6 -6
View File
@@ -138,9 +138,9 @@ bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat*
FURI_LOG_E(TAG, "Deserialize error");
break;
}
if((instance->generic.data_count_bit !=
subghz_protocol_nice_flo_const.min_count_bit_for_found) &&
(instance->generic.data_count_bit !=
if((instance->generic.data_count_bit <
subghz_protocol_nice_flo_const.min_count_bit_for_found) ||
(instance->generic.data_count_bit >
2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
@@ -297,9 +297,9 @@ bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat*
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if((instance->generic.data_count_bit !=
subghz_protocol_nice_flo_const.min_count_bit_for_found) &&
(instance->generic.data_count_bit !=
if((instance->generic.data_count_bit <
subghz_protocol_nice_flo_const.min_count_bit_for_found) ||
(instance->generic.data_count_bit >
2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
-1
View File
@@ -312,7 +312,6 @@ void subghz_protocol_decoder_power_smart_feed(
if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) ==
POWER_SMART_PACKET_HEADER) {
if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) {
instance->decoder.decode_data = instance->decoder.decode_data;
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit =
subghz_protocol_power_smart_const.min_count_bit_for_found;
+3 -4
View File
@@ -15,8 +15,8 @@
#define TAG "SubGhzProtocolPrinceton"
static const SubGhzBlockConst subghz_protocol_princeton_const = {
.te_short = 400,
.te_long = 1200,
.te_short = 390,
.te_long = 1170,
.te_delta = 300,
.min_count_bit_for_found = 24,
};
@@ -245,8 +245,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t
break;
case PrincetonDecoderStepCheckDuration:
if(!level) {
if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_short * 10 +
subghz_protocol_princeton_const.te_delta)) {
if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) {
instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;
if(instance->decoder.decode_count_bit ==
subghz_protocol_princeton_const.min_count_bit_for_found) {
+1 -1
View File
@@ -12,7 +12,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
&subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec,
&subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2,
&subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3,
&subghz_protocol_clemsa,
&subghz_protocol_clemsa, &subghz_protocol_ansonic,
};
const SubGhzProtocolRegistry subghz_protocol_registry = {
+1
View File
@@ -35,5 +35,6 @@
#include "magellan.h"
#include "intertechno_v3.h"
#include "clemsa.h"
#include "ansonic.h"
extern const SubGhzProtocolRegistry subghz_protocol_registry;
+11 -12
View File
@@ -18,6 +18,7 @@ struct SubGhzFileEncoderWorker {
volatile bool worker_running;
volatile bool worker_stoping;
bool level;
bool is_storage_slow;
FuriString* str_data;
FuriString* file_path;
@@ -86,7 +87,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
if(ret == sizeof(int32_t)) {
LevelDuration level_duration = {.level = LEVEL_DURATION_RESET};
if(duration < 0) {
level_duration = level_duration_make(false, duration * -1);
level_duration = level_duration_make(false, -duration);
} else if(duration > 0) {
level_duration = level_duration_make(true, duration);
} else if(duration == 0) {
@@ -96,7 +97,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
}
return level_duration;
} else {
FURI_LOG_E(TAG, "Slow flash read");
instance->is_storage_slow = true;
return level_duration_wait();
}
}
@@ -110,6 +111,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
SubGhzFileEncoderWorker* instance = context;
FURI_LOG_I(TAG, "Worker start");
bool res = false;
instance->is_storage_slow = false;
Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
do {
if(!flipper_format_file_open_existing(
@@ -139,21 +141,21 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
furi_string_trim(instance->str_data);
if(!subghz_file_encoder_worker_data_parse(
instance, furi_string_get_cstr(instance->str_data))) {
//to stop DMA correctly
subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
break;
}
} else {
subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
break;
}
} else {
furi_delay_ms(1);
}
furi_delay_ms(5);
}
//waiting for the end of the transfer
if(instance->is_storage_slow) {
FURI_LOG_E(TAG, "Storage is slow");
}
FURI_LOG_I(TAG, "End read file");
while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) {
furi_delay_ms(5);
@@ -174,11 +176,8 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() {
SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "SubGhzFEWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread);
instance->thread =
furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance);
instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t));
instance->storage = furi_record_open(RECORD_STORAGE);
+1 -1
View File
@@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save(
}
stream_write_cstring(output_stream, encrypted_line);
} while(ret > 0 && result);
} while(result);
flipper_format_free(output_flipper_format);
+2 -5
View File
@@ -201,11 +201,8 @@ static int32_t subghz_tx_rx_worker_thread(void* context) {
SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() {
SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "SubGhzTxRxWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread);
instance->thread =
furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance);
instance->stream_tx =
furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
instance->stream_rx =
+2 -5
View File
@@ -88,11 +88,8 @@ static int32_t subghz_worker_thread_callback(void* context) {
SubGhzWorker* subghz_worker_alloc() {
SubGhzWorker* instance = malloc(sizeof(SubGhzWorker));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "SubGhzWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_callback(instance->thread, subghz_worker_thread_callback);
instance->thread =
furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance);
instance->stream =
furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration));
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <furi/furi.h>
/*
Testing 10000 api calls
No lock
Time diff: 445269.218750 us
Time per call: 44.526920 us
furi_thread_flags
Time diff: 430279.875000 us // lol wtf? smaller than no lock?
Time per call: 43.027988 us // I tested it many times, it's always smaller
FuriEventFlag
Time diff: 831523.625000 us
Time per call: 83.152359 us
FuriSemaphore
Time diff: 999807.125000 us
Time per call: 99.980713 us
FuriMutex
Time diff: 1071417.500000 us
Time per call: 107.141747 us
*/
typedef FuriEventFlag* FuriApiLock;
#define API_LOCK_EVENT (1U << 0)
#define api_lock_alloc_locked() furi_event_flag_alloc()
#define api_lock_wait_unlock(_lock) \
furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever)
#define api_lock_free(_lock) furi_event_flag_free(_lock)
#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT)
#define api_lock_wait_unlock_and_free(_lock) \
api_lock_wait_unlock(_lock); \
api_lock_free(_lock);
+11 -16
View File
@@ -38,7 +38,7 @@ void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) {
size_t dot = furi_string_search_rchar(path, '.');
size_t filename_start = furi_string_search_rchar(path, '/');
if((dot > 0) && (filename_start < dot)) {
if((dot != FURI_STRING_FAILURE) && (filename_start < dot)) {
strlcpy(ext, &(furi_string_get_cstr(path))[dot], ext_len_max);
}
}
@@ -95,22 +95,17 @@ bool path_contains_only_ascii(const char* path) {
name_pos++;
}
while(*name_pos != '\0') {
if((*name_pos >= '0') && (*name_pos <= '9')) {
name_pos++;
continue;
} else if((*name_pos >= 'A') && (*name_pos <= 'Z')) {
name_pos++;
continue;
} else if((*name_pos >= 'a') && (*name_pos <= 'z')) {
name_pos++;
continue;
} else if(strchr(" .!#\\$%&'()-@^_`{}~", *name_pos) != NULL) {
name_pos++;
continue;
}
for(; *name_pos; ++name_pos) {
const char c = *name_pos;
return false;
// Regular ASCII characters from 0x20 to 0x7e
const bool is_out_of_range = (c < ' ') || (c > '~');
// Cross-platform forbidden character set
const bool is_forbidden = strchr("\\<>*|\":?", c);
if(is_out_of_range || is_forbidden) {
return false;
}
}
return true;
+33
View File
@@ -0,0 +1,33 @@
#include "property.h"
#include <core/check.h>
void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...) {
furi_assert(ctx);
furi_string_reset(ctx->key);
va_list args;
va_start(args, nparts);
for(size_t i = 0; i < nparts; ++i) {
const char* keypart = va_arg(args, const char*);
furi_string_cat(ctx->key, keypart);
if(i < nparts - 1) {
furi_string_push_back(ctx->key, ctx->sep);
}
}
const char* value_str;
if(fmt) {
furi_string_vprintf(ctx->value, fmt, args);
value_str = furi_string_get_cstr(ctx->value);
} else {
// C string passthrough (no formatting)
value_str = va_arg(args, const char*);
}
va_end(args);
ctx->out(furi_string_get_cstr(ctx->key), value_str, ctx->last, ctx->context);
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <core/string.h>
/** Callback type called every time another key-value pair of device information is ready
*
* @param key[in] device information type identifier
* @param value[in] device information value
* @param last[in] whether the passed key-value pair is the last one
* @param context[in] to pass to callback
*/
typedef void (*PropertyValueCallback)(const char* key, const char* value, bool last, void* context);
typedef struct {
FuriString* key; /**< key string buffer, must be initialised before use */
FuriString* value; /**< value string buffer, must be initialised before use */
PropertyValueCallback out; /**< output callback function */
char sep; /**< separator character between key parts */
bool last; /**< flag to indicate last element */
void* context; /**< user-defined context, passed through to out callback */
} PropertyValueContext;
/** Builds key and value strings and outputs them via a callback function
*
* @param ctx[in] local property context
* @param fmt[in] value format, set to NULL to bypass formatting
* @param nparts[in] number of key parts (separated by character)
* @param ...[in] list of key parts followed by value
*/
void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...);
#ifdef __cplusplus
}
#endif
-6
View File
@@ -5,12 +5,6 @@
#include <furi.h>
void set_random_name(char* name, uint8_t max_name_size) {
static bool rand_generator_inited = false;
if(!rand_generator_inited) {
srand(DWT->CYCCNT);
rand_generator_inited = true;
}
const char* prefix[] = {
"ancient", "hollow", "strange", "disappeared", "unknown",
"unthinkable", "unnamable", "nameless", "my", "concealed",