diff --git a/cmake_gcc/opendisplay-bg22.cmake b/cmake_gcc/opendisplay-bg22.cmake index 0413239..ff2852c 100644 --- a/cmake_gcc/opendisplay-bg22.cmake +++ b/cmake_gcc/opendisplay-bg22.cmake @@ -189,6 +189,7 @@ add_library(slc OBJECT "../main.c" "../third_party/bb_epaper/src/bb_epaper.cpp" "../third_party/bb_epaper/src/Group5.cpp" + "../third_party/uzlib/src/od_zlib_stream.c" ) target_include_directories(slc PUBLIC @@ -198,6 +199,7 @@ target_include_directories(slc PUBLIC "../." "../third_party/segger_rtt" "../third_party/bb_epaper/src" + "../third_party/uzlib/src" "../${COPIED_SDK_PATH}/devices/platform/Device/SiliconLabs/EFR32BG22/Include" "../${COPIED_SDK_PATH}/platform_common_apps/app/common/util/app_assert" "../${COPIED_SDK_PATH}/platform_common/platform/common/inc" @@ -262,6 +264,8 @@ target_include_directories(slc PUBLIC ) target_compile_definitions(slc PUBLIC + "OPENDISPLAY_ZLIB_WINDOW_BITS=9" + "OPENDISPLAY_ZLIB_USE_HEAP_WINDOW=0" "__SILABS_BG22__=1" "EFR32BG22C222F352GM40=1" "SL_CODE_COMPONENT_SYSTEM=system" diff --git a/opendisplay_ble.c b/opendisplay_ble.c index 2294c09..3867b87 100644 --- a/opendisplay_ble.c +++ b/opendisplay_ble.c @@ -4,6 +4,7 @@ #include "opendisplay_led.h" #include "opendisplay_pipe.h" #include "opendisplay_constants.h" +#include "app.h" #include "app_assert.h" #include "gatt_db.h" #include "em_cmu.h" @@ -31,9 +32,10 @@ /* BLE adv interval in units of 0.625 ms (used when not connected / undirected adv). */ #define OD_ADV_INTERVAL_IDLE_SLOTS 1600u +#define OD_ADV_INTERVAL_BOOST_MIN 32u +#define OD_ADV_INTERVAL_BOOST_MAX 48u +#define OD_ADV_BOOST_MS 3000u #define OD_MSD_UPDATE_INTERVAL_MS 30000u -#define BUTTON_PRESS_TIMEOUT_MS 5000u -#define BUTTON_MAX_PRESS_COUNT 15u #define BUTTON_ID_MASK 0x07u #define PRESS_COUNT_MASK 0x0Fu #define PRESS_COUNT_SHIFT 3u @@ -74,11 +76,12 @@ static uint8_t reboot_flag = 1u; static uint8_t connection_requested = 0u; static uint16_t g_od_pipe_char; -static uint8_t g_connection; +static uint8_t g_connection = 0xFFu; static uint8_t s_adv_handle = 0xFFu; static char s_dev_name[16]; static struct GlobalConfig s_od_global_config; static uint32_t s_last_msd_refresh_ms; +static uint32_t s_adv_boost_until_ms; static bool s_pending_dfu; static bool s_pending_deep_sleep; static uint32_t s_connection_open_ms; @@ -137,12 +140,14 @@ typedef struct { bool inverted; uint8_t press_count; uint8_t current_state; - uint32_t last_press_ms; } od_button_state_t; static od_button_state_t s_buttons[32]; static uint8_t s_button_count; -static volatile bool s_button_event_pending; -static volatile uint8_t s_last_changed_button_index = 0xFFu; +static volatile bool s_button_msd_dirty; + +static void build_and_apply_adv(uint8_t adv_set, const char *name, bool quick); +static void od_boost_advertising(uint32_t now_ms); +static void od_apply_advertising_timing(uint8_t adv_handle, uint32_t now_ms); #if defined(__GNUC__) extern uint32_t __ResetReasonStart__; @@ -213,12 +218,29 @@ static void od_setup_button_pin(uint8_t pin_cfg, bool pullup, bool pulldown) static void od_button_irq_callback(uint8_t int_no, void *context) { od_button_state_t *st = (od_button_state_t *)context; + uint8_t pin_state; + uint8_t logical_state; + uint8_t encoded; + (void)int_no; if (st == NULL || !st->active) { return; } - s_last_changed_button_index = (uint8_t)(st - s_buttons); - s_button_event_pending = true; + pin_state = od_read_button_pin(st->pin_cfg); + logical_state = (st->inverted ? (pin_state == 0u) : (pin_state != 0u)) ? 1u : 0u; + if (logical_state == st->current_state) { + return; + } + st->current_state = logical_state; + if (logical_state != 0u) { + st->press_count = (uint8_t)((st->press_count + 1u) & PRESS_COUNT_MASK); + } + encoded = (uint8_t)((st->button_id & BUTTON_ID_MASK) + | ((st->press_count & PRESS_COUNT_MASK) << PRESS_COUNT_SHIFT) + | ((st->current_state & 0x01u) << BUTTON_STATE_SHIFT)); + dynamic_return[st->byte_index] = encoded; + s_button_msd_dirty = true; + app_proceed(); } static void od_buttons_deinit_interrupts(void) @@ -245,8 +267,7 @@ static void od_buttons_init_from_config(void) od_buttons_deinit_interrupts(); s_button_count = 0u; - s_button_event_pending = false; - s_last_changed_button_index = 0xFFu; + s_button_msd_dirty = false; memset(s_buttons, 0, sizeof(s_buttons)); printf("[OD][BTN] init: binary_input_count=%u\r\n", (unsigned)cfg->binary_input_count); for (i = 0; i < cfg->binary_input_count; i++) { @@ -331,47 +352,17 @@ static void od_buttons_init_from_config(void) printf("[OD][BTN] init done active_buttons=%u\r\n", (unsigned)s_button_count); } -static bool od_process_button_event(uint32_t now_ms) +static void od_publish_button_msd(uint8_t adv_handle, uint32_t now_ms) { - uint8_t idx; - od_button_state_t *st; - uint8_t pin_state; - uint8_t logical_state; - uint8_t encoded; - - if (!s_button_event_pending) { - return false; - } - s_button_event_pending = false; - idx = s_last_changed_button_index; - s_last_changed_button_index = 0xFFu; - if (idx >= s_button_count) { - return false; - } - st = &s_buttons[idx]; - if (!st->active) { - return false; - } - pin_state = od_read_button_pin(st->pin_cfg); - logical_state = (st->inverted ? (pin_state == 0u) : (pin_state != 0u)) ? 1u : 0u; - if (logical_state == st->current_state) { - return false; - } - st->current_state = logical_state; - if (logical_state != 0u) { - if (st->last_press_ms == 0u || (now_ms - st->last_press_ms) > BUTTON_PRESS_TIMEOUT_MS) { - st->press_count = 0u; - } - if (st->press_count < BUTTON_MAX_PRESS_COUNT) { - st->press_count++; - } - st->last_press_ms = now_ms; + if (!s_button_msd_dirty || adv_handle == 0xFFu || g_connection != 0xFFu) { + return; } - encoded = (uint8_t)((st->button_id & BUTTON_ID_MASK) - | ((st->press_count & PRESS_COUNT_MASK) << PRESS_COUNT_SHIFT) - | ((st->current_state & 0x01u) << BUTTON_STATE_SHIFT)); - dynamic_return[st->byte_index] = encoded; - return true; + s_button_msd_dirty = false; + od_boost_advertising(now_ms); + (void)sl_bt_advertiser_stop(adv_handle); + build_and_apply_adv(adv_handle, s_dev_name, true); + od_apply_advertising_timing(adv_handle, now_ms); + app_assert_status(sl_bt_legacy_advertiser_start(adv_handle, sl_bt_legacy_advertiser_connectable)); } static void od_buttons_arm_em4_wakeup(void) @@ -1513,20 +1504,56 @@ void opendisplay_ble_reload_config_from_nvm(void) opendisplay_display_boot_apply(); } -static void od_apply_idle_advertising_timing(uint8_t adv_handle) +static void od_apply_advertising_timing(uint8_t adv_handle, uint32_t now_ms) { sl_status_t sc; + uint16_t min_slots; + uint16_t max_slots; if (adv_handle == 0xFFu) { return; } - sc = sl_bt_advertiser_set_timing(adv_handle, OD_ADV_INTERVAL_IDLE_SLOTS, OD_ADV_INTERVAL_IDLE_SLOTS, 0, 0); + if (s_adv_boost_until_ms != 0u && now_ms < s_adv_boost_until_ms) { + min_slots = OD_ADV_INTERVAL_BOOST_MIN; + max_slots = OD_ADV_INTERVAL_BOOST_MAX; + } else { + s_adv_boost_until_ms = 0u; + min_slots = OD_ADV_INTERVAL_IDLE_SLOTS; + max_slots = OD_ADV_INTERVAL_IDLE_SLOTS; + } + sc = sl_bt_advertiser_set_timing(adv_handle, min_slots, max_slots, 0, 0); if (sc != SL_STATUS_OK) { printf("[OD] advertiser_set_timing sc=0x%04lX\r\n", (unsigned long)sc); } app_assert_status(sc); } +static void od_boost_advertising(uint32_t now_ms) +{ + s_adv_boost_until_ms = now_ms + OD_ADV_BOOST_MS; +} + +static void od_advertising_boost_tick(uint8_t adv_handle, uint32_t now_ms) +{ + static bool was_boosted = false; + bool boosting = (s_adv_boost_until_ms != 0u && now_ms < s_adv_boost_until_ms); + + if (boosting) { + was_boosted = true; + return; + } + if (!was_boosted) { + return; + } + was_boosted = false; + s_adv_boost_until_ms = 0u; + od_apply_advertising_timing(adv_handle, now_ms); + if (g_connection == 0xFFu) { + (void)sl_bt_advertiser_stop(adv_handle); + app_assert_status(sl_bt_legacy_advertiser_start(adv_handle, sl_bt_legacy_advertiser_connectable)); + } +} + static void chip_id_hex6(char out[7]) { uint64_t u = SYSTEM_GetUnique(); @@ -1534,14 +1561,15 @@ static void chip_id_hex6(char out[7]) snprintf(out, 7, "%06lX", (unsigned long)id); } -static void update_msd_payload(void) +static void update_msd_payload(bool quick) { float chip_temperature = EMU_TemperatureGet(); int16_t temp_encoded; uint16_t battery_voltage_10mv = 0u; uint32_t now_ms = sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count()); - bool measure_batt = (s_batt_voltage_mv_cache == 0u) - || ((now_ms - s_last_batt_measure_ms) > OD_MSD_UPDATE_INTERVAL_MS); + bool measure_batt = !quick + && ((s_batt_voltage_mv_cache == 0u) + || ((now_ms - s_last_batt_measure_ms) > OD_MSD_UPDATE_INTERVAL_MS)); uint8_t temperature_byte; uint8_t battery_voltage_low_byte; uint8_t status_byte; @@ -1672,7 +1700,7 @@ static sl_status_t install_opendisplay_gatt(void) return SL_STATUS_OK; } -static void build_and_apply_adv(uint8_t adv_set, const char *name) +static void build_and_apply_adv(uint8_t adv_set, const char *name, bool quick) { uint8_t adv[31]; uint8_t sr[31]; @@ -1681,7 +1709,7 @@ static void build_and_apply_adv(uint8_t adv_set, const char *name) size_t nl = strlen(name); sl_status_t sc; - update_msd_payload(); + update_msd_payload(quick); adv[ai++] = 2u; adv[ai++] = 0x01u; @@ -1778,10 +1806,11 @@ void opendisplay_ble_on_boot(uint8_t advertising_set_handle) opendisplay_led_init(); opendisplay_display_boot_apply(); - build_and_apply_adv(advertising_set_handle, s_dev_name); + build_and_apply_adv(advertising_set_handle, s_dev_name, false); printf("[OD] advertising + scan rsp set\r\n"); - od_apply_idle_advertising_timing(advertising_set_handle); + od_apply_advertising_timing(advertising_set_handle, + sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count())); sc = sl_bt_legacy_advertiser_start(advertising_set_handle, sl_bt_legacy_advertiser_connectable); if (sc != SL_STATUS_OK) { @@ -1793,8 +1822,9 @@ void opendisplay_ble_on_boot(uint8_t advertising_set_handle) void opendisplay_ble_restart_advertising(uint8_t advertising_set_handle) { - build_and_apply_adv(advertising_set_handle, s_dev_name); - od_apply_idle_advertising_timing(advertising_set_handle); + build_and_apply_adv(advertising_set_handle, s_dev_name, false); + od_apply_advertising_timing(advertising_set_handle, + sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count())); app_assert_status(sl_bt_legacy_advertiser_start(advertising_set_handle, sl_bt_legacy_advertiser_connectable)); } @@ -1851,16 +1881,17 @@ void opendisplay_ble_process(void) { opendisplay_led_process(); uint32_t now_ms = sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count()); - if (od_process_button_event(now_ms) && s_adv_handle != 0xFFu) { - build_and_apply_adv(s_adv_handle, s_dev_name); + od_publish_button_msd(s_adv_handle, now_ms); + if (s_adv_handle != 0xFFu) { + od_advertising_boost_tick(s_adv_handle, now_ms); } if (od_nfc_field_detect_process(now_ms) && s_adv_handle != 0xFFu) { - build_and_apply_adv(s_adv_handle, s_dev_name); + build_and_apply_adv(s_adv_handle, s_dev_name, false); } if ((now_ms - s_last_msd_refresh_ms) >= OD_MSD_UPDATE_INTERVAL_MS) { s_last_msd_refresh_ms = now_ms; if (s_adv_handle != 0xFFu) { - build_and_apply_adv(s_adv_handle, s_dev_name); + build_and_apply_adv(s_adv_handle, s_dev_name, false); } } if (g_connection != 0xFFu diff --git a/opendisplay_constants.h b/opendisplay_constants.h index f3da0a3..fb2997b 100644 --- a/opendisplay_constants.h +++ b/opendisplay_constants.h @@ -34,4 +34,8 @@ #define OD_NFC_REC_MIME 3u #define OD_NFC_REC_RAW_NDEF 4u +#define TRANSMISSION_MODE_ZIPXL (1u << 0) +#define TRANSMISSION_MODE_ZIP (1u << 1) +#define TRANSMISSION_MODE_DIRECT_WRITE (1u << 3) + #endif diff --git a/opendisplay_display.cpp b/opendisplay_display.cpp index f33958f..fb4f49e 100644 --- a/opendisplay_display.cpp +++ b/opendisplay_display.cpp @@ -2,6 +2,7 @@ #include "opendisplay_display_color.h" #include "opendisplay_ble.h" #include "opendisplay_config_parser.h" +#include "opendisplay_constants.h" #include "opendisplay_epd_map.h" #include "opendisplay_structs.h" #include "bb_epaper.h" @@ -13,6 +14,12 @@ #include #include +extern "C" { +#include "uzlib.h" +} + +#define OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE 256u + static BBEPAPER s_epd; static bool s_active; static uint32_t s_total_bytes; @@ -25,6 +32,9 @@ static uint8_t s_color_scheme; static uint32_t s_plane_size; static bool s_plane2_started; static bool s_boot_applied; +static bool s_dw_compressed; +static uint32_t s_dw_decompressed_total; +static uint8_t s_decompression_chunk[OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE]; #ifndef OD_FALLBACK_DISPLAY_PWR_PIN #define OD_FALLBACK_DISPLAY_PWR_PIN 0x00u @@ -576,6 +586,99 @@ extern "C" void opendisplay_display_abort(void) s_dw_trailing_ignores = 0; s_plane_size = 0; s_plane2_started = false; + s_dw_compressed = false; + s_dw_decompressed_total = 0; +} + +static void dw_log_progress(void) +{ + if (s_total_bytes == 0u) { + return; + } + uint8_t pct = (uint8_t)((100u * s_written_bytes) / s_total_bytes); + if (pct >= s_dw_log_pct + 25u) { + printf("[OD] dw data #%lu %lu/%lu B (%u%%)%s\r\n", (unsigned long)s_dw_chunk_n, + (unsigned long)s_written_bytes, (unsigned long)s_total_bytes, (unsigned)pct, + s_dw_compressed ? " zlib" : ""); + s_dw_log_pct = (pct / 25u) * 25u; + } +} + +static int dw_stream_raw_bytes(const uint8_t *payload, uint32_t payload_len) +{ + uint32_t remaining = (s_written_bytes < s_total_bytes) ? (s_total_bytes - s_written_bytes) : 0u; + const bool bitplanes = opendisplay_color_is_bitplanes(s_color_scheme); + const uint8_t *p = payload; + uint32_t left = payload_len; + const uint32_t written_before = s_written_bytes; + + while (left > 0u && remaining > 0u) { + uint32_t rem = remaining; + uint32_t chunk = left; + if (bitplanes && !s_plane2_started && s_plane_size > 0u) { + uint32_t to_plane_end = s_plane_size - s_written_bytes; + if (chunk > to_plane_end) { + chunk = to_plane_end; + } + } + if (chunk > rem) { + chunk = rem; + } + if (chunk == 0u) { + break; + } + s_epd.writeData((uint8_t *)(void *)p, (int)chunk); + p += chunk; + left -= chunk; + s_written_bytes += chunk; + remaining -= chunk; + if (bitplanes && !s_plane2_started && s_plane_size > 0u && s_written_bytes >= s_plane_size) { + s_epd.startWrite(PLANE_1); + s_plane2_started = true; + } + } + + if (s_written_bytes > written_before) { + dw_log_progress(); + } + return 0; +} + +static bool zlib_stream_to_direct_write(const uint8_t *data, uint32_t len, bool final) +{ + od_zlib_status_t status = od_zlib_stream_push(data, len, final); + if (status == OD_ZLIB_STATUS_ERROR) { + printf("[OD] zlib push error: %s\r\n", od_zlib_stream_error()); + return false; + } + + for (;;) { + size_t bytes_out = 0; + status = od_zlib_stream_poll(s_decompression_chunk, OPENDISPLAY_DECOMPRESSION_CHUNK_SIZE, &bytes_out); + if (bytes_out > 0u) { + uint32_t before = s_written_bytes; + if (dw_stream_raw_bytes(s_decompression_chunk, (uint32_t)bytes_out) != 0) { + return false; + } + if (s_written_bytes - before != (uint32_t)bytes_out) { + return false; + } + if (s_written_bytes > s_dw_decompressed_total) { + return false; + } + } + if (status == OD_ZLIB_STATUS_OUTPUT_READY) { + continue; + } + if (status == OD_ZLIB_STATUS_NEEDS_INPUT) { + return !final; + } + if (status == OD_ZLIB_STATUS_DONE) { + return s_written_bytes == s_dw_decompressed_total; + } + printf("[OD] zlib poll error: %s\r\n", od_zlib_stream_error()); + return false; + } } extern "C" void opendisplay_display_boot_apply(void) @@ -610,12 +713,8 @@ extern "C" void opendisplay_display_boot_apply(void) extern "C" int opendisplay_display_direct_write_start(const uint8_t *payload, uint16_t payload_len) { - (void)payload; s_dw_init_t0 = sl_sleeptimer_get_tick_count(); printf("[OD] dw init begin\r\n"); - if (payload_len != 0u) { - printf("[OD] dw start note non-empty payload len=%u (ignored)\r\n", (unsigned)payload_len); - } const struct DisplayConfig *d = display_cfg(); if (d == nullptr) { @@ -669,11 +768,43 @@ extern "C" int opendisplay_display_direct_write_start(const uint8_t *payload, ui s_dw_chunk_n = 0; s_dw_log_pct = 0; s_dw_trailing_ignores = 0; + s_dw_compressed = (payload != nullptr && payload_len >= 4u); + s_dw_decompressed_total = 0; s_active = true; - printf("[OD] dw start total=%lu B bpp=%d cs=%u panel=%u %ux%u bitplanes=%d\r\n", + + if (s_dw_compressed) { + if ((d->transmission_modes & TRANSMISSION_MODE_ZIP) == 0u) { + printf("[OD] dw start err ZIP not enabled in transmission_modes\r\n"); + opendisplay_display_abort(); + return -4; + } + s_dw_decompressed_total = + (uint32_t)payload[0] + | ((uint32_t)payload[1] << 8) + | ((uint32_t)payload[2] << 16) + | ((uint32_t)payload[3] << 24); + if (s_dw_decompressed_total != s_total_bytes) { + printf("[OD] dw start err zlib size %lu != %lu\r\n", + (unsigned long)s_dw_decompressed_total, (unsigned long)s_total_bytes); + opendisplay_display_abort(); + return -5; + } + od_zlib_stream_reset(s_dw_decompressed_total); + if (payload_len > 4u) { + if (!zlib_stream_to_direct_write(payload + 4, (uint32_t)payload_len - 4u, false)) { + opendisplay_display_abort(); + return -6; + } + } + } else if (payload_len != 0u) { + printf("[OD] dw start note non-empty payload len=%u (ignored)\r\n", (unsigned)payload_len); + } + + printf("[OD] dw start total=%lu B bpp=%d cs=%u panel=%u %ux%u bitplanes=%d%s\r\n", (unsigned long)s_total_bytes, opendisplay_color_bits_per_pixel(s_color_scheme), (unsigned)s_color_scheme, (unsigned)d->panel_ic_type, (unsigned)d->pixel_width, - (unsigned)d->pixel_height, (int)opendisplay_color_is_bitplanes(s_color_scheme)); + (unsigned)d->pixel_height, (int)opendisplay_color_is_bitplanes(s_color_scheme), + s_dw_compressed ? " zlib" : ""); return 0; } @@ -684,6 +815,17 @@ extern "C" int opendisplay_display_direct_write_data(const uint8_t *payload, uin return -1; } + if (s_dw_compressed) { + const uint32_t written_before = s_written_bytes; + if (!zlib_stream_to_direct_write(payload, payload_len, false)) { + return -3; + } + if (s_written_bytes > written_before) { + s_dw_chunk_n++; + } + return 0; + } + uint32_t remaining = (s_written_bytes < s_total_bytes) ? (s_total_bytes - s_written_bytes) : 0u; if (remaining == 0u) { if (payload_len > 0u) { @@ -697,47 +839,12 @@ extern "C" int opendisplay_display_direct_write_data(const uint8_t *payload, uin return 0; } - const bool bitplanes = opendisplay_color_is_bitplanes(s_color_scheme); - const uint8_t *p = payload; - uint16_t left = payload_len; const uint32_t written_before = s_written_bytes; - - while (left > 0u && remaining > 0u) { - uint32_t rem = (uint32_t)remaining; - uint16_t chunk = left; - if (bitplanes && !s_plane2_started && s_plane_size > 0u) { - uint32_t to_plane_end = s_plane_size - s_written_bytes; - if (chunk > to_plane_end) { - chunk = (uint16_t)to_plane_end; - } - } - if ((uint32_t)chunk > rem) { - chunk = (uint16_t)rem; - } - if (chunk == 0u) { - break; - } - s_epd.writeData((uint8_t *)(void *)p, (int)chunk); - p += chunk; - left -= chunk; - s_written_bytes += chunk; - remaining -= (uint32_t)chunk; - if (bitplanes && !s_plane2_started && s_plane_size > 0u && s_written_bytes >= s_plane_size) { - s_epd.startWrite(PLANE_1); - s_plane2_started = true; - } + if (dw_stream_raw_bytes(payload, payload_len) != 0) { + return -2; } - if (s_written_bytes > written_before) { s_dw_chunk_n++; - if (s_total_bytes > 0u) { - uint8_t pct = (uint8_t)((100u * s_written_bytes) / s_total_bytes); - if (pct >= s_dw_log_pct + 25u) { - printf("[OD] dw data #%lu %lu/%lu B (%u%%)\r\n", (unsigned long)s_dw_chunk_n, - (unsigned long)s_written_bytes, (unsigned long)s_total_bytes, (unsigned)pct); - s_dw_log_pct = (pct / 25u) * 25u; - } - } } return 0; } @@ -748,6 +855,12 @@ extern "C" int opendisplay_display_direct_write_end(const uint8_t *payload, uint printf("[OD] dw end err inactive\r\n"); return -1; } + if (s_dw_compressed) { + if (!zlib_stream_to_direct_write(nullptr, 0, true)) { + printf("[OD] dw end err zlib finalize\r\n"); + return -3; + } + } if (s_written_bytes < s_total_bytes) { printf("[OD] dw end err incomplete wr=%lu need=%lu\r\n", (unsigned long)s_written_bytes, (unsigned long)s_total_bytes); @@ -768,6 +881,8 @@ extern "C" int opendisplay_display_direct_write_end(const uint8_t *payload, uint printf("[OD] dw refresh done ok=%d busy=%d\r\n", (int)ok, (int)s_epd.isBusy()); s_epd.sleep(DEEP_SLEEP); s_active = false; + s_dw_compressed = false; + s_dw_decompressed_total = 0; display_power_set(false); if (refresh_ok != nullptr) { diff --git a/third_party/uzlib/src/od_zlib_stream.c b/third_party/uzlib/src/od_zlib_stream.c new file mode 100644 index 0000000..6e33710 --- /dev/null +++ b/third_party/uzlib/src/od_zlib_stream.c @@ -0,0 +1,723 @@ +/* + * OpenDisplay streaming zlib inflater, based on uzlib/tinf. + */ + +#include +#include +#include "uzlib.h" + +#define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) + +#ifndef OPENDISPLAY_ZLIB_USE_HEAP_WINDOW +#define OPENDISPLAY_ZLIB_USE_HEAP_WINDOW 0 +#endif + +#if OPENDISPLAY_ZLIB_USE_HEAP_WINDOW != 0 && OPENDISPLAY_ZLIB_USE_HEAP_WINDOW != 1 +#error "OPENDISPLAY_ZLIB_USE_HEAP_WINDOW must be 0 or 1" +#endif + +typedef struct { + unsigned short table[16]; + unsigned short trans[288]; +} TINF_LITERAL_TREE; + +typedef struct { + unsigned short table[16]; + unsigned short trans[32]; +} TINF_DISTANCE_TREE; + +typedef struct { + unsigned short table[16]; + unsigned short trans[19]; +} TINF_CODELEN_TREE; + +static const unsigned char length_bits[30] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 5 +}; + +static const unsigned short length_base[30] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 258 +}; + +static const unsigned char dist_bits[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +static const unsigned short dist_base[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, + 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577 +}; + +static const unsigned char clcidx[] = { + 16, 17, 18, 0, 8, 7, 9, 6, + 10, 5, 11, 4, 12, 3, 13, 2, + 14, 1, 15 +}; + +typedef enum { + ST_ZLIB_CMF, + ST_ZLIB_FLG, + ST_BLOCK_FINAL, + ST_BLOCK_TYPE, + ST_DYNAMIC_TREES, + ST_STORED_LEN, + ST_STORED_DATA, + ST_BLOCK_DATA, + ST_TRAILER, + ST_DONE, + ST_ERROR, +} inflate_stage_t; + +typedef enum { + BLK_SYMBOL, + BLK_LEN_EXTRA, + BLK_DIST_SYMBOL, + BLK_DIST_EXTRA, + BLK_MATCH_COPY, +} block_stage_t; + +typedef enum { + DYN_HLIT, + DYN_HDIST, + DYN_HCLEN, + DYN_CLEAR_CLEN, + DYN_READ_CLEN, + DYN_BUILD_CLTREE, + DYN_READ_LENGTHS, + DYN_REPEAT_BITS, + DYN_BUILD_TREES, +} dynamic_stage_t; + +typedef struct { + inflate_stage_t stage; + block_stage_t block_stage; + dynamic_stage_t dynamic_stage; + + const uint8_t *input; + size_t input_remaining; + bool input_final; + bool initialized; + + uint8_t bit_tag; + uint8_t bit_count; + + bool read_bits_active; + uint8_t read_bits_num; + uint8_t read_bits_pos; + unsigned int read_bits_base; + unsigned int read_bits_value; + + bool sym_active; + int sym_sum; + int sym_cur; + int sym_len; + + uint8_t cmf; + uint8_t bfinal; + uint8_t btype; + + TINF_LITERAL_TREE ltree; + union { + TINF_DISTANCE_TREE dtree; + TINF_CODELEN_TREE cltree; + } tree; + + unsigned char lengths[288 + 32]; + unsigned int hlit; + unsigned int hdist; + unsigned int hclen; + unsigned int hlimit; + unsigned int dyn_i; + unsigned int dyn_num; + unsigned char dyn_fill_value; + unsigned int dyn_repeat_len; + unsigned int dyn_repeat_bits; + unsigned int dyn_repeat_base; + + uint8_t stored_header_index; + uint16_t stored_len; + uint16_t stored_invlen; + + unsigned int match_len; + unsigned int match_offset; + int length_sym; + int dist_sym; + +#if OPENDISPLAY_ZLIB_USE_HEAP_WINDOW + uint8_t *window; +#else + uint8_t window[OPENDISPLAY_ZLIB_WINDOW_SIZE]; +#endif + unsigned int window_pos; + uint32_t expected_output; + uint32_t output_count; + uint32_t adler; + uint8_t trailer_index; + uint32_t trailer_value; + + const char *error; +} od_zlib_stream_state_t; + +static od_zlib_stream_state_t s; +#if OPENDISPLAY_ZLIB_USE_HEAP_WINDOW +static uint8_t *s_window; +#endif + +static void set_error(const char *error) { + s.stage = ST_ERROR; + s.error = error; +} + +static bool build_tree(unsigned short *table, unsigned short *trans, unsigned int trans_size, const unsigned char *lengths, unsigned int num) { + unsigned short offs[16]; + unsigned int i, sum; + + for (i = 0; i < 16; ++i) table[i] = 0; + for (i = 0; i < num; ++i) { + if (lengths[i] >= TINF_ARRAY_SIZE(offs)) { + set_error("invalid huffman code length"); + return false; + } + table[lengths[i]]++; + } + table[0] = 0; + + for (sum = 0, i = 0; i < 16; ++i) { + offs[i] = sum; + sum += table[i]; + } + if (sum > trans_size) { + set_error("huffman tree exceeds symbol storage"); + return false; + } + + for (i = 0; i < num; ++i) { + if (lengths[i]) trans[offs[lengths[i]]++] = i; + } + return true; +} + +static void build_fixed_trees(void) { + int i; + memset(&s.ltree, 0, sizeof(s.ltree)); + memset(&s.tree.dtree, 0, sizeof(s.tree.dtree)); + + s.ltree.table[7] = 24; + s.ltree.table[8] = 152; + s.ltree.table[9] = 112; + for (i = 0; i < 24; ++i) s.ltree.trans[i] = 256 + i; + for (i = 0; i < 144; ++i) s.ltree.trans[24 + i] = i; + for (i = 0; i < 8; ++i) s.ltree.trans[24 + 144 + i] = 280 + i; + for (i = 0; i < 112; ++i) s.ltree.trans[24 + 144 + 8 + i] = 144 + i; + + s.tree.dtree.table[5] = 32; + for (i = 0; i < 32; ++i) s.tree.dtree.trans[i] = i; +} + +static int read_byte(uint8_t *value) { + if (s.input_remaining > 0) { + *value = *s.input++; + s.input_remaining--; + return 1; + } + if (!s.input_final) return 0; + set_error("truncated zlib stream"); + return -1; +} + +static int read_bit(unsigned int *bit) { + uint8_t byte; + if (s.bit_count == 0) { + int rc = read_byte(&byte); + if (rc <= 0) return rc; + s.bit_tag = byte; + s.bit_count = 8; + } + *bit = s.bit_tag & 1u; + s.bit_tag >>= 1u; + s.bit_count--; + return 1; +} + +static int read_bits(unsigned int num, unsigned int base, unsigned int *value) { + if (!s.read_bits_active) { + s.read_bits_active = true; + s.read_bits_num = (uint8_t)num; + s.read_bits_pos = 0; + s.read_bits_base = base; + s.read_bits_value = 0; + } + + while (s.read_bits_pos < s.read_bits_num) { + unsigned int bit; + int rc = read_bit(&bit); + if (rc <= 0) return rc; + if (bit) s.read_bits_value += 1u << s.read_bits_pos; + s.read_bits_pos++; + } + + *value = s.read_bits_value + s.read_bits_base; + s.read_bits_active = false; + return 1; +} + +static int decode_symbol(const unsigned short *table, const unsigned short *trans, unsigned int trans_size, int *symbol) { + if (!s.sym_active) { + s.sym_active = true; + s.sym_sum = 0; + s.sym_cur = 0; + s.sym_len = 0; + } + + do { + unsigned int bit; + int rc = read_bit(&bit); + if (rc <= 0) return rc; + + s.sym_cur = 2 * s.sym_cur + (int)bit; + if (++s.sym_len == 16) { + set_error("invalid huffman code"); + return -1; + } + + s.sym_sum += table[s.sym_len]; + s.sym_cur -= table[s.sym_len]; + } while (s.sym_cur >= 0); + + s.sym_sum += s.sym_cur; + if (s.sym_sum < 0 || s.sym_sum >= (int)trans_size) { + set_error("invalid huffman symbol"); + return -1; + } + *symbol = trans[s.sym_sum]; + s.sym_active = false; + return 1; +} + +static void reset_code_readers(void) { + s.read_bits_active = false; + s.sym_active = false; +} + +static void adler_update_byte(uint8_t byte) { + uint32_t s1 = s.adler & 0xffffu; + uint32_t s2 = s.adler >> 16; + s1 += byte; + if (s1 >= 65521u) s1 -= 65521u; + s2 += s1; + s2 %= 65521u; + s.adler = (s2 << 16) | s1; +} + +static bool put_output_byte(uint8_t byte, uint8_t *output, size_t capacity, size_t *produced) { + if (*produced >= capacity) return false; + if (s.output_count >= s.expected_output) { + set_error("decompressed output exceeds expected size"); + return false; + } + + output[(*produced)++] = byte; + s.window[s.window_pos] = byte; + s.window_pos = (s.window_pos + 1u) & (OPENDISPLAY_ZLIB_WINDOW_SIZE - 1u); + s.output_count++; + adler_update_byte(byte); + return true; +} + +static int process_dynamic_trees(void) { + int sym; + unsigned int value; + + for (;;) { + switch (s.dynamic_stage) { + case DYN_HLIT: + if (read_bits(5, 257, &s.hlit) <= 0) return s.stage == ST_ERROR ? -1 : 0; + s.dynamic_stage = DYN_HDIST; + break; + case DYN_HDIST: + if (read_bits(5, 1, &s.hdist) <= 0) return s.stage == ST_ERROR ? -1 : 0; + s.dynamic_stage = DYN_HCLEN; + break; + case DYN_HCLEN: + if (read_bits(4, 4, &s.hclen) <= 0) return s.stage == ST_ERROR ? -1 : 0; + if (s.hlit > 286 || s.hdist > 32 || s.hlit + s.hdist > TINF_ARRAY_SIZE(s.lengths)) { + set_error("invalid dynamic tree sizes"); + return -1; + } + s.dyn_i = 0; + s.dynamic_stage = DYN_CLEAR_CLEN; + break; + case DYN_CLEAR_CLEN: + while (s.dyn_i < 19) s.lengths[s.dyn_i++] = 0; + s.dyn_i = 0; + s.dynamic_stage = DYN_READ_CLEN; + break; + case DYN_READ_CLEN: + while (s.dyn_i < s.hclen) { + if (read_bits(3, 0, &value) <= 0) return s.stage == ST_ERROR ? -1 : 0; + s.lengths[clcidx[s.dyn_i++]] = (unsigned char)value; + } + s.dynamic_stage = DYN_BUILD_CLTREE; + break; + case DYN_BUILD_CLTREE: + if (!build_tree(s.tree.cltree.table, s.tree.cltree.trans, TINF_ARRAY_SIZE(s.tree.cltree.trans), s.lengths, 19)) return -1; + s.hlimit = s.hlit + s.hdist; + s.dyn_num = 0; + s.dynamic_stage = DYN_READ_LENGTHS; + reset_code_readers(); + break; + case DYN_READ_LENGTHS: + while (s.dyn_num < s.hlimit) { + int rc = decode_symbol(s.tree.cltree.table, s.tree.cltree.trans, TINF_ARRAY_SIZE(s.tree.cltree.trans), &sym); + if (rc <= 0) return s.stage == ST_ERROR ? -1 : 0; + if (sym < 16) { + s.lengths[s.dyn_num++] = (unsigned char)sym; + continue; + } + if (sym == 16) { + if (s.dyn_num == 0) { + set_error("invalid dynamic repeat"); + return -1; + } + s.dyn_fill_value = s.lengths[s.dyn_num - 1]; + s.dyn_repeat_bits = 2; + s.dyn_repeat_base = 3; + } else if (sym == 17) { + s.dyn_fill_value = 0; + s.dyn_repeat_bits = 3; + s.dyn_repeat_base = 3; + } else if (sym == 18) { + s.dyn_fill_value = 0; + s.dyn_repeat_bits = 7; + s.dyn_repeat_base = 11; + } else { + set_error("invalid dynamic code length symbol"); + return -1; + } + s.dynamic_stage = DYN_REPEAT_BITS; + break; + } + if (s.dynamic_stage != DYN_READ_LENGTHS) break; + s.dynamic_stage = DYN_BUILD_TREES; + break; + case DYN_REPEAT_BITS: + if (read_bits(s.dyn_repeat_bits, s.dyn_repeat_base, &s.dyn_repeat_len) <= 0) { + return s.stage == ST_ERROR ? -1 : 0; + } + if (s.dyn_num + s.dyn_repeat_len > s.hlimit) { + set_error("dynamic repeat exceeds tree size"); + return -1; + } + while (s.dyn_repeat_len--) s.lengths[s.dyn_num++] = s.dyn_fill_value; + s.dynamic_stage = DYN_READ_LENGTHS; + break; + case DYN_BUILD_TREES: + if (s.lengths[256] == 0) { + set_error("dynamic tree missing end-of-block"); + return -1; + } + if (!build_tree(s.ltree.table, s.ltree.trans, TINF_ARRAY_SIZE(s.ltree.trans), s.lengths, s.hlit)) return -1; + if (!build_tree(s.tree.dtree.table, s.tree.dtree.trans, TINF_ARRAY_SIZE(s.tree.dtree.trans), s.lengths + s.hlit, s.hdist)) return -1; + s.dynamic_stage = DYN_HLIT; + reset_code_readers(); + return 1; + } + } +} + +static int process_stored_header(void) { + uint8_t byte; + while (s.stored_header_index < 4) { + int rc = read_byte(&byte); + if (rc <= 0) return rc; + if (s.stored_header_index == 0) s.stored_len = byte; + else if (s.stored_header_index == 1) s.stored_len |= (uint16_t)byte << 8; + else if (s.stored_header_index == 2) s.stored_invlen = byte; + else s.stored_invlen |= (uint16_t)byte << 8; + s.stored_header_index++; + } + if (s.stored_len != (uint16_t)(~s.stored_invlen)) { + set_error("invalid stored block length"); + return -1; + } + s.stored_header_index = 0; + return 1; +} + +static int process_stored_data(uint8_t *output, size_t capacity, size_t *produced) { + while (s.stored_len > 0) { + uint8_t byte; + int rc; + if (*produced >= capacity) return 2; + rc = read_byte(&byte); + if (rc <= 0) return rc; + if (!put_output_byte(byte, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; + s.stored_len--; + } + return 1; +} + +static int process_block_data(uint8_t *output, size_t capacity, size_t *produced) { + int sym; + + for (;;) { + if (*produced >= capacity) return 2; + switch (s.block_stage) { + case BLK_SYMBOL: { + int rc = decode_symbol(s.ltree.table, s.ltree.trans, TINF_ARRAY_SIZE(s.ltree.trans), &sym); + if (rc <= 0) return rc; + if (sym < 256) { + if (!put_output_byte((uint8_t)sym, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; + return 1; + } + if (sym == 256) { + s.block_stage = BLK_SYMBOL; + return 3; + } + sym -= 257; + if (sym < 0 || sym >= 29) { + set_error("invalid length symbol"); + return -1; + } + s.length_sym = sym; + s.block_stage = BLK_LEN_EXTRA; + break; + } + case BLK_LEN_EXTRA: + if (read_bits(length_bits[s.length_sym], length_base[s.length_sym], &s.match_len) <= 0) { + return s.stage == ST_ERROR ? -1 : 0; + } + s.block_stage = BLK_DIST_SYMBOL; + break; + case BLK_DIST_SYMBOL: { + int rc = decode_symbol(s.tree.dtree.table, s.tree.dtree.trans, TINF_ARRAY_SIZE(s.tree.dtree.trans), &s.dist_sym); + if (rc <= 0) return rc; + if (s.dist_sym < 0 || s.dist_sym >= 30) { + set_error("invalid distance symbol"); + return -1; + } + s.block_stage = BLK_DIST_EXTRA; + break; + } + case BLK_DIST_EXTRA: + if (read_bits(dist_bits[s.dist_sym], dist_base[s.dist_sym], &s.match_offset) <= 0) { + return s.stage == ST_ERROR ? -1 : 0; + } + if (s.match_offset == 0 || s.match_offset > OPENDISPLAY_ZLIB_WINDOW_SIZE || s.match_offset > s.output_count) { + set_error("invalid back-reference distance"); + return -1; + } + s.block_stage = BLK_MATCH_COPY; + break; + case BLK_MATCH_COPY: + while (s.match_len > 0) { + uint8_t byte; + unsigned int source; + if (*produced >= capacity) return 2; + source = (s.window_pos + OPENDISPLAY_ZLIB_WINDOW_SIZE - s.match_offset) & (OPENDISPLAY_ZLIB_WINDOW_SIZE - 1u); + byte = s.window[source]; + if (!put_output_byte(byte, output, capacity, produced)) return s.stage == ST_ERROR ? -1 : 2; + s.match_len--; + } + s.block_stage = BLK_SYMBOL; + break; + } + } +} + +static int process_trailer(void) { + uint8_t byte; + while (s.trailer_index < 4) { + int rc = read_byte(&byte); + if (rc <= 0) return rc; + s.trailer_value = (s.trailer_value << 8) | byte; + s.trailer_index++; + } + if (s.trailer_value != s.adler) { + set_error("zlib adler32 mismatch"); + return -1; + } + if (s.output_count != s.expected_output) { + set_error("decompressed output size mismatch"); + return -1; + } + if (s.input_remaining > 0) { + set_error("input after end of zlib stream"); + return -1; + } + s.stage = ST_DONE; + return 1; +} + +void od_zlib_stream_reset(uint32_t expected_output_size) { +#if OPENDISPLAY_ZLIB_USE_HEAP_WINDOW + if (s_window == NULL) { + s_window = (uint8_t *)malloc(OPENDISPLAY_ZLIB_WINDOW_SIZE); + } +#endif + memset(&s, 0, sizeof(s)); +#if OPENDISPLAY_ZLIB_USE_HEAP_WINDOW + if (s_window == NULL) { + s.initialized = true; + set_error("zlib history window allocation failed"); + return; + } + s.window = s_window; +#endif + s.stage = ST_ZLIB_CMF; + s.block_stage = BLK_SYMBOL; + s.dynamic_stage = DYN_HLIT; + s.expected_output = expected_output_size; + s.adler = 1; + s.initialized = true; +} + +od_zlib_status_t od_zlib_stream_push(const uint8_t *input, size_t len, bool final) { + if (!s.initialized) { + set_error("zlib stream not initialized"); + return OD_ZLIB_STATUS_ERROR; + } + if (len > 0 && input == NULL) { + set_error("zlib stream input is null"); + return OD_ZLIB_STATUS_ERROR; + } + if (s.input_remaining > 0) { + set_error("previous input not fully consumed"); + return OD_ZLIB_STATUS_ERROR; + } + s.input = input; + s.input_remaining = len; + s.input_final = final; + if (s.stage == ST_DONE) { + if (len != 0) { + set_error("input after end of zlib stream"); + return OD_ZLIB_STATUS_ERROR; + } + return OD_ZLIB_STATUS_DONE; + } + return OD_ZLIB_STATUS_NEEDS_INPUT; +} + +od_zlib_status_t od_zlib_stream_poll(uint8_t *output, size_t capacity, size_t *produced) { + *produced = 0; + if (!s.initialized) { + set_error("zlib stream not initialized"); + return OD_ZLIB_STATUS_ERROR; + } + if (s.stage == ST_ERROR) return OD_ZLIB_STATUS_ERROR; + if (s.stage == ST_DONE) return OD_ZLIB_STATUS_DONE; + + for (;;) { + int rc; + unsigned int value; + uint8_t byte; + + if (*produced >= capacity) return OD_ZLIB_STATUS_OUTPUT_READY; + + switch (s.stage) { + case ST_ZLIB_CMF: + rc = read_byte(&s.cmf); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + s.stage = ST_ZLIB_FLG; + break; + case ST_ZLIB_FLG: + rc = read_byte(&byte); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + if (((256u * s.cmf + byte) % 31u) != 0 || (s.cmf & 0x0fu) != 8u || (byte & 0x20u) != 0) { + set_error("invalid zlib header"); + return OD_ZLIB_STATUS_ERROR; + } + if (((s.cmf >> 4) + 8u) > OPENDISPLAY_ZLIB_WINDOW_BITS) { + set_error("zlib stream window exceeds firmware limit"); + return OD_ZLIB_STATUS_ERROR; + } + s.stage = ST_BLOCK_FINAL; + break; + case ST_BLOCK_FINAL: + rc = read_bit(&value); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + s.bfinal = (uint8_t)value; + s.stage = ST_BLOCK_TYPE; + break; + case ST_BLOCK_TYPE: + rc = read_bits(2, 0, &value); + if (rc <= 0) return s.stage == ST_ERROR ? OD_ZLIB_STATUS_ERROR : OD_ZLIB_STATUS_NEEDS_INPUT; + s.btype = (uint8_t)value; + if (s.btype == 0) { + s.bit_count = 0; + s.stored_header_index = 0; + s.stored_len = 0; + s.stored_invlen = 0; + s.stage = ST_STORED_LEN; + } else if (s.btype == 1) { + build_fixed_trees(); + s.block_stage = BLK_SYMBOL; + reset_code_readers(); + s.stage = ST_BLOCK_DATA; + } else if (s.btype == 2) { + s.dynamic_stage = DYN_HLIT; + reset_code_readers(); + s.stage = ST_DYNAMIC_TREES; + } else { + set_error("invalid deflate block type"); + return OD_ZLIB_STATUS_ERROR; + } + break; + case ST_DYNAMIC_TREES: + rc = process_dynamic_trees(); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return OD_ZLIB_STATUS_NEEDS_INPUT; + s.block_stage = BLK_SYMBOL; + s.stage = ST_BLOCK_DATA; + break; + case ST_STORED_LEN: + rc = process_stored_header(); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return OD_ZLIB_STATUS_NEEDS_INPUT; + s.stage = ST_STORED_DATA; + break; + case ST_STORED_DATA: + rc = process_stored_data(output, capacity, produced); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return *produced ? OD_ZLIB_STATUS_OUTPUT_READY : OD_ZLIB_STATUS_NEEDS_INPUT; + if (rc == 2) return OD_ZLIB_STATUS_OUTPUT_READY; + s.stage = s.bfinal ? ST_TRAILER : ST_BLOCK_FINAL; + break; + case ST_BLOCK_DATA: + rc = process_block_data(output, capacity, produced); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return *produced ? OD_ZLIB_STATUS_OUTPUT_READY : OD_ZLIB_STATUS_NEEDS_INPUT; + if (rc == 2) return OD_ZLIB_STATUS_OUTPUT_READY; + if (rc == 3) s.stage = s.bfinal ? ST_TRAILER : ST_BLOCK_FINAL; + break; + case ST_TRAILER: + rc = process_trailer(); + if (rc < 0) return OD_ZLIB_STATUS_ERROR; + if (rc == 0) return *produced ? OD_ZLIB_STATUS_OUTPUT_READY : OD_ZLIB_STATUS_NEEDS_INPUT; + return OD_ZLIB_STATUS_DONE; + case ST_DONE: + return OD_ZLIB_STATUS_DONE; + case ST_ERROR: + return OD_ZLIB_STATUS_ERROR; + } + } +} + +const char *od_zlib_stream_error(void) { + return s.error ? s.error : ""; +} + +uint32_t od_zlib_stream_output_count(void) { + return s.output_count; +} diff --git a/third_party/uzlib/src/uzlib.h b/third_party/uzlib/src/uzlib.h new file mode 100644 index 0000000..a978783 --- /dev/null +++ b/third_party/uzlib/src/uzlib.h @@ -0,0 +1,51 @@ +/* + * OpenDisplay streaming zlib inflater, based on uzlib/tinf. + * + * The original uzlib one-shot/callback inflater API is intentionally not + * exposed in this vendored copy. Firmware uses one global streaming inflater. + */ + +#ifndef UZLIB_H_INCLUDED +#define UZLIB_H_INCLUDED + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "uzlib_conf.h" + +#ifndef OPENDISPLAY_ZLIB_WINDOW_BITS +#define OPENDISPLAY_ZLIB_WINDOW_BITS 9 +#endif + +#if OPENDISPLAY_ZLIB_WINDOW_BITS < 9 || OPENDISPLAY_ZLIB_WINDOW_BITS > 15 +#error "OPENDISPLAY_ZLIB_WINDOW_BITS must be in range 9..15" +#endif + +#define OPENDISPLAY_ZLIB_WINDOW_SIZE (1u << OPENDISPLAY_ZLIB_WINDOW_BITS) + +typedef enum { + OD_ZLIB_STATUS_NEEDS_INPUT = 0, + OD_ZLIB_STATUS_OUTPUT_READY = 1, + OD_ZLIB_STATUS_DONE = 2, + OD_ZLIB_STATUS_ERROR = -1, +} od_zlib_status_t; + +void od_zlib_stream_reset(uint32_t expected_output_size); +od_zlib_status_t od_zlib_stream_push(const uint8_t *input, size_t len, bool final); +od_zlib_status_t od_zlib_stream_poll(uint8_t *output, size_t capacity, size_t *produced); +const char *od_zlib_stream_error(void); +uint32_t od_zlib_stream_output_count(void); + +uint32_t uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); +uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc); + +#ifdef __cplusplus +} +#endif + +#endif /* UZLIB_H_INCLUDED */ diff --git a/third_party/uzlib/src/uzlib_conf.h b/third_party/uzlib/src/uzlib_conf.h new file mode 100644 index 0000000..fd67dec --- /dev/null +++ b/third_party/uzlib/src/uzlib_conf.h @@ -0,0 +1,32 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2014-2018 by Paul Sokolovsky + */ + +#ifndef UZLIB_CONF_H_INCLUDED +#define UZLIB_CONF_H_INCLUDED + +#ifndef UZLIB_CONF_DEBUG_LOG +/* Debug logging level 0, 1, 2, etc. */ +#define UZLIB_CONF_DEBUG_LOG 0 +#endif + +#ifndef UZLIB_CONF_PARANOID_CHECKS +/* Perform extra checks on the input stream, even if they aren't proven + to be strictly required (== lack of them wasn't proven to lead to + crashes). */ +#define UZLIB_CONF_PARANOID_CHECKS 0 +#endif + +#ifndef UZLIB_CONF_USE_MEMCPY +/* Use memcpy() for copying data out of LZ window or uncompressed blocks, + instead of doing this byte by byte. For well-compressed data, this + may noticeably increase decompression speed. But for less compressed, + it can actually deteriorate it (due to the fact that many memcpy() + implementations are optimized for large blocks of data, and have + too much overhead for short strings of just a few bytes). */ +#define UZLIB_CONF_USE_MEMCPY 0 +#endif + +#endif /* UZLIB_CONF_H_INCLUDED */