diff --git a/applications/debug/unit_tests/tests/subghz/subghz_test.c b/applications/debug/unit_tests/tests/subghz/subghz_test.c index abbcbd2b0..572291006 100644 --- a/applications/debug/unit_tests/tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/tests/subghz/subghz_test.c @@ -17,7 +17,7 @@ #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 329 +#define TEST_RANDOM_COUNT_PARSE 331 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; diff --git a/applications/system/hid_app/views/hid_mouse_clicker.c b/applications/system/hid_app/views/hid_mouse_clicker.c index 491f9962b..e9fd9170d 100644 --- a/applications/system/hid_app/views/hid_mouse_clicker.c +++ b/applications/system/hid_app/views/hid_mouse_clicker.c @@ -19,6 +19,7 @@ typedef struct { bool connected; bool running; int rate; + enum HidMouseButtons btn; } HidMouseClickerModel; static void hid_mouse_clicker_start_or_restart_timer(void* context) { @@ -61,6 +62,26 @@ static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { // Ok canvas_draw_icon(canvas, 58, 25, &I_Space_65x18); + canvas_draw_icon(canvas, 61, 50, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 117, 50, &I_ButtonRight_4x7); + + const char* btn_label; + switch(model->btn) { + case HID_MOUSE_BTN_LEFT: + btn_label = "Left"; + break; + case HID_MOUSE_BTN_WHEEL: + btn_label = "Middle"; + break; + case HID_MOUSE_BTN_RIGHT: + btn_label = "Right"; + break; + default: + furi_crash(); + } + + elements_multiline_text_aligned(canvas, 89, 57, AlignCenter, AlignBottom, btn_label); + if(model->running) { elements_slightly_rounded_box(canvas, 61, 27, 60, 13); canvas_set_color(canvas, ColorWhite); @@ -100,8 +121,8 @@ static void hid_mouse_clicker_timer_callback(void* context) { HidMouseClickerModel * model, { if(model->running) { - hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); - hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_mouse_clicker->hid, model->btn); + hid_hal_mouse_release(hid_mouse_clicker->hid, model->btn); } }, false); @@ -154,6 +175,34 @@ static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { case InputKeyBack: model->running = false; break; + case InputKeyLeft: + switch(model->btn) { + case HID_MOUSE_BTN_LEFT: + model->btn = HID_MOUSE_BTN_RIGHT; + break; + case HID_MOUSE_BTN_WHEEL: + model->btn = HID_MOUSE_BTN_LEFT; + break; + case HID_MOUSE_BTN_RIGHT: + model->btn = HID_MOUSE_BTN_WHEEL; + break; + } + consumed = true; + break; + case InputKeyRight: + switch(model->btn) { + case HID_MOUSE_BTN_LEFT: + model->btn = HID_MOUSE_BTN_WHEEL; + break; + case HID_MOUSE_BTN_WHEEL: + model->btn = HID_MOUSE_BTN_RIGHT; + break; + case HID_MOUSE_BTN_RIGHT: + model->btn = HID_MOUSE_BTN_LEFT; + break; + } + consumed = true; + break; default: consumed = true; break; @@ -188,7 +237,10 @@ HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { with_view_model( hid_mouse_clicker->view, HidMouseClickerModel * model, - { model->rate = DEFAULT_CLICK_RATE; }, + { + model->rate = DEFAULT_CLICK_RATE; + model->btn = HID_MOUSE_BTN_LEFT; + }, true); return hid_mouse_clicker; diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index 90e65b282..e843ef70c 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -9,6 +9,9 @@ #define NFC_MAX_BUFFER_SIZE (256) +#define NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC (512 * 64) +#define NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC (256 * 64) + typedef enum { NfcStateIdle, NfcStateRunning, @@ -661,4 +664,20 @@ NfcError nfc_felica_listener_set_sensf_res_data( return nfc_process_hal_error(error); } +void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) { + furi_check(instance); + + furi_hal_nfc_timer_block_tx_start( + NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC + + target_time_slot * NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC); +} + +void nfc_felica_listener_timer_anticol_stop(Nfc* instance) { + furi_check(instance); + + if(furi_hal_nfc_timer_block_tx_is_running()) { + furi_hal_nfc_timer_block_tx_stop(); + } +} + #endif // FW_CFG_unit_tests diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index 8fbf90d1f..bbbaf306b 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -380,6 +380,24 @@ NfcError nfc_felica_listener_set_sensf_res_data( */ NfcError nfc_iso15693_listener_tx_sof(Nfc* instance); +/** + * @brief Start the timer used for manual FeliCa collision resolution in listener mode. + * + * This blocks TX until the desired Time Slot, and should be called as soon as the listener + * determines that a collision resolution needs to be handled manually. + * + * @param[in, out] instance instance pointer to the instance to be configured. + * @param[in] target_time_slot Target Time Slot number. Should be a value within the range of 0-15 (double-inclusive). + */ +void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot); + +/** + * @brief Cancel the timer used for manual FeliCa collision resolution in listener mode. + * + * @param[in, out] instance instance pointer to the instance to be configured. + */ +void nfc_felica_listener_timer_anticol_stop(Nfc* instance); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/nfc_mock.c b/lib/nfc/nfc_mock.c index ee4bb09cc..717c93519 100644 --- a/lib/nfc/nfc_mock.c +++ b/lib/nfc/nfc_mock.c @@ -518,4 +518,14 @@ NfcError nfc_felica_listener_set_sensf_res_data( return NfcErrorNone; } +void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) { + furi_check(instance); + + UNUSED(target_time_slot); +} + +void nfc_felica_listener_timer_anticol_stop(Nfc* instance) { + furi_check(instance); +} + #endif diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h index 7b9c78e07..b4e7af9d1 100644 --- a/lib/nfc/protocols/felica/felica.h +++ b/lib/nfc/protocols/felica/felica.h @@ -38,7 +38,7 @@ extern "C" { #define FELICA_FDT_POLL_FC (10000U) #define FELICA_POLL_POLL_MIN_US (1280U) -#define FELICA_FDT_LISTEN_FC (1172) +#define FELICA_FDT_LISTEN_FC (0) #define FELICA_SYSTEM_CODE_CODE (0xFFFFU) #define FELICA_TIME_SLOT_1 (0x00U) @@ -58,6 +58,7 @@ typedef enum { FelicaErrorWrongCrc, FelicaErrorProtocol, FelicaErrorTimeout, + FelicaErrorFeatureUnsupported, } FelicaError; typedef struct { diff --git a/lib/nfc/protocols/felica/felica_listener.c b/lib/nfc/protocols/felica/felica_listener.c index 90954de97..3bf109366 100644 --- a/lib/nfc/protocols/felica/felica_listener.c +++ b/lib/nfc/protocols/felica/felica_listener.c @@ -5,9 +5,20 @@ #include #define FELICA_LISTENER_MAX_BUFFER_SIZE (128) +#define FELICA_LISTENER_CMD_POLLING (0x00U) +#define FELICA_LISTENER_RESPONSE_POLLING (0x01U) #define FELICA_LISTENER_RESPONSE_CODE_READ (0x07) #define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09) +#define FELICA_LISTENER_REQUEST_NONE (0x00U) +#define FELICA_LISTENER_REQUEST_SYSTEM_CODE (0x01U) +#define FELICA_LISTENER_REQUEST_PERFORMANCE (0x02U) + +#define FELICA_LISTENER_SYSTEM_CODE_NDEF (__builtin_bswap16(0x12FCU)) +#define FELICA_LISTENER_SYSTEM_CODE_LITES (__builtin_bswap16(0x88B4U)) + +#define FELICA_LISTENER_PERFORMANCE_VALUE (__builtin_bswap16(0x0083U)) + #define TAG "FelicaListener" FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) { @@ -151,6 +162,70 @@ static FelicaError felica_listener_process_request( } } +static void felica_listener_populate_polling_response_header( + FelicaListener* instance, + FelicaListenerPollingResponseHeader* resp) { + resp->idm = instance->data->idm; + resp->pmm = instance->data->pmm; + resp->response_code = FELICA_LISTENER_RESPONSE_POLLING; +} + +static bool felica_listener_check_system_code( + const FelicaListenerGenericRequest* const generic_request, + uint16_t code) { + return ( + generic_request->polling.system_code == code || + generic_request->polling.system_code == (code | 0x00FFU) || + generic_request->polling.system_code == (code | 0xFF00U)); +} + +static uint16_t felica_listener_get_response_system_code( + FelicaListener* instance, + const FelicaListenerGenericRequest* const generic_request) { + uint16_t resp_system_code = FELICA_SYSTEM_CODE_CODE; + if(felica_listener_check_system_code(generic_request, FELICA_LISTENER_SYSTEM_CODE_NDEF) && + instance->data->data.fs.mc.data[FELICA_MC_SYS_OP] == 1) { + // NDEF + resp_system_code = FELICA_LISTENER_SYSTEM_CODE_NDEF; + } else if(felica_listener_check_system_code( + generic_request, FELICA_LISTENER_SYSTEM_CODE_LITES)) { + // Lite-S + resp_system_code = FELICA_LISTENER_SYSTEM_CODE_LITES; + } + return resp_system_code; +} + +static FelicaError felica_listener_process_system_code( + FelicaListener* instance, + const FelicaListenerGenericRequest* const generic_request) { + FelicaError result = FelicaErrorFeatureUnsupported; + do { + uint16_t resp_system_code = + felica_listener_get_response_system_code(instance, generic_request); + if(resp_system_code == FELICA_SYSTEM_CODE_CODE) break; + + FelicaListenerPollingResponse* resp = malloc(sizeof(FelicaListenerPollingResponse)); + felica_listener_populate_polling_response_header(instance, &resp->header); + + resp->header.length = sizeof(FelicaListenerPollingResponse); + if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_SYSTEM_CODE) { + resp->optional_request_data = resp_system_code; + } else if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_PERFORMANCE) { + resp->optional_request_data = FELICA_LISTENER_PERFORMANCE_VALUE; + } else { + resp->header.length = sizeof(FelicaListenerPollingResponseHeader); + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length); + free(resp); + + result = felica_listener_frame_exchange(instance, instance->tx_buffer); + } while(false); + + return result; +} + NfcCommand felica_listener_run(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.protocol == NfcProtocolInvalid); @@ -187,7 +262,23 @@ NfcCommand felica_listener_run(NfcGenericEvent event, void* context) { break; } - if(!felica_listener_check_idm(instance, &request->header.idm)) { + if(request->header.code == FELICA_LISTENER_CMD_POLLING) { + // Will always respond at Time Slot 0 for now. + nfc_felica_listener_timer_anticol_start(instance->nfc, 0); + if(request->polling.system_code != FELICA_SYSTEM_CODE_CODE) { + FelicaError error = felica_listener_process_system_code(instance, request); + if(error == FelicaErrorFeatureUnsupported) { + command = NfcCommandReset; + } else if(error != FelicaErrorNone) { + FURI_LOG_E( + TAG, "Error when handling Polling with System Code: %2X", error); + } + break; + } else { + FURI_LOG_E(TAG, "Hardware Polling command leaking through"); + break; + } + } else if(!felica_listener_check_idm(instance, &request->header.idm)) { FURI_LOG_E(TAG, "Wrong IDm"); break; } diff --git a/lib/nfc/protocols/felica/felica_listener_i.c b/lib/nfc/protocols/felica/felica_listener_i.c index b3ae24635..d69c8c66a 100644 --- a/lib/nfc/protocols/felica/felica_listener_i.c +++ b/lib/nfc/protocols/felica/felica_listener_i.c @@ -7,19 +7,6 @@ #define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U) #define FELICA_WCNT_MC2_00_WARNING_END_VALUE (0x00FFFDFFU) -#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U) -#define FELICA_MC_ALL_BYTE (2U) -#define FELICA_MC_SYS_OP (3U) -#define FELICA_MC_RF_PRM (4U) -#define FELICA_MC_CKCKV_W_MAC_A (5U) -#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U) -#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U) -#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U) -#define FELICA_MC_STATE_W_MAC_A (12U) -#define FELICA_MC_RESERVED_13 (13U) -#define FELICA_MC_RESERVED_14 (14U) -#define FELICA_MC_RESERVED_15 (15U) - #define FELICA_MC_BYTE_GET(data, byte) (data->data.fs.mc.data[byte]) #define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00) #define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF) diff --git a/lib/nfc/protocols/felica/felica_listener_i.h b/lib/nfc/protocols/felica/felica_listener_i.h index 0ea144249..f64a598cc 100644 --- a/lib/nfc/protocols/felica/felica_listener_i.h +++ b/lib/nfc/protocols/felica/felica_listener_i.h @@ -7,15 +7,50 @@ #define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U) #define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U) +#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U) +#define FELICA_MC_ALL_BYTE (2U) +#define FELICA_MC_SYS_OP (3U) +#define FELICA_MC_RF_PRM (4U) +#define FELICA_MC_CKCKV_W_MAC_A (5U) +#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U) +#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U) +#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U) +#define FELICA_MC_STATE_W_MAC_A (12U) +#define FELICA_MC_RESERVED_13 (13U) +#define FELICA_MC_RESERVED_14 (14U) +#define FELICA_MC_RESERVED_15 (15U) + typedef enum { Felica_ListenerStateIdle, Felica_ListenerStateActivated, } FelicaListenerState; +typedef struct FURI_PACKED { + uint8_t code; + uint16_t system_code; + uint8_t request_code; + uint8_t time_slot; +} FelicaListenerPollingHeader; + +typedef struct { + uint8_t length; + uint8_t response_code; + FelicaIDm idm; + FelicaPMm pmm; +} FelicaListenerPollingResponseHeader; + +typedef struct FURI_PACKED { + FelicaListenerPollingResponseHeader header; + uint16_t optional_request_data; +} FelicaListenerPollingResponse; + /** Generic Felica request same for both read and write operations. */ typedef struct { uint8_t length; - FelicaCommandHeader header; + union { + FelicaCommandHeader header; + FelicaListenerPollingHeader polling; + }; } FelicaListenerGenericRequest; /** Generic request but with list of requested elements. */ diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 2f1b0646e..ec711acc8 100755 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,86.0,, +Version,+,86.1,, 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,, @@ -2931,6 +2931,8 @@ Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceDat Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t, const uint16_t" +Function,+,nfc_felica_listener_timer_anticol_start,void,"Nfc*, uint8_t" +Function,+,nfc_felica_listener_timer_anticol_stop,void,Nfc* Function,+,nfc_free,void,Nfc* Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*" diff --git a/targets/f7/furi_hal/furi_hal_nfc_felica.c b/targets/f7/furi_hal/furi_hal_nfc_felica.c index 1267b7b13..49c59eb79 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_felica.c +++ b/targets/f7/furi_hal/furi_hal_nfc_felica.c @@ -1,8 +1,7 @@ #include "furi_hal_nfc_i.h" #include "furi_hal_nfc_tech_i.h" -// Prevent FDT timer from starting -#define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (INT32_MAX) +#define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (0) #define FURI_HAL_FELICA_COMMUNICATION_PERFORMANCE (0x0083U) #define FURI_HAL_FELICA_RESPONSE_CODE (0x01)