Merge remote-tracking branch 'UFW/dev' into cc1101_ext

This commit is contained in:
gid9798
2023-07-03 11:15:32 +03:00
201 changed files with 4851 additions and 1904 deletions
+2
View File
@@ -15,6 +15,7 @@ env.Append(
Dir("u8g2"),
Dir("update_util"),
Dir("print"),
Dir("music_worker"),
],
)
@@ -100,6 +101,7 @@ libs = env.BuildModules(
"misc",
"lfrfid",
"flipper_application",
"music_worker",
],
)
+101 -98
View File
@@ -51,8 +51,16 @@ struct DigitalSignalInternals {
#define T_TIM 1562 /* 15.625 ns *100 */
#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */
/* end marker in DMA ringbuffer, will get written into timer register at the end */
#define SEQ_TIMER_MAX 0xFFFFFFFF
/* time to wait in loops before returning */
#define SEQ_LOCK_WAIT_MS 10UL
#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64)
/* maximum entry count of the sequence dma ring buffer */
#define SEQUENCE_DMA_RINGBUFFER_SIZE 32
#define RINGBUFFER_SIZE 128
/* maximum number of DigitalSignals in a sequence */
#define SEQUENCE_SIGNALS_SIZE 32
/*
@@ -214,12 +222,12 @@ void digital_signal_prepare_arr(DigitalSignal* signal) {
for(size_t pos = 0; pos < signal->edge_cnt; pos++) {
uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder;
if(pulse_duration < 10 || pulse_duration > 10000000) {
/*FURI_LOG_D(
FURI_LOG_D(
TAG,
"[prepare] pulse_duration out of range: %lu = %lu * %llu",
pulse_duration,
signal->edge_timings[pos],
internals->factor);*/
internals->factor);
pulse_duration = 100;
}
uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM;
@@ -243,20 +251,16 @@ static void digital_signal_stop_timer() {
LL_TIM_DisableUpdateEvent(TIM2);
LL_TIM_DisableDMAReq_UPDATE(TIM2);
if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) {
furi_hal_bus_disable(FuriHalBusTIM2);
}
furi_hal_bus_disable(FuriHalBusTIM2);
}
static void digital_signal_setup_timer() {
if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) {
furi_hal_bus_enable(FuriHalBusTIM2);
}
furi_hal_bus_enable(FuriHalBusTIM2);
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX);
LL_TIM_SetCounter(TIM2, 0);
}
@@ -339,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
sequence->bake = false;
sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer));
sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE;
sequence->dma_buffer->size = RINGBUFFER_SIZE;
sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t));
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
@@ -458,42 +462,26 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) {
return ret;
}
static void digital_sequence_update_pos(DigitalSequence* sequence) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
}
static const uint32_t wait_ms = 10;
static const uint32_t wait_ticks = wait_ms * 1000 * 64;
static void digital_sequence_finish(DigitalSequence* sequence) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
do {
uint32_t last_pos = dma_buffer->read_pos;
digital_sequence_update_pos(sequence);
/* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */
if(dma_buffer->read_pos == end_pos) {
/* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */
if(TIM2->ARR == SEQ_TIMER_MAX) {
break;
}
if(last_pos != dma_buffer->read_pos) { //-V547
prev_timer = DWT->CYCCNT;
}
if(DWT->CYCCNT - prev_timer > wait_ticks) {
/*FURI_LOG_D(
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
dma_buffer->read_pos =
RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
wait_ms,
SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);*/
dma_buffer->write_pos);
break;
}
} while(1);
@@ -508,34 +496,42 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
do {
uint32_t last_pos = dma_buffer->read_pos;
digital_sequence_update_pos(sequence);
dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
if(dma_buffer->read_pos != end_pos) {
uint32_t free =
(RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE;
if(free > 2) {
break;
}
if(last_pos != dma_buffer->read_pos) { //-V547
prev_timer = DWT->CYCCNT;
}
if(DWT->CYCCNT - prev_timer > wait_ticks) {
/*FURI_LOG_D(
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)",
wait_ms,
SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);*/
dma_buffer->write_pos);
break;
}
if(TIM2->ARR == SEQ_TIMER_MAX) {
FURI_LOG_D(
TAG,
"[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)",
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
} while(1);
}
dma_buffer->buffer[dma_buffer->write_pos] = length;
dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF;
dma_buffer->write_pos++;
dma_buffer->write_pos %= RINGBUFFER_SIZE;
dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX;
}
bool digital_sequence_send(DigitalSequence* sequence) {
@@ -557,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) {
return true;
}
int32_t remainder = 0;
bool traded_first = false;
if(!sequence->sequence_used) {
return false;
}
FURI_CRITICAL_ENTER();
int32_t remainder = 0;
uint32_t trade_for_next = 0;
uint32_t seq_pos_next = 1;
dma_buffer->dma_active = false;
dma_buffer->buffer[0] = 0xFFFFFFFF;
dma_buffer->buffer[0] = SEQ_TIMER_MAX;
dma_buffer->read_pos = 0;
dma_buffer->write_pos = 0;
for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) {
uint8_t signal_index = sequence->sequence[seq_pos];
DigitalSignal* sig = sequence->signals[signal_index];
bool last_signal = ((seq_pos + 1) == sequence->sequence_used);
/* already prepare the current signal pointer */
DigitalSignal* sig = sequence->signals[sequence->sequence[0]];
DigitalSignal* sig_next = NULL;
/* re-use the GPIO buffer from the first signal */
sequence->gpio_buff = sig->internals->gpio_buff;
/* all signals are prepared and we can re-use the GPIO buffer from the fist signal */
if(seq_pos == 0) {
sequence->gpio_buff = sig->internals->gpio_buff;
FURI_CRITICAL_ENTER();
while(sig) {
bool last_signal = (seq_pos_next >= sequence->sequence_used);
if(!last_signal) {
sig_next = sequence->signals[sequence->sequence[seq_pos_next++]];
}
for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) {
if(traded_first) {
traded_first = false;
continue;
}
uint32_t pulse_length = 0;
bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries);
bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries);
uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next;
pulse_length = sig->reload_reg_buff[pulse_pos];
trade_for_next = 0;
/* when we are too late more than half a tick, make the first edge temporarily longer */
if(remainder >= T_TIM_DIV2) {
remainder -= T_TIM;
pulse_length += 1;
}
remainder += sig->internals->reload_reg_remainder;
/* last pulse in that signal and have a next signal? */
if(last_pulse) {
if((seq_pos + 1) < sequence->sequence_used) {
DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]];
/* last pulse in current signal and have a next signal? */
if(last_pulse && sig_next) {
/* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse.
beware, we do not want the level after the last edge, but the last level before that edge */
bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
/* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */
/* beware, we do not want the level after the last edge, but the last level before that edge */
bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
/* take from the next, add it to the current if they have the same level */
if(end_level == sig_next->start_level) {
pulse_length += sig_next->reload_reg_buff[0];
traded_first = true;
}
/* if they have the same level, pass the duration to the next pulse(s) */
if(end_level == sig_next->start_level) {
trade_for_next = pulse_length;
}
}
digital_sequence_queue_pulse(sequence, pulse_length);
/* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */
if(!trade_for_next) {
digital_sequence_queue_pulse(sequence, pulse_length);
/* start transmission when buffer was filled enough */
bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4);
if(!dma_buffer->dma_active) {
/* start transmission when buffer was filled enough */
bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2);
/* or it was the last pulse */
if(last_pulse && last_signal) {
start_send = true;
}
/* or it was the last pulse */
if(last_pulse && last_signal) {
start_send = true;
}
/* start transmission */
if(start_send && !dma_buffer->dma_active) {
digital_sequence_setup_dma(sequence);
digital_signal_setup_timer();
/* start transmission */
if(start_send) {
digital_sequence_setup_dma(sequence);
digital_signal_setup_timer();
/* if the send time is specified, wait till the core timer passed beyond that time */
if(sequence->send_time_active) {
sequence->send_time_active = false;
while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
/* if the send time is specified, wait till the core timer passed beyond that time */
if(sequence->send_time_active) {
sequence->send_time_active = false;
while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
}
}
digital_signal_start_timer();
dma_buffer->dma_active = true;
}
}
digital_signal_start_timer();
dma_buffer->dma_active = true;
}
}
remainder += sig->internals->reload_reg_remainder;
sig = sig_next;
sig_next = NULL;
}
/* wait until last dma transaction was finished */
digital_sequence_finish(sequence);
FURI_CRITICAL_EXIT();
digital_sequence_finish(sequence);
return true;
}
@@ -7,27 +7,22 @@
bool elf_resolve_from_hashtable(
const ElfApiInterface* interface,
const char* name,
uint32_t hash,
Elf32_Addr* address) {
bool result = false;
const HashtableApiInterface* hashtable_interface =
static_cast<const HashtableApiInterface*>(interface);
bool result = false;
uint32_t gnu_sym_hash = elf_gnu_hash(name);
sym_entry key = {
.hash = gnu_sym_hash,
.hash = hash,
.address = 0,
};
auto find_res =
std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key);
if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) {
if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) {
FURI_LOG_W(
TAG,
"Can't find symbol '%s' (hash %lx) @ %p!",
name,
gnu_sym_hash,
hashtable_interface->table_cbegin);
TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin);
result = false;
} else {
result = true;
@@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable(
return result;
}
uint32_t elf_symbolname_hash(const char* s) {
return elf_gnu_hash(s);
}
@@ -19,15 +19,17 @@ struct sym_entry {
/**
* @brief Resolver for API entries using a pre-sorted table with hashes
* @param interface pointer to HashtableApiInterface
* @param name function name
* @param hash gnu hash of function name
* @param address output for function address
* @return true if the table contains a function
*/
bool elf_resolve_from_hashtable(
const ElfApiInterface* interface,
const char* name,
uint32_t hash,
Elf32_Addr* address);
uint32_t elf_symbolname_hash(const char* s);
#ifdef __cplusplus
}
@@ -48,8 +50,10 @@ struct HashtableApiInterface : public ElfApiInterface {
.hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast<ret_type(*) args_type>(x)) \
}
#define API_VARIABLE(x, var_type) \
sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), }
#define API_VARIABLE(x, var_type) \
sym_entry { \
.hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \
}
constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {
return k1.hash < k2.hash;
@@ -11,6 +11,6 @@ typedef struct ElfApiInterface {
uint16_t api_version_minor;
bool (*resolver_callback)(
const struct ElfApiInterface* interface,
const char* name,
uint32_t hash,
Elf32_Addr* address);
} ElfApiInterface;
+119 -7
View File
@@ -2,6 +2,7 @@
#include "elf_file.h"
#include "elf_file_i.h"
#include "elf_api_interface.h"
#include "../api_hashtable/api_hashtable.h"
#define TAG "elf"
@@ -9,6 +10,7 @@
#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr))
#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m))
#define RESOLVER_THREAD_YIELD_STEP 30
#define FAST_RELOCATION_VERSION 1
// #define ELF_DEBUG_LOG 1
@@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) {
.size = 0,
.rel_count = 0,
.rel_offset = 0,
.fast_rel = NULL,
});
section_p = elf_file_get_section(elf, name);
}
@@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) {
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
if(sym->st_shndx == SHN_UNDEF) {
Elf32_Addr addr = 0;
if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) {
uint32_t hash = elf_symbolname_hash(sName);
if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {
return addr;
}
} else {
@@ -424,6 +428,7 @@ typedef enum {
SectionTypeSymTab = 1 << 3,
SectionTypeStrTab = 1 << 4,
SectionTypeDebugLink = 1 << 5,
SectionTypeFastRelData = 1 << 6,
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
} SectionType;
@@ -505,7 +510,8 @@ static SectionType elf_preload_section(
// TODO: how to do it not by name?
// .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER
// .rel.ARM: type 0x9, flags SHT_REL
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) {
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") ||
str_prefix(name, ".fast.rel.ARM.")) {
FURI_LOG_D(TAG, "Ignoring ARM section");
return SectionTypeUnused;
}
@@ -536,11 +542,31 @@ static SectionType elf_preload_section(
// Load link info section
if(section_header->sh_flags & SHF_INFO_LINK) {
name = name + strlen(".rel");
if(str_prefix(name, ".rel")) {
name = name + strlen(".rel");
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);
section_p->rel_offset = section_header->sh_offset;
return SectionTypeRelData;
} else {
FURI_LOG_E(TAG, "Unknown link info section '%s'", name);
return SectionTypeERROR;
}
}
// Load fast rel section
if(str_prefix(name, ".fast.rel")) {
name = name + strlen(".fast.rel");
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);
section_p->rel_offset = section_header->sh_offset;
return SectionTypeRelData;
section_p->fast_rel = malloc(sizeof(ELFSection));
if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) {
FURI_LOG_E(TAG, "Error loading section '%s'", name);
return SectionTypeERROR;
}
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
return SectionTypeFastRelData;
}
// Load symbol table
@@ -571,8 +597,90 @@ static SectionType elf_preload_section(
return SectionTypeUnused;
}
static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) {
Elf32_Addr addr = 0;
if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {
return addr;
}
return ELF_INVALID_ADDRESS;
}
static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) {
UNUSED(elf);
const uint8_t* start = s->fast_rel->data;
const uint8_t version = *start;
if(version != FAST_RELOCATION_VERSION) {
FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version);
return false;
}
start += 1;
const uint32_t records_count = *((uint32_t*)start);
start += 4;
FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count);
for(uint32_t i = 0; i < records_count; i++) {
bool is_section = (*start & (0x1 << 7)) ? true : false;
uint8_t type = *start & 0x7F;
start += 1;
uint32_t hash_or_section_index = *((uint32_t*)start);
start += 4;
uint32_t section_value = ELF_INVALID_ADDRESS;
if(is_section) {
section_value = *((uint32_t*)start);
start += 4;
}
const uint32_t offsets_count = *((uint32_t*)start);
start += 4;
FURI_LOG_D(
TAG,
"Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld",
i,
is_section,
type,
hash_or_section_index,
offsets_count);
Elf32_Addr address = 0;
if(is_section) {
ELFSection* symSec = elf_section_of(elf, hash_or_section_index);
if(symSec) {
address = ((Elf32_Addr)symSec->data) + section_value;
}
} else {
address = elf_address_of_by_hash(elf, hash_or_section_index);
}
if(address == ELF_INVALID_ADDRESS) {
FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index);
return false;
}
for(uint32_t j = 0; j < offsets_count; j++) {
uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF;
start += 3;
// FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset);
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset;
elf_relocate_symbol(elf, relAddr, type, address);
}
}
aligned_free(s->fast_rel->data);
free(s->fast_rel);
s->fast_rel = NULL;
return true;
}
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
if(section->rel_count) {
if(section->fast_rel) {
FURI_LOG_D(TAG, "Fast relocating section");
return elf_relocate_fast(elf, section);
} else if(section->rel_count) {
FURI_LOG_D(TAG, "Relocating section");
return elf_relocate(elf, section);
} else {
@@ -630,6 +738,10 @@ void elf_file_free(ELFFile* elf) {
if(itref->value.data) {
aligned_free(itref->value.data);
}
if(itref->value.fast_rel) {
aligned_free(itref->value.fast_rel->data);
free(itref->value.fast_rel);
}
free((void*)itref->key);
}
+7 -3
View File
@@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
*/
typedef int32_t(entry_t)(void*);
typedef struct {
typedef struct ELFSection ELFSection;
struct ELFSection {
void* data;
uint16_t sec_idx;
Elf32_Word size;
size_t rel_count;
Elf32_Off rel_offset;
} ELFSection;
ELFSection* fast_rel;
uint16_t sec_idx;
};
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
+46 -2
View File
@@ -2,6 +2,7 @@
#include "elf/elf_file.h"
#include <notification/notification_messages.h>
#include "application_assets.h"
#include <loader/firmware_api/firmware_api.h>
#include <m-list.h>
@@ -81,6 +82,12 @@ void flipper_application_free(FlipperApplication* app) {
}
elf_file_free(app->elf);
if(app->ep_thread_args) {
free(app->ep_thread_args);
app->ep_thread_args = NULL;
}
free(app);
}
@@ -224,10 +231,19 @@ static int32_t flipper_application_thread(void* context) {
return ret_code;
}
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args) {
furi_check(app->thread == NULL);
furi_check(!flipper_application_is_plugin(app));
app->ep_thread_args = args;
if(app->ep_thread_args) {
free(app->ep_thread_args);
}
if(args) {
app->ep_thread_args = strdup(args);
} else {
app->ep_thread_args = NULL;
}
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
app->thread = furi_thread_alloc_ex(
@@ -289,4 +305,32 @@ const FlipperAppPluginDescriptor*
lib_descriptor->ep_api_version);
return lib_descriptor;
}
bool flipper_application_load_name_and_icon(
FuriString* path,
Storage* storage,
uint8_t** icon_ptr,
FuriString* item_name) {
FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
FlipperApplicationPreloadStatus preload_res =
flipper_application_preload_manifest(app, furi_string_get_cstr(path));
bool load_success = false;
if(preload_res == FlipperApplicationPreloadStatusSuccess) {
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
if(manifest->has_icon) {
memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE);
}
furi_string_set(item_name, manifest->name);
load_success = true;
} else {
FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path));
load_success = false;
}
flipper_application_free(app);
return load_success;
}
+18 -3
View File
@@ -106,14 +106,14 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app);
/**
* @brief Create application thread at entry point address, using app name and
* @brief Allocate application thread at entry point address, using app name and
* stack size from metadata. Returned thread isn't started yet.
* Can be only called once for application instance.
* @param app Applicaiton pointer
* @param args Object to pass to app's entry point
* @param args Args to pass to app's entry point
* @return Created thread
*/
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args);
FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args);
/**
* @brief Check if application is a plugin (not a runnable standalone app)
@@ -149,6 +149,21 @@ typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)(
const FlipperAppPluginDescriptor*
flipper_application_plugin_get_descriptor(FlipperApplication* app);
/**
* @brief Load name and icon from FAP file.
*
* @param path Path to FAP file.
* @param storage Storage instance.
* @param icon_ptr Icon pointer.
* @param item_name Application name.
* @return true if icon and name were loaded successfully.
*/
bool flipper_application_load_name_and_icon(
FuriString* path,
Storage* storage,
uint8_t** icon_ptr,
FuriString* item_name);
#ifdef __cplusplus
}
#endif
@@ -13,12 +13,12 @@ struct CompositeApiResolver {
static bool composite_api_resolver_callback(
const ElfApiInterface* interface,
const char* name,
uint32_t hash,
Elf32_Addr* address) {
CompositeApiResolver* resolver = (CompositeApiResolver*)interface;
for
M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) {
if((*interface)->resolver_callback(*interface, name, address)) {
if((*interface)->resolver_callback(*interface, hash, address)) {
return true;
}
}
+27
View File
@@ -0,0 +1,27 @@
Import("env")
env.Append(
CPPPATH=[
"#/lib/music_worker",
],
SDK_HEADERS=[
File("music_worker.h"),
],
)
libenv = env.Clone(FW_LIB_NAME="music_worker")
libenv.ApplyLibFlags()
libenv.AppendUnique(
CCFLAGS=[
# Required for lib to be linkable with .faps
"-mword-relocations",
"-mlong-calls",
],
)
sources = libenv.GlobRecursive("*.c*")
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
Return("lib")
+507
View File
@@ -0,0 +1,507 @@
#include "music_worker.h"
#include <furi_hal.h>
#include <furi.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <math.h>
#include <m-array.h>
#define TAG "MusicWorker"
#define MUSIC_PLAYER_FILETYPE "Flipper Music Format"
#define MUSIC_PLAYER_VERSION 0
#define SEMITONE_PAUSE 0xFF
#define NOTE_C4 261.63f
#define NOTE_C4_SEMITONE (4.0f * 12.0f)
#define TWO_POW_TWELTH_ROOT 1.059463094359f
typedef struct {
uint8_t semitone;
uint8_t duration;
uint8_t dots;
} NoteBlock;
ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST);
struct MusicWorker {
FuriThread* thread;
bool should_work;
MusicWorkerCallback callback;
void* callback_context;
float volume;
uint32_t bpm;
uint32_t duration;
uint32_t octave;
NoteBlockArray_t notes;
};
static int32_t music_worker_thread_callback(void* context) {
furi_assert(context);
MusicWorker* instance = context;
NoteBlockArray_it_t it;
NoteBlockArray_it(it, instance->notes);
if(furi_hal_speaker_acquire(1000)) {
while(instance->should_work) {
if(NoteBlockArray_end_p(it)) {
NoteBlockArray_it(it, instance->notes);
furi_delay_ms(10);
} else {
NoteBlock* note_block = NoteBlockArray_ref(it);
float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE;
float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4);
float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm /
note_block->duration;
uint32_t dots = note_block->dots;
while(dots > 0) {
duration += duration / 2;
dots--;
}
uint32_t next_tick = furi_get_tick() + duration;
float volume = instance->volume;
if(instance->callback) {
instance->callback(
note_block->semitone,
note_block->dots,
note_block->duration,
0.0,
instance->callback_context);
}
furi_hal_speaker_stop();
furi_hal_speaker_start(frequency, volume);
while(instance->should_work && furi_get_tick() < next_tick) {
volume *= 0.9945679;
furi_hal_speaker_set_volume(volume);
furi_delay_ms(2);
}
NoteBlockArray_next(it);
}
}
furi_hal_speaker_stop();
furi_hal_speaker_release();
} else {
FURI_LOG_E(TAG, "Speaker system is busy with another process.");
}
return 0;
}
MusicWorker* music_worker_alloc() {
MusicWorker* instance = malloc(sizeof(MusicWorker));
NoteBlockArray_init(instance->notes);
instance->thread =
furi_thread_alloc_ex("MusicWorker", 1024, music_worker_thread_callback, instance);
instance->volume = 1.0f;
return instance;
}
void music_worker_clear(MusicWorker* instance) {
NoteBlockArray_reset(instance->notes);
}
void music_worker_free(MusicWorker* instance) {
furi_assert(instance);
furi_thread_free(instance->thread);
NoteBlockArray_clear(instance->notes);
free(instance);
}
static bool is_digit(const char c) {
return isdigit(c) != 0;
}
static bool is_letter(const char c) {
return islower(c) != 0 || isupper(c) != 0;
}
static bool is_space(const char c) {
return c == ' ' || c == '\t';
}
static size_t extract_number(const char* string, uint32_t* number) {
size_t ret = 0;
*number = 0;
while(is_digit(*string)) {
*number *= 10;
*number += (*string - '0');
string++;
ret++;
}
return ret;
}
static size_t extract_dots(const char* string, uint32_t* number) {
size_t ret = 0;
*number = 0;
while(*string == '.') {
*number += 1;
string++;
ret++;
}
return ret;
}
static size_t extract_char(const char* string, char* symbol) {
if(is_letter(*string)) {
*symbol = *string;
return 1;
} else {
return 0;
}
}
static size_t extract_sharp(const char* string, char* symbol) {
if(*string == '#' || *string == '_') {
*symbol = '#';
return 1;
} else {
return 0;
}
}
static size_t skip_till(const char* string, const char symbol) {
size_t ret = 0;
while(*string != '\0' && *string != symbol) {
string++;
ret++;
}
if(*string != symbol) {
ret = 0;
}
return ret;
}
static bool
music_worker_add_note(MusicWorker* instance, uint8_t semitone, uint8_t duration, uint8_t dots) {
NoteBlock note_block;
note_block.semitone = semitone;
note_block.duration = duration;
note_block.dots = dots;
NoteBlockArray_push_back(instance->notes, note_block);
return true;
}
static int8_t note_to_semitone(const char note) {
switch(note) {
case 'C':
return 0;
// C#
case 'D':
return 2;
// D#
case 'E':
return 4;
case 'F':
return 5;
// F#
case 'G':
return 7;
// G#
case 'A':
return 9;
// A#
case 'B':
return 11;
default:
return 0;
}
}
static bool music_worker_parse_notes(MusicWorker* instance, const char* string) {
const char* cursor = string;
bool result = true;
while(*cursor != '\0') {
if(!is_space(*cursor)) {
uint32_t duration = 0;
char note_char = '\0';
char sharp_char = '\0';
uint32_t octave = 0;
uint32_t dots = 0;
// Parsing
cursor += extract_number(cursor, &duration);
cursor += extract_char(cursor, &note_char);
cursor += extract_sharp(cursor, &sharp_char);
cursor += extract_number(cursor, &octave);
cursor += extract_dots(cursor, &dots);
// Post processing
note_char = toupper(note_char);
if(!duration) {
duration = instance->duration;
}
if(!octave) {
octave = instance->octave;
}
// Validation
bool is_valid = true;
is_valid &= (duration >= 1 && duration <= 128);
is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P');
is_valid &= (sharp_char == '#' || sharp_char == '\0');
is_valid &= (octave <= 16);
is_valid &= (dots <= 16);
if(!is_valid) {
FURI_LOG_E(
TAG,
"Invalid note: %lu%c%c%lu.%lu",
duration,
note_char == '\0' ? '_' : note_char,
sharp_char == '\0' ? '_' : sharp_char,
octave,
dots);
result = false;
break;
}
// Note to semitones
uint8_t semitone = 0;
if(note_char == 'P') {
semitone = SEMITONE_PAUSE;
} else {
semitone += octave * 12;
semitone += note_to_semitone(note_char);
semitone += sharp_char == '#' ? 1 : 0;
}
if(music_worker_add_note(instance, semitone, duration, dots)) {
FURI_LOG_D(
TAG,
"Added note: %c%c%lu.%lu = %u %lu",
note_char == '\0' ? '_' : note_char,
sharp_char == '\0' ? '_' : sharp_char,
octave,
dots,
semitone,
duration);
} else {
FURI_LOG_E(
TAG,
"Invalid note: %c%c%lu.%lu = %u %lu",
note_char == '\0' ? '_' : note_char,
sharp_char == '\0' ? '_' : sharp_char,
octave,
dots,
semitone,
duration);
}
cursor += skip_till(cursor, ',');
}
if(*cursor != '\0') cursor++;
}
return result;
}
bool music_worker_load(MusicWorker* instance, const char* file_path) {
furi_assert(instance);
furi_assert(file_path);
bool ret = false;
if(strcasestr(file_path, ".fmf")) {
ret = music_worker_load_fmf_from_file(instance, file_path);
} else {
ret = music_worker_load_rtttl_from_file(instance, file_path);
}
return ret;
}
bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path) {
furi_assert(instance);
furi_assert(file_path);
bool result = false;
FuriString* temp_str;
temp_str = furi_string_alloc();
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(file, file_path)) break;
uint32_t version = 0;
if(!flipper_format_read_header(file, temp_str, &version)) break;
if(furi_string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) ||
(version != MUSIC_PLAYER_VERSION)) {
FURI_LOG_E(TAG, "Incorrect file format or version");
break;
}
if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) {
FURI_LOG_E(TAG, "BPM is missing");
break;
}
if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) {
FURI_LOG_E(TAG, "Duration is missing");
break;
}
if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) {
FURI_LOG_E(TAG, "Octave is missing");
break;
}
if(!flipper_format_read_string(file, "Notes", temp_str)) {
FURI_LOG_E(TAG, "Notes is missing");
break;
}
if(!music_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) {
break;
}
result = true;
} while(false);
furi_record_close(RECORD_STORAGE);
flipper_format_free(file);
furi_string_free(temp_str);
return result;
}
bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path) {
furi_assert(instance);
furi_assert(file_path);
bool result = false;
FuriString* content;
content = furi_string_alloc();
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
do {
if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
FURI_LOG_E(TAG, "Unable to open file");
break;
};
uint16_t ret = 0;
do {
uint8_t buffer[65] = {0};
ret = storage_file_read(file, buffer, sizeof(buffer) - 1);
for(size_t i = 0; i < ret; i++) {
furi_string_push_back(content, buffer[i]);
}
} while(ret > 0);
furi_string_trim(content);
if(!furi_string_size(content)) {
FURI_LOG_E(TAG, "Empty file");
break;
}
if(!music_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) {
FURI_LOG_E(TAG, "Invalid file content");
break;
}
result = true;
} while(0);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
furi_string_free(content);
return result;
}
bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string) {
furi_assert(instance);
const char* cursor = string;
// Skip name
cursor += skip_till(cursor, ':');
if(*cursor != ':') {
return false;
}
// Duration
cursor += skip_till(cursor, '=');
if(*cursor != '=') {
return false;
}
cursor++;
cursor += extract_number(cursor, &instance->duration);
// Octave
cursor += skip_till(cursor, '=');
if(*cursor != '=') {
return false;
}
cursor++;
cursor += extract_number(cursor, &instance->octave);
// BPM
cursor += skip_till(cursor, '=');
if(*cursor != '=') {
return false;
}
cursor++;
cursor += extract_number(cursor, &instance->bpm);
// Notes
cursor += skip_till(cursor, ':');
if(*cursor != ':') {
return false;
}
cursor++;
if(!music_worker_parse_notes(instance, cursor)) {
return false;
}
return true;
}
void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context) {
furi_assert(instance);
instance->callback = callback;
instance->callback_context = context;
}
void music_worker_set_volume(MusicWorker* instance, float volume) {
furi_assert(instance);
instance->volume = volume;
}
void music_worker_start(MusicWorker* instance) {
furi_assert(instance);
furi_assert(instance->should_work == false);
instance->should_work = true;
furi_thread_start(instance->thread);
}
void music_worker_stop(MusicWorker* instance) {
furi_assert(instance);
furi_assert(instance->should_work == true);
instance->should_work = false;
furi_thread_join(instance->thread);
}
bool music_worker_is_playing(MusicWorker* instance) {
furi_assert(instance);
return instance->should_work;
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef void (*MusicWorkerCallback)(
uint8_t semitone,
uint8_t dots,
uint8_t duration,
float position,
void* context);
typedef struct MusicWorker MusicWorker;
MusicWorker* music_worker_alloc();
void music_worker_clear(MusicWorker* instance);
void music_worker_free(MusicWorker* instance);
bool music_worker_load(MusicWorker* instance, const char* file_path);
bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path);
bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path);
bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string);
void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context);
void music_worker_set_volume(MusicWorker* instance, float volume);
void music_worker_start(MusicWorker* instance);
void music_worker_stop(MusicWorker* instance);
bool music_worker_is_playing(MusicWorker* instance);
+186 -174
View File
@@ -657,178 +657,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
return parsed;
}
static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) {
static bool nfc_device_save_slix_data(
FlipperFormat* file,
NfcDevice* dev,
SlixTypeFeatures features,
const char* type) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
char msg[64];
snprintf(msg, sizeof(msg), "%s specific data", type);
if(!flipper_format_write_comment_cstr(file, msg)) break;
if(!flipper_format_write_comment_cstr(
file, "Passwords are optional. If password is omitted, any password is accepted"))
break;
if(features & SlixFeatureRead) {
if(data->flags & NfcVSlixDataFlagsHasKeyRead) {
if(!flipper_format_write_hex(
file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
}
}
if(features & SlixFeatureWrite) {
if(data->flags & NfcVSlixDataFlagsHasKeyWrite) {
if(!flipper_format_write_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
}
}
if(features & SlixFeaturePrivacy) {
if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) {
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
}
}
if(features & SlixFeatureDestroy) {
if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) {
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
}
}
if(features & SlixFeatureEas) {
if(data->flags & NfcVSlixDataFlagsHasKeyEas) {
if(!flipper_format_write_hex(
file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
}
}
if(features & SlixFeatureSignature) {
if(!flipper_format_write_comment_cstr(
file,
"This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key."))
break;
if(!flipper_format_write_hex(
file, "Signature", data->signature, sizeof(data->signature)))
break;
}
if(features & SlixFeaturePrivacy) {
bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false;
if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break;
}
if(features & SlixFeatureProtection) {
if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break;
if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break;
if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1))
break;
}
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) {
bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
parsed = true;
} while(false);
return parsed;
}
static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break;
if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(!flipper_format_write_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(!flipper_format_read_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
parsed = true;
} while(false);
return parsed;
}
static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break;
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
parsed = true;
} while(false);
return parsed;
}
static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break;
if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(!flipper_format_write_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(!flipper_format_read_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
data->flags = 0;
if(features & SlixFeatureRead) {
if(flipper_format_key_exist(file, "Password Read")) {
if(!flipper_format_read_hex(
file, "Password Read", data->key_read, sizeof(data->key_read))) {
FURI_LOG_D(TAG, "Failed reading Password Read");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyRead;
}
}
if(features & SlixFeatureWrite) {
if(flipper_format_key_exist(file, "Password Write")) {
if(!flipper_format_read_hex(
file, "Password Write", data->key_write, sizeof(data->key_write))) {
FURI_LOG_D(TAG, "Failed reading Password Write");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyWrite;
}
}
if(features & SlixFeaturePrivacy) {
if(flipper_format_key_exist(file, "Password Privacy")) {
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) {
FURI_LOG_D(TAG, "Failed reading Password Privacy");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyPrivacy;
}
}
if(features & SlixFeatureDestroy) {
if(flipper_format_key_exist(file, "Password Destroy")) {
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) {
FURI_LOG_D(TAG, "Failed reading Password Destroy");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyDestroy;
}
}
if(features & SlixFeatureEas) {
if(flipper_format_key_exist(file, "Password EAS")) {
if(!flipper_format_read_hex(
file, "Password EAS", data->key_eas, sizeof(data->key_eas))) {
FURI_LOG_D(TAG, "Failed reading Password EAS");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyEas;
}
}
if(features & SlixFeatureSignature) {
if(!flipper_format_read_hex(
file, "Signature", data->signature, sizeof(data->signature))) {
FURI_LOG_D(TAG, "Failed reading Signature");
break;
}
}
if(features & SlixFeaturePrivacy) {
bool privacy;
if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) {
FURI_LOG_D(TAG, "Failed reading Privacy Mode");
break;
}
if(privacy) {
data->flags |= NfcVSlixDataFlagsPrivacy;
}
}
if(features & SlixFeatureProtection) {
if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) {
FURI_LOG_D(TAG, "Failed reading Protection pointer");
break;
}
if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) {
FURI_LOG_D(TAG, "Failed reading Protection condition");
break;
}
}
parsed = true;
} while(false);
@@ -859,7 +848,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
file, "Data Content", data->data, data->block_num * data->block_size))
break;
if(!flipper_format_write_comment_cstr(
file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info"))
file,
"First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info"))
break;
if(!flipper_format_write_hex(
file, "Security Status", data->security_status, 1 + data->block_num))
@@ -877,16 +867,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
saved = true;
break;
case NfcVTypeSlix:
saved = nfc_device_save_slix_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX");
break;
case NfcVTypeSlixS:
saved = nfc_device_save_slix_s_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S");
break;
case NfcVTypeSlixL:
saved = nfc_device_save_slix_l_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L");
break;
case NfcVTypeSlix2:
saved = nfc_device_save_slix2_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2");
break;
default:
break;
@@ -906,23 +896,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
uint32_t temp_uint32 = 0;
uint8_t temp_value = 0;
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break;
if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break;
if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break;
if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break;
data->block_num = temp_uint32;
if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break;
if(!flipper_format_read_hex(
file, "Data Content", data->data, data->block_num * data->block_size))
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) {
FURI_LOG_D(TAG, "Failed reading DSFID");
break;
}
if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) {
FURI_LOG_D(TAG, "Failed reading AFI");
break;
}
if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) {
FURI_LOG_D(TAG, "Failed reading IC Reference");
break;
}
if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) {
FURI_LOG_D(TAG, "Failed reading Block Count");
break;
}
data->block_num = temp_uint32;
if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) {
FURI_LOG_D(TAG, "Failed reading Block Size");
break;
}
if(!flipper_format_read_hex(
file, "Data Content", data->data, data->block_num * data->block_size)) {
FURI_LOG_D(TAG, "Failed reading Data Content");
break;
}
/* optional, as added later */
if(flipper_format_key_exist(file, "Security Status")) {
if(!flipper_format_read_hex(
file, "Security Status", data->security_status, 1 + data->block_num))
file, "Security Status", data->security_status, 1 + data->block_num)) {
FURI_LOG_D(TAG, "Failed reading Security Status");
break;
}
}
if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) {
FURI_LOG_D(TAG, "Failed reading Subtype");
break;
}
if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break;
data->sub_type = temp_value;
switch(data->sub_type) {
@@ -930,16 +942,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
parsed = true;
break;
case NfcVTypeSlix:
parsed = nfc_device_load_slix_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix);
break;
case NfcVTypeSlixS:
parsed = nfc_device_load_slix_s_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS);
break;
case NfcVTypeSlixL:
parsed = nfc_device_load_slix_l_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL);
break;
case NfcVTypeSlix2:
parsed = nfc_device_load_slix2_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2);
break;
default:
break;
+9 -9
View File
@@ -1025,14 +1025,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
deactivated = true;
} else {
// If the key A is marked as found and matches the searching key, invalidate it
uint8_t found_key[6];
memcpy(found_key, data->block[i].value, 6);
MfClassicSectorTrailer* sec_trailer =
mf_classic_get_sector_trailer_by_sector(data, i);
uint8_t current_key[6];
memcpy(current_key, &key, 6);
nfc_util_num2bytes(key, 6, current_key);
if(mf_classic_is_key_found(data, i, MfClassicKeyA) &&
memcmp(found_key, current_key, 6) == 0) {
memcmp(sec_trailer->key_a, current_key, 6) == 0) {
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
is_key_a_found = false;
FURI_LOG_D(TAG, "Key %dA not found in attack", i);
@@ -1051,14 +1051,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
deactivated = true;
} else {
// If the key B is marked as found and matches the searching key, invalidate it
uint8_t found_key[6];
memcpy(found_key, data->block[i].value + 10, 6);
MfClassicSectorTrailer* sec_trailer =
mf_classic_get_sector_trailer_by_sector(data, i);
uint8_t current_key[6];
memcpy(current_key, &key, 6);
nfc_util_num2bytes(key, 6, current_key);
if(mf_classic_is_key_found(data, i, MfClassicKeyB) &&
memcmp(found_key, current_key, 6) == 0) {
memcmp(sec_trailer->key_b, current_key, 6) == 0) {
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
is_key_b_found = false;
FURI_LOG_D(TAG, "Key %dB not found in attack", i);
@@ -1074,7 +1074,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
}
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
}
memcpy(&prev_key, &key, sizeof(key));
prev_key = key;
}
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
mf_classic_read_sector(&tx_rx, data, i);
+48 -8
View File
@@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n
return false;
}
/* clear all know sub type data before reading them */
memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data));
if(slix_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX detected");
nfcv_data->sub_type = NfcVTypeSlix;
} else if(slix2_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX2 detected");
nfcv_data->sub_type = NfcVTypeSlix2;
if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) {
return false;
}
} else if(slix_s_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX-S detected");
nfcv_data->sub_type = NfcVTypeSlixS;
@@ -612,9 +618,34 @@ void nfcv_emu_handle_packet(
if(ctx->flags & NFCV_REQ_FLAG_AFI) {
uint8_t afi = nfcv_data->frame[ctx->payload_offset];
if(afi == nfcv_data->afi) {
respond = true;
uint8_t family = (afi & 0xF0);
uint8_t subfamily = (afi & 0x0F);
if(family) {
if(subfamily) {
/* selected family and subfamily only */
if(afi == nfcv_data->afi) {
respond = true;
}
} else {
/* selected family, any subfamily */
if(family == (nfcv_data->afi & 0xf0)) {
respond = true;
}
}
} else {
if(subfamily) {
/* proprietary subfamily only */
if(afi == nfcv_data->afi) {
respond = true;
}
} else {
/* all families and subfamilies */
respond = true;
}
}
} else {
respond = true;
}
@@ -740,13 +771,19 @@ void nfcv_emu_handle_packet(
case NFCV_CMD_READ_MULTI_BLOCK:
case NFCV_CMD_READ_BLOCK: {
uint8_t block = nfcv_data->frame[ctx->payload_offset];
uint8_t blocks = 1;
int blocks = 1;
if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) {
blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1;
}
if(block + blocks <= nfcv_data->block_num) {
/* limit the maximum block count, underflow accepted */
if(block + blocks > nfcv_data->block_num) {
blocks = nfcv_data->block_num - block;
}
/* only respond with the valid blocks, if there are any */
if(blocks > 0) {
uint8_t buffer_pos = 0;
ctx->response_buffer[buffer_pos++] = NFCV_NOERROR;
@@ -773,10 +810,13 @@ void nfcv_emu_handle_packet(
ctx->response_flags,
ctx->send_time);
} else {
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time);
/* reply with an error only in addressed or selected mode */
if(ctx->addressed || ctx->selected) {
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time);
}
}
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block);
+46 -3
View File
@@ -139,8 +139,10 @@ typedef enum {
} NfcVErrorcodes;
typedef enum {
NfcVLockBitDsfid = 1,
NfcVLockBitAfi = 2,
NfcVLockBitDsfid = 1 << 0,
NfcVLockBitAfi = 1 << 1,
NfcVLockBitEas = 1 << 2,
NfcVLockBitPpl = 1 << 3,
} NfcVLockBits;
typedef enum {
@@ -168,14 +170,55 @@ typedef enum {
NfcVSendFlagsHighRate = 1 << 4
} NfcVSendFlags;
/* SLIX specific config flags */
typedef enum {
NfcVSlixDataFlagsNone = 0,
NfcVSlixDataFlagsHasKeyRead = 1 << 0,
NfcVSlixDataFlagsHasKeyWrite = 1 << 1,
NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2,
NfcVSlixDataFlagsHasKeyDestroy = 1 << 3,
NfcVSlixDataFlagsHasKeyEas = 1 << 4,
NfcVSlixDataFlagsValidKeyRead = 1 << 8,
NfcVSlixDataFlagsValidKeyWrite = 1 << 9,
NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10,
NfcVSlixDataFlagsValidKeyDestroy = 1 << 11,
NfcVSlixDataFlagsValidKeyEas = 1 << 12,
NfcVSlixDataFlagsPrivacy = 1 << 16,
NfcVSlixDataFlagsDestroyed = 1 << 17
} NfcVSlixDataFlags;
/* abstract the file read/write operations for all SLIX types to reduce duplicated code */
typedef enum {
SlixFeatureRead = 1 << 0,
SlixFeatureWrite = 1 << 1,
SlixFeaturePrivacy = 1 << 2,
SlixFeatureDestroy = 1 << 3,
SlixFeatureEas = 1 << 4,
SlixFeatureSignature = 1 << 5,
SlixFeatureProtection = 1 << 6,
SlixFeatureSlix = SlixFeatureEas,
SlixFeatureSlixS =
(SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy |
SlixFeatureEas),
SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas),
SlixFeatureSlix2 =
(SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy |
SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection),
} SlixTypeFeatures;
typedef struct {
uint32_t flags;
uint8_t key_read[4];
uint8_t key_write[4];
uint8_t key_privacy[4];
uint8_t key_destroy[4];
uint8_t key_eas[4];
uint8_t rand[2];
bool privacy;
uint8_t signature[32];
/* SLIX2 options */
uint8_t pp_pointer;
uint8_t pp_condition;
} NfcVSlixData;
typedef union {
+413 -41
View File
@@ -9,6 +9,120 @@
#define TAG "SLIX"
ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
furi_assert(nfc_data);
furi_assert(nfcv_data);
uint8_t rxBuf[32];
uint16_t received = 0;
ReturnCode ret = ERR_NONE;
FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION...");
for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
uint8_t cmd[] = {};
uint8_t uid[NFCV_UID_LENGTH];
/* UID is stored reversed in requests */
for(int pos = 0; pos < nfc_data->uid_len; pos++) {
uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
}
ReturnCode ret = rfalNfcvPollerTransceiveReq(
NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION,
RFAL_NFCV_REQ_FLAG_DEFAULT,
NFCV_MANUFACTURER_NXP,
uid,
cmd,
sizeof(cmd),
rxBuf,
sizeof(rxBuf),
&received);
if(ret == ERR_NONE) {
break;
}
}
if(ret != ERR_NONE || received != 8) { //-V560
FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
return ret;
}
FURI_LOG_D(TAG, "Success...");
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
slix->pp_pointer = rxBuf[1];
slix->pp_condition = rxBuf[2];
/* convert NXP's to our internal lock bits format */
nfcv_data->security_status[0] = 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0;
return ERR_NONE;
}
ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
furi_assert(nfc_data);
furi_assert(nfcv_data);
uint8_t rxBuf[64];
uint16_t received = 0;
ReturnCode ret = ERR_NONE;
FURI_LOG_D(TAG, "Read SIGNATURE...");
for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
uint8_t cmd[] = {};
uint8_t uid[NFCV_UID_LENGTH];
/* UID is stored reversed in requests */
for(int pos = 0; pos < nfc_data->uid_len; pos++) {
uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
}
ReturnCode ret = rfalNfcvPollerTransceiveReq(
NFCV_CMD_NXP_READ_SIGNATURE,
RFAL_NFCV_REQ_FLAG_DEFAULT,
NFCV_MANUFACTURER_NXP,
uid,
cmd,
sizeof(cmd),
rxBuf,
sizeof(rxBuf),
&received);
if(ret == ERR_NONE) {
break;
}
}
if(ret != ERR_NONE || received != 33) { //-V560
FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
return ret;
}
FURI_LOG_D(TAG, "Success...");
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
memcpy(slix->signature, &rxBuf[1], 32);
return ERR_NONE;
}
ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
ReturnCode ret = ERR_NONE;
ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data);
if(ret != ERR_NONE) {
return ret;
}
ret = slix2_read_signature(nfc_data, nfcv_data);
return ret;
}
static uint32_t slix_read_be(uint8_t* data, uint32_t length) {
uint32_t value = 0;
@@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) {
return ret;
}
static void slix_generic_pass_infos(
uint8_t password_id,
NfcVSlixData* slix,
uint8_t** password,
uint32_t* flag_valid,
uint32_t* flag_set) {
switch(password_id) {
case SLIX_PASS_READ:
*password = slix->key_read;
*flag_valid = NfcVSlixDataFlagsValidKeyRead;
*flag_set = NfcVSlixDataFlagsHasKeyRead;
break;
case SLIX_PASS_WRITE:
*password = slix->key_write;
*flag_valid = NfcVSlixDataFlagsValidKeyWrite;
*flag_set = NfcVSlixDataFlagsHasKeyWrite;
break;
case SLIX_PASS_PRIVACY:
*password = slix->key_privacy;
*flag_valid = NfcVSlixDataFlagsValidKeyPrivacy;
*flag_set = NfcVSlixDataFlagsHasKeyPrivacy;
break;
case SLIX_PASS_DESTROY:
*password = slix->key_destroy;
*flag_valid = NfcVSlixDataFlagsValidKeyDestroy;
*flag_set = NfcVSlixDataFlagsHasKeyDestroy;
break;
case SLIX_PASS_EASAFI:
*password = slix->key_eas;
*flag_valid = NfcVSlixDataFlagsValidKeyEas;
*flag_set = NfcVSlixDataFlagsHasKeyEas;
break;
default:
break;
}
}
bool slix_generic_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
@@ -150,7 +301,8 @@ bool slix_generic_protocol_filter(
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
if((slix->flags & NfcVSlixDataFlagsPrivacy) &&
ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
ctx->command != NFCV_CMD_NXP_SET_PASSWORD) {
snprintf(
nfcv_data->last_command,
@@ -186,66 +338,73 @@ bool slix_generic_protocol_filter(
}
case NFCV_CMD_NXP_SET_PASSWORD: {
/* the password to be set is the first parameter */
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
/* right after that is the XORed password */
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
/* only handle if the password type is supported */
if(!(password_id & password_supported)) {
break;
}
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
/* fetch the last RAND value */
uint8_t* rand = slix->rand;
uint8_t* password = NULL;
/* first calc the password that has been sent */
uint8_t password_rcv[4];
switch(password_id) {
case SLIX_PASS_READ:
password = slix->key_read;
break;
case SLIX_PASS_WRITE:
password = slix->key_write;
break;
case SLIX_PASS_PRIVACY:
password = slix->key_privacy;
break;
case SLIX_PASS_DESTROY:
password = slix->key_destroy;
break;
case SLIX_PASS_EASAFI:
password = slix->key_eas;
break;
default:
break;
for(int pos = 0; pos < 4; pos++) {
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
}
uint32_t pass_received = slix_read_be(password_rcv, 4);
/* then determine the password type (or even update if not set yet) */
uint8_t* password = NULL;
uint32_t flag_valid = 0;
uint32_t flag_set = 0;
slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
/* when the password is not supported, return silently */
if(!password) {
break;
}
for(int pos = 0; pos < 4; pos++) {
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
}
uint32_t pass_expect = slix_read_be(password, 4);
uint32_t pass_received = slix_read_be(password_rcv, 4);
/* check if the password is known */
bool pass_valid = false;
uint32_t pass_expect = 0;
/* if the password is all-zeroes, just accept any password*/
if(!pass_expect || pass_expect == pass_received) {
if(slix->flags & flag_set) {
/* if so, fetch the stored password and compare */
pass_expect = slix_read_be(password, 4);
pass_valid = (pass_expect == pass_received);
} else {
/* if not known, just accept it and store that password */
memcpy(password, password_rcv, 4);
nfcv_data->modified = true;
slix->flags |= flag_set;
pass_valid = true;
}
/* if the pass was valid or accepted for other reasons, continue */
if(pass_valid) {
slix->flags |= flag_valid;
/* handle actions when a correct password was given, aside of setting the flag */
switch(password_id) {
case SLIX_PASS_READ:
break;
case SLIX_PASS_WRITE:
break;
case SLIX_PASS_PRIVACY:
slix->privacy = false;
slix->flags &= ~NfcVSlixDataFlagsPrivacy;
nfcv_data->modified = true;
break;
case SLIX_PASS_DESTROY:
slix->flags |= NfcVSlixDataFlagsDestroyed;
FURI_LOG_D(TAG, "Pooof! Got destroyed");
break;
case SLIX_PASS_EASAFI:
break;
default:
break;
}
ctx->response_buffer[0] = NFCV_NOERROR;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
@@ -268,6 +427,49 @@ bool slix_generic_protocol_filter(
break;
}
case NFCV_CMD_NXP_WRITE_PASSWORD: {
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
if(!(password_id & password_supported)) {
break;
}
uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1];
uint8_t* password = NULL;
uint32_t flag_valid = 0;
uint32_t flag_set = 0;
slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
/* when the password is not supported, return silently */
if(!password) {
break;
}
bool pass_valid = (slix->flags & flag_valid);
if(!(slix->flags & flag_set)) {
pass_valid = true;
}
if(pass_valid) {
slix->flags |= flag_valid;
slix->flags |= flag_set;
memcpy(password, new_password, 4);
ctx->response_buffer[0] = NFCV_NOERROR;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
snprintf(
nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK");
} else {
snprintf(
nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL");
}
handled = true;
break;
}
case NFCV_CMD_NXP_ENABLE_PRIVACY: {
ctx->response_buffer[0] = NFCV_NOERROR;
@@ -278,7 +480,7 @@ bool slix_generic_protocol_filter(
sizeof(nfcv_data->last_command),
"NFCV_CMD_NXP_ENABLE_PRIVACY");
slix->privacy = true;
slix->flags |= NfcVSlixDataFlagsPrivacy;
handled = true;
break;
}
@@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_l_protocol_filter;
@@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_s_protocol_filter;
@@ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_protocol_filter;
@@ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
@@ -396,6 +611,160 @@ bool slix2_protocol_filter( // -V524
return true;
}
switch(ctx->command) {
/* override WRITE BLOCK for block 79 (16 bit counter) */
case NFCV_CMD_WRITE_BLOCK:
case NFCV_CMD_WRITE_MULTI_BLOCK: {
uint8_t resp_len = 1;
uint8_t blocks = 1;
uint8_t block = nfcv_data->frame[ctx->payload_offset];
uint8_t data_pos = ctx->payload_offset + 1;
if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
blocks = nfcv_data->frame[data_pos] + 1;
data_pos++;
}
uint8_t* data = &nfcv_data->frame[data_pos];
uint32_t data_len = nfcv_data->block_size * blocks;
if((block + blocks) <= nfcv_data->block_num &&
(data_pos + data_len + 2) == nfcv_data->frame_length) {
ctx->response_buffer[0] = NFCV_NOERROR;
for(int block_num = block; block_num < block + blocks; block_num++) {
/* special case, 16-bit counter */
if(block_num == 79) {
uint32_t dest;
uint32_t ctr_old;
memcpy(&dest, &nfcv_data->frame[data_pos], 4);
memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4);
uint32_t ctr_new = ctr_old;
bool allowed = true;
/* increment counter */
if(dest == 1) {
ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF);
/* protection flag set? */
if(ctr_old & 0x01000000) { //-V1051
allowed = nfcv_data->sub_data.slix.flags &
NfcVSlixDataFlagsValidKeyRead;
}
} else {
ctr_new = dest;
allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite;
}
if(allowed) {
memcpy( //-V1086
&nfcv_data->data[nfcv_data->block_size * block_num],
&ctr_new,
4);
} else {
/* incorrect read or write password */
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
resp_len = 2;
}
} else {
memcpy(
&nfcv_data->data[nfcv_data->block_size * block_num],
&nfcv_data->frame[data_pos],
nfcv_data->block_size);
}
data_pos += nfcv_data->block_size;
}
nfcv_data->modified = true;
} else {
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
resp_len = 2;
}
bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) ||
(ctx->addressed || ctx->selected);
if(respond) {
nfcv_emu_send(
tx_rx,
nfcv_data,
ctx->response_buffer,
resp_len,
ctx->response_flags,
ctx->send_time);
}
if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"WRITE MULTI BLOCK %d, %d blocks",
block,
blocks);
} else {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"WRITE BLOCK %d <- %02X %02X %02X %02X",
block,
data[0],
data[1],
data[2],
data[3]);
}
handled = true;
break;
}
case NFCV_CMD_NXP_READ_SIGNATURE: {
uint32_t len = 0;
ctx->response_buffer[len++] = NFCV_NOERROR;
memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature));
len += sizeof(slix->signature);
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE");
handled = true;
break;
}
case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: {
uint32_t len = 0;
uint8_t lock_bits = 0;
/* convert our internal lock bits format into NXP's */
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0;
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0;
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0;
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0;
ctx->response_buffer[len++] = NFCV_NOERROR;
ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer;
ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition;
ctx->response_buffer[len++] = lock_bits;
ctx->response_buffer[len++] = 0x7F; /* features LSB */
ctx->response_buffer[len++] = 0x35; /* features */
ctx->response_buffer[len++] = 0; /* features */
ctx->response_buffer[len++] = 0; /* features MSB */
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"GET_NXP_SYSTEM_INFORMATION");
handled = true;
break;
}
}
return handled;
}
@@ -405,7 +774,10 @@ void slix2_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix2_protocol_filter;
+33 -13
View File
@@ -8,19 +8,35 @@
#define NFCV_MANUFACTURER_NXP 0x04
/* ISO15693-3 CUSTOM NXP COMMANDS */
#define NFCV_CMD_NXP_SET_EAS 0xA2
#define NFCV_CMD_NXP_RESET_EAS 0xA3
#define NFCV_CMD_NXP_LOCK_EAS 0xA4
#define NFCV_CMD_NXP_EAS_ALARM 0xA5
#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2
#define NFCV_CMD_NXP_SET_PASSWORD 0xB3
#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4
#define NFCV_CMD_NXP_DESTROY 0xB9
#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA
typedef enum {
NFCV_CMD_NXP_SET_EAS = 0xA2,
NFCV_CMD_NXP_RESET_EAS = 0xA3,
NFCV_CMD_NXP_LOCK_EAS = 0xA4,
NFCV_CMD_NXP_EAS_ALARM = 0xA5,
NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6,
NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7,
NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB,
NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0,
NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1,
NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2,
NFCV_CMD_NXP_SET_PASSWORD = 0xB3,
NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4,
NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5,
NFCV_CMD_NXP_PROTECT_PAGE = 0xB6,
NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7,
NFCV_CMD_NXP_DESTROY = 0xB9,
NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA,
NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC,
NFCV_CMD_NXP_READ_SIGNATURE = 0xBD
} SlixCommands;
/* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */
typedef enum {
SlixLockBitAfi = 1 << 0,
SlixLockBitEas = 1 << 1,
SlixLockBitDsfid = 1 << 2,
SlixLockBitPpl = 1 << 3,
} SlixLockBits;
/* available passwords */
#define SLIX_PASS_READ 0x01
@@ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data);
bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data);
bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data);
ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
ReturnCode slix_get_random(NfcVData* data);
ReturnCode slix_unlock(NfcVData* data, uint32_t password_id);
+2 -1
View File
@@ -63,7 +63,8 @@ const SubGhzProtocolEncoder subghz_protocol_kia_encoder = {
const SubGhzProtocol subghz_protocol_kia = {
.name = SUBGHZ_PROTOCOL_KIA_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_AutoAlarms,
.decoder = &subghz_protocol_kia_decoder,
.encoder = &subghz_protocol_kia_encoder,
+2 -1
View File
@@ -64,7 +64,8 @@ const SubGhzProtocol subghz_protocol_magellan = {
.name = SUBGHZ_PROTOCOL_MAGELLAN_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send |
SubGhzProtocolFlag_Magelan,
.decoder = &subghz_protocol_magellan_decoder,
.encoder = &subghz_protocol_magellan_encoder,
+1 -1
View File
@@ -70,7 +70,7 @@ const SubGhzProtocol subghz_protocol_scher_khan = {
.name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Save,
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_AutoAlarms,
.decoder = &subghz_protocol_scher_khan_decoder,
.encoder = &subghz_protocol_scher_khan_encoder,
+2 -1
View File
@@ -79,7 +79,8 @@ const SubGhzProtocol subghz_protocol_star_line = {
.name = SUBGHZ_PROTOCOL_STAR_LINE_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send |
SubGhzProtocolFlag_StarLine,
.decoder = &subghz_protocol_star_line_decoder,
.encoder = &subghz_protocol_star_line_encoder,
+32
View File
@@ -483,3 +483,35 @@ uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) {
return subghz_setting_get_frequency(
instance, subghz_setting_get_frequency_default_index(instance));
}
uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance) {
furi_assert(instance);
#ifndef FURI_DEBUG
FURI_LOG_I(TAG, "Logging loaded presets allow only Debug build");
#else
uint8_t count = 0;
FuriString* temp = furi_string_alloc();
FURI_LOG_I(TAG, "Loaded presets");
for
M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {
furi_string_reset(temp);
for(uint8_t i = 0; i < item->custom_preset_data_size; i++) {
furi_string_cat_printf(temp, "%02u ", item->custom_preset_data[i]);
}
FURI_LOG_I(
TAG, "%u - %s", count + 1, furi_string_get_cstr(item->custom_preset_name));
FURI_LOG_I(TAG, " Size: %u", item->custom_preset_data_size);
FURI_LOG_I(TAG, " Data: %s", furi_string_get_cstr(temp));
count++;
}
furi_string_free(temp);
return count;
#endif
return 0;
}
+2
View File
@@ -53,6 +53,8 @@ uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance);
void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup);
uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance);
#ifdef __cplusplus
}
#endif
+3
View File
@@ -123,6 +123,9 @@ typedef enum {
SubGhzProtocolFlag_Load = (1 << 8),
SubGhzProtocolFlag_Send = (1 << 9),
SubGhzProtocolFlag_BinRAW = (1 << 10),
SubGhzProtocolFlag_StarLine = (1 << 11),
SubGhzProtocolFlag_AutoAlarms = (1 << 12),
SubGhzProtocolFlag_Magelan = (1 << 13),
} SubGhzProtocolFlag;
struct SubGhzProtocol {
+10 -9
View File
@@ -2,15 +2,16 @@
#include <m-core.h>
#define M_INIT_DUP(a) ((a) = strdup(""))
#define M_SET_DUP(a, b) (M_CHECK_DEFAULT_TYPE(a), free((void*)a), (a) = strdup(b))
#define M_INIT_SET_DUP(a, b) ((a) = strdup(b))
#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b))
#define M_CLEAR_DUP(a) (free((void*)a))
#define M_CSTR_DUP_OPLIST \
(INIT(M_INIT_DUP), \
INIT_SET(M_SET_DUP), \
SET(M_SET_DUP), \
CLEAR(M_CLEAR_DUP), \
HASH(m_core_cstr_hash), \
EQUAL(M_CSTR_EQUAL), \
CMP(strcmp), \
#define M_CSTR_DUP_OPLIST \
(INIT(M_INIT_DUP), \
INIT_SET(M_INIT_SET_DUP), \
SET(M_SET_DUP), \
CLEAR(M_CLEAR_DUP), \
HASH(m_core_cstr_hash), \
EQUAL(M_CSTR_EQUAL), \
CMP(strcmp), \
TYPE(const char*))
+1 -1
View File
@@ -2,7 +2,7 @@
#include <furi_hal.h>
#define CONTRAST_ERC 32
#define CONTRAST_ERC 31
#define CONTRAST_MGG 31
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {