mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-12 21:28:36 -07:00
Merge branch 'dev' into zlo/allocator-playground
This commit is contained in:
@@ -177,6 +177,14 @@ static bool subghz_device_cc1101_ext_check_init(void) {
|
|||||||
furi_hal_gpio_init(
|
furi_hal_gpio_init(
|
||||||
subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||||
|
|
||||||
|
// Reset GDO2 (!TX/RX) to floating state
|
||||||
|
cc1101_status = cc1101_write_reg(
|
||||||
|
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);
|
||||||
|
if(cc1101_status.CHIP_RDYn != 0) {
|
||||||
|
//timeout or error
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Go to sleep
|
// Go to sleep
|
||||||
cc1101_status = cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle);
|
cc1101_status = cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle);
|
||||||
if(cc1101_status.CHIP_RDYn != 0) {
|
if(cc1101_status.CHIP_RDYn != 0) {
|
||||||
@@ -385,6 +393,9 @@ void subghz_device_cc1101_ext_reset(void) {
|
|||||||
// Warning: push pull cc1101 clock output on GD0
|
// Warning: push pull cc1101 clock output on GD0
|
||||||
cc1101_write_reg(
|
cc1101_write_reg(
|
||||||
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);
|
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);
|
||||||
|
// Reset GDO2 (!TX/RX) to floating state
|
||||||
|
cc1101_write_reg(
|
||||||
|
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);
|
||||||
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +405,9 @@ void subghz_device_cc1101_ext_idle(void) {
|
|||||||
//waiting for the chip to switch to IDLE mode
|
//waiting for the chip to switch to IDLE mode
|
||||||
furi_check(cc1101_wait_status_state(
|
furi_check(cc1101_wait_status_state(
|
||||||
subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000));
|
subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000));
|
||||||
|
// Reset GDO2 (!TX/RX) to floating state
|
||||||
|
cc1101_write_reg(
|
||||||
|
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHighImpedance);
|
||||||
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +417,10 @@ void subghz_device_cc1101_ext_rx(void) {
|
|||||||
//waiting for the chip to switch to Rx mode
|
//waiting for the chip to switch to Rx mode
|
||||||
furi_check(
|
furi_check(
|
||||||
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000));
|
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000));
|
||||||
|
// Go GDO2 (!TX/RX) to high (RX state)
|
||||||
|
cc1101_write_reg(
|
||||||
|
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV);
|
||||||
|
|
||||||
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,6 +431,8 @@ bool subghz_device_cc1101_ext_tx(void) {
|
|||||||
//waiting for the chip to switch to Tx mode
|
//waiting for the chip to switch to Tx mode
|
||||||
furi_check(
|
furi_check(
|
||||||
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000));
|
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000));
|
||||||
|
// Go GDO2 (!TX/RX) to low (TX state)
|
||||||
|
cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW);
|
||||||
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
21
applications/main/nfc/helpers/felica_auth.c
Normal file
21
applications/main/nfc/helpers/felica_auth.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "felica_auth.h"
|
||||||
|
|
||||||
|
FelicaAuthenticationContext* felica_auth_alloc() {
|
||||||
|
FelicaAuthenticationContext* instance = malloc(sizeof(FelicaAuthenticationContext));
|
||||||
|
memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
instance->skip_auth = true;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_auth_free(FelicaAuthenticationContext* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_auth_reset(FelicaAuthenticationContext* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
instance->skip_auth = true;
|
||||||
|
instance->auth_status.external = 0;
|
||||||
|
instance->auth_status.internal = 0;
|
||||||
|
}
|
||||||
17
applications/main/nfc/helpers/felica_auth.h
Normal file
17
applications/main/nfc/helpers/felica_auth.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lib/nfc/protocols/felica/felica.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FelicaAuthenticationContext* felica_auth_alloc();
|
||||||
|
|
||||||
|
void felica_auth_free(FelicaAuthenticationContext* instance);
|
||||||
|
|
||||||
|
void felica_auth_reset(FelicaAuthenticationContext* instance);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
#include "../nfc_protocol_support_common.h"
|
#include "../nfc_protocol_support_common.h"
|
||||||
#include "../nfc_protocol_support_gui_common.h"
|
#include "../nfc_protocol_support_gui_common.h"
|
||||||
|
#include "../nfc_protocol_support_unlock_helper.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||||
|
};
|
||||||
|
|
||||||
static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
||||||
const NfcDevice* device = instance->nfc_device;
|
const NfcDevice* device = instance->nfc_device;
|
||||||
@@ -18,6 +23,35 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
|||||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);
|
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeRight,
|
||||||
|
"More",
|
||||||
|
nfc_protocol_support_common_widget_callback,
|
||||||
|
instance);
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||||
|
if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
|
||||||
|
nfc_render_felica_dump(data, temp_str);
|
||||||
|
|
||||||
widget_add_text_scroll_element(
|
widget_add_text_scroll_element(
|
||||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
@@ -29,29 +63,75 @@ static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, v
|
|||||||
|
|
||||||
NfcApp* instance = context;
|
NfcApp* instance = context;
|
||||||
const FelicaPollerEvent* felica_event = event.event_data;
|
const FelicaPollerEvent* felica_event = event.event_data;
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
if(felica_event->type == FelicaPollerEventTypeReady) {
|
if(felica_event->type == FelicaPollerEventTypeReady) {
|
||||||
nfc_device_set_data(
|
nfc_device_set_data(
|
||||||
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||||
return NfcCommandStop;
|
command = NfcCommandStop;
|
||||||
|
} else if(
|
||||||
|
felica_event->type == FelicaPollerEventTypeError ||
|
||||||
|
felica_event->type == FelicaPollerEventTypeIncomplete) {
|
||||||
|
nfc_device_set_data(
|
||||||
|
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventPollerIncomplete);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||||
|
FelicaAuthenticationContext* ctx = felica_event->data->auth_context;
|
||||||
|
ctx->skip_auth = instance->felica_auth->skip_auth;
|
||||||
|
memcpy(ctx->card_key.data, instance->felica_auth->card_key.data, FELICA_DATA_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NfcCommandContinue;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfc_scene_read_on_enter_felica(NfcApp* instance) {
|
static void nfc_scene_read_on_enter_felica(NfcApp* instance) {
|
||||||
|
nfc_unlock_helper_setup_from_state(instance);
|
||||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance);
|
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_read_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventCardDetected) {
|
||||||
|
nfc_unlock_helper_card_detected_handler(instance);
|
||||||
|
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
||||||
|
notification_message(instance->notifications, &sequence_semi_success);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
||||||
const NfcDevice* device = instance->nfc_device;
|
const NfcDevice* device = instance->nfc_device;
|
||||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||||
|
|
||||||
FuriString* temp_str = furi_string_alloc();
|
FuriString* temp_str = furi_string_alloc();
|
||||||
furi_string_cat_printf(
|
|
||||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn)) {
|
||||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
furi_string_cat_printf(
|
||||||
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
|
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||||
|
} else {
|
||||||
|
bool all_unlocked = data->blocks_read == data->blocks_total;
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str,
|
||||||
|
"\e#%s\n",
|
||||||
|
all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked");
|
||||||
|
nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str);
|
||||||
|
uint8_t* ck_data = instance->felica_auth->card_key.data;
|
||||||
|
furi_string_cat_printf(temp_str, "Key:");
|
||||||
|
for(uint8_t i = 0; i < 7; i++) {
|
||||||
|
furi_string_cat_printf(temp_str, " %02X", ck_data[i]);
|
||||||
|
if(i == 6) furi_string_cat_printf(temp_str, "...");
|
||||||
|
}
|
||||||
|
nfc_render_felica_blocks_count(data, temp_str, false);
|
||||||
|
}
|
||||||
|
felica_auth_reset(instance->felica_auth);
|
||||||
|
|
||||||
widget_add_text_scroll_element(
|
widget_add_text_scroll_element(
|
||||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||||
@@ -74,23 +154,50 @@ static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
|
|||||||
nfc_listener_start(instance->listener, NULL, NULL);
|
nfc_listener_start(instance->listener, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_read_menu_on_enter_felica(NfcApp* instance) {
|
||||||
|
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
|
||||||
|
if(data->blocks_read != data->blocks_total) {
|
||||||
|
submenu_add_item(
|
||||||
|
instance->submenu,
|
||||||
|
"Unlock",
|
||||||
|
SubmenuIndexUnlock,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubmenuIndexUnlock) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaKeyInput);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_felica = {
|
const NfcProtocolSupportBase nfc_protocol_support_felica = {
|
||||||
.features = NfcProtocolFeatureEmulateUid,
|
.features = NfcProtocolFeatureEmulateUid,
|
||||||
|
|
||||||
.scene_info =
|
.scene_info =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_scene_info_on_enter_felica,
|
.on_enter = nfc_scene_info_on_enter_felica,
|
||||||
|
.on_event = nfc_scene_info_on_event_felica,
|
||||||
|
},
|
||||||
|
.scene_more_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_more_info_on_enter_felica,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_read =
|
.scene_read =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_scene_read_on_enter_felica,
|
.on_enter = nfc_scene_read_on_enter_felica,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_scene_read_on_event_felica,
|
||||||
},
|
},
|
||||||
.scene_read_menu =
|
.scene_read_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_scene_read_menu_on_enter_felica,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_scene_read_menu_on_event_felica,
|
||||||
},
|
},
|
||||||
.scene_read_success =
|
.scene_read_success =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,19 +1,107 @@
|
|||||||
#include "felica_render.h"
|
#include "felica_render.h"
|
||||||
|
|
||||||
|
void nfc_render_felica_blocks_count(
|
||||||
|
const FelicaData* data,
|
||||||
|
FuriString* str,
|
||||||
|
bool render_auth_notification) {
|
||||||
|
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||||
|
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||||
|
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_render_felica_idm(
|
||||||
|
const FelicaData* data,
|
||||||
|
NfcProtocolFormatType format_type,
|
||||||
|
FuriString* str) {
|
||||||
|
furi_string_cat_printf(str, (format_type == NfcProtocolFormatTypeFull) ? "IDm:\n" : "IDm:");
|
||||||
|
|
||||||
|
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
(format_type == NfcProtocolFormatTypeFull) ? "%02X " : " %02X",
|
||||||
|
data->idm.data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void nfc_render_felica_info(
|
void nfc_render_felica_info(
|
||||||
const FelicaData* data,
|
const FelicaData* data,
|
||||||
NfcProtocolFormatType format_type,
|
NfcProtocolFormatType format_type,
|
||||||
FuriString* str) {
|
FuriString* str) {
|
||||||
furi_string_cat_printf(str, "IDm:");
|
if(format_type == NfcProtocolFormatTypeFull) {
|
||||||
|
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
||||||
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
|
|
||||||
furi_string_cat_printf(str, " %02X", data->idm.data[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nfc_render_felica_idm(data, format_type, str);
|
||||||
|
|
||||||
if(format_type == NfcProtocolFormatTypeFull) {
|
if(format_type == NfcProtocolFormatTypeFull) {
|
||||||
furi_string_cat_printf(str, "\nPMm:");
|
furi_string_cat_printf(str, "\nPMm:\n");
|
||||||
for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) {
|
for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) {
|
||||||
furi_string_cat_printf(str, " %02X", data->pmm.data[i]);
|
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nfc_render_felica_blocks_count(data, str, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_render_felica_block_name(
|
||||||
|
const char* name,
|
||||||
|
FuriString* str,
|
||||||
|
uint8_t prefix_separator_cnt,
|
||||||
|
uint8_t suffix_separator_cnt) {
|
||||||
|
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
|
||||||
|
furi_string_cat_printf(str, ":");
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(str, "[ %s ]", name);
|
||||||
|
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
|
||||||
|
furi_string_cat_printf(str, ":");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
||||||
|
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
||||||
|
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||||
|
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
|
||||||
|
furi_string_cat_printf(str, "%02X ", block->data[j]);
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(str, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_render_felica_block(
|
||||||
|
const FelicaBlock* block,
|
||||||
|
FuriString* str,
|
||||||
|
const char* name,
|
||||||
|
uint8_t prefix_separator_cnt,
|
||||||
|
uint8_t suffix_separator_cnt) {
|
||||||
|
nfc_render_felica_block_name(name, str, prefix_separator_cnt, suffix_separator_cnt);
|
||||||
|
nfc_render_felica_block_data(block, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||||
|
FuriString* name = furi_string_alloc();
|
||||||
|
for(size_t i = 0; i < 14; i++) {
|
||||||
|
furi_string_printf(name, "S_PAD%d", i);
|
||||||
|
uint8_t suf_cnt = 18;
|
||||||
|
if(i == 1) {
|
||||||
|
suf_cnt = 19;
|
||||||
|
} else if((i == 10) || (i == 12) || (i == 13)) {
|
||||||
|
suf_cnt = 16;
|
||||||
|
}
|
||||||
|
nfc_render_felica_block(
|
||||||
|
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
|
||||||
|
}
|
||||||
|
furi_string_free(name);
|
||||||
|
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
|
||||||
|
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
|
||||||
|
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
|
||||||
|
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
|
||||||
|
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
|
||||||
|
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
|
||||||
|
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
|
||||||
|
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
|
||||||
|
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
|
||||||
|
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
|
||||||
|
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
|
||||||
|
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
|
||||||
|
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||||
|
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,19 @@
|
|||||||
|
|
||||||
#include "../nfc_protocol_support_render_common.h"
|
#include "../nfc_protocol_support_render_common.h"
|
||||||
|
|
||||||
|
void nfc_render_felica_blocks_count(
|
||||||
|
const FelicaData* data,
|
||||||
|
FuriString* str,
|
||||||
|
bool render_auth_notification);
|
||||||
|
|
||||||
void nfc_render_felica_info(
|
void nfc_render_felica_info(
|
||||||
const FelicaData* data,
|
const FelicaData* data,
|
||||||
NfcProtocolFormatType format_type,
|
NfcProtocolFormatType format_type,
|
||||||
FuriString* str);
|
FuriString* str);
|
||||||
|
|
||||||
|
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
|
||||||
|
|
||||||
|
void nfc_render_felica_idm(
|
||||||
|
const FelicaData* data,
|
||||||
|
NfcProtocolFormatType format_type,
|
||||||
|
FuriString* str);
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "../nfc_protocol_support_common.h"
|
#include "../nfc_protocol_support_common.h"
|
||||||
#include "../nfc_protocol_support_gui_common.h"
|
#include "../nfc_protocol_support_gui_common.h"
|
||||||
|
#include "../nfc_protocol_support_unlock_helper.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||||
@@ -157,48 +158,15 @@ static NfcCommand
|
|||||||
return NfcCommandContinue;
|
return NfcCommandContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum {
|
|
||||||
NfcSceneMfUltralightReadMenuStateCardSearch,
|
|
||||||
NfcSceneMfUltralightReadMenuStateCardFound,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void nfc_scene_read_setup_view(NfcApp* instance) {
|
|
||||||
Popup* popup = instance->popup;
|
|
||||||
popup_reset(popup);
|
|
||||||
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
|
|
||||||
|
|
||||||
if(state == NfcSceneMfUltralightReadMenuStateCardSearch) {
|
|
||||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
|
||||||
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
|
||||||
popup_set_text(
|
|
||||||
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
|
||||||
} else {
|
|
||||||
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
|
|
||||||
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
|
|
||||||
}
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
|
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
|
||||||
bool unlocking =
|
nfc_unlock_helper_setup_from_state(instance);
|
||||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);
|
|
||||||
|
|
||||||
uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch :
|
|
||||||
NfcSceneMfUltralightReadMenuStateCardFound;
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
|
|
||||||
|
|
||||||
nfc_scene_read_setup_view(instance);
|
|
||||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
|
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == NfcCustomEventCardDetected) {
|
if(event.event == NfcCustomEventCardDetected) {
|
||||||
scene_manager_set_scene_state(
|
nfc_unlock_helper_card_detected_handler(instance);
|
||||||
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
|
|
||||||
nfc_scene_read_setup_view(instance);
|
|
||||||
} else if((event.event == NfcCustomEventPollerIncomplete)) {
|
} else if((event.event == NfcCustomEventPollerIncomplete)) {
|
||||||
notification_message(instance->notifications, &sequence_semi_success);
|
notification_message(instance->notifications, &sequence_semi_success);
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#include "nfc_protocol_support_unlock_helper.h"
|
||||||
|
|
||||||
|
static void nfc_scene_read_setup_view(NfcApp* instance) {
|
||||||
|
Popup* popup = instance->popup;
|
||||||
|
popup_reset(popup);
|
||||||
|
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
|
||||||
|
|
||||||
|
if(state == NfcSceneReadMenuStateCardSearch) {
|
||||||
|
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||||
|
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
||||||
|
popup_set_text(
|
||||||
|
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
||||||
|
} else {
|
||||||
|
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
|
||||||
|
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_unlock_helper_setup_from_state(NfcApp* instance) {
|
||||||
|
bool unlocking =
|
||||||
|
scene_manager_has_previous_scene(
|
||||||
|
instance->scene_manager, NfcSceneMfUltralightUnlockWarn) ||
|
||||||
|
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||||
|
|
||||||
|
uint32_t state = unlocking ? NfcSceneReadMenuStateCardSearch : NfcSceneReadMenuStateCardFound;
|
||||||
|
|
||||||
|
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
|
||||||
|
|
||||||
|
nfc_scene_read_setup_view(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_unlock_helper_card_detected_handler(NfcApp* instance) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager, NfcSceneRead, NfcSceneReadMenuStateCardFound);
|
||||||
|
nfc_scene_read_setup_view(instance);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include "nfc/nfc_app_i.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NfcSceneReadMenuStateCardSearch,
|
||||||
|
NfcSceneReadMenuStateCardFound,
|
||||||
|
} NfcSceneUnlockReadState;
|
||||||
|
|
||||||
|
void nfc_unlock_helper_setup_from_state(NfcApp* instance);
|
||||||
|
void nfc_unlock_helper_card_detected_handler(NfcApp* instance);
|
||||||
@@ -50,6 +50,7 @@ NfcApp* nfc_app_alloc(void) {
|
|||||||
|
|
||||||
instance->nfc = nfc_alloc();
|
instance->nfc = nfc_alloc();
|
||||||
|
|
||||||
|
instance->felica_auth = felica_auth_alloc();
|
||||||
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
||||||
instance->slix_unlock = slix_unlock_alloc();
|
instance->slix_unlock = slix_unlock_alloc();
|
||||||
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
||||||
@@ -141,6 +142,7 @@ void nfc_app_free(NfcApp* instance) {
|
|||||||
|
|
||||||
nfc_free(instance->nfc);
|
nfc_free(instance->nfc);
|
||||||
|
|
||||||
|
felica_auth_free(instance->felica_auth);
|
||||||
mf_ultralight_auth_free(instance->mf_ul_auth);
|
mf_ultralight_auth_free(instance->mf_ul_auth);
|
||||||
slix_unlock_free(instance->slix_unlock);
|
slix_unlock_free(instance->slix_unlock);
|
||||||
mf_classic_key_cache_free(instance->mfc_key_cache);
|
mf_classic_key_cache_free(instance->mfc_key_cache);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "helpers/mfkey32_logger.h"
|
#include "helpers/mfkey32_logger.h"
|
||||||
#include "helpers/mf_classic_key_cache.h"
|
#include "helpers/mf_classic_key_cache.h"
|
||||||
#include "helpers/nfc_supported_cards.h"
|
#include "helpers/nfc_supported_cards.h"
|
||||||
|
#include "helpers/felica_auth.h"
|
||||||
#include "helpers/slix_unlock.h"
|
#include "helpers/slix_unlock.h"
|
||||||
|
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
@@ -129,6 +130,7 @@ struct NfcApp {
|
|||||||
NfcScanner* scanner;
|
NfcScanner* scanner;
|
||||||
NfcListener* listener;
|
NfcListener* listener;
|
||||||
|
|
||||||
|
FelicaAuthenticationContext* felica_auth;
|
||||||
MfUltralightAuth* mf_ul_auth;
|
MfUltralightAuth* mf_ul_auth;
|
||||||
SlixUnlock* slix_unlock;
|
SlixUnlock* slix_unlock;
|
||||||
NfcMfClassicDictAttackContext nfc_dict_context;
|
NfcMfClassicDictAttackContext nfc_dict_context;
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
|||||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||||
ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)
|
ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)
|
||||||
|
ADD_SCENE(nfc, felica_key_input, FelicaKeyInput)
|
||||||
|
ADD_SCENE(nfc, felica_unlock_warn, FelicaUnlockWarn)
|
||||||
|
|
||||||
ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)
|
ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)
|
||||||
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
||||||
|
|||||||
46
applications/main/nfc/scenes/nfc_scene_felica_key_input.c
Normal file
46
applications/main/nfc/scenes/nfc_scene_felica_key_input.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_felica_key_input_byte_input_callback(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_key_input_on_enter(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
ByteInput* byte_input = nfc->byte_input;
|
||||||
|
byte_input_set_header_text(byte_input, "Enter key in hex");
|
||||||
|
byte_input_set_result_callback(
|
||||||
|
byte_input,
|
||||||
|
nfc_scene_felica_key_input_byte_input_callback,
|
||||||
|
NULL,
|
||||||
|
nfc,
|
||||||
|
nfc->felica_auth->card_key.data,
|
||||||
|
FELICA_DATA_BLOCK_SIZE);
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_felica_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
UNUSED(event);
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventByteInputDone) {
|
||||||
|
nfc->felica_auth->skip_auth = false;
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_key_input_on_exit(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||||
|
byte_input_set_header_text(nfc->byte_input, "");
|
||||||
|
}
|
||||||
59
applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c
Normal file
59
applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_felica_unlock_warn_dialog_callback(DialogExResult result, void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_unlock_warn_on_enter(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
const char* message = "Risky Action!";
|
||||||
|
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||||
|
dialog_ex_set_context(dialog_ex, nfc);
|
||||||
|
dialog_ex_set_result_callback(dialog_ex, nfc_scene_felica_unlock_warn_dialog_callback);
|
||||||
|
|
||||||
|
dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);
|
||||||
|
|
||||||
|
FuriString* str = furi_string_alloc();
|
||||||
|
furi_string_cat_printf(str, "Unlock with key: ");
|
||||||
|
for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++)
|
||||||
|
furi_string_cat_printf(str, "%02X ", nfc->felica_auth->card_key.data[i]);
|
||||||
|
furi_string_cat_printf(str, "?");
|
||||||
|
|
||||||
|
nfc_text_store_set(nfc, furi_string_get_cstr(str));
|
||||||
|
furi_string_free(str);
|
||||||
|
|
||||||
|
dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 12, AlignLeft, AlignTop);
|
||||||
|
|
||||||
|
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||||
|
dialog_ex_set_right_button_text(dialog_ex, "Unlock");
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
UNUSED(event);
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == DialogExResultRight) {
|
||||||
|
nfc->felica_auth->skip_auth = false;
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == DialogExResultLeft) {
|
||||||
|
scene_manager_previous_scene(nfc->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_unlock_warn_on_exit(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
dialog_ex_reset(nfc->dialog_ex);
|
||||||
|
nfc_text_store_clear(nfc);
|
||||||
|
}
|
||||||
@@ -32,10 +32,12 @@ static void desktop_loader_callback(const void* message, void* context) {
|
|||||||
Desktop* desktop = context;
|
Desktop* desktop = context;
|
||||||
const LoaderEvent* event = message;
|
const LoaderEvent* event = message;
|
||||||
|
|
||||||
if(event->type == LoaderEventTypeApplicationStarted) {
|
if(event->type == LoaderEventTypeApplicationBeforeLoad) {
|
||||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted);
|
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted);
|
||||||
furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk);
|
furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk);
|
||||||
} else if(event->type == LoaderEventTypeApplicationStopped) {
|
} else if(
|
||||||
|
event->type == LoaderEventTypeApplicationLoadFailed ||
|
||||||
|
event->type == LoaderEventTypeApplicationStopped) {
|
||||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished);
|
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,23 +62,18 @@ static void
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void desktop_scene_main_open_app_or_profile(Desktop* desktop, FavoriteApp* application) {
|
static void desktop_scene_main_open_app_or_profile(Desktop* desktop, FavoriteApp* application) {
|
||||||
bool load_ok = false;
|
|
||||||
if(strlen(application->name_or_path) > 0) {
|
if(strlen(application->name_or_path) > 0) {
|
||||||
if(loader_start(desktop->loader, application->name_or_path, NULL, NULL) ==
|
loader_start_detached_with_gui_error(desktop->loader, application->name_or_path, NULL);
|
||||||
LoaderStatusOk) {
|
} else {
|
||||||
load_ok = true;
|
loader_start_detached_with_gui_error(desktop->loader, "Passport", NULL);
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!load_ok) {
|
|
||||||
loader_start(desktop->loader, "Passport", NULL, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) {
|
static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) {
|
||||||
if(strlen(application->name_or_path) > 0) {
|
if(strlen(application->name_or_path) > 0) {
|
||||||
loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL);
|
loader_start_detached_with_gui_error(desktop->loader, application->name_or_path, NULL);
|
||||||
} else {
|
} else {
|
||||||
loader_start(desktop->loader, LOADER_APPLICATIONS_NAME, NULL, NULL);
|
loader_start_detached_with_gui_error(desktop->loader, LOADER_APPLICATIONS_NAME, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +136,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DesktopMainEventOpenPowerOff: {
|
case DesktopMainEventOpenPowerOff: {
|
||||||
loader_start(desktop->loader, "Power", "off", NULL);
|
loader_start_detached_with_gui_error(desktop->loader, "Power", "off");
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,13 +47,8 @@ LoaderStatus
|
|||||||
return result.value;
|
return result.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) {
|
static void
|
||||||
furi_check(loader);
|
loader_show_gui_error(LoaderStatus status, const char* name, FuriString* error_message) {
|
||||||
furi_check(name);
|
|
||||||
|
|
||||||
FuriString* error_message = furi_string_alloc();
|
|
||||||
LoaderStatus status = loader_start(loader, name, args, error_message);
|
|
||||||
|
|
||||||
if(status == LoaderStatusErrorUnknownApp &&
|
if(status == LoaderStatusErrorUnknownApp &&
|
||||||
loader_find_external_application_by_name(name) != NULL) {
|
loader_find_external_application_by_name(name) != NULL) {
|
||||||
// Special case for external apps
|
// Special case for external apps
|
||||||
@@ -86,11 +81,31 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const
|
|||||||
dialog_message_free(message);
|
dialog_message_free(message);
|
||||||
furi_record_close(RECORD_DIALOGS);
|
furi_record_close(RECORD_DIALOGS);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) {
|
||||||
|
furi_check(loader);
|
||||||
|
furi_check(name);
|
||||||
|
|
||||||
|
FuriString* error_message = furi_string_alloc();
|
||||||
|
LoaderStatus status = loader_start(loader, name, args, error_message);
|
||||||
|
loader_show_gui_error(status, name, error_message);
|
||||||
furi_string_free(error_message);
|
furi_string_free(error_message);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args) {
|
||||||
|
furi_check(loader);
|
||||||
|
furi_check(name);
|
||||||
|
|
||||||
|
LoaderMessage message = {
|
||||||
|
.type = LoaderMessageTypeStartByNameDetachedWithGuiError,
|
||||||
|
.start.name = strdup(name),
|
||||||
|
.start.args = args ? strdup(args) : NULL,
|
||||||
|
};
|
||||||
|
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
bool loader_lock(Loader* loader) {
|
bool loader_lock(Loader* loader) {
|
||||||
furi_check(loader);
|
furi_check(loader);
|
||||||
|
|
||||||
@@ -166,11 +181,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
|||||||
|
|
||||||
Loader* loader = context;
|
Loader* loader = context;
|
||||||
|
|
||||||
if(thread_state == FuriThreadStateRunning) {
|
if(thread_state == FuriThreadStateStopped) {
|
||||||
LoaderEvent event;
|
|
||||||
event.type = LoaderEventTypeApplicationStarted;
|
|
||||||
furi_pubsub_publish(loader->pubsub, &event);
|
|
||||||
} else if(thread_state == FuriThreadStateStopped) {
|
|
||||||
LoaderMessage message;
|
LoaderMessage message;
|
||||||
message.type = LoaderMessageTypeAppClosed;
|
message.type = LoaderMessageTypeAppClosed;
|
||||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||||
@@ -255,6 +266,9 @@ static void loader_start_internal_app(
|
|||||||
const FlipperInternalApplication* app,
|
const FlipperInternalApplication* app,
|
||||||
const char* args) {
|
const char* args) {
|
||||||
FURI_LOG_I(TAG, "Starting %s", app->name);
|
FURI_LOG_I(TAG, "Starting %s", app->name);
|
||||||
|
LoaderEvent event;
|
||||||
|
event.type = LoaderEventTypeApplicationBeforeLoad;
|
||||||
|
furi_pubsub_publish(loader->pubsub, &event);
|
||||||
|
|
||||||
// store args
|
// store args
|
||||||
furi_assert(loader->app.args == NULL);
|
furi_assert(loader->app.args == NULL);
|
||||||
@@ -309,6 +323,9 @@ static LoaderStatus loader_start_external_app(
|
|||||||
const char* args,
|
const char* args,
|
||||||
FuriString* error_message) {
|
FuriString* error_message) {
|
||||||
LoaderStatus status = loader_make_success_status(error_message);
|
LoaderStatus status = loader_make_success_status(error_message);
|
||||||
|
LoaderEvent event;
|
||||||
|
event.type = LoaderEventTypeApplicationBeforeLoad;
|
||||||
|
furi_pubsub_publish(loader->pubsub, &event);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
loader->app.fap = flipper_application_alloc(storage, firmware_api_interface);
|
loader->app.fap = flipper_application_alloc(storage, firmware_api_interface);
|
||||||
@@ -356,6 +373,8 @@ static LoaderStatus loader_start_external_app(
|
|||||||
if(status != LoaderStatusOk) {
|
if(status != LoaderStatusOk) {
|
||||||
flipper_application_free(loader->app.fap);
|
flipper_application_free(loader->app.fap);
|
||||||
loader->app.fap = NULL;
|
loader->app.fap = NULL;
|
||||||
|
event.type = LoaderEventTypeApplicationLoadFailed;
|
||||||
|
furi_pubsub_publish(loader->pubsub, &event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
@@ -528,6 +547,16 @@ int32_t loader_srv(void* p) {
|
|||||||
loader, message.start.name, message.start.args, message.start.error_message);
|
loader, message.start.name, message.start.args, message.start.error_message);
|
||||||
api_lock_unlock(message.api_lock);
|
api_lock_unlock(message.api_lock);
|
||||||
break;
|
break;
|
||||||
|
case LoaderMessageTypeStartByNameDetachedWithGuiError: {
|
||||||
|
FuriString* error_message = furi_string_alloc();
|
||||||
|
LoaderStatus status = loader_do_start_by_name(
|
||||||
|
loader, message.start.name, message.start.args, error_message);
|
||||||
|
loader_show_gui_error(status, message.start.name, error_message);
|
||||||
|
if(message.start.name) free((void*)message.start.name);
|
||||||
|
if(message.start.args) free((void*)message.start.args);
|
||||||
|
furi_string_free(error_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case LoaderMessageTypeShowMenu:
|
case LoaderMessageTypeShowMenu:
|
||||||
loader_do_menu_show(loader);
|
loader_do_menu_show(loader);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ typedef enum {
|
|||||||
} LoaderStatus;
|
} LoaderStatus;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LoaderEventTypeApplicationStarted,
|
LoaderEventTypeApplicationBeforeLoad,
|
||||||
|
LoaderEventTypeApplicationLoadFailed,
|
||||||
LoaderEventTypeApplicationStopped
|
LoaderEventTypeApplicationStopped
|
||||||
} LoaderEventType;
|
} LoaderEventType;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ typedef struct {
|
|||||||
* @param[in] name application name or id
|
* @param[in] name application name or id
|
||||||
* @param[in] args application arguments
|
* @param[in] args application arguments
|
||||||
* @param[out] error_message detailed error message, can be NULL
|
* @param[out] error_message detailed error message, can be NULL
|
||||||
* @return LoaderStatus
|
* @return LoaderStatus
|
||||||
*/
|
*/
|
||||||
LoaderStatus
|
LoaderStatus
|
||||||
loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message);
|
loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message);
|
||||||
@@ -42,11 +43,19 @@ LoaderStatus
|
|||||||
* @param[in] instance loader instance
|
* @param[in] instance loader instance
|
||||||
* @param[in] name application name or id
|
* @param[in] name application name or id
|
||||||
* @param[in] args application arguments
|
* @param[in] args application arguments
|
||||||
* @return LoaderStatus
|
* @return LoaderStatus
|
||||||
*/
|
*/
|
||||||
LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
|
LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @brief Start application detached with GUI error message
|
||||||
|
* @param[in] instance loader instance
|
||||||
|
* @param[in] name application name or id
|
||||||
|
* @param[in] args application arguments
|
||||||
|
*/
|
||||||
|
void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args);
|
||||||
|
|
||||||
|
/**
|
||||||
* @brief Lock application start
|
* @brief Lock application start
|
||||||
* @param[in] instance loader instance
|
* @param[in] instance loader instance
|
||||||
* @return true on success
|
* @return true on success
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ typedef enum {
|
|||||||
LoaderMessageTypeLock,
|
LoaderMessageTypeLock,
|
||||||
LoaderMessageTypeUnlock,
|
LoaderMessageTypeUnlock,
|
||||||
LoaderMessageTypeIsLocked,
|
LoaderMessageTypeIsLocked,
|
||||||
|
LoaderMessageTypeStartByNameDetachedWithGuiError,
|
||||||
} LoaderMessageType;
|
} LoaderMessageType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ The documentation is divided into several sections, with all of them accessible
|
|||||||
- @ref dev_tools - Hardware and software tools for all kinds of programming
|
- @ref dev_tools - Hardware and software tools for all kinds of programming
|
||||||
- @ref expansion - Additional modules to expand Flipper's consciousness
|
- @ref expansion - Additional modules to expand Flipper's consciousness
|
||||||
- @ref misc - Various useful pieces of information
|
- @ref misc - Various useful pieces of information
|
||||||
|
- @ref js - JS-based scripting engine documentation
|
||||||
|
|
||||||
Aside from the manually-written documentation files, there's also a few automatically-generated ones at the bottom of the sidebar:
|
Aside from the manually-written documentation files, there's also a few automatically-generated ones at the bottom of the sidebar:
|
||||||
|
|
||||||
|
|||||||
18
documentation/doxygen/js.dox
Normal file
18
documentation/doxygen/js.dox
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
@page js JavaScript
|
||||||
|
|
||||||
|
This page contains some information on the Flipper Zero scripting engine, which is based on a modified mJS library
|
||||||
|
|
||||||
|
- [Brief mJS description](https://github.com/cesanta/mjs/blob/master/README.md)
|
||||||
|
- @subpage js_data_types
|
||||||
|
- @subpage js_builtin
|
||||||
|
|
||||||
|
JavaScript Modules
|
||||||
|
JS modules use the Flipper app plugin system. Each module is compiled into a .fal library file and is located on a microSD card. Here is a list of implemented modules:
|
||||||
|
|
||||||
|
- @subpage js_badusb - BadUSB module
|
||||||
|
- @subpage js_serial - Serial module
|
||||||
|
- @subpage js_dialog - Dialog module
|
||||||
|
- @subpage js_notification - Notifications module
|
||||||
|
|
||||||
|
*/
|
||||||
144
documentation/js/js_badusb.md
Normal file
144
documentation/js/js_badusb.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# js_badusb {#js_badusb}
|
||||||
|
|
||||||
|
# BadUSB module
|
||||||
|
```js
|
||||||
|
let badusb = require("badusb");
|
||||||
|
```
|
||||||
|
# Methods
|
||||||
|
## setup
|
||||||
|
Start USB HID with optional parameters. Should be called before all other methods.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
Configuration object (optional):
|
||||||
|
- vid, pid (number): VID and PID values, both are mandatory
|
||||||
|
- mfr_name (string): Manufacturer name (32 ASCII characters max), optional
|
||||||
|
- prod_name (string): Product name (32 ASCII characters max), optional
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
// Start USB HID with default parameters
|
||||||
|
badusb.setup();
|
||||||
|
// Start USB HID with custom vid:pid = AAAA:BBBB, manufacturer and product strings not defined
|
||||||
|
badusb.setup({ vid: 0xAAAA, pid: 0xBBBB });
|
||||||
|
// Start USB HID with custom vid:pid = AAAA:BBBB, manufacturer string = "Flipper Devices", product string = "Flipper Zero"
|
||||||
|
badusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfr_name: "Flipper Devices", prod_name: "Flipper Zero" });
|
||||||
|
```
|
||||||
|
|
||||||
|
## isConnected
|
||||||
|
Returns USB connection state.
|
||||||
|
|
||||||
|
### Example:
|
||||||
|
```js
|
||||||
|
if (badusb.isConnected()) {
|
||||||
|
// Do something
|
||||||
|
} else {
|
||||||
|
// Show an error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## press
|
||||||
|
Press and release a key.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
Key or modifier name, key code.
|
||||||
|
|
||||||
|
See a list of key names below.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
badusb.press("a"); // Press "a" key
|
||||||
|
badusb.press("A"); // SHIFT + "a"
|
||||||
|
badusb.press("CTRL", "a"); // CTRL + "a"
|
||||||
|
badusb.press("CTRL", "SHIFT", "ESC"); // CTRL + SHIFT + ESC combo
|
||||||
|
badusb.press(98); // Press key with HID code (dec) 98 (Numpad 0 / Insert)
|
||||||
|
badusb.press(0x47); // Press key with HID code (hex) 0x47 (Scroll lock)
|
||||||
|
```
|
||||||
|
|
||||||
|
## hold
|
||||||
|
Hold a key. Up to 5 keys (excluding modifiers) can be held simultaneously.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
Same as `press`
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
badusb.hold("a"); // Press and hold "a" key
|
||||||
|
badusb.hold("CTRL", "v"); // Press and hold CTRL + "v" combo
|
||||||
|
```
|
||||||
|
|
||||||
|
## release
|
||||||
|
Release a previously hold key.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
Same as `press`
|
||||||
|
|
||||||
|
Release all keys if called without parameters
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
badusb.release(); // Release all keys
|
||||||
|
badusb.release("a"); // Release "a" key
|
||||||
|
```
|
||||||
|
|
||||||
|
## print
|
||||||
|
Print a string.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- A string to print
|
||||||
|
- (optional) delay between key presses
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
badusb.print("Hello, world!"); // print "Hello, world!"
|
||||||
|
badusb.print("Hello, world!", 100); // Add 100ms delay between key presses
|
||||||
|
```
|
||||||
|
|
||||||
|
## println
|
||||||
|
Same as `print` but ended with "ENTER" press.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- A string to print
|
||||||
|
- (optional) delay between key presses
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
badusb.println("Hello, world!"); // print "Hello, world!" and press "ENTER"
|
||||||
|
```
|
||||||
|
|
||||||
|
# Key names list
|
||||||
|
|
||||||
|
## Modifier keys
|
||||||
|
|
||||||
|
| Name |
|
||||||
|
| ------------- |
|
||||||
|
| CTRL |
|
||||||
|
| SHIFT |
|
||||||
|
| ALT |
|
||||||
|
| GUI |
|
||||||
|
|
||||||
|
## Special keys
|
||||||
|
|
||||||
|
| Name | Notes |
|
||||||
|
| ------------------ | ---------------- |
|
||||||
|
| DOWN | Down arrow |
|
||||||
|
| LEFT | Left arrow |
|
||||||
|
| RIGHT | Right arrow |
|
||||||
|
| UP | Up arrow |
|
||||||
|
| ENTER | |
|
||||||
|
| DELETE | |
|
||||||
|
| BACKSPACE | |
|
||||||
|
| END | |
|
||||||
|
| HOME | |
|
||||||
|
| ESC | |
|
||||||
|
| INSERT | |
|
||||||
|
| PAGEUP | |
|
||||||
|
| PAGEDOWN | |
|
||||||
|
| CAPSLOCK | |
|
||||||
|
| NUMLOCK | |
|
||||||
|
| SCROLLLOCK | |
|
||||||
|
| PRINTSCREEN | |
|
||||||
|
| PAUSE | Pause/Break key |
|
||||||
|
| SPACE | |
|
||||||
|
| TAB | |
|
||||||
|
| MENU | Context menu key |
|
||||||
|
| Fx | F1-F24 keys |
|
||||||
56
documentation/js/js_builtin.md
Normal file
56
documentation/js/js_builtin.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Built-in methods {#js_builtin}
|
||||||
|
|
||||||
|
## require
|
||||||
|
Load a module plugin.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Module name
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
let serial = require("serial"); // Load "serial" module
|
||||||
|
```
|
||||||
|
|
||||||
|
## delay
|
||||||
|
### Parameters
|
||||||
|
- Delay value in ms
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
delay(500); // Delay for 500ms
|
||||||
|
```
|
||||||
|
## print
|
||||||
|
Print a message on a screen console.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
The following argument types are supported:
|
||||||
|
- String
|
||||||
|
- Number
|
||||||
|
- Bool
|
||||||
|
- undefined
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
print("string1", "string2", 123);
|
||||||
|
```
|
||||||
|
|
||||||
|
## console.log
|
||||||
|
## console.warn
|
||||||
|
## console.error
|
||||||
|
## console.debug
|
||||||
|
Same as `print`, but output to serial console only, with corresponding log level.
|
||||||
|
|
||||||
|
## to_string
|
||||||
|
Convert a number to string.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
to_string(123)
|
||||||
|
```
|
||||||
|
## to_hex_string
|
||||||
|
Convert a number to string(hex format).
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
to_hex_string(0xFF)
|
||||||
|
```
|
||||||
13
documentation/js/js_data_types.md
Normal file
13
documentation/js/js_data_types.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Data types {#js_data_types}
|
||||||
|
|
||||||
|
Here is a list of common data types used by mJS.
|
||||||
|
- string - sequence of single byte characters, no UTF8 support
|
||||||
|
- number
|
||||||
|
- boolean
|
||||||
|
- foreign - C function or data pointer
|
||||||
|
- undefined
|
||||||
|
- null
|
||||||
|
- object - a data structure with named fields
|
||||||
|
- array - special type of object, all items have indexes and equal types
|
||||||
|
- ArrayBuffer - raw data buffer
|
||||||
|
- DataView - provides interface for accessing ArrayBuffer contents
|
||||||
49
documentation/js/js_dialog.md
Normal file
49
documentation/js/js_dialog.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# js_dialog {#js_dialog}
|
||||||
|
|
||||||
|
# Dialog module
|
||||||
|
```js
|
||||||
|
let dialog = require("dialog");
|
||||||
|
```
|
||||||
|
# Methods
|
||||||
|
|
||||||
|
## message
|
||||||
|
Show a simple message dialog with header, text and "OK" button.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Dialog header text
|
||||||
|
- Dialog text
|
||||||
|
|
||||||
|
### Retuns
|
||||||
|
true if central button was pressed, false if the dialog was closed by back key press
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
dialog.message("Dialog demo", "Press OK to start");
|
||||||
|
```
|
||||||
|
|
||||||
|
## custom
|
||||||
|
More complex dialog with configurable buttons
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
Configuration object with the following fileds:
|
||||||
|
- header: Dialog header text
|
||||||
|
- text: Dialog text
|
||||||
|
- button_left: (optional) left button name
|
||||||
|
- button_right: (optional) right button name
|
||||||
|
- button_center: (optional) central button name
|
||||||
|
|
||||||
|
### Retuns
|
||||||
|
Name of pressed button or empty string if the dialog was closed by back key press
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
let dialog_params = ({
|
||||||
|
header: "Dialog header",
|
||||||
|
text: "Dialog text",
|
||||||
|
button_left: "Left",
|
||||||
|
button_right: "Right",
|
||||||
|
button_center: "OK"
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.custom(dialog_params);
|
||||||
|
```
|
||||||
36
documentation/js/js_notification.md
Normal file
36
documentation/js/js_notification.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# js_notification {#js_notification}
|
||||||
|
|
||||||
|
# Notification module
|
||||||
|
```js
|
||||||
|
let notify = require("notification");
|
||||||
|
```
|
||||||
|
# Methods
|
||||||
|
|
||||||
|
## success
|
||||||
|
"Success" flipper notification message
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
notify.success();
|
||||||
|
```
|
||||||
|
|
||||||
|
## error
|
||||||
|
"Error" flipper notification message
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
notify.error();
|
||||||
|
```
|
||||||
|
|
||||||
|
## blink
|
||||||
|
Blink notification LED
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Blink color (blue/red/green/yellow/cyan/magenta)
|
||||||
|
- Blink type (short/long)
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
notify.blink("red", "short"); // Short blink of red LED
|
||||||
|
notify.blink("green", "short"); // Long blink of green LED
|
||||||
|
```
|
||||||
107
documentation/js/js_serial.md
Normal file
107
documentation/js/js_serial.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# js_serial {#js_serial}
|
||||||
|
|
||||||
|
# Serial module
|
||||||
|
```js
|
||||||
|
let serial = require("serial");
|
||||||
|
```
|
||||||
|
# Methods
|
||||||
|
|
||||||
|
## setup
|
||||||
|
Configure serial port. Should be called before all other methods.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Serial port name (usart, lpuart)
|
||||||
|
- Baudrate
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
// Configure LPUART port with baudrate = 115200
|
||||||
|
serial.setup("lpuart", 115200);
|
||||||
|
```
|
||||||
|
|
||||||
|
## write
|
||||||
|
Write data to serial port
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
One or more arguments of the following types:
|
||||||
|
- A string
|
||||||
|
- Single number, each number is interpreted as a byte
|
||||||
|
- Array of numbers, each number is interpreted as a byte
|
||||||
|
- ArrayBuffer or DataView
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
serial.write(0x0a); // Write a single byte 0x0A
|
||||||
|
serial.write("Hello, world!"); // Write a string
|
||||||
|
serial.write("Hello, world!", [0x0d, 0x0a]); // Write a string followed by two bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
## read
|
||||||
|
Read a fixed number of characters from serial port.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Number of bytes to read
|
||||||
|
- (optional) Timeout value in ms
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
A sting of received characters or undefined if nothing was received before timeout.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
serial.read(1); // Read a single byte, without timeout
|
||||||
|
serial.read(10, 5000); // Read 10 bytes, with 5s timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
## readln
|
||||||
|
Read from serial port untill line break character
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
(optional) Timeout value in ms
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
A sting of received characters or undefined if nothing was received before timeout.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
serial.readln(); // Read without timeout
|
||||||
|
serial.readln(5000); // Read with 5s timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
## readBytes
|
||||||
|
Read from serial port untill line break character
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Number of bytes to read
|
||||||
|
- (optional) Timeout value in ms
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
ArrayBuffer with received data or undefined if nothing was received before timeout.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
serial.readBytes(4); // Read 4 bytes, without timeout
|
||||||
|
|
||||||
|
// Read one byte from receive buffer with zero timeout, returns UNDEFINED if Rx bufer is empty
|
||||||
|
serial.readBytes(1, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
## expect
|
||||||
|
Search for a string pattern in received data stream
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- Single argument or array of the following types:
|
||||||
|
- A string
|
||||||
|
- Array of numbers, each number is interpreted as a byte
|
||||||
|
- (optional) Timeout value in ms
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
Index of matched pattern in input patterns list, undefined if nothing was found.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
```js
|
||||||
|
// Wait for root shell prompt with 1s timeout, returns 0 if it was received before timeout, undefined if not
|
||||||
|
serial.expect("# ", 1000);
|
||||||
|
|
||||||
|
// Infinitely wait for one of two strings, should return 0 if the first string got matched, 1 if the second one
|
||||||
|
serial.expect([": not found", "Usage: "]);
|
||||||
|
```
|
||||||
@@ -22,7 +22,7 @@ DIST_SUFFIX = "local"
|
|||||||
COPRO_OB_DATA = "scripts/ob.data"
|
COPRO_OB_DATA = "scripts/ob.data"
|
||||||
|
|
||||||
# Must match lib/stm32wb_copro version
|
# Must match lib/stm32wb_copro version
|
||||||
COPRO_CUBE_VERSION = "1.17.3"
|
COPRO_CUBE_VERSION = "1.19.0"
|
||||||
|
|
||||||
COPRO_CUBE_DIR = "lib/stm32wb_copro"
|
COPRO_CUBE_DIR = "lib/stm32wb_copro"
|
||||||
|
|
||||||
|
|||||||
@@ -380,8 +380,8 @@ static GapConfig template_config = {
|
|||||||
.pairing_method = GapPairingPinCodeVerifyYesNo,
|
.pairing_method = GapPairingPinCodeVerifyYesNo,
|
||||||
.conn_param =
|
.conn_param =
|
||||||
{
|
{
|
||||||
.conn_int_min = 0x18, // 30 ms
|
.conn_int_min = 0x18, // AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
|
||||||
.conn_int_max = 0x24, // 45 ms
|
.conn_int_max = 0x18, // 30 ms
|
||||||
.slave_latency = 0,
|
.slave_latency = 0,
|
||||||
.supervisor_timeout = 0,
|
.supervisor_timeout = 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ env.Append(
|
|||||||
File("protocols/mf_desfire/mf_desfire.h"),
|
File("protocols/mf_desfire/mf_desfire.h"),
|
||||||
File("protocols/slix/slix.h"),
|
File("protocols/slix/slix.h"),
|
||||||
File("protocols/st25tb/st25tb.h"),
|
File("protocols/st25tb/st25tb.h"),
|
||||||
|
File("protocols/felica/felica.h"),
|
||||||
# Pollers
|
# Pollers
|
||||||
File("protocols/iso14443_3a/iso14443_3a_poller.h"),
|
File("protocols/iso14443_3a/iso14443_3a_poller.h"),
|
||||||
File("protocols/iso14443_3b/iso14443_3b_poller.h"),
|
File("protocols/iso14443_3b/iso14443_3b_poller.h"),
|
||||||
@@ -33,6 +34,7 @@ env.Append(
|
|||||||
File("protocols/mf_classic/mf_classic_poller.h"),
|
File("protocols/mf_classic/mf_classic_poller.h"),
|
||||||
File("protocols/mf_desfire/mf_desfire_poller.h"),
|
File("protocols/mf_desfire/mf_desfire_poller.h"),
|
||||||
File("protocols/st25tb/st25tb_poller.h"),
|
File("protocols/st25tb/st25tb_poller.h"),
|
||||||
|
File("protocols/felica/felica_poller.h"),
|
||||||
# Listeners
|
# Listeners
|
||||||
File("protocols/iso14443_3a/iso14443_3a_listener.h"),
|
File("protocols/iso14443_3a/iso14443_3a_listener.h"),
|
||||||
File("protocols/iso14443_4a/iso14443_4a_listener.h"),
|
File("protocols/iso14443_4a/iso14443_4a_listener.h"),
|
||||||
|
|||||||
@@ -13,6 +13,14 @@
|
|||||||
|
|
||||||
static const uint32_t felica_data_format_version = 1;
|
static const uint32_t felica_data_format_version = 1;
|
||||||
|
|
||||||
|
/** @brief This is used in felica_prepare_first_block to define which
|
||||||
|
* type of block needs to be prepared.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
FelicaMACTypeRead,
|
||||||
|
FelicaMACTypeWrite,
|
||||||
|
} FelicaMACType;
|
||||||
|
|
||||||
const NfcDeviceBase nfc_device_felica = {
|
const NfcDeviceBase nfc_device_felica = {
|
||||||
.protocol_name = FELICA_PROTOCOL_NAME,
|
.protocol_name = FELICA_PROTOCOL_NAME,
|
||||||
.alloc = (NfcDeviceAlloc)felica_alloc,
|
.alloc = (NfcDeviceAlloc)felica_alloc,
|
||||||
@@ -35,18 +43,18 @@ FelicaData* felica_alloc(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void felica_free(FelicaData* data) {
|
void felica_free(FelicaData* data) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_reset(FelicaData* data) {
|
void felica_reset(FelicaData* data) {
|
||||||
|
furi_check(data);
|
||||||
memset(data, 0, sizeof(FelicaData));
|
memset(data, 0, sizeof(FelicaData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_copy(FelicaData* data, const FelicaData* other) {
|
void felica_copy(FelicaData* data, const FelicaData* other) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
furi_assert(other);
|
furi_check(other);
|
||||||
|
|
||||||
*data = *other;
|
*data = *other;
|
||||||
}
|
}
|
||||||
@@ -59,7 +67,7 @@ bool felica_verify(FelicaData* data, const FuriString* device_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
|
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|
||||||
@@ -77,13 +85,32 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
parsed = true;
|
parsed = true;
|
||||||
|
uint32_t blocks_total = 0;
|
||||||
|
uint32_t blocks_read = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
||||||
|
data->blocks_total = (uint8_t)blocks_total;
|
||||||
|
data->blocks_read = (uint8_t)blocks_read;
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
for(uint8_t i = 0; i < data->blocks_total; i++) {
|
||||||
|
furi_string_printf(temp_str, "Block %d", i);
|
||||||
|
if(!flipper_format_read_hex(
|
||||||
|
ff,
|
||||||
|
furi_string_get_cstr(temp_str),
|
||||||
|
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||||
|
sizeof(FelicaBlock))) {
|
||||||
|
parsed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
|
|
||||||
bool saved = false;
|
bool saved = false;
|
||||||
|
|
||||||
@@ -98,15 +125,33 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
|||||||
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
uint32_t blocks_total = data->blocks_total;
|
||||||
|
uint32_t blocks_read = data->blocks_read;
|
||||||
|
if(!flipper_format_write_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
||||||
|
if(!flipper_format_write_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
||||||
|
|
||||||
saved = true;
|
saved = true;
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
for(uint8_t i = 0; i < blocks_total; i++) {
|
||||||
|
furi_string_printf(temp_str, "Block %d", i);
|
||||||
|
if(!flipper_format_write_hex(
|
||||||
|
ff,
|
||||||
|
furi_string_get_cstr(temp_str),
|
||||||
|
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||||
|
sizeof(FelicaBlock))) {
|
||||||
|
saved = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_string_free(temp_str);
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
|
bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
furi_assert(other);
|
furi_check(other);
|
||||||
|
|
||||||
return memcmp(data, other, sizeof(FelicaData)) == 0;
|
return memcmp(data, other, sizeof(FelicaData)) == 0;
|
||||||
}
|
}
|
||||||
@@ -119,7 +164,7 @@ const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType nam
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
|
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
|
|
||||||
// Consider Manufacturer ID as UID
|
// Consider Manufacturer ID as UID
|
||||||
if(uid_len) {
|
if(uid_len) {
|
||||||
@@ -130,7 +175,7 @@ const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
|
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
|
||||||
furi_assert(data);
|
furi_check(data);
|
||||||
|
|
||||||
// Consider Manufacturer ID as UID
|
// Consider Manufacturer ID as UID
|
||||||
const bool uid_valid = uid_len == FELICA_IDM_SIZE;
|
const bool uid_valid = uid_len == FELICA_IDM_SIZE;
|
||||||
@@ -145,3 +190,149 @@ FelicaData* felica_get_base_data(const FelicaData* data) {
|
|||||||
UNUSED(data);
|
UNUSED(data);
|
||||||
furi_crash("No base data");
|
furi_crash("No base data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void felica_reverse_copy_block(const uint8_t* array, uint8_t* reverse_array) {
|
||||||
|
furi_assert(array);
|
||||||
|
furi_assert(reverse_array);
|
||||||
|
|
||||||
|
for(int i = 0; i < 8; i++) {
|
||||||
|
reverse_array[i] = array[7 - i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_calculate_session_key(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* ck,
|
||||||
|
const uint8_t* rc,
|
||||||
|
uint8_t* out) {
|
||||||
|
furi_check(ctx);
|
||||||
|
furi_check(ck);
|
||||||
|
furi_check(rc);
|
||||||
|
furi_check(out);
|
||||||
|
|
||||||
|
uint8_t iv[8];
|
||||||
|
memset(iv, 0, 8);
|
||||||
|
|
||||||
|
uint8_t ck_reversed[16];
|
||||||
|
felica_reverse_copy_block(ck, ck_reversed);
|
||||||
|
felica_reverse_copy_block(ck + 8, ck_reversed + 8);
|
||||||
|
|
||||||
|
uint8_t rc_reversed[16];
|
||||||
|
felica_reverse_copy_block(rc, rc_reversed);
|
||||||
|
felica_reverse_copy_block(rc + 8, rc_reversed + 8);
|
||||||
|
|
||||||
|
mbedtls_des3_set2key_enc(ctx, ck_reversed);
|
||||||
|
mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, FELICA_DATA_BLOCK_SIZE, iv, rc_reversed, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool felica_calculate_mac(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* session_key,
|
||||||
|
const uint8_t* rc,
|
||||||
|
const uint8_t* first_block,
|
||||||
|
const uint8_t* data,
|
||||||
|
const size_t length,
|
||||||
|
uint8_t* mac) {
|
||||||
|
furi_check((length % 8) == 0);
|
||||||
|
|
||||||
|
uint8_t reverse_data[8];
|
||||||
|
uint8_t iv[8];
|
||||||
|
uint8_t out[8];
|
||||||
|
mbedtls_des3_set2key_enc(ctx, session_key);
|
||||||
|
|
||||||
|
felica_reverse_copy_block(rc, iv);
|
||||||
|
felica_reverse_copy_block(first_block, reverse_data);
|
||||||
|
uint8_t i = 0;
|
||||||
|
bool error = false;
|
||||||
|
do {
|
||||||
|
if(mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 8, iv, reverse_data, out) == 0) {
|
||||||
|
memcpy(iv, out, sizeof(iv));
|
||||||
|
felica_reverse_copy_block(data + i, reverse_data);
|
||||||
|
i += 8;
|
||||||
|
} else {
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(i <= length);
|
||||||
|
|
||||||
|
if(!error) {
|
||||||
|
felica_reverse_copy_block(out, mac);
|
||||||
|
}
|
||||||
|
return !error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void felica_prepare_first_block(
|
||||||
|
FelicaMACType operation_type,
|
||||||
|
const uint8_t* blocks,
|
||||||
|
const uint8_t block_count,
|
||||||
|
uint8_t* out) {
|
||||||
|
furi_check(blocks);
|
||||||
|
furi_check(out);
|
||||||
|
if(operation_type == FelicaMACTypeRead) {
|
||||||
|
memset(out, 0xFF, 8);
|
||||||
|
for(uint8_t i = 0, j = 0; i < block_count; i++, j += 2) {
|
||||||
|
out[j] = blocks[i];
|
||||||
|
out[j + 1] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
furi_check(block_count == 4);
|
||||||
|
memset(out, 0, 8);
|
||||||
|
out[0] = blocks[0];
|
||||||
|
out[1] = blocks[1];
|
||||||
|
out[2] = blocks[2];
|
||||||
|
out[4] = blocks[3];
|
||||||
|
out[6] = FELICA_BLOCK_INDEX_MAC_A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool felica_check_mac(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* session_key,
|
||||||
|
const uint8_t* rc,
|
||||||
|
const uint8_t* blocks,
|
||||||
|
const uint8_t block_count,
|
||||||
|
uint8_t* data) {
|
||||||
|
furi_check(ctx);
|
||||||
|
furi_check(session_key);
|
||||||
|
furi_check(rc);
|
||||||
|
furi_check(blocks);
|
||||||
|
furi_check(data);
|
||||||
|
|
||||||
|
uint8_t first_block[8];
|
||||||
|
uint8_t mac[8];
|
||||||
|
felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);
|
||||||
|
|
||||||
|
uint8_t data_size_without_mac = FELICA_DATA_BLOCK_SIZE * (block_count - 1);
|
||||||
|
felica_calculate_mac(ctx, session_key, rc, first_block, data, data_size_without_mac, mac);
|
||||||
|
|
||||||
|
uint8_t* mac_ptr = data + data_size_without_mac;
|
||||||
|
return !memcmp(mac, mac_ptr, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_calculate_mac_write(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* session_key,
|
||||||
|
const uint8_t* rc,
|
||||||
|
const uint8_t* wcnt,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint8_t* mac) {
|
||||||
|
furi_check(ctx);
|
||||||
|
furi_check(session_key);
|
||||||
|
furi_check(rc);
|
||||||
|
furi_check(wcnt);
|
||||||
|
furi_check(data);
|
||||||
|
furi_check(mac);
|
||||||
|
|
||||||
|
const uint8_t WCNT_length = 3;
|
||||||
|
uint8_t block_data[WCNT_length + 1];
|
||||||
|
uint8_t first_block[8];
|
||||||
|
|
||||||
|
memcpy(block_data, wcnt, WCNT_length);
|
||||||
|
block_data[3] = FELICA_BLOCK_INDEX_STATE;
|
||||||
|
felica_prepare_first_block(FelicaMACTypeWrite, block_data, WCNT_length + 1, first_block);
|
||||||
|
|
||||||
|
uint8_t session_swapped[FELICA_DATA_BLOCK_SIZE];
|
||||||
|
memcpy(session_swapped, session_key + 8, 8);
|
||||||
|
memcpy(session_swapped + 8, session_key, 8);
|
||||||
|
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <toolbox/bit_buffer.h>
|
#include <toolbox/bit_buffer.h>
|
||||||
#include <nfc/protocols/nfc_device_base_i.h>
|
#include <nfc/protocols/nfc_device_base_i.h>
|
||||||
|
#include <mbedtls/include/mbedtls/des.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -9,6 +10,23 @@ extern "C" {
|
|||||||
|
|
||||||
#define FELICA_IDM_SIZE (8U)
|
#define FELICA_IDM_SIZE (8U)
|
||||||
#define FELICA_PMM_SIZE (8U)
|
#define FELICA_PMM_SIZE (8U)
|
||||||
|
#define FELICA_DATA_BLOCK_SIZE (16U)
|
||||||
|
|
||||||
|
#define FELICA_BLOCKS_TOTAL_COUNT (27U)
|
||||||
|
#define FELICA_BLOCK_INDEX_REG (0x0EU)
|
||||||
|
#define FELICA_BLOCK_INDEX_RC (0x80U)
|
||||||
|
#define FELICA_BLOCK_INDEX_MAC (0x81U)
|
||||||
|
#define FELICA_BLOCK_INDEX_ID (0x82U)
|
||||||
|
#define FELICA_BLOCK_INDEX_D_ID (0x83U)
|
||||||
|
#define FELICA_BLOCK_INDEX_SER_C (0x84U)
|
||||||
|
#define FELICA_BLOCK_INDEX_SYS_C (0x85U)
|
||||||
|
#define FELICA_BLOCK_INDEX_CKV (0x86U)
|
||||||
|
#define FELICA_BLOCK_INDEX_CK (0x87U)
|
||||||
|
#define FELICA_BLOCK_INDEX_MC (0x88U)
|
||||||
|
#define FELICA_BLOCK_INDEX_WCNT (0x90U)
|
||||||
|
#define FELICA_BLOCK_INDEX_MAC_A (0x91U)
|
||||||
|
#define FELICA_BLOCK_INDEX_STATE (0x92U)
|
||||||
|
#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)
|
||||||
|
|
||||||
#define FELICA_GUARD_TIME_US (20000U)
|
#define FELICA_GUARD_TIME_US (20000U)
|
||||||
#define FELICA_FDT_POLL_FC (10000U)
|
#define FELICA_FDT_POLL_FC (10000U)
|
||||||
@@ -23,6 +41,7 @@ extern "C" {
|
|||||||
#define FELICA_TIME_SLOT_8 (0x07U)
|
#define FELICA_TIME_SLOT_8 (0x07U)
|
||||||
#define FELICA_TIME_SLOT_16 (0x0FU)
|
#define FELICA_TIME_SLOT_16 (0x0FU)
|
||||||
|
|
||||||
|
/** @brief Type of possible Felica errors */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FelicaErrorNone,
|
FelicaErrorNone,
|
||||||
FelicaErrorNotPresent,
|
FelicaErrorNotPresent,
|
||||||
@@ -35,17 +54,78 @@ typedef enum {
|
|||||||
FelicaErrorTimeout,
|
FelicaErrorTimeout,
|
||||||
} FelicaError;
|
} FelicaError;
|
||||||
|
|
||||||
|
/** @brief Separate type for card key block. Used in authentication process */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[FELICA_DATA_BLOCK_SIZE];
|
||||||
|
} FelicaCardKey;
|
||||||
|
|
||||||
|
/** @brief In Felica there two types of auth. Internal is the first one, after
|
||||||
|
* which external became possible. Here are two flags representing which one
|
||||||
|
* was passed */
|
||||||
|
typedef struct {
|
||||||
|
bool internal : 1;
|
||||||
|
bool external : 1;
|
||||||
|
} FelicaAuthenticationStatus;
|
||||||
|
|
||||||
|
/** @brief Struct which controls the process of authentication and can be passed as
|
||||||
|
* a parameter to the application level. In order to force user to fill card key block data. */
|
||||||
|
typedef struct {
|
||||||
|
bool skip_auth; /**< By default it is true, so auth is skipped. By setting this to false several auth steps will be performed in order to pass auth*/
|
||||||
|
FelicaCardKey
|
||||||
|
card_key; /**< User must fill this field with known card key in order to pass auth*/
|
||||||
|
FelicaAuthenticationStatus auth_status; /**< Authentication status*/
|
||||||
|
} FelicaAuthenticationContext;
|
||||||
|
|
||||||
|
/** @brief Felica ID block */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t data[FELICA_IDM_SIZE];
|
uint8_t data[FELICA_IDM_SIZE];
|
||||||
} FelicaIDm;
|
} FelicaIDm;
|
||||||
|
|
||||||
|
/** @brief Felica PMm block */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t data[FELICA_PMM_SIZE];
|
uint8_t data[FELICA_PMM_SIZE];
|
||||||
} FelicaPMm;
|
} FelicaPMm;
|
||||||
|
|
||||||
|
/** @brief Felica block with status flags indicating last operation with it.
|
||||||
|
* See Felica manual for more details on status codes. */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t SF1; /**< Status flag 1, equals to 0 when success*/
|
||||||
|
uint8_t SF2; /**< Status flag 2, equals to 0 when success*/
|
||||||
|
uint8_t data[FELICA_DATA_BLOCK_SIZE]; /**< Block data */
|
||||||
|
} FelicaBlock;
|
||||||
|
|
||||||
|
/** @brief Felica filesystem structure */
|
||||||
|
typedef struct {
|
||||||
|
FelicaBlock spad[14];
|
||||||
|
FelicaBlock reg;
|
||||||
|
FelicaBlock rc;
|
||||||
|
FelicaBlock mac;
|
||||||
|
FelicaBlock id;
|
||||||
|
FelicaBlock d_id;
|
||||||
|
FelicaBlock ser_c;
|
||||||
|
FelicaBlock sys_c;
|
||||||
|
FelicaBlock ckv;
|
||||||
|
FelicaBlock ck;
|
||||||
|
FelicaBlock mc;
|
||||||
|
FelicaBlock wcnt;
|
||||||
|
FelicaBlock mac_a;
|
||||||
|
FelicaBlock state;
|
||||||
|
FelicaBlock crc_check;
|
||||||
|
} FelicaFileSystem;
|
||||||
|
|
||||||
|
/** @brief Union which represents filesystem in junction with plain data dump */
|
||||||
|
typedef union {
|
||||||
|
FelicaFileSystem fs;
|
||||||
|
uint8_t dump[sizeof(FelicaFileSystem)];
|
||||||
|
} FelicaFSUnion;
|
||||||
|
|
||||||
|
/** @brief Structure used to store Felica data and additional values about reading */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FelicaIDm idm;
|
FelicaIDm idm;
|
||||||
FelicaPMm pmm;
|
FelicaPMm pmm;
|
||||||
|
uint8_t blocks_total;
|
||||||
|
uint8_t blocks_read;
|
||||||
|
FelicaFSUnion data;
|
||||||
} FelicaData;
|
} FelicaData;
|
||||||
|
|
||||||
extern const NfcDeviceBase nfc_device_felica;
|
extern const NfcDeviceBase nfc_device_felica;
|
||||||
@@ -74,6 +154,27 @@ bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);
|
|||||||
|
|
||||||
FelicaData* felica_get_base_data(const FelicaData* data);
|
FelicaData* felica_get_base_data(const FelicaData* data);
|
||||||
|
|
||||||
|
void felica_calculate_session_key(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* ck,
|
||||||
|
const uint8_t* rc,
|
||||||
|
uint8_t* out);
|
||||||
|
|
||||||
|
bool felica_check_mac(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* session_key,
|
||||||
|
const uint8_t* rc,
|
||||||
|
const uint8_t* blocks,
|
||||||
|
const uint8_t block_count,
|
||||||
|
uint8_t* data);
|
||||||
|
|
||||||
|
void felica_calculate_mac_write(
|
||||||
|
mbedtls_des3_context* ctx,
|
||||||
|
const uint8_t* session_key,
|
||||||
|
const uint8_t* rc,
|
||||||
|
const uint8_t* wcnt,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint8_t* mac);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
#include <nfc/protocols/nfc_poller_base.h>
|
#include <nfc/protocols/nfc_poller_base.h>
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#define TAG "FelicaPoller"
|
||||||
|
|
||||||
|
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
|
||||||
|
|
||||||
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
|
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
@@ -23,6 +28,8 @@ static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
|
|||||||
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
|
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
|
||||||
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
|
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
|
||||||
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
|
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
|
||||||
|
|
||||||
|
mbedtls_des3_init(&instance->auth.des_context);
|
||||||
instance->data = felica_alloc();
|
instance->data = felica_alloc();
|
||||||
|
|
||||||
instance->felica_event.data = &instance->felica_event_data;
|
instance->felica_event.data = &instance->felica_event_data;
|
||||||
@@ -40,6 +47,7 @@ static void felica_poller_free(FelicaPoller* instance) {
|
|||||||
furi_assert(instance->rx_buffer);
|
furi_assert(instance->rx_buffer);
|
||||||
furi_assert(instance->data);
|
furi_assert(instance->data);
|
||||||
|
|
||||||
|
mbedtls_des3_free(&instance->auth.des_context);
|
||||||
bit_buffer_free(instance->tx_buffer);
|
bit_buffer_free(instance->tx_buffer);
|
||||||
bit_buffer_free(instance->rx_buffer);
|
bit_buffer_free(instance->rx_buffer);
|
||||||
felica_free(instance->data);
|
felica_free(instance->data);
|
||||||
@@ -55,6 +63,212 @@ static void
|
|||||||
instance->context = context;
|
instance->context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_idle(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Idle");
|
||||||
|
felica_reset(instance->data);
|
||||||
|
instance->state = FelicaPollerStateActivated;
|
||||||
|
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Activate");
|
||||||
|
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
|
FelicaError error = felica_poller_activate(instance, instance->data);
|
||||||
|
if(error == FelicaErrorNone) {
|
||||||
|
furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext;
|
||||||
|
instance->felica_event_data.auth_context = &instance->auth.context;
|
||||||
|
|
||||||
|
instance->callback(instance->general_event, instance->context);
|
||||||
|
|
||||||
|
bool skip_auth = instance->auth.context.skip_auth;
|
||||||
|
instance->state = skip_auth ? FelicaPollerStateReadBlocks :
|
||||||
|
FelicaPollerStateAuthenticateInternal;
|
||||||
|
} else if(error != FelicaErrorTimeout) {
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||||
|
instance->felica_event_data.error = error;
|
||||||
|
instance->state = FelicaPollerStateReadFailed;
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Auth Internal");
|
||||||
|
|
||||||
|
felica_calculate_session_key(
|
||||||
|
&instance->auth.des_context,
|
||||||
|
instance->auth.context.card_key.data,
|
||||||
|
instance->data->data.fs.rc.data,
|
||||||
|
instance->auth.session_key.data);
|
||||||
|
|
||||||
|
instance->state = FelicaPollerStateReadBlocks;
|
||||||
|
|
||||||
|
uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0};
|
||||||
|
FelicaPollerWriteCommandResponse* tx_resp;
|
||||||
|
do {
|
||||||
|
FelicaError error = felica_poller_write_blocks(
|
||||||
|
instance, 1, blocks, instance->data->data.fs.rc.data, &tx_resp);
|
||||||
|
if((error != FelicaErrorNone) || (tx_resp->SF1 != 0) || (tx_resp->SF2 != 0)) break;
|
||||||
|
|
||||||
|
blocks[0] = FELICA_BLOCK_INDEX_ID;
|
||||||
|
blocks[1] = FELICA_BLOCK_INDEX_WCNT;
|
||||||
|
blocks[2] = FELICA_BLOCK_INDEX_MAC_A;
|
||||||
|
FelicaPollerReadCommandResponse* rx_resp;
|
||||||
|
error = felica_poller_read_blocks(instance, sizeof(blocks), blocks, &rx_resp);
|
||||||
|
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
|
||||||
|
|
||||||
|
if(felica_check_mac(
|
||||||
|
&instance->auth.des_context,
|
||||||
|
instance->auth.session_key.data,
|
||||||
|
instance->data->data.fs.rc.data,
|
||||||
|
blocks,
|
||||||
|
rx_resp->block_count,
|
||||||
|
rx_resp->data)) {
|
||||||
|
instance->auth.context.auth_status.internal = true;
|
||||||
|
instance->data->data.fs.wcnt.SF1 = 0;
|
||||||
|
instance->data->data.fs.wcnt.SF2 = 0;
|
||||||
|
memcpy(
|
||||||
|
instance->data->data.fs.wcnt.data,
|
||||||
|
rx_resp->data + FELICA_DATA_BLOCK_SIZE,
|
||||||
|
FELICA_DATA_BLOCK_SIZE);
|
||||||
|
instance->state = FelicaPollerStateAuthenticateExternal;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Auth External");
|
||||||
|
instance->state = FelicaPollerStateReadBlocks;
|
||||||
|
uint8_t blocks[2];
|
||||||
|
|
||||||
|
instance->data->data.fs.state.data[0] = 1;
|
||||||
|
FelicaAuthentication* auth = &instance->auth;
|
||||||
|
felica_calculate_mac_write(
|
||||||
|
&auth->des_context,
|
||||||
|
auth->session_key.data,
|
||||||
|
instance->data->data.fs.rc.data,
|
||||||
|
instance->data->data.fs.wcnt.data,
|
||||||
|
instance->data->data.fs.state.data,
|
||||||
|
instance->data->data.fs.mac_a.data);
|
||||||
|
|
||||||
|
memcpy(instance->data->data.fs.mac_a.data + 8, instance->data->data.fs.wcnt.data, 3); //-V1086
|
||||||
|
|
||||||
|
uint8_t tx_data[FELICA_DATA_BLOCK_SIZE * 2];
|
||||||
|
memcpy(tx_data, instance->data->data.fs.state.data, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
memcpy(
|
||||||
|
tx_data + FELICA_DATA_BLOCK_SIZE,
|
||||||
|
instance->data->data.fs.mac_a.data,
|
||||||
|
FELICA_DATA_BLOCK_SIZE);
|
||||||
|
|
||||||
|
do {
|
||||||
|
blocks[0] = FELICA_BLOCK_INDEX_STATE;
|
||||||
|
blocks[1] = FELICA_BLOCK_INDEX_MAC_A;
|
||||||
|
FelicaPollerWriteCommandResponse* tx_resp;
|
||||||
|
FelicaError error = felica_poller_write_blocks(instance, 2, blocks, tx_data, &tx_resp);
|
||||||
|
if(error != FelicaErrorNone || tx_resp->SF1 != 0 || tx_resp->SF2 != 0) break;
|
||||||
|
|
||||||
|
FelicaPollerReadCommandResponse* rx_resp;
|
||||||
|
error = felica_poller_read_blocks(instance, 1, blocks, &rx_resp);
|
||||||
|
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
|
||||||
|
|
||||||
|
instance->data->data.fs.state.SF1 = 0;
|
||||||
|
instance->data->data.fs.state.SF2 = 0;
|
||||||
|
memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];
|
||||||
|
} while(false);
|
||||||
|
instance->state = FelicaPollerStateReadBlocks;
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_read_blocks(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Read Blocks");
|
||||||
|
|
||||||
|
uint8_t block_count = 1;
|
||||||
|
uint8_t block_list[4] = {0, 0, 0, 0};
|
||||||
|
block_list[0] = instance->block_index;
|
||||||
|
|
||||||
|
instance->block_index++;
|
||||||
|
if(instance->block_index == FELICA_BLOCK_INDEX_REG + 1) {
|
||||||
|
instance->block_index = FELICA_BLOCK_INDEX_RC;
|
||||||
|
} else if(instance->block_index == FELICA_BLOCK_INDEX_MC + 1) {
|
||||||
|
instance->block_index = FELICA_BLOCK_INDEX_WCNT;
|
||||||
|
} else if(instance->block_index == FELICA_BLOCK_INDEX_STATE + 1) {
|
||||||
|
instance->block_index = FELICA_BLOCK_INDEX_CRC_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
FelicaPollerReadCommandResponse* response;
|
||||||
|
FelicaError error = felica_poller_read_blocks(instance, block_count, block_list, &response);
|
||||||
|
if(error == FelicaErrorNone) {
|
||||||
|
block_count = (response->SF1 == 0) ? response->block_count : block_count;
|
||||||
|
uint8_t* data_ptr =
|
||||||
|
instance->data->data.dump + instance->data->blocks_total * sizeof(FelicaBlock);
|
||||||
|
|
||||||
|
*data_ptr++ = response->SF1;
|
||||||
|
*data_ptr++ = response->SF2;
|
||||||
|
|
||||||
|
if(response->SF1 == 0) {
|
||||||
|
uint8_t* response_data_ptr = response->data;
|
||||||
|
instance->data->blocks_read++;
|
||||||
|
memcpy(data_ptr, response_data_ptr, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
} else {
|
||||||
|
memset(data_ptr, 0, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
instance->data->blocks_total++;
|
||||||
|
|
||||||
|
if(instance->data->blocks_total == FELICA_BLOCKS_TOTAL_COUNT) {
|
||||||
|
instance->state = FelicaPollerStateReadSuccess;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||||
|
instance->felica_event_data.error = error;
|
||||||
|
instance->state = FelicaPollerStateReadFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Read Success");
|
||||||
|
|
||||||
|
if(!instance->auth.context.auth_status.internal ||
|
||||||
|
!instance->auth.context.auth_status.external) {
|
||||||
|
instance->data->blocks_read--;
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeIncomplete;
|
||||||
|
} else {
|
||||||
|
memcpy(
|
||||||
|
instance->data->data.fs.ck.data,
|
||||||
|
instance->auth.context.card_key.data,
|
||||||
|
FELICA_DATA_BLOCK_SIZE);
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->felica_event_data.error = FelicaErrorNone;
|
||||||
|
return instance->callback(instance->general_event, instance->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Read Fail");
|
||||||
|
instance->callback(instance->general_event, instance->context);
|
||||||
|
|
||||||
|
return NfcCommandStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = {
|
||||||
|
[FelicaPollerStateIdle] = felica_poller_state_handler_idle,
|
||||||
|
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
||||||
|
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
||||||
|
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
|
||||||
|
[FelicaPollerStateReadBlocks] = felica_poller_state_handler_read_blocks,
|
||||||
|
[FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success,
|
||||||
|
[FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,
|
||||||
|
};
|
||||||
|
|
||||||
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
|
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
furi_assert(event.protocol == NfcProtocolInvalid);
|
furi_assert(event.protocol == NfcProtocolInvalid);
|
||||||
@@ -65,24 +279,7 @@ static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
|
|||||||
NfcCommand command = NfcCommandContinue;
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||||
if(instance->state != FelicaPollerStateActivated) {
|
command = felica_poller_handler[instance->state](instance);
|
||||||
FelicaError error = felica_poller_activate(instance, instance->data);
|
|
||||||
if(error == FelicaErrorNone) {
|
|
||||||
instance->felica_event.type = FelicaPollerEventTypeReady;
|
|
||||||
instance->felica_event_data.error = error;
|
|
||||||
command = instance->callback(instance->general_event, instance->context);
|
|
||||||
} else {
|
|
||||||
instance->felica_event.type = FelicaPollerEventTypeError;
|
|
||||||
instance->felica_event_data.error = error;
|
|
||||||
command = instance->callback(instance->general_event, instance->context);
|
|
||||||
// Add delay to switch context
|
|
||||||
furi_delay_ms(100);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
instance->felica_event.type = FelicaPollerEventTypeReady;
|
|
||||||
instance->felica_event_data.error = FelicaErrorNone;
|
|
||||||
command = instance->callback(instance->general_event, instance->context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
|
|||||||
@@ -19,14 +19,33 @@ typedef struct FelicaPoller FelicaPoller;
|
|||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FelicaPollerEventTypeError, /**< An error occured during activation procedure. */
|
FelicaPollerEventTypeError, /**< An error occured during activation procedure. */
|
||||||
FelicaPollerEventTypeReady, /**< The card was activated by the poller. */
|
FelicaPollerEventTypeReady, /**< The card was activated and fully read by the poller. */
|
||||||
|
FelicaPollerEventTypeIncomplete, /**< The card was activated and partly read by the poller. */
|
||||||
|
FelicaPollerEventTypeRequestAuthContext, /**< Authentication context was requested by poller. */
|
||||||
} FelicaPollerEventType;
|
} FelicaPollerEventType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stucture for holding Felica session key which is calculated from rc and ck.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[FELICA_DATA_BLOCK_SIZE];
|
||||||
|
} FelicaSessionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure used to hold authentication related fields.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */
|
||||||
|
FelicaSessionKey session_key; /**< Calculated session key. */
|
||||||
|
FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */
|
||||||
|
} FelicaAuthentication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Felica poller event data.
|
* @brief Felica poller event data.
|
||||||
*/
|
*/
|
||||||
typedef union {
|
typedef union {
|
||||||
FelicaError error; /**< Error code indicating card activation fail reason. */
|
FelicaError error; /**< Error code indicating card activation fail reason. */
|
||||||
|
FelicaAuthenticationContext* auth_context; /**< Authentication context to be filled by user. */
|
||||||
} FelicaPollerEventData;
|
} FelicaPollerEventData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
#include <nfc/helpers/felica_crc.h>
|
#include <nfc/helpers/felica_crc.h>
|
||||||
|
|
||||||
#define TAG "FelicaPoller"
|
#define TAG "FelicaPoller"
|
||||||
|
#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06U)
|
||||||
|
#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U)
|
||||||
|
|
||||||
|
#define FELICA_SERVICE_RW_ACCESS (0x0009U)
|
||||||
|
#define FELICA_SERVICE_RO_ACCESS (0x000BU)
|
||||||
|
|
||||||
static FelicaError felica_poller_process_error(NfcError error) {
|
static FelicaError felica_poller_process_error(NfcError error) {
|
||||||
switch(error) {
|
switch(error) {
|
||||||
@@ -15,8 +20,8 @@ static FelicaError felica_poller_process_error(NfcError error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FelicaError felica_poller_frame_exchange(
|
FelicaError felica_poller_frame_exchange(
|
||||||
FelicaPoller* instance,
|
const FelicaPoller* instance,
|
||||||
const BitBuffer* tx_buffer,
|
const BitBuffer* tx_buffer,
|
||||||
BitBuffer* rx_buffer,
|
BitBuffer* rx_buffer,
|
||||||
uint32_t fwt) {
|
uint32_t fwt) {
|
||||||
@@ -93,11 +98,105 @@ FelicaError felica_poller_polling(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void felica_poller_prepare_tx_buffer(
|
||||||
|
const FelicaPoller* instance,
|
||||||
|
const uint8_t command,
|
||||||
|
const uint16_t service_code,
|
||||||
|
const uint8_t block_count,
|
||||||
|
const uint8_t* const blocks,
|
||||||
|
const uint8_t data_block_count,
|
||||||
|
const uint8_t* data) {
|
||||||
|
FelicaCommandHeader cmd = {
|
||||||
|
.code = command,
|
||||||
|
.idm = instance->data->idm,
|
||||||
|
.service_num = 1,
|
||||||
|
.service_code = service_code,
|
||||||
|
.block_count = block_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
FelicaBlockListElement block_list[4] = {{0}, {0}, {0}, {0}};
|
||||||
|
for(uint8_t i = 0; i < block_count; i++) {
|
||||||
|
block_list[i].length = 1;
|
||||||
|
block_list[i].block_number = blocks[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t block_list_count = block_count;
|
||||||
|
uint8_t block_list_size = block_list_count * sizeof(FelicaBlockListElement);
|
||||||
|
uint8_t total_size = sizeof(FelicaCommandHeader) + 1 + block_list_size +
|
||||||
|
data_block_count * FELICA_DATA_BLOCK_SIZE;
|
||||||
|
bit_buffer_reset(instance->tx_buffer);
|
||||||
|
bit_buffer_append_byte(instance->tx_buffer, total_size);
|
||||||
|
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeader));
|
||||||
|
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block_list, block_list_size);
|
||||||
|
|
||||||
|
if(data_block_count != 0) {
|
||||||
|
bit_buffer_append_bytes(
|
||||||
|
instance->tx_buffer, data, data_block_count * FELICA_DATA_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FelicaError felica_poller_read_blocks(
|
||||||
|
FelicaPoller* instance,
|
||||||
|
const uint8_t block_count,
|
||||||
|
const uint8_t* const block_numbers,
|
||||||
|
FelicaPollerReadCommandResponse** const response_ptr) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(block_count <= 4);
|
||||||
|
furi_assert(block_numbers);
|
||||||
|
furi_assert(response_ptr);
|
||||||
|
|
||||||
|
felica_poller_prepare_tx_buffer(
|
||||||
|
instance,
|
||||||
|
FELICA_CMD_READ_WITHOUT_ENCRYPTION,
|
||||||
|
FELICA_SERVICE_RO_ACCESS,
|
||||||
|
block_count,
|
||||||
|
block_numbers,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
bit_buffer_reset(instance->rx_buffer);
|
||||||
|
|
||||||
|
FelicaError error = felica_poller_frame_exchange(
|
||||||
|
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
|
||||||
|
if(error == FelicaErrorNone) {
|
||||||
|
*response_ptr = (FelicaPollerReadCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
FelicaError felica_poller_write_blocks(
|
||||||
|
const FelicaPoller* instance,
|
||||||
|
const uint8_t block_count,
|
||||||
|
const uint8_t* const block_numbers,
|
||||||
|
const uint8_t* data,
|
||||||
|
FelicaPollerWriteCommandResponse** const response_ptr) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(block_count <= 2);
|
||||||
|
furi_assert(block_numbers);
|
||||||
|
furi_assert(data);
|
||||||
|
furi_assert(response_ptr);
|
||||||
|
|
||||||
|
felica_poller_prepare_tx_buffer(
|
||||||
|
instance,
|
||||||
|
FELICA_CMD_WRITE_WITHOUT_ENCRYPTION,
|
||||||
|
FELICA_SERVICE_RW_ACCESS,
|
||||||
|
block_count,
|
||||||
|
block_numbers,
|
||||||
|
block_count,
|
||||||
|
data);
|
||||||
|
bit_buffer_reset(instance->rx_buffer);
|
||||||
|
|
||||||
|
FelicaError error = felica_poller_frame_exchange(
|
||||||
|
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
|
||||||
|
if(error == FelicaErrorNone) {
|
||||||
|
*response_ptr =
|
||||||
|
(FelicaPollerWriteCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {
|
FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|
||||||
felica_reset(data);
|
|
||||||
|
|
||||||
FelicaError ret;
|
FelicaError ret;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "felica_poller.h"
|
#include "felica_poller.h"
|
||||||
|
|
||||||
#include <toolbox/bit_buffer.h>
|
#include <toolbox/bit_buffer.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -18,11 +17,20 @@ extern "C" {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
FelicaPollerStateIdle,
|
FelicaPollerStateIdle,
|
||||||
FelicaPollerStateActivated,
|
FelicaPollerStateActivated,
|
||||||
|
FelicaPollerStateAuthenticateInternal,
|
||||||
|
FelicaPollerStateAuthenticateExternal,
|
||||||
|
FelicaPollerStateReadBlocks,
|
||||||
|
FelicaPollerStateReadSuccess,
|
||||||
|
FelicaPollerStateReadFailed,
|
||||||
|
|
||||||
|
FelicaPollerStateNum
|
||||||
} FelicaPollerState;
|
} FelicaPollerState;
|
||||||
|
|
||||||
struct FelicaPoller {
|
struct FelicaPoller {
|
||||||
Nfc* nfc;
|
Nfc* nfc;
|
||||||
FelicaPollerState state;
|
FelicaPollerState state;
|
||||||
|
FelicaAuthentication auth;
|
||||||
|
|
||||||
FelicaData* data;
|
FelicaData* data;
|
||||||
BitBuffer* tx_buffer;
|
BitBuffer* tx_buffer;
|
||||||
BitBuffer* rx_buffer;
|
BitBuffer* rx_buffer;
|
||||||
@@ -31,6 +39,7 @@ struct FelicaPoller {
|
|||||||
FelicaPollerEvent felica_event;
|
FelicaPollerEvent felica_event;
|
||||||
FelicaPollerEventData felica_event_data;
|
FelicaPollerEventData felica_event_data;
|
||||||
NfcGenericCallback callback;
|
NfcGenericCallback callback;
|
||||||
|
uint8_t block_index;
|
||||||
void* context;
|
void* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,13 +55,106 @@ typedef struct {
|
|||||||
uint8_t request_data[2];
|
uint8_t request_data[2];
|
||||||
} FelicaPollerPollingResponse;
|
} FelicaPollerPollingResponse;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t service_code : 4;
|
||||||
|
uint8_t access_mode : 3;
|
||||||
|
uint8_t length : 1;
|
||||||
|
uint8_t block_number;
|
||||||
|
} FelicaBlockListElement;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
uint8_t code;
|
||||||
|
FelicaIDm idm;
|
||||||
|
uint8_t service_num;
|
||||||
|
uint16_t service_code;
|
||||||
|
uint8_t block_count;
|
||||||
|
} FelicaCommandHeader;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t response_code;
|
||||||
|
FelicaIDm idm;
|
||||||
|
uint8_t SF1;
|
||||||
|
uint8_t SF2;
|
||||||
|
uint8_t block_count;
|
||||||
|
uint8_t data[];
|
||||||
|
} FelicaPollerReadCommandResponse;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t response_code;
|
||||||
|
FelicaIDm idm;
|
||||||
|
uint8_t SF1;
|
||||||
|
uint8_t SF2;
|
||||||
|
} FelicaPollerWriteCommandResponse;
|
||||||
|
|
||||||
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
|
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs felica polling operation as part of the activation process
|
||||||
|
*
|
||||||
|
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||||
|
* @param[in] cmd Pointer to polling command structure
|
||||||
|
* @param[out] resp Pointer to the response structure
|
||||||
|
* @return FelicaErrorNone on success, an error code on failure
|
||||||
|
*/
|
||||||
FelicaError felica_poller_polling(
|
FelicaError felica_poller_polling(
|
||||||
FelicaPoller* instance,
|
FelicaPoller* instance,
|
||||||
const FelicaPollerPollingCommand* cmd,
|
const FelicaPollerPollingCommand* cmd,
|
||||||
FelicaPollerPollingResponse* resp);
|
FelicaPollerPollingResponse* resp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs felica read operation for blocks provided as parameters
|
||||||
|
*
|
||||||
|
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||||
|
* @param[in] block_count Amount of blocks involved in reading procedure
|
||||||
|
* @param[in] block_numbers Array with block indexes according to felica docs
|
||||||
|
* @param[out] response_ptr Pointer to the response structure
|
||||||
|
* @return FelicaErrorNone on success, an error code on failure.
|
||||||
|
*/
|
||||||
|
FelicaError felica_poller_read_blocks(
|
||||||
|
FelicaPoller* instance,
|
||||||
|
const uint8_t block_count,
|
||||||
|
const uint8_t* const block_numbers,
|
||||||
|
FelicaPollerReadCommandResponse** const response_ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs felica write operation with data provided as parameters
|
||||||
|
*
|
||||||
|
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||||
|
* @param[in] block_count Amount of blocks involved in writing procedure
|
||||||
|
* @param[in] block_numbers Array with block indexes according to felica docs
|
||||||
|
* @param[in] data Data of blocks provided in block_numbers
|
||||||
|
* @param[out] response_ptr Pointer to the response structure
|
||||||
|
* @return FelicaErrorNone on success, an error code on failure.
|
||||||
|
*/
|
||||||
|
FelicaError felica_poller_write_blocks(
|
||||||
|
const FelicaPoller* instance,
|
||||||
|
const uint8_t block_count,
|
||||||
|
const uint8_t* const block_numbers,
|
||||||
|
const uint8_t* data,
|
||||||
|
FelicaPollerWriteCommandResponse** const response_ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform frame exchange procedure.
|
||||||
|
*
|
||||||
|
* Prepares data for sending by adding crc, after that performs
|
||||||
|
* low level calls to send package data to the card
|
||||||
|
*
|
||||||
|
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||||
|
* @param[in] tx_buffer pointer to the buffer with data to be transmitted
|
||||||
|
* @param[out] rx_buffer pointer to the buffer with received data from card
|
||||||
|
* @param[in] fwt timeout window
|
||||||
|
* @return FelicaErrorNone on success, an error code on failure.
|
||||||
|
*/
|
||||||
|
FelicaError felica_poller_frame_exchange(
|
||||||
|
const FelicaPoller* instance,
|
||||||
|
const BitBuffer* tx_buffer,
|
||||||
|
BitBuffer* rx_buffer,
|
||||||
|
uint32_t fwt);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Submodule lib/stm32wb_copro updated: d8a6f1feb0...64a060d91f
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,62.0,,
|
Version,+,60.7,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
@@ -514,7 +514,9 @@ Function,-,acosh,double,double
|
|||||||
Function,-,acoshf,float,float
|
Function,-,acoshf,float,float
|
||||||
Function,-,acoshl,long double,long double
|
Function,-,acoshl,long double,long double
|
||||||
Function,-,acosl,long double,long double
|
Function,-,acosl,long double,long double
|
||||||
Function,+,aligned_alloc,void*,"size_t, size_t"
|
Function,-,aligned_alloc,void*,"size_t, size_t"
|
||||||
|
Function,+,aligned_free,void,void*
|
||||||
|
Function,+,aligned_malloc,void*,"size_t, size_t"
|
||||||
Function,-,arc4random,__uint32_t,
|
Function,-,arc4random,__uint32_t,
|
||||||
Function,-,arc4random_buf,void,"void*, size_t"
|
Function,-,arc4random_buf,void,"void*, size_t"
|
||||||
Function,-,arc4random_uniform,__uint32_t,__uint32_t
|
Function,-,arc4random_uniform,__uint32_t,__uint32_t
|
||||||
@@ -1746,6 +1748,7 @@ Function,+,loader_is_locked,_Bool,Loader*
|
|||||||
Function,+,loader_lock,_Bool,Loader*
|
Function,+,loader_lock,_Bool,Loader*
|
||||||
Function,+,loader_show_menu,void,Loader*
|
Function,+,loader_show_menu,void,Loader*
|
||||||
Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*"
|
Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*"
|
||||||
|
Function,+,loader_start_detached_with_gui_error,void,"Loader*, const char*, const char*"
|
||||||
Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*"
|
Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*"
|
||||||
Function,+,loader_unlock,void,Loader*
|
Function,+,loader_unlock,void,Loader*
|
||||||
Function,+,loading_alloc,Loading*,
|
Function,+,loading_alloc,Loading*,
|
||||||
@@ -1970,8 +1973,7 @@ Function,+,memchr,void*,"const void*, int, size_t"
|
|||||||
Function,+,memcmp,int,"const void*, const void*, size_t"
|
Function,+,memcmp,int,"const void*, const void*, size_t"
|
||||||
Function,+,memcpy,void*,"void*, const void*, size_t"
|
Function,+,memcpy,void*,"void*, const void*, size_t"
|
||||||
Function,-,memmem,void*,"const void*, size_t, const void*, size_t"
|
Function,-,memmem,void*,"const void*, size_t, const void*, size_t"
|
||||||
Function,-,memmgr_aux_pool_alloc,void*,size_t
|
Function,-,memmgr_alloc_from_pool,void*,size_t
|
||||||
Function,-,memmgr_aux_pool_get_free,size_t,
|
|
||||||
Function,+,memmgr_get_free_heap,size_t,
|
Function,+,memmgr_get_free_heap,size_t,
|
||||||
Function,+,memmgr_get_minimum_free_heap,size_t,
|
Function,+,memmgr_get_minimum_free_heap,size_t,
|
||||||
Function,+,memmgr_get_total_heap,size_t,
|
Function,+,memmgr_get_total_heap,size_t,
|
||||||
@@ -1979,7 +1981,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId
|
|||||||
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
|
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
|
||||||
Function,+,memmgr_heap_get_max_free_block,size_t,
|
Function,+,memmgr_heap_get_max_free_block,size_t,
|
||||||
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
|
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
|
||||||
Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*"
|
Function,+,memmgr_heap_printf_free_blocks,void,
|
||||||
|
Function,-,memmgr_pool_get_free,size_t,
|
||||||
Function,-,memmgr_pool_get_max_block,size_t,
|
Function,-,memmgr_pool_get_max_block,size_t,
|
||||||
Function,+,memmove,void*,"void*, const void*, size_t"
|
Function,+,memmove,void*,"void*, const void*, size_t"
|
||||||
Function,-,mempcpy,void*,"void*, const void*, size_t"
|
Function,-,mempcpy,void*,"void*, const void*, size_t"
|
||||||
|
|||||||
|
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,62.0,,
|
Version,+,60.7,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
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.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
@@ -133,6 +133,8 @@ Header,+,lib/nfc/nfc_device.h,,
|
|||||||
Header,+,lib/nfc/nfc_listener.h,,
|
Header,+,lib/nfc/nfc_listener.h,,
|
||||||
Header,+,lib/nfc/nfc_poller.h,,
|
Header,+,lib/nfc/nfc_poller.h,,
|
||||||
Header,+,lib/nfc/nfc_scanner.h,,
|
Header,+,lib/nfc/nfc_scanner.h,,
|
||||||
|
Header,+,lib/nfc/protocols/felica/felica.h,,
|
||||||
|
Header,+,lib/nfc/protocols/felica/felica_poller.h,,
|
||||||
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,,
|
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,,
|
||||||
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,,
|
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,,
|
||||||
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,,
|
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,,
|
||||||
@@ -583,7 +585,9 @@ Function,-,acosh,double,double
|
|||||||
Function,-,acoshf,float,float
|
Function,-,acoshf,float,float
|
||||||
Function,-,acoshl,long double,long double
|
Function,-,acoshl,long double,long double
|
||||||
Function,-,acosl,long double,long double
|
Function,-,acosl,long double,long double
|
||||||
Function,+,aligned_alloc,void*,"size_t, size_t"
|
Function,-,aligned_alloc,void*,"size_t, size_t"
|
||||||
|
Function,+,aligned_free,void,void*
|
||||||
|
Function,+,aligned_malloc,void*,"size_t, size_t"
|
||||||
Function,-,arc4random,__uint32_t,
|
Function,-,arc4random,__uint32_t,
|
||||||
Function,-,arc4random_buf,void,"void*, size_t"
|
Function,-,arc4random_buf,void,"void*, size_t"
|
||||||
Function,-,arc4random_uniform,__uint32_t,__uint32_t
|
Function,-,arc4random_uniform,__uint32_t,__uint32_t
|
||||||
@@ -985,6 +989,22 @@ Function,-,fdim,double,"double, double"
|
|||||||
Function,-,fdimf,float,"float, float"
|
Function,-,fdimf,float,"float, float"
|
||||||
Function,-,fdiml,long double,"long double, long double"
|
Function,-,fdiml,long double,"long double, long double"
|
||||||
Function,-,fdopen,FILE*,"int, const char*"
|
Function,-,fdopen,FILE*,"int, const char*"
|
||||||
|
Function,+,felica_alloc,FelicaData*,
|
||||||
|
Function,+,felica_calculate_mac_write,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*"
|
||||||
|
Function,+,felica_calculate_session_key,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, uint8_t*"
|
||||||
|
Function,+,felica_check_mac,_Bool,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*"
|
||||||
|
Function,+,felica_copy,void,"FelicaData*, const FelicaData*"
|
||||||
|
Function,+,felica_free,void,FelicaData*
|
||||||
|
Function,+,felica_get_base_data,FelicaData*,const FelicaData*
|
||||||
|
Function,+,felica_get_device_name,const char*,"const FelicaData*, NfcDeviceNameType"
|
||||||
|
Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*"
|
||||||
|
Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*"
|
||||||
|
Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t"
|
||||||
|
Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*"
|
||||||
|
Function,+,felica_reset,void,FelicaData*
|
||||||
|
Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*"
|
||||||
|
Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t"
|
||||||
|
Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*"
|
||||||
Function,-,feof,int,FILE*
|
Function,-,feof,int,FILE*
|
||||||
Function,-,feof_unlocked,int,FILE*
|
Function,-,feof_unlocked,int,FILE*
|
||||||
Function,-,ferror,int,FILE*
|
Function,-,ferror,int,FILE*
|
||||||
@@ -2137,6 +2157,7 @@ Function,+,loader_is_locked,_Bool,Loader*
|
|||||||
Function,+,loader_lock,_Bool,Loader*
|
Function,+,loader_lock,_Bool,Loader*
|
||||||
Function,+,loader_show_menu,void,Loader*
|
Function,+,loader_show_menu,void,Loader*
|
||||||
Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*"
|
Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*"
|
||||||
|
Function,+,loader_start_detached_with_gui_error,void,"Loader*, const char*, const char*"
|
||||||
Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*"
|
Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*"
|
||||||
Function,+,loader_unlock,void,Loader*
|
Function,+,loader_unlock,void,Loader*
|
||||||
Function,+,loading_alloc,Loading*,
|
Function,+,loading_alloc,Loading*,
|
||||||
@@ -2361,8 +2382,7 @@ Function,+,memchr,void*,"const void*, int, size_t"
|
|||||||
Function,+,memcmp,int,"const void*, const void*, size_t"
|
Function,+,memcmp,int,"const void*, const void*, size_t"
|
||||||
Function,+,memcpy,void*,"void*, const void*, size_t"
|
Function,+,memcpy,void*,"void*, const void*, size_t"
|
||||||
Function,-,memmem,void*,"const void*, size_t, const void*, size_t"
|
Function,-,memmem,void*,"const void*, size_t, const void*, size_t"
|
||||||
Function,-,memmgr_aux_pool_alloc,void*,size_t
|
Function,-,memmgr_alloc_from_pool,void*,size_t
|
||||||
Function,-,memmgr_aux_pool_get_free,size_t,
|
|
||||||
Function,+,memmgr_get_free_heap,size_t,
|
Function,+,memmgr_get_free_heap,size_t,
|
||||||
Function,+,memmgr_get_minimum_free_heap,size_t,
|
Function,+,memmgr_get_minimum_free_heap,size_t,
|
||||||
Function,+,memmgr_get_total_heap,size_t,
|
Function,+,memmgr_get_total_heap,size_t,
|
||||||
@@ -2370,7 +2390,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId
|
|||||||
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
|
Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId
|
||||||
Function,+,memmgr_heap_get_max_free_block,size_t,
|
Function,+,memmgr_heap_get_max_free_block,size_t,
|
||||||
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
|
Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId
|
||||||
Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*"
|
Function,+,memmgr_heap_printf_free_blocks,void,
|
||||||
|
Function,-,memmgr_pool_get_free,size_t,
|
||||||
Function,-,memmgr_pool_get_max_block,size_t,
|
Function,-,memmgr_pool_get_max_block,size_t,
|
||||||
Function,+,memmove,void*,"void*, const void*, size_t"
|
Function,+,memmove,void*,"void*, const void*, size_t"
|
||||||
Function,-,mempcpy,void*,"void*, const void*, size_t"
|
Function,-,mempcpy,void*,"void*, const void*, size_t"
|
||||||
@@ -3736,6 +3757,7 @@ Variable,+,message_red_255,const NotificationMessage,
|
|||||||
Variable,+,message_sound_off,const NotificationMessage,
|
Variable,+,message_sound_off,const NotificationMessage,
|
||||||
Variable,+,message_vibro_off,const NotificationMessage,
|
Variable,+,message_vibro_off,const NotificationMessage,
|
||||||
Variable,+,message_vibro_on,const NotificationMessage,
|
Variable,+,message_vibro_on,const NotificationMessage,
|
||||||
|
Variable,-,nfc_device_felica,const NfcDeviceBase,
|
||||||
Variable,-,nfc_device_mf_classic,const NfcDeviceBase,
|
Variable,-,nfc_device_mf_classic,const NfcDeviceBase,
|
||||||
Variable,-,nfc_device_mf_desfire,const NfcDeviceBase,
|
Variable,-,nfc_device_mf_desfire,const NfcDeviceBase,
|
||||||
Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase,
|
Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase,
|
||||||
|
|||||||
|
@@ -54,8 +54,8 @@ static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
|
|||||||
.PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE,
|
.PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE,
|
||||||
.MblockCount = CFG_BLE_MBLOCK_COUNT,
|
.MblockCount = CFG_BLE_MBLOCK_COUNT,
|
||||||
.AttMtu = CFG_BLE_MAX_ATT_MTU,
|
.AttMtu = CFG_BLE_MAX_ATT_MTU,
|
||||||
.SlaveSca = CFG_BLE_SLAVE_SCA,
|
.PeripheralSca = CFG_BLE_SLAVE_SCA,
|
||||||
.MasterSca = CFG_BLE_MASTER_SCA,
|
.CentralSca = CFG_BLE_MASTER_SCA,
|
||||||
.LsSource = CFG_BLE_LSE_SOURCE,
|
.LsSource = CFG_BLE_LSE_SOURCE,
|
||||||
.MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH,
|
.MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH,
|
||||||
.HsStartupTime = CFG_BLE_HSE_STARTUP_TIME,
|
.HsStartupTime = CFG_BLE_HSE_STARTUP_TIME,
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
#define GAP_MS_TO_SCAN_INTERVAL(x) ((uint16_t)((x) / 0.625))
|
#define GAP_MS_TO_SCAN_INTERVAL(x) ((uint16_t)((x) / 0.625))
|
||||||
|
|
||||||
// Also used as an indicator of whether the beacon had ever been configured
|
// Also used as an indicator of whether the beacon had ever been configured
|
||||||
#define GAP_MIN_ADV_INTERVAL_MS (20)
|
// AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
|
||||||
|
#define GAP_MIN_ADV_INTERVAL_MS (30u)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GapExtraBeaconConfig last_config;
|
GapExtraBeaconConfig last_config;
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ static GapConfig serial_template_config = {
|
|||||||
.bonding_mode = true,
|
.bonding_mode = true,
|
||||||
.pairing_method = GapPairingPinCodeShow,
|
.pairing_method = GapPairingPinCodeShow,
|
||||||
.conn_param = {
|
.conn_param = {
|
||||||
.conn_int_min = 0x18, // 30 ms
|
.conn_int_min = 0x18, // AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
|
||||||
.conn_int_max = 0x24, // 45 ms
|
.conn_int_max = 0x18, // 30 ms
|
||||||
.slave_latency = 0,
|
.slave_latency = 0,
|
||||||
.supervisor_timeout = 0,
|
.supervisor_timeout = 0,
|
||||||
}};
|
}};
|
||||||
|
|||||||
Reference in New Issue
Block a user