Merge branch 'dev' into nfcf

This commit is contained in:
nullableVoidPtr
2023-03-24 18:55:58 +11:00
1084 changed files with 30782 additions and 11463 deletions
+23 -12
View File
@@ -8,7 +8,6 @@ env.Append(
Dir("flipper_format"),
Dir("infrared"),
Dir("nfc"),
Dir("one_wire"),
Dir("ST25RFAL002"),
Dir("subghz"),
Dir("toolbox"),
@@ -16,16 +15,9 @@ env.Append(
Dir("update_util"),
Dir("print"),
],
SDK_HEADERS=[
File("one_wire/one_wire_host_timing.h"),
File("one_wire/one_wire_host.h"),
File("one_wire/one_wire_slave.h"),
File("one_wire/one_wire_device.h"),
File("one_wire/ibutton/ibutton_worker.h"),
File("one_wire/maxim_crc.h"),
],
)
env.Append(
CPPPATH=[
"#/",
@@ -34,6 +26,23 @@ env.Append(
# Ugly hack
Dir("../assets/compiled"),
],
SDK_HEADERS=[
*(
File(f"#/lib/mlib/m-{name}.h")
for name in (
"algo",
"array",
"bptree",
"core",
"deque",
"dict",
"list",
"rbtree",
"tuple",
"variant",
)
),
],
CPPDEFINES=[
'"M_MEMORY_FULL(x)=abort()"',
],
@@ -47,13 +56,13 @@ env.Append(
# littlefs
# subghz
# toolbox
# one_wire
# micro-ecc
# misc
# digital_signal
# fnv1a-hash
# micro-ecc
# fnv1a_hash
# microtar
# nfc
# one_wire
# qrcode
# u8g2
# update_util
@@ -77,6 +86,8 @@ libs = env.BuildModules(
"drivers",
"fatfs",
"flipper_format",
"one_wire",
"ibutton",
"infrared",
"littlefs",
"mbedtls",
@@ -1,5 +1,6 @@
#include "popup_vm.h"
#include "gui/modules/popup.h"
#include <gui/modules/popup.h>
PopupVM::PopupVM() {
popup = popup_alloc();
}
+8 -11
View File
@@ -140,19 +140,16 @@ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) {
void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) {
if(vreg_voltage < 3840) {
// Minimum value is 3840 mV
bq25896_regs.r06.VREG = 0;
} else {
// Find the nearest voltage value (subtract offset, divide into sections)
// Values are truncated downward as needed (e.g. 4200mV -> 4192 mV)
bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16);
// Minimum valid value is 3840 mV
vreg_voltage = 3840;
} else if(vreg_voltage > 4208) {
// Maximum safe value is 4208 mV
vreg_voltage = 4208;
}
// Do not allow values above 23 (0x17, 4208mV)
// Exceeding 4.2v will overcharge the battery!
if(bq25896_regs.r06.VREG > 23) {
bq25896_regs.r06.VREG = 23;
}
// Find the nearest voltage value (subtract offset, divide into sections)
// Values are truncated downward as needed (e.g. 4200mV -> 4192 mV)
bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16);
// Apply changes
furi_hal_i2c_write_reg_8(
+2 -2
View File
@@ -36,10 +36,10 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle);
/** Is otg enabled */
bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle);
/** Get VREG (charging) voltage in mV */
/** Get VREG (charging limit) voltage in mV */
uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle);
/** Set VREG (charging) voltage in mV
/** Set VREG (charging limit) voltage in mV
*
* Valid range: 3840mV - 4208mV, in steps of 16mV
*/
+7 -8
View File
@@ -150,9 +150,8 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint
}
uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) {
uint8_t buff_tx[64];
buff_tx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST;
uint8_t buff_rx[2];
uint8_t buff_trx[2];
buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST;
// Start transaction
// Wait IC to become ready
@@ -160,15 +159,15 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si
;
// First byte - packet length
furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT);
furi_hal_spi_bus_trx(handle, buff_trx, buff_trx, 2, CC1101_TIMEOUT);
// Check that the packet is placed in the receive buffer
if(buff_rx[1] > 64) {
if(buff_trx[1] > 64) {
*size = 64;
} else {
*size = buff_rx[1];
*size = buff_trx[1];
}
furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT);
furi_hal_spi_bus_trx(handle, NULL, data, *size, CC1101_TIMEOUT);
return *size;
}
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include <furi.h>
#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__)
+5
View File
@@ -6,6 +6,10 @@ env.Append(
],
SDK_HEADERS=[
File("flipper_application.h"),
File("plugins/plugin_manager.h"),
File("plugins/composite_resolver.h"),
File("api_hashtable/api_hashtable.h"),
File("api_hashtable/compilesort.hpp"),
],
)
@@ -14,6 +18,7 @@ libenv = env.Clone(FW_LIB_NAME="flipper_application")
libenv.ApplyLibFlags()
sources = libenv.GlobRecursive("*.c")
sources.append(File("api_hashtable/api_hashtable.cpp"))
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
@@ -0,0 +1,38 @@
#include "api_hashtable.h"
#include <furi.h>
#include <algorithm>
#define TAG "hashtable_api"
bool elf_resolve_from_hashtable(
const ElfApiInterface* interface,
const char* name,
Elf32_Addr* address) {
const HashtableApiInterface* hashtable_interface =
static_cast<const HashtableApiInterface*>(interface);
bool result = false;
uint32_t gnu_sym_hash = elf_gnu_hash(name);
sym_entry key = {
.hash = gnu_sym_hash,
.address = 0,
};
auto find_res =
std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key);
if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) {
FURI_LOG_W(
TAG,
"Can't find symbol '%s' (hash %lx) @ %p!",
name,
gnu_sym_hash,
hashtable_interface->table_cbegin);
result = false;
} else {
result = true;
*address = find_res->address;
}
return result;
}
@@ -0,0 +1,85 @@
#pragma once
#include <flipper_application/elf/elf_api_interface.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Symbol table entry
*/
struct sym_entry {
uint32_t hash;
uint32_t address;
};
/**
* @brief Resolver for API entries using a pre-sorted table with hashes
* @param interface pointer to HashtableApiInterface
* @param name function name
* @param address output for function address
* @return true if the table contains a function
*/
bool elf_resolve_from_hashtable(
const ElfApiInterface* interface,
const char* name,
Elf32_Addr* address);
#ifdef __cplusplus
}
#include <array>
#include <algorithm>
/**
* @brief HashtableApiInterface is an implementation of ElfApiInterface
* that uses a hash table to resolve function addresses.
* table_cbegin and table_cend must point to a sorted array of sym_entry
*/
struct HashtableApiInterface : public ElfApiInterface {
const sym_entry *table_cbegin, *table_cend;
};
#define API_METHOD(x, ret_type, args_type) \
sym_entry { \
.hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast<ret_type(*) args_type>(x)) \
}
#define API_VARIABLE(x, var_type) \
sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), }
constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {
return k1.hash < k2.hash;
}
/**
* @brief Calculate hash for a string using the ELF GNU hash algorithm
* @param s string to calculate hash for
* @return hash value
*/
constexpr uint32_t elf_gnu_hash(const char* s) {
uint32_t h = 0x1505;
for(unsigned char c = *s; c != '\0'; c = *++s) {
h = (h << 5) + h + c;
}
return h;
}
/* Compile-time check for hash collisions in API table.
* Usage: static_assert(!has_hash_collisions(api_methods), "Hash collision detected");
*/
template <std::size_t N>
constexpr bool has_hash_collisions(const std::array<sym_entry, N>& api_methods) {
for(std::size_t i = 0; i < (N - 1); ++i) {
if(api_methods[i].hash == api_methods[i + 1].hash) {
return true;
}
}
return false;
}
#endif
@@ -0,0 +1,115 @@
/**
* Implementation of compile-time sort for symbol table entries.
*/
#pragma once
#ifdef __cplusplus
#include <iterator>
#include <array>
namespace cstd {
template <typename RAIt>
constexpr RAIt next(RAIt it, typename std::iterator_traits<RAIt>::difference_type n = 1) {
return it + n;
}
template <typename RAIt>
constexpr auto distance(RAIt first, RAIt last) {
return last - first;
}
template <class ForwardIt1, class ForwardIt2>
constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) {
auto temp = std::move(*a);
*a = std::move(*b);
*b = std::move(temp);
}
template <class InputIt, class UnaryPredicate>
constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) {
for(; first != last; ++first) {
if(!q(*first)) {
return first;
}
}
return last;
}
template <class ForwardIt, class UnaryPredicate>
constexpr ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p) {
first = cstd::find_if_not(first, last, p);
if(first == last) return first;
for(ForwardIt i = cstd::next(first); i != last; ++i) {
if(p(*i)) {
cstd::iter_swap(i, first);
++first;
}
}
return first;
}
}
template <class RAIt, class Compare = std::less<> >
constexpr void quick_sort(RAIt first, RAIt last, Compare cmp = Compare{}) {
auto const N = cstd::distance(first, last);
if(N <= 1) return;
auto const pivot = *cstd::next(first, N / 2);
auto const middle1 =
cstd::partition(first, last, [=](auto const& elem) { return cmp(elem, pivot); });
auto const middle2 =
cstd::partition(middle1, last, [=](auto const& elem) { return !cmp(pivot, elem); });
quick_sort(first, middle1, cmp); // assert(std::is_sorted(first, middle1, cmp));
quick_sort(middle2, last, cmp); // assert(std::is_sorted(middle2, last, cmp));
}
template <typename Range>
constexpr auto sort(Range&& range) {
quick_sort(std::begin(range), std::end(range));
return range;
}
template <typename V, typename... T>
constexpr auto array_of(T&&... t) -> std::array<V, sizeof...(T)> {
return {{std::forward<T>(t)...}};
}
template <typename T, typename... N>
constexpr auto my_make_array(N&&... args) -> std::array<T, sizeof...(args)> {
return {std::forward<N>(args)...};
}
namespace traits {
template <typename T, typename... Ts>
struct array_type {
using type = T;
};
template <typename T, typename... Ts>
static constexpr bool are_same_type() {
return std::conjunction_v<std::is_same<T, Ts>...>;
}
}
template <typename... T>
constexpr auto create_array(const T&&... values) {
using array_type = typename traits::array_type<T...>::type;
static_assert(sizeof...(T) > 0, "an array must have at least one element");
static_assert(traits::are_same_type<T...>(), "all elements must have same type");
return std::array<array_type, sizeof...(T)>{values...};
}
template <typename T, typename... Ts>
constexpr auto create_array_t(const Ts&&... values) {
using array_type = T;
static_assert(sizeof...(Ts) > 0, "an array must have at least one element");
static_assert(traits::are_same_type<Ts...>(), "all elements must have same type");
return std::array<array_type, sizeof...(Ts)>{static_cast<T>(values)...};
}
#endif
@@ -0,0 +1,361 @@
#include "application_assets.h"
#include <toolbox/path.h>
#include <storage/storage_i.h>
// #define ELF_ASSETS_DEBUG_LOG 1
#ifndef ELF_ASSETS_DEBUG_LOG
#undef FURI_LOG_D
#define FURI_LOG_D(...)
#undef FURI_LOG_E
#define FURI_LOG_E(...)
#endif
#define FLIPPER_APPLICATION_ASSETS_MAGIC 0x4F4C5A44
#define FLIPPER_APPLICATION_ASSETS_VERSION 1
#define FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME ".assets.signature"
#define BUFFER_SIZE 512
#define TAG "fap_assets"
#pragma pack(push, 1)
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t dirs_count;
uint32_t files_count;
} FlipperApplicationAssetsHeader;
#pragma pack(pop)
typedef enum {
AssetsSignatureResultEqual,
AssetsSignatureResultNotEqual,
AssetsSignatureResultError,
} AssetsSignatureResult;
static FuriString* flipper_application_assets_alloc_app_full_path(FuriString* app_name) {
furi_assert(app_name);
FuriString* full_path = furi_string_alloc_set(APPS_ASSETS_PATH "/");
furi_string_cat(full_path, app_name);
return full_path;
}
static FuriString* flipper_application_assets_alloc_signature_file_path(FuriString* app_name) {
furi_assert(app_name);
FuriString* signature_file_path = flipper_application_assets_alloc_app_full_path(app_name);
furi_string_cat(signature_file_path, "/" FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME);
return signature_file_path;
}
static uint8_t* flipper_application_assets_alloc_and_load_data(File* file, size_t* size) {
furi_assert(file);
uint8_t* data = NULL;
uint32_t length = 0;
// read data length
if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {
return NULL;
}
data = malloc(length);
// read data
if(storage_file_read(file, (void*)data, length) != length) {
free((void*)data);
return NULL;
}
if(size != NULL) {
*size = length;
}
return data;
}
static bool flipper_application_assets_process_files(
Storage* storage,
File* file,
FuriString* app_name,
uint32_t files_count) {
furi_assert(storage);
furi_assert(file);
furi_assert(app_name);
UNUSED(storage);
bool success = false;
uint32_t length = 0;
char* path = NULL;
FuriString* file_path = furi_string_alloc();
File* destination = storage_file_alloc(storage);
FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
for(uint32_t i = 0; i < files_count; i++) {
path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);
if(path == NULL) {
break;
}
// read file size
if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {
break;
}
furi_string_set(file_path, full_path);
furi_string_cat(file_path, "/");
furi_string_cat(file_path, path);
if(!storage_file_open(
destination, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
FURI_LOG_E(TAG, "Can't create file: %s", furi_string_get_cstr(file_path));
break;
}
// copy data to file
if(!storage_file_copy_to_file(file, destination, length)) {
FURI_LOG_E(TAG, "Can't copy data to file: %s", furi_string_get_cstr(file_path));
break;
}
storage_file_close(destination);
free(path);
path = NULL;
if(i == files_count - 1) {
success = true;
}
}
if(path != NULL) {
free(path);
}
storage_file_free(destination);
furi_string_free(file_path);
return success;
}
static bool flipper_application_assets_process_dirs(
Storage* storage,
File* file,
FuriString* app_name,
uint32_t dirs_count) {
furi_assert(storage);
furi_assert(file);
furi_assert(app_name);
bool success = false;
FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
do {
if(!storage_simply_mkdir(storage, APPS_ASSETS_PATH)) {
break;
}
if(!storage_simply_mkdir(storage, furi_string_get_cstr(full_path))) {
break;
}
FuriString* dir_path = furi_string_alloc();
char* path = NULL;
for(uint32_t i = 0; i < dirs_count; i++) {
path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);
if(path == NULL) {
break;
}
furi_string_set(dir_path, full_path);
furi_string_cat(dir_path, "/");
furi_string_cat(dir_path, path);
if(!storage_simply_mkdir(storage, furi_string_get_cstr(dir_path))) {
FURI_LOG_E(TAG, "Can't create directory: %s", furi_string_get_cstr(dir_path));
break;
}
free(path);
path = NULL;
if(i == dirs_count - 1) {
success = true;
}
}
if(path != NULL) {
free(path);
}
furi_string_free(dir_path);
} while(false);
furi_string_free(full_path);
return success;
}
static AssetsSignatureResult flipper_application_assets_process_signature(
Storage* storage,
File* file,
FuriString* app_name,
uint8_t** signature_data,
size_t* signature_data_size) {
furi_assert(storage);
furi_assert(file);
furi_assert(app_name);
furi_assert(signature_data);
furi_assert(signature_data_size);
AssetsSignatureResult result = AssetsSignatureResultError;
File* signature_file = storage_file_alloc(storage);
FuriString* signature_file_path =
flipper_application_assets_alloc_signature_file_path(app_name);
do {
// read signature
*signature_data =
flipper_application_assets_alloc_and_load_data(file, signature_data_size);
if(*signature_data == NULL) { //-V547
FURI_LOG_E(TAG, "Can't read signature");
break;
}
result = AssetsSignatureResultNotEqual;
if(!storage_file_open(
signature_file,
furi_string_get_cstr(signature_file_path),
FSAM_READ_WRITE,
FSOM_OPEN_EXISTING)) {
FURI_LOG_E(TAG, "Can't open signature file");
break;
}
size_t signature_size = storage_file_size(signature_file);
uint8_t* signature_file_data = malloc(signature_size);
if(storage_file_read(signature_file, signature_file_data, signature_size) !=
signature_size) {
FURI_LOG_E(TAG, "Can't read signature file");
free(signature_file_data);
break;
}
if(memcmp(*signature_data, signature_file_data, signature_size) == 0) {
FURI_LOG_D(TAG, "Assets signature is equal");
result = AssetsSignatureResultEqual;
}
free(signature_file_data);
} while(0);
storage_file_free(signature_file);
furi_string_free(signature_file_path);
return result;
}
bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size) {
UNUSED(size);
furi_assert(file);
furi_assert(elf_path);
FlipperApplicationAssetsHeader header;
bool result = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
uint8_t* signature_data = NULL;
size_t signature_data_size = 0;
FuriString* app_name = furi_string_alloc();
path_extract_filename_no_ext(elf_path, app_name);
FURI_LOG_D(TAG, "Loading assets for %s", furi_string_get_cstr(app_name));
do {
if(!storage_file_seek(file, offset, true)) {
break;
}
// read header
if(storage_file_read(file, &header, sizeof(header)) != sizeof(header)) {
break;
}
if(header.magic != FLIPPER_APPLICATION_ASSETS_MAGIC) {
break;
}
if(header.version != FLIPPER_APPLICATION_ASSETS_VERSION) {
break;
}
// process signature
AssetsSignatureResult signature_result = flipper_application_assets_process_signature(
storage, file, app_name, &signature_data, &signature_data_size);
if(signature_result == AssetsSignatureResultError) {
FURI_LOG_E(TAG, "Assets signature error");
break;
} else if(signature_result == AssetsSignatureResultEqual) {
FURI_LOG_D(TAG, "Assets signature equal, skip loading");
result = true;
break;
} else {
FURI_LOG_D(TAG, "Assets signature not equal, loading");
// remove old assets
FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
storage_simply_remove_recursive(storage, furi_string_get_cstr(full_path));
furi_string_free(full_path);
FURI_LOG_D(TAG, "Assets removed");
}
// process directories
if(!flipper_application_assets_process_dirs(storage, file, app_name, header.dirs_count)) {
break;
}
// process files
if(!flipper_application_assets_process_files(storage, file, app_name, header.files_count)) {
break;
}
// write signature
FuriString* signature_file_path =
flipper_application_assets_alloc_signature_file_path(app_name);
File* signature_file = storage_file_alloc(storage);
if(storage_file_open(
signature_file,
furi_string_get_cstr(signature_file_path),
FSAM_WRITE,
FSOM_CREATE_ALWAYS)) {
storage_file_write(signature_file, signature_data, signature_data_size);
}
storage_file_free(signature_file);
furi_string_free(signature_file_path);
result = true;
} while(false);
if(signature_data != NULL) {
free(signature_data);
}
furi_record_close(RECORD_STORAGE);
furi_string_free(app_name);
FURI_LOG_D(TAG, "Assets loading %s", result ? "success" : "failed");
return result;
}
@@ -0,0 +1,17 @@
/**
* @file application_assets.h
* Flipper application assets
*/
#pragma once
#include <storage/storage.h>
#ifdef __cplusplus
extern "C" {
#endif
bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size);
#ifdef __cplusplus
}
#endif
@@ -1,5 +1,7 @@
#include "application_manifest.h"
#include <furi_hal_version.h>
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) {
if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) ||
(manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) {
@@ -19,3 +21,8 @@ bool flipper_application_manifest_is_compatible(
return true;
}
bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest) {
const Version* version = furi_hal_version_get_firmware_version();
return version_get_target(version) == manifest->base.hardware_target_id;
}
@@ -65,6 +65,14 @@ bool flipper_application_manifest_is_compatible(
const FlipperApplicationManifest* manifest,
const ElfApiInterface* api_interface);
/**
* @brief Check if application is compatible with current hardware
*
* @param manifest
* @return bool
*/
bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest);
#ifdef __cplusplus
}
#endif
@@ -3,10 +3,14 @@
#include <elf.h>
#include <stdbool.h>
#define ELF_INVALID_ADDRESS 0xFFFFFFFF
typedef struct {
/**
* @brief Interface for ELF loader to resolve symbols
*/
typedef struct ElfApiInterface {
uint16_t api_version_major;
uint16_t api_version_minor;
bool (*resolver_callback)(const char* name, Elf32_Addr* address);
bool (*resolver_callback)(
const struct ElfApiInterface* interface,
const char* name,
Elf32_Addr* address);
} ElfApiInterface;
+75 -79
View File
@@ -17,6 +17,8 @@
#define FURI_LOG_D(...)
#endif
#define ELF_INVALID_ADDRESS 0xFFFFFFFF
#define TRAMPOLINE_CODE_SIZE 6
/**
@@ -166,7 +168,7 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) {
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
if(sym->st_shndx == SHN_UNDEF) {
Elf32_Addr addr = 0;
if(elf->api_interface->resolver_callback(sName, &addr)) {
if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) {
return addr;
}
} else {
@@ -241,7 +243,7 @@ static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, El
if(to_thumb || (symAddr & 2) || (!is_call)) {
FURI_LOG_D(
TAG,
"can't relocate value at %x, %s, doing trampoline",
"can't relocate value at %lx, %s, doing trampoline",
relAddr,
elf_reloc_type_to_str(type));
@@ -421,29 +423,11 @@ typedef enum {
SectionTypeRelData = 1 << 2,
SectionTypeSymTab = 1 << 3,
SectionTypeStrTab = 1 << 4,
SectionTypeManifest = 1 << 5,
SectionTypeDebugLink = 1 << 6,
SectionTypeDebugLink = 1 << 5,
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab | SectionTypeManifest,
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
} SectionType;
static bool elf_load_metadata(
ELFFile* elf,
Elf32_Shdr* section_header,
FlipperApplicationManifest* manifest) {
if(section_header->sh_size < sizeof(FlipperApplicationManifest)) {
return false;
}
if(manifest == NULL) {
return true;
}
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
storage_file_read(elf->fd, manifest, section_header->sh_size) ==
section_header->sh_size;
}
static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
elf->debug_link_info.debug_link_size = section_header->sh_size;
elf->debug_link_info.debug_link = malloc(section_header->sh_size);
@@ -478,7 +462,7 @@ static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr*
return false;
}
FURI_LOG_D(TAG, "0x%X", section->data);
FURI_LOG_D(TAG, "0x%p", section->data);
return true;
}
@@ -486,8 +470,7 @@ static SectionType elf_preload_section(
ELFFile* elf,
size_t section_idx,
Elf32_Shdr* section_header,
FuriString* name_string,
FlipperApplicationManifest* manifest) {
FuriString* name_string) {
const char* name = furi_string_get_cstr(name_string);
#ifdef ELF_DEBUG_LOG
@@ -533,10 +516,13 @@ static SectionType elf_preload_section(
section_p->sec_idx = section_idx;
if(section_header->sh_type == SHT_PREINIT_ARRAY) {
furi_assert(elf->preinit_array == NULL);
elf->preinit_array = section_p;
} else if(section_header->sh_type == SHT_INIT_ARRAY) {
furi_assert(elf->init_array == NULL);
elf->init_array = section_p;
} else if(section_header->sh_type == SHT_FINI_ARRAY) {
furi_assert(elf->fini_array == NULL);
elf->fini_array = section_p;
}
@@ -572,16 +558,6 @@ static SectionType elf_preload_section(
return SectionTypeStrTab;
}
// Load manifest section
if(strcmp(name, ".fapmeta") == 0) {
FURI_LOG_D(TAG, "Found .fapmeta section");
if(elf_load_metadata(elf, section_header, manifest)) {
return SectionTypeManifest;
} else {
return SectionTypeERROR;
}
}
// Load debug link section
if(strcmp(name, ".gnu_debuglink") == 0) {
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
@@ -634,10 +610,17 @@ ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface)
elf->api_interface = api_interface;
ELFSectionDict_init(elf->sections);
AddressCache_init(elf->trampoline_cache);
elf->init_array_called = false;
return elf;
}
void elf_file_free(ELFFile* elf) {
// furi_check(!elf->init_array_called);
if(elf->init_array_called) {
FURI_LOG_W(TAG, "Init array was called, but fini array wasn't");
elf_file_call_section_list(elf->fini_array, true);
}
// free sections data
{
ELFSectionDict_it_t it;
@@ -692,41 +675,12 @@ bool elf_file_open(ELFFile* elf, const char* path) {
return true;
}
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest) {
bool result = false;
FuriString* name;
name = furi_string_alloc();
FURI_LOG_D(TAG, "Looking for manifest section");
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
furi_string_reset(name);
if(!elf_read_section(elf, section_idx, &section_header, name)) {
break;
}
if(furi_string_cmp(name, ".fapmeta") == 0) {
if(elf_load_metadata(elf, &section_header, manifest)) {
FURI_LOG_D(TAG, "Load manifest done");
result = true;
break;
} else {
break;
}
}
}
furi_string_free(name);
return result;
}
bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manifest) {
bool elf_file_load_section_table(ELFFile* elf) {
SectionType loaded_sections = SectionTypeERROR;
FuriString* name;
name = furi_string_alloc();
FuriString* name = furi_string_alloc();
FURI_LOG_D(TAG, "Scan ELF indexs...");
// TODO: why we start from 1?
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
@@ -738,8 +692,7 @@ bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manif
FURI_LOG_D(
TAG, "Preloading data for section #%d %s", section_idx, furi_string_get_cstr(name));
SectionType section_type =
elf_preload_section(elf, section_idx, &section_header, name, manifest);
SectionType section_type = elf_preload_section(elf, section_idx, &section_header, name);
loaded_sections |= section_type;
if(section_type == SectionTypeERROR) {
@@ -753,14 +706,49 @@ bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manif
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
}
ElfProcessSectionResult elf_process_section(
ELFFile* elf,
const char* name,
ElfProcessSection* process_section,
void* context) {
ElfProcessSectionResult result = ElfProcessSectionResultNotFound;
FuriString* section_name = furi_string_alloc();
Elf32_Shdr section_header;
// find section
// TODO: why we start from 1?
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
furi_string_reset(section_name);
if(!elf_read_section(elf, section_idx, &section_header, section_name)) {
break;
}
if(furi_string_cmp(section_name, name) == 0) {
result = ElfProcessSectionResultCannotProcess;
break;
}
}
if(result != ElfProcessSectionResultNotFound) { //-V547
if(process_section(elf->fd, section_header.sh_offset, section_header.sh_size, context)) {
result = ElfProcessSectionResultSuccess;
} else {
result = ElfProcessSectionResultCannotProcess;
}
}
furi_string_free(section_name);
return result;
}
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
ELFFileLoadStatus status = ELFFileLoadStatusSuccess;
ELFSectionDict_it_t it;
AddressCache_init(elf->relocation_cache);
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) {
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
FURI_LOG_D(TAG, "Relocating section '%s'", itref->key);
if(!elf_relocate_section(elf, &itref->value)) {
@@ -798,19 +786,26 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
return status;
}
void elf_file_pre_run(ELFFile* elf) {
void elf_file_call_init(ELFFile* elf) {
furi_check(!elf->init_array_called);
elf_file_call_section_list(elf->preinit_array, false);
elf_file_call_section_list(elf->init_array, false);
elf->init_array_called = true;
}
int32_t elf_file_run(ELFFile* elf, void* args) {
int32_t result;
result = ((int32_t(*)(void*))elf->entry)(args);
return result;
bool elf_file_is_init_complete(ELFFile* elf) {
return elf->init_array_called;
}
void elf_file_post_run(ELFFile* elf) {
void* elf_file_get_entry_point(ELFFile* elf) {
furi_check(elf->init_array_called);
return (void*)elf->entry;
}
void elf_file_call_fini(ELFFile* elf) {
furi_check(elf->init_array_called);
elf_file_call_section_list(elf->fini_array, true);
elf->init_array_called = false;
}
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) {
@@ -835,8 +830,9 @@ void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) {
const void* data_ptr = itref->value.data;
if(data_ptr) {
debug_info->mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
debug_info->mmap_entries[mmap_entry_idx].name = itref->key;
ELFMemoryMapEntry* entry = &debug_info->mmap_entries[mmap_entry_idx];
entry->address = (uint32_t)data_ptr;
entry->name = itref->key;
mmap_entry_idx++;
}
}
+40 -16
View File
@@ -37,6 +37,14 @@ typedef enum {
ELFFileLoadStatusMissingImports,
} ELFFileLoadStatus;
typedef enum {
ElfProcessSectionResultNotFound,
ElfProcessSectionResultCannotProcess,
ElfProcessSectionResultSuccess,
} ElfProcessSectionResult;
typedef bool(ElfProcessSection)(File* file, size_t offset, size_t size, void* context);
/**
* @brief Allocate ELFFile instance
* @param storage
@@ -59,21 +67,12 @@ void elf_file_free(ELFFile* elf_file);
*/
bool elf_file_open(ELFFile* elf_file, const char* path);
/**
* @brief Load ELF file manifest
* @param elf
* @param manifest
* @return bool
*/
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest);
/**
* @brief Load ELF file section table (load stage #1)
* @param elf_file
* @param manifest
* @return bool
*/
bool elf_file_load_section_table(ELFFile* elf_file, FlipperApplicationManifest* manifest);
bool elf_file_load_section_table(ELFFile* elf_file);
/**
* @brief Load and relocate ELF file sections (load stage #2)
@@ -83,24 +82,34 @@ bool elf_file_load_section_table(ELFFile* elf_file, FlipperApplicationManifest*
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file);
/**
* @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3)
* @brief Execute ELF file pre-run stage,
* call static constructors for example (load stage #3)
* Must be done before invoking any code from the ELF file
* @param elf
*/
void elf_file_pre_run(ELFFile* elf);
void elf_file_call_init(ELFFile* elf);
/**
* @brief Run ELF file (load stage #4)
* @brief Check if ELF file pre-run stage was executed and its code is runnable
* @param elf
*/
bool elf_file_is_init_complete(ELFFile* elf);
/**
* @brief Get actual entry point for ELF file
* @param elf_file
* @param args
* @return int32_t
*/
int32_t elf_file_run(ELFFile* elf_file, void* args);
void* elf_file_get_entry_point(ELFFile* elf_file);
/**
* @brief Execute ELF file post-run stage, call static destructors for example (load stage #5)
* @brief Execute ELF file post-run stage,
* call static destructors for example (load stage #5)
* Must be done if any code from the ELF file was executed
* @param elf
*/
void elf_file_post_run(ELFFile* elf);
void elf_file_call_fini(ELFFile* elf);
/**
* @brief Get ELF file API interface
@@ -122,6 +131,21 @@ void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info);
*/
void elf_file_clear_debug_info(ELFDebugInfo* debug_info);
/**
* @brief Process ELF file section
*
* @param elf_file
* @param name
* @param process_section
* @param context
* @return ElfProcessSectionResult
*/
ElfProcessSectionResult elf_process_section(
ELFFile* elf_file,
const char* name,
ElfProcessSection* process_section,
void* context);
#ifdef __cplusplus
}
#endif
+2
View File
@@ -45,6 +45,8 @@ struct ELFFile {
ELFSection* preinit_array;
ELFSection* init_array;
ELFSection* fini_array;
bool init_array_called;
};
#ifdef __cplusplus
+162 -21
View File
@@ -1,27 +1,67 @@
#include "flipper_application.h"
#include "elf/elf_file.h"
#include <notification/notification_messages.h>
#include "application_assets.h"
#define TAG "fapp"
#include <m-list.h>
#define TAG "Fap"
struct FlipperApplication {
ELFDebugInfo state;
FlipperApplicationManifest manifest;
ELFFile* elf;
FuriThread* thread;
void* ep_thread_args;
};
/* For debugger access to app state */
FlipperApplication* last_loaded_app = NULL;
/********************** Debugger access to loader state **********************/
LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST);
FlipperApplicationList_t flipper_application_loaded_app_list = {0};
static bool flipper_application_loaded_app_list_initialized = false;
static void flipper_application_list_add_app(const FlipperApplication* app) {
furi_assert(app);
if(!flipper_application_loaded_app_list_initialized) {
FlipperApplicationList_init(flipper_application_loaded_app_list);
flipper_application_loaded_app_list_initialized = true;
}
FlipperApplicationList_push_back(flipper_application_loaded_app_list, app);
}
static void flipper_application_list_remove_app(const FlipperApplication* app) {
furi_assert(flipper_application_loaded_app_list_initialized);
furi_assert(app);
FlipperApplicationList_it_t it;
for(FlipperApplicationList_it(it, flipper_application_loaded_app_list);
!FlipperApplicationList_end_p(it);
FlipperApplicationList_next(it)) {
if(*FlipperApplicationList_ref(it) == app) {
FlipperApplicationList_remove(flipper_application_loaded_app_list, it);
break;
}
}
}
/*****************************************************************************/
FlipperApplication*
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
FlipperApplication* app = malloc(sizeof(FlipperApplication));
app->elf = elf_file_alloc(storage, api_interface);
app->thread = NULL;
app->ep_thread_args = NULL;
return app;
}
bool flipper_application_is_plugin(FlipperApplication* app) {
return app->manifest.stack_size == 0;
}
void flipper_application_free(FlipperApplication* app) {
furi_assert(app);
@@ -30,9 +70,16 @@ void flipper_application_free(FlipperApplication* app) {
furi_thread_free(app->thread);
}
last_loaded_app = NULL;
if(app->state.entry) {
flipper_application_list_remove_app(app);
}
elf_file_clear_debug_info(&app->state);
if(elf_file_is_init_complete(app->elf)) {
elf_file_call_fini(app->elf);
}
elf_file_free(app->elf);
free(app);
}
@@ -43,6 +90,10 @@ static FlipperApplicationPreloadStatus
return FlipperApplicationPreloadStatusInvalidManifest;
}
if(!flipper_application_manifest_is_target_compatible(&app->manifest)) {
return FlipperApplicationPreloadStatusTargetMismatch;
}
if(!flipper_application_manifest_is_compatible(
&app->manifest, elf_file_get_api_interface(app->elf))) {
return FlipperApplicationPreloadStatusApiMismatch;
@@ -51,24 +102,83 @@ static FlipperApplicationPreloadStatus
return FlipperApplicationPreloadStatusSuccess;
}
/* Parse headers, load manifest */
FlipperApplicationPreloadStatus
flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
if(!elf_file_open(app->elf, path) || !elf_file_load_manifest(app->elf, &app->manifest)) {
static bool flipper_application_process_manifest_section(
File* file,
size_t offset,
size_t size,
void* context) {
FlipperApplicationManifest* manifest = context;
if(size < sizeof(FlipperApplicationManifest)) {
return false;
}
if(manifest == NULL) {
return true;
}
return storage_file_seek(file, offset, true) &&
storage_file_read(file, manifest, size) == size;
}
// we can't use const char* as context because we will lose the const qualifier
typedef struct {
const char* path;
} FlipperApplicationPreloadAssetsContext;
static bool flipper_application_process_assets_section(
File* file,
size_t offset,
size_t size,
void* context) {
FlipperApplicationPreloadAssetsContext* preload_context = context;
return flipper_application_assets_load(file, preload_context->path, offset, size);
}
static FlipperApplicationPreloadStatus
flipper_application_load(FlipperApplication* app, const char* path, bool load_full) {
if(!elf_file_open(app->elf, path)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
// if we are loading full file
if(load_full) {
// load section table
if(!elf_file_load_section_table(app->elf)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
// load assets section
FlipperApplicationPreloadAssetsContext preload_context = {.path = path};
if(elf_process_section(
app->elf,
".fapassets",
flipper_application_process_assets_section,
&preload_context) == ElfProcessSectionResultCannotProcess) {
return FlipperApplicationPreloadStatusInvalidFile;
}
}
// load manifest section
if(elf_process_section(
app->elf, ".fapmeta", flipper_application_process_manifest_section, &app->manifest) !=
ElfProcessSectionResultSuccess) {
return FlipperApplicationPreloadStatusInvalidFile;
}
return flipper_application_validate_manifest(app);
}
/* Parse headers, load manifest */
FlipperApplicationPreloadStatus
flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
return flipper_application_load(app, path, false);
}
/* Parse headers, load full file */
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path) {
if(!elf_file_open(app->elf, path) || !elf_file_load_section_table(app->elf, &app->manifest)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
return flipper_application_validate_manifest(app);
return flipper_application_load(app, path, true);
}
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
@@ -76,12 +186,12 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic
}
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
last_loaded_app = app;
ELFFileLoadStatus status = elf_file_load_sections(app->elf);
switch(status) {
case ELFFileLoadStatusSuccess:
elf_file_init_debug_info(app->elf, &app->state);
flipper_application_list_add_app(app);
return FlipperApplicationLoadStatusSuccess;
case ELFFileLoadStatusNoFreeMemory:
return FlipperApplicationLoadStatusNoFreeMemory;
@@ -93,9 +203,15 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio
}
static int32_t flipper_application_thread(void* context) {
elf_file_pre_run(last_loaded_app->elf);
int32_t result = elf_file_run(last_loaded_app->elf, context);
elf_file_post_run(last_loaded_app->elf);
furi_assert(context);
FlipperApplication* app = (FlipperApplication*)context;
elf_file_call_init(app->elf);
FlipperApplicationEntryPoint entry_point = elf_file_get_entry_point(app->elf);
int32_t ret_code = entry_point(app->ep_thread_args);
elf_file_call_fini(app->elf);
// wait until all notifications from RAM are completed
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
@@ -105,17 +221,17 @@ static int32_t flipper_application_thread(void* context) {
notification_message_block(notifications, &sequence_empty);
furi_record_close(RECORD_NOTIFICATION);
return result;
return ret_code;
}
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
furi_check(app->thread == NULL);
furi_check(!flipper_application_is_plugin(app));
app->ep_thread_args = args;
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
furi_check(manifest->stack_size > 0);
app->thread = furi_thread_alloc_ex(
manifest->name, manifest->stack_size, flipper_application_thread, args);
manifest->name, manifest->stack_size, flipper_application_thread, app);
return app->thread;
}
@@ -149,3 +265,28 @@ const char* flipper_application_load_status_to_string(FlipperApplicationLoadStat
}
return load_status_strings[status];
}
const FlipperAppPluginDescriptor*
flipper_application_plugin_get_descriptor(FlipperApplication* app) {
if(!flipper_application_is_plugin(app)) {
return NULL;
}
if(!elf_file_is_init_complete(app->elf)) {
elf_file_call_init(app->elf);
}
typedef const FlipperAppPluginDescriptor* (*get_lib_descriptor_t)(void);
get_lib_descriptor_t lib_ep = elf_file_get_entry_point(app->elf);
furi_check(lib_ep);
const FlipperAppPluginDescriptor* lib_descriptor = lib_ep();
FURI_LOG_D(
TAG,
"Library for %s, API v. %lu loaded",
lib_descriptor->appid,
lib_descriptor->ep_api_version);
return lib_descriptor;
}
+35 -1
View File
@@ -115,6 +115,40 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio
*/
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args);
/**
* @brief Check if application is a plugin (not a runnable standalone app)
* @param app Application pointer
* @return true if application is a plugin, false otherwise
*/
bool flipper_application_is_plugin(FlipperApplication* app);
/**
* @brief Entry point prototype for standalone applications
*/
typedef int32_t (*FlipperApplicationEntryPoint)(void*);
/**
* @brief An object that describes a plugin - must be returned by plugin's entry point
*/
typedef struct {
const char* appid;
const uint32_t ep_api_version;
const void* entry_point;
} FlipperAppPluginDescriptor;
/**
* @brief Entry point prototype for plugins
*/
typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)(void);
/**
* @brief Get plugin descriptor for preloaded plugin
* @param app Application pointer
* @return Pointer to plugin descriptor
*/
const FlipperAppPluginDescriptor*
flipper_application_plugin_get_descriptor(FlipperApplication* app);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,52 @@
#include "composite_resolver.h"
#include <m-list.h>
#include <m-algo.h>
LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST)
#define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST)
struct CompositeApiResolver {
ElfApiInterface api_interface;
ElfApiInterfaceList_t interfaces;
};
static bool composite_api_resolver_callback(
const ElfApiInterface* interface,
const char* name,
Elf32_Addr* address) {
CompositeApiResolver* resolver = (CompositeApiResolver*)interface;
for
M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) {
if((*interface)->resolver_callback(*interface, name, address)) {
return true;
}
}
return false;
}
CompositeApiResolver* composite_api_resolver_alloc() {
CompositeApiResolver* resolver = malloc(sizeof(CompositeApiResolver));
resolver->api_interface.api_version_major = 0;
resolver->api_interface.api_version_minor = 0;
resolver->api_interface.resolver_callback = &composite_api_resolver_callback;
ElfApiInterfaceList_init(resolver->interfaces);
return resolver;
}
void composite_api_resolver_free(CompositeApiResolver* resolver) {
ElfApiInterfaceList_clear(resolver->interfaces);
free(resolver);
}
void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface) {
if(ElfApiInterfaceList_empty_p(resolver->interfaces)) {
resolver->api_interface.api_version_major = interface->api_version_major;
resolver->api_interface.api_version_minor = interface->api_version_minor;
}
ElfApiInterfaceList_push_back(resolver->interfaces, interface);
}
const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver) {
return &resolver->api_interface;
}
@@ -0,0 +1,46 @@
#pragma once
#include <flipper_application/elf/elf_api_interface.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Composite API resolver
* Resolves API interface by calling all resolvers in order
* Uses API version from first resolver
* Note: when using hashtable resolvers, collisions between tables are not detected
* Can be cast to ElfApiInterface*
*/
typedef struct CompositeApiResolver CompositeApiResolver;
/**
* @brief Allocate composite API resolver
* @return CompositeApiResolver* instance
*/
CompositeApiResolver* composite_api_resolver_alloc();
/**
* @brief Free composite API resolver
* @param resolver Instance
*/
void composite_api_resolver_free(CompositeApiResolver* resolver);
/**
* @brief Add API resolver to composite resolver
* @param resolver Instance
* @param interface API resolver
*/
void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface);
/**
* @brief Get API interface from composite resolver
* @param resolver Instance
* @return API interface
*/
const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,153 @@
#include "plugin_manager.h"
#include <loader/firmware_api/firmware_api.h>
#include <storage/storage.h>
#include <toolbox/path.h>
#include <m-array.h>
#include <m-algo.h>
#include <furi.h>
#define TAG "libmgr"
ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST)
#define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST)
struct PluginManager {
const char* application_id;
uint32_t api_version;
Storage* storage;
FlipperApplicationList_t libs;
const ElfApiInterface* api_interface;
};
PluginManager* plugin_manager_alloc(
const char* application_id,
uint32_t api_version,
const ElfApiInterface* api_interface) {
PluginManager* manager = malloc(sizeof(PluginManager));
manager->application_id = application_id;
manager->api_version = api_version;
manager->api_interface = api_interface ? api_interface : firmware_api_interface;
manager->storage = furi_record_open(RECORD_STORAGE);
FlipperApplicationList_init(manager->libs);
return manager;
}
void plugin_manager_free(PluginManager* manager) {
for
M_EACH(loaded_lib, manager->libs, FlipperApplicationList_t) {
flipper_application_free(*loaded_lib);
}
FlipperApplicationList_clear(manager->libs);
furi_record_close(RECORD_STORAGE);
free(manager);
}
PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path) {
FlipperApplication* lib = flipper_application_alloc(manager->storage, manager->api_interface);
PluginManagerError error = PluginManagerErrorNone;
do {
FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path);
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
FURI_LOG_E(TAG, "Failed to preload %s", path);
error = PluginManagerErrorLoaderError;
break;
}
if(!flipper_application_is_plugin(lib)) {
FURI_LOG_E(TAG, "Not a plugin %s", path);
error = PluginManagerErrorLoaderError;
break;
}
FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib);
if(load_status != FlipperApplicationLoadStatusSuccess) {
FURI_LOG_E(TAG, "Failed to load module_demo_plugin1.fal");
break;
}
const FlipperAppPluginDescriptor* app_descriptor =
flipper_application_plugin_get_descriptor(lib);
if(!app_descriptor) {
FURI_LOG_E(TAG, "Failed to get descriptor %s", path);
error = PluginManagerErrorLoaderError;
break;
}
if(strcmp(app_descriptor->appid, manager->application_id) != 0) {
FURI_LOG_E(TAG, "Application id mismatch %s", path);
error = PluginManagerErrorApplicationIdMismatch;
break;
}
if(app_descriptor->ep_api_version != manager->api_version) {
FURI_LOG_E(TAG, "API version mismatch %s", path);
error = PluginManagerErrorAPIVersionMismatch;
break;
}
FlipperApplicationList_push_back(manager->libs, lib);
} while(false);
if(error != PluginManagerErrorNone) {
flipper_application_free(lib);
}
return error;
}
PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) {
File* directory = storage_file_alloc(manager->storage);
char file_name_buffer[256];
FuriString* file_name = furi_string_alloc();
do {
if(!storage_dir_open(directory, path)) {
FURI_LOG_E(TAG, "Failed to open directory %s", path);
break;
}
while(true) {
if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) {
break;
}
furi_string_set(file_name, file_name_buffer);
if(!furi_string_end_with_str(file_name, ".fal")) {
continue;
}
path_concat(path, file_name_buffer, file_name);
FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(file_name));
PluginManagerError error =
plugin_manager_load_single(manager, furi_string_get_cstr(file_name));
if(error != PluginManagerErrorNone) {
FURI_LOG_E(TAG, "Failed to load %s", furi_string_get_cstr(file_name));
break;
}
}
} while(false);
storage_dir_close(directory);
storage_file_free(directory);
furi_string_free(file_name);
return PluginManagerErrorNone;
}
uint32_t plugin_manager_get_count(PluginManager* manager) {
return FlipperApplicationList_size(manager->libs);
}
const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index) {
FlipperApplication* app = *FlipperApplicationList_get(manager->libs, index);
return flipper_application_plugin_get_descriptor(app);
}
const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index) {
const FlipperAppPluginDescriptor* lib_descr = plugin_manager_get(manager, index);
furi_check(lib_descr);
return lib_descr->entry_point;
}
@@ -0,0 +1,82 @@
#pragma once
#include <flipper_application/flipper_application.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Object that manages plugins for an application
* Implements mass loading of plugins and provides access to their descriptors
*/
typedef struct PluginManager PluginManager;
typedef enum {
PluginManagerErrorNone = 0,
PluginManagerErrorLoaderError,
PluginManagerErrorApplicationIdMismatch,
PluginManagerErrorAPIVersionMismatch,
} PluginManagerError;
/**
* @brief Allocates new PluginManager
* @param application_id Application ID filter - only plugins with matching ID will be loaded
* @param api_version Application API version filter - only plugins with matching API version
* @param api_interface Application API interface - used to resolve plugins' API imports
* If plugin uses private application's API, use CompoundApiInterface
* @return new PluginManager instance
*/
PluginManager* plugin_manager_alloc(
const char* application_id,
uint32_t api_version,
const ElfApiInterface* api_interface);
/**
* @brief Frees PluginManager
* @param manager PluginManager instance
*/
void plugin_manager_free(PluginManager* manager);
/**
* @brief Loads single plugin by full path
* @param manager PluginManager instance
* @param path Path to plugin
* @return Error code
*/
PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path);
/**
* @brief Loads all plugins from specified directory
* @param manager PluginManager instance
* @param path Path to directory
* @return Error code
*/
PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path);
/**
* @brief Returns number of loaded plugins
* @param manager PluginManager instance
* @return Number of loaded plugins
*/
uint32_t plugin_manager_get_count(PluginManager* manager);
/**
* @brief Returns plugin descriptor by index
* @param manager PluginManager instance
* @param index Plugin index
* @return Plugin descriptor
*/
const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index);
/**
* @brief Returns plugin entry point by index
* @param manager PluginManager instance
* @param index Plugin index
* @return Plugin entry point
*/
const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index);
#ifdef __cplusplus
}
#endif
+6
View File
@@ -91,6 +91,12 @@ bool flipper_format_file_open_always(FlipperFormat* flipper_format, const char*
return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);
}
bool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path) {
furi_assert(flipper_format);
return buffered_file_stream_open(
flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);
}
bool flipper_format_file_open_new(FlipperFormat* flipper_format, const char* path) {
furi_assert(flipper_format);
return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_NEW);
+10 -1
View File
@@ -131,7 +131,7 @@ bool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char
/**
* Open existing file, buffered mode.
* Use only if FlipperFormat allocated as a file.
* Use only if FlipperFormat allocated as a buffered file.
* @param flipper_format Pointer to a FlipperFormat instance
* @param path File path
* @return True on success
@@ -156,6 +156,15 @@ bool flipper_format_file_open_append(FlipperFormat* flipper_format, const char*
*/
bool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* path);
/**
* Open file. Creates a new file, or deletes the contents of the file if it already exists, buffered mode.
* Use only if FlipperFormat allocated as a buffered file.
* @param flipper_format Pointer to a FlipperFormat instance
* @param path File path
* @return True on success
*/
bool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path);
/**
* Open file. Creates a new file, fails if file already exists.
* Use only if FlipperFormat allocated as a file.
+1
Submodule lib/heatshrink added at 7398ccc916
-20
View File
@@ -1,20 +0,0 @@
#ifndef HEATSHRINK_H
#define HEATSHRINK_H
#define HEATSHRINK_AUTHOR "Scott Vokes <vokes.s@gmail.com>"
#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink"
/* Version 0.4.1 */
#define HEATSHRINK_VERSION_MAJOR 0
#define HEATSHRINK_VERSION_MINOR 4
#define HEATSHRINK_VERSION_PATCH 1
#define HEATSHRINK_MIN_WINDOW_BITS 4
#define HEATSHRINK_MAX_WINDOW_BITS 15
#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3
#define HEATSHRINK_LITERAL_MARKER 0x01
#define HEATSHRINK_BACKREF_MARKER 0x00
#endif
-28
View File
@@ -1,28 +0,0 @@
#ifndef HEATSHRINK_CONFIG_H
#define HEATSHRINK_CONFIG_H
#include <furi.h>
/* Should functionality assuming dynamic allocation be used? */
#ifndef HEATSHRINK_DYNAMIC_ALLOC
#define HEATSHRINK_DYNAMIC_ALLOC 1
#endif
#if HEATSHRINK_DYNAMIC_ALLOC
/* Optional replacement of malloc/free */
#define HEATSHRINK_MALLOC(SZ) malloc(SZ)
#define HEATSHRINK_FREE(P, SZ) free(P)
#else
/* Required parameters for static configuration */
#define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 1024
#define HEATSHRINK_STATIC_WINDOW_BITS 8
#define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
#endif
/* Turn on logging for debugging. */
#define HEATSHRINK_DEBUGGING_LOGS 0
/* Use indexing for faster compression. (This requires additional space.) */
#define HEATSHRINK_USE_INDEX 1
#endif
-364
View File
@@ -1,364 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "heatshrink_decoder.h"
/* States for the polling state machine. */
typedef enum {
HSDS_TAG_BIT, /* tag bit */
HSDS_YIELD_LITERAL, /* ready to yield literal byte */
HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */
HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */
HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */
HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */
HSDS_YIELD_BACKREF, /* ready to yield back-reference */
} HSD_state;
#if HEATSHRINK_DEBUGGING_LOGS
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define ASSERT(X) assert(X)
static const char *state_names[] = {
"tag_bit",
"yield_literal",
"backref_index_msb",
"backref_index_lsb",
"backref_count_msb",
"backref_count_lsb",
"yield_backref",
};
#else
#define LOG(...) /* no-op */
#define ASSERT(X) /* no-op */
#endif
typedef struct {
uint8_t *buf; /* output buffer */
size_t buf_size; /* buffer size */
size_t *output_size; /* bytes pushed to buffer, so far */
} output_info;
#define NO_BITS ((uint16_t)-1)
/* Forward references. */
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count);
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte);
#if HEATSHRINK_DYNAMIC_ALLOC
heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer,
uint16_t input_buffer_size,
uint8_t window_sz2,
uint8_t lookahead_sz2) {
if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
(window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
(input_buffer_size == 0) ||
(lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
(lookahead_sz2 >= window_sz2)) {
return NULL;
}
size_t sz = sizeof(heatshrink_decoder);
heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz);
if (hsd == NULL) { return NULL; }
hsd->input_buffer_size = input_buffer_size;
hsd->window_sz2 = window_sz2;
hsd->lookahead_sz2 = lookahead_sz2;
hsd->buffers = buffer;
heatshrink_decoder_reset(hsd);
LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n",
sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size);
return hsd;
}
void heatshrink_decoder_free(heatshrink_decoder *hsd) {
size_t sz = sizeof(heatshrink_decoder);
HEATSHRINK_FREE(hsd, sz);
(void)sz; /* may not be used by free */
}
#endif
void heatshrink_decoder_reset(heatshrink_decoder *hsd) {
hsd->state = HSDS_TAG_BIT;
hsd->input_size = 0;
hsd->input_index = 0;
hsd->bit_index = 0x00;
hsd->current_byte = 0x00;
hsd->output_count = 0;
hsd->output_index = 0;
hsd->head_index = 0;
}
/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
uint8_t *in_buf, size_t size, size_t *input_size) {
if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) {
return HSDR_SINK_ERROR_NULL;
}
size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size;
if (rem == 0) {
*input_size = 0;
return HSDR_SINK_FULL;
}
size = rem < size ? rem : size;
LOG("-- sinking %zd bytes\n", size);
/* copy into input buffer (at head of buffers) */
memcpy(&hsd->buffers[hsd->input_size], in_buf, size);
hsd->input_size += size;
*input_size = size;
return HSDR_SINK_OK;
}
/*****************
* Decompression *
*****************/
#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD))
#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD))
// States
static HSD_state st_tag_bit(heatshrink_decoder *hsd);
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
output_info *oi);
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd);
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd);
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd);
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd);
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
output_info *oi);
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) {
return HSDR_POLL_ERROR_NULL;
}
*output_size = 0;
output_info oi;
oi.buf = out_buf;
oi.buf_size = out_buf_size;
oi.output_size = output_size;
while (1) {
LOG("-- poll, state is %d (%s), input_size %d\n",
hsd->state, state_names[hsd->state], hsd->input_size);
uint8_t in_state = hsd->state;
switch (in_state) {
case HSDS_TAG_BIT:
hsd->state = st_tag_bit(hsd);
break;
case HSDS_YIELD_LITERAL:
hsd->state = st_yield_literal(hsd, &oi);
break;
case HSDS_BACKREF_INDEX_MSB:
hsd->state = st_backref_index_msb(hsd);
break;
case HSDS_BACKREF_INDEX_LSB:
hsd->state = st_backref_index_lsb(hsd);
break;
case HSDS_BACKREF_COUNT_MSB:
hsd->state = st_backref_count_msb(hsd);
break;
case HSDS_BACKREF_COUNT_LSB:
hsd->state = st_backref_count_lsb(hsd);
break;
case HSDS_YIELD_BACKREF:
hsd->state = st_yield_backref(hsd, &oi);
break;
default:
return HSDR_POLL_ERROR_UNKNOWN;
}
/* If the current state cannot advance, check if input or output
* buffer are exhausted. */
if (hsd->state == in_state) {
if (*output_size == out_buf_size) { return HSDR_POLL_MORE; }
return HSDR_POLL_EMPTY;
}
}
}
static HSD_state st_tag_bit(heatshrink_decoder *hsd) {
uint32_t bits = get_bits(hsd, 1); // get tag bit
if (bits == NO_BITS) {
return HSDS_TAG_BIT;
} else if (bits) {
return HSDS_YIELD_LITERAL;
} else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) {
return HSDS_BACKREF_INDEX_MSB;
} else {
hsd->output_index = 0;
return HSDS_BACKREF_INDEX_LSB;
}
}
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
output_info *oi) {
/* Emit a repeated section from the window buffer, and add it (again)
* to the window buffer. (Note that the repetition can include
* itself.)*/
if (*oi->output_size < oi->buf_size) {
uint16_t byte = get_bits(hsd, 8);
if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
uint8_t c = byte & 0xFF;
LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.');
buf[hsd->head_index++ & mask] = c;
push_byte(hsd, oi, c);
return HSDS_TAG_BIT;
} else {
return HSDS_YIELD_LITERAL;
}
}
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) {
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
ASSERT(bit_ct > 8);
uint16_t bits = get_bits(hsd, bit_ct - 8);
LOG("-- backref index (msb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; }
hsd->output_index = bits << 8;
return HSDS_BACKREF_INDEX_LSB;
}
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) {
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8);
LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; }
hsd->output_index |= bits;
hsd->output_index++;
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
hsd->output_count = 0;
return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB;
}
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) {
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
ASSERT(br_bit_ct > 8);
uint16_t bits = get_bits(hsd, br_bit_ct - 8);
LOG("-- backref count (msb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; }
hsd->output_count = bits << 8;
return HSDS_BACKREF_COUNT_LSB;
}
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) {
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8);
LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits);
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; }
hsd->output_count |= bits;
hsd->output_count++;
return HSDS_YIELD_BACKREF;
}
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
output_info *oi) {
size_t count = oi->buf_size - *oi->output_size;
if (count > 0) {
size_t i = 0;
if (hsd->output_count < count) count = hsd->output_count;
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
uint16_t neg_offset = hsd->output_index;
LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset);
ASSERT(neg_offset <= mask + 1);
ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd)));
for (i=0; i<count; i++) {
uint8_t c = buf[(hsd->head_index - neg_offset) & mask];
push_byte(hsd, oi, c);
buf[hsd->head_index & mask] = c;
hsd->head_index++;
LOG(" -- ++ 0x%02x\n", c);
}
hsd->output_count -= count;
if (hsd->output_count == 0) { return HSDS_TAG_BIT; }
}
return HSDS_YIELD_BACKREF;
}
/* Get the next COUNT bits from the input buffer, saving incremental progress.
* Returns NO_BITS on end of input, or if more than 15 bits are requested. */
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) {
uint16_t accumulator = 0;
int i = 0;
if (count > 15) { return NO_BITS; }
LOG("-- popping %u bit(s)\n", count);
/* If we aren't able to get COUNT bits, suspend immediately, because we
* don't track how many bits of COUNT we've accumulated before suspend. */
if (hsd->input_size == 0) {
if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; }
}
for (i = 0; i < count; i++) {
if (hsd->bit_index == 0x00) {
if (hsd->input_size == 0) {
LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n",
accumulator, accumulator);
return NO_BITS;
}
hsd->current_byte = hsd->buffers[hsd->input_index++];
LOG(" -- pulled byte 0x%02x\n", hsd->current_byte);
if (hsd->input_index == hsd->input_size) {
hsd->input_index = 0; /* input is exhausted */
hsd->input_size = 0;
}
hsd->bit_index = 0x80;
}
accumulator <<= 1;
if (hsd->current_byte & hsd->bit_index) {
accumulator |= 0x01;
if (0) {
LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n",
accumulator, hsd->bit_index);
}
} else {
if (0) {
LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n",
accumulator, hsd->bit_index);
}
}
hsd->bit_index >>= 1;
}
if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); }
return accumulator;
}
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) {
if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; }
switch (hsd->state) {
case HSDS_TAG_BIT:
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
/* If we want to finish with no input, but are in these states, it's
* because the 0-bit padding to the last byte looks like a backref
* marker bit followed by all 0s for index and count bits. */
case HSDS_BACKREF_INDEX_LSB:
case HSDS_BACKREF_INDEX_MSB:
case HSDS_BACKREF_COUNT_LSB:
case HSDS_BACKREF_COUNT_MSB:
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
/* If the output stream is padded with 0xFFs (possibly due to being in
* flash memory), also explicitly check the input size rather than
* uselessly returning MORE but yielding 0 bytes when polling. */
case HSDS_YIELD_LITERAL:
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
default:
return HSDR_FINISH_MORE;
}
}
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) {
LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.');
oi->buf[(*oi->output_size)++] = byte;
(void)hsd;
}
-100
View File
@@ -1,100 +0,0 @@
#ifndef HEATSHRINK_DECODER_H
#define HEATSHRINK_DECODER_H
#include <stdint.h>
#include <stddef.h>
#include "heatshrink_common.h"
#include "heatshrink_config.h"
typedef enum {
HSDR_SINK_OK, /* data sunk, ready to poll */
HSDR_SINK_FULL, /* out of space in internal buffer */
HSDR_SINK_ERROR_NULL=-1, /* NULL argument */
} HSD_sink_res;
typedef enum {
HSDR_POLL_EMPTY, /* input exhausted */
HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */
HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */
HSDR_POLL_ERROR_UNKNOWN=-2,
} HSD_poll_res;
typedef enum {
HSDR_FINISH_DONE, /* output is done */
HSDR_FINISH_MORE, /* more output remains */
HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */
} HSD_finish_res;
#if HEATSHRINK_DYNAMIC_ALLOC
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \
((BUF)->input_buffer_size)
#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \
((BUF)->window_sz2)
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
((BUF)->lookahead_sz2)
#else
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \
HEATSHRINK_STATIC_INPUT_BUFFER_SIZE
#define HEATSHRINK_DECODER_WINDOW_BITS(_) \
(HEATSHRINK_STATIC_WINDOW_BITS)
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
(HEATSHRINK_STATIC_LOOKAHEAD_BITS)
#endif
typedef struct {
uint16_t input_size; /* bytes in input buffer */
uint16_t input_index; /* offset to next unprocessed input byte */
uint16_t output_count; /* how many bytes to output */
uint16_t output_index; /* index for bytes to output */
uint16_t head_index; /* head of window buffer */
uint8_t state; /* current state machine node */
uint8_t current_byte; /* current byte of input */
uint8_t bit_index; /* current bit index */
#if HEATSHRINK_DYNAMIC_ALLOC
/* Fields that are only used if dynamically allocated. */
uint8_t window_sz2; /* window buffer bits */
uint8_t lookahead_sz2; /* lookahead bits */
uint16_t input_buffer_size; /* input buffer size */
/* Input buffer, then expansion window buffer */
uint8_t* buffers;
#else
/* Input buffer, then expansion window buffer */
uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_))
+ HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)];
#endif
} heatshrink_decoder;
#if HEATSHRINK_DYNAMIC_ALLOC
/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes,
* an expansion buffer size of 2^WINDOW_SZ2, and a lookahead
* size of 2^lookahead_sz2. (The window buffer and lookahead sizes
* must match the settings used when the data was compressed.)
* Returns NULL on error. */
heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer, uint16_t input_buffer_size,
uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2);
/* Free a decoder. */
void heatshrink_decoder_free(heatshrink_decoder *hsd);
#endif
/* Reset a decoder. */
void heatshrink_decoder_reset(heatshrink_decoder *hsd);
/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to
* indicate how many bytes were actually sunk (in case a buffer was filled). */
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
uint8_t *in_buf, size_t size, size_t *input_size);
/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
/* Notify the dencoder that the input stream is finished.
* If the return value is HSDR_FINISH_MORE, there is still more output, so
* call heatshrink_decoder_poll and repeat. */
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd);
#endif
-602
View File
@@ -1,602 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "heatshrink_encoder.h"
typedef enum {
HSES_NOT_FULL, /* input buffer not full enough */
HSES_FILLED, /* buffer is full */
HSES_SEARCH, /* searching for patterns */
HSES_YIELD_TAG_BIT, /* yield tag bit */
HSES_YIELD_LITERAL, /* emit literal byte */
HSES_YIELD_BR_INDEX, /* yielding backref index */
HSES_YIELD_BR_LENGTH, /* yielding backref length */
HSES_SAVE_BACKLOG, /* copying buffer to backlog */
HSES_FLUSH_BITS, /* flush bit buffer */
HSES_DONE, /* done */
} HSE_state;
#if HEATSHRINK_DEBUGGING_LOGS
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define ASSERT(X) assert(X)
static const char *state_names[] = {
"not_full",
"filled",
"search",
"yield_tag_bit",
"yield_literal",
"yield_br_index",
"yield_br_length",
"save_backlog",
"flush_bits",
"done",
};
#else
#define LOG(...) /* no-op */
#define ASSERT(X) /* no-op */
#endif
// Encoder flags
enum {
FLAG_IS_FINISHING = 0x01,
};
typedef struct {
uint8_t *buf; /* output buffer */
size_t buf_size; /* buffer size */
size_t *output_size; /* bytes pushed to buffer, so far */
} output_info;
#define MATCH_NOT_FOUND ((uint16_t)-1)
static uint16_t get_input_offset(heatshrink_encoder *hse);
static uint16_t get_input_buffer_size(heatshrink_encoder *hse);
static uint16_t get_lookahead_size(heatshrink_encoder *hse);
static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag);
static int can_take_byte(output_info *oi);
static int is_finishing(heatshrink_encoder *hse);
static void save_backlog(heatshrink_encoder *hse);
/* Push COUNT (max 8) bits to the output buffer, which has room. */
static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits,
output_info *oi);
static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi);
static void push_literal_byte(heatshrink_encoder *hse, output_info *oi);
#if HEATSHRINK_DYNAMIC_ALLOC
heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2,
uint8_t lookahead_sz2) {
if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
(window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
(lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
(lookahead_sz2 >= window_sz2)) {
return NULL;
}
/* Note: 2 * the window size is used because the buffer needs to fit
* (1 << window_sz2) bytes for the current input, and an additional
* (1 << window_sz2) bytes for the previous buffer of input, which
* will be scanned for useful backreferences. */
size_t buf_sz = (2 << window_sz2);
heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse));
if (hse == NULL) { return NULL; }
hse->window_sz2 = window_sz2;
hse->lookahead_sz2 = lookahead_sz2;
hse->buffer = buffer;
heatshrink_encoder_reset(hse);
#if HEATSHRINK_USE_INDEX
size_t index_sz = buf_sz*sizeof(uint16_t);
hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index));
if (hse->search_index == NULL) {
HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz);
return NULL;
}
hse->search_index->size = index_sz;
#endif
LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n",
buf_sz, get_input_buffer_size(hse));
return hse;
}
void heatshrink_encoder_free(heatshrink_encoder *hse) {
#if HEATSHRINK_USE_INDEX
size_t index_sz = sizeof(struct hs_index) + hse->search_index->size;
HEATSHRINK_FREE(hse->search_index, index_sz);
(void)index_sz;
#endif
HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder));
}
#endif
void heatshrink_encoder_reset(heatshrink_encoder *hse) {
hse->input_size = 0;
hse->state = HSES_NOT_FULL;
hse->match_scan_index = 0;
hse->flags = 0;
hse->bit_index = 0x80;
hse->current_byte = 0x00;
hse->match_length = 0;
hse->outgoing_bits = 0x0000;
hse->outgoing_bits_count = 0;
#ifdef LOOP_DETECT
hse->loop_detect = (uint32_t)-1;
#endif
}
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse,
uint8_t *in_buf, size_t size, size_t *input_size) {
if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) {
return HSER_SINK_ERROR_NULL;
}
/* Sinking more content after saying the content is done, tsk tsk */
if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; }
/* Sinking more content before processing is done */
if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; }
uint16_t write_offset = get_input_offset(hse) + hse->input_size;
uint16_t ibs = get_input_buffer_size(hse);
uint16_t rem = ibs - hse->input_size;
uint16_t cp_sz = rem < size ? rem : size;
memcpy(&hse->buffer[write_offset], in_buf, cp_sz);
*input_size = cp_sz;
hse->input_size += cp_sz;
LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n",
cp_sz, size, write_offset, hse->input_size);
if (cp_sz == rem) {
LOG("-- internal buffer is now full\n");
hse->state = HSES_FILLED;
}
return HSER_SINK_OK;
}
/***************
* Compression *
***************/
static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start,
uint16_t end, const uint16_t maxlen, uint16_t *match_length);
static void do_indexing(heatshrink_encoder *hse);
static HSE_state st_step_search(heatshrink_encoder *hse);
static HSE_state st_yield_tag_bit(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_yield_literal(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_yield_br_index(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_yield_br_length(heatshrink_encoder *hse,
output_info *oi);
static HSE_state st_save_backlog(heatshrink_encoder *hse);
static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse,
output_info *oi);
HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) {
return HSER_POLL_ERROR_NULL;
}
if (out_buf_size == 0) {
LOG("-- MISUSE: output buffer size is 0\n");
return HSER_POLL_ERROR_MISUSE;
}
*output_size = 0;
output_info oi;
oi.buf = out_buf;
oi.buf_size = out_buf_size;
oi.output_size = output_size;
while (1) {
LOG("-- polling, state %u (%s), flags 0x%02x\n",
hse->state, state_names[hse->state], hse->flags);
uint8_t in_state = hse->state;
switch (in_state) {
case HSES_NOT_FULL:
return HSER_POLL_EMPTY;
case HSES_FILLED:
do_indexing(hse);
hse->state = HSES_SEARCH;
break;
case HSES_SEARCH:
hse->state = st_step_search(hse);
break;
case HSES_YIELD_TAG_BIT:
hse->state = st_yield_tag_bit(hse, &oi);
break;
case HSES_YIELD_LITERAL:
hse->state = st_yield_literal(hse, &oi);
break;
case HSES_YIELD_BR_INDEX:
hse->state = st_yield_br_index(hse, &oi);
break;
case HSES_YIELD_BR_LENGTH:
hse->state = st_yield_br_length(hse, &oi);
break;
case HSES_SAVE_BACKLOG:
hse->state = st_save_backlog(hse);
break;
case HSES_FLUSH_BITS:
hse->state = st_flush_bit_buffer(hse, &oi);
/* fall through */
case HSES_DONE:
return HSER_POLL_EMPTY;
default:
LOG("-- bad state %s\n", state_names[hse->state]);
return HSER_POLL_ERROR_MISUSE;
}
if (hse->state == in_state) {
/* Check if output buffer is exhausted. */
if (*output_size == out_buf_size) return HSER_POLL_MORE;
}
}
}
HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) {
if (hse == NULL) { return HSER_FINISH_ERROR_NULL; }
LOG("-- setting is_finishing flag\n");
hse->flags |= FLAG_IS_FINISHING;
if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; }
return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE;
}
static HSE_state st_step_search(heatshrink_encoder *hse) {
uint16_t window_length = get_input_buffer_size(hse);
uint16_t lookahead_sz = get_lookahead_size(hse);
uint16_t msi = hse->match_scan_index;
LOG("## step_search, scan @ +%d (%d/%d), input size %d\n",
msi, hse->input_size + msi, 2*window_length, hse->input_size);
bool fin = is_finishing(hse);
if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) {
/* Current search buffer is exhausted, copy it into the
* backlog and await more input. */
LOG("-- end of search @ %d\n", msi);
return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG;
}
uint16_t input_offset = get_input_offset(hse);
uint16_t end = input_offset + msi;
uint16_t start = end - window_length;
uint16_t max_possible = lookahead_sz;
if (hse->input_size - msi < lookahead_sz) {
max_possible = hse->input_size - msi;
}
uint16_t match_length = 0;
uint16_t match_pos = find_longest_match(hse,
start, end, max_possible, &match_length);
if (match_pos == MATCH_NOT_FOUND) {
LOG("ss Match not found\n");
hse->match_scan_index++;
hse->match_length = 0;
return HSES_YIELD_TAG_BIT;
} else {
LOG("ss Found match of %d bytes at %d\n", match_length, match_pos);
hse->match_pos = match_pos;
hse->match_length = match_length;
ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/);
return HSES_YIELD_TAG_BIT;
}
}
static HSE_state st_yield_tag_bit(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
if (hse->match_length == 0) {
add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER);
return HSES_YIELD_LITERAL;
} else {
add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER);
hse->outgoing_bits = hse->match_pos - 1;
hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse);
return HSES_YIELD_BR_INDEX;
}
} else {
return HSES_YIELD_TAG_BIT; /* output is full, continue */
}
}
static HSE_state st_yield_literal(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
push_literal_byte(hse, oi);
return HSES_SEARCH;
} else {
return HSES_YIELD_LITERAL;
}
}
static HSE_state st_yield_br_index(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
LOG("-- yielding backref index %u\n", hse->match_pos);
if (push_outgoing_bits(hse, oi) > 0) {
return HSES_YIELD_BR_INDEX; /* continue */
} else {
hse->outgoing_bits = hse->match_length - 1;
hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse);
return HSES_YIELD_BR_LENGTH; /* done */
}
} else {
return HSES_YIELD_BR_INDEX; /* continue */
}
}
static HSE_state st_yield_br_length(heatshrink_encoder *hse,
output_info *oi) {
if (can_take_byte(oi)) {
LOG("-- yielding backref length %u\n", hse->match_length);
if (push_outgoing_bits(hse, oi) > 0) {
return HSES_YIELD_BR_LENGTH;
} else {
hse->match_scan_index += hse->match_length;
hse->match_length = 0;
return HSES_SEARCH;
}
} else {
return HSES_YIELD_BR_LENGTH;
}
}
static HSE_state st_save_backlog(heatshrink_encoder *hse) {
LOG("-- saving backlog\n");
save_backlog(hse);
return HSES_NOT_FULL;
}
static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse,
output_info *oi) {
if (hse->bit_index == 0x80) {
LOG("-- done!\n");
return HSES_DONE;
} else if (can_take_byte(oi)) {
LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index);
oi->buf[(*oi->output_size)++] = hse->current_byte;
LOG("-- done!\n");
return HSES_DONE;
} else {
return HSES_FLUSH_BITS;
}
}
static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) {
LOG("-- adding tag bit: %d\n", tag);
push_bits(hse, 1, tag, oi);
}
static uint16_t get_input_offset(heatshrink_encoder *hse) {
return get_input_buffer_size(hse);
}
static uint16_t get_input_buffer_size(heatshrink_encoder *hse) {
return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse));
(void)hse;
}
static uint16_t get_lookahead_size(heatshrink_encoder *hse) {
return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse));
(void)hse;
}
static void do_indexing(heatshrink_encoder *hse) {
#if HEATSHRINK_USE_INDEX
/* Build an index array I that contains flattened linked lists
* for the previous instances of every byte in the buffer.
*
* For example, if buf[200] == 'x', then index[200] will either
* be an offset i such that buf[i] == 'x', or a negative offset
* to indicate end-of-list. This significantly speeds up matching,
* while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM.
*
* Future optimization options:
* 1. Since any negative value represents end-of-list, the other
* 15 bits could be used to improve the index dynamically.
*
* 2. Likewise, the last lookahead_sz bytes of the index will
* not be usable, so temporary data could be stored there to
* dynamically improve the index.
* */
struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse);
int16_t last[256];
memset(last, 0xFF, sizeof(last));
uint8_t * const data = hse->buffer;
int16_t * const index = hsi->index;
const uint16_t input_offset = get_input_offset(hse);
const uint16_t end = input_offset + hse->input_size;
for (uint16_t i=0; i<end; i++) {
uint8_t v = data[i];
int16_t lv = last[v];
index[i] = lv;
last[v] = i;
}
#else
(void)hse;
#endif
}
static int is_finishing(heatshrink_encoder *hse) {
return hse->flags & FLAG_IS_FINISHING;
}
static int can_take_byte(output_info *oi) {
return *oi->output_size < oi->buf_size;
}
/* Return the longest match for the bytes at buf[end:end+maxlen] between
* buf[start] and buf[end-1]. If no match is found, return -1. */
static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start,
uint16_t end, const uint16_t maxlen, uint16_t *match_length) {
LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n",
end, end + maxlen, start, end + maxlen - 1, maxlen);
uint8_t *buf = hse->buffer;
uint16_t match_maxlen = 0;
uint16_t match_index = MATCH_NOT_FOUND;
uint16_t len = 0;
uint8_t * const needlepoint = &buf[end];
#if HEATSHRINK_USE_INDEX
struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse);
int16_t pos = hsi->index[end];
while (pos - (int16_t)start >= 0) {
uint8_t * const pospoint = &buf[pos];
len = 0;
/* Only check matches that will potentially beat the current maxlen.
* This is redundant with the index if match_maxlen is 0, but the
* added branch overhead to check if it == 0 seems to be worse. */
if (pospoint[match_maxlen] != needlepoint[match_maxlen]) {
pos = hsi->index[pos];
continue;
}
for (len = 1; len < maxlen; len++) {
if (pospoint[len] != needlepoint[len]) break;
}
if (len > match_maxlen) {
match_maxlen = len;
match_index = pos;
if (len == maxlen) { break; } /* won't find better */
}
pos = hsi->index[pos];
}
#else
for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) {
uint8_t * const pospoint = &buf[pos];
if ((pospoint[match_maxlen] == needlepoint[match_maxlen])
&& (*pospoint == *needlepoint)) {
for (len=1; len<maxlen; len++) {
if (0) {
LOG(" --> cmp buf[%d] == 0x%02x against %02x (start %u)\n",
pos + len, pospoint[len], needlepoint[len], start);
}
if (pospoint[len] != needlepoint[len]) { break; }
}
if (len > match_maxlen) {
match_maxlen = len;
match_index = pos;
if (len == maxlen) { break; } /* don't keep searching */
}
}
}
#endif
const size_t break_even_point =
(1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) +
HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse));
/* Instead of comparing break_even_point against 8*match_maxlen,
* compare match_maxlen against break_even_point/8 to avoid
* overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and
* 3, respectively, break_even_point/8 will always be at least 1. */
if (match_maxlen > (break_even_point / 8)) {
LOG("-- best match: %u bytes at -%u\n",
match_maxlen, end - match_index);
*match_length = match_maxlen;
return end - match_index;
}
LOG("-- none found\n");
return MATCH_NOT_FOUND;
}
static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) {
uint8_t count = 0;
uint8_t bits = 0;
if (hse->outgoing_bits_count > 8) {
count = 8;
bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8);
} else {
count = hse->outgoing_bits_count;
bits = hse->outgoing_bits;
}
if (count > 0) {
LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits);
push_bits(hse, count, bits, oi);
hse->outgoing_bits_count -= count;
}
return count;
}
/* Push COUNT (max 8) bits to the output buffer, which has room.
* Bytes are set from the lowest bits, up. */
static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits,
output_info *oi) {
ASSERT(count <= 8);
LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits);
/* If adding a whole byte and at the start of a new output byte,
* just push it through whole and skip the bit IO loop. */
if (count == 8 && hse->bit_index == 0x80) {
oi->buf[(*oi->output_size)++] = bits;
} else {
for (int i=count - 1; i>=0; i--) {
bool bit = bits & (1 << i);
if (bit) { hse->current_byte |= hse->bit_index; }
if (0) {
LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n",
bit ? 1 : 0, hse->bit_index, hse->current_byte);
}
hse->bit_index >>= 1;
if (hse->bit_index == 0x00) {
hse->bit_index = 0x80;
LOG(" > pushing byte 0x%02x\n", hse->current_byte);
oi->buf[(*oi->output_size)++] = hse->current_byte;
hse->current_byte = 0x00;
}
}
}
}
static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) {
uint16_t processed_offset = hse->match_scan_index - 1;
uint16_t input_offset = get_input_offset(hse) + processed_offset;
uint8_t c = hse->buffer[input_offset];
LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n",
c, isprint(c) ? c : '.', input_offset);
push_bits(hse, 8, c, oi);
}
static void save_backlog(heatshrink_encoder *hse) {
size_t input_buf_sz = get_input_buffer_size(hse);
uint16_t msi = hse->match_scan_index;
/* Copy processed data to beginning of buffer, so it can be
* used for future matches. Don't bother checking whether the
* input is less than the maximum size, because if it isn't,
* we're done anyway. */
uint16_t rem = input_buf_sz - msi; // unprocessed bytes
uint16_t shift_sz = input_buf_sz + rem;
memmove(&hse->buffer[0],
&hse->buffer[input_buf_sz - rem],
shift_sz);
hse->match_scan_index = 0;
hse->input_size -= input_buf_sz - rem;
}
-109
View File
@@ -1,109 +0,0 @@
#ifndef HEATSHRINK_ENCODER_H
#define HEATSHRINK_ENCODER_H
#include <stdint.h>
#include <stddef.h>
#include "heatshrink_common.h"
#include "heatshrink_config.h"
typedef enum {
HSER_SINK_OK, /* data sunk into input buffer */
HSER_SINK_ERROR_NULL=-1, /* NULL argument */
HSER_SINK_ERROR_MISUSE=-2, /* API misuse */
} HSE_sink_res;
typedef enum {
HSER_POLL_EMPTY, /* input exhausted */
HSER_POLL_MORE, /* poll again for more output */
HSER_POLL_ERROR_NULL=-1, /* NULL argument */
HSER_POLL_ERROR_MISUSE=-2, /* API misuse */
} HSE_poll_res;
typedef enum {
HSER_FINISH_DONE, /* encoding is complete */
HSER_FINISH_MORE, /* more output remaining; use poll */
HSER_FINISH_ERROR_NULL=-1, /* NULL argument */
} HSE_finish_res;
#if HEATSHRINK_DYNAMIC_ALLOC
#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \
((HSE)->window_sz2)
#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \
((HSE)->lookahead_sz2)
#define HEATSHRINK_ENCODER_INDEX(HSE) \
((HSE)->search_index)
struct hs_index {
uint16_t size;
int16_t index[];
};
#else
#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \
(HEATSHRINK_STATIC_WINDOW_BITS)
#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \
(HEATSHRINK_STATIC_LOOKAHEAD_BITS)
#define HEATSHRINK_ENCODER_INDEX(HSE) \
(&(HSE)->search_index)
struct hs_index {
uint16_t size;
int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS];
};
#endif
typedef struct {
uint16_t input_size; /* bytes in input buffer */
uint16_t match_scan_index;
uint16_t match_length;
uint16_t match_pos;
uint16_t outgoing_bits; /* enqueued outgoing bits */
uint8_t outgoing_bits_count;
uint8_t flags;
uint8_t state; /* current state machine node */
uint8_t current_byte; /* current byte of output */
uint8_t bit_index; /* current bit index */
#if HEATSHRINK_DYNAMIC_ALLOC
uint8_t window_sz2; /* 2^n size of window */
uint8_t lookahead_sz2; /* 2^n size of lookahead */
#if HEATSHRINK_USE_INDEX
struct hs_index *search_index;
#endif
/* input buffer and / sliding window for expansion */
uint8_t* buffer;
#else
#if HEATSHRINK_USE_INDEX
struct hs_index search_index;
#endif
/* input buffer and / sliding window for expansion */
uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)];
#endif
} heatshrink_encoder;
#if HEATSHRINK_DYNAMIC_ALLOC
/* Allocate a new encoder struct and its buffers.
* Returns NULL on error. */
heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2,
uint8_t lookahead_sz2);
/* Free an encoder. */
void heatshrink_encoder_free(heatshrink_encoder *hse);
#endif
/* Reset an encoder. */
void heatshrink_encoder_reset(heatshrink_encoder *hse);
/* Sink up to SIZE bytes from IN_BUF into the encoder.
* INPUT_SIZE is set to the number of bytes actually sunk (in case a
* buffer was filled.). */
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse,
uint8_t *in_buf, size_t size, size_t *input_size);
/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse,
uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
/* Notify the encoder that the input stream is finished.
* If the return value is HSER_FINISH_MORE, there is still more output, so
* call heatshrink_encoder_poll and repeat. */
HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse);
#endif
+24
View File
@@ -0,0 +1,24 @@
Import("env")
env.Append(
LINT_SOURCES=[
Dir("."),
],
CPPPATH=[
"#/lib/ibutton",
],
SDK_HEADERS=[
File("ibutton_key.h"),
File("ibutton_worker.h"),
File("ibutton_protocols.h"),
],
)
libenv = env.Clone(FW_LIB_NAME="ibutton")
libenv.ApplyLibFlags()
sources = libenv.GlobRecursive("*.c*")
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
Return("lib")
+43
View File
@@ -0,0 +1,43 @@
#include "ibutton_key_i.h"
struct iButtonKey {
iButtonProtocolId protocol_id;
iButtonProtocolData* protocol_data;
size_t protocol_data_size;
};
iButtonKey* ibutton_key_alloc(size_t data_size) {
iButtonKey* key = malloc(sizeof(iButtonKey));
key->protocol_id = iButtonProtocolIdInvalid;
key->protocol_data = malloc(data_size);
key->protocol_data_size = data_size;
return key;
}
void ibutton_key_free(iButtonKey* key) {
free(key->protocol_data);
free(key);
}
void ibutton_key_reset(iButtonKey* key) {
key->protocol_id = iButtonProtocolIdInvalid;
memset(key->protocol_data, 0, key->protocol_data_size);
}
iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key) {
return key->protocol_id;
}
void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id) {
key->protocol_id = protocol_id;
}
iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key) {
return key->protocol_data;
}
size_t ibutton_key_get_protocol_data_size(const iButtonKey* key) {
return key->protocol_data_size;
}
+54
View File
@@ -0,0 +1,54 @@
/**
* @file ibutton_key.h
*
* iButton key data holder
*/
#pragma once
#include <core/string.h>
#include "protocols/protocol_common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct iButtonKey iButtonKey;
/**
* Allocate a key object
* @param [in] data_size maximum data size held by the key
* @return pointer to the key object
*/
iButtonKey* ibutton_key_alloc(size_t data_size);
/**
* Destroy the key object, free resources
* @param [in] key pointer to the key object
*/
void ibutton_key_free(iButtonKey* key);
/**
* Get the protocol id held by the key
* @param [in] key pointer to the key object
* @return protocol id held by the key
*/
iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key);
/**
* Set the protocol id held by the key
* @param [in] key pointer to the key object
* @param [in] protocol_id new protocol id
*/
void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id);
/**
* Reset the protocol id and data held by the key
* @param [in] key pointer to the key object
*/
void ibutton_key_reset(iButtonKey* key);
#ifdef __cplusplus
}
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "ibutton_key.h"
#include "protocols/protocol_common_i.h"
iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key);
size_t ibutton_key_get_protocol_data_size(const iButtonKey* key);
+309
View File
@@ -0,0 +1,309 @@
#include "ibutton_protocols.h"
#include <storage/storage.h>
#include "ibutton_key_i.h"
#include "protocols/protocol_group_defs.h"
#define IBUTTON_FILE_TYPE "Flipper iButton key"
#define IBUTTON_PROTOCOL_KEY_V1 "Key type"
#define IBUTTON_PROTOCOL_KEY_V2 "Protocol"
#define IBUTTON_CURRENT_FORMAT_VERSION 2U
#define GET_PROTOCOL_GROUP(id) \
iButtonProtocolGroupInfo info; \
ibutton_protocols_get_group_by_id(protocols, (id), &info);
#define GROUP_BASE (info.base)
#define GROUP_DATA (info.group)
#define PROTOCOL_ID (info.id)
struct iButtonProtocols {
iButtonProtocolGroupData** group_datas;
};
typedef struct {
const iButtonProtocolGroupBase* base;
iButtonProtocolGroupData* group;
iButtonProtocolLocalId id;
} iButtonProtocolGroupInfo;
static void ibutton_protocols_get_group_by_id(
iButtonProtocols* protocols,
iButtonProtocolId id,
iButtonProtocolGroupInfo* info) {
iButtonProtocolLocalId local_id = id;
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
if(local_id < (signed)ibutton_protocol_groups[i]->protocol_count) {
info->base = ibutton_protocol_groups[i];
info->group = protocols->group_datas[i];
info->id = local_id;
return;
} else {
local_id -= ibutton_protocol_groups[i]->protocol_count;
}
}
furi_crash(NULL);
}
iButtonProtocols* ibutton_protocols_alloc() {
iButtonProtocols* protocols = malloc(sizeof(iButtonProtocols*));
protocols->group_datas = malloc(sizeof(iButtonProtocolGroupData*) * iButtonProtocolGroupMax);
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
protocols->group_datas[i] = ibutton_protocol_groups[i]->alloc();
}
return protocols;
}
void ibutton_protocols_free(iButtonProtocols* protocols) {
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
ibutton_protocol_groups[i]->free(protocols->group_datas[i]);
}
free(protocols->group_datas);
free(protocols);
}
uint32_t ibutton_protocols_get_protocol_count() {
uint32_t count = 0;
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
count += ibutton_protocol_groups[i]->protocol_count;
}
return count;
}
iButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name) {
iButtonProtocolLocalId offset = 0;
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
iButtonProtocolLocalId local_id;
if(ibutton_protocol_groups[i]->get_id_by_name(protocols->group_datas[i], &local_id, name)) {
return local_id + offset;
}
offset += ibutton_protocol_groups[i]->protocol_count;
}
return iButtonProtocolIdInvalid;
}
uint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id) {
GET_PROTOCOL_GROUP(id);
return GROUP_BASE->get_features(GROUP_DATA, PROTOCOL_ID);
}
size_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols) {
size_t max_size = 0;
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
const size_t current_max_size =
ibutton_protocol_groups[i]->get_max_data_size(protocols->group_datas[i]);
if(current_max_size > max_size) {
max_size = current_max_size;
}
}
return max_size;
}
const char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id) {
GET_PROTOCOL_GROUP(id);
return GROUP_BASE->get_manufacturer(GROUP_DATA, PROTOCOL_ID);
}
const char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id) {
GET_PROTOCOL_GROUP(id);
return GROUP_BASE->get_name(GROUP_DATA, PROTOCOL_ID);
}
bool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key) {
iButtonProtocolLocalId id = iButtonProtocolIdInvalid;
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
iButtonProtocolLocalId offset = 0;
for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) {
if(ibutton_protocol_groups[i]->read(protocols->group_datas[i], data, &id)) {
id += offset;
break;
}
offset += ibutton_protocol_groups[i]->protocol_count;
}
ibutton_key_set_protocol_id(key, id);
return id != iButtonProtocolIdInvalid;
}
bool ibutton_protocols_write_blank(iButtonProtocols* protocols, iButtonKey* key) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
return GROUP_BASE->write_blank(GROUP_DATA, data, PROTOCOL_ID);
}
bool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
return GROUP_BASE->write_copy(GROUP_DATA, data, PROTOCOL_ID);
}
void ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->emulate_start(GROUP_DATA, data, PROTOCOL_ID);
}
void ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->emulate_stop(GROUP_DATA, data, PROTOCOL_ID);
}
bool ibutton_protocols_save(
iButtonProtocols* protocols,
const iButtonKey* key,
const char* file_name) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
bool success = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
do {
const char* protocol_name = ibutton_protocols_get_name(protocols, id);
if(!flipper_format_buffered_file_open_always(ff, file_name)) break;
if(!flipper_format_write_header_cstr(ff, IBUTTON_FILE_TYPE, IBUTTON_CURRENT_FORMAT_VERSION))
break;
if(!flipper_format_write_string_cstr(ff, IBUTTON_PROTOCOL_KEY_V2, protocol_name)) break;
GET_PROTOCOL_GROUP(id);
if(!GROUP_BASE->save(GROUP_DATA, data, PROTOCOL_ID, ff)) break;
success = true;
} while(false);
flipper_format_free(ff);
furi_record_close(RECORD_STORAGE);
return success;
}
bool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name) {
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
bool success = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
FuriString* tmp = furi_string_alloc();
do {
if(!flipper_format_buffered_file_open_existing(ff, file_name)) break;
uint32_t version;
if(!flipper_format_read_header(ff, tmp, &version)) break;
if(!furi_string_equal(tmp, IBUTTON_FILE_TYPE)) break;
if(version == 1) {
if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V1, tmp)) break;
} else if(version == 2) {
if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V2, tmp)) break;
} else {
break;
}
const iButtonProtocolId id =
ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(tmp));
ibutton_key_set_protocol_id(key, id);
GET_PROTOCOL_GROUP(id);
if(!GROUP_BASE->load(GROUP_DATA, data, PROTOCOL_ID, version, ff)) break;
success = true;
} while(false);
flipper_format_free(ff);
furi_string_free(tmp);
furi_record_close(RECORD_STORAGE);
return success;
}
void ibutton_protocols_render_data(
iButtonProtocols* protocols,
const iButtonKey* key,
FuriString* result) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->render_data(GROUP_DATA, data, PROTOCOL_ID, result);
}
void ibutton_protocols_render_brief_data(
iButtonProtocols* protocols,
const iButtonKey* key,
FuriString* result) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->render_brief_data(GROUP_DATA, data, PROTOCOL_ID, result);
}
void ibutton_protocols_render_error(
iButtonProtocols* protocols,
const iButtonKey* key,
FuriString* result) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->render_error(GROUP_DATA, data, PROTOCOL_ID, result);
}
bool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
const iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
return GROUP_BASE->is_valid(GROUP_DATA, data, PROTOCOL_ID);
}
void ibutton_protocols_get_editable_data(
iButtonProtocols* protocols,
const iButtonKey* key,
iButtonEditableData* editable) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->get_editable_data(GROUP_DATA, data, PROTOCOL_ID, editable);
}
void ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key) {
const iButtonProtocolId id = ibutton_key_get_protocol_id(key);
iButtonProtocolData* data = ibutton_key_get_protocol_data(key);
GET_PROTOCOL_GROUP(id);
GROUP_BASE->apply_edits(GROUP_DATA, data, PROTOCOL_ID);
}
+197
View File
@@ -0,0 +1,197 @@
/**
* @file ibutton_protocols.h
*
* Common interface for accessing various iButton protocols
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "protocols/protocol_common.h"
#include "ibutton_key.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct iButtonProtocols iButtonProtocols;
/**
* Allocate an iButtonProtocols object
* @return pointer to an iButtonProtocols object
*/
iButtonProtocols* ibutton_protocols_alloc();
/**
* Destroy an iButtonProtocols object, free resources
* @param [in] protocols pointer to an iButtonProtocols object
*/
void ibutton_protocols_free(iButtonProtocols* protocols);
/**
* Get the total number of available protocols
*/
uint32_t ibutton_protocols_get_protocol_count();
/**
* Get maximum data size out of all protocols available
* @param [in] protocols pointer to an iButtonProtocols object
* @return maximum data size in bytes
*/
size_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols);
/**
* Get the protocol id based on its name
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] name pointer to a string containing the name
* @return protocol id on success on iButtonProtocolIdInvalid on failure
*/
iButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name);
/**
* Get the manufacturer name based on the protocol id
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] id id of the protocol in question
* @return pointer to a statically allocated string with manufacturer name
*/
const char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id);
/**
* Get the protocol name based on the protocol id
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] id id of the protocol in question
* @return pointer to a statically allocated string with protocol name
*/
const char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id);
/**
* Get protocol features bitmask by protocol id
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] id id of the protocol in question
*/
uint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id);
/**
* Read a physical device (a key or an emulator)
* @param [in] protocols pointer to an iButtonProtocols object
* @param [out] key pointer to the key to read into (must be allocated before)
* @return true on success, false on failure
*/
bool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key);
/**
* Write the key to a blank
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be written
* @return true on success, false on failure
*/
bool ibutton_protocols_write_blank(iButtonProtocols* protocols, iButtonKey* key);
/**
* Write the key to another one of the same type
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be written
* @return true on success, false on failure
*/
bool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key);
/**
* Start emulating the key
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be emulated
*/
void ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key);
/**
* Stop emulating the key
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be emulated
*/
void ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key);
/**
* Save the key data to a file.
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be saved
* @param [in] file_name full absolute path to the file name
* @return true on success, false on failure
*/
bool ibutton_protocols_save(
iButtonProtocols* protocols,
const iButtonKey* key,
const char* file_name);
/**
* Load the key from a file.
* @param [in] protocols pointer to an iButtonProtocols object
* @param [out] key pointer to the key to load into (must be allocated before)
* @param [in] file_name full absolute path to the file name
* @return true on success, false on failure
*/
bool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name);
/**
* Format a string containing device full data
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be rendered
* @param [out] result pointer to the FuriString instance (must be initialized)
*/
void ibutton_protocols_render_data(
iButtonProtocols* protocols,
const iButtonKey* key,
FuriString* result);
/**
* Format a string containing device brief data
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be rendered
* @param [out] result pointer to the FuriString instance (must be initialized)
*/
void ibutton_protocols_render_brief_data(
iButtonProtocols* protocols,
const iButtonKey* key,
FuriString* result);
/**
* Format a string containing error message (for invalid keys)
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be rendered
* @param [out] result pointer to the FuriString instance (must be initialized)
*/
void ibutton_protocols_render_error(
iButtonProtocols* protocols,
const iButtonKey* key,
FuriString* result);
/**
* Check whether the key data is valid
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be checked
* @return true if data is valid, false otherwise
*/
bool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key);
/**
* Get a pointer to the key's editable data (for in-place editing)
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in] key pointer to the key to be checked
* @param [out] editable pointer to a structure to contain the editable data
*/
void ibutton_protocols_get_editable_data(
iButtonProtocols* protocols,
const iButtonKey* key,
iButtonEditableData* editable);
/**
* Make all necessary internal adjustments after editing the key
* @param [in] protocols pointer to an iButtonProtocols object
* @param [in,out] key pointer to the key to be adjusted
*/
void ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key);
#ifdef __cplusplus
}
#endif
@@ -1,13 +1,14 @@
#include <furi.h>
#include <furi_hal.h>
#include <atomic.h>
#include "ibutton_worker_i.h"
#include "ibutton_protocols.h"
#include <core/check.h>
typedef enum {
iButtonMessageEnd,
iButtonMessageStop,
iButtonMessageRead,
iButtonMessageWrite,
iButtonMessageWriteBlank,
iButtonMessageWriteCopy,
iButtonMessageEmulate,
iButtonMessageNotifyEmulate,
} iButtonMessageType;
@@ -21,26 +22,15 @@ typedef struct {
static int32_t ibutton_worker_thread(void* thread_context);
iButtonWorker* ibutton_worker_alloc() {
iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols) {
iButtonWorker* worker = malloc(sizeof(iButtonWorker));
worker->key_p = NULL;
worker->key_data = malloc(ibutton_key_get_max_size());
worker->host = onewire_host_alloc();
worker->slave = onewire_slave_alloc();
worker->writer = ibutton_writer_alloc(worker->host);
worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0);
worker->protocols = protocols;
worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage));
worker->mode_index = iButtonWorkerIdle;
worker->read_cb = NULL;
worker->write_cb = NULL;
worker->emulate_cb = NULL;
worker->cb_ctx = NULL;
worker->mode_index = iButtonWorkerModeIdle;
worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker);
worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax);
return worker;
}
@@ -48,7 +38,7 @@ void ibutton_worker_read_set_callback(
iButtonWorker* worker,
iButtonWorkerReadCallback callback,
void* context) {
furi_check(worker->mode_index == iButtonWorkerIdle);
furi_check(worker->mode_index == iButtonWorkerModeIdle);
worker->read_cb = callback;
worker->cb_ctx = context;
}
@@ -57,7 +47,7 @@ void ibutton_worker_write_set_callback(
iButtonWorker* worker,
iButtonWorkerWriteCallback callback,
void* context) {
furi_check(worker->mode_index == iButtonWorkerIdle);
furi_check(worker->mode_index == iButtonWorkerModeIdle);
worker->write_cb = callback;
worker->cb_ctx = context;
}
@@ -66,7 +56,7 @@ void ibutton_worker_emulate_set_callback(
iButtonWorker* worker,
iButtonWorkerEmulateCallback callback,
void* context) {
furi_check(worker->mode_index == iButtonWorkerIdle);
furi_check(worker->mode_index == iButtonWorkerModeIdle);
worker->emulate_cb = callback;
worker->cb_ctx = context;
}
@@ -77,8 +67,14 @@ void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) {
furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);
}
void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key) {
iButtonMessage message = {.type = iButtonMessageWrite, .data.key = key};
void ibutton_worker_write_blank_start(iButtonWorker* worker, iButtonKey* key) {
iButtonMessage message = {.type = iButtonMessageWriteBlank, .data.key = key};
furi_check(
furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);
}
void ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key) {
iButtonMessage message = {.type = iButtonMessageWriteCopy, .data.key = key};
furi_check(
furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk);
}
@@ -96,19 +92,8 @@ void ibutton_worker_stop(iButtonWorker* worker) {
}
void ibutton_worker_free(iButtonWorker* worker) {
ibutton_writer_free(worker->writer);
onewire_slave_free(worker->slave);
onewire_host_free(worker->host);
onewire_device_free(worker->device);
protocol_dict_free(worker->protocols);
furi_message_queue_free(worker->messages);
furi_thread_free(worker->thread);
free(worker->key_data);
free(worker);
}
@@ -137,7 +122,7 @@ void ibutton_worker_notify_emulate(iButtonWorker* worker) {
}
void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) {
worker->key_p = key;
worker->key = key;
}
static int32_t ibutton_worker_thread(void* thread_context) {
@@ -154,25 +139,29 @@ static int32_t ibutton_worker_thread(void* thread_context) {
if(status == FuriStatusOk) {
switch(message.type) {
case iButtonMessageEnd:
ibutton_worker_switch_mode(worker, iButtonWorkerIdle);
ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle);
ibutton_worker_set_key_p(worker, NULL);
running = false;
break;
case iButtonMessageStop:
ibutton_worker_switch_mode(worker, iButtonWorkerIdle);
ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle);
ibutton_worker_set_key_p(worker, NULL);
break;
case iButtonMessageRead:
ibutton_worker_set_key_p(worker, message.data.key);
ibutton_worker_switch_mode(worker, iButtonWorkerRead);
ibutton_worker_switch_mode(worker, iButtonWorkerModeRead);
break;
case iButtonMessageWrite:
case iButtonMessageWriteBlank:
ibutton_worker_set_key_p(worker, message.data.key);
ibutton_worker_switch_mode(worker, iButtonWorkerWrite);
ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteBlank);
break;
case iButtonMessageWriteCopy:
ibutton_worker_set_key_p(worker, message.data.key);
ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteCopy);
break;
case iButtonMessageEmulate:
ibutton_worker_set_key_p(worker, message.data.key);
ibutton_worker_switch_mode(worker, iButtonWorkerEmulate);
ibutton_worker_switch_mode(worker, iButtonWorkerModeEmulate);
break;
case iButtonMessageNotifyEmulate:
if(worker->emulate_cb) {
@@ -5,7 +5,9 @@
*/
#pragma once
#include "ibutton_key.h"
#include "ibutton_protocols.h"
#ifdef __cplusplus
extern "C" {
@@ -28,7 +30,7 @@ typedef struct iButtonWorker iButtonWorker;
* Allocate ibutton worker
* @return iButtonWorker*
*/
iButtonWorker* ibutton_worker_alloc();
iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols);
/**
* Free ibutton worker
@@ -78,11 +80,18 @@ void ibutton_worker_write_set_callback(
void* context);
/**
* Start write mode
* Start write blank mode
* @param worker
* @param key
*/
void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key);
void ibutton_worker_write_blank_start(iButtonWorker* worker, iButtonKey* key);
/**
* Start write copy mode
* @param worker
* @param key
*/
void ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key);
/**
* Set "emulate success" callback
@@ -5,13 +5,11 @@
*/
#pragma once
#include <core/thread.h>
#include <core/message_queue.h>
#include "ibutton_worker.h"
#include "ibutton_writer.h"
#include "../one_wire_host.h"
#include "../one_wire_slave.h"
#include "../one_wire_device.h"
#include <toolbox/protocols/protocol_dict.h>
#include "protocols/ibutton_protocols.h"
#ifdef __cplusplus
extern "C" {
@@ -25,19 +23,16 @@ typedef struct {
} iButtonWorkerModeType;
typedef enum {
iButtonWorkerIdle = 0,
iButtonWorkerRead = 1,
iButtonWorkerWrite = 2,
iButtonWorkerEmulate = 3,
iButtonWorkerModeIdle,
iButtonWorkerModeRead,
iButtonWorkerModeWriteBlank,
iButtonWorkerModeWriteCopy,
iButtonWorkerModeEmulate,
} iButtonWorkerMode;
struct iButtonWorker {
iButtonKey* key_p;
uint8_t* key_data;
OneWireHost* host;
OneWireSlave* slave;
OneWireDevice* device;
iButtonWriter* writer;
iButtonKey* key;
iButtonProtocols* protocols;
iButtonWorkerMode mode_index;
FuriMessageQueue* messages;
FuriThread* thread;
@@ -45,10 +40,8 @@ struct iButtonWorker {
iButtonWorkerReadCallback read_cb;
iButtonWorkerWriteCallback write_cb;
iButtonWorkerEmulateCallback emulate_cb;
void* cb_ctx;
ProtocolDict* protocols;
iButtonProtocol protocol_to_encode;
void* cb_ctx;
};
extern const iButtonWorkerModeType ibutton_worker_modes[];
+153
View File
@@ -0,0 +1,153 @@
#include "ibutton_worker_i.h"
#include <core/check.h>
#include <furi_hal_rfid.h>
#include <furi_hal_power.h>
#include "ibutton_protocols.h"
static void ibutton_worker_mode_idle_start(iButtonWorker* worker);
static void ibutton_worker_mode_idle_tick(iButtonWorker* worker);
static void ibutton_worker_mode_idle_stop(iButtonWorker* worker);
static void ibutton_worker_mode_emulate_start(iButtonWorker* worker);
static void ibutton_worker_mode_emulate_tick(iButtonWorker* worker);
static void ibutton_worker_mode_emulate_stop(iButtonWorker* worker);
static void ibutton_worker_mode_read_start(iButtonWorker* worker);
static void ibutton_worker_mode_read_tick(iButtonWorker* worker);
static void ibutton_worker_mode_read_stop(iButtonWorker* worker);
static void ibutton_worker_mode_write_common_start(iButtonWorker* worker);
static void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker);
static void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker);
static void ibutton_worker_mode_write_common_stop(iButtonWorker* worker);
const iButtonWorkerModeType ibutton_worker_modes[] = {
{
.quant = FuriWaitForever,
.start = ibutton_worker_mode_idle_start,
.tick = ibutton_worker_mode_idle_tick,
.stop = ibutton_worker_mode_idle_stop,
},
{
.quant = 100,
.start = ibutton_worker_mode_read_start,
.tick = ibutton_worker_mode_read_tick,
.stop = ibutton_worker_mode_read_stop,
},
{
.quant = 1000,
.start = ibutton_worker_mode_write_common_start,
.tick = ibutton_worker_mode_write_blank_tick,
.stop = ibutton_worker_mode_write_common_stop,
},
{
.quant = 1000,
.start = ibutton_worker_mode_write_common_start,
.tick = ibutton_worker_mode_write_copy_tick,
.stop = ibutton_worker_mode_write_common_stop,
},
{
.quant = 1000,
.start = ibutton_worker_mode_emulate_start,
.tick = ibutton_worker_mode_emulate_tick,
.stop = ibutton_worker_mode_emulate_stop,
},
};
/*********************** IDLE ***********************/
void ibutton_worker_mode_idle_start(iButtonWorker* worker) {
UNUSED(worker);
}
void ibutton_worker_mode_idle_tick(iButtonWorker* worker) {
UNUSED(worker);
}
void ibutton_worker_mode_idle_stop(iButtonWorker* worker) {
UNUSED(worker);
}
/*********************** READ ***********************/
void ibutton_worker_mode_read_start(iButtonWorker* worker) {
UNUSED(worker);
furi_hal_power_enable_otg();
}
void ibutton_worker_mode_read_tick(iButtonWorker* worker) {
if(ibutton_protocols_read(worker->protocols, worker->key)) {
if(worker->read_cb != NULL) {
worker->read_cb(worker->cb_ctx);
}
ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle);
}
}
void ibutton_worker_mode_read_stop(iButtonWorker* worker) {
UNUSED(worker);
furi_hal_power_disable_otg();
}
/*********************** EMULATE ***********************/
void ibutton_worker_mode_emulate_start(iButtonWorker* worker) {
furi_assert(worker->key);
furi_hal_rfid_pins_reset();
furi_hal_rfid_pin_pull_pulldown();
ibutton_protocols_emulate_start(worker->protocols, worker->key);
}
void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) {
UNUSED(worker);
}
void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) {
furi_assert(worker->key);
ibutton_protocols_emulate_stop(worker->protocols, worker->key);
furi_hal_rfid_pins_reset();
}
/*********************** WRITE ***********************/
void ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524
UNUSED(worker);
furi_hal_power_enable_otg();
}
void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker) {
furi_assert(worker->key);
const bool success = ibutton_protocols_write_blank(worker->protocols, worker->key);
// TODO: pass a proper result to the callback
const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK :
iButtonWorkerWriteNoDetect;
if(worker->write_cb != NULL) {
worker->write_cb(worker->cb_ctx, result);
}
}
void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) {
furi_assert(worker->key);
const bool success = ibutton_protocols_write_copy(worker->protocols, worker->key);
// TODO: pass a proper result to the callback
const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK :
iButtonWorkerWriteNoDetect;
if(worker->write_cb != NULL) {
worker->write_cb(worker->cb_ctx, result);
}
}
void ibutton_worker_mode_write_common_stop(iButtonWorker* worker) { //-V524
UNUSED(worker);
furi_hal_power_disable_otg();
}
+95
View File
@@ -0,0 +1,95 @@
#include "rw1990.h"
#include <core/kernel.h>
#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1
#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5
#define RW1990_1_CMD_WRITE_ROM 0xD5
#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D
#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E
#define RW1990_2_CMD_WRITE_ROM 0xD5
#define DS1990_CMD_READ_ROM 0x33
static void rw1990_write_byte(OneWireHost* host, uint8_t value) {
for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
onewire_host_write_bit(host, (bool)(bitMask & value));
furi_delay_us(5000);
}
}
static bool rw1990_read_and_compare(OneWireHost* host, const uint8_t* data, size_t data_size) {
bool success = false;
if(onewire_host_reset(host)) {
success = true;
onewire_host_write(host, DS1990_CMD_READ_ROM);
for(size_t i = 0; i < data_size; ++i) {
if(data[i] != onewire_host_read(host)) {
success = false;
break;
}
}
}
return success;
}
bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) {
// Unlock sequence
onewire_host_reset(host);
onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG);
furi_delay_us(10);
onewire_host_write_bit(host, false);
furi_delay_us(5000);
// Write data
onewire_host_reset(host);
onewire_host_write(host, RW1990_1_CMD_WRITE_ROM);
for(size_t i = 0; i < data_size; ++i) {
// inverted key for RW1990.1
rw1990_write_byte(host, ~(data[i]));
furi_delay_us(30000);
}
// Lock sequence
onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG);
onewire_host_write_bit(host, true);
furi_delay_us(10000);
// TODO: Better error handling
return rw1990_read_and_compare(host, data, data_size);
}
bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size) {
// Unlock sequence
onewire_host_reset(host);
onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG);
furi_delay_us(10);
onewire_host_write_bit(host, true);
furi_delay_us(5000);
// Write data
onewire_host_reset(host);
onewire_host_write(host, RW1990_2_CMD_WRITE_ROM);
for(size_t i = 0; i < data_size; ++i) {
rw1990_write_byte(host, data[i]);
furi_delay_us(30000);
}
// Lock sequence
onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG);
onewire_host_write_bit(host, false);
furi_delay_us(10000);
// TODO: Better error handling
return rw1990_read_and_compare(host, data, data_size);
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stddef.h>
#include <one_wire/one_wire_host.h>
bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size);
bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size);
+42
View File
@@ -0,0 +1,42 @@
#include "tm2004.h"
#include <core/kernel.h>
#define TM2004_CMD_READ_STATUS 0xAA
#define TM2004_CMD_READ_MEMORY 0xF0
#define TM2004_CMD_WRITE_ROM 0x3C
#define TM2004_CMD_FINALIZATION 0x35
#define TM2004_ANSWER_READ_MEMORY 0xF5
bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) {
onewire_host_reset(host);
onewire_host_write(host, TM2004_CMD_WRITE_ROM);
// Starting writing from address 0x0000
onewire_host_write(host, 0x00);
onewire_host_write(host, 0x00);
size_t i;
for(i = 0; i < data_size; ++i) {
uint8_t answer;
onewire_host_write(host, data[i]);
answer = onewire_host_read(host);
// TODO: check answer CRC
// pulse indicating that data is correct
furi_delay_us(600);
onewire_host_write_bit(host, true);
furi_delay_us(50000);
// read written key byte
answer = onewire_host_read(host); //-V519
// check that written and read are same
if(data[i] != answer) {
break;
}
}
// TODO: Better error handling
return i == data_size;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <stddef.h>
#include <one_wire/one_wire_host.h>
bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size);
@@ -0,0 +1,261 @@
#include "dallas_common.h"
#include <core/common_defines.h>
#include <one_wire/maxim_crc.h>
#define BITS_IN_BYTE 8U
#define DALLAS_COMMON_ROM_DATA_KEY_V1 "Data"
#define DALLAS_COMMON_ROM_DATA_KEY_V2 "Rom Data"
#define DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US 5U
#define DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT 20U
#define DALLAS_COMMON_END_ADDRESS_MASK 0x01F
#define DALLAS_COMMON_STATUS_FLAG_PF (1U << 5)
#define DALLAS_COMMON_STATUS_FLAG_OF (1U << 6)
#define DALLAS_COMMON_STATUS_FLAG_AA (1U << 7)
#define DALLAS_COMMON_BRIEF_HEAD_COUNT 4U
#define DALLAS_COMMON_BRIEF_TAIL_COUNT 3U
#define BITS_IN_BYTE 8U
#define BITS_IN_KBIT 1024U
#define BITS_IN_MBIT (BITS_IN_KBIT * 1024U)
bool dallas_common_skip_rom(OneWireHost* host) {
onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);
return true;
}
bool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data) {
onewire_host_write(host, DALLAS_COMMON_CMD_READ_ROM);
onewire_host_read_bytes(host, rom_data->bytes, sizeof(DallasCommonRomData));
return dallas_common_is_valid_crc(rom_data);
}
bool dallas_common_write_scratchpad(
OneWireHost* host,
uint16_t address,
const uint8_t* data,
size_t data_size) {
onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH);
onewire_host_write(host, (uint8_t)address);
onewire_host_write(host, (uint8_t)(address >> BITS_IN_BYTE));
onewire_host_write_bytes(host, data, data_size);
return true;
}
bool dallas_common_read_scratchpad(
OneWireHost* host,
DallasCommonAddressRegs* regs,
uint8_t* data,
size_t data_size) {
onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH);
onewire_host_read_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs));
onewire_host_read_bytes(host, data, data_size);
return true;
}
bool dallas_common_copy_scratchpad(
OneWireHost* host,
const DallasCommonAddressRegs* regs,
uint32_t timeout_us) {
onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH);
onewire_host_write_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs));
const uint32_t poll_delay =
MAX(timeout_us / DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT,
DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US);
uint32_t time_elapsed;
for(time_elapsed = 0; time_elapsed < timeout_us; time_elapsed += poll_delay) {
if(!onewire_host_read_bit(host)) break;
furi_delay_us(poll_delay);
}
return time_elapsed < timeout_us;
}
bool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size) {
onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM);
onewire_host_write(host, (uint8_t)address);
onewire_host_write(host, (uint8_t)(address >> BITS_IN_BYTE));
onewire_host_read_bytes(host, data, (uint16_t)data_size);
return true;
}
bool dallas_common_write_mem(
OneWireHost* host,
uint32_t timeout_us,
size_t page_size,
const uint8_t* data,
size_t data_size) {
// Data size must be a multiple of page size
furi_check(data_size % page_size == 0);
DallasCommonAddressRegs regs;
uint8_t* scratch = malloc(page_size);
size_t i;
for(i = 0; i < data_size; i += page_size) {
const uint8_t* data_ptr = data + i;
// Write scratchpad with the next page value
if(!onewire_host_reset(host)) break;
if(!dallas_common_skip_rom(host)) break;
if(!dallas_common_write_scratchpad(host, i, data_ptr, page_size)) break;
// Read back the scratchpad contents and address registers
if(!onewire_host_reset(host)) break;
if(!dallas_common_skip_rom(host)) break;
if(!dallas_common_read_scratchpad(host, &regs, scratch, page_size)) break;
// Verify scratchpad contents
if(memcmp(data_ptr, scratch, page_size) != 0) break;
// Write scratchpad to internal memory
if(!onewire_host_reset(host)) break;
if(!dallas_common_skip_rom(host)) break;
if(!dallas_common_copy_scratchpad(host, &regs, timeout_us)) break;
// Read back the address registers again
if(!onewire_host_reset(host)) break;
if(!dallas_common_skip_rom(host)) break;
if(!dallas_common_read_scratchpad(host, &regs, scratch, 0)) break;
// Check if AA flag is set
if(!(regs.fields.status & DALLAS_COMMON_STATUS_FLAG_AA)) break;
}
free(scratch);
return i == data_size;
}
bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) {
for(size_t i = 0; i < sizeof(DallasCommonRomData); i++) {
for(size_t j = 0; j < BITS_IN_BYTE; j++) {
bool bit = (rom_data->bytes[i] >> j) & 0x01;
if(!onewire_slave_send_bit(bus, bit)) return false;
if(!onewire_slave_send_bit(bus, !bit)) return false;
onewire_slave_receive_bit(bus);
// TODO: check for errors and return if any
}
}
return true;
}
bool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) {
return onewire_slave_send(bus, rom_data->bytes, sizeof(DallasCommonRomData));
}
bool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) {
bool success = false;
union {
uint8_t bytes[sizeof(uint16_t)];
uint16_t word;
} address;
do {
if(!onewire_slave_receive(bus, address.bytes, sizeof(address))) break;
if(address.word >= data_size) break;
if(!onewire_slave_send(bus, data + address.word, data_size - address.word)) break;
success = true;
} while(false);
return success;
}
bool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data) {
return flipper_format_write_hex(
ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData));
}
bool dallas_common_load_rom_data(
FlipperFormat* ff,
uint32_t format_version,
DallasCommonRomData* rom_data) {
switch(format_version) {
case 1:
return flipper_format_read_hex(
ff, DALLAS_COMMON_ROM_DATA_KEY_V1, rom_data->bytes, sizeof(DallasCommonRomData));
case 2:
return flipper_format_read_hex(
ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData));
default:
return false;
}
}
bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data) {
const uint8_t crc_calculated =
maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT);
const uint8_t crc_received = rom_data->fields.checksum;
return crc_calculated == crc_received;
}
void dallas_common_render_brief_data(
FuriString* result,
const DallasCommonRomData* rom_data,
const uint8_t* mem_data,
size_t mem_size,
const char* mem_name) {
for(size_t i = 0; i < sizeof(rom_data->bytes); ++i) {
furi_string_cat_printf(result, "%02X ", rom_data->bytes[i]);
}
const char* size_prefix = "";
size_t mem_size_bits = mem_size * BITS_IN_BYTE;
if(mem_size_bits >= BITS_IN_MBIT) {
size_prefix = "M";
mem_size_bits /= BITS_IN_MBIT;
} else if(mem_size_bits >= BITS_IN_KBIT) {
size_prefix = "K";
mem_size_bits /= BITS_IN_KBIT;
}
furi_string_cat_printf(
result, "\nInternal %s: %zu %sbit\n", mem_name, mem_size_bits, size_prefix);
for(size_t i = 0; i < DALLAS_COMMON_BRIEF_HEAD_COUNT; ++i) {
furi_string_cat_printf(result, "%02X ", mem_data[i]);
}
furi_string_cat_printf(result, "[ . . . ]");
for(size_t i = mem_size - DALLAS_COMMON_BRIEF_TAIL_COUNT; i < mem_size; ++i) {
furi_string_cat_printf(result, " %02X", mem_data[i]);
}
}
void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data) {
furi_string_set(result, "CRC Error\n");
const size_t data_size = sizeof(DallasCommonRomData);
for(size_t i = 0; i < data_size; ++i) {
furi_string_cat_printf(result, (i < data_size - 1) ? "%02X " : "%02X", rom_data->bytes[i]);
}
}
void dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code) {
rom_data->fields.family_code = family_code;
const uint8_t crc =
maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT);
rom_data->fields.checksum = crc;
}
@@ -0,0 +1,108 @@
#pragma once
#include <one_wire/one_wire_host.h>
#include <one_wire/one_wire_slave.h>
#include <flipper_format/flipper_format.h>
#define DALLAS_COMMON_MANUFACTURER_NAME "Dallas"
#define DALLAS_COMMON_CMD_READ_ROM 0x33U
#define DALLAS_COMMON_CMD_MATCH_ROM 0x55U
#define DALLAS_COMMON_CMD_SKIP_ROM 0xCCU
#define DALLAS_COMMON_CMD_COND_SEARCH 0xECU
#define DALLAS_COMMON_CMD_SEARCH_ROM 0xF0U
#define DALLAS_COMMON_CMD_READ_SCRATCH 0xAAU
#define DALLAS_COMMON_CMD_WRITE_SCRATCH 0x0FU
#define DALLAS_COMMON_CMD_COPY_SCRATCH 0x55U
#define DALLAS_COMMON_CMD_READ_MEM 0xF0U
#define DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM 0x3CU
#define DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM 0x69U
typedef enum {
DallasCommonCommandStateIdle,
DallasCommonCommandStateRomCmd,
DallasCommonCommandStateMemCmd,
} DallasCommonCommandState;
typedef union {
struct {
uint8_t family_code;
uint8_t serial_number[6];
uint8_t checksum;
} fields;
uint8_t bytes[8];
} DallasCommonRomData;
typedef union {
struct {
uint8_t address_lo;
uint8_t address_hi;
uint8_t status;
} fields;
uint8_t bytes[3];
} DallasCommonAddressRegs;
/* Standard(ish) iButton commands */
bool dallas_common_skip_rom(OneWireHost* host);
bool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data);
bool dallas_common_write_scratchpad(
OneWireHost* host,
uint16_t address,
const uint8_t* data,
size_t data_size);
bool dallas_common_read_scratchpad(
OneWireHost* host,
DallasCommonAddressRegs* regs,
uint8_t* data,
size_t data_size);
bool dallas_common_copy_scratchpad(
OneWireHost* host,
const DallasCommonAddressRegs* regs,
uint32_t timeout_us);
bool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size);
/* Combined operations */
bool dallas_common_write_mem(
OneWireHost* host,
uint32_t timeout_us,
size_t page_size,
const uint8_t* data,
size_t data_size);
/* Emulation */
bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data);
bool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data);
bool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size);
/* Save & Load */
bool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data);
bool dallas_common_load_rom_data(
FlipperFormat* ff,
uint32_t format_version,
DallasCommonRomData* rom_data);
/* Miscellaneous */
bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data);
void dallas_common_render_brief_data(
FuriString* result,
const DallasCommonRomData* rom_data,
const uint8_t* mem_data,
size_t mem_size,
const char* mem_name);
void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data);
void dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code);
@@ -0,0 +1,39 @@
#pragma once
#include "../protocol_common_i.h"
#include <one_wire/one_wire_host.h>
#include <one_wire/one_wire_slave.h>
#include <flipper_format/flipper_format.h>
typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*);
typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*);
typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*);
typedef bool (*iButtonProtocolDallasLoadFunc)(FlipperFormat*, uint32_t, iButtonProtocolData*);
typedef void (*iButtonProtocolDallasRenderDataFunc)(FuriString*, const iButtonProtocolData*);
typedef bool (*iButtonProtocolDallasIsValidFunc)(const iButtonProtocolData*);
typedef void (
*iButtonProtocolDallasGetEditableDataFunc)(iButtonEditableData*, iButtonProtocolData*);
typedef void (*iButtonProtocolDallasApplyEditsFunc)(iButtonProtocolData*);
typedef struct {
const uint8_t family_code;
const uint32_t features;
const size_t data_size;
const char* manufacturer;
const char* name;
iButtonProtocolDallasReadWriteFunc read;
iButtonProtocolDallasReadWriteFunc write_blank;
iButtonProtocolDallasReadWriteFunc write_copy;
iButtonProtocolDallasEmulateFunc emulate;
iButtonProtocolDallasSaveFunc save;
iButtonProtocolDallasLoadFunc load;
iButtonProtocolDallasRenderDataFunc render_data;
iButtonProtocolDallasRenderDataFunc render_brief_data;
iButtonProtocolDallasRenderDataFunc render_error;
iButtonProtocolDallasIsValidFunc is_valid;
iButtonProtocolDallasGetEditableDataFunc get_editable_data;
iButtonProtocolDallasApplyEditsFunc apply_edits;
} iButtonProtocolDallasBase;
@@ -0,0 +1,276 @@
#include "protocol_ds1971.h"
#include <core/core_defines.h>
#include <toolbox/pretty_format.h>
#include "dallas_common.h"
#define DS1971_FAMILY_CODE 0x14U
#define DS1971_FAMILY_NAME "DS1971"
#define DS1971_EEPROM_DATA_SIZE 32U
#define DS1971_SRAM_PAGE_SIZE 32U
#define DS1971_COPY_SCRATCH_DELAY_US 250U
#define DS1971_DATA_BYTE_COUNT 4U
#define DS1971_EEPROM_DATA_KEY "Eeprom Data"
#define DS1971_MEMORY_TYPE "EEPROM"
#define DS1971_CMD_FINALIZATION 0xA5
typedef struct {
OneWireSlave* bus;
DallasCommonCommandState command_state;
} DS1971ProtocolState;
typedef struct {
DallasCommonRomData rom_data;
uint8_t eeprom_data[DS1971_EEPROM_DATA_SIZE];
DS1971ProtocolState state;
} DS1971ProtocolData;
static bool dallas_ds1971_read(OneWireHost*, void*);
static bool dallas_ds1971_write_copy(OneWireHost*, iButtonProtocolData*);
static void dallas_ds1971_emulate(OneWireSlave*, iButtonProtocolData*);
static bool dallas_ds1971_load(FlipperFormat*, uint32_t, iButtonProtocolData*);
static bool dallas_ds1971_save(FlipperFormat*, const iButtonProtocolData*);
static void dallas_ds1971_render_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1971_render_brief_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1971_render_error(FuriString*, const iButtonProtocolData*);
static bool dallas_ds1971_is_data_valid(const iButtonProtocolData*);
static void dallas_ds1971_get_editable_data(iButtonEditableData*, iButtonProtocolData*);
static void dallas_ds1971_apply_edits(iButtonProtocolData*);
static bool
dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size);
static bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size);
const iButtonProtocolDallasBase ibutton_protocol_ds1971 = {
.family_code = DS1971_FAMILY_CODE,
.features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteCopy,
.data_size = sizeof(DS1971ProtocolData),
.manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,
.name = DS1971_FAMILY_NAME,
.read = dallas_ds1971_read,
.write_blank = NULL, // TODO: Implement writing to blank
.write_copy = dallas_ds1971_write_copy,
.emulate = dallas_ds1971_emulate,
.save = dallas_ds1971_save,
.load = dallas_ds1971_load,
.render_data = dallas_ds1971_render_data,
.render_brief_data = dallas_ds1971_render_brief_data,
.render_error = dallas_ds1971_render_error,
.is_valid = dallas_ds1971_is_data_valid,
.get_editable_data = dallas_ds1971_get_editable_data,
.apply_edits = dallas_ds1971_apply_edits,
};
bool dallas_ds1971_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1971ProtocolData* data = protocol_data;
return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) &&
dallas_ds1971_read_mem(host, 0, data->eeprom_data, DS1971_EEPROM_DATA_SIZE);
}
bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1971ProtocolData* data = protocol_data;
onewire_host_reset(host);
onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);
// Starting writing from address 0x0000
onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH);
onewire_host_write(host, 0x00);
// Write data to scratchpad
onewire_host_write_bytes(host, data->eeprom_data, DS1971_EEPROM_DATA_SIZE);
// Read data from scratchpad and verify
bool pad_valid = false;
if(onewire_host_reset(host)) {
pad_valid = true;
onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);
onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH);
onewire_host_write(host, 0x00);
for(size_t i = 0; i < DS1971_EEPROM_DATA_SIZE; ++i) {
uint8_t scratch = onewire_host_read(host);
if(data->eeprom_data[i] != scratch) {
pad_valid = false;
break;
}
}
}
// Copy scratchpad to memory and confirm
if(pad_valid) {
if(onewire_host_reset(host)) {
onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);
onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH);
onewire_host_write(host, DS1971_CMD_FINALIZATION);
furi_delay_us(DS1971_COPY_SCRATCH_DELAY_US);
}
}
return pad_valid;
}
static bool dallas_ds1971_reset_callback(bool is_short, void* context) {
furi_assert(context);
DS1971ProtocolData* data = context;
if(!is_short) {
data->state.command_state = DallasCommonCommandStateIdle;
onewire_slave_set_overdrive(data->state.bus, is_short);
}
return !is_short;
}
static bool dallas_ds1971_command_callback(uint8_t command, void* context) {
furi_assert(context);
DS1971ProtocolData* data = context;
OneWireSlave* bus = data->state.bus;
switch(command) {
case DALLAS_COMMON_CMD_SEARCH_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return dallas_common_emulate_search_rom(bus, &data->rom_data);
} else if(data->state.command_state == DallasCommonCommandStateRomCmd) {
data->state.command_state = DallasCommonCommandStateMemCmd;
ds1971_emulate_read_mem(bus, data->eeprom_data, DS1971_EEPROM_DATA_SIZE);
return false;
} else {
return false;
}
case DALLAS_COMMON_CMD_READ_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return dallas_common_emulate_read_rom(bus, &data->rom_data);
} else {
return false;
}
case DALLAS_COMMON_CMD_SKIP_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return true;
} else {
return false;
}
default:
return false;
}
}
void dallas_ds1971_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
DS1971ProtocolData* data = protocol_data;
data->state.bus = bus;
onewire_slave_set_reset_callback(bus, dallas_ds1971_reset_callback, protocol_data);
onewire_slave_set_command_callback(bus, dallas_ds1971_command_callback, protocol_data);
}
bool dallas_ds1971_load(
FlipperFormat* ff,
uint32_t format_version,
iButtonProtocolData* protocol_data) {
DS1971ProtocolData* data = protocol_data;
bool success = false;
do {
if(format_version < 2) break;
if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break;
if(!flipper_format_read_hex(
ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE))
break;
success = true;
} while(false);
return success;
}
bool dallas_ds1971_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {
const DS1971ProtocolData* data = protocol_data;
bool success = false;
do {
if(!dallas_common_save_rom_data(ff, &data->rom_data)) break;
if(!flipper_format_write_hex(
ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE))
break;
success = true;
} while(false);
return success;
}
void dallas_ds1971_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1971ProtocolData* data = protocol_data;
pretty_format_bytes_hex_canonical(
result,
DS1971_DATA_BYTE_COUNT,
PRETTY_FORMAT_FONT_MONOSPACE,
data->eeprom_data,
DS1971_EEPROM_DATA_SIZE);
}
void dallas_ds1971_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1971ProtocolData* data = protocol_data;
dallas_common_render_brief_data(
result, &data->rom_data, data->eeprom_data, DS1971_EEPROM_DATA_SIZE, DS1971_MEMORY_TYPE);
}
void dallas_ds1971_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1971ProtocolData* data = protocol_data;
if(!dallas_common_is_valid_crc(&data->rom_data)) {
dallas_common_render_crc_error(result, &data->rom_data);
}
}
bool dallas_ds1971_is_data_valid(const iButtonProtocolData* protocol_data) {
const DS1971ProtocolData* data = protocol_data;
return dallas_common_is_valid_crc(&data->rom_data);
}
void dallas_ds1971_get_editable_data(
iButtonEditableData* editable_data,
iButtonProtocolData* protocol_data) {
DS1971ProtocolData* data = protocol_data;
editable_data->ptr = data->rom_data.bytes;
editable_data->size = sizeof(DallasCommonRomData);
}
void dallas_ds1971_apply_edits(iButtonProtocolData* protocol_data) {
DS1971ProtocolData* data = protocol_data;
dallas_common_apply_edits(&data->rom_data, DS1971_FAMILY_CODE);
}
bool dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size) {
onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM);
onewire_host_write(host, address);
onewire_host_read_bytes(host, data, (uint8_t)data_size);
return true;
}
bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) {
bool success = false;
do {
uint8_t address;
if(!onewire_slave_receive(bus, &address, sizeof(address))) break;
if(address >= data_size) break;
if(!onewire_slave_send(bus, data + address, data_size - address)) break;
success = true;
} while(false);
return success;
}
@@ -0,0 +1,5 @@
#pragma once
#include "protocol_dallas_base.h"
extern const iButtonProtocolDallasBase ibutton_protocol_ds1971;
@@ -0,0 +1,152 @@
#include "protocol_ds1990.h"
#include <core/string.h>
#include <core/core_defines.h>
#include "dallas_common.h"
#include "../blanks/rw1990.h"
#include "../blanks/tm2004.h"
#define DS1990_FAMILY_CODE 0x01U
#define DS1990_FAMILY_NAME "DS1990"
#define DS1990_CMD_READ_ROM 0x0FU
typedef struct {
OneWireSlave* bus;
} DS1990ProtocolState;
typedef struct {
DallasCommonRomData rom_data;
DS1990ProtocolState state;
} DS1990ProtocolData;
static bool dallas_ds1990_read(OneWireHost*, iButtonProtocolData*);
static bool dallas_ds1990_write_blank(OneWireHost*, iButtonProtocolData*);
static void dallas_ds1990_emulate(OneWireSlave*, iButtonProtocolData*);
static bool dallas_ds1990_load(FlipperFormat*, uint32_t, iButtonProtocolData*);
static bool dallas_ds1990_save(FlipperFormat*, const iButtonProtocolData*);
static void dallas_ds1990_render_brief_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1990_render_error(FuriString*, const iButtonProtocolData*);
static bool dallas_ds1990_is_data_valid(const iButtonProtocolData*);
static void dallas_ds1990_get_editable_data(iButtonEditableData*, iButtonProtocolData*);
static void dallas_ds1990_apply_edits(iButtonProtocolData*);
const iButtonProtocolDallasBase ibutton_protocol_ds1990 = {
.family_code = DS1990_FAMILY_CODE,
.features = iButtonProtocolFeatureWriteBlank,
.data_size = sizeof(DS1990ProtocolData),
.manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,
.name = DS1990_FAMILY_NAME,
.read = dallas_ds1990_read,
.write_blank = dallas_ds1990_write_blank,
.write_copy = NULL, /* No data to write a copy */
.emulate = dallas_ds1990_emulate,
.save = dallas_ds1990_save,
.load = dallas_ds1990_load,
.render_data = NULL, /* No data to render */
.render_brief_data = dallas_ds1990_render_brief_data,
.render_error = dallas_ds1990_render_error,
.is_valid = dallas_ds1990_is_data_valid,
.get_editable_data = dallas_ds1990_get_editable_data,
.apply_edits = dallas_ds1990_apply_edits,
};
bool dallas_ds1990_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1990ProtocolData* data = protocol_data;
return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data);
}
bool dallas_ds1990_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1990ProtocolData* data = protocol_data;
return rw1990_write_v1(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||
rw1990_write_v2(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||
tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
}
static bool dallas_ds1990_reset_callback(bool is_short, void* context) {
DS1990ProtocolData* data = context;
if(!is_short) {
onewire_slave_set_overdrive(data->state.bus, is_short);
}
return !is_short;
}
static bool dallas_ds1990_command_callback(uint8_t command, void* context) {
furi_assert(context);
DS1990ProtocolData* data = context;
OneWireSlave* bus = data->state.bus;
switch(command) {
case DALLAS_COMMON_CMD_SEARCH_ROM:
dallas_common_emulate_search_rom(bus, &data->rom_data);
break;
case DALLAS_COMMON_CMD_READ_ROM:
case DS1990_CMD_READ_ROM:
dallas_common_emulate_read_rom(bus, &data->rom_data);
break;
default:
break;
}
// No support for multiple consecutive commands
return false;
}
void dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
DS1990ProtocolData* data = protocol_data;
data->state.bus = bus;
onewire_slave_set_reset_callback(bus, dallas_ds1990_reset_callback, protocol_data);
onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data);
}
bool dallas_ds1990_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {
const DS1990ProtocolData* data = protocol_data;
return dallas_common_save_rom_data(ff, &data->rom_data);
}
bool dallas_ds1990_load(
FlipperFormat* ff,
uint32_t format_version,
iButtonProtocolData* protocol_data) {
DS1990ProtocolData* data = protocol_data;
return dallas_common_load_rom_data(ff, format_version, &data->rom_data);
}
void dallas_ds1990_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1990ProtocolData* data = protocol_data;
for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) {
furi_string_cat_printf(result, "%02X ", data->rom_data.bytes[i]);
}
}
void dallas_ds1990_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1990ProtocolData* data = protocol_data;
if(!dallas_common_is_valid_crc(&data->rom_data)) {
dallas_common_render_crc_error(result, &data->rom_data);
}
}
bool dallas_ds1990_is_data_valid(const iButtonProtocolData* protocol_data) {
const DS1990ProtocolData* data = protocol_data;
return dallas_common_is_valid_crc(&data->rom_data);
}
void dallas_ds1990_get_editable_data(
iButtonEditableData* editable_data,
iButtonProtocolData* protocol_data) {
DS1990ProtocolData* data = protocol_data;
editable_data->ptr = data->rom_data.bytes;
editable_data->size = sizeof(DallasCommonRomData);
}
void dallas_ds1990_apply_edits(iButtonProtocolData* protocol_data) {
DS1990ProtocolData* data = protocol_data;
dallas_common_apply_edits(&data->rom_data, DS1990_FAMILY_CODE);
}
@@ -0,0 +1,5 @@
#pragma once
#include "protocol_dallas_base.h"
extern const iButtonProtocolDallasBase ibutton_protocol_ds1990;
@@ -0,0 +1,225 @@
#include "protocol_ds1992.h"
#include <core/core_defines.h>
#include <toolbox/pretty_format.h>
#include "dallas_common.h"
#include "../blanks/tm2004.h"
#define DS1992_FAMILY_CODE 0x08U
#define DS1992_FAMILY_NAME "DS1992"
#define DS1992_SRAM_DATA_SIZE 128U
#define DS1992_SRAM_PAGE_SIZE 4U
#define DS1992_COPY_SCRATCH_TIMEOUT_US 100U
#define DS1992_DATA_BYTE_COUNT 4U
#define DS1992_SRAM_DATA_KEY "Sram Data"
#define DS1992_MEMORY_TYPE "SRAM"
typedef struct {
OneWireSlave* bus;
DallasCommonCommandState command_state;
} DS1992ProtocolState;
typedef struct {
DallasCommonRomData rom_data;
uint8_t sram_data[DS1992_SRAM_DATA_SIZE];
DS1992ProtocolState state;
} DS1992ProtocolData;
static bool dallas_ds1992_read(OneWireHost*, void*);
static bool dallas_ds1992_write_blank(OneWireHost*, iButtonProtocolData*);
static bool dallas_ds1992_write_copy(OneWireHost*, iButtonProtocolData*);
static void dallas_ds1992_emulate(OneWireSlave*, iButtonProtocolData*);
static bool dallas_ds1992_load(FlipperFormat*, uint32_t, iButtonProtocolData*);
static bool dallas_ds1992_save(FlipperFormat*, const iButtonProtocolData*);
static void dallas_ds1992_render_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1992_render_brief_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1992_render_error(FuriString*, const iButtonProtocolData*);
static bool dallas_ds1992_is_data_valid(const iButtonProtocolData*);
static void dallas_ds1992_get_editable_data(iButtonEditableData*, iButtonProtocolData*);
static void dallas_ds1992_apply_edits(iButtonProtocolData*);
const iButtonProtocolDallasBase ibutton_protocol_ds1992 = {
.family_code = DS1992_FAMILY_CODE,
.features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteBlank |
iButtonProtocolFeatureWriteCopy,
.data_size = sizeof(DS1992ProtocolData),
.manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,
.name = DS1992_FAMILY_NAME,
.read = dallas_ds1992_read,
.write_blank = dallas_ds1992_write_blank,
.write_copy = dallas_ds1992_write_copy,
.emulate = dallas_ds1992_emulate,
.save = dallas_ds1992_save,
.load = dallas_ds1992_load,
.render_data = dallas_ds1992_render_data,
.render_brief_data = dallas_ds1992_render_brief_data,
.render_error = dallas_ds1992_render_error,
.is_valid = dallas_ds1992_is_data_valid,
.get_editable_data = dallas_ds1992_get_editable_data,
.apply_edits = dallas_ds1992_apply_edits,
};
bool dallas_ds1992_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) &&
dallas_common_read_mem(host, 0, data->sram_data, DS1992_SRAM_DATA_SIZE);
}
bool dallas_ds1992_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
// TODO: Make this work, currently broken
return tm2004_write(host, (uint8_t*)data, sizeof(DallasCommonRomData) + DS1992_SRAM_DATA_SIZE);
}
bool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
return dallas_common_write_mem(
host,
DS1992_COPY_SCRATCH_TIMEOUT_US,
DS1992_SRAM_PAGE_SIZE,
data->sram_data,
DS1992_SRAM_DATA_SIZE);
}
static bool dallas_ds1992_reset_callback(bool is_short, void* context) {
furi_assert(context);
DS1992ProtocolData* data = context;
if(!is_short) {
data->state.command_state = DallasCommonCommandStateIdle;
onewire_slave_set_overdrive(data->state.bus, is_short);
}
return !is_short;
}
static bool dallas_ds1992_command_callback(uint8_t command, void* context) {
furi_assert(context);
DS1992ProtocolData* data = context;
OneWireSlave* bus = data->state.bus;
switch(command) {
case DALLAS_COMMON_CMD_SEARCH_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return dallas_common_emulate_search_rom(bus, &data->rom_data);
} else if(data->state.command_state == DallasCommonCommandStateRomCmd) {
data->state.command_state = DallasCommonCommandStateMemCmd;
dallas_common_emulate_read_mem(bus, data->sram_data, DS1992_SRAM_DATA_SIZE);
return false;
} else {
return false;
}
case DALLAS_COMMON_CMD_READ_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return dallas_common_emulate_read_rom(bus, &data->rom_data);
} else {
return false;
}
case DALLAS_COMMON_CMD_SKIP_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return true;
} else {
return false;
}
default:
return false;
}
}
void dallas_ds1992_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
data->state.bus = bus;
onewire_slave_set_reset_callback(bus, dallas_ds1992_reset_callback, protocol_data);
onewire_slave_set_command_callback(bus, dallas_ds1992_command_callback, protocol_data);
}
bool dallas_ds1992_load(
FlipperFormat* ff,
uint32_t format_version,
iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
bool success = false;
do {
if(format_version < 2) break;
if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break;
if(!flipper_format_read_hex(
ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE))
break;
success = true;
} while(false);
return success;
}
bool dallas_ds1992_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {
const DS1992ProtocolData* data = protocol_data;
bool success = false;
do {
if(!dallas_common_save_rom_data(ff, &data->rom_data)) break;
if(!flipper_format_write_hex(
ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE))
break;
success = true;
} while(false);
return success;
}
void dallas_ds1992_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1992ProtocolData* data = protocol_data;
pretty_format_bytes_hex_canonical(
result,
DS1992_DATA_BYTE_COUNT,
PRETTY_FORMAT_FONT_MONOSPACE,
data->sram_data,
DS1992_SRAM_DATA_SIZE);
}
void dallas_ds1992_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1992ProtocolData* data = protocol_data;
dallas_common_render_brief_data(
result, &data->rom_data, data->sram_data, DS1992_SRAM_DATA_SIZE, DS1992_MEMORY_TYPE);
}
void dallas_ds1992_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1992ProtocolData* data = protocol_data;
if(!dallas_common_is_valid_crc(&data->rom_data)) {
dallas_common_render_crc_error(result, &data->rom_data);
}
}
bool dallas_ds1992_is_data_valid(const iButtonProtocolData* protocol_data) {
const DS1992ProtocolData* data = protocol_data;
return dallas_common_is_valid_crc(&data->rom_data);
}
void dallas_ds1992_get_editable_data(
iButtonEditableData* editable_data,
iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
editable_data->ptr = data->rom_data.bytes;
editable_data->size = sizeof(DallasCommonRomData);
}
void dallas_ds1992_apply_edits(iButtonProtocolData* protocol_data) {
DS1992ProtocolData* data = protocol_data;
dallas_common_apply_edits(&data->rom_data, DS1992_FAMILY_CODE);
}
@@ -0,0 +1,5 @@
#pragma once
#include "protocol_dallas_base.h"
extern const iButtonProtocolDallasBase ibutton_protocol_ds1992;
@@ -0,0 +1,251 @@
#include "protocol_ds1996.h"
#include <core/core_defines.h>
#include <toolbox/pretty_format.h>
#include "dallas_common.h"
#define DS1996_FAMILY_CODE 0x0CU
#define DS1996_FAMILY_NAME "DS1996"
#define DS1996_SRAM_DATA_SIZE 8192U
#define DS1996_SRAM_PAGE_SIZE 32U
#define DS1996_COPY_SCRATCH_TIMEOUT_US 100U
#define DS1996_DATA_BYTE_COUNT 4U
#define DS1996_SRAM_DATA_KEY "Sram Data"
#define DS1996_MEMORY_TYPE "SRAM"
typedef struct {
OneWireSlave* bus;
DallasCommonCommandState command_state;
} DS1996ProtocolState;
typedef struct {
DallasCommonRomData rom_data;
uint8_t sram_data[DS1996_SRAM_DATA_SIZE];
DS1996ProtocolState state;
} DS1996ProtocolData;
static bool dallas_ds1996_read(OneWireHost*, void*);
static bool dallas_ds1996_write_copy(OneWireHost*, iButtonProtocolData*);
static void dallas_ds1996_emulate(OneWireSlave*, iButtonProtocolData*);
static bool dallas_ds1996_load(FlipperFormat*, uint32_t, iButtonProtocolData*);
static bool dallas_ds1996_save(FlipperFormat*, const iButtonProtocolData*);
static void dallas_ds1996_render_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1996_render_brief_data(FuriString*, const iButtonProtocolData*);
static void dallas_ds1996_render_error(FuriString*, const iButtonProtocolData*);
static bool dallas_ds1996_is_data_valid(const iButtonProtocolData*);
static void dallas_ds1996_get_editable_data(iButtonEditableData*, iButtonProtocolData*);
static void dallas_ds1996_apply_edits(iButtonProtocolData*);
const iButtonProtocolDallasBase ibutton_protocol_ds1996 = {
.family_code = DS1996_FAMILY_CODE,
.features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteCopy,
.data_size = sizeof(DS1996ProtocolData),
.manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,
.name = DS1996_FAMILY_NAME,
.read = dallas_ds1996_read,
.write_blank = NULL, /* Data too big for known blanks */
.write_copy = dallas_ds1996_write_copy,
.emulate = dallas_ds1996_emulate,
.save = dallas_ds1996_save,
.load = dallas_ds1996_load,
.render_data = dallas_ds1996_render_data,
.render_brief_data = dallas_ds1996_render_brief_data,
.render_error = dallas_ds1996_render_error,
.is_valid = dallas_ds1996_is_data_valid,
.get_editable_data = dallas_ds1996_get_editable_data,
.apply_edits = dallas_ds1996_apply_edits,
};
bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1996ProtocolData* data = protocol_data;
bool success = false;
do {
if(!onewire_host_reset(host)) break;
if(!dallas_common_read_rom(host, &data->rom_data)) break;
if(!onewire_host_reset(host)) break;
onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM);
onewire_host_set_overdrive(host, true);
if(!dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE)) break;
success = true;
} while(false);
onewire_host_set_overdrive(host, false);
return success;
}
bool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {
DS1996ProtocolData* data = protocol_data;
bool success = false;
do {
if(!onewire_host_reset(host)) break;
onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM);
onewire_host_set_overdrive(host, true);
if(!dallas_common_write_mem(
host,
DS1996_COPY_SCRATCH_TIMEOUT_US,
DS1996_SRAM_PAGE_SIZE,
data->sram_data,
DS1996_SRAM_DATA_SIZE))
break;
success = true;
} while(false);
onewire_host_set_overdrive(host, false);
return success;
}
static bool dallas_ds1996_reset_callback(bool is_short, void* context) {
furi_assert(context);
DS1996ProtocolData* data = context;
data->state.command_state = DallasCommonCommandStateIdle;
onewire_slave_set_overdrive(data->state.bus, is_short);
return true;
}
static bool dallas_ds1996_command_callback(uint8_t command, void* context) {
furi_assert(context);
DS1996ProtocolData* data = context;
OneWireSlave* bus = data->state.bus;
switch(command) {
case DALLAS_COMMON_CMD_SEARCH_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return dallas_common_emulate_search_rom(bus, &data->rom_data);
} else if(data->state.command_state == DallasCommonCommandStateRomCmd) {
data->state.command_state = DallasCommonCommandStateMemCmd;
return dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE);
} else {
return false;
}
case DALLAS_COMMON_CMD_READ_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return dallas_common_emulate_read_rom(bus, &data->rom_data);
} else {
return false;
}
case DALLAS_COMMON_CMD_SKIP_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
return true;
} else {
return false;
}
case DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM:
if(data->state.command_state == DallasCommonCommandStateIdle) {
data->state.command_state = DallasCommonCommandStateRomCmd;
onewire_slave_set_overdrive(bus, true);
return true;
} else {
return false;
}
case DALLAS_COMMON_CMD_MATCH_ROM:
case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM:
/* TODO: Match ROM command support */
default:
return false;
}
}
void dallas_ds1996_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
DS1996ProtocolData* data = protocol_data;
data->state.bus = bus;
onewire_slave_set_reset_callback(bus, dallas_ds1996_reset_callback, protocol_data);
onewire_slave_set_command_callback(bus, dallas_ds1996_command_callback, protocol_data);
}
bool dallas_ds1996_load(
FlipperFormat* ff,
uint32_t format_version,
iButtonProtocolData* protocol_data) {
DS1996ProtocolData* data = protocol_data;
bool success = false;
do {
if(format_version < 2) break;
if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break;
if(!flipper_format_read_hex(
ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE))
break;
success = true;
} while(false);
return success;
}
bool dallas_ds1996_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {
const DS1996ProtocolData* data = protocol_data;
bool success = false;
do {
if(!dallas_common_save_rom_data(ff, &data->rom_data)) break;
if(!flipper_format_write_hex(
ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE))
break;
success = true;
} while(false);
return success;
}
void dallas_ds1996_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1996ProtocolData* data = protocol_data;
pretty_format_bytes_hex_canonical(
result,
DS1996_DATA_BYTE_COUNT,
PRETTY_FORMAT_FONT_MONOSPACE,
data->sram_data,
DS1996_SRAM_DATA_SIZE);
}
void dallas_ds1996_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1996ProtocolData* data = protocol_data;
dallas_common_render_brief_data(
result, &data->rom_data, data->sram_data, DS1996_SRAM_DATA_SIZE, DS1996_MEMORY_TYPE);
}
void dallas_ds1996_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {
const DS1996ProtocolData* data = protocol_data;
if(!dallas_common_is_valid_crc(&data->rom_data)) {
dallas_common_render_crc_error(result, &data->rom_data);
}
}
bool dallas_ds1996_is_data_valid(const iButtonProtocolData* protocol_data) {
const DS1996ProtocolData* data = protocol_data;
return dallas_common_is_valid_crc(&data->rom_data);
}
void dallas_ds1996_get_editable_data(
iButtonEditableData* editable_data,
iButtonProtocolData* protocol_data) {
DS1996ProtocolData* data = protocol_data;
editable_data->ptr = data->rom_data.bytes;
editable_data->size = sizeof(DallasCommonRomData);
}
void dallas_ds1996_apply_edits(iButtonProtocolData* protocol_data) {
DS1996ProtocolData* data = protocol_data;
dallas_common_apply_edits(&data->rom_data, DS1996_FAMILY_CODE);
}
@@ -0,0 +1,5 @@
#pragma once
#include "protocol_dallas_base.h"
extern const iButtonProtocolDallasBase ibutton_protocol_ds1996;
@@ -0,0 +1,141 @@
#include "protocol_ds_generic.h"
#include <core/string.h>
#include <core/core_defines.h>
#include "dallas_common.h"
#include "../blanks/tm2004.h"
#define DALLAS_GENERIC_FAMILY_CODE 0x00U
#define DALLAS_GENERIC_FAMILY_NAME "DSGeneric"
typedef struct {
OneWireSlave* bus;
} DallasGenericProtocolState;
typedef struct {
DallasCommonRomData rom_data;
DallasGenericProtocolState state;
} DallasGenericProtocolData;
static bool ds_generic_read(OneWireHost*, iButtonProtocolData*);
static bool ds_generic_write_blank(OneWireHost*, iButtonProtocolData*);
static void ds_generic_emulate(OneWireSlave*, iButtonProtocolData*);
static bool ds_generic_load(FlipperFormat*, uint32_t, iButtonProtocolData*);
static bool ds_generic_save(FlipperFormat*, const iButtonProtocolData*);
static void ds_generic_render_brief_data(FuriString*, const iButtonProtocolData*);
static void ds_generic_render_error(FuriString*, const iButtonProtocolData*);
static bool ds_generic_is_data_valid(const iButtonProtocolData*);
static void ds_generic_get_editable_data(iButtonEditableData*, iButtonProtocolData*);
static void ds_generic_apply_edits(iButtonProtocolData*);
const iButtonProtocolDallasBase ibutton_protocol_ds_generic = {
.family_code = DALLAS_GENERIC_FAMILY_CODE,
.features = iButtonProtocolFeatureWriteBlank,
.data_size = sizeof(DallasGenericProtocolData),
.manufacturer = DALLAS_COMMON_MANUFACTURER_NAME,
.name = DALLAS_GENERIC_FAMILY_NAME,
.read = ds_generic_read,
.write_blank = ds_generic_write_blank,
.write_copy = NULL, /* No data to write a copy */
.emulate = ds_generic_emulate,
.save = ds_generic_save,
.load = ds_generic_load,
.render_data = NULL, /* No data to render */
.render_brief_data = ds_generic_render_brief_data,
.render_error = ds_generic_render_error,
.is_valid = ds_generic_is_data_valid,
.get_editable_data = ds_generic_get_editable_data,
.apply_edits = ds_generic_apply_edits,
};
bool ds_generic_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
DallasGenericProtocolData* data = protocol_data;
return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data);
}
bool ds_generic_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) {
DallasGenericProtocolData* data = protocol_data;
return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
}
static bool ds_generic_reset_callback(bool is_short, void* context) {
DallasGenericProtocolData* data = context;
if(!is_short) {
onewire_slave_set_overdrive(data->state.bus, is_short);
}
return !is_short;
}
static bool ds_generic_command_callback(uint8_t command, void* context) {
furi_assert(context);
DallasGenericProtocolData* data = context;
OneWireSlave* bus = data->state.bus;
switch(command) {
case DALLAS_COMMON_CMD_SEARCH_ROM:
dallas_common_emulate_search_rom(bus, &data->rom_data);
break;
case DALLAS_COMMON_CMD_READ_ROM:
dallas_common_emulate_read_rom(bus, &data->rom_data);
break;
default:
break;
}
// No support for multiple consecutive commands
return false;
}
void ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
DallasGenericProtocolData* data = protocol_data;
data->state.bus = bus;
onewire_slave_set_reset_callback(bus, ds_generic_reset_callback, NULL);
onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data);
}
bool ds_generic_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) {
const DallasGenericProtocolData* data = protocol_data;
return dallas_common_save_rom_data(ff, &data->rom_data);
}
bool ds_generic_load(
FlipperFormat* ff,
uint32_t format_version,
iButtonProtocolData* protocol_data) {
DallasGenericProtocolData* data = protocol_data;
return dallas_common_load_rom_data(ff, format_version, &data->rom_data);
}
void ds_generic_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
const DallasGenericProtocolData* data = protocol_data;
for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) {
furi_string_cat_printf(result, "%02X ", data->rom_data.bytes[i]);
}
}
void ds_generic_render_error(FuriString* result, const iButtonProtocolData* protocol_data) {
UNUSED(result);
UNUSED(protocol_data);
}
bool ds_generic_is_data_valid(const iButtonProtocolData* protocol_data) {
UNUSED(protocol_data);
return true;
}
void ds_generic_get_editable_data(
iButtonEditableData* editable_data,
iButtonProtocolData* protocol_data) {
DallasGenericProtocolData* data = protocol_data;
editable_data->ptr = data->rom_data.bytes;
editable_data->size = sizeof(DallasCommonRomData);
}
void ds_generic_apply_edits(iButtonProtocolData* protocol_data) {
UNUSED(protocol_data);
}
@@ -0,0 +1,5 @@
#pragma once
#include "protocol_dallas_base.h"
extern const iButtonProtocolDallasBase ibutton_protocol_ds_generic;
@@ -0,0 +1,310 @@
#include "protocol_group_dallas.h"
#include <furi_hal_resources.h>
#include "protocol_group_dallas_defs.h"
#define IBUTTON_ONEWIRE_ROM_SIZE 8U
typedef struct {
OneWireHost* host;
OneWireSlave* bus;
} iButtonProtocolGroupDallas;
static iButtonProtocolGroupDallas* ibutton_protocol_group_dallas_alloc() {
iButtonProtocolGroupDallas* group = malloc(sizeof(iButtonProtocolGroupDallas));
group->host = onewire_host_alloc(&ibutton_gpio);
group->bus = onewire_slave_alloc(&ibutton_gpio);
return group;
}
static void ibutton_protocol_group_dallas_free(iButtonProtocolGroupDallas* group) {
onewire_slave_free(group->bus);
onewire_host_free(group->host);
free(group);
}
static size_t ibutton_protocol_group_dallas_get_max_data_size(iButtonProtocolGroupDallas* group) {
UNUSED(group);
size_t max_data_size = 0;
for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) {
const size_t current_rom_size = ibutton_protocols_dallas[i]->data_size;
if(current_rom_size > max_data_size) {
max_data_size = current_rom_size;
}
}
return max_data_size;
}
static bool ibutton_protocol_group_dallas_get_id_by_name(
iButtonProtocolGroupDallas* group,
iButtonProtocolLocalId* id,
const char* name) {
UNUSED(group);
// Handle older key files which refer to DS1990 as just "Dallas"
if(strcmp(name, "Dallas") == 0) {
*id = iButtonProtocolDS1990;
return true;
}
for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) {
if(strcmp(ibutton_protocols_dallas[i]->name, name) == 0) {
*id = i;
return true;
}
}
return false;
}
static uint32_t ibutton_protocol_group_dallas_get_features(
iButtonProtocolGroupDallas* group,
iButtonProtocolLocalId id) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
return ibutton_protocols_dallas[id]->features;
}
static const char* ibutton_protocol_group_dallas_get_manufacturer(
iButtonProtocolGroupDallas* group,
iButtonProtocolLocalId id) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
return ibutton_protocols_dallas[id]->manufacturer;
}
static const char* ibutton_protocol_group_dallas_get_name(
iButtonProtocolGroupDallas* group,
iButtonProtocolLocalId id) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
return ibutton_protocols_dallas[id]->name;
}
static iButtonProtocolLocalId
ibutton_protocol_group_dallas_get_id_by_family_code(uint8_t family_code) {
iButtonProtocolLocalId id;
for(id = 0; id < iButtonProtocolDSGeneric; ++id) {
if(ibutton_protocols_dallas[id]->family_code == family_code) break;
}
return id;
}
static bool ibutton_protocol_group_dallas_read(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId* id) {
bool success = false;
uint8_t rom_data[IBUTTON_ONEWIRE_ROM_SIZE];
OneWireHost* host = group->host;
onewire_host_start(host);
furi_delay_ms(100);
FURI_CRITICAL_ENTER();
if(onewire_host_search(host, rom_data, OneWireHostSearchModeNormal)) {
/* Considering any found 1-Wire device a success.
* It can be checked later with ibutton_key_is_valid(). */
success = true;
/* If a 1-Wire device was found, id is guaranteed to be
* one of the known keys or DSGeneric. */
*id = ibutton_protocol_group_dallas_get_id_by_family_code(rom_data[0]);
ibutton_protocols_dallas[*id]->read(host, data);
}
onewire_host_reset_search(host);
onewire_host_stop(host);
FURI_CRITICAL_EXIT();
return success;
}
static bool ibutton_protocol_group_dallas_write_blank(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
furi_assert(id < iButtonProtocolDSMax);
const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];
furi_assert(protocol->features & iButtonProtocolFeatureWriteBlank);
OneWireHost* host = group->host;
onewire_host_start(host);
furi_delay_ms(100);
FURI_CRITICAL_ENTER();
const bool success = protocol->write_blank(host, data);
onewire_host_stop(host);
FURI_CRITICAL_EXIT();
return success;
}
static bool ibutton_protocol_group_dallas_write_copy(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
furi_assert(id < iButtonProtocolDSMax);
const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];
furi_assert(protocol->features & iButtonProtocolFeatureWriteCopy);
OneWireHost* host = group->host;
onewire_host_start(host);
furi_delay_ms(100);
FURI_CRITICAL_ENTER();
const bool success = protocol->write_copy(host, data);
onewire_host_stop(host);
FURI_CRITICAL_EXIT();
return success;
}
static void ibutton_protocol_group_dallas_emulate_start(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
furi_assert(id < iButtonProtocolDSMax);
OneWireSlave* bus = group->bus;
ibutton_protocols_dallas[id]->emulate(bus, data);
onewire_slave_start(bus);
}
static void ibutton_protocol_group_dallas_emulate_stop(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
furi_assert(id < iButtonProtocolDSMax);
UNUSED(data);
onewire_slave_stop(group->bus);
}
static bool ibutton_protocol_group_dallas_save(
iButtonProtocolGroupDallas* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FlipperFormat* ff) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
return ibutton_protocols_dallas[id]->save(ff, data);
}
static bool ibutton_protocol_group_dallas_load(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id,
uint32_t version,
FlipperFormat* ff) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
return ibutton_protocols_dallas[id]->load(ff, version, data);
}
static void ibutton_protocol_group_dallas_render_data(
iButtonProtocolGroupDallas* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FuriString* result) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id];
furi_assert(protocol->render_data);
protocol->render_data(result, data);
}
static void ibutton_protocol_group_dallas_render_brief_data(
iButtonProtocolGroupDallas* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FuriString* result) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
ibutton_protocols_dallas[id]->render_brief_data(result, data);
}
static void ibutton_protocol_group_dallas_render_error(
iButtonProtocolGroupDallas* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FuriString* result) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
ibutton_protocols_dallas[id]->render_error(result, data);
}
static bool ibutton_protocol_group_dallas_is_valid(
iButtonProtocolGroupDallas* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
return ibutton_protocols_dallas[id]->is_valid(data);
}
static void ibutton_protocol_group_dallas_get_editable_data(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id,
iButtonEditableData* editable) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
ibutton_protocols_dallas[id]->get_editable_data(editable, data);
}
static void ibutton_protocol_group_dallas_apply_edits(
iButtonProtocolGroupDallas* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
UNUSED(group);
furi_assert(id < iButtonProtocolDSMax);
ibutton_protocols_dallas[id]->apply_edits(data);
}
const iButtonProtocolGroupBase ibutton_protocol_group_dallas = {
.protocol_count = iButtonProtocolDSMax,
.alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_dallas_alloc,
.free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_dallas_free,
.get_max_data_size =
(iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_dallas_get_max_data_size,
.get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_dallas_get_id_by_name,
.get_features =
(iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_dallas_get_features,
.get_manufacturer =
(iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_manufacturer,
.get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_name,
.read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_dallas_read,
.write_blank = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_blank,
.write_copy = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_copy,
.emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_start,
.emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_stop,
.save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_dallas_save,
.load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_dallas_load,
.render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_data,
.render_brief_data =
(iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_brief_data,
.render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_error,
.is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_dallas_is_valid,
.get_editable_data =
(iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_dallas_get_editable_data,
.apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_apply_edits,
};
@@ -0,0 +1,5 @@
#pragma once
#include "../protocol_group_base.h"
extern const iButtonProtocolGroupBase ibutton_protocol_group_dallas;
@@ -0,0 +1,18 @@
#include "protocol_group_dallas_defs.h"
#include "protocol_ds1990.h"
#include "protocol_ds1992.h"
#include "protocol_ds1996.h"
#include "protocol_ds1971.h"
#include "protocol_ds_generic.h"
const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = {
[iButtonProtocolDS1990] = &ibutton_protocol_ds1990,
[iButtonProtocolDS1992] = &ibutton_protocol_ds1992,
[iButtonProtocolDS1996] = &ibutton_protocol_ds1996,
[iButtonProtocolDS1971] = &ibutton_protocol_ds1971,
/* Add new 1-Wire protocols here */
/* Default catch-all 1-Wire protocol */
[iButtonProtocolDSGeneric] = &ibutton_protocol_ds_generic,
};
@@ -0,0 +1,17 @@
#pragma once
#include "protocol_dallas_base.h"
typedef enum {
iButtonProtocolDS1990,
iButtonProtocolDS1992,
iButtonProtocolDS1996,
iButtonProtocolDS1971,
/* Add new 1-Wire protocols here */
/* Default catch-all 1-Wire protocol */
iButtonProtocolDSGeneric,
iButtonProtocolDSMax,
} iButtonProtocolDallas;
extern const iButtonProtocolDallasBase* ibutton_protocols_dallas[];
@@ -1,5 +1,6 @@
#include <furi.h>
#include <furi_hal.h>
#include "protocol_cyfral.h"
#define CYFRAL_DATA_SIZE sizeof(uint16_t)
@@ -324,7 +325,13 @@ static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) {
return result;
}
const ProtocolBase protocol_cyfral = {
static void protocol_cyfral_render_brief_data(ProtocolCyfral* proto, FuriString* result) {
for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) {
furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]);
}
}
const ProtocolBase ibutton_protocol_misc_cyfral = {
.name = "Cyfral",
.manufacturer = "Cyfral",
.data_size = CYFRAL_DATA_SIZE,
@@ -341,4 +348,5 @@ const ProtocolBase protocol_cyfral = {
.start = (ProtocolEncoderStart)protocol_cyfral_encoder_start,
.yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield,
},
.render_brief_data = (ProtocolRenderData)protocol_cyfral_render_brief_data,
};
@@ -0,0 +1,4 @@
#pragma once
#include "toolbox/protocols/protocol.h"
extern const ProtocolBase ibutton_protocol_misc_cyfral;
@@ -0,0 +1,295 @@
#include "protocol_group_misc.h"
#include <furi_hal_rfid.h>
#include <furi_hal_ibutton.h>
#include <toolbox/protocols/protocol_dict.h>
#include "protocol_group_misc_defs.h"
#define IBUTTON_MISC_READ_TIMEOUT 100
#define IBUTTON_MISC_DATA_KEY_KEY_COMMON "Data"
typedef struct {
ProtocolDict* dict;
ProtocolId emulate_id;
} iButtonProtocolGroupMisc;
static iButtonProtocolGroupMisc* ibutton_protocol_group_misc_alloc() {
iButtonProtocolGroupMisc* group = malloc(sizeof(iButtonProtocolGroupMisc));
group->dict = protocol_dict_alloc(ibutton_protocols_misc, iButtonProtocolMiscMax);
group->emulate_id = PROTOCOL_NO;
return group;
}
static void ibutton_protocol_group_misc_free(iButtonProtocolGroupMisc* group) {
protocol_dict_free(group->dict);
free(group);
}
static size_t ibutton_protocol_group_misc_get_max_data_size(iButtonProtocolGroupMisc* group) {
return protocol_dict_get_max_data_size(group->dict);
}
static bool ibutton_protocol_group_misc_get_id_by_name(
iButtonProtocolGroupMisc* group,
iButtonProtocolLocalId* id,
const char* name) {
const ProtocolId found_id = protocol_dict_get_protocol_by_name(group->dict, name);
if(found_id != PROTOCOL_NO) {
*id = found_id;
return true;
}
return false;
}
static uint32_t ibutton_protocol_group_misc_get_features(
iButtonProtocolGroupMisc* group,
iButtonProtocolLocalId id) {
UNUSED(group);
UNUSED(id);
return 0;
}
static const char* ibutton_protocol_group_misc_get_manufacturer(
iButtonProtocolGroupMisc* group,
iButtonProtocolLocalId id) {
return protocol_dict_get_manufacturer(group->dict, id);
}
static const char* ibutton_protocol_group_misc_get_name(
iButtonProtocolGroupMisc* group,
iButtonProtocolLocalId id) {
return protocol_dict_get_name(group->dict, id);
}
typedef struct {
uint32_t last_dwt_value;
FuriStreamBuffer* stream;
} iButtonReadContext;
static void ibutton_protocols_comparator_callback(bool level, void* context) {
iButtonReadContext* read_context = context;
uint32_t current_dwt_value = DWT->CYCCNT;
LevelDuration data =
level_duration_make(level, current_dwt_value - read_context->last_dwt_value);
furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0);
read_context->last_dwt_value = current_dwt_value;
}
static bool ibutton_protocol_group_misc_read(
iButtonProtocolGroupMisc* group,
iButtonProtocolData* data,
iButtonProtocolLocalId* id) {
bool result = false;
protocol_dict_decoders_start(group->dict);
furi_hal_rfid_pins_reset();
// pulldown pull pin, we sense the signal through the analog part of the RFID schematic
furi_hal_rfid_pin_pull_pulldown();
iButtonReadContext read_context = {
.last_dwt_value = DWT->CYCCNT,
.stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1),
};
furi_hal_rfid_comp_set_callback(ibutton_protocols_comparator_callback, &read_context);
furi_hal_rfid_comp_start();
const uint32_t tick_start = furi_get_tick();
for(;;) {
LevelDuration level;
size_t ret = furi_stream_buffer_receive(
read_context.stream, &level, sizeof(LevelDuration), IBUTTON_MISC_READ_TIMEOUT);
if((furi_get_tick() - tick_start) > IBUTTON_MISC_READ_TIMEOUT) {
break;
}
if(ret > 0) {
ProtocolId decoded_index = protocol_dict_decoders_feed(
group->dict, level_duration_get_level(level), level_duration_get_duration(level));
if(decoded_index == PROTOCOL_NO) continue;
*id = decoded_index;
protocol_dict_get_data(
group->dict,
decoded_index,
data,
protocol_dict_get_data_size(group->dict, decoded_index));
result = true;
}
}
furi_hal_rfid_comp_stop();
furi_hal_rfid_comp_set_callback(NULL, NULL);
furi_hal_rfid_pins_reset();
furi_stream_buffer_free(read_context.stream);
return result;
}
static void ibutton_protocol_group_misc_emulate_callback(void* context) {
iButtonProtocolGroupMisc* group = context;
const LevelDuration level_duration =
protocol_dict_encoder_yield(group->dict, group->emulate_id);
furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level_duration));
furi_hal_ibutton_pin_write(level_duration_get_level(level_duration));
}
static void ibutton_protocol_group_misc_emulate_start(
iButtonProtocolGroupMisc* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
group->emulate_id = id;
protocol_dict_set_data(group->dict, id, data, protocol_dict_get_data_size(group->dict, id));
protocol_dict_encoder_start(group->dict, group->emulate_id);
furi_hal_ibutton_pin_configure();
furi_hal_ibutton_emulate_start(0, ibutton_protocol_group_misc_emulate_callback, group);
}
static void ibutton_protocol_group_misc_emulate_stop(
iButtonProtocolGroupMisc* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
UNUSED(group);
UNUSED(data);
UNUSED(id);
furi_hal_ibutton_emulate_stop();
furi_hal_ibutton_pin_reset();
}
static bool ibutton_protocol_group_misc_save(
iButtonProtocolGroupMisc* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FlipperFormat* ff) {
const size_t data_size = protocol_dict_get_data_size(group->dict, id);
return flipper_format_write_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size);
}
static bool ibutton_protocol_group_misc_load(
iButtonProtocolGroupMisc* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id,
uint32_t version,
FlipperFormat* ff) {
const size_t data_size = protocol_dict_get_data_size(group->dict, id);
switch(version) {
case 1:
case 2:
return flipper_format_read_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size);
default:
return false;
}
}
static void ibutton_protocol_group_misc_render_data(
iButtonProtocolGroupMisc* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FuriString* result) {
const size_t data_size = protocol_dict_get_data_size(group->dict, id);
protocol_dict_set_data(group->dict, id, data, data_size);
protocol_dict_render_data(group->dict, result, id);
}
static void ibutton_protocol_group_misc_render_brief_data(
iButtonProtocolGroupMisc* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FuriString* result) {
const size_t data_size = protocol_dict_get_data_size(group->dict, id);
protocol_dict_set_data(group->dict, id, data, data_size);
protocol_dict_render_brief_data(group->dict, result, id);
}
static void ibutton_protocol_group_misc_render_error(
iButtonProtocolGroupMisc* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id,
FuriString* result) {
UNUSED(group);
UNUSED(data);
UNUSED(id);
UNUSED(result);
}
static bool ibutton_protocol_group_misc_is_valid(
iButtonProtocolGroupMisc* group,
const iButtonProtocolData* data,
iButtonProtocolLocalId id) {
UNUSED(group);
UNUSED(data);
UNUSED(id);
return true;
}
static void ibutton_protocol_group_misc_get_editable_data(
iButtonProtocolGroupMisc* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id,
iButtonEditableData* editable) {
editable->ptr = data;
editable->size = protocol_dict_get_data_size(group->dict, id);
}
static void ibutton_protocol_group_misc_apply_edits(
iButtonProtocolGroupMisc* group,
iButtonProtocolData* data,
iButtonProtocolLocalId id) {
const size_t data_size = protocol_dict_get_data_size(group->dict, id);
protocol_dict_set_data(group->dict, id, data, data_size);
}
const iButtonProtocolGroupBase ibutton_protocol_group_misc = {
.protocol_count = iButtonProtocolMiscMax,
.alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_misc_alloc,
.free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_misc_free,
.get_max_data_size =
(iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_misc_get_max_data_size,
.get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_misc_get_id_by_name,
.get_features = (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_misc_get_features,
.get_manufacturer =
(iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_manufacturer,
.get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_name,
.read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_misc_read,
.write_blank = NULL,
.write_copy = NULL,
.emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_start,
.emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_stop,
.save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_misc_save,
.load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_misc_load,
.render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_data,
.render_brief_data =
(iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_brief_data,
.render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_error,
.is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_misc_is_valid,
.get_editable_data =
(iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_misc_get_editable_data,
.apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_apply_edits,
};
@@ -0,0 +1,5 @@
#pragma once
#include "../protocol_group_base.h"
extern const iButtonProtocolGroupBase ibutton_protocol_group_misc;
@@ -0,0 +1,10 @@
#include "protocol_group_misc_defs.h"
#include "protocol_cyfral.h"
#include "protocol_metakom.h"
const ProtocolBase* ibutton_protocols_misc[] = {
[iButtonProtocolMiscCyfral] = &ibutton_protocol_misc_cyfral,
[iButtonProtocolMiscMetakom] = &ibutton_protocol_misc_metakom,
/* Add new misc protocols here */
};
@@ -0,0 +1,11 @@
#pragma once
#include <toolbox/protocols/protocol.h>
typedef enum {
iButtonProtocolMiscCyfral,
iButtonProtocolMiscMetakom,
iButtonProtocolMiscMax,
} iButtonProtocolMisc;
extern const ProtocolBase* ibutton_protocols_misc[];
@@ -1,5 +1,6 @@
#include <furi.h>
#include <furi_hal.h>
#include "protocol_metakom.h"
#define METAKOM_DATA_SIZE sizeof(uint32_t)
@@ -300,7 +301,13 @@ static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) {
return result;
}
const ProtocolBase protocol_metakom = {
static void protocol_metakom_render_brief_data(ProtocolMetakom* proto, FuriString* result) {
for(size_t i = 0; i < METAKOM_DATA_SIZE; ++i) {
furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]);
}
}
const ProtocolBase ibutton_protocol_misc_metakom = {
.name = "Metakom",
.manufacturer = "Metakom",
.data_size = METAKOM_DATA_SIZE,
@@ -317,4 +324,5 @@ const ProtocolBase protocol_metakom = {
.start = (ProtocolEncoderStart)protocol_metakom_encoder_start,
.yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield,
},
.render_brief_data = (ProtocolRenderData)protocol_metakom_render_brief_data,
};
@@ -0,0 +1,4 @@
#pragma once
#include "toolbox/protocols/protocol.h"
extern const ProtocolBase ibutton_protocol_misc_metakom;
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
typedef int32_t iButtonProtocolId;
enum {
iButtonProtocolIdInvalid = -1,
};
typedef enum {
iButtonProtocolFeatureExtData = (1U << 0),
iButtonProtocolFeatureWriteBlank = (1U << 1),
iButtonProtocolFeatureWriteCopy = (1U << 2),
} iButtonProtocolFeature;
typedef struct {
uint8_t* ptr;
size_t size;
} iButtonEditableData;
@@ -0,0 +1,6 @@
#pragma once
#include "protocol_common.h"
typedef void iButtonProtocolData;
typedef int32_t iButtonProtocolLocalId;
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include <stdbool.h>
#include <flipper_format.h>
#include "protocol_common_i.h"
typedef void iButtonProtocolGroupData;
typedef int32_t iButtonProtocolGroupId;
typedef iButtonProtocolGroupData* (*iButtonProtocolGroupAllocFunc)(void);
typedef void (*iButtonProtocolGroupFreeFunc)(iButtonProtocolGroupData*);
typedef void (*iButtonProtocolGroupRenderFunc)(
iButtonProtocolGroupData*,
const iButtonProtocolData*,
iButtonProtocolLocalId,
FuriString*);
typedef bool (*iButtonProtocolGroupIsValidFunc)(
iButtonProtocolGroupData*,
const iButtonProtocolData*,
iButtonProtocolLocalId);
typedef void (*iButtonProtocolGroupGetDataFunc)(
iButtonProtocolGroupData*,
iButtonProtocolData*,
iButtonProtocolLocalId,
iButtonEditableData*);
typedef void (*iButtonProtocolGroupApplyFunc)(
iButtonProtocolGroupData*,
iButtonProtocolData*,
iButtonProtocolLocalId);
typedef size_t (*iButtonProtocolGropuGetSizeFunc)(iButtonProtocolGroupData*);
typedef uint32_t (
*iButtonProtocolGroupGetFeaturesFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId);
typedef const char* (
*iButtonProtocolGroupGetStringFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId);
typedef bool (*iButtonProtocolGroupGetIdFunc)(
iButtonProtocolGroupData*,
iButtonProtocolLocalId*,
const char*);
typedef bool (*iButtonProtocolGroupReadFunc)(
iButtonProtocolGroupData*,
iButtonProtocolData*,
iButtonProtocolLocalId*);
typedef bool (*iButtonProtocolGroupWriteFunc)(
iButtonProtocolGroupData*,
iButtonProtocolData*,
iButtonProtocolLocalId);
typedef bool (*iButtonProtocolGroupSaveFunc)(
iButtonProtocolGroupData*,
const iButtonProtocolData*,
iButtonProtocolLocalId,
FlipperFormat*);
typedef bool (*iButtonProtocolGroupLoadFunc)(
iButtonProtocolGroupData*,
iButtonProtocolData*,
iButtonProtocolLocalId,
uint32_t,
FlipperFormat*);
typedef struct {
const uint32_t protocol_count;
iButtonProtocolGroupAllocFunc alloc;
iButtonProtocolGroupFreeFunc free;
iButtonProtocolGropuGetSizeFunc get_max_data_size;
iButtonProtocolGroupGetIdFunc get_id_by_name;
iButtonProtocolGroupGetFeaturesFunc get_features;
iButtonProtocolGroupGetStringFunc get_manufacturer;
iButtonProtocolGroupGetStringFunc get_name;
iButtonProtocolGroupReadFunc read;
iButtonProtocolGroupWriteFunc write_blank;
iButtonProtocolGroupWriteFunc write_copy;
iButtonProtocolGroupApplyFunc emulate_start;
iButtonProtocolGroupApplyFunc emulate_stop;
iButtonProtocolGroupSaveFunc save;
iButtonProtocolGroupLoadFunc load;
iButtonProtocolGroupRenderFunc render_data;
iButtonProtocolGroupRenderFunc render_brief_data;
iButtonProtocolGroupRenderFunc render_error;
iButtonProtocolGroupIsValidFunc is_valid;
iButtonProtocolGroupGetDataFunc get_editable_data;
iButtonProtocolGroupApplyFunc apply_edits;
} iButtonProtocolGroupBase;
@@ -0,0 +1,9 @@
#include "protocol_group_defs.h"
#include "dallas/protocol_group_dallas.h"
#include "misc/protocol_group_misc.h"
const iButtonProtocolGroupBase* ibutton_protocol_groups[] = {
[iButtonProtocolGroupDallas] = &ibutton_protocol_group_dallas,
[iButtonProtocolGroupMisc] = &ibutton_protocol_group_misc,
};
@@ -0,0 +1,11 @@
#pragma once
#include "protocol_group_base.h"
typedef enum {
iButtonProtocolGroupDallas,
iButtonProtocolGroupMisc,
iButtonProtocolGroupMax
} iButtonProtocolGroup;
extern const iButtonProtocolGroupBase* ibutton_protocol_groups[];
+13 -3
View File
@@ -1,5 +1,7 @@
Import("env")
from fbt.util import GLOB_FILE_EXCLUSION
env.Append(
CPPPATH=[
"#/lib/digital_signal",
@@ -26,7 +28,6 @@ sources = []
libs_recurse = [
"digital_signal",
"micro-ecc",
"one_wire",
"u8g2",
"update_util",
]
@@ -35,12 +36,21 @@ for lib in libs_recurse:
sources += libenv.GlobRecursive("*.c*", lib)
libs_plain = [
"heatshrink",
"nanopb",
]
for lib in libs_plain:
sources += Glob(lib + "/*.c*", source=True)
sources += Glob(
lib + "/*.c*",
exclude=GLOB_FILE_EXCLUSION,
source=True,
)
sources += Glob(
"heatshrink/heatshrink_*.c*",
exclude=GLOB_FILE_EXCLUSION,
source=True,
)
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
+62 -2
View File
@@ -56,6 +56,8 @@ void nfc_worker_start(
while(furi_hal_nfc_is_busy()) {
furi_delay_ms(10);
}
furi_hal_nfc_deinit();
furi_hal_nfc_init();
nfc_worker->callback = callback;
nfc_worker->context = context;
@@ -628,6 +630,32 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
}
}
static bool nfc_worker_mf_get_b_key_from_sector_trailer(
FuriHalNfcTxRxContext* tx_rx,
uint16_t sector,
uint64_t key,
uint64_t* found_key) {
// Some access conditions allow reading B key via A key
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
Crypto1 crypto = {};
MfClassicBlock block_tmp = {};
MfClassicAuthContext auth_context = {.sector = sector, .key_a = MF_CLASSIC_NO_KEY, .key_b = 0};
furi_hal_nfc_sleep();
if(mf_classic_auth_attempt(tx_rx, &crypto, &auth_context, key)) {
if(mf_classic_read_block(tx_rx, &crypto, block, &block_tmp)) {
*found_key = nfc_util_bytes2num(&block_tmp.value[10], sizeof(uint8_t) * 6);
return *found_key;
}
}
return false;
}
static void nfc_worker_mf_classic_key_attack(
NfcWorker* nfc_worker,
uint64_t key,
@@ -673,6 +701,16 @@ static void nfc_worker_mf_classic_key_attack(
mf_classic_set_key_found(data, i, MfClassicKeyA, key);
FURI_LOG_D(TAG, "Key found");
nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
uint64_t found_key;
if(nfc_worker_mf_get_b_key_from_sector_trailer(tx_rx, i, key, &found_key)) {
FURI_LOG_D(TAG, "Found B key via reading sector %d", i);
mf_classic_set_key_found(data, i, MfClassicKeyB, found_key);
if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
}
}
}
}
if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) {
@@ -762,23 +800,45 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
if(mf_classic_authenticate_skip_activate(
&tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) {
mf_classic_set_key_found(data, i, MfClassicKeyA, key);
FURI_LOG_D(TAG, "Key found");
FURI_LOG_D(TAG, "Key A found");
nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
uint64_t found_key;
if(nfc_worker_mf_get_b_key_from_sector_trailer(
&tx_rx, i, key, &found_key)) {
FURI_LOG_D(TAG, "Found B key via reading sector %d", i);
mf_classic_set_key_found(data, i, MfClassicKeyB, found_key);
if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
}
nfc_worker_mf_classic_key_attack(nfc_worker, found_key, &tx_rx, i + 1);
break;
}
nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1);
}
furi_hal_nfc_sleep();
deactivated = true;
} else {
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
is_key_a_found = false;
FURI_LOG_D(TAG, "Key %dA not found in attack", i);
}
if(!is_key_b_found) {
is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB);
if(mf_classic_authenticate_skip_activate(
&tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) {
FURI_LOG_D(TAG, "Key found");
FURI_LOG_D(TAG, "Key B found");
mf_classic_set_key_found(data, i, MfClassicKeyB, key);
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1);
}
deactivated = true;
} else {
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
is_key_b_found = false;
FURI_LOG_D(TAG, "Key %dB not found in attack", i);
}
if(is_key_a_found && is_key_b_found) break;
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
+1 -1
View File
@@ -4,7 +4,7 @@
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
#include <furi_hal.h>
#define ALL_IN_ONE_LAYOUT_UNKNOWN 0
#define ALL_IN_ONE_LAYOUT_A 1
+2 -2
View File
@@ -3,7 +3,7 @@
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
#include <furi_hal.h>
static const MfClassicAuthContext plantain_keys_4k[] = {
{.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF},
@@ -118,7 +118,7 @@ bool plantain_4k_parser_parse(NfcDeviceData* dev_data) {
}
furi_string_printf(
dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%ld\n", card_number, balance);
dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance);
return true;
}
+2 -2
View File
@@ -3,7 +3,7 @@
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
#include <furi_hal.h>
static const MfClassicAuthContext plantain_keys[] = {
{.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
@@ -91,7 +91,7 @@ bool plantain_parser_parse(NfcDeviceData* dev_data) {
}
furi_string_printf(
dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%ld\n", card_number, balance);
dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance);
return true;
}
+1 -1
View File
@@ -99,7 +99,7 @@ bool troika_4k_parser_parse(NfcDeviceData* dev_data) {
number >>= 4;
furi_string_printf(
dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance);
dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance);
return true;
}
+1 -1
View File
@@ -79,7 +79,7 @@ bool troika_parser_parse(NfcDeviceData* dev_data) {
number >>= 4;
furi_string_printf(
dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance);
dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance);
troika_parsed = true;
} while(false);
+2 -2
View File
@@ -4,7 +4,7 @@
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
#include <furi_hal.h>
static const MfClassicAuthContext two_cities_keys_4k[] = {
{.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
@@ -136,7 +136,7 @@ bool two_cities_parser_parse(NfcDeviceData* dev_data) {
furi_string_printf(
dev_data->parsed_data,
"\e#Troika+Plantain\nPN: %llu-\nPB: %ld rur.\nTN: %ld\nTB: %d rur.\n",
"\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n",
card_number,
balance,
troika_number,
+501 -97
View File
@@ -7,10 +7,17 @@
#define TAG "MfClassic"
#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U)
#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U)
#define MF_CLASSIC_READ_BLOCK_CMD (0x30)
#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0)
#define MF_CLASSIC_ACK_CMD 0xAU
#define MF_CLASSIC_NACK_BUF_VALID_CMD 0x0U
#define MF_CLASSIC_NACK_BUF_INVALID_CMD 0x4U
#define MF_CLASSIC_AUTH_KEY_A_CMD 0x60U
#define MF_CLASSIC_AUTH_KEY_B_CMD 0x61U
#define MF_CLASSIC_READ_BLOCK_CMD 0x30U
#define MF_CLASSIC_WRITE_BLOCK_CMD 0xA0U
#define MF_CLASSIC_TRANSFER_CMD 0xB0U
#define MF_CLASSIC_DECREMENT_CMD 0xC0U
#define MF_CLASSIC_INCREMENT_CMD 0xC1U
#define MF_CLASSIC_RESTORE_CMD 0xC2U
const char* mf_classic_get_type_str(MfClassicType type) {
if(type == MfClassicTypeMini) {
@@ -217,8 +224,8 @@ void mf_classic_get_read_sectors_and_keys(
uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i);
bool blocks_read = true;
for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) {
blocks_read = mf_classic_is_block_read(data, i);
for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) {
blocks_read = mf_classic_is_block_read(data, j);
if(!blocks_read) break;
}
if(blocks_read) {
@@ -284,6 +291,10 @@ bool mf_classic_is_allowed_access_data_block(
uint8_t* sector_trailer =
data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
if(block_num == 0 && action == MfClassicActionDataWrite) {
return false;
}
uint8_t sector_block;
if(block_num <= 128) {
sector_block = block_num & 0x03;
@@ -353,6 +364,16 @@ static bool mf_classic_is_allowed_access(
}
}
bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) {
// Check if key A can write, if it can, it's transport configuration, not data block
return !mf_classic_is_allowed_access_data_block(
data, block_num, MfClassicKeyA, MfClassicActionDataWrite) &&
(mf_classic_is_allowed_access_data_block(
data, block_num, MfClassicKeyB, MfClassicActionDataInc) ||
mf_classic_is_allowed_access_data_block(
data, block_num, MfClassicKeyB, MfClassicActionDataDec));
}
bool mf_classic_check_card_type(FuriHalNfcADevData* data) {
uint8_t ATQA0 = data->atqa[0];
uint8_t ATQA1 = data->atqa[1];
@@ -405,6 +426,36 @@ void mf_classic_reader_add_sector(
}
}
bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr) {
uint32_t v = *(uint32_t*)&block[0];
uint32_t v_inv = *(uint32_t*)&block[4];
uint32_t v1 = *(uint32_t*)&block[8];
bool val_checks =
((v == v1) && (v == ~v_inv) && (block[12] == (~block[13] & 0xFF)) &&
(block[14] == (~block[15] & 0xFF)) && (block[12] == block[14]));
if(value) {
*value = (int32_t)v;
}
if(addr) {
*addr = block[12];
}
return val_checks;
}
void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) {
uint32_t v_inv = ~((uint32_t)value);
memcpy(block, &value, 4); //-V1086
memcpy(block + 4, &v_inv, 4); //-V1086
memcpy(block + 8, &value, 4); //-V1086
block[12] = addr;
block[13] = ~addr & 0xFF;
block[14] = addr;
block[15] = ~addr & 0xFF;
}
void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) {
furi_assert(auth_ctx);
auth_ctx->sector = sector;
@@ -498,6 +549,7 @@ bool mf_classic_authenticate_skip_activate(
bool mf_classic_auth_attempt(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
MfClassicAuthContext* auth_ctx,
uint64_t key) {
furi_assert(tx_rx);
@@ -506,15 +558,14 @@ bool mf_classic_auth_attempt(
bool need_halt = (auth_ctx->key_a == MF_CLASSIC_NO_KEY) &&
(auth_ctx->key_b == MF_CLASSIC_NO_KEY);
Crypto1 crypto;
if(auth_ctx->key_a == MF_CLASSIC_NO_KEY) {
// Try AUTH with key A
if(mf_classic_auth(
tx_rx,
mf_classic_get_first_block_num_of_sector(auth_ctx->sector),
mf_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector),
key,
MfClassicKeyA,
&crypto,
crypto,
false,
0)) {
auth_ctx->key_a = key;
@@ -530,10 +581,10 @@ bool mf_classic_auth_attempt(
// Try AUTH with key B
if(mf_classic_auth(
tx_rx,
mf_classic_get_first_block_num_of_sector(auth_ctx->sector),
mf_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector),
key,
MfClassicKeyB,
&crypto,
crypto,
false,
0)) {
auth_ctx->key_b = key;
@@ -557,8 +608,9 @@ bool mf_classic_read_block(
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00};
nfca_append_crc16(plain_cmd, 2);
crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 4 * 9;
crypto1_encrypt(
crypto, NULL, plain_cmd, sizeof(plain_cmd) * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = sizeof(plain_cmd) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
@@ -603,7 +655,12 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
if(!key_a_found) break;
FURI_LOG_D(TAG, "Try to read blocks with key A");
key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a));
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto, false, 0)) break;
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto, false, 0)) {
mf_classic_set_key_not_found(data, sec_num, MfClassicKeyA);
FURI_LOG_D(TAG, "Key %dA not found in read", sec_num);
break;
}
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
@@ -612,7 +669,11 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
} else if(i > start_block) {
// Try to re-auth to read block in case prevous block was protected from read
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) break;
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) {
mf_classic_set_key_not_found(data, sec_num, MfClassicKeyA);
FURI_LOG_D(TAG, "Key %dA not found in read", sec_num);
break;
}
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
@@ -627,9 +688,17 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
do {
if(blocks_read == total_blocks) break;
if(!key_b_found) break;
if(key_a_found) {
furi_hal_nfc_sleep();
}
FURI_LOG_D(TAG, "Try to read blocks with key B");
key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b));
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) break;
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) {
mf_classic_set_key_not_found(data, sec_num, MfClassicKeyB);
FURI_LOG_D(TAG, "Key %dB not found in read", sec_num);
break;
}
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
@@ -638,7 +707,11 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
} else if(i > start_block) {
// Try to re-auth to read block in case prevous block was protected from read
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) break;
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) {
mf_classic_set_key_not_found(data, sec_num, MfClassicKeyB);
FURI_LOG_D(TAG, "Key %dB not found in read", sec_num);
break;
}
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
@@ -783,6 +856,9 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
bool is_encrypted = false;
uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE];
MfClassicKey access_key = MfClassicKeyA;
// Used for decrement and increment - copy to block on transfer
uint8_t transfer_buf[MF_CLASSIC_BLOCK_SIZE] = {};
bool transfer_buf_valid = false;
// Read command
while(!command_processed) { //-V654
@@ -801,18 +877,25 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
}
if(plain_data[0] == 0x50 && plain_data[1] == 0x00) {
// After increment, decrement or restore the only allowed command is transfer
uint8_t cmd = plain_data[0];
if(transfer_buf_valid && cmd != MF_CLASSIC_TRANSFER_CMD) {
break;
}
if(cmd == 0x50 && plain_data[1] == 0x00) {
FURI_LOG_T(TAG, "Halt received");
furi_hal_nfc_listen_sleep();
command_processed = true;
break;
} else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) {
}
if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD || cmd == MF_CLASSIC_AUTH_KEY_B_CMD) {
uint8_t block = plain_data[1];
uint64_t key = 0;
uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block);
MfClassicSectorTrailer* sector_trailer =
(MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value;
if(plain_data[0] == 0x60) {
if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) {
key = nfc_util_bytes2num(sector_trailer->key_a, 6);
access_key = MfClassicKeyA;
} else {
@@ -830,9 +913,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0);
memcpy(tx_rx->tx_data, nt, sizeof(nt));
tx_rx->tx_parity[0] = 0;
for(size_t i = 0; i < sizeof(nt); i++) {
tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i);
}
nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(nt));
tx_rx->tx_bits = sizeof(nt) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
} else {
@@ -853,7 +934,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
}
if(tx_rx->rx_bits != 64) {
FURI_LOG_W(TAG, "Incorrect nr + ar");
FURI_LOG_W(TAG, "Incorrect nr + ar length: %d", tx_rx->rx_bits);
command_processed = true;
break;
}
@@ -861,16 +942,6 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4);
uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4);
FURI_LOG_D(
TAG,
"%08lx key%c block %d nt/nr/ar: %08lx %08lx %08lx",
emulator->cuid,
access_key == MfClassicKeyA ? 'A' : 'B',
sector_trailer_block,
nonce,
nr,
ar);
crypto1_word(&emulator->crypto, nr, 1);
uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0);
if(cardRr != prng_successor(nonce, 64)) {
@@ -881,22 +952,30 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
}
uint32_t ans = prng_successor(nonce, 96);
uint8_t responce[4] = {};
nfc_util_num2bytes(ans, 4, responce);
uint8_t response[4] = {};
nfc_util_num2bytes(ans, 4, response);
crypto1_encrypt(
&emulator->crypto,
NULL,
responce,
sizeof(responce) * 8,
response,
sizeof(response) * 8,
tx_rx->tx_data,
tx_rx->tx_parity);
tx_rx->tx_bits = sizeof(responce) * 8;
tx_rx->tx_bits = sizeof(response) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
is_encrypted = true;
} else if(is_encrypted && plain_data[0] == 0x30) {
continue;
}
if(!is_encrypted) {
FURI_LOG_T(TAG, "Invalid command before auth session established: %02X", cmd);
break;
}
if(cmd == MF_CLASSIC_READ_BLOCK_CMD) {
uint8_t block = plain_data[1];
uint8_t block_data[18] = {};
uint8_t block_data[MF_CLASSIC_BLOCK_SIZE + 2] = {};
memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE);
if(mf_classic_is_sector_trailer(block)) {
if(!mf_classic_is_allowed_access(
@@ -931,24 +1010,24 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
sizeof(block_data) * 8,
tx_rx->tx_data,
tx_rx->tx_parity);
tx_rx->tx_bits = 18 * 8;
tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
} else if(is_encrypted && plain_data[0] == 0xA0) {
} else if(cmd == MF_CLASSIC_WRITE_BLOCK_CMD) {
uint8_t block = plain_data[1];
if(block > mf_classic_get_total_block_num(emulator->data.type)) {
break;
}
// Send ACK
uint8_t ack = 0x0A;
uint8_t ack = MF_CLASSIC_ACK_CMD;
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
tx_rx->tx_bits = 4;
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
if(tx_rx->rx_bits != 18 * 8) break;
if(tx_rx->rx_bits != (MF_CLASSIC_BLOCK_SIZE + 2) * 8) break;
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
uint8_t block_data[16] = {};
uint8_t block_data[MF_CLASSIC_BLOCK_SIZE] = {};
memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE);
if(mf_classic_is_sector_trailer(block)) {
if(mf_classic_is_allowed_access(
@@ -967,6 +1046,8 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
if(mf_classic_is_allowed_access(
emulator, block, access_key, MfClassicActionDataWrite)) {
memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE);
} else {
break;
}
}
if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != 0) {
@@ -974,19 +1055,83 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
emulator->data_changed = true;
}
// Send ACK
ack = 0x0A;
ack = MF_CLASSIC_ACK_CMD;
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
tx_rx->tx_bits = 4;
} else if(
cmd == MF_CLASSIC_DECREMENT_CMD || cmd == MF_CLASSIC_INCREMENT_CMD ||
cmd == MF_CLASSIC_RESTORE_CMD) {
uint8_t block = plain_data[1];
if(block > mf_classic_get_total_block_num(emulator->data.type)) {
break;
}
MfClassicAction action = MfClassicActionDataDec;
if(cmd == MF_CLASSIC_INCREMENT_CMD) {
action = MfClassicActionDataInc;
}
if(!mf_classic_is_allowed_access(emulator, block, access_key, action)) {
break;
}
int32_t prev_value;
uint8_t addr;
if(!mf_classic_block_to_value(emulator->data.block[block].value, &prev_value, &addr)) {
break;
}
// Send ACK
uint8_t ack = MF_CLASSIC_ACK_CMD;
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
tx_rx->tx_bits = 4;
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
if(tx_rx->rx_bits != (sizeof(int32_t) + 2) * 8) break;
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
int32_t value = *(int32_t*)&plain_data[0];
if(value < 0) {
value = -value;
}
if(cmd == MF_CLASSIC_DECREMENT_CMD) {
value = -value;
} else if(cmd == MF_CLASSIC_RESTORE_CMD) {
value = 0;
}
mf_classic_value_to_block(prev_value + value, addr, transfer_buf);
transfer_buf_valid = true;
// Commands do not ACK
tx_rx->tx_bits = 0;
} else if(cmd == MF_CLASSIC_TRANSFER_CMD) {
uint8_t block = plain_data[1];
if(!mf_classic_is_allowed_access(emulator, block, access_key, MfClassicActionDataDec)) {
break;
}
if(memcmp(transfer_buf, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) !=
0) {
memcpy(emulator->data.block[block].value, transfer_buf, MF_CLASSIC_BLOCK_SIZE);
emulator->data_changed = true;
}
transfer_buf_valid = false;
uint8_t ack = MF_CLASSIC_ACK_CMD;
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
tx_rx->tx_bits = 4;
} else {
// Unknown command
FURI_LOG_T(TAG, "Unknown command: %02X", cmd);
break;
}
}
if(!command_processed) {
// Send NACK
uint8_t nack = 0x04;
uint8_t nack = transfer_buf_valid ? MF_CLASSIC_NACK_BUF_VALID_CMD :
MF_CLASSIC_NACK_BUF_INVALID_CMD;
if(is_encrypted) {
crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
} else {
@@ -1000,37 +1145,50 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
return true;
}
void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) {
furi_assert(tx_rx);
uint8_t plain_data[4] = {0x50, 0x00, 0x00, 0x00};
nfca_append_crc16(plain_data, 2);
if(crypto) {
crypto1_encrypt(
crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity);
} else {
memcpy(tx_rx->tx_data, plain_data, sizeof(plain_data));
nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(plain_data));
}
tx_rx->tx_bits = sizeof(plain_data) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
furi_hal_nfc_tx_rx(tx_rx, 50);
}
bool mf_classic_write_block(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
Crypto1* crypto,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key) {
MfClassicBlock* src_block) {
furi_assert(tx_rx);
furi_assert(crypto);
furi_assert(src_block);
Crypto1 crypto = {};
uint8_t plain_data[18] = {};
uint8_t resp = 0;
bool write_success = false;
uint8_t plain_data[MF_CLASSIC_BLOCK_SIZE + 2] = {};
uint8_t resp;
do {
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) {
FURI_LOG_D(TAG, "Auth fail");
break;
}
// Send write command
plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD;
plain_data[1] = block_num;
nfca_append_crc16(plain_data, 2);
crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 4 * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp);
break;
@@ -1048,7 +1206,7 @@ bool mf_classic_write_block(
memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE);
nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE);
crypto1_encrypt(
&crypto,
crypto,
NULL,
plain_data,
(MF_CLASSIC_BLOCK_SIZE + 2) * 8,
@@ -1058,8 +1216,8 @@ bool mf_classic_write_block(
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp);
if(resp != MF_CLASSIC_ACK_CMD) {
FURI_LOG_D(TAG, "NACK received on sending data");
break;
}
@@ -1071,22 +1229,200 @@ bool mf_classic_write_block(
FURI_LOG_D(TAG, "Failed to send data");
break;
}
write_success = true;
// Send Halt
plain_data[0] = 0x50;
plain_data[1] = 0x00;
nfca_append_crc16(plain_data, 2);
crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 2 * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
// No response is expected
furi_hal_nfc_tx_rx(tx_rx, 50);
write_success = true;
} while(false);
return write_success;
}
bool mf_classic_auth_write_block(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key) {
furi_assert(tx_rx);
furi_assert(src_block);
Crypto1 crypto = {};
bool write_success = false;
do {
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) {
FURI_LOG_D(TAG, "Auth fail");
break;
}
if(!mf_classic_write_block(tx_rx, &crypto, block_num, src_block)) {
FURI_LOG_D(TAG, "Write fail");
break;
}
write_success = true;
mf_classic_halt(tx_rx, &crypto);
} while(false);
return write_success;
}
bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num) {
furi_assert(tx_rx);
furi_assert(crypto);
// Send transfer command
uint8_t plain_data[4] = {MF_CLASSIC_TRANSFER_CMD, block_num, 0, 0};
uint8_t resp = 0;
bool transfer_success = false;
nfca_append_crc16(plain_data, 2);
crypto1_encrypt(
crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = sizeof(plain_data) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
do {
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp);
break;
}
} else {
FURI_LOG_D(TAG, "Not ACK received");
break;
}
} else {
FURI_LOG_D(TAG, "Failed to send transfer cmd");
break;
}
transfer_success = true;
} while(false);
return transfer_success;
}
bool mf_classic_value_cmd(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
uint8_t block_num,
uint8_t cmd,
int32_t d_value) {
furi_assert(tx_rx);
furi_assert(crypto);
furi_assert(
cmd == MF_CLASSIC_INCREMENT_CMD || cmd == MF_CLASSIC_DECREMENT_CMD ||
cmd == MF_CLASSIC_RESTORE_CMD);
furi_assert(d_value >= 0);
uint8_t plain_data[sizeof(d_value) + 2] = {};
uint8_t resp = 0;
bool success = false;
do {
// Send cmd
plain_data[0] = cmd;
plain_data[1] = block_num;
nfca_append_crc16(plain_data, 2);
crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 4 * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp);
break;
}
} else {
FURI_LOG_D(TAG, "Not ACK received");
break;
}
} else {
FURI_LOG_D(TAG, "Failed to send write cmd");
break;
}
// Send data
memcpy(plain_data, &d_value, sizeof(d_value));
nfca_append_crc16(plain_data, sizeof(d_value));
crypto1_encrypt(
crypto, NULL, plain_data, (sizeof(d_value) + 2) * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = (sizeof(d_value) + 2) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
// inc, dec, restore do not ACK, but they do NACK
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp);
break;
}
} else {
FURI_LOG_D(TAG, "Not NACK received");
break;
}
}
success = true;
} while(false);
return success;
}
bool mf_classic_value_cmd_full(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key,
int32_t d_value) {
furi_assert(tx_rx);
furi_assert(src_block);
Crypto1 crypto = {};
uint8_t cmd;
bool success = false;
if(d_value > 0) {
cmd = MF_CLASSIC_INCREMENT_CMD;
} else if(d_value < 0) {
cmd = MF_CLASSIC_DECREMENT_CMD;
d_value = -d_value;
} else {
cmd = MF_CLASSIC_RESTORE_CMD;
}
do {
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) {
FURI_LOG_D(TAG, "Value cmd auth fail");
break;
}
if(!mf_classic_value_cmd(tx_rx, &crypto, block_num, cmd, d_value)) {
FURI_LOG_D(TAG, "Value cmd inc/dec/res fail");
break;
}
if(!mf_classic_transfer(tx_rx, &crypto, block_num)) {
FURI_LOG_D(TAG, "Value cmd transfer fail");
break;
}
success = true;
// Send Halt
mf_classic_halt(tx_rx, &crypto);
} while(false);
return success;
}
bool mf_classic_write_sector(
FuriHalNfcTxRxContext* tx_rx,
MfClassicData* dest_data,
@@ -1107,31 +1443,99 @@ bool mf_classic_write_sector(
// Compare blocks
if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE) !=
0) {
bool key_a_write_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyA, MfClassicActionDataWrite);
bool key_b_write_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyB, MfClassicActionDataWrite);
if(mf_classic_is_value_block(dest_data, i)) {
bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyA, MfClassicActionDataInc);
bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyB, MfClassicActionDataInc);
bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyA, MfClassicActionDataDec);
bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyB, MfClassicActionDataDec);
if(key_a_found && key_a_write_allowed) {
FURI_LOG_I(TAG, "Writing block %d with key A", i);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) {
FURI_LOG_E(TAG, "Failed to write block %d", i);
write_success = false;
break;
}
} else if(key_b_found && key_b_write_allowed) {
FURI_LOG_I(TAG, "Writing block %d with key A", i);
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) {
FURI_LOG_E(TAG, "Failed to write block %d", i);
write_success = false;
break;
int32_t src_value, dst_value;
mf_classic_block_to_value(src_data->block[i].value, &src_value, NULL);
mf_classic_block_to_value(dest_data->block[i].value, &dst_value, NULL);
int32_t diff = src_value - dst_value;
if(diff > 0) {
if(key_a_found && key_a_inc_allowed) {
FURI_LOG_I(TAG, "Incrementing block %d with key A by %ld", i, diff);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(!mf_classic_value_cmd_full(
tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) {
FURI_LOG_E(TAG, "Failed to increment block %d", i);
write_success = false;
break;
}
} else if(key_b_found && key_b_inc_allowed) {
FURI_LOG_I(TAG, "Incrementing block %d with key B by %ld", i, diff);
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
if(!mf_classic_value_cmd_full(
tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) {
FURI_LOG_E(TAG, "Failed to increment block %d", i);
write_success = false;
break;
}
} else {
FURI_LOG_E(TAG, "Failed to increment block %d", i);
}
} else if(diff < 0) {
if(key_a_found && key_a_dec_allowed) {
FURI_LOG_I(TAG, "Decrementing block %d with key A by %ld", i, -diff);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(!mf_classic_value_cmd_full(
tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) {
FURI_LOG_E(TAG, "Failed to decrement block %d", i);
write_success = false;
break;
}
} else if(key_b_found && key_b_dec_allowed) {
FURI_LOG_I(TAG, "Decrementing block %d with key B by %ld", i, diff);
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
if(!mf_classic_value_cmd_full(
tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) {
FURI_LOG_E(TAG, "Failed to decrement block %d", i);
write_success = false;
break;
}
} else {
FURI_LOG_E(TAG, "Failed to decrement block %d", i);
}
} else {
FURI_LOG_E(TAG, "Value block %d address changed, cannot write it", i);
}
} else {
FURI_LOG_E(TAG, "Failed to find key with write access");
write_success = false;
break;
bool key_a_write_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyA, MfClassicActionDataWrite);
bool key_b_write_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyB, MfClassicActionDataWrite);
if(key_a_found && key_a_write_allowed) {
FURI_LOG_I(TAG, "Writing block %d with key A", i);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(!mf_classic_auth_write_block(
tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) {
FURI_LOG_E(TAG, "Failed to write block %d", i);
write_success = false;
break;
}
} else if(key_b_found && key_b_write_allowed) {
FURI_LOG_I(TAG, "Writing block %d with key A", i);
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
if(!mf_classic_auth_write_block(
tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) {
FURI_LOG_E(TAG, "Failed to write block %d", i);
write_success = false;
break;
}
} else {
FURI_LOG_E(TAG, "Failed to find key with write access");
write_success = false;
break;
}
}
} else {
FURI_LOG_D(TAG, "Blocks %d are equal", i);
+38
View File
@@ -120,6 +120,12 @@ bool mf_classic_is_allowed_access_data_block(
MfClassicKey key,
MfClassicAction action);
bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num);
bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr);
void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block);
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
void mf_classic_set_key_found(
@@ -168,6 +174,7 @@ bool mf_classic_authenticate_skip_activate(
bool mf_classic_auth_attempt(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
MfClassicAuthContext* auth_ctx,
uint64_t key);
@@ -177,6 +184,12 @@ void mf_classic_reader_add_sector(
uint64_t key_a,
uint64_t key_b);
bool mf_classic_read_block(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
uint8_t block_num,
MfClassicBlock* block);
void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num);
uint8_t mf_classic_read_card(
@@ -188,13 +201,38 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx);
void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto);
bool mf_classic_write_block(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
uint8_t block_num,
MfClassicBlock* src_block);
bool mf_classic_auth_write_block(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key);
bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num);
bool mf_classic_value_cmd(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
uint8_t block_num,
uint8_t cmd,
int32_t d_value);
bool mf_classic_value_cmd_full(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key,
int32_t d_value);
bool mf_classic_write_sector(
FuriHalNfcTxRxContext* tx_rx,
MfClassicData* dest_data,
+1 -1
View File
@@ -3,7 +3,7 @@
#include "mifare_ultralight.h"
#include "nfc_util.h"
#include <furi.h>
#include "furi_hal_nfc.h"
#include <furi_hal_nfc.h>
#define TAG "MfUltralight"
+24 -1
View File
@@ -23,7 +23,7 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest) {
}
}
uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len) {
uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) {
furi_assert(src);
furi_assert(len <= 8);
@@ -45,3 +45,26 @@ uint8_t nfc_util_even_parity32(uint32_t data) {
uint8_t nfc_util_odd_parity8(uint8_t data) {
return nfc_util_odd_byte_parity[data];
}
void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len) {
furi_assert(src);
furi_assert(dst);
uint8_t parity = 0;
uint8_t bit = 0;
while(len--) {
parity |= nfc_util_odd_parity8(*src) << (7 - bit); // parity is MSB first
bit++;
if(bit == 8) {
*dst = parity;
dst++;
parity = 0;
bit = 0;
}
src++;
}
if(bit) {
*dst = parity;
}
}
+3 -1
View File
@@ -4,8 +4,10 @@
void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest);
uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len);
uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len);
uint8_t nfc_util_even_parity32(uint32_t data);
uint8_t nfc_util_odd_parity8(uint8_t data);
void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len);
+24
View File
@@ -0,0 +1,24 @@
Import("env")
env.Append(
LINT_SOURCES=[
Dir("."),
],
CPPPATH=[
"#/lib/one_wire",
],
SDK_HEADERS=[
File("one_wire_host.h"),
File("one_wire_slave.h"),
File("maxim_crc.h"),
],
)
libenv = env.Clone(FW_LIB_NAME="one_wire")
libenv.ApplyLibFlags()
sources = libenv.GlobRecursive("*.c*")
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
Return("lib")
-110
View File
@@ -1,110 +0,0 @@
#include <furi.h>
#include <one_wire/maxim_crc.h>
#include "ibutton_key.h"
struct iButtonKey {
uint8_t data[IBUTTON_KEY_DATA_SIZE];
iButtonKeyType type;
};
iButtonKey* ibutton_key_alloc() {
iButtonKey* key = malloc(sizeof(iButtonKey));
memset(key, 0, sizeof(iButtonKey));
return key;
}
void ibutton_key_free(iButtonKey* key) {
free(key);
}
void ibutton_key_set(iButtonKey* to, const iButtonKey* from) {
memcpy(to, from, sizeof(iButtonKey));
}
void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count) {
furi_check(data_count > 0);
furi_check(data_count <= IBUTTON_KEY_DATA_SIZE);
memset(key->data, 0, IBUTTON_KEY_DATA_SIZE);
memcpy(key->data, data, data_count);
}
void ibutton_key_clear_data(iButtonKey* key) {
memset(key->data, 0, IBUTTON_KEY_DATA_SIZE);
}
const uint8_t* ibutton_key_get_data_p(iButtonKey* key) {
return key->data;
}
uint8_t ibutton_key_get_data_size(iButtonKey* key) {
return ibutton_key_get_size_by_type(key->type);
}
void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type) {
key->type = key_type;
}
iButtonKeyType ibutton_key_get_type(iButtonKey* key) {
return key->type;
}
const char* ibutton_key_get_string_by_type(iButtonKeyType key_type) {
switch(key_type) {
case iButtonKeyCyfral:
return "Cyfral";
break;
case iButtonKeyMetakom:
return "Metakom";
break;
case iButtonKeyDS1990:
return "Dallas";
break;
default:
furi_crash("Invalid iButton type");
}
}
bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type) {
if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyCyfral)) == 0) {
*key_type = iButtonKeyCyfral;
} else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyMetakom)) == 0) {
*key_type = iButtonKeyMetakom;
} else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyDS1990)) == 0) {
*key_type = iButtonKeyDS1990;
} else {
return false;
}
return true;
}
uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type) {
uint8_t size = 0;
switch(key_type) {
case iButtonKeyCyfral:
size = 2;
break;
case iButtonKeyMetakom:
size = 4;
break;
case iButtonKeyDS1990:
size = 8;
break;
}
return size;
}
uint8_t ibutton_key_get_max_size() {
return IBUTTON_KEY_DATA_SIZE;
}
bool ibutton_key_dallas_crc_is_valid(iButtonKey* key) {
return (maxim_crc8(key->data, 8, MAXIM_CRC8_INIT) == 0);
}
bool ibutton_key_dallas_is_1990_key(iButtonKey* key) {
return (key->data[0] == 0x01);
}
-132
View File
@@ -1,132 +0,0 @@
/**
* @file ibutton_key.h
*
* iButton key data holder
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define IBUTTON_KEY_DATA_SIZE 8
#define IBUTTON_KEY_NAME_SIZE 22
typedef enum {
iButtonKeyDS1990,
iButtonKeyCyfral,
iButtonKeyMetakom,
} iButtonKeyType;
typedef struct iButtonKey iButtonKey;
/**
* Allocate key
* @return iButtonKey*
*/
iButtonKey* ibutton_key_alloc();
/**
* Free key
* @param key
*/
void ibutton_key_free(iButtonKey* key);
/**
* Copy key
* @param to
* @param from
*/
void ibutton_key_set(iButtonKey* to, const iButtonKey* from);
/**
* Set key data
* @param key
* @param data
* @param data_count
*/
void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count);
/**
* Clear key data
* @param key
*/
void ibutton_key_clear_data(iButtonKey* key);
/**
* Get pointer to key data
* @param key
* @return const uint8_t*
*/
const uint8_t* ibutton_key_get_data_p(iButtonKey* key);
/**
* Get key data size
* @param key
* @return uint8_t
*/
uint8_t ibutton_key_get_data_size(iButtonKey* key);
/**
* Set key type
* @param key
* @param key_type
*/
void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type);
/**
* Get key type
* @param key
* @return iButtonKeyType
*/
iButtonKeyType ibutton_key_get_type(iButtonKey* key);
/**
* Get type string from key type
* @param key_type
* @return const char*
*/
const char* ibutton_key_get_string_by_type(iButtonKeyType key_type);
/**
* Get key type from string
* @param type_string
* @param key_type
* @return bool
*/
bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type);
/**
* Get key data size from type
* @param key_type
* @return uint8_t
*/
uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type);
/**
* Get max key size
* @return uint8_t
*/
uint8_t ibutton_key_get_max_size();
/**
* Check if CRC for onewire key is valid
* @param key
* @return true
* @return false
*/
bool ibutton_key_dallas_crc_is_valid(iButtonKey* key);
/**
* Check if onewire key is a DS1990 key
* @param key
* @return true
* @return false
*/
bool ibutton_key_dallas_is_1990_key(iButtonKey* key);
#ifdef __cplusplus
}
#endif
@@ -1,28 +0,0 @@
/**
* @file ibutton_key_command.h
*
* List of misc commands for Dallas and blanks
*/
#pragma once
#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1
#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5
#define RW1990_1_CMD_WRITE_ROM 0xD5
#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D
#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E
#define RW1990_2_CMD_WRITE_ROM 0xD5
#define TM2004_CMD_READ_STATUS 0xAA
#define TM2004_CMD_READ_MEMORY 0xF0
#define TM2004_CMD_WRITE_ROM 0x3C
#define TM2004_CMD_FINALIZATION 0x35
#define TM2004_ANSWER_READ_MEMORY 0xF5
#define TM01_CMD_WRITE_RECORD_FLAG 0xC1
#define TM01_CMD_WRITE_ROM 0xC5
#define TM01_CMD_SWITCH_TO_CYFRAL 0xCA
#define TM01_CMD_SWITCH_TO_METAKOM 0xCB
#define DS1990_CMD_READ_ROM 0x33
-352
View File
@@ -1,352 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include "ibutton_worker_i.h"
#include "ibutton_key_command.h"
void ibutton_worker_mode_idle_start(iButtonWorker* worker);
void ibutton_worker_mode_idle_tick(iButtonWorker* worker);
void ibutton_worker_mode_idle_stop(iButtonWorker* worker);
void ibutton_worker_mode_emulate_start(iButtonWorker* worker);
void ibutton_worker_mode_emulate_tick(iButtonWorker* worker);
void ibutton_worker_mode_emulate_stop(iButtonWorker* worker);
void ibutton_worker_mode_read_start(iButtonWorker* worker);
void ibutton_worker_mode_read_tick(iButtonWorker* worker);
void ibutton_worker_mode_read_stop(iButtonWorker* worker);
void ibutton_worker_mode_write_start(iButtonWorker* worker);
void ibutton_worker_mode_write_tick(iButtonWorker* worker);
void ibutton_worker_mode_write_stop(iButtonWorker* worker);
const iButtonWorkerModeType ibutton_worker_modes[] = {
{
.quant = FuriWaitForever,
.start = ibutton_worker_mode_idle_start,
.tick = ibutton_worker_mode_idle_tick,
.stop = ibutton_worker_mode_idle_stop,
},
{
.quant = 100,
.start = ibutton_worker_mode_read_start,
.tick = ibutton_worker_mode_read_tick,
.stop = ibutton_worker_mode_read_stop,
},
{
.quant = 1000,
.start = ibutton_worker_mode_write_start,
.tick = ibutton_worker_mode_write_tick,
.stop = ibutton_worker_mode_write_stop,
},
{
.quant = 1000,
.start = ibutton_worker_mode_emulate_start,
.tick = ibutton_worker_mode_emulate_tick,
.stop = ibutton_worker_mode_emulate_stop,
},
};
/*********************** IDLE ***********************/
void ibutton_worker_mode_idle_start(iButtonWorker* worker) {
UNUSED(worker);
}
void ibutton_worker_mode_idle_tick(iButtonWorker* worker) {
UNUSED(worker);
}
void ibutton_worker_mode_idle_stop(iButtonWorker* worker) {
UNUSED(worker);
}
/*********************** READ ***********************/
typedef struct {
uint32_t last_dwt_value;
FuriStreamBuffer* stream;
} iButtonReadContext;
void ibutton_worker_comparator_callback(bool level, void* context) {
iButtonReadContext* read_context = context;
uint32_t current_dwt_value = DWT->CYCCNT;
LevelDuration data =
level_duration_make(level, current_dwt_value - read_context->last_dwt_value);
furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0);
read_context->last_dwt_value = current_dwt_value;
}
bool ibutton_worker_read_comparator(iButtonWorker* worker) {
bool result = false;
protocol_dict_decoders_start(worker->protocols);
furi_hal_rfid_pins_reset();
// pulldown pull pin, we sense the signal through the analog part of the RFID schematic
furi_hal_rfid_pin_pull_pulldown();
iButtonReadContext read_context = {
.last_dwt_value = DWT->CYCCNT,
.stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1),
};
furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, &read_context);
furi_hal_rfid_comp_start();
uint32_t tick_start = furi_get_tick();
while(true) {
LevelDuration level;
size_t ret =
furi_stream_buffer_receive(read_context.stream, &level, sizeof(LevelDuration), 100);
if((furi_get_tick() - tick_start) > 100) {
break;
}
if(ret > 0) {
ProtocolId decoded_index = protocol_dict_decoders_feed(
worker->protocols,
level_duration_get_level(level),
level_duration_get_duration(level));
if(decoded_index == PROTOCOL_NO) continue;
protocol_dict_get_data(
worker->protocols, decoded_index, worker->key_data, ibutton_key_get_max_size());
switch(decoded_index) {
case iButtonProtocolCyfral:
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyCyfral);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
result = true;
break;
case iButtonProtocolMetakom:
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyMetakom);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
result = true;
break;
default:
break;
}
}
}
furi_hal_rfid_comp_stop();
furi_hal_rfid_comp_set_callback(NULL, NULL);
furi_hal_rfid_pins_reset();
furi_stream_buffer_free(read_context.stream);
return result;
}
bool ibutton_worker_read_dallas(iButtonWorker* worker) {
bool result = false;
onewire_host_start(worker->host);
furi_delay_ms(100);
FURI_CRITICAL_ENTER();
if(onewire_host_search(worker->host, worker->key_data, NORMAL_SEARCH)) {
onewire_host_reset_search(worker->host);
// key found, verify
if(onewire_host_reset(worker->host)) {
onewire_host_write(worker->host, DS1990_CMD_READ_ROM);
bool key_valid = true;
for(uint8_t i = 0; i < ibutton_key_get_max_size(); i++) {
if(onewire_host_read(worker->host) != worker->key_data[i]) {
key_valid = false;
break;
}
}
if(key_valid) {
result = true;
furi_check(worker->key_p != NULL);
ibutton_key_set_type(worker->key_p, iButtonKeyDS1990);
ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
}
}
} else {
onewire_host_reset_search(worker->host);
}
onewire_host_stop(worker->host);
FURI_CRITICAL_EXIT();
return result;
}
void ibutton_worker_mode_read_start(iButtonWorker* worker) {
UNUSED(worker);
furi_hal_power_enable_otg();
}
void ibutton_worker_mode_read_tick(iButtonWorker* worker) {
bool valid = false;
if(ibutton_worker_read_dallas(worker)) {
valid = true;
} else if(ibutton_worker_read_comparator(worker)) {
valid = true;
}
if(valid) {
if(worker->read_cb != NULL) {
worker->read_cb(worker->cb_ctx);
}
ibutton_worker_switch_mode(worker, iButtonWorkerIdle);
}
}
void ibutton_worker_mode_read_stop(iButtonWorker* worker) {
UNUSED(worker);
furi_hal_power_disable_otg();
}
/*********************** EMULATE ***********************/
static void onewire_slave_callback(void* context) {
furi_assert(context);
iButtonWorker* worker = context;
ibutton_worker_notify_emulate(worker);
}
void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) {
uint8_t* device_id = onewire_device_get_id_p(worker->device);
const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p);
const uint8_t key_size = ibutton_key_get_max_size();
memcpy(device_id, key_id, key_size);
onewire_slave_attach(worker->slave, worker->device);
onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker);
onewire_slave_start(worker->slave);
}
void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) {
onewire_slave_stop(worker->slave);
onewire_slave_detach(worker->slave);
}
void ibutton_worker_emulate_timer_cb(void* context) {
furi_assert(context);
iButtonWorker* worker = context;
LevelDuration level =
protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode);
furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level));
if(level_duration_get_level(level)) {
furi_hal_ibutton_pin_high();
} else {
furi_hal_ibutton_pin_low();
}
}
void ibutton_worker_emulate_timer_start(iButtonWorker* worker) {
furi_assert(worker->key_p);
const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p);
const uint8_t key_size = ibutton_key_get_max_size();
switch(ibutton_key_get_type(worker->key_p)) {
case iButtonKeyDS1990:
return;
break;
case iButtonKeyCyfral:
worker->protocol_to_encode = iButtonProtocolCyfral;
break;
case iButtonKeyMetakom:
worker->protocol_to_encode = iButtonProtocolMetakom;
break;
}
protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size);
protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode);
furi_hal_ibutton_start_drive();
furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker);
}
void ibutton_worker_emulate_timer_stop(iButtonWorker* worker) {
UNUSED(worker);
furi_hal_ibutton_emulate_stop();
}
void ibutton_worker_mode_emulate_start(iButtonWorker* worker) {
furi_assert(worker->key_p);
furi_hal_rfid_pins_reset();
furi_hal_rfid_pin_pull_pulldown();
switch(ibutton_key_get_type(worker->key_p)) {
case iButtonKeyDS1990:
ibutton_worker_emulate_dallas_start(worker);
break;
case iButtonKeyCyfral:
case iButtonKeyMetakom:
ibutton_worker_emulate_timer_start(worker);
break;
}
}
void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) {
UNUSED(worker);
}
void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) {
furi_assert(worker->key_p);
furi_hal_rfid_pins_reset();
switch(ibutton_key_get_type(worker->key_p)) {
case iButtonKeyDS1990:
ibutton_worker_emulate_dallas_stop(worker);
break;
case iButtonKeyCyfral:
case iButtonKeyMetakom:
ibutton_worker_emulate_timer_stop(worker);
break;
}
}
/*********************** WRITE ***********************/
void ibutton_worker_mode_write_start(iButtonWorker* worker) {
furi_hal_power_enable_otg();
onewire_host_start(worker->host);
}
void ibutton_worker_mode_write_tick(iButtonWorker* worker) {
furi_check(worker->key_p != NULL);
iButtonWriterResult writer_result = ibutton_writer_write(worker->writer, worker->key_p);
iButtonWorkerWriteResult result;
switch(writer_result) {
case iButtonWriterOK:
result = iButtonWorkerWriteOK;
break;
case iButtonWriterSameKey:
result = iButtonWorkerWriteSameKey;
break;
case iButtonWriterNoDetect:
result = iButtonWorkerWriteNoDetect;
break;
case iButtonWriterCannotWrite:
result = iButtonWorkerWriteCannotWrite;
break;
default:
result = iButtonWorkerWriteNoDetect;
break;
}
if(worker->write_cb != NULL) {
worker->write_cb(worker->cb_ctx, result);
}
}
void ibutton_worker_mode_write_stop(iButtonWorker* worker) {
furi_hal_power_disable_otg();
onewire_host_stop(worker->host);
}
-298
View File
@@ -1,298 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include "ibutton_writer.h"
#include "ibutton_key_command.h"
/*********************** PRIVATE ***********************/
struct iButtonWriter {
OneWireHost* host;
};
static void writer_write_one_bit(iButtonWriter* writer, bool value, uint32_t delay) {
onewire_host_write_bit(writer->host, value);
furi_delay_us(delay);
}
static void writer_write_byte_ds1990(iButtonWriter* writer, uint8_t data) {
for(uint8_t n_bit = 0; n_bit < 8; n_bit++) {
onewire_host_write_bit(writer->host, data & 1);
furi_delay_us(5000);
data = data >> 1;
}
}
static bool writer_compare_key_ds1990(iButtonWriter* writer, iButtonKey* key) {
bool result = false;
if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
FURI_CRITICAL_ENTER();
bool presence = onewire_host_reset(writer->host);
if(presence) {
onewire_host_write(writer->host, DS1990_CMD_READ_ROM);
result = true;
for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
if(ibutton_key_get_data_p(key)[i] != onewire_host_read(writer->host)) {
result = false;
break;
}
}
}
FURI_CRITICAL_EXIT();
}
return result;
}
static bool writer_write_TM2004(iButtonWriter* writer, iButtonKey* key) {
uint8_t answer;
bool result = true;
if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
FURI_CRITICAL_ENTER();
// write rom, addr is 0x0000
onewire_host_reset(writer->host);
onewire_host_write(writer->host, TM2004_CMD_WRITE_ROM);
onewire_host_write(writer->host, 0x00);
onewire_host_write(writer->host, 0x00);
// write key
for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
// write key byte
onewire_host_write(writer->host, ibutton_key_get_data_p(key)[i]);
answer = onewire_host_read(writer->host);
// TODO: check answer CRC
// pulse indicating that data is correct
furi_delay_us(600);
writer_write_one_bit(writer, 1, 50000);
// read written key byte
answer = onewire_host_read(writer->host); //-V519
// check that written and read are same
if(ibutton_key_get_data_p(key)[i] != answer) {
result = false;
break;
}
}
if(!writer_compare_key_ds1990(writer, key)) {
result = false;
}
onewire_host_reset(writer->host);
FURI_CRITICAL_EXIT();
} else {
result = false;
}
return result;
}
static bool writer_write_1990_1(iButtonWriter* writer, iButtonKey* key) {
bool result = false;
if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
FURI_CRITICAL_ENTER();
// unlock
onewire_host_reset(writer->host);
onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG);
furi_delay_us(10);
writer_write_one_bit(writer, 0, 5000);
// write key
onewire_host_reset(writer->host);
onewire_host_write(writer->host, RW1990_1_CMD_WRITE_ROM);
for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
// inverted key for RW1990.1
writer_write_byte_ds1990(writer, ~ibutton_key_get_data_p(key)[i]);
furi_delay_us(30000);
}
// lock
onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG);
writer_write_one_bit(writer, 1, 10000);
FURI_CRITICAL_EXIT();
if(writer_compare_key_ds1990(writer, key)) {
result = true;
}
}
return result;
}
static bool writer_write_1990_2(iButtonWriter* writer, iButtonKey* key) {
bool result = false;
if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
FURI_CRITICAL_ENTER();
// unlock
onewire_host_reset(writer->host);
onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG);
furi_delay_us(10);
writer_write_one_bit(writer, 1, 5000);
// write key
onewire_host_reset(writer->host);
onewire_host_write(writer->host, RW1990_2_CMD_WRITE_ROM);
for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
writer_write_byte_ds1990(writer, ibutton_key_get_data_p(key)[i]);
furi_delay_us(30000);
}
// lock
onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG);
writer_write_one_bit(writer, 0, 10000);
FURI_CRITICAL_EXIT();
if(writer_compare_key_ds1990(writer, key)) {
result = true;
}
}
return result;
}
/*
// TODO: adapt and test
static bool writer_write_TM01(
iButtonWriter* writer,
iButtonKey type,
const uint8_t* key,
uint8_t key_length) {
bool result = true;
{
// TODO test and encoding
FURI_CRITICAL_ENTER();
// unlock
onewire_host_reset(writer->host);
onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG);
onewire_write_one_bit(1, 10000);
// write key
onewire_host_reset(writer->host);
onewire_host_write(writer->host, TM01::CMD_WRITE_ROM);
// TODO: key types
//if(type == KEY_METAKOM || type == KEY_CYFRAL) {
//} else {
for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
write_byte_ds1990(key->get_data()[i]);
furi_delay_us(10000);
}
//}
// lock
onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG);
onewire_write_one_bit(0, 10000);
FURI_CRITICAL_EXIT();
}
if(!compare_key_ds1990(key)) {
result = false;
}
{
FURI_CRITICAL_ENTER();
if(key->get_key_type() == iButtonKeyType::KeyMetakom ||
key->get_key_type() == iButtonKeyType::KeyCyfral) {
onewire_host_reset(writer->host);
if(key->get_key_type() == iButtonKeyType::KeyCyfral)
onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_CYFRAL);
else
onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_METAKOM);
onewire_write_one_bit(1);
}
FURI_CRITICAL_EXIT();
}
return result;
}
*/
static iButtonWriterResult writer_write_DS1990(iButtonWriter* writer, iButtonKey* key) {
iButtonWriterResult result = iButtonWriterNoDetect;
bool same_key = writer_compare_key_ds1990(writer, key);
if(!same_key) {
// currently we can write:
// RW1990_1, TM08v2, TM08vi-2 by write_1990_1()
// RW1990_2 by write_1990_2()
// RW2004, RW2004, TM2004 with EEPROM by write_TM2004();
bool write_result = true;
do {
if(writer_write_1990_1(writer, key)) break;
if(writer_write_1990_2(writer, key)) break;
if(writer_write_TM2004(writer, key)) break;
write_result = false;
} while(false);
if(write_result) {
result = iButtonWriterOK;
} else {
result = iButtonWriterCannotWrite;
}
} else {
result = iButtonWriterSameKey;
}
return result;
}
/*********************** PUBLIC ***********************/
iButtonWriter* ibutton_writer_alloc(OneWireHost* host) {
iButtonWriter* writer = malloc(sizeof(iButtonWriter));
writer->host = host;
return writer;
}
void ibutton_writer_free(iButtonWriter* writer) {
free(writer);
}
iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key) {
iButtonWriterResult result = iButtonWriterNoDetect;
furi_kernel_lock();
bool blank_present = onewire_host_reset(writer->host);
furi_kernel_unlock();
if(blank_present) {
switch(ibutton_key_get_type(key)) {
case iButtonKeyDS1990:
result = writer_write_DS1990(writer, key);
default:
break;
}
}
return result;
}
void ibutton_writer_start(iButtonWriter* writer) {
furi_hal_power_enable_otg();
onewire_host_start(writer->host);
}
void ibutton_writer_stop(iButtonWriter* writer) {
onewire_host_stop(writer->host);
furi_hal_power_disable_otg();
}
-60
View File
@@ -1,60 +0,0 @@
/**
* @file ibutton_writer.h
*
* iButton blanks writer
*/
#pragma once
#include <furi_hal_gpio.h>
#include "ibutton_key.h"
#include "../one_wire_host.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
iButtonWriterOK,
iButtonWriterSameKey,
iButtonWriterNoDetect,
iButtonWriterCannotWrite,
} iButtonWriterResult;
typedef struct iButtonWriter iButtonWriter;
/**
* Allocate writer
* @param host
* @return iButtonWriter*
*/
iButtonWriter* ibutton_writer_alloc(OneWireHost* host);
/**
* Deallocate writer
* @param writer
*/
void ibutton_writer_free(iButtonWriter* writer);
/**
* Write key to blank
* @param writer
* @param key
* @return iButtonWriterResult
*/
iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key);
/**
* Start writing. Must be called before write attempt
* @param writer
*/
void ibutton_writer_start(iButtonWriter* writer);
/**
* Stop writing
* @param writer
*/
void ibutton_writer_stop(iButtonWriter* writer);
#ifdef __cplusplus
}
#endif
@@ -1,8 +0,0 @@
#include "ibutton_protocols.h"
#include "protocol_cyfral.h"
#include "protocol_metakom.h"
const ProtocolBase* ibutton_protocols[] = {
[iButtonProtocolCyfral] = &protocol_cyfral,
[iButtonProtocolMetakom] = &protocol_metakom,
};
@@ -1,11 +0,0 @@
#pragma once
#include "toolbox/protocols/protocol.h"
typedef enum {
iButtonProtocolCyfral,
iButtonProtocolMetakom,
iButtonProtocolMax,
} iButtonProtocol;
extern const ProtocolBase* ibutton_protocols[];
@@ -1,4 +0,0 @@
#pragma once
#include "toolbox/protocols/protocol.h"
extern const ProtocolBase protocol_cyfral;

Some files were not shown because too many files have changed in this diff Show More