mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 12:51:22 +04:00
* feat: FuriThread stdin * ci: fix f18 * feat: stdio callback context * feat: FuriPipe * POTENTIALLY EXPLOSIVE pipe welding * fix: non-explosive welding * Revert welding * docs: furi_pipe * feat: pipe event loop integration * update f18 sdk * f18 * docs: make doxygen happy * fix: event loop not triggering when pipe attached to stdio * fix: partial stdout in pipe * allow simultaneous in and out subscription in event loop * feat: vcp i/o * feat: cli ansi stuffs and history * feat: more line editing * working but slow cli rewrite * restore previous speed after 4 days of debugging 🥲 * fix: cli_app_should_stop * fix: cli and event_loop memory leaks * style: remove commented out code * ci: fix pvs warnings * fix: unit tests, event_loop crash * ci: fix build * ci: silence pvs warning * feat: cli gpio * ci: fix formatting * Fix memory leak during event loop unsubscription * Event better memory leak fix * feat: cli completions * Merge remote-tracking branch 'origin/dev' into portasynthinca3/3928-cli-threads * merge fixups * temporarily exclude speaker_debug app * pvs and unit tests fixups * feat: commands in fals * move commands out of flash, code cleanup * ci: fix errors * fix: run commands in buffer when stopping session * speedup cli file transfer * fix f18 * separate cli_shell into modules * fix pvs warning * fix qflipper refusing to connect * remove temp debug logs * remove erroneous conclusion * Fix memory leak during event loop unsubscription * Event better memory leak fix * unit test for the fix * improve thread stdio callback signatures * pipe stdout timeout * update api symbols * fix f18, formatting * fix pvs warnings * increase stack size, hope to fix unit tests * cli completions * more key combos * commands in fals * move commands out of flash * ci: fix errors * speedup cli file transfer * merge fixups * fix f18 * cli: revert flag changes * cli: fix formatting * cli, fbt: loopback perf benchmark * thread, event_loop: subscribing to thread flags * cli: signal internal events using thread flags, improve performance * fix f18, formatting * event_loop: fix crash * storage_cli: increase write_chunk buffer size again * cli: explanation for order=0 * thread, event_loop: thread flags callback refactor * cli: increase stack size * cli: rename cli_app_should_stop -> cli_is_pipe_broken_or_is_etx_next_char * cli: use plain array instead of mlib for history * cli: prepend file name to static fns * cli: fix formatting * cli_shell: increase stack size * cli_shell: give up pipe to command thread * fix formatting * fix: format * fix merge * fix. merge. * cli_shell: fix detach ordering * desktop: record_cli -> record_cli_vcp * cli: fix spelling, reload/remove ext cmds on card mount/unmount * cli: fix race conditions and formatting * scripts: wait for CTS to go high before starting flipper * scripts: better race condition fix * REVERT THIS: test script race condition fix * Revert "REVERT THIS: test script race condition fix" This reverts commit3b028d29b0. * REVERT THIS: test script fix * scripts: sleep? * cli: updated oplist for CliCommandTree * Revert "REVERT THIS: test script fix" This reverts commite984631854. * cli: mention memory leak in FL ticket --------- Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com> Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: hedger <hedger@nanode.su>
244 lines
7.3 KiB
C
244 lines
7.3 KiB
C
#include <furi.h>
|
|
#include <furi_hal.h>
|
|
|
|
#include <cli/cli_commands.h>
|
|
#include <toolbox/args.h>
|
|
#include <toolbox/pipe.h>
|
|
|
|
#include <ibutton/ibutton_key.h>
|
|
#include <ibutton/ibutton_worker.h>
|
|
#include <ibutton/ibutton_protocols.h>
|
|
|
|
static void ibutton_cli_print_usage(void) {
|
|
printf("Usage:\r\n");
|
|
printf("ikey read\r\n");
|
|
printf("ikey emulate <key_type> <key_data>\r\n");
|
|
printf("ikey write Dallas <key_data>\r\n");
|
|
printf("\t<key_type> choose from:\r\n");
|
|
printf("\tDallas (8 bytes key_data)\r\n");
|
|
printf("\tCyfral (2 bytes key_data)\r\n");
|
|
printf("\tMetakom (4 bytes key_data), must contain correct parity\r\n");
|
|
printf("\t<key_data> are hex-formatted\r\n");
|
|
}
|
|
|
|
static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) {
|
|
bool result = false;
|
|
FuriString* name = furi_string_alloc();
|
|
|
|
do {
|
|
// Read protocol name
|
|
if(!args_read_string_and_trim(args, name)) break;
|
|
|
|
// Make the protocol name uppercase
|
|
const char first = furi_string_get_char(name, 0);
|
|
furi_string_set_char(name, 0, toupper((int)first));
|
|
|
|
const iButtonProtocolId id =
|
|
ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name));
|
|
if(id == iButtonProtocolIdInvalid) break;
|
|
|
|
ibutton_key_set_protocol_id(key, id);
|
|
|
|
// Get the data pointer
|
|
iButtonEditableData data;
|
|
ibutton_protocols_get_editable_data(protocols, key, &data);
|
|
|
|
// Read data
|
|
if(!args_read_hex_bytes(args, data.ptr, data.size)) break;
|
|
|
|
result = true;
|
|
} while(false);
|
|
|
|
furi_string_free(name);
|
|
return result;
|
|
}
|
|
|
|
static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) {
|
|
const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key));
|
|
|
|
if(strncmp(name, "DS", 2) == 0) {
|
|
name = "Dallas";
|
|
}
|
|
|
|
printf("%s ", name);
|
|
|
|
iButtonEditableData data;
|
|
ibutton_protocols_get_editable_data(protocols, key, &data);
|
|
|
|
for(size_t i = 0; i < data.size; i++) {
|
|
printf("%02X", data.ptr[i]);
|
|
}
|
|
|
|
printf("\r\n");
|
|
}
|
|
|
|
#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0)
|
|
|
|
static void ibutton_cli_worker_read_cb(void* context) {
|
|
furi_assert(context);
|
|
FuriEventFlag* event = context;
|
|
furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE);
|
|
}
|
|
|
|
static void ibutton_cli_read(PipeSide* pipe) {
|
|
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
|
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
|
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
|
FuriEventFlag* event = furi_event_flag_alloc();
|
|
|
|
ibutton_worker_start_thread(worker);
|
|
ibutton_worker_read_set_callback(worker, ibutton_cli_worker_read_cb, event);
|
|
|
|
printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n");
|
|
ibutton_worker_read_start(worker, key);
|
|
|
|
while(true) {
|
|
uint32_t flags =
|
|
furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
|
|
|
|
if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
|
|
ibutton_cli_print_key(protocols, key);
|
|
break;
|
|
}
|
|
|
|
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
|
}
|
|
|
|
ibutton_worker_stop(worker);
|
|
ibutton_worker_stop_thread(worker);
|
|
|
|
ibutton_key_free(key);
|
|
ibutton_worker_free(worker);
|
|
ibutton_protocols_free(protocols);
|
|
|
|
furi_event_flag_free(event);
|
|
}
|
|
|
|
typedef struct {
|
|
FuriEventFlag* event;
|
|
iButtonWorkerWriteResult result;
|
|
} iButtonWriteContext;
|
|
|
|
static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
|
|
furi_assert(context);
|
|
iButtonWriteContext* write_context = (iButtonWriteContext*)context;
|
|
write_context->result = result;
|
|
furi_event_flag_set(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE);
|
|
}
|
|
|
|
void ibutton_cli_write(PipeSide* pipe, FuriString* args) {
|
|
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
|
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
|
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
|
|
|
iButtonWriteContext write_context;
|
|
write_context.event = furi_event_flag_alloc();
|
|
|
|
ibutton_worker_start_thread(worker);
|
|
ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context);
|
|
|
|
do {
|
|
if(!ibutton_cli_parse_key(protocols, key, args)) {
|
|
ibutton_cli_print_usage();
|
|
break;
|
|
}
|
|
|
|
if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) &
|
|
iButtonProtocolFeatureWriteId)) {
|
|
ibutton_cli_print_usage();
|
|
break;
|
|
}
|
|
|
|
printf("Writing key ");
|
|
ibutton_cli_print_key(protocols, key);
|
|
printf("Press Ctrl+C to abort\r\n");
|
|
|
|
ibutton_worker_write_id_start(worker, key);
|
|
while(true) {
|
|
uint32_t flags = furi_event_flag_wait(
|
|
write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
|
|
|
|
if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
|
|
if(write_context.result == iButtonWorkerWriteSameKey ||
|
|
write_context.result == iButtonWorkerWriteOK) {
|
|
printf("Write success\r\n");
|
|
break;
|
|
} else if(write_context.result == iButtonWorkerWriteCannotWrite) {
|
|
printf("Write fail\r\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break;
|
|
}
|
|
} while(false);
|
|
|
|
ibutton_worker_stop(worker);
|
|
ibutton_worker_stop_thread(worker);
|
|
|
|
ibutton_key_free(key);
|
|
ibutton_worker_free(worker);
|
|
ibutton_protocols_free(protocols);
|
|
|
|
furi_event_flag_free(write_context.event);
|
|
}
|
|
|
|
void ibutton_cli_emulate(PipeSide* pipe, FuriString* args) {
|
|
iButtonProtocols* protocols = ibutton_protocols_alloc();
|
|
iButtonWorker* worker = ibutton_worker_alloc(protocols);
|
|
iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
|
|
|
|
ibutton_worker_start_thread(worker);
|
|
|
|
do {
|
|
if(!ibutton_cli_parse_key(protocols, key, args)) {
|
|
ibutton_cli_print_usage();
|
|
break;
|
|
}
|
|
|
|
printf("Emulating key ");
|
|
ibutton_cli_print_key(protocols, key);
|
|
printf("Press Ctrl+C to abort\r\n");
|
|
|
|
ibutton_worker_emulate_start(worker, key);
|
|
|
|
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
|
furi_delay_ms(100);
|
|
};
|
|
|
|
} while(false);
|
|
|
|
ibutton_worker_stop(worker);
|
|
ibutton_worker_stop_thread(worker);
|
|
|
|
ibutton_key_free(key);
|
|
ibutton_worker_free(worker);
|
|
ibutton_protocols_free(protocols);
|
|
}
|
|
|
|
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|
UNUSED(context);
|
|
FuriString* cmd;
|
|
cmd = furi_string_alloc();
|
|
|
|
if(!args_read_string_and_trim(args, cmd)) {
|
|
furi_string_free(cmd);
|
|
ibutton_cli_print_usage();
|
|
return;
|
|
}
|
|
|
|
if(furi_string_cmp_str(cmd, "read") == 0) {
|
|
ibutton_cli_read(pipe);
|
|
} else if(furi_string_cmp_str(cmd, "write") == 0) {
|
|
ibutton_cli_write(pipe, args);
|
|
} else if(furi_string_cmp_str(cmd, "emulate") == 0) {
|
|
ibutton_cli_emulate(pipe, args);
|
|
} else {
|
|
ibutton_cli_print_usage();
|
|
}
|
|
|
|
furi_string_free(cmd);
|
|
}
|
|
|
|
CLI_COMMAND_INTERFACE(ikey, execute, CliCommandFlagDefault, 1024);
|