mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Merge remote-tracking branch 'OFW/dev' into dev [ci skip]
This commit is contained in:
commit
ec747892c9
16 changed files with 654 additions and 67 deletions
|
@ -392,37 +392,69 @@ void cli_command_led(Cli* cli, FuriString* args, void* context) {
|
|||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
void cli_command_ps(Cli* cli, FuriString* args, void* context) {
|
||||
static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
const uint8_t threads_num_max = 32;
|
||||
FuriThreadId threads_ids[threads_num_max];
|
||||
uint32_t thread_num = furi_thread_enumerate(threads_ids, threads_num_max);
|
||||
printf(
|
||||
"%-17s %-20s %-5s %-13s %-6s %-8s %s\r\n",
|
||||
"AppID",
|
||||
"Name",
|
||||
"Prio",
|
||||
"Stack start",
|
||||
"Heap",
|
||||
"Stack",
|
||||
"Stack min free");
|
||||
for(uint8_t i = 0; i < thread_num; i++) {
|
||||
TaskControlBlock* tcb = (TaskControlBlock*)threads_ids[i];
|
||||
size_t thread_heap = memmgr_heap_get_thread_memory(threads_ids[i]);
|
||||
int interval = 1000;
|
||||
args_read_int_and_trim(args, &interval);
|
||||
|
||||
FuriThreadList* thread_list = furi_thread_list_alloc();
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
uint32_t tick = furi_get_tick();
|
||||
furi_thread_enumerate(thread_list);
|
||||
|
||||
if(interval) printf("\e[2J\e[0;0f"); // Clear display and return to 0
|
||||
|
||||
uint32_t uptime = tick / furi_kernel_get_tick_frequency();
|
||||
printf(
|
||||
"%-17s %-20s %-5d 0x%-11lx %-6zu %-8lu %-8lu\r\n",
|
||||
furi_thread_get_appid(threads_ids[i]),
|
||||
furi_thread_get_name(threads_ids[i]),
|
||||
furi_thread_get_priority(threads_ids[i]),
|
||||
(uint32_t)tcb->pxStack,
|
||||
thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap,
|
||||
(uint32_t)(tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t),
|
||||
furi_thread_get_stack_space(threads_ids[i]));
|
||||
"Threads: %zu, Uptime: %luh%lum%lus\r\n",
|
||||
furi_thread_list_size(thread_list),
|
||||
uptime / 60 / 60,
|
||||
uptime / 60 % 60,
|
||||
uptime % 60);
|
||||
|
||||
printf(
|
||||
"Heap: total %zu, free %zu, minimum %zu, max block %zu\r\n\r\n",
|
||||
memmgr_get_total_heap(),
|
||||
memmgr_get_free_heap(),
|
||||
memmgr_get_minimum_free_heap(),
|
||||
memmgr_heap_get_max_free_block());
|
||||
|
||||
printf(
|
||||
"%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\r\n",
|
||||
"AppID",
|
||||
"Name",
|
||||
"State",
|
||||
"Prio",
|
||||
"Stack start",
|
||||
"Stack",
|
||||
"Stack Min",
|
||||
"Heap",
|
||||
"CPU");
|
||||
|
||||
for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) {
|
||||
const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i);
|
||||
printf(
|
||||
"%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\r\n",
|
||||
item->app_id,
|
||||
item->name,
|
||||
item->state,
|
||||
item->priority,
|
||||
item->stack_address,
|
||||
item->stack_size,
|
||||
item->stack_min_free,
|
||||
item->heap,
|
||||
(double)item->cpu);
|
||||
}
|
||||
|
||||
if(interval > 0) {
|
||||
furi_delay_ms(interval);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("\r\nTotal: %lu", thread_num);
|
||||
furi_thread_list_free(thread_list);
|
||||
}
|
||||
|
||||
void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
||||
|
@ -484,7 +516,7 @@ void cli_commands_init(Cli* cli) {
|
|||
cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||
cli_add_command(cli, "l", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||
cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);
|
||||
cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL);
|
||||
cli_add_command(cli, "top", CliCommandFlagParallelSafe, cli_command_top, NULL);
|
||||
cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
|
||||
cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL);
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {
|
|||
|
||||
view_dispatcher->event_loop = furi_event_loop_alloc();
|
||||
|
||||
view_dispatcher->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
view_dispatcher->input_queue = furi_message_queue_alloc(16, sizeof(InputEvent));
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
view_dispatcher->event_loop,
|
||||
view_dispatcher->input_queue,
|
||||
|
@ -60,7 +60,7 @@ void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {
|
|||
view_dispatcher_run_input_callback,
|
||||
view_dispatcher);
|
||||
|
||||
view_dispatcher->event_queue = furi_message_queue_alloc(8, sizeof(uint32_t));
|
||||
view_dispatcher->event_queue = furi_message_queue_alloc(16, sizeof(uint32_t));
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
view_dispatcher->event_loop,
|
||||
view_dispatcher->event_queue,
|
||||
|
@ -298,7 +298,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
|
|||
} else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
|
||||
"View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view",
|
||||
view_dispatcher->ongoing_input_view,
|
||||
view_dispatcher->current_view,
|
||||
input_get_key_name(event->key),
|
||||
|
|
|
@ -139,12 +139,17 @@ uint8_t view_port_get_height(const ViewPort* view_port) {
|
|||
|
||||
void view_port_enabled_set(ViewPort* view_port, bool enabled) {
|
||||
furi_check(view_port);
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
// We are not going to lockup system, but will notify you instead
|
||||
// Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call
|
||||
if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {
|
||||
FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
|
||||
}
|
||||
if(view_port->is_enabled != enabled) {
|
||||
view_port->is_enabled = enabled;
|
||||
if(view_port->gui) gui_update(view_port->gui);
|
||||
}
|
||||
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
furi_mutex_release(view_port->mutex);
|
||||
}
|
||||
|
||||
bool view_port_is_enabled(const ViewPort* view_port) {
|
||||
|
@ -236,8 +241,13 @@ void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientat
|
|||
}
|
||||
|
||||
ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) {
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
furi_check(view_port);
|
||||
// We are not going to lockup system, but will notify you instead
|
||||
// Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call
|
||||
if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {
|
||||
FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
|
||||
}
|
||||
ViewPortOrientation orientation = view_port->orientation;
|
||||
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
furi_mutex_release(view_port->mutex);
|
||||
return orientation;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ Only two parameters are mandatory: **appid** and **apptype**. Others are optiona
|
|||
- **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build.
|
||||
- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, `fbt` will abort the firmware build process.
|
||||
- **provides**: functionally identical to **_requires_** field.
|
||||
- **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `ps` and `free` CLI commands to profile your app's memory usage._
|
||||
- **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `top` and `free` CLI commands to profile your app's memory usage._
|
||||
- **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware.
|
||||
- **order**: order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. _Used for ordering startup hooks and menu entries._
|
||||
- **sdk_headers**: list of C header files from this app's code to include in API definitions for external applications.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "thread.h"
|
||||
#include "thread_list.h"
|
||||
#include "kernel.h"
|
||||
#include "memmgr.h"
|
||||
#include "memmgr_heap.h"
|
||||
|
@ -13,6 +14,8 @@
|
|||
#include <stdint.h>
|
||||
#include <task.h>
|
||||
|
||||
#include <task_control_block.h>
|
||||
|
||||
#define TAG "FuriThread"
|
||||
|
||||
#define THREAD_NOTIFY_INDEX (1) // Index 0 is used for stream buffers
|
||||
|
@ -547,33 +550,71 @@ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeo
|
|||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count) {
|
||||
uint32_t i, count;
|
||||
TaskStatus_t* task;
|
||||
static const char* furi_thread_state_name(eTaskState state) {
|
||||
switch(state) {
|
||||
case eRunning:
|
||||
return "Running";
|
||||
case eReady:
|
||||
return "Ready";
|
||||
case eBlocked:
|
||||
return "Blocked";
|
||||
case eSuspended:
|
||||
return "Suspended";
|
||||
case eDeleted:
|
||||
return "Deleted";
|
||||
case eInvalid:
|
||||
return "Invalid";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
if(FURI_IS_IRQ_MODE() || (thread_array == NULL) || (array_item_count == 0U)) {
|
||||
count = 0U;
|
||||
} else {
|
||||
vTaskSuspendAll();
|
||||
bool furi_thread_enumerate(FuriThreadList* thread_list) {
|
||||
furi_check(thread_list);
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
|
||||
bool result = false;
|
||||
|
||||
vTaskSuspendAll();
|
||||
do {
|
||||
uint32_t tick = furi_get_tick();
|
||||
uint32_t count = uxTaskGetNumberOfTasks();
|
||||
|
||||
TaskStatus_t* task = pvPortMalloc(count * sizeof(TaskStatus_t));
|
||||
|
||||
if(!task) break;
|
||||
|
||||
count = uxTaskGetNumberOfTasks();
|
||||
task = pvPortMalloc(count * sizeof(TaskStatus_t));
|
||||
configRUN_TIME_COUNTER_TYPE total_run_time;
|
||||
count = uxTaskGetSystemState(task, count, &total_run_time);
|
||||
for(uint32_t i = 0U; i < count; i++) {
|
||||
TaskControlBlock* tcb = (TaskControlBlock*)task[i].xHandle;
|
||||
|
||||
if(task != NULL) {
|
||||
count = uxTaskGetSystemState(task, count, &total_run_time);
|
||||
FuriThreadListItem* item =
|
||||
furi_thread_list_get_or_insert(thread_list, (FuriThread*)task[i].xHandle);
|
||||
|
||||
for(i = 0U; (i < count) && (i < array_item_count); i++) {
|
||||
thread_array[i] = (FuriThreadId)task[i].xHandle;
|
||||
}
|
||||
count = i;
|
||||
item->thread = (FuriThreadId)task[i].xHandle;
|
||||
item->app_id = furi_thread_get_appid(item->thread);
|
||||
item->name = task[i].pcTaskName;
|
||||
item->priority = task[i].uxCurrentPriority;
|
||||
item->stack_address = (uint32_t)tcb->pxStack;
|
||||
size_t thread_heap = memmgr_heap_get_thread_memory(item->thread);
|
||||
item->heap = thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap;
|
||||
item->stack_size = (tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t);
|
||||
item->stack_min_free = furi_thread_get_stack_space(item->thread);
|
||||
item->state = furi_thread_state_name(task[i].eCurrentState);
|
||||
item->counter_previous = item->counter_current;
|
||||
item->counter_current = task[i].ulRunTimeCounter;
|
||||
item->tick = tick;
|
||||
}
|
||||
(void)xTaskResumeAll();
|
||||
|
||||
vPortFree(task);
|
||||
}
|
||||
furi_thread_list_process(thread_list, total_run_time, tick);
|
||||
|
||||
return count;
|
||||
result = true;
|
||||
} while(false);
|
||||
(void)xTaskResumeAll();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* furi_thread_get_name(FuriThreadId thread_id) {
|
||||
|
|
|
@ -46,6 +46,9 @@ typedef enum {
|
|||
*/
|
||||
typedef struct FuriThread FuriThread;
|
||||
|
||||
/** FuriThreadList type */
|
||||
typedef struct FuriThreadList FuriThreadList;
|
||||
|
||||
/**
|
||||
* @brief Unique thread identifier type (used by the OS kernel).
|
||||
*/
|
||||
|
@ -379,13 +382,13 @@ uint32_t furi_thread_flags_get(void);
|
|||
uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Enumerate all threads.
|
||||
*
|
||||
* @param[out] thread_array pointer to the output array (must be properly allocated)
|
||||
* @param[in] array_item_count output array capacity in elements (NOT bytes)
|
||||
* @return total thread count (array_item_count or less)
|
||||
* @brief Enumerate all threads.
|
||||
*
|
||||
* @param[out] thread_list pointer to the FuriThreadList container
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count);
|
||||
bool furi_thread_enumerate(FuriThreadList* thread_list);
|
||||
|
||||
/**
|
||||
* @brief Get the name of a thread based on its unique identifier.
|
||||
|
|
110
furi/core/thread_list.c
Normal file
110
furi/core/thread_list.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
#include "thread_list.h"
|
||||
#include "check.h"
|
||||
|
||||
#include <m-array.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
ARRAY_DEF(FuriThreadListItemArray, FuriThreadListItem*, M_PTR_OPLIST) // NOLINT
|
||||
|
||||
#define M_OPL_FuriThreadListItemArray_t() ARRAY_OPLIST(FuriThreadListItemArray, M_PTR_OPLIST)
|
||||
|
||||
DICT_DEF2(
|
||||
FuriThreadListItemDict,
|
||||
uint32_t,
|
||||
M_DEFAULT_OPLIST,
|
||||
FuriThreadListItem*,
|
||||
M_PTR_OPLIST) // NOLINT
|
||||
|
||||
#define M_OPL_FuriThreadListItemDict_t() \
|
||||
DICT_OPLIST(FuriThreadListItemDict, M_DEFAULT_OPLIST, M_PTR_OPLIST)
|
||||
|
||||
struct FuriThreadList {
|
||||
FuriThreadListItemArray_t items;
|
||||
FuriThreadListItemDict_t search;
|
||||
uint32_t runtime_previous;
|
||||
uint32_t runtime_current;
|
||||
};
|
||||
|
||||
FuriThreadList* furi_thread_list_alloc(void) {
|
||||
FuriThreadList* instance = malloc(sizeof(FuriThreadList));
|
||||
|
||||
FuriThreadListItemArray_init(instance->items);
|
||||
FuriThreadListItemDict_init(instance->search);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void furi_thread_list_free(FuriThreadList* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
FuriThreadListItemArray_it_t it;
|
||||
FuriThreadListItemArray_it(it, instance->items);
|
||||
while(!FuriThreadListItemArray_end_p(it)) {
|
||||
FuriThreadListItem* item = *FuriThreadListItemArray_cref(it);
|
||||
free(item);
|
||||
FuriThreadListItemArray_next(it);
|
||||
}
|
||||
|
||||
FuriThreadListItemDict_clear(instance->search);
|
||||
FuriThreadListItemArray_clear(instance->items);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
size_t furi_thread_list_size(FuriThreadList* instance) {
|
||||
furi_check(instance);
|
||||
return FuriThreadListItemArray_size(instance->items);
|
||||
}
|
||||
|
||||
FuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position) {
|
||||
furi_check(instance);
|
||||
furi_check(position < furi_thread_list_size(instance));
|
||||
|
||||
return *FuriThreadListItemArray_get(instance->items, position);
|
||||
}
|
||||
|
||||
FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread) {
|
||||
furi_check(instance);
|
||||
|
||||
FuriThreadListItem** item_ptr = FuriThreadListItemDict_get(instance->search, (uint32_t)thread);
|
||||
if(item_ptr) {
|
||||
return *item_ptr;
|
||||
}
|
||||
|
||||
FuriThreadListItem* item = malloc(sizeof(FuriThreadListItem));
|
||||
|
||||
FuriThreadListItemArray_push_back(instance->items, item);
|
||||
FuriThreadListItemDict_set_at(instance->search, (uint32_t)thread, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick) {
|
||||
furi_check(instance);
|
||||
|
||||
instance->runtime_previous = instance->runtime_current;
|
||||
instance->runtime_current = runtime;
|
||||
|
||||
uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;
|
||||
|
||||
FuriThreadListItemArray_it_t it;
|
||||
FuriThreadListItemArray_it(it, instance->items);
|
||||
while(!FuriThreadListItemArray_end_p(it)) {
|
||||
FuriThreadListItem* item = *FuriThreadListItemArray_cref(it);
|
||||
if(item->tick != tick) {
|
||||
FuriThreadListItemArray_remove(instance->items, it);
|
||||
(void)FuriThreadListItemDict_erase(instance->search, (uint32_t)item->thread);
|
||||
free(item);
|
||||
} else {
|
||||
uint32_t item_counter = item->counter_current - item->counter_previous;
|
||||
if(item_counter && item->counter_previous && item->counter_current) {
|
||||
item->cpu = (float)item_counter / (float)runtime_counter * 100.0f;
|
||||
if(item->cpu > 200.0f) item->cpu = 0.0f;
|
||||
} else {
|
||||
item->cpu = 0.0f;
|
||||
}
|
||||
|
||||
FuriThreadListItemArray_next(it);
|
||||
}
|
||||
}
|
||||
}
|
81
furi/core/thread_list.h
Normal file
81
furi/core/thread_list.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "common_defines.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
FuriThread* thread; /**< Pointer to FuriThread, valid while it is running */
|
||||
const char* app_id; /**< Thread application id, valid while it is running */
|
||||
const char* name; /**< Thread name, valid while it is running */
|
||||
FuriThreadPriority priority; /**< Thread priority */
|
||||
uint32_t stack_address; /**< Thread stack address */
|
||||
size_t heap; /**< Thread heap size if tracking enabled, 0 - otherwise */
|
||||
uint32_t stack_size; /**< Thread stack size */
|
||||
uint32_t stack_min_free; /**< Thread minimum of the stack size ever reached */
|
||||
const char*
|
||||
state; /**< Thread state, can be: "Running", "Ready", "Blocked", "Suspended", "Deleted", "Invalid" */
|
||||
float cpu; /**< Thread CPU usage time in percents (including interrupts happened while running) */
|
||||
|
||||
// Service variables
|
||||
uint32_t counter_previous; /**< Thread previous runtime counter */
|
||||
uint32_t counter_current; /**< Thread current runtime counter */
|
||||
uint32_t tick; /**< Thread last seen tick */
|
||||
} FuriThreadListItem;
|
||||
|
||||
/** Anonymous FuriThreadList type */
|
||||
typedef struct FuriThreadList FuriThreadList;
|
||||
|
||||
/** Allocate FuriThreadList instance
|
||||
*
|
||||
* @return FuriThreadList instance
|
||||
*/
|
||||
FuriThreadList* furi_thread_list_alloc(void);
|
||||
|
||||
/** Free FuriThreadList instance
|
||||
*
|
||||
* @param instance The FuriThreadList instance to free
|
||||
*/
|
||||
void furi_thread_list_free(FuriThreadList* instance);
|
||||
|
||||
/** Get FuriThreadList instance size
|
||||
*
|
||||
* @param instance The instance
|
||||
*
|
||||
* @return Item count
|
||||
*/
|
||||
size_t furi_thread_list_size(FuriThreadList* instance);
|
||||
|
||||
/** Get item at position
|
||||
*
|
||||
* @param instance The FuriThreadList instance
|
||||
* @param[in] position The position of the item
|
||||
*
|
||||
* @return The FuriThreadListItem instance
|
||||
*/
|
||||
FuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position);
|
||||
|
||||
/** Get item by thread FuriThread pointer
|
||||
*
|
||||
* @param instance The FuriThreadList instance
|
||||
* @param thread The FuriThread pointer
|
||||
*
|
||||
* @return The FuriThreadListItem instance
|
||||
*/
|
||||
FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread);
|
||||
|
||||
/** Process items in the FuriThreadList instance
|
||||
*
|
||||
* @param instance The instance
|
||||
* @param[in] runtime The runtime of the system since start
|
||||
* @param[in] tick The tick when processing happened
|
||||
*/
|
||||
void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -16,6 +16,7 @@
|
|||
#include "core/record.h"
|
||||
#include "core/semaphore.h"
|
||||
#include "core/thread.h"
|
||||
#include "core/thread_list.h"
|
||||
#include "core/timer.h"
|
||||
#include "core/string.h"
|
||||
#include "core/stream_buffer.h"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "protocol_keri.h"
|
||||
#include "protocol_gallagher.h"
|
||||
#include "protocol_nexwatch.h"
|
||||
#include "protocol_securakey.h"
|
||||
|
||||
const ProtocolBase* lfrfid_protocols[] = {
|
||||
[LFRFIDProtocolEM4100] = &protocol_em4100,
|
||||
|
@ -41,4 +42,5 @@ const ProtocolBase* lfrfid_protocols[] = {
|
|||
[LFRFIDProtocolKeri] = &protocol_keri,
|
||||
[LFRFIDProtocolGallagher] = &protocol_gallagher,
|
||||
[LFRFIDProtocolNexwatch] = &protocol_nexwatch,
|
||||
[LFRFIDProtocolSecurakey] = &protocol_securakey,
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@ typedef enum {
|
|||
LFRFIDProtocolKeri,
|
||||
LFRFIDProtocolGallagher,
|
||||
LFRFIDProtocolNexwatch,
|
||||
LFRFIDProtocolSecurakey,
|
||||
LFRFIDProtocolMax,
|
||||
} LFRFIDProtocol;
|
||||
|
||||
|
|
294
lib/lfrfid/protocols/protocol_securakey.c
Normal file
294
lib/lfrfid/protocols/protocol_securakey.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
// The timing parameters and data structure used in this file
|
||||
// are based on the knowledge found in Proxmark3's firmware:
|
||||
// https://github.com/RfidResearchGroup/proxmark3/blob/1c52152d30f7744c0336633317ea6640dbcdc796/client/src/cmdlfsecurakey.c
|
||||
// PM3's repo has mentioned the existence of non-26-or-32-bit formats.
|
||||
// Those are not supported here for preventing false positives.
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <toolbox/hex.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
#include <toolbox/manchester_decoder.h>
|
||||
|
||||
#define TAG "SECURAKEY"
|
||||
#define SECURAKEY_ENCODED_SIZE_BITS (84)
|
||||
#define SECURAKEY_PREAMBLE_SIZE_BITS (12)
|
||||
#define SECURAKEY_ENCODED_FULL_SIZE_BITS \
|
||||
(SECURAKEY_ENCODED_SIZE_BITS + SECURAKEY_PREAMBLE_SIZE_BITS)
|
||||
#define SECURAKEY_ENCODED_FULL_SIZE_BYTE (SECURAKEY_ENCODED_FULL_SIZE_BITS / 8)
|
||||
#define SECURAKEY_DECODED_DATA_SIZE_BITS \
|
||||
(48) // 16-bit for facility code/number, 16-bit for card number, 16-bit for two checksum
|
||||
#define SECURAKEY_DECODED_DATA_SIZE_BYTES (SECURAKEY_DECODED_DATA_SIZE_BITS / 8)
|
||||
#define LFRFID_FREQUENCY (125000)
|
||||
#define SECURAKEY_CLOCK_PER_BIT (40) // RF/40
|
||||
#define SECURAKEY_READ_LONG_TIME \
|
||||
(1000000 / (LFRFID_FREQUENCY / SECURAKEY_CLOCK_PER_BIT)) // 1000000 micro sec / sec
|
||||
#define SECURAKEY_READ_SHORT_TIME (SECURAKEY_READ_LONG_TIME / 2)
|
||||
#define SECURAKEY_READ_JITTER_TIME (SECURAKEY_READ_SHORT_TIME * 40 / 100) // 40% jitter tolerance
|
||||
#define SECURAKEY_READ_SHORT_TIME_LOW \
|
||||
(SECURAKEY_READ_SHORT_TIME - \
|
||||
SECURAKEY_READ_JITTER_TIME) // these are used for manchester decoding
|
||||
#define SECURAKEY_READ_SHORT_TIME_HIGH (SECURAKEY_READ_SHORT_TIME + SECURAKEY_READ_JITTER_TIME)
|
||||
#define SECURAKEY_READ_LONG_TIME_LOW (SECURAKEY_READ_LONG_TIME - SECURAKEY_READ_JITTER_TIME)
|
||||
#define SECURAKEY_READ_LONG_TIME_HIGH (SECURAKEY_READ_LONG_TIME + SECURAKEY_READ_JITTER_TIME)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[SECURAKEY_DECODED_DATA_SIZE_BYTES];
|
||||
uint8_t encoded_data[SECURAKEY_ENCODED_FULL_SIZE_BYTE];
|
||||
uint8_t encoded_data_index;
|
||||
bool encoded_polarity;
|
||||
ManchesterState decoder_manchester_state;
|
||||
uint8_t bit_format;
|
||||
} ProtocolSecurakey;
|
||||
|
||||
ProtocolSecurakey* protocol_securakey_alloc(void) {
|
||||
ProtocolSecurakey* protocol = malloc(sizeof(ProtocolSecurakey));
|
||||
return (void*)protocol;
|
||||
};
|
||||
|
||||
void protocol_securakey_free(ProtocolSecurakey* protocol) {
|
||||
free(protocol);
|
||||
};
|
||||
|
||||
uint8_t* protocol_securakey_get_data(ProtocolSecurakey* protocol) {
|
||||
return protocol->data;
|
||||
};
|
||||
|
||||
static bool protocol_securakey_can_be_decoded(ProtocolSecurakey* protocol) {
|
||||
// check 11 bits preamble
|
||||
if(bit_lib_get_bits_16(protocol->encoded_data, 0, SECURAKEY_PREAMBLE_SIZE_BITS) ==
|
||||
0b011111111100) {
|
||||
if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 26 ||
|
||||
bit_lib_get_bits(protocol->encoded_data, 13, 6) == 32) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static void protocol_securakey_decode(ProtocolSecurakey* protocol) {
|
||||
memset(protocol->data, 0, SECURAKEY_DECODED_DATA_SIZE_BYTES);
|
||||
// encoded_data looks like this (citation: pm3 repo):
|
||||
// 26-bit format (1-bit even parity bit, 8-bit facility number, 16-bit card number, 1-bit odd parity bit)
|
||||
// preamble ??bitlen reserved EPf fffffffc cccccccc cccccccOP CS? CS2?
|
||||
// 0111111111 0 01011010 0 00000000 0 00000010 0 00110110 0 00111110 0 01100010 0 00001111 0 01100000 0 00000000 0 0000
|
||||
|
||||
// 32-bit format (1-bit even parity bit, 14-bit facility number, 16-bit card number, 1-bit odd parity bit)
|
||||
// preamble ??bitlen reserved EPfffffff fffffffc cccccccc cccccccOP CS? CS2?
|
||||
// 0111111111 0 01100000 0 00000000 0 10000100 0 11001010 0 01011011 0 01010110 0 00010110 0 11100000 0 00000000 0 0000
|
||||
|
||||
// left two 0 paddings in the beginning for easier parsing (00011010 = 011010)
|
||||
// get facility number (f)
|
||||
if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 26) {
|
||||
FURI_LOG_D(TAG, "26-bit Securakey detected");
|
||||
protocol->bit_format = 26;
|
||||
bit_lib_copy_bits(protocol->data, 8, 1, protocol->encoded_data, 36);
|
||||
// have to skip one spacer
|
||||
bit_lib_copy_bits(protocol->data, 9, 7, protocol->encoded_data, 38);
|
||||
} else if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 32) {
|
||||
FURI_LOG_D(TAG, "32-bit Securakey detected");
|
||||
protocol->bit_format = 32;
|
||||
// same two 0 paddings here, otherwise should be bit_lib_copy_bits(protocol->data, 8, 7, protocol->encoded_data, 30);
|
||||
bit_lib_copy_bits(protocol->data, 2, 7, protocol->encoded_data, 30);
|
||||
// have to skip one spacer
|
||||
bit_lib_copy_bits(protocol->data, 9, 7, protocol->encoded_data, 38);
|
||||
}
|
||||
|
||||
// get card number (c)
|
||||
bit_lib_copy_bits(protocol->data, 16, 1, protocol->encoded_data, 45);
|
||||
// same skips here
|
||||
bit_lib_copy_bits(protocol->data, 17, 8, protocol->encoded_data, 47);
|
||||
bit_lib_copy_bits(protocol->data, 25, 7, protocol->encoded_data, 56);
|
||||
|
||||
// unsure about CS yet, might as well just save it
|
||||
// CS1
|
||||
bit_lib_copy_bits(protocol->data, 32, 8, protocol->encoded_data, 65);
|
||||
// CS2
|
||||
bit_lib_copy_bits(protocol->data, 40, 8, protocol->encoded_data, 74);
|
||||
|
||||
// (decoded) data looks like this (pp are zero paddings):
|
||||
// 26-bit format (1-bit EP, 8-bit facility number, 16-bit card number, 1-bit OP)
|
||||
// pppppppp ffffffff cccccccc cccccccc CS1 CS2
|
||||
// 00000000 00011011 00011111 00110001 00001111 01100000
|
||||
|
||||
// 32-bit format (1-bit EP, 14-bit facility number, 16-bit card number, 1-bit OP)
|
||||
// ppffffff ffffffff cccccccc cccccccc CS1 CS2
|
||||
// 00000010 01100101 00101101 10101011 00010110 11100000
|
||||
};
|
||||
|
||||
void protocol_securakey_decoder_start(ProtocolSecurakey* protocol) {
|
||||
memset(protocol->encoded_data, 0, SECURAKEY_ENCODED_FULL_SIZE_BYTE);
|
||||
manchester_advance(
|
||||
protocol->decoder_manchester_state,
|
||||
ManchesterEventReset,
|
||||
&protocol->decoder_manchester_state,
|
||||
NULL);
|
||||
};
|
||||
|
||||
bool protocol_securakey_decoder_feed(ProtocolSecurakey* protocol, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
// this is where we do manchester demodulation on already ASK-demoded data
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
if(duration > SECURAKEY_READ_SHORT_TIME_LOW && duration < SECURAKEY_READ_SHORT_TIME_HIGH) {
|
||||
if(!level) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else {
|
||||
event = ManchesterEventShortLow;
|
||||
}
|
||||
} else if(duration > SECURAKEY_READ_LONG_TIME_LOW && duration < SECURAKEY_READ_LONG_TIME_HIGH) {
|
||||
if(!level) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
event = ManchesterEventLongLow;
|
||||
}
|
||||
}
|
||||
// append a new bit to the encoded bit stream
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data);
|
||||
if(data_ok) {
|
||||
bit_lib_push_bit(protocol->encoded_data, SECURAKEY_ENCODED_FULL_SIZE_BYTE, data);
|
||||
if(protocol_securakey_can_be_decoded(protocol)) {
|
||||
protocol_securakey_decode(protocol);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
void protocol_securakey_render_data(ProtocolSecurakey* protocol, FuriString* result) {
|
||||
if(bit_lib_get_bits(protocol->data, 0, 8) == 0) {
|
||||
protocol->bit_format = 26;
|
||||
} else {
|
||||
protocol->bit_format = 32;
|
||||
}
|
||||
furi_string_printf(
|
||||
result,
|
||||
"%u-bit format\nFacility code: %u\nCard number: %u",
|
||||
protocol->bit_format,
|
||||
bit_lib_get_bits_16(protocol->data, 0, 16),
|
||||
bit_lib_get_bits_16(protocol->data, 16, 16));
|
||||
};
|
||||
|
||||
bool protocol_securakey_encoder_start(ProtocolSecurakey* protocol) {
|
||||
// set all of our encoded_data bits to zeros.
|
||||
memset(protocol->encoded_data, 0, SECURAKEY_ENCODED_FULL_SIZE_BYTE);
|
||||
|
||||
// write the preamble to the beginning of the encoded_data
|
||||
bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8);
|
||||
bit_lib_set_bits(protocol->encoded_data, 8, 0b11001, 5);
|
||||
|
||||
if(bit_lib_get_bits(protocol->data, 0, 8) == 0) {
|
||||
protocol->bit_format = 26;
|
||||
// set bit length
|
||||
bit_lib_set_bits(protocol->encoded_data, 13, protocol->bit_format, 6);
|
||||
// set even parity & odd parity
|
||||
if(!bit_lib_test_parity(protocol->data, 8, 12, BitLibParityOdd, 12)) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 35, 1);
|
||||
}
|
||||
if(bit_lib_test_parity(protocol->data, 20, 12, BitLibParityOdd, 12)) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 63, 1);
|
||||
}
|
||||
// write facility number (f)
|
||||
bit_lib_copy_bits(protocol->encoded_data, 36, 1, protocol->data, 8);
|
||||
// have to skip one spacer
|
||||
bit_lib_copy_bits(protocol->encoded_data, 38, 7, protocol->data, 9);
|
||||
|
||||
} else {
|
||||
protocol->bit_format = 32;
|
||||
// set bit length
|
||||
bit_lib_set_bits(protocol->encoded_data, 13, protocol->bit_format, 6);
|
||||
// set EP & OP
|
||||
if(!bit_lib_test_parity(protocol->data, 2, 15, BitLibParityOdd, 15)) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 29, 1);
|
||||
}
|
||||
if(bit_lib_test_parity(protocol->data, 17, 15, BitLibParityOdd, 15)) {
|
||||
bit_lib_set_bit(protocol->encoded_data, 63, 1);
|
||||
}
|
||||
// write facility number (f)
|
||||
bit_lib_copy_bits(protocol->encoded_data, 30, 7, protocol->data, 2);
|
||||
// have to skip one spacer
|
||||
bit_lib_copy_bits(protocol->encoded_data, 38, 7, protocol->data, 3);
|
||||
}
|
||||
|
||||
// write card number (c)
|
||||
bit_lib_copy_bits(protocol->encoded_data, 45, 1, protocol->data, 16);
|
||||
// same skips here
|
||||
bit_lib_copy_bits(protocol->encoded_data, 47, 8, protocol->data, 17);
|
||||
bit_lib_copy_bits(protocol->encoded_data, 56, 7, protocol->data, 25);
|
||||
|
||||
// unsure about CS yet might as well just copy it from saved
|
||||
// CS1
|
||||
bit_lib_copy_bits(protocol->encoded_data, 65, 8, protocol->data, 32);
|
||||
// CS2
|
||||
bit_lib_copy_bits(protocol->encoded_data, 74, 8, protocol->data, 40);
|
||||
|
||||
// for sending we start at bit 0.
|
||||
protocol->encoded_data_index = 0;
|
||||
protocol->encoded_polarity = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_securakey_encoder_yield(ProtocolSecurakey* protocol) {
|
||||
bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index);
|
||||
uint32_t duration = SECURAKEY_CLOCK_PER_BIT / 2;
|
||||
if(protocol->encoded_polarity) {
|
||||
protocol->encoded_polarity = false;
|
||||
} else {
|
||||
level = !level;
|
||||
protocol->encoded_polarity = true;
|
||||
bit_lib_increment_index(protocol->encoded_data_index, SECURAKEY_ENCODED_FULL_SIZE_BITS);
|
||||
}
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_securakey_write_data(ProtocolSecurakey* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
// Correct protocol data by redecoding
|
||||
protocol_securakey_encoder_start(protocol);
|
||||
protocol_securakey_decode(protocol);
|
||||
protocol_securakey_encoder_start(protocol);
|
||||
// Write T5577
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] =
|
||||
(LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_40 |
|
||||
(3
|
||||
<< LFRFID_T5577_MAXBLOCK_SHIFT)); // we only need 3 32-bit blocks for our 96-bit encoded data
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
|
||||
request->t5577.blocks_to_write = 4;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_securakey = {
|
||||
.name = "Radio Key",
|
||||
.manufacturer = "Securakey",
|
||||
.data_size = SECURAKEY_DECODED_DATA_SIZE_BYTES,
|
||||
.features = LFRFIDFeatureASK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_securakey_alloc,
|
||||
.free = (ProtocolFree)protocol_securakey_free,
|
||||
.get_data = (ProtocolGetData)protocol_securakey_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_securakey_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_securakey_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_securakey_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_securakey_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_securakey_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_securakey_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_securakey_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_securakey.h
Normal file
4
lib/lfrfid/protocols/protocol_securakey.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_securakey;
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,64.3,,
|
||||
Version,+,65.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
|
@ -1594,7 +1594,7 @@ Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCa
|
|||
Function,-,furi_thread_alloc_service,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*"
|
||||
Function,-,furi_thread_disable_heap_trace,void,FuriThread*
|
||||
Function,+,furi_thread_enable_heap_trace,void,FuriThread*
|
||||
Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t"
|
||||
Function,+,furi_thread_enumerate,_Bool,FuriThreadList*
|
||||
Function,+,furi_thread_flags_clear,uint32_t,uint32_t
|
||||
Function,+,furi_thread_flags_get,uint32_t,
|
||||
Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t"
|
||||
|
@ -1614,6 +1614,12 @@ Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
|
|||
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
|
||||
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
|
||||
Function,+,furi_thread_join,_Bool,FuriThread*
|
||||
Function,+,furi_thread_list_alloc,FuriThreadList*,
|
||||
Function,+,furi_thread_list_free,void,FuriThreadList*
|
||||
Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t"
|
||||
Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*"
|
||||
Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t"
|
||||
Function,+,furi_thread_list_size,size_t,FuriThreadList*
|
||||
Function,+,furi_thread_resume,void,FuriThreadId
|
||||
Function,+,furi_thread_set_appid,void,"FuriThread*, const char*"
|
||||
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
|
||||
|
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,64.3,,
|
||||
Version,+,65.0,,
|
||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
|
@ -1848,7 +1848,7 @@ Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCa
|
|||
Function,-,furi_thread_alloc_service,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*"
|
||||
Function,-,furi_thread_disable_heap_trace,void,FuriThread*
|
||||
Function,+,furi_thread_enable_heap_trace,void,FuriThread*
|
||||
Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t"
|
||||
Function,+,furi_thread_enumerate,_Bool,FuriThreadList*
|
||||
Function,+,furi_thread_flags_clear,uint32_t,uint32_t
|
||||
Function,+,furi_thread_flags_get,uint32_t,
|
||||
Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t"
|
||||
|
@ -1868,6 +1868,12 @@ Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
|
|||
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
|
||||
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
|
||||
Function,+,furi_thread_join,_Bool,FuriThread*
|
||||
Function,+,furi_thread_list_alloc,FuriThreadList*,
|
||||
Function,+,furi_thread_list_free,void,FuriThreadList*
|
||||
Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t"
|
||||
Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*"
|
||||
Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t"
|
||||
Function,+,furi_thread_list_size,size_t,FuriThreadList*
|
||||
Function,+,furi_thread_resume,void,FuriThreadId
|
||||
Function,+,furi_thread_set_appid,void,"FuriThread*, const char*"
|
||||
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
|
||||
|
|
|
|
@ -29,13 +29,9 @@
|
|||
// #define configTOTAL_HEAP_SIZE ((size_t)0)
|
||||
#define configMAX_TASK_NAME_LEN (32)
|
||||
|
||||
/* Run-time stats - broken ATM, to be fixed */
|
||||
/*
|
||||
#define configGENERATE_RUN_TIME_STATS 1
|
||||
#define configRUN_TIME_COUNTER_TYPE uint64_t
|
||||
#define portGET_RUN_TIME_COUNTER_VALUE() (DWT->CYCCNT)
|
||||
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
|
||||
*/
|
||||
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
|
|
Loading…
Reference in a new issue