mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge branch 'ul-dev' into xfw-dev
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -31,3 +31,6 @@
|
||||
[submodule "applications/external/dap_link/lib/free-dap"]
|
||||
path = applications/external/dap_link/lib/free-dap
|
||||
url = https://github.com/ataradov/free-dap.git
|
||||
[submodule "lib/heatshrink"]
|
||||
path = lib/heatshrink
|
||||
url = https://github.com/flipperdevices/heatshrink.git
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
#include <one_wire/maxim_crc.h>
|
||||
#include <one_wire/one_wire_host.h>
|
||||
|
||||
#include <furi_hal_power.h>
|
||||
|
||||
#define UPDATE_PERIOD_MS 1000UL
|
||||
#define TEXT_STORE_SIZE 64U
|
||||
|
||||
#define DS18B20_CMD_SKIP_ROM 0xccU
|
||||
#define DS18B20_CMD_CONVERT 0x44U
|
||||
#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU
|
||||
|
||||
@@ -92,7 +95,7 @@ static void example_thermo_request_temperature(ExampleThermoContext* context) {
|
||||
/* After the reset, a ROM operation must follow.
|
||||
If there is only one device connected, the "Skip ROM" command is most appropriate
|
||||
(it can also be used to address all of the connected devices in some cases).*/
|
||||
onewire_host_skip(onewire);
|
||||
onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM);
|
||||
/* After the ROM operation, a device-specific command is issued.
|
||||
In this case, it's a request to start measuring the temperature. */
|
||||
onewire_host_write(onewire, DS18B20_CMD_CONVERT);
|
||||
@@ -133,7 +136,7 @@ static void example_thermo_read_temperature(ExampleThermoContext* context) {
|
||||
/* After the reset, a ROM operation must follow.
|
||||
If there is only one device connected, the "Skip ROM" command is most appropriate
|
||||
(it can also be used to address all of the connected devices in some cases).*/
|
||||
onewire_host_skip(onewire);
|
||||
onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM);
|
||||
|
||||
/* After the ROM operation, a device-specific command is issued.
|
||||
This time, it will be the "Read Scratchpad" command which will
|
||||
@@ -267,6 +270,9 @@ static void example_thermo_input_callback(InputEvent* event, void* ctx) {
|
||||
|
||||
/* Starts the reader thread and handles the input */
|
||||
static void example_thermo_run(ExampleThermoContext* context) {
|
||||
/* Enable power on external pins */
|
||||
furi_hal_power_enable_otg();
|
||||
|
||||
/* Configure the hardware in host mode */
|
||||
onewire_host_start(context->onewire);
|
||||
|
||||
@@ -299,6 +305,9 @@ static void example_thermo_run(ExampleThermoContext* context) {
|
||||
|
||||
/* Reset the hardware */
|
||||
onewire_host_stop(context->onewire);
|
||||
|
||||
/* Disable power on external pins */
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
/******************** Initialisation & startup *****************************/
|
||||
|
||||
@@ -19,6 +19,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
|
||||
|
||||
Canvas* canvas_init() {
|
||||
Canvas* canvas = malloc(sizeof(Canvas));
|
||||
canvas->compress_icon = compress_icon_alloc();
|
||||
|
||||
// Setup u8g2
|
||||
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||
@@ -37,6 +38,7 @@ Canvas* canvas_init() {
|
||||
|
||||
void canvas_free(Canvas* canvas) {
|
||||
furi_assert(canvas);
|
||||
compress_icon_free(canvas->compress_icon);
|
||||
free(canvas);
|
||||
}
|
||||
|
||||
@@ -233,7 +235,7 @@ void canvas_draw_bitmap(
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
uint8_t* bitmap_data = NULL;
|
||||
furi_hal_compress_icon_decode(compressed_bitmap_data, &bitmap_data);
|
||||
compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
|
||||
}
|
||||
|
||||
@@ -248,7 +250,8 @@ void canvas_draw_icon_animation(
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
uint8_t* icon_data = NULL;
|
||||
furi_hal_compress_icon_decode(icon_animation_get_data(icon_animation), &icon_data);
|
||||
compress_icon_decode(
|
||||
canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
|
||||
u8g2_DrawXBM(
|
||||
&canvas->fb,
|
||||
x,
|
||||
@@ -265,7 +268,7 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
uint8_t* icon_data = NULL;
|
||||
furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data);
|
||||
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
|
||||
}
|
||||
|
||||
@@ -399,7 +402,7 @@ void canvas_draw_icon_bitmap(
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
uint8_t* icon_data = NULL;
|
||||
furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data);
|
||||
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, w, h, icon_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "canvas.h"
|
||||
#include <u8g2.h>
|
||||
#include <toolbox/compress.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -21,6 +22,7 @@ struct Canvas {
|
||||
uint8_t offset_y;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
CompressIcon* compress_icon;
|
||||
};
|
||||
|
||||
/** Allocate memory and initialize canvas
|
||||
|
||||
@@ -288,6 +288,7 @@ static void gui_redraw(Gui* gui) {
|
||||
p->callback(
|
||||
canvas_get_buffer(gui->canvas),
|
||||
canvas_get_buffer_size(gui->canvas),
|
||||
canvas_get_orientation(gui->canvas),
|
||||
p->context);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
@@ -27,7 +27,11 @@ typedef enum {
|
||||
} GuiLayer;
|
||||
|
||||
/** Gui Canvas Commit Callback */
|
||||
typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context);
|
||||
typedef void (*GuiCanvasCommitCallback)(
|
||||
uint8_t* data,
|
||||
size_t size,
|
||||
CanvasOrientation orientation,
|
||||
void* context);
|
||||
|
||||
#define RECORD_GUI "gui"
|
||||
|
||||
|
||||
@@ -33,8 +33,18 @@ typedef struct {
|
||||
uint32_t input_counter;
|
||||
} RpcGuiSystem;
|
||||
|
||||
static void
|
||||
rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) {
|
||||
static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = {
|
||||
[CanvasOrientationHorizontal] = PB_Gui_ScreenOrientation_HORIZONTAL,
|
||||
[CanvasOrientationHorizontalFlip] = PB_Gui_ScreenOrientation_HORIZONTAL_FLIP,
|
||||
[CanvasOrientationVertical] = PB_Gui_ScreenOrientation_VERTICAL,
|
||||
[CanvasOrientationVerticalFlip] = PB_Gui_ScreenOrientation_VERTICAL_FLIP,
|
||||
};
|
||||
|
||||
static void rpc_system_gui_screen_stream_frame_callback(
|
||||
uint8_t* data,
|
||||
size_t size,
|
||||
CanvasOrientation orientation,
|
||||
void* context) {
|
||||
furi_assert(data);
|
||||
furi_assert(context);
|
||||
|
||||
@@ -44,6 +54,8 @@ static void
|
||||
furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);
|
||||
|
||||
memcpy(buffer, data, size);
|
||||
rpc_gui->transmit_frame->content.gui_screen_frame.orientation =
|
||||
rpc_system_gui_screen_orientation_map[orientation];
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit);
|
||||
}
|
||||
@@ -53,12 +65,22 @@ static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context)
|
||||
|
||||
RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;
|
||||
|
||||
uint32_t transmit_time = 0;
|
||||
while(true) {
|
||||
uint32_t flags =
|
||||
furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
||||
if(flags & RpcGuiWorkerFlagTransmit) {
|
||||
transmit_time = furi_get_tick();
|
||||
rpc_send(rpc_gui->session, rpc_gui->transmit_frame);
|
||||
transmit_time = furi_get_tick() - transmit_time;
|
||||
|
||||
// Guaranteed bandwidth reserve
|
||||
uint32_t extra_delay = transmit_time / 20;
|
||||
if(extra_delay > 500) extra_delay = 500;
|
||||
if(extra_delay) furi_delay_tick(extra_delay);
|
||||
}
|
||||
|
||||
if(flags & RpcGuiWorkerFlagExit) {
|
||||
break;
|
||||
}
|
||||
|
||||
Submodule assets/protobuf updated: 6460660237...1f6b4a08c5
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,19.0,,
|
||||
Version,+,20.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
@@ -57,7 +57,6 @@ Header,+,firmware/targets/furi_hal_include/furi_hal.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,,
|
||||
@@ -153,7 +152,6 @@ Header,+,lib/mlib/m-tuple.h,,
|
||||
Header,+,lib/mlib/m-variant.h,,
|
||||
Header,+,lib/one_wire/maxim_crc.h,,
|
||||
Header,+,lib/one_wire/one_wire_host.h,,
|
||||
Header,+,lib/one_wire/one_wire_host_timing.h,,
|
||||
Header,+,lib/one_wire/one_wire_slave.h,,
|
||||
Header,+,lib/print/wrappers.h,,
|
||||
Header,+,lib/toolbox/args.h,,
|
||||
@@ -877,12 +875,12 @@ Function,-,furi_hal_clock_resume_tick,void,
|
||||
Function,-,furi_hal_clock_suspend_tick,void,
|
||||
Function,-,furi_hal_clock_switch_to_hsi,void,
|
||||
Function,-,furi_hal_clock_switch_to_pll,void,
|
||||
Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t
|
||||
Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,furi_hal_compress_free,void,FuriHalCompress*
|
||||
Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
||||
Function,-,furi_hal_compress_icon_init,void,
|
||||
Function,-,compress_alloc,Compress*,uint16_t
|
||||
Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,compress_free,void,Compress*
|
||||
Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
||||
Function,-,compress_icon_init,void,
|
||||
Function,+,furi_hal_console_disable,void,
|
||||
Function,+,furi_hal_console_enable,void,
|
||||
Function,+,furi_hal_console_init,void,
|
||||
@@ -1482,8 +1480,8 @@ Function,+,onewire_host_read_bit,_Bool,OneWireHost*
|
||||
Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t"
|
||||
Function,+,onewire_host_reset,_Bool,OneWireHost*
|
||||
Function,+,onewire_host_reset_search,void,OneWireHost*
|
||||
Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
||||
Function,+,onewire_host_skip,void,OneWireHost*
|
||||
Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
||||
Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
|
||||
Function,+,onewire_host_start,void,OneWireHost*
|
||||
Function,+,onewire_host_stop,void,OneWireHost*
|
||||
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
|
||||
@@ -1497,6 +1495,7 @@ Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave*
|
||||
Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t"
|
||||
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
|
||||
Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*"
|
||||
Function,+,onewire_slave_set_overdrive,void,"OneWireSlave*, _Bool"
|
||||
Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*"
|
||||
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
|
||||
Function,+,onewire_slave_start,void,OneWireSlave*
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_mpu.h>
|
||||
#include <furi_hal_memory.h>
|
||||
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
|
||||
@@ -7,29 +8,20 @@
|
||||
|
||||
void furi_hal_init_early() {
|
||||
furi_hal_cortex_init_early();
|
||||
|
||||
furi_hal_clock_init_early();
|
||||
|
||||
furi_hal_resources_init_early();
|
||||
|
||||
furi_hal_os_init();
|
||||
|
||||
furi_hal_spi_config_init_early();
|
||||
|
||||
furi_hal_i2c_init_early();
|
||||
furi_hal_light_init();
|
||||
|
||||
furi_hal_rtc_init_early();
|
||||
}
|
||||
|
||||
void furi_hal_deinit_early() {
|
||||
furi_hal_rtc_deinit_early();
|
||||
|
||||
furi_hal_i2c_deinit_early();
|
||||
furi_hal_spi_config_deinit_early();
|
||||
|
||||
furi_hal_resources_deinit_early();
|
||||
|
||||
furi_hal_clock_deinit_early();
|
||||
}
|
||||
|
||||
@@ -38,40 +30,24 @@ void furi_hal_init() {
|
||||
furi_hal_clock_init();
|
||||
furi_hal_console_init();
|
||||
furi_hal_rtc_init();
|
||||
|
||||
furi_hal_interrupt_init();
|
||||
|
||||
furi_hal_flash_init();
|
||||
|
||||
furi_hal_resources_init();
|
||||
FURI_LOG_I(TAG, "GPIO OK");
|
||||
|
||||
furi_hal_version_init();
|
||||
|
||||
furi_hal_spi_config_init();
|
||||
furi_hal_spi_dma_init();
|
||||
|
||||
furi_hal_speaker_init();
|
||||
FURI_LOG_I(TAG, "Speaker OK");
|
||||
|
||||
furi_hal_crypto_init();
|
||||
|
||||
// USB
|
||||
#ifndef FURI_RAM_EXEC
|
||||
furi_hal_usb_init();
|
||||
FURI_LOG_I(TAG, "USB OK");
|
||||
#endif
|
||||
|
||||
furi_hal_i2c_init();
|
||||
|
||||
// High Level
|
||||
furi_hal_power_init();
|
||||
furi_hal_light_init();
|
||||
furi_hal_bt_init();
|
||||
furi_hal_memory_init();
|
||||
|
||||
#ifndef FURI_RAM_EXEC
|
||||
furi_hal_usb_init();
|
||||
furi_hal_vibro_init();
|
||||
#endif
|
||||
furi_hal_bt_init();
|
||||
furi_hal_compress_icon_init();
|
||||
}
|
||||
|
||||
void furi_hal_switch(void* address) {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <stm32wbxx_ll_rcc.h>
|
||||
#include <stm32wbxx_ll_pwr.h>
|
||||
|
||||
#define TAG "FuriHalResources"
|
||||
|
||||
const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8};
|
||||
const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14};
|
||||
|
||||
@@ -198,6 +200,8 @@ void furi_hal_resources_init() {
|
||||
|
||||
NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,19.0,,
|
||||
Version,+,20.0,,
|
||||
Header,+,applications/main/fap_loader/fap_loader_app.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
@@ -68,7 +68,6 @@ Header,+,firmware/targets/furi_hal_include/furi_hal.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
|
||||
Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,,
|
||||
@@ -180,7 +179,6 @@ Header,+,lib/mlib/m-variant.h,,
|
||||
Header,+,lib/nfc/nfc_device.h,,
|
||||
Header,+,lib/one_wire/maxim_crc.h,,
|
||||
Header,+,lib/one_wire/one_wire_host.h,,
|
||||
Header,+,lib/one_wire/one_wire_host_timing.h,,
|
||||
Header,+,lib/one_wire/one_wire_slave.h,,
|
||||
Header,+,lib/print/wrappers.h,,
|
||||
Header,+,lib/subghz/blocks/const.h,,
|
||||
@@ -731,6 +729,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI
|
||||
Function,+,composite_api_resolver_alloc,CompositeApiResolver*,
|
||||
Function,+,composite_api_resolver_free,void,CompositeApiResolver*
|
||||
Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver*
|
||||
Function,-,compress_alloc,Compress*,uint16_t
|
||||
Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,compress_free,void,Compress*
|
||||
Function,-,compress_icon_alloc,CompressIcon*,
|
||||
Function,-,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**"
|
||||
Function,-,compress_icon_free,void,CompressIcon*
|
||||
Function,-,copysign,double,"double, double"
|
||||
Function,-,copysignf,float,"float, float"
|
||||
Function,-,copysignl,long double,"long double, long double"
|
||||
@@ -1134,12 +1139,6 @@ Function,-,furi_hal_clock_resume_tick,void,
|
||||
Function,-,furi_hal_clock_suspend_tick,void,
|
||||
Function,-,furi_hal_clock_switch_to_hsi,void,
|
||||
Function,-,furi_hal_clock_switch_to_pll,void,
|
||||
Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t
|
||||
Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,furi_hal_compress_free,void,FuriHalCompress*
|
||||
Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
||||
Function,-,furi_hal_compress_icon_init,void,
|
||||
Function,+,furi_hal_console_disable,void,
|
||||
Function,+,furi_hal_console_enable,void,
|
||||
Function,+,furi_hal_console_init,void,
|
||||
@@ -2210,8 +2209,8 @@ Function,+,onewire_host_read_bit,_Bool,OneWireHost*
|
||||
Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t"
|
||||
Function,+,onewire_host_reset,_Bool,OneWireHost*
|
||||
Function,+,onewire_host_reset_search,void,OneWireHost*
|
||||
Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
||||
Function,+,onewire_host_skip,void,OneWireHost*
|
||||
Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
||||
Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
|
||||
Function,+,onewire_host_start,void,OneWireHost*
|
||||
Function,+,onewire_host_stop,void,OneWireHost*
|
||||
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
|
||||
@@ -2225,6 +2224,7 @@ Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave*
|
||||
Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t"
|
||||
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
|
||||
Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*"
|
||||
Function,+,onewire_slave_set_overdrive,void,"OneWireSlave*, _Bool"
|
||||
Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*"
|
||||
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
|
||||
Function,+,onewire_slave_start,void,OneWireSlave*
|
||||
|
||||
|
@@ -8,29 +8,20 @@
|
||||
|
||||
void furi_hal_init_early() {
|
||||
furi_hal_cortex_init_early();
|
||||
|
||||
furi_hal_clock_init_early();
|
||||
|
||||
furi_hal_resources_init_early();
|
||||
|
||||
furi_hal_os_init();
|
||||
|
||||
furi_hal_spi_config_init_early();
|
||||
|
||||
furi_hal_i2c_init_early();
|
||||
furi_hal_light_init();
|
||||
|
||||
furi_hal_rtc_init_early();
|
||||
}
|
||||
|
||||
void furi_hal_deinit_early() {
|
||||
furi_hal_rtc_deinit_early();
|
||||
|
||||
furi_hal_i2c_deinit_early();
|
||||
furi_hal_spi_config_deinit_early();
|
||||
|
||||
furi_hal_resources_deinit_early();
|
||||
|
||||
furi_hal_clock_deinit_early();
|
||||
}
|
||||
|
||||
@@ -39,40 +30,23 @@ void furi_hal_init() {
|
||||
furi_hal_clock_init();
|
||||
furi_hal_console_init();
|
||||
furi_hal_rtc_init();
|
||||
|
||||
furi_hal_interrupt_init();
|
||||
|
||||
furi_hal_flash_init();
|
||||
|
||||
furi_hal_resources_init();
|
||||
FURI_LOG_I(TAG, "GPIO OK");
|
||||
|
||||
furi_hal_version_init();
|
||||
|
||||
furi_hal_spi_config_init();
|
||||
furi_hal_spi_dma_init();
|
||||
|
||||
furi_hal_ibutton_init();
|
||||
FURI_LOG_I(TAG, "iButton OK");
|
||||
furi_hal_speaker_init();
|
||||
FURI_LOG_I(TAG, "Speaker OK");
|
||||
|
||||
furi_hal_crypto_init();
|
||||
|
||||
furi_hal_i2c_init();
|
||||
|
||||
// High Level
|
||||
furi_hal_power_init();
|
||||
furi_hal_light_init();
|
||||
|
||||
furi_hal_bt_init();
|
||||
furi_hal_memory_init();
|
||||
furi_hal_compress_icon_init();
|
||||
|
||||
#ifndef FURI_RAM_EXEC
|
||||
// USB
|
||||
furi_hal_usb_init();
|
||||
FURI_LOG_I(TAG, "USB OK");
|
||||
furi_hal_vibro_init();
|
||||
furi_hal_subghz_init();
|
||||
furi_hal_nfc_init();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "FuriHalIbutton"
|
||||
#define FURI_HAL_IBUTTON_TIMER TIM1
|
||||
#define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16
|
||||
|
||||
@@ -33,6 +34,8 @@ static void furi_hal_ibutton_emulate_isr() {
|
||||
void furi_hal_ibutton_init() {
|
||||
furi_hal_ibutton = malloc(sizeof(FuriHalIbutton));
|
||||
furi_hal_ibutton->state = FuriHalIbuttonStateIdle;
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_ibutton_emulate_start(
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <stm32wbxx_ll_rcc.h>
|
||||
#include <stm32wbxx_ll_pwr.h>
|
||||
|
||||
#define TAG "FuriHalResources"
|
||||
|
||||
const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
|
||||
const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin};
|
||||
|
||||
@@ -195,6 +197,8 @@ void furi_hal_resources_init() {
|
||||
|
||||
NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
|
||||
|
||||
@@ -44,7 +44,8 @@ static void furi_hal_usart_init(uint32_t baud) {
|
||||
while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
|
||||
;
|
||||
|
||||
LL_USART_EnableIT_RXNE_RXFNE(USART1);
|
||||
LL_USART_DisableIT_ERROR(USART1);
|
||||
|
||||
NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
}
|
||||
|
||||
@@ -79,8 +80,8 @@ static void furi_hal_lpuart_init(uint32_t baud) {
|
||||
;
|
||||
|
||||
furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud);
|
||||
LL_LPUART_DisableIT_ERROR(LPUART1);
|
||||
|
||||
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
|
||||
NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
}
|
||||
|
||||
@@ -190,19 +191,25 @@ void furi_hal_uart_set_irq_cb(
|
||||
void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx),
|
||||
void* ctx) {
|
||||
if(cb == NULL) {
|
||||
if(ch == FuriHalUartIdUSART1)
|
||||
if(ch == FuriHalUartIdUSART1) {
|
||||
NVIC_DisableIRQ(USART1_IRQn);
|
||||
else if(ch == FuriHalUartIdLPUART1)
|
||||
LL_USART_DisableIT_RXNE_RXFNE(USART1);
|
||||
} else if(ch == FuriHalUartIdLPUART1) {
|
||||
NVIC_DisableIRQ(LPUART1_IRQn);
|
||||
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
|
||||
}
|
||||
irq_cb[ch] = cb;
|
||||
irq_ctx[ch] = ctx;
|
||||
} else {
|
||||
irq_ctx[ch] = ctx;
|
||||
irq_cb[ch] = cb;
|
||||
if(ch == FuriHalUartIdUSART1)
|
||||
if(ch == FuriHalUartIdUSART1) {
|
||||
NVIC_EnableIRQ(USART1_IRQn);
|
||||
else if(ch == FuriHalUartIdLPUART1)
|
||||
LL_USART_EnableIT_RXNE_RXFNE(USART1);
|
||||
} else if(ch == FuriHalUartIdLPUART1) {
|
||||
NVIC_EnableIRQ(LPUART1_IRQn);
|
||||
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
#include <alt_boot.h>
|
||||
#include <u8g2_glue.h>
|
||||
#include <assets_icons.h>
|
||||
#include <toolbox/compress.h>
|
||||
|
||||
void flipper_boot_dfu_show_splash() {
|
||||
// Initialize
|
||||
furi_hal_compress_icon_init();
|
||||
CompressIcon* compress_icon = compress_icon_alloc();
|
||||
|
||||
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
||||
memset(fb, 0, sizeof(u8g2_t));
|
||||
@@ -15,13 +16,15 @@ void flipper_boot_dfu_show_splash() {
|
||||
u8g2_InitDisplay(fb);
|
||||
u8g2_SetDrawColor(fb, 0x01);
|
||||
uint8_t* splash_data = NULL;
|
||||
furi_hal_compress_icon_decode(icon_get_data(&I_DFU_128x50), &splash_data);
|
||||
compress_icon_decode(compress_icon, icon_get_data(&I_DFU_128x50), &splash_data);
|
||||
u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data);
|
||||
u8g2_SetFont(fb, u8g2_font_helvB08_tr);
|
||||
u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
|
||||
u8g2_DrawStr(fb, 2, 21, "DFU Started");
|
||||
u8g2_SetPowerSave(fb, 0);
|
||||
u8g2_SendBuffer(fb);
|
||||
|
||||
compress_icon_free(compress_icon);
|
||||
}
|
||||
|
||||
void flipper_boot_dfu_exec() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <alt_boot.h>
|
||||
#include <u8g2_glue.h>
|
||||
#include <assets_icons.h>
|
||||
#include <toolbox/compress.h>
|
||||
|
||||
#define COUNTER_VALUE (136U)
|
||||
|
||||
@@ -27,9 +28,9 @@ void flipper_boot_recovery_exec() {
|
||||
u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||
u8g2_InitDisplay(fb);
|
||||
|
||||
furi_hal_compress_icon_init();
|
||||
CompressIcon* compress_icon = compress_icon_alloc();
|
||||
uint8_t* splash_data = NULL;
|
||||
furi_hal_compress_icon_decode(icon_get_data(&I_Erase_pin_128x64), &splash_data);
|
||||
compress_icon_decode(compress_icon, icon_get_data(&I_Erase_pin_128x64), &splash_data);
|
||||
|
||||
u8g2_ClearBuffer(fb);
|
||||
u8g2_SetDrawColor(fb, 0x01);
|
||||
@@ -38,6 +39,7 @@ void flipper_boot_recovery_exec() {
|
||||
u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
|
||||
u8g2_SendBuffer(fb);
|
||||
u8g2_SetPowerSave(fb, 0);
|
||||
compress_icon_free(compress_icon);
|
||||
|
||||
size_t counter = COUNTER_VALUE;
|
||||
while(counter) {
|
||||
|
||||
@@ -33,7 +33,6 @@ struct STOP_EXTERNING_ME {};
|
||||
#include <furi_hal_vibro.h>
|
||||
#include <furi_hal_usb.h>
|
||||
#include <furi_hal_usb_hid.h>
|
||||
#include <furi_hal_compress.h>
|
||||
#include <furi_hal_uart.h>
|
||||
#include <furi_hal_info.h>
|
||||
#include <furi_hal_random.h>
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* @file furi_hal_compress.h
|
||||
* LZSS based compression HAL API
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Defines encoder and decoder window size */
|
||||
#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG (8)
|
||||
|
||||
/** Defines encoder and decoder lookahead buffer size */
|
||||
#define FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4)
|
||||
|
||||
/** FuriHalCompress control structure */
|
||||
typedef struct FuriHalCompress FuriHalCompress;
|
||||
|
||||
/** Initialize icon decoder
|
||||
*/
|
||||
void furi_hal_compress_icon_init();
|
||||
|
||||
/** Icon decoder
|
||||
*
|
||||
* @param icon_data pointer to icon data
|
||||
* @param decoded_buff pointer to decoded buffer
|
||||
*/
|
||||
void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff);
|
||||
|
||||
/** Allocate encoder and decoder
|
||||
*
|
||||
* @param compress_buff_size size of decoder and encoder buffer to allocate
|
||||
*
|
||||
* @return FuriHalCompress instance
|
||||
*/
|
||||
FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size);
|
||||
|
||||
/** Free encoder and decoder
|
||||
*
|
||||
* @param compress FuriHalCompress instance
|
||||
*/
|
||||
void furi_hal_compress_free(FuriHalCompress* compress);
|
||||
|
||||
/** Encode data
|
||||
*
|
||||
* @param compress FuriHalCompress instance
|
||||
* @param data_in pointer to input data
|
||||
* @param data_in_size size of input data
|
||||
* @param data_out maximum size of output data
|
||||
* @param data_res_size pointer to result output data size
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_compress_encode(
|
||||
FuriHalCompress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size);
|
||||
|
||||
/** Decode data
|
||||
*
|
||||
* @param compress FuriHalCompress instance
|
||||
* @param data_in pointer to input data
|
||||
* @param data_in_size size of input data
|
||||
* @param data_out maximum size of output data
|
||||
* @param data_res_size pointer to result output data size
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_compress_decode(
|
||||
FuriHalCompress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
4
lib/err.h
Normal file
4
lib/err.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
|
||||
#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__)
|
||||
1
lib/heatshrink
Submodule
1
lib/heatshrink
Submodule
Submodule lib/heatshrink added at 7398ccc916
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <flipper_format.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
#include "../protocol_common_i.h"
|
||||
|
||||
#include <flipper_format.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*);
|
||||
|
||||
@@ -67,6 +67,14 @@ bool dallas_ds1420_write_blank(OneWireHost* host, iButtonProtocolData* protocol_
|
||||
tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
|
||||
}
|
||||
|
||||
static bool dallas_ds1420_reset_callback(bool is_short, void* context) {
|
||||
DS1420ProtocolData* data = context;
|
||||
if(!is_short) {
|
||||
onewire_slave_set_overdrive(data->state.bus, is_short);
|
||||
}
|
||||
return !is_short;
|
||||
}
|
||||
|
||||
static bool dallas_ds1420_command_callback(uint8_t command, void* context) {
|
||||
furi_assert(context);
|
||||
DS1420ProtocolData* data = context;
|
||||
@@ -92,7 +100,7 @@ void dallas_ds1420_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data
|
||||
DS1420ProtocolData* data = protocol_data;
|
||||
data->state.bus = bus;
|
||||
|
||||
onewire_slave_set_reset_callback(bus, NULL, NULL);
|
||||
onewire_slave_set_reset_callback(bus, dallas_ds1420_reset_callback, protocol_data);
|
||||
onewire_slave_set_command_callback(bus, dallas_ds1420_command_callback, protocol_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1971 = {
|
||||
.name = DS1971_FAMILY_NAME,
|
||||
|
||||
.read = dallas_ds1971_read,
|
||||
.write_blank = NULL, /* No data to write a blank */
|
||||
.write_blank = NULL, // TODO: Implement writing to blank
|
||||
.write_copy = dallas_ds1971_write_copy,
|
||||
.emulate = dallas_ds1971_emulate,
|
||||
.save = dallas_ds1971_save,
|
||||
@@ -76,7 +76,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
||||
DS1971ProtocolData* data = protocol_data;
|
||||
|
||||
onewire_host_reset(host);
|
||||
onewire_host_skip(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);
|
||||
@@ -87,7 +87,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
||||
bool pad_valid = false;
|
||||
if(onewire_host_reset(host)) {
|
||||
pad_valid = true;
|
||||
onewire_host_skip(host);
|
||||
onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);
|
||||
onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH);
|
||||
onewire_host_write(host, 0x00);
|
||||
|
||||
@@ -103,7 +103,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
||||
// Copy scratchpad to memory and confirm
|
||||
if(pad_valid) {
|
||||
if(onewire_host_reset(host)) {
|
||||
onewire_host_skip(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);
|
||||
|
||||
@@ -114,10 +114,16 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
||||
return pad_valid;
|
||||
}
|
||||
|
||||
static void dallas_ds1971_reset_callback(void* context) {
|
||||
static bool dallas_ds1971_reset_callback(bool is_short, void* context) {
|
||||
furi_assert(context);
|
||||
DS1971ProtocolData* data = context;
|
||||
data->state.command_state = DallasCommonCommandStateIdle;
|
||||
|
||||
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) {
|
||||
|
||||
@@ -67,6 +67,14 @@ bool dallas_ds1990_write_blank(OneWireHost* host, iButtonProtocolData* protocol_
|
||||
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;
|
||||
@@ -92,7 +100,7 @@ void dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data
|
||||
DS1990ProtocolData* data = protocol_data;
|
||||
data->state.bus = bus;
|
||||
|
||||
onewire_slave_set_reset_callback(bus, NULL, NULL);
|
||||
onewire_slave_set_reset_callback(bus, dallas_ds1990_reset_callback, protocol_data);
|
||||
onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +87,16 @@ bool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
||||
DS1992_SRAM_DATA_SIZE);
|
||||
}
|
||||
|
||||
static void dallas_ds1992_reset_callback(void* context) {
|
||||
static bool dallas_ds1992_reset_callback(bool is_short, void* context) {
|
||||
furi_assert(context);
|
||||
DS1992ProtocolData* data = context;
|
||||
data->state.command_state = DallasCommonCommandStateIdle;
|
||||
|
||||
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) {
|
||||
|
||||
@@ -63,24 +63,54 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1996 = {
|
||||
|
||||
bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
|
||||
DS1996ProtocolData* 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, DS1996_SRAM_DATA_SIZE);
|
||||
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;
|
||||
return dallas_common_write_mem(
|
||||
host,
|
||||
DS1996_COPY_SCRATCH_TIMEOUT_US,
|
||||
DS1996_SRAM_PAGE_SIZE,
|
||||
data->sram_data,
|
||||
DS1996_SRAM_DATA_SIZE);
|
||||
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 void dallas_ds1996_reset_callback(void* context) {
|
||||
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) {
|
||||
@@ -96,8 +126,7 @@ static bool dallas_ds1996_command_callback(uint8_t command, void* context) {
|
||||
|
||||
} else if(data->state.command_state == DallasCommonCommandStateRomCmd) {
|
||||
data->state.command_state = DallasCommonCommandStateMemCmd;
|
||||
dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE);
|
||||
return false;
|
||||
return dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE);
|
||||
|
||||
} else {
|
||||
return false;
|
||||
@@ -120,8 +149,17 @@ static bool dallas_ds1996_command_callback(uint8_t command, void* context) {
|
||||
}
|
||||
|
||||
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: Overdrive mode support */
|
||||
/* TODO: Match ROM command support */
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,15 @@ bool ds_generic_write_blank(OneWireHost* host, iButtonProtocolData* protocol_dat
|
||||
return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
|
||||
}
|
||||
|
||||
static bool ds_generic_reset_callback(bool is_short, void* context) {
|
||||
furi_assert(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;
|
||||
@@ -85,7 +94,7 @@ void ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
|
||||
DallasGenericProtocolData* data = protocol_data;
|
||||
data->state.bus = bus;
|
||||
|
||||
onewire_slave_set_reset_callback(bus, NULL, NULL);
|
||||
onewire_slave_set_reset_callback(bus, ds_generic_reset_callback, protocol_data);
|
||||
onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ for lib in libs_recurse:
|
||||
sources += libenv.GlobRecursive("*.c*", lib)
|
||||
|
||||
libs_plain = [
|
||||
"heatshrink",
|
||||
"nanopb",
|
||||
]
|
||||
|
||||
@@ -49,6 +48,12 @@ for lib in libs_plain:
|
||||
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)
|
||||
Return("lib")
|
||||
|
||||
@@ -971,7 +971,8 @@ static void nfc_worker_mf_classic_key_attack(
|
||||
(uint32_t)key);
|
||||
if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) {
|
||||
mf_classic_set_key_found(data, i, MfClassicKeyA, key);
|
||||
FURI_LOG_D(TAG, "Key found");
|
||||
FURI_LOG_D(
|
||||
TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key);
|
||||
nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
|
||||
|
||||
uint64_t found_key;
|
||||
@@ -994,7 +995,8 @@ static void nfc_worker_mf_classic_key_attack(
|
||||
(uint32_t)key);
|
||||
if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) {
|
||||
mf_classic_set_key_found(data, i, MfClassicKeyB, key);
|
||||
FURI_LOG_D(TAG, "Key found");
|
||||
FURI_LOG_D(
|
||||
TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key);
|
||||
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
|
||||
}
|
||||
}
|
||||
@@ -1094,9 +1096,13 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
||||
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 the key A is marked as found and matches the searching key, invalidate it
|
||||
if(mf_classic_is_key_found(data, i, MfClassicKeyA) &&
|
||||
data->block[i].value[0] == key) {
|
||||
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);
|
||||
@@ -1110,9 +1116,13 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
||||
}
|
||||
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 the key B is marked as found and matches the searching key, invalidate it
|
||||
if(mf_classic_is_key_found(data, i, MfClassicKeyB) &&
|
||||
data->block[i].value[10] == key) {
|
||||
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;
|
||||
|
||||
@@ -8,7 +8,6 @@ env.Append(
|
||||
"#/lib/one_wire",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("one_wire_host_timing.h"),
|
||||
File("one_wire_host.h"),
|
||||
File("one_wire_slave.h"),
|
||||
File("maxim_crc.h"),
|
||||
|
||||
@@ -1,10 +1,54 @@
|
||||
#include <furi.h>
|
||||
|
||||
/**
|
||||
* Timings based on Application Note 126:
|
||||
* https://www.analog.com/media/en/technical-documentation/tech-articles/1wire-communication-through-software--maxim-integrated.pdf
|
||||
*/
|
||||
|
||||
#include "one_wire_host.h"
|
||||
#include "one_wire_host_timing.h"
|
||||
|
||||
typedef struct {
|
||||
uint16_t a;
|
||||
uint16_t b;
|
||||
uint16_t c;
|
||||
uint16_t d;
|
||||
uint16_t e;
|
||||
uint16_t f;
|
||||
uint16_t g;
|
||||
uint16_t h;
|
||||
uint16_t i;
|
||||
uint16_t j;
|
||||
} OneWireHostTimings;
|
||||
|
||||
static const OneWireHostTimings onewire_host_timings_normal = {
|
||||
.a = 9,
|
||||
.b = 64,
|
||||
.c = 64,
|
||||
.d = 14,
|
||||
.e = 9,
|
||||
.f = 55,
|
||||
.g = 0,
|
||||
.h = 480,
|
||||
.i = 70,
|
||||
.j = 410,
|
||||
};
|
||||
|
||||
static const OneWireHostTimings onewire_host_timings_overdrive = {
|
||||
.a = 1,
|
||||
.b = 8,
|
||||
.c = 8,
|
||||
.d = 3,
|
||||
.e = 1,
|
||||
.f = 7,
|
||||
.g = 3,
|
||||
.h = 70,
|
||||
.i = 9,
|
||||
.j = 40,
|
||||
};
|
||||
|
||||
struct OneWireHost {
|
||||
const GpioPin* gpio_pin;
|
||||
const OneWireHostTimings* timings;
|
||||
unsigned char saved_rom[8]; /** < global search state */
|
||||
uint8_t last_discrepancy;
|
||||
uint8_t last_family_discrepancy;
|
||||
@@ -15,6 +59,7 @@ OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) {
|
||||
OneWireHost* host = malloc(sizeof(OneWireHost));
|
||||
host->gpio_pin = gpio_pin;
|
||||
onewire_host_reset_search(host);
|
||||
onewire_host_set_overdrive(host, false);
|
||||
return host;
|
||||
}
|
||||
|
||||
@@ -27,6 +72,8 @@ bool onewire_host_reset(OneWireHost* host) {
|
||||
uint8_t r;
|
||||
uint8_t retries = 125;
|
||||
|
||||
const OneWireHostTimings* timings = host->timings;
|
||||
|
||||
// wait until the gpio is high
|
||||
furi_hal_gpio_write(host->gpio_pin, true);
|
||||
do {
|
||||
@@ -35,19 +82,19 @@ bool onewire_host_reset(OneWireHost* host) {
|
||||
} while(!furi_hal_gpio_read(host->gpio_pin));
|
||||
|
||||
// pre delay
|
||||
furi_delay_us(OWH_RESET_DELAY_PRE);
|
||||
furi_delay_us(timings->g);
|
||||
|
||||
// drive low
|
||||
furi_hal_gpio_write(host->gpio_pin, false);
|
||||
furi_delay_us(OWH_RESET_DRIVE);
|
||||
furi_delay_us(timings->h);
|
||||
|
||||
// release
|
||||
furi_hal_gpio_write(host->gpio_pin, true);
|
||||
furi_delay_us(OWH_RESET_RELEASE);
|
||||
furi_delay_us(timings->i);
|
||||
|
||||
// read and post delay
|
||||
r = !furi_hal_gpio_read(host->gpio_pin);
|
||||
furi_delay_us(OWH_RESET_DELAY_POST);
|
||||
furi_delay_us(timings->j);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -55,17 +102,19 @@ bool onewire_host_reset(OneWireHost* host) {
|
||||
bool onewire_host_read_bit(OneWireHost* host) {
|
||||
bool result;
|
||||
|
||||
const OneWireHostTimings* timings = host->timings;
|
||||
|
||||
// drive low
|
||||
furi_hal_gpio_write(host->gpio_pin, false);
|
||||
furi_delay_us(OWH_READ_DRIVE);
|
||||
furi_delay_us(timings->a);
|
||||
|
||||
// release
|
||||
furi_hal_gpio_write(host->gpio_pin, true);
|
||||
furi_delay_us(OWH_READ_RELEASE);
|
||||
furi_delay_us(timings->e);
|
||||
|
||||
// read and post delay
|
||||
result = furi_hal_gpio_read(host->gpio_pin);
|
||||
furi_delay_us(OWH_READ_DELAY_POST);
|
||||
furi_delay_us(timings->f);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -89,22 +138,24 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count)
|
||||
}
|
||||
|
||||
void onewire_host_write_bit(OneWireHost* host, bool value) {
|
||||
const OneWireHostTimings* timings = host->timings;
|
||||
|
||||
if(value) {
|
||||
// drive low
|
||||
furi_hal_gpio_write(host->gpio_pin, false);
|
||||
furi_delay_us(OWH_WRITE_1_DRIVE);
|
||||
furi_delay_us(timings->a);
|
||||
|
||||
// release
|
||||
furi_hal_gpio_write(host->gpio_pin, true);
|
||||
furi_delay_us(OWH_WRITE_1_RELEASE);
|
||||
furi_delay_us(timings->b);
|
||||
} else {
|
||||
// drive low
|
||||
furi_hal_gpio_write(host->gpio_pin, false);
|
||||
furi_delay_us(OWH_WRITE_0_DRIVE);
|
||||
furi_delay_us(timings->c);
|
||||
|
||||
// release
|
||||
furi_hal_gpio_write(host->gpio_pin, true);
|
||||
furi_delay_us(OWH_WRITE_0_RELEASE);
|
||||
furi_delay_us(timings->d);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +173,6 @@ void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t
|
||||
}
|
||||
}
|
||||
|
||||
void onewire_host_skip(OneWireHost* host) {
|
||||
onewire_host_write(host, 0xCC);
|
||||
}
|
||||
|
||||
void onewire_host_start(OneWireHost* host) {
|
||||
furi_hal_gpio_write(host->gpio_pin, true);
|
||||
furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
@@ -154,7 +201,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code) {
|
||||
host->last_device_flag = false;
|
||||
}
|
||||
|
||||
uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) {
|
||||
bool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) {
|
||||
uint8_t id_bit_number;
|
||||
uint8_t last_zero, rom_byte_number, search_result;
|
||||
uint8_t id_bit, cmp_id_bit;
|
||||
@@ -268,3 +315,7 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSea
|
||||
|
||||
return search_result;
|
||||
}
|
||||
|
||||
void onewire_host_set_overdrive(OneWireHost* host, bool set) {
|
||||
host->timings = set ? &onewire_host_timings_overdrive : &onewire_host_timings_normal;
|
||||
}
|
||||
|
||||
@@ -15,114 +15,115 @@ extern "C" {
|
||||
|
||||
typedef enum {
|
||||
OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */
|
||||
OneWireHostSearchModeNormal = 1, /**< Search all devices */
|
||||
OneWireHostSearchModeNormal = 1, /**< Search for all devices */
|
||||
} OneWireHostSearchMode;
|
||||
|
||||
typedef struct OneWireHost OneWireHost;
|
||||
|
||||
/**
|
||||
* Allocate onewire host bus
|
||||
* @param pin
|
||||
* @return OneWireHost*
|
||||
* Allocate OneWireHost instance
|
||||
* @param [in] gpio_pin connection pin
|
||||
* @return pointer to OneWireHost instance
|
||||
*/
|
||||
OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin);
|
||||
|
||||
/**
|
||||
* Deallocate onewire host bus
|
||||
* @param host
|
||||
* Destroy OneWireHost instance, free resources
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
*/
|
||||
void onewire_host_free(OneWireHost* host);
|
||||
|
||||
/**
|
||||
* Reset bus
|
||||
* @param host
|
||||
* @return bool
|
||||
* Reset the 1-Wire bus
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @return true if presence was detected, false otherwise
|
||||
*/
|
||||
bool onewire_host_reset(OneWireHost* host);
|
||||
|
||||
/**
|
||||
* Read one bit
|
||||
* @param host
|
||||
* @return bool
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @return received bit value
|
||||
*/
|
||||
bool onewire_host_read_bit(OneWireHost* host);
|
||||
|
||||
/**
|
||||
* Read one byte
|
||||
* @param host
|
||||
* @return uint8_t
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @return received byte value
|
||||
*/
|
||||
uint8_t onewire_host_read(OneWireHost* host);
|
||||
|
||||
/**
|
||||
* Read many bytes
|
||||
* @param host
|
||||
* @param buffer
|
||||
* @param count
|
||||
* Read one or more bytes
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param [out] buffer received data buffer
|
||||
* @param [in] count number of bytes to read
|
||||
*/
|
||||
void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count);
|
||||
|
||||
/**
|
||||
* Write one bit
|
||||
* @param host
|
||||
* @param value
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param value bit value to write
|
||||
*/
|
||||
void onewire_host_write_bit(OneWireHost* host, bool value);
|
||||
|
||||
/**
|
||||
* Write one byte
|
||||
* @param host
|
||||
* @param value
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param value byte value to write
|
||||
*/
|
||||
void onewire_host_write(OneWireHost* host, uint8_t value);
|
||||
|
||||
/**
|
||||
* Write many bytes
|
||||
* @param host
|
||||
* @param buffer
|
||||
* @param count
|
||||
* Write one or more bytes
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param [in] buffer pointer to the data to write
|
||||
* @param [in] count size of the data to write
|
||||
*/
|
||||
void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count);
|
||||
|
||||
/**
|
||||
* Skip ROM command
|
||||
* @param host
|
||||
*/
|
||||
void onewire_host_skip(OneWireHost* host);
|
||||
|
||||
/**
|
||||
* Start working with the bus
|
||||
* @param host
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
*/
|
||||
void onewire_host_start(OneWireHost* host);
|
||||
|
||||
/**
|
||||
* Stop working with the bus
|
||||
* @param host
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
*/
|
||||
void onewire_host_stop(OneWireHost* host);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param host
|
||||
* Reset previous search results
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
*/
|
||||
void onewire_host_reset_search(OneWireHost* host);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param host
|
||||
* @param family_code
|
||||
* Set the family code to search for
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param [in] family_code device family code
|
||||
*/
|
||||
void onewire_host_target_search(OneWireHost* host, uint8_t family_code);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param host
|
||||
* @param newAddr
|
||||
* @param mode
|
||||
* @return uint8_t
|
||||
* Search for devices on the 1-Wire bus
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param [out] new_addr pointer to the buffer to contain the unique ROM of the found device
|
||||
* @param [in] mode search mode
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode);
|
||||
bool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode);
|
||||
|
||||
/**
|
||||
* Enable overdrive mode
|
||||
* @param [in] host pointer to OneWireHost instance
|
||||
* @param [in] set true to turn overdrive on, false to turn it off
|
||||
*/
|
||||
void onewire_host_set_overdrive(OneWireHost* host, bool set);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file one_wire_host_timing.h
|
||||
*
|
||||
* 1-Wire library, timing list
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define OWH_TIMING_A 9
|
||||
#define OWH_TIMING_B 64
|
||||
#define OWH_TIMING_C 64
|
||||
#define OWH_TIMING_D 14
|
||||
#define OWH_TIMING_E 9
|
||||
#define OWH_TIMING_F 55
|
||||
#define OWH_TIMING_G 0
|
||||
#define OWH_TIMING_H 480
|
||||
#define OWH_TIMING_I 70
|
||||
#define OWH_TIMING_J 410
|
||||
|
||||
#define OWH_WRITE_1_DRIVE OWH_TIMING_A
|
||||
#define OWH_WRITE_1_RELEASE OWH_TIMING_B
|
||||
#define OWH_WRITE_0_DRIVE OWH_TIMING_C
|
||||
#define OWH_WRITE_0_RELEASE OWH_TIMING_D
|
||||
#define OWH_READ_DRIVE 3
|
||||
#define OWH_READ_RELEASE OWH_TIMING_E
|
||||
#define OWH_READ_DELAY_POST OWH_TIMING_F
|
||||
#define OWH_RESET_DELAY_PRE OWH_TIMING_G
|
||||
#define OWH_RESET_DRIVE OWH_TIMING_H
|
||||
#define OWH_RESET_RELEASE OWH_TIMING_I
|
||||
#define OWH_RESET_DELAY_POST OWH_TIMING_J
|
||||
@@ -3,20 +3,7 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define ONEWIRE_TRSTL_MIN 270 /* Minimum Reset Low time */
|
||||
#define ONEWIRE_TRSTL_MAX 1200 /* Maximum Reset Low time */
|
||||
|
||||
#define ONEWIRE_TPDH_TYP 20 /* Typical Presence Detect High time */
|
||||
#define ONEWIRE_TPDL_MIN 100 /* Minimum Presence Detect Low time */
|
||||
#define ONEWIRE_TPDL_MAX 480 /* Maximum Presence Detect Low time */
|
||||
|
||||
#define ONEWIRE_TSLOT_MIN 60 /* Minimum Read/Write Slot time */
|
||||
#define ONEWIRE_TSLOT_MAX 135 /* Maximum Read/Write Slot time */
|
||||
|
||||
#define ONEWIRE_TW1L_MAX 20 /* Maximum Master Write 1 time */
|
||||
#define ONEWIRE_TRL_TMSR_MAX 30 /* Maximum Master Read Low + Read Sample time */
|
||||
|
||||
#define ONEWIRE_TH_TIMEOUT 15000 /* Maximum time before general timeout */
|
||||
#define TH_TIMEOUT_MAX 15000 /* Maximum time before general timeout */
|
||||
|
||||
typedef enum {
|
||||
OneWireSlaveErrorNone = 0,
|
||||
@@ -26,10 +13,29 @@ typedef enum {
|
||||
OneWireSlaveErrorTimeout,
|
||||
} OneWireSlaveError;
|
||||
|
||||
typedef struct {
|
||||
uint16_t trstl_min; /* Minimum Reset Low time */
|
||||
uint16_t trstl_max; /* Maximum Reset Low time */
|
||||
|
||||
uint16_t tpdh_typ; /* Typical Presence Detect High time */
|
||||
uint16_t tpdl_min; /* Minimum Presence Detect Low time */
|
||||
uint16_t tpdl_max; /* Maximum Presence Detect Low time */
|
||||
|
||||
uint16_t tslot_min; /* Minimum Read/Write Slot time */
|
||||
uint16_t tslot_max; /* Maximum Read/Write Slot time */
|
||||
|
||||
uint16_t tw1l_max; /* Maximum Master Write 1 time */
|
||||
uint16_t trl_tmsr_max; /* Maximum Master Read Low + Read Sample time */
|
||||
} OneWireSlaveTimings;
|
||||
|
||||
struct OneWireSlave {
|
||||
const GpioPin* gpio_pin;
|
||||
const OneWireSlaveTimings* timings;
|
||||
OneWireSlaveError error;
|
||||
|
||||
bool is_first_reset;
|
||||
bool is_short_reset;
|
||||
|
||||
OneWireSlaveResetCallback reset_callback;
|
||||
OneWireSlaveCommandCallback command_callback;
|
||||
OneWireSlaveResultCallback result_callback;
|
||||
@@ -39,42 +45,72 @@ struct OneWireSlave {
|
||||
void* command_callback_context;
|
||||
};
|
||||
|
||||
static const OneWireSlaveTimings onewire_slave_timings_normal = {
|
||||
.trstl_min = 270,
|
||||
.trstl_max = 1200,
|
||||
|
||||
.tpdh_typ = 20,
|
||||
.tpdl_min = 100,
|
||||
.tpdl_max = 480,
|
||||
|
||||
.tslot_min = 60,
|
||||
.tslot_max = 135,
|
||||
|
||||
.tw1l_max = 20,
|
||||
.trl_tmsr_max = 30,
|
||||
};
|
||||
|
||||
static const OneWireSlaveTimings onewire_slave_timings_overdrive = {
|
||||
.trstl_min = 48,
|
||||
.trstl_max = 80,
|
||||
|
||||
.tpdh_typ = 0,
|
||||
.tpdl_min = 8,
|
||||
.tpdl_max = 24,
|
||||
|
||||
.tslot_min = 6,
|
||||
.tslot_max = 16,
|
||||
|
||||
.tw1l_max = 2,
|
||||
.trl_tmsr_max = 3,
|
||||
};
|
||||
|
||||
/*********************** PRIVATE ***********************/
|
||||
|
||||
static uint32_t
|
||||
onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) {
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond();
|
||||
uint32_t time_captured;
|
||||
static bool
|
||||
onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time_us, const bool pin_value) {
|
||||
const uint32_t time_start = DWT->CYCCNT;
|
||||
const uint32_t time_ticks = time_us * furi_hal_cortex_instructions_per_microsecond();
|
||||
|
||||
uint32_t time_elapsed;
|
||||
|
||||
do { //-V1044
|
||||
time_captured = DWT->CYCCNT;
|
||||
time_elapsed = DWT->CYCCNT - time_start;
|
||||
if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) {
|
||||
uint32_t remaining_time = time_ticks - (time_captured - start);
|
||||
remaining_time /= furi_hal_cortex_instructions_per_microsecond();
|
||||
return remaining_time;
|
||||
return time_ticks >= time_elapsed;
|
||||
}
|
||||
} while((time_captured - start) < time_ticks);
|
||||
} while(time_elapsed < time_ticks);
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool onewire_slave_show_presence(OneWireSlave* bus) {
|
||||
static inline bool onewire_slave_show_presence(OneWireSlave* bus) {
|
||||
const OneWireSlaveTimings* timings = bus->timings;
|
||||
// wait until the bus is high (might return immediately)
|
||||
onewire_slave_wait_while_gpio_is(bus, ONEWIRE_TRSTL_MAX, false);
|
||||
onewire_slave_wait_while_gpio_is(bus, timings->trstl_max, false);
|
||||
// wait while master delay presence check
|
||||
furi_delay_us(ONEWIRE_TPDH_TYP);
|
||||
furi_delay_us(timings->tpdh_typ);
|
||||
|
||||
// show presence
|
||||
furi_hal_gpio_write(bus->gpio_pin, false);
|
||||
furi_delay_us(ONEWIRE_TPDL_MIN);
|
||||
furi_delay_us(timings->tpdl_min);
|
||||
furi_hal_gpio_write(bus->gpio_pin, true);
|
||||
|
||||
// somebody also can show presence
|
||||
const uint32_t wait_low_time = ONEWIRE_TPDL_MAX - ONEWIRE_TPDL_MIN;
|
||||
const uint32_t wait_low_time = timings->tpdl_max - timings->tpdl_min;
|
||||
|
||||
// so we will wait
|
||||
if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) {
|
||||
if(!onewire_slave_wait_while_gpio_is(bus, wait_low_time, false)) {
|
||||
bus->error = OneWireSlaveErrorPresenceConflict;
|
||||
return false;
|
||||
}
|
||||
@@ -85,27 +121,36 @@ static bool onewire_slave_show_presence(OneWireSlave* bus) {
|
||||
static inline bool onewire_slave_receive_and_process_command(OneWireSlave* bus) {
|
||||
/* Reset condition detected, send a presence pulse and reset protocol state */
|
||||
if(bus->error == OneWireSlaveErrorResetInProgress) {
|
||||
if(onewire_slave_show_presence(bus)) {
|
||||
bus->error = OneWireSlaveErrorNone;
|
||||
if(!bus->is_first_reset) {
|
||||
/* Guess the reset type */
|
||||
bus->is_short_reset = onewire_slave_wait_while_gpio_is(
|
||||
bus,
|
||||
onewire_slave_timings_overdrive.trstl_max -
|
||||
onewire_slave_timings_overdrive.tslot_max,
|
||||
false);
|
||||
} else {
|
||||
bus->is_first_reset = false;
|
||||
}
|
||||
|
||||
if(bus->reset_callback != NULL) {
|
||||
bus->reset_callback(bus->reset_callback_context);
|
||||
furi_assert(bus->reset_callback);
|
||||
|
||||
if(bus->reset_callback(bus->is_short_reset, bus->reset_callback_context)) {
|
||||
if(onewire_slave_show_presence(bus)) {
|
||||
bus->error = OneWireSlaveErrorNone;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(bus->error == OneWireSlaveErrorNone) {
|
||||
uint8_t command;
|
||||
if(!onewire_slave_receive(bus, &command, 1)) {
|
||||
/* Upon failure, request an additional iteration to
|
||||
choose the appropriate action by checking bus->error */
|
||||
return true;
|
||||
} else if(bus->command_callback) {
|
||||
return bus->command_callback(command, bus->command_callback_context);
|
||||
} else {
|
||||
bus->error = OneWireSlaveErrorInvalidCommand;
|
||||
if(onewire_slave_receive(bus, &command, sizeof(command))) {
|
||||
furi_assert(bus->command_callback);
|
||||
if(bus->command_callback(command, bus->command_callback_context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return (bus->error == OneWireSlaveErrorResetInProgress);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -115,9 +160,6 @@ static inline bool onewire_slave_bus_start(OneWireSlave* bus) {
|
||||
FURI_CRITICAL_ENTER();
|
||||
furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
/* Start in Reset state in order to send a presence pulse immediately */
|
||||
bus->error = OneWireSlaveErrorResetInProgress;
|
||||
|
||||
while(onewire_slave_receive_and_process_command(bus))
|
||||
;
|
||||
|
||||
@@ -139,7 +181,15 @@ static void onewire_slave_exti_callback(void* context) {
|
||||
const uint32_t pulse_length =
|
||||
(DWT->CYCCNT - pulse_start) / furi_hal_cortex_instructions_per_microsecond();
|
||||
|
||||
if((pulse_length >= ONEWIRE_TRSTL_MIN) && pulse_length <= (ONEWIRE_TRSTL_MAX)) {
|
||||
if((pulse_length >= onewire_slave_timings_overdrive.trstl_min) &&
|
||||
(pulse_length <= onewire_slave_timings_normal.trstl_max)) {
|
||||
/* Start in reset state in order to send a presence pulse immediately */
|
||||
bus->error = OneWireSlaveErrorResetInProgress;
|
||||
/* Determine reset type (chooses speed mode if supported by the emulated device) */
|
||||
bus->is_short_reset = pulse_length <= onewire_slave_timings_overdrive.trstl_max;
|
||||
/* Initial reset allows going directly into overdrive mode */
|
||||
bus->is_first_reset = true;
|
||||
|
||||
const bool result = onewire_slave_bus_start(bus);
|
||||
|
||||
if(result && bus->result_callback != NULL) {
|
||||
@@ -158,6 +208,7 @@ OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) {
|
||||
OneWireSlave* bus = malloc(sizeof(OneWireSlave));
|
||||
|
||||
bus->gpio_pin = gpio_pin;
|
||||
bus->timings = &onewire_slave_timings_normal;
|
||||
bus->error = OneWireSlaveErrorNone;
|
||||
|
||||
return bus;
|
||||
@@ -205,52 +256,45 @@ void onewire_slave_set_result_callback(
|
||||
}
|
||||
|
||||
bool onewire_slave_receive_bit(OneWireSlave* bus) {
|
||||
const OneWireSlaveTimings* timings = bus->timings;
|
||||
// wait while bus is low
|
||||
uint32_t time = ONEWIRE_TSLOT_MAX;
|
||||
time = onewire_slave_wait_while_gpio_is(bus, time, false);
|
||||
if(time == 0) {
|
||||
if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) {
|
||||
bus->error = OneWireSlaveErrorResetInProgress;
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait while bus is high
|
||||
time = ONEWIRE_TH_TIMEOUT;
|
||||
time = onewire_slave_wait_while_gpio_is(bus, time, true);
|
||||
if(time == 0) {
|
||||
if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) {
|
||||
bus->error = OneWireSlaveErrorTimeout;
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait a time of zero
|
||||
time = ONEWIRE_TW1L_MAX;
|
||||
time = onewire_slave_wait_while_gpio_is(bus, time, false);
|
||||
|
||||
return (time > 0);
|
||||
return onewire_slave_wait_while_gpio_is(bus, timings->tw1l_max, false);
|
||||
}
|
||||
|
||||
bool onewire_slave_send_bit(OneWireSlave* bus, bool value) {
|
||||
const OneWireSlaveTimings* timings = bus->timings;
|
||||
// wait while bus is low
|
||||
uint32_t time = ONEWIRE_TSLOT_MAX;
|
||||
time = onewire_slave_wait_while_gpio_is(bus, time, false);
|
||||
if(time == 0) {
|
||||
if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) {
|
||||
bus->error = OneWireSlaveErrorResetInProgress;
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait while bus is high
|
||||
time = ONEWIRE_TH_TIMEOUT;
|
||||
time = onewire_slave_wait_while_gpio_is(bus, time, true);
|
||||
if(time == 0) {
|
||||
if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) {
|
||||
bus->error = OneWireSlaveErrorTimeout;
|
||||
return false;
|
||||
}
|
||||
|
||||
// choose write time
|
||||
uint32_t time;
|
||||
|
||||
if(!value) {
|
||||
furi_hal_gpio_write(bus->gpio_pin, false);
|
||||
time = ONEWIRE_TRL_TMSR_MAX;
|
||||
time = timings->trl_tmsr_max;
|
||||
} else {
|
||||
time = ONEWIRE_TSLOT_MIN;
|
||||
time = timings->tslot_min;
|
||||
}
|
||||
|
||||
// hold line for ZERO or ONE time
|
||||
@@ -301,3 +345,13 @@ bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void onewire_slave_set_overdrive(OneWireSlave* bus, bool set) {
|
||||
const OneWireSlaveTimings* new_timings = set ? &onewire_slave_timings_overdrive :
|
||||
&onewire_slave_timings_normal;
|
||||
if(bus->timings != new_timings) {
|
||||
/* Prevent erroneous reset by waiting for the previous time slot to finish */
|
||||
onewire_slave_wait_while_gpio_is(bus, bus->timings->tslot_max, false);
|
||||
bus->timings = new_timings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,68 +18,85 @@ extern "C" {
|
||||
typedef struct OneWireDevice OneWireDevice;
|
||||
typedef struct OneWireSlave OneWireSlave;
|
||||
|
||||
typedef void (*OneWireSlaveResetCallback)(void* context);
|
||||
typedef void (*OneWireSlaveResultCallback)(void* context);
|
||||
typedef bool (*OneWireSlaveResetCallback)(bool is_short, void* context);
|
||||
typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context);
|
||||
typedef void (*OneWireSlaveResultCallback)(void* context);
|
||||
|
||||
/**
|
||||
* Allocate onewire slave
|
||||
* @param gpio_pin
|
||||
* @return OneWireSlave*
|
||||
* Allocate OneWireSlave instance
|
||||
* @param [in] gpio_pin connection pin
|
||||
* @return pointer to OneWireSlave instance
|
||||
*/
|
||||
OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin);
|
||||
|
||||
/**
|
||||
* Free onewire slave
|
||||
* @param bus
|
||||
* Destroy OneWireSlave instance, free resources
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
*/
|
||||
void onewire_slave_free(OneWireSlave* bus);
|
||||
|
||||
/**
|
||||
* Start working with the bus
|
||||
* @param bus
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
*/
|
||||
void onewire_slave_start(OneWireSlave* bus);
|
||||
|
||||
/**
|
||||
* Stop working with the bus
|
||||
* @param bus
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
*/
|
||||
void onewire_slave_stop(OneWireSlave* bus);
|
||||
|
||||
/**
|
||||
* TODO: description comment
|
||||
* Receive one bit
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @return received bit value
|
||||
*/
|
||||
bool onewire_slave_receive_bit(OneWireSlave* bus);
|
||||
|
||||
/**
|
||||
* TODO: description comment
|
||||
* Send one bit
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [in] value bit value to send
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool onewire_slave_send_bit(OneWireSlave* bus, bool value);
|
||||
|
||||
/**
|
||||
* Send data
|
||||
* @param bus
|
||||
* @param data
|
||||
* @param data_size
|
||||
* @return bool
|
||||
* Send one or more bytes of data
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [in] data pointer to the data to send
|
||||
* @param [in] data_size size of the data to send
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size);
|
||||
|
||||
/**
|
||||
* Receive data
|
||||
* @param bus
|
||||
* @param data
|
||||
* @param data_size
|
||||
* @return bool
|
||||
* Receive one or more bytes of data
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [out] data pointer to the receive buffer
|
||||
* @param [in] data_size number of bytes to receive
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size);
|
||||
|
||||
/**
|
||||
* Set a callback to be called on each reset
|
||||
* @param bus
|
||||
* @param callback
|
||||
* @param context
|
||||
* Enable overdrive mode
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [in] set true to turn overdrive on, false to turn it off
|
||||
*/
|
||||
void onewire_slave_set_overdrive(OneWireSlave* bus, bool set);
|
||||
|
||||
/**
|
||||
* Set a callback function to be called on each reset.
|
||||
* The return value of the callback determines whether the emulated device
|
||||
* supports the short reset (passed as the is_short parameter).
|
||||
* In most applications, it should also call onewire_slave_set_overdrive()
|
||||
* to set the appropriate speed mode.
|
||||
*
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [in] callback pointer to a callback function
|
||||
* @param [in] context additional parameter to be passed to the callback
|
||||
*/
|
||||
void onewire_slave_set_reset_callback(
|
||||
OneWireSlave* bus,
|
||||
@@ -87,10 +104,13 @@ void onewire_slave_set_reset_callback(
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Set a callback to be called on each command
|
||||
* @param bus
|
||||
* @param callback
|
||||
* @param context
|
||||
* Set a callback function to be called on each command.
|
||||
* The return value of the callback determines whether further operation
|
||||
* is possible. As a rule of thumb, return true unless a critical error happened.
|
||||
*
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [in] callback pointer to a callback function
|
||||
* @param [in] context additional parameter to be passed to the callback
|
||||
*/
|
||||
void onewire_slave_set_command_callback(
|
||||
OneWireSlave* bus,
|
||||
@@ -99,9 +119,9 @@ void onewire_slave_set_command_callback(
|
||||
|
||||
/**
|
||||
* Set a callback to report emulation success
|
||||
* @param bus
|
||||
* @param result_cb
|
||||
* @param context
|
||||
* @param [in] bus pointer to OneWireSlave instance
|
||||
* @param [in] result_cb pointer to a callback function
|
||||
* @param [in] context additional parameter to be passed to the callback
|
||||
*/
|
||||
void onewire_slave_set_result_callback(
|
||||
OneWireSlave* bus,
|
||||
|
||||
@@ -1,115 +1,112 @@
|
||||
#include <furi_hal_compress.h>
|
||||
#include "compress.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/heatshrink/heatshrink_encoder.h>
|
||||
#include <lib/heatshrink/heatshrink_decoder.h>
|
||||
|
||||
#define TAG "FuriHalCompress"
|
||||
/** Defines encoder and decoder window size */
|
||||
#define COMPRESS_EXP_BUFF_SIZE_LOG (8u)
|
||||
|
||||
#define FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE (2 * 512)
|
||||
#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024)
|
||||
/** Defines encoder and decoder lookahead buffer size */
|
||||
#define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u)
|
||||
|
||||
#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE (1 << FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG)
|
||||
/** Buffer sizes for input and output data */
|
||||
#define COMPRESS_ICON_ENCODED_BUFF_SIZE (1024u)
|
||||
#define COMPRESS_ICON_DECODED_BUFF_SIZE (1024u)
|
||||
|
||||
typedef struct {
|
||||
uint8_t is_compressed;
|
||||
uint8_t reserved;
|
||||
uint16_t compressed_buff_size;
|
||||
} FuriHalCompressHeader;
|
||||
} CompressHeader;
|
||||
|
||||
typedef struct {
|
||||
heatshrink_decoder* decoder;
|
||||
uint8_t
|
||||
compress_buff[FURI_HAL_COMPRESS_EXP_BUFF_SIZE + FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE];
|
||||
uint8_t decoded_buff[FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE];
|
||||
} FuriHalCompressIcon;
|
||||
_Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size");
|
||||
|
||||
struct FuriHalCompress {
|
||||
heatshrink_encoder* encoder;
|
||||
struct CompressIcon {
|
||||
heatshrink_decoder* decoder;
|
||||
uint8_t* compress_buff;
|
||||
uint16_t compress_buff_size;
|
||||
uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE];
|
||||
};
|
||||
|
||||
static FuriHalCompressIcon* icon_decoder;
|
||||
CompressIcon* compress_icon_alloc() {
|
||||
CompressIcon* instance = malloc(sizeof(CompressIcon));
|
||||
instance->decoder = heatshrink_decoder_alloc(
|
||||
COMPRESS_ICON_ENCODED_BUFF_SIZE,
|
||||
COMPRESS_EXP_BUFF_SIZE_LOG,
|
||||
COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
heatshrink_decoder_reset(instance->decoder);
|
||||
memset(instance->decoded_buff, 0, sizeof(instance->decoded_buff));
|
||||
|
||||
static void furi_hal_compress_reset(FuriHalCompress* compress) {
|
||||
furi_assert(compress);
|
||||
heatshrink_encoder_reset(compress->encoder);
|
||||
heatshrink_decoder_reset(compress->decoder);
|
||||
memset(compress->compress_buff, 0, compress->compress_buff_size);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void furi_hal_compress_icon_init() {
|
||||
icon_decoder = malloc(sizeof(FuriHalCompressIcon));
|
||||
icon_decoder->decoder = heatshrink_decoder_alloc(
|
||||
icon_decoder->compress_buff,
|
||||
FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE,
|
||||
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
|
||||
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
heatshrink_decoder_reset(icon_decoder->decoder);
|
||||
memset(icon_decoder->decoded_buff, 0, sizeof(icon_decoder->decoded_buff));
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
void compress_icon_free(CompressIcon* instance) {
|
||||
furi_assert(instance);
|
||||
heatshrink_decoder_free(instance->decoder);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff) {
|
||||
void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff) {
|
||||
furi_assert(instance);
|
||||
furi_assert(icon_data);
|
||||
furi_assert(decoded_buff);
|
||||
|
||||
FuriHalCompressHeader* header = (FuriHalCompressHeader*)icon_data;
|
||||
CompressHeader* header = (CompressHeader*)icon_data;
|
||||
if(header->is_compressed) {
|
||||
size_t data_processed = 0;
|
||||
heatshrink_decoder_sink(
|
||||
icon_decoder->decoder,
|
||||
(uint8_t*)&icon_data[4],
|
||||
instance->decoder,
|
||||
(uint8_t*)&icon_data[sizeof(CompressHeader)],
|
||||
header->compressed_buff_size,
|
||||
&data_processed);
|
||||
while(1) {
|
||||
HSD_poll_res res = heatshrink_decoder_poll(
|
||||
icon_decoder->decoder,
|
||||
icon_decoder->decoded_buff,
|
||||
sizeof(icon_decoder->decoded_buff),
|
||||
instance->decoder,
|
||||
instance->decoded_buff,
|
||||
sizeof(instance->decoded_buff),
|
||||
&data_processed);
|
||||
furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE));
|
||||
if(res != HSDR_POLL_MORE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
heatshrink_decoder_reset(icon_decoder->decoder);
|
||||
memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff));
|
||||
*decoded_buff = icon_decoder->decoded_buff;
|
||||
heatshrink_decoder_reset(instance->decoder);
|
||||
*decoded_buff = instance->decoded_buff;
|
||||
} else {
|
||||
*decoded_buff = (uint8_t*)&icon_data[1];
|
||||
}
|
||||
}
|
||||
|
||||
FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) {
|
||||
FuriHalCompress* compress = malloc(sizeof(FuriHalCompress));
|
||||
compress->compress_buff = malloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE);
|
||||
compress->encoder = heatshrink_encoder_alloc(
|
||||
compress->compress_buff,
|
||||
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
|
||||
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
struct Compress {
|
||||
heatshrink_encoder* encoder;
|
||||
heatshrink_decoder* decoder;
|
||||
};
|
||||
|
||||
static void compress_reset(Compress* compress) {
|
||||
furi_assert(compress);
|
||||
heatshrink_encoder_reset(compress->encoder);
|
||||
heatshrink_decoder_reset(compress->decoder);
|
||||
}
|
||||
|
||||
Compress* compress_alloc(uint16_t compress_buff_size) {
|
||||
Compress* compress = malloc(sizeof(Compress));
|
||||
compress->encoder =
|
||||
heatshrink_encoder_alloc(COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
compress->decoder = heatshrink_decoder_alloc(
|
||||
compress->compress_buff,
|
||||
compress_buff_size,
|
||||
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
|
||||
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
compress_buff_size, COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
|
||||
return compress;
|
||||
}
|
||||
|
||||
void furi_hal_compress_free(FuriHalCompress* compress) {
|
||||
void compress_free(Compress* compress) {
|
||||
furi_assert(compress);
|
||||
|
||||
heatshrink_encoder_free(compress->encoder);
|
||||
heatshrink_decoder_free(compress->decoder);
|
||||
free(compress->compress_buff);
|
||||
free(compress);
|
||||
}
|
||||
|
||||
bool furi_hal_compress_encode(
|
||||
FuriHalCompress* compress,
|
||||
bool compress_encode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
@@ -126,7 +123,7 @@ bool furi_hal_compress_encode(
|
||||
HSE_finish_res finish_res;
|
||||
bool encode_failed = false;
|
||||
size_t sunk = 0;
|
||||
size_t res_buff_size = sizeof(FuriHalCompressHeader);
|
||||
size_t res_buff_size = sizeof(CompressHeader);
|
||||
|
||||
// Sink data to encoding buffer
|
||||
while((sunk < data_in_size) && !encode_failed) {
|
||||
@@ -174,7 +171,7 @@ bool furi_hal_compress_encode(
|
||||
bool result = true;
|
||||
// Write encoded data to output buffer if compression is efficient. Else - write header and original data
|
||||
if(!encode_failed && (res_buff_size < data_in_size + 1)) {
|
||||
FuriHalCompressHeader header = {
|
||||
CompressHeader header = {
|
||||
.is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size};
|
||||
memcpy(data_out, &header, sizeof(header));
|
||||
*data_res_size = res_buff_size;
|
||||
@@ -186,13 +183,13 @@ bool furi_hal_compress_encode(
|
||||
*data_res_size = 0;
|
||||
result = false;
|
||||
}
|
||||
furi_hal_compress_reset(compress);
|
||||
compress_reset(compress);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool furi_hal_compress_decode(
|
||||
FuriHalCompress* compress,
|
||||
bool compress_decode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
@@ -212,11 +209,11 @@ bool furi_hal_compress_decode(
|
||||
size_t res_buff_size = 0;
|
||||
size_t poll_size = 0;
|
||||
|
||||
FuriHalCompressHeader* header = (FuriHalCompressHeader*)data_in;
|
||||
CompressHeader* header = (CompressHeader*)data_in;
|
||||
if(header->is_compressed) {
|
||||
// Sink data to decoding buffer
|
||||
size_t compressed_size = header->compressed_buff_size;
|
||||
size_t sunk = sizeof(FuriHalCompressHeader);
|
||||
size_t sunk = sizeof(CompressHeader);
|
||||
while(sunk < compressed_size && !decode_failed) {
|
||||
sink_res = heatshrink_decoder_sink(
|
||||
compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
|
||||
@@ -258,7 +255,7 @@ bool furi_hal_compress_decode(
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
furi_hal_compress_reset(compress);
|
||||
compress_reset(compress);
|
||||
|
||||
return result;
|
||||
}
|
||||
96
lib/toolbox/compress.h
Normal file
96
lib/toolbox/compress.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @file compress.h
|
||||
* LZSS based compression HAL API
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Compress Icon control structure */
|
||||
typedef struct CompressIcon CompressIcon;
|
||||
|
||||
/** Initialize icon compressor
|
||||
*
|
||||
* @return Compress Icon instance
|
||||
*/
|
||||
CompressIcon* compress_icon_alloc();
|
||||
|
||||
/** Free icon compressor
|
||||
*
|
||||
* @param instance The Compress Icon instance
|
||||
*/
|
||||
void compress_icon_free(CompressIcon* instance);
|
||||
|
||||
/** Decompress icon
|
||||
*
|
||||
* @warning decoded_buff pointer set by this function is valid till next
|
||||
* `compress_icon_decode` or `compress_icon_free` call
|
||||
*
|
||||
* @param instance The Compress Icon instance
|
||||
* @param icon_data pointer to icon data
|
||||
* @param[in] decoded_buff pointer to decoded buffer pointer
|
||||
*/
|
||||
void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff);
|
||||
|
||||
/** Compress control structure */
|
||||
typedef struct Compress Compress;
|
||||
|
||||
/** Allocate encoder and decoder
|
||||
*
|
||||
* @param compress_buff_size size of decoder and encoder buffer to allocate
|
||||
*
|
||||
* @return Compress instance
|
||||
*/
|
||||
Compress* compress_alloc(uint16_t compress_buff_size);
|
||||
|
||||
/** Free encoder and decoder
|
||||
*
|
||||
* @param compress Compress instance
|
||||
*/
|
||||
void compress_free(Compress* compress);
|
||||
|
||||
/** Encode data
|
||||
*
|
||||
* @param compress Compress instance
|
||||
* @param data_in pointer to input data
|
||||
* @param data_in_size size of input data
|
||||
* @param data_out maximum size of output data
|
||||
* @param data_res_size pointer to result output data size
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool compress_encode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size);
|
||||
|
||||
/** Decode data
|
||||
*
|
||||
* @param compress Compress instance
|
||||
* @param data_in pointer to input data
|
||||
* @param data_in_size size of input data
|
||||
* @param data_out maximum size of output data
|
||||
* @param data_res_size pointer to result output data size
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool compress_decode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user