mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-22 20:43:07 +00:00
Merge branch 'fz-dev' into dev
This commit is contained in:
commit
473f9c0e44
41 changed files with 1812 additions and 128 deletions
|
@ -13,7 +13,7 @@
|
|||
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
||||
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
||||
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
||||
#define TEST_RANDOM_COUNT_PARSE 253
|
||||
#define TEST_RANDOM_COUNT_PARSE 273
|
||||
#define TEST_TIMEOUT 10000
|
||||
|
||||
static SubGhzEnvironment* environment_handler;
|
||||
|
@ -597,6 +597,13 @@ MU_TEST(subghz_decoder_smc5326_test) {
|
|||
"Test decoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_holtek_ht12x_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/holtek_ht12x_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
|
||||
}
|
||||
|
||||
//test encoders
|
||||
MU_TEST(subghz_encoder_princeton_test) {
|
||||
mu_assert(
|
||||
|
@ -730,6 +737,12 @@ MU_TEST(subghz_encoder_smc5326_test) {
|
|||
"Test encoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_holtek_ht12x_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek_ht12x.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_random_test) {
|
||||
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
|
||||
}
|
||||
|
@ -774,6 +787,7 @@ MU_TEST_SUITE(subghz) {
|
|||
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
||||
MU_RUN_TEST(subghz_decoder_ansonic_test);
|
||||
MU_RUN_TEST(subghz_decoder_smc5326_test);
|
||||
MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
|
||||
|
||||
MU_RUN_TEST(subghz_encoder_princeton_test);
|
||||
MU_RUN_TEST(subghz_encoder_came_test);
|
||||
|
@ -797,6 +811,7 @@ MU_TEST_SUITE(subghz) {
|
|||
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
||||
MU_RUN_TEST(subghz_encoder_ansonic_test);
|
||||
MU_RUN_TEST(subghz_encoder_smc5326_test);
|
||||
MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
|
||||
|
||||
MU_RUN_TEST(subghz_random_test);
|
||||
subghz_test_deinit();
|
||||
|
|
|
@ -43,7 +43,7 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexMfClassicKeys) {
|
||||
if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) {
|
||||
if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
|
||||
|
|
|
@ -53,10 +53,10 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt
|
|||
// Setup view
|
||||
if(state == DictAttackStateUserDictInProgress) {
|
||||
worker_state = NfcWorkerStateMfClassicDictAttack;
|
||||
dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict.");
|
||||
dict_attack_set_header(nfc->dict_attack, "MF Classic User Dictionary");
|
||||
dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
|
||||
|
||||
// If failed to load user dictionary - try flipper dictionary
|
||||
// If failed to load user dictionary - try the system dictionary
|
||||
if(!dict) {
|
||||
FURI_LOG_E(TAG, "User dictionary not found");
|
||||
state = DictAttackStateFlipperDictInProgress;
|
||||
|
@ -64,11 +64,11 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt
|
|||
}
|
||||
if(state == DictAttackStateFlipperDictInProgress) {
|
||||
worker_state = NfcWorkerStateMfClassicDictAttack;
|
||||
dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict.");
|
||||
dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper);
|
||||
dict_attack_set_header(nfc->dict_attack, "MF Classic System Dictionary");
|
||||
dict = mf_classic_dict_alloc(MfClassicDictTypeSystem);
|
||||
if(!dict) {
|
||||
FURI_LOG_E(TAG, "Flipper dictionary not found");
|
||||
// Pass through to let worker handle the failure
|
||||
// Pass through to let the worker handle the failure
|
||||
}
|
||||
}
|
||||
// Free previous dictionary
|
||||
|
@ -153,6 +153,15 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
|||
nfc_worker_stop(nfc->worker);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.event == NfcWorkerEventKeyAttackStart) {
|
||||
dict_attack_set_key_attack(
|
||||
nfc->dict_attack,
|
||||
true,
|
||||
nfc->dev->dev_data.mf_classic_dict_attack_data.current_sector);
|
||||
} else if(event.event == NfcWorkerEventKeyAttackStop) {
|
||||
dict_attack_set_key_attack(nfc->dict_attack, false, 0);
|
||||
} else if(event.event == NfcWorkerEventKeyAttackNextSector) {
|
||||
dict_attack_inc_key_attack_current_sector(nfc->dict_attack);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
|
||||
|
|
|
@ -12,7 +12,7 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
|
|||
|
||||
// Load flipper dict keys total
|
||||
uint32_t flipper_dict_keys_total = 0;
|
||||
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper);
|
||||
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeSystem);
|
||||
if(dict) {
|
||||
flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict);
|
||||
mf_classic_dict_free(dict);
|
||||
|
@ -26,11 +26,11 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
|
|||
}
|
||||
|
||||
widget_add_string_element(
|
||||
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys");
|
||||
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys");
|
||||
char temp_str[32];
|
||||
snprintf(temp_str, sizeof(temp_str), "Flipper list: %lu", flipper_dict_keys_total);
|
||||
snprintf(temp_str, sizeof(temp_str), "System dict: %lu", flipper_dict_keys_total);
|
||||
widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str);
|
||||
snprintf(temp_str, sizeof(temp_str), "User list: %lu", user_dict_keys_total);
|
||||
snprintf(temp_str, sizeof(temp_str), "User dict: %lu", user_dict_keys_total);
|
||||
widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
|
||||
widget_add_button_element(
|
||||
nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
|
||||
|
|
|
@ -91,7 +91,7 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
|
|||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) {
|
||||
if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) {
|
||||
if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
|
||||
|
|
|
@ -24,6 +24,8 @@ typedef struct {
|
|||
uint8_t keys_found;
|
||||
uint16_t dict_keys_total;
|
||||
uint16_t dict_keys_current;
|
||||
bool is_key_attack;
|
||||
uint8_t key_attack_current_sector;
|
||||
} DictAttackViewModel;
|
||||
|
||||
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
||||
|
@ -36,10 +38,19 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
||||
} else if(m->state == DictAttackStateRead) {
|
||||
char draw_str[32] = {};
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
|
||||
if(m->is_key_attack) {
|
||||
snprintf(
|
||||
draw_str,
|
||||
sizeof(draw_str),
|
||||
"Reuse key check for sector: %d",
|
||||
m->key_attack_current_sector);
|
||||
} else {
|
||||
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current);
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||
float dict_progress = m->dict_keys_total == 0 ?
|
||||
0 :
|
||||
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
||||
|
@ -49,13 +60,14 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||
if(progress > 1.0) {
|
||||
progress = 1.0;
|
||||
}
|
||||
elements_progress_bar(canvas, 5, 15, 120, progress);
|
||||
snprintf(draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total);
|
||||
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total);
|
||||
canvas_draw_str_aligned(canvas, 1, 28, AlignLeft, AlignTop, draw_str);
|
||||
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
||||
snprintf(
|
||||
draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total);
|
||||
canvas_draw_str_aligned(canvas, 1, 40, AlignLeft, AlignTop, draw_str);
|
||||
canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
|
||||
}
|
||||
elements_button_center(canvas, "Skip");
|
||||
}
|
||||
|
@ -113,6 +125,7 @@ void dict_attack_reset(DictAttack* dict_attack) {
|
|||
model->keys_found = 0;
|
||||
model->dict_keys_total = 0;
|
||||
model->dict_keys_current = 0;
|
||||
model->is_key_attack = false;
|
||||
furi_string_reset(model->header);
|
||||
},
|
||||
false);
|
||||
|
@ -235,3 +248,28 @@ void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tri
|
|||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) {
|
||||
furi_assert(dict_attack);
|
||||
with_view_model(
|
||||
dict_attack->view,
|
||||
DictAttackViewModel * model,
|
||||
{
|
||||
model->is_key_attack = is_key_attack;
|
||||
model->key_attack_current_sector = sector;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) {
|
||||
furi_assert(dict_attack);
|
||||
with_view_model(
|
||||
dict_attack->view,
|
||||
DictAttackViewModel * model,
|
||||
{
|
||||
if(model->key_attack_current_sector < model->sectors_total) {
|
||||
model->key_attack_current_sector++;
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
|
|
@ -38,3 +38,7 @@ void dict_attack_inc_keys_found(DictAttack* dict_attack);
|
|||
void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total);
|
||||
|
||||
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried);
|
||||
|
||||
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector);
|
||||
|
||||
void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack);
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 657 B |
|
@ -42,10 +42,12 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
|
|||
furi_assert(context);
|
||||
Hid* hid = context;
|
||||
bool connected = (status == BtStatusConnected);
|
||||
if(connected) {
|
||||
notification_internal_message(hid->notifications, &sequence_set_blue_255);
|
||||
} else {
|
||||
notification_internal_message(hid->notifications, &sequence_reset_blue);
|
||||
if(hid->transport == HidTransportBle) {
|
||||
if(connected) {
|
||||
notification_internal_message(hid->notifications, &sequence_set_blue_255);
|
||||
} else {
|
||||
notification_internal_message(hid->notifications, &sequence_reset_blue);
|
||||
}
|
||||
}
|
||||
hid_keynote_set_connected_status(hid->hid_keynote, connected);
|
||||
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
|
||||
|
@ -186,7 +188,9 @@ void hid_free(Hid* app) {
|
|||
furi_assert(app);
|
||||
|
||||
// Reset notification
|
||||
notification_internal_message(app->notifications, &sequence_reset_blue);
|
||||
if(app->transport == HidTransportBle) {
|
||||
notification_internal_message(app->notifications, &sequence_reset_blue);
|
||||
}
|
||||
|
||||
// Free views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);
|
||||
|
|
|
@ -25,6 +25,7 @@ typedef struct {
|
|||
bool back_pressed;
|
||||
bool connected;
|
||||
char key_string[5];
|
||||
HidTransport transport;
|
||||
} HidKeyboardModel;
|
||||
|
||||
typedef struct {
|
||||
|
@ -207,7 +208,7 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
|
|||
HidKeyboardModel* model = context;
|
||||
|
||||
// Header
|
||||
if(!model->connected) {
|
||||
if((!model->connected) && (model->transport == HidTransportBle)) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard");
|
||||
|
@ -361,6 +362,12 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
|
|||
view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback);
|
||||
view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_keyboard->view,
|
||||
HidKeyboardModel * model,
|
||||
{ model->transport = bt_hid->transport; },
|
||||
true);
|
||||
|
||||
return hid_keyboard;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ typedef struct {
|
|||
bool ok_pressed;
|
||||
bool back_pressed;
|
||||
bool connected;
|
||||
HidTransport transport;
|
||||
} HidKeynoteModel;
|
||||
|
||||
static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
||||
|
@ -39,11 +40,14 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
|||
HidKeynoteModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
|
||||
|
||||
|
@ -186,6 +190,9 @@ HidKeynote* hid_keynote_alloc(Hid* hid) {
|
|||
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
|
||||
view_set_input_callback(hid_keynote->view, hid_keynote_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true);
|
||||
|
||||
return hid_keynote;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ typedef struct {
|
|||
bool down_pressed;
|
||||
bool ok_pressed;
|
||||
bool connected;
|
||||
HidTransport transport;
|
||||
} HidMediaModel;
|
||||
|
||||
static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
||||
|
@ -41,11 +42,14 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
|
|||
HidMediaModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
@ -190,6 +194,9 @@ HidMedia* hid_media_alloc(Hid* hid) {
|
|||
view_set_draw_callback(hid_media->view, hid_media_draw_callback);
|
||||
view_set_input_callback(hid_media->view, hid_media_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true);
|
||||
|
||||
return hid_media;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ typedef struct {
|
|||
bool left_mouse_held;
|
||||
bool right_mouse_pressed;
|
||||
bool connected;
|
||||
HidTransport transport;
|
||||
} HidMouseModel;
|
||||
|
||||
static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
|
@ -27,11 +28,14 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
|||
HidMouseModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
@ -198,6 +202,9 @@ HidMouse* hid_mouse_alloc(Hid* hid) {
|
|||
view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback);
|
||||
view_set_input_callback(hid_mouse->view, hid_mouse_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true);
|
||||
|
||||
return hid_mouse;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ typedef struct {
|
|||
bool connected;
|
||||
bool running;
|
||||
uint8_t counter;
|
||||
HidTransport transport;
|
||||
} HidMouseJigglerModel;
|
||||
|
||||
static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
|
||||
|
@ -23,11 +24,14 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
|
|||
HidMouseJigglerModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler");
|
||||
|
||||
|
@ -120,6 +124,12 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
|
|||
hid_mouse_jiggler->timer = furi_timer_alloc(
|
||||
hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler);
|
||||
|
||||
with_view_model(
|
||||
hid_mouse_jiggler->view,
|
||||
HidMouseJigglerModel * model,
|
||||
{ model->transport = hid->transport; },
|
||||
true);
|
||||
|
||||
return hid_mouse_jiggler;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ typedef struct {
|
|||
bool ok_pressed;
|
||||
bool connected;
|
||||
bool is_cursor_set;
|
||||
HidTransport transport;
|
||||
} HidTikTokModel;
|
||||
|
||||
static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
|
||||
|
@ -26,11 +27,14 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
|
|||
HidTikTokModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
@ -207,6 +211,9 @@ HidTikTok* hid_tiktok_alloc(Hid* bt_hid) {
|
|||
view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback);
|
||||
view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true);
|
||||
|
||||
return hid_tiktok;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ extern "C" {
|
|||
typedef enum {
|
||||
ColorWhite = 0x00,
|
||||
ColorBlack = 0x01,
|
||||
ColorXOR = 0x02,
|
||||
} Color;
|
||||
|
||||
/** Fonts enumeration */
|
||||
|
|
|
@ -41,6 +41,31 @@ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width,
|
|||
canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2);
|
||||
}
|
||||
|
||||
void elements_progress_bar_with_text(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
float progress,
|
||||
const char* text) {
|
||||
furi_assert(canvas);
|
||||
furi_assert((progress >= 0.0f) && (progress <= 1.0f));
|
||||
uint8_t height = 11;
|
||||
|
||||
uint8_t progress_length = roundf(progress * (width - 2));
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_rframe(canvas, x, y, width, height, 3);
|
||||
|
||||
canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2);
|
||||
|
||||
canvas_set_color(canvas, ColorXOR);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, x + width / 2, y + 2, AlignCenter, AlignTop, text);
|
||||
}
|
||||
|
||||
void elements_scrollbar_pos(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
|
|
|
@ -31,6 +31,23 @@ extern "C" {
|
|||
*/
|
||||
void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, float progress);
|
||||
|
||||
/** Draw progress bar with text.
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
* @param x progress bar position on X axis
|
||||
* @param y progress bar position on Y axis
|
||||
* @param width progress bar width
|
||||
* @param progress progress (0.0 - 1.0)
|
||||
* @param text text to draw
|
||||
*/
|
||||
void elements_progress_bar_with_text(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
float progress,
|
||||
const char* text);
|
||||
|
||||
/** Draw scrollbar on canvas at specific position.
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
|
|
|
@ -544,6 +544,18 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) {
|
|||
model->item_cnt);
|
||||
}
|
||||
|
||||
uint32_t folder_item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1);
|
||||
if(folder_item_cnt == 0) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
canvas_width(canvas) / 2,
|
||||
canvas_height(canvas) / 2,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
"<Empty>");
|
||||
}
|
||||
|
||||
furi_string_free(filename);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,43 +34,43 @@ ENTER
|
|||
STRING _.-------.._ -,
|
||||
ENTER
|
||||
HOME
|
||||
STRING .-"```"--..,,_/ /`-, -, \
|
||||
STRING .-"'''"--..,,_/ /'-, -, \
|
||||
ENTER
|
||||
HOME
|
||||
STRING .:" /:/ /'\ \ ,_..., `. | |
|
||||
STRING .:" /:/ /'\ \ ,_..., '. | |
|
||||
ENTER
|
||||
HOME
|
||||
STRING / ,----/:/ /`\ _\~`_-"` _;
|
||||
STRING / ,----/:/ /'\ _\~'_-"' _;
|
||||
ENTER
|
||||
HOME
|
||||
STRING ' / /`"""'\ \ \.~`_-' ,-"'/
|
||||
STRING ' / /'"""'\ \ \.~'_-' ,-"'/
|
||||
ENTER
|
||||
HOME
|
||||
STRING | | | 0 | | .-' ,/` /
|
||||
STRING | | | 0 | | .-' ,/' /
|
||||
ENTER
|
||||
HOME
|
||||
STRING | ,..\ \ ,.-"` ,/` /
|
||||
STRING | ,..\ \ ,.-"' ,/' /
|
||||
ENTER
|
||||
HOME
|
||||
STRING ; : `/`""\` ,/--==,/-----,
|
||||
STRING ; : '/'""\' ,/--==,/-----,
|
||||
ENTER
|
||||
HOME
|
||||
STRING | `-...| -.___-Z:_______J...---;
|
||||
STRING | '-...| -.___-Z:_______J...---;
|
||||
ENTER
|
||||
HOME
|
||||
STRING : ` _-'
|
||||
STRING : ' _-'
|
||||
ENTER
|
||||
HOME
|
||||
STRING _L_ _ ___ ___ ___ ___ ____--"`
|
||||
STRING _L_ _ ___ ___ ___ ___ ____--"'
|
||||
ENTER
|
||||
HOME
|
||||
STRING | __|| | |_ _|| _ \| _ \| __|| _ \
|
||||
STRING | __|| | |_ _|| _ \| _ \| __|| _ \
|
||||
ENTER
|
||||
HOME
|
||||
STRING | _| | |__ | | | _/| _/| _| | /
|
||||
STRING | _| | |__ | | | _/| _/| _| | /
|
||||
ENTER
|
||||
HOME
|
||||
STRING |_| |____||___||_| |_| |___||_|_\
|
||||
STRING |_| |____||___||_| |_| |___||_|_\
|
||||
ENTER
|
||||
HOME
|
||||
ENTER
|
||||
|
|
8
assets/unit_tests/subghz/holtek_ht12x.sub
Normal file
8
assets/unit_tests/subghz/holtek_ht12x.sub
Normal file
|
@ -0,0 +1,8 @@
|
|||
Filetype: Flipper SubGhz Key File
|
||||
Version: 1
|
||||
Frequency: 433920000
|
||||
Preset: FuriHalSubGhzPresetOok650Async
|
||||
Protocol: Holtek_HT12X
|
||||
Bit: 12
|
||||
Key: 00 00 00 00 00 00 0F FB
|
||||
TE: 205
|
10
assets/unit_tests/subghz/holtek_ht12x_raw.sub
Normal file
10
assets/unit_tests/subghz/holtek_ht12x_raw.sub
Normal file
|
@ -0,0 +1,10 @@
|
|||
Filetype: Flipper SubGhz RAW File
|
||||
Version: 1
|
||||
Frequency: 433920000
|
||||
Preset: FuriHalSubGhzPresetOok650Async
|
||||
Protocol: RAW
|
||||
RAW_Data: 97 -264 65 -1890 231 -366 165 -232 99 -166 297 -68 401 -100 97 -100 759 -264 823 -132 919 -98 65 -230 367 -98 1055 -52 51 -104 101 -126 173 -148 151 -100 153 -222 77 -288 53 -280 331 -212 81 -108 246 -156 267 -268 183 -1260 53 -622 51 -468 183 -264 105 -990 77 -421 75 -52 53 -214 185 -504 75 -563 131 -338 165 -82 133 -80 53 -236 231 -208 282 -836 187 -406 81 -102 181 -54 107 -415 81 -54 109 -298 55 -406 323 -136 299 -136 437 -52 133 -294 53 -110 133 -420 333 -402 269 -574 79 -270 163 -594 124 -52 51 -320 79 -324 109 -78 105 -236 79 -292 53 -432 181 -110 187 -268 107 -78 79 -188 79 -78 75 -130 75 -128 103 -628 79 -322 79 -136 81 -196 85 -296 51 -349 159 -325 131 -80 131 -80 402 -110 55 -194 109 -108 53 -108 135 -224 81 -162 57 -228 161 -357 105 -54 103 -347 279 -294 422 -152 51 -104 99 -146 147 -205 77 -216 107 -52 181 -52 107 -158 105 -52 133 -134 139 -418 291 -102 103 -106 105 -584 103 -540 53 -52 153 -208 227 -128 307 -184 105 -328 109 -862 129 -196 55 -244 51 -1501 55 -82 81 -326 79 -500 103 -406 207 -130 75 -126 155 -202 53 -376 135 -158 53 -428 107 -467 167 -138 81 -106 79 -346 157 -84 143 -144 85 -136 53 -308 189 -248 79 -711 109 -592 107 -220 107 -104 77 -583 131 -158 53 -110 135 -191 107 -748 105 -478 77 -398 107 -138 83 -410 127 -477 103 -162 119 -72 103 -442 129 -237 107 -664 175 -74 147 -406 53 -78 127 -178 51 -102 101 -259 125 -202 257 -210 77 -52 77 -130 251 -230 77 -76 77 -178 155 -98 97 -100 153 -54 185 -82 79 -82 135 -398 241 -130 77 -76 311 -210 53 -238 181 -404 241 -352 189 -270 265 -164 215 -998 157 -82 77 -106 51 -54 75 -100 328 -178 103 -224 253 -341 147 -334 446 -76 125 -100 75 -126 173 -690 75 -250 51 -554 81 -300 53 -188 53 -106 265 -1024 105 -646 221 -82 191 -362 75 -234 131 -128 83 -224 55 -240 77 -376 157 -78 187 -358 389 -76 103 -128 199 -513 83 -492 129 -618 123 -52 129 -52 79 -376 101 -688 105 -110 55 -84 179 -152 51 -486 358 -276 99 -74 73 -679 51 -210 113 -196 365 -104 223 -104 53 -430 55 -140 55 -84 153 -202 181 -358 149 -820 103 -80 215 -108 309 -368 105 -538 154 -732 233 -588 53 -253 155 -288 55 -136 55715 -102 407 -100 259 -78 157 -130 206 -52 258 -78 441 -104 77 -80 492 -104 182 -104 79 -155
|
||||
RAW_Data: 287 -78 807 -104 1588 -52 2952 -78 1018 -240 341 -262 361 -258 335 -280 309 -100 509 -280 307 -302 307 -6968 321 -290 293 -298 323 -294 289 -314 285 -334 285 -330 257 -330 281 -328 293 -310 281 -124 479 -330 281 -326 267 -6994 295 -324 267 -320 287 -340 259 -332 257 -358 257 -354 257 -328 257 -352 257 -352 255 -150 455 -360 255 -326 255 -7024 253 -352 255 -352 255 -354 267 -326 269 -352 239 -346 261 -362 233 -358 257 -356 255 -152 455 -334 255 -354 255 -7006 267 -342 267 -352 241 -350 263 -342 259 -362 233 -356 257 -356 231 -378 231 -380 231 -150 455 -358 231 -378 229 -7050 229 -378 229 -352 253 -352 239 -372 239 -380 243 -348 235 -368 231 -388 231 -356 233 -176 431 -384 229 -380 231 -7028 239 -368 241 -372 241 -350 239 -372 233 -366 233 -384 231 -358 231 -380 231 -378 231 -176 429 -384 205 -402 205 -7064 213 -380 243 -378 215 -374 233 -368 233 -386 231 -382 205 -382 231 -380 229 -380 231 -176 429 -386 203 -378 231 -7062 229 -378 229 -378 229 -378 229 -378 213 -396 203 -404 203 -404 203 -404 203 -404 203 -198 431 -376 215 -376 239 -7064 235 -364 231 -386 231 -384 205 -408 205 -380 231 -380 231 -380 231 -378 207 -404 205 -200 431 -384 205 -404 205 -7118 205 -408 205 -406 205 -408 205 -408 205 -408 205 -436 179 -440 283 -460 75 -1486 105 -54 103 -784 107 -218 113 -824 125 -2140 85 -583 51 -1291 53 -722 113 -622 107 -1176 51 -52 75 -152 113 -56 111 -784 105 -436 81 -112 85 -272 53 -406 81 -1345 105 -3008 53 -1418 105 -254 107 -874 131 -818 53 -595 77 -130 123 -1088 213 -724 131 -793 99 -428 75 -288 51 -334 101 -1471 81 -274 53 -164 107 -2618 129 -532 55 -366 297 -160 83 -166 184 -208 57 -196 328 -1640 235 -1743 79 -594 51 -76 77 -270 163 -728 51 -408 55 -354 101 -126 153 -184 133 -392 53 -604 77 -804 127 -965 51 -78 103 -668 262 -210 237 -439 75 -340 77 -106 105 -1775 79 -284 51 -3100 157 -433 55 -1355 107 -198 165 -82 79 -770 79 -1587 81 -638 79 -530 103 -703 51 -396 71 -956 151 -248 53 -553 103 -154 75 -806 75 -660 53 -698 127 -1210 53 -3175 79 -608 245 -1590 207 -164 107 -232 51 -1094 99 -695 135 -955 53 -804 217 -587 55 -1452 163 -1232 79 -968 79 -720 81 -1110 129 -1194 105 -1736 79 -386 79 -184 77 -652 75 -2442 103 -555 129 -125 75 -1115 81 -108 135 -980 53 -808 105 -1001 155 -1068 131 -850 99 -268 51 -1106 159 -2408 79 -612 51 -1191
|
||||
RAW_Data: 135 -212 77 -2816 137 -980 51 -1921 79 -1554 53 -906 83 -1042 99 -5161 51 -290 75 -558 51 -260 77 -738 99 -2128 105 -1384 53 -108 79 -188 55 -830 77 -5690 109 -2652 51 -1374 53 -2469 79 -2163 203 -152 71 -1459 79 -2741 105 -262 75 -1194 75 -228 153 -210 107 -3236 107 -1500 77 -3562 79 -170 55 -540 53 -1270 51 -304 51 -2071 137 -134 105 -266 101 -834 51 -8117 73 -3292 81 -732 181 -1539 53 -132 109 -1375 77 -132 81 -1061 77 -1620 79 -1373 105 -643 183 -190 53 -620 77 -1633 55 -931 51 -398 135 -924 111 -1122 51 -128 125 -2361 97 -2571 51 -3563 51 -794 77 -844 81 -788 81 -2040 77 -232 77 -1000 131 -322 73 -848 55 -336 107 -1546 81 -602 79 -954 107 -1824 73 -1131 240 -1048 197 -2442 73 -720 127 -78 77 -574 79 -624 101 -5754 103 -1829 149 -719 77 -982 53 -250 53 -634 161 -2201 75 -3483 149 -356 73 -98 123 -176 75 -702 231 -1633 121 -704 53 -1163 51 -1227 73 -368 123 -258 103 -530 279 -628 53 -884 55 -82 101 -102 135 -162 107 -1264 159 -3370 77 -3538 131 -130 81 -2460 129 -588 51 -514 77 -376 53 -616 53 -956 51 -1508 53 -1044 53 -1160 261 -442 53 -640 83 -300 79 -1723 75 -124 125 -532 103 -500 79 -764 53 -1220 279 -458 77 -548 159 -450 223 -718 55 -716 51 -716 203 -732 79 -6974 53 -1086 51 -1794 79 -882 53 -4221 53 -1070 77 -1126 75 -2307 155 -1233 131 -156 111 -1352 51 -478 51 -154 75 -724 51 -635 103 -652 77 -294 75 -268 81 -1046 177 -1778 51 -298 109 -336 77 -80 53 -192 133 -556 99 -394 81 -654 51 -106 79 -1893 55 -240 55 -136 55 -2680 77 -136 81 -164 53 -576 135 -3304 131 -654 103 -788 53 -224 187 -354 79 -1872 81 -290 207 -410 77 -1132 55 -112 57 -512 51 -2736 123 -404 239 -1283 51 -1526 213 -720 133 -354 109 -396 77 -1518 105 -632 53 -164 53 -1090 55 -730 79 -1705 75 -370 75 -378 79 -2022 81 -2480 103 -1108 79 -106 53 -1784 105 -512 219 -669 179 -795 105 -376 105 -514 53 -402 53 -282 83 -554 133 -448 105 -972 79 -490 81 -1184 77 -216 109 -132 51 -164 135 -1570 51 -1329 79 -1218 53 -2450 75 -1246 51 -3825 77 -1114 53 -1718 51 -846 79 -826 165 -306 75 -3821 51 -606 55 -880 107 -56 55 -5505 53 -504 79 -1356 77 -569 51 -2268 149 -358 53 -1979 51 -106 103 -176 221 -508 51 -110 53 -3896 127 -234 79 -1012 79 -84 243 -2860 53 -1010 73 -286 133 -2198 151 -370 129 -1942 101 -154 109 -702 79 -696 51 -302
|
||||
RAW_Data: 51 -744 83 -496 79 -672 77 -170 111 -264 103 -472 51 -524 75 -1172 51 -240 51963 -254 333 -278 333 -276 333 -278 305 -302 307 -76 531 -280 331 -276 331 -6938 347 -268 319 -272 319 -292 311 -312 283 -306 309 -304 283 -328 293 -310 281 -326 293 -110 479 -322 295 -322 291 -6964 283 -334 283 -304 283 -328 283 -328 281 -328 255 -354 265 -338 255 -352 255 -352 255 -124 481 -338 277 -328 279 -6984 269 -350 265 -320 287 -340 257 -332 257 -358 255 -356 255 -328 257 -354 257 -352 255 -150 455 -360 229 -376 231 -7040 239 -348 267 -350 239 -346 261 -364 231 -360 257 -356 231 -380 231 -378 231 -352 257 -150 453 -360 229 -378 229 -7032 239 -372 227 -378 227 -380 241 -352 241 -374 233 -366 233 -384 233 -356 231 -380 231 -176 431 -384 229 -354 255 -7030 231 -376 231 -376 229 -378 229 -378 229 -352 255 -352 255 -352 229 -378 229 -378 229 -174 427 -384 229 -376 241 -7040 241 -350 235 -368 233 -388 231 -358 233 -380 231 -380 229 -380 231 -380 229 -380 211 -192 421 -366 227 -380 227 -7068 239 -378 215 -402 209 -394 207 -388 231 -386 231 -384 205 -408 205 -408 205 -408 205 -202 409 -412 207 -1400 105 -1002 51 -9495 53 -250 57 -1093 155 -124 73 -344 75 -2759 83 -468 53 -738 77 -134 53 -1581 51 -106 127 -1209 121 -956 51 -918 83 -276 85 -1696 125 -618 81 -1666 51 -152 101 -1324 107 -54 141 -586 75 -784 55 -1828 51 -4052 81 -480 53 -218 141 -1346 105 -1152 127 -776 53 -426 135 -390 105 -939 81 -887 71 -492 107 -1311 105 -1844 101 -1340 77 -2586 51 -2637 51 -1626 105 -54 53 -1672 151 -2830 57 -3143 51 -1859 79 -929 179 -78 77 -890 73 -894 79 -80 79 -1184 53 -323 53 -1344 79 -636 53 -1808 55 -3048 79 -2287 53 -572 51 -822 51 -608 77 -1772 75 -2521 79 -162 81 -664 163 -110 83 -524 53 -930 53 -1816 79 -1305 51 -816 53 -1358 55 -822 55 -594 81 -2230 55 -234 77 -600 201 -3174 151 -2534 71 -122 51 -1370 81 -3130 127 -236 79 -728 101 -1472 53 -800 127 -528 51 -802 77 -52 99 -3144 77 -6346 51 -1090 81 -588 79 -292 169 -2345 107 -370 187 -1218 81 -296 75 -696 51 -516 77 -2154 75 -558 75 -816 103 -2200 125 -2766 229 -376 151 -3375 79 -1466 53 -535 167 -524 217 -54 131 -3408 51 -54 109 -1886 77 -732 83 -536 99 -3128 103 -168 57 -1852 51 -574 79 -296 155 -844 99 -767 147 -2406 99 -1014 81 -4460 175 -226 195 -454 73 -2236 53 -818 155 -352 83 -752 79 -5083 51 -1716
|
||||
RAW_Data: 77 -1925 51 -1760 81 -162 53 -1469 79 -362 53 -471 103 -750 53 -562 127 -238 79 -250 107 -52 53 -210 83 -504 79 -1566 107 -82 79 -968 53 -458 131 -1944 301 -594 79 -382 51 -1196 79 -3963 183 -670 111 -360 105 -1990 53 -1084 79 -658 73 -301 73 -264 75 -1335 79 -1110 81 -1856 159 -3986 75 -1262 53 -1189 53 -158 53 -903 81 -1135 133 -350 107 -178 151 -1096 73 -2734 81 -1280 107 -950 79 -52 53 -5912 53 -1653 111 -1601 79 -742 51 -1104 55 -1254 51 -867 53 -136 109 -813 79 -1082 53 -395 53 -54 109 -504 79 -218 109 -114 57 -1620 157 -3003 79 -656 53 -510 209 -1933 107 -1197 159 -5508 79 -1164 77 -1466 77 -742 101 -124 71 -2049 85 -144 109 -1304 105 -2310 55 -381 83 -114 113 -944 103 -184 83 -558 55 -2064 109 -760 75 -1036 77 -574 51 -134 131 -224 57 -104 53 -440 53 -1262 183 -2454 51 -1966 73 -1950 125 -1095 51 -480 121 -1994 57 -1930 103 -786 79 -2272 105 -3312 51 -746 127 -144 133 -1608 77 -692 51 -1136 53 -164 55 -573 55 -3110 53 -1558 105 -6248 53 -1051 111 -886 105 -2234 103 -106 53 -1256 101 -1446 111 -974 79 -851 81 -136 193 -3392 83 -582 103 -1197 111 -196 55 -906 51 -742 77 -2038 71 -686 53 -1943 51 -134 51 -852 51 -1658 133 -2050 161 -388 77 -326 81 -412 55 -1137 81 -3256 55 -1516 53 -1414 117 -372 51 -1144 199 -3087 51 -430 75 -1856 151 -128 95 -192 398 -1207 77 -280 51 -2716 51 -808 53 -78 77 -1524 109 -54 79 -410 79 -132 53 -770 109 -2066 185 -368 131 -2102 125 -1037 75 -780 127 -128 51 -176 53 -1982 77 -140 111 -1046 109 -3166 169 -1956 77 -4040 79 -2778 73 -204 129 -1546 82945 -150 359 -252 333 -76 533 -280 319 -286 305 -6960 319 -300 323 -270 317 -290 313 -308 283 -306 309 -304 283 -328 291 -310 281 -326 281 -124 481 -334 279 -302 305 -6970 293 -318 301 -304 279 -328 279 -328 277 -330 277 -330 293 -298 295 -320 287 -314 283 -128 489 -334 255 -330 281 -7012 265 -340 265 -344 251 -354 253 -354 269 -322 293 -322 263 -344 259 -336 257 -360 257 -152 457 -358 231 -354 257 -7060 237 -364 255 -354 237 -366 255 -354 255 -354 267 -352 241 -352 265 -348 261 -342 259 -154 439 -388 231 -358 255 -7114 237 -370 235 -394 233 -362 233 -386 233 -386 233 -386 233 -386 231 -388 233 -414 233 -180 441 -392 233 -1832 53 -564 53 -370 289 -867 201 -78 103 -352 213 -586 103 -1226 165 -112 55 -300 105 -975 107 -358 77 -410 55 -1777 51 -973 51 -828
|
|
@ -168,3 +168,8 @@ RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139
|
|||
RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144
|
||||
RAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940
|
||||
RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617
|
||||
RAW_Data: 77 -76 131 -244 81 -210 55 -1428 53 -344 53 -238 51 -448 51 -804 125 -1490 51 -452 79 -1816 51 -176 197 -700 133 -563 51 -386 79 -474 109 -626 55 -266 103 -616 283 -1932 51 -1034 51 -2809 75 -244 83 -5339 77 -260 105 -839 107 -1806 53 -1408 81 -810 135 -488 187 -1469 73 -2596 75 -74 51 -726 113 -136 83 -406 55 -194 133 -606 55 -1018 55 -1774 51 -1954 75 -910 51 -944 137 -1337 51 -1606 101 -566 75 -584 51 -1470 133 -242 159 -2798 51 -1568 97 -100 71 -556 77 -1234 53 -320 53 -274 68337 -252 333 -278 333 -74 533 -280 307 -276 331 -6948 347 -262 329 -278 329 -278 329 -278 303 -304 303 -302 303 -304 303 -278 329 -278 329 -74 533 -294 325 -270 317 -6944 335 -282 309 -304 309 -304 281 -328 293 -310 281 -326 281 -326 281 -326 279 -302 305 -100 503 -306 305 -302 293 -6992 295 -294 291 -314 285 -336 283 -334 257 -328 283 -328 291 -310 281 -328 279 -328 279 -124 479 -332 279 -328 255 -7012 295 -324 271 -330 267 -346 261 -342 259 -334 257 -358 255 -356 257 -354 231 -354 255 -152 453 -356 257 -352 255 -7024 255 -352 257 -352 255 -354 255 -326 257 -352 255 -352 255 -354 255 -352 253 -352 255 -150 453 -356 255 -354 253 -7030 255 -352 267 -344 251 -354 253 -354 253 -354 267 -322 267 -350 261 -344 257 -364 231 -154 459 -360 257 -354 231 -7062 231 -380 231 -380 231 -352 257 -352 257 -352 257 -352 257 -352 257 -352 255 -354 231 -174 457 -360 229 -378 229 -7084 239 -364 239 -366 229 -380 229 -378 229 -380 229 -378 255 -354 255 -354 255 -352 255 -150 457 -364 253 -354 255 -9542 101 -3126 53 -814 109 -406 51 -162 109 -2219 183 -496 103 -1369 81 -603 99 -2172 79 -1103 75 -676 77 -560 103 -378 51 -654 95 -888 155 -1322 111 -1626 53 -182 51 -166 83 -52 181 -182 71 -2132 77 -2839 103 -4022 79 -362 81 -466 75 -970 203 -998 51 -2085 51 -1853 99 -328 75 -346 55 -1949 79 -2648 79 -434 75 -6757 51 -1920 109 -306 51 -612 101 -996 77 -764 81 -790 125 -1489 99 -430 77 -4142 165 -372 101 -198 71 -1688 51 -1636 99 -434 81 -794 135 -1973 79 -188 109 -2678 81 -196 109 -2099 51 -504 77 -1854 51 -910 107 -948 75 -122 131 -78 79 -1781 103 -3344 111 -406 79 -184 51 -408 103 -54 79 -1474 127 -1789 213 -683 131 -348 161 -5237 53 -2675 101 -52 105 -474 103 -1336 99 -3548 105 -1724 161 -2180 107 -2514 97 -3784 51 -910 77 -505 71 -494 131 -1154 79 -2295 75 -350 161 -274 81 -222
|
||||
RAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 187 -574 79 -332 51 -3765 51 -1042 75 -1413 103 -2163 75 -218 73 -4118 73 -716 51 -1720 51 -176 145 -817 79 -602 55 -1270 53 -2290 81 -346 79 -1840 53 -596 97 -1135 155 -1672 157 -1150 53 -52 101 -2753 153 -1546 158 -698 79 -2962 215 -490 161 -766 51 -2170 55 -811 51 -694 51 -1461 103 -2590 149 -3785 130 -54 103 -1108 103 -3978 51 -1626 81 -1825 109 -452 129 -1000 79 -1651 157 -276 53 -104 81 -2440 81 -1780 53 -1554 51 -512 131 -2508 99 -176 73 -914 51 -76 81 -202 111 -690 109 -790 109 -584 53 -244 79 -706 73 -550 129 -142 127 -546 77 -296 53 -874 105 -2623 51 -1004 77 -3131 79 -552 81 -2008 187 -1168 55 -1173 51 -2146 99 -700 51 -1580 101 -252 75 -474 77 -569 73 -5817 77 -614 77 -712 149 -228 73 -562 201 -274 127 -648 77 -578 75 -1810 109 -2106 171 -5996 81 -366 159 -274 55 -1228 77 -3386 53 -106 81 -1024 133 -2331 53 -2636 53 -780 149 -842 79 -2288 53 -2807 107 -1410 51 -620 75 -428 71 -272 75 -1140 103 -4912 55 -2261 53 -716 53 -3093 109 -502 111 -1492 53 -4317 51 -500 83 -338 129 -698 105 -1565 103 -1874 105 -344 77 -546 79 -2826 105 -260 75 -616 103 -1254 113 -2687 77 -977 73 -246 97 -1054 109 -4681 67101 -252 357 -252 333 -276 333 -252 355 -254 333 -276 331 -6932 347 -272 353 -242 343 -268 339 -286 309 -282 309 -306 307 -304 283 -328 281 -328 281 -102 505 -308 291 -314 279 -6996 281 -326 279 -328 277 -328 277 -328 279 -328 277 -328 279 -328 279 -328 277 -330 277 -124 483 -322 297 -298 293 -6972 283 -336 281 -332 257 -330 281 -328 283 -328 281 -330 265 -338 255 -352 253 -352 255 -150 455 -336 279 -328 279 -6992 295 -322 265 -348 261 -342 259 -334 259 -356 257 -356 255 -330 257 -354 257 -352 255 -152 455 -358 239 -368 253 -7026 243 -352 243 -350 265 -344 261 -362 231 -358 255 -356 257 -354 231 -380 229 -354 255 -150 455 -360 231 -376 231 -7050 229 -378 229 -378 229 -378 229 -378 229 -378 229 -378 229 -352 255 -352 253 -352 255 -150 455 -362 229 -378 227 -7050 253 -354 229 -378 229 -378 229 -378 227 -378 229 -378 229 -378 229 -378 229 -376 229 -174 431 -374 243 -378 241 -7032 261 -370 233 -362 233 -384 233 -356 233 -380 231 -380 231 -380 231 -380 231 -378 231 -176 431 -384 231 -380 211 -7114 231 -384 231 -384 205 -408 207 -408 207 -408 231 -384 231 -412 207 -412 181 -530 77 -1144
|
||||
RAW_Data: 79 -4798 53 -918 83 -4847 51 -755 103 -732 81 -388 55 -1026 77 -1506 101 -242 107 -469 51 -2026 79 -686 77 -348 51 -104 131 -860 129 -148 73 -446 75 -440 97 -306 99 -600 51 -626 105 -1350 95 -674 83 -230 119 -1714 135 -396 155 -1111 109 -652 111 -482 51 -506 55 -1715 103 -968 207 -1156 81 -164 57 -404 99 -508 205 -126 75 -1417 51 -186 77 -588 53 -54 103 -2854 73 -1010 53 -800 51 -2494 53 -106 105 -52 51 -104 79 -1116 51 -654 103 -220 77 -162 71 -5385 137 -2232 79 -1159 79 -250 57 -108 79 -164 107 -1660 79 -3927 129 -992 73 -1913 51 -1430 51 -1498 55 -514 103 -586 81 -386 53 -2402 175 -1994 85 -3431 53 -3209 99 -372 79 -78 53 -1338 75 -682 97 -680 51 -206 101 -1708 101 -452 131 -1397 161 -2272 53 -456 77 -1413 193 -270 109 -466 53 -2432 77 -222 189 -474 107 -774 171 -192 79 -1327 75 -2141 51 -908 135 -3866 75 -804 129 -468 101 -1040 79 -1470 55 -869 77 -1448 105 -160 55 -1916 240 -588 79 -1587 53 -922 79 -2292 181 -1448 51 -552 77 -2189 75 -2545 77 -384 300 -2478 101 -1092 73 -558 79 -132 105 -884 103 -1177 109 -880 79 -2431 109 -1006 105 -468 53 -1378 235 -684 75 -285 73 -604 129 -528 77 -1582 51 -1240 105 -2750 75 -252 51 -1024 95 -1891 51 -864 107 -326 83 -887 159 -1058 163 -322 105 -722 83 -388 81 -936 155 -880 55 -220 83 -2123 135 -2100 73 -1926 103 -1633 149 -526 51 -324 51 -1538 103 -164 137 -964 81 -152 111 -781 225 -655 53 -2888 105 -151 131 -454 53 -4109 77 -1052 53 -178 163 -910 51 -733 207 -2070 53 -474 79 -54 53 -818 51 -1228 53 -2262 79 -788 79 -480 73 -2747 83 -316 183 -1880 105 -862 53 -662 53 -2287 153 -1630 51 -817 243 -806 55 -510 51 -1389 75 -986 135 -498 109 -532 131 -5521 99 -2948 209 -764 75 -1168 75 -886 83 -2065 53 -710 51 -596 77 -374 73 -628 99 -732 51 -202 73 -632 53 -222 55 -511 79 -4884 53 -1826 81 -1266 107 -356 55 -110 113 -280 83 -756 169 -252 81 -1854 51 -1556 157 -258 75 -748 53 -1438 291 -244 71 -1092 77 -1220 229 -1055 181 -1182 71 -1284 77 -864 79 -138 53 -160 53 -952 81 -80 127 -1272 51 -590 103 -502 77 -634 101 -74 51 -224 101 -912 77 -562 51 -164 83 -396 105 -4643 111 -3293 133 -1395 107 -3047 137 -2353 53 -298 83 -54 81 -80 53 -162 83 -392 105 -606 107 -787 53 -928 51 -2800 161 -1146 51 -182 103 -536 103 -994 81 -2044 83 -732 133 -1881 133 -2160 75 -178
|
||||
RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 125 -128 51 -5075 77 -1992 83 -1272 176 -2100 53 -2044 53 -1234 79 -1704 157 -519 99 -2374 101 -100 103 -202 51 -360 77 -1962 103 -2153 77 -1820 191 -164 167 -1320 77 -1718 127 -1374 81 -1047 53 -54 79 -632 53 -656 51 -128 81 -216 51 -755 79 -2692 103 -1478 125 -452 51 -896 157 -3679 135 -632 105 -134 55 -112 77 -588 79 -188 55 -1118 79 -1152 51 -1950 109 -1858 103 -1104 81 -580 131 -226 255 -2932 77 -1536 51 -1044 159 -2135 67667 -252 333 -278 333 -276 333 -74 533 -280 307 -276 345 -6930 331 -276 329 -278 329 -278 327 -278 349 -270 325 -270 317 -290 313 -308 283 -306 309 -100 509 -306 283 -328 281 -6972 307 -302 281 -326 281 -326 281 -328 279 -328 281 -326 279 -328 279 -326 281 -328 279 -124 481 -308 281 -326 279 -6998 281 -326 279 -328 279 -328 277 -330 277 -328 279 -328 279 -328 277 -304 303 -302 303 -100 503 -306 295 -322 293 -6968 287 -342 259 -336 283 -332 257 -356 255 -328 283 -328 257 -352 257 -352 257 -352 255 -150 455 -334 281 -326 281 -6996 265 -342 253 -354 253 -354 253 -354 253 -354 267 -326 269 -350 263 -342 257 -336 259 -152 459 -360 257 -354 231 -7038 267 -338 255 -352 253 -354 253 -354 239 -354 269 -352 239 -372 233 -366 233 -358 257 -154 457 -360 231 -378 231 -7050 231 -380 231 -378 231 -380 231 -378 231 -354 255 -352 257 -352 255 -354 231 -378 229 -176 453 -358 229 -378 231 -7076 231 -378 231 -380 231 -380 229 -354 255 -354 257 -352 257 -352 257 -352 257 -354 255 -150 455 -358 265 -364 229 -4941 101 -1058 153 -670 157 -532 124 -1396 133 -82 165 -162 153 -258 207 -156 131 -1582 85 -714 53 -774 103 -396 274 -110 131 -1965 55 -402 159 -1026 79 -590 77 -3531 57 -500 51 -4770 109 -722 77 -186 53 -298 79 -502 165 -808 77 -438 53 -382 101 -1914 75 -504 77 -1969 135 -5517 99 -576 51 -608 243 -684 53 -2058 315 -1384 79 -1079 77 -232 79 -212 155 -1500 137 -258 75 -975 204 -752 83 -2542 51 -484 103 -78 77 -210 53 -922 157 -1900 107 -2173 83 -384 101 -80 128 -814 183 -978 127 -772 105 -2073 51 -708 53 -300 83 -739 237 -884 131 -3412 157 -1752 81 -164 83 -3373 53 -1406 105 -3809 79 -432 51 -724 77 -548 53 -1955 79 -807 81 -2096 103 -490 105 -1196 109 -108 79 -394 71 -1159 129 -126 143 -340 107 -556 81 -2390 135 -106 133 -690 133 -4347 189 -290 51 -110 53 -78 103 -1101 51 -1362
|
||||
RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,11.2,,
|
||||
Version,+,11.3,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
|
@ -773,6 +773,7 @@ Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*"
|
|||
Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*"
|
||||
Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*"
|
||||
Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float"
|
||||
Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*"
|
||||
Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool"
|
||||
Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t"
|
||||
Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t"
|
||||
|
|
|
|
@ -488,7 +488,7 @@ static const FuriHalFlashObMapping furi_hal_flash_ob_reg_map[FURI_HAL_FLASH_OB_T
|
|||
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
|
||||
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
|
||||
|
||||
OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (NULL)),
|
||||
OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (&FLASH->IPCCBR)),
|
||||
OB_REG_DEF(FuriHalFlashObRegisterSecureFlash, (NULL)),
|
||||
OB_REG_DEF(FuriHalFlashObRegisterC2Opts, (NULL)),
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ bool mf_classic_dict_check_presence(MfClassicDictType dict_type) {
|
|||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool dict_present = false;
|
||||
if(dict_type == MfClassicDictTypeFlipper) {
|
||||
if(dict_type == MfClassicDictTypeSystem) {
|
||||
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK;
|
||||
} else if(dict_type == MfClassicDictTypeUser) {
|
||||
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK;
|
||||
|
@ -42,7 +42,7 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) {
|
|||
|
||||
bool dict_loaded = false;
|
||||
do {
|
||||
if(dict_type == MfClassicDictTypeFlipper) {
|
||||
if(dict_type == MfClassicDictTypeSystem) {
|
||||
if(!buffered_file_stream_open(
|
||||
dict->stream,
|
||||
MF_CLASSIC_DICT_FLIPPER_PATH,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
typedef enum {
|
||||
MfClassicDictTypeUser,
|
||||
MfClassicDictTypeFlipper,
|
||||
MfClassicDictTypeSystem,
|
||||
MfClassicDictTypeUnitTest,
|
||||
} MfClassicDictType;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ extern "C" {
|
|||
|
||||
#define NFC_DEV_NAME_MAX_LEN 22
|
||||
#define NFC_READER_DATA_MAX_SIZE 64
|
||||
#define NFC_DICT_KEY_BATCH_SIZE 50
|
||||
#define NFC_DICT_KEY_BATCH_SIZE 10
|
||||
|
||||
#define NFC_APP_EXTENSION ".nfc"
|
||||
#define NFC_APP_SHADOW_EXTENSION ".shd"
|
||||
|
@ -48,6 +48,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
MfClassicDict* dict;
|
||||
uint8_t current_sector;
|
||||
} NfcMfClassicDictAttackData;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -573,17 +573,24 @@ static void nfc_worker_mf_classic_key_attack(
|
|||
FuriHalNfcTxRxContext* tx_rx,
|
||||
uint16_t start_sector) {
|
||||
furi_assert(nfc_worker);
|
||||
furi_assert(nfc_worker->callback);
|
||||
|
||||
bool card_found_notified = true;
|
||||
bool card_removed_notified = false;
|
||||
|
||||
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
|
||||
NfcMfClassicDictAttackData* dict_attack_data =
|
||||
&nfc_worker->dev_data->mf_classic_dict_attack_data;
|
||||
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
||||
|
||||
furi_assert(start_sector < total_sectors);
|
||||
|
||||
nfc_worker->callback(NfcWorkerEventKeyAttackStart, nfc_worker->context);
|
||||
|
||||
// Check every sector's A and B keys with the given key
|
||||
for(size_t i = start_sector; i < total_sectors; i++) {
|
||||
nfc_worker->callback(NfcWorkerEventKeyAttackNextSector, nfc_worker->context);
|
||||
dict_attack_data->current_sector = i;
|
||||
furi_hal_nfc_sleep();
|
||||
if(furi_hal_nfc_activate_nfca(200, NULL)) {
|
||||
furi_hal_nfc_sleep();
|
||||
|
@ -632,6 +639,7 @@ static void nfc_worker_mf_classic_key_attack(
|
|||
}
|
||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||
}
|
||||
nfc_worker->callback(NfcWorkerEventKeyAttackStop, nfc_worker->context);
|
||||
}
|
||||
|
||||
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
||||
|
|
|
@ -55,6 +55,9 @@ typedef enum {
|
|||
NfcWorkerEventNewDictKeyBatch,
|
||||
NfcWorkerEventFoundKeyA,
|
||||
NfcWorkerEventFoundKeyB,
|
||||
NfcWorkerEventKeyAttackStart,
|
||||
NfcWorkerEventKeyAttackStop,
|
||||
NfcWorkerEventKeyAttackNextSector,
|
||||
|
||||
// Write Mifare Classic events
|
||||
NfcWorkerEventWrongCard,
|
||||
|
|
400
lib/subghz/protocols/holtek_ht12x.c
Normal file
400
lib/subghz/protocols/holtek_ht12x.c
Normal file
|
@ -0,0 +1,400 @@
|
|||
#include "holtek_ht12x.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolHoltek_HT12X"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define CNT_TO_DIP(dip) \
|
||||
(dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \
|
||||
(dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \
|
||||
(dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = {
|
||||
.te_short = 320,
|
||||
.te_long = 640,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 12,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderHoltek_HT12X {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t te;
|
||||
uint32_t last_data;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHoltek_HT12X {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t te;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Holtek_HT12XDecoderStepReset = 0,
|
||||
Holtek_HT12XDecoderStepFoundStartBit,
|
||||
Holtek_HT12XDecoderStepSaveDuration,
|
||||
Holtek_HT12XDecoderStepCheckDuration,
|
||||
} Holtek_HT12XDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = {
|
||||
.alloc = subghz_protocol_decoder_holtek_th12x_alloc,
|
||||
.free = subghz_protocol_decoder_holtek_th12x_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_holtek_th12x_feed,
|
||||
.reset = subghz_protocol_decoder_holtek_th12x_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_holtek_th12x_serialize,
|
||||
.deserialize = subghz_protocol_decoder_holtek_th12x_deserialize,
|
||||
.get_string = subghz_protocol_decoder_holtek_th12x_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = {
|
||||
.alloc = subghz_protocol_encoder_holtek_th12x_alloc,
|
||||
.free = subghz_protocol_encoder_holtek_th12x_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_holtek_th12x_deserialize,
|
||||
.stop = subghz_protocol_encoder_holtek_th12x_stop,
|
||||
.yield = subghz_protocol_encoder_holtek_th12x_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_holtek_th12x = {
|
||||
.name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_holtek_th12x_decoder,
|
||||
.encoder = &subghz_protocol_holtek_th12x_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_holtek_th12x;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_holtek_th12x_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);
|
||||
//Send key data
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)instance->te * 2);
|
||||
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)instance->te * 2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing TE");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_holtek_th12x_stop(void* context) {
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) {
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
|
||||
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
||||
instance->encoder.is_running = false;
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X));
|
||||
instance->base.protocol = &subghz_protocol_holtek_th12x;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case Holtek_HT12XDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta * 36)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case Holtek_HT12XDecoderStepFoundStartBit:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta)) {
|
||||
//Found StartBit
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->te = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Holtek_HT12XDecoderStepSaveDuration:
|
||||
//save duration
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 +
|
||||
subghz_protocol_holtek_th12x_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {
|
||||
if((instance->last_data == instance->decoder.decode_data) &&
|
||||
instance->last_data) {
|
||||
instance->te /= (instance->decoder.decode_count_bit * 3 + 1);
|
||||
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->last_data = instance->decoder.decode_data;
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->te = 0;
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->te += duration;
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Holtek_HT12XDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
instance->te += duration;
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->btn = instance->data & 0x0F;
|
||||
instance->cnt = (instance->data >> 4) & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_holtek_th12x_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add TE");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing TE");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s%s%s%s\r\n",
|
||||
(((event >> 3) & 0x1) == 0x0 ? "B1 " : ""),
|
||||
(((event >> 2) & 0x1) == 0x0 ? "B2 " : ""),
|
||||
(((event >> 1) & 0x1) == 0x0 ? "B3 " : ""),
|
||||
(((event >> 0) & 0x1) == 0x0 ? "B4 " : ""));
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%03lX\r\n"
|
||||
"Btn: ",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFF));
|
||||
subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"DIP:" DIP_PATTERN "\r\n"
|
||||
"Te:%luus\r\n",
|
||||
CNT_TO_DIP(instance->generic.cnt),
|
||||
instance->te);
|
||||
}
|
107
lib/subghz/protocols/holtek_ht12x.h
Normal file
107
lib/subghz/protocols/holtek_ht12x.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X;
|
||||
typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_holtek_th12x;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderHoltek_HT12X.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_encoder_holtek_th12x_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_encoder_holtek_th12x_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_holtek_th12x_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output);
|
|
@ -13,6 +13,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
|
|||
&subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2,
|
||||
&subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3,
|
||||
&subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_smc5326,
|
||||
&subghz_protocol_holtek_th12x,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "clemsa.h"
|
||||
#include "ansonic.h"
|
||||
#include "smc5326.h"
|
||||
#include "holtek_ht12x.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
173
scripts/flipper/utils/openocd.py
Normal file
173
scripts/flipper/utils/openocd.py
Normal file
|
@ -0,0 +1,173 @@
|
|||
import socket
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
|
||||
class OpenOCD:
|
||||
"""OpenOCD cli wrapper"""
|
||||
|
||||
COMMAND_TOKEN = "\x1a"
|
||||
|
||||
def __init__(self, config: dict = {}) -> None:
|
||||
assert isinstance(config, dict)
|
||||
|
||||
# Params base
|
||||
self.params = []
|
||||
|
||||
self.gdb_port = 3333
|
||||
self.telnet_port = 4444
|
||||
self.tcl_port = 6666
|
||||
|
||||
# Port
|
||||
if port_base := config.get("port_base", None):
|
||||
self.gdb_port = port_base
|
||||
self.tcl_port = port_base + 1
|
||||
self.telnet_port = port_base + 2
|
||||
|
||||
self._add_command(f"gdb_port {self.gdb_port}")
|
||||
self._add_command(f"tcl_port {self.tcl_port}")
|
||||
self._add_command(f"telnet_port {self.telnet_port}")
|
||||
|
||||
# Config files
|
||||
|
||||
if interface := config.get("interface", None):
|
||||
pass
|
||||
else:
|
||||
interface = "interface/stlink.cfg"
|
||||
|
||||
if target := config.get("target", None):
|
||||
pass
|
||||
else:
|
||||
target = "target/stm32wbx.cfg"
|
||||
|
||||
self._add_file(interface)
|
||||
self._add_file(target)
|
||||
|
||||
# Programmer settings
|
||||
if serial := config.get("serial", None):
|
||||
self._add_command(f"{serial}")
|
||||
|
||||
# Other params
|
||||
if "params" in config:
|
||||
self.params += config["params"]
|
||||
|
||||
# logging
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def _add_command(self, command: str):
|
||||
self.params.append("-c")
|
||||
self.params.append(command)
|
||||
|
||||
def _add_file(self, file: str):
|
||||
self.params.append("-f")
|
||||
self.params.append(file)
|
||||
|
||||
def start(self, args: list[str] = []):
|
||||
"""Start OpenOCD process"""
|
||||
|
||||
params = ["openocd", *self.params, *args]
|
||||
self.logger.debug(f"_execute: {params}")
|
||||
self.process = subprocess.Popen(
|
||||
params, stderr=subprocess.PIPE, stdout=subprocess.PIPE
|
||||
)
|
||||
|
||||
self._wait_for_openocd_tcl()
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.connect(("127.0.0.1", self.tcl_port))
|
||||
|
||||
def _wait_for_openocd_tcl(self):
|
||||
"""Wait for OpenOCD to start"""
|
||||
# TODO: timeout
|
||||
while True:
|
||||
stderr = self.process.stderr
|
||||
if not stderr:
|
||||
break
|
||||
line = stderr.readline()
|
||||
if not line:
|
||||
break
|
||||
line = line.decode("utf-8").strip()
|
||||
self.logger.debug(f"OpenOCD: {line}")
|
||||
if "Listening on port" in line and "for tcl connections" in line:
|
||||
break
|
||||
|
||||
def stop(self):
|
||||
self.send_tcl("exit")
|
||||
self.send_tcl("shutdown")
|
||||
self.socket.close()
|
||||
try:
|
||||
self.process.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired as e:
|
||||
self.process.kill()
|
||||
self.logger.error("Failed to stop OpenOCD")
|
||||
self.logger.exception(e)
|
||||
self.postmortem()
|
||||
|
||||
def send_tcl(self, cmd) -> str:
|
||||
"""Send a command string to TCL RPC. Return the result that was read."""
|
||||
|
||||
try:
|
||||
data = (cmd + OpenOCD.COMMAND_TOKEN).encode("utf-8")
|
||||
self.logger.debug(f"<- {data}")
|
||||
|
||||
self.socket.send(data)
|
||||
except Exception as e:
|
||||
self.logger.error("Failed to send command to OpenOCD")
|
||||
self.logger.exception(e)
|
||||
self.postmortem()
|
||||
raise
|
||||
|
||||
try:
|
||||
data = self._recv()
|
||||
return data
|
||||
except Exception as e:
|
||||
self.logger.error("Failed to receive response from OpenOCD")
|
||||
self.logger.exception(e)
|
||||
self.postmortem()
|
||||
raise
|
||||
|
||||
def _recv(self):
|
||||
"""Read from the stream until the token (\x1a) was received."""
|
||||
# TODO: timeout
|
||||
data = bytes()
|
||||
while True:
|
||||
chunk = self.socket.recv(4096)
|
||||
data += chunk
|
||||
if bytes(OpenOCD.COMMAND_TOKEN, encoding="utf-8") in chunk:
|
||||
break
|
||||
|
||||
self.logger.debug(f"-> {data}")
|
||||
|
||||
data = data.decode("utf-8").strip()
|
||||
data = data[:-1] # strip trailing \x1a
|
||||
|
||||
return data
|
||||
|
||||
def postmortem(self) -> None:
|
||||
"""Postmortem analysis of the OpenOCD process"""
|
||||
stdout, stderr = self.process.communicate()
|
||||
|
||||
log = self.logger.error
|
||||
if self.process.returncode == 0:
|
||||
log = self.logger.debug
|
||||
log("OpenOCD exited normally")
|
||||
else:
|
||||
log("OpenOCD exited with error")
|
||||
|
||||
log(f"Exit code: {self.process.returncode}")
|
||||
for line in stdout.decode("utf-8").splitlines():
|
||||
log(f"Stdout: {line}")
|
||||
|
||||
for line in stderr.decode("utf-8").splitlines():
|
||||
log(f"Stderr: {line}")
|
||||
|
||||
def read_32(self, addr: int) -> int:
|
||||
"""Read 32-bit value from memory"""
|
||||
data = self.send_tcl(f"mdw {addr}").strip()
|
||||
data = data.split(": ")[-1]
|
||||
data = int(data, 16)
|
||||
return data
|
||||
|
||||
def write_32(self, addr: int, value: int) -> None:
|
||||
"""Write 32-bit value to memory"""
|
||||
self.send_tcl(f"mww {addr} {value}")
|
31
scripts/flipper/utils/programmer.py
Normal file
31
scripts/flipper/utils/programmer.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Programmer(ABC):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class RunMode(Enum):
|
||||
Run = "run"
|
||||
Stop = "stop"
|
||||
|
||||
@abstractmethod
|
||||
def reset(self, mode: RunMode = RunMode.Run) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def flash(self, address: int, file_path: str, verify: bool = True) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def option_bytes_validate(self, file_path: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def option_bytes_set(self, file_path: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def otp_write(self, address: int, file_path: str) -> bool:
|
||||
pass
|
281
scripts/flipper/utils/programmer_openocd.py
Normal file
281
scripts/flipper/utils/programmer_openocd.py
Normal file
|
@ -0,0 +1,281 @@
|
|||
import logging
|
||||
import os
|
||||
import typing
|
||||
|
||||
from flipper.utils.programmer import Programmer
|
||||
from flipper.utils.openocd import OpenOCD
|
||||
from flipper.utils.stm32wb55 import STM32WB55
|
||||
from flipper.assets.obdata import OptionBytesData
|
||||
|
||||
|
||||
class OpenOCDProgrammer(Programmer):
|
||||
def __init__(
|
||||
self,
|
||||
interface: str = "interface/cmsis-dap.cfg",
|
||||
port_base: typing.Union[int, None] = None,
|
||||
serial: typing.Union[str, None] = None,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
config = {}
|
||||
|
||||
config["interface"] = interface
|
||||
config["target"] = "target/stm32wbx.cfg"
|
||||
|
||||
if not serial is None:
|
||||
if interface == "interface/cmsis-dap.cfg":
|
||||
config["serial"] = f"cmsis_dap_serial {serial}"
|
||||
elif "stlink" in interface:
|
||||
config["serial"] = f"stlink_serial {serial}"
|
||||
|
||||
if not port_base is None:
|
||||
config["port_base"] = port_base
|
||||
|
||||
self.openocd = OpenOCD(config)
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool:
|
||||
stm32 = STM32WB55()
|
||||
if mode == Programmer.RunMode.Run:
|
||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
||||
elif mode == Programmer.RunMode.Stop:
|
||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
||||
else:
|
||||
raise Exception("Unknown mode")
|
||||
|
||||
return True
|
||||
|
||||
def flash(self, address: int, file_path: str, verify: bool = True) -> bool:
|
||||
if not os.path.exists(file_path):
|
||||
raise Exception(f"File {file_path} not found")
|
||||
|
||||
self.openocd.start()
|
||||
self.openocd.send_tcl(f"init")
|
||||
self.openocd.send_tcl(
|
||||
f"program {file_path} 0x{address:08x}{' verify' if verify else ''} reset exit"
|
||||
)
|
||||
self.openocd.stop()
|
||||
|
||||
return True
|
||||
|
||||
def _ob_print_diff_table(self, ob_reference: bytes, ob_read: bytes, print_fn):
|
||||
print_fn(
|
||||
f'{"Reference": <20} {"Device": <20} {"Diff Reference": <20} {"Diff Device": <20}'
|
||||
)
|
||||
|
||||
# Split into 8 byte, word + word
|
||||
for i in range(0, len(ob_reference), 8):
|
||||
ref = ob_reference[i : i + 8]
|
||||
read = ob_read[i : i + 8]
|
||||
|
||||
diff_str1 = ""
|
||||
diff_str2 = ""
|
||||
for j in range(0, len(ref.hex()), 2):
|
||||
byte_str_1 = ref.hex()[j : j + 2]
|
||||
byte_str_2 = read.hex()[j : j + 2]
|
||||
|
||||
if byte_str_1 == byte_str_2:
|
||||
diff_str1 += "__"
|
||||
diff_str2 += "__"
|
||||
else:
|
||||
diff_str1 += byte_str_1
|
||||
diff_str2 += byte_str_2
|
||||
|
||||
print_fn(
|
||||
f"{ref.hex(): <20} {read.hex(): <20} {diff_str1: <20} {diff_str2: <20}"
|
||||
)
|
||||
|
||||
def option_bytes_validate(self, file_path: str) -> bool:
|
||||
# Registers
|
||||
stm32 = STM32WB55()
|
||||
|
||||
# OpenOCD
|
||||
self.openocd.start()
|
||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
||||
|
||||
# Generate Option Bytes data
|
||||
ob_data = OptionBytesData(file_path)
|
||||
ob_values = ob_data.gen_values().export()
|
||||
ob_reference = ob_values.reference
|
||||
ob_compare_mask = ob_values.compare_mask
|
||||
ob_length = len(ob_reference)
|
||||
ob_words = int(ob_length / 4)
|
||||
|
||||
# Read Option Bytes
|
||||
ob_read = bytes()
|
||||
for i in range(ob_words):
|
||||
addr = stm32.OPTION_BYTE_BASE + i * 4
|
||||
value = self.openocd.read_32(addr)
|
||||
ob_read += value.to_bytes(4, "little")
|
||||
|
||||
# Compare Option Bytes with reference by mask
|
||||
ob_compare = bytes()
|
||||
for i in range(ob_length):
|
||||
ob_compare += bytes([ob_read[i] & ob_compare_mask[i]])
|
||||
|
||||
# Compare Option Bytes
|
||||
return_code = False
|
||||
|
||||
if ob_reference == ob_compare:
|
||||
self.logger.info("Option Bytes are valid")
|
||||
return_code = True
|
||||
else:
|
||||
self.logger.error("Option Bytes are invalid")
|
||||
self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error)
|
||||
|
||||
# Stop OpenOCD
|
||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
||||
self.openocd.stop()
|
||||
|
||||
return return_code
|
||||
|
||||
def _unpack_u32(self, data: bytes, offset: int):
|
||||
return int.from_bytes(data[offset : offset + 4], "little")
|
||||
|
||||
def option_bytes_set(self, file_path: str) -> bool:
|
||||
# Registers
|
||||
stm32 = STM32WB55()
|
||||
|
||||
# OpenOCD
|
||||
self.openocd.start()
|
||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
||||
|
||||
# Generate Option Bytes data
|
||||
ob_data = OptionBytesData(file_path)
|
||||
ob_values = ob_data.gen_values().export()
|
||||
ob_reference_bytes = ob_values.reference
|
||||
ob_compare_mask_bytes = ob_values.compare_mask
|
||||
ob_write_mask_bytes = ob_values.write_mask
|
||||
ob_length = len(ob_reference_bytes)
|
||||
ob_dwords = int(ob_length / 8)
|
||||
|
||||
# Clear flash errors
|
||||
stm32.clear_flash_errors(self.openocd)
|
||||
|
||||
# Unlock Flash and Option Bytes
|
||||
stm32.flash_unlock(self.openocd)
|
||||
stm32.option_bytes_unlock(self.openocd)
|
||||
|
||||
ob_need_to_apply = False
|
||||
|
||||
for i in range(ob_dwords):
|
||||
device_addr = stm32.OPTION_BYTE_BASE + i * 8
|
||||
device_value = self.openocd.read_32(device_addr)
|
||||
ob_write_mask = self._unpack_u32(ob_write_mask_bytes, i * 8)
|
||||
ob_compare_mask = self._unpack_u32(ob_compare_mask_bytes, i * 8)
|
||||
ob_value_ref = self._unpack_u32(ob_reference_bytes, i * 8)
|
||||
ob_value_masked = device_value & ob_compare_mask
|
||||
|
||||
need_patch = ((ob_value_masked ^ ob_value_ref) & ob_write_mask) != 0
|
||||
if need_patch:
|
||||
ob_need_to_apply = True
|
||||
|
||||
self.logger.info(
|
||||
f"Need to patch: {device_addr:08X}: {ob_value_masked:08X} != {ob_value_ref:08X}, REG[{i}]"
|
||||
)
|
||||
|
||||
# Check if this option byte (dword) is mapped to a register
|
||||
device_reg_addr = stm32.option_bytes_id_to_address(i)
|
||||
|
||||
# Construct new value for the OB register
|
||||
ob_value = device_value & (~ob_write_mask)
|
||||
ob_value |= ob_value_ref & ob_write_mask
|
||||
|
||||
self.logger.info(f"Writing {ob_value:08X} to {device_reg_addr:08X}")
|
||||
self.openocd.write_32(device_reg_addr, ob_value)
|
||||
|
||||
if ob_need_to_apply:
|
||||
stm32.option_bytes_apply(self.openocd)
|
||||
else:
|
||||
self.logger.info(f"Option Bytes are already correct")
|
||||
|
||||
# Load Option Bytes
|
||||
# That will reset and also lock the Option Bytes and the Flash
|
||||
stm32.option_bytes_load(self.openocd)
|
||||
|
||||
# Stop OpenOCD
|
||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
||||
self.openocd.stop()
|
||||
|
||||
return True
|
||||
|
||||
def otp_write(self, address: int, file_path: str) -> bool:
|
||||
# Open file, check that it aligned to 8 bytes
|
||||
with open(file_path, "rb") as f:
|
||||
data = f.read()
|
||||
if len(data) % 8 != 0:
|
||||
self.logger.error(f"File {file_path} is not aligned to 8 bytes")
|
||||
return False
|
||||
|
||||
# Check that address is aligned to 8 bytes
|
||||
if address % 8 != 0:
|
||||
self.logger.error(f"Address {address} is not aligned to 8 bytes")
|
||||
return False
|
||||
|
||||
# Get size of data
|
||||
data_size = len(data)
|
||||
|
||||
# Check that data size is aligned to 8 bytes
|
||||
if data_size % 8 != 0:
|
||||
self.logger.error(f"Data size {data_size} is not aligned to 8 bytes")
|
||||
return False
|
||||
|
||||
self.logger.debug(f"Writing {data_size} bytes to OTP at {address:08X}")
|
||||
self.logger.debug(f"Data: {data.hex().upper()}")
|
||||
|
||||
# Start OpenOCD
|
||||
oocd = self.openocd
|
||||
oocd.start()
|
||||
|
||||
# Registers
|
||||
stm32 = STM32WB55()
|
||||
|
||||
try:
|
||||
# Check that OTP is empty for the given address
|
||||
# Also check that data is already written
|
||||
already_written = True
|
||||
for i in range(0, data_size, 4):
|
||||
file_word = int.from_bytes(data[i : i + 4], "little")
|
||||
device_word = oocd.read_32(address + i)
|
||||
if device_word != 0xFFFFFFFF and device_word != file_word:
|
||||
self.logger.error(
|
||||
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
|
||||
)
|
||||
raise Exception("OTP memory is not empty")
|
||||
|
||||
if device_word != file_word:
|
||||
already_written = False
|
||||
|
||||
if already_written:
|
||||
self.logger.info(f"OTP memory is already written with the given data")
|
||||
return True
|
||||
|
||||
self.reset(self.RunMode.Stop)
|
||||
stm32.clear_flash_errors(oocd)
|
||||
|
||||
# Write OTP memory by 8 bytes
|
||||
for i in range(0, data_size, 8):
|
||||
word_1 = int.from_bytes(data[i : i + 4], "little")
|
||||
word_2 = int.from_bytes(data[i + 4 : i + 8], "little")
|
||||
self.logger.debug(
|
||||
f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}"
|
||||
)
|
||||
stm32.write_flash_64(oocd, address + i, word_1, word_2)
|
||||
|
||||
# Validate OTP memory
|
||||
validation_result = True
|
||||
|
||||
for i in range(0, data_size, 4):
|
||||
file_word = int.from_bytes(data[i : i + 4], "little")
|
||||
device_word = oocd.read_32(address + i)
|
||||
if file_word != device_word:
|
||||
self.logger.error(
|
||||
f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}"
|
||||
)
|
||||
validation_result = False
|
||||
finally:
|
||||
# Stop OpenOCD
|
||||
stm32.reset(oocd, stm32.RunMode.Run)
|
||||
oocd.stop()
|
||||
|
||||
return validation_result
|
95
scripts/flipper/utils/register.py
Normal file
95
scripts/flipper/utils/register.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
from dataclasses import dataclass
|
||||
from flipper.utils.openocd import OpenOCD
|
||||
|
||||
|
||||
@dataclass
|
||||
class RegisterBitDefinition:
|
||||
name: str
|
||||
offset: int
|
||||
size: int
|
||||
value: int = 0
|
||||
|
||||
|
||||
class Register32:
|
||||
def __init__(self, address: int, definition_list: list[RegisterBitDefinition]):
|
||||
self.__dict__["names"] = [definition.name for definition in definition_list]
|
||||
self.names = [definition.name for definition in definition_list] # typecheck
|
||||
self.address = address
|
||||
self.definition_list = definition_list
|
||||
|
||||
# Validate that the definitions are not overlapping
|
||||
for i in range(len(definition_list)):
|
||||
for j in range(i + 1, len(definition_list)):
|
||||
if self._is_overlapping(definition_list[i], definition_list[j]):
|
||||
raise ValueError("Register definitions are overlapping")
|
||||
|
||||
self.freezed = True
|
||||
|
||||
def _is_overlapping(
|
||||
self, a: RegisterBitDefinition, b: RegisterBitDefinition
|
||||
) -> bool:
|
||||
if a.offset + a.size <= b.offset:
|
||||
return False
|
||||
if b.offset + b.size <= a.offset:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_definition(self, name: str) -> RegisterBitDefinition:
|
||||
for definition in self.definition_list:
|
||||
if definition.name == name:
|
||||
return definition
|
||||
raise ValueError(f"Register definition '{name}' not found")
|
||||
|
||||
def get_definition_list(self) -> list[RegisterBitDefinition]:
|
||||
return self.definition_list
|
||||
|
||||
def get_address(self) -> int:
|
||||
return self.address
|
||||
|
||||
def set_reg_value(self, name: str, value: int):
|
||||
definition = self._get_definition(name)
|
||||
if value > (1 << definition.size) - 1:
|
||||
raise ValueError(
|
||||
f"Value {value} is too large for register definition '{name}'"
|
||||
)
|
||||
definition.value = value
|
||||
|
||||
def get_reg_value(self, name: str) -> int:
|
||||
definition = self._get_definition(name)
|
||||
return definition.value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if str(attr) in self.names:
|
||||
return self.get_reg_value(str(attr))
|
||||
else:
|
||||
return self.__dict__[attr]
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if str(attr) in self.names:
|
||||
self.set_reg_value(str(attr), value)
|
||||
else:
|
||||
if attr in self.__dict__ or "freezed" not in self.__dict__:
|
||||
self.__dict__[attr] = value
|
||||
else:
|
||||
raise AttributeError(f"Attribute '{attr}' not found")
|
||||
|
||||
def __dir__(self):
|
||||
return self.names
|
||||
|
||||
def set(self, value: int):
|
||||
for definition in self.definition_list:
|
||||
definition.value = (value >> definition.offset) & (
|
||||
(1 << definition.size) - 1
|
||||
)
|
||||
|
||||
def get(self) -> int:
|
||||
value = 0
|
||||
for definition in self.definition_list:
|
||||
value |= definition.value << definition.offset
|
||||
return value
|
||||
|
||||
def load(self, openocd: OpenOCD):
|
||||
self.set(openocd.read_32(self.address))
|
||||
|
||||
def store(self, openocd: OpenOCD):
|
||||
openocd.write_32(self.address, self.get())
|
352
scripts/flipper/utils/stm32wb55.py
Normal file
352
scripts/flipper/utils/stm32wb55.py
Normal file
|
@ -0,0 +1,352 @@
|
|||
import logging
|
||||
from enum import Enum
|
||||
|
||||
from flipper.utils.openocd import OpenOCD
|
||||
from flipper.utils.register import Register32, RegisterBitDefinition
|
||||
|
||||
|
||||
class STM32WB55:
|
||||
# Address of OTP memory in flash
|
||||
OTP_BASE = 0x1FFF7000
|
||||
|
||||
# Address of Option byte in flash
|
||||
OPTION_BYTE_BASE = 0x1FFF8000
|
||||
|
||||
# Flash base address
|
||||
FLASH_BASE = 0x58004000
|
||||
|
||||
# Flash unlock register
|
||||
FLASH_KEYR = FLASH_BASE + 0x08
|
||||
|
||||
# Option byte unlock register
|
||||
FLASH_OPTKEYR = FLASH_BASE + 0x0C
|
||||
|
||||
# Flash unlock keys
|
||||
FLASH_UNLOCK_KEY1 = 0x45670123
|
||||
FLASH_UNLOCK_KEY2 = 0xCDEF89AB
|
||||
|
||||
# Option byte unlock keys
|
||||
FLASH_UNLOCK_OPTKEY1 = 0x08192A3B
|
||||
FLASH_UNLOCK_OPTKEY2 = 0x4C5D6E7F
|
||||
|
||||
# Flash control register
|
||||
FLASH_CR = Register32(
|
||||
FLASH_BASE + 0x14,
|
||||
[
|
||||
RegisterBitDefinition("PG", 0, 1),
|
||||
RegisterBitDefinition("PER", 1, 1),
|
||||
RegisterBitDefinition("MER", 2, 1),
|
||||
RegisterBitDefinition("PNB", 3, 8),
|
||||
RegisterBitDefinition("_", 11, 5),
|
||||
RegisterBitDefinition("STRT", 16, 1),
|
||||
RegisterBitDefinition("OPT_STRT", 17, 1),
|
||||
RegisterBitDefinition("FSTPG", 18, 1),
|
||||
RegisterBitDefinition("_", 19, 5),
|
||||
RegisterBitDefinition("EOPIE", 24, 1),
|
||||
RegisterBitDefinition("ERRIE", 25, 1),
|
||||
RegisterBitDefinition("RD_ERRIE", 26, 1),
|
||||
RegisterBitDefinition("OBL_LAUNCH", 27, 1),
|
||||
RegisterBitDefinition("_", 28, 2),
|
||||
RegisterBitDefinition("OPT_LOCK", 30, 1),
|
||||
RegisterBitDefinition("LOCK", 31, 1),
|
||||
],
|
||||
)
|
||||
|
||||
# Flash status register
|
||||
FLASH_SR = Register32(
|
||||
FLASH_BASE + 0x10,
|
||||
[
|
||||
RegisterBitDefinition("EOP", 0, 1),
|
||||
RegisterBitDefinition("OP_ERR", 1, 1),
|
||||
RegisterBitDefinition("_", 2, 1),
|
||||
RegisterBitDefinition("PROG_ERR", 3, 1),
|
||||
RegisterBitDefinition("WRP_ERR", 4, 1),
|
||||
RegisterBitDefinition("PGA_ERR", 5, 1),
|
||||
RegisterBitDefinition("SIZE_ERR", 6, 1),
|
||||
RegisterBitDefinition("PGS_ERR", 7, 1),
|
||||
RegisterBitDefinition("MISS_ERR", 8, 1),
|
||||
RegisterBitDefinition("FAST_ERR", 9, 1),
|
||||
RegisterBitDefinition("_", 10, 3),
|
||||
RegisterBitDefinition("OPTNV", 13, 1),
|
||||
RegisterBitDefinition("RD_ERR", 14, 1),
|
||||
RegisterBitDefinition("OPTV_ERR", 15, 1),
|
||||
RegisterBitDefinition("BSY", 16, 1),
|
||||
RegisterBitDefinition("_", 17, 1),
|
||||
RegisterBitDefinition("CFGBSY", 18, 1),
|
||||
RegisterBitDefinition("PESD", 19, 1),
|
||||
RegisterBitDefinition("_", 20, 12),
|
||||
],
|
||||
)
|
||||
|
||||
# Option byte registers
|
||||
FLASH_OPTR = FLASH_BASE + 0x20
|
||||
FLASH_PCROP1ASR = FLASH_BASE + 0x24
|
||||
FLASH_PCROP1AER = FLASH_BASE + 0x28
|
||||
FLASH_WRP1AR = FLASH_BASE + 0x2C
|
||||
FLASH_WRP1BR = FLASH_BASE + 0x30
|
||||
FLASH_PCROP1BSR = FLASH_BASE + 0x34
|
||||
FLASH_PCROP1BER = FLASH_BASE + 0x38
|
||||
FLASH_IPCCBR = FLASH_BASE + 0x3C
|
||||
|
||||
# Map option byte dword index to register address
|
||||
OPTION_BYTE_MAP_TO_REGS = {
|
||||
0: FLASH_OPTR,
|
||||
1: FLASH_PCROP1ASR,
|
||||
2: FLASH_PCROP1AER,
|
||||
3: FLASH_WRP1AR,
|
||||
4: FLASH_WRP1BR,
|
||||
5: FLASH_PCROP1BSR,
|
||||
6: FLASH_PCROP1BER,
|
||||
7: None, # Invalid Options
|
||||
8: None, # Invalid Options
|
||||
9: None, # Invalid Options
|
||||
10: None, # Invalid Options
|
||||
11: None, # Invalid Options
|
||||
12: None, # Invalid Options
|
||||
13: FLASH_IPCCBR,
|
||||
14: None, # Secure Flash
|
||||
15: None, # Core 2 Options
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger("STM32WB55")
|
||||
|
||||
class RunMode(Enum):
|
||||
Init = "init"
|
||||
Run = "run"
|
||||
Halt = "halt"
|
||||
|
||||
def reset(self, oocd: OpenOCD, mode: RunMode):
|
||||
self.logger.debug("Resetting device")
|
||||
oocd.send_tcl(f"reset {mode.value}")
|
||||
|
||||
def clear_flash_errors(self, oocd: OpenOCD):
|
||||
# Errata 2.2.9: Flash OPTVERR flag is always set after system reset
|
||||
# And also clear all other flash error flags
|
||||
self.logger.debug(f"Resetting flash errors")
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.OP_ERR = 1
|
||||
self.FLASH_SR.PROG_ERR = 1
|
||||
self.FLASH_SR.WRP_ERR = 1
|
||||
self.FLASH_SR.PGA_ERR = 1
|
||||
self.FLASH_SR.SIZE_ERR = 1
|
||||
self.FLASH_SR.PGS_ERR = 1
|
||||
self.FLASH_SR.MISS_ERR = 1
|
||||
self.FLASH_SR.FAST_ERR = 1
|
||||
self.FLASH_SR.RD_ERR = 1
|
||||
self.FLASH_SR.OPTV_ERR = 1
|
||||
self.FLASH_SR.store(oocd)
|
||||
|
||||
def flash_unlock(self, oocd: OpenOCD):
|
||||
# Check if flash is already unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.LOCK == 0:
|
||||
self.logger.debug("Flash is already unlocked")
|
||||
return
|
||||
|
||||
# Unlock flash
|
||||
self.logger.debug("Unlocking Flash")
|
||||
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
|
||||
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
|
||||
|
||||
# Check if flash is unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.LOCK == 0:
|
||||
self.logger.debug("Flash unlocked")
|
||||
else:
|
||||
self.logger.error("Flash unlock failed")
|
||||
raise Exception("Flash unlock failed")
|
||||
|
||||
def option_bytes_unlock(self, oocd: OpenOCD):
|
||||
# Check if options is already unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.OPT_LOCK == 0:
|
||||
self.logger.debug("Options is already unlocked")
|
||||
return
|
||||
|
||||
# Unlock options
|
||||
self.logger.debug("Unlocking Options")
|
||||
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
|
||||
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
|
||||
|
||||
# Check if options is unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.OPT_LOCK == 0:
|
||||
self.logger.debug("Options unlocked")
|
||||
else:
|
||||
self.logger.error("Options unlock failed")
|
||||
raise Exception("Options unlock failed")
|
||||
|
||||
def option_bytes_lock(self, oocd: OpenOCD):
|
||||
# Check if options is already locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.OPT_LOCK == 1:
|
||||
self.logger.debug("Options is already locked")
|
||||
return
|
||||
|
||||
# Lock options
|
||||
self.logger.debug("Locking Options")
|
||||
self.FLASH_CR.OPT_LOCK = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
# Check if options is locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.OPT_LOCK == 1:
|
||||
self.logger.debug("Options locked")
|
||||
else:
|
||||
self.logger.error("Options lock failed")
|
||||
raise Exception("Options lock failed")
|
||||
|
||||
def flash_lock(self, oocd: OpenOCD):
|
||||
# Check if flash is already locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.LOCK == 1:
|
||||
self.logger.debug("Flash is already locked")
|
||||
return
|
||||
|
||||
# Lock flash
|
||||
self.logger.debug("Locking Flash")
|
||||
self.FLASH_CR.LOCK = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
# Check if flash is locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
if self.FLASH_CR.LOCK == 1:
|
||||
self.logger.debug("Flash locked")
|
||||
else:
|
||||
self.logger.error("Flash lock failed")
|
||||
raise Exception("Flash lock failed")
|
||||
|
||||
def option_bytes_apply(self, oocd: OpenOCD):
|
||||
self.logger.debug(f"Applying Option Bytes")
|
||||
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.OPT_STRT = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
# Wait for Option Bytes to be applied
|
||||
self.flash_wait_for_operation(oocd)
|
||||
|
||||
def option_bytes_load(self, oocd: OpenOCD):
|
||||
self.logger.debug(f"Loading Option Bytes")
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.OBL_LAUNCH = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
def option_bytes_id_to_address(self, id: int) -> int:
|
||||
# Check if this option byte (dword) is mapped to a register
|
||||
device_reg_addr = self.OPTION_BYTE_MAP_TO_REGS.get(id, None)
|
||||
if device_reg_addr is None:
|
||||
raise Exception(f"Option Byte {id} is not mapped to a register")
|
||||
|
||||
return device_reg_addr
|
||||
|
||||
def flash_wait_for_operation(self, oocd: OpenOCD):
|
||||
# Wait for flash operation to complete
|
||||
# TODO: timeout
|
||||
while True:
|
||||
self.FLASH_SR.load(oocd)
|
||||
if self.FLASH_SR.BSY == 0:
|
||||
break
|
||||
|
||||
def flash_dump_status_register(self, oocd: OpenOCD):
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
|
||||
if self.FLASH_SR.EOP:
|
||||
self.logger.info(" End of operation")
|
||||
if self.FLASH_SR.OP_ERR:
|
||||
self.logger.error(" Operation error")
|
||||
if self.FLASH_SR.PROG_ERR:
|
||||
self.logger.error(" Programming error")
|
||||
if self.FLASH_SR.WRP_ERR:
|
||||
self.logger.error(" Write protection error")
|
||||
if self.FLASH_SR.PGA_ERR:
|
||||
self.logger.error(" Programming alignment error")
|
||||
if self.FLASH_SR.SIZE_ERR:
|
||||
self.logger.error(" Size error")
|
||||
if self.FLASH_SR.PGS_ERR:
|
||||
self.logger.error(" Programming sequence error")
|
||||
if self.FLASH_SR.MISS_ERR:
|
||||
self.logger.error(" Fast programming data miss error")
|
||||
if self.FLASH_SR.FAST_ERR:
|
||||
self.logger.error(" Fast programming error")
|
||||
if self.FLASH_SR.OPTNV:
|
||||
self.logger.info(" User option OPTVAL indication")
|
||||
if self.FLASH_SR.RD_ERR:
|
||||
self.logger.info(" PCROP read error")
|
||||
if self.FLASH_SR.OPTV_ERR:
|
||||
self.logger.info(" Option and Engineering bits loading validity error")
|
||||
if self.FLASH_SR.BSY:
|
||||
self.logger.info(" Busy")
|
||||
if self.FLASH_SR.CFGBSY:
|
||||
self.logger.info(" Programming or erase configuration busy")
|
||||
if self.FLASH_SR.PESD:
|
||||
self.logger.info(" Programming / erase operation suspended.")
|
||||
|
||||
def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int):
|
||||
self.logger.debug(f"Writing flash at address {address:08x}")
|
||||
|
||||
if address % 8 != 0:
|
||||
self.logger.error("Address must be aligned to 8 bytes")
|
||||
raise Exception("Address must be aligned to 8 bytes")
|
||||
|
||||
if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4):
|
||||
self.logger.debug("Data is already programmed")
|
||||
return
|
||||
|
||||
self.flash_unlock(oocd)
|
||||
|
||||
# Check that no flash main memory operation is ongoing by checking the BSY bit
|
||||
self.FLASH_SR.load(oocd)
|
||||
if self.FLASH_SR.BSY:
|
||||
self.logger.error("Flash is busy")
|
||||
self.flash_dump_status_register(oocd)
|
||||
raise Exception("Flash is busy")
|
||||
|
||||
# Enable end of operation interrupts and error interrupts
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.EOPIE = 1
|
||||
self.FLASH_CR.ERRIE = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
# Check that flash memory program and erase operations are allowed
|
||||
if self.FLASH_SR.PESD:
|
||||
self.logger.error("Flash operations are not allowed")
|
||||
self.flash_dump_status_register(oocd)
|
||||
raise Exception("Flash operations are not allowed")
|
||||
|
||||
# Check and clear all error programming flags due to a previous programming.
|
||||
self.clear_flash_errors(oocd)
|
||||
|
||||
# Set the PG bit in the Flash memory control register (FLASH_CR)
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.PG = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
# Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed.
|
||||
# Write the first word
|
||||
oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
|
||||
# Write the second word
|
||||
oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
|
||||
|
||||
# Wait for the BSY bit to be cleared
|
||||
self.flash_wait_for_operation(oocd)
|
||||
|
||||
# Check that EOP flag is set in the FLASH_SR register
|
||||
self.FLASH_SR.load(oocd)
|
||||
if not self.FLASH_SR.EOP:
|
||||
self.logger.error("Flash operation failed")
|
||||
self.flash_dump_status_register(oocd)
|
||||
raise Exception("Flash operation failed")
|
||||
|
||||
# Clear the EOP flag
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.EOP = 1
|
||||
self.FLASH_SR.store(oocd)
|
||||
|
||||
# Clear the PG bit in the FLASH_CR register
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.PG = 0
|
||||
self.FLASH_CR.store(oocd)
|
||||
|
||||
self.flash_lock(oocd)
|
|
@ -1,69 +1,79 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import logging
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
from os import path
|
||||
|
||||
from flipper.app import App
|
||||
from flipper.cube import CubeProgrammer
|
||||
from flipper.utils.programmer_openocd import OpenOCDProgrammer
|
||||
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
# Subparsers
|
||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||
|
||||
# Check command
|
||||
self.parser_check = self.subparsers.add_parser(
|
||||
"check", help="Check Option Bytes"
|
||||
)
|
||||
self._addArgsSWD(self.parser_check)
|
||||
self._add_args(self.parser_check)
|
||||
self.parser_check.set_defaults(func=self.check)
|
||||
|
||||
# Set command
|
||||
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
||||
self._addArgsSWD(self.parser_set)
|
||||
self._add_args(self.parser_set)
|
||||
self.parser_set.set_defaults(func=self.set)
|
||||
# OB
|
||||
self.ob = {}
|
||||
|
||||
def _addArgsSWD(self, parser):
|
||||
def _add_args(self, parser):
|
||||
parser.add_argument(
|
||||
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
|
||||
"--port-base", type=int, help="OpenOCD port base", default=3333
|
||||
)
|
||||
parser.add_argument(
|
||||
"--interface",
|
||||
type=str,
|
||||
help="OpenOCD interface",
|
||||
default="interface/cmsis-dap.cfg",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--serial", type=str, help="OpenOCD interface serial number"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ob-path",
|
||||
type=str,
|
||||
help="Option bytes file",
|
||||
default=path.join(path.dirname(__file__), "ob.data"),
|
||||
)
|
||||
parser.add_argument("--serial", type=str, help="ST-Link Serial Number")
|
||||
|
||||
def _getCubeParams(self):
|
||||
return {
|
||||
"port": self.args.port,
|
||||
"serial": self.args.serial,
|
||||
}
|
||||
|
||||
def before(self):
|
||||
self.logger.info(f"Loading Option Bytes data")
|
||||
file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
|
||||
with open(file_path, "r") as file:
|
||||
for line in file.readlines():
|
||||
k, v, o = line.split(":")
|
||||
self.ob[k.strip()] = v.strip(), o.strip()
|
||||
|
||||
def check(self):
|
||||
self.logger.info(f"Checking Option Bytes")
|
||||
cp = CubeProgrammer(self._getCubeParams())
|
||||
if cp.checkOptionBytes(self.ob):
|
||||
self.logger.info(f"OB Check OK")
|
||||
return 0
|
||||
else:
|
||||
self.logger.error(f"OB Check FAIL")
|
||||
return 255
|
||||
|
||||
# OpenOCD
|
||||
openocd = OpenOCDProgrammer(
|
||||
self.args.interface,
|
||||
self.args.port_base,
|
||||
self.args.serial,
|
||||
)
|
||||
|
||||
return_code = 1
|
||||
if openocd.option_bytes_validate(self.args.ob_path):
|
||||
return_code = 0
|
||||
|
||||
return return_code
|
||||
|
||||
def set(self):
|
||||
self.logger.info(f"Setting Option Bytes")
|
||||
cp = CubeProgrammer(self._getCubeParams())
|
||||
if cp.setOptionBytes(self.ob):
|
||||
self.logger.info(f"OB Set OK")
|
||||
return 0
|
||||
else:
|
||||
self.logger.error(f"OB Set FAIL")
|
||||
return 255
|
||||
|
||||
# OpenOCD
|
||||
openocd = OpenOCDProgrammer(
|
||||
self.args.interface,
|
||||
self.args.port_base,
|
||||
self.args.serial,
|
||||
)
|
||||
|
||||
return_code = 1
|
||||
if openocd.option_bytes_set(self.args.ob_path):
|
||||
return_code = 0
|
||||
|
||||
return return_code
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -35,6 +35,7 @@ OTP_DISPLAYS = {
|
|||
|
||||
from flipper.app import App
|
||||
from flipper.cube import CubeProgrammer
|
||||
from flipper.utils.programmer_openocd import OpenOCDProgrammer
|
||||
|
||||
|
||||
class Main(App):
|
||||
|
@ -53,21 +54,21 @@ class Main(App):
|
|||
self.parser_flash_first = self.subparsers.add_parser(
|
||||
"flash_first", help="Flash first block of OTP to device"
|
||||
)
|
||||
self._addArgsSWD(self.parser_flash_first)
|
||||
self._addArgsOpenOCD(self.parser_flash_first)
|
||||
self._addFirstArgs(self.parser_flash_first)
|
||||
self.parser_flash_first.set_defaults(func=self.flash_first)
|
||||
# Flash Second
|
||||
self.parser_flash_second = self.subparsers.add_parser(
|
||||
"flash_second", help="Flash second block of OTP to device"
|
||||
)
|
||||
self._addArgsSWD(self.parser_flash_second)
|
||||
self._addArgsOpenOCD(self.parser_flash_second)
|
||||
self._addSecondArgs(self.parser_flash_second)
|
||||
self.parser_flash_second.set_defaults(func=self.flash_second)
|
||||
# Flash All
|
||||
self.parser_flash_all = self.subparsers.add_parser(
|
||||
"flash_all", help="Flash OTP to device"
|
||||
)
|
||||
self._addArgsSWD(self.parser_flash_all)
|
||||
self._addArgsOpenOCD(self.parser_flash_all)
|
||||
self._addFirstArgs(self.parser_flash_all)
|
||||
self._addSecondArgs(self.parser_flash_all)
|
||||
self.parser_flash_all.set_defaults(func=self.flash_all)
|
||||
|
@ -75,17 +76,19 @@ class Main(App):
|
|||
self.logger = logging.getLogger()
|
||||
self.timestamp = datetime.datetime.now().timestamp()
|
||||
|
||||
def _addArgsSWD(self, parser):
|
||||
def _addArgsOpenOCD(self, parser):
|
||||
parser.add_argument(
|
||||
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
|
||||
"--port-base", type=int, help="OpenOCD port base", default=3333
|
||||
)
|
||||
parser.add_argument(
|
||||
"--interface",
|
||||
type=str,
|
||||
help="OpenOCD interface",
|
||||
default="interface/cmsis-dap.cfg",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--serial", type=str, help="OpenOCD interface serial number"
|
||||
)
|
||||
parser.add_argument("--serial", type=str, help="ST-Link Serial Number")
|
||||
|
||||
def _getCubeParams(self):
|
||||
return {
|
||||
"port": self.args.port,
|
||||
"serial": self.args.serial,
|
||||
}
|
||||
|
||||
def _addFirstArgs(self, parser):
|
||||
parser.add_argument("--version", type=int, help="Version", required=True)
|
||||
|
@ -173,14 +176,22 @@ class Main(App):
|
|||
file.write(self._packFirst())
|
||||
|
||||
self.logger.info(f"Flashing OTP")
|
||||
cp = CubeProgrammer(self._getCubeParams())
|
||||
cp.flashBin("0x1FFF7000", filename)
|
||||
cp.resetTarget()
|
||||
|
||||
openocd = OpenOCDProgrammer(
|
||||
self.args.interface,
|
||||
self.args.port_base,
|
||||
self.args.serial,
|
||||
)
|
||||
|
||||
if not openocd.otp_write(0x1FFF7000, filename):
|
||||
raise Exception("Failed to flash OTP")
|
||||
|
||||
self.logger.info(f"Flashed Successfully")
|
||||
os.remove(filename)
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
return 1
|
||||
finally:
|
||||
os.remove(filename)
|
||||
|
||||
return 0
|
||||
|
||||
|
@ -197,14 +208,22 @@ class Main(App):
|
|||
file.write(self._packSecond())
|
||||
|
||||
self.logger.info(f"Flashing OTP")
|
||||
cp = CubeProgrammer(self._getCubeParams())
|
||||
cp.flashBin("0x1FFF7010", filename)
|
||||
cp.resetTarget()
|
||||
|
||||
openocd = OpenOCDProgrammer(
|
||||
self.args.interface,
|
||||
self.args.port_base,
|
||||
self.args.serial,
|
||||
)
|
||||
|
||||
if not openocd.otp_write(0x1FFF7010, filename):
|
||||
raise Exception("Failed to flash OTP")
|
||||
|
||||
self.logger.info(f"Flashed Successfully")
|
||||
os.remove(filename)
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
return 1
|
||||
finally:
|
||||
os.remove(filename)
|
||||
|
||||
return 0
|
||||
|
||||
|
@ -223,14 +242,22 @@ class Main(App):
|
|||
file.write(self._packSecond())
|
||||
|
||||
self.logger.info(f"Flashing OTP")
|
||||
cp = CubeProgrammer(self._getCubeParams())
|
||||
cp.flashBin("0x1FFF7000", filename)
|
||||
cp.resetTarget()
|
||||
|
||||
openocd = OpenOCDProgrammer(
|
||||
self.args.interface,
|
||||
self.args.port_base,
|
||||
self.args.serial,
|
||||
)
|
||||
|
||||
if not openocd.otp_write(0x1FFF7000, filename):
|
||||
raise Exception("Failed to flash OTP")
|
||||
|
||||
self.logger.info(f"Flashed Successfully")
|
||||
os.remove(filename)
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
return 1
|
||||
finally:
|
||||
os.remove(filename)
|
||||
|
||||
return 0
|
||||
|
||||
|
|
Loading…
Reference in a new issue