mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 05:06:30 +04:00
Compare commits
47 Commits
readme_upd
...
transport_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebde816105 | ||
|
|
6a2adf69e6 | ||
|
|
19ca956e7c | ||
|
|
1530c2b4f1 | ||
|
|
953a747b1a | ||
|
|
8835ef3598 | ||
|
|
cbc5d61ab7 | ||
|
|
e8210cd94d | ||
|
|
b6ff400587 | ||
|
|
5949d7be1c | ||
|
|
ac3bd337a1 | ||
|
|
f09d364e95 | ||
|
|
7f474ed9c6 | ||
|
|
04c914d945 | ||
|
|
93ae4db35a | ||
|
|
2db719f35c | ||
|
|
e36b87ddd9 | ||
|
|
1c360ae110 | ||
|
|
44140caa2d | ||
|
|
15894235a9 | ||
|
|
fae8d91880 | ||
|
|
78446bab07 | ||
|
|
84e94e728c | ||
|
|
b2042fd044 | ||
|
|
67a681f8b8 | ||
|
|
eb2607f308 | ||
|
|
9f6fc6fe79 | ||
|
|
77d6c41914 | ||
|
|
4705812d24 | ||
|
|
1c0276a0be | ||
|
|
abc4110198 | ||
|
|
99ba1b6a24 | ||
|
|
1fec80a533 | ||
|
|
53fffffd1b | ||
|
|
dfd5233760 | ||
|
|
7b4d66f0f4 | ||
|
|
c3d2a1f243 | ||
|
|
3491844d41 | ||
|
|
9ef1a4dfaa | ||
|
|
f53246d40b | ||
|
|
d1f1635dd2 | ||
|
|
e7c52828ea | ||
|
|
fea15bedd4 | ||
|
|
5bbee02704 | ||
|
|
8c44dfb6af | ||
|
|
540862fbf2 | ||
|
|
500ca0758a |
114
.github/workflows/codeql.yml
vendored
Normal file
114
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
run-name: "CodeQL Analyze ${{ github.ref_name }} by @${{ github.ACTOR }}"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["dev"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners
|
||||
# Consider using larger runners for possible analysis time improvements.
|
||||
runs-on: [ "ubuntu-latest" ]
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ["cpp"]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
env:
|
||||
PATH_SARIF_DIR: ".github/results.sarif"
|
||||
PATH_SARIF_FILE: ".github/results.sarif/${{ matrix.language }}.sarif"
|
||||
FBT_NO_SYNC: 0
|
||||
DIST_SUFFIX: "codeql"
|
||||
WORKFLOW_BRANCH_OR_TAG: release-cfw
|
||||
LANG_CATEGORY: "/language:${{matrix.language}}"
|
||||
|
||||
steps:
|
||||
- name: Checkout Firmware Files
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
clean: "true"
|
||||
submodules: "true"
|
||||
fetch-depth: "0"
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
setup-python-dependencies: true
|
||||
|
||||
- name: Resolve CodeQL Build Env
|
||||
uses: github/codeql-action/resolve-environment@v2
|
||||
with:
|
||||
language: ${{ matrix.language }}
|
||||
#debug: true
|
||||
|
||||
- name: Build Firmware
|
||||
shell: bash
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
./fbt COMPACT=1 DEBUG=0 FBT_NO_SYNC=${{ env.FBT_NO_SYNC }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
threads: 4
|
||||
category: "${{ env.LANG_CATEGORY }}"
|
||||
output: "${{ env.PATH_SARIF_DIR }}"
|
||||
upload-database: false
|
||||
upload: "failure-only" # disable the upload here - we will upload in a different action
|
||||
|
||||
- name: Filter dirs for SARIF
|
||||
uses: advanced-security/filter-sarif@v1
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
# filter out all test files unless they contain a sql-injection vulnerability
|
||||
patterns: |
|
||||
-build/**
|
||||
-dist/**
|
||||
-toolchain/**
|
||||
-lib/**
|
||||
input: "${{ env.PATH_SARIF_FILE }}"
|
||||
output: "${{ env.PATH_SARIF_FILE }}"
|
||||
|
||||
- name: Upload CodeQL SARIF
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
category: "${{ env.LANG_CATEGORY }}"
|
||||
sarif_file: "${{ env.PATH_SARIF_FILE }}"
|
||||
|
||||
# optional: for debugging the uploaded sarif
|
||||
# - name: Upload loc as a Build Artifact
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: sarif-results
|
||||
# path: sarif-results
|
||||
# retention-days: 1
|
||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,18 +1,27 @@
|
||||
## New changes
|
||||
* **Apple BLE Spam app** (by @Willy-JL | Plus research from ECTO-1A, xMasterX and techryptic) -> (app can be found in builds ` `, `e`, `n`, `r`)
|
||||
* Plugins -> Note for new users: **PicoPass emulation is available** in (Apps -> NFC -> PicoPass) + Latest PicoPass emulation fixes (by nvx) -> (app can be found in builds ` `, `e`, `n`, `r`)
|
||||
* SubGHz: **FAAC SLH - Programming mode** (by @xMasterX & @Eng1n33r (full research and PoC by @Skorpionm)| PR #585) -> [How to use](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
* SubGHz: FAAC SLH -> Add manually new options
|
||||
* SubGHz: **Ignore Princeton** option
|
||||
* SubGHz: **Save all settings, option to reset config to default** (by @derskythe | PR #590)
|
||||
* SubGHz: Fix 0xFFFF counter value being skipped
|
||||
* SubGHz: Fix path reset on save name scene exit
|
||||
* SubGHz: Various fixes
|
||||
* SubGHz Remote: New design (by @Svaarich) - Implemented by @gid9798
|
||||
* SubGHz Remote: Fix Sub-GHz Remote folder name (by @OperKH | PR #583)
|
||||
* SubGHz Remote: submodule (by @gid9798 | PR #592)
|
||||
* Infrared: Updated universal assets (by @amec0e | PR #594)
|
||||
* Infrared: Remake custom universal remotes to use new design (New icons by @Svaarich)
|
||||
* UI: Keyboard ok to toggle select all in cursor mode (by @Willy-JL)
|
||||
* CI/CD: CodeQL for internal usage
|
||||
* CI/CD: Fixed regular builds having `c` in version name in the device info while not being actual `c` build
|
||||
* Docs: New FAAC SLH instructions
|
||||
* Docs: Readme & Changelog fixes (by @gid9798 | PR #586)
|
||||
* Docs: Readme & Changelog fixes (by @gid9798 | PR #586 #600)
|
||||
* OFW: Sub-GHz: fix incorrect key parsing crash
|
||||
* OFW: fbt: added FW_CFG_name with build configuration
|
||||
* OFW: SD-Card: proper HAL -> **Breaking API change, API 37.x -> API 38.x** - **Update your apps!**
|
||||
* OFW: Various Fixes and Improvements -> **Breaking API change, API 36.x -> API 37.x** - **Update your apps!**
|
||||
* OFW: iButton: Return to the file selection if file is corrupted
|
||||
* OFW: Account for the "-" in line carry-over
|
||||
* OFW: github: workflow improvements
|
||||
|
||||
@@ -166,13 +166,13 @@ You can support us by using links or addresses below:
|
||||
### [🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins/releases/latest)
|
||||
### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Deafult)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack)
|
||||
|
||||
See full list and sources here: [xMasterX/all-the-plugin](https://github.com/xMasterX/all-the-plugins/tree/dev)
|
||||
See full list and sources here: [xMasterX/all-the-plugins](https://github.com/xMasterX/all-the-plugins/tree/dev)
|
||||
|
||||
### Official Flipper Zero Apps Catalog [web version](https://lab.flipper.net/apps) or mobile app
|
||||
|
||||
# Instructions
|
||||
## First lock official docs [docs.flipper.net](https://docs.flipper.net/)
|
||||
## [How to install](/documentation/HowToInstall.md) - [versions info](/CHANGELOG.md#what-n-r-e--c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater): `n`,` `,`e`...
|
||||
## [How to install](/documentation/HowToInstall.md) - [versions info](/CHANGELOG.md#recommended-update-option---web-updater): `n`,` `,`e`...
|
||||
## Firmware & Development
|
||||
|
||||
### - **[How to build](/documentation/HowToBuild.md#how-to-build-by-yourself) | [Project-structure](#project-structure)**
|
||||
|
||||
@@ -77,6 +77,7 @@ typedef enum {
|
||||
SubGhzCustomEventSceneAnalyzerLock,
|
||||
SubGhzCustomEventSceneAnalyzerUnlock,
|
||||
SubGhzCustomEventSceneSettingLock,
|
||||
SubGhzCustomEventSceneSettingResetToDefault,
|
||||
|
||||
SubGhzCustomEventSceneExit,
|
||||
SubGhzCustomEventSceneStay,
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
|
||||
#define TAG "SubGhz"
|
||||
#define TAG "SubGhzTxRx"
|
||||
|
||||
static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
|
||||
UNUSED(instance);
|
||||
@@ -30,8 +29,7 @@ SubGhzTxRx* subghz_txrx_alloc() {
|
||||
|
||||
instance->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||
instance->preset->name = furi_string_alloc();
|
||||
subghz_txrx_set_preset(
|
||||
instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0);
|
||||
subghz_txrx_set_default_preset(instance, 0);
|
||||
|
||||
instance->txrx_state = SubGhzTxRxStateSleep;
|
||||
|
||||
@@ -184,10 +182,11 @@ static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
|
||||
|
||||
static void subghz_txrx_idle(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_txrx_speaker_off(instance);
|
||||
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||
if(instance->txrx_state != SubGhzTxRxStateSleep) {
|
||||
subghz_devices_idle(instance->radio_device);
|
||||
subghz_txrx_speaker_off(instance);
|
||||
instance->txrx_state = SubGhzTxRxStateIDLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_txrx_rx_end(SubGhzTxRx* instance) {
|
||||
@@ -380,10 +379,11 @@ void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
float rssi = -127.0f;
|
||||
// Init value isn't using
|
||||
// float rssi = -127.0f;
|
||||
if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {
|
||||
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||
rssi = subghz_devices_get_rssi(instance->radio_device);
|
||||
float rssi = subghz_devices_get_rssi(instance->radio_device);
|
||||
|
||||
// Stay if RSSI is high enough
|
||||
if(rssi > -90.0f) {
|
||||
@@ -404,7 +404,7 @@ void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
|
||||
|
||||
if(instance->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_txrx_rx_end(instance);
|
||||
};
|
||||
}
|
||||
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
|
||||
subghz_receiver_reset(instance->receiver);
|
||||
instance->preset->frequency =
|
||||
@@ -551,7 +551,7 @@ void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag fi
|
||||
subghz_receiver_set_filter(instance->receiver, filter);
|
||||
}
|
||||
|
||||
void subghz_txrx_set_rx_calback(
|
||||
void subghz_txrx_set_rx_callback(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzReceiverCallback callback,
|
||||
void* context) {
|
||||
@@ -671,4 +671,32 @@ void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) {
|
||||
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->receiver;
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_set_default_preset(SubGhzTxRx* instance, uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
|
||||
const char* default_modulation = "AM650";
|
||||
if(frequency == 0) {
|
||||
frequency = subghz_setting_get_default_frequency(subghz_txrx_get_setting(instance));
|
||||
}
|
||||
subghz_txrx_set_preset(instance, default_modulation, frequency, NULL, 0);
|
||||
}
|
||||
|
||||
const char*
|
||||
subghz_txrx_set_preset_internal(SubGhzTxRx* instance, uint32_t frequency, uint8_t index) {
|
||||
furi_assert(instance);
|
||||
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(instance);
|
||||
const char* preset_name = subghz_setting_get_preset_name(setting, index);
|
||||
subghz_setting_set_default_frequency(setting, frequency);
|
||||
|
||||
subghz_txrx_set_preset(
|
||||
instance,
|
||||
preset_name,
|
||||
frequency,
|
||||
subghz_setting_get_preset_data(setting, index),
|
||||
subghz_setting_get_preset_data_size(setting, index));
|
||||
|
||||
return preset_name;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag fi
|
||||
* @param callback Callback for receive data
|
||||
* @param context Context for callback
|
||||
*/
|
||||
void subghz_txrx_set_rx_calback(
|
||||
void subghz_txrx_set_rx_callback(
|
||||
SubGhzTxRx* instance,
|
||||
SubGhzReceiverCallback callback,
|
||||
void* context);
|
||||
@@ -329,7 +329,7 @@ float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance);
|
||||
*/
|
||||
const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance);
|
||||
|
||||
/* Get get intelligence whether frequency the selected radio device to use
|
||||
/* Get intelligence whether frequency the selected radio device to use
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @return bool True if the frequency is valid
|
||||
@@ -344,3 +344,22 @@ bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance);
|
||||
void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance);
|
||||
|
||||
SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance); // TODO use only in DecodeRaw
|
||||
|
||||
/**
|
||||
* @brief Set current preset AM650 without additional params
|
||||
*
|
||||
* @param instance - instance Pointer to a SubGhzTxRx
|
||||
* @param frequency - frequency of preset, if pass 0 then taking default frequency 433.92MHz
|
||||
*/
|
||||
void subghz_txrx_set_default_preset(SubGhzTxRx* instance, uint32_t frequency);
|
||||
|
||||
/**
|
||||
* @brief Set current preset by index
|
||||
*
|
||||
* @param instance - instance Pointer to a SubGhzTxRx
|
||||
* @param frequency - frequency of new preset
|
||||
* @param index - index of preset taken from SubGhzSetting
|
||||
* @return const char* - name of preset
|
||||
*/
|
||||
const char*
|
||||
subghz_txrx_set_preset_internal(SubGhzTxRx* instance, uint32_t frequency, uint8_t index);
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "../views/receiver.h"
|
||||
#include <lib/subghz/protocols/raw.h>
|
||||
|
||||
#include <lib/subghz/subghz_file_encoder_worker.h>
|
||||
|
||||
#define TAG "SubGhzDecodeRaw"
|
||||
#define SAMPLES_TO_READ_PER_TICK 400
|
||||
@@ -21,13 +17,20 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
|
||||
subghz->subghz_receiver,
|
||||
furi_string_get_cstr(frequency_str),
|
||||
furi_string_get_cstr(modulation_str),
|
||||
furi_string_get_cstr(history_stat_str));
|
||||
furi_string_get_cstr(history_stat_str),
|
||||
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
|
||||
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0);
|
||||
|
||||
furi_string_free(frequency_str);
|
||||
furi_string_free(modulation_str);
|
||||
} else {
|
||||
subghz_view_receiver_add_data_statusbar(
|
||||
subghz->subghz_receiver, furi_string_get_cstr(history_stat_str), "", "");
|
||||
subghz->subghz_receiver,
|
||||
furi_string_get_cstr(history_stat_str),
|
||||
"",
|
||||
"",
|
||||
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
|
||||
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0);
|
||||
}
|
||||
furi_string_free(history_stat_str);
|
||||
}
|
||||
@@ -154,7 +157,7 @@ void subghz_scene_decode_raw_on_enter(void* context) {
|
||||
subghz_view_receiver_set_callback(
|
||||
subghz->subghz_receiver, subghz_scene_decode_raw_callback, subghz);
|
||||
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
|
||||
subghz_txrx_set_rx_callback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
|
||||
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_Decodable);
|
||||
|
||||
@@ -170,7 +173,7 @@ void subghz_scene_decode_raw_on_enter(void* context) {
|
||||
} else {
|
||||
//Load history to receiver
|
||||
subghz_view_receiver_exit(subghz->subghz_receiver);
|
||||
for(uint8_t i = 0; i < subghz_history_get_item(subghz->history); i++) {
|
||||
for(uint16_t i = 0; i < subghz_history_get_item(subghz->history); i++) {
|
||||
furi_string_reset(item_name);
|
||||
furi_string_reset(item_time);
|
||||
subghz_history_get_text_item_menu(subghz->history, item_name, i);
|
||||
@@ -202,7 +205,7 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart);
|
||||
subghz->idx_menu_chosen = 0;
|
||||
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
|
||||
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
|
||||
|
||||
if(subghz_file_encoder_worker_is_running(subghz->decode_raw_file_worker_encoder)) {
|
||||
subghz_file_encoder_worker_stop(subghz->decode_raw_file_worker_encoder);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "../subghz.h"
|
||||
#include "../subghz_i.h"
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
|
||||
void subghz_scene_delete_raw_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
@@ -60,6 +60,9 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e
|
||||
subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer);
|
||||
if(frequency > 0) {
|
||||
subghz->last_settings->frequency = frequency;
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,8 +104,15 @@ void subghz_scene_read_raw_on_enter(void* context) {
|
||||
|
||||
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) {
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
if(furi_string_empty(file_name)) {
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx,
|
||||
subghz->last_settings->frequency,
|
||||
subghz->last_settings->preset_index);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
furi_string_free(file_name);
|
||||
subghz_scene_read_raw_update_statusbar(subghz);
|
||||
|
||||
//set callback view raw
|
||||
@@ -115,6 +122,8 @@ void subghz_scene_read_raw_on_enter(void* context) {
|
||||
|
||||
//set filter RAW feed
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_RAW);
|
||||
furi_string_free(file_name);
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);
|
||||
}
|
||||
|
||||
@@ -139,10 +148,9 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
} else {
|
||||
//Restore default setting
|
||||
if(subghz->raw_send_only) {
|
||||
subghz_set_default_preset(subghz);
|
||||
subghz_txrx_set_default_preset(subghz->txrx, 0);
|
||||
} else {
|
||||
subghz_txrx_set_preset(
|
||||
subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0);
|
||||
subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency);
|
||||
}
|
||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneSaved)) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "../views/receiver.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <lib/subghz/protocols/bin_raw.h>
|
||||
|
||||
@@ -70,13 +69,20 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
|
||||
subghz->subghz_receiver,
|
||||
furi_string_get_cstr(frequency_str),
|
||||
furi_string_get_cstr(modulation_str),
|
||||
furi_string_get_cstr(history_stat_str));
|
||||
furi_string_get_cstr(history_stat_str),
|
||||
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
|
||||
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0);
|
||||
|
||||
furi_string_free(frequency_str);
|
||||
furi_string_free(modulation_str);
|
||||
} else {
|
||||
subghz_view_receiver_add_data_statusbar(
|
||||
subghz->subghz_receiver, furi_string_get_cstr(history_stat_str), "", "");
|
||||
subghz->subghz_receiver,
|
||||
furi_string_get_cstr(history_stat_str),
|
||||
"",
|
||||
"",
|
||||
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
|
||||
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0);
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
}
|
||||
furi_string_free(history_stat_str);
|
||||
@@ -142,7 +148,17 @@ void subghz_scene_receiver_on_enter(void* context) {
|
||||
FuriString* item_time = furi_string_alloc();
|
||||
|
||||
if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) {
|
||||
subghz_txrx_set_preset(subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0);
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx, subghz->last_settings->frequency, subghz->last_settings->preset_index);
|
||||
#else
|
||||
subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency);
|
||||
#endif
|
||||
|
||||
subghz->filter = subghz->last_settings->filter;
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
|
||||
subghz->ignore_filter = subghz->last_settings->ignore_filter;
|
||||
|
||||
subghz_history_reset(history);
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart);
|
||||
subghz->idx_menu_chosen = 0;
|
||||
@@ -151,9 +167,9 @@ void subghz_scene_receiver_on_enter(void* context) {
|
||||
subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz));
|
||||
subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeLive);
|
||||
|
||||
//Load history to receiver
|
||||
// Load history to receiver
|
||||
subghz_view_receiver_exit(subghz->subghz_receiver);
|
||||
for(uint8_t i = 0; i < subghz_history_get_item(history); i++) {
|
||||
for(uint16_t i = 0; i < subghz_history_get_item(history); i++) {
|
||||
furi_string_reset(item_name);
|
||||
furi_string_reset(item_time);
|
||||
subghz_history_get_text_item_menu(history, item_name, i);
|
||||
@@ -167,13 +183,22 @@ void subghz_scene_receiver_on_enter(void* context) {
|
||||
}
|
||||
furi_string_free(item_name);
|
||||
furi_string_free(item_time);
|
||||
|
||||
subghz_view_receiver_set_callback(
|
||||
subghz->subghz_receiver, subghz_scene_receiver_callback, subghz);
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
|
||||
subghz_txrx_set_rx_callback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
|
||||
|
||||
if(!subghz_history_get_text_space_left(subghz->history, NULL)) {
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
}
|
||||
|
||||
// Check if hopping was enabled
|
||||
if(subghz->last_settings->enable_hopping) {
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateRunning);
|
||||
} else {
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
|
||||
}
|
||||
|
||||
subghz_txrx_rx_start(subghz->txrx);
|
||||
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
|
||||
|
||||
@@ -199,15 +224,14 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
|
||||
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
|
||||
|
||||
if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
|
||||
} else {
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
|
||||
subghz_txrx_set_preset(
|
||||
subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0);
|
||||
subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneStart);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "../subghz_i.h"
|
||||
#include <lib/toolbox/value_index.h>
|
||||
|
||||
#define TAG "SubGhzSceneReceiverConfig"
|
||||
|
||||
enum SubGhzSettingIndex {
|
||||
SubGhzSettingIndexFrequency,
|
||||
SubGhzSettingIndexHopping,
|
||||
@@ -9,7 +11,9 @@ enum SubGhzSettingIndex {
|
||||
SubGhzSettingIndexIgnoreStarline,
|
||||
SubGhzSettingIndexIgnoreCars,
|
||||
SubGhzSettingIndexIgnoreMagellan,
|
||||
SubGhzSettingIndexIgnorePrinceton,
|
||||
SubGhzSettingIndexSound,
|
||||
SubGhzSettingIndexResetToDefault,
|
||||
SubGhzSettingIndexLock,
|
||||
SubGhzSettingIndexRAWThresholdRSSI,
|
||||
};
|
||||
@@ -43,47 +47,51 @@ const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
|
||||
-40.0f,
|
||||
};
|
||||
|
||||
#define HOPPING_COUNT 2
|
||||
const char* const hopping_text[HOPPING_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||
#define COMBO_BOX_COUNT 2
|
||||
|
||||
const uint32_t hopping_value[COMBO_BOX_COUNT] = {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStateRunning,
|
||||
};
|
||||
|
||||
#define SPEAKER_COUNT 2
|
||||
const char* const speaker_text[SPEAKER_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t speaker_value[SPEAKER_COUNT] = {
|
||||
const uint32_t speaker_value[COMBO_BOX_COUNT] = {
|
||||
SubGhzSpeakerStateShutdown,
|
||||
SubGhzSpeakerStateEnable,
|
||||
};
|
||||
#define BIN_RAW_COUNT 2
|
||||
const char* const bin_raw_text[BIN_RAW_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
|
||||
|
||||
const uint32_t bin_raw_value[COMBO_BOX_COUNT] = {
|
||||
SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,
|
||||
};
|
||||
#define PROTOCOL_IGNORE_COUNT 2
|
||||
const char* const protocol_ignore_text[PROTOCOL_IGNORE_COUNT] = {
|
||||
|
||||
const char* const combobox_text[COMBO_BOX_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
|
||||
static void
|
||||
subghz_scene_receiver_config_set_ignore_filter(VariableItem* item, SubGhzProtocolFlag filter) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, combobox_text[index]);
|
||||
|
||||
if(index == 0) {
|
||||
CLEAR_BIT(subghz->ignore_filter, filter);
|
||||
} else {
|
||||
SET_BIT(subghz->ignore_filter, filter);
|
||||
}
|
||||
|
||||
subghz->last_settings->ignore_filter = subghz->ignore_filter;
|
||||
}
|
||||
|
||||
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
|
||||
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) {
|
||||
for(size_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) {
|
||||
if(value == subghz_setting_get_frequency(setting, i)) {
|
||||
index = i;
|
||||
break;
|
||||
@@ -100,7 +108,7 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void*
|
||||
uint8_t index = 0;
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
|
||||
|
||||
for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) {
|
||||
for(size_t i = 0; i < subghz_setting_get_preset_count(setting); i++) {
|
||||
if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) {
|
||||
index = i;
|
||||
break;
|
||||
@@ -111,23 +119,18 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void*
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t subghz_scene_receiver_config_hopper_value_index(
|
||||
const uint32_t value,
|
||||
const uint32_t values[],
|
||||
uint8_t values_count,
|
||||
void* context) {
|
||||
SubGhzHopperState subghz_scene_receiver_config_hopper_value_index(void* context) {
|
||||
furi_assert(context);
|
||||
UNUSED(values_count);
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(value == values[0]) {
|
||||
return 0;
|
||||
if(subghz_txrx_hopper_get_state(subghz->txrx) == SubGhzHopperStateOFF) {
|
||||
return SubGhzHopperStateOFF;
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||
" -----");
|
||||
return 1;
|
||||
return SubGhzHopperStateRunning;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,17 +183,19 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
|
||||
preset.frequency,
|
||||
subghz_setting_get_preset_data(setting, index),
|
||||
subghz_setting_get_preset_data_size(setting, index));
|
||||
subghz->last_settings->preset_index = index;
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
SubGhzHopperState index = variable_item_get_current_value_index(item);
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
|
||||
VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReceiverConfig);
|
||||
|
||||
variable_item_set_current_value_text(item, hopping_text[index]);
|
||||
if(hopping_value[index] == SubGhzHopperStateOFF) {
|
||||
variable_item_set_current_value_text(item, combobox_text[(uint8_t)index]);
|
||||
|
||||
if(index == SubGhzHopperStateOFF) {
|
||||
char text_buf[10] = {0};
|
||||
uint32_t frequency = subghz_setting_get_default_frequency(setting);
|
||||
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
|
||||
@@ -203,6 +208,7 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item)
|
||||
(frequency % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(frequency_item, text_buf);
|
||||
|
||||
// Maybe better add one more function with only with the frequency argument?
|
||||
subghz_txrx_set_preset(
|
||||
subghz->txrx,
|
||||
furi_string_get_cstr(preset.name),
|
||||
@@ -216,15 +222,15 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item)
|
||||
variable_item_set_current_value_index(
|
||||
frequency_item, subghz_setting_get_frequency_default_index(setting));
|
||||
}
|
||||
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[index]);
|
||||
subghz->last_settings->enable_hopping = index != SubGhzHopperStateOFF;
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, index);
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, speaker_text[index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[index]);
|
||||
subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]);
|
||||
}
|
||||
|
||||
@@ -232,9 +238,12 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, bin_raw_text[index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[index]);
|
||||
subghz->filter = bin_raw_value[index];
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
|
||||
|
||||
// We can set here, but during subghz_last_settings_save filter was changed to ignore BinRAW
|
||||
subghz->last_settings->filter = subghz->filter;
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
|
||||
@@ -243,21 +252,10 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
|
||||
|
||||
variable_item_set_current_value_text(item, raw_threshold_rssi_text[index]);
|
||||
subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[index]);
|
||||
|
||||
subghz->last_settings->rssi = raw_threshold_rssi_value[index];
|
||||
}
|
||||
|
||||
static inline void
|
||||
subghz_scene_receiver_config_set_ignore_filter(VariableItem* item, SubGhzProtocolFlag filter) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, protocol_ignore_text[index]);
|
||||
|
||||
if(index == 0) {
|
||||
CLEAR_BIT(subghz->ignore_filter, filter);
|
||||
} else {
|
||||
SET_BIT(subghz->ignore_filter, filter);
|
||||
}
|
||||
}
|
||||
static inline bool subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
SubGhzProtocolFlag filter,
|
||||
SubGhzProtocolFlag flag) {
|
||||
@@ -273,7 +271,11 @@ static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) {
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_magellan(VariableItem* item) {
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magelan);
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magellan);
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_princeton(VariableItem* item) {
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Princeton);
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
||||
@@ -282,6 +284,46 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context,
|
||||
if(index == SubGhzSettingIndexLock) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock);
|
||||
} else if(index == SubGhzSettingIndexResetToDefault) {
|
||||
// Reset all values to default state!
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx,
|
||||
SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY,
|
||||
SUBGHZ_LAST_SETTING_DEFAULT_PRESET);
|
||||
#else
|
||||
subghz_txrx_set_default_preset(subghz->txrx, SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY);
|
||||
#endif
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
|
||||
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
|
||||
const char* preset_name = furi_string_get_cstr(preset.name);
|
||||
int preset_index = subghz_setting_get_inx_preset_by_name(setting, preset_name);
|
||||
const int default_index = 0;
|
||||
|
||||
subghz->last_settings->frequency = preset.frequency;
|
||||
subghz->last_settings->preset_index = preset_index;
|
||||
|
||||
subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[default_index]);
|
||||
subghz->filter = bin_raw_value[0];
|
||||
subghz->ignore_filter = 0x00;
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
|
||||
subghz->last_settings->ignore_filter = subghz->ignore_filter;
|
||||
subghz->last_settings->filter = subghz->filter;
|
||||
|
||||
subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]);
|
||||
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]);
|
||||
subghz->last_settings->enable_hopping = hopping_value[default_index];
|
||||
|
||||
variable_item_list_set_selected_item(subghz->variable_item_list, default_index);
|
||||
variable_item_list_reset(subghz->variable_item_list);
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneSettingResetToDefault);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,13 +372,13 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Hopping:",
|
||||
HOPPING_COUNT,
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_hopping_running,
|
||||
subghz);
|
||||
value_index = subghz_scene_receiver_config_hopper_value_index(
|
||||
subghz_txrx_hopper_get_state(subghz->txrx), hopping_value, HOPPING_COUNT, subghz);
|
||||
value_index = subghz_scene_receiver_config_hopper_value_index(subghz);
|
||||
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
}
|
||||
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||
@@ -344,12 +386,13 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Bin RAW:",
|
||||
BIN_RAW_COUNT,
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_bin_raw,
|
||||
subghz);
|
||||
value_index = value_index_uint32(subghz->filter, bin_raw_value, BIN_RAW_COUNT);
|
||||
|
||||
value_index = value_index_uint32(subghz->filter, bin_raw_value, COMBO_BOX_COUNT);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, bin_raw_text[value_index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
}
|
||||
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||
@@ -357,52 +400,74 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore Starline:",
|
||||
PROTOCOL_IGNORE_COUNT,
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_starline,
|
||||
subghz);
|
||||
|
||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_StarLine);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, protocol_ignore_text[value_index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore Cars:",
|
||||
PROTOCOL_IGNORE_COUNT,
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_auto_alarms,
|
||||
subghz);
|
||||
|
||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_AutoAlarms);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, protocol_ignore_text[value_index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore Magellan:",
|
||||
PROTOCOL_IGNORE_COUNT,
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_magellan,
|
||||
subghz);
|
||||
|
||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_Magelan);
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_Magellan);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, protocol_ignore_text[value_index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore Princeton:",
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_princeton,
|
||||
subghz);
|
||||
|
||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_Princeton);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
}
|
||||
|
||||
// Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :)
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Sound:",
|
||||
SPEAKER_COUNT,
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_speaker,
|
||||
subghz);
|
||||
value_index = value_index_uint32(
|
||||
subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, SPEAKER_COUNT);
|
||||
subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, COMBO_BOX_COUNT);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, speaker_text[value_index]);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||
SubGhzCustomEventManagerSet) {
|
||||
// Reset to default
|
||||
variable_item_list_add(subghz->variable_item_list, "Reset to default", 1, NULL, NULL);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
subghz->variable_item_list,
|
||||
subghz_scene_receiver_config_var_list_enter_callback,
|
||||
subghz);
|
||||
}
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||
SubGhzCustomEventManagerSet) {
|
||||
// Lock keyboard
|
||||
@@ -412,6 +477,7 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
subghz_scene_receiver_config_var_list_enter_callback,
|
||||
subghz);
|
||||
}
|
||||
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
|
||||
SubGhzCustomEventManagerSet) {
|
||||
item = variable_item_list_add(
|
||||
@@ -439,6 +505,9 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
|
||||
subghz_lock(subghz);
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.event == SubGhzCustomEventSceneSettingResetToDefault) {
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
@@ -448,6 +517,9 @@ void subghz_scene_receiver_config_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
variable_item_list_set_selected_item(subghz->variable_item_list, 0);
|
||||
variable_item_list_reset(subghz->variable_item_list);
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
|
||||
#define TAG "SubGhzSceneReceiverInfo"
|
||||
|
||||
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
|
||||
@@ -42,7 +42,7 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart);
|
||||
|
||||
subghz->idx_menu_chosen = 0;
|
||||
subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
|
||||
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
|
||||
|
||||
if(subghz_file_encoder_worker_is_running(subghz->decode_raw_file_worker_encoder)) {
|
||||
subghz_file_encoder_worker_stop(subghz->decode_raw_file_worker_encoder);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "../subghz_i.h"
|
||||
|
||||
#define TAG "SubGhzSceneSaved"
|
||||
|
||||
void subghz_scene_saved_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
|
||||
#define TAG "SubGhzSceneTransmitter"
|
||||
|
||||
void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/* Abandon hope, all ye who enter here. */
|
||||
|
||||
#include <furi/core/log.h>
|
||||
#include <subghz/types.h>
|
||||
#include <lib/toolbox/path.h>
|
||||
#include <float_tools.h>
|
||||
#include "subghz_i.h"
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
|
||||
#define TAG "SubGhzApp"
|
||||
|
||||
@@ -73,7 +74,7 @@ static void subghz_load_custom_presets(SubGhzSetting* setting) {
|
||||
|
||||
FlipperFormat* fff_temp = flipper_format_string_alloc();
|
||||
|
||||
for(uint8_t i = 0; i < COUNT_OF(presets); i++) {
|
||||
for(size_t i = 0; i < COUNT_OF(presets); i++) {
|
||||
flipper_format_insert_or_update_string_cstr(fff_temp, "Custom_preset_data", presets[i][1]);
|
||||
|
||||
flipper_format_rewind(fff_temp);
|
||||
@@ -111,7 +112,9 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
||||
|
||||
// Open Notification record
|
||||
subghz->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
#if SUBGHZ_MEASURE_LOADING
|
||||
uint32_t load_ticks = furi_get_tick();
|
||||
#endif
|
||||
subghz->txrx = subghz_txrx_alloc();
|
||||
|
||||
if(!alloc_for_tx_only) {
|
||||
@@ -195,35 +198,46 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
||||
|
||||
// Load last used values for Read, Read RAW, etc. or default
|
||||
subghz->last_settings = subghz_last_settings_alloc();
|
||||
subghz_last_settings_load(subghz->last_settings, 0);
|
||||
if(!alloc_for_tx_only) {
|
||||
#if FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"last frequency: %ld, preset: %ld",
|
||||
subghz->last_settings->frequency,
|
||||
subghz->last_settings->preset);
|
||||
size_t preset_count = subghz_setting_get_preset_count(setting);
|
||||
subghz_last_settings_load(subghz->last_settings, preset_count);
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
subghz_setting_set_default_frequency(setting, subghz->last_settings->frequency);
|
||||
}
|
||||
|
||||
if(!alloc_for_tx_only) {
|
||||
subghz_txrx_set_preset(subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0);
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx, subghz->last_settings->frequency, subghz->last_settings->preset_index);
|
||||
#else
|
||||
subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency);
|
||||
#endif
|
||||
subghz->history = subghz_history_alloc();
|
||||
}
|
||||
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
|
||||
|
||||
if(!alloc_for_tx_only) {
|
||||
subghz->history = subghz_history_alloc();
|
||||
}
|
||||
|
||||
subghz->secure_data = malloc(sizeof(SecureData));
|
||||
|
||||
subghz->filter = SubGhzProtocolFlag_Decodable;
|
||||
subghz->ignore_filter = 0x0;
|
||||
if(!alloc_for_tx_only) {
|
||||
subghz->ignore_filter = subghz->last_settings->ignore_filter;
|
||||
subghz->filter = subghz->last_settings->filter;
|
||||
} else {
|
||||
subghz->filter = SubGhzProtocolFlag_Decodable;
|
||||
subghz->ignore_filter = 0x0;
|
||||
}
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
|
||||
subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz);
|
||||
|
||||
if(!alloc_for_tx_only) {
|
||||
if(!float_is_equal(subghz->last_settings->rssi, 0)) {
|
||||
subghz_threshold_rssi_set(subghz->threshold_rssi, subghz->last_settings->rssi);
|
||||
} else {
|
||||
subghz->last_settings->rssi = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
|
||||
}
|
||||
}
|
||||
#if SUBGHZ_MEASURE_LOADING
|
||||
load_ticks = furi_get_tick() - load_ticks;
|
||||
FURI_LOG_I(TAG, "Loaded: %ld ms.", load_ticks);
|
||||
#endif
|
||||
//Init Error_str
|
||||
subghz->error_str = furi_string_alloc();
|
||||
|
||||
|
||||
@@ -4,30 +4,13 @@
|
||||
#include "subghz/types.h"
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include "views/receiver.h"
|
||||
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
#include <lib/subghz/protocols/raw.h>
|
||||
|
||||
#define TAG "SubGhz"
|
||||
|
||||
void subghz_set_default_preset(SubGhz* subghz) {
|
||||
furi_assert(subghz);
|
||||
subghz_txrx_set_preset(
|
||||
subghz->txrx,
|
||||
"AM650",
|
||||
subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)),
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
|
||||
void subghz_blink_start(SubGhz* subghz) {
|
||||
furi_assert(subghz);
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
@@ -60,7 +43,7 @@ void subghz_dialog_message_freq_error(SubGhz* subghz, bool only_rx) {
|
||||
DialogsApp* dialogs = subghz->dialogs;
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
const char* header_text = "Frequency not supported";
|
||||
const char* message_text = "Frequency\nis outside of\nsuported range.";
|
||||
const char* message_text = "Frequency\nis outside of\nsupported range.";
|
||||
|
||||
if(only_rx) {
|
||||
header_text = "Transmission is blocked";
|
||||
@@ -124,6 +107,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
|
||||
// TODO: use different frequency allowed lists for differnet modules (non cc1101)
|
||||
if(!furi_hal_subghz_is_tx_allowed(temp_data32)) {
|
||||
FURI_LOG_E(TAG, "This frequency can only be used for RX");
|
||||
|
||||
load_key_state = SubGhzLoadKeyStateOnlyRx;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
|
||||
#define SUBGHZ_MAX_LEN_NAME 64
|
||||
#define SUBGHZ_EXT_PRESET_NAME true
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN (-90.0f)
|
||||
#define SUBGHZ_MEASURE_LOADING false
|
||||
|
||||
typedef struct {
|
||||
uint8_t fix[4];
|
||||
@@ -98,7 +100,6 @@ struct SubGhz {
|
||||
void* rpc_ctx;
|
||||
};
|
||||
|
||||
void subghz_set_default_preset(SubGhz* subghz);
|
||||
void subghz_blink_start(SubGhz* subghz);
|
||||
void subghz_blink_stop(SubGhz* subghz);
|
||||
|
||||
|
||||
@@ -6,21 +6,19 @@
|
||||
#define SUBGHZ_LAST_SETTING_FILE_TYPE "Flipper SubGhz Last Setting File"
|
||||
#define SUBGHZ_LAST_SETTING_FILE_VERSION 1
|
||||
#define SUBGHZ_LAST_SETTINGS_PATH EXT_PATH("subghz/assets/last_subghz.settings")
|
||||
// 1 = "AM650"
|
||||
// "AM270", "AM650", "FM238", "FM476",
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
|
||||
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
|
||||
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER -93.0f
|
||||
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency"
|
||||
//#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" // AKA Modulation
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES "TimestampNames"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI"
|
||||
|
||||
SubGhzLastSettings* subghz_last_settings_alloc(void) {
|
||||
SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings));
|
||||
@@ -33,11 +31,7 @@ void subghz_last_settings_free(SubGhzLastSettings* instance) {
|
||||
}
|
||||
|
||||
void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count) {
|
||||
UNUSED(preset_count);
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "subghz_last_settings_load");
|
||||
#endif
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
@@ -49,15 +43,23 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
|
||||
bool temp_external_module_power_5v_disable = false;
|
||||
bool temp_external_module_power_amp = false;
|
||||
bool temp_timestamp_file_names = false;
|
||||
//int32_t temp_preset = 0;
|
||||
bool temp_enable_hopping = false;
|
||||
uint32_t temp_ignore_filter = 0;
|
||||
uint32_t temp_filter = 0;
|
||||
float temp_rssi = 0;
|
||||
uint32_t temp_preset = 0;
|
||||
|
||||
bool preset_was_read = false;
|
||||
bool rssi_was_read = false;
|
||||
bool filter_was_read = false;
|
||||
bool ignore_filter_was_read = false;
|
||||
bool frequency_analyzer_feedback_level_was_read = false;
|
||||
bool frequency_analyzer_trigger_was_read = false;
|
||||
|
||||
if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH &&
|
||||
flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) {
|
||||
/*
|
||||
flipper_format_read_int32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (int32_t*)&temp_preset, 1);*/
|
||||
preset_was_read = flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (uint32_t*)&temp_preset, 1);
|
||||
flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, (uint32_t*)&temp_frequency, 1);
|
||||
frequency_analyzer_feedback_level_was_read = flipper_format_read_uint32(
|
||||
@@ -90,22 +92,40 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP,
|
||||
(bool*)&temp_external_module_power_amp,
|
||||
1);
|
||||
|
||||
flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE,
|
||||
(bool*)&temp_enable_hopping,
|
||||
1);
|
||||
rssi_was_read = flipper_format_read_float(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, (float*)&temp_rssi, 1);
|
||||
ignore_filter_was_read = flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER,
|
||||
(uint32_t*)&temp_ignore_filter,
|
||||
1);
|
||||
filter_was_read = flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH);
|
||||
}
|
||||
|
||||
if(temp_frequency == 0 || !furi_hal_subghz_is_tx_allowed(temp_frequency)) {
|
||||
FURI_LOG_W(TAG, "Last used frequency not found or can't be used!");
|
||||
|
||||
instance->frequency = SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY;
|
||||
instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
instance->frequency_analyzer_feedback_level =
|
||||
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
|
||||
instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
|
||||
instance->external_module_enabled = false;
|
||||
instance->timestamp_file_names = false;
|
||||
instance->external_module_power_amp = false;
|
||||
|
||||
instance->enable_hopping = false;
|
||||
instance->ignore_filter = 0x00;
|
||||
// See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN;
|
||||
} else {
|
||||
instance->frequency = temp_frequency;
|
||||
instance->frequency_analyzer_feedback_level =
|
||||
@@ -117,10 +137,19 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
|
||||
temp_frequency_analyzer_trigger :
|
||||
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
|
||||
|
||||
/*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) {
|
||||
FURI_LOG_W(TAG, "Last used preset no found");*/
|
||||
instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
|
||||
if(!preset_was_read) {
|
||||
FURI_LOG_W(TAG, "Preset was not read. Set default");
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
} else if(temp_preset > (uint32_t)preset_count - 1) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Last used preset out of range. Preset to set: %ld, Max index: %ld. Set default",
|
||||
temp_preset,
|
||||
(uint32_t)preset_count - 1);
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
} else {
|
||||
instance->preset_index = temp_preset;
|
||||
}
|
||||
instance->external_module_enabled = temp_external_module_enabled;
|
||||
|
||||
instance->external_module_power_5v_disable = temp_external_module_power_5v_disable;
|
||||
@@ -130,12 +159,22 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
|
||||
// External power amp CC1101
|
||||
instance->external_module_power_amp = temp_external_module_power_amp;
|
||||
|
||||
instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN;
|
||||
instance->enable_hopping = temp_enable_hopping;
|
||||
instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00;
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW
|
||||
instance->filter = filter_was_read ? temp_filter : SubGhzProtocolFlag_Decodable;
|
||||
#else
|
||||
if(filter_was_read) {
|
||||
instance->filter = temp_filter != SubGhzProtocolFlag_Decodable ?
|
||||
SubGhzProtocolFlag_Decodable :
|
||||
temp_filter;
|
||||
} else {
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
}
|
||||
#endif
|
||||
// Set globally in furi hal
|
||||
furi_hal_subghz_set_ext_power_amp(instance->external_module_power_amp);
|
||||
|
||||
/*/} else {
|
||||
instance->preset = temp_preset;
|
||||
}*/
|
||||
}
|
||||
|
||||
flipper_format_file_close(fff_data_file);
|
||||
@@ -145,10 +184,10 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
|
||||
|
||||
bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "last_settings_save");
|
||||
#endif
|
||||
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW != true
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
#endif
|
||||
bool saved = false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
@@ -165,12 +204,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
if(!flipper_format_write_header_cstr(
|
||||
file, SUBGHZ_LAST_SETTING_FILE_TYPE, SUBGHZ_LAST_SETTING_FILE_VERSION))
|
||||
break;
|
||||
|
||||
/*
|
||||
if(!flipper_format_insert_or_update_int32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset, 1)) {
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset_index, 1)) {
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, &instance->frequency, 1)) {
|
||||
break;
|
||||
@@ -217,6 +254,22 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_float(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, &instance->rssi, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, &instance->ignore_filter, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) {
|
||||
break;
|
||||
}
|
||||
saved = true;
|
||||
} while(0);
|
||||
|
||||
@@ -230,3 +283,43 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
const char* LOG_ON = "ON";
|
||||
const char* LOG_OFF = "OFF";
|
||||
|
||||
static inline const char*
|
||||
subghz_last_settings_log_filter_get_index(uint32_t filter, uint32_t flag) {
|
||||
return READ_BIT(filter, flag) > 0 ? LOG_ON : LOG_OFF;
|
||||
}
|
||||
|
||||
static inline const char* bool_to_char(bool value) {
|
||||
return value ? LOG_ON : LOG_OFF;
|
||||
}
|
||||
|
||||
void subghz_last_settings_log(SubGhzLastSettings* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n"
|
||||
"Hopping: %s,\nPreset: %ld, RSSI: %.2f, "
|
||||
"Starline: %s, Cars: %s, Magellan: %s, BinRAW: %s",
|
||||
instance->frequency / 1000000 % 1000,
|
||||
instance->frequency / 10000 % 100,
|
||||
instance->frequency_analyzer_feedback_level,
|
||||
(double)instance->frequency_analyzer_trigger,
|
||||
bool_to_char(instance->external_module_enabled),
|
||||
bool_to_char(instance->external_module_power_5v_disable),
|
||||
bool_to_char(instance->timestamp_file_names),
|
||||
bool_to_char(instance->external_module_power_amp),
|
||||
bool_to_char(instance->enable_hopping),
|
||||
instance->preset_index,
|
||||
(double)instance->rssi,
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFlag_StarLine),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFlag_AutoAlarms),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFlag_Magellan),
|
||||
subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW));
|
||||
}
|
||||
|
||||
@@ -4,10 +4,20 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/subghz/types.h>
|
||||
|
||||
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER (-93.0f)
|
||||
#define SUBGHZ_LAST_SETTING_SAVE_BIN_RAW true
|
||||
#define SUBGHZ_LAST_SETTING_SAVE_PRESET true
|
||||
// 1 = "AM650"
|
||||
// "AM270", "AM650", "FM238", "FM476",
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
|
||||
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
|
||||
|
||||
typedef struct {
|
||||
uint32_t frequency;
|
||||
int32_t preset;
|
||||
uint32_t preset_index; // AKA Modulation
|
||||
uint32_t frequency_analyzer_feedback_level;
|
||||
float frequency_analyzer_trigger;
|
||||
// TODO not using but saved so as not to change the version
|
||||
@@ -16,6 +26,10 @@ typedef struct {
|
||||
bool external_module_power_amp;
|
||||
// saved so as not to change the version
|
||||
bool timestamp_file_names;
|
||||
bool enable_hopping;
|
||||
uint32_t ignore_filter;
|
||||
uint32_t filter;
|
||||
float rssi;
|
||||
} SubGhzLastSettings;
|
||||
|
||||
SubGhzLastSettings* subghz_last_settings_alloc(void);
|
||||
@@ -25,3 +39,5 @@ void subghz_last_settings_free(SubGhzLastSettings* instance);
|
||||
void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count);
|
||||
|
||||
bool subghz_last_settings_save(SubGhzLastSettings* instance);
|
||||
|
||||
void subghz_last_settings_log(SubGhzLastSettings* instance);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define MENU_ITEMS 4u
|
||||
#define UNLOCK_CNT 3
|
||||
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
|
||||
// #define SUBGHZ_RAW_THRESHOLD_MIN (-90.0f)
|
||||
|
||||
#define FLIP_TIMEOUT (500)
|
||||
|
||||
@@ -62,6 +62,8 @@ typedef struct {
|
||||
FuriString* preset_str;
|
||||
FuriString* history_stat_str;
|
||||
FuriString* progress_str;
|
||||
bool hopping_enabled;
|
||||
bool bin_raw_enabled;
|
||||
SubGhzReceiverHistory* history;
|
||||
uint16_t idx;
|
||||
uint16_t list_offset;
|
||||
@@ -200,7 +202,9 @@ void subghz_view_receiver_add_data_statusbar(
|
||||
SubGhzViewReceiver* subghz_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str) {
|
||||
const char* history_stat_str,
|
||||
bool hopping_enabled,
|
||||
bool bin_raw_enabled) {
|
||||
furi_assert(subghz_receiver);
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
@@ -209,6 +213,8 @@ void subghz_view_receiver_add_data_statusbar(
|
||||
furi_string_set(model->frequency_str, frequency_str);
|
||||
furi_string_set(model->preset_str, preset_str);
|
||||
furi_string_set(model->history_stat_str, history_stat_str);
|
||||
model->hopping_enabled = hopping_enabled;
|
||||
model->bin_raw_enabled = bin_raw_enabled;
|
||||
},
|
||||
true);
|
||||
}
|
||||
@@ -311,7 +317,6 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(model->history_item == 0) {
|
||||
// TODO
|
||||
if(model->mode == SubGhzViewReceiverModeLive) {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
@@ -320,9 +325,19 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
|
||||
(model->device_type == SubGhzRadioDeviceTypeInternal) ? &I_Scanning_123x52 :
|
||||
&I_Fishing_123x52);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 63, 46, "Scanning...");
|
||||
if(model->hopping_enabled) {
|
||||
canvas_draw_str(canvas, 59, 46, "Hopper scan...");
|
||||
} else {
|
||||
canvas_draw_str(canvas, 59, 46, "Fixed scan...");
|
||||
}
|
||||
//canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(model->bin_raw_enabled) {
|
||||
const uint8_t vertical_offset = 17;
|
||||
const uint8_t horizontal_offset = 118;
|
||||
canvas_draw_icon(canvas, horizontal_offset, vertical_offset, &I_Cos_9x7);
|
||||
}
|
||||
} else {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
@@ -458,8 +473,10 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool consumed = false;
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context);
|
||||
consumed = true;
|
||||
} else if(
|
||||
event->key == InputKeyUp &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
@@ -471,6 +488,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
subghz_view_receiver_show_time_moment(context);
|
||||
},
|
||||
true);
|
||||
consumed = true;
|
||||
} else if(
|
||||
event->key == InputKeyDown &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
@@ -484,8 +502,10 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
}
|
||||
},
|
||||
true);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyRight && event->type == InputTypeLong) {
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
@@ -498,6 +518,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
}
|
||||
},
|
||||
false);
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_receiver->view,
|
||||
@@ -509,11 +530,13 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
|
||||
}
|
||||
},
|
||||
false);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
subghz_view_receiver_update_offset(subghz_receiver);
|
||||
|
||||
return true;
|
||||
if(consumed) {
|
||||
subghz_view_receiver_update_offset(subghz_receiver);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void subghz_view_receiver_enter(void* context) {
|
||||
@@ -543,6 +566,8 @@ void subghz_view_receiver_exit(void* context) {
|
||||
model->list_offset = 0;
|
||||
model->history_item = 0;
|
||||
model->nodraw = false;
|
||||
model->hopping_enabled = false;
|
||||
model->bin_raw_enabled = false;
|
||||
},
|
||||
false);
|
||||
furi_timer_stop(subghz_receiver->timer);
|
||||
@@ -579,6 +604,8 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() {
|
||||
model->bar_show = SubGhzViewReceiverBarShowDefault;
|
||||
model->nodraw = false;
|
||||
model->history = malloc(sizeof(SubGhzReceiverHistory));
|
||||
model->hopping_enabled = false;
|
||||
model->bin_raw_enabled = false;
|
||||
SubGhzReceiverMenuItemArray_init(model->history->data);
|
||||
},
|
||||
true);
|
||||
@@ -622,7 +649,7 @@ View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
|
||||
|
||||
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
uint16_t idx = 0;
|
||||
uint16_t idx;
|
||||
with_view_model(
|
||||
subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false);
|
||||
return idx;
|
||||
|
||||
@@ -31,7 +31,9 @@ void subghz_view_receiver_add_data_statusbar(
|
||||
SubGhzViewReceiver* subghz_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str);
|
||||
const char* history_stat_str,
|
||||
bool hopping_enabled,
|
||||
bool bin_raw_enabled);
|
||||
|
||||
void subghz_view_receiver_set_radio_device_type(
|
||||
SubGhzViewReceiver* subghz_receiver,
|
||||
|
||||
@@ -32,4 +32,4 @@ SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level(
|
||||
SubGHzFrequencyAnalyzerFeedbackLevel level,
|
||||
bool update);
|
||||
|
||||
float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance);
|
||||
float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance);
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "../helpers/subghz_types.h"
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
|
||||
|
||||
typedef struct SubGhzReadRAW SubGhzReadRAW;
|
||||
|
||||
typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context);
|
||||
|
||||
Submodule applications/main/subghz_remote updated: a905d701ee...fb508ea02e
@@ -473,6 +473,7 @@ void cli_commands_init(Cli* cli) {
|
||||
cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, NULL);
|
||||
cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL);
|
||||
cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||
cli_add_command(cli, "l", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||
cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);
|
||||
cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL);
|
||||
cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
|
||||
|
||||
@@ -290,8 +290,7 @@ void elements_multiline_text_aligned(
|
||||
} else if((y + font_height) > canvas_height(canvas)) {
|
||||
line = furi_string_alloc_printf("%.*s...\n", chars_fit, start);
|
||||
} else {
|
||||
// Account for the added "-" in length
|
||||
line = furi_string_alloc_printf("%.*s-\n", chars_fit - 1, start);
|
||||
line = furi_string_alloc_printf("%.*s-\n", chars_fit, start);
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line));
|
||||
furi_string_free(line);
|
||||
|
||||
@@ -79,24 +79,37 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
uint8_t temp_x_default = 73;
|
||||
uint8_t temp_w_default = 66;
|
||||
if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) {
|
||||
// Only left text, no right text
|
||||
canvas_draw_str(canvas, 6, item_text_y, item->label);
|
||||
} else {
|
||||
if(furi_string_size(item->current_value_text) < (size_t)4) {
|
||||
temp_x_default = 80;
|
||||
temp_w_default = 71;
|
||||
}
|
||||
elements_scrollable_text_line_str(
|
||||
canvas, 6, item_text_y, 66, item->label, scroll_counter, false, false);
|
||||
canvas,
|
||||
6,
|
||||
item_text_y,
|
||||
temp_w_default,
|
||||
item->label,
|
||||
scroll_counter,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
if(item->locked) {
|
||||
canvas_draw_icon(canvas, 110, item_text_y - 8, &I_Lock_7x8);
|
||||
} else {
|
||||
if(item->current_value_index > 0) {
|
||||
canvas_draw_str(canvas, 73, item_text_y, "<");
|
||||
canvas_draw_str(canvas, temp_x_default, item_text_y, "<");
|
||||
}
|
||||
|
||||
elements_scrollable_text_line_str(
|
||||
canvas,
|
||||
(115 + 73) / 2 + 1,
|
||||
(115 + temp_x_default) / 2 + 1,
|
||||
item_text_y,
|
||||
37,
|
||||
furi_string_get_cstr(item->current_value_text),
|
||||
|
||||
@@ -174,9 +174,10 @@ void view_port_input_callback_set(
|
||||
|
||||
void view_port_update(ViewPort* view_port) {
|
||||
furi_assert(view_port);
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
// TODO: Uncomment when all apps are verified to be fixed !!!!!!!!!!!!!!!!!!!!!!!
|
||||
//furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
|
||||
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
//furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void view_port_gui_set(ViewPort* view_port, Gui* gui) {
|
||||
|
||||
@@ -303,7 +303,8 @@ static LoaderStatus loader_start_external_app(
|
||||
FlipperApplicationPreloadStatus preload_res =
|
||||
flipper_application_preload(loader->app.fap, path);
|
||||
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
|
||||
if(preload_res == FlipperApplicationPreloadStatusApiMismatch) {
|
||||
if((preload_res == FlipperApplicationPreloadStatusApiTooOld) ||
|
||||
(preload_res == FlipperApplicationPreloadStatusApiTooNew)) {
|
||||
if(!ignore_mismatch) {
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
@@ -32,8 +32,6 @@ typedef struct {
|
||||
uint32_t product_serial_number;
|
||||
uint8_t manufacturing_month;
|
||||
uint16_t manufacturing_year;
|
||||
|
||||
FS_Error error;
|
||||
} SDInfo;
|
||||
|
||||
const char* sd_api_get_fs_type_text(SDFsType fs_type);
|
||||
|
||||
@@ -26,11 +26,11 @@ static FS_Error storage_ext_parse_error(SDError error);
|
||||
|
||||
static bool sd_mount_card_internal(StorageData* storage, bool notify) {
|
||||
bool result = false;
|
||||
uint8_t counter = sd_max_mount_retry_count();
|
||||
uint8_t counter = furi_hal_sd_max_mount_retry_count();
|
||||
uint8_t bsp_result;
|
||||
SDData* sd_data = storage->data;
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
while(result == false && counter > 0 && furi_hal_sd_is_present()) {
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_wait(notification);
|
||||
@@ -39,9 +39,9 @@ static bool sd_mount_card_internal(StorageData* storage, bool notify) {
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
bsp_result = sd_init(true);
|
||||
bsp_result = furi_hal_sd_init(true);
|
||||
} else {
|
||||
bsp_result = sd_init(false);
|
||||
bsp_result = furi_hal_sd_init(false);
|
||||
}
|
||||
|
||||
if(bsp_result) {
|
||||
@@ -225,18 +225,18 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
|
||||
#endif
|
||||
}
|
||||
|
||||
SD_CID cid;
|
||||
SdSpiStatus status = sd_get_cid(&cid);
|
||||
FuriHalSdInfo info;
|
||||
FuriStatus status = furi_hal_sd_info(&info);
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
sd_info->manufacturer_id = cid.ManufacturerID;
|
||||
memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID));
|
||||
memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName));
|
||||
sd_info->product_revision_major = cid.ProdRev >> 4;
|
||||
sd_info->product_revision_minor = cid.ProdRev & 0x0F;
|
||||
sd_info->product_serial_number = cid.ProdSN;
|
||||
sd_info->manufacturing_year = 2000 + cid.ManufactYear;
|
||||
sd_info->manufacturing_month = cid.ManufactMonth;
|
||||
if(status == FuriStatusOk) {
|
||||
sd_info->manufacturer_id = info.manufacturer_id;
|
||||
memcpy(sd_info->oem_id, info.oem_id, sizeof(info.oem_id));
|
||||
memcpy(sd_info->product_name, info.product_name, sizeof(info.product_name));
|
||||
sd_info->product_revision_major = info.product_revision_major;
|
||||
sd_info->product_revision_minor = info.product_revision_minor;
|
||||
sd_info->product_serial_number = info.product_serial_number;
|
||||
sd_info->manufacturing_year = info.manufacturing_year;
|
||||
sd_info->manufacturing_month = info.manufacturing_month;
|
||||
}
|
||||
|
||||
return storage_ext_parse_error(error);
|
||||
@@ -246,19 +246,19 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
|
||||
SDData* sd_data = storage->data;
|
||||
|
||||
if(sd_data->sd_was_present) {
|
||||
if(hal_sd_detect()) {
|
||||
if(furi_hal_sd_is_present()) {
|
||||
FURI_LOG_I(TAG, "card detected");
|
||||
sd_data->sd_was_present = false;
|
||||
sd_mount_card(storage, notify);
|
||||
|
||||
if(!hal_sd_detect()) {
|
||||
if(!furi_hal_sd_is_present()) {
|
||||
FURI_LOG_I(TAG, "card removed while mounting");
|
||||
sd_unmount_card(storage);
|
||||
sd_data->sd_was_present = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(!hal_sd_detect()) {
|
||||
if(!furi_hal_sd_is_present()) {
|
||||
FURI_LOG_I(TAG, "card removed");
|
||||
sd_data->sd_was_present = true;
|
||||
|
||||
@@ -639,7 +639,7 @@ void storage_ext_init(StorageData* storage) {
|
||||
storage->api.tick = storage_ext_tick;
|
||||
storage->fs_api = &fs_api;
|
||||
|
||||
hal_sd_detect_init();
|
||||
furi_hal_sd_presence_init();
|
||||
|
||||
// do not notify on first launch, notifications app is waiting for our thread to read settings
|
||||
storage_ext_tick_internal(storage, false);
|
||||
|
||||
BIN
assets/icons/SubGhz/Cos_9x7.png
Normal file
BIN
assets/icons/SubGhz/Cos_9x7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 B |
@@ -24,3 +24,7 @@ There are 2 signals that will be exposed to external GPIO pins:
|
||||
|
||||
- `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode.
|
||||
- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible.
|
||||
|
||||
## FuriHalSD
|
||||
|
||||
`--extra-define=FURI_HAL_SD_SPI_DEBUG` enables SD card SPI bus logging.
|
||||
|
||||
@@ -93,6 +93,7 @@ else:
|
||||
"FURI_RAM_EXEC",
|
||||
],
|
||||
)
|
||||
env.AppendUnique(CPPDEFINES=["FW_CFG_${FIRMWARE_APP_SET}"])
|
||||
|
||||
env.ConfigureForTarget(env.subst("${TARGET_HW}"))
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,36.1,,
|
||||
Version,+,38.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
@@ -826,8 +826,9 @@ Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,Fl
|
||||
Function,+,flipper_application_is_plugin,_Bool,FlipperApplication*
|
||||
Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*"
|
||||
Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus
|
||||
Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_manifest_is_too_new,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_too_old,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
|
||||
Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication*
|
||||
@@ -1222,6 +1223,14 @@ Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
|
||||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
|
||||
Function,+,furi_hal_rtc_sync_shadow,void,
|
||||
Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
|
||||
Function,+,furi_hal_sd_get_card_state,FuriStatus,
|
||||
Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo*
|
||||
Function,+,furi_hal_sd_init,FuriStatus,_Bool
|
||||
Function,+,furi_hal_sd_is_present,_Bool,
|
||||
Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
|
||||
Function,+,furi_hal_sd_presence_init,void,
|
||||
Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
|
||||
Function,-,furi_hal_speaker_deinit,void,
|
||||
Function,-,furi_hal_speaker_init,void,
|
||||
@@ -1477,9 +1486,6 @@ Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
|
||||
Function,+,gui_set_lockdown,void,"Gui*, _Bool"
|
||||
Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
|
||||
Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
|
||||
Function,+,hal_sd_detect,_Bool,
|
||||
Function,+,hal_sd_detect_init,void,
|
||||
Function,+,hal_sd_detect_set_low,void,
|
||||
Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
|
||||
Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
|
||||
Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
|
||||
@@ -2423,7 +2429,6 @@ Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
|
||||
Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
|
||||
Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*,
|
||||
Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
|
||||
Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
|
||||
Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
|
||||
|
||||
|
@@ -136,6 +136,14 @@ static void furi_hal_resources_init_input_pins(GpioMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_resources_init_gpio_pins(GpioMode mode) {
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(!gpio_pins[i].debug) {
|
||||
furi_hal_gpio_init(gpio_pins[i].pin, mode, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_resources_init_early() {
|
||||
furi_hal_bus_enable(FuriHalBusGPIOA);
|
||||
furi_hal_bus_enable(FuriHalBusGPIOB);
|
||||
@@ -179,14 +187,7 @@ void furi_hal_resources_init_early() {
|
||||
furi_hal_gpio_write(&gpio_usb_dp, 0);
|
||||
|
||||
// External header pins
|
||||
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_resources_init_gpio_pins(GpioModeAnalog);
|
||||
}
|
||||
|
||||
void furi_hal_resources_deinit_early() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,36.1,,
|
||||
Version,+,38.0,,
|
||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
@@ -913,8 +913,9 @@ Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,Fl
|
||||
Function,+,flipper_application_is_plugin,_Bool,FlipperApplication*
|
||||
Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*"
|
||||
Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus
|
||||
Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_manifest_is_too_new,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_too_old,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
|
||||
Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication*
|
||||
@@ -1392,7 +1393,16 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t
|
||||
Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
|
||||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
|
||||
Function,+,furi_hal_rtc_sync_shadow,void,
|
||||
Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*"
|
||||
Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
|
||||
Function,+,furi_hal_sd_get_card_state,FuriStatus,
|
||||
Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo*
|
||||
Function,+,furi_hal_sd_init,FuriStatus,_Bool
|
||||
Function,+,furi_hal_sd_is_present,_Bool,
|
||||
Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
|
||||
Function,+,furi_hal_sd_presence_init,void,
|
||||
Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
|
||||
Function,-,furi_hal_speaker_deinit,void,
|
||||
Function,-,furi_hal_speaker_init,void,
|
||||
@@ -1688,9 +1698,6 @@ Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
|
||||
Function,+,gui_set_lockdown,void,"Gui*, _Bool"
|
||||
Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
|
||||
Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
|
||||
Function,+,hal_sd_detect,_Bool,
|
||||
Function,+,hal_sd_detect_init,void,
|
||||
Function,+,hal_sd_detect_set_low,void,
|
||||
Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
|
||||
Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
|
||||
Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
|
||||
@@ -3266,7 +3273,6 @@ Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
|
||||
Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
|
||||
Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*,
|
||||
Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
|
||||
Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
|
||||
Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
|
||||
|
||||
|
@@ -1,843 +0,0 @@
|
||||
#include "sd_spi_io.h"
|
||||
#include "sector_cache.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi/core/core_defines.h>
|
||||
|
||||
// #define SD_SPI_DEBUG 1
|
||||
#define TAG "SdSpi"
|
||||
|
||||
#ifdef SD_SPI_DEBUG
|
||||
#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define sd_spi_debug(...)
|
||||
#endif
|
||||
|
||||
#define SD_CMD_LENGTH 6
|
||||
#define SD_DUMMY_BYTE 0xFF
|
||||
#define SD_ANSWER_RETRY_COUNT 8
|
||||
#define SD_IDLE_RETRY_COUNT 100
|
||||
|
||||
#define FLAG_SET(x, y) (((x) & (y)) == (y))
|
||||
|
||||
static bool sd_high_capacity = false;
|
||||
|
||||
typedef enum {
|
||||
SdSpiDataResponceOK = 0x05,
|
||||
SdSpiDataResponceCRCError = 0x0B,
|
||||
SdSpiDataResponceWriteError = 0x0D,
|
||||
SdSpiDataResponceOtherError = 0xFF,
|
||||
} SdSpiDataResponce;
|
||||
|
||||
typedef struct {
|
||||
uint8_t r1;
|
||||
uint8_t r2;
|
||||
uint8_t r3;
|
||||
uint8_t r4;
|
||||
uint8_t r5;
|
||||
} SdSpiCmdAnswer;
|
||||
|
||||
typedef enum {
|
||||
SdSpiCmdAnswerTypeR1,
|
||||
SdSpiCmdAnswerTypeR1B,
|
||||
SdSpiCmdAnswerTypeR2,
|
||||
SdSpiCmdAnswerTypeR3,
|
||||
SdSpiCmdAnswerTypeR4R5,
|
||||
SdSpiCmdAnswerTypeR7,
|
||||
} SdSpiCmdAnswerType;
|
||||
|
||||
/*
|
||||
SdSpiCmd and SdSpiToken use non-standard enum value names convention,
|
||||
because it is more convenient to look for documentation on a specific command.
|
||||
For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for
|
||||
SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification".
|
||||
|
||||
Do not use that naming convention in other places.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
SD_CMD0_GO_IDLE_STATE = 0,
|
||||
SD_CMD1_SEND_OP_COND = 1,
|
||||
SD_CMD8_SEND_IF_COND = 8,
|
||||
SD_CMD9_SEND_CSD = 9,
|
||||
SD_CMD10_SEND_CID = 10,
|
||||
SD_CMD12_STOP_TRANSMISSION = 12,
|
||||
SD_CMD13_SEND_STATUS = 13,
|
||||
SD_CMD16_SET_BLOCKLEN = 16,
|
||||
SD_CMD17_READ_SINGLE_BLOCK = 17,
|
||||
SD_CMD18_READ_MULT_BLOCK = 18,
|
||||
SD_CMD23_SET_BLOCK_COUNT = 23,
|
||||
SD_CMD24_WRITE_SINGLE_BLOCK = 24,
|
||||
SD_CMD25_WRITE_MULT_BLOCK = 25,
|
||||
SD_CMD27_PROG_CSD = 27,
|
||||
SD_CMD28_SET_WRITE_PROT = 28,
|
||||
SD_CMD29_CLR_WRITE_PROT = 29,
|
||||
SD_CMD30_SEND_WRITE_PROT = 30,
|
||||
SD_CMD32_SD_ERASE_GRP_START = 32,
|
||||
SD_CMD33_SD_ERASE_GRP_END = 33,
|
||||
SD_CMD34_UNTAG_SECTOR = 34,
|
||||
SD_CMD35_ERASE_GRP_START = 35,
|
||||
SD_CMD36_ERASE_GRP_END = 36,
|
||||
SD_CMD37_UNTAG_ERASE_GROUP = 37,
|
||||
SD_CMD38_ERASE = 38,
|
||||
SD_CMD41_SD_APP_OP_COND = 41,
|
||||
SD_CMD55_APP_CMD = 55,
|
||||
SD_CMD58_READ_OCR = 58,
|
||||
} SdSpiCmd;
|
||||
|
||||
/** Data tokens */
|
||||
typedef enum {
|
||||
SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE,
|
||||
SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE,
|
||||
SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE,
|
||||
SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC,
|
||||
SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD,
|
||||
} SdSpiToken;
|
||||
|
||||
/** R1 answer value */
|
||||
typedef enum {
|
||||
SdSpi_R1_NO_ERROR = 0x00,
|
||||
SdSpi_R1_IN_IDLE_STATE = 0x01,
|
||||
SdSpi_R1_ERASE_RESET = 0x02,
|
||||
SdSpi_R1_ILLEGAL_COMMAND = 0x04,
|
||||
SdSpi_R1_COM_CRC_ERROR = 0x08,
|
||||
SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10,
|
||||
SdSpi_R1_ADDRESS_ERROR = 0x20,
|
||||
SdSpi_R1_PARAMETER_ERROR = 0x40,
|
||||
} SdSpiR1;
|
||||
|
||||
/** R2 answer value */
|
||||
typedef enum {
|
||||
/* R2 answer value */
|
||||
SdSpi_R2_NO_ERROR = 0x00,
|
||||
SdSpi_R2_CARD_LOCKED = 0x01,
|
||||
SdSpi_R2_LOCKUNLOCK_ERROR = 0x02,
|
||||
SdSpi_R2_ERROR = 0x04,
|
||||
SdSpi_R2_CC_ERROR = 0x08,
|
||||
SdSpi_R2_CARD_ECC_FAILED = 0x10,
|
||||
SdSpi_R2_WP_VIOLATION = 0x20,
|
||||
SdSpi_R2_ERASE_PARAM = 0x40,
|
||||
SdSpi_R2_OUTOFRANGE = 0x80,
|
||||
} SdSpiR2;
|
||||
|
||||
static inline void sd_spi_select_card() {
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false);
|
||||
furi_delay_us(10); // Entry guard time for some SD cards
|
||||
}
|
||||
|
||||
static inline void sd_spi_deselect_card() {
|
||||
furi_delay_us(10); // Exit guard time for some SD cards
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true);
|
||||
}
|
||||
|
||||
static void sd_spi_bus_to_ground() {
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->miso,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFnUnused);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->mosi,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFnUnused);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->sck,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFnUnused);
|
||||
|
||||
sd_spi_select_card();
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false);
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false);
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false);
|
||||
}
|
||||
|
||||
static void sd_spi_bus_rise_up() {
|
||||
sd_spi_deselect_card();
|
||||
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->miso,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->mosi,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->sck,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
}
|
||||
|
||||
static inline uint8_t sd_spi_read_byte(void) {
|
||||
uint8_t responce;
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS));
|
||||
return responce;
|
||||
}
|
||||
|
||||
static inline void sd_spi_write_byte(uint8_t data) {
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) {
|
||||
uint8_t responce;
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS));
|
||||
return responce;
|
||||
}
|
||||
|
||||
static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) {
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) {
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) {
|
||||
uint32_t timeout_mul = (size / 512) + 1;
|
||||
furi_check(furi_hal_spi_bus_trx_dma(
|
||||
furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul));
|
||||
}
|
||||
|
||||
static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) {
|
||||
uint32_t timeout_mul = (size / 512) + 1;
|
||||
furi_check(furi_hal_spi_bus_trx_dma(
|
||||
furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul));
|
||||
}
|
||||
|
||||
static uint8_t sd_spi_wait_for_data_and_read(void) {
|
||||
uint8_t retry_count = SD_ANSWER_RETRY_COUNT;
|
||||
uint8_t responce;
|
||||
|
||||
// Wait until we get a valid data
|
||||
do {
|
||||
responce = sd_spi_read_byte();
|
||||
retry_count--;
|
||||
|
||||
} while((responce == SD_DUMMY_BYTE) && retry_count);
|
||||
|
||||
return responce;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000);
|
||||
uint8_t byte;
|
||||
|
||||
do {
|
||||
byte = sd_spi_read_byte();
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
return SdSpiStatusTimeout;
|
||||
}
|
||||
} while((byte != data));
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static inline void sd_spi_deselect_card_and_purge() {
|
||||
sd_spi_deselect_card();
|
||||
sd_spi_read_byte();
|
||||
}
|
||||
|
||||
static inline void sd_spi_purge_crc() {
|
||||
sd_spi_read_byte();
|
||||
sd_spi_read_byte();
|
||||
}
|
||||
|
||||
static SdSpiCmdAnswer
|
||||
sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) {
|
||||
uint8_t frame[SD_CMD_LENGTH];
|
||||
SdSpiCmdAnswer cmd_answer = {
|
||||
.r1 = SD_DUMMY_BYTE,
|
||||
.r2 = SD_DUMMY_BYTE,
|
||||
.r3 = SD_DUMMY_BYTE,
|
||||
.r4 = SD_DUMMY_BYTE,
|
||||
.r5 = SD_DUMMY_BYTE,
|
||||
};
|
||||
|
||||
// R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes
|
||||
// R1b identical to R1 + Busy information
|
||||
// R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes
|
||||
|
||||
frame[0] = ((uint8_t)cmd | 0x40);
|
||||
frame[1] = (uint8_t)(arg >> 24);
|
||||
frame[2] = (uint8_t)(arg >> 16);
|
||||
frame[3] = (uint8_t)(arg >> 8);
|
||||
frame[4] = (uint8_t)(arg);
|
||||
frame[5] = (crc | 0x01);
|
||||
|
||||
sd_spi_select_card();
|
||||
sd_spi_write_bytes(frame, sizeof(frame));
|
||||
|
||||
switch(answer_type) {
|
||||
case SdSpiCmdAnswerTypeR1:
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
break;
|
||||
case SdSpiCmdAnswerTypeR1B:
|
||||
// TODO FL-3507: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
|
||||
// In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B
|
||||
// reassert card
|
||||
sd_spi_deselect_card();
|
||||
furi_delay_us(1000);
|
||||
sd_spi_deselect_card();
|
||||
|
||||
// and wait for it to be ready
|
||||
while(sd_spi_read_byte() != 0xFF) {
|
||||
};
|
||||
|
||||
break;
|
||||
case SdSpiCmdAnswerTypeR2:
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
cmd_answer.r2 = sd_spi_read_byte();
|
||||
break;
|
||||
case SdSpiCmdAnswerTypeR3:
|
||||
case SdSpiCmdAnswerTypeR7:
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
cmd_answer.r2 = sd_spi_read_byte();
|
||||
cmd_answer.r3 = sd_spi_read_byte();
|
||||
cmd_answer.r4 = sd_spi_read_byte();
|
||||
cmd_answer.r5 = sd_spi_read_byte();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cmd_answer;
|
||||
}
|
||||
|
||||
static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) {
|
||||
SdSpiDataResponce responce = sd_spi_read_byte();
|
||||
// read busy response byte
|
||||
sd_spi_read_byte();
|
||||
|
||||
switch(responce & 0x1F) {
|
||||
case SdSpiDataResponceOK:
|
||||
// TODO FL-3508: check timings
|
||||
sd_spi_deselect_card();
|
||||
sd_spi_select_card();
|
||||
|
||||
// wait for 0xFF
|
||||
if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) {
|
||||
return SdSpiDataResponceOK;
|
||||
} else {
|
||||
return SdSpiDataResponceOtherError;
|
||||
}
|
||||
case SdSpiDataResponceCRCError:
|
||||
return SdSpiDataResponceCRCError;
|
||||
case SdSpiDataResponceWriteError:
|
||||
return SdSpiDataResponceWriteError;
|
||||
default:
|
||||
return SdSpiDataResponceOtherError;
|
||||
}
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_init_spi_mode_v1(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
uint8_t retry_count = 0;
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v1");
|
||||
|
||||
do {
|
||||
retry_count++;
|
||||
|
||||
// CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
|
||||
sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
// ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v1 done");
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_init_spi_mode_v2(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
uint8_t retry_count = 0;
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v2");
|
||||
|
||||
do {
|
||||
retry_count++;
|
||||
// CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
|
||||
sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
// ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
|
||||
response =
|
||||
sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
sd_spi_debug("ACMD41 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
|
||||
|
||||
if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) {
|
||||
sd_spi_debug("ACMD41 is illegal command");
|
||||
retry_count = 0;
|
||||
do {
|
||||
retry_count++;
|
||||
// CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_IN_IDLE_STATE) {
|
||||
sd_spi_debug("CMD55 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
// ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
sd_spi_debug("ACMD41 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
|
||||
}
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v2 done");
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_init_spi_mode(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
uint8_t retry_count;
|
||||
|
||||
// CMD0 (GO_IDLE_STATE) to put SD in SPI mode and
|
||||
// wait for In Idle State Response (R1 Format) equal to 0x01
|
||||
retry_count = 0;
|
||||
do {
|
||||
retry_count++;
|
||||
response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
sd_spi_debug("CMD0 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 != SdSpi_R1_IN_IDLE_STATE);
|
||||
|
||||
// CMD8 (SEND_IF_COND) to check the power supply status
|
||||
// and wait until response (R7 Format) equal to 0xAA and
|
||||
response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) {
|
||||
if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) {
|
||||
sd_spi_debug("Init mode v1 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
sd_high_capacity = 0;
|
||||
} else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) {
|
||||
if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) {
|
||||
sd_spi_debug("Init mode v2 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response
|
||||
response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
sd_spi_debug("CMD58 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
sd_high_capacity = (response.r2 & 0x40) >> 6;
|
||||
} else {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC");
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) {
|
||||
uint16_t counter = 0;
|
||||
uint8_t csd_data[16];
|
||||
SdSpiStatus ret = SdSpiStatusError;
|
||||
SdSpiCmdAnswer response;
|
||||
|
||||
// CMD9 (SEND_CSD): R1 format (0x00 is no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
|
||||
if(response.r1 == SdSpi_R1_NO_ERROR) {
|
||||
if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) ==
|
||||
SdSpiStatusOK) {
|
||||
// read CSD data
|
||||
for(counter = 0; counter < 16; counter++) {
|
||||
csd_data[counter] = sd_spi_read_byte();
|
||||
}
|
||||
|
||||
sd_spi_purge_crc();
|
||||
|
||||
/*************************************************************************
|
||||
CSD header decoding
|
||||
*************************************************************************/
|
||||
|
||||
csd->CSDStruct = (csd_data[0] & 0xC0) >> 6;
|
||||
csd->Reserved1 = csd_data[0] & 0x3F;
|
||||
csd->TAAC = csd_data[1];
|
||||
csd->NSAC = csd_data[2];
|
||||
csd->MaxBusClkFrec = csd_data[3];
|
||||
csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4);
|
||||
csd->RdBlockLen = csd_data[5] & 0x0F;
|
||||
csd->PartBlockRead = (csd_data[6] & 0x80) >> 7;
|
||||
csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6;
|
||||
csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5;
|
||||
csd->DSRImpl = (csd_data[6] & 0x10) >> 4;
|
||||
|
||||
/*************************************************************************
|
||||
CSD v1/v2 decoding
|
||||
*************************************************************************/
|
||||
|
||||
if(sd_high_capacity == 0) {
|
||||
csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2);
|
||||
csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) |
|
||||
((csd_data[8] & 0xC0) >> 6);
|
||||
csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3;
|
||||
csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07);
|
||||
csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5;
|
||||
csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2;
|
||||
csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) |
|
||||
((csd_data[10] & 0x80) >> 7);
|
||||
} else {
|
||||
csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) |
|
||||
((csd_data[7] & 0xC0) >> 6);
|
||||
csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) |
|
||||
csd_data[9];
|
||||
csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8);
|
||||
}
|
||||
|
||||
csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6;
|
||||
csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7);
|
||||
csd->WrProtectGrSize = (csd_data[11] & 0x7F);
|
||||
csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7;
|
||||
csd->Reserved2 = (csd_data[12] & 0x60) >> 5;
|
||||
csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2;
|
||||
csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6);
|
||||
csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5;
|
||||
csd->Reserved3 = (csd_data[13] & 0x1F);
|
||||
csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7;
|
||||
csd->CopyFlag = (csd_data[14] & 0x40) >> 6;
|
||||
csd->PermWrProtect = (csd_data[14] & 0x20) >> 5;
|
||||
csd->TempWrProtect = (csd_data[14] & 0x10) >> 4;
|
||||
csd->FileFormat = (csd_data[14] & 0x0C) >> 2;
|
||||
csd->Reserved4 = (csd_data[14] & 0x03);
|
||||
csd->crc = (csd_data[15] & 0xFE) >> 1;
|
||||
csd->Reserved5 = (csd_data[15] & 0x01);
|
||||
|
||||
ret = SdSpiStatusOK;
|
||||
}
|
||||
}
|
||||
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) {
|
||||
uint16_t counter = 0;
|
||||
uint8_t cid_data[16];
|
||||
SdSpiStatus ret = SdSpiStatusError;
|
||||
SdSpiCmdAnswer response;
|
||||
|
||||
// CMD10 (SEND_CID): R1 format (0x00 is no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
|
||||
if(response.r1 == SdSpi_R1_NO_ERROR) {
|
||||
if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) ==
|
||||
SdSpiStatusOK) {
|
||||
// read CID data
|
||||
for(counter = 0; counter < 16; counter++) {
|
||||
cid_data[counter] = sd_spi_read_byte();
|
||||
}
|
||||
|
||||
sd_spi_purge_crc();
|
||||
|
||||
Cid->ManufacturerID = cid_data[0];
|
||||
memcpy(Cid->OEM_AppliID, cid_data + 1, 2);
|
||||
memcpy(Cid->ProdName, cid_data + 3, 5);
|
||||
Cid->ProdRev = cid_data[8];
|
||||
Cid->ProdSN = cid_data[9] << 24;
|
||||
Cid->ProdSN |= cid_data[10] << 16;
|
||||
Cid->ProdSN |= cid_data[11] << 8;
|
||||
Cid->ProdSN |= cid_data[12];
|
||||
Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4;
|
||||
Cid->ManufactYear = (cid_data[13] & 0x0F) << 4;
|
||||
Cid->ManufactYear |= (cid_data[14] & 0xF0) >> 4;
|
||||
Cid->ManufactMonth = (cid_data[14] & 0x0F);
|
||||
Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1;
|
||||
Cid->Reserved2 = 1;
|
||||
|
||||
ret = SdSpiStatusOK;
|
||||
}
|
||||
}
|
||||
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SdSpiStatus
|
||||
sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
|
||||
uint32_t block_address = address;
|
||||
uint32_t offset = 0;
|
||||
|
||||
// CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
|
||||
SdSpiCmdAnswer response =
|
||||
sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
if(!sd_high_capacity) {
|
||||
block_address = address * SD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
while(blocks--) {
|
||||
// CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors)
|
||||
response =
|
||||
sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
sd_spi_deselect_card_and_purge();
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// Wait for the data start token
|
||||
if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) ==
|
||||
SdSpiStatusOK) {
|
||||
// Read the data block
|
||||
sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE);
|
||||
sd_spi_purge_crc();
|
||||
|
||||
// increase offset
|
||||
offset += SD_BLOCK_SIZE;
|
||||
|
||||
// increase block address
|
||||
if(sd_high_capacity) {
|
||||
block_address += 1;
|
||||
} else {
|
||||
block_address += SD_BLOCK_SIZE;
|
||||
}
|
||||
} else {
|
||||
sd_spi_deselect_card_and_purge();
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
sd_spi_deselect_card_and_purge();
|
||||
}
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_cmd_write_blocks(
|
||||
uint32_t* data,
|
||||
uint32_t address,
|
||||
uint32_t blocks,
|
||||
uint32_t timeout_ms) {
|
||||
uint32_t block_address = address;
|
||||
uint32_t offset = 0;
|
||||
|
||||
// CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
|
||||
SdSpiCmdAnswer response =
|
||||
sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
if(!sd_high_capacity) {
|
||||
block_address = address * SD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
while(blocks--) {
|
||||
// CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(
|
||||
SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
sd_spi_deselect_card_and_purge();
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN
|
||||
// TODO FL-3509: check bytes count
|
||||
sd_spi_write_byte(SD_DUMMY_BYTE);
|
||||
sd_spi_write_byte(SD_DUMMY_BYTE);
|
||||
|
||||
// Send the data start token
|
||||
sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE);
|
||||
sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE);
|
||||
sd_spi_purge_crc();
|
||||
|
||||
// Read data response
|
||||
SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(data_responce != SdSpiDataResponceOK) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// increase offset
|
||||
offset += SD_BLOCK_SIZE;
|
||||
|
||||
// increase block address
|
||||
if(sd_high_capacity) {
|
||||
block_address += 1;
|
||||
} else {
|
||||
block_address += SD_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
uint8_t sd_max_mount_retry_count() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_init(bool power_reset) {
|
||||
// Slow speed init
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow;
|
||||
|
||||
// We reset card in spi_lock context, so it is safe to disturb spi bus
|
||||
if(power_reset) {
|
||||
sd_spi_debug("Power reset");
|
||||
|
||||
// disable power and set low on all bus pins
|
||||
furi_hal_power_disable_external_3_3v();
|
||||
sd_spi_bus_to_ground();
|
||||
hal_sd_detect_set_low();
|
||||
furi_delay_ms(250);
|
||||
|
||||
// reinit bus and enable power
|
||||
sd_spi_bus_rise_up();
|
||||
hal_sd_detect_init();
|
||||
furi_hal_power_enable_external_3_3v();
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
SdSpiStatus status = SdSpiStatusError;
|
||||
|
||||
// Send 80 dummy clocks with CS high
|
||||
sd_spi_deselect_card();
|
||||
for(uint8_t i = 0; i < 80; i++) {
|
||||
sd_spi_write_byte(SD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 128; i++) {
|
||||
status = sd_spi_init_spi_mode();
|
||||
if(status == SdSpiStatusOK) {
|
||||
// SD initialized and init to SPI mode properly
|
||||
sd_spi_debug("SD init OK after %d retries", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
|
||||
|
||||
// Init sector cache
|
||||
sector_cache_init();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_get_card_state(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
|
||||
// Send CMD13 (SEND_STATUS) to get SD status
|
||||
response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
// Return status OK if response is valid
|
||||
if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) {
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) {
|
||||
SdSpiStatus status;
|
||||
|
||||
status = sd_spi_get_csd(&(card_info->Csd));
|
||||
|
||||
if(status != SdSpiStatusOK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = sd_spi_get_cid(&(card_info->Cid));
|
||||
|
||||
if(status != SdSpiStatusOK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if(sd_high_capacity == 1) {
|
||||
card_info->LogBlockSize = 512;
|
||||
card_info->CardBlockSize = 512;
|
||||
card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL *
|
||||
(uint64_t)card_info->LogBlockSize;
|
||||
card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize);
|
||||
} else {
|
||||
card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1);
|
||||
card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2));
|
||||
card_info->LogBlockSize = 512;
|
||||
card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen);
|
||||
card_info->CardCapacity *= card_info->CardBlockSize;
|
||||
card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus
|
||||
sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
|
||||
SdSpiStatus status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms);
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus
|
||||
sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
|
||||
SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms);
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_get_cid(SD_CID* cid) {
|
||||
SdSpiStatus status;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
memset(cid, 0, sizeof(SD_CID));
|
||||
status = sd_spi_get_cid(cid);
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define __IO volatile
|
||||
|
||||
#define SD_TIMEOUT_MS (1000)
|
||||
#define SD_BLOCK_SIZE 512
|
||||
|
||||
typedef enum {
|
||||
SdSpiStatusOK,
|
||||
SdSpiStatusError,
|
||||
SdSpiStatusTimeout,
|
||||
} SdSpiStatus;
|
||||
|
||||
/**
|
||||
* @brief Card Specific Data: CSD Register
|
||||
*/
|
||||
typedef struct {
|
||||
/* Header part */
|
||||
uint8_t CSDStruct : 2; /* CSD structure */
|
||||
uint8_t Reserved1 : 6; /* Reserved */
|
||||
uint8_t TAAC : 8; /* Data read access-time 1 */
|
||||
uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */
|
||||
uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */
|
||||
uint16_t CardComdClasses : 12; /* Card command classes */
|
||||
uint8_t RdBlockLen : 4; /* Max. read data block length */
|
||||
uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */
|
||||
uint8_t WrBlockMisalign : 1; /* Write block misalignment */
|
||||
uint8_t RdBlockMisalign : 1; /* Read block misalignment */
|
||||
uint8_t DSRImpl : 1; /* DSR implemented */
|
||||
|
||||
/* v1 or v2 struct */
|
||||
union csd_version {
|
||||
struct {
|
||||
uint8_t Reserved1 : 2; /* Reserved */
|
||||
uint16_t DeviceSize : 12; /* Device Size */
|
||||
uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */
|
||||
uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */
|
||||
uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */
|
||||
uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */
|
||||
uint8_t DeviceSizeMul : 3; /* Device size multiplier */
|
||||
} v1;
|
||||
struct {
|
||||
uint8_t Reserved1 : 6; /* Reserved */
|
||||
uint32_t DeviceSize : 22; /* Device Size */
|
||||
uint8_t Reserved2 : 1; /* Reserved */
|
||||
} v2;
|
||||
} version;
|
||||
|
||||
uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */
|
||||
uint8_t EraseSectorSize : 7; /* Erase group size multiplier */
|
||||
uint8_t WrProtectGrSize : 7; /* Write protect group size */
|
||||
uint8_t WrProtectGrEnable : 1; /* Write protect group enable */
|
||||
uint8_t Reserved2 : 2; /* Reserved */
|
||||
uint8_t WrSpeedFact : 3; /* Write speed factor */
|
||||
uint8_t MaxWrBlockLen : 4; /* Max. write data block length */
|
||||
uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */
|
||||
uint8_t Reserved3 : 5; /* Reserved */
|
||||
uint8_t FileFormatGrouop : 1; /* File format group */
|
||||
uint8_t CopyFlag : 1; /* Copy flag (OTP) */
|
||||
uint8_t PermWrProtect : 1; /* Permanent write protection */
|
||||
uint8_t TempWrProtect : 1; /* Temporary write protection */
|
||||
uint8_t FileFormat : 2; /* File Format */
|
||||
uint8_t Reserved4 : 2; /* Reserved */
|
||||
uint8_t crc : 7; /* Reserved */
|
||||
uint8_t Reserved5 : 1; /* always 1*/
|
||||
|
||||
} SD_CSD;
|
||||
|
||||
/**
|
||||
* @brief Card Identification Data: CID Register
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t ManufacturerID; /* ManufacturerID */
|
||||
char OEM_AppliID[2]; /* OEM/Application ID */
|
||||
char ProdName[5]; /* Product Name */
|
||||
uint8_t ProdRev; /* Product Revision */
|
||||
uint32_t ProdSN; /* Product Serial Number */
|
||||
uint8_t Reserved1; /* Reserved1 */
|
||||
uint8_t ManufactYear; /* Manufacturing Year */
|
||||
uint8_t ManufactMonth; /* Manufacturing Month */
|
||||
uint8_t CID_CRC; /* CID CRC */
|
||||
uint8_t Reserved2; /* always 1 */
|
||||
} SD_CID;
|
||||
|
||||
/**
|
||||
* @brief SD Card information structure
|
||||
*/
|
||||
typedef struct {
|
||||
SD_CSD Csd;
|
||||
SD_CID Cid;
|
||||
uint64_t CardCapacity; /*!< Card Capacity */
|
||||
uint32_t CardBlockSize; /*!< Card Block Size */
|
||||
uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */
|
||||
uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */
|
||||
} SD_CardInfo;
|
||||
|
||||
/**
|
||||
* @brief SD card max mount retry count
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t sd_max_mount_retry_count();
|
||||
|
||||
/**
|
||||
* @brief Init sd card
|
||||
*
|
||||
* @param power_reset reset card power
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_init(bool power_reset);
|
||||
|
||||
/**
|
||||
* @brief Get card state
|
||||
*
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_get_card_state(void);
|
||||
|
||||
/**
|
||||
* @brief Get card info
|
||||
*
|
||||
* @param card_info
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_get_card_info(SD_CardInfo* card_info);
|
||||
|
||||
/**
|
||||
* @brief Read blocks
|
||||
*
|
||||
* @param data
|
||||
* @param address
|
||||
* @param blocks
|
||||
* @param timeout_ms
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Write blocks
|
||||
*
|
||||
* @param data
|
||||
* @param address
|
||||
* @param blocks
|
||||
* @param timeout_ms
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus
|
||||
sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Get card CSD register
|
||||
*
|
||||
* @param Cid
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_get_cid(SD_CID* cid);
|
||||
@@ -1,17 +1,8 @@
|
||||
#include "user_diskio.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "user_diskio.h"
|
||||
#include "sector_cache.h"
|
||||
|
||||
static DSTATUS driver_check_status(BYTE lun) {
|
||||
UNUSED(lun);
|
||||
DSTATUS status = 0;
|
||||
if(sd_get_card_state() != SdSpiStatusOK) {
|
||||
status = STA_NOINIT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static DSTATUS driver_initialize(BYTE pdrv);
|
||||
static DSTATUS driver_status(BYTE pdrv);
|
||||
static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
@@ -26,79 +17,6 @@ Diskio_drvTypeDef sd_fatfs_driver = {
|
||||
driver_ioctl,
|
||||
};
|
||||
|
||||
static inline bool sd_cache_get(uint32_t address, uint32_t* data) {
|
||||
uint8_t* cached_data = sector_cache_get(address);
|
||||
if(cached_data) {
|
||||
memcpy(data, cached_data, SD_BLOCK_SIZE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void sd_cache_put(uint32_t address, uint32_t* data) {
|
||||
sector_cache_put(address, (uint8_t*)data);
|
||||
}
|
||||
|
||||
static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) {
|
||||
sector_cache_invalidate_range(start_sector, end_sector);
|
||||
}
|
||||
|
||||
static inline void sd_cache_invalidate_all() {
|
||||
sector_cache_init();
|
||||
}
|
||||
|
||||
static bool sd_device_read(uint32_t* buff, uint32_t sector, uint32_t count) {
|
||||
bool result = false;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
if(sd_read_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
|
||||
|
||||
/* wait until the read operation is finished */
|
||||
result = true;
|
||||
while(sd_get_card_state() != SdSpiStatusOK) {
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) {
|
||||
bool result = false;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
if(sd_write_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
|
||||
|
||||
/* wait until the Write operation is finished */
|
||||
result = true;
|
||||
while(sd_get_card_state() != SdSpiStatusOK) {
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
sd_cache_invalidate_all();
|
||||
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes a Drive
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
@@ -115,13 +33,11 @@ static DSTATUS driver_initialize(BYTE pdrv) {
|
||||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS driver_status(BYTE pdrv) {
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
DSTATUS status = driver_check_status(pdrv);
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
UNUSED(pdrv);
|
||||
DSTATUS status = 0;
|
||||
if(furi_hal_sd_get_card_state() != FuriStatusOk) {
|
||||
status = STA_NOINIT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -136,43 +52,8 @@ static DSTATUS driver_status(BYTE pdrv) {
|
||||
*/
|
||||
static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
|
||||
UNUSED(pdrv);
|
||||
|
||||
bool result;
|
||||
bool single_sector = count == 1;
|
||||
|
||||
if(single_sector) {
|
||||
if(sd_cache_get(sector, (uint32_t*)buff)) {
|
||||
return RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
|
||||
if(!result) {
|
||||
uint8_t counter = sd_max_mount_retry_count();
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
SdSpiStatus status;
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
status = sd_init(true);
|
||||
} else {
|
||||
status = sd_init(false);
|
||||
}
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
}
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
|
||||
if(single_sector && result == true) {
|
||||
sd_cache_put(sector, (uint32_t*)buff);
|
||||
}
|
||||
|
||||
return result ? RES_OK : RES_ERROR;
|
||||
FuriStatus status = furi_hal_sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
return status == FuriStatusOk ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,33 +66,8 @@ static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
|
||||
*/
|
||||
static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
|
||||
UNUSED(pdrv);
|
||||
bool result;
|
||||
|
||||
sd_cache_invalidate_range(sector, sector + count);
|
||||
|
||||
result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
|
||||
if(!result) {
|
||||
uint8_t counter = sd_max_mount_retry_count();
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
SdSpiStatus status;
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
status = sd_init(true);
|
||||
} else {
|
||||
status = sd_init(false);
|
||||
}
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
}
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
|
||||
return result ? RES_OK : RES_ERROR;
|
||||
FuriStatus status = furi_hal_sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
return status == FuriStatusOk ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,12 +79,9 @@ static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT coun
|
||||
*/
|
||||
static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||
DRESULT res = RES_ERROR;
|
||||
SD_CardInfo CardInfo;
|
||||
FuriHalSdInfo sd_info;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
DSTATUS status = driver_check_status(pdrv);
|
||||
DSTATUS status = driver_status(pdrv);
|
||||
if(status & STA_NOINIT) return RES_NOTRDY;
|
||||
|
||||
switch(cmd) {
|
||||
@@ -239,22 +92,22 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||
|
||||
/* Get number of sectors on the disk (DWORD) */
|
||||
case GET_SECTOR_COUNT:
|
||||
sd_get_card_info(&CardInfo);
|
||||
*(DWORD*)buff = CardInfo.LogBlockNbr;
|
||||
furi_hal_sd_info(&sd_info);
|
||||
*(DWORD*)buff = sd_info.logical_block_count;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get R/W sector size (WORD) */
|
||||
case GET_SECTOR_SIZE:
|
||||
sd_get_card_info(&CardInfo);
|
||||
*(WORD*)buff = CardInfo.LogBlockSize;
|
||||
furi_hal_sd_info(&sd_info);
|
||||
*(WORD*)buff = sd_info.logical_block_size;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get erase block size in unit of sector (DWORD) */
|
||||
case GET_BLOCK_SIZE:
|
||||
sd_get_card_info(&CardInfo);
|
||||
*(DWORD*)buff = CardInfo.LogBlockSize;
|
||||
furi_hal_sd_info(&sd_info);
|
||||
*(DWORD*)buff = sd_info.logical_block_size;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
@@ -262,8 +115,5 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||
res = RES_PARERR;
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sd_spi_io.h"
|
||||
#include "fatfs/ff_gen_drv.h"
|
||||
|
||||
extern Diskio_drvTypeDef sd_fatfs_driver;
|
||||
|
||||
@@ -117,6 +117,14 @@ static void furi_hal_resources_init_input_pins(GpioMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_resources_init_gpio_pins(GpioMode mode) {
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(!gpio_pins[i].debug) {
|
||||
furi_hal_gpio_init(gpio_pins[i].pin, mode, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_resources_init_early() {
|
||||
furi_hal_bus_enable(FuriHalBusGPIOA);
|
||||
furi_hal_bus_enable(FuriHalBusGPIOB);
|
||||
@@ -161,14 +169,7 @@ void furi_hal_resources_init_early() {
|
||||
furi_hal_gpio_write(&gpio_usb_dp, 0);
|
||||
|
||||
// External header pins
|
||||
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_resources_init_gpio_pins(GpioModeAnalog);
|
||||
}
|
||||
|
||||
void furi_hal_resources_deinit_early() {
|
||||
|
||||
@@ -429,3 +429,32 @@ bool furi_hal_rtc_is_leap_year(uint16_t year) {
|
||||
uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month) {
|
||||
return furi_hal_rtc_days_per_month[leap_year ? 1 : 0][month - 1];
|
||||
}
|
||||
|
||||
void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) {
|
||||
datetime->year = timestamp / (60 * 60 * 24 * 366) + FURI_HAL_RTC_EPOCH_START_YEAR;
|
||||
uint16_t extra_days = (datetime->year - FURI_HAL_RTC_EPOCH_START_YEAR - 1) / 4;
|
||||
uint16_t days_since_epoch = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY;
|
||||
uint16_t days_since_epoch_without_extra_days = days_since_epoch - extra_days;
|
||||
uint16_t days_in_this_year =
|
||||
days_since_epoch_without_extra_days % furi_hal_rtc_days_per_year[0] + 1;
|
||||
while(days_in_this_year > furi_hal_rtc_days_per_month[0][datetime->month]) {
|
||||
days_in_this_year -= furi_hal_rtc_days_per_month[0][datetime->month];
|
||||
datetime->month++;
|
||||
}
|
||||
datetime->month++;
|
||||
if(furi_hal_rtc_is_leap_year(datetime->year)) {
|
||||
datetime->day = days_in_this_year - 1;
|
||||
} else {
|
||||
datetime->day = days_in_this_year;
|
||||
}
|
||||
// uint16_t seconds_in_this_day =
|
||||
// timestamp - (days_since_epoch * FURI_HAL_RTC_SECONDS_PER_DAY);
|
||||
// datetime->hour = seconds_in_this_day / FURI_HAL_RTC_SECONDS_PER_HOUR;
|
||||
// uint16_t minutes_in_this_day =
|
||||
// seconds_in_this_day - (datetime->hour * FURI_HAL_RTC_SECONDS_PER_HOUR);
|
||||
// datetime->minute = minutes_in_this_day / FURI_HAL_RTC_SECONDS_PER_MINUTE;
|
||||
// datetime->second = minutes_in_this_day - (datetime->minute * FURI_HAL_RTC_SECONDS_PER_MINUTE);
|
||||
datetime->second = timestamp % 60;
|
||||
datetime->minute = timestamp / 60 % 60;
|
||||
datetime->hour = timestamp / (60 * 60) % 24;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,8 +23,8 @@ static FATFS* pfs = NULL;
|
||||
}
|
||||
|
||||
static bool flipper_update_mount_sd() {
|
||||
for(int i = 0; i < sd_max_mount_retry_count(); ++i) {
|
||||
if(sd_init((i % 2) == 0) != SdSpiStatusOK) {
|
||||
for(int i = 0; i < furi_hal_sd_max_mount_retry_count(); ++i) {
|
||||
if(furi_hal_sd_init((i % 2) == 0) != FuriStatusOk) {
|
||||
/* Next attempt will be without card reset, let it settle */
|
||||
furi_delay_ms(1000);
|
||||
continue;
|
||||
@@ -51,7 +51,7 @@ static bool flipper_update_init() {
|
||||
furi_hal_spi_config_init();
|
||||
|
||||
fatfs_init();
|
||||
if(!hal_sd_detect()) {
|
||||
if(!furi_hal_sd_is_present()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -280,6 +280,13 @@ bool furi_hal_rtc_is_leap_year(uint16_t year);
|
||||
*/
|
||||
uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month);
|
||||
|
||||
/** Convert UNIX timestamp to DateTime
|
||||
* @param timestamp The UNIX timestamp
|
||||
*
|
||||
* @param datetime The datetime
|
||||
*/
|
||||
void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,30 +4,82 @@
|
||||
* SD Card HAL API
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <furi_hal_spi_types.h>
|
||||
#include <furi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Init SD card detect
|
||||
*/
|
||||
void hal_sd_detect_init(void);
|
||||
typedef struct {
|
||||
uint64_t capacity; /*!< total capacity in bytes */
|
||||
uint32_t block_size; /*!< block size */
|
||||
uint32_t logical_block_count; /*!< logical capacity in blocks */
|
||||
uint32_t logical_block_size; /*!< logical block size in bytes */
|
||||
|
||||
/** Set SD card detect pin to low
|
||||
*/
|
||||
void hal_sd_detect_set_low(void);
|
||||
uint8_t manufacturer_id; /*!< manufacturer ID */
|
||||
char oem_id[3]; /*!< OEM ID, 2 characters + null terminator */
|
||||
char product_name[6]; /*!< product name, 5 characters + null terminator */
|
||||
uint8_t product_revision_major; /*!< product revision major */
|
||||
uint8_t product_revision_minor; /*!< product revision minor */
|
||||
uint32_t product_serial_number; /*!< product serial number */
|
||||
uint8_t manufacturing_month; /*!< manufacturing month */
|
||||
uint16_t manufacturing_year; /*!< manufacturing year */
|
||||
} FuriHalSdInfo;
|
||||
|
||||
/** Get SD card status
|
||||
*
|
||||
* @return true if SD card present, false if SD card not present
|
||||
/**
|
||||
* @brief Init SD card presence detection
|
||||
*/
|
||||
bool hal_sd_detect(void);
|
||||
void furi_hal_sd_presence_init();
|
||||
|
||||
/** Pointer to currently used SPI Handle */
|
||||
extern FuriHalSpiBusHandle* furi_hal_sd_spi_handle;
|
||||
/**
|
||||
* @brief Get SD card status
|
||||
* @return true if SD card is present
|
||||
*/
|
||||
bool furi_hal_sd_is_present();
|
||||
|
||||
/**
|
||||
* @brief SD card max mount retry count
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t furi_hal_sd_max_mount_retry_count();
|
||||
|
||||
/**
|
||||
* @brief Init SD card
|
||||
* @param power_reset reset card power
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_init(bool power_reset);
|
||||
|
||||
/**
|
||||
* @brief Read blocks from SD card
|
||||
* @param buff
|
||||
* @param sector
|
||||
* @param count
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_read_blocks(uint32_t* buff, uint32_t sector, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Write blocks to SD card
|
||||
* @param buff
|
||||
* @param sector
|
||||
* @param count
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_write_blocks(const uint32_t* buff, uint32_t sector, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Get SD card info
|
||||
* @param info
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_info(FuriHalSdInfo* info);
|
||||
|
||||
/**
|
||||
* @brief Get SD card state
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_get_card_state();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -11,10 +11,21 @@ bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* man
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flipper_application_manifest_is_compatible(
|
||||
bool flipper_application_manifest_is_too_old(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface) {
|
||||
if(manifest->base.api_version.major != api_interface->api_version_major /* ||
|
||||
if(manifest->base.api_version.major < api_interface->api_version_major /* ||
|
||||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flipper_application_manifest_is_too_new(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface) {
|
||||
if(manifest->base.api_version.major > api_interface->api_version_major /* ||
|
||||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -54,14 +54,25 @@ typedef FlipperApplicationManifestV1 FlipperApplicationManifest;
|
||||
*/
|
||||
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest);
|
||||
|
||||
/**
|
||||
* @brief Check if manifest is compatible with current ELF API interface
|
||||
*
|
||||
* @param manifest
|
||||
* @param api_interface
|
||||
* @return bool
|
||||
/** Check if API Version declared in manifest is older than firmware ELF API interface
|
||||
*
|
||||
* @param manifest The manifest
|
||||
* @param api_interface The api interface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool flipper_application_manifest_is_compatible(
|
||||
bool flipper_application_manifest_is_too_old(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface);
|
||||
|
||||
/** Check if API Version declared in manifest is newer than firmware ELF API interface
|
||||
*
|
||||
* @param manifest The manifest
|
||||
* @param api_interface The api interface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool flipper_application_manifest_is_too_new(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface);
|
||||
|
||||
|
||||
@@ -101,9 +101,14 @@ static FlipperApplicationPreloadStatus
|
||||
return FlipperApplicationPreloadStatusTargetMismatch;
|
||||
}
|
||||
|
||||
if(!flipper_application_manifest_is_compatible(
|
||||
if(!flipper_application_manifest_is_too_old(
|
||||
&app->manifest, elf_file_get_api_interface(app->elf))) {
|
||||
return FlipperApplicationPreloadStatusApiMismatch;
|
||||
return FlipperApplicationPreloadStatusApiTooOld;
|
||||
}
|
||||
|
||||
if(!flipper_application_manifest_is_too_new(
|
||||
&app->manifest, elf_file_get_api_interface(app->elf))) {
|
||||
return FlipperApplicationPreloadStatusApiTooNew;
|
||||
}
|
||||
|
||||
return FlipperApplicationPreloadStatusSuccess;
|
||||
@@ -257,7 +262,8 @@ static const char* preload_status_strings[] = {
|
||||
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
|
||||
[FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
|
||||
[FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
|
||||
[FlipperApplicationPreloadStatusApiMismatch] = "API version mismatch",
|
||||
[FlipperApplicationPreloadStatusApiTooOld] = "Update Application to use with this Firmware (ApiTooOld)",
|
||||
[FlipperApplicationPreloadStatusApiTooNew] = "Update Firmware to use with this Application (ApiTooNew)",
|
||||
[FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
|
||||
};
|
||||
|
||||
@@ -265,7 +271,7 @@ static const char* load_status_strings[] = {
|
||||
[FlipperApplicationLoadStatusSuccess] = "Success",
|
||||
[FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
|
||||
[FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
|
||||
[FlipperApplicationLoadStatusMissingImports] = "Found unsatisfied imports",
|
||||
[FlipperApplicationLoadStatusMissingImports] = "Update Firmware to use with this Application (MissingImports)",
|
||||
};
|
||||
|
||||
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
|
||||
|
||||
@@ -21,7 +21,8 @@ typedef enum {
|
||||
FlipperApplicationPreloadStatusUnspecifiedError,
|
||||
FlipperApplicationPreloadStatusInvalidFile,
|
||||
FlipperApplicationPreloadStatusInvalidManifest,
|
||||
FlipperApplicationPreloadStatusApiMismatch,
|
||||
FlipperApplicationPreloadStatusApiTooOld,
|
||||
FlipperApplicationPreloadStatusApiTooNew,
|
||||
FlipperApplicationPreloadStatusTargetMismatch,
|
||||
} FlipperApplicationPreloadStatus;
|
||||
|
||||
|
||||
398
lib/nfc/helpers/bit_lib.c
Normal file
398
lib/nfc/helpers/bit_lib.c
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "bit_lib.h"
|
||||
#include <core/check.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit) {
|
||||
size_t last_index = data_size - 1;
|
||||
|
||||
for(size_t i = 0; i < last_index; ++i) {
|
||||
data[i] = (data[i] << 1) | ((data[i + 1] >> 7) & 1);
|
||||
}
|
||||
data[last_index] = (data[last_index] << 1) | bit;
|
||||
}
|
||||
|
||||
void bit_lib_set_bit(uint8_t* data, size_t position, bool bit) {
|
||||
if(bit) {
|
||||
data[position / 8] |= 1UL << (7 - (position % 8));
|
||||
} else {
|
||||
data[position / 8] &= ~(1UL << (7 - (position % 8)));
|
||||
}
|
||||
}
|
||||
|
||||
void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length) {
|
||||
furi_check(length <= 8);
|
||||
furi_check(length > 0);
|
||||
|
||||
for(uint8_t i = 0; i < length; ++i) {
|
||||
uint8_t shift = (length - 1) - i;
|
||||
bit_lib_set_bit(data, position + i, (byte >> shift) & 1); //-V610
|
||||
}
|
||||
}
|
||||
|
||||
bool bit_lib_get_bit(const uint8_t* data, size_t position) {
|
||||
return (data[position / 8] >> (7 - (position % 8))) & 1;
|
||||
}
|
||||
|
||||
uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint8_t shift = position % 8;
|
||||
if(shift == 0) {
|
||||
return data[position / 8] >> (8 - length);
|
||||
} else {
|
||||
// TODO fix read out of bounds
|
||||
uint8_t value = (data[position / 8] << (shift));
|
||||
value |= data[position / 8 + 1] >> (8 - shift);
|
||||
value = value >> (8 - length);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint16_t value = 0;
|
||||
if(length <= 8) {
|
||||
value = bit_lib_get_bits(data, position, length);
|
||||
} else {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, length - 8);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint32_t value = 0;
|
||||
if(length <= 8) {
|
||||
value = bit_lib_get_bits(data, position, length);
|
||||
} else if(length <= 16) {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, length - 8);
|
||||
} else if(length <= 24) {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= bit_lib_get_bits(data, position + 16, length - 16);
|
||||
} else {
|
||||
value = (uint32_t)bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= (uint32_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= (uint32_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);
|
||||
value |= bit_lib_get_bits(data, position + 24, length - 24);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t bit_lib_get_bits_64(const uint8_t* data, size_t position, uint8_t length) {
|
||||
uint64_t value = 0;
|
||||
if(length <= 8) {
|
||||
value = bit_lib_get_bits(data, position, length);
|
||||
} else if(length <= 16) {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, length - 8);
|
||||
} else if(length <= 24) {
|
||||
value = bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= bit_lib_get_bits(data, position + 16, length - 16);
|
||||
} else if(length <= 32) {
|
||||
value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);
|
||||
value |= bit_lib_get_bits(data, position + 24, length - 24);
|
||||
} else {
|
||||
value = (uint64_t)bit_lib_get_bits(data, position, 8) << (length - 8);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 24, 8) << (length - 32);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 32, 8) << (length - 40);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 40, 8) << (length - 48);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 48, 8) << (length - 56);
|
||||
value |= (uint64_t)bit_lib_get_bits(data, position + 56, 8) << (length - 64);
|
||||
value |= bit_lib_get_bits(data, position + 64, length - 64);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity) {
|
||||
#if !defined __GNUC__
|
||||
#error Please, implement parity test for non-GCC compilers
|
||||
#else
|
||||
switch(parity) {
|
||||
case BitLibParityEven:
|
||||
return __builtin_parity(bits);
|
||||
case BitLibParityOdd:
|
||||
return !__builtin_parity(bits);
|
||||
default:
|
||||
furi_crash("Unknown parity");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool bit_lib_test_parity(
|
||||
const uint8_t* bits,
|
||||
size_t position,
|
||||
uint8_t length,
|
||||
BitLibParity parity,
|
||||
uint8_t parity_length) {
|
||||
uint32_t parity_block;
|
||||
bool result = true;
|
||||
const size_t parity_blocks_count = length / parity_length;
|
||||
|
||||
for(size_t i = 0; i < parity_blocks_count; ++i) {
|
||||
switch(parity) {
|
||||
case BitLibParityEven:
|
||||
case BitLibParityOdd:
|
||||
parity_block = bit_lib_get_bits_32(bits, position + i * parity_length, parity_length);
|
||||
if(!bit_lib_test_parity_32(parity_block, parity)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
case BitLibParityAlways0:
|
||||
if(bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
case BitLibParityAlways1:
|
||||
if(!bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!result) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t bit_lib_add_parity(
|
||||
const uint8_t* data,
|
||||
size_t position,
|
||||
uint8_t* dest,
|
||||
size_t dest_position,
|
||||
uint8_t source_length,
|
||||
uint8_t parity_length,
|
||||
BitLibParity parity) {
|
||||
uint32_t parity_word = 0;
|
||||
size_t j = 0, bit_count = 0;
|
||||
for(int word = 0; word < source_length; word += parity_length - 1) {
|
||||
for(int bit = 0; bit < parity_length - 1; bit++) {
|
||||
parity_word = (parity_word << 1) | bit_lib_get_bit(data, position + word + bit);
|
||||
bit_lib_set_bit(
|
||||
dest, dest_position + j++, bit_lib_get_bit(data, position + word + bit));
|
||||
}
|
||||
// if parity fails then return 0
|
||||
switch(parity) {
|
||||
case BitLibParityAlways0:
|
||||
bit_lib_set_bit(dest, dest_position + j++, 0);
|
||||
break; // marker bit which should be a 0
|
||||
case BitLibParityAlways1:
|
||||
bit_lib_set_bit(dest, dest_position + j++, 1);
|
||||
break; // marker bit which should be a 1
|
||||
default:
|
||||
bit_lib_set_bit(
|
||||
dest,
|
||||
dest_position + j++,
|
||||
(bit_lib_test_parity_32(parity_word, BitLibParityOdd) ^ parity) ^ 1);
|
||||
break;
|
||||
}
|
||||
bit_count += parity_length;
|
||||
parity_word = 0;
|
||||
}
|
||||
// if we got here then all the parities passed
|
||||
// return bit count
|
||||
return bit_count;
|
||||
}
|
||||
|
||||
size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n) {
|
||||
size_t counter = 0;
|
||||
size_t result_counter = 0;
|
||||
uint8_t bit_buffer = 0;
|
||||
uint8_t bit_counter = 0;
|
||||
|
||||
while(counter < length) {
|
||||
if((counter + 1) % n != 0) {
|
||||
bit_buffer = (bit_buffer << 1) | bit_lib_get_bit(data, position + counter);
|
||||
bit_counter++;
|
||||
}
|
||||
|
||||
if(bit_counter == 8) {
|
||||
bit_lib_set_bits(data, position + result_counter, bit_buffer, 8);
|
||||
bit_counter = 0;
|
||||
bit_buffer = 0;
|
||||
result_counter += 8;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
|
||||
if(bit_counter != 0) {
|
||||
bit_lib_set_bits(data, position + result_counter, bit_buffer, bit_counter);
|
||||
result_counter += bit_counter;
|
||||
}
|
||||
return result_counter;
|
||||
}
|
||||
|
||||
void bit_lib_copy_bits(
|
||||
uint8_t* data,
|
||||
size_t position,
|
||||
size_t length,
|
||||
const uint8_t* source,
|
||||
size_t source_position) {
|
||||
for(size_t i = 0; i < length; ++i) {
|
||||
bit_lib_set_bit(data, position + i, bit_lib_get_bit(source, source_position + i));
|
||||
}
|
||||
}
|
||||
|
||||
void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length) {
|
||||
size_t i = 0;
|
||||
size_t j = length - 1;
|
||||
|
||||
while(i < j) {
|
||||
bool tmp = bit_lib_get_bit(data, position + i);
|
||||
bit_lib_set_bit(data, position + i, bit_lib_get_bit(data, position + j));
|
||||
bit_lib_set_bit(data, position + j, tmp);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t bit_lib_get_bit_count(uint32_t data) {
|
||||
#if defined __GNUC__
|
||||
return __builtin_popcountl(data);
|
||||
#else
|
||||
#error Please, implement popcount for non-GCC compilers
|
||||
#endif
|
||||
}
|
||||
|
||||
void bit_lib_print_bits(const uint8_t* data, size_t length) {
|
||||
for(size_t i = 0; i < length; ++i) {
|
||||
printf("%u", bit_lib_get_bit(data, i));
|
||||
}
|
||||
}
|
||||
|
||||
void bit_lib_print_regions(
|
||||
const BitLibRegion* regions,
|
||||
size_t region_count,
|
||||
const uint8_t* data,
|
||||
size_t length) {
|
||||
// print data
|
||||
bit_lib_print_bits(data, length);
|
||||
printf("\r\n");
|
||||
|
||||
// print regions
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
bool print = false;
|
||||
|
||||
for(size_t i = 0; i < region_count; i++) {
|
||||
if(regions[i].start <= c && c < regions[i].start + regions[i].length) {
|
||||
print = true;
|
||||
printf("%c", regions[i].mark);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!print) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
// print regions data
|
||||
for(size_t c = 0; c < length; ++c) {
|
||||
bool print = false;
|
||||
|
||||
for(size_t i = 0; i < region_count; i++) {
|
||||
if(regions[i].start <= c && c < regions[i].start + regions[i].length) {
|
||||
print = true;
|
||||
printf("%u", bit_lib_get_bit(data, c));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!print) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
uint16_t bit_lib_reverse_16_fast(uint16_t data) {
|
||||
uint16_t result = 0;
|
||||
result |= (data & 0x8000) >> 15;
|
||||
result |= (data & 0x4000) >> 13;
|
||||
result |= (data & 0x2000) >> 11;
|
||||
result |= (data & 0x1000) >> 9;
|
||||
result |= (data & 0x0800) >> 7;
|
||||
result |= (data & 0x0400) >> 5;
|
||||
result |= (data & 0x0200) >> 3;
|
||||
result |= (data & 0x0100) >> 1;
|
||||
result |= (data & 0x0080) << 1;
|
||||
result |= (data & 0x0040) << 3;
|
||||
result |= (data & 0x0020) << 5;
|
||||
result |= (data & 0x0010) << 7;
|
||||
result |= (data & 0x0008) << 9;
|
||||
result |= (data & 0x0004) << 11;
|
||||
result |= (data & 0x0002) << 13;
|
||||
result |= (data & 0x0001) << 15;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t bit_lib_reverse_8_fast(uint8_t byte) {
|
||||
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
|
||||
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
|
||||
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
|
||||
return byte;
|
||||
}
|
||||
|
||||
uint16_t bit_lib_crc8(
|
||||
uint8_t const* data,
|
||||
size_t data_size,
|
||||
uint8_t polynom,
|
||||
uint8_t init,
|
||||
bool ref_in,
|
||||
bool ref_out,
|
||||
uint8_t xor_out) {
|
||||
uint8_t crc = init;
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
uint8_t byte = data[i];
|
||||
if(ref_in) bit_lib_reverse_bits(&byte, 0, 8);
|
||||
crc ^= byte;
|
||||
|
||||
for(size_t j = 8; j > 0; --j) {
|
||||
if(crc & TOPBIT(8)) {
|
||||
crc = (crc << 1) ^ polynom;
|
||||
} else {
|
||||
crc = (crc << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ref_out) bit_lib_reverse_bits(&crc, 0, 8);
|
||||
crc ^= xor_out;
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint16_t bit_lib_crc16(
|
||||
uint8_t const* data,
|
||||
size_t data_size,
|
||||
uint16_t polynom,
|
||||
uint16_t init,
|
||||
bool ref_in,
|
||||
bool ref_out,
|
||||
uint16_t xor_out) {
|
||||
uint16_t crc = init;
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
uint8_t byte = data[i];
|
||||
if(ref_in) byte = bit_lib_reverse_16_fast(byte) >> 8;
|
||||
|
||||
for(size_t j = 0; j < 8; ++j) {
|
||||
bool c15 = (crc >> 15 & 1);
|
||||
bool bit = (byte >> (7 - j) & 1);
|
||||
crc <<= 1;
|
||||
if(c15 ^ bit) crc ^= polynom;
|
||||
}
|
||||
}
|
||||
|
||||
if(ref_out) crc = bit_lib_reverse_16_fast(crc);
|
||||
crc ^= xor_out;
|
||||
|
||||
return crc;
|
||||
}
|
||||
281
lib/nfc/helpers/bit_lib.h
Normal file
281
lib/nfc/helpers/bit_lib.h
Normal file
@@ -0,0 +1,281 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TOPBIT(X) (1 << ((X)-1))
|
||||
|
||||
typedef enum {
|
||||
BitLibParityEven,
|
||||
BitLibParityOdd,
|
||||
BitLibParityAlways0,
|
||||
BitLibParityAlways1,
|
||||
} BitLibParity;
|
||||
|
||||
/** @brief Increment and wrap around a value.
|
||||
* @param index value to increment
|
||||
* @param length wrap-around range
|
||||
*/
|
||||
#define bit_lib_increment_index(index, length) (index = (((index) + 1) % (length)))
|
||||
|
||||
/** @brief Test if a bit is set.
|
||||
* @param data value to test
|
||||
* @param index bit index to test
|
||||
*/
|
||||
#define bit_lib_bit_is_set(data, index) (((data) & (1 << (index))) != 0)
|
||||
|
||||
/** @brief Test if a bit is not set.
|
||||
* @param data value to test
|
||||
* @param index bit index to test
|
||||
*/
|
||||
#define bit_lib_bit_is_not_set(data, index) (((data) & (1 << (index))) == 0)
|
||||
|
||||
/** @brief Push a bit into a byte array.
|
||||
* @param data array to push bit into
|
||||
* @param data_size array size
|
||||
* @param bit bit to push
|
||||
*/
|
||||
void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit);
|
||||
|
||||
/** @brief Set a bit in a byte array.
|
||||
* @param data array to set bit in
|
||||
* @param position The position of the bit to set.
|
||||
* @param bit bit value to set
|
||||
*/
|
||||
void bit_lib_set_bit(uint8_t* data, size_t position, bool bit);
|
||||
|
||||
/** @brief Set the bit at the given position to the given value.
|
||||
* @param data The data to set the bit in.
|
||||
* @param position The position of the bit to set.
|
||||
* @param byte The data to set the bit to.
|
||||
* @param length The length of the data.
|
||||
*/
|
||||
void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length);
|
||||
|
||||
/** @brief Get the bit of a byte.
|
||||
* @param data The byte to get the bits from.
|
||||
* @param position The position of the bit.
|
||||
* @return The bit.
|
||||
*/
|
||||
bool bit_lib_get_bit(const uint8_t* data, size_t position);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint8_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint16_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint32_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the bits of a data, as uint64_t.
|
||||
* @param data The data to get the bits from.
|
||||
* @param position The position of the first bit.
|
||||
* @param length The length of the bits.
|
||||
* @return The bits.
|
||||
*/
|
||||
uint64_t bit_lib_get_bits_64(const uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Test parity of given bits
|
||||
* @param bits Bits to test parity of
|
||||
* @param parity Parity to test against
|
||||
* @return true if parity is correct, false otherwise
|
||||
*/
|
||||
bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity);
|
||||
|
||||
/**
|
||||
* @brief Test parity of bit array, check parity for every parity_length block from start
|
||||
*
|
||||
* @param data Bit array
|
||||
* @param position Start position
|
||||
* @param length Bit count
|
||||
* @param parity Parity to test against
|
||||
* @param parity_length Parity block length
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool bit_lib_test_parity(
|
||||
const uint8_t* data,
|
||||
size_t position,
|
||||
uint8_t length,
|
||||
BitLibParity parity,
|
||||
uint8_t parity_length);
|
||||
|
||||
/**
|
||||
* @brief Add parity to bit array
|
||||
*
|
||||
* @param data Source bit array
|
||||
* @param position Start position
|
||||
* @param dest Destination bit array
|
||||
* @param dest_position Destination position
|
||||
* @param source_length Source bit count
|
||||
* @param parity_length Parity block length
|
||||
* @param parity Parity to test against
|
||||
* @return size_t
|
||||
*/
|
||||
size_t bit_lib_add_parity(
|
||||
const uint8_t* data,
|
||||
size_t position,
|
||||
uint8_t* dest,
|
||||
size_t dest_position,
|
||||
uint8_t source_length,
|
||||
uint8_t parity_length,
|
||||
BitLibParity parity);
|
||||
|
||||
/**
|
||||
* @brief Remove bit every n in array and shift array left. Useful to remove parity.
|
||||
*
|
||||
* @param data Bit array
|
||||
* @param position Start position
|
||||
* @param length Bit count
|
||||
* @param n every n bit will be removed
|
||||
* @return size_t
|
||||
*/
|
||||
size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n);
|
||||
|
||||
/**
|
||||
* @brief Copy bits from source to destination.
|
||||
*
|
||||
* @param data destination array
|
||||
* @param position position in destination array
|
||||
* @param length length of bits to copy
|
||||
* @param source source array
|
||||
* @param source_position position in source array
|
||||
*/
|
||||
void bit_lib_copy_bits(
|
||||
uint8_t* data,
|
||||
size_t position,
|
||||
size_t length,
|
||||
const uint8_t* source,
|
||||
size_t source_position);
|
||||
|
||||
/**
|
||||
* @brief Reverse bits in bit array
|
||||
*
|
||||
* @param data Bit array
|
||||
* @param position start position
|
||||
* @param length length of bits to reverse
|
||||
*/
|
||||
void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Count 1 bits in data
|
||||
*
|
||||
* @param data
|
||||
* @return uint8_t set bit count
|
||||
*/
|
||||
uint8_t bit_lib_get_bit_count(uint32_t data);
|
||||
|
||||
/**
|
||||
* @brief Print data as bit array
|
||||
*
|
||||
* @param data
|
||||
* @param length
|
||||
*/
|
||||
void bit_lib_print_bits(const uint8_t* data, size_t length);
|
||||
|
||||
typedef struct {
|
||||
const char mark;
|
||||
const size_t start;
|
||||
const size_t length;
|
||||
} BitLibRegion;
|
||||
|
||||
/**
|
||||
* @brief Print data as bit array and mark regions. Regions needs to be sorted by start position.
|
||||
*
|
||||
* @param regions
|
||||
* @param region_count
|
||||
* @param data
|
||||
* @param length
|
||||
*/
|
||||
void bit_lib_print_regions(
|
||||
const BitLibRegion* regions,
|
||||
size_t region_count,
|
||||
const uint8_t* data,
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* @brief Reverse bits in uint16_t, faster than generic bit_lib_reverse_bits.
|
||||
*
|
||||
* @param data
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t bit_lib_reverse_16_fast(uint16_t data);
|
||||
|
||||
/**
|
||||
* @brief Reverse bits in uint8_t, faster than generic bit_lib_reverse_bits.
|
||||
*
|
||||
* @param byte Byte
|
||||
* @return uint8_t the reversed byte
|
||||
*/
|
||||
uint8_t bit_lib_reverse_8_fast(uint8_t byte);
|
||||
|
||||
/**
|
||||
* @brief Slow, but generic CRC8 implementation
|
||||
*
|
||||
* @param data
|
||||
* @param data_size
|
||||
* @param polynom CRC polynom
|
||||
* @param init init value
|
||||
* @param ref_in true if the right bit is older
|
||||
* @param ref_out true to reverse output
|
||||
* @param xor_out xor output with this value
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint16_t bit_lib_crc8(
|
||||
uint8_t const* data,
|
||||
size_t data_size,
|
||||
uint8_t polynom,
|
||||
uint8_t init,
|
||||
bool ref_in,
|
||||
bool ref_out,
|
||||
uint8_t xor_out);
|
||||
|
||||
/**
|
||||
* @brief Slow, but generic CRC16 implementation
|
||||
*
|
||||
* @param data
|
||||
* @param data_size
|
||||
* @param polynom CRC polynom
|
||||
* @param init init value
|
||||
* @param ref_in true if the right bit is older
|
||||
* @param ref_out true to reverse output
|
||||
* @param xor_out xor output with this value
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t bit_lib_crc16(
|
||||
uint8_t const* data,
|
||||
size_t data_size,
|
||||
uint16_t polynom,
|
||||
uint16_t init,
|
||||
bool ref_in,
|
||||
bool ref_out,
|
||||
uint16_t xor_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
956
lib/nfc/helpers/transport.c
Normal file
956
lib/nfc/helpers/transport.c
Normal file
@@ -0,0 +1,956 @@
|
||||
#include "transport.h"
|
||||
#include <furi_hal_rtc.h>
|
||||
#define TAG "Transport parser"
|
||||
|
||||
void from_days_to_datetime(uint16_t days, FuriHalRtcDateTime* datetime, uint16_t start_year) {
|
||||
uint32_t timestamp = days * 24 * 60 * 60;
|
||||
FuriHalRtcDateTime start_datetime = {0};
|
||||
start_datetime.year = start_year - 1;
|
||||
start_datetime.month = 12;
|
||||
start_datetime.day = 31;
|
||||
timestamp += furi_hal_rtc_datetime_to_timestamp(&start_datetime);
|
||||
furi_hal_rtc_timestamp_to_datetime(timestamp, datetime);
|
||||
}
|
||||
|
||||
void from_minutes_to_datetime(uint32_t minutes, FuriHalRtcDateTime* datetime, uint16_t start_year) {
|
||||
uint32_t timestamp = minutes * 60;
|
||||
FuriHalRtcDateTime start_datetime = {0};
|
||||
start_datetime.year = start_year - 1;
|
||||
start_datetime.month = 12;
|
||||
start_datetime.day = 31;
|
||||
timestamp += furi_hal_rtc_datetime_to_timestamp(&start_datetime);
|
||||
furi_hal_rtc_timestamp_to_datetime(timestamp, datetime);
|
||||
}
|
||||
|
||||
bool parse_transport_block(MfClassicBlock* block, FuriString* result) {
|
||||
uint16_t transport_departament = bit_lib_get_bits_16(block->value, 0, 10);
|
||||
|
||||
FURI_LOG_D(TAG, "Transport departament: %x", transport_departament);
|
||||
|
||||
uint16_t layout_type = bit_lib_get_bits_16(block->value, 52, 4);
|
||||
if(layout_type == 0xE) {
|
||||
layout_type = bit_lib_get_bits_16(block->value, 52, 9);
|
||||
} else if(layout_type == 0xF) {
|
||||
layout_type = bit_lib_get_bits_16(block->value, 52, 14);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Layout type %x", layout_type);
|
||||
|
||||
uint16_t card_view = 0;
|
||||
uint16_t card_type = 0;
|
||||
uint32_t card_number = 0;
|
||||
uint8_t card_layout = 0;
|
||||
uint8_t card_layout2 = 0;
|
||||
uint16_t card_use_before_date = 0;
|
||||
uint16_t card_blank_type = 0;
|
||||
uint32_t card_start_trip_minutes = 0;
|
||||
uint8_t card_minutes_pass = 0;
|
||||
uint32_t card_remaining_funds = 0;
|
||||
uint16_t card_validator = 0;
|
||||
uint8_t card_blocked = 0;
|
||||
uint32_t card_hash = 0;
|
||||
|
||||
switch(layout_type) {
|
||||
case 0x02: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 56, 16); //202
|
||||
uint8_t card_benefit_code = bit_lib_get_bits(block->value, 72, 8); //124
|
||||
uint32_t card_rfu1 = bit_lib_get_bits_32(block->value, 80, 32); //rfu1
|
||||
uint16_t card_crc16 = bit_lib_get_bits_16(block->value, 112, 16); //501.1
|
||||
card_blocked = bit_lib_get_bits(block->value, 128, 1); //303
|
||||
uint16_t card_start_trip_time = bit_lib_get_bits_16(block->value, 177, 12); //403
|
||||
uint16_t card_start_trip_date = bit_lib_get_bits_16(block->value, 189, 16); //402
|
||||
uint16_t card_valid_from_date = bit_lib_get_bits_16(block->value, 157, 16); //311
|
||||
uint16_t card_valid_by_date = bit_lib_get_bits_16(block->value, 173, 16); //312
|
||||
uint8_t card_start_trip_seconds = bit_lib_get_bits(block->value, 189, 6); //406
|
||||
uint8_t card_transport_type1 = bit_lib_get_bits(block->value, 180, 2); //421.1
|
||||
uint8_t card_transport_type2 = bit_lib_get_bits(block->value, 182, 2); //421.2
|
||||
uint8_t card_transport_type3 = bit_lib_get_bits(block->value, 184, 2); //421.3
|
||||
uint8_t card_transport_type4 = bit_lib_get_bits(block->value, 186, 2); //421.4
|
||||
uint16_t card_use_with_date = bit_lib_get_bits_16(block->value, 189, 16); //205
|
||||
uint8_t card_route = bit_lib_get_bits(block->value, 205, 1); //424
|
||||
uint16_t card_validator1 = bit_lib_get_bits_16(block->value, 206, 15); //422.1
|
||||
card_validator = bit_lib_get_bits_16(block->value, 205, 16); //422
|
||||
uint16_t card_total_trips = bit_lib_get_bits_16(block->value, 221, 16); //331
|
||||
uint8_t card_write_enabled = bit_lib_get_bits(block->value, 237, 1); //write_enabled
|
||||
uint8_t card_rfu2 = bit_lib_get_bits(block->value, 238, 2); //rfu2
|
||||
uint16_t card_crc16_2 = bit_lib_get_bits_16(block->value, 240, 16); //501.2
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_use_before_date,
|
||||
card_benefit_code,
|
||||
card_rfu1,
|
||||
card_crc16,
|
||||
card_blocked,
|
||||
card_start_trip_time,
|
||||
card_start_trip_date,
|
||||
card_valid_from_date,
|
||||
card_valid_by_date,
|
||||
card_start_trip_seconds,
|
||||
card_transport_type1,
|
||||
card_transport_type2,
|
||||
card_transport_type3,
|
||||
card_transport_type4,
|
||||
card_use_with_date,
|
||||
card_route,
|
||||
card_validator1,
|
||||
card_validator,
|
||||
card_total_trips,
|
||||
card_write_enabled,
|
||||
card_rfu2,
|
||||
card_crc16_2);
|
||||
break;
|
||||
}
|
||||
case 0x06: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 56, 16); //202
|
||||
uint8_t card_geozone_a = bit_lib_get_bits(block->value, 72, 4); //GeoZoneA
|
||||
uint8_t card_geozone_b = bit_lib_get_bits(block->value, 76, 4); //GeoZoneB
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 80, 10); //121.
|
||||
uint16_t card_type_of_extended = bit_lib_get_bits_16(block->value, 90, 10); //122
|
||||
uint32_t card_rfu1 = bit_lib_get_bits_16(block->value, 100, 12); //rfu1
|
||||
uint16_t card_crc16 = bit_lib_get_bits_16(block->value, 112, 16); //501.1
|
||||
card_blocked = bit_lib_get_bits(block->value, 128, 1); //303
|
||||
uint16_t card_start_trip_time = bit_lib_get_bits_16(block->value, 129, 12); //403
|
||||
uint16_t card_start_trip_date = bit_lib_get_bits_16(block->value, 141, 16); //402
|
||||
uint16_t card_valid_from_date = bit_lib_get_bits_16(block->value, 157, 16); //311
|
||||
uint16_t card_valid_by_date = bit_lib_get_bits_16(block->value, 173, 16); //312
|
||||
uint16_t card_company = bit_lib_get_bits(block->value, 189, 4); //Company
|
||||
uint8_t card_validator1 = bit_lib_get_bits(block->value, 193, 4); //422.1
|
||||
uint16_t card_remaining_trips = bit_lib_get_bits_16(block->value, 197, 10); //321
|
||||
uint8_t card_units = bit_lib_get_bits(block->value, 207, 6); //Units
|
||||
uint16_t card_validator2 = bit_lib_get_bits_16(block->value, 213, 10); //422.2
|
||||
uint16_t card_total_trips = bit_lib_get_bits_16(block->value, 223, 16); //331
|
||||
uint8_t card_extended = bit_lib_get_bits(block->value, 239, 1); //123
|
||||
uint16_t card_crc16_2 = bit_lib_get_bits_16(block->value, 240, 16); //501.2
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_use_before_date,
|
||||
card_geozone_a,
|
||||
card_geozone_b,
|
||||
card_blank_type,
|
||||
card_type_of_extended,
|
||||
card_rfu1,
|
||||
card_crc16,
|
||||
card_blocked,
|
||||
card_start_trip_time,
|
||||
card_start_trip_date,
|
||||
card_valid_from_date,
|
||||
card_valid_by_date,
|
||||
card_company,
|
||||
card_validator1,
|
||||
card_remaining_trips,
|
||||
card_units,
|
||||
card_validator2,
|
||||
card_total_trips,
|
||||
card_extended,
|
||||
card_crc16_2);
|
||||
card_validator = card_validator1 * 1024 + card_validator2;
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_valid_by_date - 1, &card_use_before_date_s, 1992);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
(card_start_trip_date - 1) * 24 * 60 + card_start_trip_time,
|
||||
&card_start_trip_minutes_s,
|
||||
1992);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrips left: %d of %d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_remaining_trips,
|
||||
card_total_trips,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x08: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 56, 16); //202
|
||||
uint64_t card_rfu1 = bit_lib_get_bits_64(block->value, 72, 56); //rfu1
|
||||
uint16_t card_valid_from_date = bit_lib_get_bits_16(block->value, 128, 16); //311
|
||||
uint8_t card_valid_for_days = bit_lib_get_bits(block->value, 144, 8); //313
|
||||
uint8_t card_requires_activation = bit_lib_get_bits(block->value, 152, 1); //301
|
||||
uint8_t card_rfu2 = bit_lib_get_bits(block->value, 153, 7); //rfu2
|
||||
uint8_t card_remaining_trips1 = bit_lib_get_bits(block->value, 160, 8); //321.1
|
||||
uint8_t card_remaining_trips = bit_lib_get_bits(block->value, 168, 8); //321
|
||||
uint8_t card_validator1 = bit_lib_get_bits(block->value, 193, 2); //422.1
|
||||
uint16_t card_validator = bit_lib_get_bits_16(block->value, 177, 15); //422
|
||||
card_hash = bit_lib_get_bits_32(block->value, 192, 32); //502
|
||||
uint32_t card_rfu3 = bit_lib_get_bits_32(block->value, 224, 32); //rfu3
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %llx %x %x %x %x %x %x %x %x %lx %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_use_before_date,
|
||||
card_rfu1,
|
||||
card_valid_from_date,
|
||||
card_valid_for_days,
|
||||
card_requires_activation,
|
||||
card_rfu2,
|
||||
card_remaining_trips1,
|
||||
card_remaining_trips,
|
||||
card_validator1,
|
||||
card_validator,
|
||||
card_hash,
|
||||
card_valid_from_date,
|
||||
card_rfu3);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 1992);
|
||||
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrips left: %d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_remaining_trips,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x0A: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
uint16_t card_valid_from_date = bit_lib_get_bits_16(block->value, 64, 12); //311
|
||||
uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->value, 76, 19); //314
|
||||
uint8_t card_requires_activation = bit_lib_get_bits(block->value, 95, 1); //301
|
||||
card_start_trip_minutes = bit_lib_get_bits_32(block->value, 96, 19); //405
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 119, 7); //412
|
||||
uint8_t card_transport_type_flag = bit_lib_get_bits(block->value, 126, 2); //421.0
|
||||
uint8_t card_remaining_trips = bit_lib_get_bits(block->value, 128, 8); //321
|
||||
uint16_t card_validator = bit_lib_get_bits_16(block->value, 136, 16); //422
|
||||
uint8_t card_transport_type1 = bit_lib_get_bits(block->value, 152, 2); //421.1
|
||||
uint8_t card_transport_type2 = bit_lib_get_bits(block->value, 154, 2); //421.2
|
||||
uint8_t card_transport_type3 = bit_lib_get_bits(block->value, 156, 2); //421.3
|
||||
uint8_t card_transport_type4 = bit_lib_get_bits(block->value, 158, 2); //421.4
|
||||
card_hash = bit_lib_get_bits_32(block->value, 192, 32); //502
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %lx %x %lx %x %x %x %x %x %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_use_before_date,
|
||||
card_valid_from_date,
|
||||
card_valid_for_minutes,
|
||||
card_requires_activation,
|
||||
card_start_trip_minutes,
|
||||
card_minutes_pass,
|
||||
card_transport_type_flag,
|
||||
card_remaining_trips,
|
||||
card_validator,
|
||||
card_transport_type1,
|
||||
card_transport_type2,
|
||||
card_transport_type3,
|
||||
card_transport_type4,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 2016);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
card_start_trip_minutes - (2 * 24 * 60), &card_start_trip_minutes_s, 2016);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_remaining_trips,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x0C: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 56, 16); //202
|
||||
uint64_t card_rfu1 = bit_lib_get_bits_64(block->value, 72, 56); //rfu1
|
||||
uint16_t card_valid_from_date = bit_lib_get_bits_16(block->value, 128, 16); //311
|
||||
uint8_t card_valid_for_days = bit_lib_get_bits(block->value, 144, 8); //313
|
||||
uint8_t card_requires_activation = bit_lib_get_bits(block->value, 152, 1); //301
|
||||
uint16_t card_rfu2 = bit_lib_get_bits_16(block->value, 153, 13); //rfu2
|
||||
uint16_t card_remaining_trips = bit_lib_get_bits_16(block->value, 166, 10); //321
|
||||
uint16_t card_validator = bit_lib_get_bits_16(block->value, 176, 16); //422
|
||||
card_hash = bit_lib_get_bits_32(block->value, 192, 32); //502
|
||||
uint16_t card_start_trip_date = bit_lib_get_bits_16(block->value, 224, 16); //402
|
||||
uint16_t card_start_trip_time = bit_lib_get_bits_16(block->value, 240, 11); //403
|
||||
uint8_t card_transport_type = bit_lib_get_bits(block->value, 251, 2); //421
|
||||
uint8_t card_rfu3 = bit_lib_get_bits(block->value, 253, 2); //rfu3
|
||||
uint8_t card_transfer_in_metro = bit_lib_get_bits(block->value, 255, 1); //432
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %llx %x %x %x %x %x %x %x %x %x %x %x",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_use_before_date,
|
||||
card_rfu1,
|
||||
card_valid_from_date,
|
||||
card_valid_for_days,
|
||||
card_requires_activation,
|
||||
card_rfu2,
|
||||
card_remaining_trips,
|
||||
card_validator,
|
||||
card_start_trip_date,
|
||||
card_start_trip_time,
|
||||
card_transport_type,
|
||||
card_rfu3,
|
||||
card_transfer_in_metro);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 1992);
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
(card_start_trip_date - 1) * 24 * 60 + card_start_trip_time,
|
||||
&card_start_trip_minutes_s,
|
||||
1992);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_remaining_trips,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x0D: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
uint8_t card_rfu1 = bit_lib_get_bits(block->value, 56, 8); //rfu1
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 64, 16); //202
|
||||
uint16_t card_valid_for_time = bit_lib_get_bits_16(block->value, 80, 11); //316
|
||||
uint8_t card_rfu2 = bit_lib_get_bits(block->value, 91, 5); //rfu2
|
||||
uint16_t card_use_before_date2 = bit_lib_get_bits_16(block->value, 96, 16); //202.2
|
||||
uint16_t card_valid_for_time2 = bit_lib_get_bits_16(block->value, 123, 11); //316.2
|
||||
uint8_t card_rfu3 = bit_lib_get_bits(block->value, 123, 5); //rfu3
|
||||
uint16_t card_valid_from_date = bit_lib_get_bits_16(block->value, 128, 16); //311
|
||||
uint8_t card_valid_for_days = bit_lib_get_bits(block->value, 144, 8); //313
|
||||
uint8_t card_requires_activation = bit_lib_get_bits(block->value, 152, 1); //301
|
||||
uint8_t card_rfu4 = bit_lib_get_bits(block->value, 153, 2); //rfu4
|
||||
uint8_t card_passage_5_minutes = bit_lib_get_bits(block->value, 155, 5); //413
|
||||
uint8_t card_transport_type1 = bit_lib_get_bits(block->value, 160, 2); //421.1
|
||||
uint8_t card_passage_in_metro = bit_lib_get_bits(block->value, 162, 1); //431
|
||||
uint8_t card_passages_ground_transport = bit_lib_get_bits(block->value, 163, 3); //433
|
||||
uint16_t card_remaining_trips = bit_lib_get_bits_16(block->value, 166, 10); //321
|
||||
uint16_t card_validator = bit_lib_get_bits_16(block->value, 176, 16); //422
|
||||
card_hash = bit_lib_get_bits_32(block->value, 192, 32); //502
|
||||
uint16_t card_start_trip_date = bit_lib_get_bits_16(block->value, 224, 16); //402
|
||||
uint16_t card_start_trip_time = bit_lib_get_bits_16(block->value, 240, 11); //403
|
||||
uint8_t card_transport_type2 = bit_lib_get_bits(block->value, 251, 2); //421.2
|
||||
uint8_t card_rfu5 = bit_lib_get_bits(block->value, 253, 2); //rfu5
|
||||
uint8_t card_transfer_in_metro = bit_lib_get_bits(block->value, 255, 1); //432
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_rfu1,
|
||||
card_use_before_date,
|
||||
card_valid_for_time,
|
||||
card_rfu2,
|
||||
card_use_before_date2,
|
||||
card_valid_for_time2,
|
||||
card_rfu3,
|
||||
card_valid_from_date,
|
||||
card_valid_for_days,
|
||||
card_requires_activation,
|
||||
card_rfu4,
|
||||
card_passage_5_minutes,
|
||||
card_transport_type1,
|
||||
card_passage_in_metro,
|
||||
card_passages_ground_transport,
|
||||
card_remaining_trips,
|
||||
card_validator,
|
||||
card_start_trip_date,
|
||||
card_start_trip_time,
|
||||
card_transport_type2,
|
||||
card_rfu5,
|
||||
card_transfer_in_metro);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 1992);
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
(card_start_trip_date - 1) * 24 * 60 + card_start_trip_time,
|
||||
&card_start_trip_minutes_s,
|
||||
1992);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_remaining_trips,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x1C1: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_layout2 = bit_lib_get_bits(block->value, 56, 5); //112
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 61, 16); //202.
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 77, 10); //121.
|
||||
card_validator = bit_lib_get_bits_16(block->value, 128, 16); //422
|
||||
uint16_t card_start_trip_date = bit_lib_get_bits_16(block->value, 144, 16); //402
|
||||
uint16_t card_start_trip_time = bit_lib_get_bits_16(block->value, 160, 11); //403
|
||||
uint8_t card_transport_type1 = bit_lib_get_bits(block->value, 171, 2); //421.1
|
||||
uint8_t card_transport_type2 = bit_lib_get_bits(block->value, 173, 2); //421.2
|
||||
uint8_t card_transfer_in_metro = bit_lib_get_bits(block->value, 177, 1); //432
|
||||
uint8_t card_passage_in_metro = bit_lib_get_bits(block->value, 178, 1); //431
|
||||
uint8_t card_passages_ground_transport = bit_lib_get_bits(block->value, 179, 3); //433
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 185, 8); //412.
|
||||
card_remaining_funds = bit_lib_get_bits_32(block->value, 196, 19) / 100; //322
|
||||
uint8_t card_fare_trip = bit_lib_get_bits(block->value, 215, 2); //441
|
||||
card_blocked = bit_lib_get_bits(block->value, 202, 1); //303
|
||||
uint8_t card_zoo = bit_lib_get_bits(block->value, 218, 1); //zoo
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %lx %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_layout2,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_validator,
|
||||
card_start_trip_date,
|
||||
card_start_trip_time,
|
||||
card_transport_type1,
|
||||
card_transport_type2,
|
||||
card_transfer_in_metro,
|
||||
card_passage_in_metro,
|
||||
card_passages_ground_transport,
|
||||
card_minutes_pass,
|
||||
card_remaining_funds,
|
||||
card_fare_trip,
|
||||
card_blocked,
|
||||
card_zoo,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 1992);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
card_start_trip_minutes - (2 * 24 * 60), &card_start_trip_minutes_s, 1992);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x1C2: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_layout2 = bit_lib_get_bits(block->value, 56, 5); //112
|
||||
uint16_t card_type_of_extended = bit_lib_get_bits_16(block->value, 61, 10); //122
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 71, 16); //202.
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 87, 10); //121.
|
||||
uint16_t card_valid_to_date = bit_lib_get_bits_16(block->value, 97, 16); //311
|
||||
uint16_t card_activate_during = bit_lib_get_bits_16(block->value, 113, 9); //302
|
||||
uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->value, 131, 20); //314
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 154, 8); //412.
|
||||
uint8_t card_transport_type = bit_lib_get_bits(block->value, 163, 2); //421
|
||||
uint8_t card_passage_in_metro = bit_lib_get_bits(block->value, 165, 1); //431
|
||||
uint8_t card_transfer_in_metro = bit_lib_get_bits(block->value, 166, 1); //432
|
||||
uint16_t card_remaining_trips = bit_lib_get_bits_16(block->value, 167, 10); //321
|
||||
card_validator = bit_lib_get_bits_16(block->value, 177, 16); //422
|
||||
uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->value, 196, 20); //404
|
||||
uint8_t card_requires_activation = bit_lib_get_bits(block->value, 216, 1); //301
|
||||
card_blocked = bit_lib_get_bits(block->value, 217, 1); //303
|
||||
uint8_t card_extended = bit_lib_get_bits(block->value, 218, 1); //123
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %x %x %lx %x %x %x %x %x %x %lx %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_layout2,
|
||||
card_type_of_extended,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_valid_to_date,
|
||||
card_activate_during,
|
||||
card_valid_for_minutes,
|
||||
card_minutes_pass,
|
||||
card_transport_type,
|
||||
card_passage_in_metro,
|
||||
card_transfer_in_metro,
|
||||
card_remaining_trips,
|
||||
card_validator,
|
||||
card_start_trip_neg_minutes,
|
||||
card_requires_activation,
|
||||
card_blocked,
|
||||
card_extended,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 2016);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
(card_valid_to_date - 1) * 24 * 60 + card_valid_for_minutes -
|
||||
card_start_trip_neg_minutes,
|
||||
&card_start_trip_minutes_s,
|
||||
2016); //-time
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x1C3: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_layout2 = bit_lib_get_bits(block->value, 56, 5); //112
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 61, 16); //202
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 77, 10); //121
|
||||
card_remaining_funds = bit_lib_get_bits_32(block->value, 188, 22) / 100; //322
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502
|
||||
card_validator = bit_lib_get_bits_16(block->value, 128, 16); //422
|
||||
card_start_trip_minutes = bit_lib_get_bits_32(block->value, 144, 23); //405
|
||||
uint8_t card_fare_trip = bit_lib_get_bits(block->value, 210, 2); //441
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 171, 7); //412
|
||||
uint8_t card_transport_type_flag = bit_lib_get_bits(block->value, 178, 2); //421.0
|
||||
uint8_t card_transport_type1 = bit_lib_get_bits(block->value, 180, 2); //421.1
|
||||
uint8_t card_transport_type2 = bit_lib_get_bits(block->value, 182, 2); //421.2
|
||||
uint8_t card_transport_type3 = bit_lib_get_bits(block->value, 184, 2); //421.3
|
||||
uint8_t card_transport_type4 = bit_lib_get_bits(block->value, 186, 2); //421.4
|
||||
card_blocked = bit_lib_get_bits(block->value, 212, 1); //303
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Card view: %x, type: %x, number: %lx, layout: %x, layout2: %x, use before date: %x, blank type: %x, remaining funds: %lx, hash: %lx, validator: %x, start trip minutes: %lx, fare trip: %x, minutes pass: %x, transport type flag: %x, transport type1: %x, transport type2: %x, transport type3: %x, transport type4: %x, blocked: %x",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_layout2,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_remaining_funds,
|
||||
card_hash,
|
||||
card_validator,
|
||||
card_start_trip_minutes,
|
||||
card_fare_trip,
|
||||
card_minutes_pass,
|
||||
card_transport_type_flag,
|
||||
card_transport_type1,
|
||||
card_transport_type2,
|
||||
card_transport_type3,
|
||||
card_transport_type4,
|
||||
card_blocked);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2016);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nBalance: %ld rub\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_remaining_funds,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x1C4: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_layout2 = bit_lib_get_bits(block->value, 56, 5); //112
|
||||
uint16_t card_type_of_extended = bit_lib_get_bits_16(block->value, 61, 10); //122
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 71, 13); //202.
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 84, 10); //121.
|
||||
uint16_t card_valid_to_date = bit_lib_get_bits_16(block->value, 94, 13); //311
|
||||
uint16_t card_activate_during = bit_lib_get_bits_16(block->value, 107, 9); //302
|
||||
uint16_t card_extension_counter = bit_lib_get_bits_16(block->value, 116, 10); //304
|
||||
uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->value, 128, 20); //314
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 158, 7); //412.
|
||||
uint8_t card_transport_type_flag = bit_lib_get_bits(block->value, 178, 2); //421.0
|
||||
uint8_t card_transport_type1 = bit_lib_get_bits(block->value, 180, 2); //421.1
|
||||
uint8_t card_transport_type2 = bit_lib_get_bits(block->value, 182, 2); //421.2
|
||||
uint8_t card_transport_type3 = bit_lib_get_bits(block->value, 184, 2); //421.3
|
||||
uint8_t card_transport_type4 = bit_lib_get_bits(block->value, 186, 2); //421.4
|
||||
uint16_t card_remaining_trips = bit_lib_get_bits_16(block->value, 169, 10); //321
|
||||
card_validator = bit_lib_get_bits_16(block->value, 179, 16); //422
|
||||
uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->value, 195, 20); //404
|
||||
uint8_t card_requires_activation = bit_lib_get_bits(block->value, 215, 1); //301
|
||||
card_blocked = bit_lib_get_bits(block->value, 216, 1); //303
|
||||
uint8_t card_extended = bit_lib_get_bits(block->value, 217, 1); //123
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %x %x %x %lx %x %x %x %x %x %x %x %x %lx %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_layout2,
|
||||
card_type_of_extended,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_valid_to_date,
|
||||
card_activate_during,
|
||||
card_extension_counter,
|
||||
card_valid_for_minutes,
|
||||
card_minutes_pass,
|
||||
card_transport_type_flag,
|
||||
card_transport_type1,
|
||||
card_transport_type2,
|
||||
card_transport_type3,
|
||||
card_transport_type4,
|
||||
card_remaining_trips,
|
||||
card_validator,
|
||||
card_start_trip_neg_minutes,
|
||||
card_requires_activation,
|
||||
card_blocked,
|
||||
card_extended,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 2016);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
(card_use_before_date - 1) * 24 * 60 + card_valid_for_minutes -
|
||||
card_start_trip_neg_minutes,
|
||||
&card_start_trip_minutes_s,
|
||||
2016); //-time
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x1C5: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_layout2 = bit_lib_get_bits(block->value, 56, 5); //112
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 61, 13); //202.
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 74, 10); //121.
|
||||
uint32_t card_valid_to_time = bit_lib_get_bits_32(block->value, 84, 23); //317
|
||||
uint16_t card_extension_counter = bit_lib_get_bits_16(block->value, 107, 10); //304
|
||||
card_start_trip_minutes = bit_lib_get_bits_32(block->value, 128, 23); //405
|
||||
uint8_t card_metro_ride_with = bit_lib_get_bits(block->value, 151, 7); //414
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 158, 7); //412.
|
||||
card_remaining_funds = bit_lib_get_bits_32(block->value, 167, 19) / 100; //322
|
||||
card_validator = bit_lib_get_bits_16(block->value, 186, 16); //422
|
||||
card_blocked = bit_lib_get_bits(block->value, 202, 1); //303
|
||||
uint16_t card_route = bit_lib_get_bits_16(block->value, 204, 12); //424
|
||||
uint8_t card_passages_ground_transport = bit_lib_get_bits(block->value, 216, 7); //433
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %lx %x %lx %x %x %lx %x %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_layout2,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_valid_to_time,
|
||||
card_extension_counter,
|
||||
card_start_trip_minutes,
|
||||
card_metro_ride_with,
|
||||
card_minutes_pass,
|
||||
card_remaining_funds,
|
||||
card_validator,
|
||||
card_blocked,
|
||||
card_route,
|
||||
card_passages_ground_transport,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
|
||||
from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2019);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
card_start_trip_minutes - (24 * 60), &card_start_trip_minutes_s, 2019);
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nBalance: %ld rub\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_remaining_funds,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x1C6: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
card_layout2 = bit_lib_get_bits(block->value, 56, 5); //112
|
||||
uint16_t card_type_of_extended = bit_lib_get_bits_16(block->value, 61, 10); //122
|
||||
card_use_before_date = bit_lib_get_bits_16(block->value, 71, 13); //202.
|
||||
card_blank_type = bit_lib_get_bits_16(block->value, 84, 10); //121.
|
||||
uint32_t card_valid_from_date = bit_lib_get_bits_32(block->value, 94, 23); //311
|
||||
uint16_t card_extension_counter = bit_lib_get_bits_16(block->value, 117, 10); //304
|
||||
uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->value, 128, 20); //314
|
||||
uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->value, 148, 20); //404
|
||||
uint8_t card_metro_ride_with = bit_lib_get_bits(block->value, 168, 7); //414
|
||||
card_minutes_pass = bit_lib_get_bits(block->value, 175, 7); //412.
|
||||
uint16_t card_remaining_trips = bit_lib_get_bits_16(block->value, 182, 7); //321
|
||||
card_validator = bit_lib_get_bits_16(block->value, 189, 16); //422
|
||||
card_blocked = bit_lib_get_bits(block->value, 205, 1); //303
|
||||
uint8_t card_extended = bit_lib_get_bits(block->value, 206, 1); //123
|
||||
uint16_t card_route = bit_lib_get_bits_16(block->value, 212, 12); //424
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %lx %x %lx %lx %x %x %x %x %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_layout2,
|
||||
card_type_of_extended,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_valid_from_date,
|
||||
card_extension_counter,
|
||||
card_valid_for_minutes,
|
||||
card_start_trip_neg_minutes,
|
||||
card_metro_ride_with,
|
||||
card_minutes_pass,
|
||||
card_remaining_trips,
|
||||
card_validator,
|
||||
card_blocked,
|
||||
card_extended,
|
||||
card_route,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_use_before_date - 1, &card_use_before_date_s, 2019);
|
||||
|
||||
FuriHalRtcDateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
card_valid_from_date + card_valid_for_minutes - card_start_trip_neg_minutes - 24 * 60,
|
||||
&card_start_trip_minutes_s,
|
||||
2019); //-time
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
card_start_trip_minutes_s.hour,
|
||||
card_start_trip_minutes_s.minute,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x3CCB: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
uint16_t card_tech_code = bit_lib_get_bits_32(block->value, 56, 10); //tech_code
|
||||
uint16_t card_valid_to_minutes = bit_lib_get_bits_16(block->value, 66, 16); //311
|
||||
uint16_t card_valid_by_date = bit_lib_get_bits_16(block->value, 82, 16); //312
|
||||
uint8_t card_interval = bit_lib_get_bits(block->value, 98, 4); //interval
|
||||
uint16_t card_app_code1 = bit_lib_get_bits_16(block->value, 102, 16); //app_code1
|
||||
uint16_t card_hash1 = bit_lib_get_bits_16(block->value, 112, 16); //502.1
|
||||
uint16_t card_type1 = bit_lib_get_bits_16(block->value, 128, 10); //type1
|
||||
uint16_t card_app_code2 = bit_lib_get_bits_16(block->value, 138, 10); //app_code2
|
||||
uint16_t card_type2 = bit_lib_get_bits_16(block->value, 148, 10); //type2
|
||||
uint16_t card_app_code3 = bit_lib_get_bits_16(block->value, 158, 10); //app_code3
|
||||
uint16_t card_type3 = bit_lib_get_bits_16(block->value, 148, 10); //type3
|
||||
uint16_t card_app_code4 = bit_lib_get_bits_16(block->value, 168, 10); //app_code4
|
||||
uint16_t card_type4 = bit_lib_get_bits_16(block->value, 178, 10); //type4
|
||||
card_hash = bit_lib_get_bits_32(block->value, 224, 32); //502.2
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %lx",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_tech_code,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_valid_to_minutes,
|
||||
card_valid_by_date,
|
||||
card_interval,
|
||||
card_app_code1,
|
||||
card_hash1,
|
||||
card_type1,
|
||||
card_app_code2,
|
||||
card_type2,
|
||||
card_app_code3,
|
||||
card_type3,
|
||||
card_app_code4,
|
||||
card_type4,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_valid_by_date - 1, &card_use_before_date_s, 1992);
|
||||
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
case 0x3C0B: {
|
||||
card_view = bit_lib_get_bits_16(block->value, 0, 10); //101
|
||||
card_type = bit_lib_get_bits_16(block->value, 10, 10); //102
|
||||
card_number = bit_lib_get_bits_32(block->value, 20, 32); //201
|
||||
card_layout = bit_lib_get_bits(block->value, 52, 4); //111
|
||||
uint16_t card_tech_code = bit_lib_get_bits_32(block->value, 56, 10); //tech_code
|
||||
uint16_t card_valid_to_minutes = bit_lib_get_bits_16(block->value, 66, 16); //311
|
||||
uint16_t card_valid_by_date = bit_lib_get_bits_16(block->value, 82, 16); //312
|
||||
uint16_t card_hash = bit_lib_get_bits_16(block->value, 112, 16); //502.1
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"%x %x %lx %x %x %x %x %x %x %x",
|
||||
card_view,
|
||||
card_type,
|
||||
card_number,
|
||||
card_layout,
|
||||
card_tech_code,
|
||||
card_use_before_date,
|
||||
card_blank_type,
|
||||
card_valid_to_minutes,
|
||||
card_valid_by_date,
|
||||
card_hash);
|
||||
FuriHalRtcDateTime card_use_before_date_s = {0};
|
||||
from_days_to_datetime(card_valid_by_date - 1, &card_use_before_date_s, 1992);
|
||||
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Number: %010lu\nValid for: %02d.%02d.%04d\nValidator: %05d",
|
||||
card_number,
|
||||
card_use_before_date_s.day,
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year,
|
||||
card_validator);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
7
lib/nfc/helpers/transport.h
Normal file
7
lib/nfc/helpers/transport.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal_rtc.h>
|
||||
#include <nfc/helpers/bit_lib.h>
|
||||
#include <nfc_worker_i.h>
|
||||
|
||||
bool parse_transport_block(MfClassicBlock* block, FuriString* result);
|
||||
@@ -50,14 +50,14 @@ void nfc_device_free(NfcDevice* nfc_dev) {
|
||||
static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_string) {
|
||||
if(dev->format == NfcDeviceSaveFormatUid) {
|
||||
furi_string_set(format_string, "UID");
|
||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||
furi_string_set(format_string, "Bank card");
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||
furi_string_set(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true));
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
furi_string_set(format_string, "Mifare Classic");
|
||||
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
||||
furi_string_set(format_string, "Mifare DESFire");
|
||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
||||
furi_string_set(format_string, "Bank card");
|
||||
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
|
||||
furi_string_set(format_string, "ISO15693");
|
||||
} else {
|
||||
@@ -71,11 +71,6 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st
|
||||
dev->dev_data.protocol = NfcDeviceProtocolUnknown;
|
||||
return true;
|
||||
}
|
||||
if(furi_string_start_with_str(format_string, "Bank card")) {
|
||||
dev->format = NfcDeviceSaveFormatBankCard;
|
||||
dev->dev_data.protocol = NfcDeviceProtocolEMV;
|
||||
return true;
|
||||
}
|
||||
// Check Mifare Ultralight types
|
||||
for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) {
|
||||
if(furi_string_equal(format_string, nfc_mf_ul_type(type, true))) {
|
||||
@@ -95,6 +90,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st
|
||||
dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire;
|
||||
return true;
|
||||
}
|
||||
if(furi_string_start_with_str(format_string, "Bank card")) {
|
||||
dev->format = NfcDeviceSaveFormatBankCard;
|
||||
dev->dev_data.protocol = NfcDeviceProtocolEMV;
|
||||
return true;
|
||||
}
|
||||
if(furi_string_start_with_str(format_string, "ISO15693")) {
|
||||
dev->format = NfcDeviceSaveFormatNfcV;
|
||||
dev->dev_data.protocol = NfcDeviceProtocolNfcV;
|
||||
|
||||
@@ -29,20 +29,20 @@ typedef void (*NfcLoadingCallback)(void* context, bool state);
|
||||
|
||||
typedef enum {
|
||||
NfcDeviceProtocolUnknown,
|
||||
NfcDeviceProtocolEMV,
|
||||
NfcDeviceProtocolMifareUl,
|
||||
NfcDeviceProtocolMifareClassic,
|
||||
NfcDeviceProtocolMifareDesfire,
|
||||
NfcDeviceProtocolNfcV
|
||||
NfcDeviceProtocolNfcV,
|
||||
NfcDeviceProtocolEMV,
|
||||
} NfcProtocol;
|
||||
|
||||
typedef enum {
|
||||
NfcDeviceSaveFormatUid,
|
||||
NfcDeviceSaveFormatBankCard,
|
||||
NfcDeviceSaveFormatMifareUl,
|
||||
NfcDeviceSaveFormatMifareClassic,
|
||||
NfcDeviceSaveFormatMifareDesfire,
|
||||
NfcDeviceSaveFormatNfcV,
|
||||
NfcDeviceSaveFormatBankCard,
|
||||
} NfcDeviceSaveFormat;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -15,14 +15,14 @@ const char* nfc_get_dev_type(FuriHalNfcType type) {
|
||||
}
|
||||
|
||||
const char* nfc_guess_protocol(NfcProtocol protocol) {
|
||||
if(protocol == NfcDeviceProtocolEMV) {
|
||||
return "EMV bank card";
|
||||
if(protocol == NfcDeviceProtocolMifareClassic) {
|
||||
return "Mifare Classic";
|
||||
} else if(protocol == NfcDeviceProtocolMifareUl) {
|
||||
return "Mifare Ultral/NTAG";
|
||||
} else if(protocol == NfcDeviceProtocolMifareClassic) {
|
||||
return "Mifare Classic";
|
||||
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||
return "Mifare DESFire";
|
||||
} else if(protocol == NfcDeviceProtocolEMV) {
|
||||
return "EMV bank card";
|
||||
} else {
|
||||
return "Unrecognized";
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
#include "plantain_parser.h"
|
||||
#include "troika_parser.h"
|
||||
#include "plantain_4k_parser.h"
|
||||
#include "troika_4k_parser.h"
|
||||
#include "two_cities.h"
|
||||
#include "all_in_one.h"
|
||||
#include "opal.h"
|
||||
#include "social_card_moscow_parser.h"
|
||||
|
||||
NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
|
||||
[NfcSupportedCardTypePlantain] =
|
||||
@@ -23,6 +23,13 @@ NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
|
||||
.read = troika_parser_read,
|
||||
.parse = troika_parser_parse,
|
||||
},
|
||||
[NfcSupportedCardTypeSocialCardMoscow] =
|
||||
{
|
||||
.protocol = NfcDeviceProtocolMifareClassic,
|
||||
.verify = social_card_moscow_parser_verify,
|
||||
.read = social_card_moscow_parser_read,
|
||||
.parse = social_card_moscow_parser_parse,
|
||||
},
|
||||
[NfcSupportedCardTypePlantain4K] =
|
||||
{
|
||||
.protocol = NfcDeviceProtocolMifareClassic,
|
||||
@@ -30,13 +37,6 @@ NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
|
||||
.read = plantain_4k_parser_read,
|
||||
.parse = plantain_4k_parser_parse,
|
||||
},
|
||||
[NfcSupportedCardTypeTroika4K] =
|
||||
{
|
||||
.protocol = NfcDeviceProtocolMifareClassic,
|
||||
.verify = troika_4k_parser_verify,
|
||||
.read = troika_4k_parser_read,
|
||||
.parse = troika_4k_parser_parse,
|
||||
},
|
||||
[NfcSupportedCardTypeTwoCities] =
|
||||
{
|
||||
.protocol = NfcDeviceProtocolMifareClassic,
|
||||
@@ -58,7 +58,6 @@ NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
|
||||
.read = stub_parser_verify_read,
|
||||
.parse = opal_parser_parse,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data) {
|
||||
|
||||
@@ -11,8 +11,8 @@ extern "C" {
|
||||
typedef enum {
|
||||
NfcSupportedCardTypePlantain,
|
||||
NfcSupportedCardTypeTroika,
|
||||
NfcSupportedCardTypeSocialCardMoscow,
|
||||
NfcSupportedCardTypePlantain4K,
|
||||
NfcSupportedCardTypeTroika4K,
|
||||
NfcSupportedCardTypeTwoCities,
|
||||
NfcSupportedCardTypeAllInOne,
|
||||
NfcSupportedCardTypeOpal,
|
||||
|
||||
131
lib/nfc/parsers/social_card_moscow_parser.c
Normal file
131
lib/nfc/parsers/social_card_moscow_parser.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "nfc_supported_card.h"
|
||||
|
||||
#include <gui/modules/widget.h>
|
||||
#include <nfc_worker_i.h>
|
||||
#include <nfc/helpers/transport.h>
|
||||
#include <nfc/helpers/bit_lib.h>
|
||||
|
||||
static const MfClassicAuthContext social_card_moscow_keys[] = {
|
||||
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0x7de02a7f6025},
|
||||
{.sector = 1, .key_a = 0x2735fc181807, .key_b = 0xbf23a53c1f63},
|
||||
{.sector = 2, .key_a = 0x2aba9519f574, .key_b = 0xcb9a1f2d7368},
|
||||
{.sector = 3, .key_a = 0x84fd7f7a12b6, .key_b = 0xc7c0adb3284f},
|
||||
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
|
||||
{.sector = 5, .key_a = 0x186d8c4b93f9, .key_b = 0x9f131d8c2057},
|
||||
{.sector = 6, .key_a = 0x3a4bba8adaf0, .key_b = 0x67362d90f973},
|
||||
{.sector = 7, .key_a = 0x8765b17968a2, .key_b = 0x6202a38f69e2},
|
||||
{.sector = 8, .key_a = 0x40ead80721ce, .key_b = 0x100533b89331},
|
||||
{.sector = 9, .key_a = 0x0db5e6523f7c, .key_b = 0x653a87594079},
|
||||
{.sector = 10, .key_a = 0x51119dae5216, .key_b = 0xd8a274b2e026},
|
||||
{.sector = 11, .key_a = 0x51119dae5216, .key_b = 0xd8a274b2e026},
|
||||
{.sector = 12, .key_a = 0x51119dae5216, .key_b = 0xd8a274b2e026},
|
||||
{.sector = 13, .key_a = 0xa0a1a2a3a4a5, .key_b = 0x7de02a7f6025},
|
||||
{.sector = 14, .key_a = 0xa0a1a2a3a4a5, .key_b = 0x7de02a7f6025},
|
||||
{.sector = 15, .key_a = 0xa0a1a2a3a4a5, .key_b = 0x7de02a7f6025},
|
||||
{.sector = 16, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 17, .key_a = 0x2aba9519f574, .key_b = 0xcb9a1f2d7368},
|
||||
{.sector = 18, .key_a = 0x84fd7f7a12b6, .key_b = 0xc7c0adb3284f},
|
||||
{.sector = 19, .key_a = 0x2aba9519f574, .key_b = 0xcb9a1f2d7368},
|
||||
{.sector = 20, .key_a = 0x84fd7f7a12b6, .key_b = 0xc7c0adb3284f},
|
||||
{.sector = 21, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 22, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 23, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 24, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 25, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 26, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 27, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 28, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 29, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 30, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 31, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 32, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 33, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 34, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 35, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 36, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 37, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 38, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
{.sector = 39, .key_a = 0xa229e68ad9e5, .key_b = 0x49c2b5296ef4},
|
||||
};
|
||||
|
||||
bool social_card_moscow_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(nfc_worker);
|
||||
|
||||
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
|
||||
FURI_LOG_I("Social card moscow", "Card type mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t sector = 15;
|
||||
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
|
||||
FURI_LOG_D("Social card moscow", "Verifying sector %d", sector);
|
||||
if(mf_classic_authenticate(tx_rx, block, 0xa0a1a2a3a4a5, MfClassicKeyA)) {
|
||||
FURI_LOG_D("Social card moscow", "Sector %d verified", sector);
|
||||
return true;
|
||||
}
|
||||
FURI_LOG_I("Social card moscow", "Not verified");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool social_card_moscow_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(nfc_worker);
|
||||
|
||||
MfClassicReader reader = {};
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
|
||||
for(size_t i = 0; i < COUNT_OF(social_card_moscow_keys); i++) {
|
||||
mf_classic_reader_add_sector(
|
||||
&reader,
|
||||
social_card_moscow_keys[i].sector,
|
||||
social_card_moscow_keys[i].key_a,
|
||||
social_card_moscow_keys[i].key_b);
|
||||
}
|
||||
|
||||
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40;
|
||||
}
|
||||
|
||||
bool social_card_moscow_parser_parse(NfcDeviceData* dev_data) {
|
||||
MfClassicData* data = &dev_data->mf_classic_data;
|
||||
|
||||
// Verify key
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 15);
|
||||
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||
if(key != social_card_moscow_keys[15].key_a) return false;
|
||||
|
||||
// Verify card type
|
||||
if(data->type != MfClassicType4k) return false;
|
||||
|
||||
//Print social number
|
||||
|
||||
uint32_t card_code = bit_lib_get_bits_32(data->block[60].value, 8, 24);
|
||||
uint8_t card_region = bit_lib_get_bits(data->block[60].value, 32, 8);
|
||||
uint64_t card_number = bit_lib_get_bits_64(data->block[60].value, 40, 40);
|
||||
uint8_t card_control = bit_lib_get_bits(data->block[60].value, 80, 4);
|
||||
uint64_t omc_number = bit_lib_get_bits_64(data->block[21].value, 8, 64);
|
||||
uint8_t year = data->block[60].value[11];
|
||||
uint8_t month = data->block[60].value[12];
|
||||
|
||||
FuriString* metro_result = furi_string_alloc();
|
||||
FuriString* ground_result = furi_string_alloc();
|
||||
|
||||
parse_transport_block(&data->block[4], metro_result);
|
||||
parse_transport_block(&data->block[16], ground_result);
|
||||
|
||||
furi_string_printf(
|
||||
dev_data->parsed_data,
|
||||
"\e#Social \ecard\nNumber: %lx %x %llx %x\nOMC: %llx\nValid for: %02x/%02x %02x%02x\n%s\n%s",
|
||||
card_code,
|
||||
card_region,
|
||||
card_number,
|
||||
card_control,
|
||||
omc_number,
|
||||
month,
|
||||
year,
|
||||
data->block[60].value[13],
|
||||
data->block[60].value[14],
|
||||
furi_string_get_cstr(metro_result),
|
||||
furi_string_get_cstr(ground_result));
|
||||
furi_string_free(metro_result);
|
||||
furi_string_free(ground_result);
|
||||
return true;
|
||||
}
|
||||
9
lib/nfc/parsers/social_card_moscow_parser.h
Normal file
9
lib/nfc/parsers/social_card_moscow_parser.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "nfc_supported_card.h"
|
||||
|
||||
bool social_card_moscow_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
|
||||
|
||||
bool social_card_moscow_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
|
||||
|
||||
bool social_card_moscow_parser_parse(NfcDeviceData* dev_data);
|
||||
@@ -1,106 +0,0 @@
|
||||
#include "nfc_supported_card.h"
|
||||
|
||||
#include <gui/modules/widget.h>
|
||||
#include <nfc_worker_i.h>
|
||||
|
||||
static const MfClassicAuthContext troika_4k_keys[] = {
|
||||
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
|
||||
{.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
|
||||
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
|
||||
{.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698},
|
||||
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb},
|
||||
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
|
||||
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
|
||||
{.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d},
|
||||
{.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b},
|
||||
{.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0},
|
||||
{.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
|
||||
{.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75},
|
||||
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408},
|
||||
{.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df},
|
||||
{.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9},
|
||||
{.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4},
|
||||
{.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216},
|
||||
{.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba},
|
||||
{.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252},
|
||||
{.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01},
|
||||
{.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994},
|
||||
{.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6},
|
||||
{.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32},
|
||||
{.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3},
|
||||
{.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5},
|
||||
{.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a},
|
||||
{.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80},
|
||||
{.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d},
|
||||
{.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2},
|
||||
{.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324},
|
||||
{.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae},
|
||||
{.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda},
|
||||
{.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d},
|
||||
{.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465},
|
||||
{.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4},
|
||||
{.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70},
|
||||
};
|
||||
|
||||
bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(nfc_worker);
|
||||
|
||||
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t sector = 11;
|
||||
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
|
||||
FURI_LOG_D("Troika", "Verifying sector %d", sector);
|
||||
if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) {
|
||||
FURI_LOG_D("Troika", "Sector %d verified", sector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(nfc_worker);
|
||||
|
||||
MfClassicReader reader = {};
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
|
||||
for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) {
|
||||
mf_classic_reader_add_sector(
|
||||
&reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b);
|
||||
}
|
||||
|
||||
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40;
|
||||
}
|
||||
|
||||
bool troika_4k_parser_parse(NfcDeviceData* dev_data) {
|
||||
MfClassicData* data = &dev_data->mf_classic_data;
|
||||
|
||||
// Verify key
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
|
||||
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||
if(key != troika_4k_keys[4].key_a) return false;
|
||||
|
||||
// Verify card type
|
||||
if(data->type != MfClassicType4k) return false;
|
||||
|
||||
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
|
||||
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
|
||||
temp_ptr = &data->block[8 * 4].value[2];
|
||||
uint32_t number = 0;
|
||||
for(size_t i = 1; i < 5; i++) {
|
||||
number <<= 8;
|
||||
number |= temp_ptr[i];
|
||||
}
|
||||
number >>= 4;
|
||||
number |= (temp_ptr[0] & 0xf) << 28;
|
||||
|
||||
furi_string_printf(
|
||||
dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "nfc_supported_card.h"
|
||||
|
||||
bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
|
||||
|
||||
bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
|
||||
|
||||
bool troika_4k_parser_parse(NfcDeviceData* dev_data);
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <gui/modules/widget.h>
|
||||
#include <nfc_worker_i.h>
|
||||
#include <nfc/helpers/transport.h>
|
||||
|
||||
#define TAG "Troyka parser"
|
||||
|
||||
static const MfClassicAuthContext troika_keys[] = {
|
||||
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
|
||||
@@ -9,9 +12,9 @@ static const MfClassicAuthContext troika_keys[] = {
|
||||
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
|
||||
{.sector = 5, .key_a = 0xfbc2793d540b, .key_b = 0xd3a297dc2698},
|
||||
{.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698},
|
||||
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dba},
|
||||
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb},
|
||||
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
|
||||
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
|
||||
{.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d},
|
||||
@@ -20,19 +23,44 @@ static const MfClassicAuthContext troika_keys[] = {
|
||||
{.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
|
||||
{.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75},
|
||||
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
|
||||
{.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408},
|
||||
{.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df},
|
||||
{.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9},
|
||||
{.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4},
|
||||
{.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216},
|
||||
{.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba},
|
||||
{.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252},
|
||||
{.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01},
|
||||
{.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994},
|
||||
{.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6},
|
||||
{.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32},
|
||||
{.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3},
|
||||
{.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5},
|
||||
{.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a},
|
||||
{.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80},
|
||||
{.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d},
|
||||
{.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2},
|
||||
{.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324},
|
||||
{.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae},
|
||||
{.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda},
|
||||
{.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d},
|
||||
{.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465},
|
||||
{.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4},
|
||||
{.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70},
|
||||
};
|
||||
|
||||
bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(nfc_worker);
|
||||
UNUSED(nfc_worker);
|
||||
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) {
|
||||
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k &&
|
||||
nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t sector = 11;
|
||||
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
|
||||
FURI_LOG_D("Troika", "Verifying sector %d", sector);
|
||||
if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) {
|
||||
if(mf_classic_authenticate(tx_rx, block, troika_keys[sector].key_a, MfClassicKeyA)) {
|
||||
FURI_LOG_D("Troika", "Sector %d verified", sector);
|
||||
return true;
|
||||
}
|
||||
@@ -42,16 +70,20 @@ bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(nfc_worker);
|
||||
|
||||
MfClassicReader reader = {};
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
|
||||
MfClassicData* mf_classic_data = &nfc_worker->dev_data->mf_classic_data;
|
||||
MfClassicType type = mf_classic_data->type;
|
||||
size_t sectors = type == MfClassicType4k ? 40 : 16;
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(troika_keys); i++) {
|
||||
mf_classic_reader_add_sector(
|
||||
&reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b);
|
||||
for(size_t i = 0; i < sectors; i++) {
|
||||
mf_classic_set_key_found(
|
||||
mf_classic_data, troika_keys[i].sector, MfClassicKeyA, troika_keys[i].key_a);
|
||||
mf_classic_set_key_found(
|
||||
mf_classic_data, troika_keys[i].sector, MfClassicKeyB, troika_keys[i].key_b);
|
||||
}
|
||||
|
||||
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
|
||||
uint8_t res = mf_classic_update_card(tx_rx, mf_classic_data);
|
||||
FURI_LOG_D("Troika", "Update card res: %d", res);
|
||||
return res == sectors;
|
||||
}
|
||||
|
||||
bool troika_parser_parse(NfcDeviceData* dev_data) {
|
||||
@@ -63,24 +95,24 @@ bool troika_parser_parse(NfcDeviceData* dev_data) {
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
|
||||
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||
if(key != troika_keys[8].key_a) break;
|
||||
sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
|
||||
key = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||
if(key != troika_keys[4].key_a) break;
|
||||
|
||||
// Verify card type
|
||||
if(data->type != MfClassicType1k) break;
|
||||
|
||||
// Parse data
|
||||
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
|
||||
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
|
||||
temp_ptr = &data->block[8 * 4].value[2];
|
||||
uint32_t number = 0;
|
||||
for(size_t i = 1; i < 5; i++) {
|
||||
number <<= 8;
|
||||
number |= temp_ptr[i];
|
||||
}
|
||||
number >>= 4;
|
||||
number |= (temp_ptr[0] & 0xf) << 28;
|
||||
if(data->type != MfClassicType1k && data->type != MfClassicType4k) break;
|
||||
|
||||
FuriString* metro_result = furi_string_alloc();
|
||||
FuriString* ground_result = furi_string_alloc();
|
||||
parse_transport_block(&data->block[32], metro_result);
|
||||
parse_transport_block(&data->block[28], ground_result);
|
||||
furi_string_printf(
|
||||
dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance);
|
||||
dev_data->parsed_data,
|
||||
"\e#Troika\n%s\n%s",
|
||||
furi_string_get_cstr(metro_result),
|
||||
furi_string_get_cstr(ground_result));
|
||||
furi_string_free(metro_result);
|
||||
furi_string_free(ground_result);
|
||||
troika_parsed = true;
|
||||
} while(false);
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ const SubGhzProtocol subghz_protocol_magellan = {
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send |
|
||||
SubGhzProtocolFlag_Magelan,
|
||||
SubGhzProtocolFlag_Magellan,
|
||||
|
||||
.decoder = &subghz_protocol_magellan_decoder,
|
||||
.encoder = &subghz_protocol_magellan_encoder,
|
||||
|
||||
@@ -73,7 +73,7 @@ const SubGhzProtocol subghz_protocol_princeton = {
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Princeton,
|
||||
|
||||
.decoder = &subghz_protocol_princeton_decoder,
|
||||
.encoder = &subghz_protocol_princeton_encoder,
|
||||
|
||||
@@ -127,7 +127,8 @@ typedef enum {
|
||||
SubGhzProtocolFlag_BinRAW = (1 << 10),
|
||||
SubGhzProtocolFlag_StarLine = (1 << 11),
|
||||
SubGhzProtocolFlag_AutoAlarms = (1 << 12),
|
||||
SubGhzProtocolFlag_Magelan = (1 << 13),
|
||||
SubGhzProtocolFlag_Magellan = (1 << 13),
|
||||
SubGhzProtocolFlag_Princeton = (1 << 14),
|
||||
} SubGhzProtocolFlag;
|
||||
|
||||
struct SubGhzProtocol {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define CONTRAST_ERC 31
|
||||
#define CONTRAST_MGG 31
|
||||
#define CONTRAST_MGG 27
|
||||
|
||||
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
|
||||
UNUSED(u8x8);
|
||||
|
||||
@@ -67,7 +67,10 @@ class FlipperFormatFile:
|
||||
self.writeLine("")
|
||||
|
||||
def writeComment(self, text: str):
|
||||
self.writeLine(f"# {text}")
|
||||
if text:
|
||||
self.writeLine(f"# {text}")
|
||||
else:
|
||||
self.writeLine("#")
|
||||
|
||||
def getHeader(self):
|
||||
if self.cursor != 0 and len(self.lines) == 0:
|
||||
|
||||
79
scripts/infrared.py
Executable file
79
scripts/infrared.py
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from os import path
|
||||
|
||||
from flipper.app import App
|
||||
from flipper.utils.fff import *
|
||||
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
# Subparsers
|
||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||
|
||||
self.parser_cleanup = self.subparsers.add_parser(
|
||||
"cleanup", help="Cleanup duplicate remotes"
|
||||
)
|
||||
self.parser_cleanup.add_argument("filename", type=str)
|
||||
self.parser_cleanup.set_defaults(func=self.cleanup)
|
||||
|
||||
def cleanup(self):
|
||||
f = FlipperFormatFile()
|
||||
f.load(self.args.filename)
|
||||
|
||||
filetype, version = f.getHeader()
|
||||
if filetype != "IR library file" or version != 1:
|
||||
self.logger.error(f"Incorrect file type({filetype}) or version({version})")
|
||||
return 1
|
||||
|
||||
data = []
|
||||
unique = {}
|
||||
while True:
|
||||
try:
|
||||
d = {}
|
||||
d["name"] = f.readKey("name")
|
||||
d["type"] = f.readKey("type")
|
||||
key = None
|
||||
if d["type"] == "parsed":
|
||||
d["protocol"] = f.readKey("protocol")
|
||||
d["address"] = f.readKey("address")
|
||||
d["command"] = f.readKey("command")
|
||||
key = f'{d["protocol"]}{d["address"]}{d["command"]}'
|
||||
elif d["type"] == "raw":
|
||||
d["frequency"] = f.readKey("frequency")
|
||||
d["duty_cycle"] = f.readKey("duty_cycle")
|
||||
d["data"] = f.readKey("data")
|
||||
key = f'{d["frequency"]}{d["duty_cycle"]}{d["data"]}'
|
||||
else:
|
||||
raise Exception(f'Unknown type: {d["type"]}')
|
||||
if not key in unique:
|
||||
unique[key] = d
|
||||
data.append(d)
|
||||
else:
|
||||
self.logger.warn(f"Duplicate key: {key}")
|
||||
except EOFError:
|
||||
break
|
||||
# Form new file
|
||||
f = FlipperFormatFile()
|
||||
f.setHeader(filetype, version)
|
||||
for i in data:
|
||||
f.writeComment(None)
|
||||
f.writeKey("name", i["name"])
|
||||
f.writeKey("type", i["type"])
|
||||
if i["type"] == "parsed":
|
||||
f.writeKey("protocol", i["protocol"])
|
||||
f.writeKey("address", i["address"])
|
||||
f.writeKey("command", i["command"])
|
||||
elif i["type"] == "raw":
|
||||
f.writeKey("frequency", i["frequency"])
|
||||
f.writeKey("duty_cycle", i["duty_cycle"])
|
||||
f.writeKey("data", i["data"])
|
||||
else:
|
||||
raise Exception(f'Unknown type: {i["type"]}')
|
||||
f.save(self.args.filename)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()()
|
||||
@@ -22,7 +22,8 @@ class Main(App):
|
||||
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
||||
self._add_args(self.parser_set)
|
||||
self.parser_set.set_defaults(func=self.set)
|
||||
# Set command
|
||||
|
||||
# Recover command
|
||||
self.parser_recover = self.subparsers.add_parser(
|
||||
"recover", help="Recover Option Bytes"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user