From 02c27becb0d682c6ab757e9f7fa6774e1e5d783e Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 18 Oct 2022 17:10:21 +0300 Subject: [PATCH 1/4] [FL-2912] Forced RAW receive option for Infrared CLI #1891 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 47 +++++++++++++++-------- lib/infrared/worker/infrared_worker.c | 10 ++++- lib/infrared/worker/infrared_worker.h | 8 ++++ 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 54e3e2515..7723dc973 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -71,25 +71,9 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv } } -static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - InfraredWorker* worker = infrared_worker_alloc(); - infrared_worker_rx_start(worker); - infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); - - printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - - infrared_worker_rx_stop(worker); - infrared_worker_free(worker); -} - static void infrared_cli_print_usage(void) { printf("Usage:\r\n"); - printf("\tir rx\r\n"); + printf("\tir rx [raw]\r\n"); printf("\tir tx
\r\n"); printf("\t and
are hex-formatted\r\n"); printf("\tAvailable protocols:"); @@ -108,6 +92,35 @@ static void infrared_cli_print_usage(void) { printf("\tir universal list \r\n"); } +static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { + UNUSED(cli); + + bool enable_decoding = true; + + if(!furi_string_empty(args)) { + if(!furi_string_cmp_str(args, "raw")) { + enable_decoding = false; + } else { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + return; + } + } + + InfraredWorker* worker = infrared_worker_alloc(); + infrared_worker_rx_enable_signal_decoding(worker, enable_decoding); + infrared_worker_rx_start(worker); + infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); + + printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + + infrared_worker_rx_stop(worker); + infrared_worker_free(worker); +} + static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { char protocol_name[32]; InfraredMessage message; diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 86b19114c..c03f180f6 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -57,6 +57,7 @@ struct InfraredWorker { InfraredDecoderHandler* infrared_decoder; NotificationApp* notification; bool blink_enable; + bool decode_enable; union { struct { @@ -131,7 +132,8 @@ static void infrared_worker_process_timeout(InfraredWorker* instance) { static void infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) { const InfraredMessage* message_decoded = - infrared_decode(instance->infrared_decoder, level, duration); + instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) : + NULL; if(message_decoded) { instance->signal.message = *message_decoded; instance->signal.timings_cnt = 0; @@ -233,6 +235,7 @@ InfraredWorker* infrared_worker_alloc() { instance->infrared_decoder = infrared_alloc_decoder(); instance->infrared_encoder = infrared_alloc_encoder(); instance->blink_enable = false; + instance->decode_enable = true; instance->notification = furi_record_open(RECORD_NOTIFICATION); instance->state = InfraredWorkerStateIdle; @@ -316,6 +319,11 @@ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool instance->blink_enable = enable; } +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) { + furi_assert(instance); + instance->decode_enable = enable; +} + void infrared_worker_tx_start(InfraredWorker* instance) { furi_assert(instance); furi_assert(instance->state == InfraredWorkerStateIdle); diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index c6617e501..26919c4f5 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -76,6 +76,14 @@ void infrared_worker_rx_set_received_signal_callback( */ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable); +/** Enable decoding of received infrared signals. + * + * @param[in] instance - instance of InfraredWorker + * @param[in] enable - true if you want to enable decoding + * false otherwise + */ +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable); + /** Clarify is received signal either decoded or raw * * @param[in] signal - received signal From 68009c6230c5898d96409255897c35ead7991bcf Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:24:53 +0400 Subject: [PATCH 2/4] [FL-2919] SubGhz: CAME Wrong number of bits in key (add protocol Airforce) (#1890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- lib/subghz/protocols/came.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 7c037bd12..3fc61bf60 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -16,6 +16,8 @@ #define CAME_24_COUNT_BIT 24 #define PRASTEL_COUNT_BIT 25 #define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" static const SubGhzBlockConst subghz_protocol_came_const = { .te_short = 320, @@ -86,7 +88,7 @@ void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; - instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -151,10 +153,7 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip FURI_LOG_E(TAG, "Deserialize error"); break; } - if((instance->generic.data_count_bit != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != CAME_24_COUNT_BIT) && - (instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) { + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -310,10 +309,7 @@ bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flip if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if((instance->generic.data_count_bit != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != CAME_24_COUNT_BIT) && - (instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) { + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -338,8 +334,11 @@ void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n", - (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? PRASTEL_NAME : - instance->generic.protocol_name), + (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? + PRASTEL_NAME : + (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? + AIRFORCE_NAME : + instance->generic.protocol_name)), instance->generic.data_count_bit, code_found_lo, code_found_reverse_lo); From 56f760aa07ba5e659f7d8278819f3699afc61504 Mon Sep 17 00:00:00 2001 From: Patrick Cunningham Date: Tue, 18 Oct 2022 09:58:26 -0500 Subject: [PATCH 3/4] Picopass: Read Elite (#1888) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * working elite dict * add csn to display Co-authored-by: あく --- .../picopass/helpers/iclass_elite_dict.c | 151 ++++++++++++++++++ .../picopass/helpers/iclass_elite_dict.h | 28 ++++ .../picopass/lib/loclass/optimized_elite.c | 2 +- .../plugins/picopass/picopass_device.h | 2 + .../plugins/picopass/picopass_worker.c | 103 ++++++++++-- .../plugins/picopass/picopass_worker_i.h | 1 + .../scenes/picopass_scene_read_card_success.c | 29 ++-- 7 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 applications/plugins/picopass/helpers/iclass_elite_dict.c create mode 100644 applications/plugins/picopass/helpers/iclass_elite_dict.h diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/plugins/picopass/helpers/iclass_elite_dict.c new file mode 100644 index 000000000..455eb23c1 --- /dev/null +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.c @@ -0,0 +1,151 @@ +#include "iclass_elite_dict.h" + +#include +#include + +#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt") +#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt") + +#define TAG "IclassEliteDict" + +#define ICLASS_ELITE_KEY_LINE_LEN (17) +#define ICLASS_ELITE_KEY_LEN (8) + +struct IclassEliteDict { + Stream* stream; + uint32_t total_keys; +}; + +bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool dict_present = false; + if(dict_type == IclassEliteDictTypeFlipper) { + dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) == + FSE_OK; + } else if(dict_type == IclassEliteDictTypeUser) { + dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK; + } + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { + IclassEliteDict* dict = malloc(sizeof(IclassEliteDict)); + Storage* storage = furi_record_open(RECORD_STORAGE); + dict->stream = buffered_file_stream_alloc(storage); + furi_record_close(RECORD_STORAGE); + FuriString* next_line = furi_string_alloc(); + + bool dict_loaded = false; + do { + if(dict_type == IclassEliteDictTypeFlipper) { + if(!buffered_file_stream_open( + dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(dict->stream); + break; + } + } else if(dict_type == IclassEliteDictTypeUser) { + if(!buffered_file_stream_open( + dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { + buffered_file_stream_close(dict->stream); + break; + } + } + + // Read total amount of keys + while(true) { + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; + dict->total_keys++; + } + furi_string_reset(next_line); + stream_rewind(dict->stream); + + dict_loaded = true; + FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); + } while(false); + + if(!dict_loaded) { + buffered_file_stream_close(dict->stream); + free(dict); + dict = NULL; + } + + furi_string_free(next_line); + + return dict; +} + +void iclass_elite_dict_free(IclassEliteDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + buffered_file_stream_close(dict->stream); + stream_free(dict->stream); + free(dict); +} + +uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) { + furi_assert(dict); + + return dict->total_keys; +} + +bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + uint8_t key_byte_tmp = 0; + FuriString* next_line = furi_string_alloc(); + + bool key_read = false; + *key = 0ULL; + while(!key_read) { + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; + for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) { + args_char_to_hex( + furi_string_get_char(next_line, i), + furi_string_get_char(next_line, i + 1), + &key_byte_tmp); + key[i / 2] = key_byte_tmp; + } + key_read = true; + } + + furi_string_free(next_line); + return key_read; +} + +bool iclass_elite_dict_rewind(IclassEliteDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + return stream_rewind(dict->stream); +} + +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + FuriString* key_str = furi_string_alloc(); + for(size_t i = 0; i < 6; i++) { + furi_string_cat_printf(key_str, "%02X", key[i]); + } + furi_string_cat_printf(key_str, "\n"); + + bool key_added = false; + do { + if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; + if(!stream_insert_string(dict->stream, key_str)) break; + key_added = true; + } while(false); + + furi_string_free(key_str); + return key_added; +} \ No newline at end of file diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.h b/applications/plugins/picopass/helpers/iclass_elite_dict.h new file mode 100644 index 000000000..e5ec8dfcb --- /dev/null +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + IclassEliteDictTypeUser, + IclassEliteDictTypeFlipper, +} IclassEliteDictType; + +typedef struct IclassEliteDict IclassEliteDict; + +bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type); + +IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type); + +void iclass_elite_dict_free(IclassEliteDict* dict); + +uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict); + +bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key); + +bool iclass_elite_dict_rewind(IclassEliteDict* dict); + +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); \ No newline at end of file diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.c b/applications/plugins/picopass/lib/loclass/optimized_elite.c index c175f3986..34e987060 100644 --- a/applications/plugins/picopass/lib/loclass/optimized_elite.c +++ b/applications/plugins/picopass/lib/loclass/optimized_elite.c @@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 * @param loclass_hash1 loclass_hash1 * @param key_sel output key_sel=h[loclass_hash1[i]] */ -void hash2(uint8_t* key64, uint8_t* outp_keytable) { +void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { /** *Expected: * High Security Key Table diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h index ed6bb4781..26f215941 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -9,6 +9,7 @@ #include "rfal_picopass.h" #include #include +#include "helpers/iclass_elite_dict.h" #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 @@ -49,6 +50,7 @@ typedef struct { bool se_enabled; bool sio; bool biometrics; + uint8_t key[8]; uint8_t pin_length; PicopassEncryption encryption; uint8_t credential[8]; diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index 532effd9a..2c9b1a0ce 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -1,5 +1,7 @@ #include "picopass_worker_i.h" +#include + #define TAG "PicopassWorker" const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; @@ -176,7 +178,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { return ERR_NONE; } -ReturnCode picopass_read_card(PicopassBlock* AA1) { +ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { rfalPicoPassReadCheckRes rcRes; rfalPicoPassCheckRes chkRes; @@ -197,10 +199,68 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { loclass_opt_doReaderMAC(ccnr, div_key, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return err; + if(err == ERR_NONE) { + return ERR_NONE; } + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + + FURI_LOG_E(TAG, "Starting dictionary attack"); + + size_t index = 0; + uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + + if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) { + FURI_LOG_E(TAG, "Dictionary not found"); + return ERR_PARAM; + } + + IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Dictionary not allocated"); + return ERR_PARAM; + } + + FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict)); + while(iclass_elite_dict_get_next_key(dict, key)) { + FURI_LOG_D( + TAG, + "Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x", + index++, + key[0], + key[1], + key[2], + key[3], + key[4], + key[5], + key[6], + key[7]); + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err == ERR_NONE) { + memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + break; + } + } + + if(dict) { + iclass_elite_dict_free(dict); + } + + return err; +} + +ReturnCode picopass_read_card(PicopassBlock* AA1) { + ReturnCode err; size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : @@ -352,28 +412,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); if(pacs->se_enabled) { FURI_LOG_D(TAG, "SE enabled"); + nextState = PicopassWorkerEventFail; } - err = picopass_read_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - nextState = PicopassWorkerEventFail; + if(nextState == PicopassWorkerEventSuccess) { + err = picopass_auth(AA1, pacs); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_try_auth error %d", err); + nextState = PicopassWorkerEventFail; + } + } + + if(nextState == PicopassWorkerEventSuccess) { + err = picopass_read_card(AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_card error %d", err); + nextState = PicopassWorkerEventFail; + } } if(nextState == PicopassWorkerEventSuccess) { err = picopass_device_parse_credential(AA1, pacs); - } - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); - nextState = PicopassWorkerEventFail; + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); + nextState = PicopassWorkerEventFail; + } } if(nextState == PicopassWorkerEventSuccess) { err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); - } - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); - nextState = PicopassWorkerEventFail; + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); + nextState = PicopassWorkerEventFail; + } } // Notify caller and exit diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index 9b215fb74..101d4d8f2 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/applications/plugins/picopass/picopass_worker_i.h @@ -18,6 +18,7 @@ struct PicopassWorker { FuriThread* thread; Storage* storage; + Stream* dict_stream; PicopassDeviceData* dev_data; PicopassWorkerCallback callback; diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index 37f1db4f2..bb170ac45 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback( void picopass_scene_read_card_success_on_enter(void* context) { Picopass* picopass = context; - FuriString* credential_str; - FuriString* wiegand_str; - FuriString* sio_str; - credential_str = furi_string_alloc(); - wiegand_str = furi_string_alloc(); - sio_str = furi_string_alloc(); + FuriString* csn_str = furi_string_alloc_set("CSN:"); + FuriString* credential_str = furi_string_alloc(); + FuriString* wiegand_str = furi_string_alloc(); + FuriString* sio_str = furi_string_alloc(); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) { notification_message(picopass->notifications, &sequence_success); // Setup view + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - if(pacs->record.bitLength == 0) { + uint8_t csn[PICOPASS_BLOCK_LEN]; + memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(csn_str, " %02X", csn[i]); + } + + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { furi_string_cat_printf(wiegand_str, "Read Failed"); if(pacs->se_enabled) { @@ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) { } widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, - 32, + 36, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(credential_str)); widget_add_string_element( - widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); furi_string_free(sio_str); From 72713d6f4e8525d9550285bd6c57c30d789a1dc1 Mon Sep 17 00:00:00 2001 From: Kevin Kwok Date: Tue, 18 Oct 2022 08:06:18 -0700 Subject: [PATCH 4/4] Allow pins 0 and 1 as RTS/DTR for USB UART Bridge (#1864) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow pins 0 and 1 as RTS/DTR for USB UART Bridge * add logic to gpio_scene_usb_uart_config, fix flow_pins * fixing count of pins * disable PC0,PC1 RTS/DTR when using LPUART * add logic to ensure flow pins dont overlap with uart lines Co-authored-by: あく --- applications/main/gpio/gpio_app_i.h | 1 + .../gpio/scenes/gpio_scene_usb_uart_config.c | 26 +++++++++++++++++-- applications/main/gpio/usb_uart_bridge.c | 1 + .../services/gui/modules/variable_item_list.c | 4 +++ .../services/gui/modules/variable_item_list.h | 7 +++++ 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index fa91e8f79..fff35c95a 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -24,6 +24,7 @@ struct GpioApp { Widget* widget; VariableItemList* var_item_list; + VariableItem* var_item_flow; GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; UsbUartBridge* usb_uart_bridge; diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 653a306aa..c114d79a2 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -13,7 +13,7 @@ static UsbUartConfig* cfg_set; static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* uart_ch[] = {"13,14", "15,16"}; -static const char* flow_pins[] = {"None", "2,3", "6,7"}; +static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; static const char* baudrate_mode[] = {"Host"}; static const uint32_t baudrate_list[] = { 2400, @@ -33,6 +33,24 @@ bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { return false; } +void line_ensure_flow_invariant(GpioApp* app) { + // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is + // selected. This function enforces that invariant by resetting flow_pins + // to None if it is configured to 16,15 when LPUART is selected. + + uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + VariableItem* item = app->var_item_flow; + variable_item_set_values_count(item, available_flow_pins); + + if(cfg_set->flow_pins >= available_flow_pins) { + cfg_set->flow_pins = 0; + usb_uart_set_config(app->usb_uart_bridge, cfg_set); + + variable_item_set_current_value_index(item, cfg_set->flow_pins); + variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + } +} + static void line_vcp_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -54,6 +72,7 @@ static void line_port_cb(VariableItem* item) { else if(index == 1) cfg_set->uart_ch = FuriHalUartIdLPUART1; usb_uart_set_config(app->usb_uart_bridge, cfg_set); + line_ensure_flow_invariant(app); } static void line_flow_cb(VariableItem* item) { @@ -116,9 +135,12 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) { variable_item_set_current_value_index(item, cfg_set->uart_ch); variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); - item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app); + item = variable_item_list_add( + var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + app->var_item_flow = item; + line_ensure_flow_invariant(app); variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 6e0bce736..a5caceafa 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -14,6 +14,7 @@ static const GpioPin* flow_pins[][2] = { {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7 + {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15 }; typedef enum { diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index acd46ee4b..a9b89d63b 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -396,6 +396,10 @@ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_v item->current_value_index = current_value_index; } +void variable_item_set_values_count(VariableItem* item, uint8_t values_count) { + item->values_count = values_count; +} + void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) { furi_string_set(item->current_value_text, current_value_text); } diff --git a/applications/services/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h index 78c7769c5..db2a58993 100644 --- a/applications/services/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -81,6 +81,13 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it */ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index); +/** Set number of values for item + * + * @param item VariableItem* instance + * @param values_count The new values count + */ +void variable_item_set_values_count(VariableItem* item, uint8_t values_count); + /** Set item current selected text * * @param item VariableItem* instance